Česky
Kamil Dudka

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

File detail

Name:Downloadbuilder.cc [Download]
Location: vyp08 > vyp08-1.0pre1 > src
Size:21.0 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 "builder.h"
 
#include "cmd.h"
#include "parser.h"
#include "vm.h"
#include "vypIO.h"
 
#ifndef BUILDING_DOX
#   include <stack>
#   include <string>
#endif
 
using namespace StreamDecorator;
using std::string;
 
/// see IBuilder interface documentation
class Builder: public IBuilder {
    public:
        Builder(Vm *);
        virtual ~Builder();
        virtual bool hasError() const { return hasError_; }
        virtual void errorDetected();
        virtual void glVar(EToken type, Token id);
        virtual void fncDeclInit(EToken type, Token id);
        virtual void fncDeclArg(EToken type);
        virtual void fncDecl();
        virtual void fncDefInit(EToken type, Token id);
        virtual void fncDefArg(EToken type, Token id);
        virtual void fncDefVar(EToken type, Token id);
        virtual void fncDefBody();
        virtual void fncDef();
        virtual void assign(Token token);
        virtual void ifEnter(Token token);
        virtual void ifElse();
        virtual void ifLeave();
        virtual void whileInit(Token token);
        virtual void whileEnter();
        virtual void whileLeave();
        virtual void pushToken(Token token);
        virtual void evalUnOp(Token token);
        virtual void evalBinOp(Token token);
        virtual void fncCall(Token id, int argsToPop, bool pushResult);
        virtual void fncCallPrint(Token id, EToken valType);
    private:
        Vm              *vm_;                           ///< virtual machine being built
        bool            hasError_;                      ///< true if any error occurred
        FncDeclaration  *fncDecl_;                      ///< pointer to declaration being built, or 0 if not building declaration
        FncDefinition   *fncDef_;                       ///< pointer to definition being built, or 0 if not building definition
        CmdFactory      cmdFactory_;                    ///< common factory for virtual machine commands creation
 
        typedef STD_PAIR(Token, PCmdList)   TBlock;
        typedef STD_STACK(TBlock)           TBlockStack;
        TBlockStack     blockStack_;                    ///< stack for blocks of commands
 
        typedef STD_STACK(Value::VType)     TTypeStack;
        TTypeStack      typeStack_;                     ///< stack for expression types
 
        PVar createVar(EToken type, Token id);          ///< common code for variable creation
        bool appendCmd(PCmd);                           ///< append given command to current stack top
        bool chkBlockStack();                           ///< return true if stack is not empty, print error otherwise
        void pushTypeToStack(Value::VType);             ///< push type to stack
        Value::VType popTypeFromStack(const Token &);   ///< pop type from stack, V_NULL if empty
        void chkTypeOnTop(Value::VType type, const Token &);    ///< pop type from stack and compare with given type
};
 
// /////////////////////////////////////////////////////////////////////////////
// BuilderFactory implementation
IBuilder* BuilderFactory::createBuilder(Vm *vm) {
    return new Builder(vm);
}
 
// /////////////////////////////////////////////////////////////////////////////
// Builder implementation
Builder::Builder(Vm *vm):
    vm_(vm),
    hasError_(false),
    fncDecl_(0),
    fncDef_(0),
    cmdFactory_(vm)
{
}
 
Builder::~Builder() {
    delete fncDecl_;
    delete fncDef_;
}
 
void Builder::errorDetected() {
    hasError_ = true;
    if (fncDef_ && !blockStack_.empty())
        blockStack_.pop();
    delete fncDecl_;
    delete fncDef_;
    fncDecl_ = 0;
    fncDef_ = 0;
}
 
void Builder::glVar(EToken type, Token id) {
    PVar var = createVar(type, id);
    if (!var)
        return;
 
    VarSet &varSet = vm_ -> glVars;
    if (!varSet.add(var)) {
        hasError_ = true;
        std::cerr << Error(E_ERROR, vm_->fileName, "redefinition of global variable", id) << std::endl;
        PVar prev = varSet[id.text];
        if (prev && prev->defined.lineno)
            std::cerr << Error(E_NOTE, vm_->fileName, "previous definition was here", prev->defined.lineno) << std::endl;
    }
}
 
void Builder::fncDeclInit(EToken type, Token id) {
    if (fncDecl_) { 
        std::cerr << Error(E_WARNING, vm_->fileName, "unexpected declaration of function", id, true) << std::endl;
        return;
    }
 
    PVar var = createVar(type, id);
    if (!var)
        return;
 
    fncDecl_ = new FncDeclaration;
    fncDecl_ -> self = *var;
}
 
