%lex
%options flex

/* port the character classes from flex */
/* mostly the same except for char literals */

D           [0-9]
L           [a-zA-Z_]
H           [a-fA-F0-9]
E           [Ee][+-]?{D}+
FS          ('f'|'F'|'l'|'L')
IS          ('u'|'U'|'l'|'L')*

%%

'/*'        comment(this)

'auto'      return 'AUTO'
'break'     return 'BREAK'
'case'      return 'CASE'
'char'      return 'CHAR'
'const'     return 'CONST'
'continue'  return 'CONTINUE'
'default'   return 'DEFAULT'
'do'        return 'DO'
'double'    return 'DOUBLE'
'else'      return 'ELSE'
'enum'      return 'ENUM'
'extern'    return 'EXTERN'
'float'     return 'FLOAT'
'for'       return 'FOR'
'goto'      return 'GOTO'
'if'        return 'IF'
'int'       return 'INT'
'long'      return 'LONG'
'register'  return 'REGISTER'
'return'    return 'RETURN'
'short'     return 'SHORT'
'signed'    return 'SIGNED'
'sizeof'    return 'SIZEOF'
'static'    return 'STATIC'
'struct'    return 'STRUCT'
'switch'    return 'SWITCH'
'typedef'   return 'TYPEDEF'
'union'     return 'UNION'
'unsigned'  return 'UNSIGNED'
'void'      return 'VOID'
'volatile'  return 'VOLATILE'
'while'     return 'WHILE'

{L}({L}|{D})* return is_type(pdata, yytext) ? 'TYPE_NAME' : 'IDENTIFIER';

0[xX]{H}+{IS}?      return 'CONSTANT'
0{D}+{IS}?          return 'CONSTANT'
{D}+{IS}?           return 'CONSTANT'
L?'(\.|[^\'])+'     return 'CONSTANT'

{D}+{E}{FS}?        return 'CONSTANT'
{D}*"."{D}+({E})?{FS}?  return 'CONSTANT'
{D}+"."{D}*({E})?{FS}?  return 'CONSTANT'

L?\"(\\.|[^\\"])*\" return 'STRING_LITERAL';

"..."                   return 'ELLIPSIS'
">>="                   return 'RIGHT_ASSIGN'
"<<="                   return 'LEFT_ASSIGN'
"+="                    return 'ADD_ASSIGN'
"-="                    return 'SUB_ASSIGN'
"*="                    return 'MUL_ASSIGN'
"/="                    return 'DIV_ASSIGN'
"%="                    return 'MOD_ASSIGN'
"&="                    return 'AND_ASSIGN'
"^="                    return 'XOR_ASSIGN'
"|="                    return 'OR_ASSIGN'
">>"                    return 'RIGHT_OP'
"<<"                    return 'LEFT_OP'
"++"                    return 'INC_OP'
"--"                    return 'DEC_OP'
"->"                    return 'PTR_OP'
"&&"                    return 'AND_OP'
"||"                    return 'OR_OP'
"<="                    return 'LE_OP'
">="                    return 'GE_OP'
"=="                    return 'EQ_OP'
"!="                    return 'NE_OP'

";"         return ';'
("{"|"<%")  return  '{'
("}"|"%>")  return  '}'
","         return  ','
":"         return  ':'
"="         return  '='
"("         return  '('
")"         return  ')'
("["|"<:")  return  '['
("]"|":>")  return  ']'
"."         return  '.'
"&"         return  '&'
"!"         return  '!'
"~"         return  '~'
"-"         return  '-'
'+'         return  '+'
"*"         return  '*'
"/"         return  '/'
"%"         return  '%'
"<"         return  '<'
">"         return  '>'
"^"         return  '^'
"|"         return  '|'
"?"         return  '?'

[ \t\v\n\f] /* ignore ws */
.           /* ignore bad chars */

/lex

%start top_level
%lex-param pdata
%parse-param pdata

%% /* language grammar */

top_level
       : translation_unit { return $1; }
       ;

/* AUTOGEN SECTION */

primary_expression
    : IDENTIFIER { $$ = make_node(pdata, "primary_expression.0"); node_add_leaf(pdata, $$, 'IDENTIFIER', @1); }
    | CONSTANT { $$ = make_node(pdata, "primary_expression.1"); node_add_leaf(pdata, $$, 'CONSTANT', @1); }
    | STRING_LITERAL { $$ = make_node(pdata, "primary_expression.2"); node_add_leaf(pdata, $$, 'STRING_LITERAL', @1); }
    | '(' expression ')' { $$ = make_node(pdata, "primary_expression.3"); node_add_leaf(pdata, $$, '(', @1); node_add_child(pdata, $$, $2); node_add_leaf(pdata, $$, ')', @3); }
    ;

postfix_expression
    : primary_expression 
    | postfix_expression '[' expression ']' { $$ = make_node(pdata, "postfix_expression.1"); node_add_child(pdata, $$, $1); node_add_leaf(pdata, $$, '[', @2); node_add_child(pdata, $$, $3); node_add_leaf(pdata, $$, ']', @4); }
    | postfix_expression '(' ')' { $$ = make_node(pdata, "postfix_expression.2"); node_add_child(pdata, $$, $1); node_add_leaf(pdata, $$, '(', @2); node_add_leaf(pdata, $$, ')', @3); }
    | postfix_expression '(' argument_expression_list ')' { $$ = make_node(pdata, "postfix_expression.3"); node_add_child(pdata, $$, $1); node_add_leaf(pdata, $$, '(', @2); node_add_child(pdata, $$, $3); node_add_leaf(pdata, $$, ')', @4); }
    | postfix_expression '.' IDENTIFIER { $$ = make_node(pdata, "postfix_expression.4"); node_add_child(pdata, $$, $1); node_add_leaf(pdata, $$, '.', @2); node_add_leaf(pdata, $$, 'IDENTIFIER', @3); }
    | postfix_expression PTR_OP IDENTIFIER { $$ = make_node(pdata, "postfix_expression.5"); node_add_child(pdata, $$, $1); node_add_leaf(pdata, $$, 'PTR_OP', @2); node_add_leaf(pdata, $$, 'IDENTIFIER', @3); }
    | postfix_expression INC_OP { $$ = make_node(pdata, "postfix_expression.6"); node_add_child(pdata, $$, $1); node_add_leaf(pdata, $$, 'INC_OP', @2); }
    | postfix_expression DEC_OP { $$ = make_node(pdata, "postfix_expression.7"); node_add_child(pdata, $$, $1); node_add_leaf(pdata, $$, 'DEC_OP', @2); }
    ;

