Appearance
| 1 | namespace Syntax.Trees is | |
| 2 | use IO.Std | |
| 3 | ||
| 4 | use System | |
| 5 | use Source | |
| 6 | ||
| 7 | class Node abstract is | |
| 8 | _next_node_id: int static | |
| 9 | ||
| 10 | // Process-unique, monotonically increasing, never reused. | |
| 11 | // Node-keyed state stores key on this instead of object | |
| 12 | // identity. Never-reused is load-bearing: a reused id could | |
| 13 | // serve one node's stale store entry to a different node, so | |
| 14 | // the counter is global and never reset. | |
| 15 | node_id: int public | |
| 16 | ||
| 17 | location: LOCATION public | |
| 18 | ||
| 19 | is_poisoned: bool | |
| 20 | ||
| 21 | // The span a debug sequence point should carry for this node. | |
| 22 | // A compound statement narrows it to its own header, so the | |
| 23 | // sequence point stops short of the body: a span reaching the | |
| 24 | // closing keyword makes every comment and blank line inside | |
| 25 | // the body look like executable code to a coverage tool, and | |
| 26 | // leaves a debugger highlighting the whole construct while it | |
| 27 | // evaluates the condition. | |
| 28 | debug_location: LOCATION => location | |
| 29 | ||
| 30 | init(location: LOCATION) is | |
| 31 | _next_node_id = _next_node_id + 1 | |
| 32 | node_id = _next_node_id | |
| 33 | ||
| 34 | self.location = location | |
| 35 | si | |
| 36 | ||
| 37 | // A tree node is the thing it is rather than what it holds: | |
| 38 | // identical spans are written all over a program, nodes are | |
| 39 | // held in identity-keyed sets and maps, and a node reaches | |
| 40 | // itself through the links a tree keeps. So the tree declares | |
| 41 | // its own equality rather than taking a memberwise one, and | |
| 42 | // every node kind inherits it. | |
| 43 | =~(other: Node) -> bool pure => self == other | |
| 44 | ||
| 45 | get_hash_code() -> int => node_id | |
| 46 | ||
| 47 | clone() -> Node is | |
| 48 | let result = cast Node?(memberwise_clone())! | |
| 49 | ||
| 50 | // A memberwise clone copies node_id; the clone must not | |
| 51 | // answer for the original in node-keyed stores. | |
| 52 | _next_node_id = _next_node_id + 1 | |
| 53 | result.node_id = _next_node_id | |
| 54 | ||
| 55 | // A memberwise clone also copies the original's scope; the | |
| 56 | // clone starts unassociated until a declaration pass walks it. | |
| 57 | if let carrier: ScopeCarrier = result then | |
| 58 | carrier.scope = null | |
| 59 | fi | |
| 60 | ||
| 61 | return result | |
| 62 | si | |
| 63 | ||
| 64 | accept(visitor: Visitor) is | |
| 65 | visitor.visit(self) | |
| 66 | si | |
| 67 | ||
| 68 | walk(visitor: Visitor) is | |
| 69 | accept(visitor) | |
| 70 | si | |
| 71 | ||
| 72 | // Reset per-build state cached on this node by previous compilation | |
| 73 | // passes. Non-recursive — clears only this node's own state, never | |
| 74 | // children. CLEAR_STATE_VISITOR is responsible for traversal and | |
| 75 | // calling clear() on each node it visits. | |
| 76 | // | |
| 77 | // Default is a no-op: most node types have no per-build mutable | |
| 78 | // state. Subclasses with state (Expression, TypeExpression, ...) | |
| 79 | // override. | |
| 80 | // | |
| 81 | // Run between compile-expressions invocations on the same AST | |
| 82 | // (analyser COMPILE-after-EDIT, multi-file rebuilds where retained | |
| 83 | // ASTs already have prior compile-expressions output) — see | |
| 84 | // project_analysis_mode_workflow. | |
| 85 | clear() is | |
| 86 | si | |
| 87 | ||
| 88 | // Reset only what a compile-expressions walk produced on this | |
| 89 | // node, keeping what its context pushed down into it - expected | |
| 90 | // types and value-demand flags - so the next walk of the same | |
| 91 | // body starts from a first walk's inputs and none of the | |
| 92 | // previous walk's outputs. Defaults to clear() for nodes with | |
| 93 | // no such split. | |
| 94 | clear_outputs() is | |
| 95 | clear() | |
| 96 | si | |
| 97 | ||
| 98 | poison(should_poison: bool) is | |
| 99 | if should_poison then | |
| 100 | is_poisoned = true | |
| 101 | fi | |
| 102 | si | |
| 103 | ||
| 104 | poison() is | |
| 105 | is_poisoned = true | |
| 106 | si | |
| 107 | ||
| 108 | to_string() -> string is | |
| 109 | try | |
| 110 | let printer = Process.Printer.GHUL() | |
| 111 | accept(printer) | |
| 112 | return printer.result | |
| 113 | catch ex: Exception | |
| 114 | return "unprintable {get_type()}: {ex}" | |
| 115 | yrt | |
| 116 | si | |
| 117 | si | |
| 118 | si |