我有这个语法:
grammar MkSh;
script
: (statement
| targetRule
)*
;
statement
: assignment
;
assignment
: ID '=' STRING
;
targetRule
: TARGET ':' TARGET*
;
ID
: ('a'..'z'|'A'..'Z'|'_') ('a'..'z'|'A'..'Z'|'0'..'9'|'_')*
;
WS
: ( ' '
| '\t'
| '\r'
| '\n'
) -> channel(HIDDEN)
;
STRING
: '\"' CHR* '\"'
;
fragment
CHR
: ('a'..'z'|'A'..'Z'|' ')
;
TARGET
: ('a'..'z'|'A'..'Z'|'0'..'9'|'_'|'-'|'/'|'.')+
;
和此输入文件:
hello="world"
target: CLASSES
运行我的解析器时,我收到此错误:
line 3:6 mismatched input ':' expecting '='
line 3:15 mismatched input ';' expecting '='
这是因为解析器正在采取" target"作为ID而不是TARGET。我希望解析器根据分隔符选择规则(':' vs' =')。
我怎样才能实现这一目标?
(这是我的第一个Antlr项目,所以我对任何事情都开放。)
答案 0 :(得分:1)
首先,你需要知道 target 这个词被匹配为 ID 令牌,而不是 TARGET 令牌,因为你已经在 TARGET 之前编写了规则 ID ,它将始终被词法分析器识别为 ID 。请注意, target 这个词完全符合 ID 和 TARGET 词法分析器规则,(我假设您正在编写一个laguage) ,意味着作为关键字的目标也可以用作id。在书中 - “最终的ANTLR参考”中有一个副标题“将关键字作为标识符处理”,可以直接处理这些问题。我建议你看一下。或者如果您更喜欢快速回答,解决方案是使用词法分析器模式。将语法分解为解析器和词法分析器语法也会更好。
答案 1 :(得分:1)
正如@cantSleepNow所暗示的那样,你已经定义了一个令牌(TARGET),它是另一个令牌(ID)的词汇超集,然后告诉词法分析器只将字符串标记为TARGET,如果不能被标记为ID。由于ANTLR lexing规则看起来像ANTLR解析规则,所有这些都变得更加模糊,尽管它们实际上是完全不同的野兽。
(警告:在没有测试的情况下写下我的头顶: - )
您的真实项目可能更复杂,但在您发布的可能简化示例中,您可以推迟将两者区分为解析阶段,而不是在词法分析器中区分它们:
id : TARGET
{ complain if not legal identifier (e.g., contains slashes, etc.) }
;
assignment
: id '=' STRING
;
似乎可以解决lexing问题,并允许您提供比#34;语法错误"更智能的错误消息。当用户获取ID错误的语法时。语法仍然含糊不清,但也许ANTLR轮盘会在模糊的情况下做出你喜欢的选择。当然,明确的语法往往会使人类发现更具可读性的语言,现在你可以看到为什么经典makefile
语法需要在赋值或目标规则之后的换行符。