aboutsummaryrefslogtreecommitdiffstats
path: root/runnerline_instructions.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'runnerline_instructions.cpp')
-rw-r--r--runnerline_instructions.cpp4277
1 files changed, 4277 insertions, 0 deletions
diff --git a/runnerline_instructions.cpp b/runnerline_instructions.cpp
new file mode 100644
index 0000000..08d2fc4
--- /dev/null
+++ b/runnerline_instructions.cpp
@@ -0,0 +1,4277 @@
+// 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 <memory>
+using std::auto_ptr;
+
+#include <iostream>
+using std::cerr;
+using std::endl;
+using std::flush;
+
+#include <string.h>
+#include <time.h>
+
+#include <cassert>
+#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 <char> aux (size);
+ is.read (aux, size);
+ result.assign (aux, size);
+ }
+ assignvarstring (varname, result);
+ }
+ else
+ {
+ char * init;
+ {
+ BlNumber bn= evalnum ();
+ init= reinterpret_cast <char *> (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 <BlFile> 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 <const char *>
+ (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 <ForElement> 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 <unsigned int> (result);
+ seedvalue= util::checked_cast <unsigned int>
+ (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 <BlLineNumber> 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 <BlFile> 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 <BlFile> 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 <BlFile> 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 <graphics::Point> 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 <BlFile::field_element> 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 <BlFile> 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 <size_t> (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 <std::string::size_type>
+ (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 <Function> 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 <std::string> 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 <int> (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 <unsigned char> (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 <short> (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 <std::string> 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 <unsigned long> (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 <char> 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 <int> (radius / elliptic);
+ ry= radius;
+ }
+ else
+ {
+ rx= radius;
+ ry= static_cast <int> (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 <BlFile> 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
Un proyecto texto-plano.xyz