grammar cardinal::Grammar is PCT::Grammar;

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

token comp_stmt { {*} #= open <stmts> {*} #= close }

rule stmts { <.term>?[ <stmt> [<.term>+ | <.before <end_block>> | $] ]* {*} }

token term { \n | ';' } token end_block { <.ws> [ 'end' | '}' ] }

token basic_stmt { | <alias> {*} #= alias | <classdef> {*} #= classdef | <functiondef> {*} #= functiondef | <if_stmt> {*} #= if_stmt | <while_stmt> {*} #= while_stmt | <for_stmt> {*} #= for_stmt | <unless_stmt> {*} #= unless_stmt | <module> {*} #= module | <begin_end> {*} #= begin_end | <indexed_assignment> {*} #= indexed_assignment | <member_assignment> {*} #= member_assignment | <assignment> {*} #= assignment | <return_stmt> {*} #= return_stmt | <expr> {*} #= expr | <begin> {*} #= begin | <end> {*} #= end }

token return_stmt { 'return' <.ws> <call_args> {*} }

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

token stmt { <basic_stmt> <.ws> <stmt_mod>* {*} }

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

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

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

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

token indexed_assignment { <basic_primary> '[' <key=arg> ']' <.ws> '=' <.ws> <rhs=arg> {*} }

token member_assignment { <basic_primary> '.' <key=identifier> <.ws> '=' <.ws> <rhs=arg> {*} }

rule assignment { <mlhs=lhs> '=' <mrhs=arg> #XXX need to figure out multiple assignment {*} }

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

token lhs { | <basic_primary> {*} #= basic_primary }

token indexed { '[' <args>? ']' {*} }

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

token methodcall { $<dot>=['.'|'::'] <operation> <call_args>? <do_block>? {*} }

rule do_block { | 'do' <do_args> <.term>? <comp_stmt> 'end' {*} | '{' <do_args> <.term>? <comp_stmt> '}' {*} }

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

token operation { <.identifier> ('!'|'?')? }

#XXX UGLY! Refactor into <args> maybe? token call_args { | '()' [<.ws> <do_block>]? {*} | <args> [<.ws> <do_block>]? {*} | '(' <.ws> <args> <.ws> ')' [<.ws> <do_block>]? {*} }

rule do_args { '|' <block_signature> '|' }

rule block_signature { [ | <identifier> [',' <identifier>]* [',' <slurpy_param>]? [',' <block_param>]? | <slurpy_param> [',' <block_param>]? | <block_param>? ] {*} }

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

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

token funcall { <!reserved_word> <local_variable> <.before \s|'('> <.before <call_args>> {*} }

token mrhs { <args> {*} }

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

rule 'arg' is optable { ... }

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

token basic_primary { | <literal> {*} #= literal | <funcall> {*} #= funcall | <variable> {*} #= variable | <do_block> {*} #= do_block | <array> {*} #= array | <ahash> {*} #= ahash | <pcomp_stmt> {*} #= pcomp_stmt | <scope_identifier> {*} #= scope_identifier }

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

token post_primary_expr { | <indexed> {*} #= indexed | <call_args> {*} #= call_args | <methodcall> {*} #= methodcall | <scope_identifier> {*} #= scope_identifier | '[' <args>? ']' {*} #= args }

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

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

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

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

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

rule for_stmt { 'for' <variable> 'in' <expr> <.do> <comp_stmt> 'end' {*} }

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

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

token else { 'else' <.ws> <comp_stmt> {*} }

token ensure { 'ensure' <.ws> <comp_stmt> {*} }

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

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

rule classdef { 'class' <module_identifier> {*} #= open <comp_stmt> 'end' {*} #= block }

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

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

rule argdecl { ['(' <block_signature> ')']? }

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' {*} }

token fname { <.identifier> <[=!?]>? }

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

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

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

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

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

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

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

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

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

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

token constant_variable { <before <[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') is pasttype('copy') is lvalue(1) { ... }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

proto 'infix:...' is equiv('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 tighter('infix:===') { ... } proto infix:«>» is tighter('infix:===') { ... } proto infix:«<» is tighter('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 equiv('infix:+') is pirop('n_sub') { ... }

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

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

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

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

proto 'term:' is tighter('infix:*') is parsed(&primary) { ... }


parrot