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>> | $ | <panic: unterminated statement>] ]* {*} }
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>? <.before <stmt>><comp_stmt> 'end' {*} | '{' <do_args>? <.term>? <.before <stmt>><comp_stmt> '}' {*} }
rule super_call { 'super' <call_args> {*} }
token operation { 'class'| 'nil?' | 'next' | 'begin'| 'end' | '`' | <.identifier> ('!'|'?')? }
#XXX UGLY! Refactor into <args> maybe? token call_args { | '()' [<.ws> <do_block>]? {*} | [ <.after \s|\)> | <.before \s> ] <args> [<.ws> <do_block>]? {*} | '(' <.ws> <args> <.ws> ')' [<.ws> <do_block>]? {*} }
rule do_args { '|' <block_signature> '|' }
rule sig_identifier { #XXX Should this be basic_primary or expr or what? <identifier>[ '=' <default=basic_primary>]? {*} }
rule block_signature { [ | <sig_identifier> [',' <sig_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 | <ahash> {*} #= ahash | <regex> {*} #= regex | <do_block> {*} #= do_block | <quote_string> {*} #= quote_string | <warray> {*} #= warray | <array> {*} #= array | <pcomp_stmt> {*} #= pcomp_stmt | <yield> {*} #= yield | <control_command> {*} #= control_command }
token primary { <basic_primary> <post_primary_expr>* {*} }
token post_primary_expr { | <indexed> {*} #= indexed | <call_args> {*} #= call_args | <methodcall> {*} #= methodcall | '[' <args>? ']' {*} #= args }
token pcomp_stmt { '(' <comp_stmt> ')' {*} }
rule if_stmt { 'if' <expr> <.then> [<comp_stmt> ['elsif' <expr> <.then> <comp_stmt>]* <else>? 'end' |<panic: syntax error in if statement>] {*} }
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>]+ {*} }
token control_command { | 'next' {*} #= next | 'break' {*} #= break | 'redo' {*} #= redo }
token yield { 'yield' <call_args> {*} }
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> <[=!?]>? }
token quote_string { ['%q'|'%Q'] <.before <[<[_|({]>> <quote_expression: :qq> {*} }
token warray { '%w' <.before <[<[({]>> <quote_expression: :w :q> {*} }
rule array { '[' [ <args> [',']? ]? ']' {*} }
rule ahash { '{' [ <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 { [<ns=ident> '::']* [ <before <[a..z_]>> | <after '::'> ] <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 regex { <.before '/'> [<quote_expression: :regex> $<modifiers>=[<alpha>]* |<panic: problem parsing regex>] {*} }
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* <ws>]? }
proto 'infix:=' is precedence('1') is pasttype('copy') is lvalue(1) { ... }
proto 'prefix:defined?' is looser('infix:=') { ... }
proto 'infix:+=' is equiv('infix:=') { ... }
proto 'infix:-=' is equiv('infix:=') { ... }
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 parsed(&primary) { ... } #is pirop('add') { ... }
proto 'infix:...' is equiv('infix:..') { ... }
proto 'infix:||' is tighter('infix:..') is pasttype('unless') { ... }
proto 'infix:&&' is tighter('infix:||') is pasttype('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:«<<») { ... }
proto 'infix:-' is equiv('infix:+') { ... } #is pirop('sub') { ... }
proto 'infix:*' is tighter('infix:+') { ... } #is pirop('mul') { ... }
proto 'infix:/' is equiv('infix:*') { ... } #is pirop('div') { ... }
proto 'infix:%' is equiv('infix:*') is pirop('mod') { ... }
proto 'postfix:++' is tighter('infix:*') is pirop('n_add') { ... }
proto 'postfix:--' is tighter('infix:*') is pirop('n_sub') { ... } # #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) { ... }