ANTLR4 C++版计算器遭遇<EOF>输入不匹配错误求助
ANTLR4 C++版本计算器Lexer仅识别问题排查求助
我正在学习T. Parr所著《The Definitive ANTLR4 Reference》第四章的「使用Visitor构建计算器」内容,Java版本代码可正常运行并复现书中输出。但开发C版本时,已安装对应版本的antlr-cpp-runtime-4.13.1且测试全部通过,通过命令antlr4 -Dlanguage=Cpp -no-listener -visitor -encoding UTF8 calc.g4生成C文件无报错。编写C++主程序后,使用与Java版相同的语法文件和输入文件运行,却出现错误:line 1:0 mismatched input '<EOF>' expecting {'(', ID, INT}。调试发现Lexer仅识别到
相关代码
语法文件calc.g4
grammar calc; prog: stat* ; stat: expr NEWLINE # printExpr | ID '=' expr NEWLINE # assign | NEWLINE # blank ; expr: expr op=('*'|'/') expr # MulDiv | expr op=('+'|'-') expr # AddSub | INT # int | ID # id | '(' expr ')' # parens ; MUL : '*' ; // assigns token name to '*' used above in grammar DIV : '/' ; ADD : '+' ; SUB : '-' ; ID : [a-zA-Z]+ ; // match identifiers INT : [0-9]+ ; // match integers NEWLINE : '\r'? '\n' ; // return newlines to parser (is end-statement signal ) WS : [ \t]+ -> skip ; // toss out whitespace
C++主程序
int main(int argc, char** argv) { std::string inputFile = ""; std::ifstream ins; if(argc > 2) inputFile = argv[1]; ins.open(inputFile); ANTLRInputStream input(ins); calcLexer lexer(&input); CommonTokenStream tokens(&lexer); //tokens.fill(); //for(auto token: tokens.getTokens()) //{ // std::cout << token->toString() << std::endl; //} calcParser parser(&tokens); std::cout << parser.expr()->toStringTree() << std::endl; auto tree = parser.prog(); evalVisitor visitor = evalVisitor(); visitor.visitProg(tree); return 0; }
evalVisitor.h
#ifndef _calcVisitorImpl_h_ #define _calcVisitorImpl_h_ #include "calcVisitor.h" #include "calcBaseVisitor.h" class evalVisitor : public calcBaseVisitor { public: evalVisitor() : calcBaseVisitor() { } ~evalVisitor() { } //antlrcpp::Any visitProg(calcParser::ProgContext *context); antlrcpp::Any visitPrintExpr(calcParser::PrintExprContext *context); antlrcpp::Any visitAssign(calcParser::AssignContext *context); //antlrcpp::Any visitBlank(calcParser::BlankContext *context); antlrcpp::Any visitParens(calcParser::ParensContext *context); antlrcpp::Any visitMulDiv(calcParser::MulDivContext *context); antlrcpp::Any visitAddSub(calcParser::AddSubContext *context); antlrcpp::Any visitId(calcParser::IdContext *context); antlrcpp::Any visitInt(calcParser::IntContext *context); private: std::map<std::string, int> memory; }; #endif
evalVisitor.cpp
#include "evalVisitor.h" #include <iostream> antlrcpp::Any evalVisitor::visitPrintExpr(calcParser::PrintExprContext *context) { std::cerr << "[?] in evalVisitor::visitPrintExpt" << std::endl; int value = std::any_cast<int>(visit(context->expr())); std::cout << value << std::endl; return 0; } antlrcpp::Any evalVisitor::visitAssign(calcParser::AssignContext *context) { std::cerr << "[?] in evalVisitor::visitAssign" << std::endl; std::string id = context->ID()->getText(); int value = std::any_cast<int>(visit(context->expr())); memory.insert(std::pair<std::string, int>(id, value)); return 0; } antlrcpp::Any evalVisitor::visitParens(calcParser::ParensContext *context) { std::cerr << "[?] in evalVisitor::visitParens" << std::endl; return visit(context->expr()); return 0; } antlrcpp::Any evalVisitor::visitMulDiv(calcParser::MulDivContext *context) { std::cerr << "[?] in evalVisitor::visitMulDiv" << std::endl; int left = std::any_cast<int>(visit(context->expr(0))); int right = std::any_cast<int>(visit(context->expr(1))); if(context->op->getType() == calcParser::MUL) return left * right; else return left / right; return 0; } antlrcpp::Any evalVisitor::visitAddSub(calcParser::AddSubContext *context) { std::cerr << "[?] in evalVisitor::visitAddSub" << std::endl; int left = std::any_cast<int>(visit(context->expr(0))); int right = std::any_cast<int>(visit(context->expr(1))); if(context->op->getType() == calcParser::ADD) return left + right; else return left - right; return 0; } antlrcpp::Any evalVisitor::visitId(calcParser::IdContext *context) { std::map<std::string, int>::iterator iter; std::cerr << "[?] in evalVisitor::visitId" << std::endl; std::string id = context->ID()->getText(); if(memory.end() != (iter = memory.find(id))) return (*iter).second; return 0; } antlrcpp::Any evalVisitor::visitInt(calcParser::IntContext *context) { std::cerr << "[?] in evalVisitor::visitInt" << std::endl; return atoi(context->INT()->getText().c_str()); }
排查建议
- 修正输入文件读取条件:主程序中
if(argc > 2)逻辑错误,传入单个输入文件参数时argc为2,此时不会读取文件。应改为if(argc >= 2),确保能正确获取输入文件路径。 - 添加文件打开校验:在
ins.open(inputFile)后增加判断,确认文件是否成功打开,避免读取空流:if (!ins.is_open()) { std::cerr << "无法打开输入文件: " << inputFile << std::endl; return 1; } - 启用Token调试:取消主程序中
tokens.fill()和Token遍历打印的注释,运行后查看输出,确认Lexer是否真的没有读取到输入内容。 - 移除重复解析代码:主程序中先调用
parser.expr()会消耗Token流,导致后续parser.prog()解析时Token流已到末尾。需删除std::cout << parser.expr()->toStringTree() << std::endl;这一行。 - 修复语法文件错误:语法中
NEWLINE定义末尾存在多余的换行和右括号,修正为:NEWLINE : '\r'? '\n' ; // return newlines to parser (is end-statement signal)
内容的提问来源于stack exchange,提问作者thurizas
相关产品推荐
相关产品推荐

