# C99 grammar with a few extensions needed to make more syntax tests designed for gcc/clang extensions work (e.g. they always support some c11/c23-isms because they originated as GNU extensions)

__COMMENTS ::= "//" | "#"
__COMMENT_PAIRS ::= /* */
__BRACKET_PAIRS ::= { } | ( ) | [ ]
# only used by the peekres command (not peekr, not regex terminals)
__RESERVED_WORDS ::=
    auto break case char const continue default do double else enum extern float for goto
    if inline __inline__ __inline int long
    register restrict __cdecl __stdcall __restrict__ return short signed sizeof static struct switch typedef union unsigned void volatile
    while _Bool _Complex _Imaginary _Float16 __bf16 __int128

S ::= translation_unit

declaration_specifiers ::=
    declaration_specifier $become declaration_specifiers_star
declaration_specifiers_star ::=
    @peekr(1, r`[\[\),;:=]`r) #empty
    | @peekr(0, r`__attribute__|__attribute`r)  declaration_specifiers_star_guarded
    | @peekr(1, r`\(`r) $become declaration_specifiers_star_guard2
    | $become declaration_specifiers_star_guarded
declaration_specifiers_star_guard2 ::=
    @peekr(2, r`\*|__attribute__|__attribute`r) $become declaration_specifiers_star_guarded
    | #empty
declaration_specifiers_star_guarded ::=
    @guard(is_declaration_indicator) declaration_specifier $become declaration_specifiers_star
    | #empty
declaration_specifier ::=
    @peekr(0, r`typedef|extern|static|auto|register`r) storage_class_specifier $become attribute_list
    | @peekr(0, r`const|restrict|__cdecl|__stdcall|__restrict__|volatile`r) type_qualifier $become attribute_list
    | @peekr(0, r`inline|__inline__|__inline`r) function_specifier $become attribute_list
    | type_specifier $become attribute_list

# gcc extension
attribute_list ::=
    @peekr(0, r`__attribute__|__attribute`r) attribute $become attribute_list
    | #empty

storage_class_specifier ::=
    @auto R`typedef|extern|static|auto|register`r
type_specifier ::=
    @auto R`void|char|short|int|__builtin_va_list|long|float|double|signed|unsigned|_Bool|_Complex|_Imaginary`r
    | @peek(0, "enum") enum_specifier
    | @peekr(0, r`struct|union`r) struct_or_union_specifier
    | @peekr(0, r`typeof|__typeof__`r) typeof_specifier
    | typedef_name
type_qualifier ::=
    @auto R`const|restrict|__cdecl|__stdcall|__restrict__|volatile`r

typeof_specifier ::=
    R`typeof|__typeof__`r "(" many_balanced ")"
struct_or_union_specifier ::=
    struct_or_union $become struct_or_union_specifier_next
struct_or_union_specifier_next ::=
    @peekr(0, r`__attribute__|__attribute`r) attribute_list $become struct_or_union_specifier_info
    | $become struct_or_union_specifier_info
struct_or_union_specifier_info ::=
    @auto "{" !hook(scope_push) struct_declaration_list !hook(scope_pop) "}"
    | identifier attribute_list $become struct_or_union_specifier_info2
struct_or_union_specifier_info2 ::=
    @auto "{" !hook(scope_push) struct_declaration_list !hook(scope_pop) "}" | #empty

struct_or_union ::=
    @auto "struct" | "union"
struct_declaration_list ::=
    @peek(0, "}") #empty
    | struct_declaration $become struct_declaration_star
struct_declaration_star ::=
    @peek(0, "}") #empty (FOLLOW set logic)
    | struct_declaration $become struct_declaration_star
struct_declaration ::=
    specifier_qualifier_list struct_declarator_list ";"

specifier_qualifier_list ::=
    specifier_qualifier specifier_qualifier_star
specifier_qualifier ::=
    @peekr(0, r`const|restrict|__cdecl|__stdcall|__restrict__|volatile`r) type_qualifier
    | type_specifier
specifier_qualifier_star ::=
    @peekr(1, r`[\[;:=]`r) #empty
    | @peekr(0, r`const|restrict|__cdecl|__stdcall|__restrict__|volatile`r) type_qualifier $become specifier_qualifier_star
    | @guard(is_type_specifier_start) type_specifier $become specifier_qualifier_star
    | #empty
struct_declarator_list ::=
    struct_declarator $become struct_declarator_list_tail
