Files correlati :ve0 ve1 Ricompilazione Demo : [ ] Commento :sistemato problema cambio stato ed errore fatale in stampa docs con reports git-svn-id: svn://10.65.10.50/trunk@14734 c028cbd2-c16b-5b4b-a496-9718f37d4682
1311 lines
30 KiB
C++
Executable File
1311 lines
30 KiB
C++
Executable File
#include <ctype.h>
|
|
#include <stdlib.h>
|
|
|
|
#include <alex.h>
|
|
#include <automask.h>
|
|
#include <colors.h>
|
|
#include <defmask.h>
|
|
#include <dongle.h>
|
|
#include <prefix.h>
|
|
#include <recarray.h>
|
|
#include <statbar.h>
|
|
#include <urldefid.h>
|
|
#include <utility.h>
|
|
|
|
///////////////////////////////////////////////////////////
|
|
// TAVM_op
|
|
///////////////////////////////////////////////////////////
|
|
|
|
enum AVM_opcode
|
|
{
|
|
avm_nop,
|
|
avm_add, avm_and,
|
|
avm_begin,
|
|
avm_call_word, avm_ceil, avm_cold, avm_cr,
|
|
avm_cmp_eq, avm_cmp_gt, avm_cmp_gteq, avm_cmp_lt, avm_cmp_lteq, avm_cmp_noteq,
|
|
avm_cmp_emptyeq, avm_cmp_nulleq, avm_cmp_zeroeq,
|
|
avm_div, avm_divide, avm_do, avm_dot, avm_drop, avm_dup,
|
|
avm_else,
|
|
avm_fetch, avm_forget,
|
|
avm_i, avm_if, avm_include,
|
|
avm_j,
|
|
avm_loop,
|
|
avm_mod, avm_mon, avm_mul,
|
|
avm_negate, avm_null,
|
|
avm_or, avm_over,
|
|
avm_perform, avm_pick, avm_plus_loop, avm_plus_store, avm_push,
|
|
avm_repeat, avm_rdrop, avm_rpeek, avm_rpush, avm_roll, avm_rot, avm_round,
|
|
avm_strlen, avm_strmid, avm_strtok_fetch, avm_strtok_add,
|
|
avm_store, avm_sp, avm_sub, avm_swap,
|
|
avm_then, avm_trunc,
|
|
avm_until, avm_usrword,
|
|
avm_warm, avm_while,
|
|
avm_zzz
|
|
};
|
|
|
|
const char* AVM_TOKENS[avm_zzz+1] =
|
|
{
|
|
"$NOP$",
|
|
"+", "AND",
|
|
"BEGIN",
|
|
"$CALL_WORD$", "CEIL", "COLD", "CR",
|
|
"=", ">", ">=", "<", "<=", "<>",
|
|
"EMPTY=", "NULL=", "0=",
|
|
"DIV", "/", "DO", ".", "DROP", "DUP",
|
|
"ELSE",
|
|
"@", "FORGET",
|
|
"I", "IF", "INCLUDE",
|
|
"J",
|
|
"LOOP",
|
|
"MOD", "MON", "*",
|
|
"NEGATE", "NULL",
|
|
"OR", "OVER",
|
|
"PERFORM", "PICK", "+LOOP", "+!", "$PUSH$",
|
|
"REPEAT", "R>", "R@", ">R", "ROLL", "ROT", "ROUND",
|
|
"STRLEN", "STRMID", "STRTOK@", "STRTOK+",
|
|
"!", "SP", "-", "SWAP",
|
|
"THEN", "TRUNC",
|
|
"UNTIL", "$USR$",
|
|
"WARM", "WHILE"
|
|
};
|
|
|
|
enum TBreakpointType { brk_none = 0x0, brk_user = 0x1, brk_auto = 0x2 };
|
|
|
|
class TAVM_op : public TObject
|
|
{
|
|
AVM_opcode _op;
|
|
TVariant _var;
|
|
int _break_pointer;
|
|
|
|
public:
|
|
const TVariant& var() const { return _var; }
|
|
TVariant& var() { return _var; }
|
|
AVM_opcode op() const { return _op; }
|
|
bool has_break() const { return _break_pointer != brk_none; }
|
|
bool has_auto_break() const { return (_break_pointer & brk_auto) != 0; }
|
|
void set_user_break(bool on);
|
|
void set_auto_break(bool on);
|
|
|
|
TAVM_op(AVM_opcode o, const TString& str);
|
|
TAVM_op(AVM_opcode o, const real& num);
|
|
TAVM_op(AVM_opcode o, const long num);
|
|
TAVM_op(const TAVM_op& op);
|
|
TAVM_op(AVM_opcode o);
|
|
};
|
|
|
|
void TAVM_op::set_user_break(bool on)
|
|
{
|
|
_break_pointer = on ? brk_user : brk_none;
|
|
}
|
|
|
|
void TAVM_op::set_auto_break(bool on)
|
|
{
|
|
if (on)
|
|
_break_pointer |= brk_auto;
|
|
else
|
|
_break_pointer &= ~brk_auto;
|
|
}
|
|
|
|
|
|
TAVM_op::TAVM_op(AVM_opcode o, const TString& str)
|
|
: _op(o), _var(str), _break_pointer(0)
|
|
{ }
|
|
|
|
TAVM_op::TAVM_op(AVM_opcode o, const real& num)
|
|
: _op(o), _var(num), _break_pointer(0)
|
|
{ }
|
|
|
|
TAVM_op::TAVM_op(AVM_opcode o, const long num)
|
|
: _op(o), _var(num), _break_pointer(0)
|
|
{ }
|
|
|
|
TAVM_op::TAVM_op(AVM_opcode o)
|
|
: _op(o), _break_pointer(0)
|
|
{ }
|
|
|
|
TAVM_op::TAVM_op(const TAVM_op& op) : _op(op._op), _var(op._var), _break_pointer(0)
|
|
{
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////
|
|
// TAVM_monitor
|
|
///////////////////////////////////////////////////////////
|
|
|
|
class TAVM_list_window : public TField_window
|
|
{
|
|
TBytecode* _bc;
|
|
int _ip;
|
|
const TString_array* _user_words;
|
|
|
|
protected:
|
|
virtual void update();
|
|
virtual void handler(WINDOW win, EVENT* ep);
|
|
|
|
public:
|
|
void set_bytecode(const TBytecode* bc, int ip, const TString_array& uw);
|
|
TAVM_list_window(int x, int y, int dx, int dy, WINDOW parent, TWindowed_field* owner);
|
|
};
|
|
|
|
void TAVM_list_window::handler(WINDOW win, EVENT* ep)
|
|
{
|
|
if (ep->type == E_MOUSE_DOWN)
|
|
{
|
|
const TPoint pt = dev2log(ep->v.mouse.where);
|
|
if (pt.x <= 5)
|
|
{
|
|
TAVM_op& op = *(TAVM_op*)_bc->objptr(pt.y-1);
|
|
op.set_user_break(ep->v.mouse.button == 0);
|
|
force_update();
|
|
}
|
|
}
|
|
TField_window::handler(win, ep);
|
|
}
|
|
|
|
void TAVM_list_window::update()
|
|
{
|
|
clear(NORMAL_BACK_COLOR);
|
|
if (_bc != NULL)
|
|
{
|
|
autoscroll(false);
|
|
set_brush(DISABLED_BACK_COLOR);
|
|
bar(0, 0, columns()+1, 1);
|
|
bar(0, 0, 5, rows()+1);
|
|
set_brush(NORMAL_BACK_COLOR);
|
|
printat(0, 0, _bc->name());
|
|
autoscroll(true);
|
|
|
|
TString str;
|
|
int tab = 6;
|
|
|
|
const int last = min(_bc->items(), rows());
|
|
for (int i = 0; i < last; i++)
|
|
{
|
|
|
|
const int y = i+1;
|
|
if (_ip == i)
|
|
{
|
|
set_brush(FOCUS_BACK_COLOR);
|
|
bar(0, y, 80, y+1);
|
|
set_brush(NORMAL_BACK_COLOR);
|
|
}
|
|
printat(0, y, "%04d", i);
|
|
const TAVM_op& op = *(const TAVM_op*)_bc->objptr(i);
|
|
const AVM_opcode co = op.op();
|
|
const TVariant& var = op.var();
|
|
|
|
if (op.has_break())
|
|
printat(4, y, "<");
|
|
|
|
if (co == avm_else || co == avm_then ||
|
|
co == avm_loop || co == avm_plus_loop ||
|
|
co == avm_repeat || co == avm_until || co == avm_while)
|
|
tab -= 2;
|
|
if (co == avm_push)
|
|
{
|
|
if (var.is_string() && var.as_string()[0] != '#')
|
|
str.cut(0) << '"' << var.as_string() << '"';
|
|
else
|
|
{
|
|
if (var.is_null())
|
|
str = "NULL";
|
|
else
|
|
str = var.as_string();
|
|
}
|
|
} else
|
|
if (co == avm_call_word)
|
|
{
|
|
str = var.as_string();
|
|
} else
|
|
if (co == avm_usrword)
|
|
{
|
|
str = _user_words->row(var.as_int());
|
|
str << " (#" << var.as_int() << ')';
|
|
}
|
|
else
|
|
{
|
|
str = AVM_TOKENS[co];
|
|
if (!var.is_null())
|
|
str << " (" << var.as_string() << ')';
|
|
}
|
|
printat(tab, y, str);
|
|
if (co == avm_if || co == avm_else ||
|
|
co == avm_do || co == avm_begin || co == avm_while)
|
|
tab += 2;
|
|
}
|
|
}
|
|
}
|
|
|
|
void TAVM_list_window::set_bytecode(const TBytecode* bc, int ip, const TString_array& uw)
|
|
{
|
|
_bc = (TBytecode*)bc;
|
|
_ip = ip;
|
|
_user_words = &uw;
|
|
set_scroll_max(80, _bc->items() - rows());
|
|
}
|
|
|
|
TAVM_list_window::TAVM_list_window(int x, int y, int dx, int dy, WINDOW parent, TWindowed_field* owner)
|
|
: TField_window(x, y, dx, dy, parent, owner), _bc(NULL)
|
|
{
|
|
autoscroll(true);
|
|
}
|
|
|
|
class TAVM_stack_window : public TField_window
|
|
{
|
|
TVariant_stack* _stack;
|
|
|
|
protected:
|
|
virtual void update();
|
|
|
|
public:
|
|
void set_stack(TVariant_stack& s);
|
|
TAVM_stack_window(int x, int y, int dx, int dy, WINDOW parent, TWindowed_field* owner);
|
|
};
|
|
|
|
void TAVM_stack_window::update()
|
|
{
|
|
clear(NORMAL_BACK_COLOR);
|
|
if (_stack != NULL)
|
|
{
|
|
for (int i = 0; i < _stack->items(); i++)
|
|
{
|
|
const TVariant& var = _stack->peek(i);
|
|
if (var.is_null())
|
|
printat(0, i, "NULL");
|
|
else
|
|
printat(0, i, var.as_string());
|
|
}
|
|
}
|
|
}
|
|
|
|
void TAVM_stack_window::set_stack(TVariant_stack& s)
|
|
{
|
|
_stack = &s;
|
|
set_scroll_max(80, s.items() - rows());
|
|
}
|
|
|
|
TAVM_stack_window::TAVM_stack_window(int x, int y, int dx, int dy, WINDOW parent, TWindowed_field* owner)
|
|
: TField_window(x, y, dx, dy, parent, owner), _stack(NULL)
|
|
{
|
|
autoscroll(true);
|
|
}
|
|
|
|
class TAVM_list_field : public TWindowed_field
|
|
{
|
|
protected:
|
|
virtual TField_window* create_window(int x, int y, int dx, int dy, WINDOW parent);
|
|
|
|
public:
|
|
TAVM_list_field(TMask* m) : TWindowed_field(m) { }
|
|
virtual ~TAVM_list_field() { }
|
|
};
|
|
|
|
TField_window* TAVM_list_field::create_window(int x, int y, int dx, int dy, WINDOW parent)
|
|
{
|
|
return new TAVM_list_window(x, y, dx, dy, parent, this);
|
|
}
|
|
|
|
class TAVM_stack_field : public TWindowed_field
|
|
{
|
|
protected:
|
|
virtual TField_window* create_window(int x, int y, int dx, int dy, WINDOW parent);
|
|
|
|
public:
|
|
TAVM_stack_field(TMask* m) : TWindowed_field(m) { }
|
|
virtual ~TAVM_stack_field() { }
|
|
};
|
|
|
|
TField_window* TAVM_stack_field::create_window(int x, int y, int dx, int dy, WINDOW parent)
|
|
{
|
|
return new TAVM_stack_window(x, y, dx, dy, parent, this);
|
|
}
|
|
|
|
class TAVM_monitor : public TAutomask
|
|
{
|
|
TAVM_list_window *_lw;
|
|
TAVM_stack_window *_sw, *_rw;
|
|
bool _ignore_mon;
|
|
|
|
protected:
|
|
virtual bool on_field_event(TOperable_field& o, TField_event e, long jolly);
|
|
virtual bool on_key(KEY k);
|
|
|
|
public:
|
|
void set_ignore_mon(bool im) { _ignore_mon = im; }
|
|
bool ignore_mon() const { return _ignore_mon; }
|
|
TAVM_list_window& monitor() const { return *_lw; }
|
|
TAVM_stack_window& stacker() const { return *_sw; }
|
|
TAVM_stack_window& rstacker() const { return *_rw; }
|
|
TAVM_monitor();
|
|
};
|
|
|
|
|
|
bool TAVM_monitor::on_field_event(TOperable_field& o, TField_event e, long jolly)
|
|
{
|
|
switch (o.dlg())
|
|
{
|
|
case 104:
|
|
if (e == fe_init)
|
|
o.set(ignore_mon() ? "X" : "");
|
|
if (e == fe_modify)
|
|
set_ignore_mon(!o.get().blank());
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool TAVM_monitor::on_key(KEY k)
|
|
{
|
|
switch (k)
|
|
{
|
|
case K_F10:
|
|
case K_F11:
|
|
stop_run(k);
|
|
return true;
|
|
default: break;
|
|
}
|
|
return TAutomask::on_key(k);
|
|
}
|
|
|
|
TAVM_monitor::TAVM_monitor() : TAutomask("Monitor", 1, 50, 20), _ignore_mon(false)
|
|
{
|
|
TWindowed_field* wf = new TAVM_list_field(this);
|
|
wf->create(101, 0, 0, 24, -4); add_field(wf);
|
|
_lw = (TAVM_list_window*)&wf->win();
|
|
|
|
TWindowed_field* sf = new TAVM_stack_field(this);
|
|
sf->create(102, 27, 0, -3, 9); add_field(sf);
|
|
_sw = (TAVM_stack_window*)&sf->win();
|
|
|
|
TWindowed_field* rf = new TAVM_stack_field(this);
|
|
rf->create(103, 27, 10, -3, -4); add_field(rf);
|
|
_rw = (TAVM_stack_window*)&rf->win();
|
|
|
|
add_boolean(104, 0, "Ignora MON d'ora in poi", 1, -3);
|
|
|
|
add_button(DLG_NEXTREC, 0, "", -14, -1, 10, 2, "", 124).set_exit_key(K_F11);
|
|
add_button(DLG_LASTREC, 0, "", -24, -1, 10, 2, "", 1671).set_exit_key(K_F10);
|
|
add_button(DLG_ELABORA, 0, "", -34, -1, 10, 2, "", BMP_LASTREC).set_exit_key(K_F5);
|
|
add_button(DLG_QUIT, 0, "", -44, -1, 10, 2).set_exit_key(K_QUIT);
|
|
|
|
set_handlers();
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////
|
|
// TAVM
|
|
///////////////////////////////////////////////////////////
|
|
|
|
class TAVM
|
|
{
|
|
TAlex_virtual_machine* _vm;
|
|
bool _interactive;
|
|
TString _last_error;
|
|
TVariant_stack _stack, _rstack;
|
|
const TBytecode* _bc; // Current word (or command line)
|
|
int _ip; // Current instruction pointer
|
|
ostream* _outstr;
|
|
|
|
TAssoc_array _words;
|
|
TAssoc_array _vars;
|
|
TString_array _user_words;
|
|
TAVM_monitor _mon;
|
|
|
|
protected:
|
|
bool get_token(istream& instr, TString& str) const;
|
|
AVM_opcode token2opcode(const TString& str) const;
|
|
int compare_tos_nos();
|
|
int find_matching(const TBytecode& bytecode, AVM_opcode op1, AVM_opcode op2 = avm_nop) const;
|
|
void execute(const TAVM_op& op);
|
|
void do_call(const TString& func);
|
|
|
|
const TString_array& get_user_words();
|
|
int compile_user_word(const char* n);
|
|
|
|
public:
|
|
void log_error(const char* str);
|
|
const TString& get_last_error() const { return _last_error; }
|
|
|
|
bool compile(istream& instr, TBytecode& bc);
|
|
bool execute(const TBytecode& bc);
|
|
void do_restart(bool cold);
|
|
bool do_include(const char* fname);
|
|
void do_fetch(const TString& name);
|
|
void do_add();
|
|
void do_store(const TString& name);
|
|
void set_interactive(bool inter) { _interactive = inter; }
|
|
bool defined(const char* w);
|
|
|
|
TAVM(TAlex_virtual_machine* vm);
|
|
virtual ~TAVM();
|
|
};
|
|
|
|
void TAVM::log_error(const char* str)
|
|
{
|
|
_last_error = str;
|
|
if (_interactive)
|
|
error_box(str);
|
|
#ifdef DBG
|
|
else
|
|
statbar_set_title(TASK_WIN, str);
|
|
#endif
|
|
}
|
|
|
|
bool TAVM::get_token(istream& instr, TString& str) const
|
|
{
|
|
str.cut(0);
|
|
eatwhite(instr);
|
|
if (instr.eof())
|
|
return false;
|
|
char c;
|
|
instr.get(c);
|
|
str << c;
|
|
if (c == '"')
|
|
{
|
|
char* buf = str.get_buffer()+1;
|
|
const int bufsize = str.size()-1;
|
|
instr.getline(buf, bufsize, '"');
|
|
str << '"';
|
|
}
|
|
else
|
|
{
|
|
instr.get(c);
|
|
while (!isspace(c) && !instr.eof())
|
|
{
|
|
str << c;
|
|
instr.get(c);
|
|
}
|
|
}
|
|
|
|
return *str > ' ';
|
|
}
|
|
|
|
#define ALEX_TOKENS 24
|
|
|
|
|
|
AVM_opcode TAVM::token2opcode(const TString& str) const
|
|
{
|
|
for (int i = 0; AVM_TOKENS[i] != NULL; i++)
|
|
{
|
|
if (str == AVM_TOKENS[i])
|
|
return AVM_opcode(i);
|
|
}
|
|
|
|
const TBytecode* bc =(const TBytecode*)_words.objptr(str);
|
|
if (bc != NULL)
|
|
return avm_call_word;
|
|
|
|
return avm_nop;
|
|
}
|
|
|
|
int TAVM::find_matching(const TBytecode& bytecode, AVM_opcode op1, AVM_opcode op2) const
|
|
{
|
|
int i;
|
|
for (i = bytecode.last(); i >= 0; i--)
|
|
{
|
|
TAVM_op& theop = (TAVM_op&)bytecode[i];
|
|
if ((theop.op() == op1 || theop.op() == op2) && theop.var().is_null())
|
|
{
|
|
theop.var() = long(bytecode.items());
|
|
break;
|
|
}
|
|
}
|
|
return i;
|
|
}
|
|
|
|
const TString_array& TAVM::get_user_words()
|
|
{
|
|
if (_user_words.items() == 0)
|
|
{
|
|
_user_words.add("***");
|
|
_vm->get_usr_words(_user_words);
|
|
}
|
|
return _user_words;
|
|
}
|
|
|
|
int TAVM::compile_user_word(const char* w)
|
|
{
|
|
const TString_array& uw = get_user_words();
|
|
const int i = uw.find(w);
|
|
return i > 0 ? i : 0;
|
|
}
|
|
|
|
bool TAVM::compile(istream& instr, TBytecode& bytecode)
|
|
{
|
|
TString str(256);
|
|
bytecode.destroy();
|
|
while (get_token(instr, str))
|
|
{
|
|
TAVM_op* op = NULL;
|
|
if (str[0] == '"')
|
|
{
|
|
str.rtrim(1); str.ltrim(1);
|
|
op = new TAVM_op(avm_push, str);
|
|
} else
|
|
if ((isdigit(str[0]) || (str[0]=='-')) && isdigit(str[str.len()-1]))
|
|
{
|
|
const real r(str);
|
|
op = new TAVM_op(avm_push, r);
|
|
} else
|
|
if (str[0] == '#') // User variable
|
|
{
|
|
op = new TAVM_op(avm_push, str);
|
|
} else
|
|
if (str == ":") // User word
|
|
{
|
|
if (get_token(instr, str))
|
|
{
|
|
TBytecode* bc = new TBytecode;
|
|
bc->set_name(str);
|
|
_words.add(str, bc, true);
|
|
compile(instr, *bc);
|
|
op = new TAVM_op(avm_nop);
|
|
}
|
|
else
|
|
{
|
|
_last_error = "Missing word after :";
|
|
log_error(_last_error);
|
|
return false;
|
|
}
|
|
} else
|
|
if (str == "VARIABLE")
|
|
{
|
|
if (get_token(instr, str))
|
|
{
|
|
_vars.add(str, NULL_VARIANT);
|
|
op = new TAVM_op(avm_nop);
|
|
}
|
|
else
|
|
{
|
|
_last_error = "Missing VARIABLE name";
|
|
log_error(_last_error);
|
|
return false;
|
|
}
|
|
} else
|
|
if (str == ";")
|
|
{
|
|
return true;
|
|
} else
|
|
if (str == "(")
|
|
{
|
|
TString256 str;
|
|
instr.getline(str.get_buffer(), str.size(), ')');
|
|
op = new TAVM_op(avm_nop);
|
|
} else
|
|
if (str == "\\")
|
|
{
|
|
TString256 str;
|
|
instr.getline(str.get_buffer(), str.size());
|
|
op = new TAVM_op(avm_nop);
|
|
} else
|
|
if (_vars.objptr(str) != NULL)
|
|
{
|
|
op = new TAVM_op(avm_push, str);
|
|
}
|
|
else
|
|
{
|
|
const int oc = compile_user_word(str);
|
|
if (oc > 0)
|
|
op = new TAVM_op(avm_usrword, oc);
|
|
else
|
|
{
|
|
const AVM_opcode oc = token2opcode(str);
|
|
if (oc != avm_nop)
|
|
{
|
|
switch (oc)
|
|
{
|
|
case avm_else:
|
|
if (find_matching(bytecode, avm_if) < 0)
|
|
{
|
|
_last_error = "ELSE without matching IF";
|
|
log_error(_last_error);
|
|
return false;
|
|
}
|
|
op = new TAVM_op(oc);
|
|
break;
|
|
case avm_then:
|
|
if (find_matching(bytecode, avm_if, avm_else) < 0)
|
|
{
|
|
_last_error = "THEN without matching IF";
|
|
log_error(_last_error);
|
|
return false;
|
|
}
|
|
op = new TAVM_op(oc);
|
|
break;
|
|
case avm_loop:
|
|
case avm_plus_loop:
|
|
{
|
|
const int do_pos = find_matching(bytecode, avm_do);
|
|
if (do_pos < 0)
|
|
{
|
|
_last_error.cut(0) << str << " without matching DO";
|
|
log_error(_last_error);
|
|
return false;
|
|
}
|
|
op = new TAVM_op(oc, do_pos);
|
|
}
|
|
break;
|
|
case avm_repeat:
|
|
case avm_until:
|
|
{
|
|
const int begin_pos = find_matching(bytecode, avm_begin);
|
|
if (begin_pos < 0)
|
|
{
|
|
_last_error.cut(0) << str << " without matching BEGIN";
|
|
log_error(_last_error);
|
|
return false;
|
|
}
|
|
find_matching(bytecode, avm_while);
|
|
op = new TAVM_op(oc, begin_pos);
|
|
}
|
|
break;
|
|
case avm_call_word:
|
|
{
|
|
const TBytecode* bc = (const TBytecode*)_words.objptr(str);
|
|
if (bc != NULL && bc->items() == 1)
|
|
{
|
|
const TAVM_op* inline_op = (const TAVM_op*)bc->objptr(0);
|
|
op = new TAVM_op(*inline_op);
|
|
}
|
|
else
|
|
op = new TAVM_op(oc, str);
|
|
}
|
|
break;
|
|
default:
|
|
op = new TAVM_op(oc);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (op != NULL)
|
|
{
|
|
if (op->op() != avm_nop)
|
|
bytecode.add(op);
|
|
else
|
|
delete op;
|
|
}
|
|
else
|
|
{
|
|
_last_error.cut(0) << "Unknown WORD: " << str;
|
|
log_error(_last_error);
|
|
// return false; // Non e' un errore gravissimo!
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
int TAVM::compare_tos_nos()
|
|
{
|
|
const TVariant& tos = _stack.pop();
|
|
const TVariant& nos = _stack.pop();
|
|
return nos.compare(tos);
|
|
}
|
|
|
|
void TAVM::do_call(const TString& func)
|
|
{
|
|
if (_stack.overflow())
|
|
{
|
|
log_error("Stack overflow");
|
|
_bc = NULL;
|
|
return;
|
|
}
|
|
TBytecode* bc = (TBytecode*)_words.objptr(func);
|
|
if (bc != NULL)
|
|
{
|
|
_rstack.push(_bc->name());
|
|
_rstack.push(_ip+1);
|
|
_ip = -1; // will be incremented!
|
|
_bc = bc;
|
|
}
|
|
else
|
|
{
|
|
_last_error = func; _last_error << " ?";
|
|
log_error(_last_error);
|
|
_bc = NULL;
|
|
}
|
|
}
|
|
|
|
bool TAVM::do_include(const char* fname)
|
|
{
|
|
TFilename name = fname;
|
|
bool ok = name.custom_path();
|
|
if (ok)
|
|
{
|
|
TBytecode bc;
|
|
ifstream inf(name);
|
|
ok = compile(inf, bc);
|
|
if (ok)
|
|
execute(bc);
|
|
}
|
|
return ok;
|
|
}
|
|
|
|
// Mette sullo stack il valore della variabile name
|
|
void TAVM::do_fetch(const TString& name)
|
|
{
|
|
const TVariant* var = name[0] != '#' ? (const TVariant*)_vars.objptr(name) : NULL;
|
|
if (var != NULL)
|
|
_stack.push(*var);
|
|
else
|
|
{
|
|
TVariant var;
|
|
_vm->get_usr_val(name, var);
|
|
_stack.push(var);
|
|
}
|
|
}
|
|
|
|
void TAVM::do_add()
|
|
{
|
|
const TVariant& v1 = _stack.pop();
|
|
TVariant& v0 = (TVariant&)_stack.peek();
|
|
v0.add(v1);
|
|
}
|
|
|
|
// Legge dallo stack il valore da asseganre alla variabile name
|
|
void TAVM::do_store(const TString& name)
|
|
{
|
|
const TVariant& var = _stack.pop();
|
|
TVariant* v = name[0] != '#' ? (TVariant*)_vars.objptr(name) : NULL;
|
|
if (v != NULL)
|
|
*v = var;
|
|
else
|
|
_vm->set_usr_val(name, var);
|
|
}
|
|
|
|
bool TAVM::defined(const char* name)
|
|
{
|
|
if (_words.objptr(name) != NULL)
|
|
return true;
|
|
|
|
return compile_user_word(name) > 0;
|
|
}
|
|
|
|
void TAVM::execute(const TAVM_op& op)
|
|
{
|
|
switch(op.op())
|
|
{
|
|
case avm_add: do_add(); break;
|
|
case avm_and:
|
|
{
|
|
const TVariant& v1 = _stack.pop();
|
|
TVariant& v0 = (TVariant&)_stack.peek();
|
|
const long r = v0.as_int() & v1.as_int();
|
|
v0.set(r);
|
|
}
|
|
break;
|
|
case avm_begin: break;
|
|
case avm_call_word: do_call(op.var().as_string()); break;
|
|
case avm_ceil:
|
|
{
|
|
const long dec = _stack.pop().as_int();
|
|
TVariant& v0 = (TVariant&)_stack.peek();
|
|
real k = v0.as_real(); k.ceil(dec);
|
|
v0 = k;
|
|
}
|
|
break;
|
|
case avm_cold: do_restart(true); _bc = NULL; break;
|
|
case avm_cr: _stack.push("\n"); break;
|
|
case avm_cmp_eq : _stack.push(compare_tos_nos() == 0); break;
|
|
case avm_cmp_gt : _stack.push(compare_tos_nos() > 0); break;
|
|
case avm_cmp_gteq : _stack.push(compare_tos_nos() >= 0); break;
|
|
case avm_cmp_lt : _stack.push(compare_tos_nos() < 0); break;
|
|
case avm_cmp_lteq : _stack.push(compare_tos_nos() <= 0); break;
|
|
case avm_cmp_noteq: _stack.push(compare_tos_nos() != 0); break;
|
|
case avm_cmp_emptyeq: _stack.push(_stack.pop().is_empty()); break;
|
|
case avm_cmp_nulleq:_stack.push(_stack.pop().is_null()); break;
|
|
case avm_cmp_zeroeq:_stack.push(_stack.pop().is_zero()); break;
|
|
case avm_div:
|
|
{
|
|
const long r0 = _stack.pop().as_int();
|
|
const long r1 = _stack.pop().as_int();
|
|
if (r0 != 0)
|
|
{
|
|
const long n = r1 / r0;
|
|
_stack.push(n);
|
|
}
|
|
else
|
|
_stack.push(NULL_VARIANT);
|
|
}
|
|
|
|
break;
|
|
case avm_divide:
|
|
{
|
|
const real& r0 = _stack.pop().as_real();
|
|
const real& r1 = _stack.pop().as_real();
|
|
if (!r0.is_zero())
|
|
{
|
|
const real n = r1 / r0;
|
|
_stack.push(n);
|
|
}
|
|
else
|
|
_stack.push(NULL_VARIANT);
|
|
}
|
|
break;
|
|
case avm_do:
|
|
{
|
|
const TVariant& start = _stack.pop();
|
|
const TVariant& limit = _stack.pop();
|
|
if (start.compare(limit) < 0)
|
|
{
|
|
_rstack.push(limit);
|
|
_rstack.push(start);
|
|
}
|
|
else
|
|
_ip = op.var().as_int();
|
|
}
|
|
break;
|
|
case avm_dot:
|
|
if (_outstr != NULL)
|
|
*_outstr << _stack.pop().as_string();
|
|
break;
|
|
case avm_drop:
|
|
if (!_stack.drop())
|
|
{
|
|
log_error("Stack underflow");
|
|
_bc = NULL;
|
|
}
|
|
break;
|
|
case avm_dup: _stack.push(_stack.peek()); break;
|
|
case avm_else:
|
|
_ip = op.var().as_int();
|
|
break;
|
|
case avm_fetch: do_fetch(_stack.pop().as_string()); break;
|
|
case avm_forget:
|
|
{
|
|
const TString& name = _stack.pop().as_string();
|
|
_vars.remove(name);
|
|
_words.remove(name);
|
|
}
|
|
break;
|
|
case avm_i: _stack.push(_rstack.peek()); break;
|
|
case avm_if:
|
|
if (_stack.pop().is_zero())
|
|
_ip = op.var().as_int();
|
|
break;
|
|
case avm_include: do_include(_stack.pop().as_string()); break;
|
|
case avm_j: _stack.push(_rstack.peek(2)); break;
|
|
case avm_loop:
|
|
{
|
|
TVariant& start = _rstack.pop();
|
|
const TVariant& limit = _rstack.pop();
|
|
start.add(TVariant(UNO));
|
|
_stack.push(limit);
|
|
_stack.push(start);
|
|
_ip = op.var().as_int()-1;
|
|
}
|
|
break;
|
|
case avm_mod:
|
|
{
|
|
const long i0 = _stack.pop().as_int();
|
|
const long i1 = _stack.pop().as_int();
|
|
_stack.push(i1 % i0);
|
|
}
|
|
break;
|
|
case avm_mon:
|
|
if (!_mon.is_open() && !_mon.ignore_mon())
|
|
_mon.open_modal();
|
|
break;
|
|
case avm_mul:
|
|
{
|
|
const TVariant& v1 = _stack.pop();
|
|
TVariant& v0 = (TVariant&)_stack.peek();
|
|
const real m = v0.as_real() * v1.as_real();
|
|
v0.set(m);
|
|
}
|
|
break;
|
|
case avm_negate:
|
|
{
|
|
TVariant& tos = _stack.peek();
|
|
tos.set(tos.as_bool() ? false : true);
|
|
}
|
|
break;
|
|
case avm_null: _stack.push(NULL_VARIANT); break;
|
|
case avm_or:
|
|
{
|
|
const TVariant& tos = _stack.pop();
|
|
TVariant& nos = (TVariant&)_stack.peek();
|
|
const long r = nos.as_int() | tos.as_int();
|
|
nos.set(r);
|
|
}
|
|
break;
|
|
case avm_over: _stack.push(_stack.peek(1)); break;
|
|
case avm_perform: do_call(_stack.pop().as_string()); break;
|
|
case avm_pick: _stack.push(_stack.peek(_stack.pop().as_int())); break;
|
|
case avm_plus_loop:
|
|
{
|
|
TVariant& start = _rstack.pop();
|
|
const TVariant& limit = _rstack.pop();
|
|
start.add(_stack.pop());
|
|
_stack.push(limit);
|
|
_stack.push(start);
|
|
_ip = op.var().as_int()-1;
|
|
}
|
|
break;
|
|
case avm_plus_store:
|
|
{
|
|
const TString name = _stack.pop().as_string();
|
|
do_fetch(name);
|
|
_stack.roll(1);
|
|
do_add();
|
|
do_store(name);
|
|
}
|
|
break;
|
|
case avm_push: _stack.push(op.var()); break;
|
|
case avm_repeat: _ip = op.var().as_int(); break;
|
|
case avm_rdrop: _stack.push(_rstack.pop()); break;
|
|
case avm_rpeek: _stack.push(_rstack.peek()); break;
|
|
case avm_rpush: _rstack.push(_stack.pop()); break;
|
|
case avm_roll: _stack.roll(_stack.pop().as_int()); break;
|
|
case avm_rot: _stack.roll(2); break;
|
|
case avm_round:
|
|
{
|
|
const long dec = _stack.pop().as_int();
|
|
TVariant& v0 = (TVariant&)_stack.peek();
|
|
real k = v0.as_real(); k.round(dec);
|
|
v0 = k;
|
|
}
|
|
break;
|
|
case avm_store: do_store(_stack.pop().as_string()); break;
|
|
case avm_strlen: _stack.push(_stack.pop().as_string().len()); break;
|
|
case avm_strmid:
|
|
{
|
|
const int len = _stack.pop().as_int();
|
|
const int frm = _stack.pop().as_int();
|
|
const TString& str = _stack.pop().as_string();
|
|
_stack.push(str.mid(frm, len)); break;
|
|
}
|
|
break;
|
|
case avm_strtok_fetch:
|
|
{
|
|
const int pos = _stack.pop().as_int();
|
|
TToken_string str(_stack.pop().as_string());
|
|
const char* tok = str.get(pos);
|
|
_stack.push(tok);
|
|
}
|
|
break;
|
|
case avm_strtok_add:
|
|
{
|
|
const TString& tok = _stack.pop().as_string();
|
|
TToken_string str(_stack.pop().as_string());
|
|
str.add(tok);
|
|
_stack.push(str);
|
|
}
|
|
break;
|
|
case avm_sp: _stack.push(_stack.items()); break;
|
|
case avm_sub :
|
|
{
|
|
const TVariant& v1 = _stack.pop();
|
|
TVariant& v0 = (TVariant&)_stack.peek();
|
|
v0.sub(v1);
|
|
}
|
|
break;
|
|
case avm_swap: _stack.roll(1); break;
|
|
case avm_then: break;
|
|
case avm_trunc:
|
|
{
|
|
const long dec = _stack.pop().as_int();
|
|
TVariant& v0 = (TVariant&)_stack.peek();
|
|
real k = v0.as_real(); k.trunc(dec);
|
|
v0 = k;
|
|
}
|
|
break;
|
|
|
|
case avm_until:
|
|
if (_stack.pop().is_zero())
|
|
_ip = op.var().as_int();
|
|
break;
|
|
case avm_usrword:
|
|
{
|
|
const long usrword = op.var().as_int();
|
|
_vm->execute_usr_word(usrword, _stack);
|
|
}
|
|
break;
|
|
case avm_warm: do_restart(false); _bc = NULL; break;
|
|
case avm_while:
|
|
if (_stack.pop().is_zero())
|
|
_ip = op.var().as_int(); // Exit loop
|
|
break;
|
|
default:
|
|
_last_error << "Unimplemented op code: " << op.op() << '\n';
|
|
log_error(_last_error);
|
|
_bc = NULL; // force exit
|
|
break;
|
|
}
|
|
}
|
|
|
|
bool TAVM::execute(const TBytecode& cmdline)
|
|
{
|
|
const TBytecode* old_bc = _bc;
|
|
const int old_ip = _ip;
|
|
bool aborted = false;
|
|
|
|
_stack.reset();
|
|
_rstack.reset();
|
|
_bc = &cmdline;
|
|
_ip = 0;
|
|
|
|
while (_bc != NULL)
|
|
{
|
|
while (_ip >= _bc->items()) // Fine funzione
|
|
{
|
|
if (_rstack.items() >= 2) // Controllo il return stack
|
|
{
|
|
_ip = _rstack.pop().as_int();
|
|
const TString& str = _rstack.pop().as_string();
|
|
if (str == cmdline.name())
|
|
_bc = &cmdline;
|
|
else
|
|
{
|
|
_bc = (const TBytecode*)_words.objptr(str);
|
|
if (_bc == NULL)
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
_bc = NULL;
|
|
break; // Fine esecuzione
|
|
}
|
|
}
|
|
if (_bc == NULL || _ip > _bc->items())
|
|
break;
|
|
|
|
TAVM_op& op = *(TAVM_op*)_bc->objptr(_ip);
|
|
if (op.has_break() && !_mon.is_open())
|
|
{
|
|
if (op.has_auto_break())
|
|
op.set_auto_break(false);
|
|
_mon.open_modal();
|
|
}
|
|
|
|
if (_mon.is_open()) // Gestione debugger
|
|
{
|
|
TAVM_list_window& monitor = _mon.monitor();
|
|
monitor.set_bytecode(_bc, _ip, _user_words);
|
|
TAVM_stack_window& stacker = _mon.stacker();
|
|
stacker.set_stack(_stack);
|
|
TAVM_stack_window& rstacker = _mon.rstacker();
|
|
rstacker.set_stack(_rstack);
|
|
const KEY k = _mon.run();
|
|
switch (k)
|
|
{
|
|
case K_F11: monitor.force_update(); stacker.force_update(); rstacker.force_update(); break;
|
|
case K_F10:
|
|
if (_ip < _bc->items()-1)
|
|
{
|
|
_mon.close_modal();
|
|
TAVM_op& op = *(TAVM_op*)_bc->objptr(_ip+1);
|
|
op.set_auto_break(true);
|
|
}
|
|
break;
|
|
case K_QUIT: aborted = true;
|
|
case K_F5 : _mon.close_modal(); break;
|
|
default: break;
|
|
}
|
|
}
|
|
|
|
execute(op);
|
|
|
|
_ip++;
|
|
}
|
|
|
|
if (_mon.is_open()) // Chiudi debugger
|
|
_mon.close_modal();
|
|
|
|
//const bool ok = _bc != NULL; // Not aborted
|
|
_bc = old_bc;
|
|
_ip = old_ip;
|
|
|
|
return !aborted;
|
|
}
|
|
|
|
void TAVM::do_restart(bool cold)
|
|
{
|
|
_stack.reset();
|
|
_rstack.reset();
|
|
if (cold)
|
|
{
|
|
_words.destroy();
|
|
_vars.destroy();
|
|
do_include("alex.alx");
|
|
}
|
|
_mon.set_ignore_mon(false);
|
|
}
|
|
|
|
TAVM::TAVM(TAlex_virtual_machine* vm)
|
|
: _vm(vm), _interactive(false), _outstr(NULL)
|
|
{
|
|
do_restart(true);
|
|
}
|
|
|
|
TAVM::~TAVM()
|
|
{
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////
|
|
// TAlex_virtual_machine
|
|
///////////////////////////////////////////////////////////
|
|
|
|
TAVM& TAlex_virtual_machine::avm()
|
|
{
|
|
if (_avm == NULL)
|
|
_avm = new TAVM(this);
|
|
return *_avm;
|
|
}
|
|
|
|
void TAlex_virtual_machine::log_error(const char* str)
|
|
{
|
|
if (_avm != NULL)
|
|
_avm->log_error(str);
|
|
}
|
|
|
|
const TString& TAlex_virtual_machine::get_last_error() const
|
|
{
|
|
if (_avm != NULL)
|
|
return _avm->get_last_error();
|
|
return EMPTY_STRING;
|
|
}
|
|
|
|
bool TAlex_virtual_machine::compile(istream& instr, TBytecode& bc)
|
|
{
|
|
return avm().compile(instr, bc);
|
|
}
|
|
|
|
bool TAlex_virtual_machine::execute(const TBytecode& bc)
|
|
{
|
|
return avm().execute(bc);
|
|
}
|
|
|
|
bool TAlex_virtual_machine::compile(const char* cmd, TBytecode& bc)
|
|
{
|
|
#ifdef LINUX
|
|
string s(cmd);
|
|
istringstream instr(s);
|
|
#else
|
|
istrstream instr((char*)cmd, (size_t)strlen(cmd));
|
|
#endif
|
|
return compile(instr, bc);
|
|
}
|
|
|
|
void TAlex_virtual_machine::warm_restart() // Ripartenza a caldo
|
|
{
|
|
avm().do_restart(false);
|
|
}
|
|
|
|
void TAlex_virtual_machine::cold_restart() // Ripartenza a freddo
|
|
{
|
|
avm().do_restart(true);
|
|
}
|
|
|
|
bool TAlex_virtual_machine::get_usr_val(const TString& name, TVariant& var) const
|
|
{
|
|
if (name.starts_with("#SYSTEM."))
|
|
{
|
|
const TFixed_string n((const char*)name+8);
|
|
if (n == "ADMINISTATOR")
|
|
{
|
|
var.set(dongle().administrator());
|
|
return true;
|
|
}
|
|
if (n == "CLOCK")
|
|
{
|
|
const long msec = clock() / (CLOCKS_PER_SEC / 1000);
|
|
var.set(msec);
|
|
return true;
|
|
}
|
|
if (n == "FIRM")
|
|
{
|
|
var.set(prefix().get_codditta());
|
|
return true;
|
|
}
|
|
if (n == "RAGSOC")
|
|
{
|
|
const long code = prefix().get_codditta();
|
|
const TString& ragsoc = cache().get(LF_NDITTE, code, n);
|
|
var.set(ragsoc);
|
|
return true;
|
|
}
|
|
if (n == "STUDY")
|
|
{
|
|
var.set(firm2dir(-1));
|
|
return true;
|
|
}
|
|
if (n == "DATE")
|
|
{
|
|
const TDate oggi(TODAY);
|
|
var.set(oggi);
|
|
return true;
|
|
}
|
|
if (n == "TIME")
|
|
{
|
|
time_t lt; time(<);
|
|
struct tm* t = localtime(<);
|
|
TString16 str;
|
|
str.format("%02d:%02d:%02d", t->tm_hour, t->tm_min, t->tm_sec);
|
|
var.set(str);
|
|
return true;
|
|
}
|
|
if (n == "USER")
|
|
{
|
|
var.set(user());
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool TAlex_virtual_machine::set_usr_val(const TString& name, const TVariant& var)
|
|
{
|
|
if (name.starts_with("#SYSTEM."))
|
|
{
|
|
const TFixed_string n((const char*)name+8);
|
|
if (n == "FIRM")
|
|
{
|
|
return prefix().set_codditta(var.as_int());
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
unsigned int TAlex_virtual_machine::get_usr_words(TString_array&) const
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
bool TAlex_virtual_machine::execute_usr_word(unsigned int opcode, TVariant_stack& stack)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool TAlex_virtual_machine::include(const char* fname)
|
|
{
|
|
return avm().do_include(fname);
|
|
}
|
|
|
|
void TAlex_virtual_machine::include_libraries(bool reload)
|
|
{
|
|
if (reload || !defined("2DUP"))
|
|
include("alex.alx");
|
|
}
|
|
|
|
void TAlex_virtual_machine::set_interactive(bool inter)
|
|
{ avm().set_interactive(inter); }
|
|
|
|
bool TAlex_virtual_machine::defined(const char* name)
|
|
{
|
|
return avm().defined(name);
|
|
}
|
|
|
|
TAlex_virtual_machine::TAlex_virtual_machine() : _avm(NULL)
|
|
{
|
|
}
|
|
|
|
TAlex_virtual_machine::~TAlex_virtual_machine()
|
|
{
|
|
if (_avm != NULL)
|
|
delete _avm;
|
|
}
|
|
|