/*
* 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 "parser.h"
#include "parser.tab.hh"
#include "vypIO.h"
#ifndef BUILDING_DOX
# include <stack>
# include <string>
#endif
using namespace StreamDecorator;
using std::string;
/// bison callback implementation
class BisonListener: public IBisonListener {
public:
BisonListener(IBuilder *, string fileName);
virtual ~BisonListener() { }
virtual void setType(EToken type); ///< token type detected
virtual void glVar(Token token); ///< global variable
virtual void fncInit(Token token); ///< fnc decl/def detected
virtual void fncDecl(); ///< fnc declaration complete
virtual void fncDefEnter(); ///< fnc definition body enter
virtual void fncDefLeave(); ///< fnc definition body leave
virtual void argDecl(); ///< arg in fnc declaration
virtual void argDef(Token token); ///< arg in fnc definition
virtual void lcVar(Token token); ///< local variable in fnc def
virtual void assign(Token token) { builder_ -> assign(token); }
virtual void ifEnter(Token token) { builder_ -> ifEnter(token); }
virtual void ifElse() { builder_ -> ifElse(); }
virtual void ifLeave() { builder_ -> ifLeave(); }
virtual void whileInit(Token token) { builder_ -> whileInit(token); }
virtual void whileEnter() { builder_ -> whileEnter(); }
virtual void whileLeave() { builder_ -> whileLeave(); }
virtual void pushToken(Token token) { builder_ -> pushToken(token); }
virtual void evalUnOp(Token token) { builder_ -> evalUnOp(token); }
virtual void evalBinOp(Token token) { builder_ -> evalBinOp(token); }
virtual void fncCallInit(Token token); ///< fnc call detected
virtual void fncCallArg(); ///< fnc call arg
virtual void fncCallPrintArg(); ///< fnc call arg of print()
virtual void fncCallAsCmd(); ///< fnc call as command
virtual void fncCallAsExpr(); ///< fnc call as expression
private:
IBuilder *builder_; ///< entry point to higher layer (IBuilder object)
string fileName_; ///< name (or alias) of input file (used in messages)
EToken type_; ///< last type detected by bison (will be never T_VOID)
EToken fncType_; ///< return type of function currently being read
Token fncId_; ///< name (as defining token) of function currently being read
bool fncInitDeclSent_; ///< true if higher layer is now reading function declaration
bool fncInitDefSent_; ///< true if higher layer is now reading function definition
EToken fncPrint_; ///< type of print function currently being read (if any)
typedef STD_PAIR(Token, int) TFncCall;
typedef STD_STACK(TFncCall) TStack;
TStack stack_; ///< function call stack
bool initDeclIfNeeded(); ///< init function declaration in higher layer (if not already initiated)
bool initDefIfNeeded(); ///< init function definition in higher layer (if not already initiated)
void errMixedDeclDef(); ///< handle error caused by mixed syntax of function declaration/definition
bool chkStack(); ///< check stack if not empty; if so handle such error; return true on success
};
BisonListener::BisonListener(IBuilder *builder, string fileName):
builder_(builder),
fileName_(fileName),
type_(ETOKEN_NULL),
fncType_(ETOKEN_NULL),
fncInitDeclSent_(false),
fncInitDefSent_(false),
fncPrint_(ETOKEN_NULL)
{
}
void BisonListener::setType(EToken type) {
type_ = type;
}
void BisonListener::glVar(Token token) {
builder_ -> glVar(type_, token);
}
void BisonListener::fncInit(Token token) {
fncType_ = type_;
fncId_ = token;
fncInitDeclSent_ = false;
fncInitDefSent_ = false;
}
void BisonListener::fncDecl() {
if (initDeclIfNeeded())
builder_ -> fncDecl();
}
void BisonListener::fncDefEnter() {
initDefIfNeeded();
builder_ -> fncDefBody();
}
void BisonListener::fncDefLeave() {
if (initDefIfNeeded())
builder_ -> fncDef();
}
void BisonListener::argDecl() {
if (initDeclIfNeeded())
builder_ -> fncDeclArg(type_);
}
void BisonListener::argDef(Token token) {
if (initDefIfNeeded())
builder_ -> fncDefArg(type_, token);
}
void BisonListener::lcVar(Token token) {
if (initDefIfNeeded())
builder_ -> fncDefVar(type_, token);
}
void BisonListener::fncCallInit(Token token) {
fncPrint_ = ETOKEN_NULL;
#if DEBUG_BISON_LISTENER_STACK
std::cerr << Color(C_LIGHT_PURPLE) << "BisonListener stack"
<< Color(C_NO_COLOR) << " <-- " << token
<< "[" << Color(C_LIGHT_PURPLE) << 0
<< Color(C_NO_COLOR) << "]" << std::endl;
#endif
stack_.push(TFncCall(token, 0));
}
void BisonListener::fncCallArg() {
TFncCall &top = stack_.top();
int &argCnt = top.second;
++ argCnt;
#if DEBUG_BISON_LISTENER_STACK
std::cerr << Color(C_LIGHT_PURPLE) << "BisonListener stack"
<< Color(C_NO_COLOR) << " <-> " << top.first
<< "[" << Color(C_LIGHT_PURPLE) << top.second
<< Color(C_NO_COLOR) << "]" << std::endl;
#endif
}
void BisonListener::fncCallPrintArg() {
if (!chkStack())
return;
TFncCall &top = stack_.top();
Token tFnc = top.first;
if (tFnc.text != "print") {
std::cerr << Error(E_ERROR, fileName_, "print syntax used for another function", tFnc) << std::endl;
builder_->errorDetected();
}
fncPrint_ = type_;
}
void BisonListener::fncCallAsCmd() {
if (!chkStack())
return;
TFncCall top = stack_.top();
stack_.pop();
#if DEBUG_BISON_LISTENER_STACK
std::cerr << Color(C_LIGHT_PURPLE) << "BisonListener stack"
<< Color(C_NO_COLOR) << " --> " << top.first
<< "[" << Color(C_LIGHT_PURPLE) << top.second
<< Color(C_NO_COLOR) << "]" << std::endl;
#endif
if (fncPrint_ == ETOKEN_NULL)
builder_ -> fncCall(top.first, top.second, false);
else
builder_ -> fncCallPrint(top.first, fncPrint_);
}
void BisonListener::fncCallAsExpr() {
if (!chkStack())
return;
TFncCall top = stack_.top();
stack_.pop();
#if DEBUG_BISON_LISTENER_STACK
std::cerr << Color(C_LIGHT_PURPLE) << "BisonListener stack"
<< Color(C_NO_COLOR) << " --> " << top.first
<< "[" << Color(C_LIGHT_PURPLE) << top.second
<< Color(C_NO_COLOR) << "]" << std::endl;
#endif
builder_ -> fncCall(top.first, top.second, true);
}
bool BisonListener::initDeclIfNeeded() {
if (fncInitDefSent_) {
errMixedDeclDef();
return false;
}
if (!fncInitDeclSent_) {
builder_ -> fncDeclInit(fncType_, fncId_);
fncInitDeclSent_ = true;
}
return true;
}
bool BisonListener::initDefIfNeeded() {
if (fncInitDeclSent_) {
errMixedDeclDef();
return false;
}
if (!fncInitDefSent_) {
builder_ -> fncDefInit(fncType_, fncId_);
fncInitDefSent_ = true;
}
return true;
}
bool BisonListener::chkStack() {
if (stack_.empty()) {
std::cerr << Error(E_ERROR, fileName_, "BisonListener: stack underflow", Token(), true) << std::endl;
builder_ -> errorDetected();
return false;
}
return true;
}
void BisonListener::errMixedDeclDef() {
std::cerr << Error(E_ERROR, fileName_, "mixed declaration/definition is not allowed", fncId_) << std::endl;
builder_ -> errorDetected();
}
namespace yy {
#ifndef BUILDING_DOX
void BisonParser::error(const yy::location& loc, const string& msg) {
std::cerr << Error(E_ERROR, fileName, msg, loc.end.line, "syntax error")
<< std::endl;
}
#endif
int yylex (Token *token, yy::location *loc, IScanner *scanner) {
for (;;) {
if (!scanner->readNext(*token))
return 0;
loc->begin.line = loc->end.line = token->lineno;
// convert scanner token enumeration to bison #define values
switch (token->type) {
case ETOKEN_ID: return BisonParser::token::T_ID;
case ETOKEN_KW_AND: return BisonParser::token::T_AND;
case ETOKEN_KW_DIV: return BisonParser::token::T_DIV;
case ETOKEN_KW_DOUBLE: return BisonParser::token::T_DOUBLE;
case ETOKEN_KW_ELSE: return BisonParser::token::T_ELSE;
case ETOKEN_KW_EQ: return BisonParser::token::T_EQ;
case ETOKEN_KW_IF: return BisonParser::token::T_IF;
case ETOKEN_KW_INT: return BisonParser::token::T_INT;
case ETOKEN_KW_OR: return BisonParser::token::T_OR;
case ETOKEN_KW_NEQ: return BisonParser::token::T_NEQ;
case ETOKEN_KW_NOT: return BisonParser::token::T_NOT;
case ETOKEN_KW_STRING: return BisonParser::token::T_STRING;
case ETOKEN_KW_VAR: return BisonParser::token::T_VAR;
case ETOKEN_KW_VOID: return BisonParser::token::T_VOID;
case ETOKEN_KW_WHILE: return BisonParser::token::T_WHILE;
case ETOKEN_NUMBER_DOUBLE: return BisonParser::token::T_DOUBLE_LIT;
case ETOKEN_NUMBER_INT: return BisonParser::token::T_INT_LIT;
case ETOKEN_OP_ASSIGN: return BisonParser::token::T_ASSIGN;
case ETOKEN_OP_COMMA: return ',';
case ETOKEN_OP_GREATER: return '>';
case ETOKEN_OP_GREATER_EQ: return BisonParser::token::T_GE;
case ETOKEN_OP_LCBR: return '{';
case ETOKEN_OP_LESS: return '<';
case ETOKEN_OP_LESS_EQ: return BisonParser::token::T_LE;
case ETOKEN_OP_LPAR: return '(';
case ETOKEN_OP_MINUS: return '-';
case ETOKEN_OP_PLUS: return '+';
case ETOKEN_OP_RCBR: return '}';
case ETOKEN_OP_RPAR: return ')';
case ETOKEN_OP_SEMICOLON: return ';';
case ETOKEN_OP_SLASH: return '/';
case ETOKEN_OP_STAR: return '*';
case ETOKEN_STRING: return BisonParser::token::T_STRING_LIT;
default:
std::cerr << Error(E_WARNING, "yylex", "unhandled token", *token, true) << std::endl;
}
}
}
}
int Parser::parse(IScanner *scanner, IBuilder *builder, std::string fileName) {
IBisonListener *listener = new BisonListener(builder, fileName);
yy::BisonParser *parser = new yy::BisonParser(scanner, listener, fileName);
#if 0
parser->set_debug_level(1);
#endif
int status = parser->parse();
delete parser;
delete listener;
return status;
}