struct_declarator_list_tail ::=
    @auto "," struct_declarator $become struct_declarator_list_tail
    | #empty

struct_declarator ::=
    @peek(0, ";") #empty
    | @auto ":" constant_expression
    | declarator $become struct_declarator_declarator_tail
struct_declarator_declarator_tail ::=
    @auto ":" constant_expression
    | #empty

enum_specifier ::=
    "enum" attribute_list $become enum_specifier_contents
enum_specifier_contents ::=
    @peek(0, "{") $become enum_specifier_info
    | identifier $become enum_specifier_info_maybe
enum_specifier_info_maybe ::=
    @peek(0, "{") $become enum_specifier_info
    | #empty
enum_specifier_info ::=
    "{" enumerator_list "}" !hook(enums_log)

enumerator_list ::=
    enumerator $become enumerator_list_tail
enumerator_list_tail ::=
    @auto "," $become enumerator_list_tail2
    | #empty
enumerator_list_tail2 ::=
    @peek(0, "}") #empty # FOLLOW set logic
    | enumerator $become enumerator_list_tail
enumerator ::=
    enumeration_constant attribute_list $become enumerator_value
enumerator_value ::=
    @auto "=" constant_expression
    | #empty
function_specifier ::=
    @auto "inline" | @auto "__inline__" | "__inline"

identifier_list ::=
    identifier $become identifier_list_tail
identifier_list_tail ::=
    @auto "," identifier $become identifier_list_tail
    | #empty

pointer ::=
    pointer_info $become pointer_star
pointer_info ::=
    "*" type_qualifier_star
pointer_star ::=
    @peek(0, "*") pointer_info $become pointer_star
    | #empty

type_qualifier_list ::=
    type_qualifier type_qualifier_star
type_qualifier_star ::=
    @peekr(0, r`const|restrict|__cdecl|__stdcall|__restrict__|volatile`r) type_qualifier $become type_qualifier_star
    | #empty

parameter_type_list ::=
    parameter_declaration $become parameter_list_tail
parameter_list_tail ::=
    @auto "," $become parameter_list_tail2
    | @peek(0, ")") #empty # FOLOW set logic
parameter_list_tail2 ::=
    @auto "..."
    | parameter_declaration $become parameter_list_tail
parameter_declaration ::=
    attribute_list declaration_specifiers attribute_list parameter_declarator !hook(typedefs_log)
parameter_declarator ::=
    @peek(0, "*") pointer $become parameter_declarator2
    | @peekr(0, r`\(|\[`r) NA_direct_abstract_declarator
    | @peekr(0, r`[^,)]*`r) declarator
    | #empty (no variable name)
parameter_declarator2 ::=
    @peekr(0, r`\(|\[`r) NA_direct_abstract_declarator
    | @peekr(0, r`[^,)]*`r) direct_declarator attribute_list
    | #empty (no variable name)

type_name ::=
    specifier_qualifier_list abstract_declarator_maybe
abstract_declarator_maybe ::=
    @peekr(0, r`\(|\[|\*`r) abstract_declarator
    | #empty

declarator ::=
    @peek(0, "*") pointer attribute_list direct_declarator maybe_asm_statement attribute_list
    | direct_declarator maybe_asm_statement attribute_list
direct_declarator ::=
    @auto "(" attribute_list declarator ")" $become direct_declarator_corelist
    | attribute_list identifier direct_declarator_corelist

direct_declarator_core ::=
    @auto "[" any_direct_declarator_core_sq
    | "(" direct_declarator_core_p
direct_declarator_core_p ::=
    @auto ")"
    | @auto "..." ")"
    | @peekr(0, r`__attribute__|__attribute`r) !hook(scope_push) parameter_type_list !hook(scope_pop) ")"
    | @guard(is_declaration_indicator) !hook(scope_push) parameter_type_list !hook(scope_pop) ")"
    | !hook(scope_push) identifier_list !hook(scope_pop) ")" # Blame this rule specifically for concrete and abstract declarators not being merged.
direct_declarator_corelist ::=
    @peekr(0, r`\[|\(`r) direct_declarator_core $become direct_declarator_corelist
    | #empty

direct_abstract_declarator_core ::=
    @auto "[" any_direct_declarator_core_sq
    | "(" direct_abstract_declarator_core_p
direct_abstract_declarator_core_p ::=
    @auto ")"
    | @auto "..." ")"
    | !hook(scope_push) parameter_type_list !hook(scope_pop) ")"
    # Note the lack of identifier_list rule.
