Search code examples
c++c++11lemon

Never declared custom class members in lemon-generated parser cause compile errors


I am trying to learn how to use the Lemon parser generator, and I built a learning project. However, when I try to compile the sources, I get some strange errors from GCC. It looks like there could be some text transcribing problem or typo, but I can't find it. I am compiling using the C++11 standard. The file "log" contains the compiler output.

I compile with

flex lexicon.l
lemon grammar.y
g++ -std=c++11 -o parser lex.yy.c grammar.c main.cpp 2> log

AST.h

#ifndef AST_H
#define AST_H

#include <string>
#include <sstream>
#include <iostream>
#include <unordered_map>
using namespace std;

string itos(int i) {
    stringstream ss;
    ss << i;
    return ss.str();
}

class Identifier {
    string name;
public:
    Identifier()
    : name("") {
        cerr << "This initializer should never be called!" << endl;
    }

    Identifier(string _name)
    : name(_name) {
    }
    string getName() {
        return name;
    }
};

unordered_map<string, Identifier> identifiers;

class ASTNode {
public:
    virtual string toString() = 0;
};

class StatementNode: public ASTNode {
public:
    virtual string toString() = 0;
};

class AssignmentNode: public StatementNode {
    Identifier *id;
    int newValue;
public:
    AssignmentNode(Identifier *_id, int _newValue)
    : id(_id),
      newValue(_newValue) {
    }

    string toString() {
        return id->getName() + " is now " + itos(newValue);
    }
};

class ExpressionNode: public StatementNode {
public:
    virtual string toString() = 0;
};

class ValueExpressionNode: public ExpressionNode {
    int value;
public:
    ValueExpressionNode(int _value)
    : value(_value) {
    }

    string toString() {
        return string("value: ") + itos(value);
    }
};

class IdentifierExpressionNode: public ExpressionNode {
    Identifier *id;
public:
    IdentifierExpressionNode(Identifier *_id)
    : id(_id) {
    }

    string toString() {
        return string("id : ") + id->getName();
};

class PlusExpressionNode: public ExpressionNode {
    ExpressionNode *lexp;
    ExpressionNode *rexp;
public:
    PlusExpressionNode(ExpressionNode *_lexp, ExpressionNode *_rexp)
    : lexp(_lexp),
      rexp(_rexp) {
    }

    string toString() {
        return string("(") + lexp->toString() + " + " + rexp->toString() + ")";
    }
};

class MinusExpressionNode: public ExpressionNode {
    ExpressionNode *lexp;
    ExpressionNode *rexp;
public:
    MinusExpressionNode(ExpressionNode *_lexp, ExpressionNode *_rexp)
    : lexp(_lexp),
      rexp(_rexp) {
    }

    string toString() {
        return string("(") + lexp->toString() + " - " + rexp->toString() + ")";
    }
};

class TimesExpressionNode: public ExpressionNode {
    ExpressionNode *lexp;
    ExpressionNode *rexp;
public:
    TimesExpressionNode(ExpressionNode *_lexp, ExpressionNode *_rexp)
    : lexp(_lexp),
      rexp(_rexp) {
    }

    string toString() {
        return string("(") + lexp->toString() + " * " + rexp->toString() + ")";
    }
};

class DividedByExpressionNode: public ExpressionNode {
    ExpressionNode *lexp;
    ExpressionNode *rexp;
public:
    DividedByExpressionNode(ExpressionNode *_lexp, ExpressionNode *_rexp)
    : lexp(_lexp),
      rexp(_rexp) {
    }

    string toString() {
        return string("(") + lexp->toString() + " / " + rexp->toString() + ")";
    }
};

#endif

grammar.y:

%include {
#include <vector>
#include <iostream>
#include <cassert>
#include <sstream>
#include "AST.h"

int atoi (char * a) {
    std::stringstream ss;
    ss << a;
    int i;
    ss >> i;
    return i;
}

Identifier* idForName(string name) {
    if (identifiers.find(name) == identifiers.end()) {
        identifiers[name] = Identifier(name);
    }
    return &(identifiers.at(name));
}
}

%token_type {char *}
%extra_argument {std::vector<StatementNode*>* statements}

start ::= statements.

statements ::= statement(stmt).
{
    statements->push_back(stmt);
}

statements ::= statements NEWLINE statement(stmt).
{
    statements->push_back(stmt);
}

%type statement {StatementNode*}

statement(stmt) ::= assignment(asgn).
{
    stmt = asgn;
}

%type assignment {AssignmentNode*}

assignment(asgn) ::= IDENTIFIER(id) EQUALS NUMBER(num).
{

    asgn = new AssignmentNode(idForName(id)), atoi(num));
}

