Files
Noja/src/lib/compiler/codegen.c
T

792 lines
24 KiB
C

/* +--------------------------------------------------------------------------+
** | _ _ _ |
** | | \ | | (_) |
** | | \| | ___ _ __ _ |
** | | . ` |/ _ \| |/ _` | |
** | | |\ | (_) | | (_| | |
** | |_| \_|\___/| |\__,_| |
** | _/ | |
** | |__/ |
** +--------------------------------------------------------------------------+
** | Copyright (c) 2022 Francesco Cozzuto <francesco.cozzuto@gmail.com> |
** +--------------------------------------------------------------------------+
** | This file is part of The Noja Interpreter. |
** | |
** | The Noja Interpreter is free software: you can redistribute it and/or |
** | modify it under the terms of the GNU General Public License as published |
** | by the Free Software Foundation, either version 3 of the License, or (at |
** | your option) any later version. |
** | |
** | The Noja Interpreter is distributed in the hope that it will be useful, |
** | but WITHOUT ANY WARRANTY; without even the implied warranty of |
** | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General |
** | Public License for more details. |
** | |
** | You should have received a copy of the GNU General Public License along |
** | with The Noja Interpreter. If not, see <http://www.gnu.org/licenses/>. |
** +--------------------------------------------------------------------------+
** | WHAT IS THIS FILE? |
** | |
** | This file implements the routines that transform the AST into a list of |
** | bytecodes. The functionalities of this file are exposed through the |
** | `compile` function, that takes as input an `AST` and outputs an |
** | `Executable`. |
** | |
** | The function that does the heavy lifting is `emitInstrForNode` which |
** | walks the tree and writes instructions to the `ExeBuilder`. |
** | |
** | Some semantic errors are catched at this phase, in which case, they are |
** | reported by filling out the `error` structure and aborting. It's also |
** | possible that the compilation fails bacause of internal errors (which |
** | usually means "out of memory"). |
** +--------------------------------------------------------------------------+
*/
#include <assert.h>
#include <stdlib.h>
#include <stdbool.h>
#include "../utils/defs.h"
#include "codegenctx.h"
#include "compile.h"
#include "ASTi.h"
static void emitInstr_POP(CodegenContext *ctx,
long long int op0,
int off, int len)
{
Operand opv[1] = {
{ .type = OPTP_INT, .as_int = op0 }
};
CodegenContext_EmitInstr(ctx, OPCODE_POP, opv, 1, off, len);
}
static void emitInstr_POP1(CodegenContext *ctx, int off, int len)
{
emitInstr_POP(ctx, 1, off, len);
}
static void emitInstr_ASS(CodegenContext *ctx, const char *name, int off, int len)
{
Operand opv[] = {
{ .type = OPTP_STRING, .as_string = name },
};
CodegenContext_EmitInstr(ctx, OPCODE_ASS, opv, 1, off, len);
}
static void emitInstr_RETURN(CodegenContext *ctx,
long long int op0,
int off, int len)
{
Operand opv[1] = {
{ .type = OPTP_INT, .as_int = op0 }
};
CodegenContext_EmitInstr(ctx, OPCODE_RETURN, opv, 1, off, len);
}
static void emitInstr_EXIT(CodegenContext *ctx,
int off, int len)
{
CodegenContext_EmitInstr(ctx, OPCODE_EXIT, NULL, 0, off, len);
}
static void emitInstr_JUMP(CodegenContext *ctx,
Label *op0,
int off, int len)
{
Operand opv[1] = {
{ .type = OPTP_PROMISE, .as_promise = Label_ToPromise(op0) }
};
CodegenContext_EmitInstr(ctx, OPCODE_JUMP, opv, 1, off, len);
}
static void emitInstr_JUMPIFNOTANDPOP(CodegenContext *ctx,
Label *op0,
int off, int len)
{
Operand opv[1] = {
{ .type = OPTP_PROMISE, .as_promise = Label_ToPromise(op0) }
};
CodegenContext_EmitInstr(ctx, OPCODE_JUMPIFNOTANDPOP, opv, 1, off, len);
}
static void emitInstr_JUMPIFANDPOP(CodegenContext *ctx,
long long int op0,
int off, int len)
{
Operand opv[1] = {
{ .type = OPTP_IDX, .as_int = op0 }
};
CodegenContext_EmitInstr(ctx, OPCODE_JUMPIFANDPOP, opv, 1, off, len);
}
static void emitInstr_JUMPIFANDPOP_2(CodegenContext *ctx,
Label *op0,
int off, int len)
{
Operand opv[1] = {
{ .type = OPTP_PROMISE, .as_promise = Label_ToPromise(op0) }
};
CodegenContext_EmitInstr(ctx, OPCODE_JUMPIFANDPOP, opv, 1, off, len);
}
static void emitInstr_EQL(CodegenContext *ctx, int off, int len)
{
CodegenContext_EmitInstr(ctx, OPCODE_EQL, NULL, 0, off, len);
}
static void emitInstr_CHECKTYPE(CodegenContext *ctx, int arg_index, const char *arg_name, int off, int len)
{
Operand opv[2] = {
{ .type = OPTP_INT, .as_int = arg_index },
{ .type = OPTP_STRING, .as_string = arg_name },
};
CodegenContext_EmitInstr(ctx, OPCODE_CHECKTYPE, opv, 2, off, len);
}
static void emitInstr_PUSHTRU(CodegenContext *ctx, int off, int len)
{
CodegenContext_EmitInstr(ctx, OPCODE_PUSHTRU, NULL, 0, off, len);
}
static void emitInstr_PUSHFLS(CodegenContext *ctx, int off, int len)
{
CodegenContext_EmitInstr(ctx, OPCODE_PUSHFLS, NULL, 0, off, len);
}
static void emitInstr_PUSHTYP(CodegenContext *ctx, int off, int len)
{
CodegenContext_EmitInstr(ctx, OPCODE_PUSHTYP, NULL, 0, off, len);
}
static void emitInstr_PUSHNNETYP(CodegenContext *ctx, int off, int len)
{
CodegenContext_EmitInstr(ctx, OPCODE_PUSHNNETYP, NULL, 0, off, len);
}
static void emitInstrForNode(CodegenContext *ctx, Node *node, Label *label_break);
static Opcode exprkind_to_opcode(ExprKind kind)
{
switch(kind)
{
case EXPR_NULLABLETYPE: return OPCODE_NLB;
case EXPR_SUMTYPE: return OPCODE_STP;
case EXPR_NOT: return OPCODE_NOT;
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;
case EXPR_MOD: return OPCODE_MOD;
case EXPR_EQL: return OPCODE_EQL;
case EXPR_NQL: return OPCODE_NQL;
case EXPR_LSS: return OPCODE_LSS;
case EXPR_LEQ: return OPCODE_LEQ;
case EXPR_GRT: return OPCODE_GRT;
case EXPR_GEQ: return OPCODE_GEQ;
default:
UNREACHABLE;
break;
}
UNREACHABLE;
return -1;
}
static void emitInstrForFuncCallNode(CodegenContext *ctx, CallExprNode *expr,
Label *label_break, int returns)
{
Node *arg = expr->argv;
while(arg)
{
emitInstrForNode(ctx, arg, label_break);
arg = arg->next;
}
int argc;
if (expr->func->kind == NODE_EXPR && ((ExprNode*) expr->func)->kind == EXPR_ARW) {
IndexSelectionExprNode *selection = (IndexSelectionExprNode*) expr->func;
Node *idx = selection->idx;
Node *set = selection->set;
emitInstrForNode(ctx, set, label_break);
emitInstrForNode(ctx, idx, label_break);
CodegenContext_EmitInstr(ctx, OPCODE_SELECT2, NULL, 0,
expr->func->offset,
expr->func->length);
argc = expr->argc+1;
} else {
emitInstrForNode(ctx, expr->func, label_break);
argc = expr->argc;
}
Operand ops[2];
ops[0] = (Operand) { .type = OPTP_INT, .as_int = argc };
ops[1] = (Operand) { .type = OPTP_INT, .as_int = returns };
CodegenContext_EmitInstr(ctx, OPCODE_CALL, ops, 2,
expr->base.base.offset,
expr->base.base.length);
}
static void emitInstrForExprNode(CodegenContext *ctx, ExprNode *expr,
Label *label_break);
static void emitInstrForArgumentNode(CodegenContext *ctx, ArgumentNode *arg, int argidx)
{
if(arg->value != NULL) {
/* Emit bytecode for the default argument */
Label *label_default_handled = Label_New(ctx);
emitInstr_PUSHTYP(ctx, arg->value->offset, arg->value->length);
emitInstr_PUSHNNETYP(ctx, arg->value->offset, arg->value->length);
emitInstr_EQL(ctx, arg->value->offset, arg->value->length);
emitInstr_JUMPIFNOTANDPOP(ctx, label_default_handled, arg->value->offset, arg->value->length);
emitInstr_POP1(ctx, arg->value->offset, arg->value->length);
emitInstrForNode(ctx, arg->value, NULL);
Label_SetHere(label_default_handled, ctx);
Label_Free(label_default_handled);
}
if (arg->type != NULL) {
emitInstrForNode(ctx, arg->type, NULL);
emitInstr_CHECKTYPE(ctx, argidx, arg->name, arg->type->offset, arg->type->length);
}
emitInstr_ASS(ctx, arg->name, arg->base.offset, arg->base.length);
emitInstr_POP1(ctx, arg->base.offset, arg->base.length);
}
static void emitInstrForFuncExprNode(CodegenContext *ctx, FuncExprNode *func, const char *name)
{
Label *label_func = Label_New(ctx);
Label *label_jump = Label_New(ctx);
// Push function.
{
Operand ops[3] = {
{ .type = OPTP_PROMISE, .as_promise = Label_ToPromise(label_func) },
{ .type = OPTP_INT, .as_int = func->argc },
{ .type = OPTP_STRING, .as_string = name },
};
CodegenContext_EmitInstr(ctx, OPCODE_PUSHFUN, ops, 3, func->base.base.offset, func->base.base.length);
}
emitInstr_JUMP(ctx, label_jump, func->base.base.offset, func->base.base.length); // Jump after the function code
Label_SetHere(label_func, ctx); // This is the function code index.
// Compile the function body.
{
// Assign the arguments.
ArgumentNode *arg = (ArgumentNode*) func->argv;
int argidx = func->argc-1;
while(arg)
{
emitInstrForArgumentNode(ctx, arg, argidx);
arg = (ArgumentNode*) arg->base.next;
argidx -= 1;
}
emitInstrForNode(ctx, func->body, NULL);
if(func->body->kind == NODE_EXPR)
emitInstr_POP1(ctx, func->body->offset + func->body->length, 0);
// Write a return instruction, just
// in case it didn't already return.
emitInstr_RETURN(ctx, 0, func->body->offset, 0);
}
// This is the first index after the function code.
Label_SetHere(label_jump, ctx);
Label_Free(label_func);
Label_Free(label_jump);
}
static void emitInstrForFuncDeclNode(CodegenContext *ctx, FuncDeclNode *func)
{
emitInstrForFuncExprNode(ctx, func->expr, func->name->val);
emitInstr_ASS(ctx, func->name->val, func->base.offset, func->base.length); // Assign variable
emitInstr_POP1(ctx, func->base.offset, func->base.length); // Pop function object
}
static void flattenTupleTree(CodegenContext *ctx, ExprNode *root, ExprNode *tuple[], int max, int *count)
{
if(root->kind == EXPR_PAIR)
{
flattenTupleTree(ctx, (ExprNode*) ((OperExprNode*) root)->head, tuple, max, count);
flattenTupleTree(ctx, (ExprNode*) ((OperExprNode*) root)->head->next, tuple, max, count);
}
else
{
if(max == *count)
{
CodegenContext_ReportErrorAndJump(ctx, 0, "Static buffer is too small");
UNREACHABLE;
}
tuple[(*count)++] = root;
}
}
static void emitInstrForAssignmentNode(CodegenContext *ctx, OperExprNode *asgn, Label *label_break)
{
Node *lop, *rop;
lop = asgn->head;
rop = lop->next;
ExprNode *tuple[32];
int count = 0;
flattenTupleTree(ctx, (ExprNode*) lop, tuple, sizeof(tuple)/sizeof(tuple[0]), &count);
assert(count > 0);
if(count == 1) /* No tuple. */
emitInstrForNode(ctx, rop, label_break);
else
{
if(((ExprNode*) rop)->kind == EXPR_CALL)
emitInstrForFuncCallNode(ctx, (CallExprNode*) rop, label_break, count);
else {
CodegenContext_ReportErrorAndJump(ctx, 0, "Assigning to %d variables only 1 value", count);
UNREACHABLE;
}
}
for(int i = 0; i < count; i += 1)
{
ExprNode *tuple_item = tuple[count-i-1];
switch(tuple_item->kind)
{
case EXPR_IDENT:
{
const char *name = ((IdentExprNode*) tuple_item)->val;
emitInstr_ASS(ctx, name, tuple_item->base.offset, tuple_item->base.length);
break;
}
case EXPR_SELECT:
{
Node *idx = ((IndexSelectionExprNode*) tuple_item)->idx;
Node *set = ((IndexSelectionExprNode*) tuple_item)->set;
emitInstrForNode(ctx, set, label_break);
emitInstrForNode(ctx, idx, label_break);
CodegenContext_EmitInstr(ctx, OPCODE_INSERT2, NULL, 0, tuple_item->base.offset, tuple_item->base.length);
break;
}
default:
CodegenContext_ReportErrorAndJump(ctx, 0, "Assigning to something that it can't be assigned to");
UNREACHABLE;
}
if(i+1 < count)
emitInstr_POP1(ctx, asgn->base.base.offset, 0);
}
}
static void emitInstrForExprNode(CodegenContext *ctx, ExprNode *expr,
Label *label_break)
{
switch(expr->kind)
{
case EXPR_PAIR:
CodegenContext_ReportErrorAndJump(
ctx, 0, "Tuple outside of "
"assignment or return statement");
UNREACHABLE;
return; // For the compiler warning.
case EXPR_NULLABLETYPE:
case EXPR_SUMTYPE:
case EXPR_NOT: case EXPR_MOD:
case EXPR_POS: case EXPR_NEG:
case EXPR_ADD: case EXPR_SUB:
case EXPR_MUL: case EXPR_DIV:
case EXPR_EQL: case EXPR_NQL:
case EXPR_LSS: case EXPR_LEQ:
case EXPR_GRT: case EXPR_GEQ:
{
OperExprNode *oper = (OperExprNode*) expr;
for(Node *operand = oper->head; operand; operand = operand->next)
emitInstrForNode(ctx, operand, label_break);
CodegenContext_EmitInstr(ctx, exprkind_to_opcode(expr->kind), NULL, 0, expr->base.offset, expr->base.length);
return;
}
case EXPR_ARW:
CodegenContext_ReportErrorAndJump(ctx, 0, "Operator -> out of a function call");
UNREACHABLE;
return;
case EXPR_AND:
{
OperExprNode *oper = (OperExprNode*) expr;
/*
* <left_oper>
* JUMPIFNOTANDPOP false;
* <right_oper>
* JUMPIFNOTANDPOP false;
* PUSHTRU;
* JUMP end;
* false:
* PUSHFLS;
* end:
*
*/
Label *label_end = Label_New(ctx);
Label *label_false = Label_New(ctx);
for(Node *operand = oper->head; operand; operand = operand->next) {
emitInstrForNode(ctx, operand, label_break);
emitInstr_JUMPIFNOTANDPOP(ctx, label_false, expr->base.offset, expr->base.length);
}
emitInstr_PUSHTRU(ctx, expr->base.offset, expr->base.length);
emitInstr_JUMP(ctx, label_end, expr->base.offset, expr->base.length);
Label_SetHere(label_false, ctx);
emitInstr_PUSHFLS(ctx, expr->base.offset, expr->base.length);
Label_SetHere(label_end, ctx);
return;
}
case EXPR_OR:
{
OperExprNode *oper = (OperExprNode*) expr;
/*
* <left_oper>
* JUMPIFANDPOP true;
* <right_oper>
* JUMPIFANDPOP true;
* PUSHFLS;
* JUMP end;
* true:
* PUSHTRU;
* JUMP end;
* end:
*
*/
Label *label_end = Label_New(ctx);
Label *label_true = Label_New(ctx);
for(Node *operand = oper->head; operand; operand = operand->next) {
emitInstrForNode(ctx, operand, label_break);
emitInstr_JUMPIFANDPOP_2(ctx, label_true, expr->base.offset, expr->base.length);
}
emitInstr_PUSHFLS(ctx, expr->base.offset, expr->base.length);
emitInstr_JUMP(ctx, label_end, expr->base.offset, expr->base.length);
Label_SetHere(label_true, ctx);
emitInstr_PUSHTRU(ctx, expr->base.offset, expr->base.length);
Label_SetHere(label_end, ctx);
return;
}
case EXPR_ASS:
emitInstrForAssignmentNode(ctx, (OperExprNode*) expr, label_break);
return;
case EXPR_INT:
{
IntExprNode *p = (IntExprNode*) expr;
Operand op = { .type = OPTP_INT, .as_int = p->val };
CodegenContext_EmitInstr(ctx, OPCODE_PUSHINT, &op, 1, expr->base.offset, expr->base.length);
return;
}
case EXPR_FLOAT:
{
FloatExprNode *p = (FloatExprNode*) expr;
Operand op = { .type = OPTP_FLOAT, .as_float = p->val };
CodegenContext_EmitInstr(ctx, OPCODE_PUSHFLT, &op, 1, expr->base.offset, expr->base.length);
return;
}
case EXPR_STRING:
{
StringExprNode *p = (StringExprNode*) expr;
Operand op = { .type = OPTP_STRING, .as_string = p->val };
CodegenContext_EmitInstr(ctx, OPCODE_PUSHSTR, &op, 1, expr->base.offset, expr->base.length);
return;
}
case EXPR_IDENT:
{
IdentExprNode *p = (IdentExprNode*) expr;
Operand op = { .type = OPTP_STRING, .as_string = p->val };
CodegenContext_EmitInstr(ctx, OPCODE_PUSHVAR, &op, 1, expr->base.offset, expr->base.length);
return;
}
case EXPR_LIST:
{
// PUSHLST
// PUSHINT
// <expr>
// INSERT
ListExprNode *l = (ListExprNode*) expr;
Operand op;
op = (Operand) { .type = OPTP_INT, .as_int = l->itemc };
CodegenContext_EmitInstr(ctx, OPCODE_PUSHLST, &op, 1, expr->base.offset, expr->base.length);
Node *item = l->items;
int i = 0;
while(item)
{
op = (Operand) { .type = OPTP_INT, .as_int = i };
CodegenContext_EmitInstr(ctx, OPCODE_PUSHINT, &op, 1, item->offset, item->length);
emitInstrForNode(ctx, item, label_break);
CodegenContext_EmitInstr(ctx, OPCODE_INSERT, NULL, 0, item->offset, item->length);
i += 1;
item = item->next;
}
return;
}
case EXPR_MAP:
{
MapExprNode *m = (MapExprNode*) expr;
Operand op;
op = (Operand) { .type = OPTP_INT, .as_int = m->itemc };
CodegenContext_EmitInstr(ctx, OPCODE_PUSHMAP, &op, 1, expr->base.offset, expr->base.length);
Node *key = m->keys;
Node *item = m->items;
while(item)
{
emitInstrForNode(ctx, key, label_break);
emitInstrForNode(ctx, item, label_break);
CodegenContext_EmitInstr(ctx, OPCODE_INSERT, NULL, 0, item->offset, item->length);
key = key->next;
item = item->next;
}
return;
}
case EXPR_CALL:
emitInstrForFuncCallNode(ctx, (CallExprNode*) expr, label_break, 1);
return;
case EXPR_FUNC:
emitInstrForFuncExprNode(ctx, (FuncExprNode*) expr, "???");
return;
case EXPR_SELECT:
{
IndexSelectionExprNode *sel = (IndexSelectionExprNode*) expr;
emitInstrForNode(ctx, sel->set, label_break);
emitInstrForNode(ctx, sel->idx, label_break);
CodegenContext_EmitInstr(ctx, OPCODE_SELECT, NULL, 0, expr->base.offset, expr->base.length);
return;
}
case EXPR_NONE: CodegenContext_EmitInstr(ctx, OPCODE_PUSHNNE, NULL, 0, expr->base.offset, expr->base.length); return;
case EXPR_TRUE: CodegenContext_EmitInstr(ctx, OPCODE_PUSHTRU, NULL, 0, expr->base.offset, expr->base.length); return;
case EXPR_FALSE: CodegenContext_EmitInstr(ctx, OPCODE_PUSHFLS, NULL, 0, expr->base.offset, expr->base.length); return;
default: UNREACHABLE; break;
}
}
static void emitInstrForIfElseNode(CodegenContext *ctx, IfElseNode *ifelse, Label *label_break)
{
emitInstrForNode(ctx, ifelse->condition, label_break);
if(ifelse->false_branch)
{
Label *label_else = Label_New(ctx);
Label *label_done = Label_New(ctx);
emitInstr_JUMPIFNOTANDPOP(ctx, label_else, ifelse->condition->offset, ifelse->condition->length);
emitInstrForNode(ctx, ifelse->true_branch, label_break);
if(ifelse->true_branch->kind == NODE_EXPR)
emitInstr_POP(ctx, 1, ifelse->true_branch->offset, 0);
emitInstr_JUMP(ctx, label_done, ifelse->base.offset, ifelse->base.length);
Label_SetHere(label_else, ctx);
emitInstrForNode(ctx, ifelse->false_branch, label_break);
if(ifelse->false_branch->kind == NODE_EXPR)
emitInstr_POP1(ctx, ifelse->false_branch->offset, 0);
Label_SetHere(label_done, ctx);
Label_Free(label_else);
Label_Free(label_done);
}
else
{
Label *label_done = Label_New(ctx);
emitInstr_JUMPIFNOTANDPOP(ctx, label_done, ifelse->condition->offset, ifelse->condition->length);
emitInstrForNode(ctx, ifelse->true_branch, label_break);
if(ifelse->true_branch->kind == NODE_EXPR)
emitInstr_POP1(ctx, ifelse->true_branch->offset, 0);
Label_SetHere(label_done, ctx);
Label_Free(label_done);
}
}
static void emitInstrForWhileLoopNode(CodegenContext *ctx, WhileNode *loop, Label *label_break)
{
/*
* start:
* <condition>
* JUMPIFNOTANDPOP end
* <body>
* JUMP start
* end:
*/
Label *label_start = Label_New(ctx);
Label *label_end = Label_New(ctx);
Label_SetHere(label_start, ctx);
emitInstrForNode(ctx, loop->condition, label_break);
emitInstr_JUMPIFNOTANDPOP(ctx, label_end, loop->condition->offset, loop->condition->length);
emitInstrForNode(ctx, loop->body, label_end);
if(loop->body->kind == NODE_EXPR)
emitInstr_POP1(ctx, loop->body->offset, 0);
emitInstr_JUMP(ctx, label_start, loop->base.offset, loop->base.length);
Label_SetHere(label_end, ctx);
Label_Free(label_start);
Label_Free(label_end);
}
static void emitInstrForDoWhileLoopNode(CodegenContext *ctx, DoWhileNode *loop, Label *label_break)
{
/*
* start:
* <body>
* <condition>
* JUMPIFANDPOP start
*/
Label *label_end = Label_New(ctx);
long long int start = CodegenContext_InstrCount(ctx);
emitInstrForNode(ctx, loop->body, label_end);
if(loop->body->kind == NODE_EXPR)
emitInstr_POP1(ctx, loop->body->offset, 0);
emitInstrForNode(ctx, loop->condition, label_break);
emitInstr_JUMPIFANDPOP(ctx, start, loop->condition->offset, loop->condition->length);
Label_SetHere(label_end, ctx);
Label_Free(label_end);
}
static void emitInstrForNode(CodegenContext *ctx, Node *node, Label *label_break)
{
assert(node != NULL);
switch(node->kind)
{
case NODE_EXPR:
emitInstrForExprNode(ctx, (ExprNode*) node, label_break);
return;
case NODE_BREAK:
if(label_break == NULL)
CodegenContext_ReportErrorAndJump(ctx, 0, "Break not inside a loop");
emitInstr_JUMP(ctx, label_break, node->offset, node->length);
return;
case NODE_IFELSE:
emitInstrForIfElseNode(ctx, (IfElseNode*) node, label_break);
return;
case NODE_WHILE:
emitInstrForWhileLoopNode(ctx, (WhileNode*) node, label_break);
return;
case NODE_DOWHILE:
emitInstrForDoWhileLoopNode(ctx, (DoWhileNode*) node, label_break);
return;
case NODE_COMP:
{
CompoundNode *comp = (CompoundNode*) node;
Node *stmt = comp->head;
while(stmt)
{
emitInstrForNode(ctx, stmt, label_break);
if(stmt->kind == NODE_EXPR)
emitInstr_POP1(ctx, stmt->offset, 0);
stmt = stmt->next;
}
return;
}
case NODE_RETURN:
{
#warning "What if this is in the global scope?"
ReturnNode *ret = (ReturnNode*) node;
ExprNode *tuple[32];
int count = 0;
flattenTupleTree(ctx, (ExprNode*) ret->val, tuple, sizeof(tuple)/sizeof(tuple[0]), &count);
for(int i = 0; i < count; i += 1)
emitInstrForNode(ctx, (Node*) tuple[i], label_break);
emitInstr_RETURN(ctx, count, ret->base.offset, ret->base.length);
return;
}
case NODE_FUNC:
emitInstrForFuncDeclNode(ctx, (FuncDeclNode*) node);
return;
default:
UNREACHABLE;
}
UNREACHABLE;
}
/* Symbol: codegen
*
* Serializes an AST into bytecode format.
*
*
* Arguments:
*
* ast: The AST to be serialized.
* alloc: The allocator that will be used to get new
* memory. (optional)
* error: Error information structure that is filled out if
* an error occurres.
*
*
* Returns:
* A pointer to an `Executable` that is the object that
* contains the bytecode. If an error occurres, NULL is
* returned and the `error` structure is filled out.
*
*/
Executable *codegen(AST *ast, BPAlloc *alloc, Error *error)
{
assert(ast != NULL);
assert(error != NULL);
jmp_buf env;
CodegenContext *ctx = CodegenContext_New(error, alloc);
if(ctx == NULL) {
Error_Report(error, 1, "No memory");
return NULL;
}
if(setjmp(env)) {
assert(error->occurred == true);
CodegenContext_Free(ctx);
return NULL;
}
assert(error->occurred == false);
CodegenContext_SetJumpDest(ctx, &env);
emitInstrForNode(ctx, ast->root, NULL);
emitInstr_EXIT(ctx, Source_GetSize(ast->src), 0);
assert(error->occurred == false);
return CodegenContext_MakeExecutableAndFree(ctx, ast->src);
}