I'm trying to parse an expression with ANTLR4. Therefor I'm using the visitor pattern and creating an abstract syntax tree.
The result can be an expression or something other (which I removed from this example code). The visitor expects a return type of antlrcpp::Any
so the elements of the abstract syntax tree are converted toantlrcpp::Any
.
But I'm getting a std::bad_cast
if I'm trying to convert the result back from antlrcpp::Any
to std::shared_ptr<Expression>
.
#include <iostream>
#include <string>
#include <memory>
#include "antlr4-runtime.h"
#include "Parser/SimpleLangLexer.h"
#include "Parser/SimpleLangParser.h"
#include "Parser/SimpleLangBaseVisitor.h"
struct Expression;
struct Node {
virtual ~Node() { }
};
struct Expression: public Node {
int constValue;
Expression(int constValue) : constValue(constValue) { }
virtual ~Expression() override { }
};
struct AstVisitor: public SimpleLangBaseVisitor {
virtual antlrcpp::Any visitTopLevelElement(SimpleLangParser::TopLevelElementContext *ctx) override {
std::shared_ptr<Expression> expression = ctx->expression()->accept(this);
return std::dynamic_pointer_cast<Node>(expression);
}
virtual antlrcpp::Any visitIntExpr(SimpleLangParser::IntExprContext *ctx) override {
std::string strInteger = ctx->INTEGER()->getSymbol()->getText();
int constValue = std::stoi(strInteger);
auto intExpr = std::make_shared<Expression>(constValue);
return intExpr;
}
};
int main() {
std::string line = "123;";
antlr4::ANTLRInputStream input(line);
SimpleLangLexer lexer(&input);
antlr4::CommonTokenStream tokens(&lexer);
SimpleLangParser parser(&tokens);
antlr4::tree::ParseTree *tree = parser.start();
// Prints:
// > (start (topLevelElement (expression 123) ;) <EOF>)
std::cout << std::endl << tree->toStringTree(&parser) << std::endl;
AstVisitor astVisitor;
antlrcpp::Any result = tree->accept(&astVisitor);
// Prints:
// > Dn
// std::cout << result.get_typeinfo().name() << std::endl;
// Error:
// > terminate called after throwing an instance of 'std::bad_cast'
// what(): std::bad_cast
std::shared_ptr<Node> node = result;
std::shared_ptr<Expression> iexpr = std::dynamic_pointer_cast<Expression>(node);
// Expected:
// > 123
std::cout << iexpr->constValue << std::endl;
return 0;
}
Implementation of antlrcpp::Any
My grammar:
grammar SimpleLang;
start
: topLevelElement* EOF
;
topLevelElement
: expression ';'
;
expression
: IDENTIFIER #identifierExpr
| INTEGER #intExpr
;
IDENTIFIER
: [_a-zA-Z][_a-zA-Z0-9]*
;
INTEGER
: [0-9]+
;
WS: [ \r\n\t] -> skip;
Note: I greatly reduced my code for this question and I hope it makes still sense.
antlrcpp::Any result = tree->accept(&astVisitor);
// Error:
// > terminate called after throwing an instance of 'std::bad_cast'
// what(): std::bad_cast
std::shared_ptr<Node> node = tree->accept(result);
here you call tree->accept
first with a pointer to an astVisitor
.
Then you take the result of that, and pass it to tree->accept
, which probably expects a pointer to SimpleLangBaseVisitor
. But the first tree->accept
call returned an Any
that probably doesn't contain a pointer to a SimpleLangBaseVisitor
.
So it throws an error.
As an aside, that Any
you are using is missing std::typeinfo const& get_typeinfo() const
. If you had that, you could do some get_typeinfo().name()
printf debugging to get a clue what is going wrong.
If you have access to the source code add:
struct Base {
virtual ~Base();
virtual Base* clone() const = 0;
std::typeinfo const& get_typeinfo() const = 0;
};
template<typename T>
struct Derived : Base {
// ...
std::typeinfo const& get_typeinfo() const override {
return typeid(T);
}
};
and to Any
itself:
std::typeinfo const& get_typeinfo() const {
if (!_ptr) return typeid(std::nullptr_t);
return ptr->get_typeinfo();
}
once you have that, std::cerr << some_any.get_typeinfo().name() << std::endl;
will solve many "why am I getting a bad_cast?!" pains. But it does require that both your code, and the code generating the Any, be built with this modified Any
.