# Rail Language Grammar (EBNF)
# Derived from the self-hosting compiler's parser (tools/compile.rail)
# For use with grammar-constrained LLM decoding (XGrammar/Outlines)

program     = { declaration } ;
declaration = type_decl | foreign_decl | func_decl ;

# Type declarations (ADTs)
type_decl   = "type" IDENT "=" { "|" IDENT { IDENT } } ;

# Foreign function declarations (FFI)
foreign_decl = "foreign" IDENT { IDENT } [ "->" IDENT ] ;

# Function declarations
func_decl   = IDENT { IDENT } "=" expr ;

# Expressions (ordered by precedence, low to high)
expr        = let_expr | if_expr | match_expr | pipe_expr ;

let_expr    = "let" ( IDENT | "(" IDENT { "," IDENT } ")" ) "=" expr NEWLINE expr ;
if_expr     = "if" expr "then" expr "else" expr ;
match_expr  = "match" expr { "|" pattern "->" expr } ;

pipe_expr   = or_expr { "|>" or_expr } ;
or_expr     = and_expr { "||" and_expr } ;
and_expr    = cmp_expr { "&&" cmp_expr } ;
cmp_expr    = add_expr [ ( "==" | "!=" | "<" | ">" | "<=" | ">=" ) add_expr ] ;
add_expr    = mul_expr { ( "+" | "-" ) mul_expr } ;
mul_expr    = app_expr { ( "*" | "/" | "%" ) app_expr } ;
app_expr    = atom { atom } ;

atom        = INT | FLOAT | STRING | IDENT | "true" | "false" | "null"
            | "(" expr { "," expr } ")"
            | "[" [ expr { "," expr } ] "]"
            | "\\" IDENT "->" expr ;

# Match patterns
pattern     = IDENT { IDENT } | "_" ;

# Tokens
INT         = DIGIT { DIGIT } ;
FLOAT       = DIGIT { DIGIT } "." DIGIT { DIGIT } ;
STRING      = '"' { CHAR | ESCAPE } '"' ;
IDENT       = ( ALPHA | "_" ) { ALPHA | DIGIT | "_" } ;
NEWLINE     = "\n" ;

DIGIT       = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" ;
ALPHA       = "a" .. "z" | "A" .. "Z" ;
ESCAPE      = "\\" ( "n" | "t" | "\\" | '"' | "{" | "}" ) ;
CHAR        = ? any character except '"' and '\\' ? ;

# Comments
COMMENT     = "--" { ? any character except newline ? } NEWLINE ;

# Keywords (reserved)
KEYWORD     = "let" | "if" | "then" | "else" | "match" | "type"
            | "import" | "foreign" | "true" | "false" | "null" ;
