vexity 0.0.4

Tiny scripting language for hacking on abstractions of financial markets.
Documentation
// Helpers
SP       = _{ " " | "\t" }
NL       = _{ "\r\n" | "\n" }
COMMENT  = _{ "#" ~ (!NL ~ ANY)* }
_W       = _{ (SP | NL)* }      // 0 or more
_WS      = _{  SP+       }      // 1 or more

// Program
program     = { SOI ~ _W ~ stmt_list ~ _W ~ EOI }
stmt_list   = { stmt ~ (NL+ ~ _W ~ stmt)* }
stmt        = { fn_def | let_stmt | print_stmt | expr }

// Statements
let_stmt    = { "let"   ~ _WS ~ ident ~ _W ~ "=" ~ _W ~ expr }
print_stmt  = { "print" ~ _W  ~ "("  ~ _W ~ ident ~ _W ~ ")" }

// Expressions
expr        = { sum }
sum         = { product ~ (_W ~ op_add ~ _W ~ product)* }
product     = { postfix_expr ~ (_W ~ op_mul ~ _W ~ postfix_expr)* }
postfix_expr = { atom ~ postfix* }
postfix     = {
      "." ~ ident ~ _W ~ "(" ~ _W ~ method_arg ~ _W ~ ")"
    | "." ~ ident
    | "[" ~ _W ~ expr ~ _W ~ "]"
}
method_arg  = { lambda_expr | expr }
call_expr   = { ident ~ _W ~ "(" ~ _W ~ arg_list? ~ _W ~ ")" }
range_expr = { paren_expr }
paren_expr = { "(" ~ _W ~ expr ~ _W ~ ".." ~ _W ~ expr ~ _W ~ ")" }

// Atom Forms (ordered)
atom = {
      paren_expr
    | range_expr
    | hashmap
    | call_stmt
    | call_expr
    | lambda_expr
    | array
    | boolean
    | number
    | string
    | ident
}

// Function
fn_def      = {
    "fn" ~ _WS ~ ident
    ~ _W ~ "(" ~ _W ~ param_list? ~ ")"
    ~ _W ~ block
}
lambda_expr = {
    "|" ~ ident ~ "|" ~ _W ~ (block | expr)
}
block       = { "{" ~ _W ~ stmt_list? ~ _W ~ "}" }

// Collections
array         = { "[" ~ _W ~ expr_list? ~ _W ~ "]" }
expr_list     = { expr ~ (_W ~ "," ~ _W ~ expr)* }

hashmap       = { "{" ~ _W ~ key_val_pairs? ~ _W ~ "}" }
key_val_pairs = { key_val_pair ~ (_W ~ "," ~ _W ~ key_val_pair)* }
key_val_pair  = { ident ~ _W ~ ":" ~ _W ~ expr }

param_list    = { ident ~ (_W ~ "," ~ _W ~ ident)* ~ _W? }
arg_list      = { expr  ~ (_W ~ "," ~ _W ~ expr)* ~ _W? }

// Infix Operators
op_add = { "+" | "-" }
op_mul = { "*" | "/" }

// Lexical Tokens
ident   = @{
    ASCII_ALPHA_LOWER+
    ~ (ASCII_ALPHA_LOWER | ASCII_DIGIT | "_")*
}
number  = @{ "-"? ~ ASCII_DIGIT+ ~ ("." ~ ASCII_DIGIT+)? }
string  = @{ "\"" ~ (!"\"" ~ ANY)* ~ "\"" }
boolean = { "true" | "false" }

// Shortcut Statement Helper
call_stmt = { ident ~ _W ~ "(" ~ _W ~ ident ~ _W ~ ")" }