statement(stmt) ::= expression(expr).
{
    stmt = expr;
}

%type expression {ExpressionNode*}

expression(expr) ::= NUMBER(num).
{
    expr = new ValueExpressionNode(atoi(num));
}

expression(expr) ::= IDENTIFIER(id).
{
    expr = IdentifierExpression(idForName(id));
}

expression(expr) ::= LROUNDPAREN expression(pexpr) RROUNDPAREN.
{
    expr = pexpr;
}

expression(expr) ::= expression(lexp) PLUS expression(rexp).
{
    expr = new PlusExpressionNode(lexp, rexp);
}

expression(expr) ::= expression(lexp) MINUS expression(rexp).
{
    expr = new MinusExpressionNode(lexp, rexp);
}

expression(expr) ::= expression(lexp) TIMES expression(rexp).
{
    expr = new TimesExpressionNode(lexp, rexp);
}

expression(expr) ::= expression(lexp) DIVIDEDBY expression(rexp).
{
    expr = new DividedByExpressionNode(lexp, rexp);
}

%left PLUS MINUS.
%left TIMES DIVIDEDBY.
%nonassoc LROUNDPAREN RROUNDPAREN.

lexicon.l:

%{
#include "grammar.h"
%}

%option noyywrap

%%

[A-Za-z_][A-Za-z0-9]*   return IDENTIFIER;
[0-9]+                  return NUMBER;
"="                     return EQUALS;
"+"                     return PLUS;
"-"                     return MINUS;
"*"                     return TIMES;
"/"                     return DIVIDEDBY;
"("                     return LROUNDPAREN;
")"                     return RROUNDPAREN;
\n                      return NEWLINE;
%%

main.cpp

#include <iostream>
#include <vector>
#include <string>
#include <cstdlib>
#include <cstring>

#include "AST.h"

using namespace std;

void* ParseAlloc(void* (*allocProc)(size_t));
void Parse(void*, int, char *, vector<StatementNode*>*);
void ParseFree(void*, void(*freeProc)(void*));

int yylex();
extern char * yytext;

int main() {
    vector<StatementNode*> statements;
    vector<char*> strpointers;
    void* parser = ParseAlloc(malloc);
    while (int lexcode = yylex()) {
        char *tmp = (char*)malloc((strlen(yytext)+1)*sizeof(char));
        strcpy(tmp, yytext);
        Parse(parser, lexcode, tmp, &statements);
        strpointers.push_back(tmp);
    }

    for (vector<StatementNode*>::iterator i = statements.begin(); i != statements.end(); i++) {
        cout << (*i)->toString() << endl;
    }

    Parse(parser, 0, NULL, &identifiers);
    ParseFree(parser, free);
    for (vector<char*>::iterator i=strpointers.begin(); i != strpointers.end(); i++) {
        free(*i);
    }

    return 0;
}

log:

