/*
* Copyright (C) 2008 Kamil Dudka <xdudka00@stud.fit.vutbr.cz>
*
* This file is part of vyp08 (compiler and interpreter of VYP08 language).
*
* vyp08 is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* vyp08 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with vyp08. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include "cmd.h"
#include "vypIO.h"
using namespace StreamDecorator;
using std::string;
/// push immediate value to vmStack
class PushValueCmd: public ICmd {
public:
PushValueCmd(const Token &t, PValue val): t_(t), val_(val) { }
virtual void toStream(std::ostream &str) const {
str << Color(C_LIGHT_CYAN) << "PUSH_VALUE_CMD" << Color(C_NO_COLOR)
<< "(" << t_ << ")";
}
virtual bool exec(FncDefinition *fnc) {
return fnc->vm->vmStack.push(val_, t_);
}
private:
Token t_;
PValue val_;
};
/// read/write value of function return value
class ReturnCmd: public ICmd {
public:
ReturnCmd(const Token &t, bool push): t_(t), push_(push) { }
virtual void toStream(std::ostream &str) const {
str << Color(C_LIGHT_CYAN)
<< (push_ ? "PUSH_RETURN_CMD" : "POP_RETURN_CMD")
<< Color(C_NO_COLOR)
<< "(" << t_ << ")";
}
virtual bool exec(FncDefinition *fnc) {
return push_ ?
fnc->vm->vmStack.pushFromVar(fnc->self, t_):
fnc->vm->vmStack.popToVar(fnc->self, t_);
}
private:
Token t_;
bool push_;
};
/// read/write value of global variable
class GlVarCmd: public ICmd {
public:
GlVarCmd(const Token &t, bool push): t_(t), push_(push) { }
virtual void toStream(std::ostream &str) const {
str << Color(C_LIGHT_CYAN)
<< (push_ ? "PUSH_GL_VAR_CMD" : "POP_GL_VAR_CMD")
<< Color(C_NO_COLOR)
<< "(" << t_ << ")";
}
virtual bool exec(FncDefinition *fnc) {
Vm *vm = fnc->vm;
Var &var = *(vm->glVars[t_.text]);
return push_ ?
fnc->vm->vmStack.pushFromVar(var, t_):
fnc->vm->vmStack.popToVar(var, t_);
}
private:
Token t_;
bool push_;
};
/// read/write value of local variable
class LcVarCmd: public ICmd {
public:
LcVarCmd(const Token &t, bool push): t_(t), push_(push) { }
virtual void toStream(std::ostream &str) const {
str << Color(C_LIGHT_CYAN)
<< (push_ ? "PUSH_LC_VAR_CMD" : "POP_LC_VAR_CMD")
<< Color(C_NO_COLOR)
<< "(" << t_ << ")";
}
virtual bool exec(FncDefinition *fnc) {
Var &var = *(fnc->vars[t_.text]);
return push_ ?
fnc->vm->vmStack.pushFromVar(var, t_):
fnc->vm->vmStack.popToVar(var, t_);
}
private:
Token t_;
bool push_;
};
/// read/write value of function argument
class ArgCmd: public ICmd {
public:
ArgCmd(const Token &t, bool push): t_(t), push_(push) { }
virtual void toStream(std::ostream &str) const {
str << Color(C_LIGHT_CYAN)
<< (push_ ? "PUSH_ARG_CMD" : "POP_ARG_CMD")
<< Color(C_NO_COLOR)
<< "(" << t_ << ")";
}
virtual bool exec(FncDefinition *fnc) {
Var &var = *(fnc->args[t_.text]);
return push_ ?
fnc->vm->vmStack.pushFromVar(var, t_):
fnc->vm->vmStack.popToVar(var, t_);
}
private:
Token t_;
bool push_;
};
/// perform unary operator operation on vmStack
class UnaryCmd: public ICmd {
public:
UnaryCmd(const Token &t):
t_(t)
{
t_.text.clear();
}
virtual void toStream(std::ostream &str) const {
str << Color(C_LIGHT_CYAN) << "UNARY_CMD"
<< Color(C_NO_COLOR) << "(" << t_ << ")";
}
virtual bool exec(FncDefinition *fnc);
private:
Token t_;
};
/// perform binary operator operation on vmStack
class BinaryCmd: public ICmd {
public:
BinaryCmd(const Token &t):
t_(t)
{
t_.text.clear();
}
virtual void toStream(std::ostream &str) const {
str << Color(C_LIGHT_CYAN) << "BINARY_CMD"
<< Color(C_NO_COLOR) << "(" << t_ << ")";
}
virtual bool exec(FncDefinition *fnc);
bool chkTypes(Value::VType, Value::VType);
private:
Token t_;
int cmp(PConstValue, PConstValue);
void aluOp(PValue, PConstValue);
bool divide(PValue, PConstValue);
};
/// if statement as virtual machine command
class IfCmd: public ICmd {
public:
IfCmd(const Token &t, PCmd ifCmd, PCmd elseCmd):
t_(t), ifCmd_(ifCmd), elseCmd_(elseCmd)
{
t_.text.clear();
}
virtual void toStream(std::ostream &str) const {
str << Color(C_LIGHT_RED) << "IF_CMD"
<< Color(C_NO_COLOR) << "(" << t_ << ")";
str << Color(C_LIGHT_BLUE) << "[[" << Color(C_NO_COLOR);
ifCmd_->toStream(str);
str << Color(C_LIGHT_BLUE) << "]] " << Color(C_NO_COLOR)
<< Color(C_LIGHT_RED) << "else" << Color(C_NO_COLOR)
<< Color(C_LIGHT_BLUE) << " [[" << Color(C_NO_COLOR);
elseCmd_->toStream(str);
str << Color(C_LIGHT_BLUE) << "]]" << Color(C_NO_COLOR);
}
virtual bool exec(FncDefinition *fnc);
private:
Token t_;
PCmd ifCmd_;
PCmd elseCmd_;
};
/// while statement as virtual machine command
class WhileCmd: public ICmd {
public:
WhileCmd(const Token &t, PCmd whileExpr, PCmd whileStat):
t_(t), whileExpr_(whileExpr), whileStat_(whileStat)
{
t_.text.clear();
}
virtual void toStream(std::ostream &str) const {
str << Color(C_LIGHT_RED) << "WHILE_CMD"
<< Color(C_NO_COLOR) << "(" << t_ << ")";
str << Color(C_LIGHT_BLUE) << "[[" << Color(C_NO_COLOR);
whileExpr_->toStream(str);
str << Color(C_LIGHT_BLUE) << "]] " << Color(C_NO_COLOR)
<< Color(C_LIGHT_RED) << "while" << Color(C_NO_COLOR)
<< Color(C_LIGHT_BLUE) << " [[" << Color(C_NO_COLOR);
whileStat_->toStream(str);
str << Color(C_LIGHT_BLUE) << "]]" << Color(C_NO_COLOR);
}
virtual bool exec(FncDefinition *fnc);
private:
Token t_;
PCmd whileExpr_;
PCmd whileStat_;
};
/// function call command operating on vmStack
class CallCmd: public ICmd {
public:
CallCmd(const Token &t, unsigned nArgs, bool pushResult):
t_(t), nArgs_(nArgs), pushResult_(pushResult)
{
}
virtual void toStream(std::ostream &str) const {
str << Color(C_LIGHT_RED) << "CALL_CMD" << Color(C_NO_COLOR)
<< "(" << t_ << ")[" << nArgs_ << "]";
if (pushResult_)
str << "/" << Color(C_LIGHT_RED) << "PUSH_RESULT" << Color(C_NO_COLOR);
}
virtual bool exec(FncDefinition *fnc);
private:
Token t_;
unsigned nArgs_;
bool pushResult_;
};
/// virtual machine command for print() function
class PrintCmd: public ICmd {
public:
PrintCmd(const Token &t, Value::VType type):
t_(t)
{
var_.value.type = type;
}
virtual void toStream(std::ostream &str) const {
str << Color(C_LIGHT_RED) << "PRINT_CMD" << Color(C_NO_COLOR)
<< "(" << t_ << ")[" << var_.value.type << "]";
}
virtual bool exec(FncDefinition *fnc);
private:
Token t_;
Var var_;
};
/// virtual machine command for input{int|double|string}() function
class InputCmd: public ICmd {
public:
InputCmd(const Token &t, Value::VType type, bool pushValue):
t_(t), type_(type), pushValue_(pushValue)
{
}
virtual void toStream(std::ostream &str) const {
str << Color(C_LIGHT_RED) << "INPUT_CMD" << Color(C_NO_COLOR)
<< "(" << t_ << ")[" << type_ << "]";
}
virtual bool exec(FncDefinition *fnc);
private:
Token t_;
Value::VType type_;
bool pushValue_;
};
// /////////////////////////////////////////////////////////////////////////////
// CmdFactory implementation
struct CmdFactory::Private {
Vm *const vm;
Private(Vm *vm_):
vm(vm_)
{
}
};
CmdFactory::CmdFactory(Vm *vm):
d(new Private(vm))
{
}
CmdFactory::~CmdFactory() {
delete d;
}
PCmd CmdFactory::createAssign(Token token, FncDefinition *fnc, VType &dstType) {
if (token.type != ETOKEN_ID) {
std::cerr << Error(E_ERROR, d->vm->fileName, "unexpected token type in assignment", token, true) << std::endl;
return PCmd();
}
const string &name = token.text;
if (name == fnc->self.name) {
// ID resolved as return value of calling function
if (fnc->self.value.type == Value::V_NULL) {
std::cerr << Error(E_ERROR, d->vm->fileName, "return statement in void function", token) << std::endl;
return PCmd();
} else {
dstType = fnc->self.value.type;
return PCmd(new ReturnCmd(token, false));
}
}
PVar arg = fnc->args[name];
if (arg) {
// ID resolved as argument of calling function
dstType = arg->value.type;
return PCmd(new ArgCmd(token, false));
}
PVar lcVar = fnc->vars[name];
if (lcVar) {
// ID resolved as local variable of calling function
dstType = lcVar->value.type;
return PCmd(new LcVarCmd(token, false));
}
PVar glVar = d->vm->glVars[name];
if (glVar) {
// ID resolved as global variable
dstType = glVar->value.type;
return PCmd(new GlVarCmd(token, false));
}
// ID not resolved
std::cerr << Error(E_ERROR, d->vm->fileName, "unknown identifier in L-Value", token) << std::endl;
return PCmd();
}
PCmd CmdFactory::createPush(Token token, FncDefinition *fnc, VType &dstType) {
switch (token.type) {
case ETOKEN_NUMBER_INT:
dstType = Value::V_INT;
return PCmd(new PushValueCmd(token, ValueFactory::create(token.numberInt)));
case ETOKEN_NUMBER_DOUBLE:
dstType = Value::V_DOUBLE;
return PCmd(new PushValueCmd(token, ValueFactory::create(token.numberDouble)));
case ETOKEN_STRING:
dstType = Value::V_STRING;
return PCmd(new PushValueCmd(token, ValueFactory::create(token.text)));
case ETOKEN_ID:
{
const string &name = token.text;
if (name == fnc->self.name) {
// ID resolved as return value of calling function
if (fnc->self.value.type == Value::V_NULL) {
std::cerr << Error(E_ERROR, d->vm->fileName, "attempt to read return value in void function", token) << std::endl;
return PCmd();
} else {
dstType = fnc->self.value.type;
return PCmd(new ReturnCmd(token, true));
}
}
PVar arg = fnc->args[name];
if (arg) {
// ID resolved as argument of calling function
dstType = arg->value.type;
arg->used = true;
return PCmd(new ArgCmd(token, true));
}
PVar lcVar = fnc->vars[name];
if (lcVar) {
// ID resolved as local variable of calling function
dstType = lcVar->value.type;
lcVar->used = true;
return PCmd(new LcVarCmd(token, true));
}
PVar glVar = d->vm->glVars[name];
if (glVar) {
// ID resolved as global variable
dstType = glVar->value.type;
glVar->used = true;
return PCmd(new GlVarCmd(token, true));
}
// ID not resolved
std::cerr << Error(E_ERROR, d->vm->fileName, "unknown identifier in expression", token) << std::endl;
return PCmd();
}
default:
std::cerr << Error(E_ERROR, d->vm->fileName, "unexpected token type in push", token, true) << std::endl;
return PCmd();
}
}
PCmd CmdFactory::createIf(Token token, PCmd ifCmd, PCmd elseCmd) {
return PCmd(new IfCmd(token, ifCmd, elseCmd));
}
PCmd CmdFactory::createWhile(Token token, PCmd whileExpr, PCmd whileStat) {
return PCmd(new WhileCmd(token, whileExpr, whileStat));
}
PCmd CmdFactory::createUnary(Token token) {
switch (token.type) {
case ETOKEN_OP_MINUS:
case ETOKEN_KW_NOT:
return PCmd(new UnaryCmd(token));
default:
std::cerr << Error(E_ERROR, d->vm->fileName, "unexpected token for unary operator", token, true) << std::endl;
return PCmd();
}
}
PCmd CmdFactory::createBinary(Token token, VType t1, VType t2, VType &dstType) {
switch (token.type) {
case ETOKEN_KW_AND:
case ETOKEN_KW_DIV:
case ETOKEN_KW_EQ:
case ETOKEN_KW_OR:
case ETOKEN_KW_NEQ:
case ETOKEN_OP_GREATER:
case ETOKEN_OP_GREATER_EQ:
case ETOKEN_OP_LESS:
case ETOKEN_OP_LESS_EQ:
case ETOKEN_OP_MINUS:
case ETOKEN_OP_PLUS:
case ETOKEN_OP_SLASH:
case ETOKEN_OP_STAR:
break;
default:
std::cerr << Error(E_ERROR, d->vm->fileName, "unexpected token for binary operator", token, true) << std::endl;
return PCmd();
}
SHARED_PTR(BinaryCmd) cmd(new BinaryCmd(token));
#if 0
std::cerr << "token = " << token << ", t1 = " << t1 << ", t2 = " << t2 << std::endl;
#endif
if (!cmd->chkTypes(t1, t2)) {
// type mismatch on operands
dstType = Value::V_NULL;
return PCmd();
}
// determine dstType
switch (token.type) {
case ETOKEN_KW_AND:
case ETOKEN_KW_OR:
case ETOKEN_OP_LESS:
case ETOKEN_OP_LESS_EQ:
case ETOKEN_KW_EQ:
case ETOKEN_KW_NEQ:
case ETOKEN_OP_GREATER_EQ:
case ETOKEN_OP_GREATER:
dstType = Value::V_BOOL;
break;
case ETOKEN_OP_PLUS:
if (t1 == Value::V_STRING) {
dstType = Value::V_STRING;
break;
}
// go through (!!)
case ETOKEN_OP_MINUS:
case ETOKEN_OP_STAR:
dstType = (t1 == Value::V_INT && t2 == Value::V_INT)
? Value::V_INT : Value::V_DOUBLE;
break;
case ETOKEN_OP_SLASH:
dstType = Value::V_DOUBLE;
break;
case ETOKEN_KW_DIV:
dstType = Value::V_INT;
break;
default:
// unexpected token
return PCmd();
}
return cmd;
}
PCmd CmdFactory::createCall(Token token, unsigned nArgs, bool pushResult) {
return PCmd(new CallCmd(token, nArgs, pushResult));
}
PCmd CmdFactory::createPrint(Token token, EToken valType, VType &srcType) {
switch (valType) {
case ETOKEN_KW_INT:
srcType = Value::V_INT;
return PCmd(new PrintCmd(token, Value::V_INT));
case ETOKEN_KW_DOUBLE:
srcType = Value::V_DOUBLE;
return PCmd(new PrintCmd(token, Value::V_DOUBLE));
case ETOKEN_KW_STRING:
srcType = Value::V_STRING;
return PCmd(new PrintCmd(token, Value::V_STRING));
default:
std::cerr << Error(E_ERROR, d->vm->fileName, "unexpected type of PrintCmd", token, true) << std::endl;
return PCmd();
}
}
PCmd CmdFactory::createInput(Token token, Value::VType type, bool pushValue) {
return PCmd(new InputCmd(token, type, pushValue));
}
// /////////////////////////////////////////////////////////////////////////////
// UnaryCmd implementation
bool UnaryCmd::exec(FncDefinition *fnc) {
Vm *vm = fnc -> vm;
// pop value from stack
PValue val;
if (!vm->vmStack.pop(val, t_))
return false;
if (t_.type == ETOKEN_KW_NOT) {
if (val->type != Value::V_BOOL) {
// TODO: mark as internal after tests as this is now handled statically
std::cerr << Error(E_ERROR, vm->fileName, "operand type not allowed for ", t_) << t_ << std::endl;
std::cerr << Error(E_NOTE, vm->fileName, "operand type: ") << val->type << std::endl;
return false;
}
val->boolVal = !(val->boolVal);
return vm->vmStack.push(val, t_);
}
if (t_.type == ETOKEN_OP_MINUS) {
switch (val->type) {
case Value::V_INT:
val->intVal = -(val->intVal);
return vm->vmStack.push(val, t_);
case Value::V_DOUBLE:
val->doubleVal = -(val->doubleVal);
return vm->vmStack.push(val, t_);
default:
// TODO: mark as internal after tests as this is now checked statically
std::cerr << Error(E_ERROR, vm->fileName, "type not allowed for unary ", t_) << t_ << std::endl;
std::cerr << Error(E_NOTE, vm->fileName, "expression type: ") << val->type << std::endl;
return false;
}
}
return false;
}
// /////////////////////////////////////////////////////////////////////////////
// BinaryCmd implementation
bool BinaryCmd::chkTypes(Value::VType t1, Value::VType t2) {
switch (t_.type) {
// logical operators
case ETOKEN_KW_AND:
case ETOKEN_KW_OR:
return t1 == Value::V_BOOL
&& t2 == Value::V_BOOL;
// relation operators
case ETOKEN_OP_LESS:
case ETOKEN_OP_LESS_EQ:
case ETOKEN_KW_EQ:
case ETOKEN_KW_NEQ:
case ETOKEN_OP_GREATER_EQ:
case ETOKEN_OP_GREATER:
return t1 == t2 && (t1 == Value::V_INT
|| t1 == Value::V_DOUBLE
|| t2 == Value::V_STRING);
// arithmetic operators
case ETOKEN_OP_PLUS:
if (t1 == Value::V_STRING && t2 == Value::V_STRING)
// sting concatenation
return true;
// go through (!!)
case ETOKEN_OP_MINUS:
case ETOKEN_OP_STAR:
case ETOKEN_OP_SLASH:
return (t1 == Value::V_INT || t1 == Value::V_DOUBLE)
&& (t2 == Value::V_INT || t2 == Value::V_DOUBLE);
// extra handling for arithmetic division
case ETOKEN_KW_DIV:
return t1 == Value::V_INT
&& t2 == Value::V_INT;
default:
// unexpected token
return false;
}
}
int BinaryCmd::cmp(PConstValue val1, PConstValue val2) {
if (val1->type == Value::V_STRING)
// compare strings
return val1->stringVal.compare(val2->stringVal);
double d1 = (val1->type == Value::V_DOUBLE)
? val1->doubleVal : val1->intVal;
double d2 = (val2->type == Value::V_DOUBLE)
? val2->doubleVal : val2->intVal;
if (d1 < d2)
return -1;
else if (d1 > d2)
return 1;
else
return 0;
}
void BinaryCmd::aluOp(PValue dst, PConstValue src) {
Value::VType tDst = dst->type;
Value::VType tSrc = src->type;
if (tSrc == Value::V_INT && tDst == Value::V_INT)
// int, int --> int
switch (t_.type) {
case ETOKEN_OP_PLUS: dst->intVal += src->intVal; return;
case ETOKEN_OP_MINUS: dst->intVal -= src->intVal; return;
case ETOKEN_OP_STAR: dst->intVal *= src->intVal; return;
default:
assert(false);
}
double dDst = (tDst == Value::V_DOUBLE)
? dst->doubleVal : dst->intVal;
double dSrc = (tSrc == Value::V_DOUBLE)
? src->doubleVal : src->intVal;
dst->type = Value::V_DOUBLE;
switch (t_.type) {
case ETOKEN_OP_PLUS: dst->doubleVal = dDst + dSrc; break;
case ETOKEN_OP_MINUS: dst->doubleVal = dDst - dSrc; break;
case ETOKEN_OP_STAR: dst->doubleVal = dDst * dSrc; break;
default:
assert(false);
}
};
bool BinaryCmd::divide(PValue dst, PConstValue src) {
Value::VType tDst = dst->type;
Value::VType tSrc = src->type;
if ((tSrc == Value::V_DOUBLE && 0.0 == src->doubleVal)
|| (tSrc == Value::V_INT && 0 == src->intVal))
return false;
if (t_.type == ETOKEN_KW_DIV) {
dst->intVal /= src->intVal;
} else {
double dDst = (tDst == Value::V_DOUBLE)
? dst->doubleVal : dst->intVal;
double dSrc = (tSrc == Value::V_DOUBLE)
? src->doubleVal : src->intVal;
dst->type = Value::V_DOUBLE;
dst->doubleVal = (dDst/dSrc);
}
return true;
}
bool BinaryCmd::exec(FncDefinition *fnc) {
Vm *vm = fnc -> vm;
// pop values from stack
PValue val1;
PValue val2;
if (!vm->vmStack.pop(val2, t_)
||!vm->vmStack.pop(val1, t_))
return false;
// check operand types
if (!chkTypes(val1->type, val2->type)) {
// TODO: mark this as internal after tests as this is now checked statically
std::cerr << Error(E_ERROR, vm->fileName, "type combination is not valid for binary ", t_) << t_ << std::endl;
std::cerr << Error(E_NOTE, vm->fileName, "type of operand 1: ") << val1->type << std::endl;
std::cerr << Error(E_NOTE, vm->fileName, "type of operand 2: ") << val2->type << std::endl;
return false;
}
switch (t_.type) {
// logical operators
case ETOKEN_KW_AND: val1->boolVal &= val2->boolVal; break;
case ETOKEN_KW_OR: val1->boolVal |= val2->boolVal; break;
// relation operators
case ETOKEN_OP_LESS: val1 = ValueFactory::create(cmp(val1, val2) < 0); break;
case ETOKEN_OP_LESS_EQ: val1 = ValueFactory::create(cmp(val1, val2) <= 0); break;
case ETOKEN_KW_EQ: val1 = ValueFactory::create(cmp(val1, val2) == 0); break;
case ETOKEN_KW_NEQ: val1 = ValueFactory::create(cmp(val1, val2) != 0); break;
case ETOKEN_OP_GREATER_EQ: val1 = ValueFactory::create(cmp(val1, val2) >= 0); break;
case ETOKEN_OP_GREATER: val1 = ValueFactory::create(cmp(val1, val2) > 0); break;
// arithmetic operators
case ETOKEN_OP_PLUS:
if (val1->type == Value::V_STRING) {
val1->stringVal += val2->stringVal;
break;
}
// go through (!!)
case ETOKEN_OP_MINUS:
case ETOKEN_OP_STAR:
aluOp(val1, val2);
break;
// extra handling for arithmetic division
case ETOKEN_OP_SLASH:
case ETOKEN_KW_DIV:
if (!divide(val1, val2)) {
std::cerr << Error(E_ERROR, vm->fileName, "division by zero", t_) << std::endl;
return false;
}
break;
default:
// unexpected token
return false;
}
// push result to stack
return vm->vmStack.push(val1, t_);
}
// /////////////////////////////////////////////////////////////////////////////
// IfCmd implementation
bool IfCmd::exec(FncDefinition *fnc) {
Vm *vm = fnc->vm;
// pop value from stack
PValue val;
if (!vm->vmStack.pop(val, t_))
return false;
// check value type
if (val->type != Value::V_BOOL) {
// TODO: mark this as internal after tests as this is now checked statically
std::cerr << Error(E_ERROR, vm->fileName, "non-bool value given as if condition", t_) << std::endl;
std::cerr << Error(E_NOTE, vm->fileName, "given value: ", t_) << *val << std::endl;
return false;
}
// call appropriate cmd
return (val -> boolVal)
? ifCmd_ -> exec(fnc)
: elseCmd_ -> exec(fnc);
}
// /////////////////////////////////////////////////////////////////////////////
// WhileCmd implementation
bool WhileCmd::exec(FncDefinition *fnc) {
Vm *vm = fnc->vm;
// while loop implemented as loop :-)
for(;;) {
// evaluate while expression
if (!whileExpr_->exec(fnc))
return false;
// pop value from stack
PValue val;
if (!vm->vmStack.pop(val, t_))
return false;
// check value type
if (val->type != Value::V_BOOL) {
// TODO: mark this as internal after tests as this is now checked statically
std::cerr << Error(E_ERROR, vm->fileName, "non-bool value given as while condition", t_) << std::endl;
std::cerr << Error(E_NOTE, vm->fileName, "given value: ", t_) << *val << std::endl;
return false;
}
if (!val->boolVal)
// got false in while condition
return true;
// call while statement
if (!whileStat_ -> exec(fnc))
return false;
}
}
// /////////////////////////////////////////////////////////////////////////////
// PrintCmd implementation
bool PrintCmd::exec(FncDefinition *fnc) {
Vm *vm = fnc->vm;
if (!vm->vmStack.popToVar(var_, t_))
return false;
Value &val = var_.value;
switch (val.type) {
case Value::V_INT: std::cout << val.intVal << std::flush; return true;
// FIXME: is the output format same as printf %g ???
case Value::V_DOUBLE: std::cout << val.doubleVal << std::flush; return true;
case Value::V_STRING: std::cout << val.stringVal << std::flush; return true;
default:
// unhandled type
return false;
}
}
// /////////////////////////////////////////////////////////////////////////////
// InputCmd implementation
bool InputCmd::exec(FncDefinition *fnc) {
Vm *vm = fnc->vm;
PValue val;
switch (type_) {
case Value::V_INT:
// inputint()
{
int i;
std::cin >> i;
if (!std::cin) {
std::cerr << Error(E_ERROR, vm->fileName, "failed to read int from stdin", t_) << std::endl;
return false;
}
val = ValueFactory::create(i);
break;
}
case Value::V_DOUBLE:
// inputdouble()
{
double d;
std::cin >> d;
if (!std::cin) {
std::cerr << Error(E_ERROR, vm->fileName, "failed to read double from stdin", t_) << std::endl;
return false;
}
val = ValueFactory::create(d);
break;
}
case Value::V_STRING:
// inputstring()
{
string s;
char c;
bool quoteFound = false;
for (;;) {
if (!(std::cin.get(c))) {
std::cerr << Error(E_ERROR, vm->fileName, "failed to read string from stdin", t_) << std::endl;
return false;
}
if (!quoteFound) {
if (c == '"')
quoteFound = true;
} else {
if (c == '"')
break;
else
s.push_back(c);
}
}
val = ValueFactory::create(s);
break;
}
default:
// unhandled type
return false;
}
if (!pushValue_)
return true;
else
return vm->vmStack.push(val, t_);
}
// /////////////////////////////////////////////////////////////////////////////
// CallCmd implementation
bool CallCmd::exec(FncDefinition *fnc) {
Vm *vm = fnc->vm;
FncDefinition *prototype = vm->fncSet.getDefinition(t_.text);
if (!prototype || prototype->args.size() != nArgs_) {
std::cerr << Error(E_ERROR, vm->fileName, "CALL_CMD failed to match called function", t_, true) << std::endl;
return false;
}
// clone for call
SHARED_PTR(FncDefinition) fncCall(new FncDefinition(*prototype));
// pop fnc arguments
for (unsigned i = 0; i < nArgs_; i++) {
Var &arg = *(fncCall->args[nArgs_-i-1]);
if (!vm->vmStack.popToVar(arg, t_))
return false;
}
// check call depth
if (vm->callDepth >= vm->maxCallDepth) {
std::cerr << Error(E_ERROR, vm->fileName, "function call depth limit reached", t_) << std::endl;
return false;
}
// call fnc
vm->callDepth ++;
PCmdList cmdList = fncCall->cmdList;
#if DEBUG_TRACE_CALL
std::cerr << Color(C_LIGHT_GREEN) << ">>>" << Color(C_NO_COLOR) << " "
<< Color(C_YELLOW) << fncCall->self.defined.text << Color(C_NO_COLOR) << ": "
<< Color(C_LIGHT_GREEN) << "calling now"
<< Color(C_NO_COLOR) << ", call depth: "
<< Color(C_LIGHT_BLUE) << vm->callDepth-1 << Color(C_NO_COLOR)
<< std::endl;
#endif
FncDefinition *rawCall = fncCall.operator->();
bool bOk = cmdList->exec(rawCall);
#if DEBUG_TRACE_CALL
if (bOk)
std::cerr << Color(C_LIGHT_GREEN) << "<<<" << Color(C_NO_COLOR) << " "
<< Color(C_YELLOW) << fncCall->self.defined.text << Color(C_NO_COLOR)
<< ": " << Color(C_LIGHT_GREEN) << "called ok";
else
std::cerr << Color(C_LIGHT_RED) << "<<<" << Color(C_NO_COLOR) << " "
<< Color(C_YELLOW) << fncCall->self.defined.text << Color(C_NO_COLOR)
<< ": " << Color(C_LIGHT_RED) << "call failed";
std::cerr << Color(C_NO_COLOR)
<< Color(C_NO_COLOR) << ", call depth: "
<< Color(C_LIGHT_BLUE) << vm->callDepth-1 << Color(C_NO_COLOR)
<< std::endl;
#endif
vm->callDepth --;
if (!bOk)
return false;
if (!pushResult_)
return true;
else
return vm->vmStack.pushFromVar(fncCall->self, t_);
}