[Lang]NodeOne node of a parsed program — every node, one class: kind answers WHICH (a Symbol: #send, #classDefinition, #stringLiteral, …), children the structural children in source order, and at:#field the kind-specific parts (#selector/#receiver/#arguments on a #send; #name/#parent/#body on a #classDefinition; #value on literals; nil for a field the kind doesn't have). Source fidelity is total: file, span (#( start end line column )), and text (the exact source slice) — which is what makes the span-based [Lang]Rewrite (qnlib/lang/ast.qn) safe. Trees are immutable views; transform by rewriting source, then parse again.
use std:lang/ast var ast = [Lang]Parser.parse:'x = 1 + 2' (ast.allNodes.select:{ |n| n.kind == #integerLiteral }) .collect:{ |n| n.at:#value } "* -> #(1 2)
Every node of this subtree, pre-order, as a List — the whole Iterate vocabulary rides on the answer (select:, detect:, groupBy:, …).
The kind-specific field named by a Symbol: #selector / #receiver / #arguments on a #send; #name / #parent / #body on a #classDefinition; #value on literals; #operator / #left / #right on a #binaryOp; #parameters / #locals / #name on a #block; #path / #glob on a #use — nil for anything the kind doesn't have (map semantics: absence is an answer).
The structural children, in source order (a #block's statements, a #send's receiver then arguments, …). Block parameters are fields, not children — children are code.
The file (or parse:named: label) this node came from; nil on the rare node the parser synthesized without a location.
The node's kind, as a Symbol — #program, #send, #classDefinition, ….
A short description: the kind and where it came from.
#( start end line column ): byte offsets (end exclusive — what [Lang]Rewrite edits by), then the 1-indexed line and 0-indexed column of the start. nil when the node has no recorded location.
The node's exact source slice — the bytes span names; nil without a span.
Pre-order walk: the block sees this node, then each child's subtree, in source order. Answers self.