1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341
|
%{
/*
* ===========================================================================
* Copyright (C) 2025 the OpenMoHAA team
*
* This file is part of OpenMoHAA source code.
*
* OpenMoHAA source code is free software; you can redistribute it
* and/or modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the License,
* or (at your option) any later version.
*
* OpenMoHAA source code is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with OpenMoHAA source code; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
* ===========================================================================
*
*
* yyParser.*: BISON Parser for MoHScript.
*/
#include "scriptcompiler.h"
#include "./yyParser.hpp"
#include "./yyLexer.h"
// the value was increased because some scripts have a lot of if/else statements
// which ends up with an high depth
#define YYINITDEPTH 500
int yyerror( const char *msg );
extern int prev_yylex;
extern yyparsedata parsedata;
int prev_yylex;
int out_pos;
int success_pos;
#define YYLLOC node_pos(success_pos - yyleng)
#define TOKPOS(pos) node_pos(pos.sourcePos)
%}
%define parse.error verbose
%locations
%define api.location.type { parse_pos_t }
%expect 123
%precedence TOKEN_EOF 0 "end of file"
%precedence TOKEN_EOL
%union {
stype_t s;
}
%left TOKEN_COMMA
%precedence TOKEN_IF
%right THEN TOKEN_ELSE
%precedence TOKEN_WHILE TOKEN_FOR TOKEN_DO
%precedence <s.val> TOKEN_IDENTIFIER
%precedence TOKEN_LEFT_BRACES TOKEN_RIGHT_BRACES
%left TOKEN_LEFT_BRACKET TOKEN_RIGHT_BRACKET
%token TOKEN_LEFT_SQUARE_BRACKET TOKEN_RIGHT_SQUARE_BRACKET
%right TOKEN_ASSIGNMENT
TOKEN_PLUS_EQUALS TOKEN_MINUS_EQUALS TOKEN_MULTIPLY_EQUALS TOKEN_DIVIDE_EQUALS TOKEN_MODULUS_EQUALS
TOKEN_AND_EQUALS TOKEN_EXCL_OR_EQUALS TOKEN_OR_EQUALS
TOKEN_SHIFT_LEFT_EQUALS TOKEN_SHIFT_RIGHT_EQUALS
TOKEN_TERNARY TOKEN_COLON
%left TOKEN_LOGICAL_OR
%left TOKEN_LOGICAL_AND
%left TOKEN_BITWISE_OR
%left TOKEN_BITWISE_EXCL_OR
%left TOKEN_BITWISE_AND
%left TOKEN_EQUALITY TOKEN_INEQUALITY
%left TOKEN_LESS_THAN TOKEN_LESS_THAN_OR_EQUAL TOKEN_GREATER_THAN TOKEN_GREATER_THAN_OR_EQUAL
%left TOKEN_SHIFT_LEFT TOKEN_SHIFT_RIGHT
%left TOKEN_PLUS TOKEN_MINUS
%left TOKEN_MULTIPLY TOKEN_DIVIDE TOKEN_MODULUS
%token TOKEN_NEG TOKEN_NOT TOKEN_COMPLEMENT
%precedence <s.val> TOKEN_STRING
%precedence <s.val> TOKEN_INTEGER
%precedence <s.val> TOKEN_FLOAT
%precedence <s.val> TOKEN_LISTENER
%precedence TOKEN_NIL TOKEN_NULL
%left TOKEN_DOUBLE_COLON
%left TOKEN_SEMICOLON
%right TOKEN_DOLLAR
%token TOKEN_INCREMENT TOKEN_DECREMENT TOKEN_PERIOD
%right TOKEN_INCREMENT TOKEN_DECREMENT TOKEN_NEG TOKEN_NOT TOKEN_COMPLEMENT
%left TOKEN_LEFT_SQUARE_BRACKET TOKEN_RIGHT_SQUARE_BRACKET TOKEN_PERIOD
%precedence TOKEN_CATCH TOKEN_TRY
TOKEN_SWITCH TOKEN_CASE
TOKEN_BREAK TOKEN_CONTINUE
TOKEN_SIZE
TOKEN_END TOKEN_RETURN
TOKEN_MAKEARRAY TOKEN_ENDARRAY
%type <s.val> event_parameter_list event_parameter_list_need event_parameter
%type <s.val> statement_list statement statement_declaration makearray_statement_list makearray_statement statement_for_condition
%type <s.val> compound_statement selection_statement iteration_statement
%type <s.val> expr
%type <s.val> func_prim_expr
%type <s.val> prim_expr
%type <s.val> listener_identifier
%type <s.val> nonident_prim_expr
%type <s.val> nonident_prim_expr_base
%type <s.val> const_array_list
%type <s.val> const_array
%type <s.val> identifier_prim
%type <s.val> identifier
%start program
%%
program
: statement_list[list] { parsedata.val = node1(ENUM_statement_list, $list); }
| line_opt { parsedata.val = node0(ENUM_NOP); }
;
statement_list
: statement { $$ = linked_list_end($1); }
| statement_list statement[stmt] { $$ = append_node($1, $stmt); }
;
statement
: line_opt statement_declaration[stmt_decl] line_opt { $$ = $stmt_decl; }
;
statement_declaration
: TOKEN_IDENTIFIER event_parameter_list TOKEN_COLON { $$ = node3(ENUM_labeled_statement, $1, $2, TOKPOS(@1)); }
| TOKEN_CASE prim_expr event_parameter_list TOKEN_COLON { $$ = node3(ENUM_int_labeled_statement, $2, $3, TOKPOS(@1)); }
| compound_statement
| selection_statement
| iteration_statement
| TOKEN_TRY compound_statement[C1] TOKEN_CATCH compound_statement[C2] { $$ = node3(ENUM_try, $C1, $C2, TOKPOS(@1)); }
| TOKEN_BREAK { $$ = node1(ENUM_break, TOKPOS(@1)); }
| TOKEN_CONTINUE { $$ = node1(ENUM_continue, TOKPOS(@1)); }
| TOKEN_IDENTIFIER event_parameter_list { $$ = node3(ENUM_cmd_event_statement, $1, $2, TOKPOS(@1)); }
| nonident_prim_expr TOKEN_IDENTIFIER event_parameter_list { $$ = node4(ENUM_method_event_statement, $1, $2, $3, TOKPOS(@2)); }
| nonident_prim_expr TOKEN_ASSIGNMENT expr { $$ = node3(ENUM_assignment_statement, $1, $3, TOKPOS(@2)); }
| nonident_prim_expr TOKEN_PLUS_EQUALS expr { $$ = node3(ENUM_assignment_statement, $1, node4(ENUM_func2_expr, node1b(OP_BIN_PLUS), $1, $3, TOKPOS(@2)), TOKPOS(@2)); }
| nonident_prim_expr TOKEN_MINUS_EQUALS expr { $$ = node3(ENUM_assignment_statement, $1, node4(ENUM_func2_expr, node1b(OP_BIN_MINUS), $1, $3, TOKPOS(@2)), TOKPOS(@2)); }
| nonident_prim_expr TOKEN_MULTIPLY_EQUALS expr { $$ = node3(ENUM_assignment_statement, $1, node4(ENUM_func2_expr, node1b(OP_BIN_MULTIPLY), $1, $3, TOKPOS(@2)), TOKPOS(@2)); }
| nonident_prim_expr TOKEN_DIVIDE_EQUALS expr { $$ = node3(ENUM_assignment_statement, $1, node4(ENUM_func2_expr, node1b(OP_BIN_DIVIDE), $1, $3, TOKPOS(@2)), TOKPOS(@2)); }
| nonident_prim_expr TOKEN_MODULUS_EQUALS expr { $$ = node3(ENUM_assignment_statement, $1, node4(ENUM_func2_expr, node1b(OP_BIN_PERCENTAGE), $1, $3, TOKPOS(@2)), TOKPOS(@2)); }
| nonident_prim_expr TOKEN_AND_EQUALS expr { $$ = node3(ENUM_assignment_statement, $1, node4(ENUM_func2_expr, node1b(OP_BIN_BITWISE_AND), $1, $3, TOKPOS(@2)), TOKPOS(@2)); }
| nonident_prim_expr TOKEN_EXCL_OR_EQUALS expr { $$ = node3(ENUM_assignment_statement, $1, node4(ENUM_func2_expr, node1b(OP_BIN_BITWISE_EXCL_OR), $1, $3, TOKPOS(@2)), TOKPOS(@2)); }
| nonident_prim_expr TOKEN_OR_EQUALS expr { $$ = node3(ENUM_assignment_statement, $1, node4(ENUM_func2_expr, node1b(OP_BIN_BITWISE_OR), $1, $3, TOKPOS(@2)), TOKPOS(@2)); }
| nonident_prim_expr TOKEN_SHIFT_LEFT_EQUALS expr { $$ = node3(ENUM_assignment_statement, $1, node4(ENUM_func2_expr, node1b(OP_BIN_SHIFT_LEFT), $1, $3, TOKPOS(@2)), TOKPOS(@2)); }
| nonident_prim_expr TOKEN_SHIFT_RIGHT_EQUALS expr { $$ = node3(ENUM_assignment_statement, $1, node4(ENUM_func2_expr, node1b(OP_BIN_SHIFT_RIGHT), $1, $3, TOKPOS(@2)), TOKPOS(@2)); }
| nonident_prim_expr TOKEN_INCREMENT { $$ = node3(ENUM_assignment_statement, $1, node3(ENUM_func1_expr, node1b(OP_UN_INC), $1, TOKPOS(@2)), TOKPOS(@2)); }
| nonident_prim_expr TOKEN_DECREMENT { $$ = node3(ENUM_assignment_statement, $1, node3(ENUM_func1_expr, node1b(OP_UN_DEC), $1, TOKPOS(@2)), TOKPOS(@2)); }
| TOKEN_SEMICOLON { $$ = node0(ENUM_NOP); }
//| TOKEN_IDENTIFIER TOKEN_DOUBLE_COLON TOKEN_IDENTIFIER event_parameter_list { $$ = node3( ENUM_method_event_statement, node_string( parsetree_string( str( $1.stringValue ) + "::" + $3.stringValue ) ), node1( ENUM_NOP, $4 ), TOKPOS(@1) ); }
//| nonident_prim_expr TOKEN_IDENTIFIER TOKEN_DOUBLE_COLON TOKEN_IDENTIFIER event_parameter_list { $$ = node4( ENUM_method_event_statement, $1, node_string( parsetree_string( str( $2.stringValue ) + "::" + $4.stringValue ) ), node1( ENUM_NOP, $5 ), TOKPOS(@2) ); }
;
statement_for_condition
: line_opt statement_declaration[stmt_decl] line_opt { $$ = $stmt_decl; }
| line_opt statement_declaration[stmt_decl] TOKEN_SEMICOLON line_opt { $$ = $stmt_decl; }
;
compound_statement
: TOKEN_LEFT_BRACES statement_list TOKEN_RIGHT_BRACES { $$ = node1(ENUM_statement_list, $2); }
| TOKEN_LEFT_BRACES line_opt TOKEN_RIGHT_BRACES { $$ = node0(ENUM_NOP); }
| line_opt compound_statement[comp_stmt] line_opt { $$ = $comp_stmt; }
;
selection_statement
: TOKEN_IF prim_expr[exp] statement_for_condition[stmt] %prec THEN { $$ = node3(ENUM_if_statement, $exp, $stmt, TOKPOS(@1)); }
| TOKEN_IF prim_expr[exp] statement_for_condition[if_stmt] TOKEN_ELSE statement_for_condition[else_stmt] { $$ = node4(ENUM_if_else_statement, $exp, $if_stmt, $else_stmt, TOKPOS(@1)); }
| TOKEN_SWITCH prim_expr[exp] compound_statement[comp_stmt] { $$ = node3(ENUM_switch, $exp, $comp_stmt, TOKPOS(@1)); }
;
iteration_statement
: TOKEN_WHILE prim_expr[exp] statement_for_condition[stmt]{ $$ = node4(ENUM_while_statement, $exp, $stmt, node0(ENUM_NOP), TOKPOS(@1)); }
| TOKEN_FOR TOKEN_LEFT_BRACKET statement[init_stmt] TOKEN_SEMICOLON expr[exp] TOKEN_SEMICOLON statement_list[inc_stmt] TOKEN_RIGHT_BRACKET statement_for_condition[stmt]
{
sval_t while_stmt = node4(ENUM_while_statement, $exp, $stmt, node1(ENUM_statement_list, $inc_stmt), TOKPOS(@1));
$$ = node1(ENUM_statement_list, append_node(linked_list_end($init_stmt), while_stmt));
}
| TOKEN_FOR TOKEN_LEFT_BRACKET TOKEN_SEMICOLON expr[exp] TOKEN_SEMICOLON statement_list[inc_stmt] TOKEN_RIGHT_BRACKET statement_for_condition[stmt]
{
$$ = node4(ENUM_while_statement, $exp, $stmt, node1(ENUM_statement_list, $inc_stmt), TOKPOS(@1));
}
| TOKEN_DO statement_for_condition[stmt] TOKEN_WHILE prim_expr[exp]{ $$ = node3(ENUM_do, $stmt, $exp, TOKPOS(@1)); }
;
expr:
expr TOKEN_LOGICAL_AND expr { $$ = node3( ENUM_logical_and, $1, $3, TOKPOS(@2) ); }
| expr TOKEN_LOGICAL_OR expr { $$ = node3( ENUM_logical_or, $1, $3, TOKPOS(@2) ); }
| expr TOKEN_BITWISE_AND expr { $$ = node4( ENUM_func2_expr, node1b( OP_BIN_BITWISE_AND ), $1, $3, TOKPOS(@2) ); }
| expr TOKEN_BITWISE_OR expr { $$ = node4( ENUM_func2_expr, node1b( OP_BIN_BITWISE_OR ), $1, $3, TOKPOS(@2) ); }
| expr TOKEN_BITWISE_EXCL_OR expr { $$ = node4( ENUM_func2_expr, node1b( OP_BIN_BITWISE_EXCL_OR ), $1, $3, TOKPOS(@2) ); }
| expr TOKEN_EQUALITY expr { $$ = node4( ENUM_func2_expr, node1b( OP_BIN_EQUALITY ), $1, $3, TOKPOS(@2) ); }
| expr TOKEN_INEQUALITY expr { $$ = node4( ENUM_func2_expr, node1b( OP_BIN_INEQUALITY ), $1, $3, TOKPOS(@2) ); }
| expr TOKEN_LESS_THAN expr { $$ = node4( ENUM_func2_expr, node1b( OP_BIN_LESS_THAN ), $1, $3, TOKPOS(@2) ); }
| expr TOKEN_GREATER_THAN expr { $$ = node4( ENUM_func2_expr, node1b( OP_BIN_GREATER_THAN ), $1, $3, TOKPOS(@2) ); }
| expr TOKEN_LESS_THAN_OR_EQUAL expr { $$ = node4( ENUM_func2_expr, node1b( OP_BIN_LESS_THAN_OR_EQUAL ), $1, $3, TOKPOS(@2) ); }
| expr TOKEN_GREATER_THAN_OR_EQUAL expr { $$ = node4( ENUM_func2_expr, node1b( OP_BIN_GREATER_THAN_OR_EQUAL ), $1, $3, TOKPOS(@2) ); }
| expr TOKEN_PLUS expr { $$ = node4( ENUM_func2_expr, node1b( OP_BIN_PLUS ), $1, $3, TOKPOS(@2) ); }
| expr TOKEN_MINUS expr { $$ = node4( ENUM_func2_expr, node1b( OP_BIN_MINUS ), $1, $3, TOKPOS(@2) ); }
| expr TOKEN_MULTIPLY expr { $$ = node4( ENUM_func2_expr, node1b( OP_BIN_MULTIPLY ), $1, $3, TOKPOS(@2) ); }
| expr TOKEN_DIVIDE expr { $$ = node4( ENUM_func2_expr, node1b( OP_BIN_DIVIDE ), $1, $3, TOKPOS(@2) ); }
| expr TOKEN_MODULUS expr { $$ = node4( ENUM_func2_expr, node1b( OP_BIN_PERCENTAGE ), $1, $3, TOKPOS(@2) ); }
| expr TOKEN_SHIFT_LEFT expr { $$ = node4( ENUM_func2_expr, node1b( OP_BIN_SHIFT_LEFT ), $1, $3, TOKPOS(@2) ); }
| expr TOKEN_SHIFT_RIGHT expr { $$ = node4( ENUM_func2_expr, node1b( OP_BIN_SHIFT_RIGHT ), $1, $3, TOKPOS(@2) ); }
| expr TOKEN_TERNARY expr TOKEN_COLON expr { $$ = node4( ENUM_if_else_statement, $1, $3, $5, TOKPOS(@2) ); }
| TOKEN_EOL expr[exp] { $$ = $exp; }
| nonident_prim_expr
| func_prim_expr
| TOKEN_IDENTIFIER { $$ = node2(ENUM_string, $1, TOKPOS(@1)); }
;
func_prim_expr:
TOKEN_IDENTIFIER event_parameter_list_need { $$ = node3(ENUM_cmd_event_expr, $1, $2, TOKPOS(@1)); }
| nonident_prim_expr_base TOKEN_IDENTIFIER event_parameter_list { $$ = node4(ENUM_method_event_expr, $1, $2, $3, TOKPOS(@2)); }
| TOKEN_NEG func_prim_expr { $$ = node3(ENUM_func1_expr, node1b(OP_UN_MINUS), $2, TOKPOS(@1)); }
| TOKEN_COMPLEMENT func_prim_expr { $$ = node3(ENUM_func1_expr, node1b(OP_UN_COMPLEMENT), $2, TOKPOS(@1)); }
| TOKEN_NOT func_prim_expr { $$ = node2(ENUM_bool_not, $2, TOKPOS(@1)); }
| TOKEN_IDENTIFIER TOKEN_DOUBLE_COLON const_array_list
{
$$ = node3(ENUM_const_array_expr, node2(ENUM_string, $1, TOKPOS(@1)), $3, TOKPOS(@2));
}
| nonident_prim_expr TOKEN_DOUBLE_COLON const_array_list
{
$$ = node3(ENUM_const_array_expr, $1, $3, TOKPOS(@2));
}
| TOKEN_MAKEARRAY makearray_statement_list[stmt] TOKEN_ENDARRAY
{
$$ = node2(ENUM_makearray, $stmt, TOKPOS(@1));
}
;
event_parameter_list
: { $$ = sval_u{}; $$.node = NULL; }
| event_parameter { $$ = $1; }
;
event_parameter_list_need
: event_parameter { $$ = $1; }
;
event_parameter
: prim_expr { $$ = linked_list_end($1); }
| event_parameter prim_expr { $$ = append_node($1, $2); }
;
const_array_list
: const_array { $$ = linked_list_end($1); }
| const_array_list TOKEN_DOUBLE_COLON const_array { $$ = append_node($1, $3); }
;
const_array
: nonident_prim_expr
| identifier_prim { $$ = node2(ENUM_string, $1, TOKPOS(@1)); }
;
prim_expr
: nonident_prim_expr
| identifier_prim { $$ = node2(ENUM_string, $1, TOKPOS(@1)); }
| const_array TOKEN_DOUBLE_COLON const_array_list { $$ = node3(ENUM_const_array_expr, $1, $3, TOKPOS(@2)); }
;
identifier_prim:
TOKEN_IDENTIFIER { $$ = $1; @$ = @1; }
;
identifier
: TOKEN_IDENTIFIER { $$ = $1; @$ = @1; }
| TOKEN_STRING { $$ = $1; @$ = @1; }
;
listener_identifier
: identifier { $$ = node_listener($1, TOKPOS(@1)); }
| TOKEN_LEFT_BRACKET expr TOKEN_RIGHT_BRACKET { $$ = $2; }
;
nonident_prim_expr
: nonident_prim_expr_base
| TOKEN_NEG nonident_prim_expr { $$ = node3(ENUM_func1_expr, node1b(OP_UN_MINUS), $2, TOKPOS(@1)); }
| TOKEN_COMPLEMENT nonident_prim_expr { $$ = node3(ENUM_func1_expr, node1b(OP_UN_COMPLEMENT), $2, TOKPOS(@1)); }
| TOKEN_NOT nonident_prim_expr { $$ = node2(ENUM_bool_not, $2, TOKPOS(@1)); }
;
nonident_prim_expr_base
: TOKEN_DOLLAR listener_identifier { $$ = node3(ENUM_func1_expr, node1b(OP_UN_TARGETNAME), $2, TOKPOS(@1)); }
| nonident_prim_expr TOKEN_PERIOD identifier { $$ = node3(ENUM_field, $1, $3, TOKPOS(@3)); }
| nonident_prim_expr TOKEN_PERIOD TOKEN_SIZE { $$ = node3(ENUM_func1_expr, node1b(OP_UN_SIZE), $1, TOKPOS(@3)); }
| nonident_prim_expr TOKEN_LEFT_SQUARE_BRACKET expr TOKEN_RIGHT_SQUARE_BRACKET { $$ = node3(ENUM_array_expr, $1, $3, TOKPOS(@2)); }
| TOKEN_STRING { $$ = node2(ENUM_string, $1, TOKPOS(@1)); }
| TOKEN_INTEGER { $$ = node2(ENUM_integer, $1, TOKPOS(@1)); }
| TOKEN_FLOAT { $$ = node2(ENUM_float, $1, TOKPOS(@1)); }
| TOKEN_LEFT_BRACKET expr[exp1] expr[exp2] expr[exp3] TOKEN_RIGHT_BRACKET { $$ = node4(ENUM_vector, $exp1, $exp2, $exp3, TOKPOS(@1)); }
| TOKEN_LISTENER { $$ = node2(ENUM_listener, $1, TOKPOS(@1)); }
| TOKEN_LEFT_BRACKET expr TOKEN_RIGHT_BRACKET { $$ = $2; }
| TOKEN_LEFT_BRACKET expr TOKEN_EOL TOKEN_RIGHT_BRACKET { $$ = $2; }
| TOKEN_NULL { $$ = node1(ENUM_NULL, TOKPOS(@1)); }
| TOKEN_NIL { $$ = node1(ENUM_NIL, TOKPOS(@1)); }
;
makearray_statement_list:
{ $$ = node0(ENUM_NOP); }
| makearray_statement_list[list] makearray_statement[ma_stmt] TOKEN_EOL { $$ = append_node($list, node2(ENUM_makearray, $ma_stmt, TOKPOS(@ma_stmt))); }
| makearray_statement[ma_stmt] TOKEN_EOL { $$ = linked_list_end(node2(ENUM_makearray, $ma_stmt, TOKPOS(@ma_stmt))); }
| TOKEN_EOL makearray_statement_list { $$ = $2; @$ = @2; }
;
makearray_statement:
prim_expr { $$ = linked_list_end( $1 ); }
| makearray_statement prim_expr { $$ = append_node( $1, $2 ); }
;
line_opt
: {}
| TOKEN_EOL
;
%%
|