keyword = @{ ("auto" | "break" | "case" | "char" | "const" | "continue" | "default" | "do" | "double" | "else" | "enum" | "extern" | "float" | "for" | "goto" | "if" | "inline" | "int" | "long" | "register" | "restrict" | "return" | "short" | "signed" | "sizeof" | "static" | "struct" | "switch" | "typedef" | "union" | "unsigned" | "void" | "volatile" | "while" | "_Bool" | "_Complex" | "_Imaginary") ~ !ASCII_ALPHANUMERIC }
nondigit = @{ 'a'..'z' | 'A'..'Z' | "_" }
digit = @{ '0'..'9' }
ident = @{ !digit ~ (nondigit | digit)+ }
long_long_suffix = @{ "ll" | "LL" }
long_suffix = @{ ^"l" }
unsigned_suffix = @{ ^"u" }
int_suffix = @{ unsigned_suffix ~ (long_long_suffix | long_suffix)? | (long_long_suffix | long_suffix) ~ unsigned_suffix? }
hexadec_digit = @{ digit | 'a'..'f' | 'A'..'F' }
octal_digit = @{ '0'..'7' }
nonzero_digit = @{ '1'..'9' }
hexadec_prefix = @{ "0x" | "0X" }
hexadec_lit = @{ hexadec_prefix ~ hexadec_digit+ }
octal_lit = @{ "0" ~ octal_digit* }
dec_lit = @{ nonzero_digit ~ digit* }
int = @{ hexadec_lit ~ int_suffix? | octal_lit ~ int_suffix? | dec_lit ~ int_suffix? }
float_suffix = @{ "f" | "l" | "F" | "L" }
hexadec_digit_seq = @{ hexadec_digit+ }
digit_seq = @{ digit+ }
sign = @{ "+" | "-" }
bin_exp_part = @{ ^"p" ~ sign? ~ digit_seq }
hexadec_frac_lit = @{ hexadec_digit_seq? ~ "." ~ hexadec_digit_seq | hexadec_digit_seq ~ "." }
exp_part = @{ ^"e" ~ sign? ~ digit_seq }
frac_lit = @{ digit_seq? ~ "." ~ digit_seq | digit_seq ~ "." }
hexadec_float_lit = @{ hexadec_prefix ~ (hexadec_frac_lit | hexadec_digit_seq) ~ bin_exp_part ~ float_suffix? }
dec_float_lit = @{ frac_lit ~ exp_part? ~ float_suffix? | digit_seq ~ exp_part ~ float_suffix? }
float = @{ hexadec_float_lit | dec_float_lit }
hexadec_esc_seq = @{ "\\x" ~ hexadec_digit+ }
octal_esc_seq = @{ "\\" ~ octal_digit{1,3} }
simple_esc_seq = @{ "\\'" | "\\\"" | "\\?" | "\\\\" | "\\a" | "\\b" | "\\f" | "\\n" | "\\r" | "\\t" | "\\v" }
esc_seq = @{ simple_esc_seq | octal_esc_seq | hexadec_esc_seq }
c_char = @{ !("'" | "\\" | NEWLINE) ~ ANY | esc_seq }
char = @{ "L"? ~ "'" ~ c_char+ ~ "'" }
s_char = @{ !("\"" | "\\" | NEWLINE) ~ ANY | esc_seq }
string = @{ "L"? ~ "\"" ~ s_char+ ~ "\"" }
lit = { float | int | char | string }
punct = @{ "[" | "<:" | "]" | ":>" | "(" | ")" | "{" | "<%" | "}" | "%>" | "##" | "%:%:" | "#" | "%:" | "..." | "." | "->" | "++" | "--" | "==" | "=" | "*=" | "/=" | "%=" | "+=" | "-=" | "<<=" | ">>=" | "&=" | "^=" | "|=" | "/" | "%" | "<<" | ">>" | "<=" | ">=" | "<" | ">" | "!=" | "&&" | "||" | "&" | "*" | "+" | "-" | "~" | "!" | "|" | "^" | "?" | ":" | ";" | "," }
q_char = @{ !("\"" | NEWLINE) ~ ANY }
h_char = @{ !(">" | NEWLINE) ~ ANY }
header_name = @{ "<" ~ h_char* ~ ">" | "\"" ~ q_char* ~ "\""}
include = { "#" ~ "include" ~ header_name }
token = { include | lit | keyword | punct | ident }
tokentree = { WHITESPACE* ~ token* ~ WHITESPACE* }
tok_ident = { "#i" ~ ident ~ "#i" }
tok_lit = { "#l" ~ lit ~ "#l" }
p = _{ "#p" ~ "(" ~ "#p" }
q = _{ "#p" ~ ")" ~ "#p" }
s = _{ "#p" ~ ";" ~ "#p" }
c = _{ "#p" ~ "," ~ "#p" }
op_br = @{ "#p" ~ ("[" | "<:") ~ "#p" }
cl_br = @{ "#p" ~ ("]" | ":>") ~ "#p" }
op_brs = _{ "#p" ~ ("[" | "<:") ~ "#p" }
cl_brs = _{ "#p" ~ ("]" | ":>") ~ "#p" }
op_bc = _{ "#p" ~ ("{" | "<%") ~ "#p" }
cl_bc = _{ "#p" ~ ("}" | "%>") ~ "#p" }
primary_expr = { tok_lit | tok_ident | p ~ expr ~ q}
postfix = { op_brs ~ expr ~ cl_brs | op_parent ~ arg_expr_list? ~ cl_parent | (dot | arrow) ~ tok_ident | inc | dec }
dot = { "#p" ~ "." ~ "#p" }
arrow = { "#p" ~ "->" ~ "#p" }
inc = { "#p" ~ "++" ~ "#p" }
dec = { "#p" ~ "--" ~ "#p" }
op_parent = @{ "#p" ~ "(" ~ "#p" }
cl_parent = @{ "#p" ~ ")" ~ "#p" }
postfix_expr = { primary_expr ~ postfix* | p ~ type_name ~ q ~ op_bc ~ init_list ~ c? ~ cl_bc }
arg_expr_list = { assign_expr ~ (c ~ assign_expr)* }
unary_expr = { (inc | dec | sizeof) ~ unary_expr | (band | mul | plus | minus | bnot | not) ~ cast_expr | sizeof ~ p ~ type_name ~ q | postfix_expr }
band = { "#p" ~ "&" ~ "#p" }
mul = { "#p" ~ "*" ~ "#p" }
plus = { "#p" ~ "+" ~ "#p" }
minus= { "#p" ~ "-" ~ "#p" }
bnot = { "#p" ~ "~" ~ "#p" }
not = { "#p" ~ "!" ~ "#p" }
sizeof = { "sizeof" }
cast_expr = { p ~ type_name ~ q ~ cast_expr | unary_expr }
mul_expr = { cast_expr ~ ((mul | div | modulo) ~ cast_expr)* }
div = { "#p" ~ "/" ~ "#p" }
modulo = { "#p" ~ "%" ~ "#p" }
add_expr = { mul_expr ~ ((plus | minus) ~ mul_expr)* }
shift_expr = { add_expr ~ ((lshift | rshift) ~ add_expr)* }
lshift = { "#p" ~ "<<" ~ "#p" }
rshift = { "#p" ~ ">>" ~ "#p" }
rel_expr = { shift_expr ~ ((gt | lt | gte | lte) ~ shift_expr)* }
gt = { "#p" ~ "<" ~ "#p" }
lt = { "#p" ~ ">" ~ "#p" }
gte = { "#p" ~ "<=" ~ "#p" }
lte = { "#p" ~ ">=" ~ "#p" }
eq_expr = { rel_expr ~ ((eq | neq) ~ rel_expr)* }
eq = { "#p" ~ "==" ~ "#p" }
neq = { "#p" ~ "!=" ~ "#p" }
and_expr = { eq_expr ~ ("#p" ~ "&" ~ "#p" ~ eq_expr)* }
xor_expr = { and_expr ~ ("#p" ~ "^" ~ "#p" ~ and_expr)* }
or_expr = { xor_expr ~ ("#p" ~ "|" ~ "#p" ~ xor_expr)* }
log_and_expr = { or_expr ~ ("#p" ~ "&&" ~ "#p" ~ or_expr)* }
log_or_expr = { log_and_expr ~ ("#p" ~ "||" ~ "#p" ~ log_and_expr)* }
cond_expr = { (log_or_expr ~ "#p" ~ "?" ~ "#p" ~ expr ~ "#p" ~ ":" ~ "#p")* ~ log_or_expr }
assign_expr = { unary_expr ~ (assign | mul_assign | div_assign | mod_assign | add_assign | sub_assign | lshift_assign | rshift_assign | and_assign | xor_assign | or_assign) ~ assign_expr | cond_expr }
assign = { "#p" ~ "=" ~ "#p" }
mul_assign = { "#p" ~ "*=" ~ "#p" }
div_assign = { "#p" ~ "/=" ~ "#p" }
mod_assign = { "#p" ~ "%=" ~ "#p" }
add_assign = { "#p" ~ "+=" ~ "#p" }
sub_assign = { "#p" ~ "-=" ~ "#p" }
lshift_assign = { "#p" ~ "<<=" ~ "#p" }
rshift_assign = { "#p" ~ ">>=" ~ "#p" }
and_assign = { "#p" ~ "&=" ~ "#p" }
xor_assign = { "#p" ~ "^=" ~ "#p" }
or_assign = { "#p" ~ "|=" ~ "#p" }
const_expr = { cond_expr }
expr = { assign_expr ~ (c ~ assign_expr)* }
decl = { specs ~ init_decl_list? ~ s }
specs = { (storage_spec | type_qual | func_spec)* ~ type_spec ~ (storage_spec | type_spec | type_qual | func_spec)* | (storage_spec | type_qual | func_spec)* ~ typedef_name ~ (storage_spec | type_qual | func_spec)* }
init_decl_list = { init_decl ~ (c ~ init_decl)* }
init_decl = { declarator ~ "#p" ~ "=" ~ "#p" ~ init | declarator }
storage_spec = { "typedef" | "extern" | "static" | "auto" | "register" }
type_spec = { "void" | "char" | "short" | "int" | "long" | "float" | "double" | "signed" | "unsigned" | "_Bool" | "_Complex" | "_Imaginary" | struct_spec | enum_spec }
struct_spec = { struct_union ~ tok_ident? ~ op_bc ~ struct_declaration_list ~ cl_bc | struct_union ~ tok_ident }
struct_union = { "struct" | "union" }
struct_declaration_list = { struct_declaration+ }
struct_declaration = { spec_qual_list ~ struct_declarator_list ~ s }
spec_qual_list = { type_qual* ~ type_spec ~ (type_spec | type_qual)* | type_qual* ~ typedef_name ~ type_qual* }
struct_declarator_list = { struct_declarator ~ (c ~ struct_declarator)* }
struct_declarator = { declarator? ~ "#p" ~ ":" ~ "#p" ~ const_expr | declarator }
enum_spec = { "enum" ~ tok_ident? ~ op_bc ~ enum_list ~ c? ~ cl_bc | "enum" ~ tok_ident }
enum_list = { enumerator ~ (c ~ enumerator)* }
enumerator = { enum_const ~ "#p" ~ "=" ~ "#p" ~ const_expr | enum_const }
enum_const = { tok_ident }
type_qual = { "const" | "restrict" | "volatile" }
func_spec = { "inline" }
declarator = { pointer? ~ direct_declarator }
decl_postfix = { op_br ~ type_qual_list? ~ assign_expr? ~ cl_br | op_br ~ stat ~ type_qual_list? ~ assign_expr ~ cl_br | op_br ~ type_qual_list ~ stat ~ assign_expr ~ cl_br | op_br ~ type_qual_list? ~ mul ~ cl_br | p ~ param_type_list ~ q | p ~ ident_list? ~ q }
stat = { "static" }
direct_declarator = { (p ~ declarator ~ q | tok_ident) ~ decl_postfix* }
pointer = { (mul ~ type_qual_list?)+ }
type_qual_list = { type_qual+ }
param_type_list = { param_list ~ c ~ tripledot | param_list }
tripledot = { "#p" ~ "..." ~ "#p" }
param_list = { param_decl ~ (c ~ param_decl)* }
param_decl = { specs ~ declarator | specs ~ abstract_declarator?}
ident_list = { tok_ident ~ (c ~ tok_ident)* }
type_name = { spec_qual_list ~ abstract_declarator? }
abstract_declarator = { pointer? ~ direct_abstract_declarator | pointer }
direct_abstract_declarator_postfix = { op_br ~ assign_expr? ~ cl_br | op_br ~ mul ~ cl_br | p ~ param_type_list? ~ q }
direct_abstract_declarator = { (p ~ abstract_declarator ~ q)? ~ direct_abstract_declarator_postfix+ | p ~ abstract_declarator ~ q }
typedef_name = { tok_ident }
init = { assign_expr | op_bc ~ init_list ~ c? ~ cl_bc }
init_list = { designation? ~ init ~ (c ~ designation? ~ init)* }
designation = { designator_list ~ "#p" ~ "=" ~ "#p" }
designator_list = { designator+ }
designator = { op_brs ~ const_expr ~ cl_brs | "#p" ~ "." ~ "#p" ~ tok_ident }
stmt = { labeled_stmt | compound_stmt | expr_stmt | select_stmt | iter_stmt | jump_stmt }
labeled_stmt = { "case" ~ const_expr ~ "#p" ~ ":" ~ "#p" ~ stmt | "default" ~ "#p" ~ ":" ~ "#p" ~ stmt | tok_ident ~ "#p" ~ ":" ~ "#p" ~ stmt}
compound_stmt = { op_bc ~ block_item_list? ~ cl_bc }
block_item_list = { block_item+ }
block_item = { decl | stmt }
expr_stmt = { expr? ~ s }
select_stmt = { "if" ~ p ~ expr ~ q ~ stmt ~ "else" ~ stmt | "if" ~ p ~ expr ~ q ~ stmt | switch ~ p ~ expr ~ q ~ stmt }
switch = { "switch" }
iter_stmt = { "while" ~ p ~ expr ~ q ~ stmt | do2 ~ stmt ~ "while" ~ p ~ expr ~ q | "for" ~ p ~ decl ~ expr? ~ s ~ expr? ~ q ~ stmt | "for" ~ p ~ expr? ~ s ~ expr? ~ s ~ expr? ~ q ~ stmt}
do2 = { "do" }
jump_stmt = { "goto" ~ tok_ident ~ s | cont ~ s | br ~ s | "return" ~ expr? ~ s }
cont = { "continue" }
br = { "break" }
trans_unit = { extern_decl+ }
extern_decl = { fn_def | decl }
fn_def = { specs ~ declarator ~ decl_list? ~ compound_stmt }
decl_list = { decl+ }
WHITESPACE = _{ WHITE_SPACE }
COMMENT = _{ "/*" ~ (!"*/" ~ ANY)* ~ "*/" | "//" ~ (!"\n" ~ ANY)* }