direct_abstract_declarator_corelist ::=
    @peekr(0, r`\[|\(`r) direct_abstract_declarator_core direct_abstract_declarator_corelist
    | #empty


abstract_declarator ::=
    @peek(0, "*") pointer $become direct_abstract_declarator_maybe
    | direct_abstract_declarator
direct_abstract_declarator_maybe ::=
    @peekr(0, r`\(|\[`r) direct_abstract_declarator
    | #empty
direct_abstract_declarator ::=
    attribute_list abstract_declarator_wrapped_and_maybe direct_abstract_declarator_corelist
abstract_declarator_wrapped_and_maybe ::=
    @peek(0, "(") $become abstract_declarator_wrapped_and_maybe2
    | #empty
abstract_declarator_wrapped_and_maybe2 ::=
    @peekr(1, r`\*|\(|\[|__attribute__|__attribute`r) "(" attribute_list abstract_declarator ")"
    | #empty

# "name allowed" variants
NA_abstract_declarator ::=
    @peek(0, "*") pointer identifier_maybe NA_direct_abstract_declarator_maybe
    | identifier_maybe NA_direct_abstract_declarator
NA_direct_abstract_declarator_maybe ::=
    @peekr(0, r`\(|\[`r) NA_direct_abstract_declarator
    | #empty
NA_direct_abstract_declarator ::=
    attribute_list NA_abstract_declarator_wrapped_and_maybe direct_abstract_declarator_corelist
    # non-empty identifier_list terms are forbidden outside of function definitions
    # AND: function definitions cannot be abstract
    # THEREFORE: it's safe to use direct_abstract_declarator_corelist instead of direct_declarator_corelist
NA_abstract_declarator_wrapped_and_maybe ::=
    @peek(0, "(") $become NA_abstract_declarator_wrapped_and_maybe2
    | #empty
NA_abstract_declarator_wrapped_and_maybe2 ::=
    @peekr(1, r`\*|\(|\[|__attribute__|__attribute`r) "(" attribute_list NA_abstract_declarator ")"
    | #empty


# agnostic to what kind of declarator we're in
any_direct_declarator_core_sq ::=
    @auto "static" type_qualifier_star assignment_expression "]"
    | @peekr(0, r`const|restrict|__cdecl|__stdcall|__restrict__|volatile`r) type_qualifier_list $become any_direct_declarator_chunk_square3
    | @auto "]"
    | @peek(0, "*") $become any_declarator_chunk_square_ptrpart
    | assignment_expression "]"
any_direct_declarator_chunk_square3 ::=
    @auto "static" assignment_expression "]"
    | @auto "]"
    | @peek(0, "*") $become any_declarator_chunk_square_ptrpart
    | assignment_expression "]"
any_declarator_chunk_square_ptrpart ::=
    @peek(1, "]") "*" "]"
    | assignment_expression "]"



typedef_name ::=
    identifier
initializer ::=
    @auto "{" initializer_list $become initializer_comma_chomp
    | assignment_expression
initializer_comma_chomp ::=
    @auto "," "}"
    | "}"
designated_initializer ::=
    @peekr(0, r`[\[.]`r) designation initializer
    | initializer
initializer_list ::=
    @peek(0, "}") #empty # C23 extension: empty initializer lists
    | designated_initializer $become initializer_list_tail
initializer_list_tail ::=
    @auto "," $become initializer_list_tail_guarded
    | #empty
initializer_list_tail_guarded ::=
    @peek(0, "}") #empty
    | designated_initializer $become initializer_list_tail
designation ::=
    designator_list "="
designator_list ::=
    designator $become designator_star
designator_star ::=
    @peekr(0, r`[\[.]`r) designator $become designator_star
    | #empty

designator ::=
    @auto "[" constant_expression "]"
    | "." identifier

primary_expression ::=
    @peekr(0, r`[L]?\x22.*\x22`r) string_literal
    | @auto "_Generic" "(" many_balanced ")"
    | @guard(is_ident_not_known_enum) $become_as identifier
    | $become constant
postfix_expression_atom ::=
    @auto "(" $become postfix_expression_atom_choice
    | $become_as primary_expression
postfix_expression_atom_choice ::=
    @guard(is_type_specifier_start) $become_as compound_lit_expression
    | $become_as primary_expression_2

