是否有可能从json的语法规则中更改词法状态(即"开始条件")?
我正在解析一种计算机语言,当满足某些语法规则时,它的词法状态会明显改变(至少在我的人类思维方式中),即使在词法分析器中没有我可以精确指向的标记。
(我认为这是因为某些关键字在一种状态下是保留的/可保留的,而在另一种状态下则不是)
绝对可以在词法分析器中更改词法状态,例如:
%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
的含义吗?),或者它就在那里,我只是没有正确阅读文档。
lexer对象在解析器操作中作为yy.lexer
可用,因此您可以使用yy.lexer.begin('expression');
更改启动条件并返回到yy.lexer.popState()
的旧条件。那部分没有问题。
但是,当新的启动条件生效时,需要考虑。LALR(1)解析器,例如由jison(或bison)实现的解析器,使用单个向前看令牌来决定采取什么操作。(LALR(1)中的"1"是可能的forward的长度。)这意味着当一个解析器动作被执行时——当它所附加的规则被缩减时——下一个令牌可能已经被读取了。
情况并非总是如此;jison和bison有时都可以在不使用forward标记的情况下进行约简,在这种情况下,它们还没有读取它。
简而言之,操作中对词法分析器状态的更改可能在读取下一个令牌之前生效,但大多数情况下,它将在读取第二个令牌时生效。由于这种模糊性,通常最好在不受词法分析器状态更改影响的令牌之前更改词法分析器状态。
以标准计算器为例。下面的例子改编自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
来改变词法分析器状态。(在bison中,我会使用中间规则操作,这非常相似,但jison似乎没有执行它们。)关键是状态更改是在[和]令牌之前完成的,它们不受状态更改的影响。因此,状态改变发生在当前的前瞻令牌之前还是之后并不重要,因为我们不需要它在下一个令牌之前生效,这个令牌可能是一个数字。
给定输入[10+a]
,该语法将正确输出26
。如果我们将hex
标记移到括号内:
/* NOT CORRECT */
| '[' hex e unhex ']' {$$ = $3;}
则开始条件更改发生在forward令牌之后,因此[10+a]
产生20
。