Semantic Actions

Semantic actions are code blocks attached to grammar rules that execute when the rule is reduced.

Basic Syntax

expr: expr PLUS expr    { $$ = $1 + $3; }
    | NUMBER            { $$ = $1; }
    ;

Value Stack References

  • $$: The result value (left-hand side)
  • $1, $2, $3, ...: Values of right-hand side symbols
/* Rule: factor -> LPAREN expr RPAREN */
factor: LPAREN expr RPAREN  { $$ = $2; }
       /* $1=LPAREN, $2=expr, $3=RPAREN */
    ;

Typed Actions

With %union, values have specific types:

%union {
    int ival;
    double dval;
    struct node *ast;
}

%type <dval> expr term factor

%%

expr: expr PLUS term    { $$ = $1 + $3; }
    ;

Multi-Statement Actions

Braces contain the full action:

stmt: PRINT expr SEMICOLON {
        printf("Value: %g\n", $2);
        $$ = $2;
    }
    ;

Mid-Rule Actions

Actions can appear in the middle of a rule:

compound: LBRACE { enter_scope(); } stmt_list RBRACE { leave_scope(); }
    ;

Mid-rule actions count as symbols:

rule: A { mid_action(); } B C
    /* $1=A, $2=mid_action result, $3=B, $4=C */
    ;

AST Construction

Building abstract syntax trees:

%union {
    struct node *ast;
}

%type <ast> expr term factor

%%

expr: expr PLUS term {
        $$ = make_binary_node(OP_ADD, $1, $3);
    }
    | term {
        $$ = $1;
    }
    ;

Language-Specific Actions

C

expr: expr PLUS expr    { $$ = $1 + $3; }
    ;

Python

expr: expr PLUS expr    { self.yyval = self.yyvs[-3] + self.yyvs[-1] }
    ;

Java

expr: expr PLUS expr    { yyval = yyvs[yyvsp-2] + yyvs[yyvsp]; }
    ;

Empty Actions

Rules without explicit actions get a default that copies $1 to $$:

term: factor    /* implicit: { $$ = $1; } */
    ;