#include "InstructionEmitter.h" #include "StoredToken.h" #include "bscript/compiler/ast/ModuleFunctionDeclaration.h" #include "bscript/compiler/codegen/CaseJumpDataBlock.h" #include "bscript/compiler/codegen/ModuleDeclarationRegistrar.h" #include "bscript/compiler/model/FlowControlLabel.h" #include "bscript/compiler/model/LocalVariableScopeInfo.h" #include "bscript/compiler/model/Variable.h" #include "bscript/compiler/representation/CompiledScript.h" #include "bscript/compiler/representation/ExportedFunction.h" #include "escriptv.h" #include "modules.h" #include "token.h" #include "tokens.h" namespace Pol::Bscript::Compiler { InstructionEmitter::InstructionEmitter( CodeSection& code, DataSection& data, DebugStore& debug, ExportedFunctions& exported_functions, ModuleDeclarationRegistrar& module_declaration_registrar ) : code_emitter( code ), data_emitter( data ), debug( debug ), exported_functions( exported_functions ), module_declaration_registrar( module_declaration_registrar ) { initialize_data(); } void InstructionEmitter::initialize_data() { std::byte nul{}; data_emitter.store( &nul, sizeof nul ); } void InstructionEmitter::register_exported_function( FlowControlLabel& label, const std::string& name, unsigned parameters ) { exported_functions.emplace_back( name, parameters, label.address() ); } unsigned InstructionEmitter::enter_debug_block( const LocalVariableScopeInfo& local_variable_scope_info ) { unsigned previous_debug_block_index = debug_instruction_info.block_index; if ( !local_variable_scope_info.variables.empty() ) { debug_instruction_info.block_index = debug.add_block( debug_instruction_info.block_index, local_variable_scope_info ); } return previous_debug_block_index; } void InstructionEmitter::set_debug_block( unsigned block_index ) { debug_instruction_info.block_index = block_index; } void InstructionEmitter::access_variable( const Variable& v ) { BTokenId token_id = v.scope == VariableScope::Global ? TOK_GLOBALVAR : TOK_LOCALVAR; emit_token( token_id, TYP_OPERAND, v.index ); } void InstructionEmitter::array_append() { emit_token( TOK_INSERTINTO, TYP_OPERATOR ); } void InstructionEmitter::array_create() { emit_token( TOK_ARRAY, TYP_OPERAND ); } void InstructionEmitter::array_declare() { emit_token( INS_DECLARE_ARRAY, TYP_RESERVED ); } void InstructionEmitter::assign() { emit_token( TOK_ASSIGN, TYP_OPERATOR ); } void InstructionEmitter::assign_subscript_consume() { unsigned indexes = 1; emit_token( INS_SUBSCRIPT_ASSIGN_CONSUME, TYP_UNARY_OPERATOR, indexes ); } void InstructionEmitter::assign_subscript() { unsigned indexes = 1; emit_token( INS_SUBSCRIPT_ASSIGN, TYP_UNARY_OPERATOR, indexes ); } void InstructionEmitter::assign_multisubscript( unsigned indexes ) { emit_token( INS_MULTISUBSCRIPT_ASSIGN, TYP_UNARY_OPERATOR, indexes ); } void InstructionEmitter::assign_variable( const Variable& v ) { auto token_id = v.scope == VariableScope::Global ? INS_ASSIGN_GLOBALVAR : INS_ASSIGN_LOCALVAR; emit_token( token_id, TYP_UNARY_OPERATOR, v.index ); } void InstructionEmitter::basic_for_init( FlowControlLabel& label ) { register_with_label( label, emit_token( INS_INITFOR, TYP_RESERVED ) ); } void InstructionEmitter::basic_for_next( FlowControlLabel& label ) { register_with_label( label, emit_token( INS_NEXTFOR, TYP_RESERVED ) ); } void InstructionEmitter::binary_operator( BTokenId token_id ) { emit_token( token_id, TYP_OPERATOR ); } void InstructionEmitter::call_method_id( MethodID method_id, unsigned argument_count ) { emit_token( INS_CALL_METHOD_ID, (BTokenType)argument_count, method_id ); } void InstructionEmitter::call_method( const std::string& name, unsigned argument_count ) { unsigned offset = emit_data( name ); emit_token( INS_CALL_METHOD, (BTokenType)argument_count, offset ); } void InstructionEmitter::call_modulefunc( const ModuleFunctionDeclaration& module_function_declaration ) { unsigned module_id, function_index; module_declaration_registrar.lookup_or_register_module_function( module_function_declaration, module_id, function_index ); unsigned sympos = include_debug ? emit_data( module_function_declaration.name ) : 0; StoredToken token( static_cast( module_id ), TOK_FUNC, static_cast( function_index ), // function index, stored in Token.lval, saved in StoredToken.type sympos ); append_token( token ); } void InstructionEmitter::call_userfunc( FlowControlLabel& label ) { unsigned addr = emit_token( CTRL_JSR_USERFUNC, TYP_CONTROL ); register_with_label( label, addr ); } unsigned InstructionEmitter::casejmp() { return emit_token( INS_CASEJMP, TYP_RESERVED ); } unsigned InstructionEmitter::case_dispatch_table( const CaseJumpDataBlock& dispatch_table ) { auto& bytes = dispatch_table.get_data(); return data_emitter.append( bytes.data(), bytes.size() ); } void InstructionEmitter::consume() { emit_token( TOK_CONSUMER, TYP_UNARY_OPERATOR ); } void InstructionEmitter::ctrl_statementbegin( unsigned file_index, unsigned file_offset, const std::string& source_text ) { unsigned source_offset = emit_data( source_text ); Pol::Bscript::DebugToken debug_token; debug_token.sourceFile = file_index + 1; debug_token.offset = file_offset; debug_token.strOffset = source_offset; unsigned offset = data_emitter.store( reinterpret_cast( &debug_token ), sizeof debug_token ); emit_token( CTRL_STATEMENTBEGIN, TYP_CONTROL, offset ); } void InstructionEmitter::declare_variable( const Variable& v ) { BTokenId token_id = v.scope == VariableScope::Global ? RSV_GLOBAL : RSV_LOCAL; emit_token( token_id, TYP_RESERVED, v.index ); } void InstructionEmitter::dictionary_create() { emit_token( TOK_DICTIONARY, TYP_OPERAND ); } void InstructionEmitter::dictionary_add_member() { emit_token( INS_DICTIONARY_ADDMEMBER, TYP_OPERATOR ); } void InstructionEmitter::error_create() { emit_token( TOK_ERROR, TYP_OPERAND ); } void InstructionEmitter::exit() { emit_token( RSV_EXIT, TYP_RESERVED ); } void InstructionEmitter::foreach_init( FlowControlLabel& label ) { register_with_label( label, emit_token( INS_INITFOREACH, TYP_RESERVED ) ); } void InstructionEmitter::foreach_step( FlowControlLabel& label ) { register_with_label( label, emit_token( INS_STEPFOREACH, TYP_RESERVED ) ); } void InstructionEmitter::function_reference( unsigned parameter_count, FlowControlLabel& label ) { register_with_label( label, emit_token( TOK_FUNCREF, (BTokenType)parameter_count ) ); } void InstructionEmitter::get_arg( const std::string& name ) { unsigned offset = emit_data( name ); emit_token( INS_GET_ARG, TYP_OPERATOR, offset ); } void InstructionEmitter::get_member( const std::string& name ) { unsigned offset = emit_data( name ); emit_token( INS_GET_MEMBER, TYP_UNARY_OPERATOR, offset ); } void InstructionEmitter::get_member_id( MemberID member_id ) { emit_token( INS_GET_MEMBER_ID, TYP_UNARY_OPERATOR, member_id ); } void InstructionEmitter::jmp_always( FlowControlLabel& label ) { register_with_label( label, emit_token( RSV_GOTO, TYP_RESERVED ) ); } void InstructionEmitter::jmp_if_false( FlowControlLabel& label ) { register_with_label( label, emit_token( RSV_JMPIFFALSE, TYP_RESERVED ) ); } void InstructionEmitter::jmp_if_true( FlowControlLabel& label ) { register_with_label( label, emit_token( RSV_JMPIFTRUE, TYP_RESERVED ) ); } void InstructionEmitter::label( FlowControlLabel& label ) { label.assign_address( code_emitter.next_address() ); for ( auto referencing_address : label.get_referencing_instruction_addresses() ) { patch_offset( referencing_address, label.address() ); } } void InstructionEmitter::leaveblock( unsigned local_vars_to_remove ) { emit_token( CTRL_LEAVE_BLOCK, TYP_CONTROL, local_vars_to_remove ); } void InstructionEmitter::makelocal() { emit_token( CTRL_MAKELOCAL, TYP_CONTROL ); } void InstructionEmitter::pop_param( const std::string& name ) { unsigned offset = emit_data( name ); emit_token( INS_POP_PARAM, TYP_OPERATOR, offset ); } void InstructionEmitter::pop_param_byref( const std::string& name ) { unsigned offset = emit_data( name ); emit_token( INS_POP_PARAM_BYREF, TYP_OPERATOR, offset ); } void InstructionEmitter::progend() { emit_token( CTRL_PROGEND, TYP_CONTROL ); } void InstructionEmitter::return_from_user_function() { emit_token( RSV_RETURN, TYP_RESERVED ); } void InstructionEmitter::set_member_id_consume( MemberID member_id ) { emit_token( INS_SET_MEMBER_ID_CONSUME, TYP_UNARY_OPERATOR, member_id ); } void InstructionEmitter::set_member_id( MemberID member_id ) { emit_token( INS_SET_MEMBER_ID, TYP_UNARY_OPERATOR, member_id ); } void InstructionEmitter::set_member_consume( const std::string& name ) { unsigned offset = emit_data( name ); emit_token( INS_SET_MEMBER_CONSUME, TYP_UNARY_OPERATOR, offset ); } void InstructionEmitter::set_member( const std::string& name ) { unsigned offset = emit_data( name ); emit_token( INS_SET_MEMBER, TYP_UNARY_OPERATOR, offset ); } void InstructionEmitter::set_member_by_operator( BTokenId token_id, MemberID member_id ) { emit_token( token_id, TYP_UNARY_OPERATOR, member_id ); } unsigned InstructionEmitter::skip_if_true_else_consume() { return emit_token( INS_SKIPIFTRUE_ELSE_CONSUME, TYP_CONTROL ); } void InstructionEmitter::struct_create() { emit_token( TOK_STRUCT, TYP_OPERAND ); } void InstructionEmitter::struct_add_member( const std::string& name ) { auto offset = emit_data( name ); emit_token( INS_ADDMEMBER_ASSIGN, TYP_OPERAND, offset ); } void InstructionEmitter::struct_add_uninit_member( const std::string& name ) { auto offset = emit_data( name ); emit_token( INS_ADDMEMBER2, TYP_OPERAND, offset ); } void InstructionEmitter::subscript_single() { emit_token( TOK_ARRAY_SUBSCRIPT, TYP_OPERATOR, 1 ); } void InstructionEmitter::subscript_multiple( unsigned indexes ) { emit_token( INS_MULTISUBSCRIPT, TYP_OPERATOR, indexes ); } void InstructionEmitter::unary_operator( BTokenId token_id ) { emit_token( token_id, TYP_UNARY_OPERATOR ); } void InstructionEmitter::uninit() { emit_token( INS_UNINIT, TYP_OPERAND ); } void InstructionEmitter::value( double v ) { unsigned offset = data_emitter.append( v ); emit_token( TOK_DOUBLE, TYP_OPERAND, offset ); } void InstructionEmitter::value( int v ) { unsigned offset = data_emitter.append( v ); emit_token( TOK_LONG, TYP_OPERAND, offset ); } void InstructionEmitter::value( const std::string& v ) { unsigned data_offset = emit_data( v ); emit_token( TOK_STRING, TYP_OPERAND, data_offset ); } void InstructionEmitter::interpolate_string( unsigned count ) { emit_token( TOK_INTERPOLATE_STRING, TYP_OPERAND, count ); } void InstructionEmitter::format_expression() { emit_token( TOK_FORMAT_EXPRESSION, TYP_OPERAND ); } unsigned InstructionEmitter::emit_data( const std::string& s ) { return data_emitter.store( s ); } unsigned InstructionEmitter::emit_token( BTokenId id, BTokenType type, unsigned offset ) { StoredToken token( Mod_Basic, id, type, offset ); return append_token( token ); } unsigned InstructionEmitter::append_token( StoredToken& token ) { debug.add_instruction( debug_instruction_info ); debug_instruction_info.statement_begin = false; return code_emitter.append( token ); } void InstructionEmitter::debug_file_line( unsigned file, unsigned line ) { // debug info always has file #0 = empty (keeping for parity, for now) debug_instruction_info.file_index = file + 1; debug_instruction_info.line_number = line; } void InstructionEmitter::debug_statementbegin() { debug_instruction_info.statement_begin = true; } unsigned InstructionEmitter::next_instruction_address() { return code_emitter.next_address(); } void InstructionEmitter::debug_user_function( const std::string& name, unsigned first_pc, unsigned last_pc ) { DebugStore::UserFunctionInfo ufi{ name, first_pc, last_pc }; debug.add_user_function( std::move( ufi ) ); } void InstructionEmitter::patch_offset( unsigned index, unsigned offset ) { code_emitter.update_offset( index, offset ); } void InstructionEmitter::register_with_label( FlowControlLabel& label, unsigned offset ) { if ( label.has_address() ) { patch_offset( offset, label.address() ); } else { label.add_referencing_instruction_address( offset ); } } } // namespace Pol::Bscript::Compiler