@precedence { line_end @left, line_break @left } @top Script { (newline | terminator)* line+ } @skip { space | Comment } line { (expression | toplevel) !line_end (newline | terminator)+ } toplevel { Import | Use | Ns | Test } Import { silent<"import"> String silent<"as"> Word } Use { silent<"use"> Word } Ns { silent<"ns"> Word "{" separator* assoc_term (separator+ assoc_term)* separator* "}" } Test { silent<"test"> String non_binding } expression { non_binding | binding } binding { Let | Ref | Fn_Named | Fn_Compound } Ref { silent<"ref"> Word "=" expression } non_binding { simple | complex } synth_root { Word | Keyword } synth_term { Args | Keyword } arg_term { Placeholder | simple } Args { ("(" (newline | separator)* ")") | ("(" (newline | separator)* arg_term ((newline | separator)+ arg_term)* (newline | separator)* ")") } Synthetic { synth_root synth_term+ } complex { Block | If | If_Let | Match | When | Do | Loop | Repeat } Repeat { silent<"repeat"> (Word | Number) Block } Recur { silent<"recur"> Args } Loop { silent<"loop"> simple "with" (Fn_Clause | Fn_Clauses) } simple { atom | collection | Synthetic | Fn_Lambda | Recur } Fn_Clause { Tuple_Pattern "->" expression } Fn_Clauses { "{" (newline | terminator)* Fn_Clause ((newline | terminator)+ Fn_Clause)* (newline | terminator)* "}" } Fn_Compound { silent<"fn"> Word Fn_Clauses } Fn_Named { silent<"fn"> Word Fn_Clause } Fn_Lambda { silent<"fn"> Fn_Clause } // TODO: figure out precedence with do/bind exprs do_expr { Fn_Lambda | Synthetic | Word | Keyword } Do { silent<"do"> simple !line_break (newline* ">" do_expr)+ } Pattern { Tuple_Pattern | List_Pattern | Dict_Pattern | atom | Placeholder | Ignored } Ignored { "_" Word } Placeholder { "_" } ellipsis { "..." } Splattern { ellipsis (Word | Placeholder | Ignored) } Tuple_Pattern { ("(" (newline | separator)* ")") | ("(" (newline | separator)* (Pattern (newline | separator)+)* (Pattern | Splattern) (newline | separator)* ")") } List_Pattern { ("[" (newline | separator)* "]") | ("[" (newline | separator)* (Pattern (newline | separator)+)* (Pattern | Splattern) (newline | separator)* "]") } Assoc_Pattern { Word | (Keyword Pattern) } Dict_Pattern { ("#{" (newline | separator)* "}") | ("#{" (newline | separator)* (Assoc_Pattern (newline | separator)+)* (Assoc_Pattern | Splattern) (newline | separator)* "}") } Let { silent<"let"> Pattern "=" !line_break newline* non_binding } Else { silent<"else"> } Match_Clause {(Pattern | Else) "->" !line_break newline* expression} match_body { Match_Clause | ( "{" (newline | terminator)* Match_Clause ((newline | terminator)+ Match_Clause)* (newline | terminator)* "}" ) } Match { silent<"match"> simple silent<"with"> match_body } When_Clause { (simple | Placeholder | Else) "->" !line_break newline* expression } When { silent<"when"> "{" (newline | terminator)* When_Clause ((newline | terminator)+ When_Clause)* (newline | terminator)* "}" } If { silent<"if"> simple !line_break newline* silent<"then"> expression !line_break newline* silent<"else"> expression } If_Let { silent<"if"> silent<"let"> Pattern "=" simple !line_break newline* silent<"then"> expression !line_break newline* silent<"else"> expression } Block { "{" (newline | terminator)* expression ((newline | terminator)+ expression)* (newline | terminator)* "}" } collection { Tuple | List | Set | Dict } Tuple { ( "(" // non-empty (newline | separator)* non_binding ((newline | separator)+ non_binding)* (newline | separator)* ")" ) | "(" (newline | separator)* ")" // empty } Splat { ellipsis Word } linear_term { Splat | non_binding } List { ("[" (newline | separator)* linear_term ((newline | separator)+ linear_term)* (newline | separator)* "]" ) | "[" (newline | separator)* "]" } Set { ("${" (newline | separator)* linear_term ((newline | separator)+ linear_term)* (newline | separator)* "}") | "${" (newline | separator)* "}" } assoc_term { Word | (Keyword non_binding) } dict_term { assoc_term | Splat } Dict { ("#{" (newline | separator)* dict_term ((newline | separator)+ dict_term)* (newline | separator)* "}" ) | "#{" (newline | separator)* "}" } atom { Boolean | Nil | String | Number | Keyword | Word } reserved { @specialize[@name={term}] } silent { @specialize } Boolean { reserved<"true"> | reserved<"false"> } Nil { silent<"nil"> } @tokens { Word { $[a-z] $[a-zA-Z_\-?/!]* } Keyword { ":" Word } Comment { "&" ![\n]* } String { '"' (!["\\] | "\\" _)* '"' } space { $[ \t\r]+ } int { $[1-9]$[0-9]* | "0" } float { ("0" | int ) "." $[0-9]+} Number { "-"? (int | float) } separator { "," } terminator { ";" } newline { "\n" } }