argument_expression_list
    : assignment_expression 
    | argument_expression_list ',' assignment_expression { $$ = make_node(pdata, "argument_expression_list.1"); node_add_child(pdata, $$, $1); node_add_leaf(pdata, $$, ',', @2); node_add_child(pdata, $$, $3); }
    ;

unary_expression
    : postfix_expression 
    | INC_OP unary_expression { $$ = make_node(pdata, "unary_expression.1"); node_add_leaf(pdata, $$, 'INC_OP', @1); node_add_child(pdata, $$, $2); }
    | DEC_OP unary_expression { $$ = make_node(pdata, "unary_expression.2"); node_add_leaf(pdata, $$, 'DEC_OP', @1); node_add_child(pdata, $$, $2); }
    | unary_operator cast_expression { $$ = make_node(pdata, "unary_expression.3"); node_add_child(pdata, $$, $1); node_add_child(pdata, $$, $2); }
    | SIZEOF unary_expression { $$ = make_node(pdata, "unary_expression.4"); node_add_leaf(pdata, $$, 'SIZEOF', @1); node_add_child(pdata, $$, $2); }
    | SIZEOF '(' type_name ')' { $$ = make_node(pdata, "unary_expression.5"); node_add_leaf(pdata, $$, 'SIZEOF', @1); node_add_leaf(pdata, $$, '(', @2); node_add_child(pdata, $$, $3); node_add_leaf(pdata, $$, ')', @4); }
    ;

unary_operator
    : '&' { $$ = make_node(pdata, "unary_operator.0"); node_add_leaf(pdata, $$, '&', @1); }
    | '*' { $$ = make_node(pdata, "unary_operator.1"); node_add_leaf(pdata, $$, '*', @1); }
    | '+' { $$ = make_node(pdata, "unary_operator.2"); node_add_leaf(pdata, $$, '+', @1); }
    | '-' { $$ = make_node(pdata, "unary_operator.3"); node_add_leaf(pdata, $$, '-', @1); }
    | '~' { $$ = make_node(pdata, "unary_operator.4"); node_add_leaf(pdata, $$, '~', @1); }
    | '!' { $$ = make_node(pdata, "unary_operator.5"); node_add_leaf(pdata, $$, '!', @1); }
    ;

cast_expression
    : unary_expression 
    | '(' type_name ')' cast_expression { $$ = make_node(pdata, "cast_expression.1"); node_add_leaf(pdata, $$, '(', @1); node_add_child(pdata, $$, $2); node_add_leaf(pdata, $$, ')', @3); node_add_child(pdata, $$, $4); }
    ;

multiplicative_expression
    : cast_expression 
    | multiplicative_expression '*' cast_expression { $$ = make_node(pdata, "multiplicative_expression.1"); node_add_child(pdata, $$, $1); node_add_leaf(pdata, $$, '*', @2); node_add_child(pdata, $$, $3); }
    | multiplicative_expression '/' cast_expression { $$ = make_node(pdata, "multiplicative_expression.2"); node_add_child(pdata, $$, $1); node_add_leaf(pdata, $$, '/', @2); node_add_child(pdata, $$, $3); }
    | multiplicative_expression '%' cast_expression { $$ = make_node(pdata, "multiplicative_expression.3"); node_add_child(pdata, $$, $1); node_add_leaf(pdata, $$, '%', @2); node_add_child(pdata, $$, $3); }
    ;

additive_expression
    : multiplicative_expression 
    | additive_expression '+' multiplicative_expression { $$ = make_node(pdata, "additive_expression.1"); node_add_child(pdata, $$, $1); node_add_leaf(pdata, $$, '+', @2); node_add_child(pdata, $$, $3); }
    | additive_expression '-' multiplicative_expression { $$ = make_node(pdata, "additive_expression.2"); node_add_child(pdata, $$, $1); node_add_leaf(pdata, $$, '-', @2); node_add_child(pdata, $$, $3); }
    ;

shift_expression
    : additive_expression 
    | shift_expression LEFT_OP additive_expression { $$ = make_node(pdata, "shift_expression.1"); node_add_child(pdata, $$, $1); node_add_leaf(pdata, $$, 'LEFT_OP', @2); node_add_child(pdata, $$, $3); }
    | shift_expression RIGHT_OP additive_expression { $$ = make_node(pdata, "shift_expression.2"); node_add_child(pdata, $$, $1); node_add_leaf(pdata, $$, 'RIGHT_OP', @2); node_add_child(pdata, $$, $3); }
    ;

