2

我正在做我自己的小语言,我正在尝试制作块,但我非常坚持如何跟踪当前块(因为我需要知道变量是在哪个块中创建的,所以 -等等等等)。

我的语法文件看起来像这样(为简单起见,它不是整个代码):

%{
    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不能使用像 in decl_expr,因为它当时是 NULL 。

nodest所以我的问题是:我怎样才能在代码中获得稍后的值(指向它指向的位置或其他) ?

或者,如果这不是真的可能,您能否给我一些说明/提示我如何才能做到这一点?

4

1 回答 1

1

规则中的操作代码在规则缩减时执行,因此如果您希望在流程中更早地执行操作,则需要将它们放在更早缩减的规则上。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 添加到当前块可能更有意义(在这种情况下,您需要确保 currentblock 确实是当前块而不是最后一个块:

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 }
;
于 2012-11-28T22:50:29.953 回答