colored demo

This commit is contained in:
Yaossg 2025-03-10 00:46:24 +08:00
parent 1bcff515b7
commit e425464aab
5 changed files with 183 additions and 35 deletions

3
.gitignore vendored
View file

@ -3,4 +3,5 @@ cov
*.o *.o
*.s *.s
*.ans *.ans
*.elf *.elf
*.html

88
boot.c
View file

@ -20,13 +20,10 @@ void exit(int status);
int fprintf(void* file, char* format, ...); int fprintf(void* file, char* format, ...);
int ungetc(int ch, void* file); int ungetc(int ch, void* file);
void ungetchar(int ch) {
ungetc(ch, stdin);
}
// limitations // limitations
enum { enum {
ECHO_BUFFER_SIZE = 65536,
STRING_TABLE_SIZE = 65536, STRING_TABLE_SIZE = 65536,
STRING_LUT_SIZE = 4096, STRING_LUT_SIZE = 4096,
ID_TABLE_SIZE = 65536, ID_TABLE_SIZE = 65536,
@ -35,6 +32,28 @@ enum {
REG_SIZE = 4096, REG_SIZE = 4096,
}; };
// hooks
char echo_buffer[ECHO_BUFFER_SIZE];
int echo_size;
int readchar() {
int ch = getchar();
if (ch != '\n') {
echo_buffer[echo_size++] = ch;
} else {
echo_buffer[echo_size++] = 0;
printf("#@%s\n", echo_buffer);
echo_size = 0;
}
return ch;
}
void ungetchar(int ch) {
--echo_size;
ungetc(ch, stdin);
}
// constants // constants
enum { enum {
@ -220,7 +239,7 @@ int is_id_cont(int ch) {
int parse_int(int ch) { int parse_int(int ch) {
int num = ch - '0'; int num = ch - '0';
while (is_digit(ch = getchar())) { while (is_digit(ch = readchar())) {
num *= 10; num *= 10;
num += ch - '0'; num += ch - '0';
} }
@ -229,7 +248,7 @@ int parse_int(int ch) {
} }
int get_escaped_char() { int get_escaped_char() {
int ch = getchar(); int ch = readchar();
if (ch == 'n') return '\n'; if (ch == 'n') return '\n';
if (ch == 't') return '\t'; if (ch == 't') return '\t';
if (ch == 'r') return '\r'; if (ch == 'r') return '\r';
@ -252,7 +271,7 @@ int string_lut_size;
int parse_string() { int parse_string() {
int offset = string_offset; int offset = string_offset;
int ch; int ch;
while ((ch = getchar()) != '"') { while ((ch = readchar()) != '"') {
if (ch == -1 || ch == '\n') { if (ch == -1 || ch == '\n') {
fprintf(stderr, "expecting '\"'\n"); fprintf(stderr, "expecting '\"'\n");
exit(1); exit(1);
@ -292,7 +311,7 @@ int id_lut_size;
int parse_id(int ch) { int parse_id(int ch) {
int offset = id_offset; int offset = id_offset;
id_table[id_offset++] = ch; id_table[id_offset++] = ch;
while (is_id_cont(ch = getchar())) { while (is_id_cont(ch = readchar())) {
id_table[id_offset++] = ch; id_table[id_offset++] = ch;
} }
ungetchar(ch); ungetchar(ch);
@ -366,9 +385,9 @@ void next_token() {
token_state = 0; token_state = 0;
return; return;
} }
int ch = getchar(); int ch = readchar();
while (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n') { while (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n') {
ch = getchar(); ch = readchar();
} }
if (ch == -1) { if (ch == -1) {
token_type = TOKEN_EOF; token_type = TOKEN_EOF;
@ -385,7 +404,7 @@ void next_token() {
} else if (ch == '}') { } else if (ch == '}') {
token_type = TOKEN_BRACE_RIGHT; token_type = TOKEN_BRACE_RIGHT;
} else if (ch == '+') { } else if (ch == '+') {
int ch2 = getchar(); int ch2 = readchar();
if (ch2 == '+') { if (ch2 == '+') {
token_type = TOKEN_INC; token_type = TOKEN_INC;
} else if (ch2 == '=') { } else if (ch2 == '=') {
@ -395,7 +414,7 @@ void next_token() {
token_type = TOKEN_ADD; token_type = TOKEN_ADD;
} }
} else if (ch == '-') { } else if (ch == '-') {
int ch2 = getchar(); int ch2 = readchar();
if (ch2 == '-') { if (ch2 == '-') {
token_type = TOKEN_DEC; token_type = TOKEN_DEC;
} else if (ch2 == '=') { } else if (ch2 == '=') {
@ -405,7 +424,7 @@ void next_token() {
token_type = TOKEN_SUB; token_type = TOKEN_SUB;
} }
} else if (ch == '*') { } else if (ch == '*') {
int ch2 = getchar(); int ch2 = readchar();
if (ch2 == '=') { if (ch2 == '=') {
token_type = TOKEN_MUL_ASSIGN; token_type = TOKEN_MUL_ASSIGN;
} else { } else {
@ -413,22 +432,22 @@ void next_token() {
token_type = TOKEN_MUL; token_type = TOKEN_MUL;
} }
} else if (ch == '/') { } else if (ch == '/') {
int ch2 = getchar(); int ch2 = readchar();
if (ch2 == '=') { if (ch2 == '=') {
token_type = TOKEN_DIV_ASSIGN; token_type = TOKEN_DIV_ASSIGN;
} else if (ch2 == '/') { } else if (ch2 == '/') {
do ch = getchar(); while (ch != -1 && ch != '\n'); do ch = readchar(); while (ch != -1 && ch != '\n');
next_token(); next_token();
return; return;
} else if (ch2 == '*') { } else if (ch2 == '*') {
while (1) { while (1) {
ch = getchar(); ch = readchar();
if (ch == -1) { if (ch == -1) {
fprintf(stderr, "expecting '*/'\n"); fprintf(stderr, "expecting '*/'\n");
exit(1); exit(1);
} }
if (ch == '*') { if (ch == '*') {
ch = getchar(); ch = readchar();
if (ch == '/') { if (ch == '/') {
break; break;
} }
@ -441,7 +460,7 @@ void next_token() {
token_type = TOKEN_DIV; token_type = TOKEN_DIV;
} }
} else if (ch == '%') { } else if (ch == '%') {
int ch2 = getchar(); int ch2 = readchar();
if (ch2 == '=') { if (ch2 == '=') {
token_type = TOKEN_REM_ASSIGN; token_type = TOKEN_REM_ASSIGN;
} else { } else {
@ -457,11 +476,11 @@ void next_token() {
} else if (ch == ',') { } else if (ch == ',') {
token_type = TOKEN_COMMA; token_type = TOKEN_COMMA;
} else if (ch == '<') { } else if (ch == '<') {
int ch2 = getchar(); int ch2 = readchar();
if (ch2 == '=') { if (ch2 == '=') {
token_type = TOKEN_LE; token_type = TOKEN_LE;
} else if (ch2 == '<') { } else if (ch2 == '<') {
int ch3 = getchar(); int ch3 = readchar();
if (ch3 == '=') { if (ch3 == '=') {
token_type = TOKEN_LSHIFT_ASSIGN; token_type = TOKEN_LSHIFT_ASSIGN;
} else { } else {
@ -473,11 +492,11 @@ void next_token() {
token_type = TOKEN_LT; token_type = TOKEN_LT;
} }
} else if (ch == '>') { } else if (ch == '>') {
int ch2 = getchar(); int ch2 = readchar();
if (ch2 == '=') { if (ch2 == '=') {
token_type = TOKEN_GE; token_type = TOKEN_GE;
} else if (ch2 == '>') { } else if (ch2 == '>') {
int ch3 = getchar(); int ch3 = readchar();
if (ch3 == '=') { if (ch3 == '=') {
token_type = TOKEN_RSHIFT_ASSIGN; token_type = TOKEN_RSHIFT_ASSIGN;
} else { } else {
@ -489,7 +508,7 @@ void next_token() {
token_type = TOKEN_GT; token_type = TOKEN_GT;
} }
} else if (ch == '=') { } else if (ch == '=') {
int ch2 = getchar(); int ch2 = readchar();
if (ch2 == '=') { if (ch2 == '=') {
token_type = TOKEN_EQ; token_type = TOKEN_EQ;
} else { } else {
@ -497,7 +516,7 @@ void next_token() {
token_type = TOKEN_ASSIGN; token_type = TOKEN_ASSIGN;
} }
} else if (ch == '!') { } else if (ch == '!') {
int ch2 = getchar(); int ch2 = readchar();
if (ch2 == '=') { if (ch2 == '=') {
token_type = TOKEN_NE; token_type = TOKEN_NE;
} else { } else {
@ -505,7 +524,7 @@ void next_token() {
token_type = TOKEN_NOT; token_type = TOKEN_NOT;
} }
} else if (ch == '&') { } else if (ch == '&') {
int ch2 = getchar(); int ch2 = readchar();
if (ch2 == '=') { if (ch2 == '=') {
token_type = TOKEN_AND_ASSIGN; token_type = TOKEN_AND_ASSIGN;
} else if (ch2 == '&') { } else if (ch2 == '&') {
@ -515,7 +534,7 @@ void next_token() {
token_type = TOKEN_AND; token_type = TOKEN_AND;
} }
} else if (ch == '|') { } else if (ch == '|') {
int ch2 = getchar(); int ch2 = readchar();
if (ch2 == '=') { if (ch2 == '=') {
token_type = TOKEN_OR_ASSIGN; token_type = TOKEN_OR_ASSIGN;
} else if (ch2 == '|') { } else if (ch2 == '|') {
@ -525,7 +544,7 @@ void next_token() {
token_type = TOKEN_OR; token_type = TOKEN_OR;
} }
} else if (ch == '^') { } else if (ch == '^') {
int ch2 = getchar(); int ch2 = readchar();
if (ch2 == '=') { if (ch2 == '=') {
token_type = TOKEN_XOR_ASSIGN; token_type = TOKEN_XOR_ASSIGN;
} else { } else {
@ -536,11 +555,11 @@ void next_token() {
token_type = TOKEN_INV; token_type = TOKEN_INV;
} else if (ch == '\'') { } else if (ch == '\'') {
token_type = TOKEN_NUMBER; token_type = TOKEN_NUMBER;
token_data = getchar(); token_data = readchar();
if (token_data == '\\') { if (token_data == '\\') {
token_data = get_escaped_char(); token_data = get_escaped_char();
} }
if (getchar() != '\'') { if (readchar() != '\'') {
fprintf(stderr, "expecting '\n"); fprintf(stderr, "expecting '\n");
exit(1); exit(1);
} }
@ -550,8 +569,8 @@ void next_token() {
dedup_string(); dedup_string();
} else if (ch == '.') { } else if (ch == '.') {
token_type = 0; token_type = 0;
if (getchar() == '.') { if (readchar() == '.') {
if (getchar() == '.') { if (readchar() == '.') {
token_type = TOKEN_ELLIPSIS; token_type = TOKEN_ELLIPSIS;
} }
} }
@ -826,7 +845,7 @@ void load(int rd, int rs) {
} }
rs = rd; rs = rd;
} }
printf(" %s %s, 0(%s) # load non-trivial register\n", op, rd_name, reg_name(rs)); printf(" %s %s, 0(%s)\n", op, rd_name, reg_name(rs));
} }
// store a trivial register into a non-trivial one // store a trivial register into a non-trivial one
@ -839,7 +858,7 @@ void store(char* rs, int reg) {
} }
reg = REG_T2; reg = REG_T2;
} }
printf(" %s %s, 0(%s) # store non-trivial register\n", op, rs, reg_name(reg)); printf(" %s %s, 0(%s)\n", op, rs, reg_name(reg));
} }
int is_nontrivial(int reg) { int is_nontrivial(int reg) {
@ -972,7 +991,7 @@ int lookup(int id) {
char* name = id_table + id_lut[id]; char* name = id_table + id_lut[id];
if (global_kind[id]) { if (global_kind[id]) {
if (global_kind[id] == KIND_FUNCTION) { if (global_kind[id] == KIND_FUNCTION) {
fprintf(stderr, "function name must not appear outside function call: %s\n", name); fprintf(stderr, "function name can only be directly called: %s\n", name);
exit(1); exit(1);
} }
int rd = next_reg(TYPE_VOID_PTR); int rd = next_reg(TYPE_VOID_PTR);
@ -1974,6 +1993,7 @@ void parse_top_level() {
} }
void dump_string_table() { void dump_string_table() {
printf("# string table: \n");
printf(".data\n"); printf(".data\n");
for (int i = 0; i < string_lut_size; ++i) { for (int i = 0; i < string_lut_size; ++i) {
printf(".LC%d: .string \"", i); printf(".LC%d: .string \"", i);

4
compile.sh Normal file
View file

@ -0,0 +1,4 @@
gcc boot.c -o boot.elf &&
./boot.elf < $1 > $1.s
python3 transcript.py < $1.s > $1.html
rm boot.elf

9
demo/for.c Normal file
View file

@ -0,0 +1,9 @@
int printf(char* format, ...);
int main() {
int i;
for (i = 0;
i < 10;
++i)
printf("hello %d\n", i);
}

114
transcript.py Normal file
View file

@ -0,0 +1,114 @@
import sys
import html
src = ''
asm = ''
style = 0
flip = False
def decorate(line):
return f'<span class="style{style}">{html.escape(line)}</span>'
for line in sys.stdin:
if line.startswith("#@"):
line = line.removeprefix("#@")
src += decorate(line) if flip else line
style += 1
style %= 5
flip = False
elif line.startswith("# string table: "):
asm += line
break
else:
asm += decorate(line)
flip = True
for line in sys.stdin:
asm += line
title = 'RVBTCC Code Gen Demo'
template = '''
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><%title%></title>
<style>
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 0;
display: flex;
flex-direction: column;
align-items: center;
}
header {
background-color: #4CAF50;
color: white;
padding: 1em;
text-align: center;
width: 100%;
}
.container {
display: flex;
width: 80%;
margin: 2em 0;
}
.column {
flex: 1;
margin: 0 1em;
}
pre {
background-color: #f4f4f4;
padding: 1em;
border: 1px solid #ddd;
overflow-x: auto;
}
code {
font-family: 'Consolas', Consolas, monospace;
font-size: 20px;
}
.style0 {
background-color: rgba(255,129,130,0.4);
}
.style1 {
background-color: rgba(212,167,44,0.4);
}
.style2 {
background-color: rgba(74,194,107,0.4);
}
.style3 {
background-color: rgba(84,174,255,0.4);
}
.style4 {
background-color: rgba(194,151,255,0.4);
}
</style>
</head>
<body>
<header>
<h1><%title%></h1>
</header>
<div class="container">
<div class="column">
<h2>Source Code</h2>
<pre>
<code><%src%></code>
</pre>
</div>
<div class="column">
<h2>Assembly</h2>
<pre>
<code><%asm%></code>
</pre>
</div>
</div>
</body>
</html>
'''
print(template.replace("<%title%>", title).replace("<%src%>", src).replace("<%asm%>", asm))