diff options
| author | hachem <im@hachem.wtf> | 2026-08-31 17:55:16 +0200 |
|---|---|---|
| committer | hachem <im@hachem.wtf> | 2026-08-31 17:55:16 +0200 |
| commit | cbdb9073480c3e4d8a088b306c23406cf42e25c0 (patch) | |
| tree | 39f3093654cc8f27ea701ed1338cac84d93f42eb /src | |
| parent | 53aed3fadf1f7ad66dea9a8a0b46bf42eda921be (diff) | |
feat: hex/binary integer literals and constant-folding const expressions
Diffstat (limited to 'src')
| -rw-r--r-- | src/codegen/nasm.c | 44 | ||||
| -rw-r--r-- | src/lexer/lexer.c | 24 | ||||
| -rw-r--r-- | src/parser/ast.c | 2 | ||||
| -rw-r--r-- | src/parser/ast.h | 2 | ||||
| -rw-r--r-- | src/parser/parser.c | 6 | ||||
| -rw-r--r-- | src/sema/sema.c | 61 |
6 files changed, 132 insertions, 7 deletions
diff --git a/src/codegen/nasm.c b/src/codegen/nasm.c index 2f8de80..fef68a8 100644 --- a/src/codegen/nasm.c +++ b/src/codegen/nasm.c @@ -6,14 +6,31 @@ #include "codegen/nasm.h" +static void emit_const_expr(struct Expr* expr, FILE* out) +{ + switch (expr->kind) + { + case EXPR_PRIMARY: + fprintf(out, "%.*s", (int)expr->primary.token.length, expr->primary.token.start); + break; + case EXPR_BINARY: + emit_const_expr(expr->binary.left, out); + fprintf(out, " %.*s ", (int)expr->binary.op.length, expr->binary.op.start); + emit_const_expr(expr->binary.right, out); + break; + default: + break; + } +} + static void emit_consts(struct Program* program, FILE* out) { for (size_t i = 0; i < program->const_count; i += 1) { struct ConstDecl decl = program->consts[i]; - fprintf(out, "%%define %.*s %.*s\n", - (int)decl.name.length, decl.name.start, - (int)decl.value.length, decl.value.start); + fprintf(out, "%%define %.*s (", (int)decl.name.length, decl.name.start); + emit_const_expr(decl.value, out); + fprintf(out, ")\n"); } } @@ -125,6 +142,27 @@ static struct Token resolve_register(struct Emitter* emitter, struct Token token static uint64_t token_to_u64(struct Token token) { + if (token.length > 2 && token.start[0] == '0' && (token.start[1] == 'x' || token.start[1] == 'X')) + { + uint64_t value = 0; + for (size_t i = 2; i < token.length; i += 1) + { + char digit = token.start[i]; + uint64_t nibble = digit <= '9' ? (uint64_t)(digit - '0') + : (uint64_t)((digit | 0x20) - 'a' + 10); + value = value * 16 + nibble; + } + return value; + } + + if (token.length > 2 && token.start[0] == '0' && (token.start[1] == 'b' || token.start[1] == 'B')) + { + uint64_t value = 0; + for (size_t i = 2; i < token.length; i += 1) + value = value * 2 + (uint64_t)(token.start[i] - '0'); + return value; + } + uint64_t value = 0; for (size_t i = 0; i < token.length; i += 1) value = value * 10 + (uint64_t)(token.start[i] - '0'); diff --git a/src/lexer/lexer.c b/src/lexer/lexer.c index fb3fce7..23d0a82 100644 --- a/src/lexer/lexer.c +++ b/src/lexer/lexer.c @@ -15,6 +15,13 @@ static bool is_digit(char character) return character >= '0' && character <= '9'; } +static bool is_hex_digit(char character) +{ + return is_digit(character) + || (character >= 'a' && character <= 'f') + || (character >= 'A' && character <= 'F'); +} + static enum TokenType identifier_type(const char* start, size_t length) { static const struct Keyword @@ -177,8 +184,23 @@ struct Token scan_token(struct Lexer* lexer) if (is_digit(character)) { - while (is_digit(peek(lexer))) + if (character == '0' && (peek(lexer) == 'x' || peek(lexer) == 'X')) + { + advance(lexer); + while (is_hex_digit(peek(lexer))) + advance(lexer); + } + else if (character == '0' && (peek(lexer) == 'b' || peek(lexer) == 'B')) + { advance(lexer); + while (peek(lexer) == '0' || peek(lexer) == '1') + advance(lexer); + } + else + { + while (is_digit(peek(lexer))) + advance(lexer); + } return make_token(lexer, TOKEN_INTEGER, start); } diff --git a/src/parser/ast.c b/src/parser/ast.c index f9f857d..704e9a4 100644 --- a/src/parser/ast.c +++ b/src/parser/ast.c @@ -78,6 +78,8 @@ struct Program create_program(void) void free_program(struct Program* program) { + for (size_t i = 0; i < program->const_count; i += 1) + free_expr(program->consts[i].value); free(program->consts); free(program->data_decls); diff --git a/src/parser/ast.h b/src/parser/ast.h index fc7af2b..98e8dea 100644 --- a/src/parser/ast.h +++ b/src/parser/ast.h @@ -9,7 +9,7 @@ struct ConstDecl { struct Token name; - struct Token value; + struct Expr* value; }; struct DataDecl diff --git a/src/parser/parser.c b/src/parser/parser.c index 969ed86..87dedbd 100644 --- a/src/parser/parser.c +++ b/src/parser/parser.c @@ -52,6 +52,8 @@ static bool consume(struct Parser* parser, enum TokenType type, const char* mess return false; } +static struct Expr* parse_expression(struct Parser* parser); + static bool parse_const(struct Parser* parser, struct Program* program) { struct ConstDecl decl; @@ -63,9 +65,9 @@ static bool parse_const(struct Parser* parser, struct Program* program) if (!consume(parser, TOKEN_EQUAL, "expected '=' after constant name")) return false; - if (!consume(parser, TOKEN_INTEGER, "expected integer value after '='")) + decl.value = parse_expression(parser); + if (decl.value == NULL) return false; - decl.value = parser->previous; add_const(program, decl); return true; diff --git a/src/sema/sema.c b/src/sema/sema.c index fb53bbf..85907e9 100644 --- a/src/sema/sema.c +++ b/src/sema/sema.c @@ -81,6 +81,65 @@ static bool check_entry_point(struct Source source, struct Program* program) return false; } +static bool is_program_const(struct Program* program, struct Token name) +{ + for (size_t i = 0; i < program->const_count; i += 1) + if (names_equal(program->consts[i].name, name)) + return true; + + return false; +} + +static struct Token first_token(struct Expr* expr) +{ + switch (expr->kind) + { + case EXPR_BINARY: return first_token(expr->binary.left); + case EXPR_MEMBER: return first_token(expr->member.object); + case EXPR_DEREF: return first_token(expr->deref.address); + default: return expr->primary.token; + } +} + +static bool check_const_value(struct Source source, struct Program* program, struct Expr* expr) +{ + if (expr->kind == EXPR_BINARY) + { + bool left = check_const_value(source, program, expr->binary.left); + bool right = check_const_value(source, program, expr->binary.right); + return left && right; + } + + if (expr->kind == EXPR_PRIMARY) + { + struct Token token = expr->primary.token; + if (token.type == TOKEN_INTEGER || token.type == TOKEN_CHAR) + return true; + + if (token.type == TOKEN_IDENTIFIER && is_program_const(program, token)) + return true; + + char message[128]; + snprintf(message, sizeof(message), "'%.*s' is not a constant", + (int)token.length, token.start); + report_error(source, token, message); + return false; + } + + report_error(source, first_token(expr), "constant must be an integer expression"); + return false; +} + +static bool check_const_values(struct Source source, struct Program* program) +{ + bool ok = true; + for (size_t i = 0; i < program->const_count; i += 1) + if (!check_const_value(source, program, program->consts[i].value)) + ok = false; + + return ok; +} + struct RefCheck { struct Source source; @@ -335,6 +394,8 @@ bool analyze_program(struct Source source, struct Program* program) ok = false; if (!check_entry_point(source, program)) ok = false; + if (!check_const_values(source, program)) + ok = false; if (!check_references(source, program)) ok = false; |
