You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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仅识别到 token,通过xxd验证输入文件编码正常,尝试已知解决方案无效,现寻求排查建议。

相关代码

语法文件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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 05:44:55