我正在做我自己的小语言,我试图让有块,但我非常困在如何跟踪当前块上(因为我需要知道变量是在哪个中创建的等等)。
我的语法文件看起来像这样(为了简单起见,它不是整个代码):
%{
struct Node *nodest = NULL;
struct Node *currentblock = NULL;
%}
%start source
%%
source
: stmts { nodest = block($1); currentblock = nodest; }
;
stmts
: stmt
| stmts stmt
;
stmt
: expr_stmt
| iter_stmt
| select_stmt
| comp_stmt
;
expr_stmt
: ';'
| expr ';'
;
expr
: binary_expr
| assign_expr
| call_expr
| decl_expr
| init_expr
| VAR_IDENT
| INTEGER
| '(' expr ')'
;
comp_stmt
: '{' '}'
| '{' stmts '}' { $$ = block($2); currentblock = $$; }
;
decl_expr
: type VAR_IDENT { $$ = declaration($1, $2, currentblock) }
;
/* ... */
type
: TYPE_INT
;
显然,这是行不通的,因为nodest
(作为保存所有其他节点的块节点)实际上在创建 AST 的最后被赋予了一些值,因此它在其余时间都是 NULL,所以currentblock
不能像在 decl_expr
中使用,因为它当时是 NULL。
所以我的问题是:我怎样才能在代码中稍后获取nodest
的值(指向它指向的位置或其他什么)?
或者,如果真的不可能,你能给我一些关于如何做到这一点的笔记/提示吗?
规则中的操作代码在规则减少时执行,因此,如果您希望在流程的早期执行操作,则需要将它们放在较早减少的规则上。 yacc 和 bison 都允许您引入匿名 epsilon 规则,只需在规则的右侧前面放置一个额外的操作即可。 因此,您可以执行以下操作:
source: { $$ = currentblock = nodest = empty_block(); } stmts
{ $$ = add_to_block($1, $2); }
comp_stmt:
'{' { $$ = currentblock = empty_block(); } stmts '}'
{ $$ = add_to_block($2, $3); }
;
请注意,当你像这样早期创建块时,你必须将它们创建为空(因为你还没有解析任何进入块的内容),然后稍后向它们添加内容。 在解析当前块时将每个 stmt 添加到当前块可能更有意义(在这种情况下,您需要确保当前块确实是当前块而不是最后一个块:
source: { currentblock = nodest = empty_block(); } stmts ;
stmts: /* empty*/
| stmts stmt { add_to_block(currentblock, $2; }
comp_stmt:
'{' { $$ = currentblock; currentblock = empty_block(); } stmts '}'
{ $$ = currentblock; currentblock = $2 }
;