-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathscope.c
293 lines (254 loc) · 10.6 KB
/
scope.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
#include "scope.h"
/* Create a new lexical scope with parent scope 'parent'. */
AScope *scope_new(AScope *parent) {
AScope *newscope = malloc(sizeof(AScope));
newscope->parent = parent;
newscope->content = NULL;
if (parent == NULL) {
/* If parent is null, we're creating the scope for
* the library. So the library scope is this one! */
newscope->libscope = newscope;
} else {
/* Otherwise point at the library scope so we can
* use it to create new scopes for imports and whatnot. */
newscope->libscope = parent->libscope;
}
return newscope;
}
/* Allocate and initialize a new scope entry mapping 'sym' to 'func'. */
static
AScopeEntry *scope_entry_new(ASymbol *sym, AFunc *func) {
AScopeEntry *newentry = malloc(sizeof(AScopeEntry));
newentry->sym = sym;
newentry->func = func;
newentry->linenum = -1;
return newentry;
}
/* Create an entry in the scope promising to fill in this word later. */
ACompileStatus scope_placehold(AScope *sc, AFuncRegistry *reg, ASymbol *symbol, unsigned int linenum) {
AScopeEntry *e = NULL;
HASH_FIND_PTR(sc->content, &symbol, e);
if (e == NULL) {
AScopeEntry *f = scope_lookup(sc->parent, symbol);
if (f != NULL) {
if (f->linenum != -1) {
fprintf(stderr, "warning: declaration of word ‘%s’ at line %d "
"shadows previous definition at line %d\n",
symbol->name, linenum, f->linenum);
} else {
fprintf(stderr, "warning: declaration of word ‘%s’ at line %d "
"shadows built-in word\n", symbol->name,
linenum);
}
}
HASH_FIND_PTR(sc->content, &symbol, e);
AUserFunc *dummy = malloc(sizeof(AUserFunc));
dummy->type = dummy_func;
dummy->words = NULL;
/* If we only have a placeholder for a function, we assume it has the
* maximum number of free variables. Being cautious like this means
* we might store a few extra closures we don't need, but if we
* actually do need one, we always have one. */
/* (I don't really like how this makes the speed of your program
* super-dependent on the order functions are declared, tho.) */
dummy->free_var_index = 0;
/* The reason for this weird structure is, now we can change the AUserFunc
* that dummyfunc has a pointer to, and anything that got a pointer to
* dummyfunc before we defined it will get that same pointer. */
AFunc *dummyfunc = malloc(sizeof(AFunc));
dummyfunc->type = user_func;
dummyfunc->data.userfunc = dummy;
dummyfunc->sym = symbol;
registry_register(reg, dummyfunc);
AScopeEntry *entry = scope_entry_new(symbol, dummyfunc);
entry->linenum = linenum;
/* 'sym' below is the field in the struct, not the variable 'symbol' here */
HASH_ADD_PTR(sc->content, sym, entry);
} else if (e->func && e->func->type != user_func) {
fprintf(stderr, "error: cannot redefine builtin word ‘%s’\n",
symbol->name);
return compile_fail;
} else if (e->func) {
fprintf(stderr, "error: duplicate definition of ‘%s’ at line %d.\n"
"(it was previously defined at line %d.)\n",
symbol->name, linenum, e->linenum);
return compile_fail;
} else {
assert(0 && "scope entry exists, but has null func pointer");
return compile_fail;
}
return compile_success;
}
/* Register a thing and maybe mark it as imported. Internal only. */
static
ACompileStatus scope_register_import_flag(AScope *sc, ASymbol *symbol, AFunc *func, int imported) {
AScopeEntry *e = NULL;
HASH_FIND_PTR(sc->content, &symbol, e);
if (e != NULL) {
fprintf(stderr, "Error importing function %s: Already defined.\n", symbol->name);
return compile_fail;
}
/* create new entry */
AScopeEntry *entry = scope_entry_new(symbol, func);
entry->imported = imported;
/* 'sym' below is the field in the struct, not the variable 'symbol' here */
HASH_ADD_PTR(sc->content, sym, entry);
return compile_success;
}
/* Register a new word into scope using the symbol ‘symbol’ as a key. */
/* Used when defining stdlib/primitive functions. */
ACompileStatus scope_register(AScope *sc, ASymbol *symbol, AFunc *func) {
return scope_register_import_flag(sc, symbol, func, 0);
}
/* Register a new word into scope using the symbol ‘symbol’ as a key.
* Also marks the word as being imported (which means code importing the current module
* won't get it exported to them as well.) */
ACompileStatus scope_import(AScope *sc, ASymbol *symbol, AFunc *func) {
return scope_register_import_flag(sc, symbol, func, 1);
}
/* Register a new user word into scope. Requires that scope_placehold was already called. */
ACompileStatus scope_user_register(AScope *sc, ASymbol *symbol, unsigned int free_index,
unsigned int vars_below, AWordSeqNode *words) {
AScopeEntry *e = NULL;
HASH_FIND_PTR(sc->content, &symbol, e);
/* handle various error conditions (which shouldn't happen if the other compilation
* code works right...) */
assert (e != NULL && "registering non-dummied user word");
assert (e->func != NULL && "user func is null somehow");
assert (e->func->type == user_func && "somehow defining word in lowest scope, and shadowing builtin");
assert (e->func->data.userfunc->type == dummy_func && "creating duplicate word, but this wasn't "
"caught in scope_placehold somehow");
/* ok i think we're good now */
e->func->data.userfunc->closure = NULL; /* named funcs don't use closure */
e->func->data.userfunc->type = const_func;
e->func->data.userfunc->words = words;
e->func->data.userfunc->free_var_index = free_index;
e->func->data.userfunc->vars_below = vars_below;
e->imported = 0;
return compile_success;
}
/* Create an entry in the scope telling it to push the
* <num>'th bound variable to the stack. */
ACompileStatus scope_create_push(AScope *sc, AFuncRegistry *reg, ASymbol *symbol,
unsigned int index, unsigned int linenum) {
AScopeEntry *e = NULL;
HASH_FIND_PTR(sc->content, &symbol, e);
if (e != NULL) {
fprintf(stderr, "error: variable ‘%s’ declared more than once at line %d\n",
symbol->name, linenum);
return compile_fail;
}
AScopeEntry *f = scope_lookup(sc->parent, symbol);
if (f != NULL) {
if (f->linenum != -1) {
fprintf(stderr, "warning: declaration of variable ‘%s’ at line %d "
"shadows previous word defined at line %d\n",
symbol->name, linenum, f->linenum);
} else {
fprintf(stderr, "warning: declaration of variable ‘%s’ at line %d "
"shadows built-in word\n", symbol->name,
linenum);
}
}
AFunc *pushfunc = malloc(sizeof(AFunc));
pushfunc->type = var_push;
pushfunc->data.varindex = index;
pushfunc->sym = symbol;
registry_register(reg, pushfunc);
AScopeEntry *entry = scope_entry_new(symbol, pushfunc);
entry->linenum = linenum;
/* 'sym' below is the field in the struct, not the variable 'symbol' here */
HASH_ADD_PTR(sc->content, sym, entry);
return compile_success;
}
/* Delete an entry from the scope. (Used if you make a mistake in interactive mode;
* we don't want to ban you from redefining the same function!!) */
void scope_delete(AScope *sc, ASymbol *symbol) {
AScopeEntry *e = NULL;
HASH_FIND_PTR(sc->content, &symbol, e);
if (e != NULL) {
/* delete from scope. */
/* TODO stuff needs to get freed here */
HASH_DEL(sc->content, e);
}
}
/* Look up the word that's bound to a given symbol in a certain lexical scope. */
AScopeEntry *scope_lookup(AScope *sc, ASymbol *symbol) {
if (sc == NULL) {
return NULL;
}
AScopeEntry *e = NULL;
HASH_FIND_PTR(sc->content, &symbol, e);
if (e == NULL) {
/* If we didn't find it here, look it up in the parent. */
return scope_lookup(sc->parent, symbol);
} else {
return e;
}
}
/* Look up a function by name in the scope. */
AFunc *scope_find_func(AScope *sc, ASymbolTable symtab, const char *name) {
/* this takes a const char* and looks up the symbol itself; mostly useful
* for calling user funcs from C code (ie: calling main) */
ASymbol *sym = get_symbol(&symtab, "main");
if (sym == NULL) {
return NULL;
}
AScopeEntry *entry = scope_lookup(sc, sym);
if (entry == NULL) {
/* We don't print an error message here -- calling code should handle it. */
return NULL;
}
return entry->func;
}
/* Free a function. */
void free_user_func(AUserFunc *f) {
if (f->type == const_func) {
/* if const func, its code pointer is just a pointer to something in the
'program' ast, so we don't need to free that. */
} else if (f->type == bound_func) {
/* if it's a bound_func, then we need to free its closure (or at least
* decrease its closure's refcount.) */
varbuf_unref(f->closure);
}
f->words = NULL;
free(f);
}
/* Free a word. */
void free_func(AFunc *f) {
/* The user-func portion (if it's a user-func) will get freed when we free
* the User Func Registry. */
if (f->type == user_func) {
free_user_func(f->data.userfunc);
}
free(f);
}
/* Free a scope entry. */
void free_scope_entry(AScopeEntry *entry) {
/* This doesn't free the function associated with it, since we want
* to dispose of the scope without disturbing the delicate functions
* within (since we're probably freeing the scope at compile-time.) */
free(entry);
}
/* Free a lexical scope at the end. */
void free_scope(AScope *sc) {
if (sc == NULL) return;
AScopeEntry *current, *tmp;
HASH_ITER(hh, sc->content, current, tmp) {
HASH_DEL(sc->content, current);
free_scope_entry(current);
}
free(sc);
}
/* Free the library scope underneath everything else. (This calls free_func
* on its elements, unlike the regular free_scope.) */
void free_lib_scope(AScope *sc) {
if (sc == NULL) return;
AScopeEntry *current, *tmp;
HASH_ITER(hh, sc->content, current, tmp) {
HASH_DEL(sc->content, current);
free_func(current->func);
free_scope_entry(current);
}
free(sc);
}