本实验为 NJU 编译原理实验 1
# 实验要求
- 通过标准错误输出(
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); | |
} | |
} |