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
current_declaration_context: DeclarationContext is
51
for scope in Collections.LIST_REVERSE_ITERATOR[Scope](_stack) do
52
let underlying = scope.underlying_scope;
53
54
if isa DeclarationContext(underlying) then
55
return underlying;
56
fi
57
od
58
59
assert false else "no current declaration context";
60
si
61
62
current_instance_context: Symbols.Classy? is
63
for scope in Collections.LIST_REVERSE_ITERATOR[Scope](_stack) do
64
let underlying = scope.underlying_scope;
65
66
if underlying.is_instance_context then
67
return cast Symbols.Classy?(underlying)!;
68
fi
69
od
70
return null;
71
si
72
73
current_union_context: Symbols.UNION? is
74
for scope in Collections.LIST_REVERSE_ITERATOR[Scope](_stack) do
75
let underlying = scope.underlying_scope;
76
77
if isa Symbols.UNION(underlying) then
78
return underlying;
79
fi
80
od
81
return null;
82
si
83
84
// The class whose code is making the current access, used as the
85
// accessor for the underscore-policy access check. Ordinarily the
86
// current function's owner; for a closure declared in a partial/impl
87
// block that owner is the block's injection scope rather than the
88
// target type, so fall back to the innermost enclosing instance type
89
// on the scope stack (which resolves to the target through the
90
// injection scope). Null in a global function.
91
current_accessor: Symbols.Classy? =>
92
cast Symbols.Classy?(current_function?.owner) ?? current_instance_context;
93
94
current_function: Symbols.Function? is
95
for scope in Collections.LIST_REVERSE_ITERATOR[Scope](_stack) do
96
if isa Symbols.Function(scope) then
97
return scope;
98
fi
99
od
100
return null;
101
si
102
103
current_closure_context: ClosureContext is
104
for scope in Collections.LIST_REVERSE_ITERATOR[Scope](_stack) do
105
if isa ClosureContext(scope) then
106
return scope;
107
fi
108
od
109
110
assert false else "no current closure context";
111
si
112
113
current_capture_context: Symbols.Symbol? is
114
for scope in Collections.LIST_REVERSE_ITERATOR[Scope](_stack) do
115
if scope.is_capture_context then
116
return cast Symbols.Symbol?(scope)!;
117
fi
118
od
119
return null;
120
si
121
122
current_closure: Symbols.Closure? is
123
for scope in Collections.LIST_REVERSE_ITERATOR[Scope](_stack) do
124
if let result: Symbols.Closure = scope then
125
return result;
126
fi
127
od
128
return null;
129
si
130
131
// The nearest enclosing `name` that could be called: a function
132
// group, or a value whose type is a function type. Walks the
133
// scope stack outward from the innermost scope, skipping any
134
// binding that is neither - so a local, field or property named
135
// `name` does not hide a callable `name` further out.
136
//
137
// Each scope's own find_enclosing does the resolution, so a
138
// callable reached through a `use` import is found the same way
139
// an ordinary reference finds it. Scopes whose find_enclosing
140
// already searches outward can return a binding declared further
141
// out than the scope being asked, which is why a non-callable
142
// result continues the walk rather than ending it.
143
find_enclosing_callable(name: string) -> Symbols.Symbol? is
144
for scope in Collections.LIST_REVERSE_ITERATOR[Scope](_stack) do
145
let candidate = scope.find_enclosing(name);
146
147
if candidate? /\ is_callable_symbol(candidate) then
148
return candidate;
149
fi
150
od
151
152
return null;
153
si
154
155
// Callable for the purposes of the search above: an overload
156
// group, or anything holding a function value. A closure is a
157
// Function, so it satisfies the first test.
158
is_callable_symbol(symbol: Symbols.Symbol) -> bool static is
159
if symbol.is_function_group \/ symbol.is_function then
160
return true;
161
fi
162
163
let type = cast Types.Typed?(symbol)?.type;
164
165
return type? /\ (type.is_function \/ type.is_action);
166
si
167
168
current_function_group: Symbols.FUNCTION_GROUP? is
169
for scope in Collections.LIST_REVERSE_ITERATOR[Scope](_stack) do
170
if isa Symbols.FUNCTION_GROUP(scope) then
171
return scope;
172
fi
173
od
174
return null;
175
si
176
177
current_property: Symbols.Property? is
178
for scope in Collections.LIST_REVERSE_ITERATOR[Scope](_stack) do
179
if isa Symbols.Property(scope) then
180
return scope;
181
fi
182
od
183
return null;
184
si
185
186
init(logger: Logger) is
187
_logger = logger;
188
189
clear();
190
si
191
192
clear() is
193
_stack = Collections.LIST[Scope](50);
194
195
enter_scope(Symbols.NAMESPACE(LOCATION.internal, "", null, "", true));
196
si
197
198
scope_for(node: Node) -> Scope? =>
199
let carrier = cast ScopeCarrier?(node) in
200
if carrier? then carrier.scope else null fi;
201
202
associate_node_with_scope(node: ScopeCarrier, scope: Scope) is
203
node.scope = scope;
204
si
205
206
mark_scope_stack() -> int => _stack.count;
207
release_scope_stack(mark: int) is
208
assert mark <= _stack.count;
209
210
while _stack.count > mark do
211
_stack.remove_at(_stack.count - 1);
212
od
213
si
214
215
enter_scope(node: ScopeCarrier) is
216
let scope = node.scope;
217
218
if !scope? then
219
_logger.poison(node.location, "no scope found for {node.get_type()}");
220
221
return;
222
fi
223
224
enter_scope(scope);
225
si
226
227
enter_scope(scope: Scope) is
228
_stack.add(scope);
229
si
230
231
leave_scope(node: ScopeCarrier) is
232
let scope = node.scope;
233
234
if !scope? then
235
_logger.poison(node.location, "no scope found for {node.get_type()}");
236
return;
237
fi
238
239
leave_scope(scope);
240
si
241
242
leave_scope(scope: Scope) is
243
assert current_scope == scope else "scope stack corrupt: stack top: {current_scope} leaving scope: {scope}";
244
245
_stack.remove_at(_stack.count - 1);
246
si
247
248
leave_scope() is
249
let scope = _stack[_stack.count - 1];
250
251
_stack.remove_at(_stack.count - 1);
252
si
253
254
to_string() -> string is
255
let result = System.Text.StringBuilder();
256
257
result.append("symbol table:\n");
258
259
for scope in Collections.LIST_REVERSE_ITERATOR[Semantic.Scope](_stack) do
260
result
261
.append(scope)
262
.append("\n");
263
od
264
265
result.append("\n");
266
267
return result.to_string();
268
si
269
si
270
si