primary_expression_2 ::= expression ")"
compound_lit_expression ::= type_name ")" "{" initializer_list initializer_comma_chomp

postfix_expression ::= 
    @peekr(0, r`__builtin_.*`r) $become postfix_expression_builtin_guard
    | postfix_expression_atom $become postfix_trailer_star
postfix_expression_builtin_guard ::=
    @peek(1, "(") ext_builtin
    | postfix_expression_atom $become postfix_trailer_star
ext_builtin ::= # builtins can take non-expressions as arguments. bypass argument parsing.
    R`__builtin_[a-zA-Z_0-9]*`r "(" many_balanced ")" $become postfix_trailer_star

postfix_trailer_star ::=
    @peekr(0, r`\[|\(|\.|->|\+\+|--`r) postfix_trailer $become postfix_trailer_star
    | #empty
postfix_trailer ::=
    @auto "[" expression "]"
    | @auto "(" $become postfix_trailer_paren
    | @auto "." identifier
    | @auto "->" identifier
    | @auto "++"
    | "--"
postfix_trailer_paren ::=
    @auto ")"
    | $become_as argument_expression_list
argument_expression_list ::=
    assignment_expression $become argument_expression_list_tail
argument_expression_list_tail ::=
    @auto "," assignment_expression $become argument_expression_list_tail
    | ")" #empty
unary_expression ::=
    @auto "++" $become unary_expression
    | @auto "--" $become unary_expression
    | @auto "sizeof" $become sizeof_argument
    | @auto "_Alignof" $become sizeof_argument
    | @auto R`[&*\+\-~!]`r cast_expression
    | $become_as postfix_expression

sizeof_argument ::=
    @peek(0, "(") $become sizeof_argument_guarded
    | $become_as unary_expression
sizeof_argument_guarded ::=
    @guard(is_postfix_not_sizeof_argument) assignment_expression
    | "(" $become sizeof_argument_trail

sizeof_argument_trail ::=
    @guard(is_type_specifier_start) type_name ")"
    | assignment_expression ")"

cast_expression ::=
    @guard(is_primitive_cast) "(" type_name ")" $become cast_expression
    | $become_as unary_expression

infix_expression ::=
    cast_expression $become infix_expression_tail
infix_expression_tail ::=
    @auto R`(?x)(?:\
        \|\||&&|==|>=|<=|!=|>>|<<|\
        \*|\+|\^|\||&|-|/|%|>|<\
    )`r cast_expression $become infix_expression_tail
    | !hook(fix_infix_precedence) #empty

conditional_expression ::=
    infix_expression $become conditional_expression_tail
conditional_expression_tail ::=
    @auto "?" expression ":" conditional_expression | #empty

# The C standard tried encoding semantics in syntax.
# It doesn't even work. Examples: ++x = 5; and (x + 3) = 5;
#assignment_expression ::=
#    conditional_expression
#    unary_expression r`(?:\*|/|%|\+|-|<<|>>|&|\^|\|)?=`r assignment_expression
# Slightly more permissive rule that is less incredibly awful to parse:
assignment_expression ::=
    @auto "__extension__" $become assignment_expression
    | conditional_expression $become assignment_expression_tail
assignment_expression_tail ::=
    @auto R`(?:\*|/|%|\+|-|<<|>>|&|\^|\|)?=`r conditional_expression $become assignment_expression_tail
    | #empty
# I suspect this is roughly what real C compilers do and that they enforce the semantics rule afterwards.
# For example in GCC this is a semantics error instead of a syntax error, saying that x+5 isn't a valid lvalue. Clang fails similarly:
# int myfunc() {
#     int* x;
#     5+x = 3;
# }
# ... and using x+5 instead of 5+x gives the same error. And using ++0 (yes, really) instead of 5+x gives the same error.

expression ::=
    @auto "__extension__" $become expression
    | @peekr(1, r`\]`r) primary_expression $become expression_tail
    | assignment_expression $become expression_tail
expression_tail ::=
    @auto "," assignment_expression $become expression_tail
    | #empty

constant_expression ::=
    $become conditional_expression

maybe_asm_statement ::=
    @peekr(0, r`__asm|__asm__|asm`r) asm_statement
    | #empty

statement ::=
    @peekr(0, r`__attribute__|__attribute`r) attribute_list $become core_statement
    | $become core_statement

