polserver/pol-core/bscript/compiler/codegen/InstructionEmitter.cpp

763 lines
22 KiB
C++
Raw Permalink Normal View History

#include "bscript/compiler/codegen/InstructionEmitter.h"
#include <limits>
#include <list>
#include <set>
#include "bscript/StoredToken.h"
#include "bscript/compiler/Report.h"
#include "bscript/compiler/ast/ClassDeclaration.h"
#include "bscript/compiler/ast/ModuleFunctionDeclaration.h"
#include "bscript/compiler/ast/UserFunction.h"
#include "bscript/compiler/codegen/CaseJumpDataBlock.h"
#include "bscript/compiler/codegen/ClassDeclarationRegistrar.h"
#include "bscript/compiler/codegen/FunctionReferenceRegistrar.h"
#include "bscript/compiler/codegen/ModuleDeclarationRegistrar.h"
#include "bscript/compiler/model/ClassLink.h"
#include "bscript/compiler/model/FlowControlLabel.h"
#include "bscript/compiler/model/FunctionLink.h"
#include "bscript/compiler/model/LocalVariableScopeInfo.h"
#include "bscript/compiler/model/ScopableName.h"
#include "bscript/compiler/model/Variable.h"
#include "bscript/compiler/representation/ClassDescriptor.h"
#include "bscript/compiler/representation/CompiledScript.h"
#include "bscript/compiler/representation/ConstructorDescriptor.h"
#include "bscript/compiler/representation/ExportedFunction.h"
#include "bscript/escriptv.h"
#include "bscript/modules.h"
#include "bscript/token.h"
#include "bscript/tokens.h"
namespace Pol::Bscript::Compiler
{
InstructionEmitter::InstructionEmitter( CodeSection& code, DataSection& data, DebugStore& debug,
ExportedFunctions& exported_functions,
ModuleDeclarationRegistrar& module_declaration_registrar,
FunctionReferenceRegistrar& function_reference_registrar,
ClassDeclarationRegistrar& class_declaration_registrar,
Report& report )
Add compiler support for function expressions (#671) * initial poc of function expressions - update grammar - mock AST builder to return BBoolean(true) for a func expr - update prettifier for skeleton implementation * more skeleton work create AST class FunctionExpression mimicking boolean value * create ast UserFunction, add to workspace from functexpr * can generate instructions * reorg tests; add test for instructions * can track FunctionDepth in Variable * can get captures for funcexprs inside funcs.. tbd if this way of nesting works * Implement create-functor instruction - Move function depth from Variable to Variables - Introduce stacking of `Variables` for function expresions via `FunctionVariableScope` - Add `TOK_FUNCTOR` instruction for 'create-functor' - Handle emitting a `FunctionExpression` AST node - Update `emit.declare_variable` and `emit.access_variable` to account for function captures - Remove `in_function` from instruction generator, as it is tracked via `UserFunction` stack - Update tests * Address Discord comments - Swap pop param order * Bubble up captured variables through nested functions * Improve testing infrastructure; add some test cases * Fix compilation error * prepend captures to function parameters in funcref mth_call * update tests * move from function{} to @{} * implementation fix * some more tests * update docs * Some cleanup * fix CI annotation warning - 'argument': conversion from 'size_t' to 'VariableIndex', possible loss of data * Address review comments - Add `passert_always` - Use better example in docs * Some cleanup - Remove unused functions
2024-07-29 22:30:52 +02:00
: code_emitter( code ),
data_emitter( data ),
debug( debug ),
exported_functions( exported_functions ),
module_declaration_registrar( module_declaration_registrar ),
function_reference_registrar( function_reference_registrar ),
class_declaration_registrar( class_declaration_registrar ),
report( report )
{
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() );
}
void InstructionEmitter::register_class_declaration(
const ClassDeclaration& node, std::map<std::string, FlowControlLabel>& user_function_labels )
{
std::set<std::string> visited;
std::set<std::string, Clib::ci_cmp_pred> visited_methods;
std::vector<ConstructorDescriptor> constructor_descriptors;
std::set<std::string> method_names;
std::vector<MethodDescriptor> method_descriptors;
std::list<const ClassDeclaration*> to_link( { &node } );
const auto& class_name = node.name;
auto class_name_offset = this->emit_data( class_name );
unsigned constructor_function_reference_index = std::numeric_limits<unsigned>::max();
report.debug( node, "Registering class: {}", node.name );
if ( node.constructor_link )
{
if ( auto uf = node.constructor_link->user_function() )
{
auto ctor_itr = user_function_labels.find( uf->scoped_name() );
if ( ctor_itr == user_function_labels.end() )
{
uf->internal_error(
fmt::format( "Constructor {} not found in user_function_labels", uf->scoped_name() ) );
}
function_reference_registrar.lookup_or_register_reference(
*uf, ctor_itr->second, constructor_function_reference_index );
}
}
if ( constructor_function_reference_index < std::numeric_limits<unsigned>::max() )
report.debug( node, " - Constructor at FuncRef index {}",
constructor_function_reference_index );
for ( auto itr = to_link.begin(); itr != to_link.end(); ++itr )
{
auto cd = *itr;
if ( visited.find( cd->name ) != visited.end() )
continue;
visited.insert( cd->name );
report.debug( *cd, "Class {} with {} methods", cd->name, cd->methods.size() );
if ( cd->constructor_link && cd->constructor_link->user_function() )
{
auto type_tag_offset = emit_data( cd->type_tag() );
constructor_descriptors.emplace_back( type_tag_offset );
}
for ( const auto& [method, uf_link] : cd->methods )
{
auto uf = uf_link->user_function();
if ( !uf )
{
cd->internal_error( fmt::format( "method {}::{} no function linked", cd->name, method ) );
}
auto method_itr = user_function_labels.find( ScopableName( cd->name, method ).string() );
if ( method_itr == user_function_labels.end() )
{
report.debug( *cd, " - Method: {} label=???", method );
cd->internal_error( fmt::format( "Method {} not found in user_function_labels", method ) );
}
auto address = method_itr->second.address();
if ( address == 0 )
{
report.debug( *cd, " - Method: {} PC=???", method );
cd->internal_error( fmt::format( "Method {} has no PC for attached label", method ) );
}
bool use_method = method_names.find( method ) == method_names.end();
if ( use_method )
{
unsigned funcref_index;
function_reference_registrar.lookup_or_register_reference( *uf, method_itr->second,
funcref_index );
auto name_offset = this->emit_data( method );
method_descriptors.emplace_back( name_offset, address, funcref_index );
report.debug( *cd, " - Method: {} PC={} funcref_index={}", method,
method_itr->second.address(), funcref_index );
method_names.insert( method );
}
else
{
report.debug( *cd, " - Method: {} PC={} [ignored]", method, method_itr->second.address() );
}
}
for ( const auto& base_cd_link : cd->base_class_links )
{
if ( auto base_cd = base_cd_link->class_declaration() )
{
to_link.push_back( base_cd );
}
}
}
report.debug( node, "Class: {}", node.name );
for ( const auto& constructor : constructor_descriptors )
{
report.debug( node, " - Constructor @ type_tag_offset={}", constructor.type_tag_offset );
}
for ( const auto& method_info : method_descriptors )
{
report.debug( node, " - Method @ PC={} name_offset={} funcref_index={} ", method_info.address,
method_info.name_offset, method_info.function_reference_index );
}
class_declaration_registrar.register_class( class_name_offset,
constructor_function_reference_index,
constructor_descriptors, method_descriptors );
}
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;
}
Add compiler support for function expressions (#671) * initial poc of function expressions - update grammar - mock AST builder to return BBoolean(true) for a func expr - update prettifier for skeleton implementation * more skeleton work create AST class FunctionExpression mimicking boolean value * create ast UserFunction, add to workspace from functexpr * can generate instructions * reorg tests; add test for instructions * can track FunctionDepth in Variable * can get captures for funcexprs inside funcs.. tbd if this way of nesting works * Implement create-functor instruction - Move function depth from Variable to Variables - Introduce stacking of `Variables` for function expresions via `FunctionVariableScope` - Add `TOK_FUNCTOR` instruction for 'create-functor' - Handle emitting a `FunctionExpression` AST node - Update `emit.declare_variable` and `emit.access_variable` to account for function captures - Remove `in_function` from instruction generator, as it is tracked via `UserFunction` stack - Update tests * Address Discord comments - Swap pop param order * Bubble up captured variables through nested functions * Improve testing infrastructure; add some test cases * Fix compilation error * prepend captures to function parameters in funcref mth_call * update tests * move from function{} to @{} * implementation fix * some more tests * update docs * Some cleanup * fix CI annotation warning - 'argument': conversion from 'size_t' to 'VariableIndex', possible loss of data * Address review comments - Add `passert_always` - Use better example in docs * Some cleanup - Remove unused functions
2024-07-29 22:30:52 +02:00
// - When visiting an identifier:
// - If type == Capture: set to Local and ValueStack offset will be current function's param count +
// this variable index
// - If type == Local: if offset >= function param count, add current function's capture count
void InstructionEmitter::access_variable( const Variable& v, VariableIndex function_params_count,
VariableIndex function_capture_count )
{
Add compiler support for function expressions (#671) * initial poc of function expressions - update grammar - mock AST builder to return BBoolean(true) for a func expr - update prettifier for skeleton implementation * more skeleton work create AST class FunctionExpression mimicking boolean value * create ast UserFunction, add to workspace from functexpr * can generate instructions * reorg tests; add test for instructions * can track FunctionDepth in Variable * can get captures for funcexprs inside funcs.. tbd if this way of nesting works * Implement create-functor instruction - Move function depth from Variable to Variables - Introduce stacking of `Variables` for function expresions via `FunctionVariableScope` - Add `TOK_FUNCTOR` instruction for 'create-functor' - Handle emitting a `FunctionExpression` AST node - Update `emit.declare_variable` and `emit.access_variable` to account for function captures - Remove `in_function` from instruction generator, as it is tracked via `UserFunction` stack - Update tests * Address Discord comments - Swap pop param order * Bubble up captured variables through nested functions * Improve testing infrastructure; add some test cases * Fix compilation error * prepend captures to function parameters in funcref mth_call * update tests * move from function{} to @{} * implementation fix * some more tests * update docs * Some cleanup * fix CI annotation warning - 'argument': conversion from 'size_t' to 'VariableIndex', possible loss of data * Address review comments - Add `passert_always` - Use better example in docs * Some cleanup - Remove unused functions
2024-07-29 22:30:52 +02:00
BTokenId token_id;
unsigned offset;
if ( v.scope == VariableScope::Capture )
{
token_id = TOK_LOCALVAR;
offset = v.index + function_params_count;
}
else if ( v.scope == VariableScope::Local )
{
token_id = TOK_LOCALVAR;
if ( v.index >= function_params_count )
{
offset = v.index + function_capture_count;
}
else
{
offset = v.index;
}
}
else
{
token_id = TOK_GLOBALVAR;
offset = v.index;
}
emit_token( token_id, TYP_OPERAND, offset );
}
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, VariableIndex function_params_count,
VariableIndex function_capture_count )
{
BTokenId token_id;
unsigned offset;
if ( v.scope == VariableScope::Capture )
{
token_id = INS_ASSIGN_LOCALVAR;
offset = v.index + function_params_count;
}
else if ( v.scope == VariableScope::Local )
{
token_id = INS_ASSIGN_LOCALVAR;
if ( v.index >= function_params_count )
{
offset = v.index + function_capture_count;
}
else
{
offset = v.index;
}
}
else
{
token_id = INS_ASSIGN_GLOBALVAR;
offset = v.index;
}
emit_token( token_id, TYP_UNARY_OPERATOR, offset );
}
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 ) );
}
2020-09-01 22:49:48 -07:00
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<unsigned char>( module_id ), TOK_FUNC,
static_cast<BTokenType>(
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 );
}
void InstructionEmitter::check_mro( unsigned offset )
{
emit_token( INS_CHECK_MRO, TYP_CONTROL, offset );
}
void InstructionEmitter::classinst_create( unsigned index )
{
emit_token( TOK_CLASSINST, TYP_OPERAND, index );
}
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<std::byte*>( &debug_token ), sizeof debug_token );
emit_token( CTRL_STATEMENTBEGIN, TYP_CONTROL, offset );
}
Add compiler support for function expressions (#671) * initial poc of function expressions - update grammar - mock AST builder to return BBoolean(true) for a func expr - update prettifier for skeleton implementation * more skeleton work create AST class FunctionExpression mimicking boolean value * create ast UserFunction, add to workspace from functexpr * can generate instructions * reorg tests; add test for instructions * can track FunctionDepth in Variable * can get captures for funcexprs inside funcs.. tbd if this way of nesting works * Implement create-functor instruction - Move function depth from Variable to Variables - Introduce stacking of `Variables` for function expresions via `FunctionVariableScope` - Add `TOK_FUNCTOR` instruction for 'create-functor' - Handle emitting a `FunctionExpression` AST node - Update `emit.declare_variable` and `emit.access_variable` to account for function captures - Remove `in_function` from instruction generator, as it is tracked via `UserFunction` stack - Update tests * Address Discord comments - Swap pop param order * Bubble up captured variables through nested functions * Improve testing infrastructure; add some test cases * Fix compilation error * prepend captures to function parameters in funcref mth_call * update tests * move from function{} to @{} * implementation fix * some more tests * update docs * Some cleanup * fix CI annotation warning - 'argument': conversion from 'size_t' to 'VariableIndex', possible loss of data * Address review comments - Add `passert_always` - Use better example in docs * Some cleanup - Remove unused functions
2024-07-29 22:30:52 +02:00
// - When declaring an identifier:
// - If type == Local: if offset >= function param count, add current function's capture count
void InstructionEmitter::declare_variable( const Variable& v, VariableIndex function_capture_count,
bool take )
{
Add compiler support for function expressions (#671) * initial poc of function expressions - update grammar - mock AST builder to return BBoolean(true) for a func expr - update prettifier for skeleton implementation * more skeleton work create AST class FunctionExpression mimicking boolean value * create ast UserFunction, add to workspace from functexpr * can generate instructions * reorg tests; add test for instructions * can track FunctionDepth in Variable * can get captures for funcexprs inside funcs.. tbd if this way of nesting works * Implement create-functor instruction - Move function depth from Variable to Variables - Introduce stacking of `Variables` for function expresions via `FunctionVariableScope` - Add `TOK_FUNCTOR` instruction for 'create-functor' - Handle emitting a `FunctionExpression` AST node - Update `emit.declare_variable` and `emit.access_variable` to account for function captures - Remove `in_function` from instruction generator, as it is tracked via `UserFunction` stack - Update tests * Address Discord comments - Swap pop param order * Bubble up captured variables through nested functions * Improve testing infrastructure; add some test cases * Fix compilation error * prepend captures to function parameters in funcref mth_call * update tests * move from function{} to @{} * implementation fix * some more tests * update docs * Some cleanup * fix CI annotation warning - 'argument': conversion from 'size_t' to 'VariableIndex', possible loss of data * Address review comments - Add `passert_always` - Use better example in docs * Some cleanup - Remove unused functions
2024-07-29 22:30:52 +02:00
int offset;
if ( v.scope == VariableScope::Local && v.index >= function_capture_count )
{
offset = v.index + function_capture_count;
}
else
{
offset = v.index;
}
BTokenId token_id = v.scope == VariableScope::Global ? ( take ? INS_TAKE_GLOBAL : RSV_GLOBAL )
: ( take ? INS_TAKE_LOCAL : RSV_LOCAL );
Add compiler support for function expressions (#671) * initial poc of function expressions - update grammar - mock AST builder to return BBoolean(true) for a func expr - update prettifier for skeleton implementation * more skeleton work create AST class FunctionExpression mimicking boolean value * create ast UserFunction, add to workspace from functexpr * can generate instructions * reorg tests; add test for instructions * can track FunctionDepth in Variable * can get captures for funcexprs inside funcs.. tbd if this way of nesting works * Implement create-functor instruction - Move function depth from Variable to Variables - Introduce stacking of `Variables` for function expresions via `FunctionVariableScope` - Add `TOK_FUNCTOR` instruction for 'create-functor' - Handle emitting a `FunctionExpression` AST node - Update `emit.declare_variable` and `emit.access_variable` to account for function captures - Remove `in_function` from instruction generator, as it is tracked via `UserFunction` stack - Update tests * Address Discord comments - Swap pop param order * Bubble up captured variables through nested functions * Improve testing infrastructure; add some test cases * Fix compilation error * prepend captures to function parameters in funcref mth_call * update tests * move from function{} to @{} * implementation fix * some more tests * update docs * Some cleanup * fix CI annotation warning - 'argument': conversion from 'size_t' to 'VariableIndex', possible loss of data * Address review comments - Add `passert_always` - Use better example in docs * Some cleanup - Remove unused functions
2024-07-29 22:30:52 +02:00
emit_token( token_id, TYP_RESERVED, offset );
}
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( const UserFunction& uf, FlowControlLabel& label )
{
unsigned index;
function_reference_registrar.lookup_or_register_reference( uf, label, index );
emit_token( TOK_FUNCREF, TYP_OPERAND, index );
}
void InstructionEmitter::functor_create( const UserFunction& uf, FlowControlLabel& label )
Add compiler support for function expressions (#671) * initial poc of function expressions - update grammar - mock AST builder to return BBoolean(true) for a func expr - update prettifier for skeleton implementation * more skeleton work create AST class FunctionExpression mimicking boolean value * create ast UserFunction, add to workspace from functexpr * can generate instructions * reorg tests; add test for instructions * can track FunctionDepth in Variable * can get captures for funcexprs inside funcs.. tbd if this way of nesting works * Implement create-functor instruction - Move function depth from Variable to Variables - Introduce stacking of `Variables` for function expresions via `FunctionVariableScope` - Add `TOK_FUNCTOR` instruction for 'create-functor' - Handle emitting a `FunctionExpression` AST node - Update `emit.declare_variable` and `emit.access_variable` to account for function captures - Remove `in_function` from instruction generator, as it is tracked via `UserFunction` stack - Update tests * Address Discord comments - Swap pop param order * Bubble up captured variables through nested functions * Improve testing infrastructure; add some test cases * Fix compilation error * prepend captures to function parameters in funcref mth_call * update tests * move from function{} to @{} * implementation fix * some more tests * update docs * Some cleanup * fix CI annotation warning - 'argument': conversion from 'size_t' to 'VariableIndex', possible loss of data * Address review comments - Add `passert_always` - Use better example in docs * Some cleanup - Remove unused functions
2024-07-29 22:30:52 +02:00
{
unsigned reference_index;
function_reference_registrar.lookup_or_register_reference( uf, label, reference_index );
StoredToken token( static_cast<unsigned char>( Mod_Basic ), TOK_FUNCTOR,
static_cast<BTokenType>(
reference_index ), // index to the EScriptProgram's function_references,
// stored in Token.lval, saved in StoredToken.type
0 );
append_token( token );
Add compiler support for function expressions (#671) * initial poc of function expressions - update grammar - mock AST builder to return BBoolean(true) for a func expr - update prettifier for skeleton implementation * more skeleton work create AST class FunctionExpression mimicking boolean value * create ast UserFunction, add to workspace from functexpr * can generate instructions * reorg tests; add test for instructions * can track FunctionDepth in Variable * can get captures for funcexprs inside funcs.. tbd if this way of nesting works * Implement create-functor instruction - Move function depth from Variable to Variables - Introduce stacking of `Variables` for function expresions via `FunctionVariableScope` - Add `TOK_FUNCTOR` instruction for 'create-functor' - Handle emitting a `FunctionExpression` AST node - Update `emit.declare_variable` and `emit.access_variable` to account for function captures - Remove `in_function` from instruction generator, as it is tracked via `UserFunction` stack - Update tests * Address Discord comments - Swap pop param order * Bubble up captured variables through nested functions * Improve testing infrastructure; add some test cases * Fix compilation error * prepend captures to function parameters in funcref mth_call * update tests * move from function{} to @{} * implementation fix * some more tests * update docs * Some cleanup * fix CI annotation warning - 'argument': conversion from 'size_t' to 'VariableIndex', possible loss of data * Address review comments - Add `passert_always` - Use better example in docs * Some cleanup - Remove unused functions
2024-07-29 22:30:52 +02:00
}
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::return_from_constructor_function( unsigned this_offset )
{
// Emit `this`
emit_token( TOK_LOCALVAR, TYP_OPERAND, this_offset );
// Emit a return
return_from_user_function();
}
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 );
}
void InstructionEmitter::spread( bool spread_into )
{
emit_token( TOK_SPREAD, TYP_OPERAND, spread_into );
}
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::unpack_sequence( unsigned count, unsigned rest_at )
{
// Two-byte offset encodes (1) if rest unpacking, (2) index of rest binding, (3) the number of
// bindings: aa'bbbbbbb'ccccccc => a: is rest, b: rest index, c: number of bindings
unsigned short offset = rest_at == 0xFF
? ( count & 0x7F )
: ( 1 << 14 ) | ( ( rest_at & 0x7F ) << 7 ) | ( count & 0x7F );
emit_token( INS_UNPACK_SEQUENCE, TYP_RESERVED, offset );
}
void InstructionEmitter::unpack_indices( unsigned count, unsigned rest_at )
{
// Two-byte offset encodes (1) if rest unpacking, (2) index of rest binding, (3) the number of
// bindings: aa'bbbbbbb'ccccccc => a: is rest, b: rest index, c: number of bindings
unsigned short offset = rest_at == 0xFF
? ( count & 0x7F )
: ( 1 << 14 ) | ( ( rest_at & 0x7F ) << 7 ) | ( count & 0x7F );
emit_token( INS_UNPACK_INDICES, TYP_RESERVED, offset );
}
2020-08-19 10:43:08 -07:00
void InstructionEmitter::value( double v )
{
unsigned offset = data_emitter.append( v );
emit_token( TOK_DOUBLE, TYP_OPERAND, offset );
}
2020-08-25 23:27:24 -07:00
void InstructionEmitter::value( int v )
{
unsigned offset = data_emitter.append( v );
emit_token( TOK_LONG, TYP_OPERAND, offset );
}
void InstructionEmitter::value( bool v )
{
emit_token( TOK_BOOL, TYP_OPERAND, v );
}
void InstructionEmitter::value( const std::string& v )
{
unsigned data_offset = emit_data( v );
emit_token( TOK_STRING, TYP_OPERAND, data_offset );
}
Add support for regular expressions (#818) * implementation * tests * maybe fix windows compilation? * undo change of match_flag_type * switch to boost regex * move flags to BRegExp object * fix tests due to cmake 4 update * update grammar * update prettifier * move flag parsing to BRegExp ctor * add AST nodes, update instr generation and execution * copy tests but use regular expression literals * fix grammar for handling division correctly * modify return values a bit, update tests - string.match: make groups hold structs of matched, offset - string.match: add offset - string.replace: use a groups array like string.match vs individual arguments * bundle of changes - standardize error messages - add more tests for coverage - move BRegExp creation to static method: previous implementation had ctor throwing, which was no good inside executor since it didn't have a try/catch * more tests; add OT_REGEXP to basic.em * move string regex stuff to bregexp and support regex/wregex via std::visit * maybe fix compilation errors? * add unicode escape sequence handling * really maybe fix compilation error? * fix compiler warnings * refactor a bit to remove duplicate code * address discord comments - just return wstring, no need for vector<wchar_t> method * Add string.split by string and regexp * Use Max_Split instead of Limit This makes it match basic::SplitWords * add support for empty string delim in SplitWords * remove duplicate code across string.split and mf_SplitWords * Squashed commit of the following: commit b3148e069f4c4bad041b36144d312f375c028a6c Author: turleypol <turley@polserver.com> Date: Sat Sep 27 17:02:22 2025 +0200 memorylocation of input string is not allowed to changed, switched back to uniqueptr commit 5a4d1c8fcb2d8239ba0b693644dd20d9a5a167bb Author: turleypol <turley@polserver.com> Date: Sat Sep 27 10:18:01 2025 +0200 no need to use ptr for input commit 90fa0edc66034db77a43222612b4981bf09081a5 Author: turleypol <turley@polserver.com> Date: Sat Sep 27 09:56:29 2025 +0200 fixed typo commit bed4ff3ac9de88fb9e91350125339c8ab04c2da3 Author: turleypol <turley@polserver.com> Date: Sat Sep 27 09:47:36 2025 +0200 make Callback for BContinuation move only * use uninit when group isn't matched * fix multiline flag handling; add test * add docs and doc example tests * some cleanup * address review comments - allow move assignment * add core-changes
2025-11-17 18:02:45 +01:00
void InstructionEmitter::regular_expression_value( const std::string& pattern,
const std::string& flags )
{
value( pattern );
value( flags );
emit_token( TOK_REGEXP, TYP_OPERAND );
}
Add support for interpolated strings ("interstrings") (#369) * Update grammar to support interpolated strings * Struct initializers, case switchs are regular strings * Initial stub for interpolated string in AST This splits the previous STRING_LITERAL terminal into a production, stringLiteral, to handle REGULAR_STRING terminal and interpolatedString production. * Change interpolated strings to expressions * Fix mode handling in grammar * Grammar fixes - Fix mode handling in lexer - Interpolated strings only have one expression * Can successfully parse, stub compile interstrings $"He {there + print("hello")}"; * Add formatting string to grammar * Fix tests, and add test stubs * Rename curleys to brace; better AST generation * initial work interstring instruction * Initial work on formatted string instruction: emit * Formatted string instruction: execution * Add simple formatted string test * Add formatted string test src * Review changes #1 - Rename `FormattedString` to `FormatExpression` - Rename overloaded `try_to_format()` to `get_formatted()` * Review changes part 2 - Rename `InterpolatedString` ast node to `InterpolateString` * Review changes part 3 - Modify `ins_interpolate_string` * Review changes part 4 - Remove unused formal parameter * Add negative tests * Spruce up positive tests * Fix braces, escape chars inside interstring * Fix, add tests for escaped char and double brace * Address differences for new lines in tests * Update Escript version * Make empty expression an error * Add documentation for interpolated strings * Quick touchup on docs
2021-03-11 22:48:16 +01:00
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 );
}
2020-08-19 10:43:08 -07:00
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 );
}
bool InstructionEmitter::has_function_reference( const UserFunction& uf )
{
unsigned index;
return function_reference_registrar.lookup_reference( uf, index );
}
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 );
}
}
void InstructionEmitter::logical_jmp( FlowControlLabel& label, bool if_true )
{
register_with_label(
label, emit_token( INS_LOGICAL_JUMP, if_true ? TYP_RESERVED : TYP_LOGICAL_JUMP_FALSE ) );
}
void InstructionEmitter::logical_convert()
{
emit_token( INS_LOGICAL_CONVERT, TYP_OPERAND );
}
} // namespace Pol::Bscript::Compiler