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

+5 -4
View File
@@ -10,7 +10,9 @@ The public named-node and field contract is documented in
## Development ## Development
Tree-sitter 0.27 and Node.js are required. Tree-sitter 0.27 is required. `tree-sitter generate` evaluates `grammar.js`
with Node.js by default; without Node.js, pass `--js-runtime native` to use the
CLI's bundled QuickJS runtime.
```sh ```sh
npm install npm install
@@ -28,9 +30,8 @@ script: the adjacency-only `=-` and `!=` lexer errors, and recovery after a
repeated result operator. These need lexical or recovery handling rather than repeated result operator. These need lexical or recovery handling rather than
a looser grammar rule. a looser grammar rule.
The future-reserved word `context` is not yet emitted as a Tree-sitter keyword The future-reserved word `context` is a `reserved_word` token with no grammar
because it has no grammar production. It should move into lexical handling with production, so it is rejected wherever an identifier is expected.
the two adjacency checks above.
Generated parser sources under `src/` are committed so consumers do not need Generated parser sources under `src/` are committed so consumers do not need
Node.js or the Tree-sitter CLI. Node.js or the Tree-sitter CLI.
+270 -13
View File
@@ -196,10 +196,10 @@ mod tests {
} }
#[test] #[test]
fn exposes_wrappers_and_untyped_inputs() { fn exposes_sum_spelled_optionals_and_untyped_inputs() {
let source = "present: i32?: some 1\n\ let source = "present: i32?: 1->|.some\n\
double :: @ value { value * 2 }\n\ double :: @ value { value * 2 }\n\
unwrap :: @ result { match result { ok inner => inner; err _ => 0 } }\n"; unwrap :: @ result { match result { |.ok inner => inner; |.err _ => 0 } }\n";
let tree = parse(source); let tree = parse(source);
assert!( assert!(
!tree.root_node().has_error(), !tree.root_node().has_error(),
@@ -209,10 +209,22 @@ mod tests {
let present = tree.root_node().named_child(0).unwrap(); let present = tree.root_node().named_child(0).unwrap();
let wrapped = present.child_by_field_name("value").unwrap(); let wrapped = present.child_by_field_name("value").unwrap();
assert_eq!(wrapped.kind(), "unary_expression"); assert_eq!(wrapped.kind(), "pipeline_expression");
assert_eq!( assert_eq!(
wrapped wrapped.child_by_field_name("input").unwrap().kind(),
.child_by_field_name("operator") "signed_numeric_literal"
);
let stage = wrapped.named_child(1).unwrap();
assert_eq!(stage.kind(), "direct_pipeline_stage");
let constructor = stage
.child_by_field_name("callable")
.unwrap()
.named_child(0)
.unwrap();
assert_eq!(constructor.kind(), "sum_constructor");
assert_eq!(
constructor
.child_by_field_name("variant")
.unwrap() .unwrap()
.utf8_text(source.as_bytes()) .utf8_text(source.as_bytes())
.unwrap(), .unwrap(),
@@ -233,25 +245,270 @@ mod tests {
let body = function.child_by_field_name("body").unwrap(); let body = function.child_by_field_name("body").unwrap();
let arm = body.named_child(0).unwrap().named_child(1).unwrap(); let arm = body.named_child(0).unwrap().named_child(1).unwrap();
let pattern = arm.child_by_field_name("pattern").unwrap(); let pattern = arm.child_by_field_name("pattern").unwrap();
let wrapper = pattern.named_child(0).unwrap(); let variant = pattern.named_child(0).unwrap();
assert_eq!(wrapper.kind(), "wrapper_pattern"); assert_eq!(variant.kind(), "sum_pattern");
assert_eq!( assert_eq!(
wrapper variant
.child_by_field_name("wrapper") .child_by_field_name("variant")
.unwrap() .unwrap()
.utf8_text(source.as_bytes()) .utf8_text(source.as_bytes())
.unwrap(), .unwrap(),
"ok" "ok"
); );
assert_eq!(
variant.child_by_field_name("payload").unwrap().kind(),
"identifier"
);
} }
#[test] #[test]
fn rejects_removed_wrapper_forms() { fn binds_same_line_stages_tighter_than_operators() {
let source = "mixed :: -3->add_ten + 2 * 3->double - {1 + 1}->double\n";
let tree = parse(source);
assert!(
!tree.root_node().has_error(),
"{}",
tree.root_node().to_sexp()
);
let declaration = tree.root_node().named_child(0).unwrap();
let subtraction = declaration.child_by_field_name("value").unwrap();
assert_eq!(subtraction.kind(), "binary_expression");
assert_eq!(
subtraction
.child_by_field_name("operator")
.unwrap()
.utf8_text(source.as_bytes())
.unwrap(),
"-"
);
let addition = subtraction.child_by_field_name("left").unwrap();
assert_eq!(addition.kind(), "binary_expression");
let negation = addition.child_by_field_name("left").unwrap();
assert_eq!(negation.kind(), "unary_expression");
let negated = negation.child_by_field_name("operand").unwrap();
assert_eq!(negated.kind(), "pipeline_expression");
assert_eq!(
negated
.child_by_field_name("input")
.unwrap()
.utf8_text(source.as_bytes())
.unwrap(),
"3"
);
let product = addition.child_by_field_name("right").unwrap();
assert_eq!(product.kind(), "binary_expression");
assert_eq!(
product.child_by_field_name("right").unwrap().kind(),
"pipeline_expression"
);
let grouped = subtraction.child_by_field_name("right").unwrap();
assert_eq!(grouped.kind(), "pipeline_expression");
assert_eq!(
grouped.child_by_field_name("input").unwrap().kind(),
"block"
);
}
#[test]
fn applies_continuation_stages_to_the_whole_preceding_expression() {
let source = "flat := value->first\n->second\nwhole := a + b->f\n->g\n";
let tree = parse(source);
assert!(
!tree.root_node().has_error(),
"{}",
tree.root_node().to_sexp()
);
let flat = tree
.root_node()
.named_child(0)
.unwrap()
.child_by_field_name("value")
.unwrap();
assert_eq!(flat.kind(), "pipeline_expression");
assert_eq!(
flat.child_by_field_name("input").unwrap().kind(),
"identifier"
);
assert_eq!(flat.named_child(1).unwrap().kind(), "direct_pipeline_stage");
assert_eq!(
flat.named_child(2).unwrap().kind(),
"continuation_pipeline_stage"
);
assert_eq!(flat.named_child_count(), 3);
let whole = tree
.root_node()
.named_child(1)
.unwrap()
.child_by_field_name("value")
.unwrap();
assert_eq!(whole.kind(), "pipeline_expression");
let head = whole.child_by_field_name("input").unwrap();
assert_eq!(head.kind(), "binary_expression");
assert_eq!(
head.child_by_field_name("right").unwrap().kind(),
"pipeline_expression"
);
assert_eq!(
whole.named_child(1).unwrap().kind(),
"continuation_pipeline_stage"
);
}
#[test]
fn exposes_generic_binders_constraints_and_output_types() {
let source = "Show :: <>{ to_str: str; ...Display }\n\
pick: <T: Show> (T, T) -> T: @ (first, second): (T, T) { first }\n\
Card :: &{ label: Output<make_label: Show>: }\n";
let tree = parse(source);
assert!(
!tree.root_node().has_error(),
"{}",
tree.root_node().to_sexp()
);
let show = tree.root_node().named_child(0).unwrap();
let definition = show.child_by_field_name("value").unwrap();
assert_eq!(definition.kind(), "type_definition");
let constraint = definition.named_child(0).unwrap();
assert_eq!(constraint.kind(), "constraint_body");
let entry = constraint.named_child(0).unwrap();
assert_eq!(entry.kind(), "constraint_entry");
assert_eq!(
entry
.child_by_field_name("name")
.unwrap()
.utf8_text(source.as_bytes())
.unwrap(),
"to_str"
);
assert_eq!(
entry.child_by_field_name("output").unwrap().kind(),
"primitive_type"
);
let spread = constraint.named_child(1).unwrap();
assert_eq!(spread.kind(), "constraint_spread");
assert_eq!(
spread
.child_by_field_name("constraint")
.unwrap()
.utf8_text(source.as_bytes())
.unwrap(),
"Display"
);
let pick = tree.root_node().named_child(1).unwrap();
let binder = pick.child_by_field_name("binder").unwrap();
assert_eq!(binder.kind(), "binder");
let parameter = binder.named_child(0).unwrap();
assert_eq!(parameter.kind(), "binder_entry");
assert_eq!(
parameter
.child_by_field_name("type")
.unwrap()
.utf8_text(source.as_bytes())
.unwrap(),
"T"
);
assert_eq!(
parameter
.child_by_field_name("constraint")
.unwrap()
.utf8_text(source.as_bytes())
.unwrap(),
"Show"
);
assert_eq!(
pick.child_by_field_name("type").unwrap().kind(),
"function_type"
);
assert_eq!(
pick.child_by_field_name("value").unwrap().kind(),
"function_literal"
);
let card = tree.root_node().named_child(2).unwrap();
let field = card
.child_by_field_name("value")
.unwrap()
.named_child(0)
.unwrap()
.named_child(0)
.unwrap();
assert_eq!(field.kind(), "product_field");
let output = field.child_by_field_name("type").unwrap();
assert_eq!(output.kind(), "output_type");
assert_eq!(
output
.child_by_field_name("function")
.unwrap()
.utf8_text(source.as_bytes())
.unwrap(),
"make_label"
);
assert_eq!(
output
.child_by_field_name("output")
.unwrap()
.utf8_text(source.as_bytes())
.unwrap(),
"Show"
);
}
#[test]
fn exposes_import_filters() {
let source = "use math.{sqrt for f64, pi}\nuse .shapes.area for Circle\npub { area for Circle; sqrt }\n";
let tree = parse(source);
assert!(
!tree.root_node().has_error(),
"{}",
tree.root_node().to_sexp()
);
let group = tree.root_node().named_child(0).unwrap();
assert_eq!(group.kind(), "use_declaration");
let member = group.named_child(1).unwrap();
assert_eq!(member.kind(), "import_member");
let filter = member.named_child(1).unwrap();
assert_eq!(filter.kind(), "import_filter");
assert_eq!(
filter.child_by_field_name("type").unwrap().kind(),
"primitive_type"
);
assert_eq!(group.named_child(2).unwrap().named_child_count(), 1);
let single = tree.root_node().named_child(1).unwrap();
assert_eq!(single.named_child(1).unwrap().kind(), "import_filter");
let publication = tree.root_node().named_child(2).unwrap();
assert_eq!(publication.kind(), "pub_declaration");
let filtered = publication.named_child(0).unwrap();
assert_eq!(filtered.kind(), "pub_entry");
assert_eq!(
filtered
.child_by_field_name("name")
.unwrap()
.utf8_text(source.as_bytes())
.unwrap(),
"area"
);
assert_eq!(filtered.named_child(1).unwrap().kind(), "import_filter");
assert_eq!(publication.named_child(1).unwrap().named_child_count(), 1);
}
#[test]
fn rejects_removed_wrapper_forms_and_reserved_words() {
for source in [ for source in [
"old: i32?: 1->|.some\n", "present: i32?: some 1\n",
"old :: @ value: i32 -> i32 ! str { value! }\n", "old :: @ value: i32 -> i32 ! str { value! }\n",
"old :: @ value: str -> i32 ! str { !value }\n", "old :: @ value: str -> i32 ! str { !value }\n",
"some := 1\n", "context := 1\n",
] { ] {
assert!(parse(source).root_node().has_error(), "accepted {source}"); assert!(parse(source).root_node().has_error(), "accepted {source}");
} }
+58 -15
View File
@@ -15,6 +15,7 @@ and `value_expression` rules are hidden supertypes.
- `name` for a named declaration; - `name` for a named declaration;
- `pattern` for a destructuring declaration; - `pattern` for a destructuring declaration;
- `binder` for the type parameters of a generic function declaration;
- `type` for an explicit type; - `type` for an explicit type;
- `value` for an initializer. - `value` for an initializer.
@@ -22,6 +23,21 @@ The punctuation distinguishes immutable, mutable, typed, and deferred forms.
Consumers should inspect that punctuation rather than infer the form from the Consumers should inspect that punctuation rather than infer the form from the
set of fields alone. set of fields alone.
A `binder` holds one `binder_entry` per parameter or bound. Each entry exposes
`type` and, after `:`, an optional `constraint`, which is a constraint name or
an inline `constraint_body`. A `constraint_body` (`<>{ ... }`) also appears as
the whole initializer of a `::` declaration, inside `type_definition`. It
contains `constraint_entry` nodes with `name` and `output` fields and
`constraint_spread` nodes with a `constraint` field.
## Modules
`use_declaration` contains a `module_path` and either an `alias`, an
`import_filter`, or `import_member` nodes. An `import_member` exposes `name`
and either `alias` or an `import_filter`. `import_filter` wraps the `for`
clause and exposes its `type`. `pub_declaration` contains `pub_entry` nodes,
each with a `name` and an optional `import_filter`.
## Expressions ## Expressions
Only expressions that actually contain an operator produce an operator node: Only expressions that actually contain an operator produce an operator node:
@@ -39,16 +55,27 @@ Precedence-only rules are hidden. For example, `a + b * -c` has one outer and
one inner `binary_expression`, then a `unary_expression`; it does not contain a one inner `binary_expression`, then a `unary_expression`; it does not contain a
wrapper for every precedence level. wrapper for every precedence level.
Literal leaves are named. Boolean and nil values use `boolean_literal` and Pipeline stages are postfix. A same-line stage binds to the operand
`nil_literal`; numeric, string, character, and unit literals retain their immediately to its left, tighter than every prefix and infix operator, so
specific nodes. `default_expression` is also named. `-x->abs` is a `unary_expression` whose operand is a `pipeline_expression`,
and `a + b->f` is a `binary_expression` whose right operand is one. A
`pipeline_expression` is one flat node: its `input` is followed by every stage
that applies to it, same-line and continuation alike. A leading-operator
continuation applies to the complete ordinary expression on the preceding
line, so in `a + b->f` followed by a continuation `->g` the outer
`pipeline_expression` has the `binary_expression` as its `input`. A negative
literal is never a stage input: `-3->f` negates the pipeline, while a bare `-3`
remains one `signed_numeric_literal`.
`some`, `ok`, and `err` construct optional and result values. They are prefix Literal leaves are named. Boolean values use `boolean_literal`; numeric,
operators, so `some value` and `ok {x->f}` appear as `unary_expression` nodes string, character, and unit literals retain their specific nodes.
whose `operator` is the keyword. They bind like `-` and `~`, so `ok x->f` `default_expression` is also named.
wraps `x` and then pipes the result into `f`, while `ok {x->f}` wraps the
pipeline. The older `|.some`/`|.ok`/`|.err` Optionals and results are ordinary sums. `|.none` is a `sum_constructor`, and
constructors, postfix `value!`, and prefix `!error` are no longer accepted. `value->|.some`, `value->|.ok`, and `error->|.err` are pipelines whose stage
callable is a `sum_constructor`. `sum_constructor` exposes an optional `type`
and the `variant`. The former `some`, `ok`, `err`, and `nil` keywords are
ordinary identifiers. Postfix `value!` and prefix `!error` are not accepted.
`function_literal` takes one input and owns the `body` field. A pattern with `function_literal` takes one input and owns the `body` field. A pattern with
one annotation for the whole input, such as `@ value: i32` or one annotation for the whole input, such as `@ value: i32` or
@@ -66,10 +93,10 @@ may also expose `effect` (`->` or `=>`) and `result`.
## Patterns ## Patterns
`wrapper_pattern` matches `some`, `ok`, or `err` applied to one pattern atom. `sum_pattern` matches a variant, including `|.none`, `|.some pattern`,
It exposes `wrapper` (the keyword) and `value` (the inner pattern). `nil` is `|.ok pattern`, and `|.err pattern`. It exposes an optional `type`, the
still a `nil_literal` pattern. Sum patterns retain `sum_pattern` with a `variant`, and an optional `payload` pattern atom. `bits_pattern` exposes the
`variant` field. optional `type` as well.
## Types ## Types
@@ -84,9 +111,13 @@ Meaningful type constructors are named directly:
| `map_type` | `key`, `value` | | `map_type` | `key`, `value` |
| `grouped_type` | `value` | | `grouped_type` | `value` |
| `type_application` | `constructor` followed by its type arguments | | `type_application` | `constructor` followed by its type arguments |
| `output_type` | `constructor`, `function`, `output` |
Primitive, unit, tuple, product, sum, and bits types retain their corresponding `output_type` is the `Output<f: U>` spelling: a type name applied to a
named nodes. Internal primary/container/result categories are hidden. function name and a type. The grammar does not check that the constructor is
spelled `Output`. Primitive, unit, tuple, product, sum, and bits types retain
their corresponding named nodes. Internal primary/container/result categories
are hidden.
## Trivia and physical lines ## Trivia and physical lines
@@ -96,6 +127,9 @@ alongside the tree. Leading-operator and pipeline continuations use composite
tokens containing their newline; this prevents a scope separator from stealing tokens containing their newline; this prevents a scope separator from stealing
a valid continuation and preserves the language's physical-line rules. a valid continuation and preserves the language's physical-line rules.
The future-reserved word `context` is a `reserved_word` token that no
production accepts, so it is rejected wherever an identifier is expected.
## Compatibility ## Compatibility
Adding nodes or fields is compatible. Removing or renaming a named node or Adding nodes or fields is compatible. Removing or renaming a named node or
@@ -103,5 +137,14 @@ field, changing its meaning, or changing which source construct owns it is a
breaking CST change. Until the package reaches 1.0, such changes require corpus breaking CST change. Until the package reaches 1.0, such changes require corpus
snapshot updates and a version change, and must be called out in release notes. snapshot updates and a version change, and must be called out in release notes.
Version 0.2.0 made these breaking changes:
- `nil_literal` and `wrapper_pattern` are gone, and `some`, `ok`, `err`, and
`nil` are no longer keywords; optionals and results use `sum_constructor`
and `sum_pattern`.
- A same-line pipeline is an operand of prefix and infix operators rather than
taking an operator expression as its `input`.
- `pub_declaration` contains `pub_entry` nodes instead of bare identifiers.
The Tree-sitter tree is designed for source tooling and error recovery; it does The Tree-sitter tree is designed for source tooling and error recovery; it does
not perform semantic validation. not perform semantic validation.
+150 -28
View File
@@ -17,10 +17,12 @@ const PREC = {
POSTFIX: 13, POSTFIX: 13,
}; };
// Hard keywords and the primitive type names; `reserved_word` adds the
// future-reserved `context`.
const RESERVED_WORDS = [ const RESERVED_WORDS = [
"and", "as", "bits", "break", "continue", "default", "distinct", "else", "and", "as", "bits", "break", "continue", "default", "distinct",
"err", "false", "for", "if", "in", "loop", "match", "nil", "not", "ok", "or", "else", "false", "for", "if", "in", "loop", "match", "not", "or", "pub",
"pub", "return", "some", "true", "use", "return", "true", "use",
"bit", "chr", "f32", "f64", "i8", "i16", "i32", "i64", "nvr", "str", "bit", "chr", "f32", "f64", "i8", "i16", "i32", "i64", "nvr", "str",
"u8", "u16", "u32", "u64", "u8", "u16", "u32", "u64",
]; ];
@@ -37,7 +39,7 @@ module.exports = grammar({
extras: $ => [/[ \t]/, $.comment], extras: $ => [/[ \t]/, $.comment],
word: $ => $.identifier, word: $ => $.identifier,
reserved: { reserved: {
global: _ => RESERVED_WORDS, global: $ => [...RESERVED_WORDS, $.reserved_word],
}, },
supertypes: $ => [ supertypes: $ => [
@@ -67,6 +69,7 @@ module.exports = grammar({
[$.product_constructor, $._scope_separator], [$.product_constructor, $._scope_separator],
[$.tuple_type], [$.tuple_type],
[$.type_application], [$.type_application],
[$.binder],
[$.tuple_pattern], [$.tuple_pattern],
[$.product_pattern], [$.product_pattern],
[$.array_pattern], [$.array_pattern],
@@ -87,6 +90,7 @@ module.exports = grammar({
[$.pattern_atom, $.primary_expression, $.type_name], [$.pattern_atom, $.primary_expression, $.type_name],
[$.type_name, $.qualified_name], [$.type_name, $.qualified_name],
[$.primary_expression, $.type_name], [$.primary_expression, $.type_name],
[$._pipeline_input_primary, $.type_name],
[$.type_expression, $.function_type], [$.type_expression, $.function_type],
[$.array_type, $.collection_literal], [$.array_type, $.collection_literal],
], ],
@@ -110,22 +114,29 @@ module.exports = grammar({
use_declaration: $ => seq( use_declaration: $ => seq(
"use", "use",
choice( 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), "}"), seq($.module_path, ".", "{", listBody($, $.import_member), "}"),
), ),
), ),
import_member: $ => seq( import_member: $ => seq(
field("name", $.identifier), 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( module_path: $ => choice(
$.type_name, $.type_name,
seq(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( expression: $ => choice(
$.declaration, $.declaration,
@@ -161,6 +172,14 @@ module.exports = grammar({
optional(seq("=", field("value", $.value_expression))), optional(seq("=", field("value", $.value_expression))),
), ),
), ),
seq(
field("name", $.identifier),
":",
field("binder", $.binder),
field("type", $.type_expression),
":",
field("value", $.value_expression),
),
seq( seq(
field("pattern", choice($.tuple_pattern, $.product_pattern)), field("pattern", choice($.tuple_pattern, $.product_pattern)),
choice("::", ":="), 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( type_expression: $ => choice(
$.function_type, $.function_type,
@@ -226,6 +271,7 @@ module.exports = grammar({
$.sum_type, $.sum_type,
$.bits_type, $.bits_type,
$.type_application, $.type_application,
$.output_type,
$.grouped_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_type: $ => seq("&", "{", scopeBody($, $.product_field), "}"),
product_field: $ => seq( product_field: $ => seq(
@@ -383,8 +438,6 @@ module.exports = grammar({
$.string_literal, $.string_literal,
$.character_literal, $.character_literal,
$.boolean_literal, $.boolean_literal,
$.nil_literal,
$.wrapper_pattern,
$.sum_pattern, $.sum_pattern,
$.bits_pattern, $.bits_pattern,
$.unit_pattern, $.unit_pattern,
@@ -401,20 +454,15 @@ module.exports = grammar({
sum_pattern: $ => seq( sum_pattern: $ => seq(
"|", "|",
optional($.type_name), optional(field("type", $.type_name)),
".", ".",
field("variant", $.identifier), field("variant", $.identifier),
optional($.pattern_atom), optional(field("payload", $.pattern_atom)),
),
wrapper_pattern: $ => seq(
field("wrapper", choice("some", "ok", "err")),
field("value", $.pattern_atom),
), ),
bits_pattern: $ => seq( bits_pattern: $ => seq(
"|", "|",
optional($.type_name), optional(field("type", $.type_name)),
".", ".",
"{", "{",
listBody($, $.identifier), listBody($, $.identifier),
@@ -424,11 +472,58 @@ module.exports = grammar({
array_pattern: $ => seq("[", listBody($, $.array_pattern_entry), "]"), array_pattern: $ => seq("[", listBody($, $.array_pattern_entry), "]"),
array_pattern_entry: $ => choice($.pattern, seq("...", choice($.identifier, "_"))), array_pattern_entry: $ => choice($.pattern, seq("...", choice($.identifier, "_"))),
pipeline_expression: $ => prec.dynamic(1, prec.left(choice( // A pipeline headed by a leading-operator continuation applies to the
seq(field("input", choice($._prefix_expression, $.function_literal)), $.inline_pipeline_stage, repeat($.pipeline_stage)), // complete ordinary expression on the preceding line. Its input excludes
seq(field("input", choice($._non_assignment_expression, $.function_literal)), $.continuation_pipeline_stage, repeat($.pipeline_stage)), // 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), pipeline_stage: $ => choice($.inline_pipeline_stage, $.continuation_pipeline_stage),
inline_pipeline_stage: $ => choice($.direct_pipeline_stage, $.aggregate_pipeline_stage), inline_pipeline_stage: $ => choice($.direct_pipeline_stage, $.aggregate_pipeline_stage),
continuation_pipeline_stage: $ => choice( continuation_pipeline_stage: $ => choice(
@@ -495,6 +590,7 @@ module.exports = grammar({
_prefix_expression: $ => choice( _prefix_expression: $ => choice(
$.unary_expression, $.unary_expression,
alias($._inline_pipeline_expression, $.pipeline_expression),
$.postfix_expression, $.postfix_expression,
$.primary_expression, $.primary_expression,
), ),
@@ -529,7 +625,7 @@ module.exports = grammar({
), ),
unary_expression: $ => prec.right(PREC.PREFIX, seq( unary_expression: $ => prec.right(PREC.PREFIX, seq(
field("operator", choice("not", "-", "~", "some", "ok", "err")), field("operator", choice("not", "-", "~")),
field("operand", $._prefix_expression), field("operand", $._prefix_expression),
)), )),
@@ -580,27 +676,49 @@ module.exports = grammar({
seq("...", $.expression), seq("...", $.expression),
), ),
sum_constructor: $ => seq("|", optional($.type_name), ".", $.identifier), sum_constructor: $ => seq(
bits_constructor: $ => seq("|", optional($.type_name), ".", "{", listBody($, $.identifier), "}"), "|",
optional(field("type", $.type_name)),
".",
field("variant", $.identifier),
),
bits_constructor: $ => seq(
"|",
optional(field("type", $.type_name)),
".",
"{",
listBody($, $.identifier),
"}",
),
literal: $ => choice( literal: $ => choice(
$.signed_numeric_literal, $.signed_numeric_literal,
$.string_literal, $.string_literal,
$.character_literal, $.character_literal,
$.boolean_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, $.unit_literal,
), ),
boolean_literal: _ => choice("true", "false"), boolean_literal: _ => choice("true", "false"),
nil_literal: _ => "nil",
default_expression: _ => "default", default_expression: _ => "default",
unit_literal: _ => seq("(", ")"), 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, $.integer_literal,
$.float_literal, $.float_literal,
alias($._trailing_dot_float, $.float_literal), alias($._trailing_dot_float, $.float_literal),
)), ),
signed_integer_literal: $ => seq(optional("-"), $.integer_literal), signed_integer_literal: $ => seq(optional("-"), $.integer_literal),
integer_literal: _ => token(choice( integer_literal: _ => token(choice(
@@ -627,6 +745,10 @@ module.exports = grammar({
_direct_pipeline_continuation: _ => token(seq(/\r?\n[ \t]*/, "->")), _direct_pipeline_continuation: _ => token(seq(/\r?\n[ \t]*/, "->")),
_aggregate_pipeline_continuation: _ => token(seq(/\r?\n[ \t]*/, "|>")), _aggregate_pipeline_continuation: _ => token(seq(/\r?\n[ \t]*/, "|>")),
_comparison_continuation: _ => token(seq(/\r?\n[ \t]*/, choice("<=", ">=", "==", "<", ">"))), _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/, _newline: _ => /\r?\n/,
_newlines: $ => repeat1($._newline), _newlines: $ => repeat1($._newline),
_scope_separator: $ => choice($._newlines, ";"), _scope_separator: $ => choice($._newlines, ";"),
+5 -4
View File
@@ -6,18 +6,15 @@
"continue" "continue"
"distinct" "distinct"
"else" "else"
"err"
"for" "for"
"if" "if"
"in" "in"
"loop" "loop"
"match" "match"
"not" "not"
"ok"
"or" "or"
"pub" "pub"
"return" "return"
"some"
"use" "use"
] @keyword ] @keyword
@@ -26,7 +23,6 @@
(comment) @comment (comment) @comment
(default_expression) @keyword (default_expression) @keyword
(boolean_literal) @constant.builtin (boolean_literal) @constant.builtin
(nil_literal) @constant.builtin
(string_literal) @string (string_literal) @string
(character_literal) @character (character_literal) @character
(integer_literal) @number (integer_literal) @number
@@ -39,7 +35,12 @@
(product_field name: (identifier) @property) (product_field name: (identifier) @property)
(product_entry (identifier) @property) (product_entry (identifier) @property)
(sum_variant name: (identifier) @constructor) (sum_variant name: (identifier) @constructor)
(sum_constructor variant: (identifier) @constructor)
(sum_pattern variant: (identifier) @constructor)
(bits_member (identifier) @constant) (bits_member (identifier) @constant)
(constraint_entry name: (identifier) @function)
(output_type function: (identifier) @function)
(output_type function: (qualified_name (identifier) @function .))
[ [
"=" "+=" "-=" "*=" "/=" "%=" "&=" "^=" "|=" "=" "+=" "-=" "*=" "/=" "%=" "&=" "^=" "|="
+718 -205
View File
File diff suppressed because it is too large. Load diff
+361 -108
View File
@@ -65,10 +65,6 @@
"type": "character_literal", "type": "character_literal",
"named": true "named": true
}, },
{
"type": "nil_literal",
"named": true
},
{ {
"type": "signed_numeric_literal", "type": "signed_numeric_literal",
"named": true "named": true
@@ -129,10 +125,6 @@
"type": "identifier", "type": "identifier",
"named": true "named": true
}, },
{
"type": "nil_literal",
"named": true
},
{ {
"type": "product_pattern", "type": "product_pattern",
"named": true "named": true
@@ -156,10 +148,6 @@
{ {
"type": "unit_pattern", "type": "unit_pattern",
"named": true "named": true
},
{
"type": "wrapper_pattern",
"named": true
} }
] ]
}, },
@@ -263,6 +251,10 @@
"type": "optional_type", "type": "optional_type",
"named": true "named": true
}, },
{
"type": "output_type",
"named": true
},
{ {
"type": "primitive_type", "type": "primitive_type",
"named": true "named": true
@@ -494,6 +486,10 @@
"type": "map_type", "type": "map_type",
"named": true "named": true
}, },
{
"type": "output_type",
"named": true
},
{ {
"type": "primitive_type", "type": "primitive_type",
"named": true "named": true
@@ -538,6 +534,10 @@
"type": "binary_expression", "type": "binary_expression",
"named": true "named": true
}, },
{
"type": "pipeline_expression",
"named": true
},
{ {
"type": "postfix_expression", "type": "postfix_expression",
"named": true "named": true
@@ -654,6 +654,10 @@
"type": "binary_expression", "type": "binary_expression",
"named": true "named": true
}, },
{
"type": "pipeline_expression",
"named": true
},
{ {
"type": "postfix_expression", "type": "postfix_expression",
"named": true "named": true
@@ -742,6 +746,10 @@
"type": "binary_expression", "type": "binary_expression",
"named": true "named": true
}, },
{
"type": "pipeline_expression",
"named": true
},
{ {
"type": "postfix_expression", "type": "postfix_expression",
"named": true "named": true
@@ -759,15 +767,71 @@
} }
}, },
{ {
"type": "bits_constructor", "type": "binder",
"named": true, "named": true,
"fields": {}, "fields": {},
"children": {
"multiple": true,
"required": true,
"types": [
{
"type": "binder_entry",
"named": true
}
]
}
},
{
"type": "binder_entry",
"named": true,
"fields": {
"constraint": {
"multiple": false,
"required": false,
"types": [
{
"type": "constraint_body",
"named": true
},
{
"type": "type_name",
"named": true
}
]
},
"type": {
"multiple": false,
"required": true,
"types": [
{
"type": "type_expression",
"named": true
}
]
}
}
},
{
"type": "bits_constructor",
"named": true,
"fields": {
"type": {
"multiple": false,
"required": false,
"types": [
{
"type": "type_name",
"named": true
}
]
}
},
"children": { "children": {
"multiple": true, "multiple": true,
"required": false, "required": false,
"types": [ "types": [
{ {
"type": "type_name", "type": "identifier",
"named": true "named": true
} }
] ]
@@ -795,13 +859,24 @@
{ {
"type": "bits_pattern", "type": "bits_pattern",
"named": true, "named": true,
"fields": {}, "fields": {
"type": {
"multiple": false,
"required": false,
"types": [
{
"type": "type_name",
"named": true
}
]
}
},
"children": { "children": {
"multiple": true, "multiple": true,
"required": false, "required": false,
"types": [ "types": [
{ {
"type": "type_name", "type": "identifier",
"named": true "named": true
} }
] ]
@@ -895,6 +970,67 @@
] ]
} }
}, },
{
"type": "constraint_body",
"named": true,
"fields": {},
"children": {
"multiple": true,
"required": false,
"types": [
{
"type": "constraint_entry",
"named": true
},
{
"type": "constraint_spread",
"named": true
}
]
}
},
{
"type": "constraint_entry",
"named": true,
"fields": {
"name": {
"multiple": false,
"required": true,
"types": [
{
"type": "identifier",
"named": true
}
]
},
"output": {
"multiple": false,
"required": true,
"types": [
{
"type": "type_expression",
"named": true
}
]
}
}
},
{
"type": "constraint_spread",
"named": true,
"fields": {
"constraint": {
"multiple": false,
"required": true,
"types": [
{
"type": "type_name",
"named": true
}
]
}
}
},
{ {
"type": "continuation_pipeline_stage", "type": "continuation_pipeline_stage",
"named": true, "named": true,
@@ -944,6 +1080,16 @@
"type": "declaration", "type": "declaration",
"named": true, "named": true,
"fields": { "fields": {
"binder": {
"multiple": false,
"required": false,
"types": [
{
"type": "binder",
"named": true
}
]
},
"name": { "name": {
"multiple": false, "multiple": false,
"required": false, "required": false,
@@ -1152,6 +1298,10 @@
"type": "optional_type", "type": "optional_type",
"named": true "named": true
}, },
{
"type": "output_type",
"named": true
},
{ {
"type": "primitive_type", "type": "primitive_type",
"named": true "named": true
@@ -1254,6 +1404,22 @@
} }
} }
}, },
{
"type": "import_filter",
"named": true,
"fields": {
"type": {
"multiple": false,
"required": true,
"types": [
{
"type": "type_expression",
"named": true
}
]
}
}
},
{ {
"type": "import_member", "type": "import_member",
"named": true, "named": true,
@@ -1278,6 +1444,16 @@
} }
] ]
} }
},
"children": {
"multiple": false,
"required": false,
"types": [
{
"type": "import_filter",
"named": true
}
]
} }
}, },
{ {
@@ -1353,6 +1529,10 @@
"type": "map_type", "type": "map_type",
"named": true "named": true
}, },
{
"type": "output_type",
"named": true
},
{ {
"type": "primitive_type", "type": "primitive_type",
"named": true "named": true
@@ -1486,6 +1666,10 @@
"type": "map_type", "type": "map_type",
"named": true "named": true
}, },
{
"type": "output_type",
"named": true
},
{ {
"type": "primitive_type", "type": "primitive_type",
"named": true "named": true
@@ -1518,6 +1702,42 @@
} }
} }
}, },
{
"type": "output_type",
"named": true,
"fields": {
"constructor": {
"multiple": false,
"required": true,
"types": [
{
"type": "type_name",
"named": true
}
]
},
"function": {
"multiple": false,
"required": true,
"types": [
{
"type": "type_name",
"named": true
}
]
},
"output": {
"multiple": false,
"required": true,
"types": [
{
"type": "type_expression",
"named": true
}
]
}
}
},
{ {
"type": "pattern", "type": "pattern",
"named": true, "named": true,
@@ -1564,6 +1784,14 @@
"type": "binary_expression", "type": "binary_expression",
"named": true "named": true
}, },
{
"type": "boolean_literal",
"named": true
},
{
"type": "character_literal",
"named": true
},
{ {
"type": "function_literal", "type": "function_literal",
"named": true "named": true
@@ -1580,9 +1808,21 @@
"type": "range_expression", "type": "range_expression",
"named": true "named": true
}, },
{
"type": "signed_numeric_literal",
"named": true
},
{
"type": "string_literal",
"named": true
},
{ {
"type": "unary_expression", "type": "unary_expression",
"named": true "named": true
},
{
"type": "unit_literal",
"named": true
} }
] ]
} }
@@ -1789,7 +2029,33 @@
"required": false, "required": false,
"types": [ "types": [
{ {
"type": "identifier", "type": "pub_entry",
"named": true
}
]
}
},
{
"type": "pub_entry",
"named": true,
"fields": {
"name": {
"multiple": false,
"required": true,
"types": [
{
"type": "identifier",
"named": true
}
]
}
},
"children": {
"multiple": false,
"required": false,
"types": [
{
"type": "import_filter",
"named": true "named": true
} }
] ]
@@ -1822,6 +2088,10 @@
"type": "binary_expression", "type": "binary_expression",
"named": true "named": true
}, },
{
"type": "pipeline_expression",
"named": true
},
{ {
"type": "postfix_expression", "type": "postfix_expression",
"named": true "named": true
@@ -1858,6 +2128,10 @@
"type": "binary_expression", "type": "binary_expression",
"named": true "named": true
}, },
{
"type": "pipeline_expression",
"named": true
},
{ {
"type": "postfix_expression", "type": "postfix_expression",
"named": true "named": true
@@ -1898,6 +2172,10 @@
"type": "map_type", "type": "map_type",
"named": true "named": true
}, },
{
"type": "output_type",
"named": true
},
{ {
"type": "primitive_type", "type": "primitive_type",
"named": true "named": true
@@ -1948,6 +2226,10 @@
"type": "map_type", "type": "map_type",
"named": true "named": true
}, },
{
"type": "output_type",
"named": true
},
{ {
"type": "primitive_type", "type": "primitive_type",
"named": true "named": true
@@ -2048,22 +2330,53 @@
{ {
"type": "sum_constructor", "type": "sum_constructor",
"named": true, "named": true,
"fields": {},
"children": {
"multiple": true,
"required": true,
"types": [
{
"type": "type_name",
"named": true
}
]
}
},
{
"type": "sum_pattern",
"named": true,
"fields": { "fields": {
"type": {
"multiple": false,
"required": false,
"types": [
{
"type": "type_name",
"named": true
}
]
},
"variant": {
"multiple": false,
"required": true,
"types": [
{
"type": "identifier",
"named": true
}
]
}
}
},
{
"type": "sum_pattern",
"named": true,
"fields": {
"payload": {
"multiple": false,
"required": false,
"types": [
{
"type": "pattern_atom",
"named": true
}
]
},
"type": {
"multiple": false,
"required": false,
"types": [
{
"type": "type_name",
"named": true
}
]
},
"variant": { "variant": {
"multiple": false, "multiple": false,
"required": true, "required": true,
@@ -2074,20 +2387,6 @@
} }
] ]
} }
},
"children": {
"multiple": true,
"required": false,
"types": [
{
"type": "pattern_atom",
"named": true
},
{
"type": "type_name",
"named": true
}
]
} }
}, },
{ {
@@ -2224,6 +2523,10 @@
"multiple": false, "multiple": false,
"required": true, "required": true,
"types": [ "types": [
{
"type": "constraint_body",
"named": true
},
{ {
"type": "type_expression", "type": "type_expression",
"named": true "named": true
@@ -2269,6 +2572,10 @@
"type": "optional_type", "type": "optional_type",
"named": true "named": true
}, },
{
"type": "output_type",
"named": true
},
{ {
"type": "primitive_type", "type": "primitive_type",
"named": true "named": true
@@ -2354,6 +2661,10 @@
"multiple": false, "multiple": false,
"required": true, "required": true,
"types": [ "types": [
{
"type": "pipeline_expression",
"named": true
},
{ {
"type": "postfix_expression", "type": "postfix_expression",
"named": true "named": true
@@ -2376,22 +2687,10 @@
"type": "-", "type": "-",
"named": false "named": false
}, },
{
"type": "err",
"named": false
},
{ {
"type": "not", "type": "not",
"named": false "named": false
}, },
{
"type": "ok",
"named": false
},
{
"type": "some",
"named": false
},
{ {
"type": "~", "type": "~",
"named": false "named": false
@@ -2439,6 +2738,10 @@
"multiple": true, "multiple": true,
"required": true, "required": true,
"types": [ "types": [
{
"type": "import_filter",
"named": true
},
{ {
"type": "import_member", "type": "import_member",
"named": true "named": true
@@ -2450,40 +2753,6 @@
] ]
} }
}, },
{
"type": "wrapper_pattern",
"named": true,
"fields": {
"value": {
"multiple": false,
"required": true,
"types": [
{
"type": "pattern_atom",
"named": true
}
]
},
"wrapper": {
"multiple": false,
"required": true,
"types": [
{
"type": "err",
"named": false
},
{
"type": "ok",
"named": false
},
{
"type": "some",
"named": false
}
]
}
}
},
{ {
"type": "!", "type": "!",
"named": false "named": false
@@ -2697,10 +2966,6 @@
"type": "else", "type": "else",
"named": false "named": false
}, },
{
"type": "err",
"named": false
},
{ {
"type": "f32", "type": "f32",
"named": false "named": false
@@ -2761,10 +3026,6 @@
"type": "match", "type": "match",
"named": false "named": false
}, },
{
"type": "nil_literal",
"named": true
},
{ {
"type": "not", "type": "not",
"named": false "named": false
@@ -2773,10 +3034,6 @@
"type": "nvr", "type": "nvr",
"named": false "named": false
}, },
{
"type": "ok",
"named": false
},
{ {
"type": "or", "type": "or",
"named": false "named": false
@@ -2789,10 +3046,6 @@
"type": "return", "type": "return",
"named": false "named": false
}, },
{
"type": "some",
"named": false
},
{ {
"type": "str", "type": "str",
"named": false "named": false
+45540 -37130
View File
File diff suppressed because it is too large. Load diff
+240 -16
View File
@@ -36,7 +36,7 @@ Point :: &{
y: i32 = 0 y: i32 = 0
} }
point := &Point.{ x: 1; y = 2 } point := &Point.{ x: 1; y = 2 }
maybe: []{i32?}: [nil, some 1] maybe: []{i32?}: [|.none, 1->|.some]
--- ---
@@ -73,11 +73,16 @@ maybe: []{i32?}: [nil, some 1]
(primitive_type)))) (primitive_type))))
(collection_literal (collection_literal
(collection_entry (collection_entry
(nil_literal)) (sum_constructor
(identifier)))
(collection_entry (collection_entry
(unary_expression (pipeline_expression
(signed_numeric_literal (signed_numeric_literal
(integer_literal))))))) (integer_literal))
(direct_pipeline_stage
(pipeline_callable
(sum_constructor
(identifier)))))))))
================== ==================
Control flow and patterns Control flow and patterns
@@ -302,17 +307,17 @@ unit :: @ () -> i32 { 42 }
Optional and result wrappers Optional and result wrappers
================== ==================
wrap :: @ value: i32 -> i32 ! str { ok value } wrap :: @ value: i32 -> i32 ! str { value->|.ok }
first :: @ items: []i32 -> i32? { first :: @ items: []i32 -> i32? {
match items { match items {
[] => nil [] => |.none
[head, ...rest] => some head [head, ...rest] => head->|.some
} }
} }
unwrap :: @ result: i32 ! str -> i32 { unwrap :: @ result: i32 ! str -> i32 {
match result { match result {
ok value => value |.ok value => value
err _ => 0 |.err _ => 0
} }
} }
@@ -329,8 +334,12 @@ unwrap :: @ result: i32 ! str -> i32 {
(primitive_type) (primitive_type)
(primitive_type)) (primitive_type))
(block (block
(unary_expression (pipeline_expression
(identifier))))) (identifier)
(direct_pipeline_stage
(pipeline_callable
(sum_constructor
(identifier))))))))
(declaration (declaration
(identifier) (identifier)
(function_literal (function_literal
@@ -346,7 +355,8 @@ unwrap :: @ result: i32 ! str -> i32 {
(match_arm (match_arm
(pattern (pattern
(array_pattern)) (array_pattern))
(nil_literal)) (sum_constructor
(identifier)))
(match_arm (match_arm
(pattern (pattern
(array_pattern (array_pattern
@@ -355,8 +365,12 @@ unwrap :: @ result: i32 ! str -> i32 {
(identifier))) (identifier)))
(array_pattern_entry (array_pattern_entry
(identifier)))) (identifier))))
(unary_expression (pipeline_expression
(identifier))))))) (identifier)
(direct_pipeline_stage
(pipeline_callable
(sum_constructor
(identifier))))))))))
(declaration (declaration
(identifier) (identifier)
(function_literal (function_literal
@@ -371,12 +385,14 @@ unwrap :: @ result: i32 ! str -> i32 {
(identifier) (identifier)
(match_arm (match_arm
(pattern (pattern
(wrapper_pattern (sum_pattern
(identifier)
(identifier))) (identifier)))
(identifier)) (identifier))
(match_arm (match_arm
(pattern (pattern
(wrapper_pattern)) (sum_pattern
(identifier)))
(signed_numeric_literal (signed_numeric_literal
(integer_literal)))))))) (integer_literal))))))))
@@ -433,3 +449,211 @@ mixed :: @ (left: i32, right) -> i32 { left + right }
(binary_expression (binary_expression
(identifier) (identifier)
(identifier)))))) (identifier))))))
==================
Postfix pipeline stages
==================
mixed :: -3->add_ten + 2 * 3->double - {1 + 1}->double
checked :: not c->is_digit
unwrapped :: input->fallible?->next
---
(source_file
(declaration
(identifier)
(binary_expression
(binary_expression
(unary_expression
(pipeline_expression
(signed_numeric_literal
(integer_literal))
(direct_pipeline_stage
(pipeline_callable
(identifier)))))
(binary_expression
(signed_numeric_literal
(integer_literal))
(pipeline_expression
(signed_numeric_literal
(integer_literal))
(direct_pipeline_stage
(pipeline_callable
(identifier))))))
(pipeline_expression
(block
(binary_expression
(signed_numeric_literal
(integer_literal))
(signed_numeric_literal
(integer_literal))))
(direct_pipeline_stage
(pipeline_callable
(identifier))))))
(declaration
(identifier)
(unary_expression
(pipeline_expression
(identifier)
(direct_pipeline_stage
(pipeline_callable
(identifier))))))
(declaration
(identifier)
(pipeline_expression
(identifier)
(direct_pipeline_stage
(pipeline_callable
(identifier)))
(direct_pipeline_stage
(pipeline_callable
(identifier))))))
==================
Continuations after same-line stages
==================
flat := value->first->second
->third
whole := a + b->f
->g
---
(source_file
(declaration
(identifier)
(pipeline_expression
(identifier)
(direct_pipeline_stage
(pipeline_callable
(identifier)))
(direct_pipeline_stage
(pipeline_callable
(identifier)))
(continuation_pipeline_stage
(pipeline_callable
(identifier)))))
(declaration
(identifier)
(pipeline_expression
(binary_expression
(identifier)
(pipeline_expression
(identifier)
(direct_pipeline_stage
(pipeline_callable
(identifier)))))
(continuation_pipeline_stage
(pipeline_callable
(identifier))))))
==================
Generic binders and constraints
==================
Show :: <>{
to_str: str
...Display
}
pick: <T: Show, (T, T): Ordered> (T, T) -> T: @ (first, second): (T, T) { first }
Card :: &{ label: Output<make_label: Show>: }
---
(source_file
(declaration
(identifier)
(type_definition
(constraint_body
(constraint_entry
(identifier)
(primitive_type))
(constraint_spread
(identifier)))))
(declaration
(identifier)
(binder
(binder_entry
(identifier)
(identifier))
(binder_entry
(tuple_type
(identifier)
(identifier))
(identifier)))
(function_type
(tuple_type
(identifier)
(identifier))
(identifier))
(function_literal
(typed_parameter
(tuple_pattern
(pattern
(identifier))
(pattern
(identifier)))
(tuple_type
(identifier)
(identifier)))
(block
(identifier))))
(declaration
(identifier)
(type_definition
(product_type
(product_field
(identifier)
(output_type
(identifier)
(identifier)
(identifier)))))))
==================
Import filters
==================
use math.{sqrt for f64, pi as p}
use .shapes.area for Circle
pub { area for Circle; sqrt }
---
(source_file
(use_declaration
(module_path
(identifier))
(import_member
(identifier)
(import_filter
(primitive_type)))
(import_member
(identifier)
(identifier)))
(use_declaration
(module_path
(qualified_name
(identifier)
(identifier)))
(import_filter
(identifier)))
(pub_declaration
(pub_entry
(identifier)
(import_filter
(identifier)))
(pub_entry
(identifier))))
==================
Reserved word
:error
==================
context := 1
---
-1
View File
@@ -1 +0,0 @@
value :: @ x: i32 -> i32 ! str { x! }
-1
View File
@@ -1 +0,0 @@
value :: @ x: str -> i32 ! str { !x }
-1
View File
@@ -1 +0,0 @@
ok :: @ value { value }
-1
View File
@@ -1 +0,0 @@
some := 1
-1
View File
@@ -1 +0,0 @@
value: i32?: 1->|.some
-1
View File
@@ -1 +0,0 @@
value :: @ x: i32? { match x { |.some y => y; nil => 0 } }
-1
View File
@@ -1 +0,0 @@
value :: 1 + 2->to_str
+2 -2
View File
@@ -6,8 +6,8 @@ more_codes := [...codes, 'failed' => 500]
empty_numbers: []i32: [] empty_numbers: []i32: []
empty_codes: [str]i32: [] empty_codes: [str]i32: []
optional_numbers: []i32?: nil optional_numbers: []i32?: |.none
optional_elements: []{i32?}: [nil, some 1] optional_elements: []{i32?}: [|.none, 1->|.some]
Options :: &{ Options :: &{
name: str: 'default' name: str: 'default'
+2 -2
View File
@@ -11,10 +11,10 @@ choose_block: (bit, i32, i32) -> i32: @ (condition: bit, when_true: i32, when_fa
first_positive: []i32 -> i32?: @ values: []i32 { first_positive: []i32 -> i32?: @ values: []i32 {
for value in values { for value in values {
if value > 0 { if value > 0 {
return some value return value->|.some
} }
} }
nil |.none
} }
labelled_search: []i32 -> i32: @ values: []i32 { loop `outer { labelled_search: []i32 -> i32: @ values: []i32 { loop `outer {
+5
View File
@@ -0,0 +1,5 @@
add_ten: i32 -> i32: @ value: i32 { value + 10 }
double: i32 -> i32: @ value: i32 { value * 2 }
# Stages bind tighter than prefix and infix operators: -{3->add_ten} + 2 * {3->double}.
mixed: i32: -3->add_ten + 2 * 3->double - {1 + 1}->double
+2 -2
View File
@@ -15,9 +15,9 @@ commented := 'value' # an inline comment preserves continuation
computed := 1 + 2 computed := 1 + 2
->to_str ->to_str
fallible: i32 -> i32 ! Error: @ value: i32 { ok value } fallible: i32 -> i32 ! Error: @ value: i32 { value->|.ok }
propagate: i32 -> i32 ! Error: @ value: i32 { propagate: i32 -> i32 ! Error: @ value: i32 {
result := {value->fallible}? result := {value->fallible}?
ok result result->|.ok
} }
-19
View File
@@ -1,19 +0,0 @@
use math.{pi, sqrt}
use math as m
use random.{Pcg32, pcg32, draw_u32}
use time.{Duration, TimeError, duration_neg}
use time as t
circle: f64: pi
root: f64: 4.0->sqrt
# Host overloads are selected by the operand, so the literal is typed first.
nine: f32: 9.0
qualified_root: f32: nine->m.sqrt
qualified_pi: f32: m.pi
seed: u64: 42
generator: Pcg32: seed->pcg32
draw: u32: generator->draw_u32
negate: Duration => Duration ! TimeError: duration_neg
qualified_negate: t.Duration => t.Duration ! t.TimeError: t.duration_neg
-24
View File
@@ -1,24 +0,0 @@
present: i32?: some 1
absent: i32?: nil
success: i32 ! str: ok 1
failure: i32 ! str: err 'failed'
piped: i32?: some 4->double
grouped: i32?: some {4->double}
double :: @ value { value * 2 }
describe :: @ (label: str, count) -> str { label }
unwrap_or: i32? -> i32: @ value {
match value {
some inner => inner
nil => 0
}
}
handle: i32 ! str -> i32: @ result {
match result {
ok value if value > 0 => value
ok _ => 0
err message => message->length
}
}