- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
Note: This is a self-answered question that aims to provide a reference about one of the most common mistakes made by ANTLR users.
grammar KeyValues;
keyValueList: keyValue*;
keyValue: key=IDENTIFIER '=' value=INTEGER ';';
IDENTIFIER: [A-Za-z0-9]+;
INTEGER: [0-9]+;
WS: [ \t\r\n]+ -> skip;
foo = 42;
line 1:6 mismatched input '42' expecting INTEGER
line 1:8 mismatched input ';' expecting '='
42
识别为
INTEGER
?
[0-9]+
模式匹配就好了。
INTEGER
和
IDENTIFIER
的定义顺序,那似乎可行,但是为什么顺序首先重要呢?
最佳答案
在ANTLR中,词法分析器与解析器是隔离的,这意味着它将根据词法分析器语法规则将文本拆分为类型化的标记,并且解析器对此过程没有任何影响(例如,不能说“现在给我一个INTEGER
”) )。它自己产生 token 流。此外,解析器不在乎 token 文本,它仅在乎 token 类型以匹配其规则。
当多个词法分析器规则可以匹配相同的输入文本时,这很容易成为问题。在这种情况下,将根据以下优先规则选择 token 类型:
'='
),则使用隐式规则作为标记类型keyValue
解析器规则匹配:
IDENTIFIER
'='
INTEGER
';'
其中
'='
和
';'
是隐式 token 类型。
42
可以同时匹配
INTEGER
和
IDENTIFIER
,并且首先定义了
IDENTIFIER
,所以解析器将接收以下输入:
IDENTIFIER
'='
IDENTIFIER
';'
,它将无法与
keyValue
规则匹配。请记住,解析器无法与词法分析器通信,它只能从词法分析器接收数据,因此它不能说“尝试下一步匹配
INTEGER
”。
IDENTIFIER
重新定义为[A-Za-z] [A-Za-z0-9]*
(要求以字母开头)。这样可以完全避免问题,但是可以防止定义以数字开头的标识符名称,因此它会改变语法的意图。 INTEGER
和IDENTIFIER
。这解决了大多数情况下的问题,但阻止定义全数字标识符,因此,它也以一种微妙的,不太明显的方式改变了语法的意图。 INTEGER
和IDENTIFIER
,使其优先于INTEGER
。然后,定义一个解析器规则id: IDENTIFIER | INTEGER;
,然后在其他解析器规则中使用该规则而不是IDENTIFIER
,这会将keyValue
更改为key=id '=' value=INTEGER ';'
。 grammar LexerPriorityRulesExample;
// Parser rules
randomParserRule: 'foo'; // Implicitly declared token type
// Lexer rules
BAR: 'bar';
IDENTIFIER: [A-Za-z]+;
BAZ: 'baz';
WS: [ \t\r\n]+ -> skip;
aaa foo bar baz barz
IDENTIFIER
'foo'
BAR
IDENTIFIER
IDENTIFIER
EOF
aaa
的类型为IDENTIFIER
IDENTIFIER
规则可以匹配此 token ,没有歧义。 foo
的类型为'foo'
randomParserRule
引入了隐式'foo'
token 类型,该类型比IDENTIFIER
规则优先。 bar
的类型为BAR
BAR
规则匹配,该规则在IDENTIFIER
规则之前定义,因此具有优先权。 baz
的类型为IDENTIFIER
BAZ
规则匹配,但也与IDENTIFIER
规则匹配。选择后者,因为它是在BAR
之前定义的。BAZ
将永远无法匹配,因为IDENTIFIER
规则已经涵盖了BAZ
可以匹配的所有内容。 barz
的类型为IDENTIFIER
BAR
规则可以匹配此字符串的前3个字符(bar
),但是IDENTIFIER
规则将匹配4个字符。由于IDENTIFIER
与更长的子字符串匹配,因此会选择它作为BAR
的替代项。 EOF
(文件末尾)是隐式定义的 token 类型,它始终出现在输入的末尾。 'foo'
)就像在所有其他词法分析器规则之前定义的一样。由于它们增加了复杂性,因此建议完全避免它们,而改为声明显式词法分析器规则。仅将标记列表放在一个位置,而不是将它们分散在整个语法中,是该方法的显着优势。
关于parsing - ANTLR词法分析器如何消除其规则的歧义(或者为什么我的解析器会产生 "mismatched input"错误)?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46267980/
虽然我正在运行以测试此 thread 中的一些代码我发现了一件奇怪的事情,如果你考虑下面的程序 import java.util.ArrayList; import java.util.List; p
我只是很好奇为什么使用 using 指令以这种方式设计它。对于 1) struct 被视为命名空间,对于 2) 它不是: struct foo { using type0 = int; }; na
我需要在 ANTLR 中匹配一条消息,该消息包含由/分隔的 2 个字段第一个字段可以有 1..3 数字,第二个字段可以有 1..2 数字, 这不起作用 msg: f1 '/' f2; f1: DIGI
我想定义一个带有 x, y, z 坐标的 3D 矢量。可以使用(+)运算符添加向量,并可以使用length函数计算长度 如果我想编译它,我会收到以下错误: It could refer to eith
所以,今天我一直在测试 Java 的重载技术,但遇到了我无法解释的歧义。基本上,当存在带有原语及其相应包装器的可变参数方法时,编译器会提示并且无法决定选择哪一个,我不明白为什么?人类很容易决定而不是编
发现: Ambiguity while overloading the cast operator 和 C++ Operator Ambiguity 没有帮助 情况: 在容器类的内部我有: opera
我正在使用以下 XPath 片段 ancestor::contribution[1]/preceding-sibling::contribution[@speaker-reference][1] 我的
在尝试依赖可变参数模板实现一些事情时,我偶然发现了一些我无法解释的事情。我将问题归结为以下代码片段: template struct A {}; template class Z, typenam
我最近对 LALR 的研究足以写一篇 LALR generator ,并且我正在尝试为其构造一个 java 或 c# 风格的语法(其开头指定为 here )。 我知道编写解析器生成器需要额外的努力
在阅读 wikipedia article关于修订控制的文章,我发现了一些似乎定义不明确的术语,并且想知道它们是如何在现实世界中实际使用/应用的。具体来说: “主线”与“基线” “分支”与“流” “结
我有以下代码: class A{ public void print(A a){ System.out.println("A 1"); } } class B exte
考虑我试过的以下查询,有两个表,Orders 和 Customers,每个表都有列名称 CustomerID,当我尝试显示两个 CustomerID 时,只有一列显示,我不明白为什么会这样,或者我是理
我正在为基本汇编语言编写一个简单的组合词法分析器/解析器。我的问题是,在解析操作码时,我需要解析一些十六进制,它是指令计数器,可能是立即值等,以及实际的操作码。 基本上,当解析像 add 这样的东西时
我正在尝试在 mysql 中跨两个不同的数据库进行标题搜索,以匹配来自不同来源的数据。在 db1 或 db2 中,标题有时会在一个数据库中以“第一个标题”开头,而在另一个数据库中则以“第一个标题”开头
我对 token 级别的歧义有疑问。 问题看起来是这样的。我的代码看起来像这样,因此标记 t_UN1 具有更高的优先级。 t_ignore = ' \t\v\r' # whitespace ....
在 Main() 的最后一行,我在尝试执行指针操作时遇到错误,但我实际上想做的是调用我在 Foo 类中定义的乘法运算符。如果这不是调用乘法的语法,那是什么? namespace test {
在此page它说: The "00" specifier causes the value to be rounded to the nearest digit preceding the decim
我正在使用 C++ Builder 并使用 TDateTime 数据类型创建 GUI 应用程序。 当我在 TDateTime 对象上使用 MonthOf() 方法时,我得到的值比 TDateTime
$7.3.3/14 (C++03) struct A { int x(); }; struct B : A { }; struct C : A { using A::x; int x(in
我一直在开发一种适配器类,当我在 clang 下遇到问题。当定义了左值引用和右值引用的转换运算符时,您会在尝试从您的类中移出时遇到歧义编译错误(当这样的代码应该没问题时,如 operator cons
我是一名优秀的程序员,十分优秀!