relational_expression
    : shift_expression 
    | relational_expression '<' shift_expression { $$ = make_node(pdata, "relational_expression.1"); node_add_child(pdata, $$, $1); node_add_leaf(pdata, $$, '<', @2); node_add_child(pdata, $$, $3); }
    | relational_expression '>' shift_expression { $$ = make_node(pdata, "relational_expression.2"); node_add_child(pdata, $$, $1); node_add_leaf(pdata, $$, '>', @2); node_add_child(pdata, $$, $3); }
    | relational_expression LE_OP shift_expression { $$ = make_node(pdata, "relational_expression.3"); node_add_child(pdata, $$, $1); node_add_leaf(pdata, $$, 'LE_OP', @2); node_add_child(pdata, $$, $3); }
    | relational_expression GE_OP shift_expression { $$ = make_node(pdata, "relational_expression.4"); node_add_child(pdata, $$, $1); node_add_leaf(pdata, $$, 'GE_OP', @2); node_add_child(pdata, $$, $3); }
    ;

equality_expression
    : relational_expression 
    | equality_expression EQ_OP relational_expression { $$ = make_node(pdata, "equality_expression.1"); node_add_child(pdata, $$, $1); node_add_leaf(pdata, $$, 'EQ_OP', @2); node_add_child(pdata, $$, $3); }
    | equality_expression NE_OP relational_expression { $$ = make_node(pdata, "equality_expression.2"); node_add_child(pdata, $$, $1); node_add_leaf(pdata, $$, 'NE_OP', @2); node_add_child(pdata, $$, $3); }
    ;

and_expression
    : equality_expression 
    | and_expression '&' equality_expression { $$ = make_node(pdata, "and_expression.1"); node_add_child(pdata, $$, $1); node_add_leaf(pdata, $$, '&', @2); node_add_child(pdata, $$, $3); }
    ;

exclusive_or_expression
    : and_expression 
    | exclusive_or_expression '^' and_expression { $$ = make_node(pdata, "exclusive_or_expression.1"); node_add_child(pdata, $$, $1); node_add_leaf(pdata, $$, '^', @2); node_add_child(pdata, $$, $3); }
    ;

inclusive_or_expression
    : exclusive_or_expression 
    | inclusive_or_expression '|' exclusive_or_expression { $$ = make_node(pdata, "inclusive_or_expression.1"); node_add_child(pdata, $$, $1); node_add_leaf(pdata, $$, '|', @2); node_add_child(pdata, $$, $3); }
    ;

logical_and_expression
    : inclusive_or_expression 
    | logical_and_expression AND_OP inclusive_or_expression { $$ = make_node(pdata, "logical_and_expression.1"); node_add_child(pdata, $$, $1); node_add_leaf(pdata, $$, 'AND_OP', @2); node_add_child(pdata, $$, $3); }
    ;

logical_or_expression
    : logical_and_expression 
    | logical_or_expression OR_OP logical_and_expression { $$ = make_node(pdata, "logical_or_expression.1"); node_add_child(pdata, $$, $1); node_add_leaf(pdata, $$, 'OR_OP', @2); node_add_child(pdata, $$, $3); }
    ;

conditional_expression
    : logical_or_expression 
    | logical_or_expression '?' expression ':' conditional_expression { $$ = make_node(pdata, "conditional_expression.1"); node_add_child(pdata, $$, $1); node_add_leaf(pdata, $$, '?', @2); node_add_child(pdata, $$, $3); node_add_leaf(pdata, $$, ':', @4); node_add_child(pdata, $$, $5); }
    ;

assignment_expression
    : conditional_expression 
    | unary_expression assignment_operator assignment_expression { $$ = make_node(pdata, "assignment_expression.1"); node_add_child(pdata, $$, $1); node_add_child(pdata, $$, $2); node_add_child(pdata, $$, $3); }
    ;

assignment_operator
    : '=' { $$ = make_node(pdata, "assignment_operator.0"); node_add_leaf(pdata, $$, '=', @1); }
    | MUL_ASSIGN { $$ = make_node(pdata, "assignment_operator.1"); node_add_leaf(pdata, $$, 'MUL_ASSIGN', @1); }
    | DIV_ASSIGN { $$ = make_node(pdata, "assignment_operator.2"); node_add_leaf(pdata, $$, 'DIV_ASSIGN', @1); }
    | MOD_ASSIGN { $$ = make_node(pdata, "assignment_operator.3"); node_add_leaf(pdata, $$, 'MOD_ASSIGN', @1); }
    | ADD_ASSIGN { $$ = make_node(pdata, "assignment_operator.4"); node_add_leaf(pdata, $$, 'ADD_ASSIGN', @1); }
    | SUB_ASSIGN { $$ = make_node(pdata, "assignment_operator.5"); node_add_leaf(pdata, $$, 'SUB_ASSIGN', @1); }
    | LEFT_ASSIGN { $$ = make_node(pdata, "assignment_operator.6"); node_add_leaf(pdata, $$, 'LEFT_ASSIGN', @1); }
    | RIGHT_ASSIGN { $$ = make_node(pdata, "assignment_operator.7"); node_add_leaf(pdata, $$, 'RIGHT_ASSIGN', @1); }
    | AND_ASSIGN { $$ = make_node(pdata, "assignment_operator.8"); node_add_leaf(pdata, $$, 'AND_ASSIGN', @1); }
    | XOR_ASSIGN { $$ = make_node(pdata, "assignment_operator.9"); node_add_leaf(pdata, $$, 'XOR_ASSIGN', @1); }
    | OR_ASSIGN { $$ = make_node(pdata, "assignment_operator.10"); node_add_leaf(pdata, $$, 'OR_ASSIGN', @1); }
    ;

expression
    : assignment_expression 
    | expression ',' assignment_expression { $$ = make_node(pdata, "expression.1"); node_add_child(pdata, $$, $1); node_add_leaf(pdata, $$, ',', @2); node_add_child(pdata, $$, $3); }
    ;