grammar.c:105:44: error: ‘constexpr’ needed for in-class initialization of static data member ‘const IdentifierExpressionNode::YYMINORTYPE IdentifierExpressionNode::yyzerominor’ of non-integral type [-fpermissive]
grammar.c:173:1: error: in-class initialization of static data member ‘const unsigned char IdentifierExpressionNode::yy_action []’ of incomplete type
grammar.c:179:1: error: in-class initialization of static data member ‘const unsigned char IdentifierExpressionNode::yy_lookahead []’ of incomplete type
grammar.c:187:1: error: in-class initialization of static data member ‘const signed char IdentifierExpressionNode::yy_shift_ofst []’ of incomplete type
grammar.c:194:1: error: in-class initialization of static data member ‘const signed char IdentifierExpressionNode::yy_reduce_ofst []’ of incomplete type
grammar.c:199:1: error: in-class initialization of static data member ‘const unsigned char IdentifierExpressionNode::yy_default []’ of incomplete type
grammar.c:257:28: error: ‘constexpr’ needed for in-class initialization of static data member ‘FILE* IdentifierExpressionNode::yyTraceFILE’ of non-integral type [-fpermissive]
grammar.c:258:30: error: ‘constexpr’ needed for in-class initialization of static data member ‘char* IdentifierExpressionNode::yyTracePrompt’ of non-integral type [-fpermissive]
grammar.c:296:1: error: in-class initialization of static data member ‘const char* const IdentifierExpressionNode::yyTokenName []’ of incomplete type
grammar.c:316:1: error: in-class initialization of static data member ‘const char* const IdentifierExpressionNode::yyRuleName []’ of incomplete type
grammar.c:642:1: error: in-class initialization of static data member ‘const IdentifierExpressionNode::<anonymous struct> IdentifierExpressionNode::yyRuleInfo []’ of incomplete type
grammar.c:842:13: error: ‘static void IdentifierExpressionNode::yy_accept(IdentifierExpressionNode::yyParser*)’ cannot be overloaded
grammar.c:644:13: error: with ‘static void IdentifierExpressionNode::yy_accept(IdentifierExpressionNode::yyParser*)’
grammar.c:1025:1: error: expected ‘}’ at end of input
grammar.y: In static member function ‘static void IdentifierExpressionNode::yy_reduce(IdentifierExpressionNode::yyParser*, int)’:
grammar.y:51:69: error: cannot call member function ‘Identifier* IdentifierExpressionNode::idForName(std::string)’ without object
grammar.y:51:96: error: cannot call member function ‘int IdentifierExpressionNode::atoi(char*)’ without object
grammar.y:51:97: error: expected ‘;’ before ‘)’ token
grammar.y:63:68: error: cannot call member function ‘int IdentifierExpressionNode::atoi(char*)’ without object
grammar.y:68:70: error: cannot call member function ‘Identifier* IdentifierExpressionNode::idForName(std::string)’ without object
grammar.y:68:71: error: ‘IdentifierExpression’ was not declared in this scope
grammar.c: At global scope:
grammar.c:1025:1: error: expected unqualified-id at end of input
main.cpp:9:7: error: expected nested-name-specifier before ‘namespace’
main.cpp:9:7: error: expected unqualified-id before ‘namespace’
main.cpp:9:7: error: expected ‘;’ before ‘namespace’
main.cpp:9:7: error: expected unqualified-id before ‘namespace’
main.cpp:16:15: error: storage class specified for ‘yytext’
main.cpp:40:1: error: expected ‘}’ at end of input
main.cpp: In member function ‘int IdentifierExpressionNode::main()’:
main.cpp:33:37: error: no matching function for call to ‘IdentifierExpressionNode::Parse(void*&, int, NULL, std::unordered_map<std::basic_string<char>, Identifier>*)’
main.cpp:33:37: note: candidate is:
main.cpp:12:6: note: void IdentifierExpressionNode::Parse(void*, int, char*, std::vector<StatementNode*>*)
main.cpp:12:6: note:   no known conversion for argument 4 from ‘std::unordered_map<std::basic_string<char>, Identifier>*’ to ‘std::vector<StatementNode*>*’
main.cpp: At global scope:
main.cpp:40:1: error: expected unqualified-id at end of input

Solution

  • You have a missing curly brace in AST.h.

    The first error message you received indicated that the given declaration wasn't allowed "in-class". This is the clue that you need. It looks like it's not supposed to be in a class, so why does the compiler think that it is? This should make you strongly suspect that somewhere earlier there is a missing closing brace. Indeed, if you look in your AST.h file, you have this:

    class IdentifierExpressionNode: public ExpressionNode {
        Identifier *id;
    public:
        IdentifierExpressionNode(Identifier *_id)
        : id(_id) {
        }
    
        string toString() {
            return string("id : ") + id->getName();
    };
    

    You need to add a missing closing brace to toString.

    In general, when you get a huge long list of error messages like this, you should look most closely at just the first error or the first few, and ignore the rest. They are all likely from the same cause, and the further you read past the point that things started going wrong, the less useful further error messages become.