mirror of
https://github.com/polserver/polserver
synced 2026-08-13 08:23:08 -04:00
* 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
177 lines
6.1 KiB
C++
177 lines
6.1 KiB
C++
#include "FunctionResolver.h"
|
|
|
|
#include "bscript/compiler/Profile.h"
|
|
#include "bscript/compiler/Report.h"
|
|
#include "bscript/compiler/ast/Function.h"
|
|
#include "bscript/compiler/ast/ModuleFunctionDeclaration.h"
|
|
#include "bscript/compiler/ast/UserFunction.h"
|
|
#include "bscript/compiler/astbuilder/AvailableUserFunction.h"
|
|
#include "bscript/compiler/file/SourceFileIdentifier.h"
|
|
#include "bscript/compiler/file/SourceLocation.h"
|
|
#include "bscript/compiler/model/FunctionLink.h"
|
|
|
|
namespace Pol::Bscript::Compiler
|
|
{
|
|
FunctionResolver::FunctionResolver( Report& report ) : report( report ) {}
|
|
FunctionResolver::~FunctionResolver() = default;
|
|
|
|
const Function* FunctionResolver::find( const std::string& scoped_name )
|
|
{
|
|
auto itr = resolved_functions_by_name.find( scoped_name );
|
|
if ( itr != resolved_functions_by_name.end() )
|
|
return ( *itr ).second;
|
|
else
|
|
return nullptr;
|
|
}
|
|
|
|
void FunctionResolver::force_reference( const std::string& function_name,
|
|
const SourceLocation& loc )
|
|
{
|
|
register_function_link( function_name, std::make_shared<FunctionLink>( loc ) );
|
|
}
|
|
|
|
void FunctionResolver::register_available_user_function(
|
|
const SourceLocation& source_location,
|
|
EscriptGrammar::EscriptParser::FunctionDeclarationContext* ctx )
|
|
{
|
|
register_available_user_function_parse_tree( source_location, ctx, ctx->IDENTIFIER(),
|
|
ctx->EXPORTED() );
|
|
}
|
|
|
|
void FunctionResolver::register_function_link( const std::string& name,
|
|
std::shared_ptr<FunctionLink> function_link )
|
|
{
|
|
auto already_resolved_itr = resolved_functions_by_name.find( name );
|
|
if ( already_resolved_itr != resolved_functions_by_name.end() )
|
|
{
|
|
function_link->link_to( ( *already_resolved_itr ).second );
|
|
}
|
|
else
|
|
{
|
|
unresolved_function_links_by_name[name].push_back( std::move( function_link ) );
|
|
}
|
|
}
|
|
|
|
std::string FunctionResolver::register_function_expression(
|
|
const SourceLocation& source_location,
|
|
EscriptGrammar::EscriptParser::FunctionExpressionContext* ctx )
|
|
{
|
|
auto name = function_expression_name( source_location );
|
|
auto auf = AvailableUserFunction{ source_location, ctx };
|
|
available_user_function_parse_trees.insert( { name, auf } );
|
|
return name;
|
|
}
|
|
|
|
void FunctionResolver::register_module_function( ModuleFunctionDeclaration* mf )
|
|
{
|
|
const auto& name = mf->name;
|
|
auto scoped_name = mf->module_name + "::" + name;
|
|
|
|
auto itr = available_user_function_parse_trees.find( name );
|
|
if ( itr != available_user_function_parse_trees.end() )
|
|
{
|
|
const auto& previous = ( *itr ).second;
|
|
|
|
report.error( previous.source_location,
|
|
"User Function '{}' conflicts with Module Function of the same name.\n"
|
|
" Module Function declaration: {}",
|
|
name, mf->source_location );
|
|
}
|
|
|
|
resolved_functions_by_name[name] = mf;
|
|
resolved_functions_by_name[scoped_name] = mf;
|
|
}
|
|
|
|
void FunctionResolver::register_user_function( UserFunction* uf )
|
|
{
|
|
resolved_functions_by_name[uf->name] = uf;
|
|
}
|
|
|
|
bool FunctionResolver::resolve( std::vector<AvailableUserFunction>& to_build_ast )
|
|
{
|
|
for ( auto unresolved_itr = unresolved_function_links_by_name.begin();
|
|
unresolved_itr != unresolved_function_links_by_name.end(); )
|
|
{
|
|
const std::string& name = ( *unresolved_itr ).first;
|
|
const std::vector<std::shared_ptr<FunctionLink>>& function_links = ( *unresolved_itr ).second;
|
|
|
|
auto previously_resolved_itr = resolved_functions_by_name.find( name );
|
|
if ( previously_resolved_itr != resolved_functions_by_name.end() )
|
|
{
|
|
Function* resolved_function = ( *previously_resolved_itr ).second;
|
|
for ( auto& function_link : function_links )
|
|
{
|
|
function_link->link_to( resolved_function );
|
|
}
|
|
|
|
unresolved_itr = unresolved_function_links_by_name.erase( unresolved_itr );
|
|
}
|
|
else
|
|
{
|
|
auto available_itr = available_user_function_parse_trees.find( name );
|
|
if ( available_itr != available_user_function_parse_trees.end() )
|
|
{
|
|
to_build_ast.push_back( ( *available_itr ).second );
|
|
available_user_function_parse_trees.erase( available_itr );
|
|
++unresolved_itr;
|
|
}
|
|
else
|
|
{
|
|
// Do not complain here, as the identifier in `name` may be a variable.
|
|
// Error handled in SemanticAnalyzer.
|
|
unresolved_itr = unresolved_function_links_by_name.erase( unresolved_itr );
|
|
}
|
|
}
|
|
}
|
|
|
|
return !to_build_ast.empty();
|
|
}
|
|
|
|
std::string FunctionResolver::function_expression_name(
|
|
const SourceLocation& source_location )
|
|
{
|
|
return fmt::format( "funcexpr@{}:{}:{}", source_location.source_file_identifier->index,
|
|
source_location.range.start.line_number,
|
|
source_location.range.start.character_column );
|
|
}
|
|
|
|
void FunctionResolver::register_available_user_function_parse_tree(
|
|
const SourceLocation& source_location, antlr4::ParserRuleContext* ctx,
|
|
antlr4::tree::TerminalNode* identifier, antlr4::tree::TerminalNode* exported )
|
|
{
|
|
std::string name = identifier->getSymbol()->getText();
|
|
auto itr = available_user_function_parse_trees.find( name );
|
|
if ( itr != available_user_function_parse_trees.end() )
|
|
{
|
|
AvailableUserFunction& previous = ( *itr ).second;
|
|
|
|
report.error( source_location,
|
|
"Function '{}' defined more than once.\n"
|
|
" Previous declaration: {}",
|
|
name, previous.source_location );
|
|
}
|
|
|
|
auto itr2 = resolved_functions_by_name.find( name );
|
|
if ( itr2 != resolved_functions_by_name.end() )
|
|
{
|
|
auto* previous = ( *itr2 ).second;
|
|
|
|
report.error( source_location,
|
|
"User Function '{}' conflicts with Module Function of the same name.\n"
|
|
" Module Function declaration: {}",
|
|
name, previous->source_location );
|
|
}
|
|
|
|
auto auf = AvailableUserFunction{ source_location, ctx };
|
|
available_user_function_parse_trees.insert( { name, auf } );
|
|
|
|
if ( exported )
|
|
{
|
|
auto function_name = identifier->getSymbol()->getText();
|
|
|
|
// just make sure there is an entry, so that we build an AST for it
|
|
unresolved_function_links_by_name[function_name];
|
|
}
|
|
}
|
|
|
|
} // namespace Pol::Bscript::Compiler
|