constant_expression
    : conditional_expression { $$ = make_node(pdata, "constant_expression.0"); node_add_child(pdata, $$, $1); }
    ;

declaration
    : declaration_specifiers ';' { $$ = make_node(pdata, "declaration.0"); node_add_child(pdata, $$, $1); node_add_leaf(pdata, $$, ';', @2); check_declaration(pdata, $$);}
    | declaration_specifiers init_declarator_list ';' { $$ = make_node(pdata, "declaration.1"); node_add_child(pdata, $$, $1); node_add_child(pdata, $$, $2); node_add_leaf(pdata, $$, ';', @3); check_declaration(pdata, $$);}
    ;

declaration_specifiers
    : storage_class_specifier { $$ = make_node(pdata, "declaration_specifiers.0"); node_add_child(pdata, $$, $1); }
    | storage_class_specifier declaration_specifiers { $$ = make_node(pdata, "declaration_specifiers.1"); node_add_child(pdata, $$, $1); node_add_child(pdata, $$, $2); }
    | type_specifier { $$ = make_node(pdata, "declaration_specifiers.2"); node_add_child(pdata, $$, $1); }
    | type_specifier declaration_specifiers { $$ = make_node(pdata, "declaration_specifiers.3"); node_add_child(pdata, $$, $1); node_add_child(pdata, $$, $2); }
    | type_qualifier { $$ = make_node(pdata, "declaration_specifiers.4"); node_add_child(pdata, $$, $1); }
    | type_qualifier declaration_specifiers { $$ = make_node(pdata, "declaration_specifiers.5"); node_add_child(pdata, $$, $1); node_add_child(pdata, $$, $2); }
    ;

init_declarator_list
    : init_declarator { $$ = make_node(pdata, "init_declarator_list.0"); node_add_child(pdata, $$, $1); }
    | init_declarator_list ',' init_declarator { $$ = make_node(pdata, "init_declarator_list.1"); node_add_child(pdata, $$, $1); node_add_leaf(pdata, $$, ',', @2); node_add_child(pdata, $$, $3); }
    ;

init_declarator
    : declarator { $$ = make_node(pdata, "init_declarator.0"); node_add_child(pdata, $$, $1); }
    | declarator '=' initializer { $$ = make_node(pdata, "init_declarator.1"); node_add_child(pdata, $$, $1); node_add_leaf(pdata, $$, '=', @2); node_add_child(pdata, $$, $3); }
    ;

storage_class_specifier
    : TYPEDEF { $$ = make_node(pdata, "storage_class_specifier.0"); node_add_leaf(pdata, $$, 'TYPEDEF', @1); }
    | EXTERN { $$ = make_node(pdata, "storage_class_specifier.1"); node_add_leaf(pdata, $$, 'EXTERN', @1); }
    | STATIC { $$ = make_node(pdata, "storage_class_specifier.2"); node_add_leaf(pdata, $$, 'STATIC', @1); }
    | AUTO { $$ = make_node(pdata, "storage_class_specifier.3"); node_add_leaf(pdata, $$, 'AUTO', @1); }
    | REGISTER { $$ = make_node(pdata, "storage_class_specifier.4"); node_add_leaf(pdata, $$, 'REGISTER', @1); }
    ;

type_specifier
    : VOID { $$ = make_node(pdata, "type_specifier.0"); node_add_leaf(pdata, $$, 'VOID', @1); }
    | CHAR { $$ = make_node(pdata, "type_specifier.1"); node_add_leaf(pdata, $$, 'CHAR', @1); }
    | SHORT { $$ = make_node(pdata, "type_specifier.2"); node_add_leaf(pdata, $$, 'SHORT', @1); }
    | INT { $$ = make_node(pdata, "type_specifier.3"); node_add_leaf(pdata, $$, 'INT', @1); }
    | LONG { $$ = make_node(pdata, "type_specifier.4"); node_add_leaf(pdata, $$, 'LONG', @1); }
    | FLOAT { $$ = make_node(pdata, "type_specifier.5"); node_add_leaf(pdata, $$, 'FLOAT', @1); }
    | DOUBLE { $$ = make_node(pdata, "type_specifier.6"); node_add_leaf(pdata, $$, 'DOUBLE', @1); }
    | SIGNED { $$ = make_node(pdata, "type_specifier.7"); node_add_leaf(pdata, $$, 'SIGNED', @1); }
    | UNSIGNED { $$ = make_node(pdata, "type_specifier.8"); node_add_leaf(pdata, $$, 'UNSIGNED', @1); }
    | struct_or_union_specifier { $$ = make_node(pdata, "type_specifier.9"); node_add_child(pdata, $$, $1); }
    | enum_specifier { $$ = make_node(pdata, "type_specifier.10"); node_add_child(pdata, $$, $1); }
    | TYPE_NAME { $$ = make_node(pdata, "type_specifier.11"); node_add_leaf(pdata, $$, 'TYPE_NAME', @1); }
    ;

struct_or_union_specifier
    : struct_or_union IDENTIFIER '{' struct_declaration_list '}' { $$ = make_node(pdata, "struct_or_union_specifier.0"); node_add_child(pdata, $$, $1); node_add_leaf(pdata, $$, 'IDENTIFIER', @2); node_add_leaf(pdata, $$, '{', @3); node_add_child(pdata, $$, $4); node_add_leaf(pdata, $$, '}', @5); }
    | struct_or_union '{' struct_declaration_list '}' { $$ = make_node(pdata, "struct_or_union_specifier.1"); node_add_child(pdata, $$, $1); node_add_leaf(pdata, $$, '{', @2); node_add_child(pdata, $$, $3); node_add_leaf(pdata, $$, '}', @4); }
    | struct_or_union IDENTIFIER { $$ = make_node(pdata, "struct_or_union_specifier.2"); node_add_child(pdata, $$, $1); node_add_leaf(pdata, $$, 'IDENTIFIER', @2); }
    ;

