add generics

This commit is contained in:
paul committed 2026-10-23 02:13:30 -04:00
1 parent 79533e9537
commit 2204d58102
22 files changed
+47358 -37579

No files matched your search

+150 -28
View File
@@ -17,10 +17,12 @@ const PREC = {
POSTFIX: 13,
};
// Hard keywords and the primitive type names; `reserved_word` adds the
// future-reserved `context`.
const RESERVED_WORDS = [
"and", "as", "bits", "break", "continue", "default", "distinct", "else",
"err", "false", "for", "if", "in", "loop", "match", "nil", "not", "ok", "or",
"pub", "return", "some", "true", "use",
"and", "as", "bits", "break", "continue", "default", "distinct",
"else", "false", "for", "if", "in", "loop", "match", "not", "or", "pub",
"return", "true", "use",
"bit", "chr", "f32", "f64", "i8", "i16", "i32", "i64", "nvr", "str",
"u8", "u16", "u32", "u64",
];
@@ -37,7 +39,7 @@ module.exports = grammar({
extras: $ => [/[ \t]/, $.comment],
word: $ => $.identifier,
reserved: {
global: _ => RESERVED_WORDS,
global: $ => [...RESERVED_WORDS, $.reserved_word],
},
supertypes: $ => [
@@ -67,6 +69,7 @@ module.exports = grammar({
[$.product_constructor, $._scope_separator],
[$.tuple_type],
[$.type_application],
[$.binder],
[$.tuple_pattern],
[$.product_pattern],
[$.array_pattern],
@@ -87,6 +90,7 @@ module.exports = grammar({
[$.pattern_atom, $.primary_expression, $.type_name],
[$.type_name, $.qualified_name],
[$.primary_expression, $.type_name],
[$._pipeline_input_primary, $.type_name],
[$.type_expression, $.function_type],
[$.array_type, $.collection_literal],
],
@@ -110,22 +114,29 @@ module.exports = grammar({
use_declaration: $ => seq(
"use",
choice(
seq($.module_path, optional(seq("as", field("alias", $.identifier)))),
seq(
$.module_path,
optional(choice(seq("as", field("alias", $.identifier)), $.import_filter)),
),
seq($.module_path, ".", "{", listBody($, $.import_member), "}"),
),
),
import_member: $ => seq(
field("name", $.identifier),
optional(seq("as", field("alias", $.identifier))),
optional(choice(seq("as", field("alias", $.identifier)), $.import_filter)),
),
import_filter: $ => seq("for", field("type", $.type_expression)),
module_path: $ => choice(
$.type_name,
seq(choice(".", ".."), $.type_name),
),
pub_declaration: $ => seq("pub", "{", scopeBody($, $.identifier), "}"),
pub_declaration: $ => seq("pub", "{", scopeBody($, $.pub_entry), "}"),
pub_entry: $ => seq(field("name", $.identifier), optional($.import_filter)),
expression: $ => choice(
$.declaration,
@@ -161,6 +172,14 @@ module.exports = grammar({
optional(seq("=", field("value", $.value_expression))),
),
),
seq(
field("name", $.identifier),
":",
field("binder", $.binder),
field("type", $.type_expression),
":",
field("value", $.value_expression),
),
seq(
field("pattern", choice($.tuple_pattern, $.product_pattern)),
choice("::", ":="),
@@ -168,7 +187,33 @@ module.exports = grammar({
),
),
type_definition: $ => seq(optional("distinct"), $.type_expression),
binder: $ => seq("<", listBodyOneOrMore($, $.binder_entry), ">"),
binder_entry: $ => seq(
field("type", $.type_expression),
optional(seq(":", field("constraint", choice($.type_name, $.constraint_body)))),
),
type_definition: $ => choice(
seq(optional("distinct"), $.type_expression),
$.constraint_body,
),
constraint_body: $ => seq(
"<",
">",
"{",
scopeBody($, choice($.constraint_entry, $.constraint_spread)),
"}",
),
constraint_entry: $ => seq(
field("name", $.identifier),
":",
field("output", $.type_expression),
),
constraint_spread: $ => seq("...", field("constraint", $.type_name)),
type_expression: $ => choice(
$.function_type,
@@ -226,6 +271,7 @@ module.exports = grammar({
$.sum_type,
$.bits_type,
$.type_application,
$.output_type,
$.grouped_type,
),
@@ -246,6 +292,15 @@ module.exports = grammar({
">",
),
output_type: $ => seq(
field("constructor", $.type_name),
"<",
field("function", $.type_name),
":",
field("output", $.type_expression),
">",
),
product_type: $ => seq("&", "{", scopeBody($, $.product_field), "}"),
product_field: $ => seq(
@@ -383,8 +438,6 @@ module.exports = grammar({
$.string_literal,
$.character_literal,
$.boolean_literal,
$.nil_literal,
$.wrapper_pattern,
$.sum_pattern,
$.bits_pattern,
$.unit_pattern,
@@ -401,20 +454,15 @@ module.exports = grammar({
sum_pattern: $ => seq(
"|",
optional($.type_name),
optional(field("type", $.type_name)),
".",
field("variant", $.identifier),
optional($.pattern_atom),
),
wrapper_pattern: $ => seq(
field("wrapper", choice("some", "ok", "err")),
field("value", $.pattern_atom),
optional(field("payload", $.pattern_atom)),
),
bits_pattern: $ => seq(
"|",
optional($.type_name),
optional(field("type", $.type_name)),
".",
"{",
listBody($, $.identifier),
@@ -424,11 +472,58 @@ module.exports = grammar({
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)),
// A pipeline headed by a leading-operator continuation applies to the
// complete ordinary expression on the preceding line. Its input excludes
// a bare same-line pipeline, which instead absorbs the continuation
// stages itself, so every pipeline is one flat node.
pipeline_expression: $ => prec.dynamic(1, prec.left(seq(
field("input", $._pipeline_head),
$.continuation_pipeline_stage,
repeat($.pipeline_stage),
))),
_pipeline_head: $ => choice(
$.range_expression,
$.binary_expression,
$.unary_expression,
$.postfix_expression,
$.primary_expression,
),
// Same-line stages are postfix: they bind to the operand immediately to
// their left, tighter than every prefix and infix operator. The static
// precedence makes a stage sequence inside an operator expression close
// before a continuation stage, which then applies to the whole
// expression. The dynamic precedence reads a category-ambiguous
// `name :: a -> b` as a pipeline rather than a function type.
_inline_pipeline_expression: $ => prec.dynamic(1, prec.left(1, seq(
field("input", choice(
$.postfix_expression,
$._pipeline_input_primary,
$.function_literal,
)),
$.inline_pipeline_stage,
repeat($.pipeline_stage),
))),
// `-3->f` negates the pipeline, so a stage input is never a negative
// literal; a bare `-3` remains one `signed_numeric_literal`.
_pipeline_input_primary: $ => choice(
$._unsigned_literal,
$.identifier,
$.block,
$.if_expression,
$.match_expression,
$.loop_expression,
$.for_expression,
$.tuple_value,
$.collection_literal,
$.product_constructor,
$.sum_constructor,
$.bits_constructor,
$.default_expression,
),
pipeline_stage: $ => choice($.inline_pipeline_stage, $.continuation_pipeline_stage),
inline_pipeline_stage: $ => choice($.direct_pipeline_stage, $.aggregate_pipeline_stage),
continuation_pipeline_stage: $ => choice(
@@ -495,6 +590,7 @@ module.exports = grammar({
_prefix_expression: $ => choice(
$.unary_expression,
alias($._inline_pipeline_expression, $.pipeline_expression),
$.postfix_expression,
$.primary_expression,
),
@@ -529,7 +625,7 @@ module.exports = grammar({
),
unary_expression: $ => prec.right(PREC.PREFIX, seq(
field("operator", choice("not", "-", "~", "some", "ok", "err")),
field("operator", choice("not", "-", "~")),
field("operand", $._prefix_expression),
)),
@@ -580,27 +676,49 @@ module.exports = grammar({
seq("...", $.expression),
),
sum_constructor: $ => seq("|", optional($.type_name), ".", $.identifier),
bits_constructor: $ => seq("|", optional($.type_name), ".", "{", listBody($, $.identifier), "}"),
sum_constructor: $ => seq(
"|",
optional(field("type", $.type_name)),
".",
field("variant", $.identifier),
),
bits_constructor: $ => seq(
"|",
optional(field("type", $.type_name)),
".",
"{",
listBody($, $.identifier),
"}",
),
literal: $ => choice(
$.signed_numeric_literal,
$.string_literal,
$.character_literal,
$.boolean_literal,
$.nil_literal,
$.unit_literal,
),
_unsigned_literal: $ => choice(
alias($._unsigned_numeric_literal, $.signed_numeric_literal),
$.string_literal,
$.character_literal,
$.boolean_literal,
$.unit_literal,
),
boolean_literal: _ => choice("true", "false"),
nil_literal: _ => "nil",
default_expression: _ => "default",
unit_literal: _ => seq("(", ")"),
signed_numeric_literal: $ => seq(optional("-"), choice(
signed_numeric_literal: $ => choice(
prec.dynamic(1, seq("-", $._unsigned_numeric_literal)),
$._unsigned_numeric_literal,
),
_unsigned_numeric_literal: $ => choice(
$.integer_literal,
$.float_literal,
alias($._trailing_dot_float, $.float_literal),
)),
),
signed_integer_literal: $ => seq(optional("-"), $.integer_literal),
integer_literal: _ => token(choice(
@@ -627,6 +745,10 @@ module.exports = grammar({
_direct_pipeline_continuation: _ => token(seq(/\r?\n[ \t]*/, "->")),
_aggregate_pipeline_continuation: _ => token(seq(/\r?\n[ \t]*/, "|>")),
_comparison_continuation: _ => token(seq(/\r?\n[ \t]*/, choice("<=", ">=", "==", "<", ">"))),
// `context` is reserved for future language use and has no production,
// so this token exists only to keep it out of identifiers.
reserved_word: _ => "context",
_newline: _ => /\r?\n/,
_newlines: $ => repeat1($._newline),
_scope_separator: $ => choice($._newlines, ";"),