void Builder::fncDeclArg(EToken type) {
    if (!fncDecl_)
        return;
 
    PVar var = createVar(type, Token());
    if (!var)
        return;
 
    if (!fncDecl_->args.add(var))
        std::cerr << Error(E_WARNING, vm_->fileName, "failed to declare function argument", Token() ,true) << std::endl;
}
 
void Builder::fncDecl() {
    if (!fncDecl_)
        return;
 
    FncSet &fncSet = vm_ -> fncSet;
    if (!fncSet.addDeclaration(fncDecl_)) {
        hasError_ = true;
        const string &name = fncDecl_ -> self.name;
        std::cerr << Error(E_ERROR, vm_->fileName, "function declaration mismatch", fncDecl_->self.defined) << std::endl;
        FncDeclaration *prev = fncSet.getDeclaration(name);
        if (prev && prev->self.defined.lineno)
            std::cerr << Error(E_NOTE, vm_->fileName, "previous declaration was here", prev->self.defined) << std::endl;
        delete fncDecl_;
    }
 
    fncDecl_ = 0;
}
 
void Builder::fncDefInit(EToken type, Token id) {
    if (fncDef_) { 
        std::cerr << Error(E_WARNING, vm_->fileName, "unexpected definition of function", id, true) << std::endl;
        return;
    }
 
    PVar var = createVar(type, id);
    if (!var)
        return;
 
    fncDef_ = new FncDefinition(vm_);
    fncDef_ -> self = *var;
}
 
void Builder::fncDefArg(EToken type, Token id) {
    if (!fncDef_)
        return;
 
    if (id.text == fncDef_->self.name) {
        hasError_ = true;
        std::cerr << Error(E_ERROR, vm_->fileName, "function argument conflicts with function name", id) << std::endl;
        return;
    }
 
    PVar var = createVar(type, id);
    if (!var)
        return;
 
    PVar prev = fncDef_->args[id.text];
    if (prev) {
        hasError_ = true;
        std::cerr << Error(E_ERROR, vm_->fileName, "redefinition of function argument", id) << std::endl;
        if (prev->defined.lineno)
            std::cerr << Error(E_NOTE, vm_->fileName, "previous definition was here", prev->defined) << std::endl;
        return;
    }
 
    if (!fncDef_->args.add(var))
        std::cerr << Error(E_WARNING, vm_->fileName, "failed to define function argument", Token() ,true) << std::endl;
}
 
void Builder::fncDefVar(EToken type, Token id) {
    if (!fncDef_)
        return;
 
    const string &name = id.text;
    if (name == fncDef_->self.name) {
        hasError_ = true;
        std::cerr << Error(E_ERROR, vm_->fileName, "local variable conflicts with function name", id) << std::endl;
        return;
    }
 
    PVar arg = fncDef_->args[name];
    if (arg) {
        hasError_ = true;
        std::cerr << Error(E_ERROR, vm_->fileName, "local variable conflicts with function parameter", id) << std::endl;
        Token &tArg = arg->defined;
        if (tArg.lineno)
            std::cerr << Error(E_NOTE, vm_->fileName, "argument was defined here", tArg) << std::endl;
        return;
    }
 
    PVar var = createVar(type, id);
    if (!var)
        return;
 
    PVar prev = fncDef_->vars[name];
    if (prev) {
        hasError_ = true;
        std::cerr << Error(E_ERROR, vm_->fileName, "redefinition of local variable", id) << std::endl;
        if (prev->defined.lineno)
            std::cerr << Error(E_NOTE, vm_->fileName, "previous definition was here", prev->defined) << std::endl;
        return;
    }
 
    if (!fncDef_->vars.add(var))
        std::cerr << Error(E_WARNING, vm_->fileName, "failed to define local variable", Token() ,true) << std::endl;
}
 
