Files
Noja/src/compiler/compile.c
T
2021-10-31 17:17:14 +00:00

201 lines
5.1 KiB
C

#include <assert.h>
#include <setjmp.h>
#include <stdlib.h>
#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;
}