我想对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
谢谢
答案 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,以了解它实际上是有意义的,并寻求任何简化。
在我看来,这是正确的,我看不到任何简化方法。
我们完成了。