void Builder::fncDefBody() {
    if (!fncDef_)
        return;
    const string &name = fncDef_ -> self.name;
    FncSet &fncSet = vm_ -> fncSet;
 
    FncDefinition *prev = fncSet.getDefinition(name);
    if (prev) {
        // fnc redefinition
        hasError_ = true;
        std::cerr << Error(E_ERROR, vm_->fileName, "redefinition of function", fncDef_->self.defined) << std::endl;
        Token &tPrev = prev->self.defined;
        if (tPrev.lineno)
            std::cerr << Error(E_NOTE, vm_->fileName, "previous definition was here", tPrev) << std::endl;
        delete fncDef_;
        fncDef_ = 0;
        return;
    }
 
    if (!fncSet.addDeclaration(new FncDeclaration(*fncDef_))) {
        // fnc decl/def mismatch
        hasError_ = true;
        std::cerr << Error(E_ERROR, vm_->fileName, "function declaration/definition mismatch", fncDef_->self.defined) << std::endl;
        FncDeclaration *prev = fncSet.getDeclaration(name);
        if (prev && prev->self.defined.lineno)
            std::cerr << Error(E_NOTE, vm_->fileName, "previous declaration was here", prev->self.defined) << std::endl;
        delete fncDef_;
        fncDef_ = 0;
        return;
    }
 
    // init BlockStack for function
    if (!blockStack_.empty())
        std::cerr << Error(E_WARNING, vm_->fileName, "BlockStack offset detected", fncDef_->self.defined, true) << std::endl;
    blockStack_.push(TBlock(fncDef_->self.defined, fncDef_->cmdList));
}
 
void Builder::fncDef() {
    if (!fncDef_)
        return;
 
    FncSet &fncSet = vm_ -> fncSet;
    if (!fncSet.addDefinition(fncDef_)) {
        hasError_ = true;
        std::cerr << Error(E_ERROR, vm_->fileName, "function definition failed", fncDef_->self.defined, true) << std::endl;
        delete fncDef_;
    }
 
    if (chkBlockStack()) {
        blockStack_.pop();
        if (!blockStack_.empty())
            std::cerr << Error(E_WARNING, vm_->fileName, "BlockStack offset detected", fncDef_->self.defined, true) << std::endl;
    }
    fncDef_ = 0;
}
 
void Builder::assign(Token token) {
    if (!fncDef_) {
        std::cerr << Error(E_WARNING, vm_->fileName, "assignement not within function definition", token, true) << std::endl;
        return;
    }
    Value::VType dstType;
    if (appendCmd (cmdFactory_.createAssign(token, fncDef_, dstType)))
        chkTypeOnTop(dstType, token);
}
 
void Builder::ifEnter(Token token) {
    PCmdList ifList(new CmdList);
    blockStack_.push(TBlock(token, ifList));
    chkTypeOnTop(Value::V_BOOL, token);
}
 
void Builder::ifElse() {
    if (!chkBlockStack())
        return;
    const TBlock &top = blockStack_.top();
 
    PCmdList elseList(new CmdList);
    blockStack_.push(TBlock(top.first, elseList));
}
 
void Builder::ifLeave() {
    // pop "else list" from stack
    if (!chkBlockStack())
        return;
    TBlock elseBlock = blockStack_.top();
    blockStack_.pop();
 
    // pop "if list" from stack
    if (!chkBlockStack())
        return;
    TBlock ifBlock = blockStack_.top();
    blockStack_.pop();
 
    // push "if command" to current list
    appendCmd(cmdFactory_.createIf(
                ifBlock.first,                          // token "if"
                ifBlock.second,                         // command list for positive branch
                elseBlock.second));                     // command list for negative branch
}
 
void Builder::whileInit(Token token) {
    PCmdList exprList(new CmdList);
    blockStack_.push(TBlock(token, exprList));
}
 
void Builder::whileEnter() {
    if (!chkBlockStack())
        return;
    const TBlock &top = blockStack_.top();
 
    PCmdList statList(new CmdList);
    blockStack_.push(TBlock(top.first, statList));
}
 
void Builder::whileLeave() {
    // pop "stat list" from stack
    if (!chkBlockStack())
        return;
    TBlock statBlock = blockStack_.top();
    blockStack_.pop();
 
    // pop "expr list" from stack
    if (!chkBlockStack())
        return;
    TBlock exprBlock = blockStack_.top();
    blockStack_.pop();
 
    // push "while command" to current list
    appendCmd(cmdFactory_.createWhile(
                exprBlock.first,                         // token "while"
                exprBlock.second,                        // command list to evaluate "while" condition
                statBlock.second));                      // block of commands for "while" statement
}
 
void Builder::pushToken(Token token) {
    if (!fncDef_) {
        std::cerr << Error(E_WARNING, vm_->fileName, "pushToken not within function definition", token, true) << std::endl;
        return;
    }
    Value::VType dstType;
    appendCmd(cmdFactory_.createPush(token, fncDef_, dstType));
    pushTypeToStack(dstType);
}
 
