是否有可能在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
的含义?),或者它在那里,我只是没有正确阅读文档。
词法分析器对象在解析器操作中可作为yy.lexer
使用,因此您可以使用yy.lexer.begin('expression');
更改开始条件,然后使用yy.lexer.popState()
返回到原来的条件。这部分没有问题。
但是,您需要考虑何时新的开始条件将生效。 LALR(1)解析器(例如,由jison(或野牛)实现的解析器)使用单个前瞻标记来决定要采取的操作。 (LALR(1)中的“ 1”是可能提前的长度。)这意味着当执行解析器操作时(当附加到该规则的规则减少时),下一个标记可能已经被读取。
并非总是如此;吉森和野牛有时都可以在不使用先行标记的情况下进行还原,在这种情况下,他们将尚未阅读该标记。
简而言之,在动作[[might]中对词法分析器状态的更改]在读取下一个标记之前生效,但是大多数情况下,在读取下一个标记之后才生效。由于这种歧义,通常最好在不受词法分析器状态更改影响的令牌之前对词法分析器状态进行更改。例如,考虑标准计算器。以下示例摘自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(); } ;
这里,我们使用空的非端子hex
和unhex
来更改词法分析器状态。 (在野牛中,我会使用中间规则操作,该操作非常相似,但是吉森似乎没有实现它们。)关键是状态更改是在[和令牌,不受状态更改的影响。因此,状态更改是在当前先行标记之前还是之后都没关系,因为我们不需要它在第二个下一个标记(可能是数字)之前生效。给定输入]之前完成的
26
,此语法将正确输出[10+a]
。如果我们将hex
标记的非末端移动到方括号内: /* NOT CORRECT */
| '[' hex e unhex ']' {$$ = $3;}
然后,开始条件更改发生在超前标记之后,因此[10+a]
产生20
。