#include #include #include #include "../utils/defs.h" #include "compile.h" #include "ASTi.h" static Opcode exprkind_to_opcode(ExprKind kind) { switch(kind) { case EXPR_POS: return OPCODE_POS; case EXPR_NEG: return OPCODE_NEG; case EXPR_ADD: return OPCODE_ADD; case EXPR_SUB: return OPCODE_SUB; case EXPR_MUL: return OPCODE_MUL; case EXPR_DIV: return OPCODE_DIV; default: UNREACHABLE; break; } } static _Bool emit_instr_for_node(ExeBuilder *exeb, Node *node, Error *error) { assert(node != NULL); switch(node->kind) { case NODE_EXPR: { ExprNode *expr = (ExprNode*) node; switch(expr->kind) { case EXPR_POS: case EXPR_NEG: case EXPR_ADD: case EXPR_SUB: case EXPR_MUL: case EXPR_DIV: { OperExprNode *oper = (OperExprNode*) expr; for(Node *operand = oper->head; operand; operand = operand->next) if(!emit_instr_for_node(exeb, operand, error)) return 0; return ExeBuilder_Append(exeb, error, exprkind_to_opcode(expr->kind), NULL, 0, node->offset, node->length); } case EXPR_INT: { IntExprNode *p = (IntExprNode*) expr; Operand op = { .type = OPTP_INT, .as_int = p->val }; return ExeBuilder_Append(exeb, error, OPCODE_PUSHINT, &op, 1, node->offset, node->length); } case EXPR_FLOAT: { FloatExprNode *p = (FloatExprNode*) expr; Operand op = { .type = OPTP_FLOAT, .as_float = p->val }; return ExeBuilder_Append(exeb, error, OPCODE_PUSHFLT, &op, 1, node->offset, node->length); } case EXPR_STRING: { StringExprNode *p = (StringExprNode*) expr; Operand op = { .type = OPTP_STRING, .as_string = p->val }; return ExeBuilder_Append(exeb, error, OPCODE_PUSHSTR, &op, 1, node->offset, node->length); } case EXPR_IDENT: { IdentExprNode *p = (IdentExprNode*) expr; Operand op = { .type = OPTP_STRING, .as_string = p->val }; return ExeBuilder_Append(exeb, error, OPCODE_PUSHVAR, &op, 1, node->offset, node->length); } case EXPR_NONE: return ExeBuilder_Append(exeb, error, OPCODE_PUSHNNE, NULL, 0, node->offset, node->length); case EXPR_TRUE: return ExeBuilder_Append(exeb, error, OPCODE_PUSHTRU, NULL, 0, node->offset, node->length); case EXPR_FALSE: return ExeBuilder_Append(exeb, error, OPCODE_PUSHFLS, NULL, 0, node->offset, node->length); default: UNREACHABLE; break; } break; } case NODE_IFELSE: { IfElseNode *ifelse = (IfElseNode*) node; if(!emit_instr_for_node(exeb, ifelse->condition, error)) return 0; if(ifelse->false_branch) { Promise *else_offset = Promise_New(ExeBuilder_GetAlloc(exeb), sizeof(long long int)); Promise *done_offset = Promise_New(ExeBuilder_GetAlloc(exeb), sizeof(long long int)); if(else_offset == NULL || done_offset == NULL) { Error_Report(error, 1, "No memory"); return 0; } Operand op = { .type = OPTP_PROMISE, .as_promise = else_offset }; if(!ExeBuilder_Append(exeb, error, OPCODE_JUMPIFNOTANDPOP, &op, 1, node->offset, node->length)) return 0; if(!emit_instr_for_node(exeb, ifelse->true_branch, error)) return 0; op = (Operand) { .type = OPTP_PROMISE, .as_promise = done_offset }; if(!ExeBuilder_Append(exeb, error, OPCODE_JUMP, &op, 1, node->offset, node->length)) return 0; long long int temp = ExeBuilder_InstrCount(exeb); Promise_Resolve(else_offset, &temp, sizeof(temp)); if(!emit_instr_for_node(exeb, ifelse->false_branch, error)) return 0; temp = ExeBuilder_InstrCount(exeb); Promise_Resolve(done_offset, &temp, sizeof(temp)); Promise_Free(else_offset); Promise_Free(done_offset); } else { Promise *done_offset = Promise_New(ExeBuilder_GetAlloc(exeb), sizeof(long long int)); if(done_offset == NULL) { Error_Report(error, 1, "No memory"); return 0; } if(!ExeBuilder_Append(exeb, error, OPCODE_JUMPIFNOTANDPOP, &(Operand) { .type = OPTP_PROMISE, .as_promise = done_offset }, 1, node->offset, node->length)) return 0; if(!emit_instr_for_node(exeb, ifelse->true_branch, error)) return 0; long long int temp = ExeBuilder_InstrCount(exeb); Promise_Resolve(done_offset, &temp, sizeof(temp)); Promise_Free(done_offset); } return 1; } default: UNREACHABLE; return 0; } UNREACHABLE; return 0; } Executable *compile(AST *ast, BPAlloc *alloc, Error *error) { assert(ast != NULL); assert(error != NULL); BPAlloc *alloc2 = alloc; if(alloc2 == NULL) { alloc2 = BPAlloc_Init(-1); if(alloc2 == NULL) return NULL; } Executable *exe = NULL; ExeBuilder *exeb = ExeBuilder_New(alloc2); if(exeb) { if(!emit_instr_for_node(exeb, ast->root, error)) return 0; if(ExeBuilder_Append(exeb, error, OPCODE_RETURN, NULL, 0, Source_GetSize(ast->src), 0)) exe = ExeBuilder_Finalize(exeb, error); } if(alloc == NULL) BPAlloc_Free(alloc2); return exe; }