diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/codegen/nasm.c | 83 | ||||
| -rw-r--r-- | src/lexer/lexer.c | 4 | ||||
| -rw-r--r-- | src/lexer/lexer.h | 2 | ||||
| -rw-r--r-- | src/parser/ast.c | 90 | ||||
| -rw-r--r-- | src/parser/ast.h | 54 | ||||
| -rw-r--r-- | src/parser/parser.c | 96 | ||||
| -rw-r--r-- | src/sema/sema.c | 76 |
7 files changed, 390 insertions, 15 deletions
diff --git a/src/codegen/nasm.c b/src/codegen/nasm.c index fef68a8..9c1a178 100644 --- a/src/codegen/nasm.c +++ b/src/codegen/nasm.c @@ -209,6 +209,84 @@ static enum StoreSize size_from_int(struct Token token) } } +static bool tokens_equal(struct Token a, struct Token b) +{ + return a.length == b.length && memcmp(a.start, b.start, a.length) == 0; +} + +static bool token_matches(struct Token token, const char* text) +{ + size_t length = strlen(text); + return token.length == length && memcmp(token.start, text, length) == 0; +} + +static struct EnumDecl* find_enum(struct Program* program, struct Token name) +{ + for (size_t i = 0; i < program->enum_count; i += 1) + if (tokens_equal(program->enums[i].name, name)) + return &program->enums[i]; + + return NULL; +} + +static struct StructDecl* find_struct(struct Program* program, struct Token name) +{ + for (size_t i = 0; i < program->struct_count; i += 1) + if (tokens_equal(program->structs[i].name, name)) + return &program->structs[i]; + + return NULL; +} + +static uint64_t store_size_bytes(enum StoreSize size) +{ + switch (size) + { + case STORE_SIZE_BYTE: return 1; + case STORE_SIZE_WORD: return 2; + case STORE_SIZE_DWORD: return 4; + default: return 8; + } +} + +// an enum member folds to its 0-based index; a struct member folds to its byte +// offset (or the total size for `.size`) +static bool emit_named_member(struct Emitter* emitter, struct Token object, struct Token member) +{ + struct EnumDecl* enumeration = find_enum(emitter->program, object); + if (enumeration != NULL) + { + for (size_t i = 0; i < enumeration->member_count; i += 1) + if (tokens_equal(enumeration->members[i], member)) + { + fprintf(emitter->out, "%zu", i); + return true; + } + } + + struct StructDecl* layout = find_struct(emitter->program, object); + if (layout != NULL) + { + uint64_t offset = 0; + for (size_t i = 0; i < layout->field_count; i += 1) + { + if (tokens_equal(layout->fields[i].name, member)) + { + fprintf(emitter->out, "%llu", (unsigned long long)offset); + return true; + } + offset += store_size_bytes(layout->fields[i].size); + } + if (token_matches(member, "size")) + { + fprintf(emitter->out, "%llu", (unsigned long long)offset); + return true; + } + } + + return false; +} + static bool emit_operand(struct Emitter* emitter, struct Expr* expr) { switch (expr->kind) @@ -235,6 +313,11 @@ static bool emit_operand(struct Emitter* emitter, struct Expr* expr) return true; } + // enum value or struct offset + if (expr->member.object->kind == EXPR_PRIMARY + && emit_named_member(emitter, expr->member.object->primary.token, expr->member.member)) + return true; + if (!emit_operand(emitter, expr->member.object)) return false; fprintf(emitter->out, ".%.*s", (int)expr->member.member.length, expr->member.member.start); diff --git a/src/lexer/lexer.c b/src/lexer/lexer.c index 23d0a82..22a671c 100644 --- a/src/lexer/lexer.c +++ b/src/lexer/lexer.c @@ -34,6 +34,8 @@ static enum TokenType identifier_type(const char* start, size_t length) { "const", 5, TOKEN_CONST }, { "data", 4, TOKEN_DATA }, { "proc", 4, TOKEN_PROC }, + { "enum", 4, TOKEN_ENUM }, + { "struct", 6, TOKEN_STRUCT }, { "stack", 5, TOKEN_STACK }, { "if", 2, TOKEN_IF }, { "goto", 4, TOKEN_GOTO }, @@ -247,6 +249,8 @@ const char* token_type_name(enum TokenType type) case TOKEN_CONST: return "const"; case TOKEN_DATA: return "data"; case TOKEN_PROC: return "proc"; + case TOKEN_ENUM: return "enum"; + case TOKEN_STRUCT: return "struct"; case TOKEN_STACK: return "stack"; case TOKEN_IF: return "if"; case TOKEN_GOTO: return "goto"; diff --git a/src/lexer/lexer.h b/src/lexer/lexer.h index afe6e7f..3bc215e 100644 --- a/src/lexer/lexer.h +++ b/src/lexer/lexer.h @@ -14,6 +14,8 @@ enum TokenType TOKEN_CONST, TOKEN_DATA, TOKEN_PROC, + TOKEN_ENUM, + TOKEN_STRUCT, TOKEN_STACK, TOKEN_IF, TOKEN_GOTO, diff --git a/src/parser/ast.c b/src/parser/ast.c index 704e9a4..f8bd9cc 100644 --- a/src/parser/ast.c +++ b/src/parser/ast.c @@ -70,6 +70,12 @@ struct Program create_program(void) program.data_decls = NULL; program.data_count = 0; program.data_capacity = 0; + program.enums = NULL; + program.enum_count = 0; + program.enum_capacity = 0; + program.structs = NULL; + program.struct_count = 0; + program.struct_capacity = 0; program.procs = NULL; program.proc_count = 0; program.proc_capacity = 0; @@ -83,6 +89,14 @@ void free_program(struct Program* program) free(program->consts); free(program->data_decls); + for (size_t i = 0; i < program->enum_count; i += 1) + free(program->enums[i].members); + free(program->enums); + + for (size_t i = 0; i < program->struct_count; i += 1) + free(program->structs[i].fields); + free(program->structs); + for (size_t i = 0; i < program->proc_count; i += 1) free_proc(&program->procs[i]); free(program->procs); @@ -93,6 +107,12 @@ void free_program(struct Program* program) program->data_decls = NULL; program->data_count = 0; program->data_capacity = 0; + program->enums = NULL; + program->enum_count = 0; + program->enum_capacity = 0; + program->structs = NULL; + program->struct_count = 0; + program->struct_capacity = 0; program->procs = NULL; program->proc_count = 0; program->proc_capacity = 0; @@ -125,6 +145,76 @@ void add_data(struct Program* program, struct DataDecl decl) program->data_count += 1; } +struct EnumDecl create_enum(void) +{ + struct EnumDecl decl; + decl.members = NULL; + decl.member_count = 0; + decl.member_capacity = 0; + return decl; +} + +void add_enum_member(struct EnumDecl* decl, struct Token member) +{ + if (decl->member_count == decl->member_capacity) + { + size_t capacity = decl->member_capacity < 8 ? 8 : decl->member_capacity * 2; + decl->members = realloc(decl->members, capacity * sizeof(struct Token)); + decl->member_capacity = capacity; + } + + decl->members[decl->member_count] = member; + decl->member_count += 1; +} + +void add_enum(struct Program* program, struct EnumDecl decl) +{ + if (program->enum_count == program->enum_capacity) + { + size_t capacity = program->enum_capacity < 8 ? 8 : program->enum_capacity * 2; + program->enums = realloc(program->enums, capacity * sizeof(struct EnumDecl)); + program->enum_capacity = capacity; + } + + program->enums[program->enum_count] = decl; + program->enum_count += 1; +} + +struct StructDecl create_struct(void) +{ + struct StructDecl decl; + decl.fields = NULL; + decl.field_count = 0; + decl.field_capacity = 0; + return decl; +} + +void add_struct_field(struct StructDecl* decl, struct StructField field) +{ + if (decl->field_count == decl->field_capacity) + { + size_t capacity = decl->field_capacity < 8 ? 8 : decl->field_capacity * 2; + decl->fields = realloc(decl->fields, capacity * sizeof(struct StructField)); + decl->field_capacity = capacity; + } + + decl->fields[decl->field_count] = field; + decl->field_count += 1; +} + +void add_struct(struct Program* program, struct StructDecl decl) +{ + if (program->struct_count == program->struct_capacity) + { + size_t capacity = program->struct_capacity < 8 ? 8 : program->struct_capacity * 2; + program->structs = realloc(program->structs, capacity * sizeof(struct StructDecl)); + program->struct_capacity = capacity; + } + + program->structs[program->struct_count] = decl; + program->struct_count += 1; +} + struct ProcDecl create_proc(void) { struct ProcDecl proc; diff --git a/src/parser/ast.h b/src/parser/ast.h index 98e8dea..b114fb1 100644 --- a/src/parser/ast.h +++ b/src/parser/ast.h @@ -6,6 +6,15 @@ #include "lexer/lexer.h" +enum StoreSize +{ + STORE_SIZE_NONE, + STORE_SIZE_BYTE, + STORE_SIZE_WORD, + STORE_SIZE_DWORD, + STORE_SIZE_QWORD, +}; + struct ConstDecl { struct Token name; @@ -18,19 +27,32 @@ struct DataDecl struct Token value; }; -struct Param +struct EnumDecl { struct Token name; - struct Token reg; + struct Token* members; + size_t member_count; + size_t member_capacity; }; -enum StoreSize +struct StructField { - STORE_SIZE_NONE, - STORE_SIZE_BYTE, - STORE_SIZE_WORD, - STORE_SIZE_DWORD, - STORE_SIZE_QWORD, + struct Token name; + enum StoreSize size; +}; + +struct StructDecl +{ + struct Token name; + struct StructField* fields; + size_t field_count; + size_t field_capacity; +}; + +struct Param +{ + struct Token name; + struct Token reg; }; enum ExprKind @@ -175,6 +197,14 @@ struct Program size_t data_count; size_t data_capacity; + struct EnumDecl* enums; + size_t enum_count; + size_t enum_capacity; + + struct StructDecl* structs; + size_t struct_count; + size_t struct_capacity; + struct ProcDecl* procs; size_t proc_count; size_t proc_capacity; @@ -185,6 +215,14 @@ void free_program(struct Program* program); void add_const(struct Program* program, struct ConstDecl decl); void add_data(struct Program* program, struct DataDecl decl); +struct EnumDecl create_enum(void); +void add_enum_member(struct EnumDecl* decl, struct Token member); +void add_enum(struct Program* program, struct EnumDecl decl); + +struct StructDecl create_struct(void); +void add_struct_field(struct StructDecl* decl, struct StructField field); +void add_struct(struct Program* program, struct StructDecl decl); + struct ProcDecl create_proc(void); void free_proc(struct ProcDecl* proc); void add_param(struct ProcDecl* proc, struct Param param); diff --git a/src/parser/parser.c b/src/parser/parser.c index 87dedbd..682e193 100644 --- a/src/parser/parser.c +++ b/src/parser/parser.c @@ -53,6 +53,7 @@ static bool consume(struct Parser* parser, enum TokenType type, const char* mess } static struct Expr* parse_expression(struct Parser* parser); +static enum StoreSize parse_store_size(struct Parser* parser); static bool parse_const(struct Parser* parser, struct Program* program) { @@ -92,6 +93,89 @@ static bool parse_data(struct Parser* parser, struct Program* program) return true; } +static bool parse_enum(struct Parser* parser, struct Program* program) +{ + struct EnumDecl decl = create_enum(); + + if (!consume(parser, TOKEN_IDENTIFIER, "expected enum name after 'enum'")) + goto error; + decl.name = parser->previous; + + if (!consume(parser, TOKEN_LEFT_BRACE, "expected '{' after enum name")) + goto error; + + while (!check(parser, TOKEN_RIGHT_BRACE)) + { + if (check(parser, TOKEN_EOF)) + { + error_at(parser, parser->current, "unterminated enum"); + goto error; + } + + if (!consume(parser, TOKEN_IDENTIFIER, "expected an enum member name")) + goto error; + add_enum_member(&decl, parser->previous); + + match_token(parser, TOKEN_COMMA); + } + advance_parser(parser); + + add_enum(program, decl); + return true; + +error: + free(decl.members); + return false; +} + +static bool parse_struct(struct Parser* parser, struct Program* program) +{ + struct StructDecl decl = create_struct(); + + if (!consume(parser, TOKEN_IDENTIFIER, "expected struct name after 'struct'")) + goto error; + decl.name = parser->previous; + + if (!consume(parser, TOKEN_LEFT_BRACE, "expected '{' after struct name")) + goto error; + + while (!check(parser, TOKEN_RIGHT_BRACE)) + { + if (check(parser, TOKEN_EOF)) + { + error_at(parser, parser->current, "unterminated struct"); + goto error; + } + + struct StructField field; + if (!consume(parser, TOKEN_IDENTIFIER, "expected a field name")) + goto error; + field.name = parser->previous; + + field.size = STORE_SIZE_QWORD; + if (match_token(parser, TOKEN_COLON)) + { + field.size = parse_store_size(parser); + if (field.size == STORE_SIZE_NONE) + { + error_at(parser, parser->current, "expected a size (byte, word, dword, qword) after ':'"); + goto error; + } + } + + add_struct_field(&decl, field); + match_token(parser, TOKEN_COMMA); + } + advance_parser(parser); + + add_struct(program, decl); + return true; + +error: + free(decl.fields); + return false; +} + static bool parse_params(struct Parser* parser, struct ProcDecl* proc) { if (check(parser, TOKEN_RIGHT_PAREN)) @@ -555,6 +639,18 @@ bool parse_program(struct Lexer* lexer, struct Program* out) if (!parse_data(&parser, out)) return false; } + else if (check(&parser, TOKEN_ENUM)) + { + advance_parser(&parser); + if (!parse_enum(&parser, out)) + return false; + } + else if (check(&parser, TOKEN_STRUCT)) + { + advance_parser(&parser); + if (!parse_struct(&parser, out)) + return false; + } else if (check(&parser, TOKEN_PROC)) { advance_parser(&parser); diff --git a/src/sema/sema.c b/src/sema/sema.c index 85907e9..d2b4668 100644 --- a/src/sema/sema.c +++ b/src/sema/sema.c @@ -27,7 +27,8 @@ static struct ProcDecl* find_proc(struct Program* program, struct Token name) static bool check_duplicate_names(struct Source source, struct Program* program) { - size_t count = program->const_count + program->data_count + program->proc_count; + size_t count = program->const_count + program->data_count + + program->enum_count + program->struct_count + program->proc_count; if (count == 0) return true; @@ -43,6 +44,16 @@ static bool check_duplicate_names(struct Source source, struct Program* program) names[n] = program->data_decls[i].name; n += 1; } + for (size_t i = 0; i < program->enum_count; i += 1) + { + names[n] = program->enums[i].name; + n += 1; + } + for (size_t i = 0; i < program->struct_count; i += 1) + { + names[n] = program->structs[i].name; + n += 1; + } for (size_t i = 0; i < program->proc_count; i += 1) { names[n] = program->procs[i].name; @@ -241,6 +252,24 @@ static bool is_data(struct RefCheck* check, struct Token token) return false; } +static struct EnumDecl* find_enum(struct RefCheck* check, struct Token token) +{ + for (size_t i = 0; i < check->program->enum_count; i += 1) + if (names_equal(check->program->enums[i].name, token)) + return &check->program->enums[i]; + + return NULL; +} + +static struct StructDecl* find_struct(struct RefCheck* check, struct Token token) +{ + for (size_t i = 0; i < check->program->struct_count; i += 1) + if (names_equal(check->program->structs[i].name, token)) + return &check->program->structs[i]; + + return NULL; +} + static bool is_stack_buffer(struct RefCheck* check, struct Token token) { for (size_t i = 0; i < check->proc->body_count; i += 1) @@ -308,15 +337,48 @@ static void check_expr(struct RefCheck* check, struct Expr* expr) { if (object->kind != EXPR_PRIMARY || !is_register(check, object->primary.token)) ref_error(check, member, "size suffix requires a register"); + break; } - else if (object->kind != EXPR_PRIMARY || !is_data(check, object->primary.token)) - { - check_expr(check, object); - } - else if (!token_is(member, "len")) + + if (object->kind == EXPR_PRIMARY) { - ref_error(check, member, "unknown member '%.*s'", (int)member.length, member.start); + struct EnumDecl* enumeration = find_enum(check, object->primary.token); + if (enumeration != NULL) + { + bool found = false; + for (size_t i = 0; i < enumeration->member_count; i += 1) + if (names_equal(enumeration->members[i], member)) + found = true; + if (!found) + ref_error(check, member, "enum '%.*s' has no member '%.*s'", + (int)object->primary.token.length, object->primary.token.start, + (int)member.length, member.start); + break; + } + + struct StructDecl* layout = find_struct(check, object->primary.token); + if (layout != NULL) + { + bool found = token_is(member, "size"); + for (size_t i = 0; i < layout->field_count; i += 1) + if (names_equal(layout->fields[i].name, member)) + found = true; + if (!found) + ref_error(check, member, "struct '%.*s' has no field '%.*s'", + (int)object->primary.token.length, object->primary.token.start, + (int)member.length, member.start); + break; + } + + if (is_data(check, object->primary.token)) + { + if (!token_is(member, "len")) + ref_error(check, member, "unknown member '%.*s'", (int)member.length, member.start); + break; + } } + + check_expr(check, object); break; } } |
