使用 antlr4 解析 XML 标签和属性时出现问题

Problem using antlr4 to parse XML tag & attributes

我正在写一个简化的 antlr4 XML 语法来解析这个例子:

<root>
  <field a="1">
    random text
  </field>
</root>

语法是:

grammar TestKo;    
root        : '<root>' WS* field* WS* '</root>' ;
field       : '<field' attr* '>' chardata* '</field>' ;     // fails
attr        : TEXT '=' '"' TEXT '"';
chardata    : TEXT | WS ;
WS          : (' '|'\t'|'\r'? '\n')+   -> skip;
TEXT        : ~[<&]+ ;

运行 与 antlr4:

$ antlr4 TestKo.g4 && javac -cp /usr/share/java/antlr4-runtime.jar TestKo*java 
$ cat ./test.xml | grun  TestKo root -tree
line 4:2 mismatched input '</field>' expecting '='
(root <root> (field <field (attr  a="1">\n\trandom text\n   </field>)) </root>)

正确解析 field'>' 之间的属性缺少什么?
感谢您的任何建议。

在解析器考虑任何解析器规则之前,词法分析器运行完成。推论:解析器规则评估不能 affect/influence 词法分析器规则评估。所以,规则

TEXT : ~[<&]+ ;

将使用 [>="/],防止解析器看到相应的隐式标记。 (这是语法的主要错误。)

解释:在解析器中为字符串生成隐式标记。这是对“解析器不影响词法分析器”这一老生常谈的警告。它们在内部被定义为位于词法分析器规则列表的顶部,即通过魔法,因此具有最高的单个字符匹配优先级。

TEXT规则可以匹配多个字符串,并且对于这些字符串,具有更高的绝对匹配优先级:当多个词法分析器规则可以匹配给定输入时,匹配长度最长的规则获胜;如果长度相同,则第一个列出的规则获胜。

出于各种实际原因——清晰度和可维护性——最好不要依赖隐式标记。

建议:解析 XML 简单。区分 random text 和标签,作为目前的语法尝试,将是有问题的。鉴于标签提供了明确的保护字符——<>——使用 Antlr mode 功能将结构化的标签内词汇与 random text 的词汇完全隔离开来语法更容易设计和维护。

顺便说一句,-> skip 确实如其所说:不会产生任何令牌。所以,解析器规则

   chardata : TEXT | WS ; 

永远不会看到 WS 令牌。

如果您是 Antlr 的新手,TDAR 非常值得一读。

当我检查问题并准备答案时,您已经有了答案...

负标记很难管理,它们会消耗文件的其余部分。随着你的语法重命名为问题和你的数据:

$ grun Question root -tokens data.txt
[@0,0:5='<root>',<'<root>'>,1:0]
[@1,9:14='<field',<'<field'>,2:2]
[@2,15:40=' a="1">\n    random text\n  ',<TEXT>,2:8]
[@3,41:48='</field>',<'</field>'>,4:2]
[@4,50:56='</root>',<'</root>'>,5:0]
[@5,58:57='<EOF>',<EOF>,6:0]
line 4:2 mismatched input '</field>' expecting '=' 

它没有找到 =,因为它已被 attr 的第一个 TEXT 规则消耗在令牌 @2 中。试试这个:

grammar Question;

root        : '<root>' WS* field* WS* '</root>' ;
field       : '<field' attr* '>' chardata* '</field>' ; 
attr        : TEXT '=' STRING ;
chardata    : TEXT | WS ;
STRING      : '"' .*? '"' ;
WS          : (' '|'\t'|'\r'? '\n')+   -> skip;
TEXT        : ~[=<>&]+ ;

执行:

$ export CLASSPATH=".:/usr/local/lib/antlr-4.6-complete.jar"
$ alias a4='java -jar /usr/local/lib/antlr-4.6-complete.jar'
$ alias grun='java org.antlr.v4.gui.TestRig'
$ a4 Question.g4 
$ javac Question*.java
$ grun Question root -tokens data.txt
[@0,0:5='<root>',<'<root>'>,1:0]
[@1,9:14='<field',<'<field'>,2:2]
[@2,15:16=' a',<TEXT>,2:8]
[@3,17:17='=',<'='>,2:10]
[@4,18:20='"1"',<STRING>,2:11]
[@5,21:21='>',<'>'>,2:14]
[@6,22:40='\n    random text\n  ',<TEXT>,2:15]
[@7,41:48='</field>',<'</field>'>,4:2]
[@8,50:56='</root>',<'</root>'>,5:0]
[@9,58:57='<EOF>',<EOF>,6:0]