// runnerline_instructions.cpp // Revision 24-apr-2009 #include "runnerline_impl.h" #include "error.h" #include "runner.h" #include "program.h" #include "sysvar.h" #include "graphics.h" #include "util.h" using util::to_string; #include "memory.h" #include "directory.h" #include "charset.h" #include "trace.h" #include using std::auto_ptr; #include using std::cerr; using std::endl; using std::flush; #include #include #include #define ASSERT assert namespace sysvar= blassic::sysvar; namespace onbreak= blassic::onbreak; using namespace blassic::file; #define requiretoken(c) if (token.code == c) ; else throw ErrSyntax #define expecttoken(c) \ do { \ gettoken (); \ if (token.code != c) throw ErrSyntax; \ } while (false) bool RunnerLineImpl::do_Colon () { return false; } bool RunnerLineImpl::do_ENDLINE () { return true; } bool RunnerLineImpl::do_INTEGER () { // goto omitido if (codprev != keyELSE && codprev != keyTHEN) { if (codprev != keyGOTO && codprev != keyGOSUB) throw ErrSyntax; if (! sysvar::hasFlags1 (sysvar::ThenOmitted) ) throw ErrBlassicInternal; // Not ErrSyntax, this is not supposed to happen. } BlLineNumber bln= evallinenumber (); require_endsentence (); if (codprev != keyGOSUB) runner.goto_to (bln); else gosub_line (bln); return true; } bool RunnerLineImpl::do_NUMBER () { return do_INTEGER (); } bool RunnerLineImpl::do_END () { errorifparam (); #if 1 runner.close_all (); runner.setstatus (Runner::Ended); return true; #else throw blassic::ProgramEnd (); #endif } void RunnerLineImpl::do_list (BlChannel nfile) { // Same function used for LIST and LLIST, differing only // in the default channel. //BlChannel nfile= (token.code == keyLLIST) ? // PrinterChannel : DefaultChannel; gettoken (); if (token.code == '#') { nfile= expectchannel (); if (!endsentence () ) { requiretoken (','); gettoken (); } } BlLineNumber iniline, endline; evallinerange (iniline, endline); require_endsentence (); program.list (iniline, endline, getfile (nfile) ); } bool RunnerLineImpl::do_LIST () { do_list (DefaultChannel); return false; } bool RunnerLineImpl::do_LLIST () { do_list (PrinterChannel); return false; } bool RunnerLineImpl::do_REM () { return true; } bool RunnerLineImpl::do_LOAD () { using std::ifstream; using std::ios; std::string progname= expectstring (); if (endsentence () ) { program.load (progname); runner.setstatus (Runner::Ended); return true; } requiretoken (','); gettoken (); if (token.code == keyIDENTIFIER && typeofvar (token.str) == VarString) { std::string varname= token.str; gettoken (); require_endsentence (); ifstream is (progname.c_str (), ios::binary | ios::in); // Without explicit in read doesn't work on hp-ux, // I don't know why. if (! is.is_open () ) throw ErrFileNotFound; is.seekg (0, std::ios::end); size_t size= is.tellg (); is.seekg (0, std::ios::beg); std::string result; // Opening a block to avoid an error on Borland C++ that // calls ~auto_buffer when throwing ErrFileNotFound { util::auto_buffer aux (size); is.read (aux, size); result.assign (aux, size); } assignvarstring (varname, result); } else { char * init; { BlNumber bn= evalnum (); init= reinterpret_cast (size_t (bn) ); } size_t len= 0; if (token.code == ',') { BlNumber bn= expectnum (); len= size_t (bn); } require_endsentence (); ifstream is (progname.c_str (), std::ios::binary); if (! is.is_open () ) throw ErrFileNotFound; if (len == 0) { is.seekg (0, std::ios::end); len= is.tellg (); is.seekg (0, std::ios::beg); } is.read (init, len); } return false; } bool RunnerLineImpl::do_SAVE () { using std::ofstream; std::string progname= expectstring (); if (endsentence () ) program.save (progname); else { requiretoken (','); expecttoken (keyIDENTIFIER); if (token.str.size () != 1) throw ErrSyntax; char c= char (toupper (token.str [0] ) ); switch (c) { case 'A': gettoken (); require_endsentence (); { //BlFileRegular fout (progname, BlFile::Output); //program.list (LineBeginProgram, // LineEndProgram, fout); auto_ptr pfout (newBlFileRegular (progname, Output) ); program.list (LineBeginProgram, LineEndProgram, * pfout); } break; case 'B': gettoken (); if (token.code != ',') throw ErrSyntax; gettoken (); { const char * init; size_t len; if (token.code == keyIDENTIFIER && typeofvar (token.str) == VarString) { std::string * pstr= addrvarstring (token.str); init= pstr->data (); len= pstr->size (); gettoken (); } else { BlNumber bn= evalnum (); init= reinterpret_cast (size_t (bn) ); if (token.code != ',') throw ErrSyntax; bn= expectnum (); len= size_t (bn); } require_endsentence (); ofstream os (progname.c_str (), std::ios::binary); if (! os.is_open () ) throw ErrFileNotFound; os.write (init, len); } break; default: throw ErrSyntax; } } return false; } bool RunnerLineImpl::do_NEW () { errorifparam (); program.renew (); make_clear (); return true; } bool RunnerLineImpl::do_EXIT () { TRACEFUNC (tr, "RunnerLineImpl::do_EXIT"); gettoken (); int r= 0; if (! endsentence () ) { BlNumber n= evalnum (); if (! endsentence () ) throw ErrSyntax; r= int (n); } throw Exit (r); } bool RunnerLineImpl::do_RUN () { gettoken (); BlLineNumber dest= LineBeginProgram; if (token.code == keyNUMBER || token.code == keyINTEGER) { dest= evallinenumber (); require_endsentence (); } else if (! endsentence () ) { std::string progname= evalstring (); require_endsentence (); program.renew (); program.load (progname); } make_clear (); runner.clearreadline (); runner.run_to (dest); return true; } bool RunnerLineImpl::do_FOR () { const BlInteger one= 1; ProgramPos posfor (getposactual () ); expecttoken (keyIDENTIFIER); std::string varname= token.str; expecttoken ('='); std::auto_ptr pelement; switch (typeofvar (varname) ) { case VarNumber: { BlNumber initial= expectnum (); requiretoken (keyTO); BlNumber final= expectnum (); BlNumber step= (token.code == keySTEP) ? expectnum () : 1.0; pelement.reset (newForElementNumber (varname, posfor, initial, final, step) ); } break; case VarInteger: { BlInteger initial= expectinteger (); requiretoken (keyTO); BlInteger final= expectinteger (); BlInteger step= (token.code == keySTEP) ? expectinteger () : one; pelement.reset (newForElementInteger (varname, posfor, initial, final, step) ); } break; default: throw ErrMismatch; } switch (token.code) { case ':': pelement->nextchunk (); break; case keyENDLINE: if (posfor.getnum () != LineDirectCommand) pelement->nextline (); else pelement->nextchunk (); break; default: throw ErrSyntax; } runner.push_for (pelement.get () ); pelement.release (); return false; } bool RunnerLineImpl::do_NEXT () { gettoken (); //std::string varname; if (endsentence () ) { #if 0 //if (runner.for_empty () ) // throw ErrNextWithoutFor; ForElement & fe= runner.for_top (); if (fe.next () ) { runner.jump_to (fe.getpos () ); return true; } runner.for_pop (); return false; #else return runner.next (); #endif } for (;;) { requiretoken (keyIDENTIFIER); //varname= token.str; //gettoken (); #if 0 //if (runner.for_empty () ) // throw ErrNextWithoutFor; ForElement * pfe= & runner.for_top (); //if (! varname.empty () && ! fe.isvar (varname) ) if (! pfe->isvar (token.str) ) { if (sysvar::get (sysvar::TypeOfNextCheck) == 0) { if (showdebuginfo () ) cerr << "Processing NEXT " << token.str << " but current FOR is " << pfe->var () << endl; throw ErrNextWithoutFor; } else { // In ZX style NEXT can be omitted. do { runner.for_pop (); pfe= & runner.for_top (); } while (! pfe->isvar (token.str) ); } } if (pfe->next () ) { runner.jump_to (pfe->getpos () ); return true; } runner.for_pop (); #else if (runner.next (token.str) ) return true; #endif gettoken (); if (endsentence () ) break; requiretoken (','); //expecttoken (keyIDENTIFIER); //varname= token.str; gettoken (); } return false; } bool RunnerLineImpl::do_IF () { BlNumber result= expectnum (); if (token.code != keyTHEN) { // First check if THEN can be omitted. if (! sysvar::hasFlags1 (sysvar::ThenOmitted) ) throw ErrSyntax; // If that case, check the valid instructions. if (token.code != keyGOTO && token.code != keyGOSUB) throw ErrSyntax; } if (result != 0) return false; else { // Search ELSE, with possible nested IFs. unsigned int iflevel= 1; do { gettoken (); switch (token.code) { case keyIF: ++iflevel; break; case keyELSE: --iflevel; break; default: break; } } while (iflevel > 0 && token.code != keyENDLINE); if (token.code == keyELSE) { // Continue processing the line in the position // after the ELSE. The flag marks the ELSE must // be entered, otherwise is supposed that has // encountered as the end of other instruction. fInElse= true; return false; } else { // Only can be ENDLINE. return true; } } } bool RunnerLineImpl::do_TRON () { gettoken (); bool fLine= false; if (token.code == keyLINE) { fLine= true; gettoken (); } //BlChannel channel= DefaultChannel; //if (token.code == '#') // channel= expectchannel (); BlChannel channel= evaloptionalchannel (); require_endsentence (); runner.tron (fLine, channel); return false; } bool RunnerLineImpl::do_TROFF () { errorifparam (); runner.troff (); return false; } #if 0 void RunnerLineImpl::letsubindex (const std::string & varname) { Dimension dims= expectdims (); requiretoken ('='); BlResult result; expect (result); require_endsentence (); switch (typeofvar (varname) ) { case VarNumber: assigndimnumber (varname, dims, result.number () ); break; case VarInteger: assigndiminteger (varname, dims, result.integer () ); break; case VarString: assigndimstring (varname, dims, result.str () ); break; default: throw ErrBlassicInternal; } } #endif bool RunnerLineImpl::do_IDENTIFIER () { #if 0 requiretoken (keyIDENTIFIER); std::string varname= token.str; gettoken (); if (token.code == '(') { letsubindex (varname); return false; } //requiretoken ('='); // Crash on hp-ux when failed. Compiler fault? // Workaround: if (token.code != '=') throw ErrSyntax; BlResult result; expect (result); require_endsentence (); switch (typeofvar (varname) ) { case VarNumber: assignvarnumber (varname, result.number () ); break; case VarInteger: assignvarinteger (varname, result.integer () ); break; case VarString: assignvarstring (varname, result.str () ); break; default: throw ErrBlassicInternal; } #else eval_let (); require_endsentence (); #endif return false; } bool RunnerLineImpl::do_LET () { gettoken (); return do_IDENTIFIER (); } bool RunnerLineImpl::do_GOTO () { gettoken (); BlLineNumber dest= evallinenumber (); require_endsentence (); runner.goto_to (dest); return true; } bool RunnerLineImpl::do_GOSUB () { gettoken (); BlLineNumber dest= evallinenumber (); require_endsentence (); gosub_line (dest); return true; } bool RunnerLineImpl::do_STOP () { errorifparam (); #if 1 BlFile & f= getfile0 (); f << "**Stopped**"; //if (line.number () != 0) // f << " in " << line.number (); //if (pline->number () != 0) // f << " in " << pline->number (); if (codeline.number () != LineDirectCommand) f << " in " << codeline.number (); //f << '\n'; f.endline (); //ProgramPos posbreak (runner.getposactual () ); ProgramPos posbreak (getposactual () ); posbreak.nextchunk (); runner.set_break (posbreak); runner.setstatus (Runner::Stopped); return true; #else throw blassic::ProgramStop (); #endif } bool RunnerLineImpl::do_CONT () { errorifparam (); runner.jump_break (); return true; } bool RunnerLineImpl::do_CLEAR () { gettoken (); switch (token.code) { case keyINK: gettoken (); require_endsentence (); graphics::clearink (); break; case keyINPUT: gettoken (); require_endsentence (); runner.clean_input (); break; default: if (! endsentence () ) throw ErrSyntax; make_clear (); break; } return false; } bool RunnerLineImpl::do_RETURN () { gettoken (); BlLineNumber line= LineEndProgram; if (! endsentence () ) { line= evallinenumber (); require_endsentence (); } ProgramPos pos; runner.gosub_pop (pos); if (line != LineEndProgram) runner.goto_to (line); else runner.jump_to (pos); return true; } bool RunnerLineImpl::do_POKE () { BlNumber bnAddr= expectnum (); requiretoken (','); //BlChar * addr= (BlChar *) (unsigned int) bnAddr; BlChar * addr= (BlChar *) (size_t) bnAddr; BlNumber bnValue= expectnum (); require_endsentence (); BlChar value= (BlChar) (unsigned int) bnValue; * addr= value; return false; } bool RunnerLineImpl::do_READ () { // Yes, I know this function has many gotos and is poorly // structured. Maybe someday I rewrite it. In the // meantime, if someone write a good replacement and send // it to me, I will put his/her name here in upper case ;) //TRACEFUNC (tr, "RunnerLineImpl::do_READ"); ListVarPointer lvp; gettoken (); evalmultivarpointer (lvp); require_endsentence (); if (lvp.empty () ) { if (showdebuginfo () ) cerr << "Empty READ var list" << endl; throw ErrSyntax; } ListVarPointer::iterator itvp= lvp.begin (); const ListVarPointer::iterator itvpend= lvp.end (); BlLineNumber & datanumline= runner.getdatanumline (); BlChunk & datachunk= runner.getdatachunk (); unsigned short & dataelem= runner.getdataelem (); //CodeLine dataline= program.getline (datanumline); CodeLine dataline; program.getline (datanumline, dataline); CodeLine::Token datatok; otra: if (dataline.number () == LineEndProgram) throw ErrDataExhausted; if (dataline.number () > datanumline) { datachunk= 0; dataelem= 0; } //datatok= dataline.gettoken (); dataline.gettoken (datatok); if (dataline.chunk () < datachunk) { while (dataline.chunk () < datachunk) { //datatok= dataline.gettoken (); dataline.gettoken (datatok); if (datatok.code == keyENDLINE) break; } if (datatok.code != keyENDLINE) //datatok= dataline.gettoken (); dataline.gettoken (datatok); } if (datatok.code == keyENDLINE) { //cerr << "Read searching next line" << endl; //dataline= program.getnextline (dataline); program.getnextline (dataline); goto otra; } otra2: while (datatok.code != keyDATA) { dataelem= 0; datachunk= dataline.chunk (); do { //datatok= dataline.gettoken (); dataline.gettoken (datatok); } while (datatok.code != keyENDLINE && dataline.chunk () == datachunk); if (datatok.code == keyENDLINE) { //cerr << "Read searching next line" << endl; //dataline= program.getnextline (dataline); program.getnextline (dataline); if (dataline.number () == LineEndProgram) throw ErrDataExhausted; //datatok= dataline.gettoken (); dataline.gettoken (datatok); } else //datatok= dataline.gettoken (); dataline.gettoken (datatok); } datatok= dataline.getdata (); unsigned short elem= 0; otra3: while (elem < dataelem) { //datatok= dataline.gettoken (); dataline.gettoken (datatok); if (datatok.code == ':') { //datatok= dataline.gettoken (); dataline.gettoken (datatok); dataelem= 0; goto otra2; } if (datatok.code == keyENDLINE) { //cerr << "Read searching next line" << endl; //dataline= program.getnextline (dataline); program.getnextline (dataline); goto otra; } if (datatok.code != ',') { if (showdebuginfo () ) cerr << "DATA invalid on line " << dataline.number () << endl; throw ErrSyntax; } datatok= dataline.getdata (); // Supressed this check to allow a comma at end of data. //if (datatok.isendsentence () ) // throw ErrSyntax; ++elem; } //cerr << "(Data en linea " << dataline.number () << // " '" << datatok.str << "' " << // datatok.integer () << ')' << flush; datanumline= dataline.number (); datachunk= dataline.chunk (); dataelem= (unsigned short) (elem + 1); switch (itvp->type) { case VarNumber: * itvp->pnumber= datatok.number (); break; case VarInteger: switch (datatok.code) { case keyINTEGER: * itvp->pinteger= datatok.valueint; break; case keyENDLINE: * itvp->pinteger= 0; break; case keySTRING: { BlResult r= datatok.number (); * itvp->pinteger= r.integer (); } break; default: if (showdebuginfo () ) cerr << "DATA mismatch on line " << dataline.number () << endl; throw ErrMismatch; } break; #if 0 case VarString: switch (datatok.code) { case keySTRING: * itvp->pstring= datatok.str; break; case keyINTEGER: * itvp->pstring= to_string (datatok.valueint); break; case keyENDLINE: * itvp->pstring= std::string (); break; default: if (showdebuginfo () ) cerr << "Unexpected token in DATA in line" << datanumline << endl; throw ErrBlassicInternal; } break; #endif case VarString: case VarStringSlice: { std::string value; switch (datatok.code) { case keySTRING: value= datatok.str; break; case keyINTEGER: value= to_string (datatok.valueint); break; case keyENDLINE: value= std::string (); break; default: if (showdebuginfo () ) cerr << "Unexpected token in DATA " "in line" << datanumline << endl; throw ErrBlassicInternal; } if (itvp->type == VarString) * itvp->pstring= value; else assignslice (* itvp, BlResult (value) ); } break; default: if (showdebuginfo () ) cerr << "Unexpected var type in READ " "of DATA in line" << datanumline << endl; throw ErrBlassicInternal; } if (++itvp != itvpend) goto otra3; return false; } bool RunnerLineImpl::do_DATA () { do { gettoken (); } while (! endsentence () ); return false; } bool RunnerLineImpl::do_RESTORE () { TRACEFUNC (tr, "RunnerLineImpl::do_restore"); gettoken (); if (! endsentence () ) { BlLineNumber bln= evallinenumber (); require_endsentence (); TRMESSAGE (tr, std::string ("Restoring to ") + to_string (bln) ); runner.setreadline (bln); } else { TRMESSAGE (tr, "Restoring to begin"); runner.clearreadline (); } return false; } namespace { struct clearvar { void operator () (VarPointer & vt) const { switch (vt.type) { case VarNumber: * vt.pnumber= 0; break; case VarInteger: * vt.pinteger= 0; break; case VarString: vt.pstring->erase (); break; default: throw ErrBlassicInternal; } } }; bool isdelimdiscardingwhite (std::istream & is, char delim) { char c; while (is.get (c) ) { if (c == delim) { is.putback (c); return true; } if (! isspace (c) ) { is.putback (c); return false; } } return true; // We count eof as delimiter } void parsestring (std::string & result, std::istringstream & iss, char quote, char delimiter) { std::string str; char c; bool atend= isdelimdiscardingwhite (iss, delimiter); if (! atend) { iss >> c; if (! iss) atend= true; } if (! atend) { if (c == quote) { #if 0 while (iss >> c && c != quote) { str+= c; } #else while (iss >> c) { if (c != quote) str+= c; else { iss >> c; if (! iss) break; if (c == quote) str+= c; else { iss.unget (); break; } } } #endif } else { while (iss && c != delimiter) { str+= c; iss >> c; } if (iss) iss.unget (); } } //* vp.pstring= str; result= str; } void parsenumber (BlNumber & result, std::istringstream & iss, char /* quote*/, char delimiter) { BlNumber n; if (isdelimdiscardingwhite (iss, delimiter) ) n= 0; else { iss >> n; if (! iss) throw ErrMismatch; } //* vp.pnumber= n; result= n; } void parseinteger (BlInteger & result, std::istringstream & iss, char /* quote*/, char delimiter) { BlInteger n; if (isdelimdiscardingwhite (iss, delimiter) ) n= 0; else { iss >> n; if (! iss) throw ErrMismatch; } //* vp.pinteger= n; result= n; } } // namespace bool RunnerLineImpl::do_INPUT () { gettoken (); BlChannel channel= DefaultChannel; if (token.code == '#') { channel= expectchannel (); requiretoken (','); gettoken (); } std::string prompt; bool lineend= true; if (token.code == ';') { lineend= false; gettoken (); } switch (token.code) { case keySTRING: prompt= token.str; gettoken (); switch (token.code) { // This was reversed, corected on 3-jun-2003 case ';': prompt+= "? "; break; case ',': break; default: throw ErrSyntax; } std::cout << flush; gettoken (); break; default: prompt= "? "; } // Parse the list of variables. ListVarPointer inputvars; evalmultivarpointer (inputvars); require_endsentence (); if (inputvars.empty () ) throw ErrSyntax; // Prepare input channel std::string input; BlFile & in= getfile (channel); char quote= in.quote (); char delimiter= in.delimiter (); // Accept line from input for (;;) { if (channel == DefaultChannel) { runner.clean_input (); //cursorvisible (); } if (! prompt.empty () ) { if (channel == DefaultChannel || in.istextwindow () ) { in << prompt; in.flush (); } } in.getline (input, lineend); if (fInterrupted) { std::cin.clear (); if (runner.getbreakstate () != onbreak::BreakCont) // This throw causes an error in windows, // we return and let the caller catch // the interrupted condition instead. //throw BlBreak (); return false; else { fInterrupted= false; //in << '\n'; in.endline (); continue; } } break; } // Process the line entered // We must take into account that an whitespace can be a delimiter // (tipically a tab). std::istringstream iss (input); iss.unsetf (std::ios::skipws); ListVarPointer::iterator itvp= inputvars.begin (); const ListVarPointer::iterator itvpend= inputvars.end (); for ( ; itvp != itvpend; ++itvp) { VarPointer & vp= * itvp; switch (vp.type) { case VarNumber: parsenumber (* vp.pnumber, iss, quote, delimiter); break; case VarInteger: parseinteger (* vp.pinteger, iss, quote, delimiter); break; case VarString: parsestring (* vp.pstring, iss, quote, delimiter); break; case VarStringSlice: { std::string str; parsestring (str, iss, quote, delimiter); assignslice (vp, BlResult (str) ); } break; default: throw ErrBlassicInternal; } if (isdelimdiscardingwhite (iss, delimiter) ) { if (iss.eof () ) { //++i; ++itvp; break; } else iss.get (); } else throw ErrMismatch; } // If not enough data entered, clear remaining vars. std::for_each (itvp, itvpend, clearvar () ); return false; } void RunnerLineImpl::do_line_input () { #if 0 gettoken (); BlChannel channel= DefaultChannel; if (token.code == '#') { channel= expectchannel (); requiretoken (','); gettoken (); } #else gettoken (); BlChannel channel= DefaultChannel; if (token.code == '#') { channel= expectchannel (); requiretoken (','); gettoken (); } std::string prompt; bool lineend= true; if (token.code == ';') { lineend= false; gettoken (); } switch (token.code) { case keySTRING: prompt= token.str; gettoken (); switch (token.code) { // This was reversed, corected on 3-jun-2003 case ';': prompt+= "? "; break; case ',': break; default: throw ErrSyntax; } std::cout << flush; gettoken (); break; default: prompt= "? "; } #endif requiretoken (keyIDENTIFIER); std::string varname= token.str; gettoken (); require_endsentence (); if (typeofvar (varname) != VarString) throw ErrMismatch; std::string value; BlFile & in= getfile (channel); if (channel == DefaultChannel) { runner.clean_input (); //cursorvisible (); } for (;;) { if (! prompt.empty () ) { if (channel == DefaultChannel || in.istextwindow () ) { in << prompt; in.flush (); } } in.getline (value, lineend); if (fInterrupted) { std::cin.clear (); if (runner.getbreakstate () != onbreak::BreakCont) return; else { fInterrupted= false; //in << '\n'; in.endline (); continue; } } break; } assignvarstring (varname, value); } bool RunnerLineImpl::do_LINE () { gettoken (); if (token.code == keyINPUT) { do_line_input (); return false; } graphics::Point from; if (token.code == '-') from= graphics::getlast (); else { requiretoken ('('); BlInteger x= expectinteger (); requiretoken (','); BlInteger y= expectinteger (); requiretoken (')'); expecttoken ('-'); from= graphics::Point (x, y); } expecttoken ('('); BlInteger x= expectinteger (); requiretoken (','); BlInteger y= expectinteger (); requiretoken (')'); gettoken (); enum Type { TypeLine, TypeRect, TypeFillRect }; Type type= TypeLine; if (! endsentence () ) { requiretoken (','); gettoken (); if (token.code != ',') { BlInteger color= evalinteger (); graphics::setcolor (color); } if (token.code == ',') { gettoken (); requiretoken (keyIDENTIFIER); if (token.str == "B") type= TypeRect; else if (token.str == "BF") type= TypeFillRect; else throw ErrSyntax; gettoken (); } require_endsentence (); } switch (type) { case TypeLine: graphics::move (from.x, from.y); graphics::line (x, y); break; case TypeRect: graphics::rectangle (from, graphics::Point (x, y) ); break; case TypeFillRect: graphics::rectanglefilled (from, graphics::Point (x, y) ); break; } return false; } bool RunnerLineImpl::do_RANDOMIZE () { gettoken (); unsigned int seedvalue; if (endsentence () ) seedvalue= time (NULL); else { BlNumber result= evalnum (); //errorifparam (); require_endsentence (); //seedvalue= static_cast (result); seedvalue= util::checked_cast (result, ErrBadSubscript); } #if 0 randgen.seed (seedvalue); #else runner.seedrandom (seedvalue); #endif return false; } bool RunnerLineImpl::do_AUTO () { if (codeline.number () != LineDirectCommand) throw ErrInvalidCommand; BlLineNumber auto_ini= sysvar::get32 (sysvar::AutoInit), auto_inc= sysvar::get32 (sysvar::AutoInc); gettoken (); if (! endsentence () ) { if (token.code != ',') { switch (token.code) { case keyNUMBER: auto_ini= (BlLineNumber) token.number (); break; case keyINTEGER: auto_ini= token.integer (); break; default: throw ErrSyntax; } gettoken (); } if (token.code == ',') { gettoken (); switch (token.code) { case keyNUMBER: auto_inc= (BlLineNumber) token.number (); break; case keyINTEGER: auto_inc= token.integer (); break; default: throw ErrSyntax; } gettoken (); } require_endsentence (); } #if 0 blnAuto= auto_ini; blnAutoInc= auto_inc; #else runner.setauto (auto_ini, auto_inc); #endif return false; } bool RunnerLineImpl::do_DIM () { TRACEFUNC (tr, "RunnerLineImpl::do_DIM"); do { expecttoken (keyIDENTIFIER); std::string varname= token.str; Dimension dims= expectdims (); switch (typeofvar (varname) ) { case VarNumber: dimvarnumber (varname, dims); break; case VarInteger: dimvarinteger (varname, dims); break; case VarString: dimvarstring (varname, dims); break; default: throw ErrBlassicInternal; } } while (token.code == ','); require_endsentence (); return false; } bool RunnerLineImpl::do_SYSTEM () { TRACEFUNC (tr, "RunnerLineImpl::do_SYSTEM"); errorifparam (); throw Exit (); } bool RunnerLineImpl::do_ON () { enum TypeOn { OnNoValid, OnGoto, OnGosub }; gettoken (); switch (token.code) { case keyERROR: { expecttoken (keyGOTO); gettoken (); // Allow use line 0 if using a label. bool islabel= token.code == keyIDENTIFIER; BlLineNumber bln= evallinenumber (); require_endsentence (); if (bln != 0 || islabel) runner.seterrorgoto (bln, codeline.number () ); else runner.clearerrorgoto (); } return false; case keyBREAK: gettoken (); switch (token.code) { case keySTOP: runner.setbreakstate (onbreak::BreakStop); break; case keyCONT: runner.setbreakstate (onbreak::BreakCont); break; case keyGOSUB: { gettoken (); BlLineNumber bln= evallinenumber (); errorifparam (); runner.setbreakgosub (bln); } break; default: throw ErrSyntax; } return false; case keySharp: { BlChannel ch= expectchannel (); requiretoken (keyGOSUB); gettoken (); BlLineNumber line= evallinenumber (); require_endsentence (); runner.pollchannel (ch, line); } return false; default: break; } BlNumber bn= evalnum (); size_t n= (size_t) bn; TypeOn type= OnNoValid; switch (token.code) { case keyGOTO: type= OnGoto; break; case keyGOSUB: type= OnGosub; break; default: throw ErrSyntax; } BlLineNumber bln; std::vector line; do { gettoken (); bln= evallinenumber (); line.push_back (bln); } while (token.code == ','); require_endsentence (); size_t l= line.size (); if (n > 0 && n <= l) { switch (type) { case OnGoto: runner.goto_to (line [n - 1] ); break; case OnGosub: gosub_line (line [n - 1] ); break; default: ; // Avoid a warning } return true; } return false; } bool RunnerLineImpl::do_ERROR () { BlNumber blCod= expectnum (); require_endsentence (); throw BlErrNo (blCod); } bool RunnerLineImpl::do_OPEN () { TRACEFUNC (tr, "RunnerLineImpl::do_OPEN"); BlCode op= token.code; BlChannel channel= DefaultChannel; std::string filename; OpenMode mode= Input; size_t record_len= 128; bool binary= false; // Defined here to avoid an error in C++ Builder auto_ptr pbf; gettoken (); if (token.code == keyERROR) { if (op != keyOPEN) throw ErrSyntax; op= keyERROR; expecttoken (keyAS); //gettoken (); //if (token.code == '#') // gettoken (); //channel= evalchannel (); channel= expectrequiredchannel (); } else { BlResult result; eval (result); std::string firstarg= result.str (); switch (token.code) { case keyFOR: // Newer syntax. filename= firstarg; gettoken (); if (token.code == keyBINARY) { binary= true; gettoken (); } switch (token.code) { case keyINPUT: mode= Input; break; case keyOUTPUT: mode= Output; break; case keyAPPEND: mode= Append; break; case keyRANDOM: mode= Random; break; case keyLPRINT: if (op != keyOPEN) throw ErrSyntax; mode= Append; channel= PrinterChannel; gettoken (); break; default: throw ErrSyntax; } if (channel == DefaultChannel) { expecttoken (keyAS); //gettoken (); //if (token.code == '#') // gettoken (); //channel= evalchannel (); channel= expectrequiredchannel (); if (token.code == keyLEN) { expecttoken ('='); gettoken (); BlNumber bn= evalnum (); record_len= size_t (bn); if (mode != Random) throw ErrFileMode; } } break; case keyAS: filename= firstarg; if (op == keyPOPEN) mode= InOut; //gettoken (); //if (token.code == '#') // gettoken (); //channel= evalchannel (); channel= expectrequiredchannel (); break; case ',': // Older syntax. if (firstarg == "I" || firstarg == "i") mode= Input; else if (firstarg == "O" || firstarg == "o") mode= Output; else if (firstarg == "A" || firstarg == "a") mode= Append; else if (firstarg == "R" || firstarg == "r") mode= Random; else throw ErrFileMode; //gettoken (); //if (token.code == '#') // gettoken (); //channel= evalchannel (); channel= expectrequiredchannel (); requiretoken (','); filename= expectstring (); if (token.code == ',') { if (mode != Random) throw ErrFileMode; BlNumber bn= expectnum (); record_len= size_t (bn); } break; default: throw ErrSyntax; } // switch } // if require_endsentence (); if (channel == DefaultChannel) throw ErrFileNumber; // BINARY is ignored except with regular file. //auto_ptr pbf; switch (op) { case keyOPEN: if (mode == Random) pbf. reset (newBlFileRandom (filename, record_len) ); else { if (binary) mode= OpenMode (mode | Binary); pbf.reset (newBlFileRegular (filename, mode) ); } break; case keyPOPEN: if (mode != Input && mode != Output && mode != InOut) throw ErrFileMode; pbf.reset (newBlFilePopen (filename, mode) ); break; case keyERROR: pbf.reset (newBlFileOutput (cerr) ); break; default: throw ErrBlassicInternal; } runner.setfile (channel, pbf.get () ); pbf.release (); return false; } bool RunnerLineImpl::do_POPEN () { TRACEFUNC (tr, "RunnerLineImpl::do_POPEN"); OpenMode mode= InOut; std::string filename= expectstring (); bool witherror= false; if (token.code == keyERROR) { witherror= true; gettoken (); } if (token.code == keyFOR) { gettoken (); switch (token.code) { case keyINPUT: mode= Input; break; case keyOUTPUT: mode= Output; break; default: throw ErrSyntax; } gettoken (); } requiretoken (keyAS); BlChannel channel= expectrequiredchannel (); require_endsentence (); if (witherror) mode= OpenMode (mode | WithErr); auto_ptr pbf (newBlFilePopen (filename, mode) ); runner.setfile (channel, pbf.get () ); pbf.release (); return false; } bool RunnerLineImpl::do_CLOSE () { gettoken (); if (endsentence () ) { runner.close_all (); return false; } if (token.code == keyINPUT || token.code == keyOUTPUT) { bool isinput= token.code == keyINPUT; for (;;) { BlChannel channel= expectrequiredchannel (); BlFile & f= getfile (channel); if (isinput) f.closein (); else f.closeout (); if (token.code != ',') break; } require_endsentence (); return false; } for (;;) { BlChannel channel; if (token.code == keyLPRINT) { channel= PrinterChannel; gettoken (); } else { if (token.code == '#') gettoken (); channel= evalchannel (); } if (channel == DefaultChannel) throw ErrFileNumber; runner.closechannel (channel); if (token.code != ',') { if (endsentence () ) break; throw ErrSyntax; } gettoken (); } return false; } bool RunnerLineImpl::do_LOCATE () { gettoken (); BlChannel ch= DefaultChannel; if (token.code == '#') { ch= expectchannel (); requiretoken (','); gettoken (); } BlInteger row= evalinteger (); requiretoken (','); BlInteger col= expectinteger (); require_endsentence (); BlFile & out= getfile (ch); if (sysvar::hasFlags1 (sysvar::LocateStyle) ) { // LOCATE style Amstrad CPC: col, row std::swap (row, col); } out.gotoxy (col - 1, row - 1); return false; } bool RunnerLineImpl::do_CLS () { //gettoken (); //BlChannel ch= DefaultChannel; //if (token.code == '#') //{ // ch= expectchannel (); //} BlChannel ch= expectoptionalchannel (); require_endsentence (); BlFile & out= getfile (ch); out.cls (); return false; } bool RunnerLineImpl::do_WRITE () { gettoken (); BlChannel channel= DefaultChannel; if (token.code == '#') { channel= expectchannel (); requiretoken (','); gettoken (); } BlFile & out= getfile (channel); BlResult result; char quote= out.quote (); for (;;) { if (token.code != ',') { eval (result); switch (result.type () ) { case VarNumber: out << result.number (); break; case VarInteger: out<< result.integer (); break; case VarString: if (quote) out << quote; out << result.str (); if (quote) out << quote; break; default: throw ErrBlassicInternal; } } if (token.code == ',') { out << out.delimiter (); gettoken (); } else break; } require_endsentence (); //out << '\n'; out.endline (); return false; } bool RunnerLineImpl::do_MODE () { //BlNumber mode= expectnum (); BlResult result; expect (result); bool spectrummode= false; //BlInteger mode; if (result.type () == VarString) { require_endsentence (); const std::string & strmode= result.str (); graphics::setmode (strmode); //mode= 1; if (strmode == "spectrum") spectrummode= true; } else { BlInteger mode= result.integer (); if (endsentence () ) graphics::setmode (mode); else { requiretoken (','); BlInteger height= expectinteger (); bool inverty= false; BlInteger zoomx= 1, zoomy= 1; if (token.code == ',') { gettoken (); if (token.code != ',') inverty= evalinteger (); if (token.code == ',') { gettoken (); if (token.code != ',') zoomx= evalinteger (); if (token.code == ',') zoomy= expectinteger (); } } require_endsentence (); graphics::setmode (mode, height, inverty, zoomx, zoomy); } } runner.destroy_windows (); runner.resetfile0 (); if (spectrummode) runner.spectrumwindows (); return false; } bool RunnerLineImpl::do_MOVE () { #if 0 BlNumber x= expectnum (); requiretoken (','); BlNumber y= expectnum (); require_endsentence (); graphics::move (int (x), int (y) ); #else BlInteger x, y; getdrawargs (x, y); graphics::move (x, y); #endif return false; } bool RunnerLineImpl::do_COLOR () { gettoken (); BlFile & out= getfile0 (); if (token.code != ',') { BlInteger color= evalinteger (); out.setcolor (color); if (graphics::ingraphicsmode () ) graphics::setcolor (color); if (endsentence () ) return false; requiretoken (','); } gettoken (); if (token.code != ',') { BlInteger back= evalinteger (); out.setbackground (back); if (graphics::ingraphicsmode () ) graphics::setbackground (back); if (endsentence () ) return false; requiretoken (','); } gettoken (); // Border color in Gwbasic. Ignored. evalinteger (); require_endsentence (); return false; } void RunnerLineImpl::do_get_image () { BlInteger x1= expectinteger (); requiretoken (','); BlInteger y1= expectinteger (); requiretoken (')'); expecttoken ('-'); expecttoken ('('); BlInteger x2= expectinteger (); requiretoken (','); BlInteger y2= expectinteger (); requiretoken (')'); expecttoken (','); gettoken (); requiretoken (keyIDENTIFIER); std::string name= token.str; gettoken (); require_endsentence (); graphics::get_image (x1, y1, x2, y2, name); } bool RunnerLineImpl::do_GET () { gettoken (); if (token.code == '(') { do_get_image (); return false; } if (token.code != keyIDENTIFIER || typeofvar (token.str) != VarString) { // GET # if (token.code == '#') gettoken (); BlChannel channel= evalchannel (); size_t pos= 0; if (token.code == ',') { BlNumber bnPos= expectnum (); pos= size_t (bnPos); if (pos == 0) throw ErrBadRecord; } require_endsentence (); BlFile & out= getfile (channel); out.get (pos); return false; } // GET var$ std::string varname= token.str; gettoken (); require_endsentence (); #if 0 std::string r= graphics::ingraphicsmode () ? graphics::getkey () : cursor::getkey (); #else std::string r= getfile0 ().getkey (); #endif if (r == "\n" && sysvar::hasFlags1 (sysvar::ConvertLFCR) ) r= "\r"; assignvarstring (varname, r); return false; } bool RunnerLineImpl::do_LABEL () { expecttoken (keyIDENTIFIER); //std::string label= token.str; gettoken (); require_endsentence (); return false; } bool RunnerLineImpl::do_DELIMITER () { gettoken (); BlChannel channel= DefaultChannel; if (token.code == '#') { channel= expectchannel (); requiretoken (','); gettoken (); } BlFile & in= getfile (channel); std::string str= evalstring (); char delim= 0, quote= 0, escape= 0; if (! str.empty () ) delim= str [0]; if (! endsentence () ) { requiretoken (','); str= expectstring (); if (! str.empty () ) quote= str [0]; if (! endsentence () ) { requiretoken(','); str= expectstring (); if (! str.empty () ) escape= str [0]; require_endsentence (); } } in.delimiter (delim); in.quote (quote); in.escape (escape); return false; } bool RunnerLineImpl::do_REPEAT () { errorifparam (); ProgramPos posrepeat (getposactual () ); if (token.code == keyENDLINE) { if (posrepeat.getnum () != LineDirectCommand) posrepeat.nextline (); else throw ErrRepeatWithoutUntil; } else posrepeat.nextchunk (); runner.repeat_push (RepeatElement (posrepeat) ); return false; } bool RunnerLineImpl::do_UNTIL () { if (runner.repeat_empty () ) throw ErrUntilWithoutRepeat; BlResult br; expect (br); bool cond= br.tobool (); require_endsentence (); if (cond) { runner.repeat_pop (); return false; } RepeatElement re= runner.repeat_top (); runner.jump_to (re.getpos () ); return true; } bool RunnerLineImpl::do_WHILE () { //ProgramPos poswhile (runner.getposactual () ); ProgramPos poswhile (getposactual () ); BlResult br; expect (br); bool cond= br.tobool (); require_endsentence (); if (cond) { if (! runner.in_wend () ) runner.while_push (WhileElement (poswhile) ); else runner.in_wend (false); return false; } if (runner.in_wend () ) { runner.while_pop (); runner.in_wend (false); } bool sameline= true; // Find the WEND. for (size_t level= 1; level > 0; ) { getnextchunk (); if (token.code == keyENDLINE) { if (codeline.number () == LineDirectCommand) throw BlError (ErrWhileWithoutWend, poswhile); program.getnextline (codeline); if (codeline.number () == LineEndProgram) throw BlError (ErrWhileWithoutWend, poswhile); sameline= false; gettoken (); } switch (token.code) { case keyWHILE: ++level; break; case keyWEND: --level; break; default: ; } } // At the WEND, check the syntax. gettoken (); require_endsentence (); if (sameline) return false; ProgramPos pos (codeline.number (), codeline.chunk () ); if (token.code == keyENDLINE) { // If direct command, is sameline. pos.nextline (); } runner.jump_to (pos); return true; } bool RunnerLineImpl::do_WEND () { errorifparam (); if (runner.while_empty () ) throw ErrWendWithoutWhile; WhileElement w= runner.while_top (); runner.jump_to (w.getpos () ); runner.in_wend (true); return true; } bool RunnerLineImpl::do_PLOT () { BlInteger x, y; std::vector points; gettoken (); if (token.code == keyTO) { points.push_back (graphics::getlast () ); } else { x= evalinteger (); requiretoken (','); y= expectinteger (); if (token.code != keyTO) { getinkparams (); graphics::plot (x, y); return false; } points.push_back (graphics::Point (int (x), int (y) ) ); } for (;;) { x= expectinteger (); requiretoken (','); y= expectinteger (); points.push_back (graphics::Point (x, y) ); if (endsentence () ) break; requiretoken (keyTO); } graphics::plot (points); return false; } bool RunnerLineImpl::do_RESUME () { ProgramPos posresume= runner.geterrpos (); BlLineNumber errline= posresume.getnum (); if (errline == LineDirectCommand || errline == LineEndProgram) throw ErrCannotResume; gettoken (); if (token.code == keyNEXT) { gettoken (); require_endsentence (); runner.resume_next (posresume); } else if (endsentence () ) { runner.jump_to (posresume); } else { BlLineNumber line= evallinenumber (); require_endsentence (); runner.goto_to (line); } runner.clearerror (); return true; } bool RunnerLineImpl::do_DELETE () { gettoken (); BlLineNumber iniline, endline; evallinerange (iniline, endline); require_endsentence (); program.deletelines (iniline, endline); runner.setstatus (Runner::Ended); return true; } bool RunnerLineImpl::do_LOCAL () { do { expecttoken (keyIDENTIFIER); runner.gosub_addlocalvar (token.str); gettoken (); } while (token.code == ','); require_endsentence (); return false; } void RunnerLineImpl::do_put_image () { BlInteger x= expectinteger (); requiretoken (','); BlInteger y= expectinteger (); requiretoken (')'); expecttoken (','); gettoken (); requiretoken (keyIDENTIFIER); std::string name= token.str; gettoken (); // Mode used. int mode= 0; if (! endsentence () ) { requiretoken (','); gettoken (); switch (token.code) { case keyXOR: mode= 1; break; case keyAND: mode= 2; break; case keyOR: mode= 3; break; case keyNOT: mode= 4; break; default: throw ErrSyntax; } gettoken (); require_endsentence (); } graphics::put_image (x, y, name, mode); } bool RunnerLineImpl::do_PUT () { gettoken (); if (token.code == '(') { do_put_image (); return false; } if (token.code == '#') gettoken (); BlChannel channel= evalchannel (); size_t pos= 0; if (token.code == ',') { BlNumber bnPos= expectnum (); pos= size_t (bnPos); } require_endsentence (); BlFile & out= getfile (channel); out.put (pos); return false; } bool RunnerLineImpl::do_FIELD () { gettoken (); enum FieldType { FieldSimple, FieldClear, FieldAppend }; FieldType type= FieldSimple; switch (token.code) { case keyCLEAR: type= FieldClear; gettoken (); break; case keyAPPEND: type= FieldAppend; gettoken (); break; default: // Nothing to do. ; } if (token.code == '#') gettoken (); BlChannel channel= evalchannel (); BlFile & out= getfile (channel); if (type == FieldClear) { require_endsentence (); out.field_clear (); return false; } std::vector fe; do { requiretoken (','); BlNumber bnSize= expectnum (); size_t size= size_t (bnSize); requiretoken (keyAS); expecttoken (keyIDENTIFIER); #if 1 // Now matrix elements are accepted as field vars. gettoken (); Dimension dim; if (token.code == '(') dim= getdims (); fe.push_back (BlFile::field_element (size, token.str, dim) ); #else fe.push_back ( BlFile::field_element (size, token.str) ); gettoken (); #endif } while (! endsentence () ); switch (type) { case FieldSimple: out.field (fe); break; case FieldAppend: out.field_append (fe); break; default: throw ErrBlassicInternal; } return false; } bool RunnerLineImpl::do_LSET () { BlFile::Align align; switch (token.code) { case keyLSET: align= BlFile::AlignLeft; break; case keyRSET: align= BlFile::AlignRight; break; default: throw ErrBlassicInternal; } // Get the variable to L/RSET. gettoken (); if (token.code != keyIDENTIFIER) throw ErrSyntax; std::string var= token.str; if (typeofvar (var) != VarString) throw ErrMismatch; //expecttoken ('='); //bool hasindex= false; Dimension dim; //std::string * pstr; gettoken (); if (token.code == '(') { // It's a matrix element. //hasindex= true; dim= getdims (); //pstr= addrdimstring (var, dim); } //else // pstr= addrvarstring (var); requiretoken ('='); const std::string value= expectstring (); require_endsentence (); // First try to assign to a field var. #if 0 // Blassic does not accept a matrix element as field. if (! hasindex) if (runner.assign_channel_var (var, value, align) ) return false; #else // Now accepts. if (runner.assign_channel_var (var, dim, value, align) ) return false; #endif // If a field with that name exists, the variable already // has been assigned, then we can return. // If there is not file field with such name, do the work // here with the previous string length. std::string * pstr= dim.empty () ? addrvarstring (var) : addrdimstring (var, dim); const std::string::size_type l= pstr->size (); if (l == 0) return false; #if 0 const std::string::size_type lvalue= value.size (); if (lvalue < l) { if (align == BlFile::AlignLeft) { * pstr= value + std::string (l - lvalue, ' '); } else { * pstr= std::string (l - lvalue, ' ') + value; } } else if (lvalue > l) { * pstr= value.substr (0, l); } else * pstr= value; #else if (align == BlFile::AlignLeft) * pstr= util::stringlset (value, l); else * pstr= util::stringrset (value, l); #endif ASSERT (pstr->size () == l); return false; } bool RunnerLineImpl::do_SOCKET () { std::string host= expectstring (); requiretoken (','); BlNumber bn= expectnum (); requiretoken (keyAS); //gettoken (); //if (token.code == '#') // gettoken (); //BlChannel channel= evalchannel (); BlChannel channel= expectrequiredchannel (); require_endsentence (); auto_ptr pbf (newBlFileSocket (host, short (bn) ) ); runner.setfile (channel, pbf.get () ); pbf.release (); return false; } bool RunnerLineImpl::do_MID_S () { expecttoken ('('); expecttoken (keyIDENTIFIER); std::string varname= token.str; if (typeofvar (varname) != VarString) throw ErrMismatch; Dimension dim; //std::string * presult; //expecttoken (','); gettoken (); if (token.code == '(') { dim= getdims (); //presult= addrdimstring (varname, dim); } //else //{ // presult= addrvarstring (varname); //} requiretoken (','); size_t inipos; { BlNumber bnInipos= expectnum (); inipos= util::checked_cast (bnInipos, ErrMismatch); } if (inipos == 0) throw ErrMismatch; --inipos; //size_t len= 0; // Initialized to avoid warning. std::string::size_type len= 0; // Initialized to avoid warning. bool fLen= false; if (token.code == ',') { BlNumber bnLen= expectnum (); len= util::checked_cast (bnLen, ErrMismatch); fLen= true; } requiretoken (')'); expecttoken ('='); std::string value= expectstring (); require_endsentence (); if (! fLen) len= std::string::npos; // First try to assign to a field var. if (runner.assign_mid_channel_var (varname, dim, value, inipos, len) ) return false; // If there is no file field, do the work here. std::string * pstr= dim.empty () ? addrvarstring (varname) : addrdimstring (varname, dim); size_t l= value.size (); if (! fLen || len > l) len= l; l= pstr->size (); if (inipos >= l) return false; if (inipos + len > l) len= l - inipos; std::copy (value.begin (), value.begin () + len, pstr->begin () + inipos); return false; } bool RunnerLineImpl::do_DRAW () { //BlNumber x= expectnum (); BlResult r; expect (r); if (r.type () == VarString) graphics::draw (r.str () ); else { BlInteger x= r.integer (); #if 0 requiretoken (','); //BlNumber y= expectnum (); expect (r); BlInteger y= r.integer (); require_endsentence (); #else BlInteger y; getdrawargs (y); #endif //graphics::line (int (x), int (y) ); graphics::line (x, y); } return false; } namespace { std::string quoteescape (const std::string & str) { std::string result; for (std::string::size_type i= 0, l= str.size (); i < l; ++i) { char c= str [i]; if (c == '"') result+= "\"\""; else result+= c; } return result; } } // namespace bool RunnerLineImpl::do_def_fn () { // Get function name. expecttoken (keyIDENTIFIER); std::string fnname= token.str; gettoken (); // Get parameters. ParameterList param; switch (token.code) { case '=': // Single sentence function without parameters break; case '(': // Function with parameters do { expecttoken (keyIDENTIFIER); param.push_back (token.str); gettoken (); } while (token.code == ','); requiretoken (')'); gettoken (); if (token.code != '=' && ! endsentence () ) throw ErrSyntax; break; default: if (endsentence () ) break; // Multi sentence function without parameters throw ErrSyntax; } // Get function body. bool retval= false; auto_ptr pf; if (endsentence () ) { // Multi sentence function. Search for FN END. ProgramPos posfn (codeline.number (), codeline.chunk () ); if (token.code == keyENDLINE) posfn.nextline (); bool sameline= true; for (bool finding= true; finding;) { getnextchunk (); if (token.code == keyENDLINE) { if (codeline.number () == LineDirectCommand) throw ErrInvalidDirect; program.getnextline (codeline); if (codeline.number () == LineEndProgram) throw ErrIncompleteDef; sameline= false; gettoken (); } switch (token.code) { case keyFN: gettoken (); if (token.code == keyEND) finding= false; //else // throw ErrSyntax; break; } } gettoken (); require_endsentence (); // Prepare to skip function body. if (! sameline) { ProgramPos pos (codeline.number (), codeline.chunk () ); if (token.code == keyENDLINE) { // Can't be direct command. pos.nextline (); } runner.jump_to (pos); retval= true; } pf.reset (new Function (posfn, param) ); } else { // Single sentence funcion gettoken (); std::string fndef ("0 "); // The "0 " is the fake line number. for ( ; ! endsentence (); gettoken () ) { if (token.code < 256) fndef+= token.code; else switch (token.code) { case keyIDENTIFIER: case keyNUMBER: fndef+= token.str; break; case keyINTEGER: fndef+= to_string (token.integer () ); break; case keySTRING: fndef+= '"'; fndef+= quoteescape (token.str); fndef+= '"'; break; default: fndef+= decodekeyword (token.code); } fndef+= ' '; } pf.reset (new Function (fndef, param) ); } // Put in table of functions. pf->insert (fnname); // No need to release pf, insert does a copy. return retval; } namespace { inline char get_letter (const std::string & str) { if (str.size () != 1) throw ErrSyntax; char c= str [0]; if (! isalpha (c) ) throw ErrSyntax; return c; } } // namespace void RunnerLineImpl::definevars (VarType type) { do { expecttoken (keyIDENTIFIER); char c= get_letter (token.str); gettoken (); if (token.code == '-') { expecttoken (keyIDENTIFIER); char c2= get_letter (token.str); definevar (type, c, c2); gettoken (); } else definevar (type, c); } while (token.code == ','); require_endsentence (); } bool RunnerLineImpl::do_DEF () { gettoken (); VarType type; switch (token.code) { case keyFN: return do_def_fn (); case keySTR: case keyINT: case keyREAL: type= token.code == keySTR ? VarString : token.code == keyINT ? VarInteger : VarNumber; #if 0 do { expecttoken (keyIDENTIFIER); char c= get_letter (token.str); gettoken (); if (token.code == '-') { expecttoken (keyIDENTIFIER); char c2= get_letter (token.str); definevar (type, c, c2); gettoken (); } else definevar (type, c); } while (token.code == ','); require_endsentence (); #else definevars (type); #endif return false; default: throw ErrSyntax; } } bool RunnerLineImpl::do_FN () { gettoken (); bool isend= true; switch (token.code) { case keyEND: break; case keyRETURN: isend= false; break; default: throw ErrSyntax; } errorifparam (); //runner.setstatus (Runner::Ended); //return true; if (! isend) { if (runner.fn_level () == 0) throw ErrUnexpectedFnEnd; while (runner.gosub_size () > 1) { ProgramPos unused; runner.gosub_pop (unused); } } //throw blassic::ProgramFnEnd (); runner.setstatus (Runner::FnEnded); return true; } bool RunnerLineImpl::do_PROGRAMARG_S () { std::vector args; gettoken (); if (! endsentence () ) { for (;;) { std::string par= evalstring (); args.push_back (par); if (endsentence () ) break; requiretoken (','); gettoken (); } } setprogramargs (args); return false; } bool RunnerLineImpl::do_ERASE () { TRACEFUNC (tr, "RunnerLineImpl::do_ERASE"); do { expecttoken (keyIDENTIFIER); std::string str (token.str); switch (typeofvar (str) ) { case VarNumber: erasevarnumber (str); break; case VarInteger: erasevarinteger (str); break; case VarString: erasevarstring (str); break; default: throw ErrBlassicInternal; } gettoken (); } while (token.code == ','); require_endsentence (); return false; } bool RunnerLineImpl::do_SWAP () { expecttoken (keyIDENTIFIER); std::string strvar1= token.str; gettoken (); Dimension dim1; bool isarray1= false; if (token.code == '(') { dim1= getdims (); isarray1= true; } requiretoken (','); gettoken (); requiretoken (keyIDENTIFIER); std::string strvar2= token.str; gettoken (); Dimension dim2; bool isarray2= false; if (token.code == '(') { dim2= getdims (); isarray2= true; } require_endsentence (); VarType type= typeofvar (strvar1); if (type != typeofvar (strvar2) ) throw ErrMismatch; switch (type) { case VarNumber: { BlNumber * pbn1= isarray1 ? addrdimnumber (strvar1, dim1) : addrvarnumber (strvar1); BlNumber * pbn2= isarray2 ? addrdimnumber (strvar2, dim2) : addrvarnumber (strvar2); std::swap (* pbn1, * pbn2); } break; case VarInteger: { BlInteger * pbi1= isarray1 ? addrdiminteger (strvar1, dim1) : addrvarinteger (strvar1); BlInteger * pbi2= isarray2 ? addrdiminteger (strvar2, dim2) : addrvarinteger (strvar2); std::swap ( * pbi1, * pbi2); } break; case VarString: { std::string * pstr1= isarray1 ? addrdimstring (strvar1, dim1) : addrvarstring (strvar1); std::string * pstr2= isarray2 ? addrdimstring (strvar2, dim2) : addrvarstring (strvar2); std::swap (* pstr1, * pstr2); } break; default: throw ErrBlassicInternal; } return false; } bool RunnerLineImpl::do_SYMBOL () { gettoken (); if (token.code == keyAFTER) { BlInteger n= expectinteger (); // Support for change of character set. if (token.code == keyAS) { using namespace charset; std::string model= expectstring (); if (model == "default") default_charset= & default_data; else if (model == "cpc") default_charset= & cpc_data; else if (model == "spectrum") default_charset= & spectrum_data; else throw ErrImproperArgument; } require_endsentence (); graphics::symbolafter (static_cast (n) ); return false; } BlNumber bnSymbol= evalnum (); unsigned char byte [8]; for (int i= 0; i < 8; ++i) byte [i]= 0; for (int i= 0; i < 8; ++i) { if (token.code != ',') { // Parameters can be omitted break; } BlNumber bnByte= expectnum (); byte [i]= static_cast (bnByte); } require_endsentence (); graphics::definesymbol ( int (bnSymbol), byte); return false; } bool RunnerLineImpl::do_ZONE () { BlInteger z= expectinteger (); require_endsentence (); sysvar::set16 (sysvar::Zone, static_cast (z) ); return false; } bool RunnerLineImpl::do_POP () { errorifparam (); ProgramPos notused; runner.gosub_pop (notused); return false; } bool RunnerLineImpl::do_NAME () { std::string strOrig= expectstring (); requiretoken (keyAS); std::string strDest= expectstring (); require_endsentence (); rename_file (strOrig, strDest); return false; } bool RunnerLineImpl::do_KILL () { std::string strFile= expectstring (); require_endsentence (); remove_file (strFile); return false; } bool RunnerLineImpl::do_FILES () { std::string param; gettoken (); BlChannel ch= DefaultChannel; if (token.code == '#') { ch= expectchannel (); if (! endsentence () ) { requiretoken (','); param= expectstring (); } } else { if (! endsentence () ) param= evalstring (); } require_endsentence (); if (param.empty () ) param= "*"; std::vector file; // Populate the vector with the files searched. Directory d; for (std::string r= d.findfirst (param.c_str () ); ! r.empty (); r= d.findnext () ) file.push_back (r); const size_t l= file.size (); // GwBasic do this, I mimic it. if (l == 0) throw ErrFileNotFound; size_t maxlength= 0; for (size_t i= 0; i < l; ++i) maxlength= std::max (maxlength, file [i].size () ); ++maxlength; BlFile & bf= getfile (ch); size_t width= 0; try { width= bf.getwidth (); } catch (...) { } size_t cols= width / maxlength; if (cols <= 1) { for (size_t i= 0; i < l; ++i) { //bf << file [i] << '\n'; bf << file [i]; bf.endline (); } } else { const size_t widthcol= width / cols; for (size_t i= 0; i < l; ++i) { const std::string & str= file [i]; bf << file [i]; if (i % cols == cols - 1) //bf << '\n'; bf.endline (); else bf << std::string (widthcol - str.size (), ' '); } if ( l > 0 && l % cols != 0) //bf << '\n'; bf.endline (); } return false; } bool RunnerLineImpl::do_PAPER () { gettoken (); BlChannel ch= DefaultChannel; if (token.code == '#') { ch= expectchannel (); requiretoken (','); gettoken (); } BlInteger color= evalinteger (); require_endsentence (); #if 0 if (graphics::ingraphicsmode () ) graphics::setbackground (color); else textbackground (color); #else BlFile & out= getfile (ch); out.setbackground (color); #endif return false; } bool RunnerLineImpl::do_PEN () { gettoken (); BlChannel ch= DefaultChannel; if (token.code == '#') { ch= expectchannel (); requiretoken (','); gettoken (); } BlFile & out= getfile (ch); if (token.code != ',') { // All parameters can't be omitted BlInteger color= evalinteger (); #if 0 if (graphics::ingraphicsmode () ) graphics::setcolor (int (color) ); else textcolor (int (color) ); #else out.setcolor (color); #endif if (token.code != ',') { require_endsentence (); return false; } } gettoken (); if (token.code != ',') { BlInteger bgmode= evalinteger (); graphics::settransparent (bgmode); if (token.code != ',') { require_endsentence (); return false; } } BlInteger mode= expectinteger (); graphics::setdrawmode (mode); require_endsentence (); return false; } bool RunnerLineImpl::do_SHELL () { gettoken (); if (endsentence () ) throw ErrNotImplemented; std::string command= evalstring (); require_endsentence (); int r= system (command.c_str () ); //std::cerr << "Result: " << r << endl; if (r == -1) throw ErrOperatingSystem; sysvar::set (sysvar::ShellResult, char (r >> 8) ); return false; } bool RunnerLineImpl::do_MERGE () { std::string progname= expectstring (); require_endsentence (); program.merge (progname); runner.setstatus (Runner::Ended); return true; } bool RunnerLineImpl::do_CHDIR () { std::string dirname= expectstring (); require_endsentence (); change_dir (dirname); return false; } bool RunnerLineImpl::do_MKDIR () { std::string dirname= expectstring (); require_endsentence (); make_dir (dirname); return false; } bool RunnerLineImpl::do_RMDIR () { std::string dirname= expectstring (); require_endsentence (); remove_dir (dirname); return false; } bool RunnerLineImpl::do_SYNCHRONIZE () { gettoken (); if (endsentence () ) graphics::synchronize (); else { BlNumber n= evalnum (); graphics::synchronize (n != 0); } return false; } bool RunnerLineImpl::do_PAUSE () { TRACEFUNC (tr, "do_pause"); BlNumber bln= expectnum (); require_endsentence (); unsigned long n= static_cast (bln); // Allow pending writes in graphic window before the pause. graphics::idle (); sleep_milisec (n); return false; } bool RunnerLineImpl::do_CHAIN () { bool merging= false; BlLineNumber inidel= LineNoDelete; BlLineNumber enddel= LineNoDelete; gettoken (); if (token.code == keyMERGE) { merging= true; gettoken (); } std::string progname= evalstring (); BlLineNumber iniline= LineBeginProgram; if (! endsentence () ) { requiretoken (','); gettoken (); if (token.code != keyDELETE) { iniline= evallinenumber (); if (! endsentence () ) { requiretoken (','); expecttoken (keyDELETE); } } if (token.code == keyDELETE) { if (! merging) throw ErrSyntax; gettoken (); evallinerange (inidel, enddel); } require_endsentence (); } if (merging) program.merge (progname, inidel, enddel); else program.load (progname); runner.run_to (iniline); return true; } bool RunnerLineImpl::do_ENVIRON () { BlResult result; expect (result); const std::string & str= result.str (); size_t l= str.size (); // We use an auto alloc, then in case of error the memory is freed. util::auto_alloc envstr (l + 1); memcpy (envstr, str.data (), l); envstr [l]= 0; if (putenv (envstr) != 0) throw ErrFunctionCall; // Do not free the string now, is part of the environment. envstr.release (); return false; } bool RunnerLineImpl::do_EDIT () { TRACEFUNC (tr, "RunnerLineImpl::do_edit"); gettoken (); if (token.code != keyNUMBER && token.code != keyINTEGER) throw ErrSyntax; BlLineNumber dest= evallinenumber (); require_endsentence (); #if 0 std::string buffer; { BlFileOutString bfos; program.list (dest, dest, bfos); buffer= bfos.str (); if (buffer.empty () ) { //bfos << dest << " \n"; bfos << dest; bfos.endline (); buffer= bfos.str (); } } buffer.erase (buffer.size () - 1); static const std::string number ("01234567890"); size_t inipos= buffer.find_first_of (number); ASSERT (inipos != std::string::npos); inipos= buffer.find_first_not_of (number, inipos); ASSERT (inipos != std::string::npos); ++inipos; if (editline (getfile0 (), buffer, inipos) ) { CodeLine code; code.scan (buffer); BlLineNumber nline= code.number (); if (nline == 0) throw ErrBlassicInternal; else { if (code.empty () ) program.deletelines (nline, nline); else program.insert (code); } } #else //editline (getfile0 (), program, dest); std::string line; if (runner.editline (dest, line) ) { CodeLine codeline; codeline.scan (line); BlLineNumber nline= codeline.number (); if (nline == LineDirectCommand) { // Need revision. runner.runline (codeline); } else { program.insert (codeline); } } #endif return false; } bool RunnerLineImpl::do_DRAWR () { #if 0 BlNumber x= expectnum (); requiretoken (','); BlNumber y= expectnum (); require_endsentence (); graphics::liner (int (x), int (y) ); #else BlInteger x, y; getdrawargs (x, y); graphics::liner (x, y); #endif return false; } bool RunnerLineImpl::do_PLOTR () { #if 0 BlNumber x= expectnum (); requiretoken (','); BlNumber y= expectnum (); require_endsentence (); graphics::plotr (int (x), int (y) ); #else BlInteger x, y; getdrawargs (x, y); graphics::plotr (x, y); #endif return false; } bool RunnerLineImpl::do_MOVER () { #if 0 BlNumber x= expectnum (); requiretoken (','); BlNumber y= expectnum (); require_endsentence (); graphics::mover (int (x), int (y) ); #else BlInteger x, y; getdrawargs (x, y); graphics::mover (x, y); #endif return false; } bool RunnerLineImpl::do_POKE16 () { BlNumber bnAddr= expectnum (); requiretoken (','); BlChar * addr= (BlChar *) (size_t) bnAddr; BlNumber bnValue= expectnum (); require_endsentence (); unsigned short value= (unsigned short) bnValue; poke16 (addr, value); return false; } bool RunnerLineImpl::do_POKE32 () { BlNumber bnAddr= expectnum (); requiretoken (','); BlChar * addr= (BlChar *) (size_t) bnAddr; BlNumber bnValue= expectnum (); require_endsentence (); BlInteger value= BlInteger (bnValue); poke32 (addr, value); return false; } bool RunnerLineImpl::do_RENUM () { gettoken (); BlLineNumber newnumber= 10; BlLineNumber oldnumber= LineBeginProgram; BlLineNumber increment= 10; BlLineNumber stop= LineEndProgram; if (! endsentence () ) { if (token.code != ',') newnumber= evallinenumber (); if (! endsentence () ) { requiretoken (','); gettoken (); if (token.code != ',') oldnumber= evallinenumber (); if (! endsentence () ) { requiretoken (','); gettoken (); if (token.code != ',') increment= evallinenumber (); if (! endsentence () ) { requiretoken (','); gettoken (); stop= evallinenumber (); require_endsentence (); } } } } program.renum (newnumber, oldnumber, increment, stop); runner.setstatus (Runner::Ended); return true; } bool RunnerLineImpl::do_CIRCLE () { gettoken (); //requiretoken ('('); if (token.code != '(') { // Spectrum syntax. BlInteger x= evalinteger (); requiretoken (','); BlInteger y= expectinteger (); requiretoken (','); BlInteger radius= expectinteger (); require_endsentence (); graphics::circle (x, y, radius); return false; } BlResult r; expect (r); BlInteger x= r.integer (); requiretoken (','); expect (r); BlInteger y= r.integer (); requiretoken (')'); expecttoken (','); expect (r); BlInteger radius= r.integer (); BlNumber arcbeg= 0, arcend= 0; bool fArc= false; bool fElliptic= false; BlNumber elliptic= 0; // initialized to avoid a warning. if (endsentence () ) goto do_it; requiretoken (','); gettoken (); if (token.code != ',') { BlInteger color= evalinteger (); graphics::setcolor (color); if (endsentence () ) goto do_it; requiretoken (','); } gettoken (); if (token.code != ',') { arcbeg= evalnum (); fArc= true; if (endsentence () ) goto do_it; requiretoken (','); } gettoken (); if (token.code != ',') { arcend= evalnum (); fArc= true; if (endsentence () ) goto do_it; requiretoken (','); } gettoken (); elliptic= evalnum (); fElliptic= true; require_endsentence (); do_it: if (! fElliptic) { if (! fArc) graphics::circle (x, y, radius); else graphics::arccircle (x, y, radius, arcbeg, arcend); } else { int rx, ry; if (elliptic > 1) { rx= static_cast (radius / elliptic); ry= radius; } else { rx= radius; ry= static_cast (radius * elliptic); } if (! fArc) graphics::ellipse (x, y, rx, ry); else graphics::arcellipse (x, y, rx, ry, arcbeg, arcend); } return false; } bool RunnerLineImpl::do_MASK () { BlResult r; gettoken (); if (token.code != ',') { eval (r); graphics::mask (r.integer () ); } if (! endsentence () ) { requiretoken (','); expect (r); graphics::maskdrawfirstpoint (r.integer () ); } require_endsentence (); return false; } bool RunnerLineImpl::do_WINDOW () { gettoken (); if (token.code == keySWAP) { BlChannel ch1= expectchannel (); requiretoken (','); BlChannel ch2= expectchannel (); require_endsentence (); runner.windowswap (ch1, ch2); return false; } BlChannel ch= DefaultChannel; if (token.code == '#') { ch= expectchannel (); requiretoken (','); gettoken (); } BlInteger x1= evalinteger (); requiretoken (','); BlInteger x2= expectinteger (); requiretoken (','); BlInteger y1= expectinteger (); requiretoken (','); BlInteger y2= expectinteger (); require_endsentence (); if (runner.isfileopen (ch) ) { BlFile & bf= runner.getfile (ch); if (bf.istextwindow () ) { bf.reset (x1, x2, y1, y2); return false; } } auto_ptr pbf (newBlFileWindow (ch, x1, x2, y1, y2) ); runner.setfile (ch, pbf.get () ); pbf.release (); return false; } void RunnerLineImpl::do_graphics_pen () { gettoken (); if (token.code != ',') { BlInteger ink= evalinteger (); graphics::setcolor (ink); if (endsentence () ) return; requiretoken (','); } BlInteger transpmode= expectinteger (); graphics::settransparent (transpmode); require_endsentence (); } void RunnerLineImpl::do_graphics_paper () { BlInteger ink= expectinteger (); require_endsentence (); graphics::setbackground (ink); } void RunnerLineImpl::do_graphics_cls () { gettoken (); if (! endsentence () ) { BlInteger ink= evalinteger (); require_endsentence (); graphics::setbackground (ink); } graphics::cls (); } bool RunnerLineImpl::do_GRAPHICS () { gettoken (); switch (token.code) { case keyPEN: do_graphics_pen (); break; case keyPAPER: do_graphics_paper (); break; case keyCLS: do_graphics_cls (); break; default: throw ErrSyntax; } return false; } bool RunnerLineImpl::do_BEEP () { gettoken (); require_endsentence (); #if 0 if (graphics::ingraphicsmode () ) graphics::ring (); else cursor::ring (); #else runner.ring (); #endif return false; } bool RunnerLineImpl::do_DEFINT () { VarType type; switch (token.code) { case keyDEFINT: type= VarInteger; break; case keyDEFSTR: type= VarString; break; case keyDEFREAL: case keyDEFSNG: case keyDEFDBL: type= VarNumber; break; default: throw ErrBlassicInternal; } definevars (type); return false; } bool RunnerLineImpl::do_INK () { int inknum= expectinteger (); if (endsentence () ) { // INK Spectrum style (set pen color). getfile0 ().setcolor (inknum); return false; } requiretoken (','); int r= expectinteger (); if (endsentence () ) { graphics::ink (inknum, r); return false; } requiretoken (','); int g= expectinteger (); if (endsentence () ) { // Flashing ink in Amstrad CPC, // just ignore second parameter. graphics::ink (inknum, r); return false; } requiretoken (','); int b= expectinteger (); require_endsentence (); graphics::ink (inknum, r, g, b); return false; } bool RunnerLineImpl::do_SET_TITLE () { std::string str= expectstring (); require_endsentence (); #if 0 if (graphics::ingraphicsmode () ) graphics::set_title (str); else cursor::set_title (str); #else runner.set_title (str); #endif return false; } bool RunnerLineImpl::do_TAG () { BlCode code= token.code; ASSERT (code == keyTAG || code == keyTAGOFF); //BlChannel ch= DefaultChannel; //gettoken (); //if (token.code == '#') // ch= expectchannel (); BlChannel ch= expectoptionalchannel (); require_endsentence (); BlFile & f= runner.getfile (ch); if (code == keyTAG) f.tag (); else f.tagoff (); return false; } bool RunnerLineImpl::do_ORIGIN () { BlInteger x= expectinteger (); requiretoken (','); BlInteger y= expectinteger (); if (! endsentence () ) { requiretoken (','); BlInteger minx= expectinteger (); requiretoken (','); BlInteger maxx= expectinteger (); requiretoken (','); BlInteger miny= expectinteger (); requiretoken (','); BlInteger maxy= expectinteger (); require_endsentence (); graphics::limits (minx, maxx, miny, maxy); } graphics::origin (x, y); return false; } bool RunnerLineImpl::do_DEG () { ASSERT (token.code == keyDEG || token.code == keyRAD); TrigonometricMode newmode= token.code == keyRAD ? TrigonometricRad : TrigonometricDeg; gettoken (); require_endsentence (); runner.trigonometric_mode (newmode); return false; } bool RunnerLineImpl::do_INVERSE () { BlChannel ch= DefaultChannel; gettoken (); if (token.code == '#') { ch= expectchannel (); requiretoken (','); gettoken (); } BlInteger inv= evalinteger (); runner.getfile (ch).inverse (inv % 2); return false; } bool RunnerLineImpl::do_IF_DEBUG () { BlInteger n= expectinteger (); require_endsentence (); // If the parameter is lower than the current debug level, // ignore the rest of the line. return n > sysvar::get16 (sysvar::DebugLevel); } bool RunnerLineImpl::do_WIDTH () { expecttoken (keyLPRINT); BlFile & printer= getfile (PrinterChannel); gettoken (); if (token.code != ',') { BlInteger w= evalinteger (); printer.setwidth (w); if (endsentence () ) return false; } requiretoken (','); BlInteger m= expectinteger (); require_endsentence (); printer.setmargin (m); return false; } bool RunnerLineImpl::do_BRIGHT () { BlChannel ch= DefaultChannel; gettoken (); if (token.code == '#') { ch= expectchannel (); requiretoken (','); gettoken (); } BlInteger br= evalinteger (); runner.getfile (ch).bright (br % 2); return false; } bool RunnerLineImpl::do_PLEASE () { gettoken (); if (endsentence () ) throw ErrPolite; if (token.code == keyPLEASE) throw ErrNoTeDejo; #if 1 return execute_instruction (); #else #if 0 mapfunc_t::const_iterator it= mapfunc.find (token.code); if (it == mapend) throw ErrSyntax; return (this->*it->second) (); #else return (this->*findfunc (token.code) ) (); #endif #endif } bool RunnerLineImpl::do_DRAWARC () { BlInteger x= expectinteger (); requiretoken (','); BlInteger y= expectinteger (); BlNumber g= 0; if (token.code == ',') g= expectnum (); require_endsentence (); graphics::drawarc (x, y, g); return false; } bool RunnerLineImpl::do_PULL () { enum PullType { PullRepeat, PullFor, PullWhile, PullGosub }; gettoken (); PullType type= PullRepeat; switch (token.code) { case keyREPEAT: gettoken (); break; case keyFOR: type= PullFor; gettoken (); break; case keyWHILE: type= PullWhile; gettoken (); break; case keyGOSUB: type= PullGosub; gettoken (); break; } require_endsentence (); switch (type) { case PullRepeat: if (runner.repeat_empty () ) throw ErrUntilWithoutRepeat; runner.repeat_pop (); break; case PullFor: runner.for_top (); // Simple way to do a for stack check. runner.for_pop (); break; case PullWhile: if (runner.while_empty () ) throw ErrWendWithoutWhile; runner.while_pop (); break; case PullGosub: // Same as POP { ProgramPos pos; runner.gosub_pop (pos); } break; } return false; } bool RunnerLineImpl::do_PAINT () { expecttoken ('('); BlInteger x= expectinteger (); requiretoken (','); BlInteger y= expectinteger (); requiretoken (')'); int actual= graphics::getcolor (); int paintattr= actual, borderattr= actual; gettoken (); if (token.code == ',') { gettoken (); if (token.code != ',') { paintattr= evalinteger (); borderattr= paintattr; } if (! endsentence () ) { requiretoken (','); borderattr= expectinteger (); } } require_endsentence (); graphics::paint (x, y, paintattr, borderattr); return false; } bool RunnerLineImpl::do_FREE_MEMORY () { gettoken (); if (endsentence () ) { blassic::memory::dyn_freeall (); return false; } BlInteger mempos= evalinteger (); require_endsentence (); blassic::memory::dyn_free (mempos); return false; } bool RunnerLineImpl::do_SCROLL () { BlChannel ch= DefaultChannel; int nlines= 1; gettoken (); if (! endsentence () ) { if (token.code == '#') { ch= expectchannel (); if (token.code == ',') nlines= expectinteger (); } else nlines= evalinteger (); require_endsentence (); } runner.getfile (ch).scroll (nlines); return false; } bool RunnerLineImpl::do_ZX_PLOT () { BlInteger x= expectinteger (); requiretoken (','); BlInteger y= expectinteger (); require_endsentence (); graphics::zxplot (graphics::Point (x, y) ); return false; } bool RunnerLineImpl::do_ZX_UNPLOT () { BlInteger x= expectinteger (); requiretoken (','); BlInteger y= expectinteger (); require_endsentence (); graphics::zxplot (graphics::Point (x, y) ); return false; } bool RunnerLineImpl::do_ELSE () { // This is not the best possible way of treating ELSE, // but can't diagnose an error of ELSE misplaced with // the available info without reexploring the line, // and that will be slow. // Anyway, several old Basic I have tested do the same // thing, and after all the goal is to be similar to // these. return true; } // End of runnerline_instructions.cpp