(* ======================== *)
(* Core Character Sequences *)
(* ======================== *)

white_space         ::= '\u0020' | '\u0009' | '\u000D' | '\u000A'
lparen              ::= '(';
rparen              ::= ')';
paren               ::= lparen | rparen;
lbracket            ::= '[';
rbracket            ::= ']';
bracket             ::= lbracket | rbracket;
lbrace              ::= '{';
rbrace              ::= '}';
brace               ::= lbrace | rbrace;
digit               ::= '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9';
hex                 ::= '0' | ... | '9' | 'A' | ... | 'F' | 'a' | ... | 'f' ;
upper               ::= 'A' | ... | 'Z' | Lu | Lt | Nl
                      | Lo upper only
                      | Lm upper only;
lower               ::= 'a' | ... | 'z' | Ll
                      | Lo lower only
                      | Lm lower only;
letter              ::= upper | lower;
escape_unicode      ::= '\', 'u', hex, hex, hex, hex;
escape_char         ::= '\', ('b' | 'f' | 'n' | 'r' | 't' | '\' | '"' | "'");
escape_seq          ::= escape_unicode | escape_char;
number_integer      ::= '0' | digit, [{digit}];
number_float        ::= number_integer, '.', number_integer;
newline             ::= '\n';
printable_char      ::= printable utf8;
char_char           ::= printable_char without newline or single quote
                      | escape_seq;
string_char         ::= printable_char without newline or double quote
                      | escape_seq;

(* Comments *)

comment             ::= '-', '-', {utf8};
docstring           ::= '-', '-', '-', {utf8};

(* ======== *)
(* Keywords *)
(* ======== *)

k_type              ::= 't', 'y', 'p', 'e';
k_class             ::= 'c', 'l', 'a', 's', 's';
k_alias             ::= 'a', 'l', 'i', 'a', 's';
k_const             ::= 'c', 'o', 'n', 's', 't';
k_enum              ::= 'e', 'n', 'u', 'm';
k_record            ::= 'r', 'e', 'c', 'o', 'r', 'd';
k_object            ::= 'o', 'b', 'j', 'e', 'c', 't';
k_let               ::= 'l', 'e', 't';
k_mut               ::= 'm', 'u', 't';
k_export            ::= 'e', 'x', 'p', 'o', 'r', 't';

(* Starts an import definition. *)
k_import            ::= 'i', 'm', 'p', 'o', 'r', 't';

(* Denotes a namespace declaration. *)
k_namespace         ::= 'n', 'a', 'm', 'e', 's', 'p', 'a', 'c', 'e';

(* Starts an infix function definition *)
k_infix             ::= 'i', 'n', 'f', 'i', 'x';

(* Starts a function definition. *)
k_fn                ::= 'f', 'n';

(* Ends any block definition. Must be followed by the keyword of the block. *)
k_end               ::= 'e', 'n', 'd';

(* Initiates a pattern match against some value. *)
k_match             ::= 'm', 'a', 't', 'c', 'h';

(* Initiates a pattern matching case. Applicable to functions and matches. *)
k_case              ::= 'c', 'a', 's', 'e';
k_lambda            ::= 'λ';

(* Used in if/else syntax. Starts such a block and used in 'else if'. *)
k_if                ::= 'i', 'f';

(* Used in if/else syntax. Must follow the condition of any 'if'. *)
k_then              ::= 't', 'h', 'e', 'n';

(* Used in if/else syntax. Indicates an 'else if' or 'else'. *)
k_else              ::= 'e', 'l', 's', 'e';

(* Used in do/return syntax. Indicates the start of such a structure. *)
k_do                ::= 'd', 'o';

(* Used in do/return syntax. Indicates the final output value. *)
k_return            ::= 'r', 'e', 't', 'u', 'r', 'n';

(* Indicates a type constructor. *)
k_given             ::= 'g', 'i', 'v', 'e', 'n';

(* Function modifier which restricts use to the enclosing scope. *)
k_private           ::= 'p', 'r', 'i', 'v', 'a', 't', 'e';

(* true is a Boolean literal *)
k_true              ::= 't', 'r', 'u', 'e';

(* false is a Boolean literal *)
k_false             ::= 'f', 'a', 'l', 's', 'e';

(* defn is fn but for definitions without implementations *)
k_false             ::= 'd', 'e', 'f', 'n';

keyword             ::= k_type | k_class | k_alias | k_const | k_enum | k_record
                      | k_object | k_let | k_mut | k_export | k_import
                      | k_namespace | k_infix | k_fn | k_end | k_match | k_case
                      | k_lambda | k_if | k_then | k_else | k_do | k_return
                      | k_given | k_private | k_true | k_false | k_defn;

(* ============== *)
(* Literal Values *)
(* ============== *)

literal_bool        ::= k_true | k_false;
literal_integer     ::= ['-'], number_integer;
literal_float       ::= ['-'], number_float;
literal_char        ::= "'", char_char, "'";
literal_string      ::= '"', {string_char}, "'";
empty_tuple         ::= op_tuple, lparen, rparen;
empty_list          ::= lbracket, rbracket;
literal             ::= literal_bool
                      | literal_integer
                      | literal_float
                      | literal_char
                      | literal_string
                      | empty_tuple
                      | empty_list;

(* ========= *)
(* Operators *)
(* ========= *)

(* Type hole used for development -- will resolve/compile, but fail at
runtime. *)
hole                ::= '?', '?', '?';

(* Used for splat imports: import foo.bar.baz.* is an example. *)
op_import_splat     ::= '*';

(* Used to access members. Relevant for namespaces, records, enums, classes. *)
op_member           ::= '.';

(* Standard separator for type definitions, tuples and records. *)
op_comma            ::= ',';

(* A | B is a type which can be either A or B. *)
op_type_union       ::= '|';

(* List operator. Prepend LHS to RHS. Can be used in pattern matching. *)
op_list_prepend     ::= ':', '-';

(* Only valid adjacent to a name declaration. Explicitly declare the type that
the name has. *)
op_bind_type        ::= ':';

(* Bind the RHS expression to the LHS name. *)
op_bind_value       ::= ':', '=';

(* Used in function definitions: the LHS input produces RHS output. *)
op_fn_return        ::= '-', '>';

(* Binds the result contained in the RHS expression to the name on the LHS. *)
(* Note that "contained" refers to the value in the [Bi]Monad. *)
op_bind_do          ::= '<', '-';

(* Indicates some type is constrained by some type class, ex F * :: Functor *)
op_class_member     ::= ':', ':';

(* Indicates the start of a pattern matching case, used in both forms:

   case <pattern> => <expr>
   λ <pattern> => <expr>

   The λ form is preferred for functions, 'case' is preferred for 'match'. *)
op_case             ::= '=', '>';

(* Declares a tuple, as in #(a, b, c) *)
op_tuple            ::= '#';

operator            ::= hole | op_import_splat | op_member | op_comma
                        | op_type_union | op_list_prepend | op_bind_type
                        | op_bind_value | op_fn_return | -> op_bind_do
                        | op_class_member | op_case | op_tuple;

(* ===== *)
(* Names *)
(* ===== *)

(* Used to denote an anonymous value, such as ignoring a pattern part. *)
name_anon_value     ::= '_';

(* Used to denote an anonymous parameter in a type definition. Helps to describe
some type with a particular shape without naming each part. *)
name_anon_type      ::= '*';