core_statement ::=
    @peekr(0, r`__asm|__asm__|asm`r) asm_statement ";"
    | @guard(is_label) $become_as labeled_statement
    | @peekr(0, r`switch|if`r) $become_as selection_statement
    | @peekr(0, r`while|do|for`r) $become_as iteration_statement
    | @peekr(0, r`goto|continue|break|return`r) $become_as jump_statement
    | @peek(0, "{") $become_as scoped_compound_statement
    | $become_as expression_statement

# gcc extension
asm_statement ::=
    R`__asm|__asm__|asm`r asm_qualifiers "(" asm_body ")"
asm_qualifiers ::=
    @auto "volatile" $become asm_qualifiers
    | @auto "__volatile__" $become asm_qualifiers
    | @auto "inline" $become asm_qualifiers
    | @auto "__inline__" $become asm_qualifiers
    | @auto "goto" $become asm_qualifiers
    | #empty
asm_body ::=
    string_literal asm_body_2
asm_body_2 ::=
    @auto ":" asm_var_regs asm_body_3
    | #empty
asm_body_3 ::=
    @auto ":" asm_var_regs asm_body_4
    | #empty
asm_body_4 ::=
    @auto ":" asm_clobbers asm_body_5
    | #empty
asm_body_5 ::=
    @auto ":" asm_gotos
    | #empty

asm_var_regs ::=
    @auto "[" identifier "]" string_literal "(" expression ")" $become asm_var_regs_tail
    | @peekr(0, r`[L]?\x22.*\x22`r) string_literal "(" expression ")" $become asm_var_regs_tail
    | #empty
asm_var_regs_tail ::=
    @auto "," $become asm_var_regs
    | #empty

asm_clobbers ::=
    @peek(0, "[") "[" identifier "]" string_literal $become asm_var_regs_tail
    | @peekr(0, r`[L]?\x22.*\x22`r) string_literal $become asm_var_regs_tail
    | #empty
asm_clobbers_tail ::=
    @auto "," $become asm_clobbers
    | #empty

asm_gotos ::=
    @peekr(0, r`[a-zA-Z_].*`r) identifier $become asm_var_regs_tail
    | #empty
asm_gotos_tail ::=
    @auto "," $become asm_gotos
    | #empty



labeled_statement ::=
    @auto "case" constant_expression ":" statement
    | @auto "default" ":" statement
    | identifier ":" statement
scoped_compound_statement ::=
    !hook(scope_push) compound_statement !hook(scope_pop)
compound_statement ::=
    @peekr(0, r`__attribute__|__attribute`r) attribute_list $become compound_statement
    | "{" $become compound_statement_trail
attribute ::=
    $pruned r`__attribute__|__attribute`r "(" "(" many_balanced ")" ")"
compound_statement_trail ::=
    @auto "}"
    | block_item_list "}"
block_item_list ::=
    block_item $become block_item_star
block_item_star ::=
    @peek(0, "}") #empty # FOLLOW set logic
    | block_item $become block_item_star
block_item ::=
    attribute_list $become block_item_unguarded
block_item_unguarded ::=
    @peek(1, ":") $become_as statement # namelookup-bug-2.c
    | @guard(is_declaration_indicator) $become_as declaration
    | $become_as statement
expression_statement ::=
    @auto ";"
    | expression ";"
selection_statement ::=
    @auto "switch" $pruned "(" expression ")" statement
    | "if" $pruned "(" expression ")" statement $become else_maybe
else_maybe ::=
    @auto "else" statement
    | #empty
iteration_statement ::=
    @auto "while" $pruned "(" expression ")" statement
    | @auto "do" $pruned statement "while" "(" expression ")" ";"
    | "for" $pruned "(" forloop_head_contents ")" statement
forloop_head_contents ::=
    @auto ";" $become forloop_head_contents2
    | @guard(is_declaration_indicator) declaration $become forloop_head_contents2
    | expression ";" $become forloop_head_contents2
forloop_head_contents2 ::=
    @auto ";" $become forloop_head_contents3
    | expression ";" $become forloop_head_contents3
forloop_head_contents3 ::=
    @peek(0, ")") #empty # FOLLOW set logic
    | expression
jump_statement ::=
    @auto "goto" identifier $pruned ";"
    | @auto "continue" $pruned ";"
    | @auto "break" $pruned ";"
    | "return" $become return_operand
return_operand ::=
    @auto ";"
    | expression $pruned ";"
translation_unit ::=
    external_declaration $become external_declaration_star
external_declaration_star ::=
    @eof #empty
    | external_declaration $become external_declaration_star


