program = line+ <program>
| eof <program>
line = comment
| opt_space exp:expression opt_space comment? (eof | eol | eob) <simple_exp>
| empty_line
empty_line = opt_space eol
expression = regex | binary_operation
| index_set | index_get | assignment
| paren_exp !(/\.|\(|->/) <simple_exp>
| method_access | method_invocation | number | string
| function_definition | array | hash
| unary_operation
paren_exp = "(" opt_space exp:expression opt_space ")" <simple_exp>
assignment = field_access space "=" spaceorbreak (function_definition !"(" | method_access) <field_assign>
| field_access space "=" spaceorbreak expression <field_assign>
| var:identifier space "=" spaceorbreak expression <var_assign>
field_access = method_chain (identifier | operator) <field_access>
index_set = indexed_expression sindexes:("[" expression "]")+ opt_space "=" spaceorbreak expression <index_set>
index_get = indexed_expression gindexes:("[" index_inner_arg_list "]")+ !"." iargs:simple_arg_list? <index_get>
indexed_expression = array | hash | paren_exp | method_invocation | string | unary_operation
method_access = "->" meth:(identifier) !"." <simple_meth_access>
| target:method_invocation "->" meth:(identifier | operator) <meth_access>
| paren_exp "->" meth:(identifier | operator) <paren_meth_access>
| "->" paren_exp <simple_paren_meth_access>
#-- Basic literals
number = /-?[0-9]+(\.[0-9]+)?/ <bnumber>
array = "[" spaceorbreak inner:array_inner spaceorbreak "]" <barray>
| "[" spaceorbreak "]" <empty_array>
array_inner = first:expression rest:(((spaceorbreak "," spaceorbreak) | (space | eol | comment)+) expression)* <array_inner>
hash = "[" opt_space ":" opt_space "]" <empty_hash>
| "[" spaceorbreak inner:hash_inner spaceorbreak "]" <bhash>
hash_inner = first:hash_argument rest:(((spaceorbreak "," spaceorbreak) | ((space | eol | comment)+)) hash_argument)* <array_inner>
hash_argument = key:((identifier | operator) (identifier | operator | number)*) ":" spaceorbreak value:expression <hash_key_arg>
| key:expression spaceorbreak ":" spaceorbreak value:expression <hash_arg>
regex = "/" body:("\\/" | !"/" . )* "/" opts:/[mix]*/ <bregex>
string = ("''" | "\"\"") <empty_string>
| "\"" values:(/[^#"\\]+/ | string_interpolation | "\\\"" | "\\\\" | !"\"" .)* ("\"" | missing_end_quote) <string_interp>
| "'" svalue:("\\'" | "\\\\" | !"'" .)+ ("'" | missing_end_quote) <simple_string>
| symbol
symbol = ":" svalue:(identifier | operator | number)+ <simple_symbol>
| (":''" | ":\"\"") <empty_symbol>
| ":'" svalue:("\\'" | "\\\\" | !"'" .)+ ("'" | missing_end_quote) <simple_symbol>
| ":\"" svalue:("\\\"" | "\\\\" | !"\"" .)+ ("\"" | missing_end_quote) <double_symbol>
string_interpolation = "#" "{" opt_space first:expression? opt_space rest:(eol opt_space expression space?)* spaceorbreak "}" <interp_value>
block_comment = opt_space "#*" (!"*#" (block_comment | .))* ("*#" | eof)
comment = block_comment
| opt_space "#" (!("\n" | eof) .)*
#-- Function definitions
function_definition = "{" opt_space args:formal_args? opt_space body:line* opt_space ("}" | missing_bracket_function) <bfunction>
formal_args = opt_space plain_formals opt_space "|" !"|"
| opt_space default_args opt_space "|" !"|"
| opt_space variable_args opt_space "|" !"|"
| opt_space plain_formals opt_space "," opt_space variable_args opt_space "|" !"|"
| opt_space default_args opt_space "," opt_space variable_args opt_space "|" !"|"
| opt_space plain_formals opt_space "," opt_space default_args opt_space "," opt_space variable_args opt_space "|" !"|"
| opt_space plain_formals opt_space "," opt_space default_args opt_space "|" !"|"
| "|"
plain_formals = plain_arg rest:rest_formals
rest_formals = (space? "," opt_space plain_arg !(space "="))*
plain_arg = identifier <plain_arg>
default_args = default_arg (space? "," opt_space default_args)*
default_arg = arg_var:identifier space "=" space arg_val:default_arg_rhs_expression <default_arg>
default_arg_rhs_expression = index_get | paren_exp | method_invocation | number | string | function_definition | array | hash
variable_args = "*" arg_var:identifier <variable_args>
#-- Unary and binary operations
unary_operation = !("-" number) operator !space unary_rhs_expression <unary_op>
unary_rhs_expression = index_get | paren_exp | number | string | array | hash | method_invocation | regex
binary_operation = binary_operation_chain expression <binary_op>
binary_operation_chain = (binary_lhs_expression space operator ((space? eol space?) | space))+ <binary_op_chain>
binary_lhs_expression = simple_method_chain | index_get | paren_exp | string | array | hash | regex | number | method_invocation | unary_operation
#-- Basic building blocks
identifier = /[a-zA-Z](?:(?!->)[a-zA-Z0-9_!?\-*+^&@~\/\\><$%])*/
operator = !("->" !operator) !("=" !(operator | "=")) ("!=" | ">=" | "<=" | /[!?\-*+^@~\/\\><$_%\=]/ | "||" | "|" | "&&" | "&")+
#-- Calling methods and functions
method_invocation = method_chain (identifier | operator) arg_list <chain_call>
| simple_method_invocation
| m_name:identifier alist:arg_list <simple_call>
| paren_exp simple_arg_list <paren_call>
| function_literal_invocation
simple_method_invocation = m_name:identifier alist:simple_arg_list <simpler_call>
simple_method_chain = method_chain (identifier | operator) simple_arg_list <chain_call>
| method_chain (identifier | operator) &space <chain_call>
| simple_method_invocation
| identifier &space <simplest_call>
function_literal_invocation = func:function_definition alist:simple_arg_list <func_lit_call>
method_chain = (method_lhs ".")+ <method_chain>
method_lhs = var:identifier !space args:arg_list ("[" opt_space index_args:inner_arg_list opt_space "]")* <simple_meth_lhs>
| target:array indexes:("[" opt_space index_args:inner_arg_list opt_space "]")+ <array_index_lhs>
| function_definition !space simple_arg_list
| function_definition &"."
| "->" var:identifier <access_meth_lhs>
| method_target_expression
method_target_expression = array | hash | paren_exp | number | string | regex | unary_operation
#-- Argument lists
arg_list = "(" opt_space inner_arg_list opt_space (")" | missing_end_parenthesis)
| "(" opt_space (")" | missing_end_parenthesis)
| space inner_arg_list
| !"(" &(space?)
simple_arg_list = "(" opt_space inner_arg_list opt_space (")" | missing_end_parenthesis)
| "(" opt_space (")" | missing_end_parenthesis)
inner_arg_list = arg_first arg_next* <inner_arg_list>
arg_first = named_argument | expression
arg_space = (space | eol_not_semicolon)
arg_next = arg_space* "," arg_space* arg_first
| arg_space+ function_definition
| arg_space+ named_argument
| opt_space expression
named_argument = key:identifier ":" spaceorbreak value:expression <named_argument>
| key:string ":" spaceorbreak value:expression <named_argument>
| key:(method_target_expression | simple_method_invocation) space ":" space spaceorbreak value:expression <named_argument>
index_inner_arg_list = arg_first inner_arg_next* <inner_arg_list>
inner_arg_next = arg_next
| arg_space+ arg_first
#-- Errors
missing_end_quote = %Missing end quote mark for string%
missing_end_slash = %Regular expression missing end `/`%
missing_bracket_function = %Missing end bracket for function definition%
missing_end_parenthesis = %Missing closing parenthesis for function arguments%
#-- Spaces
spaceorbreak = opt_space eol? (comment eol)* space?
eol_not_semicolon = ("\n" | "\r\n")+ | comment
opt_space = /(?: |\t)*/
space = /(?: |\t)+/
eol = /(?:\n|;|\r\n)+/
eob = opt_space &"}" space?
eof = (eol | space)? !.