Track the LScript changes since the grammar was first written: - some, ok and err are reserved prefix operators and wrapper patterns; the old |.some/|.ok/|.err forms and postfix/prefix ! are rejected - function literal inputs may omit their type annotation, and tuple components may omit theirs; an empty typed tuple pattern is now just the unit pattern - migrate fixtures, corpus, queries, CST contract and Rust tests Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
108 lines
4.7 KiB
Markdown
108 lines
4.7 KiB
Markdown
# Concrete syntax tree contract
|
|
|
|
This document describes the named nodes and fields intended for consumers such
|
|
as editor integrations, formatters, and structural search. Rules prefixed with `_`
|
|
and rules listed as Tree-sitter supertypes are implementation details and do not
|
|
appear as named wrapper nodes in ordinary trees.
|
|
|
|
## Source and declarations
|
|
|
|
`source_file` contains declarations, imports, publication declarations, and
|
|
standalone expressions directly. The choice-only `module_item`, `expression`,
|
|
and `value_expression` rules are hidden supertypes.
|
|
|
|
`declaration` uses these fields:
|
|
|
|
- `name` for a named declaration;
|
|
- `pattern` for a destructuring declaration;
|
|
- `type` for an explicit type;
|
|
- `value` for an initializer.
|
|
|
|
The punctuation distinguishes immutable, mutable, typed, and deferred forms.
|
|
Consumers should inspect that punctuation rather than infer the form from the
|
|
set of fields alone.
|
|
|
|
## Expressions
|
|
|
|
Only expressions that actually contain an operator produce an operator node:
|
|
|
|
| Node | Fields |
|
|
| --- | --- |
|
|
| `assignment_expression` | `left`, `operator`, `right` |
|
|
| `range_expression` | `left`, `operator`, `right` |
|
|
| `binary_expression` | `left`, `operator`, `right` |
|
|
| `unary_expression` | `operator`, `operand` |
|
|
| `postfix_expression` | `value`, repeated `operator`; member and index operators additionally expose `member` or `index` |
|
|
| `pipeline_expression` | `input`, followed by stage nodes |
|
|
|
|
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
|
|
wrapper for every precedence level.
|
|
|
|
Literal leaves are named. Boolean and nil values use `boolean_literal` and
|
|
`nil_literal`; numeric, string, character, and unit literals retain their
|
|
specific nodes. `default_expression` is also named.
|
|
|
|
`some`, `ok`, and `err` construct optional and result values. They are prefix
|
|
operators, so `some value` and `ok {x->f}` appear as `unary_expression` nodes
|
|
whose `operator` is the keyword. They bind like `-` and `~`, so `ok x->f`
|
|
wraps `x` and then pipes the result into `f`, while `ok {x->f}` wraps the
|
|
pipeline. The older `|.some`/`|.ok`/`|.err`
|
|
constructors, postfix `value!`, and prefix `!error` are no longer accepted.
|
|
|
|
`function_literal` takes one input and owns the `body` field. A pattern with
|
|
one annotation for the whole input, such as `@ value: i32` or
|
|
`@ (left, right): (i32, i32)`, appears as `parameter: (typed_parameter)`.
|
|
The annotation is optional: an input whose type comes from the expected
|
|
function type, such as `@ value` or `@ (left, right)`, is also a
|
|
`typed_parameter` without a `type` field. Component-annotated tuple
|
|
destructuring, such as `@ (left: i32, right)`, appears as
|
|
`parameter: (typed_tuple_pattern)`. A `typed_tuple_pattern` contains at least
|
|
two `typed_tuple_component` nodes, each with an optional `type`. These bind
|
|
components of one tuple input, not separate function arguments. `()` matches
|
|
unit and is a `typed_parameter` over a `unit_pattern`; singleton tuple patterns
|
|
are invalid. Bindings expose `pattern` and, when annotated, `type`; a header
|
|
may also expose `effect` (`->` or `=>`) and `result`.
|
|
|
|
## Patterns
|
|
|
|
`wrapper_pattern` matches `some`, `ok`, or `err` applied to one pattern atom.
|
|
It exposes `wrapper` (the keyword) and `value` (the inner pattern). `nil` is
|
|
still a `nil_literal` pattern. Sum patterns retain `sum_pattern` with a
|
|
`variant` field.
|
|
|
|
## Types
|
|
|
|
Meaningful type constructors are named directly:
|
|
|
|
| Node | Fields |
|
|
| --- | --- |
|
|
| `function_type` | `input`, `effect`, `result` |
|
|
| `result_type` | `value`, `error` |
|
|
| `optional_type` | `value` |
|
|
| `array_type` | `element` |
|
|
| `map_type` | `key`, `value` |
|
|
| `grouped_type` | `value` |
|
|
| `type_application` | `constructor` followed by its type arguments |
|
|
|
|
Primitive, unit, tuple, product, sum, and bits types retain their corresponding
|
|
named nodes. Internal primary/container/result categories are hidden.
|
|
|
|
## Trivia and physical lines
|
|
|
|
Comments are named extra nodes. Spaces, tabs, and structural newlines remain in
|
|
the source gaps between nodes, so a formatter must retain the original source
|
|
alongside the tree. Leading-operator and pipeline continuations use composite
|
|
tokens containing their newline; this prevents a scope separator from stealing
|
|
a valid continuation and preserves the language's physical-line rules.
|
|
|
|
## Compatibility
|
|
|
|
Adding nodes or fields is compatible. Removing or renaming a named node or
|
|
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
|
|
snapshot updates and a version change, and must be called out in release notes.
|
|
|
|
The Tree-sitter tree is designed for source tooling and error recovery; it does
|
|
not perform semantic validation.
|