Skip to content
← Back

src/semantic/symbol_table.ghul

1
namespace Semantic is
2
use IO.Std
3
4
use Logging
5
use Source
6
7
use Syntax.Trees
8
9
// the symbol table helps keeps track of what scope the compiler is currently working in. it does not
10
// manage searching for symbols within or across scopes: that's handled by the scopes themselves
11
12
class SYMBOL_TABLE is
13
_logger: Logger
14
_stack: Collections.LIST[Scope]
15
16
stack: Collections.List[Scope] => _stack
17
18
// The scope at the top of the stack. Non-optional: `clear()`
19
// seeds the stack with the root namespace, so during
20
// compilation there is always at least one scope. Bookkeeping
21
// discipline (matched enter/leave) keeps it that way.
22
current_scope: Scope => _stack[_stack.count-1]
23
24
// The innermost enclosing namespace scope. A symbol's own
25
// declaration renders relative to this - where a reader references
26
// it from - rather than the type or block it is declared inside,
27
// so a member keeps its type qualifier unless it is imported.
28
current_namespace_scope: Scope is
29
for scope in Collections.LIST_REVERSE_ITERATOR[Scope](_stack) do
30
if scope.is_namespace then
31
return scope
32
fi
33
od
34
35
return current_scope
36
si
37
38
global_scope: Scope => _stack[0]
39
40
current_namespace_context: NamespaceContext is
41
for scope in Collections.LIST_REVERSE_ITERATOR[Scope](_stack) do
42
if isa NamespaceContext(scope) then
43
return scope
44
fi
45
od
46
47
assert false else "no current namespace"
48
si
49
50
// The nearest enclosing namespace symbol, however deep the
51
// current scope sits inside functions and blocks — the
52
// declaration target for a top-level `let` promoted out of the
53
// synthesised entry.
54
current_namespace_symbol: Symbols.NAMESPACE? is
55
for scope in Collections.LIST_REVERSE_ITERATOR[Scope](_stack) do
56
if isa Symbols.NAMESPACE(scope) then
57
return scope
58
elif isa NAMESPACE_SCOPE(scope) then
59
return scope.containing_namespace
60
fi
61
od
62
63
return null
64
si
65
66
// The innermost enclosing namespace block's own scope: where a
67
// name looked up from inside the block is remembered.
68
current_namespace_block: NAMESPACE_SCOPE? is
69
for scope in Collections.LIST_REVERSE_ITERATOR[Scope](_stack) do
70
if isa NAMESPACE_SCOPE(scope) then
71
return scope
72
fi
73
od
74
75
return null
76
si
77
78
current_declaration_context: DeclarationContext is
79
for scope in Collections.LIST_REVERSE_ITERATOR[Scope](_stack) do
80
let underlying = scope.underlying_scope
81
82
if isa DeclarationContext(underlying) then
83
return underlying
84
fi
85
od
86
87
assert false else "no current declaration context"
88
si
89
90
current_instance_context: Symbols.Classy? is
91
for scope in Collections.LIST_REVERSE_ITERATOR[Scope](_stack) do
92
let underlying = scope.underlying_scope
93
94
if underlying.is_instance_context then
95
return cast Symbols.Classy?(underlying)!
96
fi
97
od
98
return null
99
si
100
101
current_union_context: Symbols.UNION? is
102
for scope in Collections.LIST_REVERSE_ITERATOR[Scope](_stack) do
103
let underlying = scope.underlying_scope
104
105
if isa Symbols.UNION(underlying) then
106
return underlying
107
fi
108
od
109
return null
110
si
111
112
// The class whose code is making the current access, used as the
113
// accessor for the underscore-policy access check. Ordinarily the
114
// current function's owner; for a closure declared in a partial/impl
115
// block that owner is the block's injection scope rather than the
116
// target type, so fall back to the innermost enclosing instance type
117
// on the scope stack (which resolves to the target through the
118
// injection scope). A closure that captures is re-owned by its
119
// capture frame when it is loaded, so that IL emission names the
120
// method inside the frame - the frame is a synthesized carrier
121
// rather than the accessing type, and falls back the same way.
122
// Null in a global function.
123
current_accessor: Symbols.Classy? =>
124
let owner = cast Symbols.Classy?(current_function?.owner) in
125
if owner? /\ !isa Symbols.FRAME(owner) then
126
owner
127
else
128
current_instance_context
129
fi
130
131
current_function: Symbols.Function? is
132
for scope in Collections.LIST_REVERSE_ITERATOR[Scope](_stack) do
133
if isa Symbols.Function(scope) then
134
return scope
135
fi
136
od
137
return null
138
si
139
140
// Whether the walk is anywhere inside the entry synthesised
141
// from a file's top-level statements - directly among them, or
142
// in a function literal one of them writes. That is where the
143
// walk maintains a top-level `let`'s defined flag, clearing it
144
// before each walk of the `let` and setting it on the `let`'s
145
// left, so it is also where asking whether one is defined yet
146
// answers about the reading position. A named function's body
147
// is walked with the entry nowhere on the stack, and the flag
148
// says nothing about position there.
149
is_within_top_level_entry: bool is
150
for scope in Collections.LIST_REVERSE_ITERATOR[Scope](_stack) do
151
if let function: Symbols.Function = scope /\ function.is_top_level_entry then
152
return true
153
fi
154
od
155
return false
156
si
157
158
current_closure_context: ClosureContext is
159
for scope in Collections.LIST_REVERSE_ITERATOR[Scope](_stack) do
160
if isa ClosureContext(scope) then
161
return scope
162
fi
163
od
164
165
assert false else "no current closure context"
166
si
167
168
current_capture_context: Symbols.Symbol? is
169
for scope in Collections.LIST_REVERSE_ITERATOR[Scope](_stack) do
170
if scope.is_capture_context then
171
return cast Symbols.Symbol?(scope)
172
fi
173
od
174
return null
175
si
176
177
current_closure: Symbols.Closure? is
178
for scope in Collections.LIST_REVERSE_ITERATOR[Scope](_stack) do
179
if let result: Symbols.Closure = scope then
180
return result
181
fi
182
od
183
return null
184
si
185
186
// The nearest enclosing `name` that could be called: a function
187
// group, or a value whose type is a function type. Walks the
188
// scope stack outward from the innermost scope, skipping any
189
// binding that is neither - so a local, field or property named
190
// `name` does not hide a callable `name` further out.
191
//
192
// Each scope's own find_enclosing does the resolution, so a
193
// callable reached through a `use` import is found the same way
194
// an ordinary reference finds it. Scopes whose find_enclosing
195
// already searches outward can return a binding declared further
196
// out than the scope being asked, which is why a non-callable
197
// result continues the walk rather than ending it.
198
find_enclosing_callable(name: string) -> Symbols.Symbol? is
199
for scope in Collections.LIST_REVERSE_ITERATOR[Scope](_stack) do
200
let candidate = scope.find_enclosing(name)
201
202
if candidate? /\ is_callable_symbol(candidate) then
203
return candidate
204
fi
205
od
206
207
return null
208
si
209
210
// Callable for the purposes of the search above: an overload
211
// group, or anything holding a function value. A closure is a
212
// Function, so it satisfies the first test.
213
is_callable_symbol(symbol: Symbols.Symbol) -> bool static is
214
if symbol.is_function_group \/ symbol.is_function then
215
return true
216
fi
217
218
let type = cast Types.Typed?(symbol)?.type
219
220
return type? /\ (type.is_function \/ type.is_action)
221
si
222
223
current_function_group: Symbols.FUNCTION_GROUP? is
224
for scope in Collections.LIST_REVERSE_ITERATOR[Scope](_stack) do
225
if isa Symbols.FUNCTION_GROUP(scope) then
226
return scope
227
fi
228
od
229
return null
230
si
231
232
current_property: Symbols.Property? is
233
for scope in Collections.LIST_REVERSE_ITERATOR[Scope](_stack) do
234
if isa Symbols.Property(scope) then
235
return scope
236
fi
237
od
238
return null
239
si
240
241
init(logger: Logger) is
242
_logger = logger
243
244
clear()
245
si
246
247
clear() is
248
_stack = Collections.LIST[Scope](50)
249
250
enter_scope(Symbols.NAMESPACE(LOCATION.internal, "", null, "", true))
251
si
252
253
scope_for(node: Node) -> Scope? =>
254
let carrier = cast ScopeCarrier?(node) in
255
if carrier? then carrier.scope else null fi
256
257
associate_node_with_scope(node: ScopeCarrier, scope: Scope) is
258
node.scope = scope
259
si
260
261
mark_scope_stack() -> int => _stack.count
262
release_scope_stack(mark: int) is
263
assert mark <= _stack.count
264
265
while _stack.count > mark do
266
_stack.remove_at(_stack.count - 1)
267
od
268
si
269
270
enter_scope(node: ScopeCarrier) is
271
let scope = node.scope
272
273
if !scope? then
274
_logger.poison(node.location, "no scope found for {node.get_type()}")
275
276
return
277
fi
278
279
enter_scope(scope)
280
si
281
282
enter_scope(scope: Scope) is
283
_stack.add(scope)
284
si
285
286
leave_scope(node: ScopeCarrier) is
287
let scope = node.scope
288
289
if !scope? then
290
_logger.poison(node.location, "no scope found for {node.get_type()}")
291
return
292
fi
293
294
leave_scope(scope)
295
si
296
297
leave_scope(scope: Scope) is
298
assert current_scope == scope else "scope stack corrupt: stack top: {current_scope} leaving scope: {scope}"
299
300
_stack.remove_at(_stack.count - 1)
301
si
302
303
leave_scope() is
304
let scope = _stack[_stack.count - 1]
305
306
_stack.remove_at(_stack.count - 1)
307
si
308
309
to_string() -> string is
310
let result = System.Text.StringBuilder()
311
312
result.append("symbol table:\n")
313
314
for scope in Collections.LIST_REVERSE_ITERATOR[Semantic.Scope](_stack) do
315
result
316
.append(scope)
317
.append("\n")
318
od
319
320
result.append("\n")
321
322
return result.to_string()
323
si
324
si
325
si