#ifndef BDL_IR_H #define BDL_IR_H typedef struct LineInfo { size_t line; size_t col; } LineInfo; typedef enum Op { // Arithmetic ops. // - Binary operations. // - Arguments are passed via the stack. OP_ADD, OP_SUB, OP_MUL, OP_DIV, OP_MOD, // Stack ops. // - Requires an Object to push into the stack. OP_PUSH, // - Discards the last value in the stack. OP_DROP, // Jump/conditional ops. // - Take a label as argument. // - For conditional jumps, the last value in the stack is used. OP_JUMP, OP_JUMP_IF_FALSE, // Primitive complex commands. // - Prints the last object in the stack. OP_PRINT, // Procedures. // - Requires a function name as parameter. OP_CALL, // - Return position is on a know location of the stack based on the offset // of locals, parameters, etc. OP_RETURN, // TODO: add remaining ops. } Op; static const char* ops_str[] = { [OP_ADD] = "OP_ADD", [OP_SUB] = "OP_SUB", [OP_MUL] = "OP_MUL", [OP_DIV] = "OP_DIV", [OP_MOD] = "OP_MOD", [OP_PUSH] = "OP_PUSH", [OP_DROP] = "OP_DROP", [OP_JUMP] = "OP_JUMP", [OP_JUMP_IF_FALSE] = "OP_JUMP_IF_FALSE", [OP_PRINT] = "OP_PRINT", [OP_CALL] = "OP_CALL", [OP_RETURN] = "OP_RETURN", }; typedef struct Instruction { Op op; Object *argument; // Original line/column for debugging purposes. size_t line; size_t col; } Instruction; typedef struct Procedure { // Procedure name. char *name; // Program code. Instruction *instructions; // Number of locals and parameters. size_t n_params; size_t n_locals; } Procedure; typedef struct ProgramIr { Procedure **procedures; Object **constants; } ProgramIr; void print_instruction(Instruction *instruction) { printf("%4ld:%-4ld ", instruction->line, instruction->col); Op op = instruction->op; switch (op) { case OP_PUSH: { printf("%-16s -> ", ops_str[op]); OBJ_PRINT(instruction->argument); } break; default: { printf("%s\n", ops_str[op]); } break; } } void print_procedure(Procedure *proc) { printf("===== %.*s =====\n", (int)array_size(proc->name), proc->name); printf("code:\n"); for (size_t i = 0; i < array_size(proc->instructions); ++i) { print_instruction(&proc->instructions[i]); } } Procedure * proc_alloc(ProgramIr *program, StringView name) { Procedure *proc = calloc(1, sizeof(Procedure)); array_init(proc->name, name.n); array_insert(proc->name, name.start, name.n); array_init(proc->instructions, 0); array_push(program->procedures, proc); return proc; } void compile_object(ProgramIr *program, Procedure *proc, Object *obj); void compile_arithmetic_list(ProgramIr *program, Procedure *proc, Op op, Object *obj) { size_t op_line = obj->head->line; size_t op_col = obj->head->col; obj = obj->tail; compile_object(program, proc, obj->head); obj = obj->tail; while (obj != NULL) { compile_object(program, proc, obj->head); obj = obj->tail; Instruction inst = (Instruction){op, NULL, op_line, op_col}; array_push(proc->instructions, inst); } } void compile_proc_call(ProgramIr *program, Procedure *proc, Object *obj) { // TODO: Handle this on the parser? if (sv_equal(&obj->head->text, &STRING("+"))) { compile_arithmetic_list(program, proc, OP_ADD, obj); } else if (sv_equal(&obj->head->text, &STRING("-"))) { compile_arithmetic_list(program, proc, OP_SUB, obj); } else if (sv_equal(&obj->head->text, &STRING("*"))) { compile_arithmetic_list(program, proc, OP_MUL, obj); } else if (sv_equal(&obj->head->text, &STRING("/"))) { compile_arithmetic_list(program, proc, OP_DIV, obj); } else if (sv_equal(&obj->head->text, &STRING("%"))) { compile_arithmetic_list(program, proc, OP_MOD, obj); } else { assert(false && "compile_proc_call: not implemented"); } } void compile_object(ProgramIr *program, Procedure *proc, Object *obj) { switch (obj->type) { case OBJ_TYPE_NIL: case OBJ_TYPE_TRUE: case OBJ_TYPE_FALSE: case OBJ_TYPE_STRING: case OBJ_TYPE_FIXNUM: { Instruction inst = (Instruction){OP_PUSH, obj, obj->line, obj->col}; array_push(proc->instructions, inst); } break; case OBJ_TYPE_PAIR: { compile_proc_call(program, proc, obj); } break; // case OBJ_TYPE_IF: { compile_if(obj); } break; // case OBJ_TYPE_LAMBDA: { compile_lambda(obj); } break; // case OBJ_TYPE_DEF: { compile_def(obj); } break; // case OBJ_TYPE_SYMBOL: { compile_symbol(obj); } break; default: { // TODO: assert? fprintf(stderr, "NOT IMPLEMENTED: compile_object for "); OBJ_PRINT(obj); exit(-1); } break; } } ProgramIr compile(Program program) { ProgramIr program_ir = {0}; array_init(program_ir.procedures, 0); array_init(program_ir.constants, 0); Procedure *main = proc_alloc(&program_ir, STRING("main")); for (size_t i = 0; i < array_size(program.roots); i++) { Object *root = program.roots[i]; compile_object(&program_ir, main, root); } // DEBUG:... for (size_t i = 0; i < array_size(program_ir.procedures); ++i) { print_procedure(program_ir.procedures[i]); } return program_ir; } #endif // BDL_IR_H