2015-07-02 98 views
2

我是ANTLR(任何版本)的新手,我剛開始編寫我的第一個語法文件。我正在使用帶有ANTLR插件的IntelliJ IDE(v1.6)。解析EOF時ANTLR4錯誤

我的語法是

grammar TestGrammar; 

testfile   :  message+ EOF; 
message   :  timestamp WS id (NL | EOF); 
timestamp  :  NumericLiteral; 
id    :  NumericLiteral; 
NumericLiteral : INTEGER | DECIMAL; 

INTEGER   : [+-]? [0-9]+; 
DECIMAL   : [+-]? [0-9]* '.' [0-9]+; 
EXPONENT  : [eE] [+-]? [0-9]+; 

WS: (' ' | '\t')+; 
NL: '\r'? '\n'; 

當我套用簡單的測試輸入

123 1231 
123 1312 

的數據被正確解析,但我得到的的IntelliJ預覽窗口中的錯誤。 「外部輸入 '<EOF>' 期待{<EOF>,NL}」

我做了什麼錯? EOF似乎被正確檢測到......如果我在最後一行添加了一個NL,那麼該文件被正確解析,但我需要確保最終的NL是可選的。

的格式附加細節:

我們逆向工程的數據格式,所以我會很誠實地說,我們真的不知道有什麼約束將是!我們目前的理解是:

  • 每個消息必須在其自己的行
  • 允許將郵件之間的空行
  • 在文件的結尾不要求新線

我們已經看到了遵循這些模式的文件的證據,所以我們知道它們是有效的輸入。

回答

4

在您的語法中,您明確指出「新行」必須結束行。這裏的問題是:該語言的message部分末尾是否有「新行」?同樣的問題出現在空白處。他們是語言的一部分嗎?如果沒有,你可以跳過它們:

WS: (' ' | '\t') -> skip; 
NL: '\r'? '\n' -> skip; 

然後,您可以簡化message規則:

message: timestamp id; 

如果你真的需要保留行的末尾:

NL: '\r'? '\n'; 

而且您在message規則的末尾添加此代幣作爲可選項:

message: timestamp id NL?; 

這將與你的榜樣工作,但會失敗:

123 1231 

123 1312 

的兩條線之間的\n將產生錯誤。這似乎是最有希望的解決方案是第一個(跳過NLWS與簡化message規則),但該條目將被匹配爲OK:

123 1231 123 1312 

這將產生兩個message規則環境。

總結一下,在您的示例中,爲了給您提供構建語法的最佳方式,我們必須知道輸入語言的約束條件。

<編輯>

關於你的評論,有兩個解決方案。要麼你確定你的文件格式良好,並且想法是無限制地提取文件的信息,或者你必須確保輸入文件符合語法(爲了還要刪除「壞文件「)。

我很確定你在第一種情況下(就像你說你正在執行逆向工程),所以你可能想從你的文件中創建一個CST來提取信息。在這種情況下,考慮到您的輸入文件總是完好無損的,您不需要費心檢查在messages的末尾是否存在NL(通過構建,文件總是有一行message)。在這種情況下,你可以跳過你不需要的一切。語法變成:

grammar TestGrammar; 

testfile  :  message+ EOF; 
message   :  timestamp id; 
timestamp  :  NumericLiteral; 
id    :  NumericLiteral; 
NumericLiteral : INTEGER | DECIMAL; 

INTEGER   : [+-]? [0-9]+; 
DECIMAL   : [+-]? [0-9]* '.' [0-9]+; 
EXPONENT  : [eE] [+-]? [0-9]+; 

WS: (' ' | '\t')+ -> skip; 
NL: '\r'? '\n' -> skip; 

此語法將認識

123 1231 
123 1312 

以及

123 1231 
    (as many as \n you want between them) 
123 1312 

而且

123 1231 123 1312 (-> this will produce two messages as expected) 

但是,如果你的輸入文件可能不形成良好,用這個語法,y你將無法排除它們。

grammar TestGrammar; 

testfile  :  (message? NL)* message EOF; 
message   :  timestamp id; 
timestamp  :  NumericLiteral; 
id    :  NumericLiteral; 

WS: [\t ]+ -> skip; 
NL: '\r'? '\n'; 

NumericLiteral : INTEGER | DECIMAL; 

INTEGER   : [+-]? [0-9]+; 
DECIMAL   : [+-]? [0-9]* '.' [0-9]+; 
EXPONENT  : [eE] [+-]? [0-9]+; 

有了這個語法:

123 1231 
(as many as \n you want between them) 
123 1312 

如果您需要確保只有一個消息是存在的線,你應該提出拉茲FRIMAN這裏的語法稍加修改的版本,請被識別出來:

123 1231 123 1312 

會引發錯誤。

+0

感謝您的迴應文森特。我以爲我試過「 - >跳過」,但遇到了另一個錯誤消息。我會再試一次,並確認。你是對的,我們不需要保留NL字符,只是說明它們會發生。 你知道爲什麼我的語法報告它的錯誤嗎?我的理解是,解析器有效地說:「錯誤:發現EOF,期待EOF或NL」 – Anthony

+0

我剛剛意識到你在最後要求瞭解關於該語言的更多細節你的建議答案; 我們正在對數據格式進行逆向工程,所以我會誠實地說,我們並不真正知道約束條件會是什麼! 我們目前的理解是: 每個消息必須在其自己的行 允許將郵件 之間的空行不要求在 我們已經看到了以下的模式,所以我們的文件證據文件的末尾新行知道他們是有效的輸入。 – Anthony

+0

說實話,我不知道爲什麼你彈出的錯誤(不管'EOF'標記表達兩次)。我知道在某些情況下,ANTLR會添加'EOF'標記(例如,當規則不完全匹配時),並且在某些情況下,我記得看到'EOF'標記經典標號爲'-1',而其他編號爲'-2'。對此缺乏細節感到抱歉:\。關於你的語言細節,我將編輯我的文章給你更多的細節。 –

0

如果您需要確保換行符的約束條件,可以使根文件檢查最終的「消息」規則,然後應用EOF規則。

這個語法是下面貼:

testfile   :  (message NL)* message EOF; 
message   :  timestamp id; 
timestamp  :  NumericLiteral; 
id    :  NumericLiteral; 

WS: [\t ]+ -> channel(HIDDEN); 
NL: '\r'? '\n'; 

NumericLiteral : INTEGER | DECIMAL; 

INTEGER   : [+-]? [0-9]+; 
DECIMAL   : [+-]? [0-9]* '.' [0-9]+; 
EXPONENT  : [eE] [+-]? [0-9]+;