campo-sirio/ba/ba8304.cpp

309 lines
6.4 KiB
C++
Raw Normal View History

#include <ctype.h>
#include <stdlib.h>
#include <date.h>
#include <real.h>
#include <stack.h>
#include "ba8304.h"
TVariant& TVariant_stack::peek(int depth)
{
const int sp = _sp-depth-1;
return sp >= 0 ? (TVariant&)_var[sp] : NULL_VARIANT;
}
TVariant& TVariant_stack::pop()
{
TVariant& var = peek();
if (_sp > 0)
_sp--;
return var;
}
void TVariant_stack::push(const TVariant& var)
{
if (_var.objptr(_sp) == NULL)
_var.add(var, _sp);
else
(TVariant&)_var[_sp] = var;
_sp++;
}
///////////////////////////////////////////////////////////
// TAVM_op
///////////////////////////////////////////////////////////
enum AVM_opcode { avm_nop, avm_add, avm_dot, avm_dup, avm_push, avm_sub,
avm_usrget, avm_usrset, 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);
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;
char* buf = str.get_buffer()+1;
const int bufsize = str.size()-1;
if (c == '"')
{
instr.getline(buf, bufsize, '"');
str << '"';
}
else
instr.getline(buf, bufsize, ' ');
return *str > ' ';
}
AVM_opcode TAVM::token2opcode(const TString& str) const
{
const char* AVM_TOKENS[4] = {
"+", "-", ".",
NULL
};
AVM_opcode AVM_OPCODES[4] = {
avm_add, avm_sub, avm_dot,
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();
bytecode.set_language("Alex");
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 (str[0] == '@') // Address of field?
{
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] == '#')
{
op = new TAVM_op(avm_usrget, str);
}
else
{
const AVM_opcode oc = token2opcode(str);
if (oc != avm_nop)
op = new TAVM_op(oc);
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;
}
bool TAVM::execute(const TBytecode& bc, ostream& outstr)
{
int ip = 0;
while (ip < bc.items())
{
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_dot: outstr << _stack.pop().as_string(); break;
case avm_dup: _stack.push(_stack.peek()); break;
case avm_push: _stack.push(op.var()); break;
case avm_sub :
{
const TVariant& v1 = _stack.pop();
TVariant& v0 = (TVariant&)_stack.peek();
v0.sub(v1);
}
break;
case avm_usrget:
{
const TString& name = op.var().as_string();
TVariant var; _vm->get_usr_val(name, var);
_stack.push(var);
}
break;
case avm_usrword: _vm->execute_usr_word(op.var().as_int(), _stack); break;
default:
_last_error << "Bad op code: " << op.op() << '\n';
log_error(_last_error);
return false;
}
if (!jumped_elsewhere)
ip++;
}
return true;
}
///////////////////////////////////////////////////////////
// TAlex_virtual_machine
///////////////////////////////////////////////////////////
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)
{
if (_avm == NULL)
_avm = new TAVM(this);
return _avm->compile(instr, bc);
}
bool TAlex_virtual_machine::execute(const TBytecode& bc, ostream& outstr)
{
if (_avm == NULL)
_avm = new TAVM(this);
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
{
return false;
}
bool TAlex_virtual_machine::set_usr_val(const TString& name, const TVariant& var)
{
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;
}