1 // Logic for the environmental context information during compilation
2 // (variable bindings, code sections, etc.)
7 #include "environment.h"
9 #include "parse/files.h"
10 #include "stdlib/datatypes.h"
11 #include "stdlib/tables.h"
12 #include "stdlib/text.h"
13 #include "stdlib/util.h"
14 #include "typecheck.h"
16 type_t *TEXT_TYPE = NULL;
18 type_t *PATH_TYPE = NULL;
20 type_t *PRESENT_TYPE = NULL;
22 type_t *RESULT_TYPE = NULL;
24 static type_t *declare_type(env_t *env, const char *def_str) {
25 ast_t *ast = parse_file_str(def_str);
26 if (!ast) errx(1, "Couldn't not parse struct def: %s", def_str);
27 if (ast->tag != Block) errx(1, "Couldn't not parse struct def: %s", def_str);
28 ast_list_t *statements = Match(ast, Block)->statements;
29 if (statements == NULL || statements->next) errx(1, "Couldn't not parse struct def: %s", def_str);
30 switch (statements->ast->tag) {
32 DeclareMatch(def, statements->ast, StructDef);
33 prebind_statement(env, statements->ast);
34 bind_statement(env, statements->ast);
35 return Table$str_get(*env->types, def->name);
38 DeclareMatch(def, statements->ast, EnumDef);
39 prebind_statement(env, statements->ast);
40 bind_statement(env, statements->ast);
41 return Table$str_get(*env->types, def->name);
43 default: errx(1, "Not a type definition: %s", def_str);
48 static type_t *bind_type(env_t *env, const char *name, type_t *type) {
49 if (Table$str_get(*env->types, name)) errx(1, "Duplicate binding for type: %s", name);
50 Table$str_set(env->types, name, type);
54 env_t *global_env(bool source_mapping) {
55 static env_t *_global_env = NULL;
56 if (_global_env != NULL) return _global_env;
58 env_t *env = new (env_t);
59 env->code = new (compilation_unit_t);
60 env->types = new (Table_t);
61 env->globals = new (Table_t);
62 env->locals = env->globals;
63 env->imports = new (Table_t);
64 env->do_source_mapping = source_mapping;
66 TEXT_TYPE = bind_type(env, "Text", Type(TextType, .lang = "Text", .env = namespace_env(env, "Text")));
67 (void)bind_type(env, "Int", Type(BigIntType));
68 (void)bind_type(env, "Int32", Type(IntType, .bits = TYPE_IBITS32));
69 (void)bind_type(env, "Memory", Type(MemoryType));
70 PATH_TYPE = bind_type(env, "Path", Type(PathType));
71 RESULT_TYPE = declare_type(env, "enum Result(Success, Failure(reason:Text))");
73 PRESENT_TYPE = declare_type(env, "struct Present()");
76 const char *name, *code, *type_str;
79 #define MAKE_TYPE(name, type, type_name, type_info, ...) \
81 name, type, type_name, type_info, TypedList(ns_entry_t, __VA_ARGS__) \
90 MAKE_TYPE("Void", Type(VoidType), Text("void"), Text("Void$info")),
91 MAKE_TYPE("Abort", Type(AbortType), Text("void"), Text("Abort$info")),
92 MAKE_TYPE("Memory", Type(MemoryType), Text("void"), Text("Memory$info")),
93 MAKE_TYPE("Present", PRESENT_TYPE, Text("Present$$type"), Text("Present$$info")),
94 MAKE_TYPE("Result", RESULT_TYPE, Text("Result_t"), Text("Result$$info")),
96 "Bool", Type(BoolType), Text("Bool_t"), Text("Bool$info"),
97 {"parse", "Bool$parse", "func(text:Text, remainder:&Text?=none -> Bool?)"}),
99 "Byte", Type(ByteType), Text("Byte_t"), Text("Byte$info"),
100 {"get_bit", "Byte$get_bit", "func(x:Byte, bit_index:Int -> Bool)"}, //
101 {"hex", "Byte$hex", "func(byte:Byte, uppercase=yes, prefix=no -> Text)"}, //
102 {"is_between", "Byte$is_between", "func(x:Byte, a:Byte, b:Byte -> Bool)"}, //
103 {"max", "Byte$max", "Byte"}, //
104 {"min", "Byte$min", "Byte"}, //
105 {"parse", "Byte$parse", "func(text:Text, base:Int?=none, remainder:&Text?=none -> Byte?)"}, //
106 {"to", "Byte$to", "func(first:Byte, last:Byte, step:Int8?=none -> func(->Byte?))"}),
108 "Int", Type(BigIntType), Text("Int_t"), Text("Int$info"), {"abs", "Int$abs", "func(x:Int -> Int)"}, //
109 {"bit_and", "Int$bit_and", "func(x,y:Int -> Int)"}, //
110 {"bit_or", "Int$bit_or", "func(x,y:Int -> Int)"}, //
111 {"bit_xor", "Int$bit_xor", "func(x,y:Int -> Int)"}, //
112 {"choose", "Int$choose", "func(x,y:Int -> Int)"}, //
113 {"clamped", "Int$clamped", "func(x,low,high:Int -> Int)"}, //
114 {"divided_by", "Int$divided_by", "func(x,y:Int -> Int)"}, //
115 {"factorial", "Int$factorial", "func(x:Int -> Int)"}, //
116 {"gcd", "Int$gcd", "func(x,y:Int -> Int)"}, //
117 {"get_bit", "Int$get_bit", "func(x,bit_index:Int -> Bool)"}, //
118 {"hex", "Int$hex", "func(i:Int, digits=0, uppercase=yes, prefix=yes -> Text)"}, //
119 {"is_between", "Int$is_between", "func(x:Int, a:Int, b:Int -> Bool)"}, //
120 {"is_prime", "Int$is_prime", "func(x:Int,reps=50 -> Bool)"}, //
121 {"left_shifted", "Int$left_shifted", "func(x,y:Int -> Int)"}, //
122 {"minus", "Int$minus", "func(x,y:Int -> Int)"}, //
123 {"modulo", "Int$modulo", "func(x,y:Int -> Int)"}, //
124 {"modulo1", "Int$modulo1", "func(x,y:Int -> Int)"}, //
125 {"negated", "Int$negated", "func(x:Int -> Int)"}, //
126 {"negative", "Int$negative", "func(x:Int -> Int)"}, //
127 {"next_prime", "Int$next_prime", "func(x:Int -> Int)"}, //
128 {"octal", "Int$octal", "func(i:Int, digits=0, prefix=yes -> Text)"}, //
129 {"onward", "Int$onward", "func(first:Int,step=1 -> func(->Int?))"}, //
130 {"parse", "Int$parse", "func(text:Text, base:Int?=none, remainder:&Text?=none -> Int?)"}, //
131 {"plus", "Int$plus", "func(x,y:Int -> Int)"}, //
132 {"power", "Int$power", "func(base:Int,exponent:Int -> Int)"}, //
133 {"right_shifted", "Int$right_shifted", "func(x,y:Int -> Int)"}, //
134 {"sqrt", "Int$sqrt", "func(x:Int -> Int?)"}, //
135 {"times", "Int$times", "func(x,y:Int -> Int)"}, //
136 {"to", "Int$to", "func(first:Int,last:Int,step:Int?=none -> func(->Int?))"}),
138 "Int64", Type(IntType, .bits = TYPE_IBITS64), Text("Int64_t"), Text("Int64$info"),
139 {"abs", "labs", "func(i:Int64 -> Int64)"}, //
140 {"bits", "Int64$bits", "func(x:Int64 -> [Bool])"}, //
141 {"clamped", "Int64$clamped", "func(x,low,high:Int64 -> Int64)"}, //
142 {"divided_by", "Int64$divided_by", "func(x,y:Int64 -> Int64)"}, //
143 {"gcd", "Int64$gcd", "func(x,y:Int64 -> Int64)"}, //
144 {"parse", "Int64$parse", "func(text:Text, base:Int?=none, remainder:&Text?=none -> Int64?)"}, //
145 {"get_bit", "Int64$get_bit", "func(x:Int64, bit_index:Int -> Bool)"}, //
146 {"hex", "Int64$hex", "func(i:Int64, digits=0, uppercase=yes, prefix=yes -> Text)"}, //
147 {"is_between", "Int64$is_between", "func(x:Int64, a:Int64, b:Int64 -> Bool)"}, //
148 {"max", "Int64$max", "Int64"}, //
149 {"min", "Int64$min", "Int64"}, //
150 {"modulo", "Int64$modulo", "func(x,y:Int64 -> Int64)"}, //
151 {"modulo1", "Int64$modulo1", "func(x,y:Int64 -> Int64)"}, //
152 {"octal", "Int64$octal", "func(i:Int64, digits=0, prefix=yes -> Text)"}, //
153 {"onward", "Int64$onward", "func(first:Int64,step=Int64(1) -> func(->Int64?))"}, //
154 {"to", "Int64$to", "func(first:Int64,last:Int64,step:Int64?=none -> func(->Int64?))"}, //
155 {"unsigned_left_shifted", "Int64$unsigned_left_shifted", "func(x:Int64,y:Int64 -> Int64)"}, //
156 {"unsigned_right_shifted", "Int64$unsigned_right_shifted", "func(x:Int64,y:Int64 -> Int64)"}, //
157 {"wrapping_minus", "Int64$wrapping_minus", "func(x:Int64,y:Int64 -> Int64)"}, //
158 {"wrapping_plus", "Int64$wrapping_plus", "func(x:Int64,y:Int64 -> Int64)"}),
160 "Int32", Type(IntType, .bits = TYPE_IBITS32), Text("Int32_t"), Text("Int32$info"), //
161 {"abs", "abs", "func(i:Int32 -> Int32)"}, //
162 {"bits", "Int32$bits", "func(x:Int32 -> [Bool])"}, //
163 {"clamped", "Int32$clamped", "func(x,low,high:Int32 -> Int32)"}, //
164 {"divided_by", "Int32$divided_by", "func(x,y:Int32 -> Int32)"}, //
165 {"gcd", "Int32$gcd", "func(x,y:Int32 -> Int32)"}, //
166 {"parse", "Int32$parse", "func(text:Text, base:Int?=none, remainder:&Text?=none -> Int32?)"}, //
167 {"get_bit", "Int32$get_bit", "func(x:Int32, bit_index:Int -> Bool)"}, //
168 {"hex", "Int32$hex", "func(i:Int32, digits=0, uppercase=yes, prefix=yes -> Text)"}, //
169 {"is_between", "Int32$is_between", "func(x:Int32, a:Int32, b:Int32 -> Bool)"}, //
170 {"max", "Int32$max", "Int32"}, //
171 {"min", "Int32$min", "Int32"}, //
172 {"modulo", "Int32$modulo", "func(x,y:Int32 -> Int32)"}, //
173 {"modulo1", "Int32$modulo1", "func(x,y:Int32 -> Int32)"}, //
174 {"octal", "Int32$octal", "func(i:Int32, digits=0, prefix=yes -> Text)"}, //
175 {"onward", "Int32$onward", "func(first:Int32,step=Int32(1) -> func(->Int32?))"}, //
176 {"to", "Int32$to", "func(first:Int32,last:Int32,step:Int32?=none -> func(->Int32?))"}, //
177 {"unsigned_left_shifted", "Int32$unsigned_left_shifted", "func(x:Int32,y:Int32 -> Int32)"}, //
178 {"unsigned_right_shifted", "Int32$unsigned_right_shifted", "func(x:Int32,y:Int32 -> Int32)"}, //
179 {"wrapping_minus", "Int32$wrapping_minus", "func(x:Int32,y:Int32 -> Int32)"}, //
180 {"wrapping_plus", "Int32$wrapping_plus", "func(x:Int32,y:Int32 -> Int32)"}),
182 "Int16", Type(IntType, .bits = TYPE_IBITS16), Text("Int16_t"), Text("Int16$info"),
183 {"abs", "abs", "func(i:Int16 -> Int16)"}, //
184 {"bits", "Int16$bits", "func(x:Int16 -> [Bool])"}, //
185 {"clamped", "Int16$clamped", "func(x,low,high:Int16 -> Int16)"}, //
186 {"divided_by", "Int16$divided_by", "func(x,y:Int16 -> Int16)"}, //
187 {"gcd", "Int16$gcd", "func(x,y:Int16 -> Int16)"}, //
188 {"parse", "Int16$parse", "func(text:Text, base:Int?=none, remainder:&Text?=none -> Int16?)"}, //
189 {"get_bit", "Int16$get_bit", "func(x:Int16, bit_index:Int -> Bool)"}, //
190 {"hex", "Int16$hex", "func(i:Int16, digits=0, uppercase=yes, prefix=yes -> Text)"}, //
191 {"is_between", "Int16$is_between", "func(x:Int16, a:Int16, b:Int16 -> Bool)"}, //
192 {"max", "Int16$max", "Int16"}, //
193 {"min", "Int16$min", "Int16"}, //
194 {"modulo", "Int16$modulo", "func(x,y:Int16 -> Int16)"}, //
195 {"modulo1", "Int16$modulo1", "func(x,y:Int16 -> Int16)"}, //
196 {"octal", "Int16$octal", "func(i:Int16, digits=0, prefix=yes -> Text)"}, //
197 {"onward", "Int16$onward", "func(first:Int16,step=Int16(1) -> func(->Int16?))"}, //
198 {"to", "Int16$to", "func(first:Int16,last:Int16,step:Int16?=none -> func(->Int16?))"}, //
199 {"unsigned_left_shifted", "Int16$unsigned_left_shifted", "func(x:Int16,y:Int16 -> Int16)"}, //
200 {"unsigned_right_shifted", "Int16$unsigned_right_shifted", "func(x:Int16,y:Int16 -> Int16)"}, //
201 {"wrapping_minus", "Int16$wrapping_minus", "func(x:Int16,y:Int16 -> Int16)"}, //
202 {"wrapping_plus", "Int16$wrapping_plus", "func(x:Int16,y:Int16 -> Int16)"}),
204 "Int8", Type(IntType, .bits = TYPE_IBITS8), Text("Int8_t"), Text("Int8$info"),
205 {"abs", "abs", "func(i:Int8 -> Int8)"}, //
206 {"bits", "Int8$bits", "func(x:Int8 -> [Bool])"}, //
207 {"clamped", "Int8$clamped", "func(x,low,high:Int8 -> Int8)"}, //
208 {"divided_by", "Int8$divided_by", "func(x,y:Int8 -> Int8)"}, //
209 {"gcd", "Int8$gcd", "func(x,y:Int8 -> Int8)"}, //
210 {"parse", "Int8$parse", "func(text:Text, base:Int?=none, remainder:&Text?=none -> Int8?)"}, //
211 {"get_bit", "Int8$get_bit", "func(x:Int8, bit_index:Int -> Bool)"}, //
212 {"hex", "Int8$hex", "func(i:Int8, digits=0, uppercase=yes, prefix=yes -> Text)"}, //
213 {"is_between", "Int8$is_between", "func(x:Int8, a:Int8, b:Int8 -> Bool)"}, //
214 {"max", "Int8$max", "Int8"}, //
215 {"min", "Int8$min", "Int8"}, //
216 {"modulo", "Int8$modulo", "func(x,y:Int8 -> Int8)"}, //
217 {"modulo1", "Int8$modulo1", "func(x,y:Int8 -> Int8)"}, //
218 {"octal", "Int8$octal", "func(i:Int8, digits=0, prefix=yes -> Text)"}, //
219 {"onward", "Int8$onward", "func(first:Int8,step=Int8(1) -> func(->Int8?))"}, //
220 {"to", "Int8$to", "func(first:Int8,last:Int8,step:Int8?=none -> func(->Int8?))"}, //
221 {"unsigned_left_shifted", "Int8$unsigned_left_shifted", "func(x:Int8,y:Int8 -> Int8)"}, //
222 {"unsigned_right_shifted", "Int8$unsigned_right_shifted", "func(x:Int8,y:Int8 -> Int8)"}, //
223 {"wrapping_minus", "Int8$wrapping_minus", "func(x:Int8,y:Int8 -> Int8)"}, //
224 {"wrapping_plus", "Int8$wrapping_plus", "func(x:Int8,y:Int8 -> Int8)"}, ),
225 #define C(name) {#name, "M_" #name, "Num"}
226 #define F(name) {#name, #name, "func(n:Num -> Num)"}
227 #define F_opt(name) {#name, #name, "func(n:Num -> Num?)"}
228 #define F2(name) {#name, #name, "func(x,y:Num -> Num)"}
230 "Num", Type(NumType, .bits = TYPE_NBITS64), Text("Num_t"), Text("Num$info"),
231 {"near", "Num$near", "func(x,y:Num, ratio=1e-9, min_epsilon=1e-9 -> Bool)"}, //
232 {"clamped", "Num$clamped", "func(x,low,high:Num -> Num)"}, //
233 {"percent", "Num$percent", "func(n:Num,precision=0.01 -> Text)"}, //
234 {"with_precision", "Num$with_precision", "func(n:Num,precision:Num -> Num)"}, //
235 {"is_between", "Num$is_between", "func(x:Num, a:Num, b:Num -> Bool)"}, //
236 {"isinf", "Num$isinf", "func(n:Num -> Bool)"}, //
237 {"isfinite", "Num$finite", "func(n:Num -> Bool)"}, //
238 {"modulo", "Num$mod", "func(x,y:Num -> Num)"}, //
239 {"modulo1", "Num$mod1", "func(x,y:Num -> Num)"}, //
240 C(2_SQRTPI), C(E), C(PI_2), C(2_PI), C(1_PI), C(LN10), C(LN2), C(LOG2E), C(PI), C(PI_4), C(SQRT2),
241 C(SQRT1_2), {"INF", "(Num_t)(INFINITY)", "Num"}, //
242 {"TAU", "(Num_t)(2.*M_PI)", "Num"}, //
243 {"mix", "Num$mix", "func(amount,x,y:Num -> Num)"}, //
244 {"parse", "Num$parse", "func(text:Text, remainder:&Text?=none -> Num?)"}, //
245 {"abs", "fabs", "func(n:Num -> Num)"}, //
246 F_opt(acos), F_opt(acosh), F_opt(asin), F(asinh), F(atan), F_opt(atanh), F(cbrt), F(ceil), F_opt(cos),
247 F(cosh), F(erf), F(erfc), F(exp), F(exp2), F(expm1), F(floor), F(j0), F(j1), F_opt(log), F_opt(log10),
248 F_opt(log1p), F_opt(log2), F(logb), F(rint), F(round), F(significand), F_opt(sin), F(sinh), F_opt(sqrt),
249 F_opt(tan), F(tanh), F_opt(tgamma), F(trunc), F_opt(y0), F_opt(y1), F2(atan2), F2(copysign), F2(fdim),
250 F2(hypot), F2(nextafter)),
255 #define C(name) {#name, "(Num32_t)(M_" #name ")", "Num32"}
256 #define F(name) {#name, #name "f", "func(n:Num32 -> Num32)"}
257 #define F_opt(name) {#name, #name "f", "func(n:Num32 -> Num32?)"}
258 #define F2(name) {#name, #name "f", "func(x,y:Num32 -> Num32)"}
260 "Num32", Type(NumType, .bits = TYPE_NBITS32), Text("Num32_t"), Text("Num32$info"), //
261 {"near", "Num32$near", "func(x,y:Num32, ratio=Num32(1e-9), min_epsilon=Num32(1e-9) -> Bool)"}, //
262 {"clamped", "Num32$clamped", "func(x,low,high:Num32 -> Num32)"}, //
263 {"percent", "Num32$percent", "func(n:Num32,precision=Num32(.01) -> Text)"}, //
264 {"with_precision", "Num32$with_precision", "func(n:Num32,precision:Num32 -> Num32)"}, //
265 {"is_between", "Num32$is_between", "func(x:Num32, a:Num32, b:Num32 -> Bool)"}, //
266 {"isinf", "Num32$isinf", "func(n:Num32 -> Bool)"}, //
267 {"isfinite", "Num32$isfinite", "func(n:Num32 -> Bool)"}, //
268 C(2_SQRTPI), C(E), C(PI_2), C(2_PI), C(1_PI), C(LN10), C(LN2), C(LOG2E), C(PI), C(PI_4), C(SQRT2),
270 {"INF", "(Num32_t)(INFINITY)", "Num32"}, //
271 {"TAU", "(Num32_t)(2.f*M_PI)", "Num32"}, //
272 {"mix", "Num32$mix", "func(amount,x,y:Num32 -> Num32)"}, //
273 {"parse", "Num32$parse", "func(text:Text, remainder:&Text?=none -> Num32?)"}, //
274 {"abs", "fabsf", "func(n:Num32 -> Num32)"}, //
275 {"modulo", "Num32$mod", "func(x,y:Num32 -> Num32)"}, //
276 {"modulo1", "Num32$mod1", "func(x,y:Num32 -> Num32)"}, //
277 F_opt(acos), F_opt(acosh), F_opt(asin), F(asinh), F(atan), F_opt(atanh), F(cbrt), F(ceil), F_opt(cos),
278 F(cosh), F(erf), F(erfc), F(exp), F(exp2), F(expm1), F(floor), F(j0), F(j1), F_opt(log), F_opt(log10),
279 F_opt(log1p), F_opt(log2), F(logb), F(rint), F(round), F(significand), F_opt(sin), F(sinh), F_opt(sqrt),
280 F_opt(tan), F(tanh), F_opt(tgamma), F(trunc), F_opt(y0), F_opt(y1), F2(atan2), F2(copysign), F2(fdim),
281 F2(hypot), F2(nextafter)),
283 "CString", Type(CStringType), Text("char*"), Text("CString$info"), //
284 {"as_text", "Text$from_str", "func(str:CString -> Text)"},
285 {"join", "CString$join", "func(glue:CString, pieces:[CString] -> CString)"}),
291 "Path", PATH_TYPE, Text("Path_t"), Text("Path$info"), //
292 {"accessed", "Path$accessed", "func(path:Path, follow_symlinks=yes -> Int64?)"}, //
293 {"append", "Path$append", "func(path:Path, text:Text, permissions=Int32(0o644) -> Result)"}, //
294 {"append_bytes", "Path$append_bytes",
295 "func(path:Path, bytes:[Byte], permissions=Int32(0o644) -> Result)"}, //
296 {"base_name", "Path$base_name", "func(path:Path -> Text)"}, //
297 {"by_line", "Path$by_line", "func(path:Path -> func(->Text?)?)"}, //
298 {"can_execute", "Path$can_execute", "func(path:Path -> Bool)"}, //
299 {"can_read", "Path$can_read", "func(path:Path -> Bool)"}, //
300 {"can_write", "Path$can_write", "func(path:Path -> Bool)"}, //
301 {"changed", "Path$changed", "func(path:Path, follow_symlinks=yes -> Int64?)"}, //
302 {"child", "Path$child", "func(path:Path, child:Text -> Path)"}, //
303 {"children", "Path$children", "func(path:Path, include_hidden=no -> [Path])"}, //
304 {"concatenated_with", "Path$concat", "func(a,b:Path -> Path)"}, //
305 {"components", "Path$components", "func(path:Path -> [Text])"}, //
306 {"create_directory", "Path$create_directory",
307 "func(path:Path, permissions=Int32(0o755), recursive=yes -> Result)"}, //
308 {"current_dir", "Path$current_dir", "func(->Path)"}, //
309 {"each_child", "Path$each_child", "func(path:Path, include_hidden=no -> func(->Path?))"}, //
310 {"exists", "Path$exists", "func(path:Path -> Bool)"}, //
311 {"expand_home", "Path$expand_home", "func(path:Path -> Path)"}, //
312 {"extension", "Path$extension", "func(path:Path, full=yes -> Text)"}, //
313 {"files", "Path$children", "func(path:Path, include_hidden=no -> [Path])"}, //
314 {"glob", "Path$glob", "func(path:Path -> [Path])"}, //
315 {"group", "Path$group", "func(path:Path, follow_symlinks=yes -> Text?)"}, //
316 {"has_extension", "Path$has_extension", "func(path:Path, extension:Text -> Bool)"}, //
317 {"is_directory", "Path$is_directory", "func(path:Path, follow_symlinks=yes -> Bool)"}, //
318 {"is_file", "Path$is_file", "func(path:Path, follow_symlinks=yes -> Bool)"}, //
319 {"is_pipe", "Path$is_pipe", "func(path:Path, follow_symlinks=yes -> Bool)"}, //
320 {"is_socket", "Path$is_socket", "func(path:Path, follow_symlinks=yes -> Bool)"}, //
321 {"is_symlink", "Path$is_symlink", "func(path:Path -> Bool)"}, //
322 {"lines", "Path$lines", "func(path:Path -> [Text]?)"}, //
323 {"matches_glob", "Path$matches_glob", "func(path:Path, glob:Text -> Bool)"}, //
324 {"modified", "Path$modified", "func(path:Path, follow_symlinks=yes -> Int64?)"}, //
325 {"move", "Path$move", "func(path:Path, dest:Path, allow_overwriting=no -> Result)"}, //
326 {"owner", "Path$owner", "func(path:Path, follow_symlinks=yes -> Text?)"}, //
327 {"parent", "Path$parent", "func(path:Path -> Path?)"}, //
328 {"read", "Path$read", "func(path:Path -> Text?)"}, //
329 {"read_bytes", "Path$read_bytes", "func(path:Path, limit:Int?=none -> [Byte]?)"}, //
330 {"relative_to", "Path$relative_to", "func(path:Path, relative_to:Path -> Path)"}, //
331 {"remove", "Path$remove", "func(path:Path, ignore_missing=no -> Result)"}, //
332 {"resolved", "Path$resolved", "func(path:Path, relative_to=(./) -> Path)"}, //
333 {"set_owner", "Path$set_owner",
334 "func(path:Path, owner:Text?=none, group:Text?=none, follow_symlinks=yes -> Result)"}, //
335 {"sibling", "Path$sibling", "func(path:Path, name:Text -> Path)"}, //
336 {"subdirectories", "Path$children", "func(path:Path, include_hidden=no -> [Path])"}, //
337 {"unique_directory", "Path$unique_directory", "func(path:Path -> Path)"}, //
338 {"walk", "Path$walk", "func(path:Path, include_hidden=no, follow_symlinks=no -> func(->Path?))"}, //
339 {"with_extension", "Path$with_extension", "func(path:Path, extension:Text, replace:Bool=yes -> Path)"}, //
340 {"write", "Path$write", "func(path:Path, text:Text, permissions=Int32(0o644) -> Result)"}, //
341 {"writer", "Path$writer",
342 "func(path:Path, append=no, permissions=Int32(0o644) -> func(text:Text, close=no -> Result))"}, //
343 {"byte_writer", "Path$byte_writer",
344 "func(path:Path, append=no, permissions=Int32(0o644) -> func(bytes:[Byte], close=no -> Result))"}, //
345 {"write_bytes", "Path$write_bytes", "func(path:Path, bytes:[Byte], permissions=Int32(0o644) -> Result)"}, //
346 {"write_unique", "Path$write_unique", "func(path:Path, text:Text -> Path?)"}, //
347 {"write_unique_bytes", "Path$write_unique_bytes", "func(path:Path, bytes:[Byte] -> Path?)"}),
349 "Text", TEXT_TYPE, Text("Text_t"), Text("Text$info"), //
350 {"as_c_string", "Text$as_c_string", "func(text:Text -> CString)"}, //
351 {"at", "Text$cluster", "func(text:Text, index:Int -> Text)"}, //
352 {"by_line", "Text$by_line", "func(text:Text -> func(->Text?))"}, //
353 {"by_split", "Text$by_split", "func(text:Text, delimiter='' -> func(->Text?))"}, //
354 {"by_split_any", "Text$by_split_any", "func(text:Text, delimiters=' \\t\\r\\n' -> func(->Text?))"}, //
355 {"caseless_equals", "Text$equal_ignoring_case", "func(a,b:Text, language='C' -> Bool)"}, //
356 {"codepoint_names", "Text$codepoint_names", "func(text:Text -> [Text])"}, //
357 {"distance", "Text$distance", "func(a,b:Text, language='C' -> Num)"}, //
358 {"ends_with", "Text$ends_with", "func(text,suffix:Text, remainder:&Text? = none -> Bool)"}, //
359 {"find", "Text$find", "func(text,target:Text, start=1 -> Int?)"}, //
360 {"from", "Text$from", "func(text:Text, first:Int -> Text)"}, //
361 {"from_c_string", "Text$from_str", "func(str:CString -> Text?)"}, //
362 {"from_codepoint_names", "Text$from_codepoint_names", "func(codepoint_names:[Text] -> Text?)"}, //
363 {"from_text", "Path$from_text", "func(text:Text -> Path)"}, //
364 {"from_utf8", "Text$from_utf8", "func(bytes:[Byte] -> Text?)"}, //
365 {"from_utf16", "Text$from_utf16", "func(codepoints:[Int16] -> Text?)"}, //
366 {"from_utf32", "Text$from_utf32", "func(codepoints:[Int32] -> Text?)"}, //
367 {"has", "Text$has", "func(text:Text, target:Text -> Bool)"}, //
368 {"join", "Text$join", "func(glue:Text, pieces:[Text] -> Text)"}, //
369 {"layout", "Text$layout", "func(text:Text -> Text)"}, //
370 {"left_pad", "Text$left_pad", "func(text:Text, count:Int, pad=' ', language='C' -> Text)"}, //
371 {"lines", "Text$lines", "func(text:Text -> [Text])"}, //
372 {"lower", "Text$lower", "func(text:Text, language='C' -> Text)"}, //
373 {"matches_glob", "Text$matches_glob", "func(text:Text, glob:Text -> Bool)"}, //
374 {"memory_size", "Text$memory_size", "func(text:Text -> Int)"}, //
375 {"middle_pad", "Text$middle_pad", "func(text:Text, count:Int, pad=' ', language='C' -> Text)"}, //
376 {"quoted", "Text$quoted", "func(text:Text, color=no, quotation_mark='\"' -> Text)"}, //
377 {"repeat", "Text$repeat", "func(text:Text, count:Int -> Text)"}, //
378 {"replace", "Text$replace", "func(text:Text, target:Text, replacement:Text -> Text)"}, //
379 {"reversed", "Text$reversed", "func(text:Text -> Text)"}, //
380 {"right_pad", "Text$right_pad", "func(text:Text, count:Int, pad=' ', language='C' -> Text)"}, //
381 {"slice", "Text$slice", "func(text:Text, from=1, to=-1 -> Text)"}, //
382 {"split", "Text$split", "func(text:Text, delimiter='' -> [Text])"}, //
383 {"split_any", "Text$split_any", "func(text:Text, delimiters=' \\t\\r\\n' -> [Text])"}, //
384 {"starts_with", "Text$starts_with", "func(text,prefix:Text, remainder:&Text? = none -> Bool)"}, //
385 {"title", "Text$title", "func(text:Text, language='C' -> Text)"}, //
386 {"to", "Text$to", "func(text:Text, last:Int -> Text)"}, //
387 {"translate", "Text$translate", "func(text:Text, translations:{Text:Text} -> Text)"}, //
388 {"trim", "Text$trim", "func(text:Text, to_trim=\" \\t\\r\\n\", left=yes, right=yes -> Text)"}, //
389 {"upper", "Text$upper", "func(text:Text, language='C' -> Text)"}, //
390 {"utf8", "Text$utf8", "func(text:Text -> [Byte])"}, //
391 {"utf16", "Text$utf16", "func(text:Text -> [Int16])"}, //
392 {"utf32", "Text$utf32", "func(text:Text -> [Int32])"}, //
393 {"width", "Text$width", "func(text:Text, language='C' -> Int)"}, //
394 {"without_prefix", "Text$without_prefix", "func(text,prefix:Text -> Text)"}, //
395 {"without_suffix", "Text$without_suffix", "func(text,suffix:Text -> Text)"}),
399 for (size_t i = 0; i < sizeof(global_types) / sizeof(global_types[0]); i++) {
400 env_t *ns_env = NULL;
401 switch (global_types[i].type->tag) {
402 case TextType: ns_env = Match(global_types[i].type, TextType)->env; break;
403 case StructType: ns_env = Match(global_types[i].type, StructType)->env; break;
404 case EnumType: ns_env = Match(global_types[i].type, EnumType)->env; break;
407 if (ns_env == NULL) ns_env = namespace_env(env, global_types[i].name);
410 .type = Type(TypeInfoType, .name = global_types[i].name, .type = global_types[i].type, .env = ns_env),
411 .code = global_types[i].typeinfo);
412 Table$str_set(env->globals, global_types[i].name, binding);
413 Table$str_set(env->types, global_types[i].name, global_types[i].type);
416 for (size_t i = 0; i < sizeof(global_types) / sizeof(global_types[0]); i++) {
417 binding_t *type_binding = Table$str_get(*env->globals, global_types[i].name);
418 assert(type_binding);
419 env_t *ns_env = Match(type_binding->type, TypeInfoType)->env;
420 for (int64_t j = 0; j < (int64_t)global_types[i].namespace.length; j++) {
421 ns_entry_t *entry = global_types[i].namespace.data + j * global_types[i].namespace.stride;
422 type_t *type = parse_type_string(ns_env, entry->type_str);
423 if (!type) compiler_err(NULL, NULL, NULL, "Couldn't parse type string: ", entry->type_str);
424 if (type->tag == ClosureType) type = Match(type, ClosureType)->fn;
425 set_binding(ns_env, entry->name, type, Text$from_str(entry->code));
429 // Conversion constructors:
430 #define ADD_CONSTRUCTORS(type_name, ...) \
432 env_t *ns_env = namespace_env(env, type_name); \
434 const char *c_name, *type_str; \
435 } constructor_infos[] = {__VA_ARGS__}; \
436 for (size_t i = 0; i < sizeof(constructor_infos) / sizeof(constructor_infos[0]); i++) { \
437 type_t *t = parse_type_string(ns_env, constructor_infos[i].type_str); \
438 List$insert(&ns_env->namespace->constructors, \
440 {.code = Text$from_str(constructor_infos[i].c_name), .type = Match(t, ClosureType)->fn}}), \
441 I(0), sizeof(binding_t)); \
445 ADD_CONSTRUCTORS("Bool", //
446 {"Bool$from_byte", "func(b:Byte -> Bool)"}, //
447 {"Bool$from_int8", "func(i:Int8 -> Bool)"}, //
448 {"Bool$from_int16", "func(i:Int16 -> Bool)"}, //
449 {"Bool$from_int32", "func(i:Int32 -> Bool)"}, //
450 {"Bool$from_int64", "func(i:Int64 -> Bool)"}, //
451 {"Bool$from_int", "func(i:Int -> Bool)"});
452 ADD_CONSTRUCTORS("Byte", //
453 {"Byte$from_bool", "func(b:Bool -> Byte)"}, //
454 {"Byte$from_int8", "func(i:Int8 -> Byte)"}, //
455 {"Byte$from_int16", "func(i:Int16, truncate=no -> Byte)"}, //
456 {"Byte$from_int32", "func(i:Int32, truncate=no -> Byte)"}, //
457 {"Byte$from_int64", "func(i:Int64, truncate=no -> Byte)"}, //
458 {"Byte$from_int", "func(i:Int, truncate=no -> Byte)"});
459 ADD_CONSTRUCTORS("Int", //
460 {"Int$from_bool", "func(b:Bool -> Int)"}, //
461 {"Int$from_byte", "func(b:Byte -> Int)"}, //
462 {"Int$from_int8", "func(i:Int8 -> Int)"}, //
463 {"Int$from_int16", "func(i:Int16 -> Int)"}, //
464 {"Int$from_int32", "func(i:Int32 -> Int)"}, //
465 {"Int$from_int64", "func(i:Int64 -> Int)"}, //
466 {"Int$from_num64", "func(n:Num, truncate=no -> Int)"}, //
467 {"Int$from_num32", "func(n:Num32, truncate=no -> Int)"});
468 ADD_CONSTRUCTORS("Int64", //
469 {"Int64$from_bool", "func(b:Bool -> Int64)"}, //
470 {"Int64$from_byte", "func(b:Byte -> Int64)"}, //
471 {"Int64$from_int8", "func(i:Int8 -> Int64)"}, //
472 {"Int64$from_int16", "func(i:Int16 -> Int64)"}, //
473 {"Int64$from_int32", "func(i:Int32 -> Int64)"}, //
474 {"Int64$from_int", "func(i:Int, truncate=no -> Int64)"}, //
475 {"Int64$from_num64", "func(n:Num, truncate=no -> Int64)"}, //
476 {"Int64$from_num32", "func(n:Num32, truncate=no -> Int64)"});
477 ADD_CONSTRUCTORS("Int32", //
478 {"Int32$from_bool", "func(b:Bool -> Int32)"}, //
479 {"Int32$from_byte", "func(b:Byte -> Int32)"}, //
480 {"Int32$from_int8", "func(i:Int8 -> Int32)"}, //
481 {"Int32$from_int16", "func(i:Int16 -> Int32)"}, //
482 {"Int32$from_int64", "func(i:Int64, truncate=no -> Int32)"}, //
483 {"Int32$from_int", "func(i:Int, truncate=no -> Int32)"}, //
484 {"Int32$from_num64", "func(n:Num, truncate=no -> Int32)"}, //
485 {"Int32$from_num32", "func(n:Num32, truncate=no -> Int32)"});
486 ADD_CONSTRUCTORS("Int16", //
487 {"Int16$from_bool", "func(b:Bool -> Int16)"}, //
488 {"Int16$from_byte", "func(b:Byte -> Int16)"}, //
489 {"Int16$from_int8", "func(i:Int8 -> Int16)"}, //
490 {"Int16$from_int32", "func(i:Int32, truncate=no -> Int16)"}, //
491 {"Int16$from_int64", "func(i:Int64, truncate=no -> Int16)"}, //
492 {"Int16$from_int", "func(i:Int, truncate=no -> Int16)"}, //
493 {"Int16$from_num64", "func(n:Num, truncate=no -> Int16)"}, //
494 {"Int16$from_num32", "func(n:Num32, truncate=no -> Int16)"});
495 ADD_CONSTRUCTORS("Int8", //
496 {"Int8$from_bool", "func(b:Bool -> Int8)"}, //
497 {"Int8$from_byte", "func(b:Byte -> Int8)"}, //
498 {"Int8$from_int16", "func(i:Int16, truncate=no -> Int8)"}, //
499 {"Int8$from_int32", "func(i:Int32, truncate=no -> Int8)"}, //
500 {"Int8$from_int64", "func(i:Int64, truncate=no -> Int8)"}, //
501 {"Int8$from_int", "func(i:Int, truncate=no -> Int8)"}, //
502 {"Int8$from_num64", "func(n:Num, truncate=no -> Int8)"}, //
503 {"Int8$from_num32", "func(n:Num32, truncate=no -> Int8)"});
504 ADD_CONSTRUCTORS("Num", //
505 {"Num$from_bool", "func(b:Bool -> Num)"}, //
506 {"Num$from_byte", "func(b:Byte -> Num)"}, //
507 {"Num$from_int8", "func(i:Int8 -> Num)"}, //
508 {"Num$from_int16", "func(i:Int16 -> Num)"}, //
509 {"Num$from_int32", "func(i:Int32 -> Num)"}, //
510 {"Num$from_int64", "func(i:Int64, truncate=no -> Num)"}, //
511 {"Num$from_int", "func(i:Int, truncate=no -> Num)"}, //
512 {"Num$from_num32", "func(n:Num32 -> Num)"});
513 ADD_CONSTRUCTORS("Num32", //
514 {"Num32$from_bool", "func(b:Bool -> Num32)"}, //
515 {"Num32$from_byte", "func(b:Byte -> Num32)"}, //
516 {"Num32$from_int8", "func(i:Int8 -> Num32)"}, //
517 {"Num32$from_int16", "func(i:Int16 -> Num32)"}, //
518 {"Num32$from_int32", "func(i:Int32, truncate=no -> Num32)"}, //
519 {"Num32$from_int64", "func(i:Int64, truncate=no -> Num32)"}, //
520 {"Num32$from_int", "func(i:Int, truncate=no -> Num32)"}, //
521 {"Num32$from_num64", "func(n:Num -> Num32)"});
522 ADD_CONSTRUCTORS("Path", //
523 {"Path$from_text", "func(text:Text -> Path)"}, //
524 {"Path$escape_path", "func(path:Path -> Path)"}, //
525 {"Int$value_as_text", "func(i:Int -> Path)"});
526 ADD_CONSTRUCTORS("CString", //
527 {"Text$as_c_string", "func(text:Text -> CString)"});
528 #undef ADD_CONSTRUCTORS
530 set_binding(namespace_env(env, "Path"), "from_text",
531 NewFunctionType(PATH_TYPE, {.name = "text", .type = TEXT_TYPE}), Text("Path$from_text"));
534 const char *name, *code, *type_str;
536 {"PRESENT", "PRESENT", "Present"},
537 {"TOMO_VERSION", "TOMO_VERSION_TEXT", "Text"},
538 {"USE_COLOR", "USE_COLOR", "Bool"},
539 {"ask", "ask", "func(prompt:Text, bold=yes, force_tty=yes -> Text?)"},
540 {"at_cleanup", "tomo_at_cleanup", "func(fn:func())"},
541 {"exit", "tomo_exit", "func(message:Text?=none, code=Int32(1) -> Abort)"},
542 {"fail", "fail_text", "func(message:Text -> Abort)"},
543 {"getenv", "getenv_text", "func(name:Text -> Text?)"},
544 {"print", "say", "func(text:Text, newline=yes)"},
545 {"say", "say", "func(text:Text, newline=yes)"},
546 {"setenv", "setenv_text", "func(name:Text, value:Text?)"},
547 {"sleep", "sleep_seconds", "func(seconds:Num)"},
550 for (size_t i = 0; i < sizeof(global_vars) / sizeof(global_vars[0]); i++) {
551 type_t *type = parse_type_string(env, global_vars[i].type_str);
553 compiler_err(NULL, NULL, NULL, "Couldn't parse type string for ", global_vars[i].name, ": ",
554 global_vars[i].type_str);
555 if (type->tag == ClosureType) type = Match(type, ClosureType)->fn;
556 Table$str_set(env->globals, global_vars[i].name,
557 new (binding_t, .type = type, .code = Text$from_str(global_vars[i].code)));
564 env_t *load_module_env(env_t *env, ast_t *ast) {
565 const char *name = ast->file->filename;
566 env_t *cached = Table$str_get(*env->imports, name);
567 if (cached) return cached;
568 env_t *module_env = fresh_scope(env);
569 module_env->code = new (compilation_unit_t);
570 module_env->namespace_bindings = module_env->locals;
571 module_env->id_suffix = get_id_suffix(ast->file->filename);
573 Table$str_set(module_env->imports, name, module_env);
575 ast_list_t *statements = Match(ast, Block)->statements;
576 visit_topologically(statements, (Closure_t){.fn = (void *)prebind_statement, .userdata = module_env});
577 visit_topologically(statements, (Closure_t){.fn = (void *)bind_statement, .userdata = module_env});
582 env_t *fresh_scope(env_t *env) {
583 env_t *scope = new (env_t);
585 scope->locals = new (Table_t, .fallback = env->locals);
589 env_t *with_enum_scope(env_t *env, type_t *t) {
590 while (t && t->tag == OptionalType)
591 t = Match(t, OptionalType)->type;
593 if (t == NULL || t->tag != EnumType) return env;
594 env = fresh_scope(env);
595 env_t *ns_env = Match(t, EnumType)->env;
596 for (tag_t *tag = Match(t, EnumType)->tags; tag; tag = tag->next) {
597 if (get_binding(env, tag->name)) continue;
598 binding_t *b = get_binding(ns_env, tag->name);
600 Table$str_set(env->locals, tag->name, b);
605 env_t *for_scope(env_t *env, ast_t *ast) {
606 DeclareMatch(for_, ast, For);
607 type_t *iter_t = value_type(get_type(env, for_->iter));
608 env_t *scope = fresh_scope(env);
610 switch (iter_t->tag) {
612 type_t *item_t = Match(iter_t, ListType)->item_type;
613 const char *vars[2] = {};
614 int64_t num_vars = 0;
615 for (ast_list_t *var = for_->vars; var; var = var->next) {
616 if (num_vars >= 2) code_err(var->ast, "This is too many variables for this loop");
617 vars[num_vars++] = Match(var->ast, Var)->name;
620 set_binding(scope, vars[0], item_t, Texts("_$", vars[0]));
621 } else if (num_vars == 2) {
622 set_binding(scope, vars[0], INT_TYPE, Texts("_$", vars[0]));
623 set_binding(scope, vars[1], item_t, Texts("_$", vars[1]));
628 const char *vars[2] = {};
629 int64_t num_vars = 0;
630 for (ast_list_t *var = for_->vars; var; var = var->next) {
631 if (num_vars >= 2) code_err(var->ast, "This is too many variables for this loop");
632 vars[num_vars++] = Match(var->ast, Var)->name;
635 type_t *key_t = Match(iter_t, TableType)->key_type;
637 set_binding(scope, vars[0], key_t, Texts("_$", vars[0]));
638 } else if (num_vars == 2) {
639 set_binding(scope, vars[0], key_t, Texts("_$", vars[0]));
640 type_t *value_t = Match(iter_t, TableType)->value_type;
641 set_binding(scope, vars[1], value_t, Texts("_$", vars[1]));
647 if (for_->vars->next) code_err(for_->vars->next->ast, "This is too many variables for this loop");
648 const char *var = Match(for_->vars->ast, Var)->name;
649 set_binding(scope, var, INT_TYPE, Texts("_$", var));
655 __typeof(iter_t->__data.FunctionType) *fn = iter_t->tag == ClosureType
656 ? Match(Match(iter_t, ClosureType)->fn, FunctionType)
657 : Match(iter_t, FunctionType);
660 if (for_->vars->next) code_err(for_->vars->next->ast, "This is too many variables for this loop");
661 const char *var = Match(for_->vars->ast, Var)->name;
662 type_t *non_opt_type = fn->ret->tag == OptionalType ? Match(fn->ret, OptionalType)->type : fn->ret;
663 set_binding(scope, var, non_opt_type, Texts("_$", var));
667 default: code_err(for_->iter, "Iteration is not implemented for type: ", type_to_text(iter_t));
672 env_t *get_namespace_by_type(env_t *env, type_t *t) {
673 t = non_optional(value_type(t));
675 case ListType: return NULL;
676 case TableType: return NULL;
684 binding_t *b = get_binding(env, Text$as_c_string(type_to_text(t)));
685 if (!b) compiler_err(NULL, NULL, NULL, "Couldn't get type namespace: ", Text$as_c_string(type_to_text(t)));
686 return Match(b->type, TypeInfoType)->env;
688 case TextType: return Match(t, TextType)->env;
690 DeclareMatch(struct_, t, StructType);
694 DeclareMatch(enum_, t, EnumType);
698 DeclareMatch(info, t, TypeInfoType);
706 env_t *namespace_env(env_t *env, const char *namespace_name) {
707 binding_t *b = get_binding(env, namespace_name);
708 if (b) return Match(b->type, TypeInfoType)->env;
710 env_t *ns_env = new (env_t);
712 ns_env->locals = new (Table_t, .fallback = env->locals);
713 ns_env->namespace = new (namespace_t, .name = namespace_name, .parent = env->namespace);
714 ns_env->namespace_bindings = ns_env->locals;
718 PUREFUNC binding_t *get_binding(env_t *env, const char *name) {
719 return Table$str_get(*env->locals, name);
722 binding_t *get_namespace_binding(env_t *env, ast_t *self, const char *name) {
723 type_t *self_type = get_type(env, self);
724 if (!self_type) code_err(self, "I couldn't get this type");
725 env_t *ns_env = get_namespace_by_type(env, self_type);
726 return ns_env ? get_binding(ns_env, name) : NULL;
729 PUREFUNC binding_t *get_constructor(env_t *env, type_t *t, arg_ast_t *args, bool allow_underscores) {
730 env_t *type_env = get_namespace_by_type(env, t);
731 if (!type_env) return NULL;
732 List_t constructors = type_env->namespace->constructors;
733 // Prioritize exact matches:
734 call_opts_t options = {.promotion = false, .underscores = allow_underscores};
735 for (int64_t i = (int64_t)constructors.length - 1; i >= 0; i--) {
736 binding_t *constructor = constructors.data + i * constructors.stride;
737 DeclareMatch(fn, constructor->type, FunctionType);
738 if (type_eq(fn->ret, t) && is_valid_call(env, fn->args, args, options)) return constructor;
740 // Fall back to promotion:
741 options.promotion = true;
742 for (int64_t i = (int64_t)constructors.length - 1; i >= 0; i--) {
743 binding_t *constructor = constructors.data + i * constructors.stride;
744 DeclareMatch(fn, constructor->type, FunctionType);
745 if (type_eq(fn->ret, t) && is_valid_call(env, fn->args, args, options)) return constructor;
750 PUREFUNC binding_t *get_metamethod_binding(env_t *env, ast_e tag, ast_t *lhs, ast_t *rhs, type_t *ret) {
751 const char *method_name = binop_info[tag].method_name;
752 if (!method_name) return NULL;
753 binding_t *b = get_namespace_binding(env, lhs, method_name);
754 if (!b || b->type->tag != FunctionType) return NULL;
755 DeclareMatch(fn, b->type, FunctionType);
756 if (!type_eq(fn->ret, ret)) return NULL;
757 arg_ast_t *args = new (arg_ast_t, .value = lhs, .next = new (arg_ast_t, .value = rhs));
758 return is_valid_call(env, fn->args, args, (call_opts_t){.promotion = true}) ? b : NULL;
761 void set_binding(env_t *env, const char *name, type_t *type, Text_t code) {
763 Table$str_set(env->locals, name, new (binding_t, .type = type, .code = code));