polserver/pol-core/bscript/compiler/analyzer/ExpressionEvaluator.cpp

283 lines
6.8 KiB
C++
Raw Permalink Normal View History

Introduce DAP Debugger (#608) * Add cppdap debugger library and simple server (#508) * Add cppdap library * Add cppdap server * fixup cmake scripts for windows * Enable caching of cppdap on GitHub actions * Fix CMake for cppdap target * Wait 1s for session close before thread ends * Address review comments - Remove unnecessary use of wait_for - Fix wait_for method * Do not use SocketClientThread for cppdap clients * Address review comments - Cleanup object ownership * Add authorization and event handling (#510) * Add authorization and event handling [WIP] * Use std::weak_ptr; Only allow one debugger conn * Only attach debugger if dbg file exists * Cleanup weak_ptr; Fix listeners; Add handler stubs * Address review comments - PolLock on uoexec and weakptr access - Use make_shared when creating client thread * Add ExecutorDebugListener on_destroy callback * Use cppdap sockets instead of clib sockets * Update cppdap repository to 2703cf4 * Implement server address binding * Update cppdap repository to eeca5ce * Fix warnings * Use weak_from_this * Fix stalled executors on disconnect * Fix stalled executors on re-attach * Clear uoexec weakptr on all debugger detaches * Move server to gamestate * move DapDebugServer into Network namespace * move DapDebugServer into GameState for storage * Move DapDebugServer into NetworkManager * Fix missing memory include * Add more events (#513) * Add more events * Implement dbg_step_out * Fix breakpoint clearing * Add even more events (#515) * add launch request * Add custom `processes` command * fixup loop * Refactoring to move handlers to member functions * Add VariablesRequest and ScopesRequest * use std::varaint for Handles * add pollocks * refactor Handles to new file * move custom messages to proto.h * move clientthread * some formatting / cleanup * Add missing PolLock * move dap variables creation to Handles * appobj members * rename ClientThread to DebugClientThread * rename LaunchRequest args to arg * Fix compiler error on size_t * Add support for EvaluateRequest and SetVariableRequest (#519) * Update grammar for evaluateUnit * Cleanup various compiler code * Add support for EvaluateRequest Only supports values, variables, element access, member access * Add report clearing to reinitialize state * Add support for SetVariableRequest * Fix CI build errors * Move ExpressionEvaluator to Bscript * Fix formatting * Fix include * Refactor global and frame variable references * Encapsulate Executor debugging environment; Fix step over (#524) * Fix step over * Some cleanup of executor - Remove unused debug states - Rename members for consistency * move debugging environment to new class * Move instruction inspection to class * Cleanup slashes on Windows; Add Executor state to processes response (#527) * Fix backspaces in script names on Windows * Fix compilation warnings with data loss * Use boost::icontains for string comparison * Add Executor state to processes response * Updates for libfmt * Fix tests; Fix cppdap build for Mac universal binary (#607) * fix tests * fix cppdap build for mac universal binary * Update cppdap to 2a4c7cf * Add tests; Modify launch behavior to not stop at entry (redo) (#609) * Remove unused setExceptionBreakpoints handler * Add debugger tests * Launching a script should not set attaching state * Add launch tests * Do not check for connection in debugger tests * Rename `program` to `script` in protocol structs * Fix launch stopAtEntry; quote string var values * Fix compilation error on Windows * Add configfiles doc entry * Various fixes for finishing touches; Add core-changes (#610) * switch cmake projects * simple docs * cmake move make_directory * Fix launch request with relative, absolute, package paths * make relative paths as absolute in source request * Add true, false, uninit to ExpressionEvaluator * Re-add setExceptionBreakpoints to avoid unhandled message errors * Better debug thread logging with instance numbers * launched scripts should terminate on disconnect (according to DAP spec) * Properly handle exited event in client thread * Add core-changes * Address Discord comments - Mention vscode-escript in core-changes - Re-add "cppdap already built" in cmake * change cmake escriptgrammarlib visibility to public
2024-01-28 15:49:55 +07:00
#include "ExpressionEvaluator.h"
#include "bscript/compiler/ast/BooleanValue.h"
#include "bscript/compiler/ast/ElementAccess.h"
#include "bscript/compiler/ast/ElementIndexes.h"
#include "bscript/compiler/ast/Expression.h"
#include "bscript/compiler/ast/FloatValue.h"
#include "bscript/compiler/ast/FunctionCall.h"
#include "bscript/compiler/ast/FunctionReference.h"
#include "bscript/compiler/ast/Identifier.h"
#include "bscript/compiler/ast/IntegerValue.h"
#include "bscript/compiler/ast/MemberAccess.h"
#include "bscript/compiler/ast/StringValue.h"
#include "bscript/compiler/ast/UninitializedValue.h"
#include "bscript/compiler/file/SourceFile.h"
#include "bscript/executor.h"
#include "bscript/impstr.h"
namespace Pol::Bscript::Compiler
{
ExpressionEvaluator::ExpressionEvaluator()
: _profile(),
_report( false, false ),
_ident( 0, "<eval>" ),
_compiler_workspace( _report ),
_cache( _profile ),
_builder_workspace( _compiler_workspace, _cache, _cache, _profile, _report ),
_expression_builder( _ident, _builder_workspace )
{
}
BObjectRef ExpressionEvaluator::evaluate( Executor* exec, EScriptProgram* script,
std::string expression )
{
SourceFile source_file( "<eval>", expression, _profile );
_report.reset();
auto unit = source_file.get_evaluate_unit( _report );
if ( unit == nullptr || _report.error_count() || !unit->expression() )
{
throw std::runtime_error( "Invalid expression" );
}
EvaluationVisitor visitor( exec, script );
auto expression_node = _expression_builder.expression( unit->expression() );
expression_node->accept( visitor );
return visitor.result();
}
EvaluationVisitor::EvaluationVisitor( Executor* exec, EScriptProgram* script )
: _exec( exec ), _script( script )
{
}
void EvaluationVisitor::visit_identifier( Identifier& identifier )
{
visit_children( identifier );
BObjectRefVec::const_iterator itr = _exec->Globals2.begin(), end = _exec->Globals2.end();
BObjectRef result;
unsigned block = _script->dbg_ins_blocks[_exec->PC];
size_t left = _exec->Locals2->size();
while ( left )
{
while ( left <= _script->blocks[block].parentvariables )
{
block = _script->blocks[block].parentblockidx;
}
size_t varidx = left - 1 - _script->blocks[block].parentvariables;
if ( _script->blocks[block].localvarnames[varidx] == identifier.name )
{
stack.push( ( *_exec->Locals2 )[left - 1] );
return;
}
--left;
}
// Then check globals
for ( unsigned idx = 0; itr != end; ++itr, ++idx )
{
if ( _script->globalvarnames.size() > idx && _script->globalvarnames[idx] == identifier.name )
{
stack.push( _exec->Globals2[idx] );
return;
}
}
throw std::runtime_error( "Unknown variable " + identifier.name );
}
void EvaluationVisitor::visit_float_value( FloatValue& node )
{
stack.push( BObjectRef( new Double( node.value ) ) );
}
void EvaluationVisitor::visit_string_value( StringValue& node )
{
stack.push( BObjectRef( new String( node.value ) ) );
}
void EvaluationVisitor::visit_integer_value( IntegerValue& node )
{
stack.push( BObjectRef( new BLong( node.value ) ) );
}
void EvaluationVisitor::visit_boolean_value( BooleanValue& node )
{
stack.push( BObjectRef( new BBoolean( node.value ) ) );
}
void EvaluationVisitor::visit_uninitialized_value( UninitializedValue& )
{
stack.push( BObjectRef( UninitObject::create() ) );
}
// Operators
void EvaluationVisitor::visit_member_access( MemberAccess& member_access )
{
visit_children( member_access );
if ( member_access.known_member )
{
BObjectRef& leftref = stack.top();
BObject& left = *leftref;
leftref = left->get_member_id( member_access.known_member->id );
}
else
{
BObjectRef& leftref = stack.top();
BObject& left = *leftref;
leftref = left->get_member( member_access.name.c_str() );
}
}
void EvaluationVisitor::visit_element_access( ElementAccess& acc )
{
visit_children( acc );
auto indices_count = static_cast<unsigned>( acc.indexes().children.size() );
if ( indices_count == 1 )
{
BObjectRef rightref = stack.top();
stack.pop();
BObjectRef& leftref = stack.top();
leftref = ( *leftref )->OperSubscript( *rightref );
}
else
{
std::stack<BObjectRef> indices;
for ( size_t i = 0; i < indices_count; ++i )
{
indices.push( stack.top() );
stack.pop();
}
BObjectRef& leftref = stack.top();
leftref = ( *leftref )->OperMultiSubscript( indices );
}
}
void EvaluationVisitor::visit_argument( Argument& )
{
throw_invalid_expression();
}
void EvaluationVisitor::visit_array_initializer( ArrayInitializer& )
{
throw_invalid_expression();
}
void EvaluationVisitor::visit_binary_operator( BinaryOperator& )
{
throw_invalid_expression();
}
void EvaluationVisitor::visit_dictionary_entry( DictionaryEntry& )
{
throw_invalid_expression();
}
void EvaluationVisitor::visit_element_assignment( ElementAssignment& )
{
throw_invalid_expression();
}
void EvaluationVisitor::visit_elvis_operator( ElvisOperator& )
{
throw_invalid_expression();
}
void EvaluationVisitor::visit_error_initializer( ErrorInitializer& )
{
throw_invalid_expression();
}
void EvaluationVisitor::visit_member_assignment( MemberAssignment& )
{
throw_invalid_expression();
}
void EvaluationVisitor::visit_member_assignment_by_operator( MemberAssignmentByOperator& )
{
throw_invalid_expression();
}
void EvaluationVisitor::visit_method_call( MethodCall& )
{
throw_invalid_expression();
}
void EvaluationVisitor::visit_method_call_argument_list( MethodCallArgumentList& )
{
throw_invalid_expression();
}
void EvaluationVisitor::visit_interpolate_string( InterpolateString& )
{
throw_invalid_expression();
}
void EvaluationVisitor::visit_format_expression( FormatExpression& )
{
throw_invalid_expression();
}
void EvaluationVisitor::visit_struct_initializer( StructInitializer& )
{
throw_invalid_expression();
}
void EvaluationVisitor::visit_struct_member_initializer( StructMemberInitializer& )
{
throw_invalid_expression();
}
void EvaluationVisitor::visit_unary_operator( UnaryOperator& )
{
throw_invalid_expression();
}
void EvaluationVisitor::visit_function_call( FunctionCall& )
{
throw_invalid_expression();
}
void EvaluationVisitor::visit_function_reference( FunctionReference& )
{
throw_invalid_expression();
}
void EvaluationVisitor::visit_variable_assignment_statement( VariableAssignmentStatement& )
{
throw_invalid_expression();
}
void EvaluationVisitor::visit_conditional_operator( ConditionalOperator& )
{
throw_invalid_expression();
}
BObjectRef& EvaluationVisitor::result()
{
if ( stack.empty() )
{
throw std::runtime_error( "Error evaluating expression (empty result?)" );
}
return stack.top();
}
void EvaluationVisitor::throw_invalid_expression() const
{
throw std::runtime_error( "Unsupported expression" );
}
} // namespace Pol::Bscript::Compiler