diff options
Diffstat (limited to 'runnerline_impl.cpp')
-rw-r--r-- | runnerline_impl.cpp | 4347 |
1 files changed, 4347 insertions, 0 deletions
diff --git a/runnerline_impl.cpp b/runnerline_impl.cpp new file mode 100644 index 0000000..1656988 --- /dev/null +++ b/runnerline_impl.cpp @@ -0,0 +1,4347 @@ +// 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 <sstream> +#include <iomanip> +#include <algorithm> +#include <functional> +#include <memory> +#include <cstdio> +#include <cerrno> +#include <ctime> +#include <cctype> +#include <iostream> + +// cmath is not used because in some platforms asinh and others +// are declared only in math.h +#include <math.h> + +#if defined __unix__ || defined __linux__ +// Using uname on unix, linux and cygwin. +#include <sys/utsname.h> +#endif + +#include <cassert> +#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 <const RunnerLineImpl::tfunctions_t *, + const RunnerLineImpl::tfunctions_t> + (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 <const RunnerLineImpl::tfunctions_t *, + const RunnerLineImpl::tfunctions_t> + (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 <const RunnerLineImpl::tfunc_t *, + const RunnerLineImpl::tfunc_t> + (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 <const RunnerLineImpl::valfunction_t *, + const RunnerLineImpl::valfunction_t> + (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 <BlChannel> (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 <BlChannel> (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 <BlChannel> (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 <std::string::size_type> (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 <unsigned long> (peek32 (addr) ) ); + result= peek32 (addr); +} + +void RunnerLineImpl::val_PROGRAMPTR (BlResult & result) +{ + gettoken (); + //result= BlNumber (size_t (program.programptr () ) ); + result= static_cast <BlInteger> (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 <BlInteger> (runner.geterr () ); + gettoken (); +} + +void RunnerLineImpl::val_ERL (BlResult & result) +{ + result= static_cast <BlInteger> (runner.geterrline () ); + gettoken (); +} + +void RunnerLineImpl::val_FIX (BlResult & result) +{ + valnumericfunc (auxFIX, result); +} + +void RunnerLineImpl::val_XMOUSE (BlResult & result) +{ + result= static_cast <BlInteger> (graphics::xmouse () ); + gettoken (); +} + +void RunnerLineImpl::val_YMOUSE (BlResult & result) +{ + result= static_cast <BlInteger> (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 <unsigned char> + (c - 'a' + base); + else if (c >= 'A' && c <= 'Z') + c= static_cast <unsigned char> + (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 <int> vparam; + util::auto_buffer <int> 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 <int> vparam; + while (token.code == ',') + { + expect (result); + vparam.push_back (result.integer () ); + ++nparams; + } + requiretoken (')'); + gettoken (); + //util::auto_buffer <int> 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 <size_t> + (addrvarnumber (varname) ); + break; + case VarInteger: + addr= reinterpret_cast <size_t> + (addrvarinteger (varname) ); + break; + case VarString: + addr= reinterpret_cast <size_t> + (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 <size_t> + (addrdimnumber (varname, dims) ); + break; + case VarInteger: + addr= reinterpret_cast <size_t> + (addrdiminteger (varname, dims) ); + break; + case VarString: + addr= reinterpret_cast <size_t> + (addrdimstring (varname, dims) ); + break; + default: + throw ErrBlassicInternal; + } + gettoken (); + } + break; + default: + throw ErrSyntax; + } + //result= BlNumber (addr); + result= static_cast <BlInteger> (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 <const float *> (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 <BlNumber> + ( (* reinterpret_cast <const double *> (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 <BlInteger> (graphics::test (x, y, false) ); +} + +void RunnerLineImpl::val_TESTR (BlResult & result) +{ + expecttoken ('('); + int x= expectinteger (); + requiretoken (','); + int y= expectinteger (); + requiretoken (')'); + gettoken (); + result= static_cast <BlInteger> (graphics::test (x, y, true) ); +} + +void RunnerLineImpl::val_POS (BlResult & result) +{ + result= static_cast <BlInteger> (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 <char *> (& f), sizeof (float) ); + result= str; +} + +void RunnerLineImpl::val_MKD_S (BlResult & result) +{ + getparenarg (result); + double f= result.number (); + std::string str (reinterpret_cast <char *> (& 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 <BlChar> (expectnum () ); + if (token.code == ',') + { + to= static_cast <BlChar> (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 <BlErrNo> (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 <UsingNumeric *> (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 <RunnerLineImpl> pfnrunnerline + // (new RunnerLineImpl (runner) ); + //CodeLine & fncodeline= pfnrunnerline->getcodeline (); + //fncodeline= f.getcode (); + + CodeLine fncodeline (f.getcode () ); + auto_ptr <RunnerLineImpl> 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 <BlNumber> (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 |