compiler-construction - JavaCC中,如何降低正则表达式的优先级?
问题描述
让我用一个具体的例子来澄清我的问题:
首先,我有一个令牌:
<T_SELECT:"SELECT">
其次,我有一个正则表达式标记:
<T_TABLE_NAME:~[","]>
三、我有一个制作:
<T_SELECT> <T_TABLE_NAME> (","<T_TABLE_NAME>)*
Finnaly,我想解析这样的字符串:
SELECT TABLE1
我的问题是: JavaCC 最终以as代替,SELECT
而不是 consume !TABLE1
SELECT TABLE1
<T_TABLE_NAME>
显然我期待它会消耗殆尽SELECT
,因为我已经定义了一个名为<T_SELECT: "SELECT">
我可以做些什么来告诉 JavaCC 使用我定义的确切字符串而不是正则表达式?
解决方案
我假设您拥有的唯一词汇规则是这些
TOKEN { <T_SELECT:"SELECT"> }
TOKEN { <T_TABLE_NAME:~[","]> }
TOKEN { <T_COMMA: "," > }
第三条规则是隐含的,因为您的语法中有字符串“,”。我在这里明确表示。现在如果要解析的字符序列是
"SELECT TABLE1" ,
该序列将按如下方式进行词法分析
- 首先找到一个 T_SELECT。
- 然后是 7 个 T_TABLE_NAME 令牌。
- 然后是一个 EOF 令牌。
IE:
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>
} ,
那么示例输入有语法错误,解析器将抛出异常。对于第三个标记,解析器期望 aT_COMMA
或 an EOF
,但它得到 a T_TABLE_NAME
。
我将做更多的假设。
- 首先,我将假设表名可以包含 0 个或多个字符,而不是 OP 中的 1 个字符。
- 其次,我将假设表名不能包含换行符或换行符。否则,除了点击文件末尾之外,将无法终止
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
规则移动到不同的词法状态。
您想在词法分析器看到 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.
它会毫无错误地解析。
推荐阅读
- neural-network - 导入新应用后,LUIS 是否会保留以前的培训?
- html - 使用 CSS Grid 时内容不会在移动设备中换行
- sql - 我有一个 WITH 子句不会按预期运行
- html - 图像标题和替代文本的最佳实践以实现可访问性?
- c# - 使用 C# 从 newtonsoft JSON 中提取嵌套值
- python - 如何使用 matplotlib 将半极坐标图设置为北
- javascript - 返回的 Promise 有什么区别?
- php - laravel 调度在 Windows 中不起作用
- firebase - 错误:无法将“AuthResult”类型的值分配给“FirebaseUser”类型的变量
- android - OneSignal 推送通知 | 在聊天时禁用它们进行聊天| 安卓