jison语法定义导致错误的令牌识别

时间:2015-02-20 21:13:29

标签: parsing yacc flex-lexer jison

我最近找到了项目 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。删除令牌INVALID后,我得到解析错误

Unrecognized text.

我在Issue with a Jison Grammar, Strange error from generate dparser上找到了关联错误的描述,导致类似的错误消息,但我在代码中找不到类似的内容。

这个问题的解决方案是什么?

1 个答案:

答案 0 :(得分:3)

好问题。

jison的词法分析器生成器有两种模式:默认模式和稍微多flex - 兼容模式。您可以在%options flex行之后放置%lex来选择后者。

在默认模式下:

  1. 第一个匹配模式获胜,即使后面的模式与更长的令牌匹配也是如此;以及

  2. 以字母或数字结尾的模式添加了隐式\b,这限制了匹配在字边界上结束。

  3. flex模式下,模式不会更改,并且适用常规弹性第一长规则。但是,生成的词法分析器会更慢(见下文)。

    因此,在词法分析器定义中,"a"将与输入字符串中的第一个 a 不匹配,因为生成的词法分析器实际上正在尝试匹配a\b - 这是, a 后跟单词边界。

    您可以通过简单地用括号括起模式来解决问题:

    ("a")    { return 'TOKEN1'; }
    

    或使用字符类而不是引用字母:

    [a]      { return 'TOKEN1'; }
    

    或将%options flex添加到%lex部分。


    作为解释

    jison不同,

    flex不构建单个DFA词法分析器。相反,它将每个lex模式转换为锚定的javascript正则表达式,并且在每次请求令牌时,它会尝试所有模式,直到找到正确的匹配。

    要实现flex第一长匹配规则,jison生成的词法分析器需要尝试每个令牌的每个正则表达式,因为它无法知道哪个是最长的匹配,直到它尝试了所有。 &#34;第一场比赛&#34;规则可以快得多,特别是如果常见的令牌模式放在文件的开头附近。

    不幸的是,在令牌可能是关键字或标识符的常见情况下,首先匹配规则更难处理,并且恰好以关键字开头的标识符需要作为标识符进行匹配。使用第一长匹配时,将关键字放在第一位就足够了,因为带有关键字前缀的标识符会更长。但是,通过第一场比赛,必须限制关键字或标识符或两者都以字边界结束。

    因此,上述两个规则的组合,这意味着在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标记匹配的规则,则解析将失败。