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