1 module zua.compiler.compiler;
2 import zua.compiler.sourcemap;
3 import zua.compiler.utils;
4 import zua.compiler.ir;
5 import zua.vm.engine;
6 import std.uuid;
7 import std.range;
8 
9 private struct Variable {
10 	bool heap;
11 	ulong index;
12 }
13 
14 private class Environment {
15 	private ulong[UUID] vars;
16 	private bool[UUID] closureMap;
17 	private ulong[UUID] upvalues;
18 	private ulong varIndex = 0;
19 	bool hasReturn = false;
20 
21 	this(FunctionExpr func) {
22 		ulong uvIndex = 0;
23 		foreach (id; func.upvalues) {
24 			upvalues[id] = uvIndex;
25 			uvIndex++;
26 		}
27 		foreach (id; func.closed) {
28 			closureMap[id] = true;
29 		}
30 	}
31 
32 	bool isRef(UUID var) {
33 		return (var in closureMap) != null;
34 	}
35 
36 	bool isUpvalue(UUID var) {
37 		return (var in upvalues) != null;
38 	}
39 
40 	void set(Function func, UUID id) {
41 		func.code ~= new MonadInstruction(getSetOp(id), get(id));
42 	}
43 
44 	void get(Function func, UUID id) {
45 		func.code ~= new MonadInstruction(getGetOp(id), get(id));
46 	}
47 
48 	Opcode getSetOp(UUID var) {
49 		if (var in closureMap)
50 			return Opcode.SetRef;
51 		else if (var in upvalues)
52 			return Opcode.SetC;
53 		else
54 			return Opcode.Set;
55 	}
56 
57 	Opcode getGetOp(UUID var) {
58 		if (var in closureMap)
59 			return Opcode.GetRef;
60 		else if (var in upvalues)
61 			return Opcode.GetC;
62 		else
63 			return Opcode.Get;
64 	}
65 
66 	ulong get(UUID var) {
67 		if (var in vars) {
68 			return vars[var];
69 		}
70 		else if (var in upvalues) {
71 			return upvalues[var];
72 		}
73 		else {
74 			vars[var] = varIndex;
75 			varIndex++;
76 			return varIndex - 1;
77 		}
78 	}
79 
80 }
81 
82 private class Compiler {
83 
84 	Function func;
85 	Function[] scopeStack;
86 
87 	Environment env;
88 	Environment[] envStack;
89 
90 	UUID[] breakStack;
91 	UUID[] continueStack;
92 	UUID[] variadicStack;
93 
94 	Indices prevIndices;
95 
96 	void pack(Expr[] tuple) {
97 		foreach (e; tuple) {
98 			if (auto call = cast(CallExpr) e) {
99 				compile(call.base);
100 				if (call.method.isNull) {
101 					maybePack(call.args);
102 					func.code ~= new AtomicInstruction(Opcode.Call);
103 				}
104 				else {
105 					func.code ~= new MonadInstruction(Opcode.NamecallPrep, getString(call.method.get));
106 					maybePack(call.args);
107 					func.code ~= new AtomicInstruction(Opcode.Namecall);
108 				}
109 			}
110 			else {
111 				compile(e);
112 			}
113 		}
114 		func.code ~= new MonadInstruction(Opcode.Pack, tuple.length);
115 	}
116 
117 	void maybePack(Expr expr) {
118 		if (auto call = cast(CallExpr) expr) {
119 			compile(call.base);
120 			if (call.method.isNull) {
121 				maybePack(call.args);
122 				func.code ~= new AtomicInstruction(Opcode.Call);
123 			}
124 			else {
125 				func.code ~= new MonadInstruction(Opcode.NamecallPrep, getString(call.method.get));
126 				maybePack(call.args);
127 				func.code ~= new AtomicInstruction(Opcode.Namecall);
128 			}
129 		}
130 		else {
131 			compile(expr);
132 		}
133 	}
134 
135 	void maybePack(Expr[] tuple) {
136 		if (tuple.length == 1) {
137 			maybePack(tuple[0]);
138 		}
139 		else {
140 			pack(tuple);
141 		}
142 	}
143 
144 	void declare(UUID[] vars, Expr[] values) {
145 		maybePack(values);
146 		func.code ~= new MonadInstruction(Opcode.Unpack, vars.length);
147 		foreach (id; vars) {
148 			if (env.isRef(id))
149 				func.code ~= new MonadInstruction(Opcode.Mkhv, env.get(id));
150 			env.set(func, id);
151 		}
152 	}
153 
154 	ulong getString(string str) {
155 		auto res = func.data.length;
156 		func.data ~= str;
157 		return res;
158 	}
159 
160 	void pushString(string str) {
161 		func.code ~= new MonadInstruction(Opcode.LdStr, getString(str));
162 	}
163 
164 	Indices indexNode(IRNode node) {
165 		// import std.stdio : writeln;
166 
167 		// writeln(node.start.index);
168 		// writeln(node.end.index + node.end.rawValue.length);
169 		return new Indices(node.start.index, node.end.index + node.end.rawValue.length);
170 	}
171 
172 	/** Compile an IR node */
173 	void compile(Stat stat) {
174 		auto save = prevIndices; // @suppress(dscanner.suspicious.unmodified)
175 		prevIndices = indexNode(stat);
176 		func.code ~= prevIndices;
177 		if (auto s = cast(AssignStat) stat)
178 			compilev(s);
179 		else if (auto s = cast(ExprStat) stat)
180 			compilev(s);
181 		else if (auto s = cast(Block) stat)
182 			compilev(s);
183 		else if (auto s = cast(DeclarationStat) stat)
184 			compilev(s);
185 		else if (auto s = cast(WhileStat) stat)
186 			compilev(s);
187 		else if (auto s = cast(RepeatStat) stat)
188 			compilev(s);
189 		else if (auto s = cast(IfStat) stat)
190 			compilev(s);
191 		else if (auto s = cast(NumericForStat) stat)
192 			compilev(s);
193 		else if (auto s = cast(ForeachStat) stat)
194 			compilev(s);
195 		else if (auto s = cast(ReturnStat) stat)
196 			compilev(s);
197 		else if (auto s = cast(AtomicStat) stat)
198 			compilev(s);
199 		else
200 			assert(0);
201 		prevIndices = save;
202 		func.code ~= prevIndices;
203 	}
204 
205 	/// ditto
206 	void compile(Expr expr) {
207 		auto save = prevIndices; // @suppress(dscanner.suspicious.unmodified)
208 		prevIndices = indexNode(expr);
209 		func.code ~= prevIndices;
210 		if (auto e = cast(AtomicExpr) expr)
211 			compilev(e);
212 		else if (auto e = cast(NumberExpr) expr)
213 			compilev(e);
214 		else if (auto e = cast(StringExpr) expr)
215 			compilev(e);
216 		else if (auto e = cast(FunctionExpr) expr)
217 			compilev(e);
218 		else if (auto e = cast(BinaryExpr) expr)
219 			compilev(e);
220 		else if (auto e = cast(UnaryExpr) expr)
221 			compilev(e);
222 		else if (auto e = cast(TableExpr) expr)
223 			compilev(e);
224 		else if (auto e = cast(LvalueExpr) expr)
225 			compile(e);
226 		else if (auto e = cast(BracketExpr) expr)
227 			compilev(e);
228 		else if (auto e = cast(CallExpr) expr)
229 			compilev(e);
230 		else
231 			assert(0);
232 		prevIndices = save;
233 		func.code ~= prevIndices;
234 	}
235 
236 	/// ditto
237 	void compile(LvalueExpr expr) {
238 		auto save = prevIndices; // @suppress(dscanner.suspicious.unmodified)
239 		prevIndices = indexNode(expr);
240 		func.code ~= prevIndices;
241 		if (auto e = cast(GlobalExpr) expr)
242 			compilev(e);
243 		else if (auto e = cast(LocalExpr) expr)
244 			compilev(e);
245 		else if (auto e = cast(UpvalueExpr) expr)
246 			compilev(e);
247 		else if (auto e = cast(IndexExpr) expr)
248 			compilev(e);
249 		else
250 			assert(0);
251 		prevIndices = save;
252 		func.code ~= prevIndices;
253 	}
254 
255 	/// ditto
256 	void compilev(Expr[] tuple) {
257 		if (tuple.length == 0) {
258 			func.code ~= new AtomicInstruction(Opcode.LdNil);
259 		}
260 		else {
261 			compile(tuple[0]);
262 			foreach (e; tuple[1 .. $]) {
263 				compile(e);
264 				func.code ~= new AtomicInstruction(Opcode.Drop);
265 			}
266 		}
267 	}
268 
269 	/// ditto
270 	void compilev(AssignStat stat) {
271 		if (stat.keys.length == 1) {
272 			auto key = stat.keys[0];
273 			if (auto e = cast(IndexExpr) key) {
274 				compile(e.base);
275 				compile(e.key);
276 			}
277 			else if (auto e = cast(GlobalExpr) key) {
278 				func.code ~= new AtomicInstruction(Opcode.Getfenv);
279 				pushString(e.name);
280 			}
281 			compile(stat.values[0]);
282 			if (auto e = cast(LocalExpr) key) {
283 				env.set(func, e.id);
284 			}
285 			else if (auto e = cast(UpvalueExpr) key) {
286 				env.set(func, e.id);
287 			}
288 			else {
289 				func.code ~= new AtomicInstruction(Opcode.SetTable);
290 			}
291 			return;
292 		}
293 
294 		ulong dropCount = 0;
295 		foreach (key; stat.keys) {
296 			if (auto e = cast(IndexExpr) key) {
297 				compile(e.base);
298 				compile(e.key);
299 				dropCount += 2;
300 			}
301 			else if (!cast(LocalExpr) key && !cast(UpvalueExpr) key && !cast(GlobalExpr) key)
302 				assert(0);
303 		}
304 		maybePack(stat.values);
305 		// a, b, c = d, e, f
306 		func.code ~= new MonadInstruction(Opcode.UnpackRev, stat.keys.length);
307 		// stack: f, e, d <top>
308 		ulong offset = stat.keys.length;
309 		foreach (key; stat.keys.retro) {
310 			if (auto e = cast(GlobalExpr) key) {
311 				pushString(e.name);
312 				func.code ~= new AtomicInstruction(Opcode.Getfenv);
313 				func.code ~= new AtomicInstruction(Opcode.SetTableRev);
314 			}
315 			else if (auto e = cast(LocalExpr) key) {
316 				env.set(func, e.id);
317 			}
318 			else if (auto e = cast(UpvalueExpr) key) {
319 				env.set(func, e.id);
320 			}
321 			else if (auto e = cast(IndexExpr) key) {
322 				func.code ~= new MonadInstruction(Opcode.Introspect, offset);
323 				offset++;
324 				func.code ~= new MonadInstruction(Opcode.Introspect, offset + 1);
325 				offset++;
326 				func.code ~= new AtomicInstruction(Opcode.SetTableRev);
327 				offset -= 3;
328 				offset += 2;
329 				offset++; // to make up for decrementing right after
330 			}
331 			else
332 				assert(0);
333 
334 			offset--;
335 		}
336 		func.code ~= new MonadInstruction(Opcode.DropTuple, dropCount);
337 	}
338 
339 	/// ditto
340 	void compilev(ExprStat stat) {
341 		compile(stat.expr);
342 		func.code ~= new AtomicInstruction(Opcode.Drop);
343 	}
344 
345 	/// ditto
346 	void compilev(Block stat) {
347 		foreach (s; stat.body)
348 			compile(s);
349 	}
350 
351 	/// ditto
352 	void compilev(DeclarationStat stat) {
353 		declare(stat.keys, stat.values);
354 	}
355 
356 	/// ditto
357 	void compilev(WhileStat stat) {
358 		const UUID start = randomUUID();
359 		const UUID skip = randomUUID();
360 		func.code ~= new Label(start);
361 		compile(stat.cond);
362 		func.code ~= new MonadInstruction(Opcode.JmpF, skip);
363 		compile(stat.body);
364 		func.code ~= new MonadInstruction(Opcode.Jmp, start);
365 		func.code ~= new Label(skip);
366 	}
367 
368 	/// ditto
369 	void compilev(RepeatStat stat) {
370 		const UUID start = randomUUID();
371 		func.code ~= new Label(start);
372 		compile(stat.body);
373 		compile(stat.endCond);
374 		func.code ~= new MonadInstruction(Opcode.JmpF, start);
375 	}
376 
377 	/// ditto
378 	void compilev(IfStat stat) {
379 		const UUID finished = randomUUID();
380 		foreach (entry; stat.entries) {
381 			compile(entry.cond);
382 			const UUID skip = randomUUID();
383 			func.code ~= new MonadInstruction(Opcode.JmpF, skip);
384 			compile(entry.body);
385 			func.code ~= new MonadInstruction(Opcode.Jmp, finished);
386 			func.code ~= new Label(skip);
387 		}
388 		if (!stat.elseBody.isNull)
389 			compile(stat.elseBody.get);
390 		func.code ~= new Label(finished);
391 	}
392 
393 	/// ditto
394 	void compilev(NumericForStat stat) {
395 		const UUID endLabel = randomUUID();
396 		const UUID breakLabel = randomUUID();
397 		const UUID continueLabel = randomUUID();
398 		compile(stat.low);
399 		compile(stat.high);
400 		if (stat.step.isNull) {
401 			func.code ~= new MonadInstruction(Opcode.LdNum, 1.0);
402 		}
403 		else {
404 			compile(stat.step.get);
405 			func.code ~= new AtomicInstruction(Opcode.Dup);
406 			func.code ~= new MonadInstruction(Opcode.LdNum, 0.0);
407 			func.code ~= new AtomicInstruction(Opcode.Eq);
408 			func.code ~= new MonadInstruction(Opcode.JmpT, endLabel);
409 		}
410 		const bool isRef = env.isRef(stat.var);
411 		const UUID iteratorVar = randomUUID();
412 		if (isRef) {
413 			func.code ~= new MonadInstruction(Opcode.ForPrep, env.get(iteratorVar));
414 		}
415 		else {
416 			func.code ~= new MonadInstruction(Opcode.ForPrep, env.get(stat.var));
417 		}
418 		const UUID jumpBack = randomUUID();
419 		func.code ~= new Label(jumpBack);
420 		func.code ~= new MonadInstruction(Opcode.Loop, breakLabel);
421 		const UUID start = randomUUID();
422 		func.code ~= new Label(start);
423 		if (isRef) {
424 			func.code ~= new MonadInstruction(Opcode.Mkhv, env.get(stat.var));
425 			func.code ~= new MonadInstruction(Opcode.Get, env.get(iteratorVar));
426 			func.code ~= new MonadInstruction(Opcode.SetRef, env.get(stat.var));
427 		}
428 		breakStack ~= breakLabel;
429 		continueStack ~= continueLabel;
430 		compile(stat.body);
431 		breakStack = breakStack[0 .. $ - 1];
432 		continueStack = continueStack[0 .. $ - 1];
433 		func.code ~= new Label(continueLabel);
434 		func.code ~= new MonadInstruction(Opcode.Jmp, jumpBack);
435 		func.code ~= new Label(breakLabel);
436 		func.code ~= new AtomicInstruction(Opcode.DropLoop);
437 		func.code ~= new Label(endLabel);
438 	}
439 
440 	/// ditto
441 	void compilev(ForeachStat stat) {
442 		const UUID fvar = randomUUID();
443 		const UUID svar = randomUUID();
444 		const UUID var = randomUUID();
445 		maybePack(stat.iter);
446 		func.code ~= new MonadInstruction(Opcode.Unpack, 3);
447 		env.set(func, fvar);
448 		env.set(func, svar);
449 		env.set(func, var);
450 
451 		const UUID breakLabel = randomUUID();
452 		const UUID continueLabel = randomUUID();
453 		
454 		func.code ~= new Label(continueLabel);
455 		env.get(func, fvar);
456 		env.get(func, svar);
457 		env.get(func, var);
458 		func.code ~= new MonadInstruction(Opcode.Pack, 2);
459 		func.code ~= new AtomicInstruction(Opcode.Call);
460 		func.code ~= new MonadInstruction(Opcode.Unpack, stat.vars.length);
461 		env.set(func, var);
462 		env.get(func, var);
463 		foreach (v; stat.vars) {
464 			if (env.isRef(v)) {
465 				func.code ~= new MonadInstruction(Opcode.Mkhv, env.get(v));
466 			}
467 			env.set(func, v);
468 		}
469 		env.get(func, var);
470 		func.code ~= new MonadInstruction(Opcode.JmpNil, breakLabel);
471 		breakStack ~= breakLabel;
472 		continueStack ~= continueLabel;
473 		compile(stat.body);
474 		breakStack = breakStack[0 .. $ - 1];
475 		continueStack = continueStack[0 .. $ - 1];
476 		func.code ~= new MonadInstruction(Opcode.Jmp, continueLabel);
477 		func.code ~= new Label(breakLabel);
478 	}
479 
480 	/// ditto
481 	void compilev(ReturnStat stat) {
482 		env.hasReturn = true;
483 		maybePack(stat.values);
484 		func.code ~= new AtomicInstruction(Opcode.Ret);
485 	}
486 
487 	/// ditto
488 	void compilev(AtomicStat stat) {
489 		switch (stat.type) {
490 		case AtomicStatType.Break:
491 			func.code ~= new MonadInstruction(Opcode.Jmp, breakStack[$ - 1]);
492 			break;
493 		default:
494 			assert(0);
495 		}
496 	}
497 
498 	/// ditto
499 	void compilev(GlobalExpr expr) {
500 		func.code ~= new AtomicInstruction(Opcode.Getfenv);
501 		pushString(expr.name);
502 		func.code ~= new AtomicInstruction(Opcode.GetTable);
503 	}
504 
505 	/// ditto
506 	void compilev(LocalExpr expr) {
507 		const ulong i = env.get(expr.id);
508 		func.code ~= new MonadInstruction(env.isRef(expr.id) ? Opcode.GetRef
509 				: Opcode.Get, OperandValue(i));
510 	}
511 
512 	/// ditto
513 	void compilev(UpvalueExpr expr) {
514 		const ulong i = env.get(expr.id);
515 		func.code ~= new MonadInstruction(Opcode.GetC, OperandValue(i));
516 	}
517 
518 	/// ditto
519 	void compilev(IndexExpr expr) {
520 		compile(expr.base);
521 		compile(expr.key);
522 		func.code ~= new AtomicInstruction(Opcode.GetTable);
523 	}
524 
525 	/// ditto
526 	void compilev(BracketExpr expr) {
527 		compile(expr.expr);
528 	}
529 
530 	/// ditto
531 	void compilev(CallExpr expr) {
532 		compile(expr.base);
533 		if (expr.method.isNull) {
534 			maybePack(expr.args);
535 			func.code ~= new AtomicInstruction(Opcode.Call);
536 		}
537 		else {
538 			func.code ~= new MonadInstruction(Opcode.NamecallPrep, getString(expr.method.get));
539 			maybePack(expr.args);
540 			func.code ~= new AtomicInstruction(Opcode.Namecall);
541 		}
542 		func.code ~= new MonadInstruction(Opcode.Unpack, 1);
543 	}
544 
545 	/// ditto
546 	void compilev(AtomicExpr expr) {
547 		switch (expr.type) {
548 		case AtomicExprType.Nil:
549 			func.code ~= new AtomicInstruction(Opcode.LdNil);
550 			break;
551 		case AtomicExprType.False:
552 			func.code ~= new AtomicInstruction(Opcode.LdFalse);
553 			break;
554 		case AtomicExprType.True:
555 			func.code ~= new AtomicInstruction(Opcode.LdTrue);
556 			break;
557 		case AtomicExprType.VariadicTuple:
558 			env.get(func, variadicStack[$ - 1]);
559 			break;
560 		default:
561 			assert(0);
562 		}
563 	}
564 
565 	/// ditto
566 	void compilev(NumberExpr expr) {
567 		func.code ~= new MonadInstruction(Opcode.LdNum, OperandValue(expr.value));
568 	}
569 
570 	/// ditto
571 	void compilev(StringExpr expr) {
572 		pushString(expr.value);
573 	}
574 
575 	/// ditto
576 	void compilev(FunctionExpr expr, bool toplevel = false) {
577 		ulong[] upvalues;
578 
579 		foreach (id; expr.upvalues) {
580 			assert(env.isRef(id));
581 			if (env.isUpvalue(id)) {
582 				upvalues ~= ~env.get(id); // complements are a sort of "magic number"
583 			}
584 			else {
585 				upvalues ~= env.get(id);
586 			}
587 		}
588 
589 		auto child = new Function;
590 
591 		if (!toplevel) {
592 			func.code ~= new LdFun(func.functions.length, upvalues);
593 			func.functions ~= child;
594 		}
595 
596 		scopeStack ~= func;
597 		envStack ~= env;
598 
599 		func = child;
600 		child.upvalues = expr.upvalues.length;
601 		// child.locals = expr.localsCount;
602 
603 		env = new Environment(expr);
604 
605 		if (expr.variadic) {
606 			UUID variadic = randomUUID();
607 			variadicStack.assumeSafeAppend ~= variadic;
608 			func.code ~= new AtomicInstruction(Opcode.LdArgs);
609 			if (expr.args.length > 0)
610 				func.code ~= new MonadInstruction(Opcode.UnpackD, expr.args.length);
611 			env.set(func, variadic);
612 			foreach (arg; expr.args) {
613 				env.set(func, arg);
614 			}
615 		}
616 		else {
617 			variadicStack.assumeSafeAppend ~= UUID();
618 			if (expr.args.length > 0) {
619 				func.code ~= new AtomicInstruction(Opcode.LdArgs);
620 				func.code ~= new MonadInstruction(Opcode.Unpack, expr.args.length);
621 				foreach (arg; expr.args) {
622 					env.set(func, arg);
623 				}
624 			}
625 		}
626 
627 		compile(expr.body);
628 
629 		variadicStack = variadicStack[0 .. $ - 1];
630 
631 		child.locals = env.vars.length;
632 
633 		if (!env.hasReturn) {
634 			func.code ~= new MonadInstruction(Opcode.Pack, 0);
635 			func.code ~= new AtomicInstruction(Opcode.Ret);
636 		}
637 
638 		if (!toplevel) {
639 			func = scopeStack[$ - 1];
640 		}
641 		scopeStack = scopeStack[0 .. $ - 1];
642 
643 		env = envStack[$ - 1];
644 		envStack = envStack[0 .. $ - 1];
645 	}
646 
647 	/// ditto
648 	void compilev(BinaryExpr expr) {
649 		if (expr.op == BinaryOperation.And) {
650 			compile(expr.lhs);
651 			func.code ~= new AtomicInstruction(Opcode.Dup);
652 			const UUID jmp = randomUUID();
653 			func.code ~= new MonadInstruction(Opcode.JmpF, jmp);
654 			func.code ~= new AtomicInstruction(Opcode.Drop);
655 			compile(expr.rhs);
656 			func.code ~= new Label(jmp);
657 		}
658 		else if (expr.op == BinaryOperation.Or) {
659 			compile(expr.lhs);
660 			func.code ~= new AtomicInstruction(Opcode.Dup);
661 			const UUID jmp = randomUUID();
662 			func.code ~= new MonadInstruction(Opcode.JmpT, jmp);
663 			func.code ~= new AtomicInstruction(Opcode.Drop);
664 			compile(expr.rhs);
665 			func.code ~= new Label(jmp);
666 		}
667 		else {
668 			Opcode op;
669 			switch (expr.op) {
670 			case BinaryOperation.Add:
671 				op = Opcode.Add;
672 				break;
673 			case BinaryOperation.Sub:
674 				op = Opcode.Sub;
675 				break;
676 			case BinaryOperation.Mul:
677 				op = Opcode.Mul;
678 				break;
679 			case BinaryOperation.Div:
680 				op = Opcode.Div;
681 				break;
682 			case BinaryOperation.Exp:
683 				op = Opcode.Exp;
684 				break;
685 			case BinaryOperation.Mod:
686 				op = Opcode.Mod;
687 				break;
688 			case BinaryOperation.Concat:
689 				op = Opcode.Concat;
690 				break;
691 			case BinaryOperation.CmpLt:
692 				op = Opcode.Lt;
693 				break;
694 			case BinaryOperation.CmpLe:
695 				op = Opcode.Le;
696 				break;
697 			case BinaryOperation.CmpGt:
698 				op = Opcode.Gt;
699 				break;
700 			case BinaryOperation.CmpGe:
701 				op = Opcode.Ge;
702 				break;
703 			case BinaryOperation.CmpEq:
704 				op = Opcode.Eq;
705 				break;
706 			case BinaryOperation.CmpNe:
707 				op = Opcode.Ne;
708 				break;
709 			default:
710 				assert(0);
711 			}
712 			compile(expr.lhs);
713 			compile(expr.rhs);
714 			func.code ~= new AtomicInstruction(op);
715 		}
716 	}
717 
718 	/// ditto
719 	void compilev(UnaryExpr expr) {
720 		Opcode op;
721 		switch (expr.op) {
722 		case UnaryOperation.Negate:
723 			op = Opcode.Unm;
724 			break;
725 		case UnaryOperation.Not:
726 			op = Opcode.Not;
727 			break;
728 		case UnaryOperation.Length:
729 			op = Opcode.Len;
730 			break;
731 		default:
732 			assert(0);
733 		}
734 		compile(expr.expr);
735 		func.code ~= new AtomicInstruction(op);
736 	}
737 
738 	/// ditto
739 	void compilev(TableExpr expr) {
740 		func.code ~= new AtomicInstruction(Opcode.NewTable);
741 		// auto dupIns = new MonadInstruction(Opcode.DupN, 1);
742 		// func.code ~= dupIns;
743 		Expr[] array;
744 		foreach (field; expr.fields) {
745 			if (TableField* f = field.peek!TableField) {
746 				func.code ~= new AtomicInstruction(Opcode.Dup);
747 				// dupIns.value.peek!OperandValue.i++;
748 				compile(f.key);
749 				compile(f.value);
750 				func.code ~= new AtomicInstruction(Opcode.SetTable);
751 			}
752 			else {
753 				array ~= *field.peek!Expr;
754 			}
755 		}
756 		func.code ~= new AtomicInstruction(Opcode.Dup);
757 		foreach (field; array) {
758 			maybePack(field);
759 		}
760 		// if (array.length > 0) {
761 		func.code ~= new MonadInstruction(Opcode.SetArray, array.length);
762 		// }
763 		// else {
764 		// 	dupIns.value.peek!OperandValue.i--;
765 		// }
766 	}
767 
768 	immutable(ubyte)[] buffer(Indices toplevelIndices, SourceMap map) {
769 		return func.serialize(toplevelIndices, map);
770 	}
771 
772 }
773 
774 /** Compile a top-level function */
775 immutable(ubyte)[] compile(SourceMap map, FunctionExpr func) {
776 	Compiler compiler = new Compiler;
777 	compiler.compilev(func, true);
778 	return compiler.buffer(new Indices(func.start.index, func.end.index + func.end.rawValue.length), map);
779 }