[英]jison grammar definition leads to wrong token recognition
我最近找到了项目jison,并从其网站上修改了计算器示例。 ( http://zaach.github.io/jison/demos/calc/ )
/* lexical grammar */
%lex
%%
"a" return 'TOKEN1'
"b" return 'TOKEN2'
<<EOF>> return 'EOF'
. return 'INVALID'
/lex
%start letters
%% /* language grammar */
letters
:
| letters letter
;
letter
: 'TOKEN1'
| 'TOKEN2'
;
使用上述语法定义生成的解析器解析字符串“ aaabbbaaba”,结果是
Parse error on line 1:
aaabbbaaba
^
Expecting 'TOKEN1', 'TOKEN2', got 'INVALID'
不幸的是,我不知道为什么TOKEN1
。 删除令牌无效后,我得到了解析错误
Unrecognized text.
我发现了有关关联错误的描述,导致了类似的错误消息,有关生成dparser的Jison语法问题,奇怪错误的问题,但我在代码中找不到相似的内容。
这个问题有什么解决方案?
好问题。
jison
的lexer生成器有两种模式:默认模式和与flex
兼容的模式。 您可以通过将%options flex
放在%lex
行之后来选择后者。
在默认模式下:
即使后面的模式匹配更长的令牌,第一个匹配的模式也会获胜; 和
以字母或数字结尾的模式上添加了隐式\\b
,这限制了匹配以单词边界结尾。
在flex
模式下,样式不会更改,并且正常的伸缩第一长规则适用。 但是,生成的词法分析器会更慢(请参见下文)。
因此,在您的词法分析器定义中, "a"
将不匹配输入字符串中的第一个a ,因为生成的词法分析器实际上正在尝试匹配a\\b
,即a与后跟单词边界。
您可以通过用括号将模式括起来来解决此问题:
("a") { return 'TOKEN1'; }
或使用字符类而不是带引号的字母:
[a] { return 'TOKEN1'; }
或通过将%options flex
添加到您的%lex
部分。
与flex
不同, jison
不会构建单个DFA词法分析器。 而是将每个lex模式转换为锚定的javascript正则表达式,并在每次请求令牌时尝试所有模式,直到找到正确的匹配项。
要实现flex
jison
匹配规则,由jison
生成的词法分析器需要为每个令牌尝试每个正则表达式,因为在尝试全部匹配之前,它不知道哪个匹配最长。 “首次匹配”规则可以更快一些,特别是如果将通用令牌模式放在文件开头附近。
不幸的是,在令牌可能是关键字或标识符且碰巧以关键字开头的标识符需要作为标识符进行匹配的常见情况下,首次匹配规则很难使用。 使用首长匹配,将关键字放在首位就足够了,因为带有关键字前缀的标识符会更长。 但是,对于首个匹配项,必须限制关键字或标识符或两者都以单词边界结尾。
因此,上述两个规则相结合,这意味着在“ Identifier
模式之前列出关键字的常规模式仍将起作用。 单词边界测试可防止关键字模式出现虚假前缀匹配。
但是,如果您有很多关键字,尽管其中大多数会很快失败,但仍然有很多模式。 因此,而不是使用flex
约定:
"do" { return DO; }
"end" { return END; }
/* ... */
[[:alpha:]][[:alnum:]_]* { return "ID"; }
让关键字代表自己(以及其他固定标记,如运算符)要好得多,因为它使您可以将所有关键字和多字符运算符模式组合到单个正则表达式中:
/* Keywords and multicharacter operators in a single enormous pattern */
/* For jison mode, I added a manual \b because it won't be added
* automatically. In flex mode, that won't hurt, but it could be
* removed.
*/
("do"|"else"|"end"|"if"|"then"|"while")\b|[<>!=]"=" { return yytext; }
[[:alpha:]][[:alnum:]_]* { return "ID"; }
[[:digit:]]+("."[[:digit:]]*)? { return "NUMBER"; }
[[:space:]]+ ;
/* All single character tokens use a fallback rule */
. { return yytext; }
<<EOF>>
规则 许多jison
语法具有明确的<<EOF>>
词汇规则,该规则返回诸如"EOF"
或"END_OF_FILE"
类的标记。 此令牌由显式增强的初始生产识别,该生产已return
其操作:
%lex
%%
// ...
<<EOF>> { return "EOF"; }
/lex
%start input
%%
input: start EOF { return $1; }
start: /* The real start token */
这是特定于jison
习惯用法,许多人认为flex/bison
样式较差。 它允许生成的语法返回实际值,该实际值是解析的结果。
不要只将<<EOF>>
规则添加到词汇规则中。 如果您提供自己的EOF
令牌,则有责任在解析器中对其进行识别。 如果解析器没有与EOF
令牌匹配的规则,则解析将失败。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.