解析EOF时ANTLR4出错

时间:2015-07-02 10:49:07

标签: antlr antlr4

我是ANTLR(任何版本)的新手,我刚刚开始编写我的第一个语法文件。我使用IntelliJ IDE和ANTLR插件(v1.6)。

我的语法是

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是可选的。

格式的其他详细信息:

我们正在对数据格式进行逆向工程,所以我会诚实地说,我们并不知道会有什么约束!我们目前的理解是:

  • 每封邮件必须在其自己的行
  • 在消息之间允许空行
  • 不要求在文件末尾添加新行

我们已经看到了遵循这些模式的文件的证据,因此我们知道它们是有效的输入。

2 个答案:

答案 0 :(得分: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将产生错误。似乎最有希望的解决方案是第一个(使用简化的NL规则跳过WSmessage)但是,此条目将匹配为OK:

123 1231 123 1312

它将生成两个message规则上下文。

总而言之,在您的示例中,为了给您提供构建语法的最佳方法,我们必须知道输入语言的限制。

<强>&LT;编辑&gt;

关于您的意见,有两种解决方案。您可以确定您的文件格式正确,并且想法是在没有约束的情况下提取文件的信息,或者您处于动态状态,您必须确保输入文件符合语法(以便同时删除&#34 ;坏文件&#34;)。

我非常确定您是第一种情况(正如您所说的那样正在执行逆向工程),因此您可能希望从文件中创建CST以提取信息。在这种情况下,考虑到您的输入文件总是很好,您不必费心检查NL末尾是否存在messages(通过构造,文件总是有一个{ {1}}逐行)。在这种情况下,您可以跳过您不需要的所有内容。语法变成了:

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

但是,如果输入文件格式不正确,使用此语法,您将无法排除它们。如果你需要确保一行只有一条消息,你应该使用Raz Friman提出的略微修改的语法版本:

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

用这个语法:

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

会抛出错误。

答案 1 :(得分:0)

如果您需要确保Newlines的约束,您可以让根文件检查最终的“消息”规则,然后应用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]+;