从Jison内部改变词汇状态

时间:2015-10-20 02:09:30

标签: jison

是否有可能从Jison的语法规则中改变词汇状态(又名"开始条件")?

解析一种计算机语言,当某些语法规则得到满足时,词汇状态明显改变(至少符合我的人性思维),即使没有令牌,我也可以在词法分析器中指出。

(之所以我认为这是某些关键字在一个州保留/保留,而不是另一个州。)

绝对有可能从词法分析器中改变词汇状态,例如:

%lex
%x expression

%% 
{id}               {  return 'ID';
"="                { this.begin('expression'); return '='; }
<expression>";"    { this.popState(); return ';'; }

但是当某些语法规则匹配时,有没有办法改变词汇状态?

%% /* language grammar */

something :  pattern1 pattern2 { this.beginState('expression'); $$ = [$1,$2]; };
pattern1 : some stuff { $$ = [$1, $2]; }
pattern2 : other stuff { $$ = [$1, $2]; }

如果我试试这个,我会

TypeError: this.popState is not a function
      at Object.anonymous (eval at createParser (/Users/me/Exp/stats/node_modules/jison/lib/jison.js:1327:23), <anonymous>:47:67)
      at Object.parse (eval at createParser (/Users/me/Exp/stats/node_modules/jison/lib/jison.js:1327:23), <anonymous>:329:36)

我不确定我要求的东西在理论上是不可能的还是在概念上是天真的(例如这是context free grammar的意思吗?),或者它在那里和我&我#39;我只是没有正确阅读文档。

1 个答案:

答案 0 :(得分:4)

词法分析器对象在解析器操作中可用yy.lexer,因此您可以使用yy.lexer.begin('expression');更改开始条件,然后使用yy.lexer.popState()返回旧版本。那部分没有问题。

但是,当新的启动条件生效时,您需要考虑。 LALR(1)解析器(例如由jison(或bison)实现的解析器)使用单个先行令牌来决定要采取的操作。 (&#34; 1&#34;在LALR(1)中是可能前瞻的长度。)这意味着当执行解析器操作时 - 当它附加到的规则减少时 - 下一个令牌具有可能已经读过了。

情况并非总是如此; jison和bison有时可以在不使用超前令牌的情况下进行减少,在这种情况下,他们还没有阅读它。

简而言之,对动作中的词法分析器状态的更改可能在读取下一个标记之前生效,但大多数时候它将在读取第二个下一个标记时生效。由于这种模糊性,通常最好在不受词法分析器状态变化影响的令牌之前进行词法分析器状态更改。

例如,考虑标准计算器。以下示例改编自jison手册:

%lex
%%

\s+                   /* skip whitespace */
[0-9]+\b              yytext=parseInt(yytext); return 'NUMBER'
[*/+%()-]             return yytext[0]
<<EOF>>               return 'EOF'
.                     return 'INVALID'

/lex

%left '+' '-'
%left '*' '/' '%'
%left UMINUS

%start expressions

%% /* language grammar */

expressions: e EOF              {return $1;};

e   : e '+' e                   {$$ = $1+$3;}
    | e '-' e                   {$$ = $1-$3;}
    | e '*' e                   {$$ = $1*$3;}
    | e '/' e                   {$$ = $1/$3;}
    | e '%' e                   {$$ = $1%$3;}
    | '-' e %prec UMINUS        {$$ = -$2;}
    | '(' e ')'                 {$$ = $2;}
    | NUMBER                    {$$ = $1;}
    ;

现在,让我们修改它,以便在 [] 之间将所有数字解释为十六进制。我们使用一个名为HEX的非独占启动条件;启用时,会识别并转换十六进制数字。

%lex
%s HEX
%%

\s+                   /* skip whitespace */
<INITIAL>[0-9]+("."[0-9]+)?\b  yytext=parseInt(yytext); return 'NUMBER'
<HEX>[0-9a-fA-F]+\b            yytext=parseInt(yytext, 16); return 'NUMBER'
[*/+%()[\]-]          return yytext[0]
<<EOF>>               return 'EOF'
.                     return 'INVALID'

/lex

%left '+' '-'
%left '*' '/' '%'
%left UMINUS

%start expressions

%% /* language grammar */

expressions: e EOF              {return $1;};

e   : e '+' e                   {$$ = $1+$3;}
    | e '-' e                   {$$ = $1-$3;}
    | e '*' e                   {$$ = $1*$3;}
    | e '/' e                   {$$ = $1/$3;}
    | e '%' e                   {$$ = $1%$3;}
    | '-' e %prec UMINUS        {$$ = -$2;}
    | '(' e ')'                 {$$ = $2;}
    | hex '[' e unhex ']'       {$$ = $3;}
    | NUMBER                    {$$ = $1;}
    ;

    hex:                        { yy.lexer.begin('HEX'); } ;
    unhex:                      { yy.lexer.popState(); };

在这里,我们使用空的非终端hexunhex来改变词法分析器状态。 (在野牛中,我会使用一个非常相似的中规则动作,但是jison似乎并没有实现它们。)关键是状态变化是在之前完成的[] 令牌,不受状态变化的影响。因此,状态变化是否发生在当前先行令牌之前或之后并不重要,因为我们不需要它直到第二个下一个令牌生效,这可能是一个数字。

在给定输入26的情况下,此语法将正确输出[10+a]。如果我们将hex标记非终端移动到括号内:

      /* NOT CORRECT */
    | '[' hex e unhex ']'       {$$ = $3;}

然后在超前令牌之后发生开始条件更改,以便[10+a]生成20