struct_or_union
    : STRUCT { $$ = make_node(pdata, "struct_or_union.0"); node_add_leaf(pdata, $$, 'STRUCT', @1); }
    | UNION { $$ = make_node(pdata, "struct_or_union.1"); node_add_leaf(pdata, $$, 'UNION', @1); }
    ;

struct_declaration_list
    : struct_declaration { $$ = make_node(pdata, "struct_declaration_list.0"); node_add_child(pdata, $$, $1); }
    | struct_declaration_list struct_declaration { $$ = $1; node_add_child(pdata, $$, $2); }
    ;

struct_declaration
    : specifier_qualifier_list struct_declarator_list ';' { $$ = make_node(pdata, "struct_declaration.0"); node_add_child(pdata, $$, $1); node_add_child(pdata, $$, $2); node_add_leaf(pdata, $$, ';', @3); }
    ;

specifier_qualifier_list
    : type_specifier specifier_qualifier_list { $$ = make_node(pdata, "specifier_qualifier_list.0"); node_add_child(pdata, $$, $1); node_add_child(pdata, $$, $2); }
    | type_specifier { $$ = make_node(pdata, "specifier_qualifier_list.1"); node_add_child(pdata, $$, $1); }
    | type_qualifier specifier_qualifier_list { $$ = make_node(pdata, "specifier_qualifier_list.2"); node_add_child(pdata, $$, $1); node_add_child(pdata, $$, $2); }
    | type_qualifier { $$ = make_node(pdata, "specifier_qualifier_list.3"); node_add_child(pdata, $$, $1); }
    ;

struct_declarator_list
    : struct_declarator { $$ = make_node(pdata, "struct_declarator_list.0"); node_add_child(pdata, $$, $1); }
    | struct_declarator_list ',' struct_declarator { $$ = make_node(pdata, "struct_declarator_list.1"); node_add_child(pdata, $$, $1); node_add_leaf(pdata, $$, ',', @2); node_add_child(pdata, $$, $3); }
    ;

struct_declarator
    : declarator { $$ = make_node(pdata, "struct_declarator.0"); node_add_child(pdata, $$, $1); }
    | ':' constant_expression { $$ = make_node(pdata, "struct_declarator.1"); node_add_leaf(pdata, $$, ':', @1); node_add_child(pdata, $$, $2); }
    | declarator ':' constant_expression { $$ = make_node(pdata, "struct_declarator.2"); node_add_child(pdata, $$, $1); node_add_leaf(pdata, $$, ':', @2); node_add_child(pdata, $$, $3); }
    ;

enum_specifier
    : ENUM '{' enumerator_list '}' { $$ = make_node(pdata, "enum_specifier.0"); node_add_leaf(pdata, $$, 'ENUM', @1); node_add_leaf(pdata, $$, '{', @2); node_add_child(pdata, $$, $3); node_add_leaf(pdata, $$, '}', @4); }
    | ENUM IDENTIFIER '{' enumerator_list '}' { $$ = make_node(pdata, "enum_specifier.1"); node_add_leaf(pdata, $$, 'ENUM', @1); node_add_leaf(pdata, $$, 'IDENTIFIER', @2); node_add_leaf(pdata, $$, '{', @3); node_add_child(pdata, $$, $4); node_add_leaf(pdata, $$, '}', @5); }
    | ENUM IDENTIFIER { $$ = make_node(pdata, "enum_specifier.2"); node_add_leaf(pdata, $$, 'ENUM', @1); node_add_leaf(pdata, $$, 'IDENTIFIER', @2); }
    ;

enumerator_list
    : enumerator { $$ = make_node(pdata, "enumerator_list.0"); node_add_child(pdata, $$, $1); }
    | enumerator_list ',' enumerator { $$ = make_node(pdata, "enumerator_list.1"); node_add_child(pdata, $$, $1); node_add_leaf(pdata, $$, ',', @2); node_add_child(pdata, $$, $3); }
    ;

enumerator
    : IDENTIFIER { $$ = make_node(pdata, "enumerator.0"); node_add_leaf(pdata, $$, 'IDENTIFIER', @1); }
    | IDENTIFIER '=' constant_expression { $$ = make_node(pdata, "enumerator.1"); node_add_leaf(pdata, $$, 'IDENTIFIER', @1); node_add_leaf(pdata, $$, '=', @2); node_add_child(pdata, $$, $3); }
    ;

type_qualifier
    : CONST { $$ = make_node(pdata, "type_qualifier.0"); node_add_leaf(pdata, $$, 'CONST', @1); }
    | VOLATILE { $$ = make_node(pdata, "type_qualifier.1"); node_add_leaf(pdata, $$, 'VOLATILE', @1); }
    ;

declarator
    : pointer direct_declarator { $$ = make_node(pdata, "declarator.0"); node_add_child(pdata, $$, $1); node_add_child(pdata, $$, $2); }
    | direct_declarator { $$ = make_node(pdata, "declarator.1"); node_add_child(pdata, $$, $1); }
    ;

