我正在用 Java 撰寫一個使用 Java 運算式決議的工具包。我以為我會嘗試使用 ANTLR,因為
- 它似乎無處不在地用于這種事情
- 似乎沒有很多開源替代品
- 實際上,我曾嘗試撰寫自己的通用決議器并放棄了。那東西很難。
我不得不說,在我感覺是大量閱讀和嘗試不同的事情之后(反正比我預期的要多),ANTLR 似乎非常難以使用。API 非常不直觀——我從來不確定我是否正確呼叫它。
盡管 ANTLR 教程和示例比比皆是,但我還沒有找到任何涉及決議 Java“運算式”的示例——其他人似乎都想決議整個 Java 檔案。
我開始這樣稱呼它:
Java8Lexer lexer = new Java8Lexer(CharStreams.fromString(text));
CommonTokenStream tokens = new CommonTokenStream(lexer);
Java8Parser parser = new Java8Parser(tokens);
ParseTree result = parser.expression();
但這不會決議整個運算式。例如,對于文本“ab”,它會回傳一個只包含“a”部分的結果,只是在它可以決議的第一件事之后退出。
美好的。所以我改為:
String input = "return " text ";";
Java8Lexer lexer = new Java8Lexer(CharStreams.fromString(input));
CommonTokenStream tokens = new CommonTokenStream(lexer);
Java8Parser parser = new Java8Parser(tokens);
ParseTree result = parser.returnStatement();
result = result.getChild(1);
認為這會迫使它決議整個運算式,然后我可以提取我關心的部分。這適用于諸如“ab”之類的名稱運算式,但是如果我嘗試決議諸如“abc(d)”之類的方法運算式,則會出現錯誤:
line 1:12 mismatched input '(' expecting '.'
有趣的是,a(),a.b()和a.b.cparse 很好,但a.b.c()也會因相同的錯誤而死亡。
這里有沒有 ANTLR 專家可能知道我做錯了什么?
另外,上面的錯誤列印到標準錯誤讓我很困擾,但我無法在結果物件中的任何地方找到它。我希望能夠向輸入運算式的用戶顯示該錯誤訊息(雖然很模糊)——他們可能沒有在看控制臺,即使他們在看,那里也沒有背景關系。有沒有辦法在我回傳的結果中找到該資訊?
任何幫助是極大的贊賞。
uj5u.com熱心網友回復:
對于像 那樣的規則expression,一旦識別出一個運算式,ANTLR 就會停止決議。
您可以通過向開始規則添加`EOF 來強制它繼續。
(您不想修改實際的`運算式規則,但您可以添加如下規則:
expressionStart: expressions EOF;
然后你可以使用:
ParseTree result = parser.expressionStart();
這將強制 ANTLR 繼續決議您的輸入,直到它到達您輸入的末尾。
回復:returnStatement
當我return a.b.c();在 IntelliJ 中運行ANTLR 預覽時,我得到了這個決議樹:

稍微遵循語法規則,我偶然發現了這些規則:
typeName: Identifier | packageOrTypeName '.' Identifier;
packageOrTypeName
: Identifier
| packageOrTypeName '.' Identifier
;
這兩個規則都包含一個packageOrTypeName '.' Identifier對我來說看起來有問題的替代方案。
在樹中,我們看到primaryNoNewArray_lfno_primary:2哪個表示匹配此規則中的第二個選項:
primaryNoNewArray_lfno_primary
: literal
| typeName ('[' ']')* '.' 'class' // <-- trying to match this rule
| unannPrimitiveType ('[' ']')* '.' 'class'
| 'void' '.' 'class'
| 'this'
| typeName '.' 'this'
| '(' expression ')'
| classInstanceCreationExpression_lfno_primary
| fieldAccess_lfno_primary
| arrayAccess_lfno_primary
| methodInvocation_lfno_primary
| methodReference_lfno_primary
;
I'm out of time at the moment, but will keep looking at it. It seems pretty unlikely there's this obvious a bug in the Java8Parser.g4, but it certainly seems like a bug at the moment. I'm not sure what about the context would change how this is parsed (by context, meaning where returnStatement is natively called in the grammar.)
I tried this input (starting with the compilationUnit rule:
class Test {
class A {
public B b;
}
class B {
String c() {
return "";
}
}
String test() {
A a = new A();
return a.b.c();
}
}
And it parses correctly (so, we've not found a major bug in the Java8Parser grammar ??):

Still, this doesn't seem right.
Getting closer:
If I start with the block rule, and wrap in curly braces ({return a.b.c();}), it parses fine.
I'm going to go with the theory that ANTLR needs a bit more lookahead to resolve an "ambiguity".
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/347892.html
下一篇:使用bash-c逐行決議
