/// // @ts-check const PREC = { ASSIGNMENT: 1, RANGE: 2, OR: 3, AND: 4, NOT: 5, COMPARE: 6, BIT_OR: 7, BIT_XOR: 8, BIT_AND: 9, ADD: 10, MULTIPLY: 11, PREFIX: 12, POSTFIX: 13, }; const RESERVED_WORDS = [ "and", "as", "bits", "break", "continue", "default", "distinct", "else", "false", "for", "if", "in", "loop", "match", "nil", "not", "or", "pub", "return", "true", "use", "bit", "chr", "f32", "f64", "i8", "i16", "i32", "i64", "nvr", "str", "u8", "u16", "u32", "u64", ]; module.exports = grammar({ name: "lscript", externals: $ => [ $._and_continuation, $._or_continuation, $._trailing_dot_float, ], extras: $ => [/[ \t]/, $.comment], word: $ => $.identifier, reserved: { global: _ => RESERVED_WORDS, }, supertypes: $ => [ $.module_item, $.expression, $.value_expression, $.type_expression, $.primary_expression, $.literal, $.type_name, $.binding_pattern, $.pattern_atom, $.pipeline_stage, $.inline_pipeline_stage, ], conflicts: $ => [ [$.product_pattern_field, $.primary_expression], [$.pattern_atom, $.literal], [$.pattern_atom, $.primary_expression], [$.signed_numeric_literal], [$.source_file, $._scope_separator], [$.qualified_name], [$.block, $._scope_separator], [$.match_expression, $._scope_separator], [$.product_constructor, $._scope_separator], [$.tuple_type], [$.type_application], [$.tuple_pattern], [$.product_pattern], [$.array_pattern], [$.tuple_value], [$.collection_literal], [$.block, $.product_pattern], [$.array_pattern, $.collection_literal], [$.typed_tuple_pattern], [$.unit_pattern, $.unit_literal], [$.sum_pattern, $.sum_constructor], [$.bits_pattern, $.bits_constructor], [$.primary_expression, $.qualified_name], [$.array_pattern_entry, $.primary_expression], [$.continue_expression], [$.unit_type, $.unit_literal], [$.unit_type, $.unit_pattern, $.unit_literal], [$.product_pattern_field, $.primary_expression, $.type_name], [$.pattern_atom, $.primary_expression, $.type_name], [$.type_name, $.qualified_name], [$.primary_expression, $.type_name], [$.type_expression, $.function_type], [$.array_type, $.collection_literal], ], rules: { source_file: $ => seq( repeat($._newline), optional(seq( $.module_item, repeat(seq($._scope_separator, $.module_item)), repeat($._newline), )), ), module_item: $ => choice( $.use_declaration, $.pub_declaration, $.expression, ), use_declaration: $ => seq( "use", choice( seq($.module_path, optional(seq("as", field("alias", $.identifier)))), seq($.module_path, ".", "{", listBody($, $.import_member), "}"), ), ), import_member: $ => seq( field("name", $.identifier), optional(seq("as", field("alias", $.identifier))), ), module_path: $ => choice( $.type_name, seq(choice(".", ".."), $.type_name), ), pub_declaration: $ => seq("pub", "{", scopeBody($, $.identifier), "}"), expression: $ => choice( $.declaration, $.value_expression, ), value_expression: $ => choice( $.function_literal, $.return_expression, $.break_expression, $.continue_expression, $.pipeline_expression, $._ordinary_expression, ), declaration: $ => choice( seq( field("name", $.identifier), "::", field("value", choice($.type_definition, prec(1, $.value_expression))), ), seq( field("name", $.identifier), ":=", field("value", $.value_expression), ), seq( field("name", $.identifier), ":", field("type", $.type_expression), choice( seq(":", field("value", $.value_expression)), optional(seq("=", field("value", $.value_expression))), ), ), seq( field("pattern", choice($.tuple_pattern, $.product_pattern)), choice("::", ":="), field("value", $.value_expression), ), ), type_definition: $ => seq(optional("distinct"), $.type_expression), type_expression: $ => choice( $.function_type, $._result_type, ), function_type: $ => prec.right(seq( field("input", $._result_type), field("effect", choice("->", "=>")), field("result", $.type_expression), )), _result_type: $ => choice( $.result_type, $.optional_type, $._container_type, ), result_type: $ => seq( field("value", $._container_type), "!", field("error", $._container_type), ), optional_type: $ => seq( field("value", $._container_type), repeat1("?"), ), _container_type: $ => choice( $.array_type, $.map_type, $._primary_type, ), array_type: $ => prec.right(seq( "[", "]", field("element", $._container_type), )), map_type: $ => prec.right(seq( "[", field("key", $.type_expression), "]", field("value", $._container_type), )), _primary_type: $ => choice( $.primitive_type, $.type_name, $.unit_type, $.tuple_type, $.product_type, $.sum_type, $.bits_type, $.type_application, $.grouped_type, ), grouped_type: $ => seq("{", field("value", $.type_expression), "}"), unit_type: _ => seq("(", ")"), tuple_type: $ => seq( "(", listBodyAtLeastTwo($, $.type_expression), ")", ), type_application: $ => seq( field("constructor", $.type_name), "<", listBody($, $.type_expression), ">", ), product_type: $ => seq("&", "{", scopeBody($, $.product_field), "}"), product_field: $ => seq( field("name", $.identifier), ":", field("type", $.type_expression), choice(":", "="), optional(field("default", $.expression)), ), sum_type: $ => seq( "|", optional($.integer_type), "{", scopeBody($, $.sum_variant), "}", ), sum_variant: $ => seq( field("name", $.identifier), optional(choice( seq(":", field("payload", $.type_expression)), seq("::", field("discriminant", $.signed_integer_literal)), )), ), bits_type: $ => seq( "bits", "|", $.unsigned_integer_type, "{", scopeBody($, $.bits_member), "}", ), bits_member: $ => seq($.identifier, "::", $.integer_literal), primitive_type: _ => choice( "f32", "f64", "bit", "chr", "str", "nvr", "i8", "i16", "i32", "i64", "u8", "u16", "u32", "u64", ), integer_type: _ => choice("i8", "i16", "i32", "i64", "u8", "u16", "u32", "u64"), unsigned_integer_type: _ => choice("u8", "u16", "u32", "u64"), function_literal: $ => choice( prec(1, seq( "@", field("parameter", $.typed_parameter), optional(seq( field("effect", choice("->", "=>")), field("result", $.type_expression), )), field("body", $.block), )), prec(1, seq( "@", field("parameter", $.typed_tuple_pattern), optional(seq( field("effect", choice("->", "=>")), field("result", $.type_expression), )), field("body", $.block), )), prec(1, seq("@", field("body", $.block))), ), typed_tuple_pattern: $ => seq("(", optional(listBodyAtLeastTwo($, $.typed_tuple_component)), ")"), typed_parameter: $ => seq( field("pattern", $.binding_pattern), ":", field("type", $._result_type), ), typed_tuple_component: $ => seq( field("pattern", $.binding_pattern), ":", field("type", $.type_expression), ), return_expression: $ => prec.right(seq("return", optional($.expression))), break_expression: $ => prec.right(seq("break", optional($.label), optional($.expression))), continue_expression: $ => seq("continue", optional($.label)), label: $ => seq("`", $.identifier), if_expression: $ => prec.right(seq( "if", field("condition", $.expression), field("consequence", $.block), optional(seq("else", field("alternative", choice($.block, $.if_expression)))), )), match_expression: $ => seq( "match", field("value", $.expression), "{", scopeBody($, $.match_arm), "}", ), match_arm: $ => seq( field("pattern", $.pattern), optional(seq("if", field("guard", $.expression))), "=>", field("body", $.expression), ), loop_expression: $ => seq("loop", optional($.label), $.block), for_expression: $ => seq( "for", field("pattern", $.binding_pattern), "in", field("value", $.expression), optional($.label), field("body", $.block), ), block: $ => seq("{", scopeBody($, $.expression), "}"), pattern: $ => prec.left(seq($.pattern_atom, repeat(seq("or", $.pattern_atom)))), binding_pattern: $ => choice( "_", $.identifier, $.unit_pattern, $.tuple_pattern, $.product_pattern, ), pattern_atom: $ => choice( "_", $.identifier, $.signed_numeric_literal, $.string_literal, $.character_literal, $.boolean_literal, $.nil_literal, $.sum_pattern, $.bits_pattern, $.unit_pattern, $.tuple_pattern, $.product_pattern, $.array_pattern, ), unit_pattern: _ => seq("(", ")"), tuple_pattern: $ => seq("(", listBodyAtLeastTwo($, $.pattern), ")"), product_pattern: $ => seq("{", listBody($, $.product_pattern_field), "}"), product_pattern_field: $ => seq($.identifier, optional(seq("as", $.identifier))), sum_pattern: $ => seq( "|", optional($.type_name), ".", field("variant", $.identifier), optional($.pattern_atom), ), bits_pattern: $ => seq( "|", optional($.type_name), ".", "{", listBody($, $.identifier), "}", ), array_pattern: $ => seq("[", listBody($, $.array_pattern_entry), "]"), array_pattern_entry: $ => choice($.pattern, seq("...", choice($.identifier, "_"))), pipeline_expression: $ => prec.dynamic(1, prec.left(choice( seq(field("input", choice($._prefix_expression, $.function_literal)), $.inline_pipeline_stage, repeat($.pipeline_stage)), seq(field("input", choice($._non_assignment_expression, $.function_literal)), $.continuation_pipeline_stage, repeat($.pipeline_stage)), ))), pipeline_stage: $ => choice($.inline_pipeline_stage, $.continuation_pipeline_stage), inline_pipeline_stage: $ => choice($.direct_pipeline_stage, $.aggregate_pipeline_stage), continuation_pipeline_stage: $ => choice( seq( $._direct_pipeline_continuation, field("callable", $.pipeline_callable), repeat(choice("?", "!")), ), seq( $._aggregate_pipeline_continuation, field("callable", $.aggregate_pipeline_callable), field("arguments", $.aggregate_tail), repeat(choice("?", "!")), ), ), direct_pipeline_stage: $ => seq( "->", field("callable", $.pipeline_callable), repeat(choice("?", "!")), ), aggregate_pipeline_stage: $ => seq( "|>", field("callable", $.aggregate_pipeline_callable), field("arguments", $.aggregate_tail), repeat(choice("?", "!")), ), aggregate_tail: $ => choice( seq("(", listBodyOneOrMore($, $.expression), ")"), seq("[", optional(listBody($, $.array_tail_entry)), "]"), ), array_tail_entry: $ => choice($.expression, seq("...", $.expression)), pipeline_callable: $ => seq( $.primary_expression, repeat(choice( seq(".", $.identifier), seq("[", $.expression, "]"), )), ), aggregate_pipeline_callable: $ => seq( $.primary_expression, repeat(seq(".", $.identifier)), ), _ordinary_expression: $ => choice( $.assignment_expression, $._non_assignment_expression, ), _non_assignment_expression: $ => choice( $.range_expression, $._binary_expression, ), _binary_expression: $ => choice( $.binary_expression, $._prefix_expression, ), _prefix_expression: $ => choice( $.unary_expression, $.postfix_expression, $.primary_expression, ), assignment_expression: $ => prec.right(PREC.ASSIGNMENT, seq( field("left", $._non_assignment_expression), field("operator", choice("=", "+=", "-=", "*=", "/=", "%=", "&=", "^=", "|=")), field("right", choice( $.function_literal, $.return_expression, $.break_expression, $.pipeline_expression, $._non_assignment_expression, )), )), range_expression: $ => prec.left(PREC.RANGE, seq( field("left", $._binary_expression), field("operator", choice("..<", "..=")), field("right", $._binary_expression), )), binary_expression: $ => choice( binaryExpression($, "or", PREC.OR, $._or_continuation, true), binaryExpression($, "and", PREC.AND, $._and_continuation, true), binaryExpression($, choice("<", "<=", ">", ">=", "=="), PREC.COMPARE, $._comparison_continuation, true), binaryExpression($, "|", PREC.BIT_OR, null), binaryExpression($, "^", PREC.BIT_XOR), binaryExpression($, "&", PREC.BIT_AND, null), binaryExpression($, choice("+", "-"), PREC.ADD, "+"), binaryExpression($, choice("*", "/", "%"), PREC.MULTIPLY), ), unary_expression: $ => prec.right(PREC.PREFIX, seq( field("operator", choice("not", "!", "-", "~")), field("operand", $._prefix_expression), )), postfix_expression: $ => prec.left(PREC.POSTFIX, seq( field("value", $.primary_expression), repeat1(field("operator", choice( seq(".", field("member", $.identifier)), seq("[", field("index", $.expression), "]"), "?", "!", ))), )), primary_expression: $ => choice( $.literal, $.identifier, $.block, $.if_expression, $.match_expression, $.loop_expression, $.for_expression, $.tuple_value, $.collection_literal, $.product_constructor, $.sum_constructor, $.bits_constructor, $.default_expression, ), tuple_value: $ => seq("(", listBodyAtLeastTwo($, $.expression), ")"), collection_literal: $ => seq("[", listBody($, $.collection_entry), "]"), collection_entry: $ => choice( seq($.expression, optional(seq("=>", $.expression))), seq("...", $.expression), ), product_constructor: $ => seq( "&", optional($.type_name), ".", "{", scopeBody($, $.product_entry), "}", ), product_entry: $ => choice( seq($.identifier, choice(":", "="), $.expression), seq("...", $.expression), ), sum_constructor: $ => seq("|", optional($.type_name), ".", $.identifier), bits_constructor: $ => seq("|", optional($.type_name), ".", "{", listBody($, $.identifier), "}"), literal: $ => choice( $.signed_numeric_literal, $.string_literal, $.character_literal, $.boolean_literal, $.nil_literal, $.unit_literal, ), boolean_literal: _ => choice("true", "false"), nil_literal: _ => "nil", default_expression: _ => "default", unit_literal: _ => seq("(", ")"), signed_numeric_literal: $ => seq(optional("-"), choice( $.integer_literal, $.float_literal, alias($._trailing_dot_float, $.float_literal), )), signed_integer_literal: $ => seq(optional("-"), $.integer_literal), integer_literal: _ => token(choice( /0[bB][01](?:_?[01])*/, /0[oO][0-7](?:_?[0-7])*/, /0[xX][0-9a-fA-F](?:_?[0-9a-fA-F])*/, /[0-9](?:_?[0-9])*/, )), float_literal: _ => token(choice( /[0-9](?:_?[0-9])*\.[0-9](?:_?[0-9])*(?:[eE][+-]?[0-9](?:_?[0-9])*)?/, /\.[0-9](?:_?[0-9])*(?:[eE][+-]?[0-9](?:_?[0-9])*)?/, /[0-9](?:_?[0-9])*[eE][+-]?[0-9](?:_?[0-9])*/, )), string_literal: _ => token(/'(?:[^'\\\r\n]|\\(?:[\\'nrt0]|u\{[0-9a-fA-F]{1,6}\}))*'/), character_literal: _ => token(/c'(?:[^'\\\r\n]|\\(?:[\\'nrt0]|u\{[0-9a-fA-F]{1,6}\}))'/), type_name: $ => choice($.identifier, $.qualified_name), qualified_name: $ => seq($.identifier, repeat1(seq(".", $.identifier))), identifier: _ => /[A-Za-z_][A-Za-z0-9_]*/, comment: _ => token(seq("#", /[^\r\n]*/)), _direct_pipeline_continuation: _ => token(seq(/\r?\n[ \t]*/, "->")), _aggregate_pipeline_continuation: _ => token(seq(/\r?\n[ \t]*/, "|>")), _comparison_continuation: _ => token(seq(/\r?\n[ \t]*/, choice("<=", ">=", "==", "<", ">"))), _newline: _ => /\r?\n/, _newlines: $ => repeat1($._newline), _scope_separator: $ => choice($._newlines, ";"), }, }); function scopeBody($, rule) { return seq( repeat($._newline), optional(seq( rule, repeat(seq($._scope_separator, rule)), repeat($._newline), )), ); } function listBody($, rule) { return seq( repeat($._newline), optional(seq( rule, repeat(seq(choice(seq(",", repeat($._newline)), $._newlines), rule)), optional(","), repeat($._newline), )), ); } function listBodyOneOrMore($, rule) { return seq( repeat($._newline), rule, repeat(seq(choice(seq(",", repeat($._newline)), $._newlines), rule)), optional(","), repeat($._newline), ); } function listBodyAtLeastTwo($, rule) { return seq( repeat($._newline), rule, choice(seq(",", repeat($._newline)), $._newlines), rule, repeat(seq(choice(seq(",", repeat($._newline)), $._newlines), rule)), optional(","), repeat($._newline), ); } function binaryExpression($, operator, precedence, continuationOperator = operator, continuationIsToken = false) { const operatorRule = continuationOperator === null ? seq(operator, optional($._newline)) : choice( seq(operator, optional($._newline)), continuationIsToken ? continuationOperator : token(seq(/\r?\n[ \t]*/, continuationOperator)), ); return prec.left(precedence, seq( field("left", $._binary_expression), field("operator", operatorRule), field("right", $._binary_expression), )); }