Forked from
Michal Vlasák / ni-run-template
10 commits behind, 68 commits ahead of the upstream repository.
-
Michal Štěpánek authoredMichal Štěpánek authored
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
bc_compiler.c 28.78 KiB
#include "bc_compiler.h"
#include <assert.h>
void string_init ( String * str ) {
str -> capacity = INIT_STRING_LENGTH;
str -> len = 0;
str -> str = (u8*) malloc ( INIT_STRING_LENGTH );
}
void string_destroy ( String * str ) {
free ( str -> str );
}
void string_write_byte ( String * str, const u8 data ) {
if ( str -> capacity == str -> len ) {
u8 * tmp = (u8*) malloc ( str -> capacity * 2 );
assert ( tmp );
memcpy ( tmp, str -> str, str -> len );
free ( str -> str );
str -> str = tmp;
str -> capacity *= 2;
}
str -> str [ str -> len ++ ] = data;
}
void string_write_u16 ( String * str, const u16 data ) {
string_write_byte ( str, data & 255 );
string_write_byte ( str, data >> 8 );
}
void string_write_i32 ( String * str, const i32 data ) {
string_write_byte ( str, data & 255 );
string_write_byte ( str, (data >> 8) & 255 );
string_write_byte ( str, (data >> 16) & 255 );
string_write_byte ( str, (data >> 24) & 255 );
}
void string_write_constant ( String * str, BCConstant constant ) {
switch ( constant . kind ) {
case CONSTANT_NULL:
string_write_byte ( str, 0x01 );
break;
case CONSTANT_BOOLEAN:
string_write_byte ( str, 0x04 );
string_write_byte ( str, constant . boolean ? 0x01 : 0x00 );
break;
case CONSTANT_INTEGER:
string_write_byte ( str, 0x00 );
string_write_i32 ( str, constant . integer );
break;
case CONSTANT_STRING:
string_write_byte ( str, 0x02 );
string_write_i32 ( str, constant . string . len );
for ( size_t i = 0; i < constant . string . len; ++i )
string_write_byte ( str, constant . string . str [ i ] );
break;
case CONSTANT_FUNCTION: {
string_write_byte ( str, 0x03 );
string_write_byte ( str, constant . function . parameters );
string_write_u16 ( str, constant . function . locals );
string_write_i32 ( str, constant . function . bc . len );
for ( size_t i = 0; i < constant . function . bc . len; ++i )
string_write_byte ( str, constant . function . bc . str [ i ] );
break;
}
case CONSTANT_CLASS: {
string_write_byte ( str, 0x05 );
string_write_u16 ( str, constant . class . fields );
for ( size_t i = 0; i < constant . class . fields; ++i )
string_write_u16 ( str, constant . class . indexes [ i ] );
break;
}
default:
assert ( false );
break;
}
}
BCConstant create_function ( u8 parameters ) {
BCFunction function = (BCFunction) { .parameters = parameters, .locals = 0 };
string_init ( & function . bc );
return (BCConstant) { .kind = CONSTANT_FUNCTION, .function = function };
}
LocalScope * create_function_scope ( u8 parameters ) {
LocalScope * scope = (LocalScope*) malloc ( sizeof (LocalScope*) + 2 * sizeof (u16) + sizeof (Str) * ( parameters + MAX_SCOPE_VARIABLES ) );
scope -> locals [ 0 ] = STR ( "this" );
scope -> used_locals = 1;
return scope;
}
void add_scope ( BCCompilerState * state ) {
LocalScope * scope = (LocalScope*) malloc ( sizeof (LocalScope*) + 2 * sizeof (u16) + sizeof (Str) * MAX_SCOPE_VARIABLES );
scope -> local_count = state -> scope -> local_count;
scope -> used_locals = 0;
scope -> prev = state -> scope;
state -> scope = scope;
}
void remove_scope ( BCCompilerState * state ) {
LocalScope * scope = state -> scope;
state -> scope = scope -> prev;
free ( scope );
}
void convert_function_to_bc ( BCCompilerState * state, u16 index ) {
BCConstant * this_constant = & state -> constants . constants [ index ];
AstFunction * func = (AstFunction*) this_constant -> ast;
LocalScope * old_scope = state -> scope;
*this_constant = create_function ( func -> parameter_cnt + 1 );
u16 old_fp = state -> fp;
state -> fp = index;
state -> scope = create_function_scope ( func -> parameter_cnt + 1 );
state -> scope -> prev = state -> top;
state -> scope -> used_locals = func -> parameter_cnt + 1;
state -> scope -> local_count = func -> parameter_cnt + 1;
for ( u16 i = 1; i < func -> parameter_cnt + 1; ++i )
state -> scope -> locals [ i ] = func -> parameters [ i - 1 ];
ast_to_bc ( state, func -> body );
free ( state -> scope );
string_write_byte ( & this_constant -> function . bc, 0x0F );
state -> scope = old_scope;
state -> fp = old_fp;
}
u16 get_string_index ( BCCompilerState * state, Str str ) {
u16 i;
for ( i = 0; i < state -> constants . constant_count; ++i )
if ( state -> constants . constants [ i ] . kind == CONSTANT_STRING && str_eq ( state -> constants . constants [ i ] . string, str ) )
return i;
return 0;
}
u16 make_string_index ( BCCompilerState * state, Str str ) {
insert_constant ( state, (BCConstant) { .kind = CONSTANT_STRING, .string = str } );
return state -> constants . constant_count - 1;
}
u16 get_local_index ( BCCompilerState * state, Str name ) {
LocalScope * scope = state -> scope;
size_t i;
while ( scope ) {
for ( i = 0; i < scope -> used_locals; ++i )
if ( str_eq ( scope -> locals [ i ], name ) )
return scope -> local_count - scope -> used_locals + i;
// return scope -> prev ? scope -> prev -> local_count + i: i;
scope = scope -> prev;
}
return INVALID_LOCAL;
}
u16 make_local_index ( BCCompilerState * state, Str name ) {
BCFunction * function = & state -> constants . constants [ state -> fp ] . function;
u16 * n = & state -> scope -> local_count;
assert ( *n != 256 * 256 );
state -> scope -> locals [ state -> scope -> used_locals ] = (Str) { .len = name . len, .str = name . str };
++(*n);
++(state -> scope -> used_locals);
if ( *n - function -> parameters > function -> locals ) {
function -> locals = *n - function -> parameters;
}
return *n - 1;
}
u16 get_or_make_class ( BCCompilerState * state, AstObject * object ) {
size_t i;
// create class
BCClass created_class = (BCClass) { .fields = object -> member_cnt, .indexes = (u16*) malloc ( sizeof (u16) * object -> member_cnt ) };
AstDefinition * def;
u16 index;
for ( size_t field = 0; field < created_class . fields; ++field ) {
assert ( object -> members [ field ] -> kind == AST_DEFINITION );
def = (AstDefinition*) object -> members [ field ];
index = get_string_index ( state, def -> name );
if ( index == 0 )
index = make_string_index ( state, def -> name );
created_class . indexes [ field ] = index;
for ( size_t prev = 0; prev < field; ++prev )
assert ( ! str_eq ( state -> constants . constants [ created_class . indexes [ prev ] ] . string, state -> constants . constants [ index ] . string ) );
}
// try to find same class as created
for ( i = 0; i < state -> constants . constant_count; ++i )
if ( state -> constants . constants [ i ] . kind == CONSTANT_CLASS ) {
BCClass class = state -> constants . constants [ i ] . class;
if ( class . fields == created_class . fields )
for ( size_t j = 0; j < class . fields; ++j ) {
if ( created_class . indexes [ j ] != class . indexes [ j ] )
continue;
}
}
// destroy created class, if class with same indexes is found nad return it's index, otherwise insert class into pool
if ( i != state -> constants . constant_count )
free ( created_class . indexes );
else
insert_constant ( state, (BCConstant) { .kind = CONSTANT_CLASS, .class = created_class } );
return i;
}
u16 get_or_make_int ( BCCompilerState * state, i32 value ) {
u16 i;
for ( i = 0; i < state -> constants . constant_count; ++i )
if ( state -> constants . constants [ i ] . kind == CONSTANT_INTEGER && state -> constants . constants [ i ] . integer == value )
return i;
insert_constant ( state, (BCConstant) { .kind = CONSTANT_INTEGER, .integer = value } );
return i;
}
void gen_cond_body ( BCCompilerState * state, Ast * cons, Ast * alt ) {
BCFunction * function = & state -> constants . constants [ state -> fp ] . function;
string_write_byte ( & function -> bc, 0x0D );
string_write_u16 ( & function -> bc, 0 );
u16 alt_pos = function -> bc . len;
add_scope ( state );
ast_to_bc ( state, alt );
remove_scope ( state );
string_write_byte ( & function -> bc, 0x0E );
string_write_u16 ( & function -> bc, 0 );
u16 cons_pos = function -> bc . len;
u16 alt_len = cons_pos - alt_pos;
function -> bc . str [ alt_pos - 2 ] = alt_len & 255;
function -> bc . str [ alt_pos - 1 ] = (alt_len >> 8) & 255;
add_scope ( state );
ast_to_bc ( state, cons );
remove_scope ( state );
u16 cons_len = function -> bc . len - cons_pos;
function -> bc . str [ cons_pos - 2 ] = cons_len & 255;
function -> bc . str [ cons_pos - 1 ] = (cons_len >> 8) & 255;
}
void gen_loop ( BCCompilerState * state, Ast * cond, Ast * body ) {
BCFunction * function = & state -> constants . constants [ state -> fp ] . function;
size_t start_pos = function -> bc . len;
ast_to_bc ( state, cond );
string_write_byte ( & function -> bc, 0x0D );
string_write_u16 ( & function -> bc, 6 );
ast_to_bc ( state, (Ast*) & (AstNull) { .base = (Ast) { .kind = AST_NULL } } );
string_write_byte ( & function -> bc, 0x0E );
string_write_u16 ( & function -> bc, 0 );
size_t body_pos = function -> bc . len;
add_scope ( state );
ast_to_bc ( state, body );
remove_scope ( state );
string_write_byte ( & function -> bc, 0x0E );
string_write_u16 ( & function -> bc, 0 );
size_t after_pos = function -> bc . len;
i16 loop_len = after_pos - start_pos;
function -> bc . str [ after_pos - 2 ] = (-loop_len) & 255;
function -> bc . str [ after_pos - 1 ] = ((-loop_len) >> 8) & 255;
u16 body_len = after_pos - body_pos;
function -> bc . str [ body_pos - 2 ] = body_len & 255;
function -> bc . str [ body_pos - 1 ] = (body_len >> 8) & 255;
}
void gen_loop_from_bc ( BCCompilerState * state, String * cond, String * body ) {
BCFunction * function = & state -> constants . constants [ state -> fp ] . function;
size_t start_pos = function -> bc . len;
for ( size_t i = 0; i < cond -> len; ++i )
string_write_byte ( & function -> bc, cond -> str [ i ] );
string_write_byte ( & function -> bc, 0x0D );
string_write_u16 ( & function -> bc, 3 );
string_write_byte ( & function -> bc, 0x0E );
string_write_u16 ( & function -> bc, 0 );
size_t body_pos = function -> bc . len;
for ( size_t i = 0; i < body -> len; ++i )
string_write_byte ( & function -> bc, body -> str [ i ] );
string_write_byte ( & function -> bc, 0x0E );
string_write_u16 ( & function -> bc, 0 );
size_t after_pos = function -> bc . len;
i16 loop_len = after_pos - start_pos;
function -> bc . str [ after_pos - 2 ] = (-loop_len) & 255;
function -> bc . str [ after_pos - 1 ] = ((-loop_len) >> 8) & 255;
u16 body_len = after_pos - body_pos;
function -> bc . str [ body_pos - 2 ] = body_len & 255;
function -> bc . str [ body_pos - 1 ] = (body_len >> 8) & 255;
}
void gen_bc_array_init ( BCCompilerState * state, AstArray * array, u16 size_index ) {
BCFunction * function = & state -> constants . constants [ state -> fp ] . function;
u16 array_index = make_local_index ( state, STR ("\0arr") );
gen_bc_set_local ( & function -> bc, array_index );
u16 one_index = get_or_make_int ( state, 1 );
gen_bc_constant ( state, one_index );
u16 i_index = make_local_index ( state, STR ("\0i") );
gen_bc_set_local ( & function -> bc, i_index );
string_write_byte ( & function -> bc, 0 );
String cond, body;
string_init ( & cond );
string_init ( & body );
// create cond BC
gen_bc_get_local ( & cond, i_index );
gen_bc_get_local ( & cond, size_index );
// call < operator to compare size and index
u16 index = get_string_index ( state, STR ("<") );
if ( index == 0 )
index = make_string_index ( state, STR ("<") );
string_write_byte ( & cond, 0x07 );
string_write_u16 ( & cond, index );
string_write_byte ( & cond, 2 );
// create body BC
gen_bc_get_local ( & body, array_index );
gen_bc_get_local ( & body, i_index );
size_t pos = function -> bc . len;
add_scope ( state );
ast_to_bc ( state, array -> initializer );
remove_scope ( state );
// value BC was created in function body, move to correct string
for ( size_t i = pos; i < function -> bc . len; ++i )
string_write_byte ( & body, function -> bc . str [ i ] );
function -> bc . len = pos;
// index assignment
index = get_string_index ( state, STR ("set") );
if ( index == 0 )
index = make_string_index ( state, STR ("set") );
string_write_byte ( & body, 0x07 );
string_write_u16 ( & body, index );
string_write_byte ( & body, 3 );
string_write_byte ( & body, 0 );
// increment i
index = get_string_index ( state, STR ("+") );
if ( index == 0 )
index = make_string_index ( state, STR ("+") );
gen_bc_get_local ( & body, i_index );
string_write_byte ( & body, 0x01 );
string_write_u16 ( & body, one_index );
string_write_byte ( & body, 0x07 );
string_write_u16 ( & body, index );
string_write_byte ( & body, 2 );
gen_bc_set_local ( & body, i_index );
string_write_byte ( & body, 0 );
gen_loop_from_bc ( state, & cond, & body );
string_destroy ( & cond );
string_destroy ( & body );
gen_bc_get_local ( & function -> bc, array_index );
}
void gen_bc_get_local ( String * body, u16 index ) {
string_write_byte ( body, 0x0A );
string_write_u16 ( body, index );
}
void gen_bc_set_local ( String * body, u16 index ) {
string_write_byte ( body, 0x09 );
string_write_u16 ( body, index );
}
void gen_bc_constant ( BCCompilerState * state, u16 index ) {
BCFunction * function = & state -> constants . constants [ state -> fp ] . function;
string_write_byte ( & function -> bc, 0x01 );
string_write_u16 ( & function -> bc, index );
}
void insert_constant ( BCCompilerState * state, BCConstant constant ) {
state -> constants . constants [ state -> constants . constant_count ++ ] = constant;
}
void bc_state_init ( BCCompilerState * state ) {
state -> constants . constant_count = 0;
state -> constants . null_pos = -1;
state -> constants . bool_pos [ 0 ] = -1;
state -> constants . bool_pos [ 1 ] = -1;
state -> globals . global_count = 0;
state -> scope = NULL;
state -> side_effect = false;
}
void bc_state_destroy ( BCCompilerState * state ) {
LocalScope * scope = state -> scope;
LocalScope * prev;
while ( scope ) {
prev = scope -> prev;
free ( scope );
scope = prev;
}
for ( size_t i = 0; i < state -> constants . constant_count; ++i ) {
BCConstant * constant = & state -> constants . constants [ i ];
if ( constant -> kind == CONSTANT_FUNCTION )
string_destroy ( & constant -> function . bc );
if ( constant -> kind == CONSTANT_CLASS )
free ( constant -> class . indexes );
}
}
void ast_to_bc ( BCCompilerState * state, Ast * ast ) {
switch ( ast -> kind ) {
case AST_TOP: {
AstTop * top = (AstTop*) ast;
state -> ep = state -> fp = state -> constants . constant_count;
insert_constant ( state, create_function ( 1 ) );
state -> scope = create_function_scope ( 1 );
state -> scope -> prev = NULL;
state -> scope -> used_locals = 1;
state -> scope -> local_count = 1;
state -> top = state -> scope;
BCFunction * function = & state -> constants . constants [ state -> fp ] . function;
for ( size_t i = 0; i < top -> expression_cnt; ++i ) {
ast_to_bc ( state, top -> expressions [ i ] );
if ( i != top -> expression_cnt - 1 )
string_write_byte ( & function -> bc, 0x00 );
}
string_write_byte ( & function -> bc, 0x0F );
return;
}
case AST_NULL: {
if ( state -> constants . null_pos < 0 ) {
state -> constants . null_pos = state -> constants . constant_count;
insert_constant ( state, (BCConstant) { .kind = CONSTANT_NULL } );
}
gen_bc_constant ( state, state -> constants . null_pos );
return;
}
case AST_INTEGER: {
AstInteger * integer = (AstInteger*) ast;
u16 i = get_or_make_int ( state, integer -> value );
gen_bc_constant ( state, i );
return;
}
case AST_BOOLEAN: {
AstBoolean * boolean = (AstBoolean*) ast;
size_t pos = boolean -> value ? 1 : 0;
if ( state -> constants . bool_pos [ pos ] < 0 ) {
state -> constants . bool_pos [ pos ] = state -> constants . constant_count;
insert_constant ( state, (BCConstant) { .kind = CONSTANT_BOOLEAN, .boolean = boolean -> value } );
}
gen_bc_constant ( state, state -> constants . bool_pos [ pos ] );
return;
}
case AST_BLOCK: {
AstBlock * block = (AstBlock*) ast;
// make local scope
add_scope ( state );
BCFunction * function = & state -> constants . constants [ state -> fp ] . function;
for ( size_t i = 0; i < block -> expression_cnt; ++i ) {
ast_to_bc ( state, block -> expressions [ i ] );
if ( i != block -> expression_cnt - 1 )
string_write_byte ( & function -> bc, 0x00 );
}
// delete local scope
remove_scope ( state );
return;
}
case AST_FUNCTION: {
insert_constant ( state, (BCConstant) { .kind = CONSTANT_AST_FUNCTION, .ast = ast } );
gen_bc_constant ( state, state -> constants . constant_count - 1 );
return;
}
case AST_FUNCTION_CALL: {
AstFunctionCall * call = (AstFunctionCall*) ast;
ast_to_bc ( state, call -> function );
for ( size_t i = 0; i < call -> argument_cnt; ++i )
ast_to_bc ( state, call -> arguments [ i ] );
BCFunction * function = & state -> constants . constants [ state -> fp ] . function;
string_write_byte ( & function -> bc, 0x08 );
string_write_byte ( & function -> bc, call -> argument_cnt & 255 );
state -> side_effect = true;
return;
}
case AST_PRINT: {
AstPrint * print = (AstPrint*) ast;
u16 index = get_string_index ( state, print -> format );
if ( index == 0 )
index = make_string_index ( state, print -> format );
for ( size_t i = 0; i < print -> argument_cnt; ++i )
ast_to_bc ( state, print -> arguments [ i ] );
BCFunction * function = & state -> constants . constants [ state -> fp ] . function;
string_write_byte ( & function -> bc, 0x02 );
string_write_u16 ( & function -> bc, index );
string_write_byte ( & function -> bc, print -> argument_cnt & 255 );
return;
}
case AST_DEFINITION: {
AstDefinition * def = (AstDefinition*) ast;
ast_to_bc ( state, def -> value );
BCFunction * function = & state -> constants . constants [ state -> fp ] . function;
if ( state -> scope == state -> top ) {
// GLOBAL
u16 index = get_string_index ( state, def -> name );
if ( index == 0 )
index = make_string_index ( state, def -> name );
u16 * count = & state -> globals . global_count;
size_t i;
for ( i = 0; i < *count; ++i )
if ( state -> globals . names [ i ] == index )
break;
if ( i == *count ) {
state -> globals . names [ *count ] = index;
++(*count);
}
string_write_byte ( & function -> bc, 0x0B );
string_write_u16 ( & function -> bc, index );
} else {
// LOCAL
u16 index = INVALID_LOCAL;
for ( size_t i = 0; i < state -> scope -> used_locals; ++i )
if ( str_eq ( state -> scope -> locals [ i ], def -> name ) )
index = function -> parameters + ( state -> scope -> prev ? state -> scope -> prev -> local_count + i: i );
if ( index == INVALID_LOCAL )
index = make_local_index ( state, def -> name );
gen_bc_set_local ( & function -> bc, index );
}
return;
}
case AST_VARIABLE_ACCESS: {
AstVariableAccess * var = (AstVariableAccess*) ast;
BCFunction * function = & state -> constants . constants [ state -> fp ] . function;
// try local
u16 index = get_local_index ( state, var -> name );
if ( index == INVALID_LOCAL ) {
// GLOBAL
u16 str_index = get_string_index ( state, var -> name );
if ( str_index == 0 ) {
// create global / local
if ( state -> scope == state -> top ) {
str_index = make_string_index ( state, var -> name );
u16 * count = & state -> globals . global_count;
state -> globals . names [ *count ] = str_index;
++(*count);
string_write_byte ( & function -> bc, 0x0C );
string_write_u16 ( & function -> bc, str_index );
return;
} else {
index = make_local_index ( state, var -> name );
gen_bc_get_local ( & function -> bc, index );
return;
}
}
u16 * count = & state -> globals . global_count;
size_t i;
for ( i = 0; i < *count; ++i )
if ( state -> globals . names [ i ] == str_index )
break;
if ( i == *count )
assert ( false );
string_write_byte ( & function -> bc, 0x0C );
string_write_u16 ( & function -> bc, str_index );
} else {
// LOCAL
gen_bc_get_local ( & function -> bc, index );
}
return;
}
case AST_VARIABLE_ASSIGNMENT: {
AstVariableAssignment * assign = (AstVariableAssignment*) ast;
ast_to_bc ( state, assign -> value );
state -> side_effect = true;
BCFunction * function = & state -> constants . constants [ state -> fp ] . function;
// try local
u16 index = get_local_index ( state, assign -> name );
if ( index == INVALID_LOCAL ) {
// GLOBAL
u16 str_index = get_string_index ( state, assign -> name );
if ( str_index == 0 ) {
// create global / local
if ( state -> scope == state -> top ) {
str_index = make_string_index ( state, assign -> name );
u16 * count = & state -> globals . global_count;
state -> globals . names [ *count ] = str_index;
++(*count);
string_write_byte ( & function -> bc, 0x0B );
string_write_u16 ( & function -> bc, str_index );
return;
} else {
index = make_local_index ( state, assign -> name );
gen_bc_set_local ( & function -> bc, index );
return;
}
}
u16 * count = & state -> globals . global_count;
size_t i;
for ( i = 0; i < *count; ++i )
if ( state -> globals . names [ i ] == str_index )
break;
if ( i == *count )
assert ( false );
string_write_byte ( & function -> bc, 0x0B );
string_write_u16 ( & function -> bc, str_index );
} else {
// LOCAL
gen_bc_set_local ( & function -> bc, index );
}
return;
}
case AST_CONDITIONAL: {
AstConditional * cond = (AstConditional*) ast;
ast_to_bc ( state, cond -> condition );
gen_cond_body ( state, cond -> consequent, cond -> alternative );
return;
}
case AST_LOOP: {
AstLoop * loop = (AstLoop*) ast;
gen_loop ( state, loop -> condition, loop -> body );
return;
}
case AST_OBJECT: {
AstObject * object = (AstObject*) ast;
u16 index = get_or_make_class ( state, object );
// push extends
ast_to_bc ( state, object -> extends );
// push members
AstDefinition * def;
add_scope ( state );
for ( size_t i = 0; i < object -> member_cnt; ++i ) {
def = (AstDefinition*) object -> members [ i ];
ast_to_bc ( state, def -> value );
}
remove_scope ( state );
// push class
BCFunction * function = & state -> constants . constants [ state -> fp ] . function;
string_write_byte ( & function -> bc, 0x04 );
string_write_u16 ( & function -> bc, index );
return;
}
case AST_FIELD_ASSIGNMENT: {
AstFieldAssignment * assign = (AstFieldAssignment*) ast;
BCFunction * function = & state -> constants . constants [ state -> fp ] . function;
ast_to_bc ( state, assign -> object );
ast_to_bc ( state, assign -> value );
state -> side_effect = true;
u16 index = get_string_index ( state, assign -> field );
if ( index == 0 )
index = make_string_index ( state, assign -> field );
string_write_byte ( & function -> bc, 0x06 );
string_write_u16 ( & function -> bc, index );
return;
}
case AST_FIELD_ACCESS: {
AstFieldAccess * access = (AstFieldAccess*) ast;
BCFunction * function = & state -> constants . constants [ state -> fp ] . function;
ast_to_bc ( state, access -> object );
u16 index = get_string_index ( state, access -> field );
if ( index == 0 )
index = make_string_index ( state, access -> field );
string_write_byte ( & function -> bc, 0x05 );
string_write_u16 ( & function -> bc, index );
return;
}
case AST_METHOD_CALL: {
AstMethodCall * method_call = (AstMethodCall*) ast;
BCFunction * function = & state -> constants . constants [ state -> fp ] . function;
ast_to_bc ( state, method_call -> object );
for ( size_t i = 0; i < method_call -> argument_cnt; ++i )
ast_to_bc ( state, method_call -> arguments [ i ] );
u16 index = get_string_index ( state, method_call -> name );
if ( index == 0 )
index = make_string_index ( state, method_call -> name );
string_write_byte ( & function -> bc, 0x07 );
string_write_u16 ( & function -> bc, index );
string_write_byte ( & function -> bc, method_call -> argument_cnt + 1 );
state -> side_effect = true;
return;
}
case AST_ARRAY: {
AstArray * array = (AstArray*) ast;
BCFunction * function = & state -> constants . constants [ state -> fp ] . function;
ast_to_bc ( state, array -> size );
add_scope ( state );
size_t size_pos = function -> bc . len;
u16 size_index = make_local_index ( state, STR ("\0size") );
gen_bc_set_local ( & function -> bc, size_index );
state -> side_effect = false;
add_scope ( state );
ast_to_bc ( state, array -> initializer );
remove_scope ( state );
string_write_byte ( & function -> bc, 0x03 );
if ( state -> side_effect ) {
gen_bc_array_init ( state, array, size_index );
} else {
// remove set local size if not needed
for ( size_t i = size_pos; i < function -> bc . len - 3; ++i )
function -> bc . str [ i ] = function -> bc . str [ i + 3 ];
function -> bc . len -= 3;
}
remove_scope ( state );
return;
}
case AST_INDEX_ASSIGNMENT: {
AstIndexAssignment * assign = (AstIndexAssignment*) ast;
BCFunction * function = & state -> constants . constants [ state -> fp ] . function;
ast_to_bc ( state, assign -> object );
ast_to_bc ( state, assign -> index );
ast_to_bc ( state, assign -> value );
state -> side_effect = true;
u16 index = get_string_index ( state, STR ("set") );
if ( index == 0 )
index = make_string_index ( state, STR ("set") );
string_write_byte ( & function -> bc, 0x07 );
string_write_u16 ( & function -> bc, index );
string_write_byte ( & function -> bc, 3 );
return;
}
case AST_INDEX_ACCESS: {
AstIndexAccess * access = (AstIndexAccess*) ast;
BCFunction * function = & state -> constants . constants [ state -> fp ] . function;
ast_to_bc ( state, access -> object );
ast_to_bc ( state, access -> index );
u16 index = get_string_index ( state, STR ("get") );
if ( index == 0 )
index = make_string_index ( state, STR ("get") );
string_write_byte ( & function -> bc, 0x07 );
string_write_u16 ( & function -> bc, index );
string_write_byte ( & function -> bc, 2 );
return;
}
default:
assert ( false );
}
}
String generate_bc ( Ast * ast ) {
// init
String bc;
string_init ( & bc );
BCCompilerState bc_state;
bc_state_init ( & bc_state );
// generate internals (constants (exp. functions), globals)
ast_to_bc ( &bc_state, ast );
// header
string_write_byte ( & bc, 'F' );
string_write_byte ( & bc, 'M' );
string_write_byte ( & bc, 'L' );
string_write_byte ( & bc, '\n' );
// constants to bc
// first functions to bc internals
for ( size_t i = 0; i < bc_state . constants . constant_count; ++i )
if ( bc_state . constants . constants [ i ] . kind == CONSTANT_AST_FUNCTION )
convert_function_to_bc ( & bc_state, i );
// fprintf ( stderr, "%d constants:\n", bc_state . constants . constant_count );
string_write_u16 ( & bc, bc_state . constants . constant_count );
for ( size_t i = 0; i < bc_state . constants . constant_count; ++i )
string_write_constant ( & bc, bc_state . constants . constants [ i ] );
// globals to bc
string_write_u16 ( & bc, bc_state . globals . global_count );
for ( size_t i = 0; i < bc_state . globals . global_count; ++i ) {
string_write_u16 ( & bc, bc_state . globals . names [ i ] );
}
// EP
string_write_u16 ( & bc, bc_state . ep );
// free
bc_state_destroy ( & bc_state );
return bc;
}