# 实验要求

  • 通过标准错误输出( stderr , 如 System.err 等), 打印程序的所有运行结果。
  • 对于包含词法错误的文件,需要打印所有错误信息,格式为: Error type A at Line [lineNo]:[errorMessage]
  • 对于没有任何词法错误的文件,打印所有识别到的 Tokens 信息

# .g4 文件编写

按照 SysY 词法规则来编写即可,这里不再赘述。

# 代码实现

Main.java 中,我们需要实现 main 函数,读取文件,进行词法分析,并输出结果。

// 通过标准错误输出(stderr, 如 System.err 等), 打印程序的 所有 运行结果。
if (args.length < 1) {
    System.err.println("input path is required");
}
String source = args[0];
CharStream input = CharStreams.fromFileName(source);
SysYLexer sysYLexer = new SysYLexer(input);

接下来实现打印错误信息,需要重写 org.antlr.v4.runtime.BaseErrorListener 中的 syntaxError 函数,并实现 printLexerErrorInformation 函数。

public class MyErrorListener extends BaseErrorListener {
    private final List<String> errorInformation = new ArrayList<>();
    private boolean hasError = false;
    @Override
    public void syntaxError(Recognizer<?, ?> recognizer, Object offendingSymbol, int line, int charPositionInLine, String msg, RecognitionException e) {
        hasError = true;
        errorInformation.add("Error type A at Line " + line + ":" + msg);
    }
    public boolean hasError() {
        return hasError;
    }
    public void printLexerErrorInformation() {
        // 你需要打印所有错误信息,
        // 格式为:Error type A at Line [lineNo]:[errorMessage],
        // 其中 lineNo 为出错的 token 首个字符所在行的行号,errorMessage 可自行定义,
        // 本实验不做要求,只要冒号前的信息正确即可。;
        for (String s : errorInformation) {
            System.err.println(s);
        }
    }
}

接下来,我们需要实现 printSysYTokenInformation 函数,用于打印所有识别到的 Tokens 信息。

private static void printSysYTokenInformation(Token t) {
    String tokenName = SysYLexer.VOCABULARY.getSymbolicName(t.getType());
    String tokenText = t.getText();
    int line = t.getLine();
    // 特别要求:输出时忽略所有注释,对十六进制和八进制数字常量输出 token 文本时需输出其十进制的值
    if (tokenName.equals("INTEGER_CONST")) {
        if (tokenText.startsWith("0x")) {
            tokenText = String.valueOf(Integer.parseInt(tokenText.substring(2), 16));
        } else if (tokenText.startsWith("0")) {
            tokenText = String.valueOf(Integer.parseInt(tokenText, 8));
        }
    }
    System.err.println(tokenName + " " + tokenText + " at Line " + line + ".");
}

最后,我们需要实现 main 函数的剩余部分。

MyErrorListener myErrorListener = new MyErrorListener();
sysYLexer.removeErrorListeners();
sysYLexer.addErrorListener(myErrorListener);
var myTokens = sysYLexer.getAllTokens();
if (myErrorListener.hasError()) {
    myErrorListener.printLexerErrorInformation();
} else {
    for (var t : myTokens) {
        printSysYTokenInformation(t);
    }
}