#include #include #include #include #include #include "ba8303.h" #include "ba8304.h" TVariant& TVariant_stack::peek(int depth) { const int sp = _sp-depth-1; return sp >= 0 ? (TVariant&)_var[sp] : NULL_VARIANT; } bool TVariant_stack::drop() { if (_sp > 0) { _sp--; return true; } return false; } TVariant& TVariant_stack::pop() { TVariant& var = peek(0); drop(); return var; } void TVariant_stack::roll(int depth) { const int sp = _sp-depth-1; if (sp >= 0) { TObject* var = _var.remove(sp); _var.insert(var, _sp-1); } } bool TVariant_stack::push(const TVariant& var) { if (_var.objptr(_sp) == NULL) _var.add(var, _sp); else (TVariant&)_var[_sp] = var; _sp++; return _sp < 1024; } bool TVariant_stack::push(long n) { const TVariant var(n); return push(var); } bool TVariant_stack::push(const real& n) { const TVariant var(n); return push(var); } bool TVariant_stack::push(const TString& str) { const TVariant var(str); return push(var); } void TVariant_stack::reset() { _sp = 0; } /////////////////////////////////////////////////////////// // TAVM_op /////////////////////////////////////////////////////////// enum AVM_opcode { avm_nop, avm_add, avm_and, avm_begin, avm_call_word, avm_cold, avm_cmp_eq, avm_cmp_gt, avm_cmp_gteq, avm_cmp_lt, avm_cmp_lteq, avm_cmp_noteq, avm_div, avm_do, avm_dot, avm_drop, avm_dup, avm_else, avm_execute, avm_false, avm_fetch, avm_if, avm_loop, avm_mon, avm_mul, avm_or, avm_over, avm_push, avm_repeat, avm_rdrop, avm_rpeek, avm_rpush, avm_rot, avm_store, avm_sub, avm_swap, avm_then, avm_true, avm_usrword, avm_warm, avm_while, avm_zzz }; const char* AVM_TOKENS[avm_zzz+1] = { "$NOP$", "+", "AND", "BEGIN", "$CALL_WORD$", "COLD", "=", ">", ">=", "<", "<=", "<>", "/", "DO", ".", "DROP", "DUP", "ELSE", "EXECUTE", "FALSE", "@", "IF", "LOOP", "MON", "*", "OR", "OVER", "PUSH", "REPEAT", "R>", "R@", ">R", "ROT", "!", "-", "SWAP", "THEN", "TRUE", "#", "WARM", "WHILE" }; class TAVM_op : public TObject { AVM_opcode _op; TVariant _var; public: const TVariant& var() const { return _var; } TVariant& var() { return _var; } AVM_opcode op() const { return _op; } 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(AVM_opcode o); }; TAVM_op::TAVM_op(AVM_opcode o, const TString& str) : _op(o), _var(str) { } TAVM_op::TAVM_op(AVM_opcode o, const real& num) : _op(o), _var(num) { } TAVM_op::TAVM_op(AVM_opcode o, const long num) : _op(o), _var(num) { } TAVM_op::TAVM_op(AVM_opcode o) : _op(o) { } /////////////////////////////////////////////////////////// // TAVM_monitor /////////////////////////////////////////////////////////// class TAVM_list_window : public TField_window { const TBytecode* _bc; int _ip; protected: virtual void update(); public: void set_bytecode(const TBytecode* bc, int ip); TAVM_list_window(int x, int y, int dx, int dy, WINDOW parent, TWindowed_field* owner); }; void TAVM_list_window::update() { clear(NORMAL_BACK_COLOR); if (_bc != NULL) { set_brush(DISABLED_BACK_COLOR); bar(0, 0, 5, _bc->items()); set_brush(NORMAL_BACK_COLOR); TString str; int tab = 6; const int first = origin().y; const int last = min(_bc->items(), first+rows()); for (int i = first; i < last; i++) { if (_ip == i) { set_brush(FOCUS_BACK_COLOR); bar(0, i, 80, i+1); set_brush(NORMAL_BACK_COLOR); } printat(0, i, "%04d", i); const TAVM_op& op = *(const TAVM_op*)_bc->objptr(i); const AVM_opcode co = op.op(); const TVariant& var = op.var(); if (co == avm_else || co == avm_then) tab -= 2; if (co == avm_push) { if (var.type() == _alfafld && var.as_string()[0] != '#') str.cut(0) << '"' << var.as_string() << '"'; else str = var.as_string(); } else if (co == avm_call_word) { str = var.as_string(); } else { str = AVM_TOKENS[co]; if (!var.is_null()) str << " (" << var.as_string() << ')'; } printat(tab, i, str); if (co == avm_if || co == avm_else) tab += 2; } } } void TAVM_list_window::set_bytecode(const TBytecode* bc, int ip) { _bc = bc; _ip = ip; 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++) printat(0, i, _stack->peek(i).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 { protected: TMask_field* parse_field(TScanner& scanner); virtual bool on_field_event(TOperable_field& o, TField_event e, long jolly); public: TAVM_list_window& monitor() { return (TAVM_list_window&)((TAVM_list_field&)field(101)).win(); } TAVM_stack_window& stacker() { return (TAVM_stack_window&)((TAVM_list_field&)field(102)).win(); } TAVM_monitor(); }; bool TAVM_monitor::on_field_event(TOperable_field& o, TField_event e, long jolly) { return true; } TMask_field* TAVM_monitor::parse_field(TScanner& scanner) { if (scanner.token().starts_with("LI")) return new TAVM_list_field(this); if (scanner.token().starts_with("ST")) return new TAVM_stack_field(this); return TAutomask::parse_field(scanner); } TAVM_monitor::TAVM_monitor() { read_mask("ba8304", 0, -1); set_handlers(); } /////////////////////////////////////////////////////////// // TAVM /////////////////////////////////////////////////////////// class TAVM { TAlex_virtual_machine* _vm; TString _last_error; TVariant_stack _stack, _rstack; const TBytecode* _bc; // Current word (or command line) int _ip; // Current instruction pointer TAssoc_array _words; TAVM_monitor _mon; protected: bool get_token(istream& instr, TString& str) const; AVM_opcode token2opcode(const TString& str) const; void log_error(const char* str); 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); public: const TString& get_last_error() const { return _last_error; } bool compile(istream& instr, TBytecode& bc); bool execute(const TBytecode& bc, ostream& outstr); void restart(bool cold); TAVM(TAlex_virtual_machine* vm); virtual ~TAVM(); }; void TAVM::log_error(const char* str) { _last_error = str; error_box(str); } bool TAVM::get_token(istream& instr, TString& str) const { str.cut(0); instr.eatwhite(); 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) && c != 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() = bytecode.items(); break; } } return i; } 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[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); compile(instr, *bc); } else { _last_error = "Missing word after :"; log_error(_last_error); return false; } } else if (str == ";") { return true; } 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_repeat: { const int begin_pos = find_matching(bytecode, avm_begin); if (begin_pos < 0) { _last_error = "REPEAT 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: op = new TAVM_op(oc, str); break; default: op = new TAVM_op(oc); break; } } else { const unsigned int oc = _vm->compile_usr_word(str); if (oc > 0) op = new TAVM_op(avm_usrword, oc); } } if (op != NULL) bytecode.add(op); else { _last_error.cut(0) << "Unknown WORD: " << str; log_error(_last_error); return false; } } return true; } int TAVM::compare_tos_nos() { const TVariant& v0 = _stack.pop(); const TVariant& v1 = _stack.pop(); return v1.compare(v0); } void TAVM::do_call(const TString& func) { _rstack.push(_bc->name()); _rstack.push(_ip+1); _ip = -1; // will be incremented! _bc = (TBytecode*)_words.objptr(func); } void TAVM::execute(const TAVM_op& op) { switch(op.op()) { case avm_add : { const TVariant& v1 = _stack.pop(); TVariant& v0 = (TVariant&)_stack.peek(); v0.add(v1); } 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_call_word: do_call(op.var().as_string()); break; case avm_cold: restart(true); _bc = NULL; 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_div: { const real& r0 = _stack.pop().as_real(); const real& r1 = _stack.pop().as_real(); real n; if (!r0.is_zero()) n = r1 / r0; _stack.push(n); } break; case avm_do: break; case avm_dot: /* *_outstr << _stack.pop().as_string(); */ break; case avm_drop: _stack.drop(); break; case avm_dup: _stack.push(_stack.peek()); break; case avm_else: _ip = op.var().as_int(); break; case avm_execute: do_call(_stack.pop().as_string()); break; case avm_false: _stack.push(0L); break; case avm_fetch: { const TString& name = _stack.pop().as_string(); TVariant var; if (name[0] == '#') _vm->get_usr_val(name, var); else { // TBI: Get global var } _stack.push(var); } break; case avm_if: if (_stack.pop().is_zero()) _ip = op.var().as_int(); break; case avm_loop: break; case avm_mon: { lock_preview_update(true); TAVM_list_window& monitor = _mon.monitor(); monitor.set_bytecode(_bc, _ip); if (!_mon.is_open()) _mon.open(); } 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_or: { 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_over: _stack.push(_stack.peek(1)); 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_rot: _stack.roll(2); break; case avm_store: { const TString& name = _stack.pop().as_string(); const TVariant& var = _stack.pop(); if (name[0] == '#') _vm->set_usr_val(name, var); else { // TBI: Set global var } } 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_true: _stack.push(1L); break; case avm_usrword: { const long usrword = op.var().as_int(); _vm->execute_usr_word(usrword, _stack); } break; case avm_warm: restart(false); _bc = NULL; break; case avm_while: _ip = op.var().as_int(); break; // Jump to BEGIN default: _last_error << "Unimplemented op code: " << op.op() << '\n'; log_error(_last_error); _bc = NULL; // force exit break; } } bool TAVM::execute(const TBytecode& cmdline, ostream& outstr) { _bc = &cmdline; _ip = 0; while (_bc != NULL) { if (_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.blank()) _bc = &cmdline; else _bc = (const TBytecode*)_words.objptr(str); } else break; // Fine esecuzione } if (_mon.is_open()) // Gestione debugger { lock_preview_update(true); TAVM_list_window& monitor = _mon.monitor(); monitor.set_bytecode(_bc, _ip); TAVM_stack_window& stacker = _mon.stacker(); stacker.set_stack(_stack); const KEY k = _mon.run(); switch (k) { case K_NEXT: monitor.force_update(); stacker.force_update(); break; case K_DEL : abort_printing(); case K_QUIT: _mon.close(); lock_preview_update(false); break; default: break; } } const TAVM_op& op = *(const TAVM_op*)_bc->objptr(_ip); execute(op); _ip++; } if (_mon.is_open()) // Chiudi debugger { _mon.close(); lock_preview_update(false); } return _bc != NULL; } void TAVM::restart(bool cold) { _stack.reset(); _rstack.reset(); } TAVM::TAVM(TAlex_virtual_machine* vm) : _vm(vm) { } TAVM::~TAVM() { } /////////////////////////////////////////////////////////// // TAlex_virtual_machine /////////////////////////////////////////////////////////// TAVM& TAlex_virtual_machine::avm() { if (_avm == NULL) _avm = new TAVM(this); return *_avm; } 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, ostream& outstr) { return avm().execute(bc, outstr); } bool TAlex_virtual_machine::compile(const char* cmd, TBytecode& bc) { istrstream instr((char*)cmd, strlen(cmd)); return compile(instr, bc); } bool TAlex_virtual_machine::execute(const TBytecode& bc, TString& outs) { char* buf = outs.get_buffer(); memset(buf, 0, outs.size()); ostrstream outstr(buf, outs.size()); return execute(bc, outstr); } void TAlex_virtual_machine::warm_restart() // Ripartenza a caldo { avm().restart(false); } void TAlex_virtual_machine::cold_restart() // Ripartenza a freddo { avm().restart(true); } bool TAlex_virtual_machine::get_usr_val(const TString& name, TVariant& var) const { if (name == "#TODAY") { const TDate oggi(TODAY); var.set(oggi); return true; } if (name == "#FIRM") { var.set(prefix().get_codditta()); return true; } if (name == "#STUDY") { var.set(firm2dir(-1)); return true; } return false; } bool TAlex_virtual_machine::set_usr_val(const TString& name, const TVariant& var) { if (name == "#FIRM") { return prefix().set_codditta(var.as_int()); } return false; } unsigned int TAlex_virtual_machine::compile_usr_word(const TString& name) const { return 0; } bool TAlex_virtual_machine::execute_usr_word(unsigned int opcode, TVariant_stack& stack) { return false; } TAlex_virtual_machine::TAlex_virtual_machine() : _avm(NULL) { } TAlex_virtual_machine::~TAlex_virtual_machine() { if (_avm != NULL) delete _avm; }