direct_declarator
    : IDENTIFIER { $$ = make_node(pdata, "direct_declarator.0"); node_add_leaf(pdata, $$, 'IDENTIFIER', @1); }
    | '(' declarator ')' { $$ = make_node(pdata, "direct_declarator.1"); node_add_leaf(pdata, $$, '(', @1); node_add_child(pdata, $$, $2); node_add_leaf(pdata, $$, ')', @3); }
    | direct_declarator '[' constant_expression ']' { $$ = make_node(pdata, "direct_declarator.2"); node_add_child(pdata, $$, $1); node_add_leaf(pdata, $$, '[', @2); node_add_child(pdata, $$, $3); node_add_leaf(pdata, $$, ']', @4); }
    | direct_declarator '[' ']' { $$ = make_node(pdata, "direct_declarator.3"); node_add_child(pdata, $$, $1); node_add_leaf(pdata, $$, '[', @2); node_add_leaf(pdata, $$, ']', @3); }
    | direct_declarator '(' parameter_type_list ')' { $$ = make_node(pdata, "direct_declarator.4"); node_add_child(pdata, $$, $1); node_add_leaf(pdata, $$, '(', @2); node_add_child(pdata, $$, $3); node_add_leaf(pdata, $$, ')', @4); }
    | direct_declarator '(' identifier_list ')' { $$ = make_node(pdata, "direct_declarator.5"); node_add_child(pdata, $$, $1); node_add_leaf(pdata, $$, '(', @2); node_add_child(pdata, $$, $3); node_add_leaf(pdata, $$, ')', @4); }
    | direct_declarator '(' ')' { $$ = make_node(pdata, "direct_declarator.6"); node_add_child(pdata, $$, $1); node_add_leaf(pdata, $$, '(', @2); node_add_leaf(pdata, $$, ')', @3); }
    ;

pointer
    : '*' { $$ = make_node(pdata, "pointer.0"); node_add_leaf(pdata, $$, '*', @1); }
    | '*' type_qualifier_list { $$ = make_node(pdata, "pointer.1"); node_add_leaf(pdata, $$, '*', @1); node_add_child(pdata, $$, $2); }
    | '*' pointer { $$ = make_node(pdata, "pointer.2"); node_add_leaf(pdata, $$, '*', @1); node_add_child(pdata, $$, $2); }
    | '*' type_qualifier_list pointer { $$ = make_node(pdata, "pointer.3"); node_add_leaf(pdata, $$, '*', @1); node_add_child(pdata, $$, $2); node_add_child(pdata, $$, $3); }
    ;

type_qualifier_list
    : type_qualifier { $$ = make_node(pdata, "type_qualifier_list.0"); node_add_child(pdata, $$, $1); }
    | type_qualifier_list type_qualifier { $$ = make_node(pdata, "type_qualifier_list.1"); node_add_child(pdata, $$, $1); node_add_child(pdata, $$, $2); }
    ;

parameter_type_list
    : parameter_list { $$ = make_node(pdata, "parameter_type_list.0"); node_add_child(pdata, $$, $1); }
    | parameter_list ',' ELLIPSIS { $$ = make_node(pdata, "parameter_type_list.1"); node_add_child(pdata, $$, $1); node_add_leaf(pdata, $$, ',', @2); node_add_leaf(pdata, $$, 'ELLIPSIS', @3); }
    ;

parameter_list
    : parameter_declaration { $$ = make_node(pdata, "parameter_list.0"); node_add_child(pdata, $$, $1); }
    | parameter_list ',' parameter_declaration { $$ = $1; node_add_leaf(pdata, $$, ',', @2); node_add_child(pdata, $$, $3); }
    ;

parameter_declaration
    : declaration_specifiers declarator { $$ = make_node(pdata, "parameter_declaration.0"); node_add_child(pdata, $$, $1); node_add_child(pdata, $$, $2); }
    | declaration_specifiers abstract_declarator { $$ = make_node(pdata, "parameter_declaration.1"); node_add_child(pdata, $$, $1); node_add_child(pdata, $$, $2); }
    | declaration_specifiers { $$ = make_node(pdata, "parameter_declaration.2"); node_add_child(pdata, $$, $1); }
    ;

identifier_list
    : IDENTIFIER { $$ = make_node(pdata, "identifier_list.0"); node_add_leaf(pdata, $$, 'IDENTIFIER', @1); }
    | identifier_list ',' IDENTIFIER { $$ = make_node(pdata, "identifier_list.1"); node_add_child(pdata, $$, $1); node_add_leaf(pdata, $$, ',', @2); node_add_leaf(pdata, $$, 'IDENTIFIER', @3); }
    ;

type_name
    : specifier_qualifier_list { $$ = make_node(pdata, "type_name.0"); node_add_child(pdata, $$, $1); }
    | specifier_qualifier_list abstract_declarator { $$ = make_node(pdata, "type_name.1"); node_add_child(pdata, $$, $1); node_add_child(pdata, $$, $2); }
    ;

abstract_declarator
    : pointer { $$ = make_node(pdata, "abstract_declarator.0"); node_add_child(pdata, $$, $1); }
    | direct_abstract_declarator { $$ = make_node(pdata, "abstract_declarator.1"); node_add_child(pdata, $$, $1); }
    | pointer direct_abstract_declarator { $$ = make_node(pdata, "abstract_declarator.2"); node_add_child(pdata, $$, $1); node_add_child(pdata, $$, $2); }
    ;

