This commit is contained in:
2026-09-23 00:36:22 -04:00
commit 6e4f9cd65a
74 changed files with 57163 additions and 0 deletions
+692
View File
@@ -0,0 +1,692 @@
/// <reference types="tree-sitter-cli/dsl" />
// @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),
));
}