151 lines
7.0 KiB
Markdown
151 lines
7.0 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;
|
|
- `binder` for the type parameters of a generic function 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.
|
|
|
|
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
|
|
|
|
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.
|
|
|
|
Pipeline stages are postfix. A same-line stage binds to the operand
|
|
immediately to its left, tighter than every prefix and infix operator, so
|
|
`-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`.
|
|
|
|
Literal leaves are named. Boolean values use `boolean_literal`; numeric,
|
|
string, character, and unit literals retain their specific nodes.
|
|
`default_expression` is also named.
|
|
|
|
Optionals and results are ordinary sums. `|.none` is a `sum_constructor`, and
|
|
`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
|
|
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
|
|
|
|
`sum_pattern` matches a variant, including `|.none`, `|.some pattern`,
|
|
`|.ok pattern`, and `|.err pattern`. It exposes an optional `type`, the
|
|
`variant`, and an optional `payload` pattern atom. `bits_pattern` exposes the
|
|
optional `type` as well.
|
|
|
|
## 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 |
|
|
| `output_type` | `constructor`, `function`, `output` |
|
|
|
|
`output_type` is the `Output<f: U>` spelling: a type name applied to a
|
|
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
|
|
|
|
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.
|
|
|
|
The future-reserved word `context` is a `reserved_word` token that no
|
|
production accepts, so it is rejected wherever an identifier is expected.
|
|
|
|
## 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.
|
|
|
|
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
|
|
not perform semantic validation.
|