campo-sirio/include/alex.cpp
luca d5776467b8 Patch level :10.0 295 e successive
Files correlati     :
Ricompilazione Demo : [ ]
Commento            :
sistemato meglio urldefid e messe le define al posto dei numeri in alex


git-svn-id: svn://10.65.10.50/trunk@18800 c028cbd2-c16b-5b4b-a496-9718f37d4682
2009-04-30 10:30:48 +00:00

1313 lines
31 KiB
C++
Executable File

#include <alex.h>
#include <automask.h>
#include <colors.h>
#include <dongle.h>
#include <prefix.h>
#include <recarray.h>
#include <statbar.h>
#include <treectrl.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_strfind, 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",
"STRFIND", "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, "", BMP_NEXTREC).set_exit_key(K_F11);
add_button(DLG_LASTREC, 0, "", -24, -1, 10, 2, "", BMP_DARROWR2).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); // Swap (serve per concatenare le stringhe in maniera corretta)
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_strfind:
{
const TString& lit = _stack.pop().as_string();
const TString& big = _stack.pop().as_string();
_stack.push(big.find(lit)); break;
}
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")
{
const struct tm* t = xvt_time_now();
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;
}