Skip to content
← Back

src/syntax/trees/node.ghul

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