typedef enum Operator { // Arithmetic ops. OP_ADD, OP_SUB, OP_MUL, OP_DIV, OP_MOD, // Load/store/copy operations. OP_LD8, OP_LD16, OP_LD32, OP_LD64, OP_ST8, OP_ST16, OP_ST32, OP_ST64, OP_CP8, OP_CP16, OP_CP32, OP_CP64, // Bit fiddling operations. OP_NOT, OP_AND, OP_OR, OP_XOR, OP_LSHIFT, OP_RSHIFT, OP_LROT, OP_RROT, // (Un)conditional jump operations. OP_LABEL, OP_JMP, OP_JMP_EQ, OP_JMP_NEQ, OP_JMP_GT, OP_JMP_LT, OP_JMP_GE, OP_JMP_LE, } Operator; typedef enum OperandType { OP_TYPE_REG, OP_TYPE_CONST, OP_TYPE_LABEL, } OperandType; static size_t reg_gen_id = 0; static size_t lab_gen_id = 0; #define NEW_REG() (Operand){ .type = OP_TYPE_REG, .id = reg_gen_id++ } #define NEW_LAB() (Operand){ .type = OP_TYPE_LABEL, .id = lab_gen_id++ } #define NEW_S64(C) (Operand){ .type = OP_TYPE_CONST, .constant.sval = (C) } #define EMIT_0(PROGRAM, LINE, OP, DST) do { \ Instruction inst = (Instruction){ \ .op = (OP), \ .dst = (DST), \ }; \ array_push((PROGRAM)->inst, inst); \ array_push((PROGRAM)->lines, (LINE)); \ } while(false); #define EMIT_1(PROGRAM, LINE, OP, DST, A) do { \ Instruction inst = (Instruction){ \ .op = (OP), \ .dst = (DST), \ .src_a = (A), \ }; \ array_push((PROGRAM)->inst, inst); \ array_push((PROGRAM)->lines, (LINE)); \ } while(false); #define EMIT_2(PROGRAM, LINE, OP, DST, A, B) do { \ Instruction inst = (Instruction){ \ .op = (OP), \ .dst = (DST), \ .src_a = (A), \ .src_b = (B), \ }; \ array_push((PROGRAM)->inst, inst); \ array_push((PROGRAM)->lines, (LINE)); \ } while(false); typedef struct Operand { OperandType type; union { // REG/LABEL size_t id; // s64 constant; struct { union { u64 uval; s64 sval; }; } constant; }; } Operand; typedef struct Instruction { Operator op; Operand dst; Operand src_a; Operand src_b; } Instruction; typedef struct LineInfo { size_t line; size_t col; } LineInfo; typedef struct ProgramBASM { Instruction *inst; LineInfo *lines; } ProgramBASM; Operand emit_basm(ProgramBASM *program, Node *node); Operand emit_arith(ProgramBASM *program, Node *node, Operator op) { LineInfo line = (LineInfo){ .line = node->line, .col = node->col, }; Operand reg_a = emit_basm(program, node->builtin.args[0]); Operand reg_b; for (size_t i = 1; i < array_size(node->builtin.args); ++i) { Node *arg = node->builtin.args[i]; Operand reg_dst = NEW_REG(); reg_b = emit_basm(program, arg); EMIT_2(program, line, op, reg_dst, reg_a, reg_b); reg_a = reg_dst; } return reg_a; } Operand emit_numcomp(ProgramBASM *program, Node *node, Operator op) { LineInfo line = (LineInfo){ .line = node->line, .col = node->col, }; Operand label_false = NEW_LAB(); Operand label_end = NEW_LAB(); Operand reg_a = emit_basm(program, node->builtin.args[0]); Operand reg_b; for (size_t i = 1; i < array_size(node->builtin.args); ++i) { Node *arg = node->builtin.args[i]; reg_b = emit_basm(program, arg); EMIT_2(program, line, op, label_false, reg_a, reg_b); reg_a = reg_b; } Operand reg_out = NEW_REG(); EMIT_1(program, line, OP_LD8, reg_out, NEW_S64(1)); EMIT_0(program, line, OP_JMP, label_end); EMIT_0(program, line, OP_LABEL, label_false); EMIT_1(program, line, OP_LD8, reg_out, NEW_S64(0)); EMIT_0(program, line, OP_LABEL, label_end); return reg_out; } Operand emit_builtin(ProgramBASM *program, Node *node) { switch (node->builtin.type) { case TOKEN_ADD: { return emit_arith(program, node, OP_ADD); } break; case TOKEN_SUB: { return emit_arith(program, node, OP_SUB); } break; case TOKEN_MUL: { return emit_arith(program, node, OP_MUL); } break; case TOKEN_DIV: { return emit_arith(program, node, OP_DIV); } break; case TOKEN_MOD: { return emit_arith(program, node, OP_MOD); } break; case TOKEN_EQ: { return emit_numcomp(program, node, OP_JMP_NEQ); } break; case TOKEN_LT: { return emit_numcomp(program, node, OP_JMP_GE); } break; case TOKEN_GT: { return emit_numcomp(program, node, OP_JMP_LE); } break; case TOKEN_LE: { return emit_numcomp(program, node, OP_JMP_GT); } break; case TOKEN_GE: { return emit_numcomp(program, node, OP_JMP_LT); } break; default: { push_error(ERR_TYPE_BASM, ERR_UNIMPLEMENTED, node->line, node->col); return (Operand){0}; } break; } } Operand emit_number(ProgramBASM *program, Node *node) { // TODO: ldX depending on type of number. LineInfo line = (LineInfo){.line = node->line, .col = node->col}; Operand reg_dst = NEW_REG(); Operand num = NEW_S64(node->number.integral); EMIT_1(program, line, OP_LD64, reg_dst, num); return reg_dst; } Operand emit_bool(ProgramBASM *program, Node *node) { LineInfo line = (LineInfo){.line = node->line, .col = node->col}; Operand reg_dst = NEW_REG(); Operand val; if (node->boolean) { val = NEW_S64(1); } else { val = NEW_S64(0); } EMIT_1(program, line, OP_LD8, reg_dst, val); return reg_dst; } // TODO: emit_if // TODO: emit_and // TODO: emit_or // TODO: emit_not // TODO: emit_global // TODO: emit_local // TODO: emit_procedure // TODO: emit_proc_call Operand emit_basm(ProgramBASM *program, Node *node) { switch (node->type) { case NODE_BOOL: { return emit_bool(program, node); } break; case NODE_NUMBER: { return emit_number(program, node); } break; case NODE_BUILTIN: { return emit_builtin(program, node); } break; default: { push_error(ERR_TYPE_BASM, ERR_UNIMPLEMENTED, node->line, node->col); return (Operand){0}; } break; } } ProgramBASM * generate_basm(ParseTree *parse_tree) { ProgramBASM *program = malloc(sizeof(ProgramBASM)); array_init(program->inst, 0); array_init(program->lines, 0); for (size_t i = 0; i < array_size(parse_tree->roots); ++i) { Node *root = parse_tree->roots[i]; emit_basm(program, root); } return program; }