Files
armorpaint/base/sources/libs/kong/compiler.c
T

915 lines
23 KiB
C

#include "compiler.h"
#include "errors.h"
#include "parser.h"
#include <assert.h>
#include <stddef.h>
#include <string.h>
typedef struct allocated_global {
global *g;
uint64_t variable_id;
} allocated_global;
static allocated_global allocated_globals[1024];
////
// static size_t allocated_globals_size = 0;
size_t allocated_globals_size = 0;
////
allocated_global find_allocated_global(name_id name) {
for (size_t i = 0; i < allocated_globals_size; ++i) {
if (name == allocated_globals[i].g->name) {
return allocated_globals[i];
}
}
allocated_global a;
a.g = NULL;
a.variable_id = 0;
return a;
}
variable find_local_var(block *b, name_id name) {
if (b == NULL) {
variable var;
var.index = 0;
init_type_ref(&var.type, NO_NAME);
return var;
}
for (size_t i = 0; i < b->vars.size; ++i) {
if (b->vars.v[i].name == name) {
debug_context context = {0};
check(b->vars.v[i].type.type != NO_TYPE, context, "Local variable does not have a type");
variable var;
var.index = b->vars.v[i].variable_id;
var.type = b->vars.v[i].type;
var.kind = VARIABLE_LOCAL;
return var;
}
}
return find_local_var(b->parent, name);
}
variable find_variable(block *parent, name_id name) {
variable local_var = find_local_var(parent, name);
if (local_var.index == 0) {
allocated_global global = find_allocated_global(name);
if (global.g->type != NO_TYPE && global.variable_id != 0) {
variable v;
init_type_ref(&v.type, NO_NAME);
v.type.type = global.g->type;
v.index = global.variable_id;
v.kind = VARIABLE_GLOBAL;
return v;
}
else {
debug_context context = {0};
error(context, "Variable %s not found", get_name(name));
variable v;
v.index = 0;
return v;
}
}
else {
return local_var;
}
}
const char all_names[1024 * 1024];
////
// static uint64_t next_variable_id = 1;
uint64_t next_variable_id = 1;
////
variable all_variables[1024 * 1024];
variable allocate_variable(type_ref type, variable_kind kind) {
variable v;
v.index = next_variable_id;
v.type = type;
v.kind = kind;
all_variables[v.index] = v;
++next_variable_id;
return v;
}
opcode *emit_op(opcodes *code, opcode *o) {
assert(code->size + o->size < OPCODES_SIZE);
uint8_t *location = &code->o[code->size];
memcpy(&code->o[code->size], o, o->size);
code->size += o->size;
return (opcode *)location;
}
variable emit_expression(opcodes *code, block *parent, expression *e) {
switch (e->kind) {
case EXPRESSION_BINARY: {
expression *left = e->binary.left;
expression *right = e->binary.right;
debug_context context = {0};
switch (e->binary.op) {
case OPERATOR_EQUALS:
case OPERATOR_NOT_EQUALS:
case OPERATOR_GREATER:
case OPERATOR_GREATER_EQUAL:
case OPERATOR_LESS:
case OPERATOR_LESS_EQUAL:
case OPERATOR_AND:
case OPERATOR_OR: {
variable right_var = emit_expression(code, parent, right);
variable left_var = emit_expression(code, parent, left);
type_ref t;
init_type_ref(&t, NO_NAME);
t.type = bool_id;
variable result_var = allocate_variable(t, VARIABLE_INTERNAL);
opcode o;
switch (e->binary.op) {
case OPERATOR_EQUALS:
o.type = OPCODE_EQUALS;
break;
case OPERATOR_NOT_EQUALS:
o.type = OPCODE_NOT_EQUALS;
break;
case OPERATOR_GREATER:
o.type = OPCODE_GREATER;
break;
case OPERATOR_GREATER_EQUAL:
o.type = OPCODE_GREATER_EQUAL;
break;
case OPERATOR_LESS:
o.type = OPCODE_LESS;
break;
case OPERATOR_LESS_EQUAL:
o.type = OPCODE_LESS_EQUAL;
break;
case OPERATOR_AND:
o.type = OPCODE_AND;
break;
case OPERATOR_OR:
o.type = OPCODE_OR;
break;
default: {
error(context, "Unexpected operator");
}
}
o.size = OP_SIZE(o, op_binary);
o.op_binary.right = right_var;
o.op_binary.left = left_var;
o.op_binary.result = result_var;
emit_op(code, &o);
return result_var;
}
case OPERATOR_MINUS:
case OPERATOR_PLUS:
case OPERATOR_DIVIDE:
case OPERATOR_MULTIPLY:
case OPERATOR_MOD:
case OPERATOR_BITWISE_XOR:
case OPERATOR_BITWISE_AND:
case OPERATOR_BITWISE_OR:
case OPERATOR_LEFT_SHIFT:
case OPERATOR_RIGHT_SHIFT: {
variable right_var = emit_expression(code, parent, right);
variable left_var = emit_expression(code, parent, left);
variable result_var = allocate_variable(e->type, VARIABLE_INTERNAL);
opcode o;
switch (e->binary.op) {
case OPERATOR_MINUS:
o.type = OPCODE_SUB;
break;
case OPERATOR_PLUS:
o.type = OPCODE_ADD;
break;
case OPERATOR_DIVIDE:
o.type = OPCODE_DIVIDE;
break;
case OPERATOR_MULTIPLY:
o.type = OPCODE_MULTIPLY;
break;
case OPERATOR_MOD:
o.type = OPCODE_MOD;
break;
case OPERATOR_BITWISE_XOR:
o.type = OPCODE_BITWISE_XOR;
break;
case OPERATOR_BITWISE_AND:
o.type = OPCODE_BITWISE_AND;
break;
case OPERATOR_BITWISE_OR:
o.type = OPCODE_BITWISE_OR;
break;
case OPERATOR_LEFT_SHIFT:
o.type = OPCODE_LEFT_SHIFT;
break;
case OPERATOR_RIGHT_SHIFT:
o.type = OPCODE_RIGHT_SHIFT;
break;
default: {
error(context, "Unexpected operator");
}
}
o.size = OP_SIZE(o, op_binary);
o.op_binary.right = right_var;
o.op_binary.left = left_var;
o.op_binary.result = result_var;
emit_op(code, &o);
return result_var;
}
case OPERATOR_NOT: {
error(context, "! is not a binary operator");
}
case OPERATOR_ASSIGN:
case OPERATOR_MINUS_ASSIGN:
case OPERATOR_PLUS_ASSIGN:
case OPERATOR_DIVIDE_ASSIGN:
case OPERATOR_MULTIPLY_ASSIGN: {
variable v = emit_expression(code, parent, right);
switch (left->kind) {
case EXPRESSION_VARIABLE: {
opcode o;
switch (e->binary.op) {
case OPERATOR_ASSIGN:
o.type = OPCODE_STORE_VARIABLE;
break;
case OPERATOR_MINUS_ASSIGN:
o.type = OPCODE_SUB_AND_STORE_VARIABLE;
break;
case OPERATOR_PLUS_ASSIGN:
o.type = OPCODE_ADD_AND_STORE_VARIABLE;
break;
case OPERATOR_DIVIDE_ASSIGN:
o.type = OPCODE_DIVIDE_AND_STORE_VARIABLE;
break;
case OPERATOR_MULTIPLY_ASSIGN:
o.type = OPCODE_MULTIPLY_AND_STORE_VARIABLE;
break;
default: {
error(context, "Unexpected operator");
}
}
o.size = OP_SIZE(o, op_store_var);
o.op_store_var.from = v;
o.op_store_var.to = find_variable(parent, left->variable);
emit_op(code, &o);
break;
}
case EXPRESSION_ELEMENT:
case EXPRESSION_MEMBER:
case EXPRESSION_SWIZZLE: {
opcode o;
switch (e->binary.op) {
case OPERATOR_ASSIGN:
o.type = OPCODE_STORE_ACCESS_LIST;
break;
case OPERATOR_MINUS_ASSIGN:
o.type = OPCODE_SUB_AND_STORE_ACCESS_LIST;
break;
case OPERATOR_PLUS_ASSIGN:
o.type = OPCODE_ADD_AND_STORE_ACCESS_LIST;
break;
case OPERATOR_DIVIDE_ASSIGN:
o.type = OPCODE_DIVIDE_AND_STORE_ACCESS_LIST;
break;
case OPERATOR_MULTIPLY_ASSIGN:
o.type = OPCODE_MULTIPLY_AND_STORE_ACCESS_LIST;
break;
default: {
error(context, "Unexpected operator");
}
}
o.size = OP_SIZE(o, op_store_access_list);
o.op_store_access_list.from = v;
expression *of = left;
access access_list[64];
uint32_t access_list_size = 0;
while (of->kind == EXPRESSION_ELEMENT || of->kind == EXPRESSION_MEMBER || of->kind == EXPRESSION_SWIZZLE) {
access *a = &access_list[access_list_size];
a->type = of->type.type;
switch (of->kind) {
case EXPRESSION_ELEMENT:
a->kind = ACCESS_ELEMENT;
a->access_element.index = emit_expression(code, parent, of->element.element_index);
of = of->element.of;
break;
case EXPRESSION_MEMBER:
a->kind = ACCESS_MEMBER;
a->access_member.name = of->member.member_name;
of = of->member.of;
break;
case EXPRESSION_SWIZZLE:
a->kind = ACCESS_SWIZZLE;
a->access_swizzle.swizzle = of->swizzle.swizz;
of = of->swizzle.of;
break;
default:
assert(false);
break;
}
access_list_size += 1;
}
o.op_store_access_list.access_list_size = access_list_size;
for (uint32_t access_index = 0; access_index < access_list_size; ++access_index) {
o.op_store_access_list.access_list[access_list_size - access_index - 1] = access_list[access_index];
}
o.op_store_access_list.to = emit_expression(code, parent, of);
emit_op(code, &o);
break;
}
default: {
debug_context context = {0};
error(context, "Expected a variable or an access");
}
}
return v;
}
}
break;
}
case EXPRESSION_UNARY: {
debug_context context = {0};
switch (e->unary.op) {
case OPERATOR_EQUALS:
error(context, "not implemented");
case OPERATOR_NOT_EQUALS:
error(context, "not implemented");
case OPERATOR_GREATER:
error(context, "not implemented");
case OPERATOR_GREATER_EQUAL:
error(context, "not implemented");
case OPERATOR_LESS:
error(context, "not implemented");
case OPERATOR_LESS_EQUAL:
error(context, "not implemented");
case OPERATOR_MINUS: {
variable v = emit_expression(code, parent, e->unary.right);
opcode o;
o.type = OPCODE_NEGATE;
o.size = OP_SIZE(o, op_negate);
o.op_negate.from = v;
o.op_negate.to = allocate_variable(v.type, VARIABLE_INTERNAL);
emit_op(code, &o);
return o.op_negate.to;
}
case OPERATOR_PLUS:
error(context, "not implemented");
case OPERATOR_DIVIDE:
error(context, "not implemented");
case OPERATOR_MULTIPLY:
error(context, "not implemented");
case OPERATOR_NOT: {
variable v = emit_expression(code, parent, e->unary.right);
opcode o;
o.type = OPCODE_NOT;
o.size = OP_SIZE(o, op_not);
o.op_not.from = v;
o.op_not.to = allocate_variable(v.type, VARIABLE_INTERNAL);
emit_op(code, &o);
return o.op_not.to;
}
case OPERATOR_OR:
error(context, "not implemented");
case OPERATOR_BITWISE_XOR:
error(context, "not implemented");
case OPERATOR_BITWISE_AND:
error(context, "not implemented");
case OPERATOR_BITWISE_OR:
error(context, "not implemented");
case OPERATOR_LEFT_SHIFT:
error(context, "not implemented");
case OPERATOR_RIGHT_SHIFT:
error(context, "not implemented");
case OPERATOR_AND:
error(context, "not implemented");
case OPERATOR_MOD:
error(context, "not implemented");
case OPERATOR_ASSIGN:
error(context, "not implemented");
case OPERATOR_PLUS_ASSIGN:
case OPERATOR_MINUS_ASSIGN:
case OPERATOR_MULTIPLY_ASSIGN:
case OPERATOR_DIVIDE_ASSIGN:
error(context, "not implemented");
}
}
case EXPRESSION_BOOLEAN: {
type_ref t;
init_type_ref(&t, NO_NAME);
t.type = float_id;
variable v = allocate_variable(t, VARIABLE_INTERNAL);
opcode o;
o.type = OPCODE_LOAD_BOOL_CONSTANT;
o.size = OP_SIZE(o, op_load_bool_constant);
o.op_load_bool_constant.boolean = e->boolean;
o.op_load_bool_constant.to = v;
emit_op(code, &o);
return v;
}
case EXPRESSION_FLOAT: {
type_ref t;
init_type_ref(&t, NO_NAME);
t.type = float_id;
variable v = allocate_variable(t, VARIABLE_INTERNAL);
opcode o;
o.type = OPCODE_LOAD_FLOAT_CONSTANT;
o.size = OP_SIZE(o, op_load_float_constant);
o.op_load_float_constant.number = (float)e->number;
o.op_load_float_constant.to = v;
emit_op(code, &o);
return v;
}
case EXPRESSION_INT: {
type_ref t;
init_type_ref(&t, NO_NAME);
t.type = int_id;
variable v = allocate_variable(t, VARIABLE_INTERNAL);
opcode o;
o.type = OPCODE_LOAD_INT_CONSTANT;
o.size = OP_SIZE(o, op_load_float_constant);
o.op_load_int_constant.number = (int)e->number;
o.op_load_int_constant.to = v;
emit_op(code, &o);
return v;
}
// case EXPRESSION_STRING:
// error("not implemented", 0, 0);
case EXPRESSION_VARIABLE: {
return find_variable(parent, e->variable);
}
case EXPRESSION_GROUPING: {
return emit_expression(code, parent, e->grouping);
}
case EXPRESSION_CALL: {
type_ref t;
init_type_ref(&t, NO_NAME);
t.type = e->type.type;
variable v = allocate_variable(t, VARIABLE_INTERNAL);
opcode o;
o.type = OPCODE_CALL;
o.size = OP_SIZE(o, op_call);
o.op_call.func = e->call.func_name;
o.op_call.var = v;
debug_context context = {0};
check(e->call.parameters.size <= sizeof(o.op_call.parameters) / sizeof(variable), context, "Call parameters missized");
for (size_t i = 0; i < e->call.parameters.size; ++i) {
o.op_call.parameters[i] = emit_expression(code, parent, e->call.parameters.e[i]);
}
o.op_call.parameters_size = (uint8_t)e->call.parameters.size;
emit_op(code, &o);
return v;
}
case EXPRESSION_ELEMENT:
case EXPRESSION_MEMBER:
case EXPRESSION_SWIZZLE: {
opcode o;
o.type = OPCODE_LOAD_ACCESS_LIST;
o.size = OP_SIZE(o, op_load_access_list);
variable v = allocate_variable(e->type, VARIABLE_INTERNAL);
o.op_load_access_list.to = v;
expression *of = e;
access access_list[64];
uint32_t access_list_size = 0;
while (of->kind == EXPRESSION_ELEMENT || of->kind == EXPRESSION_MEMBER || of->kind == EXPRESSION_SWIZZLE) {
access *a = &access_list[access_list_size];
a->type = of->type.type;
switch (of->kind) {
case EXPRESSION_ELEMENT:
a->kind = ACCESS_ELEMENT;
a->access_element.index = emit_expression(code, parent, of->element.element_index);
of = of->element.of;
break;
case EXPRESSION_MEMBER:
a->kind = ACCESS_MEMBER;
a->access_member.name = of->member.member_name;
of = of->member.of;
break;
case EXPRESSION_SWIZZLE:
a->kind = ACCESS_SWIZZLE;
a->access_swizzle.swizzle = of->swizzle.swizz;
of = of->swizzle.of;
break;
default:
assert(false);
break;
}
access_list_size += 1;
}
o.op_load_access_list.access_list_size = access_list_size;
for (uint32_t access_index = 0; access_index < access_list_size; ++access_index) {
o.op_load_access_list.access_list[access_list_size - access_index - 1] = access_list[access_index];
}
o.op_load_access_list.from = emit_expression(code, parent, of);
emit_op(code, &o);
return v;
}
default: {
debug_context context = {0};
error(context, "not implemented");
}
}
{
debug_context context = {0};
error(context, "Supposedly unreachable code reached");
variable v;
v.index = 0;
return v;
}
}
typedef struct block_ids {
uint64_t start;
uint64_t end;
} block_ids;
static block_ids emit_statement(opcodes *code, block *parent, statement *statement) {
switch (statement->kind) {
case STATEMENT_EXPRESSION:
emit_expression(code, parent, statement->expression);
break;
case STATEMENT_RETURN_EXPRESSION: {
opcode o;
o.type = OPCODE_RETURN;
variable v = emit_expression(code, parent, statement->expression);
if (v.index == 0) {
o.size = offsetof(opcode, op_return);
}
else {
o.size = OP_SIZE(o, op_return);
o.op_return.var = v;
}
emit_op(code, &o);
break;
}
case STATEMENT_DISCARD: {
opcode o;
o.type = OPCODE_DISCARD;
o.size = offsetof(opcode, op_nothing);
emit_op(code, &o);
break;
}
case STATEMENT_IF: {
struct previous_condition {
variable condition;
variable summed_condition;
};
struct previous_condition previous_conditions[64] = {0};
uint8_t previous_conditions_size = 0;
{
opcode o;
o.type = OPCODE_IF;
o.size = OP_SIZE(o, op_if);
variable initial_condition = emit_expression(code, parent, statement->iffy.test);
o.op_if.condition = initial_condition;
opcode *written_opcode = emit_op(code, &o);
previous_conditions[previous_conditions_size].condition = initial_condition;
previous_conditions_size += 1;
block_ids ids = emit_statement(code, parent, statement->iffy.if_block);
written_opcode->op_if.start_id = ids.start;
written_opcode->op_if.end_id = ids.end;
}
for (uint16_t i = 0; i < statement->iffy.else_size; ++i) {
variable current_condition;
{
opcode o;
o.type = OPCODE_NOT;
o.size = OP_SIZE(o, op_not);
o.op_not.from = previous_conditions[previous_conditions_size - 1].condition;
type_ref t;
init_type_ref(&t, NO_NAME);
t.type = bool_id;
current_condition = allocate_variable(t, VARIABLE_INTERNAL);
o.op_not.to = current_condition;
emit_op(code, &o);
}
variable summed_condition;
if (previous_conditions_size == 1) {
summed_condition = previous_conditions[0].summed_condition = current_condition;
}
else {
opcode o;
o.type = OPCODE_AND;
o.size = OP_SIZE(o, op_binary);
o.op_binary.left = previous_conditions[previous_conditions_size - 2].summed_condition;
o.op_binary.right = current_condition;
type_ref t;
init_type_ref(&t, NO_NAME);
t.type = bool_id;
summed_condition = allocate_variable(t, VARIABLE_INTERNAL);
o.op_binary.result = summed_condition;
emit_op(code, &o);
}
opcode o;
o.type = OPCODE_IF;
o.size = OP_SIZE(o, op_if);
if (statement->iffy.else_tests[i] != NULL) {
variable v = emit_expression(code, parent, statement->iffy.else_tests[i]);
variable else_test;
{
opcode o;
o.type = OPCODE_AND;
o.size = OP_SIZE(o, op_binary);
o.op_binary.left = summed_condition;
o.op_binary.right = v;
type_ref t;
init_type_ref(&t, NO_NAME);
t.type = bool_id;
else_test = allocate_variable(t, VARIABLE_INTERNAL);
o.op_binary.result = else_test;
emit_op(code, &o);
}
o.op_if.condition = else_test;
previous_conditions[previous_conditions_size].condition = v;
previous_conditions_size += 1;
}
else {
o.op_if.condition = summed_condition;
}
{
opcode *written_opcode = emit_op(code, &o);
block_ids ids = emit_statement(code, parent, statement->iffy.else_blocks[i]);
written_opcode->op_if.start_id = ids.start;
written_opcode->op_if.end_id = ids.end;
}
}
break;
}
case STATEMENT_WHILE: {
uint64_t start_id = next_variable_id;
++next_variable_id;
uint64_t continue_id = next_variable_id;
++next_variable_id;
uint64_t end_id = next_variable_id;
++next_variable_id;
{
opcode o;
o.type = OPCODE_WHILE_START;
o.op_while_start.start_id = start_id;
o.op_while_start.continue_id = continue_id;
o.op_while_start.end_id = end_id;
o.size = OP_SIZE(o, op_while_start);
emit_op(code, &o);
}
{
opcode o;
o.type = OPCODE_WHILE_CONDITION;
o.size = OP_SIZE(o, op_while);
variable v = emit_expression(code, parent, statement->whiley.test);
o.op_while.condition = v;
o.op_while.end_id = end_id;
emit_op(code, &o);
}
emit_statement(code, parent, statement->whiley.while_block);
{
opcode o;
o.type = OPCODE_WHILE_END;
o.op_while_end.start_id = start_id;
o.op_while_end.continue_id = continue_id;
o.op_while_end.end_id = end_id;
o.size = OP_SIZE(o, op_while_end);
emit_op(code, &o);
}
break;
}
case STATEMENT_DO_WHILE: {
uint64_t start_id = next_variable_id;
++next_variable_id;
uint64_t continue_id = next_variable_id;
++next_variable_id;
uint64_t end_id = next_variable_id;
++next_variable_id;
{
opcode o;
o.type = OPCODE_WHILE_START;
o.op_while_start.start_id = start_id;
o.op_while_start.continue_id = continue_id;
o.op_while_start.end_id = end_id;
o.size = OP_SIZE(o, op_while_start);
emit_op(code, &o);
}
emit_statement(code, parent, statement->whiley.while_block);
{
opcode o;
o.type = OPCODE_WHILE_CONDITION;
o.size = OP_SIZE(o, op_while);
variable v = emit_expression(code, parent, statement->whiley.test);
o.op_while.condition = v;
o.op_while.end_id = end_id;
emit_op(code, &o);
}
{
opcode o;
o.type = OPCODE_WHILE_END;
o.op_while_end.start_id = start_id;
o.op_while_end.continue_id = continue_id;
o.op_while_end.end_id = end_id;
o.size = OP_SIZE(o, op_while_end);
emit_op(code, &o);
}
break;
}
case STATEMENT_BLOCK: {
for (size_t i = 0; i < statement->block.vars.size; ++i) {
variable var = allocate_variable(statement->block.vars.v[i].type, VARIABLE_LOCAL);
statement->block.vars.v[i].variable_id = var.index;
}
uint64_t start_block_id = next_variable_id;
++next_variable_id;
uint64_t end_block_id = next_variable_id;
++next_variable_id;
{
opcode o;
o.type = OPCODE_BLOCK_START;
o.op_block.id = start_block_id;
o.size = OP_SIZE(o, op_block);
emit_op(code, &o);
}
for (size_t i = 0; i < statement->block.statements.size; ++i) {
emit_statement(code, &statement->block, statement->block.statements.s[i]);
}
{
opcode o;
o.type = OPCODE_BLOCK_END;
o.op_block.id = end_block_id;
o.size = OP_SIZE(o, op_block);
emit_op(code, &o);
}
block_ids ids;
ids.start = start_block_id;
ids.end = end_block_id;
return ids;
}
case STATEMENT_LOCAL_VARIABLE: {
opcode o;
o.type = OPCODE_VAR;
o.size = OP_SIZE(o, op_var);
variable init_var = {0};
if (statement->local_variable.init != NULL) {
init_var = emit_expression(code, parent, statement->local_variable.init);
}
variable local_var = find_local_var(parent, statement->local_variable.var.name);
statement->local_variable.var.variable_id = local_var.index;
o.op_var.var.index = statement->local_variable.var.variable_id;
debug_context context = {0};
check(statement->local_variable.var.type.type != NO_TYPE, context, "Local var has no type");
o.op_var.var.type = statement->local_variable.var.type;
emit_op(code, &o);
if (statement->local_variable.init != NULL) {
opcode o;
o.type = OPCODE_STORE_VARIABLE;
o.size = OP_SIZE(o, op_store_var);
o.op_store_var.from = init_var;
o.op_store_var.to = local_var;
emit_op(code, &o);
}
break;
}
}
block_ids ids;
ids.start = 0;
ids.end = 0;
return ids;
}
void allocate_globals(void) {
for (global_id i = 0; get_global(i) != NULL && get_global(i)->type != NO_TYPE; ++i) {
global *g = get_global(i);
type_ref t;
init_type_ref(&t, NO_NAME);
t.type = g->type;
variable v = allocate_variable(t, VARIABLE_GLOBAL);
allocated_globals[allocated_globals_size].g = g;
allocated_globals[allocated_globals_size].variable_id = v.index;
allocated_globals_size += 1;
assign_global_var(i, v.index);
}
}
void compile_function_block(opcodes *code, struct statement *block) {
if (block == NULL) {
// built-in
return;
}
if (block->kind != STATEMENT_BLOCK) {
debug_context context = {0};
error(context, "Expected a block");
}
for (size_t i = 0; i < block->block.vars.size; ++i) {
variable var = allocate_variable(block->block.vars.v[i].type, VARIABLE_LOCAL);
block->block.vars.v[i].variable_id = var.index;
}
for (size_t i = 0; i < block->block.statements.size; ++i) {
emit_statement(code, &block->block, block->block.statements.s[i]);
}
}