将令牌优先于另一个

时间:2019-04-26 17:13:09

标签: java javac lexer javacc

我想对LEXERS有所了解,我想知道如何使一个令牌优先于另一个令牌。 不适使用我遇到的一个实际问题作为参考。

我做了两个标记,一个代表TEXT,另一个代表列表。 它们都共享一个字符,这意味着列表也可以阅读为文本。

有没有一种方法可以赋予我想要拥有的令牌某种优先级?

我已经看了很多东西,但是没有发现任何关于这个话题的东西。 我尝试将列表的定义放在文本1的上方,但似乎没有任何改变。

TOKEN: {
    <#DIGIT: ["0"-"9"]>
    <#LETTER: ["a"-"z", "A"-"Z"]>
    <#SYMBOLS: ["@" , "."]>
    <#WORD: (<LETTER>|<DIGIT>|<SYMBOLS>)+>
} 


TOKEN: {
     ...
     <LI:  ((<DIGIT>)+)(".")>
     <TEXT:  <WORD>+ >
     ...
 }

如果我使用它作为LEXER的输入,

1.this is a list

我希望能回来,

LI as 1.
TEXT as this is a list

但是我得到的实际输出是

TEXT is 1.this is a list

谢谢

2 个答案:

答案 0 :(得分:0)

实际上,Javacc使用两个规则来确定优先级,

  

规则1:按声明令牌时发生的顺序排列优先级。

     

规则2:基于优先级的最长匹配。

规则2始终优先于规则1。也就是说,JavaCC始终匹配输入的最长可匹配前缀;然后,如果有多个令牌与最长匹配匹配,则使用规则1来确定要生成哪个令牌。

在您的情况下,您希望应用规则1,但是JavaCC使用规则2,即最长的匹配;因此结果。

此外,TEXT无法匹配this is a list,因为它之间包含空格字符,TEXT的定义不允许这样做。

令牌部分仅用于词法分析。因此,使用生产规则进行语法分析。

答案 1 :(得分:0)

问题是“是否有一种方法可以给我想要[match]的令牌以某种优先级?”

有几种方法。


第一种方法是

  • 列出令牌规则,以使优先级最高的规则排在第一位。

但这仅在两个规则都匹配相同长度的字符串时起作用。

在您的情况下,您希望将“ 1.this”匹配为<LI>,后跟<TEXT>。但是文本规则将匹配6个字符,而<LI>规则将仅匹配2个字符。因此,如FAQ和@sarath的回答所述,<TEXT>规则将获胜。


第二种方式。

  • 使用词法状态禁用您不希望赢得的规则。

这种方法不适用于您的问题,因为您需要同时激活两个规则。


第三种方式。

  • 重写规则,以便在应用较高优先级规则时,较低优先级规则不能应用于较长的字符串。

在您的情况下,您不希望将“ 1234.this”作为<TEXT>进行匹配,以便可以应用<LI>规则。我们将重写<TEXT>规则,以使其不适用于任何这些字符串

"1234.this"
"1234.thi"
"1234.th"
"1234.t"
"1234."

(实际上,我们可以允许最后一个字符串,然后对规则进行排序,以使<LI>优先出现。但是我发现也更容易排除最后一个字符串。)

新令牌将称为<TEXT1>,以避免混淆。因此,我们对<TEXT1>的规则将与任何匹配的字符串

  • 匹配<TEXT>,并且
  • <LI>不匹配,并且
  • 没有与<LI>匹配的前缀。

如果JavaCC为这种情况提供了一种不错的语法,那就太好了,但是事实并非如此。我们必须找到一个正则表达式。

在这种情况下,我喜欢派生正则表达式的方式是从一个正则表达式有限状态识别器(REFR)开始,然后从中派生正则表达式。幻灯片1.2 here中定义了REFR,将REFR转换为正则表达式的算法也是如此。

TEXT1的第一个REFR如下。

    0s ---P|S|L---> 1f
    0s ---D-------> 2f
    1f --P|S|L|D--> 1f
    2f --D--------> 2f
    2f --S|L------> 1f

L表示字母,D表示数字。 P表示周期。 S表示除句号以外的任何符号,即“ at”符号。共有3种状态:0s,1f和2f。最终状态(即接受状态)的名称中带有“ f”。起始状态的名称中带有“ s”。

希望机器的正确性显而易见。 特别要注意的是,在输入D或DD或DDD等之后,机器可以处于的状态集为{2f}。并且,如果机器处于状态2f且下一个字符是P,则机器没有可进入的状态,因此字符串被拒绝。因此,任何与LI匹配或前缀与LI匹配的字符串都将被拒绝。

第一步是将接受状态的数量减少到1,并确保从接受状态没有传出的转换。 (我们也要确保没有进入到起始状态的过渡,但是已经是这种情况。)新机器是

    0s ---P|S|L---> 1
    0s ---D-------> 2
    1  --P|S|L|D--> 1
    1  --epsilon--> 3f
    2  --D--------> 2
    2  --S|L-----> 1
    2  --epsilon--> 3f

下一步消除状态2。

    0s ---P|S|L----> 1
    0s --D+(S|L)---> 1
    0s ---D+-------> 3f
    1  --P|S|L|D---> 1
    1  --epsilon---> 3f

合并从0到1的两个过渡

    0s ---P|S|L|D+(S|L)----> 1
    0s ---D+---------------> 3f
    1  ---P|S|L|D----------> 1
    1  ---epsilon----------> 3f

消除状态1。

    0s ---(P|S|L|D+(S|L)) (P|S|L|D)*---> 3f
    0s ---D+---------------------------> 3f

合并要获取的过渡

     (P|S|L|D+(S|L)) (P|S|L|D)*  | D+
0s--------------------------------------> 3f

最后,我们看一下RE,以了解它实际上是有意义的,并寻求任何简化。

在我看来,这是正确的,我看不到任何简化方法。

我们完成了。