具有隐式令牌的 ANTLR 规则优先级



我正在使用ANTLR,偶然发现了一些让我感到困惑的事情:

grammar Test;
testA: 'a' 'b' 'c' | 'ab';
testB: 'abc' | 'ab';

给定testA输入"abc",它正在解析"ab"(右侧),但在testB上它正在解析"abc"(左侧)。通过研究词法分析器,我的印象是它总是试图找到最长的匹配。

我本来希望它在第一种情况下输出"a"b"c",在第二种情况下输出"abc"(两次左侧),因为它们都列在第一位,而且更长。

function
: 'afunc' '(' 'args'? ')' // line 1:5 mismatched input '()' expecting '('
| 'bfunc' '()'
;

同样,"afunc()"的输入在本例中失败,它标记化为()()。是什么解释了这些行为,解决方案是什么?

解析器在这两种情况下都表现正确。对于testA最长的单场比赛是ab.3个单字母令牌就是这样,每个令牌都是一个令牌。

对于testB字符串abcab长,因此在这种情况下它匹配。

有了这些信息,应该很清楚该怎么做:如果要将它们作为一个匹配,将它们合并为一个。

给定 testA 上输入的 "abc",它正在解析 "ab"(右侧)

它可能看起来解析正确(因为解析器可能会尝试恢复并继续解析),但事实并非如此。对于输入"abc",将不会创建令牌'ab'

给定语法:

grammar Test;
testA: 'a' 'b' 'c' | 'ab';
testB: 'abc' | 'ab';

和代码:

TestLexer lexer = new TestLexer(CharStreams.fromString("abc"));
TestParser parser = new TestParser(new CommonTokenStream(lexer));
parser.testA();

解析器将产生错误:

line 1:0 mismatched input 'abc' expecting {'a', 'ab'}

因为"abc"始终被标记为单个令牌。您尝试匹配的解析器规则并不重要。词法分析器独立于解析器运行。

我希望它在第一种情况下输出"a"b"c"和"abc">

不,请参阅我之前关于词法分析器独立于解析器工作的评论。

同样,"afunc()"的输入在本例中失败,它通过 ( 和 ) 标记为 ()。是什么解释了这些行为,解决方案是什么?

我不知道你说的"解决方案"是什么意思,因为我不知道;看不到问题。这就是词法分析器的工作原理:

  1. 它在创建令牌时尝试匹配尽可能多的字符
  2. 当 2 个或更多词法分析器规则可以匹配相同的字符时,首先定义的规则"获胜">

鉴于这 2 条规则,很明显(或应该是)输入"afunc()"只创建 2 个令牌:

  • afunc
  • ()(不是()令牌,因为第一条规则:匹配尽可能多的字符)

在这种情况下,您不应该创建像()这样的令牌,它应该是 2 个单独的令牌()

function
: 'afunc' '(' 'args'? ')'
| 'bfunc' '(' ')'
;

理解当您将文字字符串放入解析器规则时,ANTLR 将为您创建令牌规则可能会有所帮助(它只会为这些令牌编造名称(所以我通常更喜欢(大多数情况下)避免像这样的文字)。

从逻辑上讲,这在 ANTLR 中被视为具有以下语法:

grammar Test;
TOK_1 : ‘a’;
TOK_2 : ‘b’;
TOK_3 : ‘c’;
TOK_4 : ‘ab’;
TOK_5 : ‘abc’;
testA: TOK_1 TOK_2 TOK_3 | TOK_4;
testB: TOK_5 | TOK_4
;

当您了解ANTLR首先分析您的输入字符流以产生大量令牌时(并且词法分析器将始终选择与您的输入匹配的较长的令牌规则)。

因此,您输入的"abc"将始终标记为一个TOK_5令牌的流,而 testB 是处理该令牌的解析器规则。

(在分析器规则中放置文本时,很容易将它们视为作为运行分析器规则的一部分进行评估的东西。 这实际上只是一种方便(通常仅用于您在parseTree中不需要真正关心的令牌)

最新更新