source = { SOI ~ space* ~ (space* ~ toplevel ~ space*)* ~ EOI } toplevel = { main | build | test | function | type_decl | task } main = { main_keyword ~ brace_open ~ body ~ brace_close } build = { build_keyword ~ brace_open ~ body ~ brace_close } test = { test_keyword ~ (string_literal)? ~ brace_open ~ body ~ brace_close } function = { annotation* ~ fn_signature ~ brace_open ~ body ~ brace_close } fn_signature = { func_modifiers ~ function_keyword ~ ident ~ param_list ~ return_type? } // TODO: Add support for context arguments, varargs, default args and named args return_type = _{ arrow ~ type_item } annotation = ${ "@" ~ ident ~ annotation_params?} annotation_params = !{ paren_open ~ (ident ~ (comma ~ ident)*)? ~ paren_close } annotation_arg = { ident | type_ident | string_literal } // TODO: Add number literals here func_modifiers = _{ // async_keyword // | const_keyword // | visibility } visibility = { pub_keyword? } type_decl = { struct_type_decl | alias_type_decl | tuple_type_decl | empty_type_decl } type_decl_prelude = _{ visibility ~ type_keyword ~ type_ident } struct_type_decl = { type_decl_prelude ~ brace_open ~ struct_fields ~ comma? ~ brace_close } struct_fields = _{ (struct_field ~ ((comma | newline+) ~ struct_field)*)? } struct_field = { param_base } alias_type_decl = { type_decl_prelude ~ assign_ ~ type_item } tuple_type_decl = { type_decl_prelude ~ paren_open ~ tuple_fields ~ paren_close } tuple_fields = _{ (tuple_field ~ (comma ~ tuple_field)*)? } tuple_field = { type_item } tuple_element = { element_type } empty_type_decl = { type_decl_prelude } param_list = { (paren_open ~ (param ~ (comma ~ param)*)? ~ paren_close)? } // TODO: Allow patterns here instead of just identifiers param = { param_base } param_base = _{ declaration_modifier? ~ ident ~ colon ~ type_item } task = { ident ~ brace_open ~ body ~ brace_close } body = { ((newline)* ~ statement ~ (newline | semicolon)+)* ~ statement? } statement = { assignment | declaration | top_expression | block } assignment = { assignment_target ~ assignment_operator ~ top_expression } assignment_target = { member_chain | ident } top_expression = { else_expression | trailing_closure_call | expression } declaration = { declaration_modifier ~ ident ~ (colon ~ type_item)? ~ assign_ ~ top_expression } declaration_modifier = ${ (ref_keyword | let_keyword | mut_keyword) ~ w } else_expression = { (trailing_closure_call | single_expression) ~ else_keyword ~ block } // TODO: Add block expressions and parenthesized expressions expression = { operator_chain | member_chain | single_expression | closure } simple_expression = !{ member_chain | single_expression } operator_chain = ${ simple_expression ~ ((w ~ infix_operator ~ w) ~ (simple_expression))+ } member_chain = { member_chain_base ~ (trailing_closure_call | function_call | ident) } member_chain_base = { ((strict_trailing_closure_call | single_expression) ~ space* ~ dot)+ } single_expression = { collection_literal | function_call | constructor_call | literal | ident // | group } literal = { boolean_literal | string_literal | number_literal } closure = { "|" ~ closure_arguments? ~ "|" ~ (block | expression) } closure_arguments = _{ (closure_argument ~ (comma ~ closure_argument)*)? ~ comma? } closure_argument = !{ ident ~ (colon ~ type_item)? } block = !{ (brace_open ~ body ~ brace_close) } // TODO: Also allow block expression here as closure with implicit names ("it" or #0, #1, #2) trailing_closure_call = ${ ident ~ whitespace* ~ ( trailing_closure | ( trailing_closure_call_arg ~ whitespace* ~ (comma ~ trailing_closure_call_arg)* ~ whitespace* ~ comma? ~ whitespace* ~ trailing_closure?)) } strict_trailing_closure_call = ${ ident ~ (whitespace* ~ trailing_closure_call_arg* ~ whitespace* ~ comma?) ~ whitespace* ~ trailing_closure } trailing_closure = { ("|" ~ closure_arguments ~ "|" ~ whitespace*)? ~ block } trailing_closure_call_arg = { (declaration_modifier)? ~ expression } infix_operator = { custom_infix_operator | collection_operator | arithmetic_operator | comparison_operator | logical_infix_operator } function_call = { function_call_base } function_call_base = _{ ident ~ paren_open ~ function_call_args ~ paren_close } function_call_args = _{ (function_call_arg ~ (comma ~ function_call_arg)*)? } function_call_arg = { (declaration_modifier)? ~ expression } constructor_call = { type_ident ~ paren_open ~ constructor_call_args ~ paren_close } constructor_call_args = _{ (constructor_call_arg ~ (comma ~ constructor_call_arg)*)? } constructor_call_arg = { ident ~ colon ~ expression } ident = @{ !(keyword ~ w) ~ (used_ident | unused_ident | discard) } used_ident = _{ ASCII_ALPHA_LOWER ~ (ASCII_ALPHA_LOWER | ASCII_DIGIT | "_")* } unused_ident = _{ "_" ~ (ASCII_ALPHA_LOWER | ASCII_DIGIT | "_")* } discard = _{ "_" } keyword = _{ main_keyword | build_keyword | test_keyword | function_keyword | type_keyword | ref_keyword | let_keyword | mut_keyword | else_keyword | async_keyword | const_keyword | pub_keyword | true_keyword | false_keyword | and | or | xor | not | contains } // # Brackets bracket_open = _{ "[" ~ space* } bracket_close = _{ space* ~ "]" } paren_open = _{ "(" ~ space* } paren_close = _{ space* ~ ")" } brace_open = _{ "{" ~ space* } brace_close = _{ space* ~ "}" } // # Punctuation colon = _{ ":" } comma = _{ "," ~ space* } dot = _{ "." } semicolon = _{ ";" } // # Arrows arrow = _{ "->" | "→" } double_arrow = _{ "=>" | "⇒" } // # Errors exclamation_mark = _{ "!" } question_mark = _{ "?" } // # Operators custom_infix_operator = { "TODO::CUSTOM_INFIX_OPERATOR_RULE" } spread = _{".."} inclusive_range = _{ "..=" } exclusive_range = _{ "..<" } assignment_operator = { assign | add_assign | sub_assign | mul_assign | pow_assign | div_assign | rem_assign } assign = @{ assign_ } assign_ = _{ "=" } add_assign = @{ "+=" } sub_assign = @{ "-=" } mul_assign = @{ "*=" } pow_assign = @{ "^=" } div_assign = @{ "/=" } rem_assign = @{ "%=" } // ## Arithmetic arithmetic_operator = { plus | minus | multiply | divide | remainder | power } plus = @{ "+" } minus = @{ "-" } multiply = @{ "*" } power = @{ "^" } divide = @{ "/" } remainder = @{ "%" } // ## Comparison comparison_operator = { equal | not_equal | greater | greater_equal | less | less_equal | identical | not_identical } equal = @{ "==" } not_equal = @{ "!=" | "≠"} identical = @{ "===" | "≡" } not_identical = @{ "!==" | "≢" } greater = @{ ">" } greater_equal = @{ ">=" | "≥"} less = @{ "<" } less_equal = @{ "<=" | "≤" } // ## Logical logical_infix_operator = { and | or | xor } and = @{ "&&" | "and" } or = @{ "||" | "or" } xor = @{ "^^" | "xor" } not = @{ "!" | "not" } // ## Collection collection_operator = { concat | remove | contains } concat = @{ "++" } remove = @{ "--" } contains = @{ "in" | "∈" | "∊" } // # Keywords // ## Top-Level main_keyword = _{ "main" } build_keyword = _{ "build" } test_keyword = _{ "test" } function_keyword = _{ "fn" } type_keyword = _{ "type" } // ## Declaration ref_keyword = @{ "ref" } let_keyword = @{ "let" } mut_keyword = @{ "mut" } true_keyword = @{ "true" } false_keyword = @{ "false" } // ## Function Modifiers async_keyword = @{ "async" } const_keyword = @{ "const" } pub_keyword = @{ "pub" } at_sign = @{ "@" } else_keyword = _{ "else" } // # Type Definitions type_item = { result_type | optional_type | allowed_in_error_variant } // Add some additional restrictions on optional / result type nesting // - Result types can't be nested -> Flatten result instead // - Optional types can't be nested -> Flatten optional instead // - Optional types can't contain result types -> Use Option> if this is needed // - Result errors can't contain optional types -> If there is no error, the operation should have been successful // - Result errors can't contain result types -> The error itself should not fail allowed_in_error_variant = _{ basic_type | array_type | dict_type | ordered_dict_type | set_type | tuple_type } allowed_in_success_variant = _{ optional_type | basic_type | array_type | dict_type | ordered_dict_type | set_type | tuple_type } allowed_in_optional = _{ basic_type | array_type | dict_type | ordered_dict_type | set_type | tuple_type } error_variant = { allowed_in_error_variant } success_variant = { allowed_in_success_variant } ref_element = { allowed_in_error_variant } key_type = _{ type_item } value_type = _{ type_item } element_type = _{ type_item } opt_element_type = { allowed_in_optional } tuple_elements = _{ element_type ~ (comma ~ element_type)* } type_ident = @{ "_"? ~ (ASCII_ALPHA_UPPER) ~ (ASCII_ALPHANUMERIC)*} basic_type = { type_ident } array_type = !{ bracket_open ~ element_type ~ bracket_close } set_type = !{ brace_open ~ element_type ~ brace_close } dict_type = !{ brace_open ~ key_type ~ colon ~ value_type ~ brace_close } ordered_dict_type = !{ bracket_open ~ key_type ~ colon ~ value_type ~ bracket_close } tuple_type = !{ paren_open ~ tuple_elements ~ paren_close } result_type = ${ success_variant ~ exclamation_mark ~ error_variant? } optional_type = ${ opt_element_type ~ question_mark } // # Collection Literals collection_literal = { array_literal | set_literal | dict_literal | ordered_dict_literal } array_literal = { bracket_open ~ array_literal_elements ~ comma? ~ bracket_close } array_literal_elements = _{ (expression ~ (comma ~ expression)*)? } set_literal = { brace_open ~ set_literal_elements ~ comma? ~ brace_close } set_literal_elements = _{ (expression ~ (comma ~ expression)*)? } dict_literal = { brace_open ~ dict_literal_elements ~ comma? ~ brace_close } dict_literal_elements = _{ (dict_literal_element ~ (comma ~ dict_literal_element)*)? } dict_literal_element = { expression ~ colon ~ expression } ordered_dict_literal = { bracket_open ~ dict_literal_elements ~ comma? ~ bracket_close } // # Basics WHITESPACE = _{ whitespace } COMMENT = _{("/*" ~ (!"*/" ~ ANY)* ~ "*/") | single_line_comment } single_line_comment = _{ ("//" ~ (!newline ~ ANY)*) } inherited = { "" } // ## Whitespace newline = _{ NEWLINE } whitespace = _{ " " | "\t" } // convenience rule w = _{ whitespace } space = _{ whitespace | newline } // ## Literals string_literal = @{ string_quote ~ (!string_quote ~ ANY)* ~ string_quote | raw_string_literal } raw_string_literal = _{ raw_string_quote ~ (!raw_string_quote ~ ANY)* ~ raw_string_quote } string_quote = _{ "\"" } raw_string_quote = _{ "#\"" } number_literal = @{ ("-" | "+" )? ~ ("0" | ASCII_NONZERO_DIGIT ~ (ASCII_DIGIT)*) ~ ((dot ~ (ASCII_DIGIT)+) ~ float_suffix | integer_suffix | float_suffix)? } integer_suffix = _{ "_"? ~ ("u8" | "u16" | "u32" | "u64" | "u128" | "i8" | "i16" | "i32" | "i64" | "i128" | "isize" | "usize") } float_suffix = _{ "_"? ~ ("f32" | "f64") } boolean_literal = ${ true_keyword | false_keyword }