grammar cardinal::Grammar is PCT::Grammar;

token TOP { <comp_stmt> [ $ || <panic: Syntax error> ] {*} }

## XXX figure out if this \n* thing is good here. token comp_stmt { \n* <stmts> <.term>* {*} }

rule stmts { [ <stmt> [ <.term>+ <stmt> ]* ]? {*} }

rule term { \n | ';' }

rule basic_stmt { | <alias> {*} #= alias | <expr> {*} #= expr | <begin> {*} #= begin | <end> {*} #= end }

rule alias { 'alias' <fname> <fname> {*} }

rule stmt { <basic_stmt> <stmt_mod>* {*} }

rule stmt_mod { $<sym>=[if|while|unless|until] <expr> {*} }

rule expr { <basic_expr> [$<op>=['and'|'or'] <expr>]? {*} }

rule basic_expr { | <assignment> {*} #= assignment | <command> {*} #= command | <arg> {*} #= arg | <not_command> {*} #= not_command | <not_expr> {*} #= not_expr }

rule not_expr { 'not' <expr> {*} }

rule begin { 'BEGIN' '{' <comp_stmt> '}' {*} }

rule end { 'END' '{' <comp_stmt> '}' {*} }

rule assignment { <mlhs> '=' <mrhs> {*} }

rule mlhs { <mlhs_item> {*} }

rule mlhs_item { | <lhs> {*} #= lhs | '(' <mlhs> ')' {*} #= mlhs }

rule lhs { | <variable> {*} #= variable | <indexed_variable> {*} #= indexed_variable | <member_variable> {*} #= member_variable }

rule indexed_variable { <primary> '[' <args>? ']' {*} }

rule member_variable { <primary> '.' <identifier> {*} }

rule command { | <call> {*} #= call | <super_call> {*} #= super_call }

rule call { [ <primary> $<dot>=['.'|'::'] ]? <operation> <call_args> {*} }

rule super_call { 'super' <call_args> {*} }

rule not_command { '!' <command> {*} }

rule operation { <identifier> ['!'|'?']? {*} }

rule call_args { | <args> {*} }

rule variable { | <varname> {*} #= varname | 'nil' #= nil | 'self' #= self }

rule varname { <!reserved_word> [ <global> {*} #= global | <instance_variable> {*} #= instance_variable | <local_variable> {*} #= local_variable ] }

rule mrhs { <args> {*} }

rule args { <arg> [',' <arg>]* {*} }

rule 'arg' is optable { ... }

rule basic_primary { | <literal> {*} #= literal | <variable> {*} #= variable | <array> {*} #= array | <hash> {*} #= hash | <functiondef> {*} #= functiondef | <if_stmt> {*} #= if_stmt | <while_stmt> {*} #= while_stmt | <unless_stmt> {*} #= unless_stmt | <module> {*} #= module | <begin_end> {*} #= begin_end | <pcomp_stmt> {*} #= pcomp_stmt | <scope_identifier> {*} #= scope_identifier }

rule primary { <basic_primary> <post_primary_expr>* {*} }

rule post_primary_expr { | <scope_identifier> {*} #= scope_identifier | '[' <args>? ']' {*} #= args }

rule scope_identifier { '::' <identifier> {*} }

rule pcomp_stmt { '(' <comp_stmt> ')' {*} }

rule if_stmt { 'if' <expr> <.then> <comp_stmt> ['elsif' <expr> <.then> <comp_stmt>]* <else>? 'end' {*} }

rule then { ':' | 'then' | <term> ['then']? }

rule while_stmt { $<sym>=['while'|'until'] <expr> <.do> <comp_stmt> 'end' {*} }

rule do { ':' | 'do' | <term> ['do']? }

rule unless_stmt { 'unless' <expr> <.then> <comp_stmt> <else>? 'end' {*} }

rule else { 'else' <comp_stmt> {*} }

rule ensure { 'ensure' <comp_stmt> {*} }

rule rescue { # XXX check <args> ['rescue' <args> <.then> <comp_stmt>]+ {*} }

rule module { 'module' <module_identifier> <comp_stmt> 'end' {*} }

rule functiondef { 'def' <fname> <argdecl> <comp_stmt> 'end' {*} }

rule bodystmt { <comp_stmt> <rescue>? <else>? <ensure>? }

rule argdecl { ['(' [ <identifier> [',' <identifier>]* [',' <slurpy_param>]? [',' <block_param>]? | <slurpy_param> [',' <block_param>]? | <block_param>? ] ')']? {*} }

token slurpy_param { '*' <identifier> {*} }

token block_param { '&' <identifier> {*} }

rule begin_end { 'begin' <comp_stmt> ['rescue' <args>? <.do> <comp_stmt>]+ ['else' <comp_stmt>]? ['ensure' <comp_stmt>]? 'end' {*} }

rule fname { | <identifier> {*} #= identifier }

rule array { '[' [ <args> [',']? ]? ']' {*} }

rule hash { '{' [ [ <args> | <assocs> ] [',']? ]? '}' {*} }

rule assocs { <assoc> [',' <assoc>]* {*} }

rule assoc { <arg> '=>' <arg> {*} }

token identifier { <!reserved_word> <ident> {*} }

token module_identifier { <[A..Z]> <ident> {*} }

token global { '$' <ident> {*} }

token instance_variable { '@' <ident> {*} }

token class_variable { '@@' <ident> {*} }

token local_variable { <[a..z_]> <ident> {*} }

token constant_variable { <[A..Z]> <ident> {*} }

token literal { | <float> {*} #= float | <integer> {*} #= integer | <string> {*} #= string }

token float { \d* '.' \d+ {*} }

token integer { \d+ {*} }

token string { [ \' <string_literal: '> \' | \" <string_literal: "> \" ] {*} }

token reserved_word { [alias|and|BEGIN|begin|break|case |class|def|defined|do|else|elsif |END|end|ensure|false|for|if |in|module|next|nil|not|or |redo|rescue|retry|return|self|super |then|true|undef|unless|until|when |while|yield|__FILE__|__LINE__]>> }

token ws { | '\\' \n ## a backslash at end of line | <after [','|'='|'+']> \n ## a newline after a comma or operator is ignored | \h* ['#' \N* \n*]? }

proto 'infix:=' is precedence('1') { ... }

proto 'prefix:defined?' is looser('infix:=') { ... }

proto 'infix:+=' is equal('infix:=') is pirop('add') { ... }

proto 'infix:-=' is equal('infix:=') is pirop('sub') { ... }

proto 'infix:/=' is equal('infix:=') is pirop('div') { ... }

proto 'infix:*=' is equal('infix:=') is pirop('mul') { ... }

proto 'infix:%=' is equal('infix:=') is pirop('mul') { ... }

proto 'infix:|=' is equal('infix:=') { ... }

proto 'infix:&=' is equal('infix:=') { ... }

proto 'infix:~=' is equal('infix:=') { ... }

proto infix:«>>=» is equal('infix:=') is pirop('rsh') { ... }

proto infix:«<<=» is equal('infix:=') is pirop('lsh') { ... }

proto 'infix:&&=' is equal('infix:=') is pirop('and') { ... }

proto 'infix:**=' is equal('infix:=') is pirop('pow') { ... }

proto 'ternary:? :' is tighter('infix:=') is pirop('if') { ... }

proto 'infix:..' is tighter('ternary:? :') is pirop('n_add') { ... }

proto 'infix:...' is equal('infix:...') { ... }

proto 'infix:||' is tighter('infix:..') is past('unless') { ... }

proto 'infix:&&' is tighter('infix:||') is past('if') { ... }

proto 'infix:==' is tighter('infix:&&') { ... } proto 'infix:!=' is equiv('infix:==') { ... } proto 'infix:=~' is equiv('infix:==') { ... } proto 'infix:!~' is equiv('infix:==') { ... } proto 'infix:===' is equiv('infix:==') { ... } proto infix:«<=>» is equiv('infix:==') { ... }

proto 'infix:«<=»' is tighter('infix:=') { ... } proto infix:«=>» is equiv(infix:«<=») { ... } proto infix:«>» is equiv(infix:«<=») { ... } proto infix:«<» is equiv(infix:«<=») { ... }

proto 'infix:|' is tighter('infix:«<=»') { ... } proto 'infix:^' is equiv('infix:|') { ... }

proto 'infix:&' is tighter('infix:|') { ... }

proto infix:«<<» is tighter('infix:&') { ... } proto infix:«>>» is equiv(infix:«<<») { ... }

proto 'infix:+' is tighter(infix:«<<») is pirop('n_add') { ... }

proto 'infix:-' is equal('infix:+') is pirop('n_sub') { ... }

proto 'infix:*' is tighter('infix:+') is pirop('n_mul') { ... }

proto 'infix:/' is equal('infix:*') is pirop('n_div') { ... }

proto 'infix:%' is equal('infix:*') is pirop('n_mod') { ... }

proto 'prefix:+' is tighter('infix:*') { ... } proto 'prefix:-' is equal('prefix:+') { ... } proto 'prefix:!' is equal('prefix:+') { ... } proto 'prefix:~' is equal('prefix:+') { ... }

proto 'term:' is tighter('prefix:+') is parsed(&primary) { ... }


parrot