direct_abstract_declarator
    : '(' abstract_declarator ')' { $$ = make_node(pdata, "direct_abstract_declarator.0"); node_add_leaf(pdata, $$, '(', @1); node_add_child(pdata, $$, $2); node_add_leaf(pdata, $$, ')', @3); }
    | '[' ']' { $$ = make_node(pdata, "direct_abstract_declarator.1"); node_add_leaf(pdata, $$, '[', @1); node_add_leaf(pdata, $$, ']', @2); }
    | '[' constant_expression ']' { $$ = make_node(pdata, "direct_abstract_declarator.2"); node_add_leaf(pdata, $$, '[', @1); node_add_child(pdata, $$, $2); node_add_leaf(pdata, $$, ']', @3); }
    | direct_abstract_declarator '[' ']' { $$ = make_node(pdata, "direct_abstract_declarator.3"); node_add_child(pdata, $$, $1); node_add_leaf(pdata, $$, '[', @2); node_add_leaf(pdata, $$, ']', @3); }
    | direct_abstract_declarator '[' constant_expression ']' { $$ = make_node(pdata, "direct_abstract_declarator.4"); node_add_child(pdata, $$, $1); node_add_leaf(pdata, $$, '[', @2); node_add_child(pdata, $$, $3); node_add_leaf(pdata, $$, ']', @4); }
    | '(' ')' { $$ = make_node(pdata, "direct_abstract_declarator.5"); node_add_leaf(pdata, $$, '(', @1); node_add_leaf(pdata, $$, ')', @2); }
    | '(' parameter_type_list ')' { $$ = make_node(pdata, "direct_abstract_declarator.6"); node_add_leaf(pdata, $$, '(', @1); node_add_child(pdata, $$, $2); node_add_leaf(pdata, $$, ')', @3); }
    | direct_abstract_declarator '(' ')' { $$ = make_node(pdata, "direct_abstract_declarator.7"); node_add_child(pdata, $$, $1); node_add_leaf(pdata, $$, '(', @2); node_add_leaf(pdata, $$, ')', @3); }
    | direct_abstract_declarator '(' parameter_type_list ')' { $$ = make_node(pdata, "direct_abstract_declarator.8"); node_add_child(pdata, $$, $1); node_add_leaf(pdata, $$, '(', @2); node_add_child(pdata, $$, $3); node_add_leaf(pdata, $$, ')', @4); }
    ;

initializer
    : assignment_expression { $$ = make_node(pdata, "initializer.0"); node_add_child(pdata, $$, $1); }
    | '{' initializer_list '}' { $$ = make_node(pdata, "initializer.1"); node_add_leaf(pdata, $$, '{', @1); node_add_child(pdata, $$, $2); node_add_leaf(pdata, $$, '}', @3); }
    | '{' initializer_list ',' '}' { $$ = make_node(pdata, "initializer.2"); node_add_leaf(pdata, $$, '{', @1); node_add_child(pdata, $$, $2); node_add_leaf(pdata, $$, ',', @3); node_add_leaf(pdata, $$, '}', @4); }
    ;

initializer_list
    : initializer { $$ = make_node(pdata, "initializer_list.0"); node_add_child(pdata, $$, $1); }
    | initializer_list ',' initializer { $$ = make_node(pdata, "initializer_list.1"); node_add_child(pdata, $$, $1); node_add_leaf(pdata, $$, ',', @2); node_add_child(pdata, $$, $3); }
    ;

statement
    : labeled_statement { $$ = make_node(pdata, "statement.0"); node_add_child(pdata, $$, $1); }
    | compound_statement { $$ = make_node(pdata, "statement.1"); node_add_child(pdata, $$, $1); }
    | expression_statement { $$ = make_node(pdata, "statement.2"); node_add_child(pdata, $$, $1); }
    | selection_statement { $$ = make_node(pdata, "statement.3"); node_add_child(pdata, $$, $1); }
    | iteration_statement { $$ = make_node(pdata, "statement.4"); node_add_child(pdata, $$, $1); }
    | jump_statement { $$ = make_node(pdata, "statement.5"); node_add_child(pdata, $$, $1); }
    ;

labeled_statement
    : IDENTIFIER ':' statement { $$ = make_node(pdata, "labeled_statement.0"); node_add_leaf(pdata, $$, 'IDENTIFIER', @1); node_add_leaf(pdata, $$, ':', @2); node_add_child(pdata, $$, $3); }
    | CASE constant_expression ':' statement { $$ = make_node(pdata, "labeled_statement.1"); node_add_leaf(pdata, $$, 'CASE', @1); node_add_child(pdata, $$, $2); node_add_leaf(pdata, $$, ':', @3); node_add_child(pdata, $$, $4); }
    | DEFAULT ':' statement { $$ = make_node(pdata, "labeled_statement.2"); node_add_leaf(pdata, $$, 'DEFAULT', @1); node_add_leaf(pdata, $$, ':', @2); node_add_child(pdata, $$, $3); }
    ;

compound_statement
    : '{' '}' { $$ = make_node(pdata, "compound_statement.0"); node_add_leaf(pdata, $$, '{', @1); node_add_leaf(pdata, $$, '}', @2); }
    | '{' statement_list '}' { $$ = make_node(pdata, "compound_statement.1"); node_add_leaf(pdata, $$, '{', @1); node_add_child(pdata, $$, $2); node_add_leaf(pdata, $$, '}', @3); }
    | '{' declaration_list '}' { $$ = make_node(pdata, "compound_statement.2"); node_add_leaf(pdata, $$, '{', @1); node_add_child(pdata, $$, $2); node_add_leaf(pdata, $$, '}', @3); }
    | '{' declaration_list statement_list '}' { $$ = make_node(pdata, "compound_statement.3"); node_add_leaf(pdata, $$, '{', @1); node_add_child(pdata, $$, $2); node_add_child(pdata, $$, $3); node_add_leaf(pdata, $$, '}', @4); }
    ;

declaration_list
    : declaration { $$ = make_node(pdata, "declaration_list.0"); node_add_child(pdata, $$, $1); }
    | declaration_list declaration { $$ = $1; node_add_child(pdata, $$, $2); }
    ;

statement_list
    : statement { $$ = make_node(pdata, "statement_list.0"); node_add_child(pdata, $$, $1); }
    | statement_list statement { $$ = $1; node_add_child(pdata, $$, $2); }
    ;

