Skip to content
Snippets Groups Projects
bc_compiler.c 28.8 KiB
Newer Older
  • Learn to ignore specific revisions
  • #include "bc_compiler.h"
    #include <assert.h>
    
    
    void string_init    ( String * str ) {
    
      str -> capacity = INIT_STRING_LENGTH;
    
      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 ] );
    
        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 ) {
    
    Michal Štěpánek's avatar
    Michal Štěpánek committed
      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;
    }
    
    
    Michal Štěpánek's avatar
    Michal Štěpánek committed
    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 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;
    
      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 
    
    Michal Štěpánek's avatar
    Michal Štěpánek committed
      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 );
    
    Michal Štěpánek's avatar
    Michal Štěpánek committed
      gen_loop_from_bc ( state, & cond, & body );   
    
      string_destroy ( & cond );
      string_destroy ( & body );
    
    Michal Štěpánek's avatar
    Michal Štěpánek committed
      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;
    
    }
    
    void bc_state_destroy ( BCCompilerState * state ) {
    
      LocalScope * scope = state -> scope;
      LocalScope * prev;
      while ( scope ) {
        prev = scope -> prev;
        free ( scope );
        scope = prev;
      }
    
    Michal Štěpánek's avatar
    Michal Štěpánek committed
      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 );
    
        }
        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 ] );
    
        }
        case AST_BLOCK: {
          AstBlock * block = (AstBlock*) ast;
          // make local scope
    
    Michal Štěpánek's avatar
    Michal Štěpánek committed
          add_scope ( state );
    
    Michal Štěpánek's avatar
    Michal Štěpánek committed
          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 ] );
    
    Michal Štěpánek's avatar
    Michal Štěpánek committed
            if ( i != block -> expression_cnt - 1 )
              string_write_byte ( & function -> bc, 0x00 );
          }
    
          // delete local scope
    
    Michal Štěpánek's avatar
    Michal Štěpánek committed
          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 ) {
    
            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 );
    
            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 );
    
            gen_bc_get_local ( & function -> bc, index );
    
          }
          return;
        }
        case AST_VARIABLE_ASSIGNMENT: {
          AstVariableAssignment * assign = (AstVariableAssignment*) ast;
          ast_to_bc ( state, assign -> value );
    
          BCFunction * function = & state -> constants . constants [ state -> fp ] . function;
          // try local
          u16 index = get_local_index ( state, assign -> name );
    
          if ( index == INVALID_LOCAL ) {
    
            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 );
    
            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 );
    
            gen_bc_set_local ( & function -> bc, index );
    
        case AST_CONDITIONAL: {
          AstConditional * cond = (AstConditional*) ast;
          ast_to_bc ( state, cond -> condition );
    
          gen_cond_body ( state, cond -> consequent, cond -> alternative );
    
        case AST_LOOP: {
          AstLoop * loop = (AstLoop*) ast;
    
          gen_loop ( state, loop -> condition, loop -> body );
    
        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 );
    
          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 );
    
          return;
        }
        case AST_ARRAY: {
          AstArray * array = (AstArray*) ast;
          BCFunction * function = & state -> constants . constants [ state -> fp ] . function;
          ast_to_bc ( state, array -> size );
    
          size_t size_pos = function -> bc . len;
    
          u16 size_index = make_local_index ( state, STR ("\0size") );
    
          gen_bc_set_local ( & function -> bc, size_index );
    
          add_scope ( state );
    
          remove_scope ( state );
    
            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;
    
          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 );
    
          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;
        }
    
    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 );
    
      string_write_byte ( & bc, 'F' );
      string_write_byte ( & bc, 'M' );
      string_write_byte ( & bc, 'L' );
    
      string_write_byte ( & bc, '\n' );
    
      // 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 ] );
    
      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 ] );
    
      string_write_u16 ( & bc, bc_state . ep );
    
      bc_state_destroy ( & bc_state );