Files correlati : Ricompilazione Demo : [ ] Commento : Aggiunta gestione limiti di stampa git-svn-id: svn://10.65.10.50/trunk@11947 c028cbd2-c16b-5b4b-a496-9718f37d4682
490 lines
10 KiB
C++
Executable File
490 lines
10 KiB
C++
Executable File
#include <ctype.h>
|
|
#include <stdlib.h>
|
|
|
|
#include <prefix.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);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////
|
|
// TAVM_op
|
|
///////////////////////////////////////////////////////////
|
|
|
|
enum AVM_opcode { avm_nop, avm_add , avm_div , avm_dot,
|
|
avm_cmp_eq, avm_cmp_gt, avm_cmp_gteq, avm_cmp_lt, avm_cmp_lteq, avm_cmp_noteq,
|
|
avm_drop, avm_dup, avm_else,
|
|
avm_fetch, avm_if, avm_push, avm_rot, avm_store,
|
|
avm_sub, avm_swap, avm_then, avm_usrword };
|
|
|
|
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
|
|
///////////////////////////////////////////////////////////
|
|
|
|
class TAVM
|
|
{
|
|
TAlex_virtual_machine* _vm;
|
|
TString _last_error;
|
|
TVariant_stack _stack;
|
|
|
|
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();
|
|
|
|
public:
|
|
const TString& get_last_error() const { return _last_error; }
|
|
|
|
bool compile(istream& instr, TBytecode& bc);
|
|
bool execute(const TBytecode& bc, ostream& outstr);
|
|
|
|
TAVM(TAlex_virtual_machine* vm) : _vm(vm) { }
|
|
};
|
|
|
|
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 > ' ';
|
|
}
|
|
|
|
AVM_opcode TAVM::token2opcode(const TString& str) const
|
|
{
|
|
const char* AVM_TOKENS[20] = {
|
|
"+", "-", ".", "!", "@", "/",
|
|
"=", "<>", ">", "<", ">=", "<=",
|
|
"DROP", "DUP", "ELSE", "IF", "ROT",
|
|
"SWAP", "THEN",
|
|
NULL
|
|
};
|
|
|
|
AVM_opcode AVM_OPCODES[20] = {
|
|
avm_add, avm_sub, avm_dot, avm_store, avm_fetch, avm_div,
|
|
avm_cmp_eq, avm_cmp_noteq, avm_cmp_gt, avm_cmp_lt, avm_cmp_gteq, avm_cmp_lteq,
|
|
avm_drop, avm_dup, avm_else, avm_if, avm_rot, avm_swap, avm_then,
|
|
avm_nop
|
|
};
|
|
|
|
for (int i = 0; AVM_TOKENS[i] != NULL; i++)
|
|
{
|
|
if (str == AVM_TOKENS[i])
|
|
return AVM_OPCODES[i];
|
|
}
|
|
return avm_nop;
|
|
}
|
|
|
|
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]))
|
|
{
|
|
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
|
|
{
|
|
const AVM_opcode oc = token2opcode(str);
|
|
if (oc != avm_nop)
|
|
{
|
|
switch (oc)
|
|
{
|
|
case avm_else:
|
|
{
|
|
for (int i = bytecode.last(); i >= 0; i--)
|
|
{
|
|
TAVM_op& ifop = (TAVM_op&)bytecode[i];
|
|
if (ifop.op() == avm_if && ifop.var().is_null())
|
|
{
|
|
ifop.var() = bytecode.items() - i;
|
|
break;
|
|
}
|
|
}
|
|
if (i < 0)
|
|
{
|
|
_last_error = "ELSE without matching IF";
|
|
log_error(_last_error);
|
|
return false;
|
|
}
|
|
}
|
|
op = new TAVM_op(oc);
|
|
break;
|
|
case avm_then:
|
|
{
|
|
for (int i = bytecode.last(); i >= 0; i--)
|
|
{
|
|
TAVM_op& ifop = (TAVM_op&)bytecode[i];
|
|
if ((ifop.op() == avm_if || ifop.op() == avm_else) && ifop.var().is_null())
|
|
{
|
|
ifop.var() = bytecode.last() - i;
|
|
break;
|
|
}
|
|
}
|
|
if (i < 0)
|
|
{
|
|
_last_error = "THEN without matching IF";
|
|
log_error(_last_error);
|
|
return false;
|
|
}
|
|
}
|
|
op = new TAVM_op(oc);
|
|
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 v0.compare(v1);
|
|
}
|
|
|
|
bool TAVM::execute(const TBytecode& bc, ostream& outstr)
|
|
{
|
|
for (int ip = 0; ip < bc.items(); ip++)
|
|
{
|
|
const TAVM_op& op = *(const TAVM_op*)bc.objptr(ip);
|
|
bool jumped_elsewhere = false;
|
|
switch(op.op())
|
|
{
|
|
case avm_add :
|
|
{
|
|
const TVariant& v1 = _stack.pop();
|
|
TVariant& v0 = (TVariant&)_stack.peek();
|
|
v0.add(v1);
|
|
}
|
|
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;
|
|
const TVariant var(n);
|
|
_stack.push(var);
|
|
}
|
|
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();
|
|
jumped_elsewhere = true;
|
|
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().as_bool())
|
|
{
|
|
ip += op.var().as_int();
|
|
jumped_elsewhere = true;
|
|
}
|
|
break;
|
|
case avm_push: _stack.push(op.var()); 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_usrword:
|
|
{
|
|
const long usrword = op.var().as_int();
|
|
_vm->execute_usr_word(usrword, _stack);
|
|
}
|
|
break;
|
|
default:
|
|
_last_error << "Bad op code: " << op.op() << '\n';
|
|
log_error(_last_error);
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////
|
|
// 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);
|
|
}
|
|
|
|
bool TAlex_virtual_machine::get_usr_val(const TString& name, TVariant& var) const
|
|
{
|
|
if (name == "#DATE")
|
|
{
|
|
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;
|
|
}
|
|
|