diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/ast.c | 1 | ||||
| -rw-r--r-- | src/ast.h | 5 | ||||
| -rw-r--r-- | src/codegen.c | 185 | ||||
| -rw-r--r-- | src/diag.c | 8 | ||||
| -rw-r--r-- | src/parser.c | 2 | ||||
| -rw-r--r-- | src/sema.c | 30 |
6 files changed, 60 insertions, 171 deletions
@@ -149,7 +149,6 @@ void free_program(struct Program* program) program->proc_capacity = 0; } -// TODO: generalize this growable-array boilerplate once a third list appears void add_const(struct Program* program, struct ConstDecl decl) { if (program->const_count == program->const_capacity) @@ -177,9 +177,6 @@ struct StackStatement struct Expr* size; }; -// a bare instruction: a mnemonic and its operands emitted verbatim, the escape -// hatch for anything outside the assignment / control-flow model (int, hlt, -// lgdt, in/out, ...) struct InstructionStatement { struct Token mnemonic; @@ -188,8 +185,6 @@ struct InstructionStatement size_t operand_capacity; }; -// a branchless conditional move: `target = if_value if left cmp right else -// else_value`. Lowers to csel on AArch64 and cmov on x86. struct SelectStatement { struct Token target; diff --git a/src/codegen.c b/src/codegen.c index c0e67be..b111fb8 100644 --- a/src/codegen.c +++ b/src/codegen.c @@ -32,11 +32,11 @@ static const char* assign_mnemonic(enum TokenType op) { switch (op) { - case TOKEN_EQUAL: return "mov"; + case TOKEN_EQUAL: return "mov"; case TOKEN_PLUS_EQUAL: return "add"; case TOKEN_MINUS_EQUAL: return "sub"; case TOKEN_STAR_EQUAL: return "imul"; - default: return NULL; + default: return NULL; } } @@ -60,11 +60,9 @@ static bool is_float_register(struct Token token) { if (token.length < 4 || memcmp(token.start, "xmm", 3) != 0) return false; - for (size_t i = 3; i < token.length; i += 1) if (token.start[i] < '0' || token.start[i] > '9') return false; - return true; } @@ -74,7 +72,6 @@ static size_t float_index(const struct FloatTable* floats, struct Token literal) if (floats->items[i].length == literal.length && memcmp(floats->items[i].start, literal.start, literal.length) == 0) return i; - return floats->count; } @@ -105,8 +102,6 @@ static struct Token text_token(const char* text) return token; } -// with the logical_registers extension, r1..r14 name the general-purpose -// registers; rsp/rbp and the instruction pointer keep their dedicated names. static const char* logical_register_base(struct Token token) { static const char* registers[] = { @@ -135,14 +130,12 @@ static const char* logical_register_base(struct Token token) static struct Token resolve_register(struct Emitter* emitter, struct Token token) { struct Token resolved = resolve_token(emitter, token); - if (emitter->program->config.logical_registers) { const char* base = logical_register_base(resolved); if (base != NULL) return text_token(base); } - return resolved; } @@ -320,8 +313,6 @@ static bool fold_member(struct Program* program, struct Expr* object, struct Tok return false; } -// evaluates a compile-time constant expression: integer/char literals, other -// constants, enum values and struct offsets, and + - * / over them static bool fold_const(struct Program* program, struct Expr* expr, uint64_t* out) { switch (expr->kind) @@ -379,8 +370,6 @@ static bool fold_const(struct Program* program, struct Expr* expr, uint64_t* out return false; } -// 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) { const struct EnumDecl* enumeration = find_enum(emitter->program, object); @@ -429,7 +418,6 @@ static bool emit_operand(struct Emitter* emitter, struct Expr* expr) } case EXPR_MEMBER: { - // a register size suffix: r1.64 -> rax, r1.8 -> al if (expr->member.member.type == TOKEN_INTEGER && expr->member.object->kind == EXPR_PRIMARY) { @@ -442,10 +430,8 @@ static bool emit_operand(struct Emitter* emitter, struct Expr* expr) fprintf(emitter->out, "%.*s", (int)base.length, base.start); 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)) + 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)) @@ -479,14 +465,9 @@ static bool is_gp_register(struct Token token) for (size_t i = 0; i < sizeof(names) / sizeof(names[0]); i += 1) if (token_matches(token, names[i])) return true; - return false; } -// idiv can divide by a general-purpose register directly, but not by rax or rdx -// (it overwrites both with the quotient and remainder) nor by an immediate (it -// has no immediate form). Those divisors are copied into r11 first — already a -// volatile register here, clobbered by syscall just like rcx. static bool divisor_uses_scratch(struct Emitter* emitter, const struct Expr* divisor) { if (divisor->kind == EXPR_PRIMARY) @@ -497,14 +478,9 @@ static bool divisor_uses_scratch(struct Emitter* emitter, const struct Expr* div return true; } - // enum/struct members and unary minus fold to immediates; a binary or deref - // divisor is left to the direct path (unsupported, as before) return divisor->kind == EXPR_MEMBER || divisor->kind == EXPR_UNARY; } -// idiv divides rdx:rax by its operand, leaving the quotient in rax and the -// remainder in rdx. The dividend arrives in dst; this loads it into rax, routes -// an awkward divisor through r11, and runs idiv. It clobbers rax, rdx and r11. static void emit_idiv(struct Emitter* emitter, const char* dst, struct Expr* divisor) { FILE* out = emitter->out; @@ -519,9 +495,8 @@ static void emit_idiv(struct Emitter* emitter, const char* dst, struct Expr* div return; } - // a divisor in rax must be saved before the dividend overwrites rax - bool divisor_in_rax = divisor->kind == EXPR_PRIMARY - && token_matches(resolve_register(emitter, divisor->primary.token), "rax"); + bool divisor_in_rax = divisor->kind == EXPR_PRIMARY && + token_matches(resolve_register(emitter, divisor->primary.token), "rax"); if (divisor_in_rax && strcmp(dst, "rax") != 0) { @@ -574,9 +549,6 @@ static void emit_divide(struct Emitter* emitter, struct AssignStatement* assign) emit_division(emitter, dst, assign->value); } -// an expression can be evaluated into a register when it is a single term -// (primary or member), or a left-associative chain of binary operators whose -// right operands are plain operands (never a buffer or a nested binary) static bool expr_supported(struct Emitter* emitter, struct Expr* expr) { switch (expr->kind) @@ -610,9 +582,6 @@ static bool expr_supported(struct Emitter* emitter, struct Expr* expr) return false; } -// dst = [address], extending narrower loads into the full register. Unsigned -// loads zero-extend (movzx / a 32-bit mov); signed loads sign-extend (movsx, -// or movsxd for a dword). static void emit_load(struct Emitter* emitter, const char* dst, struct DerefExpr* deref) { FILE* out = emitter->out; @@ -719,9 +688,6 @@ static const char* store_size_keyword(enum StoreSize size) } } -// maps a full 64-bit register to its byte/word/dword sub-register for a sized -// store, so `^byte rsi = rdx` writes `dl` rather than the whole register. -// returns NULL when the token is not a full register, or no resizing applies. static const char* sized_register(struct Token reg, enum StoreSize size) { if (size == STORE_SIZE_NONE || size == STORE_SIZE_QWORD) @@ -795,14 +761,10 @@ static bool value_is_float(struct Emitter* emitter, const struct Expr* expr) return is_float_register(resolve_register(emitter, expr->primary.token)); } -// floating point: xmm moves and arithmetic, conversions to/from general-purpose -// registers, and float literals loaded from their .data slot -static bool emit_float_assign(struct Emitter* emitter, struct AssignStatement* assign, - struct Token target, bool target_float) +static bool emit_float_assign(struct Emitter* emitter, struct AssignStatement* assign, struct Token target, bool target_float) { struct Expr* value = assign->value; - // float store: ^ptr = xmm -> movsd [ptr], xmm if (assign->target_deref) { if (assign->op.type != TOKEN_EQUAL || value->kind != EXPR_PRIMARY) @@ -815,7 +777,6 @@ static bool emit_float_assign(struct Emitter* emitter, struct AssignStatement* a return true; } - // float load: xmm = ^ptr -> movsd xmm, [ptr] if (value->kind == EXPR_DEREF) { if (!target_float || assign->op.type != TOKEN_EQUAL) @@ -896,7 +857,6 @@ static void emit_assign(struct Emitter* emitter, struct AssignStatement* assign) return; } - // deref store or compound assignment: needs a plain operand, not a buffer or binary const char* mnemonic = assign_mnemonic(assign->op.type); bool value_is_buffer = assign->value->kind == EXPR_PRIMARY && is_buffer_name(emitter, assign->value->primary.token); @@ -907,11 +867,11 @@ static void emit_assign(struct Emitter* emitter, struct AssignStatement* assign) return; } - // adding or subtracting a constant zero (e.g. a struct field at offset 0) is a no-op uint64_t folded; - if (!assign->target_deref - && (assign->op.type == TOKEN_PLUS_EQUAL || assign->op.type == TOKEN_MINUS_EQUAL) - && fold_const(emitter->program, assign->value, &folded) && folded == 0) + if (!assign->target_deref && + (assign->op.type == TOKEN_PLUS_EQUAL || assign->op.type == TOKEN_MINUS_EQUAL) && + fold_const(emitter->program, assign->value, &folded) && + folded == 0) return; if (assign->target_deref) @@ -993,15 +953,12 @@ static void emit_call(struct Emitter* emitter, struct CallStatement* call) } static void emit_statement(struct Emitter* emitter, struct Statement* statement); - static void emit_block(struct Emitter* emitter, struct Statement* body, size_t count) { for (size_t i = 0; i < count; i += 1) emit_statement(emitter, &body[i]); } -// ucomisd sets the flags like an unsigned compare, so float branches use the -// unsigned jump family (ja/jae/jb/jbe) rather than the signed one static const char* float_jump_if_false(enum TokenType comparison) { switch (comparison) @@ -1028,10 +985,6 @@ static void emit_float_operand(struct Emitter* emitter, const struct Expr* expr) fprintf(emitter->out, "%.*s", (int)token.length, token.start); } -// Emits the comparison for `left cmp right` and a jump to `target` taken when -// the condition is false, so the code that follows runs when it is true. Both -// if and while build on this. Returns false (after a TODO note) for a form that -// isn't supported yet. static bool emit_branch_test(struct Emitter* emitter, struct Expr* left, struct Token comparison, struct Expr* right, const char* target) { @@ -1127,9 +1080,6 @@ static void emit_while(struct Emitter* emitter, struct WhileStatement* loop) fprintf(emitter->out, "%s:\n", end); } -// renders a raw instruction's operand: registers, immediates, constants and -// members reuse emit_operand; memory (^x -> [x], with an optional size) and -// address math are handled here so operands like `^byte si` and `gdt + 2` work static void emit_instruction_operand(struct Emitter* emitter, struct Expr* operand) { switch (operand->kind) @@ -1162,7 +1112,6 @@ static void emit_instruction(struct Emitter* emitter, struct InstructionStatemen fprintf(emitter->out, "\n"); } -// the x86 condition-move suffix for a comparison, true or inverted static const char* cmov_cc(enum TokenType comparison, bool when_true) { switch (comparison) @@ -1177,23 +1126,21 @@ static const char* cmov_cc(enum TokenType comparison, bool when_true) } } -// target = if_value if L cmp R else else_value, branchlessly via cmov. The -// destination defaults to one source and conditionally takes the other, choosing -// which to default so the target can alias either source safely. static void emit_select(struct Emitter* emitter, struct SelectStatement* select) { FILE* out = emitter->out; - if (select->if_value->kind != EXPR_PRIMARY || select->else_value->kind != EXPR_PRIMARY - || cmov_cc(select->comparison.type, true) == NULL) + if (select->if_value->kind != EXPR_PRIMARY || + select->else_value->kind != EXPR_PRIMARY || + cmov_cc(select->comparison.type, true) == NULL) { fprintf(out, "\t; TODO: unsupported select\n"); return; } struct Token dst = resolve_register(emitter, select->target); - struct Token a = resolve_register(emitter, select->if_value->primary.token); - struct Token b = resolve_register(emitter, select->else_value->primary.token); - bool dst_is_a = tokens_equal(dst, a); + struct Token a = resolve_register(emitter, select->if_value->primary.token); + struct Token b = resolve_register(emitter, select->else_value->primary.token); + bool dst_is_a = tokens_equal(dst, a); if (!dst_is_a && !tokens_equal(dst, b)) fprintf(out, "\tmov %.*s, %.*s\n", (int)dst.length, dst.start, (int)b.length, b.start); @@ -1204,11 +1151,9 @@ static void emit_select(struct Emitter* emitter, struct SelectStatement* select) emit_operand(emitter, select->right); fprintf(out, "\n"); - // if the destination already holds if_value, keep it when the condition is - // false and move else_value in otherwise; otherwise the normal true form struct Token source = dst_is_a ? b : a; fprintf(out, "\tcmov%s %.*s, %.*s\n", cmov_cc(select->comparison.type, !dst_is_a), - (int)dst.length, dst.start, (int)source.length, source.start); + (int)dst.length, dst.start, (int)source.length, source.start); } static void emit_statement(struct Emitter* emitter, struct Statement* statement) @@ -1267,7 +1212,6 @@ static uint64_t proc_stack_size(struct Program* program, struct ProcDecl* proc) if (total % 16 != 0) total += 16 - (total % 16); - return total; } @@ -1346,17 +1290,16 @@ static struct FloatTable collect_floats(struct Program* program) for (size_t i = 0; i < program->proc_count; i += 1) for (size_t j = 0; j < program->procs[i].body_count; j += 1) collect_floats_statement(&floats, &program->procs[i].body[j]); - return floats; } static void emit_proc_x86(struct Program* program, struct FloatTable* floats, struct ProcDecl* proc, bool is_entry, FILE* out) { struct Emitter emitter; - emitter.program = program; - emitter.proc = proc; - emitter.floats = floats; - emitter.out = out; + emitter.program = program; + emitter.proc = proc; + emitter.floats = floats; + emitter.out = out; emitter.label_id = 0; fprintf(out, "%.*s:\n", (int)proc->name.length, proc->name.start); @@ -1380,19 +1323,12 @@ static void emit_proc_x86(struct Program* program, struct FloatTable* floats, st } } -// --------------------------------------------------------------------------- -// AArch64 target -// -// A separate instruction selector: the register model (logical rN -> xN-1, or -// native x0.., w0.., sp, lr), 3-operand arithmetic, ldr/str memory, cmp + b.cond -// control flow and svc #0 syscalls are all its own. Shares only the arch-neutral -// helpers above (fold_const, resolve_token, the AST). -// --------------------------------------------------------------------------- - static bool is_a64_register(struct Token token) { - // logical rN (mapped to xN-1) - if (token.length >= 2 && token.start[0] == 'r' && token.start[1] >= '0' && token.start[1] <= '9') + if (token.length >= 2 && + token.start[0] == 'r' && + token.start[1] >= '0' && + token.start[1] <= '9') { for (size_t i = 1; i < token.length; i += 1) if (token.start[i] < '0' || token.start[i] > '9') @@ -1400,14 +1336,18 @@ static bool is_a64_register(struct Token token) return true; } - // native names x0..x30 / w0..w30 - if ((token.start[0] == 'x' || token.start[0] == 'w') && token.length >= 2 - && token.start[1] >= '0' && token.start[1] <= '9') + if ((token.start[0] == 'x' || + token.start[0] == 'w') && + token.length >= 2 && + token.start[1] >= '0' && + token.start[1] <= '9') return true; - return token_matches(token, "sp") || token_matches(token, "lr") - || token_matches(token, "fp") || token_matches(token, "xzr") - || token_matches(token, "wzr"); + return token_matches(token, "sp") || + token_matches(token, "lr") || + token_matches(token, "fp") || + token_matches(token, "xzr") || + token_matches(token, "wzr"); } static void emit_a64_reg(struct Emitter* emitter, struct Token token) @@ -1424,8 +1364,6 @@ static void emit_a64_reg(struct Emitter* emitter, struct Token token) fprintf(emitter->out, "%.*s", (int)r.length, r.start); } -// an operand in register or immediate position: a register maps through, and -// anything that folds to a constant becomes an #immediate static void emit_a64_operand(struct Emitter* emitter, struct Expr* expr) { if (expr->kind == EXPR_PRIMARY && is_a64_register(resolve_token(emitter, expr->primary.token))) @@ -1459,7 +1397,6 @@ static const char* a64_binop(enum TokenType op) } } -// branch taken when the comparison is false (to skip the guarded body) static const char* a64_jump_if_false(enum TokenType comparison) { switch (comparison) @@ -1475,7 +1412,6 @@ static const char* a64_jump_if_false(enum TokenType comparison) } static void emit_a64_statement(struct Emitter* emitter, struct Statement* statement); - static void emit_a64_block(struct Emitter* emitter, struct Statement* body, size_t count) { for (size_t i = 0; i < count; i += 1) @@ -1486,7 +1422,6 @@ static void emit_a64_assign(struct Emitter* emitter, struct AssignStatement* ass { FILE* out = emitter->out; - // store through a pointer: ^[size] p = value if (assign->target_deref) { const char* store = assign->store_size == STORE_SIZE_BYTE ? "strb" @@ -1501,7 +1436,6 @@ static void emit_a64_assign(struct Emitter* emitter, struct AssignStatement* ass struct Expr* value = assign->value; - // load through a pointer: dst = ^[size] p if (assign->op.type == TOKEN_EQUAL && value->kind == EXPR_DEREF && value->deref.address->kind == EXPR_PRIMARY) { @@ -1515,7 +1449,6 @@ static void emit_a64_assign(struct Emitter* emitter, struct AssignStatement* ass return; } - // three-operand arithmetic: dst = a op b if (assign->op.type == TOKEN_EQUAL && value->kind == EXPR_BINARY) { const char* mnemonic = a64_binop(value->binary.op.type); @@ -1534,7 +1467,6 @@ static void emit_a64_assign(struct Emitter* emitter, struct AssignStatement* ass return; } - // compound assignment: dst op= value -> op dst, dst, value if (assign->op.type != TOKEN_EQUAL) { const char* mnemonic = a64_binop(assign->op.type); @@ -1553,7 +1485,6 @@ static void emit_a64_assign(struct Emitter* emitter, struct AssignStatement* ass return; } - // plain move: dst = <register | immediate | symbol/address> if (value->kind == EXPR_PRIMARY && is_a64_register(resolve_token(emitter, value->primary.token))) { fprintf(out, "\tmov "); @@ -1573,7 +1504,6 @@ static void emit_a64_assign(struct Emitter* emitter, struct AssignStatement* ass return; } - // a data label or other symbol: load its address/value through the pool if (value->kind == EXPR_PRIMARY) { fprintf(out, "\tldr "); @@ -1693,7 +1623,6 @@ static void emit_a64_instruction(struct Emitter* emitter, struct InstructionStat fprintf(emitter->out, "\n"); } -// the AArch64 condition code for a true comparison (used by csel) static const char* a64_cond(enum TokenType comparison) { switch (comparison) @@ -1708,8 +1637,6 @@ static const char* a64_cond(enum TokenType comparison) } } -// target = if_value if L cmp R else else_value -> cmp; csel (one instruction, -// no aliasing hazard) static void emit_a64_select(struct Emitter* emitter, struct SelectStatement* select) { FILE* out = emitter->out; @@ -1790,23 +1717,16 @@ static void emit_proc_aarch64(struct Program* program, struct FloatTable* floats fprintf(out, "\tret\n"); } -// Instruction selection lives behind the Arch seam: turning a procedure's -// statements into a target's instructions (register model, mnemonics, stack -// frames) is all an architecture decides. The Backend below is the orthogonal -// axis — the assembler *syntax* (framing, data, labels) for a given arch. struct Arch { - void (*emit_proc)(struct Program* program, struct FloatTable* floats, - struct ProcDecl* proc, bool is_entry, FILE* out); + void (*emit_proc)(struct Program* program, + struct FloatTable* floats, + struct ProcDecl* proc, + bool is_entry, FILE* out); }; -static const struct Arch x86_arch = { - emit_proc_x86, -}; - -static const struct Arch aarch64_arch = { - emit_proc_aarch64, -}; +static const struct Arch x86_arch = { emit_proc_x86, }; +static const struct Arch aarch64_arch = { emit_proc_aarch64, }; struct Backend { @@ -1842,8 +1762,6 @@ static void nasm_data_section(FILE* out) static void nasm_string_data(struct DataDecl decl, FILE* out) { - // the value lexeme keeps its quotes; NASM backtick strings interpret the - // same escapes, so re-wrap the inner content fprintf(out, "%.*s: db `%.*s`\n", (int)decl.name.length, decl.name.start, (int)(decl.value.length - 2), decl.value.start + 1); @@ -1865,7 +1783,6 @@ static void nasm_global(struct Token name, FILE* out) fprintf(out, "global %.*s\n", (int)name.length, name.start); } -// pad to 510 bytes and append the 0x55AA boot signature (little-endian dw) static void nasm_boot_signature(FILE* out) { fprintf(out, "times 510-($-$$) db 0\n"); @@ -1894,9 +1811,7 @@ static void fasm_prologue(const struct Program* program, FILE* out) fprintf(out, "use%u\n", config.bits); } else - { fprintf(out, "format ELF%s\n", config.bits == 64 ? "64" : ""); - } } static void fasm_constant(struct ConstDecl decl, FILE* out) @@ -1911,8 +1826,6 @@ static void fasm_data_section(FILE* out) fprintf(out, "section '.data' writeable\n"); } -// fasm string literals are taken verbatim, so the escapes NASM would interpret -// are expanded here into the byte values fasm expects (db "run", 10, "run"). static void fasm_string_data(struct DataDecl decl, FILE* out) { fprintf(out, "%.*s db ", (int)decl.name.length, decl.name.start); @@ -2002,8 +1915,6 @@ static const struct Backend fasm_backend = { fasm_boot_signature, }; -// GNU as (the assembler for the ARM targets): different directives from the -// Intel-syntax assemblers, but the same framing shape. static void gas_prologue(const struct Program* program, FILE* out) { (void)program; @@ -2024,8 +1935,6 @@ static void gas_data_section(FILE* out) static void gas_string_data(struct DataDecl decl, FILE* out) { - // GNU as .ascii interprets the same C escapes NASM's backtick strings do, - // so the inner content passes through unchanged (no trailing NUL, matching) fprintf(out, "%.*s: .ascii \"%.*s\"\n", (int)decl.name.length, decl.name.start, (int)(decl.value.length - 2), decl.value.start + 1); @@ -2051,7 +1960,6 @@ static void gas_global(struct Token name, FILE* out) static void gas_boot_signature(FILE* out) { - // boot sectors are an x86/BIOS concept; not meaningful for the ARM targets (void)out; } @@ -2066,22 +1974,18 @@ static const struct Backend gas_backend = { gas_boot_signature, }; -// the entry procedure drops its trailing `ret`. It is the [entry: NAME] proc if -// given; otherwise a flat binary starts at its first proc. static bool proc_is_entry(struct Program* program, size_t index) { struct Config config = program->config; struct ProcDecl* proc = &program->procs[index]; if (config.has_entry) - return proc->name.length == config.entry.length - && memcmp(proc->name.start, config.entry.start, proc->name.length) == 0; - + return proc->name.length == config.entry.length && memcmp(proc->name.start, config.entry.start, proc->name.length) == 0; return config.format == OUTPUT_BIN && index == 0; } static void emit_data_block(struct Program* program, struct FloatTable* floats, - const struct Backend* backend, FILE* out) + const struct Backend* backend, FILE* out) { for (size_t i = 0; i < program->data_count; i += 1) backend->string_data(program->data_decls[i], out); @@ -2105,7 +2009,6 @@ static void generate(struct Program* program, FILE* out, const struct Arch* arch if (program->config.format == OUTPUT_BIN) { - // a flat binary executes from its origin, so code comes first, then data for (size_t i = 0; i < program->proc_count; i += 1) { if (i > 0) @@ -30,10 +30,10 @@ void report_error(struct Source source, struct Token token, const char* message) if (span == 0) span = 1; - const char* bold = colored() ? "\033[1m" : ""; + const char* bold = colored() ? "\033[1m" : ""; const char* red = colored() ? "\033[31m" : ""; const char* blue = colored() ? "\033[34m" : ""; - const char* reset = colored() ? "\033[0m" : ""; + const char* reset = colored() ? "\033[0m" : ""; int gutter = snprintf(NULL, 0, "%u", token.line); @@ -55,9 +55,9 @@ void report_error(struct Source source, struct Token token, const char* message) void report_error_message(const char* message) { - const char* bold = colored() ? "\033[1m" : ""; + const char* bold = colored() ? "\033[1m" : ""; const char* red = colored() ? "\033[31m" : ""; - const char* reset = colored() ? "\033[0m" : ""; + const char* reset = colored() ? "\033[0m" : ""; fprintf(stderr, "%s%serror:%s %s%s%s\n", bold, red, reset, bold, message, reset); } diff --git a/src/parser.c b/src/parser.c index b0e049c..1815562 100644 --- a/src/parser.c +++ b/src/parser.c @@ -279,8 +279,6 @@ static struct Expr* parse_postfix(struct Parser* parser) while (match_token(parser, TOKEN_DOT)) { - // an identifier is a member (data.len); an integer is a register size - // suffix (r1.64), meaningful with the logical_registers extension if (!check(parser, TOKEN_IDENTIFIER) && !check(parser, TOKEN_INTEGER)) { error_at(parser, parser->current, "expected a member name or size after '.'"); @@ -180,14 +180,14 @@ static void ref_error(struct RefCheck* check, struct Token token, const char* fo static bool is_arch_register(struct Token token) { static const char* names[] = { - "rax", "eax", "ax", "al", "ah", - "rbx", "ebx", "bx", "bl", "bh", - "rcx", "ecx", "cx", "cl", "ch", - "rdx", "edx", "dx", "dl", "dh", - "rsi", "esi", "si", "sil", - "rdi", "edi", "di", "dil", - "rbp", "ebp", "bp", "bpl", - "rsp", "esp", "sp", "spl", + "rax", "eax", "ax", "al", "ah", + "rbx", "ebx", "bx", "bl", "bh", + "rcx", "ecx", "cx", "cl", "ch", + "rdx", "edx", "dx", "dl", "dh", + "rsi", "esi", "si", "sil", + "rdi", "edi", "di", "dil", + "rbp", "ebp", "bp", "bpl", + "rsp", "esp", "sp", "spl", "r8", "r8d", "r8w", "r8b", "r9", "r9d", "r9w", "r9b", "r10", "r10d", "r10w", "r10b", @@ -411,8 +411,6 @@ static void check_target(struct RefCheck* check, struct Token target) ref_error(check, target, "cannot assign to '%.*s': not a register", (int)target.length, target.start); } -// a stack size must be a compile-time constant: integer/char literals, other -// constants, enum values, struct sizes/offsets, and arithmetic over them static void check_stack_size(struct RefCheck* check, struct Expr* expr) { switch (expr->kind) @@ -427,9 +425,9 @@ static void check_stack_size(struct RefCheck* check, struct Expr* expr) case EXPR_MEMBER: { const struct Expr* object = expr->member.object; - if (object->kind == EXPR_PRIMARY - && (find_enum(check, object->primary.token) != NULL - || find_struct(check, object->primary.token) != NULL)) + if (object->kind == EXPR_PRIMARY && + (find_enum(check, object->primary.token) != NULL || + find_struct(check, object->primary.token) != NULL)) check_expr(check, expr); else ref_error(check, first_token(expr), "stack size must be a constant"); @@ -500,9 +498,7 @@ static void check_statement(struct RefCheck* check, struct Statement* statement) case STATEMENT_SYSCALL: break; case STATEMENT_INSTRUCTION: - // a raw instruction is an escape hatch; its operands may name asm - // symbols or registers hdass does not model, so leave them to the - // assembler rather than flagging them as undefined + // escape hatch break; case STATEMENT_SELECT: check_target(check, statement->select.target); @@ -533,7 +529,6 @@ static bool check_references(struct Source source, struct Program* program) bool analyze_program(struct Source source, struct Program* program) { bool ok = true; - if (!check_duplicate_names(source, program)) ok = false; if (!check_entry_point(source, program)) @@ -542,6 +537,5 @@ bool analyze_program(struct Source source, struct Program* program) ok = false; if (!check_references(source, program)) ok = false; - return ok; } |
