From 66505c1389f418523d28809083d695119e9b88a2 Mon Sep 17 00:00:00 2001 From: Yaossg Date: Fri, 29 Nov 2024 16:00:22 +0800 Subject: [PATCH] const table --- README.md | 22 ++++++++++++++++------ boot.c | 52 +++++++++++++++++++++++++++++++++++----------------- 2 files changed, 51 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index f2c2324..7b37e2a 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # RVBTCC -- 约 1500 行的轻量级自举编译器。 +- 约 1800 行的轻量级自举编译器。 - 编译器和自举编译器行为一致。 - 语法类似 C,输出 RISC-V 汇编。 - 依赖几个 libc 函数用于输入输出。 @@ -64,7 +64,21 @@ $ sh boot.sh 本语言包含的关键字即为支持的标量类型的关键字和流程控制的关键字,还有 `const`。 -`const` 关键字可以在类型中使用,但会被直接忽略。支持它是为了更好兼容 C 程序。 +### `const` 关键字 + +`const` 关键字可以在类型中使用,在大部分情况下会被直接忽略。支持它是为了更好兼容 C 程序。 + +但是当在出现 + +- 全局,标量(即不是数组) +- 类型为 `const int` 或 `const int const` +- 带有初始化 + +的声明时,将会被解析为整数常量。 + +整数常量在使用的时候会被直接替换为对应的右值,失去作为全局变量左值的性质。 + +使用 `int const` 或 `int` 可以避免这样的特殊处理。 ### 支持六个基本类型 @@ -75,13 +89,11 @@ $ sh boot.sh | `int` | `int*` | - 注意指针类型不是复合得来的,而是被视作整体。因此也不存在二重指针。 - - 函数和数组不是类型系统的一部分。 - 可以认为数组的类型就是其元素对应的指针类型。 - 函数的参数类型和个数不会检查,返回值会参与类型检查。 - 函数名只能被用于调用,函数调用被视为初等表达式。 - 数组只支持一维数组,且数组的元素不能是指针类型。 -- 全局变量不能是指针类型。 - 整数和字符字面量的类型是 `int`,字符串字面量的类型是 `char*` ### 支持的流程控制 @@ -116,8 +128,6 @@ $ sh boot.sh - 算术运算的结果总是被提升为 `int` 类型。布尔值用 `int` 类型表示。 - 由于空指针就是 `0`,因此指针和整数之间的比较运算没有禁止。 - 逻辑与和逻辑或支持短路求值。 -- 表达式没有左值和右值之分。可以认为右值总是存在一个临时的变量中。 -- 赋值不检查类型。强制类型转换可以用赋值给特定类型的变量实现。 ### 其它支持与不支持 diff --git a/boot.c b/boot.c index 91f6486..0586617 100644 --- a/boot.c +++ b/boot.c @@ -381,17 +381,14 @@ void next_token() { token_data = parse_string(); dedup_string(); } else if (ch == '.') { - int ch2 = getchar(); - if (ch2 == '.') { - int ch3 = getchar(); - if (ch3 == '.') { + token_type = 0; + if (getchar() == '.') { + if (getchar() == '.') { token_type = TOKEN_ELLIPSIS; - } else { - eprintf("unexpected character: %c\n", ch3); - exit(1); } - } else { - eprintf("unexpected character: %c\n", ch2); + } + if (token_type != TOKEN_ELLIPSIS) { + eprintf("expecting '...'\n"); exit(1); } } else if (is_digit(ch)) { @@ -467,6 +464,9 @@ int max_reg_id = 18; int indirection[4096]; int overflow[4096]; +int const_table[4096]; // id -> value +int is_const[4096]; + const int REG_ZERO = 0; const int REG_RA = 1; const int REG_SP = 2; @@ -837,10 +837,19 @@ int lookup_from_slot(int slot) { return materialize_address(reg, local_type[slot], local_marker[slot]); } +int load_imm(int imm) { + if (imm == 0) return REG_ZERO; + int reg = next_reg(TYPE_INT); + _asm_i("li", reg, "", "", imm); + return reg; +} + int lookup(int id) { - int slot = local_table[id]; - if (slot) { - return lookup_from_slot(slot); + if (local_table[id]) { + return lookup_from_slot(local_table[id]); + } + if (is_const[id]) { + return load_imm(const_table[id]); } const char* name = id_table + id_lut[id]; if (global_marker[id]) { @@ -1066,10 +1075,7 @@ int parse_primary_expr() { if (token_type == TOKEN_EOF) { exit(1); } else if (token_type == TOKEN_NUMBER) { - if (token_data == 0) return REG_ZERO; - int reg = next_reg(TYPE_INT); - _asm_i("li", reg, "", "", token_data); - return reg; + return load_imm(token_data); } else if (token_type == TOKEN_ID) { next_token(); if (token_type == TOKEN_PAREN_LEFT) { @@ -1695,16 +1701,28 @@ void parse_global_variable(int id, const char* name, int type) { } void parse_global_declaration() { + int is_const_int = 1; + if (token_type != TOKEN_CONST) { + is_const_int = 0; + } int type = parse_type(); if (type < 0) { eprintf("expecting type for global declaration\n"); exit(1); } + if (type != TYPE_INT) { + is_const_int = 0; + } expect_token(TOKEN_ID); int id = token_data; char* name = id_table + id_lut[id]; next_token(); - if (token_type == TOKEN_PAREN_LEFT) { + if (is_const_int && token_type == TOKEN_ASSIGN) { + expect_token(TOKEN_NUMBER); + const_table[id] = token_data; + is_const[id] = 1; + expect_token(TOKEN_SEMICOLON); + } else if (token_type == TOKEN_PAREN_LEFT) { declare_global(id, MARKER_FUNCTION, type); parse_function(name); } else {