diff --git a/pol-core/bscript/CMakeSources.cmake b/pol-core/bscript/CMakeSources.cmake index 4f6c198d7d..918ac18179 100644 --- a/pol-core/bscript/CMakeSources.cmake +++ b/pol-core/bscript/CMakeSources.cmake @@ -13,7 +13,6 @@ set (bscript_sources # sorted ! StoredToken.h compiler/Compiler.cpp compiler/Compiler.h - compiler/LegacyFunctionOrder.h compiler/analyzer/Constants.cpp compiler/analyzer/Constants.h compiler/analyzer/Disambiguator.cpp @@ -285,8 +284,6 @@ set (bscript_sources # sorted ! bstruct.h compctx.cpp compctx.h - compiler.cpp - compiler.h compilercfg.cpp compilercfg.h compmodl.h @@ -319,6 +316,7 @@ set (bscript_sources # sorted ! fmodule.h impstr.h modules.h + objaccess.cpp object.cpp object.h objmembers.h @@ -326,8 +324,6 @@ set (bscript_sources # sorted ! objstrm.cpp operator.h options.h - parser.cpp - parser.h str.cpp symcont.cpp symcont.h diff --git a/pol-core/bscript/compiler.cpp b/pol-core/bscript/compiler.cpp deleted file mode 100644 index d277ff3105..0000000000 --- a/pol-core/bscript/compiler.cpp +++ /dev/null @@ -1,4465 +0,0 @@ -/** @file - * - * @par History - * - 2005/07/26 Shinigami: if you use 'Include ":blah:blubb";' eCompile will search for - * ":blah:blubb.inc" and ":blah:include/blubb.inc". It will use - * first file found. If both files exist eCompile will print a Warning. - * - 2005/07/28 Shinigami: Assignment inside Condition Check will produce Warning on -v5 only - * - 2005-09-07 Folko: No longer warn about unused variables in BASIC style for loops - * - 2005/09/08 Shinigami: Will warn about unused variables in BASIC style for loops on -v5 only - * - 2005/09/25 Shinigami: BugFix inside FileCheck for multiple include of same File - * e.g.: inside scripts extcmd est extcmd.src: - * Include "../../../pkg/std/housing/include/test"; - * Include ":housing:test"; - * Include ":housing:include/test"; - * will be handled as same file now - * - 2005/11/26 Shinigami: changed "strcmp" into "stricmp" to suppress Script Errors - */ - - -#include "compiler.h" - -#include -#include -#include -#include -#include -#include -#include - -#include "../clib/clib.h" -#include "../clib/filecont.h" -#include "../clib/fileutil.h" -#include "../clib/logfacility.h" -#include "../clib/passert.h" -#include "../clib/stlutil.h" -#include "../clib/strutil.h" -#include "../plib/pkg.h" -#include "compctx.h" -#include "bscript/compiler/LegacyFunctionOrder.h" -#include "compilercfg.h" -#include "eprog.h" -#include "expression.h" -#include "fmodule.h" -#include "modules.h" -#include "objmembers.h" -#include "symcont.h" -#include "token.h" -#include "tokens.h" -#include "userfunc.h" -#include -#include - -namespace Pol -{ -namespace Bscript -{ -extern int include_debug; -namespace Legacy -{ -bool Compiler::check_filecase_; - - -std::string getpathof( const std::string& fname ) -{ - std::string::size_type pos = fname.find_last_of( "\\/" ); - if ( pos == std::string::npos ) - return "./"; - else - return fname.substr( 0, pos + 1 ); -} - - -bool Scope::varexists( const std::string& varname, unsigned& idx ) const -{ - for ( int i = static_cast( variables_.size() - 1 ); i >= 0; --i ) - { - if ( Clib::stringicmp( varname, variables_[i].name ) == 0 ) - { - if ( variables_[i].unused ) - { - compiler_warning( nullptr, "Warning: variable '", variables_[i].name, - "' declared as unused but used.\n" ); - } - variables_[i].used = true; - idx = i; - return true; - } - } - return false; -} - -bool Scope::varexists( const std::string& varname ) const -{ - for ( int i = static_cast( variables_.size() - 1 ); i >= 0; --i ) - { - if ( Clib::stringicmp( varname, variables_[i].name ) == 0 ) - { - return true; - } - } - return false; -} - -BlockDesc& Scope::pushblock() -{ - blockdescs_.push_back( BlockDesc() ); - return blockdescs_.back(); -} - -void Scope::popblock( bool varsOnly = false ) -{ - BlockDesc& bd = blockdescs_.back(); - - for ( ; bd.varcount; bd.varcount-- ) // To enable popping variables only - { - Variable& bk = variables_.back(); - if ( !bk.used && !bk.unused ) - { - compiler_warning( &bk.ctx, "Warning: local variable '", bk.name, "' not used.\n" ); - } - variables_.pop_back(); - } - - if ( !varsOnly ) - blockdescs_.pop_back(); -} - -void Scope::addvar( const std::string& varname, const CompilerContext& ctx, bool warn_on_notused, - bool unused ) -{ - for ( size_t i = variables_.size() - blockdescs_.back().varcount; i < variables_.size(); ++i ) - { - if ( Clib::stringicmp( varname, variables_[i].name ) == 0 ) - { - throw std::runtime_error( "Variable " + varname + " is already in scope." ); - } - } - Variable newvar; - newvar.name = varname; - newvar.ctx = ctx; - newvar.used = !warn_on_notused; - newvar.unused = unused; - variables_.push_back( newvar ); - blockdescs_.back().varcount++; -} - -void Scope::addvalue() -{ - blockdescs_.back().valcount++; -} - -void Compiler::enterblock( eb_label_ok eblabel, eb_break_ok ebbreak, eb_continue_ok ebcontinue ) -{ - program->enterblock(); - - BlockDesc& bd = localscope.pushblock(); - bd.varcount = 0; - bd.label_ok = eblabel; - bd.break_ok = ebbreak; - bd.continue_ok = ebcontinue; - - if ( bd.label_ok == CanBeLabelled ) - { - bd.label = latest_label; - latest_label = ""; - } - else - { - bd.label = ""; - } -} -void Compiler::enterblock( eb_label_ok et ) -{ - enterblock( et, et ? BreakOk : BreakNotOk, et ? ContinueOk : ContinueNotOk ); -} - -void Compiler::patchblock_breaks( unsigned breakPC ) -{ - // now, patch up the GOTO part of BREAK statements. - // they each have a LEAVE_BLOCK appropriate to where they are. - const BlockDesc& bd = localscope.blockdesc(); - for ( auto patchip : bd.break_tokens ) - { - patchoffset( patchip, breakPC ); // program->tokens.next() - } -} - -void Compiler::patchblock_continues( unsigned continuePC ) -{ - const BlockDesc& bd = localscope.blockdesc(); - for ( auto patchip : bd.continue_tokens ) - { - patchoffset( patchip, continuePC ); - } -} - -void Compiler::emit_leaveblock() -{ - if ( localscope.numVarsInBlock() ) - { // local variables were declared in this scope. We need to kill 'em. - program->append( - StoredToken( Mod_Basic, CTRL_LEAVE_BLOCK, TYP_CONTROL, localscope.numVarsInBlock() ) ); - } -} - -void Compiler::leaveblock( unsigned breakPC, unsigned continuePC ) -{ - emit_leaveblock(); - patchblock_breaks( breakPC ); - patchblock_continues( continuePC ); - - localscope.popblock(); - program->leaveblock(); -} - -Compiler::Compiler() - : SmartParser(), - current_file_path( "" ), - curSourceFile( 0 ), - inExpr( 0 ), - inFunction( 0 ), - haveProgram( false ), - compiling_include( false ), - programPos( 0 ), - nProgramArgs( 0 ), - program_ctx(), - program_source( nullptr ), - included(), - referencedPathnames(), - program( new EScriptProgram ) -{ - setQuiet( 1 ); - err = PERR_NONE; -} - -Compiler::~Compiler() -{ - while ( !delete_these_arrays.empty() ) - { - char* s = delete_these_arrays.back(); - delete[] s; - delete_these_arrays.pop_back(); - } -} - - -bool Compiler::globalexists( const std::string& varname, unsigned& idx, CompilerContext* ctx ) const -{ - for ( unsigned i = 0; i < static_cast( globals_.size() ); ++i ) - { - if ( Clib::stringicmp( varname, globals_[i].name ) == 0 ) - { - idx = i; - if ( ctx ) - *ctx = globals_[i].ctx; - return true; - } - } - return false; -} - -bool Compiler::varexists( const std::string& varname ) const -{ - unsigned idx; - if ( localscope.varexists( varname, idx ) ) - return true; - - if ( globalexists( varname, idx ) ) - return true; - - return false; -} - -int Compiler::isLegal( Token& token ) -{ - if ( inExpr && ( token.id == TOK_ASSIGN ) ) - { - if ( compilercfg.VerbosityLevel >= 5 ) - compiler_warning( nullptr, "Warning! possible incorrect assignment.\n", "Near: ", curLine, - "\n" ); - } - - return 1; // assignments valid everywhere. back to simple parser -} - -struct Candidate -{ - Candidate( int module, int funcidx ) : module( module ), funcidx( funcidx ), modfunc( nullptr ) {} - int module; - int funcidx; - ModuleFunction* modfunc; -}; - -int Compiler::isFunc( Token& token, ModuleFunction** pmf ) -{ - typedef std::vector Candidates; - Candidates candidates; - std::string modulename; - std::string funcname; - - if ( const char* colon = strchr( token.tokval(), ':' ) ) - { - std::string tmp( token.tokval(), colon ); - if ( tmp.length() >= 9 ) - { - compiler_error( "'", tmp, "' is too long to be a module name.\n" ); - return -1; - } - modulename = tmp; - funcname = std::string( colon + 2 ); - } - else - { - modulename = ""; - funcname = token.tokval(); - } - - for ( unsigned i = 0; i < program->modules.size(); i++ ) - { - if ( !modulename.empty() && - !( modulename == program->modules[i]->modulename ) ) // STLport doesn't like != here - { - continue; - } - - int funcidx; - if ( program->modules[i]->isFunc( funcname.c_str(), pmf, &funcidx ) ) - { - candidates.push_back( Candidate( i, funcidx ) ); - } - } - - if ( candidates.empty() ) - { - return 0; - } - else if ( candidates.size() == 1 ) - { - token.module = - static_cast( candidates[0].module ); // WAS module,we're using relative now - token.type = TYP_FUNC; - token.id = TOK_FUNC; - token.lval = candidates[0].funcidx; - token.userfunc = ( *pmf )->uf; - return 1; - } - else - { - compiler_error( "Function '", funcname, - "' exists in more than module. It must be qualified.\n" ); - for ( Candidates::const_iterator itr = candidates.begin(); itr != candidates.end(); ++itr ) - { - compiler_error( "\t", program->modules[itr->module]->modulename.get(), "\n" ); - } - - return -1; - } -} - -void Compiler::addModule( FunctionalityModule* module ) -{ - module->fillFunctionsByName(); - program->modules.push_back( module ); -} - -int Compiler::isUserFunc( Token& token, UserFunction** f ) -{ - if ( token.id != TOK_IDENT ) - return 0; - passert( token.tokval() ); - - auto itr = userFunctions.find( token.tokval() ); - if ( itr != userFunctions.end() ) - { - token.module = Mod_Basic; - token.type = TYP_USERFUNC; - token.id = TOK_USERFUNC; - token.userfunc = &( *itr ).second; - *f = &( *itr ).second; - return 1; - } - return 0; -} -int Compiler::getArrayElements( Expression& expr, CompilerContext& ctx ) -{ - int res; - Token token; - res = peekToken( ctx, token ); - if ( res < 0 ) - return res; - if ( token.id != TOK_LPAREN ) - return 0; - getToken( ctx, token ); - - // it's valid to have a right-paren immediately following, so check for that: - - res = peekToken( ctx, token ); - if ( res < 0 ) - return res; - if ( token.id == TOK_RPAREN ) - { - getToken( ctx, token ); - return 0; - } - - - for ( ;; ) - { - res = peekToken( ctx, token ); - if ( res < 0 ) - return res; - /* - if we get an rparen HERE, it means the script has something like - var x := array ( 2, 3, ); - report this as an error. - */ - if ( token.id == TOK_RPAREN ) - { - compiler_error( - "Expected expression following comma before right-brace in array initializer list\n" ); - return -1; - } - if ( token.id == TOK_COMMA ) - { - compiler_error( "Unexpected comma in array initializer list\n" ); - return -1; - } - res = read_subexpression( expr, ctx, - EXPR_FLAG_COMMA_TERM_ALLOWED | EXPR_FLAG_RIGHTPAREN_TERM_ALLOWED ); - if ( res < 0 ) - return res; - - expr.CA.push( new Token( TOK_INSERTINTO, TYP_OPERATOR ) ); - - res = getToken( ctx, token ); - if ( res < 0 ) - return res; - if ( token.id == TOK_COMMA ) - { - continue; - } - else if ( token.id == TOK_RPAREN ) - { - return 0; - } - else - { - compiler_error( "Token '", token, "' unexpected in array initializer list\n" ); - return -1; - } - } -} - -int Compiler::getNewArrayElements( Expression& expr, CompilerContext& ctx ) -{ - int res; - Token token; - // the left-brace has already been eaten - - // it's valid to have a right-brace immediately following, so check for that: - - res = peekToken( ctx, token ); - if ( res < 0 ) - return res; - if ( token.id == TOK_RBRACE ) - { - getToken( ctx, token ); - return 0; - } - - for ( ;; ) - { - res = peekToken( ctx, token ); - if ( res < 0 ) - return res; - - // if we get an rbrace HERE, it means the script has something like - // var x := array { 2, 3, }; - // report this as an error. - if ( token.id == TOK_RBRACE ) - { - compiler_error( - "Expected expression following comma before right-brace in array initializer list\n" ); - return -1; - } - // we're expecting an expression, not a comma, at this point - if ( token.id == TOK_COMMA ) - { - compiler_error( "Unexpected comma in array initializer list\n" ); - return -1; - } - res = read_subexpression( expr, ctx, - EXPR_FLAG_COMMA_TERM_ALLOWED | EXPR_FLAG_RIGHTBRACE_TERM_ALLOWED ); - if ( res < 0 ) - return res; - - expr.CA.push( new Token( TOK_INSERTINTO, TYP_OPERATOR ) ); - - // the element can be followed by a comma, or by a rightbrace. eat either. - res = getToken( ctx, token ); - if ( res < 0 ) - return res; - if ( token.id == TOK_COMMA ) - { - continue; - } - else if ( token.id == TOK_RBRACE ) - { - return 0; - } - else - { - compiler_error( "Token '", token, "' unexpected in array initializer list\n" ); - return -1; - } - } -} - -int Compiler::getStructMembers( Expression& expr, CompilerContext& ctx ) -{ - int res; - Token token; - res = peekToken( ctx, token ); - if ( res < 0 ) - return res; - if ( token.id != TOK_LBRACE ) - return 0; - getToken( ctx, token ); - - // it's valid to have a right-brace immediately following, so check for that: - res = peekToken( ctx, token ); - if ( res < 0 ) - return res; - if ( token.id == TOK_RBRACE ) - { - getToken( ctx, token ); - return 0; - } - - - for ( ;; ) - { - res = peekToken( ctx, token ); - if ( res < 0 ) - return res; - // if we get an rbrace HERE, it means the script has something like - // var x := struct { x, y, }; - // report this as an error. - if ( token.id == TOK_RBRACE ) - { - compiler_error( - "Expected expression following comma before right-brace in struct initializer list\n" ); - return -1; - } - if ( token.id == TOK_COMMA ) - { - compiler_error( "Unexpected comma in struct element list\n" ); - return -1; - } - - if ( token.id == TOK_IDENT || token.id == TOK_STRING ) - { - Token ident_tkn; - getTokenWithoutConversions( ctx, ident_tkn ); - - res = peekToken( ctx, token ); - if ( token.id == TOK_ASSIGN ) - { - getToken( ctx, token ); - // something like struct { a := 5 }; - res = read_subexpression( - expr, ctx, EXPR_FLAG_COMMA_TERM_ALLOWED | EXPR_FLAG_RIGHTBRACE_TERM_ALLOWED ); - if ( res < 0 ) - return res; - - auto addmem = new Token( ident_tkn ); - addmem->id = INS_ADDMEMBER_ASSIGN; - - expr.CA.push( addmem ); - } - else if ( token.id == TOK_EQUAL1 ) - { - compiler_error( "Unexpected token: '", token, "'. Did you mean := for assign?\n" ); - return -1; - } - else - { - auto addmem = new Token( ident_tkn ); - addmem->id = INS_ADDMEMBER2; - expr.CA.push( addmem ); - } - } - else - { - compiler_error( "Unexpected token in struct initializer list: ", token, "\n" ); - return -1; - } - - - res = getToken( ctx, token ); - if ( res < 0 ) - return res; - if ( token.id == TOK_COMMA ) - { - continue; - } - else if ( token.id == TOK_RBRACE ) - { - return 0; - } - else - { - compiler_error( "Token '", token, "' unexpected in struct initializer list\n" ); - return -1; - } - } - // unreachable -} - -int Compiler::getDictionaryMembers( Expression& expr, CompilerContext& ctx ) -{ - int res; - Token token; - res = peekToken( ctx, token ); - if ( res < 0 ) - return res; - if ( token.id != TOK_LBRACE ) - return 0; - getToken( ctx, token ); - - // it's valid to have a right-brace immediately following, so check for that: - res = peekToken( ctx, token ); - if ( res < 0 ) - return res; - if ( token.id == TOK_RBRACE ) - { - getToken( ctx, token ); - return 0; - } - - - for ( ;; ) - { - res = peekToken( ctx, token ); - if ( res < 0 ) - return res; - // if we get an rbrace HERE, it means the script has something like - // var x := dictionary { "x", "y", }; - // report this as an error. - if ( token.id == TOK_RBRACE ) - { - compiler_error( - "Expected expression following comma before right-brace in dictionary initializer " - "list\n" ); - return -1; - } - if ( token.id == TOK_COMMA ) - { - compiler_error( "Unexpected comma in dictionary element list\n" ); - return -1; - } - - - // first get the key expression. - res = read_subexpression( expr, ctx, - EXPR_FLAG_COMMA_TERM_ALLOWED | EXPR_FLAG_DICTKEY_TERM_ALLOWED | - EXPR_FLAG_RIGHTBRACE_TERM_ALLOWED ); - if ( res < 0 ) - return res; - - // if the key is followed by "->", then grab the value - res = peekToken( ctx, token ); - if ( res < 0 ) - return res; - if ( token.id == TOK_DICTKEY ) - { - getToken( ctx, token ); - // get the value expression - - res = read_subexpression( expr, ctx, - EXPR_FLAG_COMMA_TERM_ALLOWED | EXPR_FLAG_RIGHTBRACE_TERM_ALLOWED ); - if ( res < 0 ) - return res; - } - else - { - // an uninit - expr.CA.push( new Token( INS_UNINIT, TYP_OPERAND ) ); - } - - expr.CA.push( new Token( INS_DICTIONARY_ADDMEMBER, TYP_OPERATOR ) ); - - res = getToken( ctx, token ); - if ( res < 0 ) - return res; - if ( token.id == TOK_COMMA ) - { - continue; - } - else if ( token.id == TOK_RBRACE ) - { - return 0; - } - else - { - compiler_error( "Token '", token, "' unexpected in struct element list\n" ); - return -1; - } - } - // unreachable -} - -int Compiler::getMethodArguments( Expression& expr, CompilerContext& ctx, int& nargs ) -{ - int res; - Token token; - nargs = 0; - res = getToken( ctx, token ); - passert( token.id == TOK_LPAREN ); - passert( res == 0 ); - for ( ;; ) - { - res = peekToken( ctx, token ); - if ( res < 0 ) - return res; - if ( token.id == TOK_RPAREN ) - { - getToken( ctx, token ); - return 0; - } - if ( token.id == TOK_COMMA ) - { - compiler_error( "Unexpected comma in array element list\n" ); - return -1; - } - res = read_subexpression( expr, ctx, - EXPR_FLAG_COMMA_TERM_ALLOWED | EXPR_FLAG_RIGHTPAREN_TERM_ALLOWED ); - if ( res < 0 ) - return res; - - ++nargs; - - res = getToken( ctx, token ); - if ( res < 0 ) - return res; - if ( token.id == TOK_COMMA ) - { - continue; - } - else if ( token.id == TOK_RPAREN ) - { - return 0; - } - else - { - compiler_error( "Token '", token, "' unexpected in array element list\n" ); - return -1; - } - } - // unreachable -} - -int Compiler::getFunctionPArgument( Expression& /*expr*/, CompilerContext& ctx, Token* ref_tkn ) -{ - int res; - Token token; - res = getToken( ctx, *ref_tkn ); - if ( res < 0 || ref_tkn->id != TOK_USERFUNC ) - { - compiler_error( "Expected user function reference.\n" ); - return -1; - } - ref_tkn->id = TOK_FUNCREF; - ref_tkn->type = TYP_OPERAND; - return 0; -} - -/* -getUserArgs is called from IIP. -It is being converted from calling IIP (and thus stuffing -expression data onto ex) to calling readexpr() and -appending the argument expressions onto the passed -ex object. - -Overall function: -Read the parameters actually passed. -Append expressions for parameters to passed expression. -Append a JSR instruction -*/ - -/* -class ParamPassed -{ -public: -ParamPassed( const std::string& name ) : name(name) {} - -string name; -Expression expr; -}; -*/ - -int Compiler::getUserArgs( Expression& ex, CompilerContext& ctx, bool inject_jsr ) -{ - int res; - Token token; - - typedef std::map ParamList; - ParamList params_passed; - // std::vector func_params; - - int any_named = 0; - UserFunction* userfunc = userfunc_; - - getToken( ctx, token ); - if ( token.id != TOK_LPAREN ) - { - compiler_error( "Expected '(' after function name '", userfunc->name, "'\n" ); - res = -1; - err = PERR_MISSLPAREN; - return -1; - } - - for ( ;; ) - { - std::string varname; - Token tk; - - CompilerContext tctx( ctx ); - - res = getTokenWithoutConversions( tctx, tk ); - if ( res < 0 ) - return res; - if ( tk.id == TOK_RPAREN ) - { - if ( params_passed.empty() ) - { - break; - } - else - { - compiler_error( "right paren not allowed here\n" ); - return -1; - } - } - - if ( params_passed.size() >= userfunc->parameters.size() ) - { - compiler_error( "Too many parameters passed to ", userfunc->name, "\n" ); - return -1; - } - - if ( tk.id == TOK_IDENT ) - { - Token tk2; - res = getToken( tctx, tk2 ); - if ( res < 0 ) - return res; - if ( tk2.id == TOK_ASSIGN ) - { - any_named = 1; - varname = tk.tokval(); - ctx = tctx; /* skip past the 'variable :=' part */ - } - else if ( tk2.id == TOK_EQUAL1 ) - { - compiler_error( "Unexpected token: '", tk2, "'. Did you mean := for assign?\n" ); - return -1; - } - } - if ( varname == "" ) - { - if ( any_named ) - { - compiler_error( "unnamed args cannot follow named args\n" ); - return -1; - } - varname = userfunc->parameters[params_passed.size()].name; - } - // FIXME case sensitivity! - if ( params_passed.find( varname ) != params_passed.end() ) - { - compiler_error( "Variable ", varname, " passed more than once to ", userfunc->name, "\n" ); - return -1; - } - - Expression& arg_expr = params_passed[varname]; - - res = - readexpr( arg_expr, ctx, EXPR_FLAG_COMMA_TERM_ALLOWED | EXPR_FLAG_RIGHTPAREN_TERM_ALLOWED ); - if ( res < 0 ) - return res; - - Token tmp; - res = peekToken( ctx, tmp ); - if ( res ) - return res; - if ( tmp.id == TOK_COMMA ) - { - getToken( ctx, tmp ); - } - else if ( tmp.id == TOK_RPAREN ) - { - break; - } - else - { - compiler_error( "Token '", token, "' unexpected (expected comma or right-paren)\n" ); - return -1; - } - } - - for ( UserFunction::Parameters::const_iterator itr = userfunc->parameters.begin(); - itr != userfunc->parameters.end(); ++itr ) - { - if ( params_passed.find( itr->name ) == params_passed.end() ) // not passed - { - if ( itr->have_default ) - { - ex.CA.push( new Token( itr->dflt_value ) ); - } - else - { - compiler_error( "Function ", userfunc->name, ": Parameter ", itr->name, - " was not passed, and there is no default.\n" ); - return -1; - } - } - else - { - Expression& arg_expr = params_passed[itr->name]; - ex.consume_tokens( arg_expr ); - params_passed.erase( itr->name ); - } - } - - if ( !params_passed.empty() ) - { - for ( const auto& elem : params_passed ) - { - compiler_error( "Parameter '", elem.first, "' passed by name to ", userfunc->name, - ", which takes no such parameter.\n" ); - } - - return -1; - } - passert( params_passed.empty() ); - - getToken( ctx, token ); - if ( token.id != TOK_RPAREN ) - { - res = -1; - err = PERR_MISSRPAREN; - return -1; - } - - if ( inject_jsr ) - { - auto t = new Token( CTRL_MAKELOCAL, TYP_CONTROL ); - t->dbg_filenum = ctx.dbg_filenum; - t->dbg_linenum = ctx.line; - ex.CA.push( t ); - t = new Token( Mod_Basic, CTRL_JSR_USERFUNC, TYP_CONTROL, userfunc ); - t->dbg_filenum = ctx.dbg_filenum; - t->dbg_linenum = ctx.line; - ex.CA.push( t ); - } - - return 0; -} - -void Compiler::addToken( Token& token ) -{ - program->addToken( token ); -} - -void Compiler::convert_variables( Expression& expr ) const -{ - for ( auto& tkn : expr.tokens ) - { - if ( tkn->id == TOK_IDENT ) - { - unsigned idx; - if ( localscope.varexists( tkn->tokval(), idx ) ) - { - tkn->id = TOK_LOCALVAR; - tkn->lval = idx; - } - else if ( globalexists( tkn->tokval(), idx ) ) - { - tkn->id = TOK_GLOBALVAR; - tkn->lval = idx; - } - } - } -} - - -int Compiler::validate( const Expression& expr, CompilerContext& ctx ) const -{ - for ( unsigned i = 0; i < static_cast( expr.tokens.size() ); i++ ) - { - Token* tkn = expr.tokens[i]; - - if ( tkn->id == TOK_IDENT ) - { - if ( !varexists( tkn->tokval() ) ) - { - compiler_error( "Variable ", tkn->tokval(), " has not been declared on line ", ctx.line, - ".\n" ); - return -1; - } - } - else if ( tkn->id == TOK_EQUAL1 ) - { - // Single '=' sign? Special error statement (since it could be a typo?) - compiler_error( "Deprecated '=' found: did you mean '==' or ':='?\n" ); - return -1; - } - - if ( tkn->deprecated ) - { - compiler_warning( &ctx, "Warning: Found deprecated ", - ( tkn->type == TYP_OPERATOR ? "operator " : "token " ), "'", tkn->tokval(), - "' on line ", ctx.line, " of ", ctx.filename, "\n" ); - } - - if ( tkn->type == TYP_OPERATOR ) - { - int right_idx = i - 1; - if ( right_idx < 0 ) - { - throw std::runtime_error( "Unbalanced operator: " + Clib::tostring( *tkn ) ); - } - - int left_idx = right_idx - expr.get_num_tokens( i - 1 ); - if ( left_idx < 0 ) - { - throw std::runtime_error( "Unbalanced operator: " + Clib::tostring( *tkn ) ); - } - } - - if ( tkn->type == TYP_UNARY_OPERATOR ) - { - int operand_idx = i - 1; - if ( operand_idx < 0 ) - { - throw std::runtime_error( "Unbalanced operator: " + Clib::tostring( *tkn ) ); - } - } - } - - return 0; -} - -bool Compiler::substitute_constant( Token* tkn ) const -{ - auto srch = constants.find( tkn->tokval() ); - if ( srch != constants.end() ) - { - int dbg_filenum = tkn->dbg_filenum; - int dbg_linenum = tkn->dbg_linenum; - *tkn = ( *srch ).second; - tkn->dbg_filenum = dbg_filenum; - tkn->dbg_linenum = dbg_linenum; - return true; - } - else - { - return false; - } -} - -void Compiler::substitute_constants( Expression& expr ) const -{ - for ( auto& tkn : expr.tokens ) - { - if ( tkn->id == TOK_IDENT ) - substitute_constant( tkn ); - } -} - -int Compiler::readexpr( Expression& expr, CompilerContext& ctx, unsigned flags ) -{ - int res; - reinit( expr ); - res = IIP( expr, ctx, flags ); - if ( res != 1 ) - return res; - while ( !expr.CA.empty() ) - { - Token* token = expr.CA.front(); - expr.CA.pop(); - expr.tokens.push_back( token ); - } - if ( ( flags & EXPR_FLAG_CONSUME_RESULT ) && !expr.tokens.empty() ) - { - auto tkn = new Token( TOK_CONSUMER, TYP_UNARY_OPERATOR ); - expr.tokens.push_back( tkn ); - } - substitute_constants( expr ); - convert_variables( expr ); - expr.optimize(); - expr.replace_elvis(); - res = validate( expr, ctx ); - if ( res != 0 ) - return -1; - return 1; -} - -int Compiler::read_subexpression( Expression& expr, CompilerContext& ctx, unsigned flags ) -{ - Expression subexpression; - int res = readexpr( subexpression, ctx, flags ); - if ( res >= 0 ) - expr.consume_tokens( subexpression ); - return res; -} - -void Compiler::inject( Expression& expr ) -{ - for ( Expression::Tokens::const_iterator itr = expr.tokens.begin(); itr != expr.tokens.end(); - ++itr ) - { - addToken( *( *itr ) ); - } -} - -int Compiler::getExpr( CompilerContext& ctx, unsigned flags, size_t* exprlen, Expression* pex ) -{ - int res; - if ( pex ) - { - res = readexpr( *pex, ctx, flags ); - if ( exprlen != nullptr ) - *exprlen = pex->tokens.size(); - inject( *pex ); - } - else - { - Expression ex; - res = readexpr( ex, ctx, flags ); - if ( exprlen != nullptr ) - *exprlen = ex.tokens.size(); - inject( ex ); - } - return res; -} - -int Compiler::getExpr2( CompilerContext& ctx, unsigned expr_flags, Expression* pex ) -{ - int orig_inExpr; - int res; - orig_inExpr = inExpr; - inExpr = 1; - res = getExpr( ctx, expr_flags, nullptr, pex ); - inExpr = orig_inExpr; - return res; -} - -/* -getExpr3 -get an expression, must be contained in parenthesis. -*/ -int Compiler::getExprInParens( CompilerContext& ctx, Expression* pex ) -{ - Token token; - getToken( ctx, token ); - if ( token.id != TOK_LPAREN ) - { - err = PERR_MISSLPAREN; - return -1; - } - - int res = getExpr2( ctx, EXPR_FLAG_RIGHTPAREN_TERM_ALLOWED, pex ); - if ( res < 0 ) - return res; - - err = PERR_NONE; - getToken( ctx, token ); - if ( token.id != TOK_RPAREN ) - { - err = PERR_MISSRPAREN; - return -1; - } - return 0; -} - -int Compiler::getSimpleExpr( CompilerContext& ctx ) -{ - Token token; - int res = peekToken( ctx, token ); - if ( res < 0 ) - return res; - if ( token.id == TOK_LPAREN ) - { - res = getExprInParens( ctx ); - if ( res < 0 ) - return res; - } - else if ( token.id == TOK_IDENT || token.id == TOK_FUNC || token.id == TOK_USERFUNC || - token.id == TOK_ARRAY || token.id == TOK_LBRACE ) - { - res = getExpr2( ctx, EXPR_FLAG_SINGLE_ELEMENT ); - if ( res < 0 ) - return res; - } - else - { - compiler_error( "Expected variable, function or parenthesized expression, got '", token, - "'\n" ); - return -1; - } - return 0; -} - -int Compiler::eatToken( CompilerContext& ctx, BTokenId tokenid ) -{ - Token token; - int res = getToken( ctx, token ); - if ( res < 0 ) - { - compiler_error( ctx, "Error reading token, expected ", Token( tokenid, TYP_RESERVED ), "\n" ); - return res; - } - - if ( token.id != tokenid ) - { - compiler_error( ctx, "Expected ", Token( tokenid, TYP_RESERVED ), ", got ", token, "\n" ); - return -1; - } - return 0; -} - -int Compiler::handleDoClause( CompilerContext& ctx, int level ) -{ - if ( !quiet ) - INFO_PRINT << "DO clause..\n"; - StoredTokenContainer* prog_tokens = &program->tokens; - unsigned body_start = prog_tokens->next(); - enterblock( CanBeLabelled ); - - Token endblock_tkn; - int res; - res = readblock( ctx, level, RSV_DOWHILE, nullptr, &endblock_tkn ); - if ( res < 0 ) - return res; - - emit_leaveblock(); - - program->update_dbg_pos( endblock_tkn ); - - // continue should jump to where the WHILE expression is evaluated, - // which is the next token after this block - patchblock_continues( prog_tokens->next() ); - program->setstatementbegin(); - - localscope.popblock( true ); // Pop only variables. - res = getExpr( ctx, EXPR_FLAG_SEMICOLON_TERM_ALLOWED ); - if ( res < 0 ) - return res; - program->append( StoredToken( Mod_Basic, RSV_JMPIFTRUE, TYP_RESERVED, body_start ) ); - // break should completely exit, of course. - patchblock_breaks( prog_tokens->next() ); - localscope.popblock(); // Pop block. - - // do-while loops until its expression evaluates to false. - - - program->leaveblock(); - - return 0; -} - - -int Compiler::handleRepeatUntil( CompilerContext& ctx, int level ) -{ - if ( !quiet ) - INFO_PRINT << "REPEAT clause..\n"; - StoredTokenContainer* prog_tokens = &program->tokens; - unsigned body_start = prog_tokens->next(); - enterblock( CanBeLabelled ); - - Token endblock_tkn; - int res; - res = readblock( ctx, level, RSV_UNTIL, nullptr, &endblock_tkn ); - if ( res < 0 ) - { - return res; - } - - emit_leaveblock(); - - program->update_dbg_pos( endblock_tkn ); - // continue should jump to where the UNTIL expression is evaluated. - patchblock_continues( prog_tokens->next() ); - program->setstatementbegin(); - localscope.popblock( true ); - res = getExpr( ctx, EXPR_FLAG_SEMICOLON_TERM_ALLOWED ); - if ( res < 0 ) - return res; - // repeat-until loops until its expression evaluates to true. - program->append( StoredToken( Mod_Basic, RSV_JMPIFFALSE, TYP_RESERVED, body_start ) ); - // break should completely exit, of course. - patchblock_breaks( prog_tokens->next() ); - localscope.popblock(); - - - program->leaveblock(); - - return 0; -} - -int Compiler::handleSwitch( CompilerContext& ctx, int level ) -{ - int res = getExprInParens( ctx ); // (expr) (parens required) - if ( res < 0 ) - return res; - - unsigned default_posn = 0; - unsigned casecmp_posn = 0; - std::vector jmpend; - std::vector caseblock; - - program->append( StoredToken( Mod_Basic, INS_CASEJMP, TYP_RESERVED, 0 ), &casecmp_posn ); - - // overview: - // we grab some case OPTIONs, - // then we grab some code. - // until we peek-see an OPTION or an ENDCASE - bool done = false; - bool onlydefault = true; - StoredTokenContainer* prog_tokens = &program->tokens; - while ( !done ) - { - bool anycases = false; - for ( ;; ) - { - Token token; - res = peekToken( ctx, token ); - if ( res < 0 ) - return res; - if ( token.id == TOK_LONG || token.id == CTRL_LABEL || token.id == TOK_STRING ) - { - /* - Okay, some trickery. First, we'll handle the 'default' label, 'cause - it isn't tricky. - The complication is that sometimes a label is actually a constant. - */ - if ( token.id == CTRL_LABEL && stricmp( token.tokval(), "default" ) == 0 ) - { - getToken( ctx, token ); - if ( default_posn != 0 ) - { - compiler_error( "CASE statement can have only one DEFAULT clause.\n" ); - return -1; - } - default_posn = prog_tokens->next(); - anycases = true; - continue; - } - - /* - A label may be a constant, say CONST_VAR: If it is, substitute that value. - */ - if ( token.id == CTRL_LABEL ) - { - substitute_constant( &token ); - /* - A label that wasn't a constant can't be used in a CASE statement. - If it's followed by a while, etc, it won't be flagged illegal, though. - this may be a Bad Thing. - */ - if ( token.id == CTRL_LABEL ) - { - break; - } - Token dummy; // don't overwrite the token we just substituted - getToken( ctx, dummy ); - } - else if ( token.id == TOK_LONG || token.id == TOK_STRING ) - { - /* - If we read a Long or a String, it'll be followed by a colon. - If it was a label, the colon is gone. - */ - getToken( ctx, token ); - - res = eatToken( ctx, RSV_COLON ); - if ( res < 0 ) - return res; - } - - anycases = true; - onlydefault = false; - if ( token.id == TOK_LONG ) - { - unsigned short offset = static_cast( prog_tokens->next() ); - unsigned char* tmppch = reinterpret_cast( &offset ); - caseblock.push_back( tmppch[0] ); - caseblock.push_back( tmppch[1] ); - caseblock.push_back( CASE_TYPE_LONG ); // FIXME hardcoded - tmppch = reinterpret_cast( &token.lval ); - caseblock.push_back( tmppch[0] ); - caseblock.push_back( tmppch[1] ); - caseblock.push_back( tmppch[2] ); - caseblock.push_back( tmppch[3] ); - } - else if ( token.id == CTRL_LABEL || token.id == TOK_STRING ) - { - if ( strlen( token.tokval() ) >= 254 ) - { - compiler_error( "String expressions in CASE statements must be <= 253 characters.\n" ); - return -1; - } - unsigned short offset = static_cast( prog_tokens->next() ); - unsigned char* tmppch = reinterpret_cast( &offset ); - caseblock.push_back( tmppch[0] ); - caseblock.push_back( tmppch[1] ); - caseblock.push_back( static_cast( strlen( token.tokval() ) ) ); - const char* str = token.tokval(); - size_t len = strlen( str ); - - for ( size_t i = 0; i < len; ++i ) - caseblock.push_back( str[i] ); - } - } - else if ( token.id == RSV_ENDSWITCH && anycases ) // only accept if OPTIONs exist! - { - getToken( ctx, token ); - done = true; - break; - } - else - { // something else. we'll assume a statement. - break; - } - } - if ( done ) - break; - - - // we've grabbed the OPTIONs. Now grab the code, until we get to an OPTION or ENDCASE - enterblock( CanBeLabelled, BreakOk, ContinueNotOk ); - while ( ctx.s[0] ) - { - Token token; - res = peekToken( ctx, token ); - if ( res < 0 ) - return res; - if ( token.id == RSV_ENDSWITCH ) - { - // Only accept ENDCASE if OPTIONs exist! - if ( anycases ) - { - getToken( ctx, token ); - done = true; - break; - } - } - else if ( token.id == TOK_LONG ) - { - break; - } - else if ( token.id == CTRL_LABEL ) - { - if ( stricmp( token.tokval(), "default" ) == 0 ) - break; - - substitute_constant( &token ); - if ( token.id != CTRL_LABEL ) - break; - } - else if ( token.id == TOK_STRING ) - { - break; - } - - // we're about to grab code. there needs to have been at least one OPTION, then. - if ( !anycases ) - { - compiler_error( "CASE statement with no options!\n", "Found '", token.tokval(), "'", - ( token.id == CTRL_LABEL ? " but no such constant is defined.\n" - : " prematurely.\n" ) ); - return -1; - } - - res = getStatement( ctx, level ); - if ( res < 0 ) - return res; - } - // NOTE, the "jump to -> jump to" optimizer will be helpful here, optimizing breaks. - - emit_leaveblock(); - patchblock_breaks( prog_tokens->next() ); - - localscope.popblock(); - program->leaveblock(); - - if ( !done ) - { - unsigned jmpend_posn; - program->append( StoredToken( Mod_Basic, RSV_GOTO, TYP_RESERVED, 0 ), &jmpend_posn ); - jmpend.push_back( jmpend_posn ); - } - } - - // patchblock_breaks( program->tokens.next() ); - // program->leaveblock(); - - // if only a 'default' block was defined, print a warning - if ( onlydefault ) - { - compiler_warning( &ctx, "Warning: CASE block only has a DEFAULT clause defined.\n", - "near: ", curLine, "\n" ); - } - - // if no default specified, pretend 'default' was specified at the end. - if ( default_posn == 0 ) - default_posn = prog_tokens->next(); - - // the default case must go at the end. - // if (1) - { - unsigned char* tmppch = reinterpret_cast( &default_posn ); - caseblock.push_back( tmppch[0] ); - caseblock.push_back( tmppch[1] ); - caseblock.push_back( CASE_TYPE_DEFAULT ); - } - - while ( !jmpend.empty() ) - { - unsigned posn = jmpend.back(); - jmpend.pop_back(); - patchoffset( posn, prog_tokens->next() ); - } - - // now, we have to emit the casecmp block. - unsigned caseblock_posn; - auto casecmp_raw = new unsigned char[caseblock.size()]; - for ( size_t i = 0; i < caseblock.size(); ++i ) - casecmp_raw[i] = caseblock[i]; - program->symbols.append( casecmp_raw, static_cast( caseblock.size() ), - caseblock_posn ); - delete[] casecmp_raw; - patchoffset( casecmp_posn, caseblock_posn ); - return 0; -} - -int Compiler::handleForEach( CompilerContext& ctx, int level ) -{ - Token itrvar; - - int res; - CompilerContext foreach_ctx( ctx ); - - res = getToken( ctx, itrvar ); - if ( res < 0 ) - return res; - if ( itrvar.id != TOK_IDENT ) - { - compiler_error( "FOREACH iterator must be an identifier, got ", itrvar, "\n" ); - return res; - } - - res = eatToken( ctx, TOK_IN ); - if ( res < 0 ) - return res; - - /* - The outer block is a hidden block. It can't be labelled, but the - inner one can. This way, 'break' and 'continue' won't touch - the iterator variable, expression, and counter that we have in - this hidden block. - */ - - res = getSimpleExpr( ctx ); - if ( res < 0 ) - return res; - /* - When these are evaluated, the value stack should look like this: - (result of EXPR) - */ - enterblock( CanNotBeLabelled ); - - unsigned initforeach_posn; - program->append( StoredToken( Mod_Basic, INS_INITFOREACH, TYP_RESERVED, 0 ), &initforeach_posn ); - /* - INITFOREACH creates three local variables, placeholders for the iterator, - expression, and counter. Only the iterator can be accessed, for now. - */ - program->addlocalvar( itrvar.tokval() ); - localscope.addvar( itrvar.tokval(), foreach_ctx ); - program->addlocalvar( "_" + std::string( itrvar.tokval() ) + "_expr" ); - localscope.addvar( "_" + std::string( itrvar.tokval() ) + "_expr", foreach_ctx, false ); - program->addlocalvar( "_" + std::string( itrvar.tokval() ) + "_iter" ); - localscope.addvar( "_" + std::string( itrvar.tokval() ) + "_iter", foreach_ctx, false ); - - - unsigned iter_posn = program->tokens.next(); - enterblock( CanBeLabelled ); - Token endforeach_token; - res = readblock( ctx, level, RSV_ENDFOREACH, nullptr, &endforeach_token ); - if ( res < 0 ) - return res; - - emit_leaveblock(); - - unsigned stepforeach_posn; - program->update_dbg_pos( endforeach_token ); - program->append( StoredToken( Mod_Basic, INS_STEPFOREACH, TYP_RESERVED, iter_posn ), - &stepforeach_posn ); - patchoffset( initforeach_posn, stepforeach_posn ); - - patchblock_continues( stepforeach_posn ); - patchblock_breaks( program->tokens.next() ); - localscope.popblock(); - program->leaveblock(); - - // FIXME this isn't right - continue needs to refer to one block, while break - // needs to refer to another! - // ie continue should use the inner block, while break should use the outer. - leaveblock( 0, 0 ); - return 0; -} - - -int Compiler::handleReturn( CompilerContext& ctx ) -{ - Token token; - int res = peekToken( ctx, token ); - if ( res ) - return res; - if ( token.id == TOK_SEMICOLON ) - { - getToken( ctx, token ); - unsigned posn = 0; - program->symbols.append( "", posn ); - program->append( StoredToken( Mod_Basic, TOK_STRING, TYP_OPERAND, posn ) ); - } - else - { - res = getExpr2( ctx, EXPR_FLAG_SEMICOLON_TERM_ALLOWED ); - if ( res < 0 ) - return res; - } - - if ( inFunction ) - { - program->append( StoredToken( Mod_Basic, RSV_RETURN, TYP_RESERVED, 0 ), nullptr ); - } - else - { - program->append( StoredToken( Mod_Basic, CTRL_PROGEND, TYP_CONTROL, 0 ), nullptr ); - } - return 0; -} - -int Compiler::handleExit( CompilerContext& ctx ) -{ - Token token; - getToken( ctx, token ); - if ( token.id != TOK_SEMICOLON ) - { - compiler_error( "Missing ';'\n" ); - err = PERR_MISSINGDELIM; - return -1; - } - - program->append( StoredToken( Mod_Basic, RSV_EXIT, TYP_RESERVED, 0 ) ); - return 0; -} - -int Compiler::handleBlock( CompilerContext& ctx, int level ) -{ - Token token; - - if ( !quiet ) - INFO_PRINT << "BEGIN block..\n"; - - enterblock( CanNotBeLabelled ); - - while ( ctx.s[0] ) - { - peekToken( ctx, token ); - if ( token.id == RSV_ENDB ) - break; - - if ( getStatement( ctx, level ) == -1 ) - return -1; - } - if ( !ctx.s[0] ) - return -1; - - getToken( ctx, token ); - if ( token.id != RSV_ENDB ) - { - return -1; - } - - - leaveblock( 0, 0 ); - - return 0; -} - -int Compiler::readFunctionDeclaration( CompilerContext& ctx, UserFunction& userfunc ) -{ - Token token; - Token funcName; - int res; - - userfunc.ctx = ctx; - - res = getToken( ctx, funcName ); - if ( res ) - return res; - bool first_time = true; - if ( first_time ) - { - if ( funcName.id != TOK_IDENT ) - { - if ( funcName.id == TOK_FUNC ) - { - compiler_error( "'", funcName.tokval(), "' is already defined as a function.\n", - "Near: ", curLine, "\n", ctx ); - return -1; - } - else - { - compiler_error( "Expected an identifier, got ", funcName, " instead.\n", "Near: ", curLine, - "\n", ctx ); - return -1; - } - } - } - userfunc.name = funcName.tokval(); - Token lparen; - res = getToken( ctx, lparen ); - if ( res ) - return res; - if ( lparen.id != TOK_LPAREN ) - { - return -1; - } - /* - We have eaten the left paren. Next follows: - RIGHT_PAREN, OR ( [refto] varname [:= default ] { COMMA or RIGHT_PAREN }) - */ - peekToken( ctx, token ); - for ( ;; ) - { - res = getTokenWithoutConversions( ctx, token ); - if ( res ) - return -1; - - bool pass_by_reference = false; - bool unused = false; - - if ( token.id == TOK_RPAREN ) - break; - if ( token.id == TOK_REFTO ) - { - pass_by_reference = true; - res = getTokenWithoutConversions( ctx, token ); - if ( res ) - return -1; - } - if ( token.id == TOK_UNUSED ) - { - unused = true; - res = getToken( ctx, token ); - if ( res ) - return -1; - } - if ( token.id != TOK_IDENT ) - { - return -1; - } - userfunc.parameters.resize( userfunc.parameters.size() + 1 ); - UserParam& param = userfunc.parameters.back(); - param.name = token.tokval(); - param.pass_by_reference = pass_by_reference; - param.unused = unused; - peekToken( ctx, token ); - if ( token.id == TOK_ASSIGN ) - { - // We have a default argument. - if ( unused ) - { - compiler_error( "Default arguments are not allowed in unused parameters\n" ); - return -1; - } - - param.have_default = 1; - getToken( ctx, token ); // Eat the assignment operator - - Expression ex; - if ( readexpr( ex, ctx, EXPR_FLAG_RIGHTPAREN_TERM_ALLOWED | EXPR_FLAG_COMMA_TERM_ALLOWED ) != - 1 ) - { - compiler_error( "Error reading expression in const declaration\n" ); - return -1; - } - - if ( ex.tokens.size() != 1 ) - { - compiler_error( "Const expression must be optimizable\n" ); - return -1; - } - - param.dflt_value = *( ex.tokens.back() ); - if ( param.dflt_value.type != TYP_OPERAND ) - { - compiler_error( "[", funcName.tokval(), - "]: Only simple operands are allowed as default arguments (", token, - " is not allowed)\n" ); - return -1; - } - peekToken( ctx, token ); - } - else if ( token.id == TOK_EQUAL1 ) - { - compiler_error( "Unexpected token: '", token, "'. Did you mean := for assign?\n" ); - return -1; - } - else - { - param.have_default = 0; - } - - if ( token.id == TOK_COMMA ) - { - getToken( ctx, token ); - continue; - } - else if ( token.id == TOK_RPAREN ) - { - continue; - } - else - return -1; - } - return 0; -} - -bool mismatched_end( const Token& token, BTokenId correct ) -{ - if ( token.id == correct ) - { - return false; - } - else if ( token.id == RSV_ENDFOREACH || token.id == RSV_ENDIF || - // token.id == RSV_ENDB || - token.id == RSV_DOWHILE || token.id == RSV_ENDWHILE || token.id == RSV_UNTIL || - token.id == RSV_ENDFOR || token.id == RSV_ENDFUNCTION || token.id == RSV_ENDSWITCH || - token.id == RSV_ENDPROGRAM || token.id == RSV_ENDENUM ) - { - Token t( correct, TYP_RESERVED ); - compiler_error( "Expected ", t, " before ", token, "\n" ); - return true; - } - else - { - return false; - } -} - -int Compiler::handleBracketedIf( CompilerContext& ctx, int level ) -{ - CompilerContext save_ctx( ctx ); - - std::vector jumpend; - - Token token; - if ( !quiet ) - INFO_PRINT << "if clause..\n"; - - token.id = RSV_ST_IF; - - EScriptProgramCheckpoint checkpt( *program ); - size_t jumpend_size = jumpend.size(); - - bool discard_rest = false; - // bool discarded_all = true; - bool included_any_tests = false; - unsigned if_token_posn = static_cast( -1 ); - StoredTokenContainer* prog_tokens = &program->tokens; - while ( token.id == RSV_ST_IF || token.id == RSV_ELSEIF ) - { - EScriptProgramCheckpoint checkpt_expr( *program ); - // dump(cout); - program->setstatementbegin(); - Expression ex; - int res = getExprInParens( ctx, &ex ); // (expr) (parens required) - if ( res < 0 ) - return res; - // dump(cout); - bool patch_if_token = true; - const unsigned last_if_token_posn = if_token_posn; - if ( ex.tokens.back()->id == TOK_LOG_NOT ) - { - if_token_posn = prog_tokens->count() - 1; - prog_tokens->atPut1( StoredToken( Mod_Basic, RSV_JMPIFTRUE, TYP_RESERVED, 0 ), - if_token_posn ); - } - else - { - program->append( StoredToken( Mod_Basic, RSV_JMPIFFALSE, TYP_RESERVED, 0 ), &if_token_posn ); - } - - bool discard_this = discard_rest; - - if ( !discard_rest && ex.tokens.size() == 1 && ex.tokens[0]->id == TOK_LONG ) - { - if ( ex.tokens[0]->lval ) - { - discard_rest = true; - } - else - { - discard_this = true; - } - rollback( *program, checkpt_expr ); // don't need the expression or the jump, - // even if we're keeping the block - patch_if_token = false; - if_token_posn = last_if_token_posn; - } - else - { - // discarded_all = discard_rest; - if ( !discard_rest ) - included_any_tests = true; - } - - - // THEN is optional, currently. - peekToken( ctx, token ); - if ( token.id == RSV_THEN ) - getToken( ctx, token ); // 'then' - if ( !quiet ) - INFO_PRINT << "then clause..\n"; - - // dump(cout); - // get the part we do - enterblock( CanNotBeLabelled ); - while ( ctx.s[0] ) - { - peekToken( ctx, token ); - if ( token.id == RSV_ELSEIF || token.id == RSV_ELSE || token.id == RSV_ENDIF ) - { - break; - } - - res = getStatement( ctx, level ); - if ( res < 0 ) - { - compiler_error( "Error in IF statement starting at ", save_ctx ); - return res; - } - } - leaveblock( 0, 0 ); - if ( !ctx.s[0] ) - return -1; - - // dump(cout); - if ( !discard_this ) - { - checkpt.commit( *program ); - jumpend_size = jumpend.size(); - } - // dump(cout); - // dump(cout); - - // this will be committed only when the next ELSEIF or ELSE is committed - if ( token.id == RSV_ELSEIF || token.id == RSV_ELSE ) - { - unsigned temp_posn; - program->update_dbg_pos( token ); - program->append( StoredToken( Mod_Basic, RSV_GOTO, TYP_RESERVED, 0 ), &temp_posn ); - jumpend.push_back( temp_posn ); - - if ( token.id == RSV_ELSEIF ) - { - readCurLine( ctx ); - savesourceline(); - getToken( ctx, token ); - } - } - if ( patch_if_token ) - { - StoredToken tkn; - prog_tokens->atGet1( if_token_posn, tkn ); - tkn.offset = static_cast( prog_tokens->next() ); - prog_tokens->atPut1( tkn, if_token_posn ); - } - - // dump(cout); - if ( discard_this ) - { - rollback( *program, checkpt ); - while ( jumpend.size() > jumpend_size ) - jumpend.pop_back(); - - if ( last_if_token_posn != static_cast( -1 ) && - last_if_token_posn < prog_tokens->count() ) - { - StoredToken tkn; - prog_tokens->atGet1( last_if_token_posn, tkn ); - tkn.offset = static_cast( prog_tokens->next() ); - prog_tokens->atPut1( tkn, last_if_token_posn ); - } - } - // dump(cout); - } - - peekToken( ctx, token ); - if ( token.id != RSV_ENDIF && token.id != RSV_ELSE ) - { - compiler_error( "Expected ELSE or ENDIF after IF statement starting at ", save_ctx, - "Did not expect: ", token, "\n" ); - return -1; - } - - // if an ELSE follows, grab the ELSE and following statement - if ( token.id == RSV_ELSE ) - { - getToken( ctx, token ); // eat the else - if ( !quiet ) - INFO_PRINT << "else clause..\n"; - enterblock( CanNotBeLabelled ); - while ( ctx.s[0] ) - { - peekToken( ctx, token ); - if ( token.id == RSV_ENDIF ) - break; - if ( mismatched_end( token, RSV_ENDIF ) ) - return -1; - - int res = getStatement( ctx, level ); - if ( res < 0 ) - return res; - } - leaveblock( 0, 0 ); - } - // eat the ENDIF - if ( !ctx.s[0] ) - return -1; - getToken( ctx, token ); - if ( token.id != RSV_ENDIF ) - return -1; - - if ( discard_rest && !included_any_tests ) - { - rollback( *program, checkpt ); - while ( jumpend.size() > jumpend_size ) - jumpend.pop_back(); - } - - - while ( !jumpend.empty() ) - { - unsigned pc = jumpend.back(); - jumpend.pop_back(); - // patch up orig. IF token to skip past if false - - prog_tokens->atPut1( StoredToken( Mod_Basic, RSV_GOTO, TYP_RESERVED, prog_tokens->next() ), - pc ); - } - - return 0; -} - - -int Compiler::readblock( CompilerContext& ctx, int level, BTokenId endtokenid, - BTokenId* last_statement_id, Token* pBlockEndToken ) -{ - CompilerContext tctx( ctx ); - int res; - while ( ctx.s[0] ) - { - Token token; - res = peekToken( ctx, token ); - if ( res < 0 ) - return res; - if ( token.id == endtokenid ) - { - if ( pBlockEndToken != nullptr ) - getToken( ctx, *pBlockEndToken ); - else - getToken( ctx, token ); // eat the end-token - return 0; - } - if ( last_statement_id != nullptr ) - *last_statement_id = token.id; - res = getStatement( ctx, level ); - if ( res < 0 ) - return res; - } - compiler_error( "Error in block beginning at ", tctx, "End-of-File detected, expected '", - Token( Mod_Basic, endtokenid, TYP_RESERVED ), "'\n" ); - return -1; -} - -int Compiler::handleBracketedWhile( CompilerContext& ctx, int level ) -{ - if ( !quiet ) - INFO_PRINT << "while clause..\n"; - StoredTokenContainer* prog_tokens = &program->tokens; - unsigned conditional_expr_posn = prog_tokens->next(); - int res = getExprInParens( ctx ); // (expr) (parens required) - if ( res < 0 ) - return res; - - unsigned test_expr_token_posn; - program->append( StoredToken( Mod_Basic, RSV_JMPIFFALSE, TYP_RESERVED, 0 ), - &test_expr_token_posn ); - - enterblock( CanBeLabelled ); - - Token endblock_tkn; - res = readblock( ctx, level, RSV_ENDWHILE, nullptr, &endblock_tkn ); - if ( res < 0 ) - return res; - - program->update_dbg_pos( endblock_tkn ); - emit_leaveblock(); - - // jump back to conditional expression - program->append( StoredToken( Mod_Basic, RSV_GOTO, TYP_RESERVED, conditional_expr_posn ), - nullptr ); - - // Control should jump past the loop when the expr evaluates to false. - unsigned exit_loop_posn = prog_tokens->next(); - prog_tokens->atPut1( StoredToken( Mod_Basic, RSV_JMPIFFALSE, TYP_RESERVED, exit_loop_posn ), - test_expr_token_posn ); - - // continues re-test the expression, which is at the top of the loop. - patchblock_continues( conditional_expr_posn ); - // breaks exit the loop, which has been wholly emitted by now. - patchblock_breaks( prog_tokens->count() ); - - localscope.popblock(); - program->leaveblock(); - - return 0; -} - -/* -allowed formats for declare: -local a; -local a array; -local a := 5; -local a := expr; -local a, b; -local a, b := expr, ...; -*/ -int Compiler::handleVarDeclare( CompilerContext& ctx, unsigned save_id ) -{ - CompilerContext savectx( ctx ); - Token tk_varname, tk_delim; - int done = 0; - if ( save_id == RSV_GLOBAL && !inGlobalScope() ) - { - compiler_error( "Globals can only be declared at global scope.\n" ); - return -1; - } - if ( save_id == RSV_LOCAL && inGlobalScope() ) - { - compiler_error( "Locals can only be declared within a block or function.\n" ); - return -1; - } - - do - { - /* - formats: varname followed by comma or semicolon - varname followed by "array", then by comma/semicolon - varname followed by ':=', then an initializer, then comma/semicolon. - */ - CompilerContext thisctx( ctx ); - - getToken( ctx, tk_varname ); - if ( tk_varname.id != TOK_IDENT ) - { - compiler_error( "Non-identifier declared as a variable: '", tk_varname.tokval(), "'\n", - "Token: ", tk_varname, "\n" ); - return -1; - } - - if ( constants.find( tk_varname.tokval() ) != constants.end() ) - { - compiler_error( tk_varname.tokval(), " is already a defined constant.\n" ); - return -1; - } - - // Add this variable to the current block/scope - unsigned varindex = 0; - - if ( inGlobalScope() ) - { - unsigned idx; - CompilerContext gctx; - if ( !globalexists( tk_varname.tokval(), idx, &gctx ) ) - { - Variable v; - v.name = tk_varname.tokval(); - v.used = true; - v.ctx = savectx; - - varindex = static_cast( globals_.size() ); - globals_.push_back( v ); - program->globalvarnames.push_back( tk_varname.tokval() ); - } - else - { - compiler_error( "Global Variable '", tk_varname.tokval(), "' is already declared at ", - gctx ); - return -1; - } - } - else - { - unsigned idx; - if ( globalexists( tk_varname.tokval(), idx ) ) - { - compiler_warning( &ctx, "Warning: Local variable '", tk_varname.tokval(), - "' hides Global variable of same name.\n" ); - } - varindex = localscope.numVariables(); - program->addlocalvar( tk_varname.tokval() ); - - localscope.addvar( tk_varname.tokval(), ctx ); - } - - // grab the comma, semicolon, := or array declare token - getToken( ctx, tk_delim ); - - // note save_id is RSV_LOCAL or RSV_GLOBAL - program->append( StoredToken( Mod_Basic, save_id, TYP_RESERVED, varindex ), thisctx ); - - if ( tk_delim.id == TOK_ARRAY ) - { - // declaring an array. - compiler_warning( &ctx, "Warning! Deprecated array-declaration syntax used.\n" ); - program->append( StoredToken( Mod_Basic, INS_DECLARE_ARRAY, TYP_RESERVED, 0 ) ); - - getToken( ctx, tk_delim ); - } - else if ( tk_delim.id == TOK_ASSIGN ) - { - int res; - res = getExpr( ctx, EXPR_FLAG_SEMICOLON_TERM_ALLOWED | EXPR_FLAG_COMMA_TERM_ALLOWED ); - if ( res <= 0 ) - return res; - program->append( StoredToken( Mod_Basic, TOK_ASSIGN, TYP_OPERATOR, 0 ) ); - - getToken( ctx, tk_delim ); - } - program->append( StoredToken( Mod_Basic, TOK_CONSUMER, TYP_UNARY_OPERATOR, 0 ) ); - - if ( tk_delim.id == TOK_COMMA ) - { - continue; - } - else if ( tk_delim.id == TOK_SEMICOLON ) - { - break; - } - else if ( tk_delim.id == TOK_EQUAL1 ) - { - compiler_error( "Unexpected token: '", tk_delim, "'. Did you mean := for assign?\n" ); - return -1; - } - else - { - compiler_error( "Unexpected token: ", tk_delim, "\n" ); - return -1; - } - } while ( !done ); - - return 0; - - // FIXME: Dead code since ages, left here because I have no idea if bug or feature... - // // insert a consumer to eat the evaluated result from the expr. - // program->append( StoredToken( Mod_Basic, TOK_CONSUMER, TYP_UNARY_OPERATOR, 0 ) ); - // return 0; -} // namespace Bscript - -/* -allowed formats for declaring const: -const a := expr; -*/ -int Compiler::handleConstDeclare( CompilerContext& ctx ) -{ - Token tk_varname, tk_assign; - // int done = 0; - - - /* - formats: varname followed by comma or semicolon - varname followed by "array", then by comma/semicolon - varname followed by ':=', then an initializer, then comma/semicolon. - */ - getToken( ctx, tk_varname ); - if ( tk_varname.id != TOK_IDENT ) - { - compiler_error( "Expected identifier after const declaration\n" ); - return -1; - } - - if ( constants.count( tk_varname.tokval() ) ) - { - compiler_error( "Constant ", tk_varname, " has already been defined.\n" ); - return -1; - } - - // grab the := token - getToken( ctx, tk_assign ); - if ( tk_assign.id != TOK_ASSIGN ) - { - compiler_error( "Expected := after identifier in const declaration\n" ); - return -1; - } - - Expression ex; - if ( readexpr( ex, ctx, EXPR_FLAG_SEMICOLON_TERM_ALLOWED ) != 1 ) - { - compiler_error( "Error reading expression in const declaration\n" ); - return -1; - } - - if ( ex.tokens.size() != 1 ) - { - compiler_error( "Const expression must be optimizable\n" ); - return -1; - } - - constants.insert( Constants::value_type( tk_varname.tokval(), *ex.tokens.back() ) ); - delete ex.tokens.back(); - ex.tokens.pop_back(); - - return 0; -} - -/* -allowed formats for declaring enum -enum FOO -const a := expr; -*/ -int Compiler::handleEnumDeclare( CompilerContext& ctx ) -{ - Token tk_enum_tag; - - // First, grab the enum tag. - // TODO: validate it isn't already used - - if ( getToken( ctx, tk_enum_tag ) < 0 ) - { - compiler_error( "Error reading enum tag\n" ); - return -1; - } - - if ( tk_enum_tag.id != TOK_IDENT ) - { - compiler_error( "Expected an enum tag after 'enum'\n" ); - return -1; - } - - int next_counter = 0; - for ( ;; ) - { - Token tk_varname, tk_assign; - // int done = 0; - if ( getToken( ctx, tk_varname ) < 0 ) - { - compiler_error( "Error reading identifier in enum declaration\n" ); - return -1; - } - - if ( tk_varname.id == RSV_ENDENUM ) - return 0; - - if ( tk_varname.id != TOK_IDENT ) - { - compiler_error( "Expected identifier in enum statement, got ", tk_varname, "\n" ); - return -1; - } - - Token tmp; - // now, the forms. THis should be followed by a comma, an 'endenum', or a ':=' - if ( peekToken( ctx, tmp ) < 0 ) - { - compiler_error( "Error reading token in enum statement\n" ); - return -1; - } - if ( tmp.id == TOK_ASSIGN ) - { - Token _tmp; - getToken( ctx, _tmp ); - Expression ex; - // FIXME doesn't work if expression is right before enum - if ( readexpr( ex, ctx, EXPR_FLAG_COMMA_TERM_ALLOWED | EXPR_FLAG_ENDENUM_TERM_ALLOWED ) != 1 ) - { - compiler_error( "Error reading expression in enum declaration\n" ); - return -1; - } - if ( !peekToken( ctx, _tmp ) ) - { // might be a comma, or an endenum - if ( _tmp.id == TOK_COMMA ) - getToken( ctx, _tmp ); - } - if ( ex.tokens.size() != 1 ) - { - compiler_error( "Enum expression must be optimizable\n" ); - return -1; - } - Token* tkn = ex.tokens.back(); - if ( tkn->id == TOK_LONG ) - next_counter = tkn->lval + 1; - else - ++next_counter; - constants.insert( Constants::value_type( tk_varname.tokval(), *tkn ) ); - delete tkn; - ex.tokens.pop_back(); - } - else if ( tmp.id == TOK_COMMA ) - { - getToken( ctx, tmp ); - Token tkn( TOK_LONG, TYP_OPERAND ); - tkn.lval = next_counter++; - constants.insert( Constants::value_type( tk_varname.tokval(), tkn ) ); - } - else if ( tmp.id == RSV_ENDENUM ) - { - Token tkn( TOK_LONG, TYP_OPERAND ); - tkn.lval = next_counter++; - constants.insert( Constants::value_type( tk_varname.tokval(), tkn ) ); - // we'll pick this one up next pass - } - else if ( tmp.id == TOK_EQUAL1 ) - { - compiler_error( "Unexpected token: '", tmp, "'. Did you mean := for assign?\n" ); - return -1; - } - else - { - compiler_error( "Unexpected token ", tmp, " in enum statement\n" ); - return -1; - } - } -} - - -int Compiler::useModule( const char* modulename ) -{ - for ( const auto& elem : program->modules ) - { - if ( modulename == elem->modulename ) - return 0; - } - - std::unique_ptr compmodl( new FunctionalityModule( modulename ) ); - - std::string filename_part = modulename; - filename_part += ".em"; - - std::string filename_full = current_file_path + filename_part; - - if ( compilercfg.VerbosityLevel >= 10 ) - INFO_PRINT << "Searching for " << filename_full << "\n"; - - if ( !Clib::FileExists( filename_full.c_str() ) ) - { - std::string try_filename_full = compilercfg.ModuleDirectory + filename_part; - if ( compilercfg.VerbosityLevel >= 10 ) - INFO_PRINT << "Searching for " << try_filename_full << "\n"; - if ( Clib::FileExists( try_filename_full.c_str() ) ) - { - if ( compilercfg.VerbosityLevel >= 10 ) - INFO_PRINT << "Found " << try_filename_full << "\n"; - // cout << "Using " << try_filename << endl; - filename_full = try_filename_full; - } - } - else - { - if ( compilercfg.VerbosityLevel >= 10 ) - INFO_PRINT << "Found " << filename_full << "\n"; - } - - char* orig_mt; - char* mt; - - if ( getFileContents( filename_full.c_str(), &orig_mt ) ) - { - compiler_error( "Unable to find module ", modulename, "\n", "\t(Filename: ", filename_full, - ")\n" ); - return -1; - } - - mt = orig_mt; - CompilerContext mod_ctx( filename_full, program->add_dbg_filename( filename_full ), mt ); - - std::string save = current_file_path; - current_file_path = getpathof( filename_full ); - - int res = -1; - for ( ;; ) - { - Token tk_dummy; - res = peekToken( mod_ctx, tk_dummy ); - if ( res < 0 ) - { - compiler_error( "Error reading token in module ", modulename, "\n" ); - free( orig_mt ); - break; - } - else if ( res == 1 ) - { - addModule( compmodl.release() ); - free( orig_mt ); - res = 0; - break; - } - if ( tk_dummy.id == RSV_CONST ) - { - getToken( mod_ctx, tk_dummy ); - int _res = handleConstDeclare( mod_ctx ); - if ( _res < 0 ) - break; - else - continue; - } - std::unique_ptr puserfunc( new UserFunction ); - if ( readFunctionDeclaration( mod_ctx, *puserfunc ) ) - { - compiler_error( "Error reading function declaration in module ", modulename, "\n" ); - free( orig_mt ); - res = -1; - break; - } - - Token tk_semicolon; - if ( getToken( mod_ctx, tk_semicolon ) ) - { - compiler_error( filename_full, ": Error in declaration for ", puserfunc->name, ":\n", - " Expected a semicolon, got end-of-file or error\n" ); - - free( orig_mt ); - res = -1; - break; - } - if ( tk_semicolon.id != TOK_SEMICOLON ) - { - compiler_error( filename_full, ": Error in declaration for ", puserfunc->name, ":\n", - " Expected a semicolon, got '", tk_semicolon, "'\n" ); - free( orig_mt ); - res = -1; - break; - } - - UserFunction* uf = puserfunc.release(); - compmodl->addFunction( uf->name.c_str(), static_cast( uf->parameters.size() ), uf ); - } - current_file_path = save; - return res; -} - -/* -Format: -Use module; - -Consider: at the beginning of program parsing, peek at the first token. -While it's a 'Use', handle it. This way, from looking at the top of a program, you -can tell what modules it uses - a simple way for someone to guage a script's -capabilities / risks, from a security standpoint. -*/ -int Compiler::handleUse( CompilerContext& ctx ) -{ - Token tk_module_name, tk_semicolon; - - if ( getToken( ctx, tk_module_name ) ) - { - compiler_error( "Error in USE statement: USE should be followed by a module name.\n" ); - return -1; - } - if ( tk_module_name.id != TOK_IDENT ) - { - compiler_error( "Error in USE statement: Expected identifier, got '", tk_module_name, "'\n" ); - return -1; - } - - if ( getToken( ctx, tk_semicolon ) ) - { - compiler_error( "Error in USE statement (module ", tk_module_name, - "): Expected ';', got end-of-file or error\n" ); - return -1; - } - if ( tk_semicolon.id != TOK_SEMICOLON ) - { - compiler_error( "Error in USE statement (module ", tk_module_name, "): Expected ';', got '", - tk_semicolon, "'\n" ); - return -1; - } - - if ( strlen( tk_module_name.tokval() ) > 10 ) - { - compiler_error( "Error in USE statement: Module names must be <= 10 characters\n", - "Module specified was: '", tk_module_name, "'\n" ); - return -1; - } - - return useModule( tk_module_name.tokval() ); -} - -int Compiler::includeModule( const std::string& modulename ) -{ - // cout << "includeModule(" << modulename << "). includes(" << included.size() << "):"; - // for( INCLUDES::const_iterator citr = included.begin(); citr != included.end(); ++citr ) - // { - // cout << " " << (*citr); - // } - // cout << endl; - - std::string filename_part = modulename; - filename_part += ".inc"; - - std::string filename_full = current_file_path + filename_part; - - if ( filename_part[0] == ':' ) - { - const Plib::Package* pkg = nullptr; - std::string path; - if ( Plib::pkgdef_split( filename_part, nullptr, &pkg, &path ) ) - { - if ( pkg != nullptr ) - { - filename_full = pkg->dir() + path; - std::string try_filename_full = pkg->dir() + "include/" + path; - - if ( compilercfg.VerbosityLevel >= 10 ) - INFO_PRINT << "Searching for " << filename_full << "\n"; - - if ( !Clib::FileExists( filename_full.c_str() ) ) - { - if ( compilercfg.VerbosityLevel >= 10 ) - INFO_PRINT << "Searching for " << try_filename_full << "\n"; - if ( Clib::FileExists( try_filename_full.c_str() ) ) - { - if ( compilercfg.VerbosityLevel >= 10 ) - INFO_PRINT << "Found " << try_filename_full << "\n"; - - filename_full = try_filename_full; - } - } - else - { - if ( compilercfg.VerbosityLevel >= 10 ) - INFO_PRINT << "Found " << filename_full << "\n"; - - if ( Clib::FileExists( try_filename_full.c_str() ) ) - compiler_error( "Warning: Found '", filename_full.c_str(), "' and '", - try_filename_full.c_str(), "'! Will use first file!\n" ); - } - } - else - { - filename_full = compilercfg.PolScriptRoot + path; - - if ( compilercfg.VerbosityLevel >= 10 ) - { - INFO_PRINT << "Searching for " << filename_full << "\n"; - if ( Clib::FileExists( filename_full.c_str() ) ) - INFO_PRINT << "Found " << filename_full << "\n"; - } - } - } - else - { - compiler_error( "Unable to read include file '", modulename, "'\n" ); - return -1; - } - } - else - { - if ( compilercfg.VerbosityLevel >= 10 ) - INFO_PRINT << "Searching for " << filename_full << "\n"; - - if ( !Clib::FileExists( filename_full.c_str() ) ) - { - std::string try_filename_full = compilercfg.IncludeDirectory + filename_part; - if ( compilercfg.VerbosityLevel >= 10 ) - INFO_PRINT << "Searching for " << try_filename_full << "\n"; - if ( Clib::FileExists( try_filename_full.c_str() ) ) - { - if ( compilercfg.VerbosityLevel >= 10 ) - INFO_PRINT << "Found " << try_filename_full << "\n"; - - // cout << "Using " << try_filename << endl; - filename_full = try_filename_full; - } - } - else - { - if ( compilercfg.VerbosityLevel >= 10 ) - INFO_PRINT << "Found " << filename_full << "\n"; - } - } - - std::string filename_check = Clib::FullPath( filename_full.c_str() ); - if ( included.count( filename_check ) ) - return 0; - included.insert( filename_check ); - - referencedPathnames.push_back( filename_full ); - - char* orig_mt; - - if ( getFileContents( filename_full.c_str(), &orig_mt ) ) - { - compiler_error( "Unable to find module ", modulename, "\n", "\t(Filename: ", filename_full, - ")\n" ); - return -1; - } - - CompilerContext mod_ctx( filename_full, program->add_dbg_filename( filename_full ), orig_mt ); - - std::string save = current_file_path; - current_file_path = getpathof( filename_full ); - - int res = compileContext( mod_ctx ); - - current_file_path = save; - - free( orig_mt ); - if ( res < 0 ) - return res; - else - return 0; -} - -int Compiler::handleInclude( CompilerContext& ctx ) -{ - Token tk_module_name, tk_semicolon; - - if ( getToken( ctx, tk_module_name ) ) - { - compiler_error( "Error in INCLUDE statement: INCLUDE should be followed by a module name.\n" ); - return -1; - } - if ( tk_module_name.id != TOK_IDENT && tk_module_name.id != TOK_STRING ) - { - compiler_error( "Error in INCLUDE statement: Expected identifier, got '", tk_module_name, - "'\n" ); - return -1; - } - - if ( getToken( ctx, tk_semicolon ) ) - { - compiler_error( "Error in INCLUDE statement (module ", tk_module_name, - "): Expected ';', got end-of-file or error\n" ); - return -1; - } - if ( tk_semicolon.id != TOK_SEMICOLON ) - { - compiler_error( "Error in INCLUDE statement (module ", tk_module_name, "): Expected ';', got '", - tk_semicolon, "'\n" ); - return -1; - } - - return includeModule( tk_module_name.tokval() ); -} - - -int Compiler::insertBreak( const std::string& label ) -{ - // Now, we've eaten the break; or break label; and 'label' contains the label, if any. - // first, we must find the block level this refers to. - unsigned numVarsToKill = 0; - for ( int i = static_cast( localscope.blockdescs_.size() - 1 ); i >= 0; --i ) - { - BlockDesc& bd = localscope.blockdescs_[i]; - - numVarsToKill += bd.varcount; - - if ( bd.break_ok && ( label == "" || // we didn't pick, and this is closest - label == bd.label ) ) // this is the one we picked - { - if ( numVarsToKill ) - { // local variables were declared in this scope. We need to kill 'em. - - program->append( StoredToken( Mod_Basic, CTRL_LEAVE_BLOCK, TYP_CONTROL, numVarsToKill ), - nullptr ); - } - - unsigned goto_posn; - program->append( StoredToken( Mod_Basic, RSV_GOTO, TYP_RESERVED, 0 ), &goto_posn ); - bd.break_tokens.push_back( goto_posn ); - return 0; - } - } - compiler_error( "Couldn't find an appropriate break point", ( label != "" ? " for label " : "" ), - label, ".\n" ); - - return -1; -} - - -// break statements come in the following forms: -// break; -// break label; -// we'll emit a LEAVE_BLOCK token if necessary, and -// a GOTO which will be patched in when the block -// is completed. -int Compiler::handleBreak( CompilerContext& ctx ) -{ - Token tk; - std::string label; - - if ( getToken( ctx, tk ) || ( ( tk.id != TOK_IDENT ) && ( tk.id != TOK_SEMICOLON ) ) ) - { - compiler_error( "break statement: expected 'break;' or 'break label;'\n" ); - return -1; - } - - if ( tk.id == TOK_IDENT ) - { - label = tk.tokval(); - if ( getToken( ctx, tk ) || tk.id != TOK_SEMICOLON ) - { - compiler_error( "break statement: expected 'break;' or 'break label;'\n" ); - return -1; - } - } - else - { - label = ""; - } - - return insertBreak( label ); -} - -// continue statements come in the following forms: -// continue; -// continue label; -// we'll emit a LEAVE_BLOCK token if necessary, and -// a GOTO which will point at the continuePC of the block. -int Compiler::handleContinue( CompilerContext& ctx ) -{ - Token tk; - std::string label; - - if ( getToken( ctx, tk ) || ( ( tk.id != TOK_IDENT ) && ( tk.id != TOK_SEMICOLON ) ) ) - { - compiler_error( "continue statement: expected 'continue;' or 'continue label;'\n" ); - return -1; - } - - if ( tk.id == TOK_IDENT ) - { - label = tk.tokval(); - if ( getToken( ctx, tk ) || tk.id != TOK_SEMICOLON ) - { - compiler_error( "continue statement: expected 'continue;' or 'continue label;'\n" ); - return -1; - } - } - else - { - label = ""; - } - - // Now, we've eaten the continue; or continue label; and 'label' contains the label, if any. - // first, we must find the block level this refers to. - unsigned numVarsToKill = 0; - for ( int i = static_cast( localscope.blockdescs_.size() - 1 ); i >= 0; --i ) - { - BlockDesc& bd = localscope.blockdescs_[i]; - numVarsToKill += bd.varcount; - - if ( bd.continue_ok && ( label == "" || // we didn't pick, and this is closest - label == bd.label ) ) // this is the one we picked - { - if ( numVarsToKill ) - { // local variables were declared in this scope. We need to kill 'em. - - program->append( StoredToken( Mod_Basic, CTRL_LEAVE_BLOCK, TYP_CONTROL, numVarsToKill ), - nullptr ); - } - - unsigned goto_posn; - program->append( StoredToken( Mod_Basic, RSV_GOTO, TYP_RESERVED, 0 ), &goto_posn ); - bd.continue_tokens.push_back( goto_posn ); - return 0; - } - } - compiler_error( "Couldn't find an appropriate continue point", - ( label != "" ? " for label " : "" ), label, ".\n" ); - - return -1; -} - - -// BASIC-style FOR loop: -// FOR I := 1 to 5 DO -// FOR I -// -// Emitted Code: -// (starting expr value) -// (ending expr value) -// INITFOR -// statement_part: -// (code block) -// NEXTFOR(statement_part) -// -// -int Compiler::handleBracketedFor_basic( CompilerContext& ctx ) -{ - CompilerContext for_ctx( ctx ); - int res; - - Token itrvar, tmptoken; - - res = getToken( ctx, itrvar ); - if ( res < 0 ) - return res; - if ( itrvar.id != TOK_IDENT ) - { - compiler_error( "FOR iterator must be an identifier, got ", itrvar, "\n" ); - return res; - } - - if ( localscope.varexists( itrvar.tokval() ) ) - { - compiler_error( "FOR iterator '", itrvar, "' hides a local variable.\n" ); - return -1; - } - - res = eatToken( ctx, TOK_ASSIGN ); - if ( res < 0 ) - return res; - - res = getExpr( ctx, EXPR_FLAG_TO_TERM_ALLOWED ); - if ( res < 0 ) - return res; - - res = eatToken( ctx, RSV_TO ); - if ( res < 0 ) - return res; - - res = getExpr2( ctx, EXPR_FLAG_AUTO_TERM_ALLOWED ); - if ( res < 0 ) - return res; - - enterblock( CanNotBeLabelled ); - unsigned initfor_posn; - program->append( StoredToken( Mod_Basic, INS_INITFOR, TYP_RESERVED, 0 ), &initfor_posn ); - - /* - INITFOR creates two local variables, placeholders for the iterator - and end value. Only the iterator can be accessed, for now. - */ - program->addlocalvar( itrvar.tokval() ); - if ( compilercfg.VerbosityLevel >= 5 ) - localscope.addvar( itrvar.tokval(), for_ctx ); - else - localscope.addvar( itrvar.tokval(), for_ctx, false ); - program->addlocalvar( "_" + std::string( itrvar.tokval() ) + "_end" ); - localscope.addvar( "_" + std::string( itrvar.tokval() ) + "_end", for_ctx, false ); - - StoredTokenContainer* prog_tokens = &program->tokens; - unsigned again_posn = prog_tokens->next(); - enterblock( CanBeLabelled ); - Token endblock_tkn; - res = readblock( ctx, 1, RSV_ENDFOR, nullptr, &endblock_tkn ); - if ( res < 0 ) - return res; - - emit_leaveblock(); - - program->update_dbg_pos( endblock_tkn ); - unsigned nextfor_posn; - program->append( StoredToken( Mod_Basic, INS_NEXTFOR, TYP_RESERVED, again_posn ), &nextfor_posn ); - - patchblock_continues( nextfor_posn ); - patchblock_breaks( prog_tokens->next() ); - localscope.popblock(); - program->leaveblock(); - - leaveblock( 0, 0 ); - - patchoffset( initfor_posn, prog_tokens->next() ); - - return 0; -} - -int Compiler::handleBracketedFor_c( CompilerContext& ctx ) -{ - { - Token tkn; - getToken( ctx, tkn ); - if ( tkn.id != TOK_LPAREN ) - { - compiler_error( "FOR: expected '('\n" ); - return -1; - } - } - - enterblock( CanBeLabelled ); - - Expression initial_expr; - Expression predicate_expr; - Expression iterate_expr; - if ( readexpr( initial_expr, ctx, EXPR_FLAG_SEMICOLON_TERM_ALLOWED ) != 1 ) - return -1; - if ( readexpr( predicate_expr, ctx, EXPR_FLAG_SEMICOLON_TERM_ALLOWED ) != 1 ) - return -1; - if ( readexpr( iterate_expr, ctx, EXPR_FLAG_RIGHTPAREN_TERM_ALLOWED ) != 1 ) - return -1; - - { - Token tkn; - if ( getToken( ctx, tkn ) || tkn.id != TOK_RPAREN ) - { - compiler_error( "FOR: expected '('\n" ); - return -1; - } - } - - /* - The c-style 'for' statement gets generated as follows: - given: - for( initial; predicate; iterate ) statement; - initial; - again: - predicate; - if true goto statement_part; - break; - statement_part: - statement; - iterate_part: - iterate; - goto again; - */ - - - inject( initial_expr ); - program->append( StoredToken( Mod_Basic, TOK_CONSUMER, TYP_UNARY_OPERATOR, 0 ) ); - StoredTokenContainer* prog_tokens = &program->tokens; - unsigned againPC = prog_tokens->next(); - inject( predicate_expr ); - - unsigned if_posn; - program->append( StoredToken( Mod_Basic, RSV_JMPIFFALSE, TYP_RESERVED, 0 ), &if_posn ); - - enterblock( CanNotBeLabelled ); - Token endblock_tkn; - int res = readblock( ctx, 1, RSV_ENDFOR, nullptr, &endblock_tkn ); - if ( res < 0 ) - return res; - leaveblock( 0, 0 ); - - unsigned continuePC = prog_tokens->next(); - - program->update_dbg_pos( endblock_tkn ); - inject( iterate_expr ); - program->append( StoredToken( Mod_Basic, TOK_CONSUMER, TYP_UNARY_OPERATOR, 0 ) ); - - program->append( StoredToken( Mod_Basic, RSV_GOTO, TYP_RESERVED, againPC ), nullptr ); - patchoffset( if_posn, prog_tokens->next() ); - - leaveblock( prog_tokens->next(), continuePC ); - - return 0; -} - -int Compiler::handleFor( CompilerContext& ctx ) -{ - Token tkn; - int res; - res = peekToken( ctx, tkn ); - if ( res ) - { - compiler_error( "Error in FOR statement\n" ); - return -1; - } - - if ( tkn.id == TOK_LPAREN ) - return handleBracketedFor_c( ctx ); - else - return handleBracketedFor_basic( ctx ); -} -void Compiler::emitFileLine( CompilerContext& ctx ) -{ - int cnt = program->tokens.count(); - program->fileline.resize( cnt + 1 ); - program->fileline[cnt] = - ctx.filename + ", Line " + Clib::tostring( static_cast( ctx.line ) ); -} -void Compiler::emitFileLineIfFileChanged( CompilerContext& ctx ) -{ - std::string last; - if ( program->fileline.size() ) - last = program->fileline.back(); - - if ( program->fileline.size() == program->tokens.count() + 1 || last.empty() || - last.substr( 0, ctx.filename.size() ) != ctx.filename ) - { - emitFileLine( ctx ); - } -} -void Compiler::savesourceline() -{ - int cnt = program->tokens.count(); - program->sourcelines.resize( cnt + 1 ); - program->sourcelines[cnt] = curLine; -} -int Compiler::_getStatement( CompilerContext& ctx, int level ) -{ - int res; - unsigned last_position = 0; - - readCurLine( ctx ); - - if ( include_debug ) - { - program->symbols.append( curLine, last_position ); - - DebugToken DT; - DT.sourceFile = curSourceFile; - DT.offset = static_cast( ctx.s - ctx.s_begin ); - DT.strOffset = last_position; - - program->symbols.append( &DT, sizeof DT, last_position ); - } - StoredToken stok( Mod_Basic, CTRL_STATEMENTBEGIN, TYP_CONTROL, last_position ); - - - // reinit(); - - Token token; - - if ( peekToken( ctx, token ) == 1 ) - { - getToken( ctx, token ); // trailing whitespace can hurt. - return 1; // all done! - } - - if ( token.id != RSV_FUNCTION && token.id != RSV_PROGRAM ) - { - savesourceline(); - emitFileLineIfFileChanged( ctx ); - } - - if ( include_debug ) - program->append( stok, &last_position ); - else - last_position = program->tokens.count(); - - if ( token.deprecated ) - { - compiler_warning( &ctx, "Warning: Found deprecated token '", token.tokval(), "' on line ", - ctx.line, " of ", ctx.filename, "\n" ); - } - - if ( token.type == TYP_RESERVED ) - { - getToken( ctx, token ); - switch ( token.id ) - { - case RSV_OPTION_BRACKETED: - compiler_error( "_OptionBracketed is obsolete.\n" ); - // bracketed_if_ = true; - return 0; - - case RSV_EXPORTED: - case RSV_FUNCTION: - return handleBracketedFunction2( ctx, level, token.type ); - - case RSV_USE_MODULE: - return handleUse( ctx ); - case RSV_INCLUDE_FILE: - return handleInclude( ctx ); - - case RSV_PROGRAM: - return handleProgram( ctx, level + 1 ); - case RSV_RETURN: - return handleReturn( ctx ); - case RSV_EXIT: - return handleExit( ctx ); - - case RSV_ST_IF: - return handleBracketedIf( ctx, level + 1 ); - - case RSV_FOR: - return handleFor( ctx ); - case RSV_FOREACH: - return handleForEach( ctx, level + 1 ); - case RSV_SWITCH: - return handleSwitch( ctx, level + 1 ); - case RSV_REPEAT: - return handleRepeatUntil( ctx, level + 1 ); - case RSV_DO: - return handleDoClause( ctx, level + 1 ); - case RSV_WHILE: - return handleBracketedWhile( ctx, level + 1 ); - - case RSV_BREAK: - return handleBreak( ctx ); - case RSV_CONTINUE: - return handleContinue( ctx ); - - case RSV_VAR: - return handleVarDeclare( ctx, inGlobalScope() ? RSV_GLOBAL : RSV_LOCAL ); - case RSV_CONST: - return handleConstDeclare( ctx ); - case RSV_ENUM: - return handleEnumDeclare( ctx ); - - // DEPRECATED: - // case RSV_BEGIN: return handleBlock(ctx, level+1); - - default: - compiler_error( "Unhandled reserved word: ", token, "\n" ); - return -1; - // assert(0); - break; - } - } - else if ( token.type == TYP_LABEL ) - { - if ( !quiet ) - INFO_PRINT << "Label found! " << token << "\n"; - getToken( ctx, token ); - - Token precedes; - res = peekToken( ctx, precedes ); - if ( res != 0 || - ( precedes.id != RSV_SWITCH && precedes.id != RSV_FOREACH && precedes.id != RSV_REPEAT && - precedes.id != RSV_WHILE && precedes.id != RSV_DO && precedes.id != RSV_FOR ) ) - { - compiler_error( - "Illegal location for label: ", token.tokval(), "\n", - "Labels can only come before DO, WHILE, FOR, FOREACH, REPEAT, and CASE statements.\n" ); - return -1; - } - latest_label = token.tokval(); - token.lval = last_position; - return 0; - } - - size_t exprlen; - res = getExpr( ctx, EXPR_FLAG_SEMICOLON_TERM_ALLOWED | EXPR_FLAG_CONSUME_RESULT, &exprlen ); - - if ( res < 0 ) - return res; - if ( exprlen != 0 ) - { - StoredToken tmptoken; - StoredTokenContainer* prog_tokens = &program->tokens; - prog_tokens->atGet1( prog_tokens->count() - 1, tmptoken ); - if ( tmptoken.id == TOK_CONSUMER ) - { - prog_tokens->atGet1( prog_tokens->count() - 2, tmptoken ); - } - - switch ( tmptoken.id ) - { - case TOK_ASSIGN: - case TOK_PLUSEQUAL: - case TOK_MINUSEQUAL: - case TOK_TIMESEQUAL: - case TOK_DIVIDEEQUAL: - case TOK_MODULUSEQUAL: - case INS_SUBSCRIPT_ASSIGN: - case INS_SUBSCRIPT_ASSIGN_CONSUME: - case INS_MULTISUBSCRIPT_ASSIGN: - case INS_ASSIGN_CONSUME: - case TOK_ADDMEMBER: - case TOK_DELMEMBER: - case TOK_FUNC: - case INS_CALL_METHOD: - case TOK_USERFUNC: - case CTRL_JSR_USERFUNC: - case INS_ASSIGN_LOCALVAR: - case INS_ASSIGN_GLOBALVAR: - case INS_SET_MEMBER: - case INS_SET_MEMBER_CONSUME: - case INS_SET_MEMBER_ID: - case INS_SET_MEMBER_ID_CONSUME: - case INS_CALL_METHOD_ID: - case INS_SET_MEMBER_ID_CONSUME_PLUSEQUAL: - case INS_SET_MEMBER_ID_CONSUME_MINUSEQUAL: - case INS_SET_MEMBER_ID_CONSUME_TIMESEQUAL: - case INS_SET_MEMBER_ID_CONSUME_DIVIDEEQUAL: - case INS_SET_MEMBER_ID_CONSUME_MODULUSEQUAL: - case TOK_UNPLUSPLUS: - case TOK_UNMINUSMINUS: - case TOK_UNPLUSPLUS_POST: - case TOK_UNMINUSMINUS_POST: - case INS_SET_MEMBER_ID_UNPLUSPLUS: - case INS_SET_MEMBER_ID_UNMINUSMINUS: - case INS_SET_MEMBER_ID_UNPLUSPLUS_POST: - case INS_SET_MEMBER_ID_UNMINUSMINUS_POST: - // ok! These operators actually accomplish something - break; - default: - if ( tmptoken.id == TOK_EQUAL1 ) - { - compiler_warning( &ctx, - "Warning: Equals test result ignored. Did you mean := for assign?\n", - "near: ", curLine, "\n" ); - } - else - { - // warn code has no effect/value lost - compiler_warning( &ctx, "Warning: Result of operation may have no effect.\n", - "Token ID: ", tmptoken.id, "\n", "near: ", curLine, "\n" ); - } - break; - } - // cout << "Statement: " << Parser.CA << endl; - } - return res; -} - -int Compiler::getStatement( CompilerContext& ctx, int level ) -{ - ctx.skipws(); - ctx.skipcomments(); - - CompilerContext savectx( ctx ); - program->setcontext( savectx ); - program->setstatementbegin(); - int res = 0; - try - { - res = _getStatement( ctx, level ); - if ( res < 0 ) - { - fmt::Writer _tmp; - _tmp << "Error compiling statement at "; - savectx.printOnShort( _tmp ); - compiler_error( _tmp.str() ); - } - } - catch ( std::exception& ex ) - { - fmt::Writer _tmp; - _tmp << "Error compiling statement at "; - savectx.printOnShort( _tmp ); - _tmp << ex.what() << "\n"; - compiler_error( _tmp.str() ); - res = -1; - } - return res; -} - -// pass 2 function: just skip past, to the ENDFUNCTION. -int Compiler::handleBracketedFunction2( CompilerContext& ctx, int /*level*/, int tokentype ) -{ - CompilerContext save_ctx( ctx ); - int res = -1; - Token tk_funcname; - - if ( tokentype == RSV_EXPORTED ) - { - Token tk_function; - res = getToken( ctx, tk_function ); - if ( res ) - return res; - if ( tk_function.id != RSV_FUNCTION ) - { - compiler_error( "Expected 'function' after 'exported'.\n" ); - return -1; - } - } - - if ( inFunction ) - { - compiler_error( "Can't declare a function inside another function.\n" ); - return -1; - } - getToken( ctx, tk_funcname ); - while ( ctx.s[0] ) - { - Token token; - res = getToken( ctx, token ); - if ( res < 0 ) - break; - if ( token.id == RSV_ENDFUNCTION ) - { - // we already grabbed the function body. - return 0; - } - } - if ( !ctx.s[0] ) - { - compiler_error( "End-of-File detected, expected 'ENDFUNCTION'\n" ); - return -1; - } - - if ( res < 0 ) - { - compiler_error( "Error occurred reading function body for '", tk_funcname.tokval(), "'\n", - "Function location: ", save_ctx, "Error location: \n" ); - return res; - } - - return res; -} - -int Compiler::handleProgram( CompilerContext& ctx, int /*level*/ ) -{ - Token tk_progname; - int res; - - if ( haveProgram ) - { - compiler_error( "'program' function has already been defined.\n" ); - return -1; - } - haveProgram = true; - program->program_decl = curLine; - program_ctx = ctx; - const char* program_body_start = ctx.s; - while ( ctx.s[0] ) - { - Token token; - res = getToken( ctx, token ); - if ( res < 0 ) - return res; - - if ( token.id == RSV_ENDPROGRAM ) - { - const char* program_body_end = ctx.s; - size_t len = program_body_end - program_body_start + 1; - program_source = new char[len]; - delete_these_arrays.push_back( program_source ); - memcpy( program_source, program_body_start, len - 1 ); - program_source[len - 1] = '\0'; - program_ctx.s = program_ctx.s_begin = program_source; - return 0; - } - } - compiler_error( "End of file detected, expected 'endprogram'\n" ); - return -1; -} - -int Compiler::handleProgram2( CompilerContext& ctx, int level ) -{ - Token tk_progname; - int res; - - emitFileLine( ctx ); - program->program_PC = program->tokens.count(); - - res = getToken( ctx, tk_progname ); - if ( res < 0 ) - return res; - if ( tk_progname.id != TOK_IDENT ) - { - compiler_error( "Error: expected identified after 'program', got '", tk_progname, "'\n" ); - return -1; - } - - program->enterfunction(); - enterblock( CanNotBeLabelled ); - - res = eatToken( ctx, TOK_LPAREN ); - if ( res < 0 ) - return res; - for ( ;; ) - { - Token token; - res = getToken( ctx, token ); - bool unused = false; - if ( res < 0 ) - return res; - if ( res > 0 ) - { - compiler_error( "End-of-file reached reading program argument list\n" ); - return -1; - } - if ( token.id == TOK_UNUSED ) - { - unused = true; - res = getToken( ctx, token ); - if ( res ) - return -1; - } - if ( token.id == TOK_RPAREN ) - { - break; - } - else if ( token.id == TOK_IDENT ) - { - unsigned varpos; - if ( localscope.varexists( token.tokval(), varpos ) ) - { - compiler_error( "Program argument '", token, "' multiply defined.\n" ); - return -1; - } - unsigned posn; - program->symbols.append( token.tokval(), posn ); - program->append( StoredToken( Mod_Basic, INS_GET_ARG, TYP_OPERATOR, posn ), nullptr ); - program->addlocalvar( token.tokval() ); - localscope.addvar( token.tokval(), ctx, true, unused ); - - res = peekToken( ctx, token ); - if ( res < 0 ) - return res; - if ( token.id == TOK_COMMA ) - getToken( ctx, token ); - ++nProgramArgs; - } - else - { - compiler_error( "Expected arguments or right-paren in program arglist, got '", token, "'\n" ); - return -1; - } - } - program->haveProgram = true; - program->expectedArgs = nProgramArgs; - Token endblock_tkn; - res = readblock( ctx, level, RSV_ENDPROGRAM, nullptr, &endblock_tkn ); - if ( res < 0 ) - return res; - - program->update_dbg_pos( endblock_tkn ); - leaveblock( 0, 0 ); - program->leavefunction(); - - return 0; -} - -int Compiler::handleBracketedFunction3( UserFunction& userfunc, CompilerContext& ctx ) -{ - StoredTokenContainer* program_tokens = &program->tokens; - unsigned first_PC = program_tokens->count(); - - emitFileLine( ctx ); - program->function_decls.resize( program_tokens->count() + 1 ); - program->function_decls[program_tokens->count()] = userfunc.declaration; - CompilerContext save_ctx( ctx ); - int res; - Token token; - - inFunction = 1; - /* - should be begin, then statements while peektoken != end, then eat end. - - getToken(s, token); - if (token.type != TYP_DELIMITER || token.id != DELIM_SEMICOLON) { - err = PERR_MISSINGDELIM; - return -1; - } - */ - /* woo-hoo! recursive calls should work. */ - // cout << "func decl: " << curLine << endl; - unsigned posn = 0; - - if ( userfunc.exported ) - { - EPExportedFunction ef; - ef.name = userfunc.name; - ef.nargs = static_cast( userfunc.parameters.size() ); - ef.PC = program_tokens->count(); - program->exported_functions.push_back( ef ); - - // insert the stub: - program->append( StoredToken( Mod_Basic, CTRL_MAKELOCAL, TYP_CONTROL, 0 ), ctx ); - program->append( StoredToken( Mod_Basic, CTRL_JSR_USERFUNC, TYP_CONTROL, ef.PC + 3 ), ctx ); - program->append( StoredToken( Mod_Basic, CTRL_PROGEND, TYP_CONTROL, 0 ), ctx ); - } - - userfunc.position = posn = program_tokens->count(); - - - program->enterfunction(); - enterblock( CanNotBeLabelled ); - - for ( int i = static_cast( userfunc.parameters.size() - 1 ); i >= 0; --i ) - { - UserParam* params = &userfunc.parameters[i]; - program->symbols.append( params->name.c_str(), posn ); - program->append( - StoredToken( Mod_Basic, params->pass_by_reference ? INS_POP_PARAM_BYREF : INS_POP_PARAM, - TYP_OPERATOR, posn ), - save_ctx, nullptr ); - program->addlocalvar( params->name ); - localscope.addvar( params->name, ctx, true, params->unused ); - } - - BTokenId last_statement_id; - Token endblock_tkn; - res = readblock( ctx, 1, RSV_ENDFUNCTION, &last_statement_id, &endblock_tkn ); - if ( res < 0 ) - { - compiler_error( "Error in function '", userfunc.name, "', ", ctx, "\n" ); - return res; - } - program->update_dbg_pos( endblock_tkn ); - - StoredToken tmp; - program_tokens->atGet1( program_tokens->count() - 1, tmp ); - - /* - This used to check to see if the last instruction was a RETURN; - however, if that's in an ELSE block, that return won't get executed. - This means if the function really does end in a return, this will - generate extra code. The optimizer will have to take care of this, - if it can. - Also note, the "leaveblock" at the end will also generate an instruction - that will never get executed (NEVER!) - */ - if ( last_statement_id != RSV_RETURN ) - { - program->symbols.append( int( 0 ), posn ); - program->append( StoredToken( Mod_Basic, TOK_LONG, TYP_OPERAND, posn ) ); - program->append( StoredToken( Mod_Basic, RSV_RETURN, TYP_RESERVED, 0 ) ); - } - - // was leaveblock(0,0) - localscope.popblock(); - program->leaveblock(); - program->leavefunction(); - - unsigned last_PC = program_tokens->count() - 1; - program->addfunction( userfunc.name, first_PC, last_PC ); - - inFunction = 0; - return 0; -} - -int Compiler::forward_read_function( CompilerContext& ctx ) -{ - CompilerContext save_ctx( ctx ); - int res; - Token token; - - UserFunction userfunc; - userfunc.declaration = curLine; - - res = getToken( ctx, token ); - if ( res ) - return res; - if ( token.id == RSV_EXPORTED ) - { - userfunc.exported = true; - res = getToken( ctx, token ); - if ( res ) - return res; - if ( token.id != RSV_FUNCTION ) - { - compiler_error( "Expected 'function' after 'exported'\n" ); - return -1; - } - } - - if ( readFunctionDeclaration( ctx, userfunc ) ) - { - compiler_error( save_ctx ); - return -1; - } - userfunc.ctx = ctx; - - const char* function_body_start = ctx.s; - while ( ctx.s[0] ) - { - Token _token; - res = getToken( ctx, _token ); - if ( res < 0 ) - break; - if ( _token.id == RSV_ENDFUNCTION ) - { - const char* function_body_end = ctx.s; - size_t len = function_body_end - function_body_start + 1; - userfunc.function_body = new char[len]; - delete_these_arrays.push_back( userfunc.function_body ); - memcpy( userfunc.function_body, function_body_start, len - 1 ); - userfunc.function_body[len - 1] = '\0'; - userfunc.ctx.s = userfunc.ctx.s_begin = userfunc.function_body; - userFunctions[userfunc.name] = userfunc; - res = 0; - return 0; - } - } - if ( !ctx.s[0] ) - { - compiler_error( "End-of-File detected, expected 'ENDFUNCTION'\n", save_ctx ); - return -1; - } - - if ( res < 0 ) - { - compiler_error( "Error occurred reading function body for '", userfunc.name, "'\n", - "Function location: ", save_ctx, "Error location: \n" ); - return res; - } - return res; -} - - -void Compiler::patchoffset( unsigned instruc, unsigned newoffset ) -{ - StoredToken tkn; - - program->tokens.atGet1( instruc, tkn ); - tkn.offset = static_cast( newoffset ); - program->tokens.atPut1( tkn, instruc ); -} - - -int Compiler::compileContext( CompilerContext& ctx ) -{ - int res = 0; - // scopes.push( LocalScope() ); - // currentscope = &scopes.top(); - // currentscope = &scope_; - - - try - { - while ( ( res >= 0 ) && ctx.s[0] ) - { - res = getStatement( ctx, 0 ); - } - } - catch ( std::exception& ) - { - compiler_error( "Exception detected during compilation.\n", ctx ); - throw; - } - - // currentscope = nullptr; - // scopes.pop(); - // assert( scopes.empty() ); - - if ( res == -1 ) - { - fmt::Writer w; - if ( err || ext_err[0] ) - { - w << "Parse Error: " << ParseErrorStr[err]; - if ( ext_err[0] ) - w << " " << ext_err; - w << "\n"; - err = PERR_NONE; - ext_err[0] = '\0'; - } - else - { - w << "Compilation Error:\n"; - } - if ( curLine[0] ) - { - w << "Near: " << curLine << "\n"; - } - compiler_error( w.str(), ctx ); - return -1; - } - return 0; -} - -int Compiler::compile( CompilerContext& ctx ) -{ - unsigned dummy = 0; - curSourceFile = 0; - // startPos = ctx.s; - program->symbols.append( "", dummy ); - - // see comment by handleUse: may want to only allow use statements at beginning of program-> - // useModule( "implicit" ); - useModule( "basic" ); - useModule( "basicio" ); - - int res = 0; - res = compileContext( ctx ); - - if ( res < 0 ) - return res; - // reinit(); - - if ( haveProgram ) - { - // the local frame should be empty, so we can use it. - // program->append( StoredToken( Mod_Basic, CTRL_MAKELOCAL, TYP_CONTROL, 0 ) ); - try - { - res = handleProgram2( program_ctx, 1 ); - } - catch ( std::runtime_error& excep ) - { - compiler_error( excep.what(), "\n" ); - res = -1; - } - catch ( ... ) - { - res = -1; - } - if ( res < 0 ) - { - fmt::Writer _tmp; - _tmp << "Error detected in program body.\n" - << "Error occurred at "; - program_ctx.printOnShort( _tmp ); - compiler_error( _tmp.str() ); - return res; - } - - // program->append( StoredToken( Mod_Basic, CTRL_JSR_USERFUNC, TYP_CONTROL, programPos ) ); - } - - unsigned last_posn; - StoredToken tkn( Mod_Basic, CTRL_PROGEND, TYP_CONTROL, 0 ); - program->append( tkn, &last_posn ); - - return 0; -} - -// what am I, too good for stdio/ftell? geez... -// rope getline -int Compiler::getFileContents( const char* file, char** iv ) -{ - // linux fails always in the call before -#if defined(_WIN32) || defined(__APPLE__) - std::string truename = Clib::GetTrueName( file ); - std::string filepart = Clib::GetFilePart( file ); - if ( truename != filepart && Clib::FileExists( file ) ) - { - if ( compilercfg.ErrorOnFileCaseMissmatch ) - { - compiler_error( "Case mismatch: \n", " Specified: ", filepart, "\n", - " Filesystem: ", truename, "\n" ); - return -1; - } - compiler_warning( nullptr, "Case mismatch: \n", " Specified: ", filepart, "\n", - " Filesystem: ", truename, "\n" ); - } -#endif - - - char* s = nullptr; - - FILE* fp = fopen( file, "rb" ); - if ( fp == nullptr ) - return -1; - - // Goes to the end of file - if ( fseek( fp, 0, SEEK_END ) != 0 ) - { - fclose( fp ); - return -1; - } - - // in order to measure its size - int filelen = ftell( fp ); - if ( filelen < 0 ) - { - fclose( fp ); - return -1; - } - - // and then return to beginning - if ( fseek( fp, 0, SEEK_SET ) != 0 ) - { - fclose( fp ); - return -1; - } - if ( filelen >= 3 ) - { - char bom[3]; - if ( fread( bom, 1, 3, fp ) != 3 ) - { - fclose( fp ); - return -1; - } - if ( !utf8::starts_with_bom( bom, bom + 3 ) ) - { - if ( fseek( fp, 0, SEEK_SET ) != 0 ) - { - fclose( fp ); - return -1; - } - } - else - filelen -= 3; - } - - s = (char*)calloc( 1, filelen + 1 ); - if ( !s ) - { - fclose( fp ); - return -1; - } - - if ( fread( s, filelen, 1, fp ) != 1 ) - { - fclose( fp ); - free( s ); - return -1; - } - - fclose( fp ); - *iv = s; - return 0; -} - -bool Compiler::read_function_declarations_in_included_file( const char* modulename ) -{ - std::string filename_part = modulename; - filename_part += ".inc"; - - std::string filename_full = current_file_path + filename_part; - - if ( filename_part[0] == ':' ) - { - const Plib::Package* pkg = nullptr; - std::string path; - if ( Plib::pkgdef_split( filename_part, nullptr, &pkg, &path ) ) - { - if ( pkg != nullptr ) - { - filename_full = pkg->dir() + path; - std::string try_filename_full = pkg->dir() + "include/" + path; - - if ( compilercfg.VerbosityLevel >= 10 ) - INFO_PRINT << "Searching for " << filename_full << "\n"; - - if ( !Clib::FileExists( filename_full.c_str() ) ) - { - if ( compilercfg.VerbosityLevel >= 10 ) - INFO_PRINT << "Searching for " << try_filename_full << "\n"; - if ( Clib::FileExists( try_filename_full.c_str() ) ) - { - if ( compilercfg.VerbosityLevel >= 10 ) - INFO_PRINT << "Found " << try_filename_full << "\n"; - - filename_full = try_filename_full; - } - } - else - { - if ( compilercfg.VerbosityLevel >= 10 ) - INFO_PRINT << "Found " << filename_full << "\n"; - - if ( Clib::FileExists( try_filename_full.c_str() ) ) - compiler_error( "Warning: Found '", filename_full.c_str(), "' and '", - try_filename_full.c_str(), "'! Will use first file!\n" ); - } - } - else - { - filename_full = compilercfg.PolScriptRoot + path; - - if ( compilercfg.VerbosityLevel >= 10 ) - { - INFO_PRINT << "Searching for " << filename_full << "\n"; - if ( Clib::FileExists( filename_full.c_str() ) ) - INFO_PRINT << "Found " << filename_full << "\n"; - } - } - } - else - { - compiler_error( "Unable to read include file '", modulename, "'\n" ); - return false; - } - } - else - { - if ( compilercfg.VerbosityLevel >= 10 ) - INFO_PRINT << "Searching for " << filename_full << "\n"; - - if ( !Clib::FileExists( filename_full.c_str() ) ) - { - std::string try_filename_full = compilercfg.IncludeDirectory + filename_part; - if ( compilercfg.VerbosityLevel >= 10 ) - INFO_PRINT << "Searching for " << try_filename_full << "\n"; - if ( Clib::FileExists( try_filename_full.c_str() ) ) - { - if ( compilercfg.VerbosityLevel >= 10 ) - INFO_PRINT << "Found " << try_filename_full << "\n"; - - filename_full = try_filename_full; - } - } - else - { - if ( compilercfg.VerbosityLevel >= 10 ) - INFO_PRINT << "Found " << filename_full << "\n"; - } - } - - std::string filename_check = Clib::FullPath( filename_full.c_str() ); - if ( included.count( filename_check ) ) - return true; - included.insert( filename_check ); - - char* orig_mt; - - if ( getFileContents( filename_full.c_str(), &orig_mt ) ) - { - compiler_error( "Unable to read include file '", filename_full, "'\n" ); - return false; - } - - CompilerContext mod_ctx( filename_full, program->add_dbg_filename( filename_full ), orig_mt ); - - std::string save = current_file_path; - current_file_path = getpathof( filename_full ); - - bool res = inner_read_function_declarations( mod_ctx ); - - current_file_path = save; - - free( orig_mt ); - return res; -} - -void Compiler::readCurLine( CompilerContext& ctx ) -{ - ctx.skipws(); - ctx.skipcomments(); - - Clib::stracpy( curLine, ctx.s, sizeof curLine ); - - char* t; - t = strchr( curLine, '\r' ); - if ( t ) - t[0] = '\0'; - t = strchr( curLine, '\n' ); - if ( t ) - t[0] = '\0'; -} - -bool Compiler::inner_read_function_declarations( const CompilerContext& ctx ) -{ - CompilerContext tctx( ctx ); - tctx.silence_unicode_warnings = true; - Token tkn; - for ( ;; ) - { - readCurLine( tctx ); - - CompilerContext save_ctx( tctx ); - if ( getToken( tctx, tkn ) != 0 ) - break; - - - if ( tkn.id == RSV_CONST ) - { - if ( handleConstDeclare( tctx ) ) - { - compiler_error( "Error in const declaration\n", tctx ); - return false; - } - } - else if ( tkn.id == RSV_ENUM ) - { - if ( handleEnumDeclare( tctx ) ) - { - compiler_error( "Error in enum declaration\n", tctx ); - return false; - } - } - else if ( tkn.id == RSV_FUNCTION || tkn.id == RSV_EXPORTED ) - { - tctx = save_ctx; - if ( forward_read_function( tctx ) ) - { - compiler_error( "Error reading function\n", tctx ); - return false; - } - } - else if ( tkn.id == RSV_INCLUDE_FILE ) - { - Token tk_module_name; - - if ( getToken( tctx, tk_module_name ) == 0 && - ( tk_module_name.id == TOK_IDENT || tk_module_name.id == TOK_STRING ) ) - { - if ( !read_function_declarations_in_included_file( tk_module_name.tokval() ) ) - { - // read.. prints out an error message - return false; - } - } - } - else if ( tkn.id == RSV_USE_MODULE ) - { - Token tk_module_name; - if ( getToken( tctx, tk_module_name ) == 0 && - ( tk_module_name.id == TOK_IDENT || tk_module_name.id == TOK_STRING ) ) - { - int res = useModule( tk_module_name.tokval() ); - if ( res < 0 ) - return false; - } - } - } - return true; -} - -bool Compiler::read_function_declarations( const CompilerContext& ctx ) -{ - bool res = inner_read_function_declarations( ctx ); - included.clear(); - constants.clear(); - program->clear_modules(); - return res; -} - -int Compiler::emit_function( UserFunction& uf ) -{ - userfunc_emit_order.push_back( uf.name ); - CompilerContext ctx( uf.ctx ); - // cout << "emitting " << uf.name << ": " << program->tokens.next() << endl; - int res = handleBracketedFunction3( uf, ctx ); - if ( res < 0 ) - { - compiler_error( "Error in function '", uf.name, "'.\n", ctx ); - } - return res; -} - -void Compiler::patch_callers( UserFunction& uf ) -{ - for ( unsigned i = 0; i < uf.forward_callers.size(); ++i ) - { - patchoffset( uf.forward_callers[i], uf.position ); - } -} - -int Compiler::emit_functions() -{ - bool any; - do - { - any = false; - for ( auto& elem : userFunctions ) - { - UserFunction& uf = elem.second; - if ( ( uf.exported || compiling_include || !uf.forward_callers.empty() ) && !uf.emitted ) - { - int res = emit_function( uf ); - if ( res < 0 ) - return res; - uf.emitted = any = true; - } - } - } while ( any ); - - for ( auto& elem : userFunctions ) - { - UserFunction& uf = elem.second; - if ( uf.emitted ) - { - patch_callers( uf ); - } - else - { - // cout << "not emitted: " << uf.name << endl; - } - } - return 0; -} - -void Compiler::rollback( EScriptProgram& prog, const EScriptProgramCheckpoint& checkpoint ) -{ - while ( prog.modules.size() > checkpoint.module_count ) - { - delete prog.modules.back(); - prog.modules.pop_back(); - } - prog.tokens.setcount( checkpoint.tokens_count ); - prog.symbols.setlength( checkpoint.symbols_length ); - while ( prog.sourcelines.size() > checkpoint.sourcelines_count ) - prog.sourcelines.pop_back(); - while ( prog.fileline.size() > checkpoint.fileline_count ) - prog.fileline.pop_back(); - while ( prog.dbg_filenum.size() > checkpoint.tokens_count ) - { - prog.dbg_filenum.pop_back(); - prog.dbg_linenum.pop_back(); - prog.dbg_ins_blocks.pop_back(); - prog.dbg_ins_statementbegin.pop_back(); - prog.statementbegin = false; - } - - for ( auto& elem : userFunctions ) - { - UserFunction& uf = elem.second; - while ( !uf.forward_callers.empty() && uf.forward_callers.back() >= checkpoint.tokens_count ) - { - uf.forward_callers.pop_back(); - } - } -} - -/** - * Given a file name, tells if this is a web script - */ -bool is_web_script( const char* file ) -{ - const char* ext = strstr( file, ".hsr" ); - if ( ext && memcmp( ext, ".hsr", 5 ) == 0 ) - return true; - ext = strstr( file, ".asp" ); - if ( ext && memcmp( ext, ".asp", 5 ) == 0 ) - return true; - return false; -} - -/** - * Transforms the raw html page into a script with a single WriteHtml() instruction - */ -std::string preprocess_web_script( const std::string& input ) -{ - std::string output; - output = "use http;"; - output += '\n'; - - bool reading_html = true; - bool source_is_emit = false; - const char* s = input.c_str(); - std::string acc; - while ( *s ) - { - if ( reading_html ) - { - if ( s[0] == '<' && s[1] == '%' ) - { - reading_html = false; - if ( !acc.empty() ) - { - output += "WriteHtmlRaw( \"" + acc + "\");\n"; - acc = ""; - } - s += 2; - source_is_emit = ( s[0] == '=' ); - if ( source_is_emit ) - { - output += "WriteHtmlRaw( "; - ++s; - } - } - else - { - if ( *s == '\"' ) - acc += "\\\""; - else if ( *s == '\r' ) - ; - else if ( *s == '\n' ) - acc += "\\n"; - else - acc += *s; - ++s; - } - } - else - { - if ( s[0] == '%' && s[1] == '>' ) - { - reading_html = true; - s += 2; - if ( source_is_emit ) - output += " );\n"; - } - else - { - output += *s++; - } - } - } - if ( !acc.empty() ) - output += "WriteHtmlRaw( \"" + acc + "\");\n"; - return output; -} - - -/** - * Here starts the real complation. Reads the given file and process it - * - * @param in_file Path for the file to compile, no more validity checks are done - * @return <0 on error - */ -int Compiler::compileFile( const char* in_file ) -{ - int res = -1; - try - { - std::string filepath = Clib::FullPath( in_file ); - referencedPathnames.push_back( filepath ); - current_file_path = getpathof( filepath ); - if ( compilercfg.VerbosityLevel >= 11 ) - INFO_PRINT << "cfp: " << current_file_path << "\n"; - Clib::FileContents fc( filepath.c_str() ); - - if ( is_web_script( filepath.c_str() ) ) - { - fc.set_contents( preprocess_web_script( fc.contents() ) ); - } - - CompilerContext ctx( filepath, program->add_dbg_filename( filepath ), fc.contents() ); - - bool bres = read_function_declarations( ctx ); - // cout << "bres:" << bres << endl; - if ( !bres ) - { - res = -1; - } - else - { - res = compile( ctx ) || emit_functions(); - } - } - catch ( const char* s ) - { - compiler_error( "Exception Detected:", s, "\n" ); - res = -1; - } - catch ( std::exception& ex ) - { - compiler_error( "Exception Detected:\n", ex.what(), "\n" ); - res = -1; - } - // catch(...) - // { - // cout << "Generic Exception" << endl; - // res = -1; - // } - - if ( res < 0 ) - compiler_error( "Compilation failed.\n" ); - - // if (contains_tabs && Compiler.warnings_) - // cout << "Warning! Source contains TAB characters" << endl; - - return res; -} - - -int Compiler::write( const char* fname ) -{ - return program->write( fname ); -} - -int Compiler::write_dbg( const char* fname, bool gen_txt ) -{ - return program->write_dbg( fname, gen_txt ); -} - -void Compiler::writeIncludedFilenames( const char* fname ) const -{ - std::ofstream ofs( fname, std::ios::out | std::ios::trunc ); - // ofs << current_file_path << endl; - for ( const auto& elem : referencedPathnames ) - { - ofs << elem << std::endl; - } -} - -void Compiler::dump( std::ostream& os ) -{ - program->dump( os ); -} - -bool Compiler::compile_file( const std::string& filename ) -{ - return compileFile( filename.c_str() ) == 0; -} - -bool Compiler::write_ecl( const std::string& pathname ) -{ - return write( pathname.c_str() ) == 0; -} - -void Compiler::write_listing( const std::string& pathname ) -{ - std::ofstream ofs( pathname ); - dump( ofs ); -} - -void Compiler::write_dbg( const std::string& pathname, bool include_debug_text ) -{ - write_dbg( pathname.c_str(), include_debug_text ); -} - -void Compiler::write_included_filenames( const std::string& pathname ) -{ - writeIncludedFilenames( pathname.c_str() ); -} - -void Compiler::set_include_compile_mode() -{ - setIncludeCompileMode(); -} - -Pol::Bscript::Compiler::LegacyFunctionOrder Compiler::get_legacy_function_order() const -{ - std::vector modulefunc_emit_order; - - if ( program.get() ) - { - for ( auto module : program->modules ) - { - for ( auto function : module->used_functions ) - { - std::string scoped_name = - std::string( module->modulename ) + "::" + std::string( function->name ); - - modulefunc_emit_order.push_back( scoped_name ); - } - } - } - - return Pol::Bscript::Compiler::LegacyFunctionOrder{ modulefunc_emit_order, userfunc_emit_order }; -} - -} // namespace Legacy -} // namespace Bscript -} // namespace Pol - /* - local x; [ "x", RSV_LOCAL, # ] - local x:=5; [ "x", RSV_LOCAL, 5, TOK_ASSIGN, # ] - local x,y:=5; [ "x", RSV_LOCAL, #, "y", TOK_LOCAL, - local x:=5,y; - - x := 5; - - declare function foo(a,b,c,d); - - function foo(a,b,c,d) - begin - return - or - return "hey" - end - - statements: - - if expr [then] statement [else statement]; - - do - statement; - while expr; - - begin - statements; - end - - - while expr - statement; - - - Alternative: - if expr - statments; - [else - statements; ] - endif; - */ diff --git a/pol-core/bscript/compiler.h b/pol-core/bscript/compiler.h deleted file mode 100644 index 4de40c5f96..0000000000 --- a/pol-core/bscript/compiler.h +++ /dev/null @@ -1,315 +0,0 @@ -/** @file - * - * @par History - */ - - -#ifndef H_COMPILER_H -#define H_COMPILER_H - -#include "compctx.h" -#include "tokens.h" -#include "userfunc.h" - -#ifndef __PARSER_H -#include "parser.h" -#endif - -#include -#include -#include -#include -#include - -#include "../clib/maputil.h" -#include "../clib/refptr.h" - -namespace Pol -{ -namespace Bscript -{ -class ModuleFunction; -class Token; -class UserFunction; -} // namespace Bscript -} // namespace Pol - -namespace Pol -{ -namespace Bscript -{ -namespace Compiler -{ - struct LegacyFunctionOrder; -} -class EScriptProgram; -class EScriptProgramCheckpoint; -class FunctionalityModule; -namespace Legacy -{ -/* - ack, this is a misnomer. - "CanBeLabelled" means "break or continue can happen here." - enum eb_type { CanBeLabelled = true, CanNotBeLabelled = false }; - */ - -enum eb_label_ok -{ - CanBeLabelled = true, - CanNotBeLabelled = false -}; -enum eb_break_ok -{ - BreakOk = true, - BreakNotOk = false -}; -enum eb_continue_ok -{ - ContinueOk = true, - ContinueNotOk = false -}; - -struct BlockDesc -{ - unsigned varcount; // how many variables in this block? - unsigned valcount; // how many values on the stack should be removed? - - eb_label_ok label_ok; - eb_break_ok break_ok; - eb_continue_ok continue_ok; - - std::string label; // label of construct - - // addresses of tokens whos offset needs to be patched with - // the final break/continue jump addresses. - typedef std::vector TokenAddrs; - TokenAddrs break_tokens; - TokenAddrs continue_tokens; - - unsigned blockidx; -}; - -struct Variable -{ - std::string name; - mutable bool used = false; - mutable bool unused = false; - CompilerContext ctx; -}; -typedef std::vector Variables; - -class Scope -{ -public: - BlockDesc& pushblock(); - void popblock( bool varsOnly ); - void addvar( const std::string& varname, const CompilerContext& ctx, bool warn_on_notused = true, - bool unused = false ); - void addvalue(); - bool inblock() const { return !blockdescs_.empty(); } - unsigned numVarsInBlock() const { return blockdescs_.back().varcount; } - const BlockDesc& blockdesc() const { return blockdescs_.back(); } - bool varexists( const std::string& varname, unsigned& idx ) const; - bool varexists( const std::string& varname ) const; - unsigned int numVariables() const { return static_cast( variables_.size() ); } - -private: - Variables variables_; - - // we may need a blocktag, too, in which case this - // should be a stack/vector of a struct. - std::vector blockdescs_; - friend class Compiler; -}; - -class Compiler final : public SmartParser -{ -public: - static bool check_filecase_; - static void setCheckFileCase( bool check ) { check_filecase_ = check; } - -private: - std::string current_file_path; - - - int curSourceFile; - /** Part of the content of the line being read, only for info/debug output, limited to 80 chars */ - char curLine[80]; - int inExpr; - int inFunction; - bool haveProgram; - bool compiling_include; - unsigned programPos; - unsigned nProgramArgs; - - CompilerContext program_ctx; - char* program_source; - - typedef std::set INCLUDES; - INCLUDES included; - - std::vector referencedPathnames; - - ref_ptr program; - typedef std::map UserFunctions; - UserFunctions userFunctions; - - int findLabel( Token& tok, unsigned& posn ); - int enterLabel( Token& tok ); - - typedef std::map Constants; - Constants constants; - - /* - scopes: - because functions don't nest, a stack of block scopes isn't necessary. - when we enter a function, we'll enter a scope. - exiting the function automatically removes its "locals" - we'll still end up inserting a "leave block", but that - could be optimized out, I suppose. - */ - Variables globals_; - - bool globalexists( const std::string& varname, unsigned& idx, - CompilerContext* atctx = nullptr ) const; - - Scope localscope; - - bool varexists( const std::string& varname ) const; - - bool inGlobalScope() const { return localscope.inblock() == false; } - /* - special note on latest_label, enterblock, and leaveblock - latest_label is a hidden parameter to enterblock, set up - by getStatement processing a label ("tag:"). - note, getStatement checks to make sure the label is - followed by a WHILE or DO statement. - */ - std::string latest_label; - void enterblock( eb_label_ok eblabel, eb_break_ok ebbreak, eb_continue_ok ebcontinue ); - void enterblock( eb_label_ok et ); - int readblock( CompilerContext& ctx, int level, BTokenId endtokenid, - BTokenId* last_statement_id = nullptr, Token* block_end = nullptr ); - void leaveblock( unsigned breakPC, unsigned continuePC ); - void emit_leaveblock(); - void patchblock_continues( unsigned continuePC ); - void patchblock_breaks( unsigned breakPC ); - - void rollback( EScriptProgram& prog, const EScriptProgramCheckpoint& checkpoint ); - bool substitute_constant( Token* tkn ) const; // returns true if a constant was found. - void substitute_constants( Expression& expr ) const; - void convert_variables( Expression& expr ) const; - int validate( const Expression& expr, CompilerContext& ctx ) const; - int readexpr( Expression& expr, CompilerContext& ctx, unsigned flags ); - int read_subexpression( Expression& expr, CompilerContext& ctx, unsigned flags ); - void inject( Expression& expr ); - int insertBreak( const std::string& label ); - -public: - Compiler(); - ~Compiler(); - - void dump( std::ostream& os ); - void setIncludeCompileMode() { compiling_include = true; } - void addModule( FunctionalityModule* module ); - int useModule( const char* modulename ); - int includeModule( const std::string& modulename ); - virtual int isFunc( Token& tok, ModuleFunction** v ) override; - virtual int isUserFunc( Token& tok, UserFunction** userfunc ) override; - - void patchoffset( unsigned instruc, unsigned newoffset ); - void addToken( Token& tok ); - - virtual int isLegal( Token& tok ) override; - - virtual int getUserArgs( Expression& ex, CompilerContext& ctx, bool inject_jsr ) override; - virtual int getArrayElements( Expression& expr, CompilerContext& ctx ) override; - virtual int getNewArrayElements( Expression& expr, CompilerContext& ctx ) override; - virtual int getStructMembers( Expression& expr, CompilerContext& ctx ) override; - virtual int getDictionaryMembers( Expression& expr, CompilerContext& ctx ) override; - virtual int getMethodArguments( Expression& expr, CompilerContext& ctx, int& nargs ) override; - virtual int getFunctionPArgument( Expression& expr, CompilerContext& ctx, Token* tok ) override; - - int eatToken( CompilerContext& ctx, BTokenId tokenid ); - int getExpr( CompilerContext& ctx, unsigned expr_flags, size_t* exprlen = nullptr, - Expression* ex = nullptr ); - int getExpr2( CompilerContext& ctx, unsigned expr_flags, Expression* ex = nullptr ); - int getExprInParens( CompilerContext& ctx, Expression* ex = nullptr ); - int getSimpleExpr( CompilerContext& ctx ); - - int handleProgram( CompilerContext& ctx, int level ); - int handleProgram2( CompilerContext& ctx, int level ); - int readFunctionDeclaration( CompilerContext& ctx, UserFunction& userfunc ); - int handleDoClause( CompilerContext& ctx, int level ); - int handleRepeatUntil( CompilerContext& ctx, int level ); - int handleForEach( CompilerContext& ctx, int level ); - int handleReturn( CompilerContext& ctx ); - int handleExit( CompilerContext& ctx ); - int handleBreak( CompilerContext& ctx ); - int handleContinue( CompilerContext& ctx ); - int handleBlock( CompilerContext& ctx, int level ); - int handleBracketedIf( CompilerContext& ctx, int level ); - int handleBracketedWhile( CompilerContext& ctx, int level ); - int handleVarDeclare( CompilerContext& ctx, unsigned save_id ); - int handleConstDeclare( CompilerContext& ctx ); - int handleEnumDeclare( CompilerContext& ctx ); - int handleUse( CompilerContext& ctx ); - int handleInclude( CompilerContext& ctx ); - int handleFor( CompilerContext& ctx ); - int handleBracketedFor_c( CompilerContext& ctx ); - int handleBracketedFor_basic( CompilerContext& ctx ); - int handleSwitch( CompilerContext& ctx, int level ); - - void emitFileLine( CompilerContext& ctx ); - void emitFileLineIfFileChanged( CompilerContext& ctx ); - void readCurLine( CompilerContext& ctx ); - void savesourceline(); - - int getStatement( CompilerContext& ctx /*char **s */, int level ); - int _getStatement( CompilerContext& ctx /*char **s */, int level ); - - int getFileContents( const char* filename, char** contents ); - int compileFile( const char* fname ); - int compileContext( CompilerContext& ctx ); - int compile( CompilerContext& ctx /* char *s */ ); - - int write( const char* fname ); - int write_dbg( const char* fname, bool generate_txtfile ); - void writeIncludedFilenames( const char* fname ) const; - - bool compile_file( const std::string& filename ); - bool write_ecl( const std::string& pathname ); - void write_listing( const std::string& pathname ); - void write_dbg( const std::string& pathname, bool include_debug_text ); - void write_included_filenames( const std::string& pathname ); - void set_include_compile_mode(); - - - Pol::Bscript::Compiler::LegacyFunctionOrder get_legacy_function_order() const; - - // phase 0: determining bracket syntax - - // phase 1: read function declarations, constants (but clear constants) - int forward_read_function( CompilerContext& ctx ); - bool read_function_declarations( const CompilerContext& ctx ); - bool read_function_declarations_in_included_file( const char* modulename ); - bool inner_read_function_declarations( const CompilerContext& ctx ); - - // phase 2: compile the source - int handleBracketedFunction2( CompilerContext& ctx, int level, int tokentype ); - - // phase 3: emit functions that are actually used. - int handleBracketedFunction3( UserFunction& userfunc, CompilerContext& ctx ); - int emit_function( UserFunction& uf ); - int emit_functions(); - void patch_callers( UserFunction& uf ); - - std::vector userfunc_emit_order; - -private: - std::vector delete_these_arrays; -}; -} // namespace Legacy -} // namespace Bscript -} // namespace Pol -#endif // H_COMPILER_H diff --git a/pol-core/bscript/compiler/Compiler.cpp b/pol-core/bscript/compiler/Compiler.cpp index 074d24ab4e..ae1b74cf3c 100644 --- a/pol-core/bscript/compiler/Compiler.cpp +++ b/pol-core/bscript/compiler/Compiler.cpp @@ -30,11 +30,6 @@ Compiler::Compiler( SourceFileCache& em_cache, SourceFileCache& inc_cache, Profi Compiler::~Compiler() = default; -bool Compiler::compile_file( const std::string& filename ) -{ - return compile_file( filename, nullptr ); -} - bool Compiler::write_ecl( const std::string& pathname ) { if ( output ) @@ -86,8 +81,7 @@ void Compiler::set_include_compile_mode() user_function_inclusion = UserFunctionInclusion::All; } -bool Compiler::compile_file( const std::string& filename, - const LegacyFunctionOrder* legacy_function_order ) +bool Compiler::compile_file( const std::string& filename ) { bool success; try @@ -96,7 +90,7 @@ bool Compiler::compile_file( const std::string& filename, Report report( compilercfg.DisplayWarnings || compilercfg.ErrorOnWarning ); - compile_file_steps( pathname, legacy_function_order, report ); + compile_file_steps( pathname, report ); display_outcome( pathname, report ); bool have_warning_as_error = report.warning_count() && compilercfg.ErrorOnWarning; @@ -110,12 +104,9 @@ bool Compiler::compile_file( const std::string& filename, return success; } -void Compiler::compile_file_steps( const std::string& pathname, - const LegacyFunctionOrder* legacy_function_order, - Report& report ) +void Compiler::compile_file_steps( const std::string& pathname, Report& report ) { - std::unique_ptr workspace = - build_workspace( pathname, legacy_function_order, report ); + std::unique_ptr workspace = build_workspace( pathname, report ); if ( report.error_count() ) return; @@ -131,21 +122,19 @@ void Compiler::compile_file_steps( const std::string& pathname, if ( report.error_count() ) return; - analyze( *workspace, report); + analyze( *workspace, report ); if ( report.error_count() ) return; - output = generate( std::move( workspace ), legacy_function_order ); + output = generate( std::move( workspace ) ); } -std::unique_ptr Compiler::build_workspace( - const std::string& pathname, const LegacyFunctionOrder* legacy_function_order, - Report& report ) +std::unique_ptr Compiler::build_workspace( const std::string& pathname, + Report& report ) { Pol::Tools::HighPerfTimer timer; CompilerWorkspaceBuilder workspace_builder( em_cache, inc_cache, profile, report ); - auto workspace = - workspace_builder.build( pathname, legacy_function_order, user_function_inclusion ); + auto workspace = workspace_builder.build( pathname, user_function_inclusion ); profile.build_workspace_micros += timer.ellapsed().count(); return workspace; } @@ -181,11 +170,10 @@ void Compiler::analyze( CompilerWorkspace& workspace, Report& report ) profile.analyze_micros += timer.ellapsed().count(); } -std::unique_ptr Compiler::generate( - std::unique_ptr workspace, const LegacyFunctionOrder* legacy_function_order ) +std::unique_ptr Compiler::generate( std::unique_ptr workspace ) { Pol::Tools::HighPerfTimer codegen_timer; - auto compiled_script = CodeGenerator::generate( std::move( workspace ), legacy_function_order ); + auto compiled_script = CodeGenerator::generate( std::move( workspace ) ); profile.codegen_micros += codegen_timer.ellapsed().count(); return compiled_script; } diff --git a/pol-core/bscript/compiler/Compiler.h b/pol-core/bscript/compiler/Compiler.h index 74006b6151..58141c1b53 100644 --- a/pol-core/bscript/compiler/Compiler.h +++ b/pol-core/bscript/compiler/Compiler.h @@ -10,7 +10,6 @@ namespace Pol::Bscript::Compiler { class CompiledScript; class CompilerWorkspace; -struct LegacyFunctionOrder; class SourceFileCache; class Profile; class Report; @@ -30,19 +29,15 @@ class Compiler void write_included_filenames( const std::string& pathname ); void set_include_compile_mode(); - bool compile_file( const std::string& filename, const LegacyFunctionOrder* ); - void compile_file_steps( const std::string& pathname, const LegacyFunctionOrder*, Report& ); + void compile_file_steps( const std::string& pathname, Report& ); private: - std::unique_ptr build_workspace( const std::string&, - const LegacyFunctionOrder*, - Report& ); + std::unique_ptr build_workspace( const std::string&, Report& ); void register_constants( CompilerWorkspace&, Report& ); void optimize( CompilerWorkspace&, Report& ); void disambiguate( CompilerWorkspace&, Report& ); void analyze( CompilerWorkspace&, Report& ); - std::unique_ptr generate( std::unique_ptr, - const LegacyFunctionOrder* ); + std::unique_ptr generate( std::unique_ptr ); void display_outcome( const std::string& filename, Report& ); diff --git a/pol-core/bscript/compiler/LegacyFunctionOrder.h b/pol-core/bscript/compiler/LegacyFunctionOrder.h deleted file mode 100644 index 4e3853a54c..0000000000 --- a/pol-core/bscript/compiler/LegacyFunctionOrder.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef POLSERVER_LEGACYFUNCTIONORDER_H -#define POLSERVER_LEGACYFUNCTIONORDER_H - -#include -#include - -namespace Pol::Bscript::Compiler -{ -struct LegacyFunctionOrder -{ - std::vector modulefunc_emit_order; - std::vector userfunc_emit_order; -}; - -} // namespace Pol::Bscript::Compiler - -#endif // POLSERVER_LEGACYFUNCTIONORDER_H diff --git a/pol-core/bscript/compiler/astbuilder/CompilerWorkspaceBuilder.cpp b/pol-core/bscript/compiler/astbuilder/CompilerWorkspaceBuilder.cpp index 09e0f2f0fe..0d5d9ce82e 100644 --- a/pol-core/bscript/compiler/astbuilder/CompilerWorkspaceBuilder.cpp +++ b/pol-core/bscript/compiler/astbuilder/CompilerWorkspaceBuilder.cpp @@ -1,7 +1,6 @@ #include "CompilerWorkspaceBuilder.h" #include "clib/timer.h" -#include "bscript/compiler/LegacyFunctionOrder.h" #include "bscript/compiler/Profile.h" #include "bscript/compiler/Report.h" #include "bscript/compiler/ast/ModuleFunctionDeclaration.h" @@ -21,15 +20,14 @@ namespace Pol::Bscript::Compiler { CompilerWorkspaceBuilder::CompilerWorkspaceBuilder( SourceFileCache& em_cache, - SourceFileCache& inc_cache, - Profile& profile, Report& report ) + SourceFileCache& inc_cache, Profile& profile, + Report& report ) : em_cache( em_cache ), inc_cache( inc_cache ), profile( profile ), report( report ) { } std::unique_ptr CompilerWorkspaceBuilder::build( - const std::string& pathname, const LegacyFunctionOrder* legacy_function_order, - UserFunctionInclusion user_function_inclusion ) + const std::string& pathname, UserFunctionInclusion user_function_inclusion ) { auto compiler_workspace = std::make_unique( report ); BuilderWorkspace workspace( *compiler_workspace, em_cache, inc_cache, profile, report ); @@ -67,36 +65,9 @@ std::unique_ptr CompilerWorkspaceBuilder::build( if ( report.error_count() == 0 ) build_referenced_user_functions( workspace ); - if ( legacy_function_order ) - { - compiler_workspace->module_functions_in_legacy_order = - get_module_functions_in_order( workspace, *legacy_function_order ); - } - return compiler_workspace; } -std::vector -CompilerWorkspaceBuilder::get_module_functions_in_order( BuilderWorkspace& workspace, - const LegacyFunctionOrder& h ) -{ - std::vector ordered; - - for ( auto& scoped_name : h.modulefunc_emit_order ) - { - auto func = workspace.function_resolver.find( scoped_name ); - if ( !func ) - throw std::runtime_error( "No modulefunc '" + scoped_name + "' for parity." ); - - auto decl = dynamic_cast( func ); - if ( !decl ) - throw std::runtime_error( "No ModuleFunctionDeclaration for '" + scoped_name + - "' for parity." ); - - ordered.push_back( decl ); - } - return ordered; -} void CompilerWorkspaceBuilder::build_referenced_user_functions( BuilderWorkspace& workspace ) { diff --git a/pol-core/bscript/compiler/astbuilder/CompilerWorkspaceBuilder.h b/pol-core/bscript/compiler/astbuilder/CompilerWorkspaceBuilder.h index fd6b42debb..efdf59d75b 100644 --- a/pol-core/bscript/compiler/astbuilder/CompilerWorkspaceBuilder.h +++ b/pol-core/bscript/compiler/astbuilder/CompilerWorkspaceBuilder.h @@ -11,7 +11,6 @@ namespace Pol::Bscript::Compiler { class BuilderWorkspace; class CompilerWorkspace; -struct LegacyFunctionOrder; class ModuleFunctionDeclaration; class Profile; class Report; @@ -23,14 +22,9 @@ class CompilerWorkspaceBuilder CompilerWorkspaceBuilder( SourceFileCache& em_cache, SourceFileCache& inc_cache, Profile&, Report& ); - std::unique_ptr build( - const std::string& pathname, - const LegacyFunctionOrder* legacy_function_order, - UserFunctionInclusion ); + std::unique_ptr build( const std::string& pathname, UserFunctionInclusion ); private: - std::vector get_module_functions_in_order( - BuilderWorkspace&, const LegacyFunctionOrder& ); void build_referenced_user_functions( BuilderWorkspace& ); SourceFileCache& em_cache; diff --git a/pol-core/bscript/compiler/astbuilder/SourceFileProcessor.cpp b/pol-core/bscript/compiler/astbuilder/SourceFileProcessor.cpp index 5b7419906b..86b6216bac 100644 --- a/pol-core/bscript/compiler/astbuilder/SourceFileProcessor.cpp +++ b/pol-core/bscript/compiler/astbuilder/SourceFileProcessor.cpp @@ -21,12 +21,10 @@ using EscriptGrammar::EscriptParser; -namespace Pol::Bscript::Legacy -{ -std::string getpathof( const std::string& fname ); -} namespace Pol::Bscript::Compiler { +std::string getpathof( const std::string& fname ); + SourceFileProcessor::SourceFileProcessor( const SourceFileIdentifier& source_file_identifier, BuilderWorkspace& workspace, bool is_src, UserFunctionInclusion user_function_inclusion ) @@ -172,8 +170,7 @@ std::optional SourceFileProcessor::locate_include_file( { std::string filename_part = include_name + ".inc"; - std::string current_file_path = - Pol::Bscript::Legacy::getpathof( source_location.source_file_identifier->pathname ); + std::string current_file_path = getpathof( source_location.source_file_identifier->pathname ); std::string filename_full = current_file_path + filename_part; if ( filename_part[0] == ':' ) @@ -356,4 +353,12 @@ SourceLocation SourceFileProcessor::location_for( antlr4::ParserRuleContext& ctx return { &source_file_identifier, ctx }; } +std::string getpathof( const std::string& fname ) +{ + std::string::size_type pos = fname.find_last_of( "\\/" ); + if ( pos == std::string::npos ) + return "./"; + else + return fname.substr( 0, pos + 1 ); +} } // namespace Pol::Bscript::Compiler diff --git a/pol-core/bscript/compiler/codegen/CodeGenerator.cpp b/pol-core/bscript/compiler/codegen/CodeGenerator.cpp index 7ed3cfdd34..4dcaf7e12b 100644 --- a/pol-core/bscript/compiler/codegen/CodeGenerator.cpp +++ b/pol-core/bscript/compiler/codegen/CodeGenerator.cpp @@ -3,7 +3,6 @@ #include #include "StoredToken.h" -#include "bscript/compiler/LegacyFunctionOrder.h" #include "bscript/compiler/ast/ModuleFunctionDeclaration.h" #include "bscript/compiler/ast/Program.h" #include "bscript/compiler/ast/ProgramParameterList.h" @@ -23,7 +22,7 @@ namespace Pol::Bscript::Compiler { std::unique_ptr CodeGenerator::generate( - std::unique_ptr workspace, const LegacyFunctionOrder* legacy_function_order ) + std::unique_ptr workspace ) { auto program_info = workspace->program @@ -51,9 +50,9 @@ std::unique_ptr CodeGenerator::generate( module_declaration_registrar ); CodeGenerator generator( instruction_emitter, module_declaration_registrar ); - generator.register_module_functions( *workspace, legacy_function_order ); + generator.register_module_functions_alphabetically( *workspace ); - sort_user_functions( *workspace, legacy_function_order ); + sort_user_functions_alphabetically( *workspace ); generator.generate_instructions( *workspace ); @@ -98,33 +97,6 @@ void CodeGenerator::generate_instructions( CompilerWorkspace& workspace ) } } -void CodeGenerator::register_module_functions( CompilerWorkspace& workspace, - const LegacyFunctionOrder* legacy_function_order ) -{ - if ( legacy_function_order ) - { - register_module_functions_as_legacy( workspace ); - } - else - { - register_module_functions_alphabetically( workspace ); - } -} - -void CodeGenerator::register_module_functions_as_legacy( CompilerWorkspace& workspace ) -{ - for ( auto& module_function : workspace.module_function_declarations ) - { - // we might assign IDs to more modules that we actually use, - // but this will help us compare output with the original compiler. - module_declaration_registrar.register_module( *module_function ); - } - for ( const auto& decl : workspace.module_functions_in_legacy_order ) - { - module_declaration_registrar.register_modulefunc( *decl ); - } -} - void CodeGenerator::register_module_functions_alphabetically( CompilerWorkspace& workspace ) { sort_module_functions_by_module_name( workspace ); @@ -162,44 +134,6 @@ void CodeGenerator::sort_module_functions_alphabetically( CompilerWorkspace& wor workspace.referenced_module_function_declarations.end(), sortByModuleFunctionName ); } -void CodeGenerator::sort_user_functions( CompilerWorkspace& workspace, - const LegacyFunctionOrder* legacy_function_order ) -{ - if ( legacy_function_order ) - { - sort_user_functions_as_legacy( workspace, *legacy_function_order ); - } - else - { - sort_user_functions_alphabetically( workspace ); - } -} - -void CodeGenerator::sort_user_functions_as_legacy( - CompilerWorkspace& workspace, const LegacyFunctionOrder& legacy_function_order ) -{ - std::map c1_order; - for ( auto& n : legacy_function_order.userfunc_emit_order ) - { - c1_order[n] = c1_order.size(); - } - - auto sortAsLegacy = [c1_order]( const std::unique_ptr& s1, - const std::unique_ptr& s2 ) -> bool { - auto itr1 = c1_order.find( s1->name ); - auto itr2 = c1_order.find( s2->name ); - if ( itr1 != c1_order.end() && itr1 != c1_order.end() ) - { - return ( *itr1 ).second < ( *itr2 ).second; - } - else - { - return s1->name < s2->name; - } - }; - std::sort( workspace.user_functions.begin(), workspace.user_functions.end(), sortAsLegacy ); -} - void CodeGenerator::sort_user_functions_alphabetically( CompilerWorkspace& workspace ) { auto sortByName = []( const std::unique_ptr& s1, diff --git a/pol-core/bscript/compiler/codegen/CodeGenerator.h b/pol-core/bscript/compiler/codegen/CodeGenerator.h index 662e2b525c..fade44e637 100644 --- a/pol-core/bscript/compiler/codegen/CodeGenerator.h +++ b/pol-core/bscript/compiler/codegen/CodeGenerator.h @@ -8,29 +8,23 @@ namespace Pol::Bscript::Compiler class CompiledScript; class CompilerWorkspace; class InstructionEmitter; -struct LegacyFunctionOrder; class ModuleDeclarationRegistrar; class CodeGenerator { public: - static std::unique_ptr generate( std::unique_ptr, - const LegacyFunctionOrder* ); + static std::unique_ptr generate( std::unique_ptr ); private: CodeGenerator( InstructionEmitter& , ModuleDeclarationRegistrar& ); void generate_instructions( CompilerWorkspace& ); - void register_module_functions( CompilerWorkspace&, const LegacyFunctionOrder* ); - void register_module_functions_as_legacy( CompilerWorkspace& ); void register_module_functions_alphabetically( CompilerWorkspace& ); static void sort_module_functions_by_module_name( CompilerWorkspace& ); static void sort_module_functions_alphabetically( CompilerWorkspace& ); - static void sort_user_functions( CompilerWorkspace&, const LegacyFunctionOrder* ); - static void sort_user_functions_as_legacy( CompilerWorkspace&, const LegacyFunctionOrder& ); static void sort_user_functions_alphabetically( CompilerWorkspace& ); private: diff --git a/pol-core/bscript/compiler/file/SourceFile.cpp b/pol-core/bscript/compiler/file/SourceFile.cpp index 765b87b9e1..5cccd08795 100644 --- a/pol-core/bscript/compiler/file/SourceFile.cpp +++ b/pol-core/bscript/compiler/file/SourceFile.cpp @@ -1,5 +1,7 @@ #include "SourceFile.h" +#include + #include "clib/filecont.h" #include "clib/fileutil.h" #include "clib/strutil.h" @@ -11,14 +13,11 @@ using EscriptGrammar::EscriptLexer; using EscriptGrammar::EscriptParser; -namespace Pol::Bscript::Legacy +namespace Pol::Bscript::Compiler { bool is_web_script( const char* filename ); std::string preprocess_web_script( const std::string& input ); -} -namespace Pol::Bscript::Compiler -{ SourceFile::SourceFile( const std::string& pathname, const std::string& contents, Profile& profile ) : pathname( pathname ), input( contents ), @@ -85,9 +84,9 @@ std::shared_ptr SourceFile::load( const SourceFileIdentifier& ident, Clib::sanitizeUnicodeWithIso( &contents ); - if ( Legacy::is_web_script( pathname.c_str() ) ) + if ( is_web_script( pathname.c_str() ) ) { - contents = Legacy::preprocess_web_script( contents ); + contents = preprocess_web_script( contents ); } return std::make_shared( pathname, contents, profile ); @@ -127,4 +126,84 @@ EscriptGrammar::EscriptParser::ModuleUnitContext* SourceFile::get_module_unit( return module_unit; } +/** + * Given a file name, tells if this is a web script + */ +bool is_web_script( const char* file ) +{ + const char* ext = strstr( file, ".hsr" ); + if ( ext && memcmp( ext, ".hsr", 5 ) == 0 ) + return true; + ext = strstr( file, ".asp" ); + if ( ext && memcmp( ext, ".asp", 5 ) == 0 ) + return true; + return false; +} + +/** + * Transforms the raw html page into a script with a single WriteHtml() instruction + */ +std::string preprocess_web_script( const std::string& input ) +{ + std::string output; + output = "use http;"; + output += '\n'; + + bool reading_html = true; + bool source_is_emit = false; + const char* s = input.c_str(); + std::string acc; + while ( *s ) + { + if ( reading_html ) + { + if ( s[0] == '<' && s[1] == '%' ) + { + reading_html = false; + if ( !acc.empty() ) + { + output += "WriteHtmlRaw( \"" + acc + "\");\n"; + acc = ""; + } + s += 2; + source_is_emit = ( s[0] == '=' ); + if ( source_is_emit ) + { + output += "WriteHtmlRaw( "; + ++s; + } + } + else + { + if ( *s == '\"' ) + acc += "\\\""; + else if ( *s == '\r' ) + ; + else if ( *s == '\n' ) + acc += "\\n"; + else + acc += *s; + ++s; + } + } + else + { + if ( s[0] == '%' && s[1] == '>' ) + { + reading_html = true; + s += 2; + if ( source_is_emit ) + output += " );\n"; + } + else + { + output += *s++; + } + } + } + if ( !acc.empty() ) + output += "WriteHtmlRaw( \"" + acc + "\");\n"; + return output; +} + } // namespace Pol::Bscript::Compiler diff --git a/pol-core/bscript/compiler/model/CompilerWorkspace.h b/pol-core/bscript/compiler/model/CompilerWorkspace.h index 496d19349c..388bfcca5b 100644 --- a/pol-core/bscript/compiler/model/CompilerWorkspace.h +++ b/pol-core/bscript/compiler/model/CompilerWorkspace.h @@ -40,7 +40,6 @@ class CompilerWorkspace // These reference ModuleFunctionDeclaration objects that are in module_functions std::vector referenced_module_function_declarations; - std::vector module_functions_in_legacy_order; std::vector> referenced_source_file_identifiers; diff --git a/pol-core/bscript/objaccess.cpp b/pol-core/bscript/objaccess.cpp new file mode 100644 index 0000000000..de897231bd --- /dev/null +++ b/pol-core/bscript/objaccess.cpp @@ -0,0 +1,483 @@ +#include "objmembers.h" +#include "objmethods.h" + +#include +#include +#include + +namespace Pol +{ +namespace Bscript +{ +ObjMember object_members[] = { + // MBR_*, "name", read_only + { MBR_X, "x", true }, // 0 + { MBR_Y, "y", true }, // 1 + { MBR_Z, "z", true }, + { MBR_NAME, "name", false }, + { MBR_OBJTYPE, "objtype", true }, + { MBR_GRAPHIC, "graphic", false }, // 5 + { MBR_SERIAL, "serial", true }, + { MBR_COLOR, "color", false }, + { MBR_HEIGHT, "height", true }, + { MBR_FACING, "facing", false }, + { MBR_DIRTY, "dirty", true }, // 10 + { MBR_WEIGHT, "weight", true }, + { MBR_MULTI, "multi", true }, + { MBR_AMOUNT, "amount", true }, // item + { MBR_LAYER, "layer", true }, + { MBR_CONTAINER, "container", true }, // 15 + { MBR_USESCRIPT, "usescript", false }, + { MBR_EQUIPSCRIPT, "equipscript", false }, + { MBR_UNEQUIPSCRIPT, "unequipscript", false }, + { MBR_DESC, "desc", false }, + { MBR_MOVABLE, "movable", false }, // 20 + { MBR_INVISIBLE, "invisible", false }, + { MBR_DECAYAT, "decayat", false }, + { MBR_SELLPRICE, "sellprice", false }, + { MBR_BUYPRICE, "buyprice", false }, + { MBR_NEWBIE, "newbie", false }, // 25 + { MBR_ITEM_COUNT, "item_count", true }, + { MBR_WARMODE, "warmode", true }, // character + { MBR_GENDER, "gender", false }, + { MBR_TRUEOBJTYPE, "trueobjtype", false }, + { MBR_TRUECOLOR, "truecolor", false }, // 30 + { MBR_AR_MOD, "ar_mod", false }, + { MBR_HIDDEN, "hidden", false }, + { MBR_CONCEALED, "concealed", false }, + { MBR_FROZEN, "frozen", false }, + { MBR_PARALYZED, "paralyzed", false }, // 35 + { MBR_POISONED, "poisoned", false }, + { MBR_STEALTHSTEPS, "stealthsteps", false }, + { MBR_SQUELCHED, "squelched", true }, + { MBR_DEAD, "dead", true }, + { MBR_AR, "ar", true }, // 40 + { MBR_BACKPACK, "backpack", true }, + { MBR_WEAPON, "weapon", true }, + { MBR_SHIELD, "shield", true }, + { MBR_ACCTNAME, "acctname", true }, + { MBR_ACCT, "acct", true }, // 45 + { MBR_CMDLEVEL, "cmdlevel", false }, + { MBR_CMDLEVELSTR, "cmdlevelstr", true }, + { MBR_CRIMINAL, "criminal", true }, + { MBR_IP, "ip", true }, + { MBR_GOLD, "gold", true }, // 50 + { MBR_TITLE_PREFIX, "title_prefix", false }, + { MBR_TITLE_SUFFIX, "title_suffix", false }, + { MBR_TITLE_GUILD, "title_guild", false }, + { MBR_TITLE_RACE, "title_race", false }, + { MBR_GUILDID, "guildid", true }, // 55 + { MBR_GUILD, "guild", true }, + { MBR_MURDERER, "murderer", false }, + { MBR_ATTACHED, "attached", true }, + { MBR_CLIENTVERSION, "clientversion", true }, + { MBR_REPORTABLES, "reportables", true }, // 60 + { MBR_SCRIPT, "script", false }, // npc + { MBR_NPCTEMPLATE, "npctemplate", true }, + { MBR_MASTER, "master", true }, + { MBR_PROCESS, "process", true }, + { MBR_EVENTMASK, "eventmask", true }, // 65 + { MBR_SPEECH_COLOR, "speech_color", false }, + { MBR_SPEECH_FONT, "speech_font", false }, + { MBR_USE_ADJUSTMENTS, "use_adjustments", false }, + { MBR_RUN_SPEED, "run_speed", false }, + { MBR_LOCKED, "locked", false }, // lockable //70 + { MBR_CORPSETYPE, "corpsetype", true }, // corpse + { MBR_TILLERMAN, "tillerman", true }, // boat + { MBR_PORTPLANK, "portplank", true }, + { MBR_STARBOARDPLANK, "starboardplank", true }, + { MBR_HOLD, "hold", true }, // 75 + { MBR_HAS_OFFLINE_MOBILES, "has_offline_mobiles", true }, + { MBR_COMPONENTS, "components", true }, // house + { MBR_ITEMS, "items", true }, // multi + { MBR_MOBILES, "mobiles", true }, + { MBR_XEAST, "xeast", false }, // map //80 + { MBR_XWEST, "xwest", false }, + { MBR_YNORTH, "ynorth", false }, + { MBR_YSOUTH, "ysouth", false }, + { MBR_GUMPWIDTH, "gumpwidth", false }, + { MBR_GUMPHEIGHT, "gumpheight", false }, // 85 + { MBR_ISOPEN, "isopen", true }, // door + { MBR_QUALITY, "quality", false }, // equipment + { MBR_HP, "hp", false }, + { MBR_MAXHP_MOD, "maxhp_mod", false }, + { MBR_MAXHP, "maxhp", true }, // 90 + { MBR_DMG_MOD, "dmg_mod", false }, // weapon + { MBR_ATTRIBUTE, "attribute", true }, + { MBR_INTRINSIC, "intrinsic", true }, + { MBR_HITSCRIPT, "hitscript", false }, + { MBR_AR_BASE, "ar_base", true }, // 95 + { MBR_ONHIT_SCRIPT, "onhitscript", false }, + { MBR_ENABLED, "enabled", true }, // account + { MBR_BANNED, "banned", true }, + { MBR_USERNAMEPASSWORDHASH, "usernamepasswordhash", true }, + { MBR_MEMBERS, "members", true }, // guild //100 + { MBR_ALLYGUILDS, "allyguilds", true }, + { MBR_ENEMYGUILDS, "enemyguilds", true }, + { MBR_PID, "pid", true }, // script + { MBR_STATE, "state", true }, + { MBR_INSTR_CYCLES, "instr_cycles", true }, // 105 + { MBR_SLEEP_CYCLES, "sleep_cycles", true }, + { MBR_CONSEC_CYCLES, "consec_cycles", true }, + { MBR_PC, "pc", true }, + { MBR_CALL_DEPTH, "call_depth", true }, + { MBR_NUM_GLOBALS, "num_globals", true }, // 110 + { MBR_VAR_SIZE, "var_size", true }, + { MBR_REALM, "realm", true }, + { MBR_UO_EXPANSION, "uo_expansion", true }, + { MBR_CUSTOM, "custom", true }, // house + { MBR_GLOBALS, "globals", true }, // 115 + { MBR_FOOTPRINT, "footprint", true }, + { MBR_CLIENTINFO, "clientinfo", true }, + { MBR_DELAY_MOD, "delay_mod", false }, + { MBR_CREATEDAT, "createdat", true }, + { MBR_OPPONENT, "opponent", true }, // 120 + { MBR_CONNECTED, "connected", true }, + { MBR_ATTACHED_TO, "attached_to", true }, + { MBR_CONTROLLER, "controller", true }, + { MBR_OWNERSERIAL, "ownerserial", true }, + { MBR_DEFAULTCMDLEVEL, "defaultcmdlevel", true }, // 125 + { MBR_UCLANG, "uclang", true }, + { MBR_RACE, "race", false }, + { MBR_TRADING_WITH, "trading_with", false }, + { MBR_TRADE_CONTAINER, "trade_container", false }, + { MBR_ALIGNMENT, "alignment", false }, // 130 + { MBR_CURSOR, "cursor", false }, + { MBR_GUMP, "gump", false }, + { MBR_PROMPT, "prompt", false }, + { MBR_STACKABLE, "stackable", false }, + { MBR_MOVEMODE, "movemode", false }, // 135 + { MBR_HITCHANCE_MOD, "hitchance_mod", false }, + { MBR_EVASIONCHANCE_MOD, "evasionchance_mod", false }, + { MBR_TILE_LAYER, "tile_layer", true }, + { MBR_CLIENTVERSIONDETAIL, "clientver_detail", true }, + { MBR_SAVEONEXIT, "saveonexit", true }, // 140 + { MBR_FIRE_RESIST, "resist_fire", true }, + { MBR_COLD_RESIST, "resist_cold", true }, + { MBR_ENERGY_RESIST, "resist_energy", true }, + { MBR_POISON_RESIST, "resist_poison", true }, + { MBR_PHYSICAL_RESIST, "resist_physical", true }, // 145 + { MBR_FIRE_RESIST_MOD, "resist_fire_mod", true }, + { MBR_COLD_RESIST_MOD, "resist_cold_mod", true }, + { MBR_ENERGY_RESIST_MOD, "resist_energy_mod", true }, + { MBR_POISON_RESIST_MOD, "resist_poison_mod", true }, + { MBR_PHYSICAL_RESIST_MOD, "resist_physical_mod", true }, // 150 + { MBR_STATCAP, "statcap", false }, + { MBR_SKILLCAP, "skillcap", false }, + { MBR_LUCK, "luck", false }, + { MBR_FOLLOWERSMAX, "followers_max", false }, + { MBR_TITHING, "tithing", false }, // 155 + { MBR_FOLLOWERS, "followers", false }, + { MBR_FIRE_DAMAGE, "damage_fire", true }, + { MBR_COLD_DAMAGE, "damage_cold", true }, + { MBR_ENERGY_DAMAGE, "damage_energy", true }, + { MBR_POISON_DAMAGE, "damage_poison", true }, // 160 + { MBR_PHYSICAL_DAMAGE, "damage_physical", true }, + { MBR_FIRE_DAMAGE_MOD, "damage_fire_mod", true }, + { MBR_COLD_DAMAGE_MOD, "damage_cold_mod", true }, + { MBR_ENERGY_DAMAGE_MOD, "damage_energy_mod", true }, + { MBR_POISON_DAMAGE_MOD, "damage_poison_mod", true }, // 165 + { MBR_PHYSICAL_DAMAGE_MOD, "damage_physical_mod", true }, + { MBR_PARTY, "party", true }, + { MBR_LEADER, "leader", true }, + { MBR_PARTYLOOT, "partycanloot", true }, + { MBR_CANDIDATE_OF_PARTY, "candidate_of_party", true }, // 170 + { MBR_CANDIDATES, "candidates", true }, + { MBR_MOVECOST_WALK, "movecost_walk_mod", true }, + { MBR_MOVECOST_RUN, "movecost_run_mod", true }, + { MBR_MOVECOST_WALK_MOUNTED, "movecost_walk_mounted_mod", true }, + { MBR_MOVECOST_RUN_MOUNTED, "movecost_run_mounted_mod", true }, // 175 + { MBR_AGGRESSORTO, "aggressorto", true }, + { MBR_LAWFULLYDAMAGED, "lawfullydamaged", true }, + { MBR_GETGOTTENBY, "getgottenby", true }, + { MBR_UO_EXPANSION_CLIENT, "uo_expansion_client", true }, + { MBR_CLIENTTYPE, "clienttype", true }, // 180 + { MBR_DEAFENED, "deafed", true }, + { MBR_CLIENT, "client", true }, + { MBR_TYPE, "type", true }, + { MBR_ATTRIBUTES, "attributes", true }, + { MBR_EDITING, "house_editing", true }, // 185 + { MBR_HOUSEPARTS, "house_parts", true }, + { MBR_DOUBLECLICKRANGE, "doubleclickrange", false }, + { MBR_MOUNTEDSTEPS, "mountedsteps", false }, + // New boat stuff start + { MBR_ROPE, "rope", true }, + { MBR_WHEEL, "wheel", true }, // 190 + { MBR_HULL, "hull", true }, + { MBR_TILLER, "tiller", true }, + { MBR_RUDDER, "rudder", true }, + { MBR_SAILS, "sails", true }, + { MBR_STORAGE, "storage", true }, // 195 + { MBR_WEAPONSLOT, "weaponslot", true }, + // New boat stuff end + { MBR_MULTIID, "multiid", true }, + { MBR_TRADEWINDOW, "tradewindow", true }, + { MBR_LASTCOORD, "lastcoord", true }, + { MBR_FACETID, "facetid", true }, // 200 + { MBR_EDITABLE, "editable", true }, + { MBR_ACTIVE_SKILL, "active_skill", true }, + { MBR_CASTING_SPELL, "casting_spell", true }, + { MBR_CARRYINGCAPACITY_MOD, "carrying_capacity_mod", false }, + { MBR_MAX_ITEMS_MOD, "max_items_mod", false }, // 205 + { MBR_MAX_WEIGHT_MOD, "max_weight_mod", false }, + { MBR_MAX_SLOTS_MOD, "max_slots_mod", false }, + { MBR_SPEED_MOD, "speed_mod", false }, + { MBR_NAME_SUFFIX, "name_suffix", false }, + { MBR_TEMPORALLY_CRIMINAL, "temporally_criminal", true }, // 210 + { MBR_LAST_TEXTCOLOR, "last_textcolor", true }, + { MBR_INSURED, "insured", false }, + { MBR_LAST_ACTIVITY_AT, "last_activity_at", false }, + { MBR_LAST_PACKET_AT, "last_packet_at", false }, + { MBR_HOUSE, "house", true }, // 215, Item + { MBR_SPECIFIC_NAME, "specific_name", true }, + { MBR_CARRYINGCAPACITY, "carrying_capacity", true }, + { MBR_NO_DROP, "no_drop", false }, + { MBR_NO_DROP_EXCEPTION, "no_drop_exception", false }, + { MBR_PORT, "port", false }, // 220 + // Additions for new properties + { MBR_LOWER_REAG_COST, "lower_reagent_cost", true }, + { MBR_SPELL_DAMAGE_INCREASE, "spell_damage_increase", true }, + { MBR_FASTER_CASTING, "faster_casting", true }, + { MBR_FASTER_CAST_RECOVERY, "faster_cast_recovery", true }, + { MBR_DEFENCE_CHANCE_INCREASE, "defence_increase", true }, // 225 + { MBR_DEFENCE_CHANCE_INCREASE_CAP, "defence_increase_cap", true }, + { MBR_LOWER_MANA_COST, "lower_mana_cost", true }, + { MBR_FIRE_RESIST_CAP, "resist_fire_cap", true }, + { MBR_COLD_RESIST_CAP, "resist_cold_cap", true }, + { MBR_ENERGY_RESIST_CAP, "resist_energy_cap", true }, // 230 + { MBR_POISON_RESIST_CAP, "resist_poison_cap", true }, + { MBR_PHYSICAL_RESIST_CAP, "resist_physical_cap", true }, + { MBR_HIT_CHANCE, "hit_chance", true }, + // Additions for new properties mods + { MBR_LOWER_REAG_COST_MOD, "lower_reagent_cost_mod", false }, + { MBR_SPELL_DAMAGE_INCREASE_MOD, "spell_damage_increase_mod", false }, + { MBR_FASTER_CASTING_MOD, "faster_casting_mod", false }, // 235 + { MBR_FASTER_CAST_RECOVERY_MOD, "faster_cast_recovery_mod", false }, + { MBR_DEFENCE_CHANCE_INCREASE_MOD, "defence_increase_mod", false }, + { MBR_DEFENCE_CHANCE_INCREASE_CAP_MOD, "defence_increase_cap_mod", false }, + { MBR_LOWER_MANA_COST_MOD, "lower_mana_cost_mod", false }, + { MBR_FIRE_RESIST_CAP_MOD, "resist_fire_cap_mod", false }, // 240 + { MBR_COLD_RESIST_CAP_MOD, "resist_cold_cap_mod", false }, + { MBR_ENERGY_RESIST_CAP_MOD, "resist_energy_cap_mod", false }, + { MBR_POISON_RESIST_CAP_MOD, "resist_poison_cap_mod", false }, + { MBR_PHYSICAL_RESIST_CAP_MOD, "resist_physical_cap_mod", false }, + { MBR_LUCK_MOD, "luck_mod", false }, // 245 + { MBR_HIT_CHANCE_MOD, "hit_chance_mod", false }, + { MBR_PACKAGE, "package", true }, + { MBR_SWING_SPEED_INCREASE, "swing_speed_increase", true }, + { MBR_SWING_SPEED_INCREASE_MOD, "swing_speed_increase_mod", false }, + { MBR_EXPORTED_FUNCTIONS, "exported_functions", false }, +}; +int n_objmembers = sizeof object_members / sizeof object_members[0]; +ObjMember* getKnownObjMember( const char* token ) +{ + static auto cache = []() -> std::unordered_map { + std::unordered_map m; + for ( int i = 0; i < n_objmembers; ++i ) + { + m[object_members[i].code] = &object_members[i]; + } + return m; + }(); + std::string temp( token ); + std::transform( temp.begin(), temp.end(), temp.begin(), + []( char c ) { return static_cast( ::tolower( c ) ); } ); + auto member = cache.find( temp ); + if ( member != cache.end() ) + return member->second; + return nullptr; +} +ObjMember* getObjMember( int id ) +{ + if ( id >= n_objmembers ) + return nullptr; + else + return &( object_members[id] ); +} + +ObjMethod object_methods[] = { + { MTH_ISA, "isa", false }, // 0 + { MTH_SET_MEMBER, "set_member", false }, // 1 + { MTH_GET_MEMBER, "get_member", false }, + { MTH_SETPOISONED, "setpoisoned", false }, + { MTH_SETPARALYZED, "setparalyzed", false }, + { MTH_SETCRIMINAL, "setcriminal", false }, // 5 + { MTH_SETLIGHTLEVEL, "setlightlevel", false }, + { MTH_SQUELCH, "squelch", false }, + { MTH_ENABLE, "enable", false }, + { MTH_DISABLE, "disable", false }, + { MTH_ENABLED, "enabled", false }, // 10 + { MTH_SETCMDLEVEL, "setcmdlevel", false }, + { MTH_SPENDGOLD, "spendgold", false }, + { MTH_SETMURDERER, "setmurderer", false }, + { MTH_REMOVEREPORTABLE, "removereportable", false }, + { MTH_GETGOTTENITEM, "getgottenitem", false }, // 15 + { MTH_CLEARGOTTENITEM, "cleargottenitem", false }, + { MTH_SETWARMODE, "setwarmode", false }, + { MTH_SETMASTER, "setmaster", false }, // npc + { MTH_MOVE_OFFLINE_MOBILES, "move_offline_mobiles", false }, // boat + { MTH_SETCUSTOM, "setcustom", false }, // house //20 + { MTH_GETPINS, "getpins", false }, // map + { MTH_INSERTPIN, "insertpin", false }, + { MTH_APPENDPIN, "appendpin", false }, + { MTH_ERASEPIN, "erasepin", false }, + { MTH_OPEN, "open", false }, // door //25 + { MTH_CLOSE, "close", false }, + { MTH_TOGGLE, "toggle", false }, + { MTH_BAN, "ban", false }, // account + { MTH_UNBAN, "unban", false }, + { MTH_SETPASSWORD, "setpassword", false }, // 30 + { MTH_CHECKPASSWORD, "checkpassword", false }, + { MTH_SETNAME, "setname", false }, + { MTH_GETCHARACTER, "getcharacter", false }, + { MTH_DELETECHARACTER, "deletecharacter", false }, + { MTH_GETPROP, "getprop", false }, // 35 + { MTH_SETPROP, "setprop", false }, + { MTH_ERASEPROP, "eraseprop", false }, + { MTH_PROPNAMES, "propnames", false }, + { MTH_ISMEMBER, "ismember", false }, // guild + { MTH_ISALLYGUILD, "isallyguild", false }, // 40 + { MTH_ISENEMYGUILD, "isenemyguild", false }, + { MTH_ADDMEMBER, "addmember", false }, + { MTH_ADDALLYGUILD, "addallyguild", false }, + { MTH_ADDENEMYGUILD, "addenemyguild", false }, + { MTH_REMOVEMEMBER, "removemember", false }, // 45 + { MTH_REMOVEALLYGUILD, "removeallyguild", false }, + { MTH_REMOVEENEMYGUILD, "removeenemyguild", false }, + { MTH_SIZE, "size", false }, // ARRAY + { MTH_ERASE, "erase", false }, + { MTH_INSERT, "insert", false }, // 50 + { MTH_SHRINK, "shrink", false }, + { MTH_APPEND, "append", false }, + { MTH_REVERSE, "reverse", false }, + { MTH_SORT, "sort", false }, // dict + { MTH_EXISTS, "exists", false }, // 55 + { MTH_KEYS, "keys", false }, + { MTH_SENDPACKET, "sendpacket", false }, // packet + { MTH_SENDAREAPACKET, "sendareapacket", false }, + { MTH_GETINT8, "getint8", false }, + { MTH_GETINT16, "getint16", false }, // 60 + { MTH_GETINT32, "getint32", false }, + { MTH_SETINT8, "setint8", false }, + { MTH_SETINT16, "setint16", false }, + { MTH_SETINT32, "setint32", false }, + { MTH_GETSTRING, "getstring", false }, // 65 + { MTH_GETUNICODESTRING, "getunicodestring", false }, + { MTH_SETSTRING, "setstring", false }, + { MTH_SETUNICODESTRING, "setunicodestring", false }, + { MTH_GETSIZE, "getsize", false }, + { MTH_SETSIZE, "setsize", false }, // 70 + { MTH_CREATEELEMENT, "createelement", false }, // datastore + { MTH_FINDELEMENT, "findelement", false }, + { MTH_DELETEELEMENT, "deleteelement", false }, + { MTH_SENDEVENT, "sendevent", false }, // script + { MTH_KILL, "kill", false }, // 75 + { MTH_LOADSYMBOLS, "loadsymbols", false }, + { MTH_SET_UO_EXPANSION, "set_uo_expansion", false }, + { MTH_CLEAR_EVENT_QUEUE, "clear_event_queue", false }, + { MTH_ADD_COMPONENT, "add_component", false }, + { MTH_ERASE_COMPONENT, "erase_component", false }, // 80 + { MTH_DELETE, "delete", false }, + { MTH_SPLIT, "split", false }, + { MTH_MOVE_CHAR, "move_char", false }, + { MTH_GETINT16FLIPPED, "getint16flipped", false }, + { MTH_GETINT32FLIPPED, "getint32flipped", false }, // 85 + { MTH_SETINT16FLIPPED, "setint16flipped", false }, + { MTH_SETINT32FLIPPED, "setint32flipped", false }, + { MTH_GETCORPSE, "getcorpse", false }, + { MTH_SETDEFAULTCMDLEVEL, "setdefaultcmdlevel", false }, + { MTH_PRIVILEGES, "privileges", false }, // 90 + { MTH_GETUNICODESTRINGFLIPPED, "getunicodestringflipped", false }, + { MTH_SETUNICODESTRINGFLIPPED, "setunicodestringflipped", false }, + { MTH_ADD_CHARACTER, "addcharacter", false }, + { MTH_SET_SWINGTIMER, "setswingtimer", false }, + { MTH_ATTACK_ONCE, "attack_once", false }, // 95 + { MTH_SETFACING, "setfacing", false }, + { MTH_COMPAREVERSION, "compareversion", false }, + { MTH_SETLEADER, "setleader", false }, + { MTH_ADDCANDIDATE, "addcandidate", false }, + { MTH_REMOVECANDIDATE, "removecandidate", false }, // 100 + { MTH_RANDOMENTRY, "randomentry", false }, + { MTH_SEEK, "seek", false }, + { MTH_PEEK, "peek", false }, + { MTH_TELL, "tell", false }, + { MTH_FLUSH, "flush", false }, // 105 + { MTH_GETSINT8, "getsint8", false }, + { MTH_GETSINT16, "getsint16", false }, + { MTH_GETSINT32, "getsint32", false }, + { MTH_SETSINT8, "setsint8", false }, + { MTH_SETSINT16, "setsint16", false }, // 110 + { MTH_SETSINT32, "setsint32", false }, + { MTH_SETAGGRESSORTO, "setaggressorto", false }, + { MTH_SETLAWFULLYDAMAGEDTO, "setlawfullydamagedto", false }, + { MTH_CLEARAGGRESSORTO, "clearaggressorto", false }, + { MTH_CLEARLAWFULLYDAMAGEDTO, "clearlawfullydamagedto", false }, // 115 + { MTH_HASSPELL, "hasspell", false }, + { MTH_SPELLS, "spells", false }, + { MTH_REMOVESPELL, "removespell", false }, + { MTH_ADDSPELL, "addspell", false }, + { MTH_DEAF, "deaf", false }, // 120 + { MTH_SETSEASON, "setseason", false }, + { MTH_NEXTSIBLING, "nextxmlsibling", false }, + { MTH_FIRSTCHILD, "firstxmlchild", false }, + { MTH_SAVEXML, "savexml", false }, + { MTH_APPENDNODE, "appendxmlnode", false }, // 125 + { MTH_SETDECLARATION, "setxmldeclaration", false }, + { MTH_SETATTRIBUTE, "setxmlattribute", false }, + { MTH_REMOVEATTRIBUTE, "removexmlattribute", false }, + { MTH_REMOVENODE, "removexmlnode", false }, + { MTH_APPENDTEXT, "appendxmltext", false }, // 130 + { MTH_XMLTOSTRING, "xmltostring", false }, + { MTH_APPENDXMLCOMMENT, "appendxmlcomment", false }, + { MTH_ADD_HOUSE_PART, "addhousepart", false }, + { MTH_ERASE_HOUSE_PART, "erasehousepart", false }, + { MTH_ACCEPT_COMMIT, "acceptcommit", false }, // 135 + { MTH_SPLITSTACK_AT, "splitstackat", false }, + { MTH_SPLITSTACK_INTO, "splitstackinto", false }, + { MTH_CANCEL_EDITING, "cancelediting", false }, + { MTH_CLONENODE, "clonenode", false }, + { MTH_HAS_EXISTING_STACK, "hasexistingstack", false }, // 140 + { MTH_LENGTH, "length", false }, + { MTH_JOIN, "join", false }, + { MTH_FIND, "find", false }, + { MTH_UPPER, "upper", false }, + { MTH_LOWER, "lower", false }, // 145 + { MTH_FORMAT, "format", false }, + { MTH_DISABLE_SKILLS_FOR, "disableskillsfor", false }, + { MTH_CYCLE, "cycle", false }, + { MTH_ADD_BUFF, "addbuff", false }, + { MTH_DEL_BUFF, "delbuff", false }, // 150 + { MTH_CLEAR_BUFFS, "clearbuffs", false }, + { MTH_CALL, "call", false }, + { MTH_SORTEDINSERT, "sorted_insert", false }, +}; +int n_objmethods = sizeof object_methods / sizeof object_methods[0]; +ObjMethod* getKnownObjMethod( const char* token ) +{ + // cache needs to hold a pointer to the original structure! eprog_read sets the override member + static auto cache = []() -> std::unordered_map { + std::unordered_map m; + for ( int i = 0; i < n_objmethods; ++i ) + { + m[object_methods[i].code] = &object_methods[i]; + } + return m; + }(); + std::string temp( token ); + std::transform( temp.begin(), temp.end(), temp.begin(), + []( char c ) { return static_cast( ::tolower( c ) ); } ); + auto method = cache.find( temp ); + if ( method != cache.end() ) + return method->second; + return nullptr; +} +ObjMethod* getObjMethod( int id ) +{ + if ( id >= n_objmethods ) + return nullptr; + else + return &( object_methods[id] ); +} + +} // namespace Bscript +} // namespace Pol diff --git a/pol-core/bscript/parser.cpp b/pol-core/bscript/parser.cpp deleted file mode 100644 index a34766ea39..0000000000 --- a/pol-core/bscript/parser.cpp +++ /dev/null @@ -1,2416 +0,0 @@ -/** @file - * - * @par History - * - 2005/01/24 Shinigami: added ObjMember character.spyonclient2 to get data from packet 0xd9 (Spy - * on Client 2) - * - 2005/03/09 Shinigami: added Prop Character::Delay_Mod [ms] for WeaponDelay - * - 2005/04/04 Shinigami: added Prop Character::CreatedAt [PolClock] - * - 2005/05/24 Shinigami: added ObjMethod account.delete() to delete this account - * - 2005/05/25 Shinigami: added ObjMethod account.split( newacctname : string, index : 1..5 ) - * to create a new account and move character to it - * - 2005/05/25 Shinigami: added ObjMethod account.move_char( destacctname : string, index : 1..5 ) - * to move character from this account to destaccount - * - 2005/08/29 Shinigami: character.spyonclient2 renamed to character.clientinfo - * - 2005/09/12 Shinigami: added ObjMethods packet.GetIntxxFlipped and packet.SetIntxxFlipped (Byte - * Order) - * - 2005/10/02 Shinigami: added Prop Script.attached_to and Script.controller - * - 2005/11/26 Shinigami: changed "strcmp" into "stricmp" to suppress Script Errors - * - 2005/12/06 MuadDib: Added uclang member for storing UC language from client. - * - 2006/05/16 Shinigami: added Prop Character.Race [RACE_* Constants] to support Elfs - * - 2006/09/16 Shinigami: added ObjMethods packet.GetUnicodeStringFlipped and - * packet.SetUnicodeStringFlipped (Byte Order) - * - 2007/07/09 Shinigami: added Prop Character.isUOKR [bool] - UO:KR client used? - * - 2008/07/08 Turley: Added character.movemode - returns the MoveMode like given in NPCDesc - * Added item.stackable - Is item stackable? - * - 2008/12/17 MuadDub: Added item.tile_layer - returns layer entry from tiledata/tiles.cfg - * - 2009/08/06 MuadDib: Removed PasswordOnlyHash support - * GetGottenBy Method - * - 2009/08/19 Turley: Added character.uo_expansion_client - * - 2009/08/25 Shinigami: STLport-5.2.1 fix: ParseErrorStr and ReservedWord changed little bit - * - 2009/09/06 Turley: Removed chr.isUOKR added chr.ClientType - * - 2009/10/09 Turley: Added spellbook.spells() & .hasspell() methods - * - 2009/10/10 Turley: Added spellbook.addspell() & .removespell() methods - * - 2009/10/14 Turley: Added char.deaf() methods & char.deafened member - * - 2011/12/13 Tomi: Boat members MBR_COMPONENT, MBR_HULL, MBR_ROPE, MBR_SAILS, MBR_WHEEL, - * MBR_TILLER, MBR_RUDDER, MBR_STORAGE, MBR_WEAPONSLOT - * - 2012/02/02 Tomi: Multi member MBR_MULTIID - * - 2012/03/26 Tomi: Added MBR_LASTCOORD - * - 2012/04/14 Tomi: Added MBR_FACETID for new map message packet - * - 2012/04/15 Tomi: Added MBR_EDITABLE for maps - * - 2012/06/02 Tomi: Added MBR_ACTIVE_SKILL and MBR_CASTING_SPELL for characters - */ - -#include "parser.h" - -#include -#include -#include -#include -#include -#include -#include - -#include "../clib/clib.h" -#include "../clib/logfacility.h" -#include "../clib/passert.h" -#include "../clib/rawtypes.h" -#include "../clib/strutil.h" -#include "../clib/unittest.h" -#include "compctx.h" -#include "compilercfg.h" -#include "expression.h" -#include "fmodule.h" -#include "impstr.h" -#include "modules.h" -#include "objmembers.h" -#include "objmethods.h" -#include "token.h" -#include "tokens.h" -#include - -namespace Pol -{ -namespace Bscript -{ -static void init_tables(); - -Parser::Parser() : quiet( 0 ), err( PERR_NONE ), contains_tabs( false ) -{ - memset( &ext_err, 0, sizeof( ext_err ) ); - memset( &buffer, 0, sizeof( buffer ) ); - init_tables(); -} - -const char* ParseErrorStr[PERR_NUM_ERRORS] = { "(No Error, or not specified)", - "Unexpected ')'", - "Missing '('", - "Missing ')'", - "Bad Token", - "Unknown Operator", - "Waaah!", - "Unterminated String Literal", - "Invalid escape sequence in String", - "Too Few Arguments", - "Too Many Arguments", - "Unexpected Comma", - "Illegal Construction", - "Missing ';'", - "Token not legal here", - "Procedure calls not allowed here", - "Unexpected Semicolon", - "Expected 'while'", - "Unexpected ']'", - "Missing ']'", - "EOF when expecting a terminator", - "Newline in string" }; -char operator_brk[] = "+-/*(),<=>,:;%"; - -char ident_allowed[] = - "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - "abcdefghijklmnopqrstuvwxyz" - "0123456789" - "_"; // $%@& removed 9/17/1998 Syz - - -int allowed_table[8][8] = { - /* this token is a(n)... */ - /* binary unary - * TERMINATOR OPERAND OPERATOR OPERATOR LPAREN RPAREN LBRACK RBRACK*/ - /* Last token was a(n)... */ - { 1, 1, 0, 1, 1, 0, 0, 0 }, /* TERMINATOR */ - { 1, 0, 1, 0, 0, 1, 1, 1 }, /* OPERAND */ - { 0, 1, 0, 1, 1, 0, 0, 0 }, /* BINARY OPERATOR */ - { 0, 1, 0, 0, 1, 0, 0, 0 }, /* UNARY OPERATOR */ - { 0, 1, 0, 1, 1, 0, 0, 0 }, /* LEFT PARENTHESIS */ - { 1, 0, 1, 0, 0, 1, 0, 1 }, /* RIGHT PARENTHESIS */ - { 0, 1, 0, 0, 1, 0, 1, 0 }, /* LEFT BRACKET */ - { 1, 0, 1, 0, 0, 1, 1, 1 }, /* RIGHT BRACKET */ - /* Separators are always allowed. Terminators are mostly always allowed. */ -}; -/* examples matrix: -- connected denotes unary operator - -legal: -{ TT T AB T -AB T ( }, -{ AB T AB + AB ) A[ A] }, -{ * AB *- * ( }, -{ -AB -( }, -{ ( AB (- ( ( }, -{ ) T ) - ) ) )] } -{ [A . [- [( [[ . } -{ ] T . ] * . . ]) ][ ][ } - -illegal: -{ T b-A T ) T [ T ] }, -{ AB AB AB~ AB ( }, -{ * T * / - ) *[ *] }, -{ b- T -* -- -) -[ -] }, -{ ( T ( * ( ) ([ (] }, -{ ) AB )- ) ( )[ } -{ [ T [+ [) [] } -{ ]A ]- ] ( . } - -*/ - - -/* operator characters // (and precedence table) - ( ) [ ] - + - (unary-arithmetic) ! (unary-logical) ~ (unary-boolean) - * / % - + - - < <= > >= - == <> - & (band ?) - ^ (bxor ?) - | (bor ?) - and - or - := - , - - Problem: How to recognize a unary operator? - Proposed solution: If a binary operator would be legal, use it - otherwise, it must be a unary operator. - Examples: (-5+7) - 1) grabs '('. now only lparens and operands are legal. - 2) grabs '-'. Since this is a binary operator (TYP_OPERATOR) - by default, this is illegal. Since it is illegal, - parseToken() tries the unary operator table, and finds it. - ( now, only left parens and operands are legal. ) - 3) gets 5, an operand. - Alternative: Pass getToken()a parameter denoting what is legal and - let it ignore possibilities that are illegal. - - See end of file for handling unary operators - */ - -/* - The precedence table should be split from the operator - recognition table, because some operators are words and - have to be picked up in the reserved word table. - */ - - -Operator binary_operators[] = { - - { "(", TOK_LPAREN, PREC_PAREN, TYP_LEFTPAREN, false, false }, - { ")", TOK_RPAREN, PREC_PAREN, TYP_RIGHTPAREN, false, false }, - { "[", TOK_LBRACKET, PREC_PAREN, TYP_LEFTBRACKET, false, false }, - { "]", TOK_RBRACKET, PREC_PAREN, TYP_RIGHTBRACKET, false, false }, - { "{", TOK_LBRACE, PREC_PAREN, TYP_LEFTBRACE, false, false }, - { "}", TOK_RBRACE, PREC_PAREN, TYP_RIGHTBRACE, false, false }, - - { ".", TOK_MEMBER, PREC_PAREN, TYP_OPERATOR, true, false }, - { "->", TOK_DICTKEY, PREC_ASSIGN, TYP_RESERVED, false, false }, - - { "*", TOK_MULT, PREC_MULT, TYP_OPERATOR, true, false }, - { "/", TOK_DIV, PREC_MULT, TYP_OPERATOR, true, false }, - { "%", TOK_MODULUS, PREC_MULT, TYP_OPERATOR, true, false }, - - { "+", TOK_ADD, PREC_PLUS, TYP_OPERATOR, true, false }, - { "-", TOK_SUBTRACT, PREC_PLUS, TYP_OPERATOR, true, false }, - - { "+=", TOK_PLUSEQUAL, PREC_ASSIGN, TYP_OPERATOR, false, false }, - { "-=", TOK_MINUSEQUAL, PREC_ASSIGN, TYP_OPERATOR, false, false }, - { "*=", TOK_TIMESEQUAL, PREC_ASSIGN, TYP_OPERATOR, false, false }, - { "/=", TOK_DIVIDEEQUAL, PREC_ASSIGN, TYP_OPERATOR, false, false }, - { "%=", TOK_MODULUSEQUAL, PREC_ASSIGN, TYP_OPERATOR, false, false }, - - { "<=", TOK_LESSEQ, PREC_LESSTHAN, TYP_OPERATOR, false, false }, - { "<", TOK_LESSTHAN, PREC_LESSTHAN, TYP_OPERATOR, true, false }, - { ">=", TOK_GREQ, PREC_LESSTHAN, TYP_OPERATOR, false, false }, - { ">", TOK_GRTHAN, PREC_LESSTHAN, TYP_OPERATOR, true, false }, - - { ">>", TOK_BSRIGHT, PREC_BSRIGHT, TYP_OPERATOR, false, false }, - { "<<", TOK_BSLEFT, PREC_BSLEFT, TYP_OPERATOR, false, false }, - { "&", TOK_BITAND, PREC_BITAND, TYP_OPERATOR, true, false }, - { "^", TOK_BITXOR, PREC_BITXOR, TYP_OPERATOR, false, false }, - { "|", TOK_BITOR, PREC_BITOR, TYP_OPERATOR, true, false }, - - { "<>", TOK_NEQ, PREC_EQUALTO, TYP_OPERATOR, false, false }, - { "!=", TOK_NEQ, PREC_EQUALTO, TYP_OPERATOR, false, false }, - { "=", TOK_EQUAL1, PREC_EQUALTO, TYP_OPERATOR, true, false }, // deprecated: :=/== - { "==", TOK_EQUAL, PREC_EQUALTO, TYP_OPERATOR, false, false }, - - // { "and", TOK_AND, PREC_LOGAND, TYP_OPERATOR, false, false }, - { "&&", TOK_AND, PREC_LOGAND, TYP_OPERATOR, false, false }, - - // { "or", TOK_OR, PREC_LOGOR, TYP_OPERATOR, false, false }, - { "||", TOK_OR, PREC_LOGOR, TYP_OPERATOR, false, false }, - - { "?:", RSV_ELVIS, PREC_ELVIS, TYP_OPERATOR, false, false }, - - { ":=", TOK_ASSIGN, PREC_ASSIGN, TYP_OPERATOR, false, false }, - { ".+", TOK_ADDMEMBER, PREC_ASSIGN, TYP_OPERATOR, false, false }, - { ".-", TOK_DELMEMBER, PREC_ASSIGN, TYP_OPERATOR, false, false }, - { ".?", TOK_CHKMEMBER, PREC_ASSIGN, TYP_OPERATOR, false, false }, - - { ",", TOK_COMMA, PREC_COMMA, TYP_SEPARATOR, false, false }, - { "", TOK_TERM, PREC_TERMINATOR, TYP_TERMINATOR, false, false }, - { "++", TOK_ADD, PREC_PLUS, TYP_UNARY_PLACEHOLDER, false, - false }, // fake entry will be converted to unary - { "--", TOK_SUBTRACT, PREC_PLUS, TYP_UNARY_PLACEHOLDER, false, - false }, // fake entry will be converted to unary -}; -int n_operators = sizeof binary_operators / sizeof binary_operators[0]; - -Operator unary_operators[] = { - { "+", TOK_UNPLUS, PREC_UNARY_OPS, TYP_UNARY_OPERATOR, false, false }, - { "-", TOK_UNMINUS, PREC_UNARY_OPS, TYP_UNARY_OPERATOR, false, false }, - { "!", TOK_LOG_NOT, PREC_UNARY_OPS, TYP_UNARY_OPERATOR, false, false }, - { "~", TOK_BITWISE_NOT, PREC_UNARY_OPS, TYP_UNARY_OPERATOR, false, false }, - { "@", TOK_FUNCREF, PREC_UNARY_OPS, TYP_FUNCREF, false, false }, - { "++", TOK_UNPLUSPLUS, PREC_UNARY_OPS, TYP_UNARY_OPERATOR, true, false }, - { "--", TOK_UNMINUSMINUS, PREC_UNARY_OPS, TYP_UNARY_OPERATOR, true, false }, - // { "not", TOK_LOG_NOT, PREC_UNARY_OPS, TYP_UNARY_OPERATOR, false, false } - // "refto", TOK_REFTO, 12, TYP_UNARY_OPERATOR, false, false -}; -int n_unary = sizeof unary_operators / sizeof unary_operators[0]; - -ObjMember object_members[] = { - // MBR_*, "name", read_only - { MBR_X, "x", true }, // 0 - { MBR_Y, "y", true }, // 1 - { MBR_Z, "z", true }, - { MBR_NAME, "name", false }, - { MBR_OBJTYPE, "objtype", true }, - { MBR_GRAPHIC, "graphic", false }, // 5 - { MBR_SERIAL, "serial", true }, - { MBR_COLOR, "color", false }, - { MBR_HEIGHT, "height", true }, - { MBR_FACING, "facing", false }, - { MBR_DIRTY, "dirty", true }, // 10 - { MBR_WEIGHT, "weight", true }, - { MBR_MULTI, "multi", true }, - { MBR_AMOUNT, "amount", true }, // item - { MBR_LAYER, "layer", true }, - { MBR_CONTAINER, "container", true }, // 15 - { MBR_USESCRIPT, "usescript", false }, - { MBR_EQUIPSCRIPT, "equipscript", false }, - { MBR_UNEQUIPSCRIPT, "unequipscript", false }, - { MBR_DESC, "desc", false }, - { MBR_MOVABLE, "movable", false }, // 20 - { MBR_INVISIBLE, "invisible", false }, - { MBR_DECAYAT, "decayat", false }, - { MBR_SELLPRICE, "sellprice", false }, - { MBR_BUYPRICE, "buyprice", false }, - { MBR_NEWBIE, "newbie", false }, // 25 - { MBR_ITEM_COUNT, "item_count", true }, - { MBR_WARMODE, "warmode", true }, // character - { MBR_GENDER, "gender", false }, - { MBR_TRUEOBJTYPE, "trueobjtype", false }, - { MBR_TRUECOLOR, "truecolor", false }, // 30 - { MBR_AR_MOD, "ar_mod", false }, - { MBR_HIDDEN, "hidden", false }, - { MBR_CONCEALED, "concealed", false }, - { MBR_FROZEN, "frozen", false }, - { MBR_PARALYZED, "paralyzed", false }, // 35 - { MBR_POISONED, "poisoned", false }, - { MBR_STEALTHSTEPS, "stealthsteps", false }, - { MBR_SQUELCHED, "squelched", true }, - { MBR_DEAD, "dead", true }, - { MBR_AR, "ar", true }, // 40 - { MBR_BACKPACK, "backpack", true }, - { MBR_WEAPON, "weapon", true }, - { MBR_SHIELD, "shield", true }, - { MBR_ACCTNAME, "acctname", true }, - { MBR_ACCT, "acct", true }, // 45 - { MBR_CMDLEVEL, "cmdlevel", false }, - { MBR_CMDLEVELSTR, "cmdlevelstr", true }, - { MBR_CRIMINAL, "criminal", true }, - { MBR_IP, "ip", true }, - { MBR_GOLD, "gold", true }, // 50 - { MBR_TITLE_PREFIX, "title_prefix", false }, - { MBR_TITLE_SUFFIX, "title_suffix", false }, - { MBR_TITLE_GUILD, "title_guild", false }, - { MBR_TITLE_RACE, "title_race", false }, - { MBR_GUILDID, "guildid", true }, // 55 - { MBR_GUILD, "guild", true }, - { MBR_MURDERER, "murderer", false }, - { MBR_ATTACHED, "attached", true }, - { MBR_CLIENTVERSION, "clientversion", true }, - { MBR_REPORTABLES, "reportables", true }, // 60 - { MBR_SCRIPT, "script", false }, // npc - { MBR_NPCTEMPLATE, "npctemplate", true }, - { MBR_MASTER, "master", true }, - { MBR_PROCESS, "process", true }, - { MBR_EVENTMASK, "eventmask", true }, // 65 - { MBR_SPEECH_COLOR, "speech_color", false }, - { MBR_SPEECH_FONT, "speech_font", false }, - { MBR_USE_ADJUSTMENTS, "use_adjustments", false }, - { MBR_RUN_SPEED, "run_speed", false }, - { MBR_LOCKED, "locked", false }, // lockable //70 - { MBR_CORPSETYPE, "corpsetype", true }, // corpse - { MBR_TILLERMAN, "tillerman", true }, // boat - { MBR_PORTPLANK, "portplank", true }, - { MBR_STARBOARDPLANK, "starboardplank", true }, - { MBR_HOLD, "hold", true }, // 75 - { MBR_HAS_OFFLINE_MOBILES, "has_offline_mobiles", true }, - { MBR_COMPONENTS, "components", true }, // house - { MBR_ITEMS, "items", true }, // multi - { MBR_MOBILES, "mobiles", true }, - { MBR_XEAST, "xeast", false }, // map //80 - { MBR_XWEST, "xwest", false }, - { MBR_YNORTH, "ynorth", false }, - { MBR_YSOUTH, "ysouth", false }, - { MBR_GUMPWIDTH, "gumpwidth", false }, - { MBR_GUMPHEIGHT, "gumpheight", false }, // 85 - { MBR_ISOPEN, "isopen", true }, // door - { MBR_QUALITY, "quality", false }, // equipment - { MBR_HP, "hp", false }, - { MBR_MAXHP_MOD, "maxhp_mod", false }, - { MBR_MAXHP, "maxhp", true }, // 90 - { MBR_DMG_MOD, "dmg_mod", false }, // weapon - { MBR_ATTRIBUTE, "attribute", true }, - { MBR_INTRINSIC, "intrinsic", true }, - { MBR_HITSCRIPT, "hitscript", false }, - { MBR_AR_BASE, "ar_base", true }, // 95 - { MBR_ONHIT_SCRIPT, "onhitscript", false }, - { MBR_ENABLED, "enabled", true }, // account - { MBR_BANNED, "banned", true }, - { MBR_USERNAMEPASSWORDHASH, "usernamepasswordhash", true }, - { MBR_MEMBERS, "members", true }, // guild //100 - { MBR_ALLYGUILDS, "allyguilds", true }, - { MBR_ENEMYGUILDS, "enemyguilds", true }, - { MBR_PID, "pid", true }, // script - { MBR_STATE, "state", true }, - { MBR_INSTR_CYCLES, "instr_cycles", true }, // 105 - { MBR_SLEEP_CYCLES, "sleep_cycles", true }, - { MBR_CONSEC_CYCLES, "consec_cycles", true }, - { MBR_PC, "pc", true }, - { MBR_CALL_DEPTH, "call_depth", true }, - { MBR_NUM_GLOBALS, "num_globals", true }, // 110 - { MBR_VAR_SIZE, "var_size", true }, - { MBR_REALM, "realm", true }, - { MBR_UO_EXPANSION, "uo_expansion", true }, - { MBR_CUSTOM, "custom", true }, // house - { MBR_GLOBALS, "globals", true }, // 115 - { MBR_FOOTPRINT, "footprint", true }, - { MBR_CLIENTINFO, "clientinfo", true }, - { MBR_DELAY_MOD, "delay_mod", false }, - { MBR_CREATEDAT, "createdat", true }, - { MBR_OPPONENT, "opponent", true }, // 120 - { MBR_CONNECTED, "connected", true }, - { MBR_ATTACHED_TO, "attached_to", true }, - { MBR_CONTROLLER, "controller", true }, - { MBR_OWNERSERIAL, "ownerserial", true }, - { MBR_DEFAULTCMDLEVEL, "defaultcmdlevel", true }, // 125 - { MBR_UCLANG, "uclang", true }, - { MBR_RACE, "race", false }, - { MBR_TRADING_WITH, "trading_with", false }, - { MBR_TRADE_CONTAINER, "trade_container", false }, - { MBR_ALIGNMENT, "alignment", false }, // 130 - { MBR_CURSOR, "cursor", false }, - { MBR_GUMP, "gump", false }, - { MBR_PROMPT, "prompt", false }, - { MBR_STACKABLE, "stackable", false }, - { MBR_MOVEMODE, "movemode", false }, // 135 - { MBR_HITCHANCE_MOD, "hitchance_mod", false }, - { MBR_EVASIONCHANCE_MOD, "evasionchance_mod", false }, - { MBR_TILE_LAYER, "tile_layer", true }, - { MBR_CLIENTVERSIONDETAIL, "clientver_detail", true }, - { MBR_SAVEONEXIT, "saveonexit", true }, // 140 - { MBR_FIRE_RESIST, "resist_fire", true }, - { MBR_COLD_RESIST, "resist_cold", true }, - { MBR_ENERGY_RESIST, "resist_energy", true }, - { MBR_POISON_RESIST, "resist_poison", true }, - { MBR_PHYSICAL_RESIST, "resist_physical", true }, // 145 - { MBR_FIRE_RESIST_MOD, "resist_fire_mod", true }, - { MBR_COLD_RESIST_MOD, "resist_cold_mod", true }, - { MBR_ENERGY_RESIST_MOD, "resist_energy_mod", true }, - { MBR_POISON_RESIST_MOD, "resist_poison_mod", true }, - { MBR_PHYSICAL_RESIST_MOD, "resist_physical_mod", true }, // 150 - { MBR_STATCAP, "statcap", false }, - { MBR_SKILLCAP, "skillcap", false }, - { MBR_LUCK, "luck", false }, - { MBR_FOLLOWERSMAX, "followers_max", false }, - { MBR_TITHING, "tithing", false }, // 155 - { MBR_FOLLOWERS, "followers", false }, - { MBR_FIRE_DAMAGE, "damage_fire", true }, - { MBR_COLD_DAMAGE, "damage_cold", true }, - { MBR_ENERGY_DAMAGE, "damage_energy", true }, - { MBR_POISON_DAMAGE, "damage_poison", true }, // 160 - { MBR_PHYSICAL_DAMAGE, "damage_physical", true }, - { MBR_FIRE_DAMAGE_MOD, "damage_fire_mod", true }, - { MBR_COLD_DAMAGE_MOD, "damage_cold_mod", true }, - { MBR_ENERGY_DAMAGE_MOD, "damage_energy_mod", true }, - { MBR_POISON_DAMAGE_MOD, "damage_poison_mod", true }, // 165 - { MBR_PHYSICAL_DAMAGE_MOD, "damage_physical_mod", true }, - { MBR_PARTY, "party", true }, - { MBR_LEADER, "leader", true }, - { MBR_PARTYLOOT, "partycanloot", true }, - { MBR_CANDIDATE_OF_PARTY, "candidate_of_party", true }, // 170 - { MBR_CANDIDATES, "candidates", true }, - { MBR_MOVECOST_WALK, "movecost_walk_mod", true }, - { MBR_MOVECOST_RUN, "movecost_run_mod", true }, - { MBR_MOVECOST_WALK_MOUNTED, "movecost_walk_mounted_mod", true }, - { MBR_MOVECOST_RUN_MOUNTED, "movecost_run_mounted_mod", true }, // 175 - { MBR_AGGRESSORTO, "aggressorto", true }, - { MBR_LAWFULLYDAMAGED, "lawfullydamaged", true }, - { MBR_GETGOTTENBY, "getgottenby", true }, - { MBR_UO_EXPANSION_CLIENT, "uo_expansion_client", true }, - { MBR_CLIENTTYPE, "clienttype", true }, // 180 - { MBR_DEAFENED, "deafed", true }, - { MBR_CLIENT, "client", true }, - { MBR_TYPE, "type", true }, - { MBR_ATTRIBUTES, "attributes", true }, - { MBR_EDITING, "house_editing", true }, // 185 - { MBR_HOUSEPARTS, "house_parts", true }, - { MBR_DOUBLECLICKRANGE, "doubleclickrange", false }, - { MBR_MOUNTEDSTEPS, "mountedsteps", false }, - // New boat stuff start - { MBR_ROPE, "rope", true }, - { MBR_WHEEL, "wheel", true }, // 190 - { MBR_HULL, "hull", true }, - { MBR_TILLER, "tiller", true }, - { MBR_RUDDER, "rudder", true }, - { MBR_SAILS, "sails", true }, - { MBR_STORAGE, "storage", true }, // 195 - { MBR_WEAPONSLOT, "weaponslot", true }, - // New boat stuff end - { MBR_MULTIID, "multiid", true }, - { MBR_TRADEWINDOW, "tradewindow", true }, - { MBR_LASTCOORD, "lastcoord", true }, - { MBR_FACETID, "facetid", true }, // 200 - { MBR_EDITABLE, "editable", true }, - { MBR_ACTIVE_SKILL, "active_skill", true }, - { MBR_CASTING_SPELL, "casting_spell", true }, - { MBR_CARRYINGCAPACITY_MOD, "carrying_capacity_mod", false }, - { MBR_MAX_ITEMS_MOD, "max_items_mod", false }, // 205 - { MBR_MAX_WEIGHT_MOD, "max_weight_mod", false }, - { MBR_MAX_SLOTS_MOD, "max_slots_mod", false }, - { MBR_SPEED_MOD, "speed_mod", false }, - { MBR_NAME_SUFFIX, "name_suffix", false }, - { MBR_TEMPORALLY_CRIMINAL, "temporally_criminal", true }, // 210 - { MBR_LAST_TEXTCOLOR, "last_textcolor", true }, - { MBR_INSURED, "insured", false }, - { MBR_LAST_ACTIVITY_AT, "last_activity_at", false }, - { MBR_LAST_PACKET_AT, "last_packet_at", false }, - { MBR_HOUSE, "house", true }, // 215, Item - { MBR_SPECIFIC_NAME, "specific_name", true }, - { MBR_CARRYINGCAPACITY, "carrying_capacity", true }, - { MBR_NO_DROP, "no_drop", false }, - { MBR_NO_DROP_EXCEPTION, "no_drop_exception", false }, - { MBR_PORT, "port", false }, // 220 - // Additions for new properties - { MBR_LOWER_REAG_COST, "lower_reagent_cost", true }, - { MBR_SPELL_DAMAGE_INCREASE, "spell_damage_increase", true }, - { MBR_FASTER_CASTING, "faster_casting", true }, - { MBR_FASTER_CAST_RECOVERY, "faster_cast_recovery", true }, - { MBR_DEFENCE_CHANCE_INCREASE, "defence_increase", true }, // 225 - { MBR_DEFENCE_CHANCE_INCREASE_CAP, "defence_increase_cap", true }, - { MBR_LOWER_MANA_COST, "lower_mana_cost", true }, - { MBR_FIRE_RESIST_CAP, "resist_fire_cap", true }, - { MBR_COLD_RESIST_CAP, "resist_cold_cap", true }, - { MBR_ENERGY_RESIST_CAP, "resist_energy_cap", true }, // 230 - { MBR_POISON_RESIST_CAP, "resist_poison_cap", true }, - { MBR_PHYSICAL_RESIST_CAP, "resist_physical_cap", true }, - { MBR_HIT_CHANCE, "hit_chance", true }, - // Additions for new properties mods - { MBR_LOWER_REAG_COST_MOD, "lower_reagent_cost_mod", false }, - { MBR_SPELL_DAMAGE_INCREASE_MOD, "spell_damage_increase_mod", false }, - { MBR_FASTER_CASTING_MOD, "faster_casting_mod", false }, // 235 - { MBR_FASTER_CAST_RECOVERY_MOD, "faster_cast_recovery_mod", false }, - { MBR_DEFENCE_CHANCE_INCREASE_MOD, "defence_increase_mod", false }, - { MBR_DEFENCE_CHANCE_INCREASE_CAP_MOD, "defence_increase_cap_mod", false }, - { MBR_LOWER_MANA_COST_MOD, "lower_mana_cost_mod", false }, - { MBR_FIRE_RESIST_CAP_MOD, "resist_fire_cap_mod", false }, // 240 - { MBR_COLD_RESIST_CAP_MOD, "resist_cold_cap_mod", false }, - { MBR_ENERGY_RESIST_CAP_MOD, "resist_energy_cap_mod", false }, - { MBR_POISON_RESIST_CAP_MOD, "resist_poison_cap_mod", false }, - { MBR_PHYSICAL_RESIST_CAP_MOD, "resist_physical_cap_mod", false }, - { MBR_LUCK_MOD, "luck_mod", false }, // 245 - { MBR_HIT_CHANCE_MOD, "hit_chance_mod", false }, - { MBR_PACKAGE, "package", true }, - { MBR_SWING_SPEED_INCREASE, "swing_speed_increase", true }, - { MBR_SWING_SPEED_INCREASE_MOD, "swing_speed_increase_mod", false }, - { MBR_EXPORTED_FUNCTIONS, "exported_functions", false }, -}; -int n_objmembers = sizeof object_members / sizeof object_members[0]; -ObjMember* getKnownObjMember( const char* token ) -{ - static auto cache = []() -> std::unordered_map { - std::unordered_map m; - for ( int i = 0; i < n_objmembers; ++i ) - { - m[object_members[i].code] = &object_members[i]; - } - return m; - }(); - std::string temp( token ); - std::transform( temp.begin(), temp.end(), temp.begin(), - []( char c ) { return static_cast( ::tolower( c ) ); } ); - auto member = cache.find( temp ); - if ( member != cache.end() ) - return member->second; - return nullptr; -} -ObjMember* getObjMember( int id ) -{ - if ( id >= n_objmembers ) - return nullptr; - else - return &( object_members[id] ); -} - -ObjMethod object_methods[] = { - { MTH_ISA, "isa", false }, // 0 - { MTH_SET_MEMBER, "set_member", false }, // 1 - { MTH_GET_MEMBER, "get_member", false }, - { MTH_SETPOISONED, "setpoisoned", false }, - { MTH_SETPARALYZED, "setparalyzed", false }, - { MTH_SETCRIMINAL, "setcriminal", false }, // 5 - { MTH_SETLIGHTLEVEL, "setlightlevel", false }, - { MTH_SQUELCH, "squelch", false }, - { MTH_ENABLE, "enable", false }, - { MTH_DISABLE, "disable", false }, - { MTH_ENABLED, "enabled", false }, // 10 - { MTH_SETCMDLEVEL, "setcmdlevel", false }, - { MTH_SPENDGOLD, "spendgold", false }, - { MTH_SETMURDERER, "setmurderer", false }, - { MTH_REMOVEREPORTABLE, "removereportable", false }, - { MTH_GETGOTTENITEM, "getgottenitem", false }, // 15 - { MTH_CLEARGOTTENITEM, "cleargottenitem", false }, - { MTH_SETWARMODE, "setwarmode", false }, - { MTH_SETMASTER, "setmaster", false }, // npc - { MTH_MOVE_OFFLINE_MOBILES, "move_offline_mobiles", false }, // boat - { MTH_SETCUSTOM, "setcustom", false }, // house //20 - { MTH_GETPINS, "getpins", false }, // map - { MTH_INSERTPIN, "insertpin", false }, - { MTH_APPENDPIN, "appendpin", false }, - { MTH_ERASEPIN, "erasepin", false }, - { MTH_OPEN, "open", false }, // door //25 - { MTH_CLOSE, "close", false }, - { MTH_TOGGLE, "toggle", false }, - { MTH_BAN, "ban", false }, // account - { MTH_UNBAN, "unban", false }, - { MTH_SETPASSWORD, "setpassword", false }, // 30 - { MTH_CHECKPASSWORD, "checkpassword", false }, - { MTH_SETNAME, "setname", false }, - { MTH_GETCHARACTER, "getcharacter", false }, - { MTH_DELETECHARACTER, "deletecharacter", false }, - { MTH_GETPROP, "getprop", false }, // 35 - { MTH_SETPROP, "setprop", false }, - { MTH_ERASEPROP, "eraseprop", false }, - { MTH_PROPNAMES, "propnames", false }, - { MTH_ISMEMBER, "ismember", false }, // guild - { MTH_ISALLYGUILD, "isallyguild", false }, // 40 - { MTH_ISENEMYGUILD, "isenemyguild", false }, - { MTH_ADDMEMBER, "addmember", false }, - { MTH_ADDALLYGUILD, "addallyguild", false }, - { MTH_ADDENEMYGUILD, "addenemyguild", false }, - { MTH_REMOVEMEMBER, "removemember", false }, // 45 - { MTH_REMOVEALLYGUILD, "removeallyguild", false }, - { MTH_REMOVEENEMYGUILD, "removeenemyguild", false }, - { MTH_SIZE, "size", false }, // ARRAY - { MTH_ERASE, "erase", false }, - { MTH_INSERT, "insert", false }, // 50 - { MTH_SHRINK, "shrink", false }, - { MTH_APPEND, "append", false }, - { MTH_REVERSE, "reverse", false }, - { MTH_SORT, "sort", false }, // dict - { MTH_EXISTS, "exists", false }, // 55 - { MTH_KEYS, "keys", false }, - { MTH_SENDPACKET, "sendpacket", false }, // packet - { MTH_SENDAREAPACKET, "sendareapacket", false }, - { MTH_GETINT8, "getint8", false }, - { MTH_GETINT16, "getint16", false }, // 60 - { MTH_GETINT32, "getint32", false }, - { MTH_SETINT8, "setint8", false }, - { MTH_SETINT16, "setint16", false }, - { MTH_SETINT32, "setint32", false }, - { MTH_GETSTRING, "getstring", false }, // 65 - { MTH_GETUNICODESTRING, "getunicodestring", false }, - { MTH_SETSTRING, "setstring", false }, - { MTH_SETUNICODESTRING, "setunicodestring", false }, - { MTH_GETSIZE, "getsize", false }, - { MTH_SETSIZE, "setsize", false }, // 70 - { MTH_CREATEELEMENT, "createelement", false }, // datastore - { MTH_FINDELEMENT, "findelement", false }, - { MTH_DELETEELEMENT, "deleteelement", false }, - { MTH_SENDEVENT, "sendevent", false }, // script - { MTH_KILL, "kill", false }, // 75 - { MTH_LOADSYMBOLS, "loadsymbols", false }, - { MTH_SET_UO_EXPANSION, "set_uo_expansion", false }, - { MTH_CLEAR_EVENT_QUEUE, "clear_event_queue", false }, - { MTH_ADD_COMPONENT, "add_component", false }, - { MTH_ERASE_COMPONENT, "erase_component", false }, // 80 - { MTH_DELETE, "delete", false }, - { MTH_SPLIT, "split", false }, - { MTH_MOVE_CHAR, "move_char", false }, - { MTH_GETINT16FLIPPED, "getint16flipped", false }, - { MTH_GETINT32FLIPPED, "getint32flipped", false }, // 85 - { MTH_SETINT16FLIPPED, "setint16flipped", false }, - { MTH_SETINT32FLIPPED, "setint32flipped", false }, - { MTH_GETCORPSE, "getcorpse", false }, - { MTH_SETDEFAULTCMDLEVEL, "setdefaultcmdlevel", false }, - { MTH_PRIVILEGES, "privileges", false }, // 90 - { MTH_GETUNICODESTRINGFLIPPED, "getunicodestringflipped", false }, - { MTH_SETUNICODESTRINGFLIPPED, "setunicodestringflipped", false }, - { MTH_ADD_CHARACTER, "addcharacter", false }, - { MTH_SET_SWINGTIMER, "setswingtimer", false }, - { MTH_ATTACK_ONCE, "attack_once", false }, // 95 - { MTH_SETFACING, "setfacing", false }, - { MTH_COMPAREVERSION, "compareversion", false }, - { MTH_SETLEADER, "setleader", false }, - { MTH_ADDCANDIDATE, "addcandidate", false }, - { MTH_REMOVECANDIDATE, "removecandidate", false }, // 100 - { MTH_RANDOMENTRY, "randomentry", false }, - { MTH_SEEK, "seek", false }, - { MTH_PEEK, "peek", false }, - { MTH_TELL, "tell", false }, - { MTH_FLUSH, "flush", false }, // 105 - { MTH_GETSINT8, "getsint8", false }, - { MTH_GETSINT16, "getsint16", false }, - { MTH_GETSINT32, "getsint32", false }, - { MTH_SETSINT8, "setsint8", false }, - { MTH_SETSINT16, "setsint16", false }, // 110 - { MTH_SETSINT32, "setsint32", false }, - { MTH_SETAGGRESSORTO, "setaggressorto", false }, - { MTH_SETLAWFULLYDAMAGEDTO, "setlawfullydamagedto", false }, - { MTH_CLEARAGGRESSORTO, "clearaggressorto", false }, - { MTH_CLEARLAWFULLYDAMAGEDTO, "clearlawfullydamagedto", false }, // 115 - { MTH_HASSPELL, "hasspell", false }, - { MTH_SPELLS, "spells", false }, - { MTH_REMOVESPELL, "removespell", false }, - { MTH_ADDSPELL, "addspell", false }, - { MTH_DEAF, "deaf", false }, // 120 - { MTH_SETSEASON, "setseason", false }, - { MTH_NEXTSIBLING, "nextxmlsibling", false }, - { MTH_FIRSTCHILD, "firstxmlchild", false }, - { MTH_SAVEXML, "savexml", false }, - { MTH_APPENDNODE, "appendxmlnode", false }, // 125 - { MTH_SETDECLARATION, "setxmldeclaration", false }, - { MTH_SETATTRIBUTE, "setxmlattribute", false }, - { MTH_REMOVEATTRIBUTE, "removexmlattribute", false }, - { MTH_REMOVENODE, "removexmlnode", false }, - { MTH_APPENDTEXT, "appendxmltext", false }, // 130 - { MTH_XMLTOSTRING, "xmltostring", false }, - { MTH_APPENDXMLCOMMENT, "appendxmlcomment", false }, - { MTH_ADD_HOUSE_PART, "addhousepart", false }, - { MTH_ERASE_HOUSE_PART, "erasehousepart", false }, - { MTH_ACCEPT_COMMIT, "acceptcommit", false }, // 135 - { MTH_SPLITSTACK_AT, "splitstackat", false }, - { MTH_SPLITSTACK_INTO, "splitstackinto", false }, - { MTH_CANCEL_EDITING, "cancelediting", false }, - { MTH_CLONENODE, "clonenode", false }, - { MTH_HAS_EXISTING_STACK, "hasexistingstack", false }, // 140 - { MTH_LENGTH, "length", false }, - { MTH_JOIN, "join", false }, - { MTH_FIND, "find", false }, - { MTH_UPPER, "upper", false }, - { MTH_LOWER, "lower", false }, // 145 - { MTH_FORMAT, "format", false }, - { MTH_DISABLE_SKILLS_FOR, "disableskillsfor", false }, - { MTH_CYCLE, "cycle", false }, - { MTH_ADD_BUFF, "addbuff", false }, - { MTH_DEL_BUFF, "delbuff", false }, // 150 - { MTH_CLEAR_BUFFS, "clearbuffs", false }, - { MTH_CALL, "call", false }, - { MTH_SORTEDINSERT, "sorted_insert", false }, -}; -int n_objmethods = sizeof object_methods / sizeof object_methods[0]; -ObjMethod* getKnownObjMethod( const char* token ) -{ - // cache needs to hold a pointer to the original structure! eprog_read sets the override member - static auto cache = []() -> std::unordered_map { - std::unordered_map m; - for ( int i = 0; i < n_objmethods; ++i ) - { - m[object_methods[i].code] = &object_methods[i]; - } - return m; - }(); - std::string temp( token ); - std::transform( temp.begin(), temp.end(), temp.begin(), - []( char c ) { return static_cast( ::tolower( c ) ); } ); - auto method = cache.find( temp ); - if ( method != cache.end() ) - return method->second; - return nullptr; -} -ObjMethod* getObjMethod( int id ) -{ - if ( id >= n_objmethods ) - return nullptr; - else - return &( object_methods[id] ); -} - -void testparserdefinitions() -{ - for ( int i = 0; i < n_objmethods; i++ ) - { - if ( object_methods[i].id != i ) - { - INFO_PRINT << "ERROR: Object Method definition of " << object_methods[i].code - << " has an invalid index!\n"; - } - auto c = reinterpret_cast( object_methods[i].code ); - while ( *c ) - { - if ( *c != tolower( *c ) ) - { - INFO_PRINT << "ERROR: Object Method definition of " << object_methods[i].code - << " is not lowercase!\n"; - break; - } - ++c; - } - } - for ( int i = 0; i < n_objmembers; i++ ) - { - if ( object_members[i].id != i ) - { - INFO_PRINT << "ERROR: Object Member definition of " << object_members[i].code - << " has an invalid index!\n"; - } - auto c = reinterpret_cast( object_members[i].code ); - while ( *c ) - { - if ( *c != tolower( *c ) ) - { - INFO_PRINT << "ERROR: Object Member definition of " << object_members[i].code - << " is not lowercase!\n"; - break; - } - ++c; - } - } -} -Clib::UnitTest testparserdefinitions_obj( testparserdefinitions ); - -void matchOperators( Operator* oplist, int n_ops, char* buf, int* nPartial, - Operator** pTotalMatchOperator ) -{ - // all operators are 1 or 2 characters. - // they also don't have case. - int lenbuf = buf[1] ? 2 : 1; - - *nPartial = 0; - *pTotalMatchOperator = nullptr; - - if ( lenbuf == 1 ) - { - Operator* op = &oplist[0]; - for ( int i = 0; i < n_ops; ++i, ++op ) - { - if ( op->code[0] == buf[0] ) - { - if ( op->code[1] == '\0' ) - { - ( *pTotalMatchOperator ) = op; - if ( !op->ambig ) - return; - } - else - { - ( *nPartial )++; - } - } - } - } - else // lenbuf == 2 - { - Operator* op = &oplist[0]; - for ( int i = 0; i < n_ops; ++i, ++op ) - { - if ( op->code[0] == buf[0] && op->code[1] == buf[1] ) - { - ( *pTotalMatchOperator ) = op; - return; - } - } - } -} - -/* -void matchOperators(char *buf, int *nPartial, Operator** ppMatch) -{ -matchOperators(binary_operators, n_operators, -buf, nPartial, ppMatch); -} -void matchUnaryOperators(char *buf, int *nPartial, Operator** ppMatch) -{ -matchOperators(unary_operators, n_unary, -buf, nPartial, ppMatch); -} -*/ - -// FIXME this really should be a word, type, and id -// (or better yet, eliminate type altogether.) -// reserved words first get read out as literals, then -// are recognized to be reserved words. -// the "and", "or", and "not" operators won't be recognized -// right, for the moment. - -typedef struct -{ - const char* word; - BTokenId id; - BTokenType type; - Precedence precedence; - bool deprecated; -} ReservedWord; - - -ReservedWord reserved_words[] = { - { "if", RSV_ST_IF, TYP_RESERVED, PREC_TERMINATOR, false }, - { "then", RSV_THEN, TYP_RESERVED, PREC_TERMINATOR, false }, - { "elseif", RSV_ELSEIF, TYP_RESERVED, PREC_TERMINATOR, false }, - { "endif", RSV_ENDIF, TYP_RESERVED, PREC_TERMINATOR, false }, - { "else", RSV_ELSE, TYP_RESERVED, PREC_TERMINATOR, false }, - { "_OptionBracketed", RSV_OPTION_BRACKETED, TYP_RESERVED, PREC_TERMINATOR, false }, - - { "goto", RSV_GOTO, TYP_RESERVED, PREC_TERMINATOR, false }, - { "gosub", RSV_GOSUB, TYP_RESERVED, PREC_TERMINATOR, false }, - { "return", RSV_RETURN, TYP_RESERVED, PREC_TERMINATOR, false }, - - // { "global", RSV_GLOBAL, TYP_RESERVED, PREC_DEPRECATED, true }, // internal only - // { "local", RSV_LOCAL, TYP_RESERVED, PREC_DEPRECATED, true }, // internal only - { "const", RSV_CONST, TYP_RESERVED, PREC_TERMINATOR, false }, - { "var", RSV_VAR, TYP_RESERVED, PREC_TERMINATOR, false }, - - // { "begin", RSV_BEGIN, TYP_RESERVED, PREC_DEPRECATED, true }, // deprecated - // { "end", RSV_ENDB, TYP_RESERVED, PREC_DEPRECATED, true }, // deprecated - - { "do", RSV_DO, TYP_RESERVED, PREC_TERMINATOR, false }, - { "dowhile", RSV_DOWHILE, TYP_RESERVED, PREC_TERMINATOR, false }, - { "while", RSV_WHILE, TYP_RESERVED, PREC_TERMINATOR, false }, - { "endwhile", RSV_ENDWHILE, TYP_RESERVED, PREC_TERMINATOR, false }, - - { "exit", RSV_EXIT, TYP_RESERVED, PREC_TERMINATOR, false }, - - { "function", RSV_FUNCTION, TYP_RESERVED, PREC_TERMINATOR, false }, - { "endfunction", RSV_ENDFUNCTION, TYP_RESERVED, PREC_TERMINATOR, false }, - { "exported", RSV_EXPORTED, TYP_RESERVED, PREC_TERMINATOR, false }, - - { "use", RSV_USE_MODULE, TYP_RESERVED, PREC_TERMINATOR, false }, - { "include", RSV_INCLUDE_FILE, TYP_RESERVED, PREC_TERMINATOR, false }, - - { "break", RSV_BREAK, TYP_RESERVED, PREC_TERMINATOR, false }, - { "continue", RSV_CONTINUE, TYP_RESERVED, PREC_TERMINATOR, false }, - - { "for", RSV_FOR, TYP_RESERVED, PREC_TERMINATOR, false }, - { "endfor", RSV_ENDFOR, TYP_RESERVED, PREC_TERMINATOR, false }, - { "to", RSV_TO, TYP_RESERVED, PREC_TERMINATOR, false }, - { "next", RSV_NEXT, TYP_RESERVED, PREC_TERMINATOR, false }, - - { "foreach", RSV_FOREACH, TYP_RESERVED, PREC_TERMINATOR, false }, - { "endforeach", RSV_ENDFOREACH, TYP_RESERVED, PREC_TERMINATOR, false }, - - { "repeat", RSV_REPEAT, TYP_RESERVED, PREC_TERMINATOR, false }, - { "until", RSV_UNTIL, TYP_RESERVED, PREC_TERMINATOR, false }, - - { "program", RSV_PROGRAM, TYP_RESERVED, PREC_TERMINATOR, false }, - { "endprogram", RSV_ENDPROGRAM, TYP_RESERVED, PREC_TERMINATOR, false }, - - { "case", RSV_SWITCH, TYP_RESERVED, PREC_TERMINATOR, false }, - // { "case", RSV_CASE, TYP_RESERVED, PREC_TERMINATOR, false }, - { "default", RSV_DEFAULT, TYP_RESERVED, PREC_TERMINATOR, false }, - { "endcase", RSV_ENDSWITCH, TYP_RESERVED, PREC_TERMINATOR, false }, - - { "enum", RSV_ENUM, TYP_RESERVED, PREC_TERMINATOR, false }, - { "endenum", RSV_ENDENUM, TYP_RESERVED, PREC_TERMINATOR, false }, - - { "downto", RSV_FUTURE, TYP_RESERVED, PREC_TERMINATOR, false }, - { "step", RSV_FUTURE, TYP_RESERVED, PREC_TERMINATOR, false }, - { "reference", RSV_FUTURE, TYP_RESERVED, PREC_TERMINATOR, false }, - { "out", RSV_FUTURE, TYP_RESERVED, PREC_TERMINATOR, false }, - { "inout", RSV_FUTURE, TYP_RESERVED, PREC_TERMINATOR, false }, - // { "ByRef", RSV_FUTURE, TYP_RESERVED, PREC_TERMINATOR, false }, - { "ByVal", RSV_FUTURE, TYP_RESERVED, PREC_TERMINATOR, false }, - - { "string", RSV_FUTURE, TYP_RESERVED, PREC_TERMINATOR, false }, - { "long", RSV_FUTURE, TYP_RESERVED, PREC_TERMINATOR, false }, - { "integer", RSV_FUTURE, TYP_RESERVED, PREC_TERMINATOR, false }, - { "unsigned", RSV_FUTURE, TYP_RESERVED, PREC_TERMINATOR, false }, - { "signed", RSV_FUTURE, TYP_RESERVED, PREC_TERMINATOR, false }, - { "real", RSV_FUTURE, TYP_RESERVED, PREC_TERMINATOR, false }, - { "float", RSV_FUTURE, TYP_RESERVED, PREC_TERMINATOR, false }, - { "double", RSV_FUTURE, TYP_RESERVED, PREC_TERMINATOR, false }, - { "as", RSV_FUTURE, TYP_RESERVED, PREC_TERMINATOR, false }, - { "is", RSV_FUTURE, TYP_RESERVED, PREC_TERMINATOR, false }, - - { "and", TOK_AND, TYP_OPERATOR, PREC_LOGAND, false }, - { "or", TOK_OR, TYP_OPERATOR, PREC_LOGOR, false }, - { "not", TOK_LOG_NOT, TYP_UNARY_OPERATOR, PREC_UNARY_OPS, false }, - { "byref", TOK_REFTO, TYP_RESERVED, PREC_TERMINATOR, false }, // UNARY_OPERATOR, 12 }, - { "unused", TOK_UNUSED, TYP_RESERVED, PREC_TERMINATOR, false }, - { "error", TOK_ERROR, TYP_OPERAND, PREC_TERMINATOR, false }, - { "hash", RSV_FUTURE, TYP_RESERVED, PREC_TERMINATOR, false }, - { "dictionary", TOK_DICTIONARY, TYP_OPERAND, PREC_TERMINATOR, false }, - { "struct", TOK_STRUCT, TYP_OPERAND, PREC_TERMINATOR, false }, - { "array", TOK_ARRAY, TYP_OPERAND, PREC_TERMINATOR, false }, - { "stack", TOK_STACK, TYP_OPERAND, PREC_TERMINATOR, false }, - { "in", TOK_IN, TYP_OPERATOR, PREC_EQUALTO, false } - // { "bitand", TOK_BITAND, TYP_OPERATOR, PREC_BITAND }, - // { "bitxor", TOK_BITXOR, TYP_OPERATOR, PREC_BITXOR }, - // { "bitor", TOK_BITOR, TYP_OPERATOR, PREC_BITOR } - /* "/""*", RSV_COMMENT_START, - "*""/", RSV_COMMENT_END, - "/""/", RSV_COMMENT_TO_EOL, - "--", RSV_COMMENT_TO_EOL - */ -}; -unsigned n_reserved = sizeof reserved_words / sizeof reserved_words[0]; - -typedef std::map ReservedWords; -ReservedWords reservedWordsByName; -static void init_tables() -{ - static std::once_flag flag; - std::call_once( flag, []() { - for ( unsigned i = 0; i < n_reserved; ++i ) - { - reservedWordsByName[reserved_words[i].word] = &reserved_words[i]; - } - } ); -} - -void Parser::write_words( std::ostream& os ) -{ - os << "Reserved:" << std::endl; - for ( unsigned i = 0; i < n_reserved; ++i ) - { - os << reserved_words[i].word << ( reserved_words[i].deprecated ? " (deprecated)" : "" ) - << std::endl; - } - os << std::endl; - os << "Binary:" << std::endl; - for ( int i = 0; i < n_operators; ++i ) - { - os << binary_operators[i].code << ( binary_operators[i].deprecated ? " (deprecated)" : "" ) - << std::endl; - } - os << std::endl; - os << "Unary:" << std::endl; - for ( int i = 0; i < n_unary; ++i ) - { - os << unary_operators[i].code << ( unary_operators[i].deprecated ? " (deprecated)" : "" ) - << std::endl; - } - os << std::endl; - os << "Methodlist:" << std::endl; - for ( int i = 0; i < n_objmethods; i++ ) - { - os << object_methods[i].id << " " << object_methods[i].code << std::endl; - } - os << std::endl; - os << "Memberlist:" << std::endl; - for ( int i = 0; i < n_objmembers; i++ ) - { - os << object_members[i].id << " " << object_members[i].code << std::endl; - } -} - -#if 0 - void matchReservedWords(char *buf, - int *nPartial, - int *nTotal) - { - int lenbuf = strlen(buf); - assert(nPartial && nTotal); - *nPartial = 0; - *nTotal = 0; - for(int i = 0; i < n_reserved; i++) - { - if (strnicmp(reserved_words[i].word, buf, lenbuf)==0) - (*nPartial)++; - if (stricmp(reserved_words[i].word, buf)==0) - (*nTotal)++; - } - } -#endif - -/* - WTF is going on in this function? It seems like it waits for a match, followed - by a nonmatch? eh? - - What does this mean for variables like "IfDone" etc? - */ - -#if 0 - int Parser::tryReservedWord(Token& tok, char *t, char **s) - { - char opbuf[10]; - int bufp = 0; - int thisMatchPartial, thisMatchTotal; - int lastMatchTotal = 0; - - while (t && *t) { - /* let's try to match it as we go. */ - if (bufp==10) { err = PERR_BADTOKEN; return -1; } - opbuf[bufp++] = *t++; - opbuf[bufp] = '\0'; - matchReservedWords(opbuf, &thisMatchPartial, &thisMatchTotal); - if (!thisMatchPartial) { /* can't match a bloody thing! */ - switch(lastMatchTotal) { - case 0: - return 0; // this just wasn't a reserved word.. - case 1: // this is the only way it will work.. - // here, we don't match now but if we don't count - // this character, it was a unique match. - opbuf[bufp-1] = '\0'; - tok.nulStr(); - recognize_reserved_word(tok, opbuf); - *s = t-1; - return 1; - case 2: // here, with this character there is no match - // but before there were multiple matches. - // really shouldn't happen. - err = PERR_BADOPER; - return -1; - } - } else { /* this partially matches.. */ - // "Remember....." - lastMatchTotal = thisMatchTotal; - } - } - - if (thisMatchTotal == 1) { - tok.nulStr(); - recognize_reserved_word( tok, opbuf ); - *s = t; - return 1; - } - - return 0; // didn't find one! - } -#endif - -/** - * Tries to read a an operator from context - * - * @param tok Token&: The token to store the found literal into - * @param opList: The list of possible operators to look for, as Operator[] - * @param n_ops: Number of operators in the list - * @param t: todo - * @param s: todo - * @param opbuf: todo - * @return 0 when no matching text is found, 1 on success, -1 on error (also sets err) - */ -int Parser::tryOperator( Token& tok, const char* t, const char** s, Operator* opList, int n_ops, - char* opbuf ) -{ - int bufp = 0; - int thisMatchPartial; - Operator* pLastMatch = nullptr; - Operator* pMatch = nullptr; - - while ( t && *t ) - { - // if (strchr(operator_brk, *t)) mustBeOperator = 1; - - /* let's try to match it as we go. */ - if ( bufp == 4 ) - { - err = PERR_BADTOKEN; - return -1; - } - opbuf[bufp++] = *t++; - opbuf[bufp] = '\0'; - matchOperators( opList, n_ops, opbuf, &thisMatchPartial, &pMatch ); - if ( !thisMatchPartial ) - { - if ( pMatch ) - { // no partial matches, but a total match. - break; - } - else if ( !pLastMatch ) - { - // no total matches, no partial matches. - return 0; - } - else // (pLastMatch) - { - // had a match before, but no partials this time. - break; - } - } - else - { /* this partially matches.. */ - // "Remember....." - if ( pMatch ) - { - pLastMatch = pMatch; - } - } - } - - if ( pMatch == nullptr ) - pMatch = pLastMatch; - - if ( pMatch ) - { - *s += strlen( pMatch->code ); - - tok.module = Mod_Basic; - tok.id = pMatch->id; - tok.type = pMatch->type; - tok.precedence = pMatch->precedence; - tok.deprecated = pMatch->deprecated; - - tok.setStr( pMatch->code ); - return 1; - } - - return 0; // didn't find one! -} - -/** - * Tries to read a binary operator from context - * - * @param tok Token&: The token to store the found literal into - * @param ctx CompilerContext&: The context to look into - * @return 0 when no matching text is found, 1 on success, -1 on error (also sets err) - */ -int Parser::tryBinaryOperator( Token& tok, CompilerContext& ctx ) -{ - char opbuf[10]; - return tryOperator( tok, ctx.s, &ctx.s, binary_operators, n_operators, opbuf ); -} - -/** - * Tries to read an unary operator from context - * - * @param tok Token&: The token to store the found literal into - * @param ctx CompilerContext&: The context to look into - * @return 0 when no matching text is found, 1 on success, -1 on error (also sets err) - */ -int Parser::tryUnaryOperator( Token& tok, CompilerContext& ctx ) -{ - char opbuf[10]; - return tryOperator( tok, ctx.s, &ctx.s, unary_operators, n_unary, opbuf ); -} - -/** - * Tries to read a numeric value from context - * - * @param tok Token&: The token to store the found literal into - * @param ctx CompilerContext&: The context to look into - * @return 0 when no matching text is found, 1 on success, -1 on error (also sets err) - */ -int Parser::tryNumeric( Token& tok, CompilerContext& ctx ) -{ - if ( isdigit( ctx.s[0] ) || ctx.s[0] == '.' ) - { - char *endptr, *endptr2; - int l = strtol( ctx.s, &endptr, 0 ); - double d = strtod( ctx.s, &endptr2 ); - - // 2015-01-21 Bodom: weird trick to remove an unwanted feature from Microsoft compiler - // interpreting 'd' as 'e' (exponent), but 'd' in UO means dice, - // leading to confusion - // TODO: The best solution would be to reimplement the int/double parsing - if ( !( ctx.s[0] == '0' && ctx.s[1] && ( ctx.s[1] == 'x' || ctx.s[1] == 'X' ) ) ) - { - // This is not hex, so no 'd' can be valid - for ( const char* i = ctx.s; i < endptr2; i++ ) - { - if ( *i == 'd' || *i == 'D' ) - { - // A 'd' has been eaten, bug could have occurred: - // re-perform parsing on a cleaned version of the string - size_t safelen = i - ctx.s + 1; - std::unique_ptr safeptr( new char[safelen]() ); - strncpy( safeptr.get(), ctx.s, safelen - 1 ); - d = strtod( safeptr.get(), &endptr2 ); - size_t newlen = endptr2 - safeptr.get(); - endptr2 = const_cast( ctx.s + newlen ); - break; - } - } - } - - tok.type = TYP_OPERAND; - if ( endptr >= endptr2 ) - { // long got more out of it, we'll go with that - tok.id = TOK_LONG; - tok.lval = l; - ctx.s = endptr; - return 1; - } - else - { - tok.id = TOK_DOUBLE; - tok.dval = d; - ctx.s = endptr2; - return 1; - } - } - return 0; // not numeric -} - -/** - * Tries to read a literal (string/variable name) from context - * - * @param tok Token&: The token to store the found literal into - * @param ctx CompilerContext&: The context to look into - * @return 0 when no matching text is found, 1 on success, -1 on error (also sets err) - */ -int Parser::tryLiteral( Token& tok, CompilerContext& ctx ) -{ - if ( ctx.s[0] == '\"' ) - { - const char* end = &ctx.s[1]; - std::string lit; - bool escnext = false; // true when waiting for 2nd char in an escape sequence - u8 hexnext = 0; // tells how many more chars in a \xNN escape sequence - char hexstr[3]; // will contain the \x escape chars to be processed - memset( hexstr, 0, 3 ); - - for ( ;; ) - { - if ( !*end ) - { - err = PERR_UNTERMSTRING; - return -1; - } - - passert_always_r( !( escnext && hexnext ), - "Bug in the compiler. Please report this on the forums." ); - - if ( escnext ) - { - // waiting for 2nd character after a backslash - escnext = false; - if ( *end == 'n' ) - lit += '\n'; - else if ( *end == 't' ) - lit += '\t'; - else if ( *end == 'x' ) - hexnext = 2; - else - lit += *end; - } - else if ( hexnext ) - { - // waiting for next (two) chars in hex escape sequence (eg. \xFF) - hexstr[2 - hexnext] = *end; - if ( !--hexnext ) - { - char* endptr; - char ord = static_cast( strtol( hexstr, &endptr, 16 ) ); - if ( *endptr != '\0' ) - { - err = PERR_INVESCAPE; - return -1; - } - lit += ord; - } - } - else - { - if ( *end == '\\' ) - escnext = true; - else if ( *end == '\"' ) - break; - else - lit += *end; - } - ++end; - } - /* - char *end = strchr(&ctx.s[1], '\"'); - if (!end) - { - err = PERR_UNTERMSTRING; - return -1; - } - */ - // int len = end - ctx.s; // "abd" len = 5-1 = 4 - if ( !Clib::isValidUnicode( lit ) ) - { - if ( !ctx.silence_unicode_warnings ) - { - compiler_warning( &ctx, "Warning: invalid unicode character detected. Assuming ISO8859\n" ); - } - Clib::sanitizeUnicodeWithIso( &lit ); - } - tok.id = TOK_STRING; // this is a misnomer I think! - tok.type = TYP_OPERAND; - tok.copyStr( lit.c_str() ); - - ctx.s = end + 1; // skip past the ending delimiter - return 1; - } - else if ( isalpha( ctx.s[0] ) || ctx.s[0] == '_' ) - { // we have a variable/label/verb. - const char* end = ctx.s; - while ( *end && !isspace( *end ) && strchr( ident_allowed, *end ) ) - { - ++end; - } - // Catch identifiers of the form module::name - if ( end[0] == ':' && end[1] == ':' ) - { - end += 2; - while ( *end && !isspace( *end ) && strchr( ident_allowed, *end ) ) - { - ++end; - } - } - - int len = static_cast( end - ctx.s + 1 ); // "abcd" - - tok.copyStr( ctx.s, len - 1 ); - - tok.module = Mod_Basic; - tok.id = TOK_IDENT; - tok.type = TYP_OPERAND; - - ctx.s = end; - return 1; - } - return 0; -} - -int Parser::recognize_binary( Token& tok, const char* buf, const char** /*s*/ ) -{ - for ( int i = 0; i < n_operators; i++ ) - { - if ( stricmp( buf, binary_operators[i].code ) == 0 ) - { - tok.module = Mod_Basic; - tok.id = binary_operators[i].id; - tok.type = binary_operators[i].type; - tok.precedence = binary_operators[i].precedence; - /* - if (tok.type == TYP_OPERATOR && - (tok.id == TOK_ADDMEMBER || - tok.id == TOK_DELMEMBER || - tok.id == TOK_CHKMEMBER || - tok.id == TOK_MEMBER) ) - { - int res; - Token tk2; - res = getToken( s, tk2 ); - if (res < 0) return res; - if (tk2.type != TYP_OPERAND || tk2.id != TOK_IDENT) - return -1; - tok.copyStr( tk2.tokval() ); - } - else - { - */ - tok.setStr( binary_operators[i].code ); - // tok.nulStr(); - /* - } - */ - - return 1; - } - } - return 0; -} - -int Parser::recognize_unary( Token& tok, const char* buf ) -{ - for ( int i = 0; i < n_unary; i++ ) - { - if ( stricmp( buf, unary_operators[i].code ) == 0 ) - { - tok.module = Mod_Basic; - tok.id = unary_operators[i].id; - tok.type = unary_operators[i].type; - tok.precedence = unary_operators[i].precedence; - tok.setStr( unary_operators[i].code ); - - return 1; - } - } - return 0; -} - - -int Parser::recognize( Token& tok, const char* buf, const char** s ) -{ - if ( recognize_binary( tok, buf, s ) ) - return 1; - return recognize_unary( tok, buf ); -} - - -bool Parser::recognize_reserved_word( Token& tok, const char* buf ) -{ - if ( tok.id != TOK_IDENT ) - return false; - - auto itr = reservedWordsByName.find( buf ); - if ( itr != reservedWordsByName.end() ) - { - ReservedWord* rv = ( *itr ).second; - - tok.module = Mod_Basic; - tok.id = rv->id; - tok.type = rv->type; - tok.precedence = rv->precedence; - tok.deprecated = rv->deprecated; - tok.setStr( rv->word ); - return true; - } - return false; -} - - -/* -int isOperator(Token& token) -{ -for(int i=0;i < n_operators; i++) { -if (stricmp(binary_operators[i].code, token )==0) { -token.setStr(binary_operators[i].code); -token.id = binary_operators[i].id; -token.type = binary_operators[i].type; -return 1; -} -} -return 0; -} -*/ - -Precedence find_precedence( Token& token ) -{ - return static_cast( token.precedence ); -} - -void Parser::reinit( Expression& ex ) -{ - // qCA.clear(); - while ( !ex.CA.empty() ) - { - delete ex.CA.front(); - ex.CA.pop(); - } - - // TX.clear() - while ( !ex.TX.empty() ) - { - delete ex.TX.top(); - ex.TX.pop(); - } - - - err = PERR_NONE; - ext_err[0] = '\0'; -} - -/** - * Reads next token from given context - * - * what is a token? a set of homogeneous characters - * a Label: - * begins with [A-Za-z_], followed by [A-Za-z0-9] - * - * A character literal: - * begins with '"', ends with '"'. anything goes in between. - * - * a Number: - * begins with [0-9] (note: not plus or minus, these get eaten as unary ops) - * can be either a float (stored as double), or a long. - * 0xABC is hex, which is read okay. - * So is 0.5e+17. I let strtod do most of the work. - * basically whichever of strtod or strtol can do more with it - * (endptr arg is greater on exit), that's what i decide that it is. - * - * an operator: - * any collection of the operator characters - * [ ( ) * / + - < = > ,] not separated by whitespace, digits, or alphas - * note a collection of more than one is considered a SINGLE operator. - * So if you put 6*-7, *- is the operator. nasty I know, but - * what am I supposed to do? (Maximal munch, is what is actually done!) - * - * @param tok Token&: The token to store the found literal into - * @param ctx CompilerContext&: The context to look into - * @param expr unused - * @return 0 when no matching text is found, 1 on success, -1 on error (also sets err) - */ -int Parser::getToken( CompilerContext& ctx, Token& tok, Expression* /* expr not used */ ) -{ - int hit = 0; - - int res = ctx.skipcomments(); - if ( res ) - return res; - - tok.dbg_filenum = ctx.dbg_filenum; - tok.dbg_linenum = ctx.line; - - if ( ctx.s[0] == ';' ) - { - tok.module = Mod_Basic; - tok.id = TOK_SEMICOLON; - tok.type = TYP_DELIMITER; - tok.setStr( ";" ); - ctx.s++; - return 0; - } - - hit = tryLiteral( tok, ctx ); - if ( hit == -1 ) - { - return -1; - } - else if ( hit ) - { - recognize_reserved_word( tok, tok.tokval() ); - return 0; - } - - /* - hit = tryReservedWord(tok, t, s); - if (hit==-1) return -1; - else if (hit) return 0; - */ - - hit = tryBinaryOperator( tok, ctx ); - if ( hit == -1 ) - return -1; - else if ( hit ) - return 0; - - hit = tryUnaryOperator( tok, ctx ); - if ( hit == -1 ) - return -1; - else if ( hit ) - return 0; - - // label: - // A:=4; - - - hit = tryNumeric( tok, ctx ); - if ( hit == -1 ) - return -1; - else if ( hit ) - return 0; - - if ( ctx.s[0] == ':' ) - { - ++ctx.s; - tok.id = RSV_COLON; - tok.type = TYP_RESERVED; - tok.setStr( ":" ); - return 0; - } - - compiler_error( "Your syntax frightens and confuses me.\n" ); - err = PERR_WAAH; - return -1; -} - -/** - * Reads next token from given context, but without modifying the context - * - * @see Parser::getToken( CompilerContext& ctx, Token& tok ) - */ -int Parser::peekToken( const CompilerContext& ctx, Token& token, Expression* expr ) -{ - CompilerContext tctx( ctx ); - tctx.silence_unicode_warnings = true; - return getToken( tctx, token, expr ); -} - -/* Parser::parseToken deleted. */ -/* not used? ens 12/10/1998 -int Parser::IP(Expression& expr, char *s) -{ -reinit(expr); - -Token *ptoken; -if (!quiet) cout << "Parsing \"" << s << '\"' << endl; - -expr.TX.push(new Token); // push terminator token - -ptoken = new Token; -while (getToken(&s, *ptoken)==0) -{ -parseToken(expr, ptoken); -ptoken = new Token; -} -parseToken(expr, ptoken); - -return 0; -} -*/ - - -int SmartParser::isOkay( const Token& token, const Token& last_token ) -{ - BTokenType last_type = last_token.type; - BTokenType this_type = token.type; - if ( !quiet ) - INFO_PRINT << "isOkay(" << this_type << "," << last_type << ")\n"; - if ( last_type == TYP_FUNC || last_type == TYP_USERFUNC || last_type == TYP_METHOD || - last_type == TYP_FUNCREF ) - last_type = TYP_OPERAND; - if ( this_type == TYP_FUNC || this_type == TYP_USERFUNC || this_type == TYP_METHOD || - this_type == TYP_FUNCREF ) - this_type = TYP_OPERAND; - if ( token.id == TOK_LBRACE ) // an array declared somewhere out there - this_type = TYP_OPERAND; - - if ( last_type == TYP_UNARY_PLACEHOLDER || this_type == TYP_UNARY_PLACEHOLDER ) - return 0; // always invalid - if ( token.id == TOK_UNPLUSPLUS_POST || - token.id == TOK_UNMINUSMINUS_POST ) // valid when other direction is valid - std::swap( this_type, last_type ); - if ( last_token.id == TOK_UNPLUSPLUS_POST || - last_token.id == TOK_UNMINUSMINUS_POST ) // behaves like the operand before - last_type = TYP_OPERAND; - if ( last_type > TYP_TESTMAX ) - return 1; // assumed okay - if ( this_type > TYP_TESTMAX ) - return 1; // assumed okay - return allowed_table[last_type][this_type]; // maybe okay -} - -/* -int SmartParser::isFunc(Token& token, Verb **v) -{ -if (token.id != TOK_IDENT) return 0; // ain't no verb.. -// note that this catches string literals. -assert(token.tokval()); - -if (isInTable(parser_verbs, n_parser_verbs, token.tokval(), v)) -{ -token.id = (*v)->id; -token.lval = (*v)->narg; -token.type = TYP_FUNC; -token.module = Mod_Basic; -return 1; -} -return 0; -} -*/ - -/** - * Like Parser::tryLiteral, with extra elements supported - * - * Labels. - * A label is an ident operand, followed by a colon, followed by - * either whitespace or end-of-file. - * Note, this definition just happens to exclude ':=' and '::', - * which is important. - * - * @see Parser::tryLiteral( Token& tok, CompilerContext& ctx ) - */ -int SmartParser::tryLiteral( Token& tok, CompilerContext& ctx ) -{ - int res; - res = Parser::tryLiteral( tok, ctx ); - if ( res == 1 && tok.id == TOK_IDENT ) - { // check for "label:" - - // whitespace used to be skipped here. - // while (*t && isspace(*t)) t++; - if ( !ctx.s[0] ) // ident EOF can't be a label - { - return 1; - } - -// this might be a nice place to look for module::function, too. -#if 0 - if (t[0] == ':' && t[1] == ':') - { - t += 2; - *s = t; - Token tok2; - int res2 = Parser::tryLiteral( tok2, t, s ); - if (res2 < 0) return res2; - if (res2 == 0) - return -1; - // append '::{tok2 tokval}' to tok.tokval - // (easier when/if token uses string) - } -#endif - if ( ctx.s[0] == ':' && ( ctx.s[1] == '\0' || isspace( ctx.s[1] ) ) ) - { - tok.id = CTRL_LABEL; - tok.type = TYP_LABEL; - ++ctx.s; - } - return 1; - } - return res; -} - -int SmartParser::parseToken( CompilerContext& ctx, Expression& expr, Token* token ) -{ - // return Parser::parseToken(token); - if ( !quiet ) - { - fmt::Writer _tmp; - _tmp << "parseToken( " << *token << ")\n"; - _tmp << " CA: "; - std::queue ca( expr.CA ); - while ( !ca.empty() ) - { - Token* tk = ca.front(); - _tmp << *tk << " "; - ca.pop(); - } - _tmp << "\n"; - _tmp << " TX: "; - std::stack tx( expr.TX ); - while ( !tx.empty() ) - { - Token* tk = tx.top(); - _tmp << *tk << " "; - tx.pop(); - } - INFO_PRINT << _tmp.str() << "\n"; - } - - for ( ;; ) - { - Token* last = expr.TX.top(); - switch ( token->type ) - { - case TYP_FUNC: - case TYP_USERFUNC: - case TYP_OPERAND: - expr.CA.push( token ); - return 0; - case TYP_TERMINATOR: - switch ( last->type ) - { - case TYP_TERMINATOR: - delete token; - delete expr.TX.top(); - expr.TX.pop(); - return 1; // all done! - case TYP_UNARY_OPERATOR: - expr.CA.push( expr.TX.top() ); - expr.TX.pop(); - break; - case TYP_OPERATOR: - expr.CA.push( expr.TX.top() ); - expr.TX.pop(); - break; - case TYP_LEFTPAREN: - err = PERR_MISSRPAREN; - return -1; - case TYP_LEFTBRACKET: - err = PERR_MISSRBRACKET; - return -1; - default: - err = PERR_WAAH; - return -1; - } - break; - case TYP_UNARY_OPERATOR: - case TYP_OPERATOR: - case TYP_LEFTBRACKET: - // these start out at 0, but need to start at one. - if ( token->type == TYP_LEFTBRACKET ) - token->lval = 1; - switch ( last->type ) - { - case TYP_TERMINATOR: - case TYP_LEFTPAREN: - case TYP_LEFTBRACKET: - // possible check CA for array dereference operator here. - // if one found, remove it, and change this assignment to a "deref and assign" - expr.TX.push( token ); - return 0; - case TYP_UNARY_OPERATOR: - case TYP_OPERATOR: - int this_prec; - int last_prec; - - this_prec = find_precedence( *token ); - last_prec = find_precedence( *last ); - if ( this_prec > last_prec ) - { - expr.TX.push( token ); - return 0; - } - else - { - expr.CA.push( expr.TX.top() ); - expr.TX.pop(); - } - break; - default: - err = PERR_WAAH; - return -1; - break; - } - break; - case TYP_LEFTPAREN: - expr.TX.push( token ); - return 0; - case TYP_RIGHTPAREN: - switch ( last->type ) - { - case TYP_TERMINATOR: - err = PERR_UNEXRPAREN; - return -1; - case TYP_UNARY_OPERATOR: - case TYP_OPERATOR: - expr.CA.push( expr.TX.top() ); - expr.TX.pop(); - break; - case TYP_LEFTPAREN: - delete token; - delete expr.TX.top(); - expr.TX.pop(); - return 0; - default: - compiler_error( "Unmatched ')' in expression. (Trying to match against a '", *last, "')\n", - ctx, "parseToken(): Not sure what to do.\n", "Token: ", *token, "\n", - "Last: ", *last, "\n" ); - throw std::runtime_error( "Error in parseToken() (1)" ); - } - break; - - // case TYP_LEFTBRACKET: - // expr.TX.push(token); - // return 0; - case TYP_RIGHTBRACKET: - switch ( last->type ) - { - case TYP_TERMINATOR: - err = PERR_UNEXRBRACKET; - return -1; - case TYP_UNARY_OPERATOR: - case TYP_OPERATOR: - expr.CA.push( expr.TX.top() ); - expr.TX.pop(); - break; - case TYP_LEFTBRACKET: - Token* ptkn; - ptkn = new Token( TOK_ARRAY_SUBSCRIPT, TYP_OPERATOR ); - ptkn->lval = last->lval; - ptkn->dbg_filenum = token->dbg_filenum; - ptkn->dbg_linenum = token->dbg_linenum; - - if ( ptkn->lval != 1 ) - ptkn->id = INS_MULTISUBSCRIPT; - expr.CA.push( ptkn ); - - delete token; - delete expr.TX.top(); - expr.TX.pop(); - return 0; - default: - compiler_error( "Unmatched ']' in expression. (Trying to match against a '", *last, "')\n", - ctx, "parseToken(): Not sure what to do.\n", "Token: ", *token, "\n", - "Last: ", *last, "\n" ); - throw std::runtime_error( "Error in parseToken() (2)" ); - } - break; - - /* Special Case: two syntaxes are supported for array indexing: - A[3][4] and A[3,4]. - If this is a comma, and we are working in a left bracket area, - increase the number of indices (stored in lval of the leftbracket) - */ - case TYP_SEPARATOR: - if ( last->type == TYP_LEFTBRACKET ) - { - ++last->lval; - // Token *ptkn = new Token( Mod_Basic, TOK_ARRAY_SUBSCRIPT, TYP_OPERATOR ); - // ptkn->lval = last->lval++; - // expr.CA.push( ptkn ); - - delete token; - return 0; - } - else if ( last->type == TYP_UNARY_OPERATOR || last->type == TYP_OPERATOR ) - { - expr.CA.push( expr.TX.top() ); - expr.TX.pop(); - break; // try again - } - else - { - err = PERR_UNEXPCOMMA; - return -1; - } - - default: - compiler_error( "Don't know what to do with '", *token, "' in SmartParser::parseToken\n", - ctx ); - err = PERR_WAAH; - return -1; - } - } -} - -/** - * Some identifiers are functions (user-defined or module-defined) - * these are recognized here. HOWEVER, in some cases these should - * be ignored - particularly, after the "." operator and its ilk. - * For example, if the LEN function is defined, - * "print a.len;" should still be valid (assuming A is a variable - * with a 'len' member). In these cases, the operator in question - * will be at the top of the TX stack. So, if we find this operator - * there, we won't check for functions. - * This is also the perfect opportunity to morph would-be identifiers - * into strings, or "member names" if that turns out the way to go. - * (Normally, we would emit TOK_IDENT(left) TOK_IDENT(right) TOK_MEMBER. - * The problem here is that TOK_IDENT(left) is seen as a variable - * (quite rightly), but so is TOK_IDENT(right), which is wrong. - * We used to transform this in addToken, but this is better - * I think.) - * - * @note let's suppose.. - * an overridden getToken is really smart, and figures out if IDENTS - * are variable names, verbs, functions, or labels. To this end it - * pulls out the ':' if necessary. - * TYP_OPERAND, TOK_VARNAME - * TYP_FUNC, TOK_MID, <-- TYP_OPERAND for purposes of legality - * TYP_PROC, TOK_PRINT, - * TYP_LABEL, (don't care) - * - * IP still does the same thing only it no longer looks for isVerb. - * have the new getToken put, say, the verb number in lval, so we now - * have an array element number. - * - * @see Parser::getToken( CompilerContext& ctx, Token& token ) - */ -int SmartParser::getToken( CompilerContext& ctx, Token& token, Expression* pexpr ) -{ - int res = Parser::getToken( ctx, token ); - if ( res == -1 ) - return -1; - if ( token.id == TOK_IDENT ) - { - if ( pexpr ) - { - if ( !pexpr->TX.empty() ) - { - Token* tkn = pexpr->TX.top(); - if ( tkn->id == TOK_MEMBER || tkn->id == TOK_ADDMEMBER || tkn->id == TOK_DELMEMBER || - tkn->id == TOK_CHKMEMBER ) - { - token.id = TOK_STRING; - return res; - } - } - } - if ( isFunc( token, &modfunc_ ) ) - return 0; - if ( isUserFunc( token, &userfunc_ ) ) - return 0; - } - return res; -} - -/** - * Get a token, but bypass the conversions in SmartParser::getToken - * Primarily used when getting an identifier that may be the same as - * a userfunc name. - */ -int SmartParser::getTokenWithoutConversions( CompilerContext& ctx, Token& token ) -{ - return Parser::getToken( ctx, token ); -} - -bool SmartParser::callingMethod( CompilerContext& ctx ) -{ - // if we have something like x.foo(), change to call-method - CompilerContext tctx( ctx ); - Token tk_name, tk_paren; - - if ( getToken( tctx, tk_name ) == 0 && getToken( tctx, tk_paren ) == 0 && - ( tk_name.id == TOK_IDENT || tk_name.id == TOK_FUNC || tk_name.id == TOK_USERFUNC ) && - tk_paren.id == TOK_LPAREN ) - { - return true; - } - return false; -} - -/** - * if comma terminator is allowed, (reading args, or declaring variables) - * leaves the terminator/comma. - * if comma term not allowed, eats the semicolon. - * if right paren allowed, leaves the right paren. - * - * @note (obviously, this function's behavior needs some work!) - **/ -int SmartParser::IIP( Expression& expr, CompilerContext& ctx, unsigned flags ) -{ - BTokenType last_type = TYP_TERMINATOR; - Token last_token; - // Token* debug_last_tx_token = nullptr; - int done = 0; - int res = 0; // 1=done, -1=error, 0=not done - - int semicolon_term_allowed = flags & EXPR_FLAG_SEMICOLON_TERM_ALLOWED; - int comma_term_allowed = flags & EXPR_FLAG_COMMA_TERM_ALLOWED; - int rightparen_term_allowed = flags & EXPR_FLAG_RIGHTPAREN_TERM_ALLOWED; - int rightbrace_term_allowed = flags & EXPR_FLAG_RIGHTBRACE_TERM_ALLOWED; - int dictkey_term_allowed = flags & EXPR_FLAG_DICTKEY_TERM_ALLOWED; - int endenum_term_allowed = flags & EXPR_FLAG_ENDENUM_TERM_ALLOWED; - int to_term_allowed = flags & EXPR_FLAG_TO_TERM_ALLOWED; - int auto_term_allowed = flags & EXPR_FLAG_AUTO_TERM_ALLOWED; - int term_required = - flags & ( EXPR_FLAG_SEMICOLON_TERM_ALLOWED | EXPR_FLAG_COMMA_TERM_ALLOWED | - EXPR_FLAG_RIGHTPAREN_TERM_ALLOWED | EXPR_FLAG_RIGHTBRACE_TERM_ALLOWED | - EXPR_FLAG_DICTKEY_TERM_ALLOWED | EXPR_FLAG_ENDENUM_TERM_ALLOWED | - EXPR_FLAG_TO_TERM_ALLOWED | EXPR_FLAG_AUTO_TERM_ALLOWED ); - if ( !quiet ) - { - char buf[80]; - Clib::stracpy( buf, ctx.s, 80 ); - strtok( buf, "\r\n" ); - INFO_PRINT << "Parsing " << buf << "\n"; - } - - expr.TX.push( new Token ); /* push a terminator token */ - int leftbracket_count = 0; - while ( !done && ( res == 0 ) ) - { - const char* t = ctx.s; - Token token; - - res = peekToken( ctx, token ); - - if ( res == 1 && term_required ) - { - compiler_error( "Unexpected end of file while reading an expression. Expected ", - semicolon_term_allowed ? " ';'" : "", comma_term_allowed ? " ," : "", - rightparen_term_allowed ? " ']'" : "", rightbrace_term_allowed ? " '}'" : "", - dictkey_term_allowed ? " '->'" : "", endenum_term_allowed ? " 'endenum'" : "", - to_term_allowed ? " 'to'" : "", "\n" ); - err = PERR_EOFEXPECTINGTERM; - res = -1; - break; - } - if ( res ) - break; - if ( !isLegal( token ) ) - { - err = PERR_NOTLEGALHERE; - res = -1; - break; - } - - // if (token.type == TYP_DELIMITER) break; - // debug_last_tx_token = expr.TX.top(); - - /* - if (comma_term_allowed) - { - Token* tkn = expr.TX.top(); - if (leftbracket_count > 0) // tkn != nullptr && tkn->id == TOK_LBRACKET ) - { - // ignore commas if we're in a left bracket situation. - ; - } - else if (token.id == TOK_COMMA || token.id == TOK_SEMICOLON) - { - break; - } - } - */ - if ( leftbracket_count == 0 ) - { - if ( comma_term_allowed && ( token.id == TOK_COMMA || token.id == TOK_SEMICOLON ) ) - { - break; - } - if ( endenum_term_allowed && ( token.id == RSV_ENDENUM ) ) - { - break; - } - if ( to_term_allowed && ( token.id == RSV_TO ) ) - { - break; - } - } - - if ( rightbrace_term_allowed && token.id == TOK_RBRACE ) - { - break; - } - if ( dictkey_term_allowed && token.id == TOK_DICTKEY ) - { - break; - } - - /* - else - { - if (token.type == TYP_SEPARATOR) - { - err = PERR_UNEXPCOMMA; - res = -1; - break; - } - } - */ - - // auto-terminated expressions need to not eat what broke them out... - CompilerContext save_ctx( ctx ); - - res = getToken( ctx, token, &expr ); - - if ( semicolon_term_allowed && token.id == TOK_SEMICOLON ) - break; - - if ( token.id == TOK_LBRACKET ) - ++leftbracket_count; - else if ( token.id == TOK_RBRACKET ) - --leftbracket_count; - - if ( !isOkay( token, last_token ) ) - - { - if ( token.type == TYP_OPERATOR || - token.type == TYP_UNARY_PLACEHOLDER ) // check for a unary operator that looks the same. - recognize_unary( token, token.tokval() ); - if ( token.id == TOK_UNPLUSPLUS && last_type == TYP_OPERAND ) // switching to post increment - token.id = TOK_UNPLUSPLUS_POST; - else if ( token.id == TOK_UNMINUSMINUS && - last_type == TYP_OPERAND ) // switching to post decrement - token.id = TOK_UNMINUSMINUS_POST; - if ( !isOkay( token, last_token ) ) - { - if ( auto_term_allowed ) - { - ctx = save_ctx; - break; - } - - res = -1; - err = PERR_ILLEGALCONS; - ctx.s = t; // FIXME operator= - compiler_error( "Token '", token, "' cannot follow token '", last_token, "'\n" ); - if ( last_token.type == TYP_OPERAND && token.id == TOK_LPAREN ) - { - compiler_error( "Function ", last_token, "() is not defined.\n" ); - } - break; - } - } - else if ( last_type == TYP_TERMINATOR && token.type == TYP_LEFTBRACE && - compilercfg.ParanoiaWarnings ) - { - compiler_warning( - &ctx, - "Warning: Using { } is inappropriate; please define array, struct or dictionary.\n" ); - } - last_type = token.type; - last_token = token; - - // - // only certain types of things can be in an auto-terminated expr - // stuff like 'var' and statements can't. - // - if ( auto_term_allowed ) - { - if ( token.type != TYP_FUNC && token.type != TYP_USERFUNC && token.type != TYP_OPERAND && - token.type != TYP_UNARY_OPERATOR && token.type != TYP_OPERATOR && - token.type != TYP_LEFTPAREN && token.type != TYP_RIGHTPAREN && - token.type != TYP_LEFTBRACKET && token.type != TYP_RIGHTBRACKET && - token.type != TYP_SEPARATOR && token.id != TOK_LBRACE && token.id != TOK_RBRACE ) - { - ctx = save_ctx; - break; - } - } - - Token* ptok2; - if ( token.type == TYP_USERFUNC ) - { - res = getUserArgs( expr, ctx ); - if ( res < 0 ) - return res; - - ptok2 = new Token( token ); - res = parseToken( ctx, expr, ptok2 ); - if ( res < 0 ) - { - delete ptok2; - ctx.s = t; - } - } - else if ( token.type == TYP_FUNC ) - { - userfunc_ = modfunc_->uf; - res = getUserArgs( expr, ctx, false ); - if ( res < 0 ) - { - compiler_error( "Error getting arguments for function ", token.tokval(), "\n", ctx, "\n" ); - return res; - } - ptok2 = new Token( token ); - res = parseToken( ctx, expr, ptok2 ); - if ( res < 0 ) - { - delete ptok2; - ctx.s = t; // FIXME operator= - - if ( ( err == PERR_UNEXRPAREN ) && rightparen_term_allowed ) - { - err = PERR_NONE; - res = 0; - done = 1; - } - } - } - else if ( token.id == TOK_ARRAY ) - { - auto array_tkn = new Token( TOK_ARRAY, TYP_OPERAND ); - array_tkn->dbg_filenum = token.dbg_filenum; - array_tkn->dbg_linenum = token.dbg_linenum; - - res = parseToken( ctx, expr, array_tkn ); - if ( res < 0 ) - return res; - - // expr.CA.push( array_tkn ); - - // 'array' can be of the following forms: - // var x := array; // preferred - // var x := array { 2, 4, 6, 1 }; // preferred - // var x := array ( 2, 4, 6, 1 ); // not preferred, looks too much like a multi-dim - - Token peek_token; - res = peekToken( ctx, peek_token ); - if ( res == 0 ) - { - if ( peek_token.id == TOK_LPAREN ) - { - res = getArrayElements( expr, ctx ); - } - else if ( peek_token.id == TOK_LBRACE ) - { - // this form expects that the LBRACE has been eaten, so do so. - getToken( ctx, peek_token ); - res = getNewArrayElements( expr, ctx ); - } - if ( res < 0 ) - compiler_error( "Error getting elements for array\n" ); - } - } - else if ( token.id == TOK_LBRACE ) // a bare array declaration, like var x := { 2, 4 }; - { - expr.CA.push( new Token( TOK_ARRAY, TYP_OPERAND ) ); - res = getNewArrayElements( expr, ctx ); - if ( res < 0 ) - compiler_error( "Error getting elements for array\n" ); - } - else if ( token.id == TOK_ERROR ) - { - auto error_tkn = new Token( TOK_ERROR, TYP_OPERAND ); - error_tkn->dbg_filenum = token.dbg_filenum; - error_tkn->dbg_linenum = token.dbg_linenum; - expr.CA.push( error_tkn ); - res = getStructMembers( expr, ctx ); - if ( res < 0 ) - compiler_error( "Error reading members for error\n" ); - } - else if ( token.id == TOK_STRUCT ) - { - auto struct_tkn = new Token( TOK_STRUCT, TYP_OPERAND ); - struct_tkn->dbg_filenum = token.dbg_filenum; - struct_tkn->dbg_linenum = token.dbg_linenum; - expr.CA.push( struct_tkn ); - res = getStructMembers( expr, ctx ); - if ( res < 0 ) - compiler_error( "Error reading members for struct\n" ); - } - else if ( token.id == TOK_DICTIONARY ) - { - auto dict_tkn = new Token( token ); // Mod_Basic, TOK_DICTIONARY, TYP_OPERAND ); - // struct_tkn->dbg_filenum = token.dbg_filenum; - // struct_tkn->dbg_linenum = token.dbg_linenum; - expr.CA.push( dict_tkn ); - res = getDictionaryMembers( expr, ctx ); - if ( res < 0 ) - compiler_error( "Error reading members for dictionary\n" ); - } - else if ( token.id == TOK_FUNCREF ) - { - auto ref_tkn = new Token( token ); - res = getFunctionPArgument( expr, ctx, ref_tkn ); - if ( res < 0 ) - compiler_error( "Error reading function reference argument\n" ); - expr.CA.push( ref_tkn ); - } - else if ( token.id == TOK_MEMBER && callingMethod( ctx ) ) - { - // look for something like x.foo(b); - // we'll catch this on the 'foo' - // so check for preceding TOK_MEMBER, and succeeding TOK_LPAREN - - ptok2 = new Token( token ); // this we will turn into an INS_CALL_METHOD. - int _res = parseToken( ctx, expr, ptok2 ); - if ( _res < 0 ) - { - delete ptok2; - return _res; - } - - // grab the method name - getToken( ctx, token, &expr ); - std::string methodName( token.tokval() ); - Clib::mklowerASCII( methodName ); - int nargs; - res = getMethodArguments( expr, ctx, nargs ); - // our ptok2 is now sitting in TX. Move it to CA. - Token* _t = expr.TX.top(); - expr.TX.pop(); - expr.CA.push( _t ); - - ObjMethod* objmeth = getKnownObjMethod( methodName.c_str() ); - if ( objmeth != nullptr && compilercfg.OptimizeObjectMembers ) - { - ptok2->id = INS_CALL_METHOD_ID; - ptok2->type = TYP_METHOD; - ptok2->lval = nargs; - ptok2->dval = objmeth->id; - } - else - { - ptok2->id = INS_CALL_METHOD; - ptok2->type = TYP_METHOD; - ptok2->lval = nargs; - ptok2->copyStr( methodName.c_str() ); - } - last_type = TYP_OPERAND; - last_token.type = TYP_OPERAND; // FIXME: kind of a hack - } - else - { - if ( res >= 0 ) - { - ptok2 = new Token( token ); - res = parseToken( ctx, expr, ptok2 ); - if ( res < 0 ) - { - delete ptok2; - ctx.s = t; // FIXME operator= - - if ( ( err == PERR_UNEXRPAREN ) && rightparen_term_allowed ) - { - err = PERR_NONE; - res = 0; - done = 1; - } - } - } - } - if ( flags & EXPR_FLAG_SINGLE_ELEMENT ) - done = 1; - } - - int nres = parseToken( ctx, expr, new Token ); /* end of expression */ - if ( res >= 0 ) - res = nres; - - if ( !comma_term_allowed && !quiet ) - { - INFO_PRINT << "Result: " << res << "\n"; - } - return res; -} - -} // namespace Bscript -} // namespace Pol diff --git a/pol-core/bscript/parser.h b/pol-core/bscript/parser.h deleted file mode 100644 index ff00836613..0000000000 --- a/pol-core/bscript/parser.h +++ /dev/null @@ -1,167 +0,0 @@ -/** @file - * - * @par History - * - 2009/08/25 Shinigami: STLport-5.2.1 fix: ParseErrorStr changed little bit - */ - - -#ifndef __PARSER_H -#define __PARSER_H - -#include -#include -#include -#include -#include -#include - -#include "tokens.h" - -#ifndef __TOKEN_H -#include "token.h" -#endif -#ifndef __OPERATOR_H -#include "operator.h" -#endif - -namespace Pol -{ -namespace Bscript -{ -class CompilerContext; -class Expression; -class ModuleFunction; -class Token; -class UserFunction; - -typedef enum -{ - PERR_NONE, - PERR_UNEXRPAREN, // unexpected RIGHT Paren - PERR_MISSLPAREN, - PERR_MISSRPAREN, // missing RPAREN - PERR_BADTOKEN, // bad token ?? - PERR_BADOPER, // unknown operator.. - PERR_WAAH, // god knows what happened - PERR_UNTERMSTRING, // "abcd (not terminated with '"') - PERR_INVESCAPE, // an invalid escape sequence (eg. \xFG) - PERR_TOOFEWARGS, - PERR_TOOMANYARGS, - PERR_UNEXPCOMMA, - PERR_ILLEGALCONS, // illegal construction - PERR_MISSINGDELIM, - PERR_NOTLEGALHERE, - PERR_PROCNOTALLOWED, - PERR_UNEXPSEMI, - PERR_EXPWHILE, - PERR_UNEXRBRACKET, - PERR_MISSRBRACKET, - PERR_EOFEXPECTINGTERM, - PERR_MULTILINESTRING, - PERR_NUM_ERRORS -} ParseError; - -extern const char* ParseErrorStr[]; - -const unsigned EXPR_FLAG_SEMICOLON_TERM_ALLOWED = 0x0001; -const unsigned EXPR_FLAG_COMMA_TERM_ALLOWED = 0x0002; -const unsigned EXPR_FLAG_RIGHTPAREN_TERM_ALLOWED = 0x0004; -const unsigned EXPR_FLAG_SINGLE_ELEMENT = 0x0008; -const unsigned EXPR_FLAG_ENDENUM_TERM_ALLOWED = 0x0010; -const unsigned EXPR_FLAG_RIGHTBRACE_TERM_ALLOWED = 0x0020; -const unsigned EXPR_FLAG_TO_TERM_ALLOWED = 0x0040; -const unsigned EXPR_FLAG_AUTO_TERM_ALLOWED = 0x0080; -const unsigned EXPR_FLAG_CONSUME_RESULT = 0x0100; -const unsigned EXPR_FLAG_DICTKEY_TERM_ALLOWED = 0x0200; - - -class Parser -{ -public: - Parser(); - virtual ~Parser() = default; - Parser& operator=( const Parser& ) { return *this; } - int quiet; - ParseError err; - - char ext_err[50]; - char buffer[51]; - - bool contains_tabs; - - void reinit( Expression& ex ); - - static void write_words( std::ostream& os ); - virtual int recognize_binary( Token& tok, const char* buf, const char** s ); - virtual int recognize_unary( Token& tok, const char* buf ); - virtual int recognize( Token& tok, const char* buf, const char** s ); - virtual bool recognize_reserved_word( Token& tok, const char* buf ); - - virtual int tryOperator( Token& tok, const char* buf, const char** s, Operator* opList, int n_ops, - char* opbuf ); - virtual int tryBinaryOperator( Token& tok, CompilerContext& ctx ); - virtual int tryUnaryOperator( Token& tok, CompilerContext& ctx ); - - virtual int tryNumeric( Token& tok, CompilerContext& ctx ); - virtual int tryLiteral( Token& tok, CompilerContext& ctx ); - - virtual int peekToken( const CompilerContext& ctx, Token& token, Expression* expr = nullptr ); - virtual int getToken( CompilerContext& ctx, Token& token, Expression* expr = nullptr ); - - - virtual int parseToken( CompilerContext& ctx, Expression& expr, Token* token ) = 0; - int IP( Expression& expr, char* s ); - - void setQuiet( int x ) { quiet = x; } -}; - - -class SmartParser : public Parser -{ -public: - virtual ~SmartParser() = default; - -protected: - virtual int tryLiteral( Token& tok, CompilerContext& ctx ) override; - - class ModuleFunction* modfunc_; - UserFunction* userfunc_; - -public: - SmartParser() : Parser(), modfunc_( nullptr ), userfunc_( nullptr ) {} - SmartParser& operator=( const SmartParser& ) { return *this; } - virtual int isLegal( Token& tok ); - virtual int isOkay( const Token& token, const Token& last_token ); - - virtual int isFunc( Token& tok, ModuleFunction** v ) = 0; - - virtual int isUserFunc( Token& tok, UserFunction** userfunc ); - - virtual int parseToken( CompilerContext& ctx, Expression& expr, Token* ) override; - virtual int getToken( CompilerContext& ctx, Token& token, Expression* expr = nullptr ) override; - int getTokenWithoutConversions( CompilerContext& ctx, Token& token ); - - bool callingMethod( CompilerContext& ctx ); - - virtual int getUserArgs( Expression& expr, CompilerContext& ctx, bool inject_jsr = true ) = 0; - virtual int getArrayElements( Expression& expr, CompilerContext& ctx ) = 0; - virtual int getNewArrayElements( Expression& expr, CompilerContext& ctx ) = 0; - virtual int getStructMembers( Expression& expr, CompilerContext& ctx ) = 0; - virtual int getDictionaryMembers( Expression& expr, CompilerContext& ctx ) = 0; - virtual int getMethodArguments( Expression& expr, CompilerContext& ctx, int& nargs ) = 0; - virtual int getFunctionPArgument( Expression& expr, CompilerContext& ctx, Token* tok ) = 0; - - int IIP( Expression& expr, CompilerContext& ctx, unsigned expr_flags ); -}; - -inline int SmartParser::isLegal( Token& ) -{ - return 1; -} -inline int SmartParser::isUserFunc( Token&, UserFunction** ) -{ - return 0; -} -} // namespace Bscript -} // namespace Pol -#endif diff --git a/pol-core/ecompile/ECompileMain.cpp b/pol-core/ecompile/ECompileMain.cpp index 7632558e4f..a2a95fec30 100644 --- a/pol-core/ecompile/ECompileMain.cpp +++ b/pol-core/ecompile/ECompileMain.cpp @@ -8,9 +8,8 @@ #include #include -#include "../bscript/compiler.h" +#include "../bscript/compctx.h" #include "../bscript/compiler/Compiler.h" -#include "../bscript/compiler/LegacyFunctionOrder.h" #include "../bscript/compiler/Profile.h" #include "../bscript/compiler/file/SourceFileCache.h" #include "../bscript/compilercfg.h" @@ -18,7 +17,6 @@ #include "../bscript/executor.h" #include "../bscript/executortype.h" #include "../bscript/filefmt.h" -#include "../bscript/parser.h" #include "../clib/Program/ProgramConfig.h" #include "../clib/Program/ProgramMain.h" #include "../clib/dirlist.h" @@ -89,7 +87,6 @@ void ECompileMain::showHelp() << " -T[N] use threaded compilation, force N threads to run\n" << " -vN verbosity level\n" << " -w display warnings\n" - << " -W generate wordfile\n" << " -y treat warnings as errors\n" << " -x write external .dbg file\n" << " -xt write external .dbg.txt info file\n" @@ -104,7 +101,6 @@ static char** s_argv; int debug = 0; bool quiet = false; -bool opt_generate_wordlist = false; bool keep_building = false; bool force_update = false; bool show_timing_details = false; @@ -137,13 +133,6 @@ struct Comparison Compiler::SourceFileCache em_parse_tree_cache( summary.profile ); Compiler::SourceFileCache inc_parse_tree_cache( summary.profile ); -void generate_wordlist() -{ - INFO_PRINT << "Writing word list to wordlist.txt\n"; - std::ofstream ofs( "wordlist.txt", std::ios::out | std::ios::trunc ); - Parser::write_words( ofs ); -} - std::unique_ptr create_compiler() { auto compiler = std::make_unique( em_parse_tree_cache, inc_parse_tree_cache, @@ -498,10 +487,6 @@ int readargs( int argc, char** argv ) compilercfg.DisplaySummary = true; break; - case 'W': - opt_generate_wordlist = true; - break; - case 'a': compilercfg.CompileAspPages = setting_value( arg ); break; @@ -995,11 +980,6 @@ int ECompileMain::main() INFO_PRINT << "EScript Compiler v" << vernum << "\n" << POL_COPYRIGHT << "\n\n"; } - if ( ECompile::opt_generate_wordlist ) - { - ECompile::generate_wordlist(); - return 0; - } int prog_res = 1; bool didanything = ECompile::run( s_argc, s_argv, &prog_res );