// runner.cpp
// Revision 14-mar-2012
#include "runner.h"
#include "result.h"
// Testing
#include "runnerline.h"
//#include "runnerline_impl.h"
#include "program.h"
#include "keyword.h"
#include "var.h"
#include "codeline.h"
#include "dim.h"
#include "cursor.h"
#include "graphics.h"
#include "sysvar.h"
#include "util.h"
using util::to_string;
using util::touch;
#include "trace.h"
#include "edit.h"
#include "socket.h"
#include "memory.h"
#include <iostream>
#include <fstream>
#include <sstream>
#include <iomanip>
#include <cmath>
#include <ctime>
#include <vector>
#include <algorithm>
#include <memory>
#include <typeinfo>
#include <math.h>
#include <cassert>
#define ASSERT assert
#if __BORLANDC__ >= 0x0560
#pragma warn -8091
#endif
using std::cerr;
using std::endl;
using std::for_each;
using std::auto_ptr;
namespace sysvar= blassic::sysvar;
namespace onbreak= blassic::onbreak;
using namespace blassic::file;
//************************************************
// Auxiliar
//************************************************
namespace {
const std::string strbreak ("**+BREAK+**");
void deletefile (GlobalRunner::ChanFile::value_type & chf)
{
delete chf.second;
}
} // namespace
//************************************************
// GlobalRunner
//************************************************
GlobalRunner::GlobalRunner (Program & prog) :
program (prog),
fTron (false),
alreadypolled (false),
breakstate (onbreak::BreakStop),
fn_current_level (0)
{
TRACEFUNC (tr, "GlobalRunner::GlobalRunner");
clearerrorgoto ();
clearreadline ();
resetfile0 ();
resetfileprinter ();
trigonometric_default ();
}
GlobalRunner::~GlobalRunner ()
{
TRACEFUNC (tr, "GlobalRunner::~GlobalRunner");
for_each (chanfile.begin (), chanfile.end (), deletefile);
blassic::memory::dyn_freeall ();
}
void GlobalRunner::tron (bool fLine, BlChannel blc)
{
fTron= true;
fTronLine= fLine;
BlChar flags= static_cast <BlChar> (fLine ? 3 : 1);
BlChar oldflags= sysvar::get (sysvar::TronFlags);
flags= flags | (oldflags & static_cast <BlChar> (~3) );
sysvar::set (sysvar::TronFlags, flags);
blcTron= blc;
sysvar::set16 (sysvar::TronChannel, blc);
}
void GlobalRunner::troff ()
{
fTron= false;
fTronLine= false;
BlChar oldflags= sysvar::get (sysvar::TronFlags);
sysvar::set (sysvar::TronFlags, oldflags & static_cast <BlChar> (~3) );
sysvar::set16 (sysvar::TronChannel, 0);
}
void GlobalRunner::do_tronline (const CodeLine & codeline)
{
if (codeline.number () == LineDirectCommand)
return;
BlFile & file= getfile (blcTron);
if (fTronLine)
program.listline (codeline, file);
else
{
//file << '[' << BlNumber (codeline.number () ) << ']';
file << '[' <<
static_cast <BlInteger> (codeline.number () ) <<
']';
file.flush ();
}
}
void GlobalRunner::clearerrorgoto ()
{
blnErrorGoto= LineEndProgram;
blnErrorGotoSource= LineEndProgram;
}
void GlobalRunner::seterrorgoto (BlLineNumber line, BlLineNumber source)
{
// Line must be a valid program line except 0, because 0 mean
// deactivate, source can be a valid program line or a direct
// command.
if (line > BlMaxLineNumber ||
(source > BlMaxLineNumber && source != LineDirectCommand ) )
{
ASSERT (false);
throw ErrBlassicInternal;
}
blnErrorGoto= line;
blnErrorGotoSource= source;
}
bool GlobalRunner::assign_channel_var
(const std::string & var, const Dimension & dim,
const std::string & value, BlFile::Align align)
{
bool r= false;
for (ChanFile::iterator it= chanfile.begin ();
it != chanfile.end ();
++it)
// Changed behaviour: instead to assign to all possible
// fields with the same name in all files, assign only
// to the first.
// Retesting the initial behaviour.
#if 1
{
if (it->second->assign (var, dim, value, align) )
r= true;
}
return r;
#else
{
if (it->second->assign (var, dim, value, align) )
return true;
}
return false;
#endif
}
bool GlobalRunner::assign_mid_channel_var
(const std::string & var, const Dimension & dim,
const std::string & value,
size_t inipos, std::string::size_type len)
{
bool r= false;
for (ChanFile::iterator it= chanfile.begin ();
it != chanfile.end ();
++it)
{
if (it->second->assign_mid (var, dim, value, inipos, len) )
r= true;
}
return r;
}
BlChannel GlobalRunner::freefile () const
{
for (BlChannel channel= 1; channel < PrinterChannel; ++channel)
if (chanfile.find (channel) == chanfile.end () )
return channel;
return 0;
}
BlFile & GlobalRunner::getfile (BlChannel channel) const
{
ChanFile::const_iterator it= chanfile.find (channel);
if (it == chanfile.end () )
{
if (showdebuginfo () )
cerr << "Channel " << channel <<
" is not opened" << endl;
throw ErrFileNumber;
}
return * it->second;
}
void GlobalRunner::setfile (BlChannel channel, BlFile * npfile)
{
TRACEFUNC (tr, "GlobalRunner::setfile");
std::pair <ChanFile::iterator, bool> r
(chanfile.insert (ChanFile::value_type (channel, npfile) ) );
if (! r.second)
{
TRMESSAGE (tr, "Exist, delete and substitute");
delete r.first->second;
r.first->second= npfile;
}
else
{
TRMESSAGE (tr, "Inserted new");
}
}
void GlobalRunner::resetfile0 ()
{
TRACEFUNC (tr, "GlobalRunner::resetfile0");
auto_ptr <BlFile> pf (graphics::ingraphicsmode () ?
newBlFileWindow (DefaultChannel) :
newBlFileConsole () );
setfile (DefaultChannel, pf.get () );
pf.release ();
}
void GlobalRunner::resetfileprinter ()
{
auto_ptr <BlFile> pf (newBlFilePrinter () );
setfile (PrinterChannel, pf.get () );
pf.release ();
}
void GlobalRunner::close_all ()
{
std::vector <BlChannel> w;
for (ChanFile::iterator it= chanfile.begin ();
it != chanfile.end (); ++it)
{
BlFile * f= it->second;
if (f->isfile () )
{
delete f;
w.push_back (it->first);
}
}
for (size_t i= 0, l= w.size (); i < l; ++i)
{
size_t d= chanfile.erase (w [i] );
ASSERT (d == 1);
(void) d; // Avoid warning about unused.
}
}
void GlobalRunner::destroy_windows ()
{
TRACEFUNC (tr, "GlobalRunner::destroy_windows");
std::vector <BlChannel> w;
for (ChanFile::iterator it= chanfile.begin ();
it != chanfile.end (); ++it)
{
BlFile * f= it->second;
//if (typeid (* f) == typeid (BlFileWindow) )
if (f->istextwindow () )
{
delete f;
w.push_back (it->first);
}
}
for (size_t i= 0, l= w.size (); i < l; ++i)
{
TRMESSAGE (tr, "Destroying window " + to_string (w [i] ) );
size_t d= chanfile.erase (w [i] );
ASSERT (d == 1);
touch (d);
}
}
void GlobalRunner::closechannel (BlChannel channel)
{
if (channel == PrinterChannel)
resetfileprinter ();
else
{
ChanFile::iterator it= chanfile.find (channel);
if (it != chanfile.end () )
{
delete it->second;
chanfile.erase (it);
}
}
}
void GlobalRunner::windowswap (BlChannel ch1, BlChannel ch2)
{
bool showdebug= showdebuginfo ();
ChanFile::iterator it1= chanfile.find (ch1);
ChanFile::iterator it2= chanfile.find (ch2);
if (it1 == chanfile.end () || it2 == chanfile.end () )
{
if (showdebug)
{
if (it1 == chanfile.end () )
cerr << "Channel " << ch1 <<
" is not opened" << endl;
if (it2 == chanfile.end () )
cerr << "Channel " << ch2 <<
" is not opened" << endl;
}
throw ErrFileNumber;
}
//bool fail1= typeid (* it1->second) != typeid (BlFileWindow);
//bool fail2= typeid (* it2->second) != typeid (BlFileWindow);
bool fail1= ! it1->second->istextwindow ();
bool fail2= ! it2->second->istextwindow ();
if (fail1 || fail2)
{
if (showdebug)
{
if (fail1)
cerr << "Channel " << ch1 <<
" is not a window" << endl;
if (fail2)
cerr << "Channel " << ch2 <<
" is not a window" << endl;
}
throw ErrFileMode;
}
std::swap (it1->second, it2->second);
}
void GlobalRunner::pollchannel (BlChannel ch, BlLineNumber line)
{
TRACEFUNC (tr, "GlobalRunner::pollchannel");
if (! isfileopen (ch) )
throw ErrFileNumber;
if (line == 0)
chanpolled.erase (ch);
else
chanpolled [ch]= line;
}
bool GlobalRunner::channelspolled ()
{
// This is to let the program execute one instruction
// before doing other poll check.
if (chanpolled.empty () )
return false;
else
{
if (alreadypolled)
{
if (clearingpolled)
alreadypolled= false;
return false;
}
else
return true;
}
}
void GlobalRunner::setpolled ()
{
alreadypolled= true;
clearingpolled= false;
}
void GlobalRunner::clearpolled ()
{
clearingpolled= true;
}
namespace {
class Polled {
GlobalRunner & gr;
public:
Polled (GlobalRunner & gr) :
gr (gr)
{ }
bool operator () (const GlobalRunner::ChanPolled::value_type & cp)
{
BlFile & f= gr.getfile (cp.first);
return f.poll ();
}
};
} // namespace
BlLineNumber GlobalRunner::getpollnumber ()
{
TRACEFUNC (tr, "GlobalRunner::getpollnumber");
ChanPolled::iterator it=
std::find_if (chanpolled.begin (), chanpolled.end (),
Polled (* this) );
if (it != chanpolled.end () )
return it->second;
else
return LineEndProgram;
}
void GlobalRunner::inc_fn_level ()
{
const unsigned long maxlevel= sysvar::get32 (sysvar::MaxFnLevel);
if (fn_current_level >= maxlevel)
{
if (showdebuginfo () )
cerr << "Maximum level of FN recursion of " <<
maxlevel << " has been reached" << endl;
throw ErrFnRecursion;
}
++fn_current_level;
}
void GlobalRunner::dec_fn_level ()
{
if (fn_current_level == 0)
{
if (showdebuginfo () )
cerr << "Internal problem handling FN exit" <<
endl;
throw ErrBlassicInternal;
}
--fn_current_level;
}
//************************************************
// LocalLevel
//************************************************
class LocalLevel::Internal {
typedef blassic::result::BlResult BlResult;
friend class LocalLevel; // Only to avoid a warning in gcc.
public:
Internal () : nref (1) { }
void addref () { ++nref; }
void delref ()
{
if (--nref == 0)
delete this;
}
void addlocalvar (const std::string & name);
void freelocalvars ();
private:
~Internal () { }
size_t nref;
typedef std::map <std::string, BlResult> maploc_t;
maploc_t maploc;
};
void LocalLevel::Internal::addlocalvar (const std::string & name)
{
TRACEFUNC (tr, "LocalLevel::Internal::addlocalvar");
// Changing this to make it exception safe.
#if 0
if (maploc.find (name) != maploc.end () )
return;
BlResult result;
switch (typeofvar (name) )
{
case VarNumber:
{
BlNumber n= 0;
std::swap (n, * addrvarnumber (name) );
result= n;
TRMESSAGE (tr, std::string ("Saving ") + name +
" " + to_string (n) );
}
break;
case VarInteger:
{
BlInteger n= 0;
std::swap (n, * addrvarinteger (name) );
result= n;
TRMESSAGE (tr, std::string ("Saving ") + name +
" " + to_string (n) );
}
break;
case VarString:
{
std::string str;
swap (str, * addrvarstring (name) );
result= str;
TRMESSAGE (tr, std::string ("Saving ") + name +
" " + str);
}
break;
default:
if (showdebuginfo () )
cerr << "Type of local variable '" <<
name << "'unknown" << endl;
throw ErrBlassicInternal;
}
maploc [name]= result;
#else
BlResult result;
VarType vtype= typeofvar (name);
VarPointer vp;
switch (vtype)
{
case VarInteger:
vp.pinteger= addrvarinteger (name);
result= * vp.pinteger;
TRMESSAGE (tr, std::string ("Saving ") + name +
" " + to_string (result.integer () ) );
break;
case VarNumber:
vp.pnumber= addrvarnumber (name);
result= * vp.pnumber;
TRMESSAGE (tr, std::string ("Saving ") + name +
" " + to_string (result.number () ) );
break;
case VarString:
vp.pstring= addrvarstring (name);
result= * vp.pstring;
TRMESSAGE (tr, std::string ("Saving ") + name +
" " + result.str () );
break;
default:
if (showdebuginfo () )
cerr << "Type of local variable '" <<
name << "'unknown" << endl;
throw ErrBlassicInternal;
}
std::pair <maploc_t::iterator, bool> r= maploc.insert
(maploc_t::value_type (name, result) );
// If already declared as local, does nothing.
if (! r.second)
{
TRMESSAGE (tr, name + " was already saved");
return;
}
switch (vtype)
{
case VarInteger:
* vp.pinteger= BlInteger ();
break;
case VarNumber:
* vp.pnumber= BlNumber ();
break;
case VarString:
vp.pstring->erase ();
break;
default:
throw ErrBlassicInternal;
}
#endif
}
namespace {
using blassic::result::BlResult;
void freelocalvar (const std::pair <std::string, BlResult> & p)
{
TRACEFUNC (tr, "freelocalvar");
switch (p.second.type () )
{
case VarNumber:
assignvarnumber (p.first, p.second.number () );
TRMESSAGE (tr, std::string ("Restoring ") + p.first +
" to " + to_string (p.second.number () ) );
break;
case VarInteger:
assignvarinteger (p.first, p.second.integer () );
TRMESSAGE (tr, std::string ("Restoring ") + p.first +
" to " + to_string (p.second.integer () ) );
break;
case VarString:
assignvarstring (p.first, p.second.str () );
TRMESSAGE (tr, std::string ("Restoring ") + p.first +
" to " + p.second.str () );
break;
default:
if (showdebuginfo () )
cerr << "Freeing local variable of unknown type" <<
endl;
throw ErrBlassicInternal;
}
}
} // namespace
void LocalLevel::Internal::freelocalvars ()
{
for_each (maploc.begin (), maploc.end (), freelocalvar);
}
LocalLevel::LocalLevel () :
pi (new Internal)
{
}
LocalLevel::LocalLevel (const LocalLevel & ll) :
//Element (ge),
pi (ll.pi)
{
pi->addref ();
}
LocalLevel::~LocalLevel ()
{
pi->delref ();
}
LocalLevel & LocalLevel::operator = (const LocalLevel & ll)
{
ll.pi->addref ();
pi->delref ();
pi= ll.pi;
return * this;
}
void LocalLevel::freelocalvars ()
{
pi->freelocalvars ();
}
void LocalLevel::addlocalvar (const std::string & name)
{
pi->addlocalvar (name);
}
//************************************************
// class GosubStack
//************************************************
GosubStack::GosubStack (GlobalRunner & globalrunner) :
globalrunner (globalrunner)
{
}
GosubStack::~GosubStack ()
{
// This is to ensure that on exiting from a multiline
// DEF FN the local variables are correctly restored,
// even on exceptions.
TRACEFUNC (tr, "GosubStack::~GosubStack");
const bool inexcept= std::uncaught_exception ();
try
{
while (! st.empty () )
{
GosubElement & ge= st.top ();
ge.freelocalvars ();
if (ge.ispolled () )
globalrunner.clearpolled ();
st.pop ();
}
}
catch (...)
{
// Bad thing. Perhaps restoring a local variable
// has run out of memory?
// Try to inform the user without aborting.
if (! inexcept)
throw;
else
{
cerr << "**ERROR** "
"Failed to restore local variables" <<
endl;
}
}
}
void GosubStack::erase ()
{
// Unnecessary restore local vars here, when is called
// vars will be cleared after.
while (! st.empty () )
st.pop ();
}
//************************************************
// Auxiliar functions of Runner
//************************************************
namespace {
const char * statusname (Runner::RunnerStatus status)
{
const char * str= "unknown";
switch (status)
{
case Runner::Ended:
str= "Ended"; break;
case Runner::FnEnded:
str= "FnEnded"; break;
case Runner::ReadyToRun:
str= "ReadyToRun"; break;
case Runner::Running:
str= "Runnig"; break;
case Runner::Stopped:
str= "Stopped"; break;
case Runner::Jump:
str= "Jump"; break;
case Runner::Goto:
str= "Goto"; break;
//case Runner::InitingCommand:
// str= "InitingCommand"; break;
//case Runner::Command:
// str= "Command"; break;
case Runner::JumpResumeNext:
str= "JumpResumeNext"; break;
}
return str;
}
} // namespace
std::ostream & operator << (std::ostream & os, Runner::RunnerStatus status)
{
os << "RunnerStatus is: " << statusname (status) << std::endl;
return os;
}
//************************************************
// class Runner
//************************************************
Runner::Runner (GlobalRunner & gr) :
globalrunner (gr),
program (gr.getprogram () ),
status (Ended),
fInElse (false),
fInWend (false),
gosubstack (globalrunner),
posbreak (LineEndProgram),
blnAuto (LineEndProgram)
{
TRACEFUNC (tr, "Runner::Runner");
clearerror ();
}
Runner::Runner (const Runner & runner) :
globalrunner (runner.globalrunner),
program (runner.program),
status (Ended),
fInElse (false),
fInWend (false),
gosubstack (globalrunner),
posbreak (LineEndProgram),
blnAuto (LineEndProgram)
{
TRACEFUNC (tr, "Runner::Runner (copy)");
}
Runner::~Runner ()
{
}
void Runner::clear ()
{
close_all ();
// Clear loops stacks.
while (! forstack.empty () )
for_pop ();
gosubstack.erase ();
while (! repeatstack.empty () )
repeatstack.pop ();
while (! whilestack.empty () )
whilestack.pop ();
// Do RESTORE
clearreadline ();
// Errors
clearerror ();
globalrunner.clearerrorgoto ();
}
// Runner ************ errors ***************
BlErrNo Runner::geterr () const
{
return berrLast.geterr ();
}
ProgramPos Runner::geterrpos () const
{
return berrLast.getpos ();
}
BlLineNumber Runner::geterrline () const
{
return berrLast.getpos ().getnum ();
}
void Runner::clearerror ()
{
//berrLast= BlError (0, LineEndProgram);
berrLast.clear ();
}
BlError Runner::geterror () const
{
return berrLast;
}
void Runner::seterror (const BlError & er)
{
berrLast= er;
}
void Runner::spectrumwindows ()
{
// Only can be called in graphics mode, default channel must
// be a window.
ASSERT (graphics::ingraphicsmode () );
ASSERT (getfile0 ().istextwindow () );
// Adjust main window to the normal print area.
getfile0 ().reset (1, 32, 1, 22);
// Set channel 1, if free or already a window, to the window
// of the INPUT area. Leave untouched if is not a window.
if (! isfileopen (1) )
{
auto_ptr <BlFile> pf (newBlFileWindow (1, 1, 32, 23, 24) );
setfile (1, pf.get () );
pf.release ();
}
else
{
BlFile & f1= getfile (1);
if (f1.istextwindow () )
f1.reset (1, 32, 23, 24);
}
}
void Runner::getline (std::string & line)
{
clean_input ();
BlFile & file= getfile0 ();
file.getline (line);
}
void Runner::run ()
{
// Una chapuza por ahora.
std::string str ("RUN");
CodeLine code;
code.scan (str);
runline (code);
}
bool Runner::next ()
{
//TRACEFUNC (tr, "Runner::next ()");
if (forstack.empty () )
throw ErrNextWithoutFor;
ForElement * pfe= forstack.top ();
if (pfe->next () )
{
//TRMESSAGE (tr, "NEXT -> " +
// to_string (pfe->getpos ().getnum () ) + ":" +
// to_string (pfe->getpos ().getchunk () ) );
jump_to (pfe->getpos () );
return true;
}
else
{
//TRMESSAGE (tr, "End FOR");
forstack.pop ();
return false;
}
}
bool Runner::next (const std::string & varname)
{
if (forstack.empty () )
throw ErrNextWithoutFor;
ForElement * pfe= forstack.top ();
if (! pfe->isvar (varname) )
{
if (sysvar::get (sysvar::TypeOfNextCheck) == 0)
{
if (showdebuginfo () )
cerr << "Processing NEXT " <<
varname <<
" but current FOR is " <<
pfe->var () <<
" and mode is strict" <<
endl;
throw ErrNextWithoutFor;
}
else
{
// In ZX style NEXT can be omitted.
do
{
forstack.pop ();
if (forstack.empty () )
throw ErrNextWithoutFor;
pfe= forstack.top ();
} while (! pfe->isvar (varname) );
}
}
if (pfe->next () )
{
jump_to (pfe->getpos () );
return true;
}
else
{
forstack.pop ();
return false;
}
}
void Runner::tron (bool fLine, BlChannel blc)
{
globalrunner.tron (fLine, blc);
}
void Runner::troff ()
{
globalrunner.troff ();
}
namespace {
inline bool goto_relaxed ()
{
return sysvar::hasFlags1 (sysvar::RelaxedGoto);
}
} // namespace
inline Runner::StatusRun Runner::checkstatusEnded
(CodeLine &, const CodeLine &)
{
return StopNow;
}
inline Runner::StatusRun Runner::checkstatusFnEnded
(CodeLine &, const CodeLine &)
{
TRACEFUNC (tr, "Runner::checkstatusFnEnded");
if (fn_level () == 0)
throw ErrUnexpectedFnEnd;
gosub_check_fn ();
return StopNow;
}
inline Runner::StatusRun Runner::checkstatusReadyToRun
(CodeLine & codeline, const CodeLine &)
{
BlLineNumber gline= posgoto.getnum ();
if (gline == LineBeginProgram)
codeline= program.getfirstline ();
else
{
CodeLine aux;
program.getline (gline, aux);
if (aux.number () != gline)
{
if (showdebuginfo () )
cerr << "Line " << gline <<
" not exist" << endl;
throw ErrLineNotExist;
}
codeline= aux;
}
//if (codeline.number () == 0)
if (codeline.number () == LineEndProgram)
{
//setstatus (Ended);
//throw blassic::ProgramPassedLastLine ();
setstatus (Ended);
return StopNow;
}
else
{
setstatus (Running);
return KeepRunning;
}
}
inline Runner::StatusRun Runner::checkstatusRunning
(CodeLine & codeline, const CodeLine &)
{
if (codeline.number () == LineDirectCommand)
{
setstatus (Ended);
return StopNow;
}
else
{
program.getnextline (codeline);
if (codeline.number () == LineEndProgram)
{
#if 1
if (fn_level () > 0)
{
//throw blassic::ProgramPassedLastLine ();
throw ErrNoFnEnd;
}
setstatus (Ended);
return StopNow;
#else
throw blassic::ProgramPassedLastLine ();
#endif
}
else
return KeepRunning;
}
}
inline Runner::StatusRun Runner::checkstatusStopped
(CodeLine &, const CodeLine &)
{
return StopNow;
}
inline Runner::StatusRun Runner::checkstatusJump
(CodeLine & codeline, const CodeLine & codeline0)
{
TRACEFUNC (tr, "Runner::checkstatusJump");
const BlLineNumber gotoline= posgoto.getnum ();
if (gotoline != LineDirectCommand)
{
if (codeline.number () != gotoline)
{
program.getline (posgoto, codeline);
if (codeline.number () == LineEndProgram)
{
//throw blassic::ProgramPassedLastLine ();
if (fn_level () > 0)
throw ErrNoFnEnd;
setstatus (Ended);
return StopNow;
}
}
else
{
TRMESSAGE (tr, "Same line");
codeline.gotochunk (posgoto.getchunk () );
}
}
else
{
if (codeline.number () != LineDirectCommand)
codeline= codeline0;
codeline.gotochunk (posgoto.getchunk () );
}
setstatus (Running);
return KeepRunning;
}
inline Runner::StatusRun Runner::checkstatusGoto
(CodeLine & codeline, const CodeLine &)
{
const BlLineNumber gotoline= posgoto.getnum ();
if (codeline.number () != gotoline)
{
CodeLine aux;
program.getline (gotoline, aux);
BlLineNumber l= aux.number ();
//if (l == 0 ||
// (l != gotoline && ! goto_relaxed () ) )
if (l != gotoline && ! goto_relaxed () )
{
if (showdebuginfo () )
cerr << "Line " << gotoline <<
" not exist" << endl;
throw ErrLineNotExist;
}
codeline= aux;
}
else
codeline.gotochunk (posgoto.getchunk () );
setstatus (Running);
return KeepRunning;
}
#if 0
inline Runner::StatusRun Runner::checkstatusInitingCommand
(CodeLine &, const CodeLine &)
{
setstatus (Command);
return KeepRunning;
}
inline Runner::StatusRun Runner::checkstatusCommand
(CodeLine &, const CodeLine &)
{
return StopNow;
//return KeepRunning;
}
#endif
inline Runner::StatusRun Runner::checkstatusJumpResumeNext
(CodeLine & codeline, const CodeLine & codeline0)
{
const BlLineNumber gotoline= posgoto.getnum ();
if (gotoline != LineDirectCommand)
{
if (codeline.number () != gotoline)
{
program.getline (posgoto, codeline);
if (codeline.number () == LineEndProgram)
{
//throw blassic::ProgramPassedLastLine ();
if (fn_level () > 0)
throw ErrNoFnEnd;
setstatus (Ended);
return StopNow;
}
}
else
codeline.gotochunk
(posgoto.getchunk () );
}
else
{
if (codeline.number () != LineDirectCommand)
codeline= codeline0;
codeline.gotochunk (posgoto.getchunk () );
}
BlLineNumber n= codeline.number ();
if (n == gotoline)
{
CodeLine::Token tok;
codeline.gettoken (tok);
if (tok.code == keyIF)
{
if (n == LineDirectCommand)
{
setstatus (Ended);
return StopNow;
}
program.getnextline (codeline);
if (codeline.number () == LineEndProgram)
{
//throw blassic::ProgramPassedLastLine ();
if (fn_level () > 0)
throw ErrNoFnEnd;
setstatus (Ended);
return StopNow;
}
}
else
{
while (! tok.isendsentence () )
codeline.gettoken (tok);
}
}
setstatus (Running);
return KeepRunning;
}
Runner::checkstatusfunc Runner::checkstatus [Runner::LastStatus + 1]=
{
&Runner::checkstatusEnded,
&Runner::checkstatusFnEnded,
&Runner::checkstatusReadyToRun,
&Runner::checkstatusRunning,
&Runner::checkstatusStopped,
&Runner::checkstatusJump,
&Runner::checkstatusGoto,
//&Runner::checkstatusInitingCommand,
//&Runner::checkstatusCommand,
&Runner::checkstatusJumpResumeNext,
};
#if 0
inline bool Runner::checkstatus
(CodeLine & codeline, const CodeLine & codeline0)
{
switch (status)
{
case ReadyToRun:
{
BlLineNumber gline= posgoto.getnum ();
if (gline == LineBeginProgram)
codeline= program.getfirstline ();
else
{
CodeLine aux;
program.getline (gline, aux);
if (aux.number () != gline)
{
if (showdebuginfo () )
cerr << "Line " << gline <<
" not exist" << endl;
throw ErrLineNotExist;
}
codeline= aux;
}
//if (codeline.number () == 0)
if (codeline.number () == LineEndProgram)
//setstatus (Ended);
throw blassic::ProgramPassedLastLine ();
else
{
setstatus (Running);
return true;
}
}
//break;
case Jump:
{
const BlLineNumber gotoline= posgoto.getnum ();
if (gotoline != LineDirectCommand)
{
if (codeline.number () != gotoline)
{
program.getline (posgoto, codeline);
if (codeline.number () == LineEndProgram)
throw blassic::ProgramPassedLastLine ();
}
else
codeline.gotochunk
(posgoto.getchunk () );
}
else
{
if (codeline.number () != LineDirectCommand)
codeline= codeline0;
codeline.gotochunk (posgoto.getchunk () );
}
}
setstatus (Running);
return true;
case Goto:
{
const BlLineNumber gotoline= posgoto.getnum ();
if (codeline.number () != gotoline)
{
CodeLine aux;
program.getline (gotoline, aux);
BlLineNumber l= aux.number ();
//if (l == 0 ||
// (l != gotoline && ! goto_relaxed () ) )
if (l != gotoline && ! goto_relaxed () )
{
if (showdebuginfo () )
cerr << "Line " << gotoline <<
" not exist" << endl;
throw ErrLineNotExist;
}
codeline= aux;
}
else
codeline.gotochunk (posgoto.getchunk () );
}
setstatus (Running);
return true;
case Running:
//if (codeline.number () != 0)
// program.getnextline (codeline);
//if (codeline.number () == 0)
//{
// status= Ended;
// if (fn_level () > 0)
// throw blassic::ProgramPassedLastLine ();
//}
//else
// return true;
if (codeline.number () == LineDirectCommand)
{
setstatus (Ended);
return false;
}
else
{
program.getnextline (codeline);
if (codeline.number () == LineEndProgram)
#if 0
{
setstatus (Ended);
if (fn_level () > 0)
throw blassic::ProgramPassedLastLine
();
return false;
}
#else
throw blassic::ProgramPassedLastLine ();
#endif
else
return true;
}
case InitingCommand:
setstatus (Command);
return true;
default:
;
}
return false;
}
#else
inline bool Runner::is_run_status
(CodeLine & codeline, const CodeLine & codeline0)
{
return (this->*(checkstatus [status]) ) (codeline, codeline0)
== KeepRunning;
}
#endif
void Runner::showfailerrorgoto () const
{
if (! showdebuginfo () )
return;
BlLineNumber source= geterrorgotosource ();
cerr << "Line " << geterrorgoto () <<
" specified in ON ERROR GOTO on ";
if (source == LineDirectCommand)
cerr << "a direct command";
else
cerr << "line " << source;
cerr << " does not exist" <<
endl;
}
namespace {
bool handle_error (Runner & runner, BlError & berr)
{
BlLineNumber errorgoto= runner.geterrorgoto ();
if (errorgoto == LineEndProgram)
{
if (runner.fn_level () > 0)
{
// The position of the error
// will be defined by the
// first fn caller.
if (showdebuginfo () )
cerr << "Error " << berr.geterr () <<
" inside DEF FN on line " <<
berr.getpos ().getnum () <<
endl;
}
return true;
}
else
{
CodeLine aux;
runner.getprogram ().getline (errorgoto, aux);
if (aux.number () != errorgoto)
{
runner.showfailerrorgoto ();
berr.seterr (ErrLineNotExist);
return true;
}
runner.seterror (berr);
// Clear berr, or it will be still active
// next time something is throwed.
berr.clear ();
runner.jump_to (errorgoto);
return false;
}
}
} // namespace
#if 0
void Runner::runline_catch (const CodeLine & codeline, ProgramPos pos,
BlError & berr, bool & endloop, bool & dobreak)
{
TRACEFUNC (tr, "Runner::runline_catch");
//bool fnend= false;
try
{
throw; // Relaunch pending exception to catch it.
}
catch (std::bad_alloc &)
{
berr= BlError (ErrOutMemory,
//runnerline.getposactual () );
pos);
endloop= handle_error (* this, berr);
//if (endloop)
// break;
}
catch (SocketError & se)
{
if (showdebuginfo () )
cerr << se.what () << endl;
berr= BlError (ErrSocket,
//runnerline.getposactual () );
pos);
endloop= handle_error (* this, berr);
//if (endloop)
// break;
}
catch (BlErrNo e)
{
berr= BlError (e,
//runnerline.getposactual () );
pos);
endloop= handle_error (* this, berr);
//if (endloop)
// break;
}
catch (BlError & newberr)
{
berr= newberr;
endloop= handle_error (* this, berr);
//if (endloop)
// break;
}
catch (BlBreak &)
{
fInterrupted= false;
//ProgramPos actual= runnerline.getposactual ();
ProgramPos actual= pos;
switch (getbreakstate () )
{
case onbreak::BreakStop:
if (fn_level () > 0)
throw;
endloop= true;
dobreak= true;
break;
case onbreak::BreakCont:
throw BlError (ErrBlassicInternal, actual);
case onbreak::BreakGosub:
gosub_line (getbreakgosub (), actual);
break;
}
//if (endloop)
// break;
}
#if 0
catch (blassic::ProgramEnd &)
{
if (fn_level () > 0)
{
if (showdebuginfo () )
cerr << "END inside DEF FN in line " <<
codeline.number () <<
endl;
throw;
}
close_all ();
setstatus (Ended);
//break;
endloop= true; // New
}
catch (blassic::ProgramPassedLastLine &)
{
if (fn_level () > 0)
{
if (showdebuginfo () )
cerr << "End of program reached "
"inside DEF FN" <<
endl;
throw;
}
//break;
endloop= true; // New
}
catch (blassic::ProgramStop &)
{
if (fn_level () > 0)
{
if (showdebuginfo () )
cerr << "STOP inside DEF FN "
"in line "
<<
codeline.number () <<
endl;
throw;
}
BlFile & f= getfile0 ();
f << "**Stopped**";
if (codeline.number () != LineDirectCommand)
f << " in " << codeline.number ();
f.endline ();
//ProgramPos posbreak (runnerline.getposactual () );
ProgramPos posbreak (pos);
posbreak.nextchunk ();
set_break (posbreak);
setstatus (Stopped);
//break;
endloop= true; // New
}
catch (blassic::ProgramFnEnd &)
{
#if 0
TRMESSAGE (tr, "FnEnd");
if (fn_level () == 0)
{
berr= BlError (ErrUnexpectedFnEnd, pos);
endloop= handle_error (* this, berr);
}
try
{
gosub_check_fn ();
setstatus (Ended);
endloop= true;
}
catch (BlErrNo e)
{
berr.set (e, pos);
endloop= handle_error (* this, berr);
}
#else
fnend= true;
#endif
}
if (fnend)
{
TRMESSAGE (tr, "FnEnd");
if (fn_level () == 0)
{
berr.set (ErrUnexpectedFnEnd, pos);
endloop= handle_error (* this, berr);
}
try
{
gosub_check_fn ();
setstatus (Ended);
endloop= true;
}
catch (BlErrNo e)
{
berr.set (e, pos);
endloop= handle_error (* this, berr);
}
}
#endif
}
#endif
#if 0
void Runner::runline (const CodeLine & codeline0)
{
TRACEFUNC (tr, "Runner::runline");
if (codeline0.number () != LineDirectCommand)
{
ASSERT (false);
throw ErrBlassicInternal;
}
auto_ptr <RunnerLine> prunnerline
(newRunnerLine (* this, codeline0) );
RunnerLine & runnerline= * prunnerline.get ();
CodeLine & codeline= runnerline.getcodeline ();
bool endloop= false;
bool dobreak= false;
BlError berr;
setstatus (InitingCommand);
// Test:
otra:
//for (;;)
//{
try
{
TRMESSAGE (tr, statusname (status) );
if (! is_run_status (codeline, codeline0) )
//if ( (this->*(checkstatus [status]) )
// (codeline, codeline0) == StopNow)
{
// Test:
//break;
goto atloopend;
}
//do {
//TRMESSAGE (tr, statusname (status) );
//runnerline.setline (codeline);
// Do tron if active.
tronline (codeline);
//runnerline.execute ();
prunnerline->execute ();
//} while (checkstatus (codeline, codeline0) );
}
catch (std::bad_alloc &)
{
berr.set (ErrOutMemory, runnerline.getposactual () );
endloop= handle_error (* this, berr);
//if (endloop)
// break;
}
catch (SocketError & se)
{
if (showdebuginfo () )
cerr << se.what () << endl;
berr.set (ErrSocket, runnerline.getposactual () );
endloop= handle_error (* this, berr);
//if (endloop)
// break;
}
catch (BlErrNo e)
{
berr.set (e, runnerline.getposactual () );
endloop= handle_error (* this, berr);
//if (endloop)
// break;
}
catch (BlError & newberr)
{
berr= newberr;
endloop= handle_error (* this, berr);
//if (endloop)
// break;
}
catch (BlBreak &)
{
fInterrupted= false;
ProgramPos actual= runnerline.getposactual ();
//ProgramPos actual= pos;
switch (getbreakstate () )
{
case onbreak::BreakStop:
if (fn_level () > 0)
throw;
endloop= true;
dobreak= true;
break;
case onbreak::BreakCont:
throw BlError (ErrBlassicInternal, actual);
case onbreak::BreakGosub:
gosub_line (getbreakgosub (), actual);
break;
}
//if (endloop)
// break;
}
#if 0
catch (blassic::ProgramEnd &)
{
if (fn_level () > 0)
{
if (showdebuginfo () )
cerr << "END inside DEF FN in line " <<
codeline.number () <<
endl;
throw;
}
close_all ();
setstatus (Ended);
//break;
endloop= true; // New
}
catch (blassic::ProgramPassedLastLine &)
{
if (fn_level () > 0)
{
if (showdebuginfo () )
cerr << "End of program reached "
"inside DEF FN" <<
endl;
throw;
}
setstatus (Ended);
//break;
endloop= true; // New
}
catch (blassic::ProgramStop &)
{
if (fn_level () > 0)
{
if (showdebuginfo () )
cerr << "STOP inside DEF FN "
"in line "
<<
codeline.number () <<
endl;
throw;
}
BlFile & f= getfile0 ();
std::ostringstream oss;
oss << "**Stopped**";
if (codeline.number () != LineDirectCommand)
oss << " in " << codeline.number ();
f << oss.str ();
f.endline ();
ProgramPos posbreak (runnerline.getposactual () );
//ProgramPos posbreak (pos);
posbreak.nextchunk ();
set_break (posbreak);
setstatus (Stopped);
//break;
endloop= true; // New
}
catch (blassic::ProgramFnEnd &)
{
#if 0
TRMESSAGE (tr, "FnEnd");
if (fn_level () == 0)
{
berr= BlError (ErrUnexpectedFnEnd, pos);
endloop= handle_error
(* this, berr);
}
try
{
gosub_check_fn ();
setstatus (Ended);
endloop= true;
}
catch (BlErrNo e)
{
berr= BlError (e, pos);
endloop= handle_error
(* this, berr);
}
#else
if (fn_level () == 0)
{
berr= BlError (ErrUnexpectedFnEnd,
//pos);
runnerline.getposactual () );
}
else
{
gosub_check_fn ();
setstatus (Ended);
}
endloop= true;
#endif
}
#endif
catch (...)
{
TRMESSAGE (tr, "catched ...");
throw;
}
#if 0
catch (...)
{
runline_catch (codeline, runnerline.getposactual (),
berr, endloop, dobreak);
}
#endif
// Test
#if 0
if (endloop)
break;
#else
if (! endloop)
goto otra;
#endif
#if 0
check_again:
try
{
if (! checkstatus (codeline, codeline0) )
//break;
endloop= true;
}
catch (...)
{
runline_catch (codeline, runnerline.getposactual (),
berr, endloop, dobreak);
if (endloop)
{
//break;
}
else
goto check_again;
}
if (endloop)
break;
#endif
// Test:
//} // for (;;)
atloopend:
if (endloop)
{
if (dobreak)
throw BlBreakInPos (runnerline.getposactual () );
else if (berr.geterr () != 0)
{
seterror (berr);
throw berr;
}
}
}
#else
void Runner::runline (const CodeLine & codeline0)
{
TRACEFUNC (tr, "Runner::runline");
if (codeline0.number () != LineDirectCommand)
{
ASSERT (false);
throw ErrBlassicInternal;
}
#if 1
auto_ptr <RunnerLine> prunnerline
(newRunnerLine (* this, codeline0) );
RunnerLine & runnerline= * prunnerline.get ();
#else
RunnerLineImpl runnerline (* this, codeline0);
#endif
CodeLine & codeline= runnerline.getcodeline ();
bool endloop= false;
bool dobreak= false;
BlError berr;
//setstatus (InitingCommand);
setstatus (Running);
// {
// TRACEFUNC (traux, "runline auxiliar block");
while (! endloop)
{
try
{
do {
//TRMESSAGE (tr, statusname (status) );
// Do tron if active.
tronline (codeline);
//prunnerline->execute ();
runnerline.execute ();
TRMESSAGE (tr, "Line finished");
} while (is_run_status (codeline, codeline0) );
endloop= true;
}
catch (std::bad_alloc &)
{
berr.set (ErrOutMemory, runnerline.getposactual () );
endloop= handle_error (* this, berr);
}
catch (SocketError & se)
{
if (showdebuginfo () )
cerr << se.what () << endl;
berr.set (ErrSocket, runnerline.getposactual () );
endloop= handle_error (* this, berr);
}
catch (BlErrNo e)
{
berr.set (e, runnerline.getposactual () );
endloop= handle_error (* this, berr);
}
catch (BlError & newberr)
{
berr= newberr;
endloop= handle_error (* this, berr);
}
catch (BlBreak &)
{
fInterrupted= false;
ProgramPos actual= runnerline.getposactual ();
switch (getbreakstate () )
{
case onbreak::BreakStop:
if (fn_level () > 0)
throw;
endloop= true;
dobreak= true;
break;
case onbreak::BreakCont:
throw BlError (ErrBlassicInternal, actual);
case onbreak::BreakGosub:
gosub_line (getbreakgosub (), actual);
break;
}
}
//catch (Exit & e)
//{
// //TRMESSAGE (traux, "Exitting");
// throw (Exit (e.code () ) );
//}
} // while
// } // Auxiliar block
//if (endloop)
//{
if (dobreak)
throw BlBreakInPos (runnerline.getposactual () );
else if (berr.geterr () != 0)
{
seterror (berr);
throw berr;
}
//}
}
#endif
bool Runner::processline (const std::string & line)
{
TRACEFUNC (tr, "Runner::processline");
CodeLine codeline;
codeline.scan (line);
BlLineNumber nline= codeline.number ();
//if (nline == 0)
if (nline == LineDirectCommand)
{
if (blnAuto != LineEndProgram)
{
// This probably must be changed.
if (codeline.empty () )
program.deletelines (blnAuto, blnAuto);
else
{
codeline.setnumber (blnAuto);
program.insert (codeline);
}
if (blnAuto > BlMaxLineNumber - blnAutoInc)
{
blnAuto= LineEndProgram;
throw BlError (ErrLineExhausted,
LineDirectCommand);
}
else
blnAuto+= blnAutoInc;
}
else
{
if (codeline.empty () )
return false;
runline (codeline);
return true;
}
}
else
{
if (nline > BlMaxLineNumber)
throw BlError (ErrLineExhausted);
if (codeline.empty () )
program.deletelines (nline, nline);
else
program.insert (codeline);
if (blnAuto != LineEndProgram)
{
blnAuto= codeline.number ();
if (blnAuto > BlMaxLineNumber - blnAutoInc)
{
blnAuto= LineEndProgram;
throw BlError (ErrLineExhausted,
LineDirectCommand);
}
else
blnAuto+= blnAutoInc;
}
}
return false;
}
namespace {
void welcome (BlFile & f)
{
f.endline ();
f << "Blassic " << version::Major << '.' <<
version::Minor << '.' <<
version::Release;
f.endline ();
f << "(C) 2001-2012 Julian Albo";
f.endline ();
f.endline ();
}
} // namespace
bool Runner::editline (BlLineNumber bln, std::string & str)
{
TRACEFUNC (tr, "editline - line number->string");
std::string buffer;
{
BlFileOutString bfos;
program.list (bln, bln, bfos);
buffer= bfos.str ();
if (buffer.empty () )
{
bfos << bln << ' ';
bfos.endline ();
buffer= bfos.str ();
}
}
buffer.erase (buffer.size () - 1);
TRMESSAGE (tr, std::string (1, '\'') + buffer + '\'');
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;
BlFile & bf= getfile0 ();
bool r;
if ( (r= blassic::edit::editline (bf, buffer, inipos) ) == true)
str= buffer;
return r;
}
void Runner::interactive ()
{
TRACEFUNC (tr, "Runner::interactive");
// Now the title is stablished in blassic.cpp
//set_title ("blassic");
welcome (getfile0 () );
bool showprompt= true;
for (;;)
{
std::string line;
if (blnAuto != LineEndProgram)
{
if (! editline (blnAuto, line) )
fInterrupted= true;
}
else
{
if (showprompt)
{
BlFile & f= getfile0 ();
f << strPrompt;
f.endline ();
}
//cout << "] " << flush;
getfile0 ().getline (line, true);
}
if (fInterrupted)
{
fInterrupted= false;
BlFile & bf= getfile0 ();
bf << strbreak;
bf.endline ();
std::cin.clear ();
blnAuto= LineEndProgram;
continue;
}
if (! std::cin)
break;
#ifndef _Windows
if (! line.empty () && line [line.size () - 1] == '\r')
line= line.substr (0, line.size () - 1);
#endif
try
{
showprompt= processline (line);
}
catch (BlErrNo ben)
{
BlFile & f= getfile0 ();
f << ErrStr (ben);
f.endline ();
setstatus (Stopped);
}
catch (BlError & be)
{
BlFile & f= getfile0 ();
f << to_string (be);
f.endline ();
setstatus (Stopped);
}
catch (BlBreakInPos & bbip)
{
BlFile & f= getfile0 ();
f << to_string (bbip);
f.endline ();
set_break (bbip.getpos () );
setstatus (Stopped);
}
} // for (;;)
}
void Runner::clean_input ()
{
if (graphics::ingraphicsmode () )
graphics::clean_input ();
else
cursor::clean_input ();
}
void Runner::ring ()
{
if (graphics::ingraphicsmode () )
graphics::ring ();
else
cursor::ring ();
}
void Runner::set_title (const std::string & str)
{
if (graphics::ingraphicsmode () )
graphics::set_title (str);
else
cursor::set_title (str);
}
// End of runner.cpp