expression_statement
    : ';' { $$ = make_node(pdata, "expression_statement.0"); node_add_leaf(pdata, $$, ';', @1); }
    | expression ';' { $$ = make_node(pdata, "expression_statement.1"); node_add_child(pdata, $$, $1); node_add_leaf(pdata, $$, ';', @2); }
    ;

selection_statement
    : IF '(' expression ')' statement { $$ = make_node(pdata, "selection_statement.0"); node_add_leaf(pdata, $$, 'IF', @1); node_add_leaf(pdata, $$, '(', @2); node_add_child(pdata, $$, $3); node_add_leaf(pdata, $$, ')', @4); node_add_child(pdata, $$, $5); }
    | IF '(' expression ')' statement ELSE statement { $$ = make_node(pdata, "selection_statement.1"); node_add_leaf(pdata, $$, 'IF', @1); node_add_leaf(pdata, $$, '(', @2); node_add_child(pdata, $$, $3); node_add_leaf(pdata, $$, ')', @4); node_add_child(pdata, $$, $5); node_add_leaf(pdata, $$, 'ELSE', @6); node_add_child(pdata, $$, $7); }
    | SWITCH '(' expression ')' statement { $$ = make_node(pdata, "selection_statement.2"); node_add_leaf(pdata, $$, 'SWITCH', @1); node_add_leaf(pdata, $$, '(', @2); node_add_child(pdata, $$, $3); node_add_leaf(pdata, $$, ')', @4); node_add_child(pdata, $$, $5); }
    ;

iteration_statement
    : WHILE '(' expression ')' statement { $$ = make_node(pdata, "iteration_statement.0"); node_add_leaf(pdata, $$, 'WHILE', @1); node_add_leaf(pdata, $$, '(', @2); node_add_child(pdata, $$, $3); node_add_leaf(pdata, $$, ')', @4); node_add_child(pdata, $$, $5); }
    | DO statement WHILE '(' expression ')' ';' { $$ = make_node(pdata, "iteration_statement.1"); node_add_leaf(pdata, $$, 'DO', @1); node_add_child(pdata, $$, $2); node_add_leaf(pdata, $$, 'WHILE', @3); node_add_leaf(pdata, $$, '(', @4); node_add_child(pdata, $$, $5); node_add_leaf(pdata, $$, ')', @6); node_add_leaf(pdata, $$, ';', @7); }
    | FOR '(' expression_statement expression_statement ')' statement { $$ = make_node(pdata, "iteration_statement.2"); node_add_leaf(pdata, $$, 'FOR', @1); node_add_leaf(pdata, $$, '(', @2); node_add_child(pdata, $$, $3); node_add_child(pdata, $$, $4); node_add_leaf(pdata, $$, ')', @5); node_add_child(pdata, $$, $6); }
    | FOR '(' expression_statement expression_statement expression ')' statement { $$ = make_node(pdata, "iteration_statement.3"); node_add_leaf(pdata, $$, 'FOR', @1); node_add_leaf(pdata, $$, '(', @2); node_add_child(pdata, $$, $3); node_add_child(pdata, $$, $4); node_add_child(pdata, $$, $5); node_add_leaf(pdata, $$, ')', @6); node_add_child(pdata, $$, $7); }
    ;

jump_statement
    : GOTO IDENTIFIER ';' { $$ = make_node(pdata, "jump_statement.0"); node_add_leaf(pdata, $$, 'GOTO', @1); node_add_leaf(pdata, $$, 'IDENTIFIER', @2); node_add_leaf(pdata, $$, ';', @3); }
    | CONTINUE ';' { $$ = make_node(pdata, "jump_statement.1"); node_add_leaf(pdata, $$, 'CONTINUE', @1); node_add_leaf(pdata, $$, ';', @2); }
    | BREAK ';' { $$ = make_node(pdata, "jump_statement.2"); node_add_leaf(pdata, $$, 'BREAK', @1); node_add_leaf(pdata, $$, ';', @2); }
    | RETURN ';' { $$ = make_node(pdata, "jump_statement.3"); node_add_leaf(pdata, $$, 'RETURN', @1); node_add_leaf(pdata, $$, ';', @2); }
    | RETURN expression ';' { $$ = make_node(pdata, "jump_statement.4"); node_add_leaf(pdata, $$, 'RETURN', @1); node_add_child(pdata, $$, $2); node_add_leaf(pdata, $$, ';', @3); }
    ;

translation_unit
    : external_declaration { $$ = make_node(pdata, "translation_unit.0"); node_add_child(pdata, $$, $1); }
    | translation_unit external_declaration { $$ = $1; node_add_child(pdata, $$, $2); }
    ;

external_declaration
    : function_definition { $$ = make_node(pdata, "external_declaration.0"); node_add_child(pdata, $$, $1); }
    | declaration { $$ = make_node(pdata, "external_declaration.1"); node_add_child(pdata, $$, $1); }
    ;

function_definition
    : declaration_specifiers declarator declaration_list compound_statement { $$ = make_node(pdata, "function_definition.0"); node_add_child(pdata, $$, $1); node_add_child(pdata, $$, $2); node_add_child(pdata, $$, $3); node_add_child(pdata, $$, $4); }
    | declaration_specifiers declarator compound_statement { $$ = make_node(pdata, "function_definition.1"); node_add_child(pdata, $$, $1); node_add_child(pdata, $$, $2); node_add_child(pdata, $$, $3); }
    | declarator declaration_list compound_statement { $$ = make_node(pdata, "function_definition.2"); node_add_child(pdata, $$, $1); node_add_child(pdata, $$, $2); node_add_child(pdata, $$, $3); }
    | declarator compound_statement { $$ = make_node(pdata, "function_definition.3"); node_add_child(pdata, $$, $1); node_add_child(pdata, $$, $2); }
    ;