# 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` 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.