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

如何让ANTLR显示所有语法错误?(Java开发Pascal编译器场景)

解决ANTLR仅捕获首个语法错误的问题(Pascal编译器场景)

要让ANTLR捕获所有语法错误而不是遇到第一个就终止,需要从错误恢复策略和错误收集机制两方面入手,以下是具体实现步骤:

1. 重写DefaultErrorStrategy,确保解析不终止

ANTLR默认的错误策略在遇到严重错误时会抛出ParseCancellationException终止解析,你需要重写核心方法,让解析器记录错误后继续执行:

import org.antlr.v4.runtime.DefaultErrorStrategy;
import org.antlr.v4.runtime.Parser;
import org.antlr.v4.runtime.RecognitionException;
import org.antlr.v4.runtime.TokenStream;

public class PascalErrorStrategy extends DefaultErrorStrategy {

    @Override
    public void recover(Parser recognizer, RecognitionException e) {
        // 记录错误信息到自定义容器
        recordError(recognizer, e);
        // 跳过错误符号,定位到下一个可恢复的语法点(比如语句结束、块结束)
        TokenStream tokens = recognizer.getInputStream();
        if (tokens != null) {
            consumeUntil(recognizer, getErrorRecoverySet(recognizer));
        }
    }

    @Override
    public Token recoverInline(Parser recognizer) throws RecognitionException {
        RecognitionException e = new InputMismatchException(recognizer);
        recordError(recognizer, e);
        // 尝试匹配下一个合法符号,恢复解析
        return super.recoverInline(recognizer);
    }

    @Override
    public void sync(Parser recognizer) {
        // 禁用默认的同步异常抛出逻辑,避免解析终止
    }

    private void recordError(Parser recognizer, RecognitionException e) {
        Token offendingToken = e.getOffendingToken();
        String errorMsg = String.format("语法错误 @ 行 %d, 列 %d: %s",
                offendingToken.getLine(),
                offendingToken.getCharPositionInLine(),
                e.getMessage());
        // 假设PascalCompiler有一个静态List<String>用于存储所有错误
        PascalCompiler.errorList.add(errorMsg);
    }
}

关键说明:

  • recover方法负责处理严重错误,跳过错误内容到安全点继续解析
  • sync方法重写为空,避免默认逻辑抛出异常终止解析
  • 自定义recordError方法统一收集错误信息

2. 注册自定义错误策略到解析器

创建Parser实例后,替换默认的错误策略:

// 假设已经生成了PascalLexer和PascalParser
PascalLexer lexer = new PascalLexer(CharStreams.fromPath(Paths.get("your-pascal-file.pas")));
CommonTokenStream tokens = new CommonTokenStream(lexer);
PascalParser parser = new PascalParser(tokens);

// 设置自定义错误策略
parser.setErrorHandler(new PascalErrorStrategy());

3. 自定义ErrorListener收集所有错误

除了重写错误策略,还需要自定义ErrorListener来捕获ANTLR抛出的所有语法错误(包括错误策略未覆盖的情况):

import org.antlr.v4.runtime.BaseErrorListener;
import org.antlr.v4.runtime.RecognitionException;
import org.antlr.v4.runtime.Recognizer;
import java.util.ArrayList;
import java.util.List;

public class PascalErrorListener extends BaseErrorListener {
    private final List<String> errorList = new ArrayList<>();

    @Override
    public void syntaxError(Recognizer<?, ?> recognizer, Object offendingSymbol,
                            int line, int charPositionInLine, String msg,
                            RecognitionException e) {
        String errorMsg = String.format("语法错误 @ 行 %d, 列 %d: %s",
                line, charPositionInLine, msg);
        errorList.add(errorMsg);
    }

    public List<String> getErrorList() {
        return errorList;
    }
}

注册监听器时,记得移除默认的控制台输出监听器,避免干扰:

PascalErrorListener errorListener = new PascalErrorListener();
// 移除默认的ConsoleErrorListener
parser.removeErrorListeners();
parser.addErrorListener(errorListener);

解析完成后,即可从errorListener.getErrorList()获取所有错误信息。

4. 优化语法规则的错误恢复能力

如果语法规则没有合适的同步点,ANTLR可能无法从错误中恢复继续解析。可以在Pascal语法文件(.g4)中添加错误恢复规则,比如:

// 在语句规则中添加错误分支,跳过未知符号直到下一个语句分隔符
statement : assignmentStmt
          | ifStmt
          | whileStmt
          | errorStmt
          ;

errorStmt : UNEXPECTED_TOKEN { skip(); } // 跳过当前错误符号,继续解析后续内容
          ;

或者在块级规则中定义同步点,让解析器能快速定位到合法的语法位置:

block : 'BEGIN' statement* 'END'
      ;

// 定义错误恢复的同步集合,比如块结束符、语句分隔符
tokens { UNEXPECTED_TOKEN }

常见问题排查

  • 确保错误策略没有抛出ParseCancellationException,否则解析会直接终止
  • 必须移除默认的ConsoleErrorListener,否则可能导致错误信息重复或收集不全
  • 错误收集容器(比如List<String>)要确保在解析过程中能被正确访问

内容的提问来源于stack exchange,提问作者Phalanx

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 05:04:58