让我用一个具体的例子来阐明我的问题:
首先,我有一个令牌:
<T_SELECT:"SELECT">
第二,我有一个正则表达式标记:
<T_TABLE_NAME:~[","]>
第三,我有一部作品:
<T_SELECT> <T_TABLE_NAME> (","<T_TABLE_NAME>)*
最后,我想解析如下字符串:
SELECT TABLE1
我的问题是:
而不是先消耗SELECT
然后再消耗TABLE1
,JavaCC最终将SELECT TABLE1
当作<T_TABLE_NAME>
!
很明显,我期望它消耗SELECT
,因为我定义了一个名为<T_SELECT: "SELECT">
的令牌
我该怎么做才能告诉JavaCC使用我定义的确切字符串而不是正则表达式?
答案 0 :(得分:1)
这几乎肯定不是您要执行的操作,因为它将把selection
变成select
ion
。
~[","]
将匹配逗号以外的任何字符,包括空格和换行符。这也不对,因为表名不是以(或包含)空格字符开头。
您需要匹配并忽略空格,并将名称限制为合法名称字符。
答案 1 :(得分:1)
我假设您唯一的词汇规则就是这些
TOKEN { <T_SELECT:"SELECT"> }
TOKEN { <T_TABLE_NAME:~[","]> }
TOKEN { <T_COMMA: "," > }
第三个规则是隐式的,因为语法中包含字符串“,”。我在这里明确指出。现在,如果要解析的字符序列是
"SELECT TABLE1" ,
序列将按以下顺序分类
即:
T_SELECT("SELECT")
T_TABLE_NAME(" ")
T_TABLE_NAME("T")
T_TABLE_NAME("A")
...
T_TABLE_NAME("1")
EOF
如果您的语法由一个规则组成
void select() : {} {
<T_SELECT>
<T_TABLE_NAME>
(<T_COMMA> <T_TABLE_NAME>)*
} ,
然后只有前两个标记会被匹配。
如果您的语法由一个规则组成
void select() : {} {
<T_SELECT>
<T_TABLE_NAME>
(<T_COMMA> <T_TABLE_NAME>)*
<EOF> // I added an <EOF>
} ,
然后,示例输入出现语法错误,解析器将引发异常。对于第三个令牌,解析器期望的是T_COMMA
或EOF
,但是它得到的是T_TABLE_NAME
。
我将做更多的假设。
SELECT
查询。SELECT
查询。SELECT
关键字仅在表名之前使用。这些可能是不正确的假设,但我需要做一些 假设才能理解这个问题。
作为第一个更改,我将允许表名称为任意长度。并且我将禁止在表名和逗号中使用换行符。我将制作一个新令牌来处理任何换行符。词汇规则现在是
TOKEN { <T_SELECT:"SELECT"> }
TOKEN { <T_TABLE_NAME: (~[",","\n","\r"])* > }
TOKEN { <T_COMMA: "," > }
TOKEN { <T_NEWLINE: ("\n" | "\n\r" | "\r") > }
现在会发生什么?再次假设输入字符串为
"SELECT TABLE1" ,
存在与规则匹配的长度为6的前缀
<T_SELECT:"SELECT">
并且有几个(实际上是14个)匹配规则的前缀
<T_TABLE_NAME: (~[",","\n","\r"])* >
由于长度6的前缀都被这两个规则匹配,因此总共有14个前缀可以被至少一个规则匹配。
在这14个前缀中,词法分析器将始终选择最长的前缀。这是最大的咀嚼规则。因此令牌的顺序将是
T_TABLE_NAME("SELECT TABLE1") EOF
无法关闭最大规则。您无法赢得这场比赛。你必须作弊。
作弊的方法是参加比赛。将T_TABLE_NAME
规则移到另一个词法状态。
您要在lexer看到SELECT关键字之后切换状态。
这样编写词法分析器规则:
TOKEN : { <T_SELECT: "SELECT"> : S_TABLE_NAME }
<S_TABLE_NAME> TOKEN :
{ <T_TABLE_NAME:(~[",","\n","\r"])*> : S_TABLE_NAME
| <T_COMMA: "," } : S_TABLE_NAME
| <T_NEWLINE: ("\n" | "\n\r" | "\r") : DEFAULT }
现在您的作品将成为
void Select() : {}
{ <T_SELECT>
<T_TABLE_NAME>
( <T_COMMA> <T_TABLE_NAME>)*
( <T_NEWLINE> )?
<EOF>
}
如果输入为
"SELECT ABC, DEF\n" ,
它将按照以下方式分类
State Token Produced
------------------------------
DEFAULT T_SELECT("SELECT")
S_TABLE_NAME T_TABLE_NAME( " ABC" )
S_TABLE_NAME T_COMMA( "," )
S_TABLE_NAME T_TABLE_NAME( " DEF" )
S_TABLE_NAME T_NEWLINE( "\n" )
DEFAULT EOF.
它将解析无误。