declaration ::=
    declaration_specifiers $become declaration_tail
declaration_tail ::=
    @auto ";"
    | init_declarator_list ";" !hook(typedefs_log)
#function_definition:
#    declaration_specifiers declarator $become function_definition_cont
#external_declaration ::=
#    @guard(is_funcdef) function_definition
#    | declaration
# UHHHH yeah it looks like this isn't how real C compilers do it and also it's insane.
# The insanity is the arbitrarily long ambiguity until the first declarator stops.
# Let's do something tractible instead.

external_declaration ::=
    @auto ";" #stray semicolon
    | @peekr(0, r`(?:_S|s)tatic_assert`r) static_assert
    | attribute_list $become external_declaration_deattributed
    
external_declaration_deattributed ::=
    @peekr(0, r`__asm|__asm__|asm`r) asm_statement ";"
    | declaration_specifiers $become extdec_chooser

extdec_chooser ::=
    @peek(0, ";") $become_as empty_declaration
    | declarator $become extdec_declarator_choice
empty_declaration ::= ";"
extdec_declarator_choice ::=
    @peekr(0, r`=|;|,|asm|__asm|__asm__`r) $become_as extdec_declaration
    | $become_as function_definition
extdec_declaration_init ::=
    init_declarator $become init_declarator_list_tail 
extdec_declaration ::=
    @auto "," init_declarator_list ";" !hook(typedefs_log)
    | @auto "=" initializer $become extdec_declaration_init_rest
    | ";" !hook(typedefs_log)
extdec_declaration_init_rest ::=
    @auto "," init_declarator_list ";" !hook(typedefs_log)
    | ";" !hook(typedefs_log)

function_definition ::=
    attribute_list func_knr_declaration_star $become func_scope

func_scope ::=
    @peekr(0, r`__attribute__|__attribute`r) attribute_list $become func_scope
    | @peek(0, "{") !hook(scope_push) !hook(entered_function) compound_statement !hook(scope_pop)

func_knr_declaration_star ::=
    @peek(0, "{") #empty
    | declaration $become func_knr_declaration_star

static_assert ::=
    R`(?:_S|s)tatic_assert`r "(" assignment_expression many_balanced ")" ";"


init_declarator_list ::=
    init_declarator $become init_declarator_list_tail
init_declarator_list_tail ::=
    @auto "," init_declarator $become init_declarator_list_tail
    | #empty
init_declarator ::=
    declarator $become init_declarator_tail
init_declarator_tail ::=
    @auto "=" initializer
    | #empty

constant ::=
      @auto R`(?x)   (?:(?:  0[xX][a-fA-F0-9]+  |  [1-9][0-9]*  |  0[0-7]*  )       (?:[uU](?:ll|LL|l|L)?|(?:ll|LL|l|L)[uU]?)?)`r
    | @auto R`(?x)   (?:(?:   (?: [0-9]*\.[0-9]+ | [0-9]+\. )  (?:[eE](?:-|\+)?[0-9]+)?   |   [0-9]+[eE](?:-|\+)?[0-9]+   )   (?:f16|F16|f|l|F|L)?)`r
    | @auto R`(?x)   (?:0[xX]  (?:  [a-fA-F0-9]*\.[a-fA-F0-9]+  |  [a-fA-F0-9]+(?:\.)?  )  [pP](?:-|\+)?  [0-9]+   (?:f16|F16|f|l|F|L)?)`r
    #| @auto R`(?x)   [L]?  \x27  (?:  [^\x27\\\n]  |  \\[\x27\x22\\abefnrtv?]  |  \\[0-7]{1,3}  |  \\x[0-9a-fA-F]+  |  (?:\\u[a-fA-F0-9]{1,4}|\\U[a-fA-F0-9]{1,8})  )+  \x27`r
    | @auto R`(?x)   [L]?  \x27  (?:  [^\x27\\\n]  |  \\[^xuU]  |  \\[0-7]{1,3}  |  \\x[0-9a-fA-F]+  |  (?:\\u[a-fA-F0-9]{1,4}|\\U[a-fA-F0-9]{1,8})  )+  \x27`r
    | @guard(is_known_enum) enumeration_constant

enumeration_constant ::=
    identifier

string_literal ::=
    string_literal_core $become string_literal_star
