2015-07-02 100 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]+;