void Builder::evalUnOp(Token token) {
    Value::VType vt = popTypeFromStack(token);
    if ((token.type == ETOKEN_KW_NOT && vt != Value::V_BOOL)
            || (token.type == ETOKEN_OP_MINUS && (vt != Value::V_INT
                    && vt != Value::V_DOUBLE)))
    {
        hasError_ = true;
        std::cerr << Error(E_ERROR, vm_->fileName, "type not allowed for unary ", token) << token << std::endl;
        std::cerr << Error(E_NOTE, vm_->fileName, "expression type: ") << vt << std::endl;
    }
    appendCmd(cmdFactory_.createUnary(token));
    pushTypeToStack(vt);
}
 
void Builder::evalBinOp(Token token) {
    Value::VType t2 = popTypeFromStack(token);
    Value::VType t1 = popTypeFromStack(token);
    Value::VType dstType;
    if (!appendCmd(cmdFactory_.createBinary(token, t1, t2, dstType))
                || dstType == Value::V_NULL)
    {
        hasError_ = true;
        std::cerr << Error(E_ERROR, vm_->fileName, "type combination is not valid for binary ", token) << token << std::endl;
        std::cerr << Error(E_NOTE, vm_->fileName, "type of operand 1: ") << t1 << std::endl;
        std::cerr << Error(E_NOTE, vm_->fileName, "type of operand 2: ") << t2 << std::endl;
    }
    pushTypeToStack(dstType);
}
 
void Builder::fncCall(Token id, int argsToPop, bool pushResult) {
    const string &name = id.text;
    if (name == "print") {
        std::cerr << Error(E_ERROR, vm_->fileName, "invalid use of reserved function name", id) << std::endl;
        hasError_ = true;
        return;
    }
 
    FncDeclaration *fnc = vm_->fncSet.getDeclaration(name);
    if (!fnc) {
        std::cerr << Error(E_ERROR, vm_->fileName, "call of undeclared function", id) << std::endl;
        hasError_ = true;
        return;
    }
    unsigned argCnt = fnc->args.size();
    if (argCnt != static_cast<unsigned>(argsToPop)) {
        std::cerr << Error(E_ERROR, vm_->fileName, "arguments count mismatch in function call", id) << std::endl;
        Token &t = fnc->self.defined;
        if (t.lineno)
            std::cerr << Error(E_NOTE, vm_->fileName, "function is declared here", t) << std::endl;
        hasError_ = true;
        return;
    }
    for (unsigned i = 0; i < argCnt; i++) {
        unsigned n = argCnt - i - 1;
        Value::VType argType = fnc->args[n]->value.type;
        chkTypeOnTop(argType, id);
    }
 
    // check for built-in input{int|double|string}() function
    bool iInt =     name == "inputint";
    bool iDouble =  name == "inputdouble";
    bool iString =  name == "inputstring";
    if (!pushResult
            && (iInt || iDouble || iString))
        std::cerr << Error(E_WARNING, vm_->fileName, "return value of input function is not used", id) << std::endl;
 
    if (iInt)
        appendCmd(cmdFactory_.createInput(id, Value::V_INT, pushResult));
    else if (iDouble)
        appendCmd(cmdFactory_.createInput(id, Value::V_DOUBLE, pushResult));
    else if (iString)
        appendCmd(cmdFactory_.createInput(id, Value::V_STRING, pushResult));
    else {
        // ordinary function call
        vm_->calleeSet.add(name, id);
        appendCmd(cmdFactory_.createCall(id, argsToPop, pushResult));
    }
    if (pushResult)
        pushTypeToStack(fnc->self.value.type);
}
 
void Builder::fncCallPrint(Token id, EToken valType) {
    Value::VType srcType;
    if (appendCmd(cmdFactory_.createPrint(id, valType, srcType)))
        chkTypeOnTop(srcType, id);
}
 
PVar Builder::createVar(EToken type, Token id) {
    PVar var;
    PValue val(new Value);
    switch (type) {
        case ETOKEN_KW_VOID:    val -> type = Value::V_NULL;    break;
        case ETOKEN_KW_INT:     val -> type = Value::V_INT;     break;
        case ETOKEN_KW_DOUBLE:  val -> type = Value::V_DOUBLE;  break;
        case ETOKEN_KW_STRING:  val -> type = Value::V_STRING;  break;
        default:
            std::cerr << Error(E_WARNING, vm_->fileName, "unhandled variable type", id, true) << std::endl;
            return var;
    }
    var.reset(new Var);
    var -> name = id.text;
    var -> value = *val;
    var -> defined = id;
    return var;
}
 
