#include #include #include #include #include #include #include #include #include #include #include /////////////////////////////////////////////////////////// // 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_rpop, 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 long 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); }; long 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(); } } return 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(), origin().y + 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) { set_color(NORMAL_COLOR, NORMAL_BACK_COLOR); 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); 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 DLG_CANCEL: if (e == fe_button) set_ignore_mon(true); break; default: break; } return true; } TAVM_monitor::TAVM_monitor() : TAutomask("Monitor", 1, 64, -1, -1, 1), _ignore_mon(false) { //toolbar modernissima add_button_tool(DLG_NEXTREC, "Step", TOOL_NEXTREC).set_exit_key(K_F11); add_button_tool(DLG_LASTREC, "Skip", TOOL_NEXTREC).set_exit_key(K_F10); add_button_tool(DLG_ELABORA, "Run", TOOL_LASTREC).set_exit_key(K_F5); add_button_tool(DLG_CANCEL, "No MON", TOOL_CANCEL).set_exit_key(K_F5); add_button_tool(DLG_QUIT, "Quit", TOOL_QUIT).set_exit_key(K_QUIT); //campi vari //finestra con codice TWindowed_field* wf = new TAVM_list_field(this); wf->create(101, 0, 0, 31, -3, page_win(0)); add_field(wf); _lw = (TAVM_list_window*)&wf->win(); //finestra stack TWindowed_field* sf = new TAVM_stack_field(this); sf->create(102, 34, 0, -3, 12, page_win(0)); add_field(sf); _sw = (TAVM_stack_window*)&sf->win(); //finestra di return stack TWindowed_field* rf = new TAVM_stack_field(this); rf->create(103, 34, 13, -2, -1, page_win(0)); add_field(rf); _rw = (TAVM_stack_window*)&rf->win(); 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 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); else xvtil_statbar_set(str); } 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: { const TVariant& var = _stack.pop(); if (!_vm->execute_dot(var)) // Demando la ridirezione dell'output xvtil_popup_message(var.as_string()); // More annoying than useful :-) } 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(); if (v0.is_real()) { const real m = v0.as_real() * v1.as_real(); v0.set(m); } else log_error("Stack underflow"); } 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_rpop: _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) { 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); } bool TAlex_virtual_machine::execute_dot(const TVariant& var) { return false; // Do nothing } TAlex_virtual_machine::TAlex_virtual_machine() : _avm(NULL) { } TAlex_virtual_machine::~TAlex_virtual_machine() { if (_avm != NULL) delete _avm; }