本文介紹了使用ANTLR進(jìn)行Java表達(dá)式解析的處理方法,對(duì)大家解決問(wèn)題具有一定的參考價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)吧!
問(wèn)題描述
我正在用Java編寫(xiě)一個(gè)使用Java表達(dá)式解析的工具包。我想我應(yīng)該試著使用ANTLR,因?yàn)?/p>
-
它似乎無(wú)處不在地用于這類事情
開(kāi)源替代方案似乎不多
不久前我實(shí)際上曾嘗試編寫(xiě)自己的通用解析器,但最終放棄了。那東西很難。
我不得不說(shuō),在我感覺(jué)自己閱讀和嘗試了很多不同的東西(無(wú)論如何,比我預(yù)期的要多)之后,ANTLR似乎非常難以使用。該API非常不直觀–我從來(lái)不太確定我調(diào)用它是否正確。
盡管ANTLR教程和示例比比皆是,但我還沒(méi)有找到任何涉及解析Java表達(dá)式的示例–其他人似乎都想解析整個(gè)Java文件。
我一開(kāi)始是這樣稱呼它的:
Java8Lexer lexer = new Java8Lexer(CharStreams.fromString(text));
CommonTokenStream tokens = new CommonTokenStream(lexer);
Java8Parser parser = new Java8Parser(tokens);
ParseTree result = parser.expression();
但這不會(huì)解析整個(gè)表達(dá)式。例如,對(duì)于TEXT&QOOT;A.B&QOOT;,它將返回只包含&QOOT;A&QOOT;部分的結(jié)果,僅在它可以分析的第一個(gè)內(nèi)容之后退出。
好的。因此我更改為:
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);
認(rèn)為這會(huì)迫使它解析整個(gè)表達(dá)式,然后我可以只提取我關(guān)心的部分。這適用于像";a.b";這樣的名稱表達(dá)式,但是如果我試圖解析像";a.bc(D)";這樣的方法表達(dá)式,它會(huì)給出一個(gè)錯(cuò)誤:
line 1:12 mismatched input '(' expecting '.'
有趣的是,a()
、a.b()
和a.b.c
解析正常,但a.b.c()
也會(huì)死,但也會(huì)出現(xiàn)相同的錯(cuò)誤。
這里有沒(méi)有ANTLR專家可能知道我做錯(cuò)了什么?
另外,上面的錯(cuò)誤被打印到stderr,但是我在Result對(duì)象中找不到它,這讓我相當(dāng)困擾。我希望能夠向輸入表達(dá)式的用戶顯示該錯(cuò)誤消息(盡管很模糊)–他們可能沒(méi)有查看控制臺(tái),即使他們正在查看控制臺(tái),也沒(méi)有上下文。有沒(méi)有辦法在我返回的結(jié)果中找到該信息?
非常感謝您的幫助。
ANTLR
對(duì)于expression
這樣的規(guī)則,一旦識(shí)別到表達(dá)式,推薦答案將停止分析。
您可以通過(guò)將`EOF添加到您的啟動(dòng)規(guī)則來(lái)強(qiáng)制它繼續(xù)。
(您不想修改實(shí)際的`表達(dá)式規(guī)則,但可以添加如下規(guī)則:
expressionStart: expressions EOF;
然后您可以使用:
ParseTree result = parser.expressionStart();
這將強(qiáng)制ANTLR繼續(xù)分析您的輸入,直到它到達(dá)您的輸入的末尾。
Re:rereturn Statement
當(dāng)我通過(guò)IntelliJ中的ANTLR預(yù)覽運(yùn)行return a.b.c();
時(shí),我得到以下解析樹(shù):
稍微遵循一下語(yǔ)法規(guī)則,我就偶然發(fā)現(xiàn)了這些規(guī)則:
typeName: Identifier | packageOrTypeName '.' Identifier;
packageOrTypeName
: Identifier
| packageOrTypeName '.' Identifier
;
這兩個(gè)規(guī)則都包含packageOrTypeName '.' Identifier
的替代規(guī)則,這在我看來(lái)是有問(wèn)題的。
在樹(shù)中,我們看到primaryNoNewArray_lfno_primary:2
,表示匹配此規(guī)則中的第二個(gè)選項(xiàng):
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
;
我現(xiàn)在沒(méi)時(shí)間了,但我會(huì)繼續(xù)關(guān)注的。在Java8Parser.g4中似乎不太可能出現(xiàn)這個(gè)明顯的錯(cuò)誤,但目前看起來(lái)肯定是一個(gè)錯(cuò)誤。我不確定上下文會(huì)改變它的解析方式(按上下文,這意味著語(yǔ)法中本地調(diào)用returnStatement
的位置)。
我嘗試了此輸入(從compilationUnit
規(guī)則開(kāi)始:
class Test {
class A {
public B b;
}
class B {
String c() {
return "";
}
}
String test() {
A a = new A();
return a.b.c();
}
}
并且它可以正確解析(因此,我們沒(méi)有在Java8Parser語(yǔ)法