bool Builder::appendCmd(PCmd cmd) {
    if (!cmd){
        hasError_ = true;
        return false;
    }
    if (!chkBlockStack())
        return false;
    TBlock &top = blockStack_.top();
    PCmdList &list = top.second;
    list -> add(cmd);
    return true;
}
bool Builder::chkBlockStack() {
    if (blockStack_.empty()) {
        std::cerr << Error(E_WARNING, vm_->fileName, "BlockStack underflow detected", fncDef_->self.defined, true) << std::endl;
        return false;
    }
    return true;
}
void Builder::pushTypeToStack(Value::VType type) {
#if DEBUG_TRACE_TYPESTACK
    std::cerr << Color(C_LIGHT_CYAN) << "TypeStack" << Color(C_NO_COLOR)
        << " <-- " << type << std::endl;
#endif
    typeStack_.push(type);
}
Value::VType Builder::popTypeFromStack(const Token &t) {
    Value::VType type = Value::V_NULL;
    if (typeStack_.empty()) {
        std::cerr << Error(E_WARNING, vm_->fileName, "TypeStack underflow detected", t, true) << std::endl;
    } else {
        type = typeStack_.top();
        typeStack_.pop();
#if DEBUG_TRACE_TYPESTACK
    std::cerr << Color(C_LIGHT_CYAN) << "TypeStack" << Color(C_NO_COLOR)
        << " --> " << type << std::endl;
#endif
    }
    return type;
}
void Builder::chkTypeOnTop(Value::VType dstType, const Token &t) {
    Value::VType srcType = popTypeFromStack(t);
    if (srcType != dstType) {
        hasError_ = true;
        std::cerr << Error(E_ERROR, vm_->fileName, "type mismatch", t) << std::endl;
        std::cerr << Error(E_NOTE, vm_->fileName, " expression type: ") << srcType << std::endl;
        std::cerr << Error(E_NOTE, vm_->fileName, "destination type: ") << dstType << std::endl;
    }
}
 
// /////////////////////////////////////////////////////////////////////////////
// FncFactory implementation
struct FncFactory::Private {
    Vm      *const vm;
    Private(Vm *vm_):
        vm(vm_)
    {
    }
    static bool addDefinition(Vm *, FncDefinition *);
    static FncDefinition* createFnc(Vm *, string, PValue);
};
FncFactory::FncFactory(Vm *vm):
    d(new Private(vm))
{
}
FncFactory::~FncFactory() {
    delete d;
}
bool FncFactory::initVm(Vm *vm) {
    FncFactory *factory = new FncFactory(vm);
 
    // declare main
    vm->calleeSet.add("main", Token());
    FncDeclaration *declMain = factory -> createMainDecl();
    bool bOk = declMain;
    bOk &= vm->fncSet.addDeclaration(declMain);
 
    // define built-ins
    bOk &= Private::addDefinition (vm, factory -> createPrint());
    bOk &= Private::addDefinition (vm, factory -> createInputString());
    bOk &= Private::addDefinition (vm, factory -> createInputInt());
    bOk &= Private::addDefinition (vm, factory -> createInputDouble());
 
    delete factory;
    return bOk;
}
bool FncFactory::Private::addDefinition(Vm *vm, FncDefinition *fnc) {
    if (!vm || !fnc)
        return false;
 
    if (!vm->fncSet.addDefinition(fnc))
        return false;
 
    vm->calleeSet.add(fnc->self.name, Token());
    return true;
}
 
FncDefinition* FncFactory::Private::createFnc(Vm *vm, string name, PValue val) {
    PVar var(new Var);
    var -> name = name;
    var -> value = *val;
 
    FncDefinition *fnc = new FncDefinition(vm);
    fnc -> self = *var;
    return fnc;
}
 
FncDeclaration* FncFactory::createMainDecl() {
    PVar var(new Var);
    var -> name = "main";
    var -> value = *(ValueFactory::create());
 
    FncDeclaration *mainDecl = new FncDeclaration;
    mainDecl -> self = *var;
    return mainDecl;
}
 
FncDefinition* FncFactory::createPrint() {
    return Private::createFnc(d->vm, "print",
            ValueFactory::create(false));
}
 
FncDefinition* FncFactory::createInputString() {
    return Private::createFnc(d->vm, "inputstring",
            ValueFactory::create(string()));
}
 
FncDefinition* FncFactory::createInputInt() {
    return Private::createFnc(d->vm, "inputint",
            ValueFactory::create(0));
}
 
FncDefinition* FncFactory::createInputDouble() {
    return Private::createFnc(d->vm, "inputdouble",
            ValueFactory::create(0.0));
}