polserver/pol-core/bscript/compiler/analyzer/ExpressionEvaluator.cpp
Kevin Eady 686090d950
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 09:49:55 +01:00

282 lines
6.8 KiB
C++

#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