Česky
Kamil Dudka

Flex/Bison based compiler and interpreter written in C++ (using Boost)

File detail

Name:Downloadcmd.cc [Download]
Location: vyp08 > vyp08-1.0pre1 > src
Size:30.4 KB
Last modification:2009-07-04 19:51

Source code

/*
 * 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_);
}