string_literal_star ::=
    #@peekr(0, r`(?x)  [L]?  \x22  (?:[^\x22\\\n]  |  \\[\x27\x22\\abefnrtv?]  |  \\[0-7]{1,3}  |  \\x[0-9a-fA-F]+  |  (?:\\u[a-fA-F0-9]{1,4}|\\U[a-fA-F0-9]{1,8})  )*\x22`r)
    @peekr(0, r`(?x)  [L]?  \x22  (?:[^\x22\\\n]  |  \\[^xuU]  |  \\[0-7]{1,3}  |  \\x[0-9a-fA-F]+  |  (?:\\u[a-fA-F0-9]{1,4}|\\U[a-fA-F0-9]{1,8})  )*\x22`r)
    string_literal_core $become string_literal_star
    | #empty
string_literal_core ::=
    #R`(?x)  [L]?  \x22  (?:[^\x22\\\n]  |  \\[\x27\x22\\abefnrtv?]  |  \\[0-7]{1,3}  |  \\x[0-9a-fA-F]+  |  (?:\\u[a-fA-F0-9]{1,4}|\\U[a-fA-F0-9]{1,8})  )*\x22`r
    R`(?x)  [L]?  \x22  (?:[^\x22\\\n]  |  \\[^xuU]  |  \\[0-7]{1,3}  |  \\x[0-9a-fA-F]+  |  (?:\\u[a-fA-F0-9]{1,4}|\\U[a-fA-F0-9]{1,8})  )*\x22`r

identifier_maybe ::=
    @auto R`(?:[a-zA-Z_]|(?:\\u[a-fA-F0-9]{1,4}|\\U[a-fA-F0-9]{1,8}))(?:[a-zA-Z_]|(?:\\u[a-fA-F0-9]{1,4}|\\U[a-fA-F0-9]{1,8})|[0-9])*`r
    | #empty
identifier ::=
    R`(?:[a-zA-Z_]|(?:\\u[a-fA-F0-9]{1,4}|\\U[a-fA-F0-9]{1,8}))(?:[a-zA-Z_]|(?:\\u[a-fA-F0-9]{1,4}|\\U[a-fA-F0-9]{1,8})|[0-9])*`r

# Dummy for tokenization
_token_dummy ::=
    r`(?x)(?:(?:   (?: [0-9]*\.[0-9]+ | [0-9]+\. )  (?:[eE](?:-|\+)?[0-9]+)?   |   [0-9]+[eE](?:-|\+)?[0-9]+   )   (?:f16|F16|f|l|F|L)?)|\
          (?:0[xX]  (?:  [a-fA-F0-9]*\.[a-fA-F0-9]+  |  [a-fA-F0-9]+(?:\.)?  )  [pP](?:-|\+)?  [0-9]+   (?:f16|F16|f|l|F|L)?)|\
          (?:[L]?  \x27  (?:[^\x27\\\n]  |  \\[^xuU]  |  \\[0-7]{1,3}  |  \\x[0-9a-fA-F]+  |  (?:\\u[a-fA-F0-9]{1,4}|\\U[a-fA-F0-9]{1,8})  )+\x27)|\
          (?:[L]?  \x22  (?:[^\x22\\\n]  |  \\[^xuU]  |  \\[0-7]{1,3}  |  \\x[0-9a-fA-F]+  |  (?:\\u[a-fA-F0-9]{1,4}|\\U[a-fA-F0-9]{1,8})  )*\x22)|\
          (?:(?:[a-zA-Z_]|(?:\\u[a-fA-F0-9]{1,4}|\\U[a-fA-F0-9]{1,8}))(?:[a-zA-Z_]|(?:\\u[a-fA-F0-9]{1,4}|\\U[a-fA-F0-9]{1,8})|[0-9])*)|\
          (?:(?:  0[xX][a-fA-F0-9]+  |  [1-9][0-9]*  |  0[0-7]*  )       (?:[uU](?:ll|LL|l|L)?|(?:ll|LL|l|L)[uU]?)?)|\
          (?:(?:\*|/|%|\+|-|<<|>>|&|\^|\|)=|<<|>>|<=|>=|==|!=|&&|\|\||[&*/\+\-~!%<>=\^\|])`r


many_balanced ::=
    @peek(0, ")") #empty
    | @peek(0, "]") #empty
    | @peek(0, "}") #empty
    | @auto "(" many_balanced ")" $become many_balanced
    | @auto "[" many_balanced "]" $become many_balanced
    | @auto "{" many_balanced "}" $become many_balanced
    | $any $become many_balanced
