mirror of
https://github.com/polserver/polserver
synced 2026-08-13 08:23:08 -04:00
Adds: - ast/CaseDispatchDefaultSelector: AST node for a "default:" selector - ast/CaseDispatchGroup: AST node for a collection of selectors and the code to execute - ast/CaseDispatchGroups: AST node holding all groups in a case statement - ast/CaseDispatchSelectors: AST node holding all selectors for one group - ast/CaseStatement: AST node for a whole case statement - codegen/CaseDispatchGroupVisitor: knows what to put in a case jump dispatch table - codegen/CaseJumpDataBlock: knows how to build the entries in the data block Also, detect multiple case selectors with the same value (or default). This is different from the OG compiler, which only detected duplicate `default:` selectors.
317 lines
7.9 KiB
C++
317 lines
7.9 KiB
C++
#include "InstructionGenerator.h"
|
|
|
|
#include <boost/range/adaptor/reversed.hpp>
|
|
|
|
#include "compiler/ast/AssignVariableConsume.h"
|
|
#include "compiler/ast/BinaryOperator.h"
|
|
#include "compiler/ast/Block.h"
|
|
#include "compiler/ast/CaseDispatchGroup.h"
|
|
#include "compiler/ast/CaseDispatchGroups.h"
|
|
#include "compiler/ast/CaseDispatchSelectors.h"
|
|
#include "compiler/ast/CaseStatement.h"
|
|
#include "compiler/ast/FloatValue.h"
|
|
#include "compiler/ast/FunctionBody.h"
|
|
#include "compiler/ast/FunctionCall.h"
|
|
#include "compiler/ast/FunctionParameterDeclaration.h"
|
|
#include "compiler/ast/FunctionParameterList.h"
|
|
#include "compiler/ast/Identifier.h"
|
|
#include "compiler/ast/IfThenElseStatement.h"
|
|
#include "compiler/ast/IntegerValue.h"
|
|
#include "compiler/ast/JumpStatement.h"
|
|
#include "compiler/ast/ModuleFunctionDeclaration.h"
|
|
#include "compiler/ast/Program.h"
|
|
#include "compiler/ast/ProgramParameterDeclaration.h"
|
|
#include "compiler/ast/ReturnStatement.h"
|
|
#include "compiler/ast/StringValue.h"
|
|
#include "compiler/ast/UnaryOperator.h"
|
|
#include "compiler/ast/UserFunction.h"
|
|
#include "compiler/ast/ValueConsumer.h"
|
|
#include "compiler/ast/VarStatement.h"
|
|
#include "compiler/ast/WhileLoop.h"
|
|
#include "compiler/codegen/CaseDispatchGroupVisitor.h"
|
|
#include "compiler/codegen/CaseJumpDataBlock.h"
|
|
#include "compiler/codegen/InstructionEmitter.h"
|
|
#include "compiler/file/SourceFileIdentifier.h"
|
|
#include "compiler/model/FlowControlLabel.h"
|
|
#include "compiler/model/FunctionLink.h"
|
|
#include "compiler/model/Variable.h"
|
|
#include "symcont.h"
|
|
|
|
namespace Pol::Bscript::Compiler
|
|
{
|
|
InstructionGenerator::InstructionGenerator(
|
|
InstructionEmitter& emitter, std::map<std::string, FlowControlLabel>& user_function_labels,
|
|
bool in_function )
|
|
: emitter( emitter ),
|
|
emit( emitter ),
|
|
user_function_labels( user_function_labels ),
|
|
in_function( in_function )
|
|
{
|
|
}
|
|
|
|
void InstructionGenerator::generate( Node& node )
|
|
{
|
|
// alternative: two identical methods 'evaluate' and 'execute', for readability
|
|
node.accept( *this );
|
|
}
|
|
|
|
void InstructionGenerator::visit_assign_variable_consume( AssignVariableConsume& node )
|
|
{
|
|
generate( node.rhs() );
|
|
auto& identifier = node.identifier();
|
|
auto& variable = identifier.variable;
|
|
|
|
emit.assign_variable( *variable );
|
|
}
|
|
|
|
void InstructionGenerator::visit_case_statement( CaseStatement& node )
|
|
{
|
|
generate( node.expression() );
|
|
const unsigned casejmp = emit.casejmp();
|
|
|
|
CaseJumpDataBlock data_block;
|
|
|
|
FlowControlLabel default_label;
|
|
|
|
auto& groups = node.dispatch_groups();
|
|
for ( int i = 0, c = groups.children.size(); i < c; ++i )
|
|
{
|
|
CaseDispatchGroup& group = groups.dispatch_group( i );
|
|
FlowControlLabel group_label;
|
|
emit.label( group_label );
|
|
generate( group.block() );
|
|
|
|
emit.label( *group.break_label );
|
|
bool last = i == c - 1;
|
|
if ( !last )
|
|
emit.jmp_always( *node.break_label );
|
|
|
|
CaseDispatchGroupVisitor visitor( data_block, group_label, default_label );
|
|
group.selectors().accept( visitor );
|
|
}
|
|
|
|
if ( !default_label.has_address() )
|
|
emit.label( default_label );
|
|
|
|
data_block.on_default_jump_to( default_label.address() );
|
|
|
|
emit.label( *node.break_label );
|
|
|
|
unsigned dispatch_table_data_offset = emit.case_dispatch_table( data_block );
|
|
emitter.patch_offset( casejmp, dispatch_table_data_offset );
|
|
}
|
|
|
|
void InstructionGenerator::visit_binary_operator( BinaryOperator& node )
|
|
{
|
|
visit_children( node );
|
|
|
|
emit.binary_operator( node.token_id );
|
|
}
|
|
|
|
void InstructionGenerator::visit_block( Block& node )
|
|
{
|
|
visit_children( node );
|
|
|
|
if ( node.locals_in_block )
|
|
{
|
|
emit.leaveblock( node.locals_in_block );
|
|
}
|
|
}
|
|
|
|
void InstructionGenerator::visit_exit_statement( ExitStatement& )
|
|
{
|
|
emit.exit();
|
|
}
|
|
|
|
void InstructionGenerator::visit_float_value( FloatValue& node )
|
|
{
|
|
emit.value( node.value );
|
|
}
|
|
|
|
void InstructionGenerator::visit_function_call( FunctionCall& call )
|
|
{
|
|
visit_children( call );
|
|
|
|
if ( auto mf = call.function_link->module_function_declaration() )
|
|
{
|
|
emit.call_modulefunc( *mf );
|
|
}
|
|
else if ( auto uf = call.function_link->user_function() )
|
|
{
|
|
FlowControlLabel& label = user_function_labels[uf->name];
|
|
emit.makelocal();
|
|
emit.call_userfunc( label );
|
|
}
|
|
else
|
|
{
|
|
call.internal_error( "neither a module function nor a user function?" );
|
|
}
|
|
}
|
|
|
|
void InstructionGenerator::visit_function_parameter_list( FunctionParameterList& node )
|
|
{
|
|
for ( auto& child : boost::adaptors::reverse( node.children ) )
|
|
{
|
|
child->accept( *this );
|
|
}
|
|
}
|
|
|
|
void InstructionGenerator::visit_function_parameter_declaration(
|
|
FunctionParameterDeclaration& node )
|
|
{
|
|
if ( node.byref )
|
|
emit.pop_param_byref( node.name );
|
|
else
|
|
emit.pop_param( node.name );
|
|
}
|
|
|
|
void InstructionGenerator::visit_identifier( Identifier& node )
|
|
{
|
|
if ( auto var = node.variable )
|
|
{
|
|
emit.access_variable( *var );
|
|
}
|
|
else
|
|
{
|
|
node.internal_error( "variable is not set" );
|
|
}
|
|
}
|
|
|
|
void InstructionGenerator::visit_if_then_else_statement( IfThenElseStatement& node )
|
|
{
|
|
bool invert_jump = false;
|
|
auto predicate = &node.predicate();
|
|
if ( auto unary_operator = dynamic_cast<UnaryOperator*>( predicate ) )
|
|
{
|
|
if ( unary_operator->token_id == TOK_LOG_NOT )
|
|
{
|
|
invert_jump = true;
|
|
predicate = &unary_operator->operand();
|
|
}
|
|
}
|
|
generate( *predicate );
|
|
|
|
FlowControlLabel skip_consequent;
|
|
|
|
if ( invert_jump )
|
|
emit.jmp_if_true( skip_consequent );
|
|
else
|
|
emit.jmp_if_false( skip_consequent );
|
|
|
|
generate( node.consequent() );
|
|
|
|
if ( auto alternative = node.alternative() )
|
|
{
|
|
FlowControlLabel skip_alternative;
|
|
emit.jmp_always( skip_alternative );
|
|
emit.label( skip_consequent );
|
|
generate( *alternative );
|
|
emit.label( skip_alternative );
|
|
}
|
|
else
|
|
{
|
|
emit.label( skip_consequent );
|
|
}
|
|
}
|
|
|
|
void InstructionGenerator::visit_integer_value( IntegerValue& node )
|
|
{
|
|
emit.value( node.value );
|
|
}
|
|
|
|
void InstructionGenerator::visit_jump_statement( JumpStatement& jump )
|
|
{
|
|
if ( jump.local_variables_to_remove )
|
|
emit.leaveblock( jump.local_variables_to_remove );
|
|
emit.jmp_always( *jump.flow_control_label );
|
|
}
|
|
|
|
void InstructionGenerator::visit_program( Program& program )
|
|
{
|
|
visit_children( program );
|
|
|
|
if ( program.locals_in_block )
|
|
{
|
|
emit.leaveblock( program.locals_in_block );
|
|
}
|
|
}
|
|
|
|
void InstructionGenerator::visit_program_parameter_declaration( ProgramParameterDeclaration& param )
|
|
{
|
|
emit.get_arg( param.name );
|
|
}
|
|
|
|
void InstructionGenerator::visit_return_statement( ReturnStatement& ret )
|
|
{
|
|
visit_children( ret );
|
|
|
|
if ( in_function )
|
|
{
|
|
emit.return_from_user_function();
|
|
}
|
|
else
|
|
{
|
|
emit.progend();
|
|
}
|
|
}
|
|
|
|
void InstructionGenerator::visit_string_value( StringValue& lit )
|
|
{
|
|
emit.value( lit.value );
|
|
}
|
|
|
|
void InstructionGenerator::visit_unary_operator( UnaryOperator& unary_operator )
|
|
{
|
|
visit_children( unary_operator );
|
|
emit.unary_operator( unary_operator.token_id );
|
|
}
|
|
|
|
void InstructionGenerator::visit_user_function( UserFunction& user_function )
|
|
{
|
|
FlowControlLabel& label = user_function_labels[user_function.name];
|
|
emit.label( label );
|
|
visit_children( user_function );
|
|
|
|
if ( !dynamic_cast<ReturnStatement*>( user_function.body().last_statement() ) )
|
|
{
|
|
emit.value( 0 );
|
|
emit.return_from_user_function();
|
|
}
|
|
}
|
|
|
|
void InstructionGenerator::visit_value_consumer( ValueConsumer& node )
|
|
{
|
|
visit_children( node );
|
|
|
|
emit.consume();
|
|
}
|
|
|
|
void InstructionGenerator::visit_var_statement( VarStatement& node )
|
|
{
|
|
if ( !node.variable )
|
|
node.internal_error( "variable is not defined" );
|
|
emit.declare_variable( *node.variable );
|
|
|
|
if ( node.initialize_as_empty_array )
|
|
{
|
|
emit.array_declare();
|
|
}
|
|
else if ( !node.children.empty() )
|
|
{
|
|
visit_children( node );
|
|
|
|
emit.assign();
|
|
}
|
|
}
|
|
|
|
void InstructionGenerator::visit_while_loop( WhileLoop& loop )
|
|
{
|
|
emit.label( *loop.continue_label );
|
|
generate( loop.predicate() );
|
|
emit.jmp_if_false( *loop.break_label );
|
|
generate( loop.block() );
|
|
emit.jmp_always( *loop.continue_label );
|
|
emit.label( *loop.break_label );
|
|
}
|
|
|
|
} // namespace Pol::Bscript::Compiler
|