// runnerline_impl.cpp // Revision 9-feb-2005 #include "runnerline_impl.h" #include "error.h" #include "dynamic.h" #include "runner.h" #include "program.h" #include "directory.h" #include "sysvar.h" #include "graphics.h" #include "util.h" #include "using.h" #include "mbf.h" #include "regexp.h" #include "memory.h" #include "trace.h" #include #include #include #include #include #include #include #include #include #include // cmath is not used because in some platforms asinh and others // are declared only in math.h #include #if defined __unix__ || defined __linux__ // Using uname on unix, linux and cygwin. #include #endif #include #define ASSERT assert using std::cerr; using std::endl; using std::auto_ptr; using std::isalpha; namespace sysvar= blassic::sysvar; namespace onbreak= blassic::onbreak; using namespace blassic::file; using util::dim_array; using util::touch; namespace { #if defined __unix__ || defined __linux__ // Even in windows with cygwin. const char * os_family= "unix"; #else const char * os_family= "windows"; #endif // Workaround for a problem in some versions of gcc. // Do not define zero as const, that does not solve the problem. // But do not modify it! You have been warned! double zero= 0.0; #if 0 inline bool iscomp (BlCode code) { return code == '=' || code == keyDISTINCT || code == '<' || code == keyMINOREQUAL || code == '>' || code == keyGREATEREQUAL; } inline bool islogical2 (BlCode code) { return code == keyOR || code == keyAND || code == keyXOR; } #else // Testing as macro for speed. #define iscomp(code) \ ((code) == '=' || (code) == keyDISTINCT || \ (code) == '<' || (code) == keyMINOREQUAL || \ (code) == '>' || (code) == keyGREATEREQUAL) #define islogical2(code) ((code) == keyOR || (code) == keyAND || \ (code) == keyXOR) #endif #ifdef M_PIl const BlNumber value_pi= M_PIl; #else const BlNumber value_pi= 4.0 * atan (1); #endif const BlNumber value_pi_div_180= value_pi / 180.0; const BlNumber value_180_div_pi= 180.0 / value_pi; // ********** Hyperbolic trigonometric arc functions ********** namespace auxmath { #if HAVE_DECL_ASINH using ::asinh; #else double asinh (double x) { return log (x + sqrt (x * x + 1) ); } #endif #if HAVE_DECL_ACOSH using ::acosh; #else double acosh (double x) { errno= 0; double r= sqrt (x * x - 1); if (errno != 0) return 0; return log (x + r ); } #endif #if HAVE_DECL_ATANH using ::atanh; #else double atanh (double x) { return log ( (1 + x) / (1 - x) ) / 2; } #endif } // namespace auxmath // ***************** Auxiliary math functions ************* double auxFIX (double n) { double r; modf (n, & r); return r; } } // namespace RunnerLineImpl::RunnerLineImpl (Runner & runner, const CodeLine & newcodeline) : RunnerLine (runner, newcodeline), program (runner.getprogram () ), pdirectory (0), fInElse (false) { TRACEFUNC (tr, "RunnerLineImpl::RunnerLineImpl"); } RunnerLineImpl::RunnerLineImpl (Runner & runner) : RunnerLine (runner), program (runner.getprogram () ), fInElse (false) { TRACEFUNC (tr, "RunnerLineImpl::RunnerLineImpl"); } RunnerLineImpl::~RunnerLineImpl () { TRACEFUNC (tr, "RunnerLineImpl::~RunnerLineImpl"); } RunnerLine * newRunnerLine (Runner & runner, const CodeLine & newcodeline) { return new RunnerLineImpl (runner, newcodeline); } #ifdef ONE_TABLE #define insfunc_element(elem) \ {key##elem, \ { & RunnerLineImpl::do_##elem, \ & RunnerLineImpl::valsyntax_error} } #define insfunc_alias(elem,alias) \ {key##elem, \ { & RunnerLineImpl::do_##alias, \ & RunnerLineImpl::valsyntax_error} } #define valfunc_element(elem) \ {key##elem, \ { & RunnerLineImpl::syntax_error, \ & RunnerLineImpl::val_##elem} } #define valfunc_alias(elem,alias) \ {key##elem, \ { & RunnerLineImpl::syntax_error, \ & RunnerLineImpl::val_##alias} } #define mixfunc_element(elem) \ {key##elem, \ { & RunnerLineImpl::do_##elem, \ & RunnerLineImpl::val_##elem} } const RunnerLineImpl::tfunctions_t RunnerLineImpl::tfunctions []= { valfunc_element (OpenPar), insfunc_element (Colon), insfunc_element (END), insfunc_element (LIST), insfunc_element (REM), insfunc_element (LOAD), insfunc_element (SAVE), insfunc_element (NEW), insfunc_element (EXIT), insfunc_element (RUN), insfunc_element (PRINT), insfunc_element (FOR), insfunc_element (NEXT), insfunc_element (IF), insfunc_element (ELSE), insfunc_element (TRON), insfunc_element (TROFF), mixfunc_element (LET), insfunc_element (GOTO), insfunc_element (STOP), insfunc_element (CONT), insfunc_element (CLEAR), insfunc_element (GOSUB), insfunc_element (RETURN), insfunc_element (POKE), insfunc_element (DATA), insfunc_element (READ), insfunc_element (RESTORE), insfunc_element (INPUT), insfunc_element (LINE), insfunc_element (RANDOMIZE), insfunc_element (PLEASE), insfunc_element (AUTO), insfunc_element (DIM), insfunc_element (SYSTEM), insfunc_element (ON), insfunc_element (ERROR), insfunc_element (OPEN), insfunc_element (CLOSE), insfunc_element (LOCATE), insfunc_element (CLS), insfunc_element (WRITE), insfunc_element (MODE), insfunc_element (MOVE), insfunc_element (COLOR), insfunc_element (GET), mixfunc_element (LABEL), insfunc_element (DELIMITER), insfunc_element (REPEAT), insfunc_element (UNTIL), insfunc_element (WHILE), insfunc_element (WEND), insfunc_element (PLOT), insfunc_element (POPEN), insfunc_element (RESUME), insfunc_element (DELETE), insfunc_element (LOCAL), insfunc_element (PUT), insfunc_element (FIELD), insfunc_element (LSET), // Lset and rset use same function. insfunc_alias (RSET, LSET), insfunc_element (SOCKET), insfunc_element (DRAW), insfunc_element (DEF), mixfunc_element (FN), insfunc_element (ERASE), insfunc_element (SWAP), insfunc_element (SYMBOL), insfunc_element (ZONE), insfunc_element (POP), insfunc_element (NAME), insfunc_element (KILL), insfunc_element (FILES), insfunc_element (PAPER), insfunc_element (PEN), insfunc_element (SHELL), insfunc_element (MERGE), insfunc_element (CHDIR), insfunc_element (MKDIR), insfunc_element (RMDIR), insfunc_element (SYNCHRONIZE), insfunc_element (PAUSE), insfunc_element (CHAIN), insfunc_element (ENVIRON), insfunc_element (EDIT), insfunc_element (DRAWR), insfunc_element (PLOTR), insfunc_element (MOVER), insfunc_element (POKE16), insfunc_element (POKE32), insfunc_element (RENUM), insfunc_element (CIRCLE), insfunc_element (MASK), insfunc_element (WINDOW), insfunc_element (GRAPHICS), insfunc_element (BEEP), insfunc_element (DEFINT), // DEFINT, DEFSTR, DEFREAL, DEFSNG and DEFDBL use same function. insfunc_alias (DEFSTR, DEFINT), insfunc_alias (DEFREAL, DEFINT), insfunc_alias (DEFSNG, DEFINT), insfunc_alias (DEFDBL, DEFINT), insfunc_element (INK), insfunc_element (SET_TITLE), insfunc_element (TAG), // TAG and TAGOFF use same function. insfunc_alias (TAGOFF, TAG), insfunc_element (ORIGIN), insfunc_element (DEG), // DEG and RAD use same function. insfunc_alias (RAD, DEG), insfunc_element (INVERSE), insfunc_element (IF_DEBUG), // LPRINT and PRINT use same function. insfunc_alias (LPRINT, PRINT), insfunc_element (LLIST), insfunc_element (WIDTH), insfunc_element (BRIGHT), insfunc_element (DRAWARC), insfunc_element (PULL), insfunc_element (PAINT), insfunc_element (FREE_MEMORY), insfunc_element (SCROLL), insfunc_element (ZX_PLOT), insfunc_element (ZX_UNPLOT), mixfunc_element (MID_S), valfunc_element (LEFT_S), valfunc_element (RIGHT_S), valfunc_element (CHR_S), valfunc_element (ENVIRON_S), valfunc_element (STRING_S), valfunc_element (OSFAMILY_S), valfunc_element (HEX_S), valfunc_element (SPACE_S), valfunc_element (UPPER_S), valfunc_element (LOWER_S), valfunc_element (STR_S), valfunc_element (OCT_S), valfunc_element (BIN_S), valfunc_element (INKEY_S), mixfunc_element (PROGRAMARG_S), valfunc_element (DATE_S), valfunc_element (TIME_S), valfunc_element (INPUT_S), valfunc_element (MKI_S), valfunc_element (MKS_S), valfunc_element (MKD_S), valfunc_element (MKL_S), valfunc_element (TRIM_S), valfunc_element (LTRIM_S), valfunc_element (RTRIM_S), valfunc_element (OSNAME_S), valfunc_element (FINDFIRST_S), valfunc_element (FINDNEXT_S), valfunc_element (COPYCHR_S), valfunc_element (STRERR_S), valfunc_element (DEC_S), valfunc_element (VAL_S), valfunc_element (SCREEN_S), valfunc_element (MKSMBF_S), valfunc_element (MKDMBF_S), valfunc_element (REGEXP_REPLACE_S), // UCASE$ and LCASE$ are alias for UPPER$ and LOWER$ valfunc_alias (UCASE_S, UPPER_S), valfunc_alias (LCASE_S, LOWER_S), valfunc_element (ASC), valfunc_element (LEN), valfunc_element (PEEK), valfunc_element (PROGRAMPTR), valfunc_element (RND), valfunc_element (INT), valfunc_element (SIN), valfunc_element (COS), valfunc_element (PI), valfunc_element (TAN), valfunc_element (SQR), valfunc_element (ASIN), valfunc_element (ACOS), valfunc_element (INSTR), valfunc_element (ATAN), valfunc_element (ABS), valfunc_element (USR), valfunc_element (VAL), valfunc_element (EOF), valfunc_element (VARPTR), valfunc_element (SYSVARPTR), valfunc_element (SGN), valfunc_element (LOG), valfunc_element (LOG10), valfunc_element (EXP), valfunc_element (TIME), valfunc_element (ERR), valfunc_element (ERL), valfunc_element (CVI), valfunc_element (CVS), valfunc_element (CVD), valfunc_element (CVL), valfunc_element (MIN), valfunc_element (MAX), valfunc_element (CINT), valfunc_element (FIX), valfunc_element (XMOUSE), valfunc_element (YMOUSE), valfunc_element (XPOS), valfunc_element (YPOS), valfunc_element (PEEK16), valfunc_element (PEEK32), valfunc_element (RINSTR), valfunc_element (FIND_FIRST_OF), valfunc_element (FIND_LAST_OF), valfunc_element (FIND_FIRST_NOT_OF), valfunc_element (FIND_LAST_NOT_OF), valfunc_element (SINH), valfunc_element (COSH), valfunc_element (TANH), valfunc_element (ASINH), valfunc_element (ACOSH), valfunc_element (ATANH), valfunc_element (ATAN2), valfunc_element (TEST), valfunc_element (TESTR), valfunc_element (POS), valfunc_element (VPOS), valfunc_element (LOF), valfunc_element (FREEFILE), valfunc_element (INKEY), valfunc_element (ROUND), valfunc_element (CVSMBF), valfunc_element (CVDMBF), valfunc_element (REGEXP_INSTR), valfunc_element (ALLOC_MEMORY), valfunc_element (LOC), mixfunc_element (IDENTIFIER), mixfunc_element (NUMBER), valfunc_element (STRING), mixfunc_element (INTEGER), insfunc_element (ENDLINE), // Table used ends here. // Serch returning end to indicate fail point // to the next entry. {0, { & RunnerLineImpl::syntax_error, & RunnerLineImpl::valsyntax_error } }, }; const RunnerLineImpl::tfunctions_t * RunnerLineImpl::tfunctionsend= tfunctions + dim_array (tfunctions) - 1; #else // No ONE_TABLE #ifndef INSTRUCTION_SWITCH #define tfunc_t_element(elem) {key##elem, & RunnerLineImpl::do_##elem} const RunnerLineImpl::tfunc_t RunnerLineImpl::tfunc []= { tfunc_t_element (Colon), tfunc_t_element (END), tfunc_t_element (LIST), tfunc_t_element (REM), tfunc_t_element (LOAD), tfunc_t_element (SAVE), tfunc_t_element (NEW), tfunc_t_element (EXIT), tfunc_t_element (RUN), tfunc_t_element (PRINT), tfunc_t_element (FOR), tfunc_t_element (NEXT), tfunc_t_element (IF), tfunc_t_element (ELSE), tfunc_t_element (TRON), tfunc_t_element (TROFF), tfunc_t_element (LET), tfunc_t_element (GOTO), tfunc_t_element (STOP), tfunc_t_element (CONT), tfunc_t_element (CLEAR), tfunc_t_element (GOSUB), tfunc_t_element (RETURN), tfunc_t_element (POKE), tfunc_t_element (DATA), tfunc_t_element (READ), tfunc_t_element (RESTORE), tfunc_t_element (INPUT), tfunc_t_element (LINE), tfunc_t_element (RANDOMIZE), tfunc_t_element (PLEASE), tfunc_t_element (AUTO), tfunc_t_element (DIM), tfunc_t_element (SYSTEM), tfunc_t_element (ON), tfunc_t_element (ERROR), tfunc_t_element (OPEN), tfunc_t_element (CLOSE), tfunc_t_element (LOCATE), tfunc_t_element (CLS), tfunc_t_element (WRITE), tfunc_t_element (MODE), tfunc_t_element (MOVE), tfunc_t_element (COLOR), tfunc_t_element (GET), tfunc_t_element (LABEL), tfunc_t_element (DELIMITER), tfunc_t_element (REPEAT), tfunc_t_element (UNTIL), tfunc_t_element (WHILE), tfunc_t_element (WEND), tfunc_t_element (PLOT), tfunc_t_element (POPEN), tfunc_t_element (RESUME), tfunc_t_element (DELETE), tfunc_t_element (LOCAL), tfunc_t_element (PUT), tfunc_t_element (FIELD), tfunc_t_element (LSET), // Lset and rset use same function. {keyRSET, & RunnerLineImpl::do_LSET}, tfunc_t_element (SOCKET), tfunc_t_element (DRAW), tfunc_t_element (DEF), tfunc_t_element (FN), tfunc_t_element (ERASE), tfunc_t_element (SWAP), tfunc_t_element (SYMBOL), tfunc_t_element (ZONE), tfunc_t_element (POP), tfunc_t_element (NAME), tfunc_t_element (KILL), tfunc_t_element (FILES), tfunc_t_element (PAPER), tfunc_t_element (PEN), tfunc_t_element (SHELL), tfunc_t_element (MERGE), tfunc_t_element (CHDIR), tfunc_t_element (MKDIR), tfunc_t_element (RMDIR), tfunc_t_element (SYNCHRONIZE), tfunc_t_element (PAUSE), tfunc_t_element (CHAIN), tfunc_t_element (ENVIRON), tfunc_t_element (EDIT), tfunc_t_element (DRAWR), tfunc_t_element (PLOTR), tfunc_t_element (MOVER), tfunc_t_element (POKE16), tfunc_t_element (POKE32), tfunc_t_element (RENUM), tfunc_t_element (CIRCLE), tfunc_t_element (MASK), tfunc_t_element (WINDOW), tfunc_t_element (GRAPHICS), tfunc_t_element (BEEP), tfunc_t_element (DEFINT), // DEFINT, DEFSTR, DEFREAL, DEFSNG and DEFDBL use same function. {keyDEFSTR, & RunnerLineImpl::do_DEFINT}, {keyDEFREAL, & RunnerLineImpl::do_DEFINT}, {keyDEFSNG, & RunnerLineImpl::do_DEFINT}, {keyDEFDBL, & RunnerLineImpl::do_DEFINT}, tfunc_t_element (INK), tfunc_t_element (SET_TITLE), tfunc_t_element (TAG), // TAG and TAGOFF use same function. {keyTAGOFF, & RunnerLineImpl::do_TAG}, tfunc_t_element (ORIGIN), tfunc_t_element (DEG), // DEG and RAD use same function. {keyRAD, & RunnerLineImpl::do_DEG}, tfunc_t_element (INVERSE), tfunc_t_element (IF_DEBUG), // LPRINT and PRINT use same function. {keyLPRINT, & RunnerLineImpl::do_PRINT}, tfunc_t_element (LLIST), tfunc_t_element (WIDTH), tfunc_t_element (BRIGHT), tfunc_t_element (DRAWARC), tfunc_t_element (PULL), tfunc_t_element (PAINT), tfunc_t_element (FREE_MEMORY), tfunc_t_element (SCROLL), tfunc_t_element (ZX_PLOT), tfunc_t_element (ZX_UNPLOT), tfunc_t_element (MID_S), tfunc_t_element (PROGRAMARG_S), {keyIDENTIFIER, & RunnerLineImpl::do_IDENTIFIER}, tfunc_t_element (NUMBER), {keyINTEGER, & RunnerLineImpl::do_NUMBER}, tfunc_t_element (ENDLINE), // Table used ends here. // Serch returning end to indicate fail point // to the next entry. {0, & RunnerLineImpl::syntax_error }, }; const RunnerLineImpl::tfunc_t * RunnerLineImpl::tfuncend= tfunc + dim_array (tfunc) - 1; #endif // INSTRUCTION_SWITCH #ifndef OPERATION_SWITCH #define valfunction_t_element(elem) {key##elem, & RunnerLineImpl::val_##elem} const RunnerLineImpl::valfunction_t RunnerLineImpl::valfunction []= { valfunction_t_element (OpenPar), valfunction_t_element (LET), valfunction_t_element (LABEL), valfunction_t_element (FN), valfunction_t_element (MID_S), valfunction_t_element (LEFT_S), valfunction_t_element (RIGHT_S), valfunction_t_element (CHR_S), valfunction_t_element (ENVIRON_S), valfunction_t_element (STRING_S), valfunction_t_element (OSFAMILY_S), valfunction_t_element (HEX_S), valfunction_t_element (SPACE_S), valfunction_t_element (UPPER_S), valfunction_t_element (LOWER_S), valfunction_t_element (STR_S), valfunction_t_element (OCT_S), valfunction_t_element (BIN_S), valfunction_t_element (INKEY_S), valfunction_t_element (PROGRAMARG_S), valfunction_t_element (DATE_S), valfunction_t_element (TIME_S), valfunction_t_element (INPUT_S), valfunction_t_element (MKI_S), valfunction_t_element (MKS_S), valfunction_t_element (MKD_S), valfunction_t_element (MKL_S), valfunction_t_element (TRIM_S), valfunction_t_element (LTRIM_S), valfunction_t_element (RTRIM_S), valfunction_t_element (OSNAME_S), valfunction_t_element (FINDFIRST_S), valfunction_t_element (FINDNEXT_S), valfunction_t_element (COPYCHR_S), valfunction_t_element (STRERR_S), valfunction_t_element (DEC_S), valfunction_t_element (VAL_S), valfunction_t_element (SCREEN_S), valfunction_t_element (MKSMBF_S), valfunction_t_element (MKDMBF_S), valfunction_t_element (REGEXP_REPLACE_S), // UCASE$ and LCASE$ are alias for UPPER$ and LOWER$ { keyUCASE_S, & RunnerLineImpl::val_UPPER_S}, { keyLCASE_S, & RunnerLineImpl::val_LOWER_S}, valfunction_t_element (ASC), valfunction_t_element (LEN), valfunction_t_element (PEEK), valfunction_t_element (PROGRAMPTR), valfunction_t_element (RND), valfunction_t_element (INT), valfunction_t_element (SIN), valfunction_t_element (COS), valfunction_t_element (PI), valfunction_t_element (TAN), valfunction_t_element (SQR), valfunction_t_element (ASIN), valfunction_t_element (ACOS), valfunction_t_element (INSTR), valfunction_t_element (ATAN), valfunction_t_element (ABS), valfunction_t_element (USR), valfunction_t_element (VAL), valfunction_t_element (EOF), valfunction_t_element (VARPTR), valfunction_t_element (SYSVARPTR), valfunction_t_element (SGN), valfunction_t_element (LOG), valfunction_t_element (LOG10), valfunction_t_element (EXP), valfunction_t_element (TIME), valfunction_t_element (ERR), valfunction_t_element (ERL), valfunction_t_element (CVI), valfunction_t_element (CVS), valfunction_t_element (CVD), valfunction_t_element (CVL), valfunction_t_element (MIN), valfunction_t_element (MAX), valfunction_t_element (CINT), valfunction_t_element (FIX), valfunction_t_element (XMOUSE), valfunction_t_element (YMOUSE), valfunction_t_element (XPOS), valfunction_t_element (YPOS), valfunction_t_element (PEEK16), valfunction_t_element (PEEK32), valfunction_t_element (RINSTR), valfunction_t_element (FIND_FIRST_OF), valfunction_t_element (FIND_LAST_OF), valfunction_t_element (FIND_FIRST_NOT_OF), valfunction_t_element (FIND_LAST_NOT_OF), valfunction_t_element (SINH), valfunction_t_element (COSH), valfunction_t_element (TANH), valfunction_t_element (ASINH), valfunction_t_element (ACOSH), valfunction_t_element (ATANH), valfunction_t_element (ATAN2), valfunction_t_element (TEST), valfunction_t_element (TESTR), valfunction_t_element (POS), valfunction_t_element (VPOS), valfunction_t_element (LOF), valfunction_t_element (FREEFILE), valfunction_t_element (INKEY), valfunction_t_element (ROUND), valfunction_t_element (CVSMBF), valfunction_t_element (CVDMBF), valfunction_t_element (REGEXP_INSTR), valfunction_t_element (ALLOC_MEMORY), valfunction_t_element (LOC), valfunction_t_element (IDENTIFIER), valfunction_t_element (NUMBER), valfunction_t_element (STRING), valfunction_t_element (INTEGER), // Table used ends here. // Serch returning end to indicate fail point // to the next entry. {0, & RunnerLineImpl::valsyntax_error }, }; const RunnerLineImpl::valfunction_t * RunnerLineImpl::valfunctionend= valfunction + dim_array (valfunction) - 1; #endif // OPERATION_SWITCH #endif // ONE_TABLE #ifndef NDEBUG bool RunnerLineImpl::checktfunc () { #ifdef ONE_TABLE for (size_t i= 1; i < dim_array (tfunctions) - 1; ++i) { if (tfunctions [i - 1].code >= tfunctions [i].code) { cerr << "Failed check of tfunctions in " << i << endl; abort (); } } #else // No ONE_TABLE #ifndef INSTRUCTION_SWITCH for (size_t i= 1; i < dim_array (tfunc) - 1; ++i) { if (tfunc [i - 1].code >= tfunc [i].code) { cerr << "Failed check of tfunc in " << i << endl; abort (); } } #endif // INSTRUCTION_SWITCH #ifndef OPERATION_SWITCH for (size_t i= 1; i < dim_array (valfunction) - 1; ++i) { if (valfunction [i - 1].code >= valfunction [i].code) { cerr << "Failed check on valfunction in " << i << endl; abort (); } } #endif // OPERATION_SWITCH #endif // ONE_TABLE return true; } const bool RunnerLineImpl::tfuncchecked= checktfunc (); #endif #ifdef BRUTAL_MODE #ifdef ONE_TABLE RunnerLineImpl::functions_t RunnerLineImpl::array_functions [keyMAX_CODE_USED + 1]; #else RunnerLineImpl::do_func RunnerLineImpl::array_func [keyMAX_CODE_USED + 1]; RunnerLineImpl::do_valfunction RunnerLineImpl::array_valfunction [keyMAX_CODE_USED + 1]; #endif bool RunnerLineImpl::init_array_func () { #ifdef ONE_TABLE // This intializer generates an internal error in some // old versions of gcc. //functions_t emptyfunc= { & RunnerLineImpl::syntax_error, // & RunnerLineImpl::valsyntax_error }; functions_t emptyfunc; emptyfunc.inst_func= & RunnerLineImpl::syntax_error; emptyfunc.val_func= & RunnerLineImpl::valsyntax_error; std::fill (array_functions, array_functions + dim_array (array_functions), emptyfunc); for (const tfunctions_t * p= tfunctions; p != tfunctionsend; ++p) array_functions [p->code]= p->f; #else std::fill (array_func, array_func + dim_array (array_func), & RunnerLineImpl::syntax_error); for (const tfunc_t * p= tfunc; p != tfuncend; ++p) array_func [p->code]= p->f; std::fill (array_valfunction, array_valfunction + dim_array (array_valfunction), & RunnerLineImpl::valsyntax_error); for (const valfunction_t * p= valfunction; p != valfunctionend; ++p) array_valfunction [p->code]= p->f; #endif return true; } bool RunnerLineImpl::array_func_inited= init_array_func (); #endif #ifdef BRUTAL_MODE #ifdef ONE_TABLE RunnerLineImpl::do_func RunnerLineImpl::findfunc (BlCode code) { return array_functions [code].inst_func; } RunnerLineImpl::do_valfunction RunnerLineImpl::findvalfunc (BlCode code) { return array_functions [code].val_func; } #else // No ONE_TABLE RunnerLineImpl::do_func RunnerLineImpl::findfunc (BlCode code) { return array_func [code]; } RunnerLineImpl::do_valfunction RunnerLineImpl::findvalfunc (BlCode code) { return array_valfunction [code]; } #endif #else // No BRUTAL_MODE #ifdef ONE_TABLE RunnerLineImpl::do_func RunnerLineImpl::findfunc (BlCode code) { using std::lower_bound; const tfunctions_t tfs= {code, {NULL, NULL} }; const tfunctions_t * ptf= lower_bound (tfunctions, tfunctionsend, tfs); if (ptf->code != code) return & RunnerLineImpl::syntax_error; else return ptf->f.inst_func; } RunnerLineImpl::do_valfunction RunnerLineImpl::findvalfunc (BlCode code) { using std::lower_bound; const tfunctions_t tfs= {code, {NULL, NULL} }; const tfunctions_t * ptf= lower_bound (tfunctions, tfunctionsend, tfs); if (ptf->code != code) return & RunnerLineImpl::valsyntax_error; else return ptf->f.val_func; } #else // No ONE_TABLE #ifndef INSTRUCTION_SWITCH RunnerLineImpl::do_func RunnerLineImpl::findfunc (BlCode code) { using std::lower_bound; const tfunc_t tfs= {code, NULL}; // C++ Builder can't deduce the lower_bound template argments, // I don't know why. const tfunc_t * ptf= lower_bound (tfunc, tfuncend, tfs); if (ptf->code != code) return & RunnerLineImpl::syntax_error; else return ptf->f; } #endif // INSTRUCTION_SWITCH #ifndef OPERATION_SWITCH RunnerLineImpl::do_valfunction RunnerLineImpl::findvalfunc (BlCode code) { using std::lower_bound; const valfunction_t vfs= { code, NULL}; const valfunction_t * pvf= lower_bound (valfunction, valfunctionend, vfs); if (pvf->code != code) return & RunnerLineImpl::valsyntax_error; else return pvf->f; } #endif // OPERATION_SWITCH #endif // ONE_TABLE #endif // BRUTAL_MODE #if 0 // Use macros instead to avoid strange errors on hp-ux. void RunnerLineImpl::requiretoken (BlCode code) const throw (BlErrNo) { if (token.code != code) throw ErrSyntax; } void RunnerLineImpl::expecttoken (BlCode code) throw (BlErrNo) { gettoken (); requiretoken (code); } #else #define requiretoken(c) if (token.code == c) ; else throw ErrSyntax #define expecttoken(c) \ do { \ gettoken (); \ if (token.code != c) throw ErrSyntax; \ } while (0) #endif void RunnerLineImpl::getnextchunk () { while (! endsentence () ) gettoken (); if (token.code != keyENDLINE) gettoken (); } BlFile & RunnerLineImpl::getfile (BlChannel channel) const { return runner.getfile (channel); } BlFile & RunnerLineImpl::getfile0 () const { return runner.getfile0 (); } BlNumber RunnerLineImpl::evalnum () { BlResult result; eval (result); return result.number (); } BlNumber RunnerLineImpl::expectnum () { gettoken (); return evalnum (); } BlInteger RunnerLineImpl::evalinteger () { BlResult result; eval (result); return result.integer (); } BlInteger RunnerLineImpl::expectinteger () { gettoken (); return evalinteger (); } std::string RunnerLineImpl::evalstring () { BlResult result; eval (result); return result.str (); } std::string RunnerLineImpl::expectstring () { gettoken (); return evalstring (); } BlChannel RunnerLineImpl::evalchannel () { BlResult result; eval (result); return util::checked_cast (result.integer (), ErrMismatch); } BlChannel RunnerLineImpl::expectchannel () { gettoken (); return evalchannel (); } BlChannel RunnerLineImpl::evaloptionalchannel (BlChannel defchan) { if (token.code == keySharp) { gettoken (); BlInteger n= evalinteger (); return util::checked_cast (n, ErrMismatch); } else return defchan; } BlChannel RunnerLineImpl::expectoptionalchannel (BlChannel defchan) { gettoken (); return evaloptionalchannel (defchan); } BlChannel RunnerLineImpl::evalrequiredchannel () { if (token.code == keySharp) gettoken (); BlInteger n= evalinteger (); return util::checked_cast (n, ErrMismatch); } BlChannel RunnerLineImpl::expectrequiredchannel () { gettoken (); return evalrequiredchannel (); } std::string::size_type RunnerLineImpl::evalstringindex () { BlInteger n= evalinteger (); if (n < 1) throw ErrBadSubscript; return static_cast (n); } void RunnerLineImpl::evalstringslice (const std::string & str, std::string::size_type & from, std::string::size_type & to) { const std::string::size_type limit= str.size (); from= 0; to= limit; gettoken (); if (token.code != ']') { if (token.code != keyTO) { from= evalstringindex () - 1; if (token.code == keyTO) { gettoken (); if (token.code != ']') to= evalstringindex (); } else to= from + 1; } else { gettoken (); if (token.code != ']') to= evalstringindex (); } requiretoken (']'); } gettoken (); if (from < to) { if (from >= limit || to > limit) throw ErrBadSubscript; } } void RunnerLineImpl::assignslice (VarPointer & vp, const BlResult & result) { ASSERT (vp.type == VarStringSlice); using std::string; string & str= * vp.pstring; string value= result.str (); if (vp.from >= vp.to) return; const string::size_type l= vp.to - vp.from; const string::size_type vsize= value.size (); if (l < vsize) value.erase (l); else if (l > vsize) value+= string (l - vsize, ' '); str.replace (vp.from, l, value); } VarPointer RunnerLineImpl::evalvarpointer () { requiretoken (keyIDENTIFIER); std::string varname= token.str; gettoken (); Dimension d; bool isarray= false; bool isslice= false; VarPointer vp; switch (token.code) { case '(': d= getdims (); isarray= true; break; case '[': { if (typeofvar (varname) != VarString) throw ErrMismatch; vp.type= VarStringSlice; vp.pstring= addrvarstring (varname); evalstringslice (* vp.pstring, vp.from, vp.to); } isslice= true; break; default: break; } if (! isslice) vp.type= typeofvar (varname); switch (vp.type) { case VarNumber: vp.pnumber= isarray ? addrdimnumber (varname, d) : addrvarnumber (varname); break; case VarInteger: vp.pinteger= isarray ? addrdiminteger (varname, d) : addrvarinteger (varname); break; case VarString: vp.pstring= isarray ? addrdimstring (varname, d) : addrvarstring (varname); break; case VarStringSlice: break; default: throw ErrBlassicInternal; } return vp; } void RunnerLineImpl::evalmultivarpointer (ListVarPointer & lvp) { for (;;) { VarPointer vp= evalvarpointer (); lvp.push_back (vp); if (token.code != ',') break; gettoken (); } } VarPointer RunnerLineImpl::eval_let () { VarPointer vp= evalvarpointer (); requiretoken ('='); BlResult result; expect (result); switch (vp.type) { case VarNumber: * vp.pnumber= result.number (); break; case VarInteger: * vp.pinteger= result.integer (); break; case VarString: * vp.pstring= result.str (); break; case VarStringSlice: assignslice (vp, result); break; default: throw ErrBlassicInternal; } return vp; } void RunnerLineImpl::parenarg (BlResult & result) { expect (result); requiretoken (')'); gettoken (); } void RunnerLineImpl::getparenarg (BlResult & result) { expecttoken ('('); expect (result); requiretoken (')'); gettoken (); } void RunnerLineImpl::getparenarg (BlResult & result, BlResult & result2) { expecttoken ('('); expect (result); requiretoken (','); expect (result2); requiretoken (')'); gettoken (); } BlFile & RunnerLineImpl::getparenfile () { expecttoken ('('); //expecttoken ('#'); //BlChannel c= expectchannel (); BlChannel c= expectrequiredchannel (); requiretoken (')'); gettoken (); return getfile (c); } namespace { inline void checkfinite (double n) { // Some errors do not set errno, this check can // detect some. #if (defined __unix__ && ! defined __hpux__) \ || defined __linux__ if (! finite (n) ) throw ErrDomain; #else touch (n); #endif } inline double callnumericfunc (double (* f) (double), double n) { errno= 0; n= f (n); switch (errno) { case 0: checkfinite (n); break; case EDOM: throw ErrDomain; case ERANGE: throw ErrRange; default: if (showdebuginfo () ) cerr << "Math error, errno= " << errno << endl; throw ErrBlassicInternal; } return n; } inline double callnumericfunc (double (* f) (double, double), double n, double n2) { errno= 0; n= f (n, n2); switch (errno) { case 0: checkfinite (n); break; case EDOM: throw ErrDomain; case ERANGE: throw ErrRange; default: if (showdebuginfo () ) cerr << "Math error, errno= " << errno << endl; throw ErrBlassicInternal; } return n; } } // namespace void RunnerLineImpl::valnumericfunc (double (* f) (double), BlResult & result) { //getparenarg (result); gettoken (); //valparen (result); valbase (result); BlNumber n= result.number (); result= callnumericfunc (f, n); } void RunnerLineImpl::valnumericfunc2 (double (* f) (double, double), BlResult & result) { BlResult result2; getparenarg (result, result2); BlNumber n= result.number (); BlNumber n2= result2.number (); result= callnumericfunc (f, n, n2); } void RunnerLineImpl::valtrigonometricfunc (double (* f) (double), BlResult & result) { //getparenarg (result); gettoken (); //valparen (result); valbase (result); BlNumber n= result.number (); switch (runner.trigonometric_mode () ) { case TrigonometricDeg: n*= value_pi_div_180; break; case TrigonometricRad: break; } result= callnumericfunc (f, n); } void RunnerLineImpl::valtrigonometricinvfunc (double (* f) (double), BlResult & result) { getparenarg (result); BlNumber n= result.number (); n= callnumericfunc (f, n); // Provisional switch (runner.trigonometric_mode () ) { case TrigonometricDeg: n*= value_180_div_pi; break; case TrigonometricRad: break; } result= n; } void RunnerLineImpl::val_ASC (BlResult & result) { //getparenarg (result); gettoken (); //valparen (result); valbase (result); const std::string & str= result.str (); if (str.empty () ) result= 0L; else result= BlInteger ( (unsigned char) str [0] ); } void RunnerLineImpl::val_LEN (BlResult & result) { //getparenarg (result); gettoken (); //valparen (result); valbase (result); result= result.str ().size (); } void RunnerLineImpl::val_PEEK (BlResult & result) { //getparenarg (result); gettoken (); //valparen (result); valbase (result); BlChar * addr= (BlChar *) size_t (result.number () ); //result= BlNumber (size_t (* addr) ); result= BlInteger (size_t (* addr) ); } void RunnerLineImpl::val_PEEK16 (BlResult & result) { getparenarg (result); BlChar * addr= (BlChar *) size_t (result.number () ); result= peek16 (addr); } void RunnerLineImpl::val_PEEK32 (BlResult & result) { getparenarg (result); BlChar * addr= (BlChar *) size_t (result.number () ); //result= BlNumber (static_cast (peek32 (addr) ) ); result= peek32 (addr); } void RunnerLineImpl::val_PROGRAMPTR (BlResult & result) { gettoken (); //result= BlNumber (size_t (program.programptr () ) ); result= static_cast (size_t (program.programptr () ) ); } void RunnerLineImpl::val_SYSVARPTR (BlResult & result) { gettoken (); result= sysvar::address (); } void RunnerLineImpl::val_RND (BlResult & result) { BlNumber n; gettoken (); if (token.code == '(') { parenarg (result); n= result.number (); } else n= 1; static BlNumber previous= 0; if (n == 0) { result= previous; return; } if (n < 0) //srand (time (0) ); runner.seedrandom (time (0) ); BlNumber r= runner.getrandom (); result= r; previous= r; } void RunnerLineImpl::val_INT (BlResult & result) { valnumericfunc (floor, result); } void RunnerLineImpl::val_SIN (BlResult & result) { valtrigonometricfunc (sin, result); } void RunnerLineImpl::val_COS (BlResult & result) { valtrigonometricfunc (cos, result); } void RunnerLineImpl::val_PI (BlResult & result) { result= value_pi; gettoken (); } void RunnerLineImpl::val_TAN (BlResult & result) { valtrigonometricfunc (tan, result); } void RunnerLineImpl::val_SQR (BlResult & result) { valnumericfunc (sqrt, result); } void RunnerLineImpl::val_ASIN (BlResult & result) { valtrigonometricinvfunc (asin, result); } void RunnerLineImpl::val_ACOS (BlResult & result) { valtrigonometricinvfunc (acos, result); } void RunnerLineImpl::val_ATAN (BlResult & result) { valtrigonometricinvfunc (atan, result); } void RunnerLineImpl::val_ABS (BlResult & result) { valnumericfunc (fabs, result); } void RunnerLineImpl::val_LOG (BlResult & result) { valnumericfunc (log, result); } void RunnerLineImpl::val_LOG10 (BlResult & result) { valnumericfunc (log10, result); } void RunnerLineImpl::val_EXP (BlResult & result) { valnumericfunc (exp, result); } void RunnerLineImpl::val_TIME (BlResult & result) { result= BlInteger (time (NULL) ); gettoken (); } void RunnerLineImpl::val_ERR (BlResult & result) { result= static_cast (runner.geterr () ); gettoken (); } void RunnerLineImpl::val_ERL (BlResult & result) { result= static_cast (runner.geterrline () ); gettoken (); } void RunnerLineImpl::val_FIX (BlResult & result) { valnumericfunc (auxFIX, result); } void RunnerLineImpl::val_XMOUSE (BlResult & result) { result= static_cast (graphics::xmouse () ); gettoken (); } void RunnerLineImpl::val_YMOUSE (BlResult & result) { result= static_cast (graphics::ymouse () ); gettoken (); } void RunnerLineImpl::val_XPOS (BlResult & result) { result= graphics::xpos (); gettoken (); } void RunnerLineImpl::val_YPOS (BlResult & result) { result= graphics::ypos (); gettoken (); } void RunnerLineImpl::val_SINH (BlResult & result) { valnumericfunc (sinh, result); } void RunnerLineImpl::val_COSH (BlResult & result) { valnumericfunc (cosh, result); } void RunnerLineImpl::val_TANH (BlResult & result) { valnumericfunc (tanh, result); } void RunnerLineImpl::val_ASINH (BlResult & result) { valnumericfunc (auxmath::asinh, result); } void RunnerLineImpl::val_ACOSH (BlResult & result) { valnumericfunc (auxmath::acosh, result); } void RunnerLineImpl::val_ATANH (BlResult & result) { valnumericfunc (auxmath::atanh, result); } void RunnerLineImpl::val_ATAN2 (BlResult & result) { valnumericfunc2 (atan2, result); } namespace { // Flags to valinstr calls. const bool instr_direct= false; const bool instr_reverse= true; } // namespace void RunnerLineImpl::valinstrbase (BlResult & result, bool reverse) { expecttoken ('('); std::string str; std::string::size_type init= reverse ? std::string::npos : 0; expect (result); switch (result.type () ) { case VarString: str= result.str (); break; case VarNumber: #if 0 init= std::string::size_type (result.number () ); if (init > 0) --init; requiretoken (','); str= expectstring (); break; #endif case VarInteger: init= result.integer (); //if (init > 0) // --init; if (init < 1) throw ErrFunctionCall; --init; requiretoken (','); str= expectstring (); break; default: throw ErrBlassicInternal; } requiretoken (','); std::string tofind= expectstring (); requiretoken (')'); gettoken (); std::string::size_type pos; if (tofind.empty () ) { if (str.empty () ) pos= 0; else if (init < str.size () ) pos= init + 1; else pos= 0; } else { pos= reverse ? str.rfind (tofind, init) : str.find (tofind, init); if (pos == std::string::npos) pos= 0; else ++pos; } result= BlInteger (pos); } void RunnerLineImpl::val_INSTR (BlResult & result) { valinstrbase (result, instr_direct); } void RunnerLineImpl::val_RINSTR (BlResult & result) { valinstrbase (result, instr_reverse); } namespace { // Flags to valfindfirstlast calls. const bool find_first= true; const bool find_last= false; const bool find_yes= true; const bool find_not= false; } // namespace void RunnerLineImpl::valfindfirstlast (BlResult & result, bool first, bool yesno) { expecttoken ('('); std::string str; std::string::size_type init= first ? 0 : std::string::npos; expect (result); switch (result.type () ) { case VarString: str= result.str (); break; case VarNumber: init= std::string::size_type (result.number () ); if (init > 0) --init; requiretoken (','); str= expectstring (); break; case VarInteger: init= result.integer (); if (init > 0) --init; requiretoken (','); str= expectstring (); break; default: throw ErrBlassicInternal; } requiretoken (','); std::string tofind= expectstring (); requiretoken (')'); gettoken (); std::string::size_type pos; if (tofind.empty () ) { if (str.empty () ) pos= 0; else if (init < str.size () ) pos= init + 1; else pos= 0; } else { pos= first ? ( yesno ? str.find_first_of (tofind, init) : str.find_first_not_of (tofind, init) ) : (yesno ? str.find_last_of (tofind, init) : str.find_last_not_of (tofind, init) ); if (pos == std::string::npos) pos= 0; else ++pos; } result= BlInteger (pos); } void RunnerLineImpl::val_FIND_FIRST_OF (BlResult & result) { valfindfirstlast (result, find_first, find_yes); } void RunnerLineImpl::val_FIND_LAST_OF (BlResult & result) { valfindfirstlast (result, find_last, find_yes); } void RunnerLineImpl::val_FIND_FIRST_NOT_OF (BlResult & result) { valfindfirstlast (result, find_first, find_not); } void RunnerLineImpl::val_FIND_LAST_NOT_OF (BlResult & result) { valfindfirstlast (result, find_last, find_not); } namespace { unsigned long spectrumUDGcode (const std::string & str) { if (str.size () != 1) throw ErrImproperArgument; unsigned char c= str [0]; const unsigned char base= 144; // Changed this. Now instead of the Spectrum limit of 'u' // a to z are allowed. if (c >= 'a' && c <= 'z') c= static_cast (c - 'a' + base); else if (c >= 'A' && c <= 'Z') c= static_cast (c - 'A' + base); else if (c < 144 || c > 169) throw ErrImproperArgument; return sysvar::get32 (sysvar::CharGen) + c * 8; } } // namespace void RunnerLineImpl::val_USR (BlResult & result) { gettoken (); if (token.code != '(') { // Without parenthesis only Spectrum style // USR char is allowed. //valparen (result); valbase (result); result= spectrumUDGcode (result.str() ); return; } DynamicUsrFunc symaddr; DynamicHandle libhandle; // These are now defined here to avoid an error // in C++ Builder. std::vector vparam; util::auto_buffer param; expect (result); switch (result.type () ) { case VarNumber: case VarInteger: symaddr= (DynamicUsrFunc) result.integer (); break; case VarString: { std::string libname= result.str (); switch (token.code) { case ',': break; case ')': // Spectrum USR character. result= spectrumUDGcode (libname); gettoken (); return; default: throw ErrSyntax; } std::string funcname= expectstring (); libhandle.assign (libname); symaddr= libhandle.addr (funcname); } break; default: throw ErrBlassicInternal; } int nparams= 0; //std::vector vparam; while (token.code == ',') { expect (result); vparam.push_back (result.integer () ); ++nparams; } requiretoken (')'); gettoken (); //util::auto_buffer param; if (nparams) { param.alloc (nparams); std::copy (vparam.begin (), vparam.end (), param.begin () ); } result= (* symaddr) (nparams, param); } void RunnerLineImpl::val_VAL (BlResult & result) { //getparenarg (result); gettoken (); //valparen (result); valbase (result); std::string str= result.str (); switch (sysvar::get (sysvar::TypeOfVal) ) { case 0: // VAL simple. { #if 0 size_t i= 0, l= str.size (); while (i < l && str [i] == ' ') ++i; #else std::string::size_type i= str.find_first_not_of (" \t"); if (i > 0) if (i == std::string::npos) str.erase (); else str= str.substr (i); #endif } result= CodeLine::Token::number (str); break; case 1: // VAL with expression evaluation (Sinclair ZX) if (str.find_first_not_of (" \t") == std::string::npos) { result= 0L; break; } str= std::string ("0 ") + str; { #if 1 CodeLine valcodeline; valcodeline.scan (str); RunnerLineImpl valrunnerline (runner, valcodeline); #else RunnerLineImpl valrunnerline (runner); CodeLine & valcodeline= valrunnerline.getcodeline (); valcodeline.scan (str); #endif valrunnerline.expect (result); if (valrunnerline.token.code != keyENDLINE) throw ErrSyntax; } if (! result.is_numeric () ) throw ErrMismatch; break; default: throw ErrNotImplemented; } } void RunnerLineImpl::val_EOF (BlResult & result) { // Change to accept the form: EOF (#file) //getparenarg (result); expecttoken (keyOpenPar); gettoken (); if (token.code == keySharp) gettoken (); eval (result); requiretoken (')'); gettoken (); BlChannel channel= BlChannel (result.integer () ); BlFile & file= getfile (channel); result= BlInteger (file.eof () ? -1 : 0); } void RunnerLineImpl::val_VARPTR (BlResult & result) { expecttoken ('('); expecttoken (keyIDENTIFIER); std::string varname (token.str); VarType type= typeofvar (varname); size_t addr= 0; gettoken (); switch (token.code) { case ')': // Simple switch (type) { case VarNumber: addr= reinterpret_cast (addrvarnumber (varname) ); break; case VarInteger: addr= reinterpret_cast (addrvarinteger (varname) ); break; case VarString: addr= reinterpret_cast (addrvarstring (varname) ); break; default: throw ErrBlassicInternal; } result= 0L; gettoken (); break; case '(': // Array { Dimension dims= getdims (); result= 0L; requiretoken (')'); switch (type) { case VarNumber: addr= reinterpret_cast (addrdimnumber (varname, dims) ); break; case VarInteger: addr= reinterpret_cast (addrdiminteger (varname, dims) ); break; case VarString: addr= reinterpret_cast (addrdimstring (varname, dims) ); break; default: throw ErrBlassicInternal; } gettoken (); } break; default: throw ErrSyntax; } //result= BlNumber (addr); result= static_cast (addr); } void RunnerLineImpl::val_SGN (BlResult & result) { //getparenarg (result); gettoken (); //valparen (result); valbase (result); BlNumber d= result.number (); // Do not use 0.0 instead of zero, is a workaround // for an error on some versions of gcc. result= d < zero ? -1L : d > zero ? 1L : 0L; } void RunnerLineImpl::val_CVI (BlResult & result) { getparenarg (result); std::string str (result.str () ); if (str.size () < 2) throw ErrFunctionCall; result= BlInteger (short ( (unsigned char) str [0] ) | short ( ( (unsigned char) str [1] ) << 8) ); } void RunnerLineImpl::val_CVS (BlResult & result) { #define SIZE_S 4 ASSERT (sizeof (float) == 4); getparenarg (result); const std::string & str (result.str () ); if (str.size () < SIZE_S) throw ErrFunctionCall; BlNumber bn= BlNumber (* reinterpret_cast (str.data () ) ); result= bn; #undef SIZE_S } void RunnerLineImpl::val_CVD (BlResult & result) { #define SIZE_D 8 ASSERT (sizeof (double) == 8); getparenarg (result); const std::string & str (result.str () ); if (str.size () < SIZE_D) throw ErrFunctionCall; BlNumber bn= static_cast ( (* reinterpret_cast (str.data () ) ) ); result= bn; #undef SIZE_D } void RunnerLineImpl::val_CVL (BlResult & result) { getparenarg (result); std::string str (result.str () ); if (str.size () < 4) throw ErrFunctionCall; result= long ( (unsigned char) str [0] ) | long ( ( (unsigned char) str [1] ) << 8) | long ( ( (unsigned char) str [2] ) << 16) | long ( ( (unsigned char) str [3] ) << 24); } void RunnerLineImpl::val_MIN (BlResult & result) { expecttoken ('('); BlNumber bnMin= expectnum (); while (token.code == ',') bnMin= std::min (bnMin, expectnum () ); requiretoken (')'); gettoken (); result= bnMin; } void RunnerLineImpl::val_MAX (BlResult & result) { expecttoken ('('); BlNumber bnMax= expectnum (); while (token.code == ',') bnMax= std::max (bnMax, expectnum () ); requiretoken (')'); gettoken (); result= bnMax; } void RunnerLineImpl::val_CINT (BlResult & result) { gettoken (); #if 0 if (token.code == '(') { expect (result); requiretoken (')'); gettoken (); } else eval (result); #else //valparen (result); valbase (result); #endif result= result.integer (); } void RunnerLineImpl::val_TEST (BlResult & result) { expecttoken ('('); int x= expectinteger (); requiretoken (','); int y= expectinteger (); requiretoken (')'); gettoken (); result= static_cast (graphics::test (x, y, false) ); } void RunnerLineImpl::val_TESTR (BlResult & result) { expecttoken ('('); int x= expectinteger (); requiretoken (','); int y= expectinteger (); requiretoken (')'); gettoken (); result= static_cast (graphics::test (x, y, true) ); } void RunnerLineImpl::val_POS (BlResult & result) { result= static_cast (getparenfile ().pos () + 1); } void RunnerLineImpl::val_VPOS (BlResult & result) { result= getparenfile ().vpos () + 1; } void RunnerLineImpl::val_LOF (BlResult & result) { //expecttoken ('('); //gettoken (); //if (token.code == '#') // gettoken (); //BlChannel ch= evalchannel (); //requiretoken (')'); //gettoken (); //result= getfile (ch).lof (); result= getparenfile ().lof (); } void RunnerLineImpl::val_FREEFILE (BlResult & result) { gettoken (); result= runner.freefile (); } void RunnerLineImpl::val_INKEY (BlResult & result) { getparenarg (result); result= graphics::keypressed (result.integer () ); } void RunnerLineImpl::val_ROUND (BlResult & result) { using blassic::result::round; expecttoken ('('); BlNumber n= expectnum (); BlInteger d= 0; if (token.code == ',') d= expectinteger (); requiretoken (')'); gettoken (); if (d == 0) result= round (n); else if (d > 0) { for (BlInteger i= 0; i < d; ++i) n*= 10; n= round (n); for (BlInteger i= 0; i < d; ++i) n/= 10; result= n; } else { for (BlInteger i= 0; i > d; --i) n/= 10; n= round (n); for (BlInteger i= 0; i > d; --i) n*= 10; result= n; } } void RunnerLineImpl::val_CVSMBF (BlResult & result) { getparenarg (result); const std::string & str (result.str () ); result= mbf::mbf_s (str); } void RunnerLineImpl::val_CVDMBF (BlResult & result) { getparenarg (result); const std::string & str (result.str () ); result= mbf::mbf_d (str); } void RunnerLineImpl::val_REGEXP_INSTR (BlResult & result) { expecttoken ('('); std::string searched; std::string::size_type init= 0; expect (result); switch (result.type () ) { case VarString: searched= result.str (); break; case VarNumber: case VarInteger: init= result.integer (); if (init < 1) throw ErrFunctionCall; --init; requiretoken (','); searched= expectstring (); break; default: throw ErrBlassicInternal; } requiretoken (','); std::string expr= expectstring (); BlInteger flags= 0; if (token.code == ',') { flags= expectinteger (); } else { // Default flags value depending on the initial position. if (init > 0) flags= Regexp::FLAG_NOBEG; } requiretoken (')'); gettoken (); Regexp regexp (expr, flags); Regexp::size_type r= regexp.find (searched, init); if (r == std::string::npos) result= 0; else result= r + 1; } void RunnerLineImpl::val_ALLOC_MEMORY (BlResult & result) { getparenarg (result); result= blassic::memory::dyn_alloc (result.integer () ); } void RunnerLineImpl::val_LOC (BlResult & result) { expecttoken (keyOpenPar); gettoken (); if (token.code == keySharp) gettoken (); eval (result); requiretoken (')'); gettoken (); BlChannel channel= BlChannel (result.integer () ); BlFile & file= getfile (channel); result= BlInteger (file.loc () ); } void RunnerLineImpl::val_MID_S (BlResult & result) { expecttoken ('('); std::string str= expectstring (); requiretoken (','); BlNumber blfrom= expectnum (); size_t from= size_t (blfrom) - 1; size_t len; if (token.code == ',') { BlNumber bllen= expectnum (); len= size_t (bllen); } else len= std::string::npos; requiretoken (')'); if (from >= str.size () ) result= std::string (); else result= str.substr (from, len); gettoken (); } void RunnerLineImpl::val_LEFT_S (BlResult & result) { expecttoken ('('); std::string str= expectstring (); requiretoken (','); BlNumber blfrom= expectnum (); requiretoken (')'); size_t from= size_t (blfrom); result= str.substr (0, from); gettoken (); } void RunnerLineImpl::val_RIGHT_S (BlResult & result) { expecttoken ('('); std::string str= expectstring (); requiretoken (','); BlNumber blfrom= expectnum (); requiretoken (')'); size_t from= size_t (blfrom); size_t l= str.size (); if (from < l) result= str.substr (str.size () - from); else result= str; gettoken (); } void RunnerLineImpl::val_CHR_S (BlResult & result) { //getparenarg (result); gettoken (); //valparen (result); valbase (result); result= std::string (1, (unsigned char) result.number () ); } void RunnerLineImpl::val_ENVIRON_S (BlResult & result) { getparenarg (result); char * str= getenv (result.str ().c_str () ); if (str) result= std::string (str); else result= std::string (); } void RunnerLineImpl::val_STRING_S (BlResult & result) { expecttoken ('('); expect (result); size_t rep= result.integer (); requiretoken (','); expect (result); requiretoken (')'); gettoken (); BlChar charrep= '\0'; switch (result.type () ) { case VarNumber: charrep= BlChar ( (unsigned int) result.number () ); break; case VarInteger: charrep= BlChar (result.integer () ); break; case VarString: { const std::string & aux= result.str (); if (aux.empty () ) charrep= '\0'; else charrep= aux [0]; } break; default: throw ErrBlassicInternal; } result= std::string (rep, charrep); } void RunnerLineImpl::val_OSFAMILY_S (BlResult & result) { gettoken (); result= std::string (os_family); } void RunnerLineImpl::val_OSNAME_S (BlResult & result) { gettoken (); #if defined __unix__ || defined __linux__ // Even in cygwin struct utsname buf; if (uname (&buf) != 0) result= "unknown"; else result= buf.sysname; #elif defined BLASSIC_USE_WINDOWS result= "Windows"; #else result= "unknown"; #endif } void RunnerLineImpl::val_HEX_S (BlResult & result) { expecttoken ('('); expect (result); if (result.type () == VarNumber) { BlNumber m= blassic::result::round (result.number () ); if (m <= BlUint32Max && m > BlInt32Max) result= m - BlUint32Max - 1; } BlInteger n= result.integer (); size_t w= 0; if (token.code == ',') { expect (result); w= result.integer (); } requiretoken (')'); gettoken (); std::ostringstream oss; oss.setf (std::ios::uppercase); oss << std::hex << std::setw (w) << std::setfill ('0') << n; result= oss.str (); } void RunnerLineImpl::val_SPACE_S (BlResult & result) { getparenarg (result); result= std::string (size_t (result.number () ), ' '); } void RunnerLineImpl::val_UPPER_S (BlResult & result) { getparenarg (result); std::string & str= result.str (); std::transform (str.begin (), str.end (), str.begin (), toupper); } void RunnerLineImpl::val_LOWER_S (BlResult & result) { getparenarg (result); std::string & str= result.str (); std::transform (str.begin (), str.end (), str.begin (), tolower); } void RunnerLineImpl::val_STR_S (BlResult & result) { gettoken (); //valparen (result); valbase (result); std::ostringstream oss; BlNumber n= result.number (); if (sysvar::hasFlags1 (sysvar::SpaceStr_s) && n >= zero) oss << ' '; oss << std::setprecision (10) << n; result= oss.str (); } void RunnerLineImpl::val_OCT_S (BlResult & result) { expecttoken ('('); BlNumber n= expectnum (); size_t w= 0; if (token.code == ',') { BlNumber blw= expectnum (); w= size_t (blw); } requiretoken (')'); gettoken (); std::ostringstream oss; oss.setf (std::ios::uppercase); oss << std::oct << std::setw (w) << std::setfill ('0') << (unsigned long) n; result= oss.str (); } void RunnerLineImpl::val_BIN_S (BlResult & result) { expecttoken ('('); BlNumber bn= expectnum (); size_t w= 0; if (token.code == ',') { BlNumber blw= expectnum (); w= size_t (blw); } requiretoken (')'); gettoken (); unsigned long n= (unsigned long) bn; std::string str; while (n) { str= ( (n & 1) ? '1' : '0') + str; n/= 2; } if (str.empty () ) str= std::string (1, '0'); if (w > 0 && str.size () < w) str= std::string (w - str.size (), '0') + str; result= str; } void RunnerLineImpl::val_INKEY_S (BlResult & result) { gettoken (); //result= inkey (); // Now we can specify channel used for input. BlChannel ch= DefaultChannel; if (token.code == '(') { //gettoken (); //if (token.code == '#') // gettoken (); //ch= evalchannel (); ch= expectrequiredchannel (); requiretoken (')'); gettoken (); } std::string r= getfile (ch).inkey (); if (r == "\n" && sysvar::hasFlags1 (sysvar::ConvertLFCR) ) r= "\r"; result= r; } void RunnerLineImpl::val_PROGRAMARG_S (BlResult & result) { getparenarg (result); result= getprogramarg (size_t (result.number () - 1) ); } void RunnerLineImpl::val_DATE_S (BlResult & result) { using std::setw; using std::setfill; gettoken (); std::time_t t= time (NULL); struct std::tm * ptm= std::localtime (& t); std::ostringstream oss; oss << setw (2) << setfill ('0') << (ptm->tm_mon + 1) << '-' << setw (2) << setfill ('0') << ptm->tm_mday << '-' << (ptm->tm_year + 1900); result= oss.str (); } void RunnerLineImpl::val_TIME_S (BlResult & result) { using std::setw; using std::setfill; gettoken (); std::time_t t= time (NULL); struct std::tm * ptm= std::localtime (& t); std::ostringstream oss; oss << setw (2) << setfill ('0') << ptm->tm_hour << ':' << setw (2) << setfill ('0') << ptm->tm_min << ':' << setw (2) << setfill ('0') << ptm->tm_sec; result= oss.str (); } void RunnerLineImpl::val_INPUT_S (BlResult & result) { expecttoken ('('); BlNumber bn= expectnum (); BlChannel channel= DefaultChannel; switch (token.code) { case ')': break; case ',': gettoken (); //if (token.code == '#') // channel= expectchannel (); channel= evaloptionalchannel (channel); requiretoken (')'); break; default: throw ErrSyntax; } gettoken (); BlFile & in= getfile (channel); result= in.read (size_t (bn) ); } void RunnerLineImpl::val_MKI_S (BlResult & result) { getparenarg (result); BlNumber bn= result.number (); unsigned short s= (unsigned short) short (bn); std::string str; str= char (s & 255); str+= char (s >> 8); result= str; } void RunnerLineImpl::val_MKS_S (BlResult & result) { getparenarg (result); float f= result.number (); std::string str (reinterpret_cast (& f), sizeof (float) ); result= str; } void RunnerLineImpl::val_MKD_S (BlResult & result) { getparenarg (result); double f= result.number (); std::string str (reinterpret_cast (& f), sizeof (double) ); result= str; } void RunnerLineImpl::val_MKL_S (BlResult & result) { getparenarg (result); BlNumber bn= result.number (); unsigned int s= (unsigned int) int (bn); std::string str; str= char (s & 255); str+= char ( (s >> 8) & 255); str+= char ( (s >> 16) & 255); str+= char ( (s >> 24) & 255); result= str; } #if 0 void RunnerLineImpl::val_TRIM (BlResult & result) { using std::string; bool tleft= false, tright= false; switch (token.code) { case keyTRIM_S: tleft= true; tright= true; break; case keyLTRIM_S: tleft= true; break; case keyRTRIM_S: tright= true; break; default: if (showdebuginfo () ) cerr << "Erroneous call to valtrim" << endl; throw ErrBlassicInternal; } getparenarg (result); string str= result.str (); if (tleft) { string::size_type inipos= str.find_first_not_of (' '); if (inipos > 0) if (inipos == string::npos) str= string (); else //str= str.substr (inipos); str.erase (0, inipos); } if (tright) { string::size_type endpos= str.find_last_not_of (' '); if (endpos != string::npos) //str= str.substr (0, endpos + 1); str.erase (endpos + 1); else str= string (); } result= str; } #endif void RunnerLineImpl::valtrimbase (BlResult & result, bool tleft, bool tright) { using std::string; if (! tleft && ! tright) { if (showdebuginfo () ) cerr << "Erroneous call to valtrim" << endl; throw ErrBlassicInternal; } getparenarg (result); string str= result.str (); if (tleft) { string::size_type inipos= str.find_first_not_of (' '); if (inipos > 0) if (inipos == string::npos) str= string (); else //str= str.substr (inipos); str.erase (0, inipos); } if (tright) { string::size_type endpos= str.find_last_not_of (' '); if (endpos != string::npos) //str= str.substr (0, endpos + 1); str.erase (endpos + 1); else str= string (); } result= str; } void RunnerLineImpl::val_TRIM_S (BlResult & result) { valtrimbase (result, true, true); } void RunnerLineImpl::val_LTRIM_S (BlResult & result) { valtrimbase (result, true, false); } void RunnerLineImpl::val_RTRIM_S (BlResult & result) { valtrimbase (result, false, true); } void RunnerLineImpl::val_FINDFIRST_S (BlResult & result) { getparenarg (result); if (! pdirectory) pdirectory= new Directory (); result= pdirectory->findfirst (result.str () ); } void RunnerLineImpl::val_FINDNEXT_S (BlResult & result) { if (! pdirectory) result= std::string (); else result= pdirectory->findnext (); gettoken (); } void RunnerLineImpl::val_COPYCHR_S (BlResult & result) { expecttoken ('('); expecttoken ('#'); BlChannel c= expectchannel (); BlChar from= BlChar (0), to= BlChar (255); if (token.code == ',') { from= static_cast (expectnum () ); if (token.code == ',') { to= static_cast (expectnum () ); } } requiretoken (')'); gettoken (); BlFile & window= getfile (c); result= window.copychr (from, to); } void RunnerLineImpl::val_STRERR_S (BlResult & result) { getparenarg (result); result= ErrStr (static_cast (result.integer () ) ); } void RunnerLineImpl::val_DEC_S (BlResult & result) { expecttoken ('('); BlNumber n= expectnum (); requiretoken (','); std::string format= expectstring (); VectorUsing usingf; parseusing (format, usingf); if (usingf.size () != 1) throw ErrFunctionCall; UsingNumeric * pun= dynamic_cast (usingf [0] ); if (pun == NULL) throw ErrFunctionCall; BlFileOutString fos; pun->putnumeric (fos, n); result= fos.str (); requiretoken (')'); gettoken (); } void RunnerLineImpl::val_VAL_S (BlResult & result) { //getparenarg (result); gettoken (); //valparen (result); valbase (result); std::string str= result.str (); if (str.find_first_not_of (" \t") == std::string::npos) { result= std::string (); } else { str= std::string ("0 ") + str; #if 1 CodeLine valcodeline; valcodeline.scan (str); RunnerLineImpl valrunnerline (runner, valcodeline); #else RunnerLineImpl valrunnerline (runner); CodeLine & valcodeline= valrunnerline.getcodeline (); valcodeline.scan (str); #endif valrunnerline.expect (result); if (valrunnerline.token.code != keyENDLINE) throw ErrSyntax; if (result.type () != VarString) throw ErrMismatch; } } void RunnerLineImpl::val_SCREEN_S (BlResult & result) { expecttoken ('('); BlInteger y= expectinteger (); requiretoken (','); BlInteger x= expectinteger (); requiretoken (')'); gettoken (); result= graphics::screenchr (x, y); } void RunnerLineImpl::val_MKSMBF_S (BlResult & result) { getparenarg (result); result= mbf::to_mbf_s (result.number () ); } void RunnerLineImpl::val_MKDMBF_S (BlResult & result) { getparenarg (result); result= mbf::to_mbf_d (result.number () ); } void RunnerLineImpl::val_REGEXP_REPLACE_S (BlResult & result) { expecttoken ('('); std::string searched; std::string::size_type init= 0; expect (result); switch (result.type () ) { case VarString: searched= result.str (); break; case VarNumber: case VarInteger: init= result.integer (); if (init < 1) throw ErrFunctionCall; --init; requiretoken (','); searched= expectstring (); break; default: throw ErrBlassicInternal; } requiretoken (','); std::string expr= expectstring (); requiretoken (','); gettoken (); std::string replaceby; bool isfunction= false; if (token.code == keyFN) { isfunction= true; gettoken (); if (token.code != keyIDENTIFIER) throw ErrSyntax; replaceby= token.str; gettoken (); } else replaceby= evalstring (); BlInteger flags= 0; if (token.code == ',') { flags= expectinteger (); } else { // Default flags value depending on the initial position. if (init > 0) flags= Regexp::FLAG_NOBEG; } requiretoken (')'); gettoken (); Regexp regexp (expr, flags); if (isfunction) result= regexp.replace (searched, init, * this, replaceby); else result= regexp.replace (searched, init, replaceby); } namespace { class FnErrorShower { public: FnErrorShower (const std::string & fname) : finished (false), name (fname) { } ~FnErrorShower () { if (! finished && showdebuginfo () ) cerr << "Error processing FN " << name << endl; } void finish () { finished= true; } private: bool finished; const std::string & name; }; class FnLevelIncrementer { public: FnLevelIncrementer (Runner & r) : r (r) { r.inc_fn_level (); } ~FnLevelIncrementer () { r.dec_fn_level (); } private: Runner & r; }; } // namespace void RunnerLineImpl::callfn (Function & f, const std::string & fname, LocalLevel & ll, BlResult & result) { switch (f.getdeftype () ) { case Function::DefSingle: { // Prevents lock on recursive calls. if (fInterrupted) { if (runner.getbreakstate () != onbreak::BreakCont) throw BlBreak (); else fInterrupted= false; } FnErrorShower check (fname); //CodeLine & code= f.getcode (); //code.gotochunk (0); //RunnerLineImpl fnrunnerline (runner, code); //RunnerLineImpl fnrunnerline (runner); //CodeLine & fncodeline= fnrunnerline.getcodeline (); // Use the heap to decrease the use // of the stack in recursive fn calls. //auto_ptr pfnrunnerline // (new RunnerLineImpl (runner) ); //CodeLine & fncodeline= pfnrunnerline->getcodeline (); //fncodeline= f.getcode (); CodeLine fncodeline (f.getcode () ); auto_ptr pfnrunnerline (new RunnerLineImpl (runner, fncodeline) ); FnLevelIncrementer fli (runner); //fnrunnerline.expect (result); pfnrunnerline->expect (result); ll.freelocalvars (); check.finish (); } break; case Function::DefMulti: { ll.addlocalvar (fname); //Runner fnrun (program); Runner fnrun (runner); // We reuse the break position // to jump to the fn definition. fnrun.set_break (f.getpos () ); fnrun.gosub_push (ll); CodeLine jumpline; jumpline.scan ("CONT"); { FnLevelIncrementer fli (runner); FnErrorShower check (fname); fnrun.runline (jumpline); check.finish (); } size_t stacksize= fnrun.gosub_size (); if (stacksize > 1) { if (showdebuginfo () ) { cerr << "FN END with GOSUB " "pending of RETURN" << endl; } throw ErrGosubWithoutReturn; } if (stacksize == 0) { if (showdebuginfo () ) { cerr << "FN control missing" << endl; } throw ErrBlassicInternal; } switch (typeofvar (fname) ) { case VarNumber: result= evaluatevarnumber (fname); break; case VarInteger: result= evaluatevarinteger (fname); break; case VarString: result= evaluatevarstring (fname); break; default: throw ErrBlassicInternal; } fnrun.fn_pop (); } break; } } void RunnerLineImpl::val_FN (BlResult & result) { // Get FN name. expecttoken (keyIDENTIFIER); std::string fname= token.str; Function f= Function::get (fname); // Get parameters. gettoken (); LocalLevel ll; const ParameterList & param= f.getparam (); size_t l= param.size (); if (l != 0) { requiretoken ('('); for (size_t i= 0; i < l; ++i) { BlResult aux; expect (result); //const std::string & var= param [i]; std::string var= param [i]; ll.addlocalvar (var); VarType type= typeofvar (var); switch (type) { case VarNumber: assignvarnumber (var, result.number () ); break; case VarInteger: assignvarinteger (var, result.integer () ); break; case VarString: assignvarstring (var, result.str () ); break; default: throw ErrBlassicInternal; } if (i < l - 1) requiretoken (','); } requiretoken (')'); gettoken (); } // Execute the function. callfn (f, fname, ll, result); } void RunnerLineImpl::valsubindex (const std::string & varname, BlResult & result) { Dimension dims= getdims (); switch (typeofvar (varname) ) { case VarNumber: result= valuedimnumber (varname, dims); break; case VarInteger: result= valuediminteger (varname, dims); break; case VarString: result= valuedimstring (varname, dims); break; default: throw ErrBlassicInternal; } } void RunnerLineImpl::val_LET (BlResult & result) { gettoken (); VarPointer vp= eval_let (); switch (vp.type) { case VarNumber: result= * vp.pnumber; break; case VarInteger: result= * vp.pinteger; break; case VarString: result= * vp.pstring; break; default: throw ErrBlassicInternal; } } void RunnerLineImpl::val_LABEL (BlResult & result) { gettoken (); if (token.code != keyIDENTIFIER) throw ErrSyntax; result= static_cast (program.getlabel (token.str) ); gettoken (); } void RunnerLineImpl::val_IDENTIFIER (BlResult & result) { std::string varname (token.str); gettoken (); if (token.code == '(') valsubindex (varname, result); else switch (typeofvar (varname) ) { case VarNumber: result= evaluatevarnumber (varname); break; case VarInteger: result= evaluatevarinteger (varname); break; case VarString: result= evaluatevarstring (varname); break; default: throw ErrBlassicInternal; } } void RunnerLineImpl::val_STRING (BlResult & result) { result= token.str; gettoken (); } void RunnerLineImpl::val_NUMBER (BlResult & result) { result= token.number (); gettoken (); } void RunnerLineImpl::val_INTEGER (BlResult & result) { result= token.integer (); gettoken (); } void RunnerLineImpl::val_OpenPar (BlResult & result) { gettoken (); eval (result); if (token.code != keyClosePar) throw ErrSyntax; gettoken (); } void RunnerLineImpl::valbase (BlResult & result) { #ifdef OPERATION_SWITCH # define HANDLE_OPER(elem) \ case key##elem: \ val_##elem (result); \ break # define HANDLE_OPER2(elem1, elem2) \ case key##elem1: \ val_##elem2 (result); \ break switch (token.code) { HANDLE_OPER (OpenPar); HANDLE_OPER (LET); HANDLE_OPER (LABEL); HANDLE_OPER (FN); HANDLE_OPER (MID_S); HANDLE_OPER (LEFT_S); HANDLE_OPER (RIGHT_S); HANDLE_OPER (CHR_S); HANDLE_OPER (ENVIRON_S); HANDLE_OPER (STRING_S); HANDLE_OPER (OSFAMILY_S); HANDLE_OPER (HEX_S); HANDLE_OPER (SPACE_S); HANDLE_OPER (UPPER_S); HANDLE_OPER (LOWER_S); HANDLE_OPER (STR_S); HANDLE_OPER (OCT_S); HANDLE_OPER (BIN_S); HANDLE_OPER (INKEY_S); HANDLE_OPER (PROGRAMARG_S); HANDLE_OPER (DATE_S); HANDLE_OPER (TIME_S); HANDLE_OPER (INPUT_S); HANDLE_OPER (MKI_S); HANDLE_OPER (MKS_S); HANDLE_OPER (MKD_S); HANDLE_OPER (MKL_S); HANDLE_OPER (TRIM_S); HANDLE_OPER (LTRIM_S); HANDLE_OPER (RTRIM_S); HANDLE_OPER (OSNAME_S); HANDLE_OPER (FINDFIRST_S); HANDLE_OPER (FINDNEXT_S); HANDLE_OPER (COPYCHR_S); HANDLE_OPER (STRERR_S); HANDLE_OPER (DEC_S); HANDLE_OPER (VAL_S); HANDLE_OPER (SCREEN_S); HANDLE_OPER (MKSMBF_S); HANDLE_OPER (MKDMBF_S); HANDLE_OPER (REGEXP_REPLACE_S); // UCASE$ and LCASE$ are alias for UPPER$ and LOWER$ HANDLE_OPER2 (UCASE_S, UPPER_S); HANDLE_OPER2 (LCASE_S, LOWER_S); HANDLE_OPER (ASC); HANDLE_OPER (LEN); HANDLE_OPER (PEEK); HANDLE_OPER (PROGRAMPTR); HANDLE_OPER (RND); HANDLE_OPER (INT); HANDLE_OPER (SIN); HANDLE_OPER (COS); HANDLE_OPER (PI); HANDLE_OPER (TAN); HANDLE_OPER (SQR); HANDLE_OPER (ASIN); HANDLE_OPER (ACOS); HANDLE_OPER (INSTR); HANDLE_OPER (ATAN); HANDLE_OPER (ABS); HANDLE_OPER (USR); HANDLE_OPER (VAL); HANDLE_OPER (EOF); HANDLE_OPER (VARPTR); HANDLE_OPER (SYSVARPTR); HANDLE_OPER (SGN); HANDLE_OPER (LOG); HANDLE_OPER (LOG10); HANDLE_OPER (EXP); HANDLE_OPER (TIME); HANDLE_OPER (ERR); HANDLE_OPER (ERL); HANDLE_OPER (CVI); HANDLE_OPER (CVS); HANDLE_OPER (CVD); HANDLE_OPER (CVL); HANDLE_OPER (MIN); HANDLE_OPER (MAX); HANDLE_OPER (CINT); HANDLE_OPER (FIX); HANDLE_OPER (XMOUSE); HANDLE_OPER (YMOUSE); HANDLE_OPER (XPOS); HANDLE_OPER (YPOS); HANDLE_OPER (PEEK16); HANDLE_OPER (PEEK32); HANDLE_OPER (RINSTR); HANDLE_OPER (FIND_FIRST_OF); HANDLE_OPER (FIND_LAST_OF); HANDLE_OPER (FIND_FIRST_NOT_OF); HANDLE_OPER (FIND_LAST_NOT_OF); HANDLE_OPER (SINH); HANDLE_OPER (COSH); HANDLE_OPER (TANH); HANDLE_OPER (ASINH); HANDLE_OPER (ACOSH); HANDLE_OPER (ATANH); HANDLE_OPER (ATAN2); HANDLE_OPER (TEST); HANDLE_OPER (TESTR); HANDLE_OPER (POS); HANDLE_OPER (VPOS); HANDLE_OPER (LOF); HANDLE_OPER (FREEFILE); HANDLE_OPER (INKEY); HANDLE_OPER (ROUND); HANDLE_OPER (CVSMBF); HANDLE_OPER (CVDMBF); HANDLE_OPER (REGEXP_INSTR); HANDLE_OPER (ALLOC_MEMORY); HANDLE_OPER (LOC); HANDLE_OPER (IDENTIFIER); HANDLE_OPER (NUMBER); HANDLE_OPER (STRING); HANDLE_OPER (INTEGER); default: throw ErrSyntax; } if (result.type () == VarString) { if (token.code == '[') slice (result); } #else // No OPERATION_SWITCH //(this->* array_valfunction [token.code] ) (result); (this->* findvalfunc (token.code) ) (result); if (result.type () == VarString) { if (token.code == '[') slice (result); } #endif // OPERATION_SWITCH } #if 0 void RunnerLineImpl::valparen (BlResult & result) { #if 0 if (token.code == '(') { gettoken (); eval (result); if (token.code != ')') throw ErrSyntax; gettoken (); } else #endif valbase (result); } #endif void RunnerLineImpl::slice (BlResult & result) { using std::string; string str= result.str (); do { #if 0 gettoken (); string::size_type from= 0; string::size_type to= str.size (); if (token.code != ']') { if (token.code != keyTO) { from= evalstringindex (); if (token.code == keyTO) { gettoken (); to= evalstringindex (); } else to= from; } else { gettoken (); if (token.code != ']') to= evalstringindex (); } requiretoken (']'); } gettoken (); if (from > to) str.erase (); else { std::string::size_type limit= str.size (); if (from >= limit || to >= limit) throw ErrBadSubscript; str= str.substr (from, to - from + 1); } #else string::size_type from, to; evalstringslice (str, from, to); if (from + 1 > to) str.erase (); else str= str.substr (from, to - from); #endif } while (token.code == '['); result= str; } void RunnerLineImpl::valexponent (BlResult & result) { //valparen (result); valbase (result); #if 0 if (result.type () == VarString) { if (token.code == '[') slice (result); } else { #endif while (token.code == '^') { gettoken (); BlResult guard; valunary (guard); result= callnumericfunc (pow, result.number (), guard.number () ); } //} } void RunnerLineImpl::valmod (BlResult & result) { valexponent (result); while (token.code == keyMOD) { gettoken (); BlResult guard; valexponent (guard); result%= guard; } } void RunnerLineImpl::valunary (BlResult & result) { BlCode op= 0; if (token.code == '+' || token.code == '-' || token.code == keyNOT) { op= token.code; gettoken (); valunary (result); } else valmod (result); switch (op) { case 0: break; case '+': if (! result.is_numeric () ) throw ErrMismatch; break; case '-': result= -result; break; case keyNOT: //result= BlNumber (~ long (result.number () ) ); { sysvar::Flags2Bit f2 (sysvar::getFlags2 () ); if (f2.has (sysvar::BoolMode) ) { bool r; switch (result.type () ) { case VarInteger: r= ! result.integer (); break; case VarNumber: r= ! result.number (); break; default: throw ErrMismatch; } result= r ? (f2.has (sysvar::TruePositive) ? 1 : -1) : 0; } else result= ~ result.integer (); } break; default: throw ErrBlassicInternal; } } void RunnerLineImpl::valdivint (BlResult & result) { valunary (result); BlCode op= token.code; while (op == '\\') { BlResult guard; gettoken (); valunary (guard); //BlInteger g= guard.integer (); //if (g == 0) // throw ErrDivZero; //result= result.integer () / g; result.integerdivideby (guard); op= token.code; } } void RunnerLineImpl::valmuldiv (BlResult & result) { //valunary (result); valdivint (result); BlCode op= token.code; while (op == '*' || op == '/') { BlResult guard; gettoken (); //valunary (guard); valdivint (guard); switch (op) { case '*': result*= guard; break; case '/': result/= guard; break; default: ; } op= token.code; } } void RunnerLineImpl::valplusmin (BlResult & result) { valmuldiv (result); BlCode op= token.code; while (op == '+' || op == '-') { BlResult guard; gettoken (); valmuldiv (guard); switch (op) { case '+': result+= guard; break; case '-': result-= guard; break; } op= token.code; } } void RunnerLineImpl::valcomp (BlResult & result) { valplusmin (result); BlCode op= token.code; if (iscomp (op) ) { bool true_positive= sysvar::hasFlags2 (sysvar::TruePositive); BlResult guard; bool r; do { gettoken (); valplusmin (guard); switch (op) { case '=': r= (result == guard); break; case keyDISTINCT: r= (result != guard); break; case '<': r= (result < guard); break; case keyMINOREQUAL: r= (result <= guard); break; case '>': r= (result > guard); break; case keyGREATEREQUAL: r= (result >= guard); break; default: throw ErrBlassicInternal; } result= r ? (true_positive ? 1 : -1) : 0; op= token.code; } while (iscomp (op) ); } } void RunnerLineImpl::valorand (BlResult & result) { valcomp (result); BlCode op= token.code; if (islogical2 (op) ) { bool boolean_mode= sysvar::hasFlags2 (sysvar::BoolMode); bool true_positive= sysvar::hasFlags2 (sysvar::TruePositive); BlResult guard; do { gettoken (); valcomp (guard); if (boolean_mode) { bool r; bool r1= result.tobool (); bool r2= guard.tobool (); switch (op) { case keyOR: r= r1 || r2; break; case keyAND: r= r1 && r2; break; case keyXOR: r= r1 != r2; break; default: throw ErrBlassicInternal; } result= r ? (true_positive ? 1 : -1) : 0; } else { BlInteger n1= result.integer (); BlInteger n2= guard.integer (); switch (op) { case keyOR: //result= BlNumber (n1 | n2); result= n1 | n2; break; case keyAND: //result= BlNumber (n1 & n2); result= n1 & n2; break; case keyXOR: //result= BlNumber (n1 ^ n2); result= n1 ^ n2; break; default: throw ErrBlassicInternal; } } op= token.code; } while (islogical2 (op) ); } } void RunnerLineImpl::eval (BlResult & result) { valorand (result); } void RunnerLineImpl::expect (BlResult & result) { gettoken (); eval (result); } BlLineNumber RunnerLineImpl::evallinenumber () { BlLineNumber bln; switch (token.code) { case keyNUMBER: bln= blassic::result::NumberToInteger (token.number () ); break; case keyINTEGER: bln= BlLineNumber (token.integer () ); break; case keyIDENTIFIER: bln= program.getlabel (token.str); if (bln == LineEndProgram) throw ErrNoLabel; break; default: throw ErrSyntax; } if (bln > BlMaxLineNumber) { if (showdebuginfo () ) cerr << "Invalid line number"; throw ErrSyntax; } gettoken (); return bln; } void RunnerLineImpl::evallinerange (BlLineNumber & blnBeg, BlLineNumber & blnEnd) { blnBeg= LineBeginProgram; blnEnd= LineEndProgram; if (! endsentence () && token.code != ',') { if (token.code != '-') { blnBeg= evallinenumber (); if (token.code == '-') { gettoken (); if (! endsentence () && token.code != ',') blnEnd= evallinenumber (); } else blnEnd= blnBeg; } else { gettoken (); blnEnd= evallinenumber (); } } } Dimension RunnerLineImpl::getdims () { //TRACEFUNC (tr, "RunnerLineImpl::getdims"); ASSERT (token.code == '('); Dimension dims; do { dims.add (expectinteger () ); } while (token.code == ','); requiretoken (')'); gettoken (); return dims; } Dimension RunnerLineImpl::evaldims () { requiretoken ('('); return getdims (); } Dimension RunnerLineImpl::expectdims () { expecttoken ('('); return getdims (); } void RunnerLineImpl::errorifparam () { gettoken (); require_endsentence (); } void RunnerLineImpl::gosub_line (BlLineNumber dest) { //ProgramPos posgosub= runner.getposactual (); ProgramPos posgosub (getposactual () ); posgosub.nextchunk (); #if 1 //if (token.code == keyENDLINE && posgosub.getnum () != 0) if (token.code == keyENDLINE && posgosub.getnum () != LineDirectCommand) { #if 1 posgosub.nextline (); #else BlLineNumber num= program.getnextnum (line); if (num != 0) posgosub= num; #endif } #endif runner.gosub_line (dest, posgosub); } void RunnerLineImpl::getinkparams () { if (endsentence () ) return; requiretoken (','); gettoken (); if (token.code != ',') { BlInteger ink= evalinteger (); graphics::setcolor (ink); if (token.code != ',') { require_endsentence (); return; } } gettoken (); BlInteger inkmode= evalinteger (); require_endsentence (); graphics::setdrawmode (inkmode); } void RunnerLineImpl::getdrawargs (BlInteger & y) { requiretoken (','); y= expectinteger (); getinkparams (); } void RunnerLineImpl::getdrawargs (BlInteger & x, BlInteger & y) { x= expectinteger (); getdrawargs (y); } void RunnerLineImpl::make_clear () { runner.clear (); runner.clearerrorgoto (); clearvars (); Function::clear (); graphics::clear_images (); definevar (VarNumber, 'A', 'Z'); runner.trigonometric_default (); } bool RunnerLineImpl::syntax_error () { TRACEFUNC (tr, "RunnerLineImpl::syntax_error"); throw ErrSyntax; } void RunnerLineImpl::valsyntax_error (BlResult &) { TRACEFUNC (tr, "RunnerLineImpl::valsyntax_error"); throw ErrSyntax; } bool RunnerLineImpl::execute_instruction () { //TRACEFUNC (tr, "RunnerLineImpl::execute_instruction"); #ifndef INSTRUCTION_SWITCH if ( (this->*findfunc (token.code) ) () ) { //TRMESSAGE (tr, "Instruction returned true"); return true; } else return false; #else # define HANDLE_ELEM(elem) \ case key##elem: \ return do_##elem () # define HANDLE_ELEM2(elem1, elem2) \ case key##elem1: \ return do_##elem2 () switch (token.code) { HANDLE_ELEM (Colon); HANDLE_ELEM (END); HANDLE_ELEM (LIST); HANDLE_ELEM (REM); HANDLE_ELEM (LOAD); HANDLE_ELEM (SAVE); HANDLE_ELEM (NEW); HANDLE_ELEM (EXIT); HANDLE_ELEM (RUN); HANDLE_ELEM (PRINT); HANDLE_ELEM (FOR); HANDLE_ELEM (NEXT); HANDLE_ELEM (IF); HANDLE_ELEM (ELSE); HANDLE_ELEM (TRON); HANDLE_ELEM (TROFF); HANDLE_ELEM (LET); HANDLE_ELEM (GOTO); HANDLE_ELEM (STOP); HANDLE_ELEM (CONT); HANDLE_ELEM (CLEAR); HANDLE_ELEM (GOSUB); HANDLE_ELEM (RETURN); HANDLE_ELEM (POKE); HANDLE_ELEM (DATA); HANDLE_ELEM (READ); HANDLE_ELEM (RESTORE); HANDLE_ELEM (INPUT); HANDLE_ELEM (LINE); HANDLE_ELEM (RANDOMIZE); HANDLE_ELEM (PLEASE); HANDLE_ELEM (AUTO); HANDLE_ELEM (DIM); HANDLE_ELEM (SYSTEM); HANDLE_ELEM (ON); HANDLE_ELEM (ERROR); HANDLE_ELEM (OPEN); HANDLE_ELEM (CLOSE); HANDLE_ELEM (LOCATE); HANDLE_ELEM (CLS); HANDLE_ELEM (WRITE); HANDLE_ELEM (MODE); HANDLE_ELEM (MOVE); HANDLE_ELEM (COLOR); HANDLE_ELEM (GET); HANDLE_ELEM (LABEL); HANDLE_ELEM (DELIMITER); HANDLE_ELEM (REPEAT); HANDLE_ELEM (UNTIL); HANDLE_ELEM (WHILE); HANDLE_ELEM (WEND); HANDLE_ELEM (PLOT); HANDLE_ELEM (POPEN); HANDLE_ELEM (RESUME); HANDLE_ELEM (DELETE); HANDLE_ELEM (LOCAL); HANDLE_ELEM (PUT); HANDLE_ELEM (FIELD); HANDLE_ELEM (LSET); // Lset and rset use same function. HANDLE_ELEM2 (RSET, LSET); HANDLE_ELEM (SOCKET); HANDLE_ELEM (DRAW); HANDLE_ELEM (DEF); HANDLE_ELEM (FN); HANDLE_ELEM (ERASE); HANDLE_ELEM (SWAP); HANDLE_ELEM (SYMBOL); HANDLE_ELEM (ZONE); HANDLE_ELEM (POP); HANDLE_ELEM (NAME); HANDLE_ELEM (KILL); HANDLE_ELEM (FILES); HANDLE_ELEM (PAPER); HANDLE_ELEM (PEN); HANDLE_ELEM (SHELL); HANDLE_ELEM (MERGE); HANDLE_ELEM (CHDIR); HANDLE_ELEM (MKDIR); HANDLE_ELEM (RMDIR); HANDLE_ELEM (SYNCHRONIZE); HANDLE_ELEM (PAUSE); HANDLE_ELEM (CHAIN); HANDLE_ELEM (ENVIRON); HANDLE_ELEM (EDIT); HANDLE_ELEM (DRAWR); HANDLE_ELEM (PLOTR); HANDLE_ELEM (MOVER); HANDLE_ELEM (POKE16); HANDLE_ELEM (POKE32); HANDLE_ELEM (RENUM); HANDLE_ELEM (CIRCLE); HANDLE_ELEM (MASK); HANDLE_ELEM (WINDOW); HANDLE_ELEM (GRAPHICS); HANDLE_ELEM (BEEP); HANDLE_ELEM (DEFINT); // DEFINT, DEFSTR, DEFREAL, DEFSNG and DEFDBL use same function. HANDLE_ELEM2 (DEFSTR, DEFINT); HANDLE_ELEM2 (DEFREAL, DEFINT); HANDLE_ELEM2 (DEFSNG, DEFINT); HANDLE_ELEM2 (DEFDBL, DEFINT); HANDLE_ELEM (INK); HANDLE_ELEM (SET_TITLE); HANDLE_ELEM (TAG); // TAG and TAGOFF use same function. HANDLE_ELEM2 (TAGOFF, TAG); HANDLE_ELEM (ORIGIN); HANDLE_ELEM (DEG); // DEG and RAD use same function. HANDLE_ELEM2 (RAD, DEG); HANDLE_ELEM (INVERSE); HANDLE_ELEM (IF_DEBUG); // LPRINT and PRINT use same function. HANDLE_ELEM2 (LPRINT, PRINT); HANDLE_ELEM (LLIST); HANDLE_ELEM (WIDTH); HANDLE_ELEM (BRIGHT); HANDLE_ELEM (DRAWARC); HANDLE_ELEM (PULL); HANDLE_ELEM (PAINT); HANDLE_ELEM (FREE_MEMORY); HANDLE_ELEM (SCROLL); HANDLE_ELEM (ZX_PLOT); HANDLE_ELEM (ZX_UNPLOT); HANDLE_ELEM (MID_S); HANDLE_ELEM (PROGRAMARG_S); HANDLE_ELEM (IDENTIFIER); HANDLE_ELEM (NUMBER); HANDLE_ELEM2 (INTEGER, NUMBER); HANDLE_ELEM (ENDLINE); default: throw ErrSyntax; } #endif } void RunnerLineImpl::execute () { TRACEFUNC (tr, "RunnerLineImpl::execute"); // Cleaned and clarified the code of this function. fInElse= false; //codprev= 0; codprev= codeline.actualcode (); for (;;) { //next_instruction: #ifndef BLASSIC_NO_GRAPHICS // Allows refreshing of the graphics window. #ifndef BLASSIC_USE_WINDOWS // In windows a thread takes care of graphics, // in others frequent calls to graphics::idle // are needed to handle events in the graphics // window. // Avoid calling idle too many times. // Someone wants to tune this? if (graphics::ingraphicsmode () ) { static size_t counter= 0; if (counter ++ == 100) { graphics::idle (); counter= 0; } } #endif #endif // Testing little change: control this after executing // the instruction. #if 0 // ELSE control. Does not allow a precise syntax // checking, but actually works. // ELSE can be found in four ways: // - When IF look for it. // - As the end of a instruction. // - After : // - As the firsts instruction of one line. // Here are treated the two first cases, the // others are processed as a normal instruction. codprev= codeline.actualcode (); if (codprev == keyELSE) { if (fInElse) { // Was found by IF, process the rest // of the line. fInElse= false; } else { // Not found by if. Exit the line. //break; return; } } #endif gettoken (); actualchunk= codeline.chunk (); if (fInterrupted) { if (runner.getbreakstate () != onbreak::BreakCont) throw BlBreak (); fInterrupted= false; } if (runner.channelspolled () ) { BlLineNumber line= runner.getpollnumber (); if (line != LineEndProgram) { runner.gosub_line (line, getposactual (), true); //break; return; } } #if 1 bool finishline= execute_instruction (); if (finishline) //break; return; #else #ifndef INSTRUCTION_SWITCH if ( (this->*findfunc (token.code) ) () ) { //TRMESSAGE (tr, "Instruction returned true"); break; } #endif #endif // Test. //do_func f= findfunc (token.code); //bool r= (this->* f) (); //if (r) // break; // This makes a tiny speed improvement with my test // programs, perhaps in other programs can be the // contrary? Anyway, the difference is not important, // can be supressed and all works. if (token.code == keyENDLINE) { //TRMESSAGE (tr, "End of line"); //break; return; } #if 1 codprev= token.code; if (codprev == keyELSE) { //TRMESSAGE (tr, "ELSE after instruction"); if (fInElse) { // Was found by IF, process the rest // of the line. fInElse= false; } else { // Not found by if. Exit the line. //break; return; } } #endif //goto next_instruction; } // for (;;) } // End of runnerline_impl.cpp