📄 runnerline.cpp
字号:
// runnerline.cpp
#include "runnerline.h"
#include "error.h"
#include "dynamic.h"
#include "runner.h"
#include "cursor.h"
#include "sysvar.h"
#include "graphics.h"
#include "function.h"
#include "edit.h"
#include "util.h"
#include <sstream>
#include <iomanip>
#include <algorithm>
#include <functional>
#include <memory>
using std::auto_ptr;
#include <cmath>
#include <cstdio>
#include <cerrno>
#include <ctime>
#include <cctype>
using std::isalpha;
#include <iostream>
using std::cerr;
using std::endl;
#if defined __unix__ || defined __linux__ // Kylix defines only __linux__
#include <unistd.h>
#include <glob.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/utsname.h>
#else
// Define unlink?
using std::unlink;
#include <dir.h>
#endif
#include <cassert>
#define ASSERT assert
namespace {
#if defined __unix__ || defined __linux__
const char * os_family= "unix";
#else
const char * os_family= "windows";
#endif
// We encapsulate the random generator in a class to have all code
// related to it in a place, and to be able to change it easily.
// random in some systems may not use the RAND_MAX value,
// then we use it only in linux.
class RandomGenerator {
public:
BlNumber operator () ()
{
#if defined __linux__
return BlNumber (random () ) / (RAND_MAX + 1.0);
#else
return BlNumber (rand () ) / (RAND_MAX + 1.0);
#endif
}
void seed (unsigned int value)
{
#if defined __linux__
srandom (value);
#else
srand (value);
#endif
}
};
RandomGenerator randgen;
inline bool iscomp (BlCode code)
{
return code == '=' || code == keyDISTINCT ||
code == '<' || code == keyMINOREQUAL ||
code == '>' || code == keyGREATEREQUAL;
}
typedef std::map <std::string, Function> mapfunction_t;
mapfunction_t mapfunction;
} // namespace
const RunnerLine::mapfunc_t RunnerLine::mapfunc= initmapfunc ();
// Avoid call mapfunc.end every time.
const RunnerLine::mapfunc_t::const_iterator
RunnerLine::mapend= mapfunc.end ();
RunnerLine::mapfunc_t RunnerLine::initmapfunc ()
{
mapfunc_t m;
m [':']= & RunnerLine::do_empty_sentence;
m [keyENDLINE]= & RunnerLine::do_endline;
m [keyNUMBER]= & RunnerLine::do_number;
m [keyINTEGER]= & RunnerLine::do_number;
m [keyEND]= & RunnerLine::do_end;
m [keyLIST]= & RunnerLine::do_list;
m [keyREM]= & RunnerLine::do_rem;
m [keyLOAD]= & RunnerLine::do_load;
m [keySAVE]= & RunnerLine::do_save;
m [keyNEW]= & RunnerLine::do_new;
m [keyEXIT]= & RunnerLine::do_exit;
m [keyRUN]= & RunnerLine::do_run;
m [keyPRINT]= & RunnerLine::do_print;
m [keyFOR]= & RunnerLine::do_for;
m [keyNEXT]= & RunnerLine::do_next;
m [keyIF]= & RunnerLine::do_if;
m [keyTRON]= & RunnerLine::do_tron;
m [keyTROFF]= & RunnerLine::do_troff;
m [keyLET]= & RunnerLine::do_let;
m [keyIDENTIFIER]= & RunnerLine::do_let;
m [keyGOTO]= & RunnerLine::do_goto;
m [keySTOP]= & RunnerLine::do_stop;
m [keyCONT]= & RunnerLine::do_cont;
m [keyCLEAR]= & RunnerLine::do_clear;
m [keyGOSUB]= & RunnerLine::do_gosub;
m [keyRETURN]= & RunnerLine::do_return;
m [keyPOKE]= & RunnerLine::do_poke;
m [keyREAD]= & RunnerLine::do_read;
m [keyDATA]= & RunnerLine::do_data;
m [keyRESTORE]= & RunnerLine::do_restore;
m [keyINPUT]= & RunnerLine::do_input;
m [keyLINE]= & RunnerLine::do_line;
m [keyRANDOMIZE]= & RunnerLine::do_randomize;
m [keyAUTO]= & RunnerLine::do_auto;
m [keyDIM]= & RunnerLine::do_dim;
m [keySYSTEM]= & RunnerLine::do_system;
m [keyON]= & RunnerLine::do_on;
m [keyERROR]= & RunnerLine::do_error;
m [keyOPEN]= & RunnerLine::do_open;
m [keyCLOSE]= & RunnerLine::do_close;
m [keyLOCATE]= & RunnerLine::do_locate;
m [keyCLS]= & RunnerLine::do_cls;
m [keyWRITE]= & RunnerLine::do_write;
m [keyMODE]= & RunnerLine::do_mode;
m [keyMOVE]= & RunnerLine::do_move;
m [keyCOLOR]= & RunnerLine::do_color;
m [keyGET]= & RunnerLine::do_get;
m [keyLABEL]= & RunnerLine::do_label;
m [keyDELIMITER]= & RunnerLine::do_delimiter;
m [keyREPEAT]= & RunnerLine::do_repeat;
m [keyUNTIL]= & RunnerLine::do_until;
m [keyWHILE]= & RunnerLine::do_while;
m [keyWEND]= & RunnerLine::do_wend;
m [keyPLOT]= & RunnerLine::do_plot;
// Open and popen use same function.
m [keyPOPEN]= & RunnerLine::do_open;
m [keyRESUME]= & RunnerLine::do_resume;
m [keyDELETE]= & RunnerLine::do_delete;
m [keyLOCAL]= & RunnerLine::do_local;
m [keyPUT]= & RunnerLine::do_put;
m [keyFIELD]= & RunnerLine::do_field;
// Lset and rset use same function.
m [keyLSET]= & RunnerLine::do_lset;
m [keyRSET]= & RunnerLine::do_lset;
m [keySOCKET]= & RunnerLine::do_socket;
m [keyMID_S]= & RunnerLine::do_mid_s;
m [keyDRAW]= & RunnerLine::do_draw;
m [keyDEF]= & RunnerLine::do_def;
m [keyFN]= & RunnerLine::do_fn;
m [keyPROGRAMARG_S]= & RunnerLine::do_programarg_s;
m [keyERASE]= & RunnerLine::do_erase;
m [keySWAP]= & RunnerLine::do_swap;
m [keySYMBOL]= & RunnerLine::do_symbol;
m [keyZONE]= & RunnerLine::do_zone;
m [keyPOP]= & RunnerLine::do_pop;
m [keyNAME]= & RunnerLine::do_name;
m [keyKILL]= & RunnerLine::do_kill;
m [keyFILES]= & RunnerLine::do_files;
m [keyPAPER]= & RunnerLine::do_paper;
m [keyPEN]= & RunnerLine::do_pen;
m [keySHELL]= & RunnerLine::do_shell;
m [keyMERGE]= & RunnerLine::do_merge;
m [keyCHDIR]= & RunnerLine::do_chdir;
m [keyMKDIR]= & RunnerLine::do_mkdir;
m [keyRMDIR]= & RunnerLine::do_rmdir;
m [keySYNCHRONIZE]= & RunnerLine::do_synchronize;
m [keyPAUSE]= & RunnerLine::do_pause;
m [keyCHAIN]= & RunnerLine::do_chain;
m [keyENVIRON]= & RunnerLine::do_environ;
m [keyEDIT]= & RunnerLine::do_edit;
m [keyDRAWR]= & RunnerLine::do_drawr;
m [keyPLOTR]= & RunnerLine::do_plotr;
m [keyMOVER]= & RunnerLine::do_mover;
m [keyPOKE16]= & RunnerLine::do_poke16;
m [keyPOKE32]= & RunnerLine::do_poke32;
m [keyRENUM]= & RunnerLine::do_renum;
m [keyCIRCLE]= & RunnerLine::do_circle;
m [keyMASK]= & RunnerLine::do_mask;
m [keyWINDOW]= & RunnerLine::do_window;
m [keyGRAPHICS]= & RunnerLine::do_graphics;
return m;
}
#if 0 // Use macros instead to avoid strange errors on hp-ux.
inline void RunnerLine::requiretoken (BlCode code) const throw (BlErrNo)
{
if (token.code != code)
throw ErrSyntax;
}
inline void RunnerLine::expecttoken (BlCode code) throw (BlErrNo)
{
gettoken ();
requiretoken (code);
}
#else
#define requiretoken(c) if (token.code == c) ; else throw ErrSyntax
#define expecttoken(c) do { \
gettoken (); \
if (token.code != c) throw ErrSyntax; \
} while (0)
#endif
void RunnerLine::getnextchunk ()
{
while (! endsentence () )
gettoken ();
if (token.code != keyENDLINE)
gettoken ();
}
inline BlFile & RunnerLine::getfile (BlChannel channel)
{
return runner.getfile (channel);
}
inline BlNumber RunnerLine::evalnum ()
{
BlResult result;
eval (result);
return result.number ();
}
inline BlNumber RunnerLine::expectnum ()
{
gettoken ();
return evalnum ();
}
inline BlInteger RunnerLine::evalinteger ()
{
BlResult result;
eval (result);
return result.integer ();
}
inline BlInteger RunnerLine::expectinteger ()
{
gettoken ();
return evalinteger ();
}
inline BlChannel RunnerLine::evalchannel ()
{
BlResult result;
eval (result);
return util::checked_cast <BlChannel> (result.integer (), ErrMismatch);
}
inline BlChannel RunnerLine::expectchannel ()
{
gettoken ();
return evalchannel ();
}
inline std::string RunnerLine::evalstring ()
{
BlResult result;
eval (result);
return result.str ();
}
inline std::string RunnerLine::expectstring ()
{
gettoken ();
return evalstring ();
}
void RunnerLine::parenarg (BlResult & result)
{
expect (result);
requiretoken (')');
gettoken ();
}
void RunnerLine::getparenarg (BlResult & result)
{
expecttoken ('(');
expect (result);
requiretoken (')');
gettoken ();
}
void RunnerLine::valnumericfunc (double (* f) (double), BlResult & result)
{
getparenarg (result);
result= f (result.number () );
}
namespace { // Auxiliary math functions
double auxFIX (double n)
{
double r;
std::modf (n, & r);
return r;
}
#ifdef __WIN32__
double auxCINT (double n)
{
// Provisional.
double r;
n= std::modf (n, & r);
if (n >= 0.5) r+= 1.0;
else if (n <= -0.5) r-= 1.0;
return r;
}
#endif
} // namespace
void RunnerLine::valasc (BlResult & result)
{
getparenarg (result);
const std::string & str= result.str ();
if (str.empty () ) result= 0;
else result= BlInteger ( (unsigned char) str [0] );
}
void RunnerLine::vallen (BlResult & result)
{
getparenarg (result);
result= result.str ().size ();
}
void RunnerLine::valpeek (BlResult & result)
{
getparenarg (result);
BlChar * addr= (BlChar *) size_t (result.number () );
//result= BlNumber (size_t (* addr) );
result= BlInteger (size_t (* addr) );
}
void RunnerLine::valpeek16 (BlResult & result)
{
getparenarg (result);
BlChar * addr= (BlChar *) size_t (result.number () );
result= peek16 (addr);
}
void RunnerLine::valpeek32 (BlResult & result)
{
getparenarg (result);
BlChar * addr= (BlChar *) size_t (result.number () );
result= BlNumber (static_cast <unsigned long> (peek32 (addr) ) );
}
void RunnerLine::valprogramptr (BlResult & result)
{
gettoken ();
result= BlNumber (size_t (program.programptr () ) );
}
void RunnerLine::valrnd (BlResult & result)
{
BlNumber n;
gettoken ();
if (token.code == '(')
{
parenarg (result);
n= result.number ();
}
else n= 1;
static BlNumber previous= 0;
if (n == 0)
{
result= previous;
return;
}
if (n < 0)
srand (time (0) );
BlNumber r;
#if 0
#if defined __unix__ || defined __linux__
r= BlNumber (random () ) / (RAND_MAX + 1.0);
#else
r= BlNumber (rand () ) / (RAND_MAX + 1.0);
#endif
#else
r= randgen ();
#endif
result= r;
previous= r;
}
void RunnerLine::valinstr (BlResult & result)
{
expecttoken ('(');
std::string str;
size_t init= 0;
expect (result);
switch (result.type () )
{
case VarString:
str= result.str ();
break;
case VarNumber:
init= size_t (result.number () );
if (init > 0)
--init;
requiretoken (',');
str= expectstring ();
break;
case VarInteger:
init= result.integer ();
if (init > 0)
--init;
requiretoken (',');
str= expectstring ();
break;
default:
throw ErrBlassicInternal;
}
requiretoken (',');
std::string tofind= expectstring ();
requiretoken (')');
gettoken ();
size_t pos;
if (tofind.empty () )
{
if (str.empty () )
pos= 0;
else
if (init < str.size () )
pos= init + 1;
else
pos= 0;
}
else
{
pos= str.find (tofind, init);
if (pos == std::string::npos)
pos= 0;
else ++pos;
}
result= BlInteger (pos);
}
namespace {
class GuardHandle {
public:
GuardHandle (DynamicHandle * pn) :
phandle (pn)
{ }
~GuardHandle ()
{
if (* phandle)
dynamicclose (* phandle);
}
private:
DynamicHandle * phandle;
};
} // namespace
void RunnerLine::valusr (BlResult & result)
{
expecttoken ('(');
void * symaddr;
DynamicHandle libhandle= 0;
GuardHandle guard (& libhandle);
expect (result);
switch (result.type () )
{
case VarNumber:
symaddr= reinterpret_cast <void *>
(size_t (result.number () ) );
break;
case VarInteger:
symaddr= reinterpret_cast <void *> (result.integer () );
break;
case VarString:
{
std::string libname= result.str ();
requiretoken (',');
std::string funcname= expectstring ();
libhandle= dynamicload (libname);
if (! libhandle)
throw ErrNoDynamicLibrary;
symaddr= dynamicaddr (libhandle, funcname);
#ifdef _Windows
if (! symaddr)
{
funcname= std::string (1, '_') + funcname;
symaddr= dynamicaddr (libhandle, funcname);
}
#endif
if (! symaddr)
throw ErrNoDynamicSymbol;
}
break;
default:
throw ErrBlassicInternal;
}
int nparams= 0;
std::vector <int> vparam;
while (token.code == ',')
{
//BlNumber newpar= expectnum ();
//vparam.push_back (int (newpar) );
expect (result);
vparam.push_back (result.integer () );
++nparams;
}
requiretoken (')');
gettoken ();
util::auto_buffer <int> param;
if (nparams)
{
param.alloc (nparams);
std::copy (vparam.begin (), vparam.end (), param.begin () );
}
// reinterpret_cast produces a warning in gcc.
//DynamicUsrFunc f= reinterpret_cast <DynamicUsrFunc> (symaddr);
DynamicUsrFunc f= (DynamicUsrFunc) symaddr;
result= static_cast <BlInteger> (f (nparams, param) );
}
void RunnerLine::valval (BlResult & result)
{
getparenarg (result);
std::string str= result.str ();
switch (sysvar::get (sysvar::TypeOfVal) )
{
case 0:
// VAL simple.
{
#if 0
size_t i= 0, l= str.size ();
while (i < l && str [i] == ' ')
++i;
#else
std::string::size_type i=
str.find_first_not_of (" \t");
if (i > 0)
if (i == std::string::npos)
str.erase ();
else
str= str.substr (i);
#endif
}
result= CodeLine::Token::number (str);
break;
case 1:
// VAL with expression evaluation (Sinclair ZX)
if (str.find_first_not_of (" \t")
== std::string::npos)
{
result= 0;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -