1/*
2 * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining
5 * a copy of this software and associated documentation files (the
6 * "Software"), to deal in the Software without restriction, including
7 * without limitation the rights to use, copy, modify, merge, publish,
8 * distribute, sublicense, and/or sell copies of the Software, and to
9 * permit persons to whom the Software is furnished to do so, subject to
10 * the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be
13 * included in all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
19 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
20 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 * SOFTWARE.
23 */
24
25using System;
26using System.Collections.Generic;
27using System.IO;
28using System.Reflection;
29using System.Text;
30
31/*
32 * This is the main compiler class.
33 */
34
35public class T0Comp {
36
37	/*
38	 * Command-line entry point.
39	 */
40	public static void Main(string[] args)
41	{
42		try {
43			List<string> r = new List<string>();
44			string outBase = null;
45			List<string> entryPoints = new List<string>();
46			string coreRun = null;
47			bool flow = true;
48			int dsLim = 32;
49			int rsLim = 32;
50			for (int i = 0; i < args.Length; i ++) {
51				string a = args[i];
52				if (!a.StartsWith("-")) {
53					r.Add(a);
54					continue;
55				}
56				if (a == "--") {
57					for (;;) {
58						if (++ i >= args.Length) {
59							break;
60						}
61						r.Add(args[i]);
62					}
63					break;
64				}
65				while (a.StartsWith("-")) {
66					a = a.Substring(1);
67				}
68				int j = a.IndexOf('=');
69				string pname;
70				string pval, pval2;
71				if (j < 0) {
72					pname = a.ToLowerInvariant();
73					pval = null;
74					pval2 = (i + 1) < args.Length
75						? args[i + 1] : null;
76				} else {
77					pname = a.Substring(0, j).Trim()
78						.ToLowerInvariant();
79					pval = a.Substring(j + 1);
80					pval2 = null;
81				}
82				switch (pname) {
83				case "o":
84				case "out":
85					if (pval == null) {
86						if (pval2 == null) {
87							Usage();
88						}
89						i ++;
90						pval = pval2;
91					}
92					if (outBase != null) {
93						Usage();
94					}
95					outBase = pval;
96					break;
97				case "r":
98				case "run":
99					if (pval == null) {
100						if (pval2 == null) {
101							Usage();
102						}
103						i ++;
104						pval = pval2;
105					}
106					coreRun = pval;
107					break;
108				case "m":
109				case "main":
110					if (pval == null) {
111						if (pval2 == null) {
112							Usage();
113						}
114						i ++;
115						pval = pval2;
116					}
117					foreach (string ep in pval.Split(',')) {
118						string epz = ep.Trim();
119						if (epz.Length > 0) {
120							entryPoints.Add(epz);
121						}
122					}
123					break;
124				case "nf":
125				case "noflow":
126					flow = false;
127					break;
128				default:
129					Usage();
130					break;
131				}
132			}
133			if (r.Count == 0) {
134				Usage();
135			}
136			if (outBase == null) {
137				outBase = "t0out";
138			}
139			if (entryPoints.Count == 0) {
140				entryPoints.Add("main");
141			}
142			if (coreRun == null) {
143				coreRun = outBase;
144			}
145			T0Comp tc = new T0Comp();
146			tc.enableFlowAnalysis = flow;
147			tc.dsLimit = dsLim;
148			tc.rsLimit = rsLim;
149			using (TextReader tr = new StreamReader(
150				Assembly.GetExecutingAssembly()
151				.GetManifestResourceStream("t0-kernel")))
152			{
153				tc.ProcessInput(tr);
154			}
155			foreach (string a in r) {
156				Console.WriteLine("[{0}]", a);
157				using (TextReader tr = File.OpenText(a)) {
158					tc.ProcessInput(tr);
159				}
160			}
161			tc.Generate(outBase, coreRun, entryPoints.ToArray());
162		} catch (Exception e) {
163			Console.WriteLine(e.ToString());
164			Environment.Exit(1);
165		}
166	}
167
168	static void Usage()
169	{
170		Console.WriteLine(
171"usage: T0Comp.exe [ options... ] file...");
172		Console.WriteLine(
173"options:");
174		Console.WriteLine(
175"   -o file    use 'file' as base for output file name (default: 't0out')");
176		Console.WriteLine(
177"   -r name    use 'name' as base for run function (default: same as output)");
178		Console.WriteLine(
179"   -m name[,name...]");
180		Console.WriteLine(
181"              define entry point(s)");
182		Console.WriteLine(
183"   -nf        disable flow analysis");
184		Environment.Exit(1);
185	}
186
187	/*
188	 * If 'delayedChar' is Int32.MinValue then there is no delayed
189	 * character.
190	 * If 'delayedChar' equals x >= 0 then there is one delayed
191	 * character of value x.
192	 * If 'delayedChar' equals y < 0 then there are two delayed
193	 * characters, a newline (U+000A) followed by character of
194	 * value -(y+1).
195	 */
196	TextReader currentInput;
197	int delayedChar;
198
199	/*
200	 * Common StringBuilder used to parse tokens; it is reused for
201	 * each new token.
202	 */
203	StringBuilder tokenBuilder;
204
205	/*
206	 * There may be a delayed token in some cases.
207	 */
208	String delayedToken;
209
210	/*
211	 * Defined words are referenced by name in this map. Names are
212	 * string-sensitive; for better reproducibility, the map is sorted
213	 * (ordinal order).
214	 */
215	IDictionary<string, Word> words;
216
217	/*
218	 * Last defined word is also referenced in 'lastWord'. This is
219	 * used by 'immediate'.
220	 */
221	Word lastWord;
222
223	/*
224	 * When compiling, this builder is used. A stack saves other
225	 * builders in case of nested definition.
226	 */
227	WordBuilder wordBuilder;
228	Stack<WordBuilder> savedWordBuilders;
229
230	/*
231	 * C code defined for words is kept in this map, by word name.
232	 */
233	IDictionary<string, string> allCCode;
234
235	/*
236	 * 'compiling' is true when compiling tokens to a word, false
237	 * when interpreting them.
238	 */
239	bool compiling;
240
241	/*
242	 * 'quitRunLoop' is set to true to trigger exit of the
243	 * interpretation loop when the end of the current input file
244	 * is reached.
245	 */
246	bool quitRunLoop;
247
248	/*
249	 * 'extraCode' is for C code that is to be added as preamble to
250	 * the C output.
251	 */
252	List<string> extraCode;
253
254	/*
255	 * 'extraCodeDefer' is for C code that is to be added in the C
256	 * output _after_ the data and code blocks.
257	 */
258	List<string> extraCodeDefer;
259
260	/*
261	 * 'dataBlock' is the data block in which constant data bytes
262	 * are accumulated.
263	 */
264	ConstData dataBlock;
265
266	/*
267	 * Counter for blocks of constant data.
268	 */
269	long currentBlobID;
270
271	/*
272	 * Flow analysis enable flag.
273	 */
274	bool enableFlowAnalysis;
275
276	/*
277	 * Data stack size limit.
278	 */
279	int dsLimit;
280
281	/*
282	 * Return stack size limit.
283	 */
284	int rsLimit;
285
286	T0Comp()
287	{
288		tokenBuilder = new StringBuilder();
289		words = new SortedDictionary<string, Word>(
290			StringComparer.Ordinal);
291		savedWordBuilders = new Stack<WordBuilder>();
292		allCCode = new SortedDictionary<string, string>(
293			StringComparer.Ordinal);
294		compiling = false;
295		extraCode = new List<string>();
296		extraCodeDefer = new List<string>();
297		enableFlowAnalysis = true;
298
299		/*
300		 * Native words are predefined and implemented only with
301		 * native code. Some may be part of the generated output,
302		 * if C code is set for them.
303		 */
304
305		/*
306		 * add-cc:
307		 * Parses next token as a word name, then a C code snippet.
308		 * Sets the C code for that word.
309		 */
310		AddNative("add-cc:", false, SType.BLANK, cpu => {
311			string tt = Next();
312			if (tt == null) {
313				throw new Exception(
314					"EOF reached (missing name)");
315			}
316			if (allCCode.ContainsKey(tt)) {
317				throw new Exception(
318					"C code already set for: " + tt);
319			}
320			allCCode[tt] = ParseCCode();
321		});
322
323		/*
324		 * cc:
325		 * Parses next token as a word name, then a C code snippet.
326		 * A new word is defined, that throws an exception when
327		 * invoked during compilation. The C code is set for that
328		 * new word.
329		 */
330		AddNative("cc:", false, SType.BLANK, cpu => {
331			string tt = Next();
332			if (tt == null) {
333				throw new Exception(
334					"EOF reached (missing name)");
335			}
336			Word w = AddNative(tt, false, cpu2 => {
337				throw new Exception(
338					"C-only word: " + tt);
339			});
340			if (allCCode.ContainsKey(tt)) {
341				throw new Exception(
342					"C code already set for: " + tt);
343			}
344			SType stackEffect;
345			allCCode[tt] = ParseCCode(out stackEffect);
346			w.StackEffect = stackEffect;
347		});
348
349		/*
350		 * preamble
351		 * Parses a C code snippet, then adds it to the generated
352		 * output preamble.
353		 */
354		AddNative("preamble", false, SType.BLANK, cpu => {
355			extraCode.Add(ParseCCode());
356		});
357
358		/*
359		 * postamble
360		 * Parses a C code snippet, then adds it to the generated
361		 * output after the data and code blocks.
362		 */
363		AddNative("postamble", false, SType.BLANK, cpu => {
364			extraCodeDefer.Add(ParseCCode());
365		});
366
367		/*
368		 * make-CX
369		 * Expects two integers and a string, and makes a
370		 * constant that stands for the string as a C constant
371		 * expression. The two integers are the expected range
372		 * (min-max, inclusive).
373		 */
374		AddNative("make-CX", false, new SType(3, 1), cpu => {
375			TValue c = cpu.Pop();
376			if (!(c.ptr is TPointerBlob)) {
377				throw new Exception(string.Format(
378					"'{0}' is not a string", c));
379			}
380			int max = cpu.Pop();
381			int min = cpu.Pop();
382			TValue tv = new TValue(0, new TPointerExpr(
383				c.ToString(), min, max));
384			cpu.Push(tv);
385		});
386
387		/*
388		 * CX  (immediate)
389		 * Parses two integer constants, then a C code snippet.
390		 * It then pushes on the stack, or compiles to the
391		 * current word, a value consisting of the given C
392		 * expression; the two integers indicate the expected
393		 * range (min-max, inclusive) of the C expression when
394		 * evaluated.
395		 */
396		AddNative("CX", true, cpu => {
397			string tt = Next();
398			if (tt == null) {
399				throw new Exception(
400					"EOF reached (missing min value)");
401			}
402			int min = ParseInteger(tt);
403			tt = Next();
404			if (tt == null) {
405				throw new Exception(
406					"EOF reached (missing max value)");
407			}
408			int max = ParseInteger(tt);
409			if (max < min) {
410				throw new Exception("min/max in wrong order");
411			}
412			TValue tv = new TValue(0, new TPointerExpr(
413				ParseCCode().Trim(), min, max));
414			if (compiling) {
415				wordBuilder.Literal(tv);
416			} else {
417				cpu.Push(tv);
418			}
419		});
420
421		/*
422		 * co
423		 * Interrupt the current execution. This implements
424		 * coroutines. It cannot be invoked during compilation.
425		 */
426		AddNative("co", false, SType.BLANK, cpu => {
427			throw new Exception("No coroutine in compile mode");
428		});
429
430		/*
431		 * :
432		 * Parses next token as word name. It begins definition
433		 * of that word, setting it as current target for
434		 * word building. Any previously opened word is saved
435		 * and will become available again as a target when that
436		 * new word is finished building.
437		 */
438		AddNative(":", false, cpu => {
439			string tt = Next();
440			if (tt == null) {
441				throw new Exception(
442					"EOF reached (missing name)");
443			}
444			if (compiling) {
445				savedWordBuilders.Push(wordBuilder);
446			} else {
447				compiling = true;
448			}
449			wordBuilder = new WordBuilder(this, tt);
450			tt = Next();
451			if (tt == null) {
452				throw new Exception(
453					"EOF reached (while compiling)");
454			}
455			if (tt == "(") {
456				SType stackEffect = ParseStackEffectNF();
457				if (!stackEffect.IsKnown) {
458					throw new Exception(
459						"Invalid stack effect syntax");
460				}
461				wordBuilder.StackEffect = stackEffect;
462			} else {
463				delayedToken = tt;
464			}
465		});
466
467		/*
468		 * Pops a string as word name, and two integers as stack
469		 * effect. It begins definition of that word, setting it
470		 * as current target for word building. Any previously
471		 * opened word is saved and will become available again as
472		 * a target when that new word is finished building.
473		 *
474		 * Stack effect is the pair 'din dout'. If din is negative,
475		 * then the stack effect is "unknown". If din is nonnegative
476		 * but dout is negative, then the word is reputed never to
477		 * return.
478		 */
479		AddNative("define-word", false, cpu => {
480			int dout = cpu.Pop();
481			int din = cpu.Pop();
482			TValue s = cpu.Pop();
483			if (!(s.ptr is TPointerBlob)) {
484				throw new Exception(string.Format(
485					"Not a string: '{0}'", s));
486			}
487			string tt = s.ToString();
488			if (compiling) {
489				savedWordBuilders.Push(wordBuilder);
490			} else {
491				compiling = true;
492			}
493			wordBuilder = new WordBuilder(this, tt);
494			wordBuilder.StackEffect = new SType(din, dout);
495		});
496
497		/*
498		 * ;  (immediate)
499		 * Ends current word. The current word is registered under
500		 * its name, and the previously opened word (if any) becomes
501		 * again the building target.
502		 */
503		AddNative(";", true, cpu => {
504			if (!compiling) {
505				throw new Exception("Not compiling");
506			}
507			Word w = wordBuilder.Build();
508			string name = w.Name;
509			if (words.ContainsKey(name)) {
510				throw new Exception(
511					"Word already defined: " + name);
512			}
513			words[name] = w;
514			lastWord = w;
515			if (savedWordBuilders.Count > 0) {
516				wordBuilder = savedWordBuilders.Pop();
517			} else {
518				wordBuilder = null;
519				compiling = false;
520			}
521		});
522
523		/*
524		 * immediate
525		 * Sets the last defined word as immediate.
526		 */
527		AddNative("immediate", false, cpu => {
528			if (lastWord == null) {
529				throw new Exception("No word defined yet");
530			}
531			lastWord.Immediate = true;
532		});
533
534		/*
535		 * literal  (immediate)
536		 * Pops the current TOS value, and add in the current word
537		 * the action of pushing that value. This cannot be used
538		 * when no word is being built.
539		 */
540		WordNative wliteral = AddNative("literal", true, cpu => {
541			CheckCompiling();
542			wordBuilder.Literal(cpu.Pop());
543		});
544
545		/*
546		 * compile
547		 * Pops the current TOS value, which must be an XT (pointer
548		 * to a word); the action of calling that word is compiled
549		 * in the current word.
550		 */
551		WordNative wcompile = AddNative("compile", false, cpu => {
552			CheckCompiling();
553			wordBuilder.Call(cpu.Pop().ToXT());
554		});
555
556		/*
557		 * postpone  (immediate)
558		 * Parses the next token as a word name, and add to the
559		 * current word the action of calling that word. This
560		 * basically removes immediatety from the next word.
561		 */
562		AddNative("postpone", true, cpu => {
563			CheckCompiling();
564			string tt = Next();
565			if (tt == null) {
566				throw new Exception(
567					"EOF reached (missing name)");
568			}
569			TValue v;
570			bool isVal = TryParseLiteral(tt, out v);
571			Word w = LookupNF(tt);
572			if (isVal && w != null) {
573				throw new Exception(String.Format(
574					"Ambiguous: both defined word and"
575					+ " literal: {0}", tt));
576			}
577			if (isVal) {
578				wordBuilder.Literal(v);
579				wordBuilder.CallExt(wliteral);
580			} else if (w != null) {
581				if (w.Immediate) {
582					wordBuilder.CallExt(w);
583				} else {
584					wordBuilder.Literal(new TValue(0,
585						new TPointerXT(w)));
586					wordBuilder.CallExt(wcompile);
587				}
588			} else {
589				wordBuilder.Literal(new TValue(0,
590					new TPointerXT(tt)));
591				wordBuilder.CallExt(wcompile);
592			}
593		});
594
595		/*
596		 * Interrupt compilation with an error.
597		 */
598		AddNative("exitvm", false, cpu => {
599			throw new Exception();
600		});
601
602		/*
603		 * Open a new data block. Its symbolic address is pushed
604		 * on the stack.
605		 */
606		AddNative("new-data-block", false, cpu => {
607			dataBlock = new ConstData(this);
608			cpu.Push(new TValue(0, new TPointerBlob(dataBlock)));
609		});
610
611		/*
612		 * Define a new data word. The data address and name are
613		 * popped from the stack.
614		 */
615		AddNative("define-data-word", false, cpu => {
616			string name = cpu.Pop().ToString();
617			TValue va = cpu.Pop();
618			TPointerBlob tb = va.ptr as TPointerBlob;
619			if (tb == null) {
620				throw new Exception(
621					"Address is not a data area");
622			}
623			Word w = new WordData(this, name, tb.Blob, va.x);
624			if (words.ContainsKey(name)) {
625				throw new Exception(
626					"Word already defined: " + name);
627			}
628			words[name] = w;
629			lastWord = w;
630		});
631
632		/*
633		 * Get an address pointing at the end of the current
634		 * data block. This is the address of the next byte that
635		 * will be added.
636		 */
637		AddNative("current-data", false, cpu => {
638			if (dataBlock == null) {
639				throw new Exception(
640					"No current data block");
641			}
642			cpu.Push(new TValue(dataBlock.Length,
643				new TPointerBlob(dataBlock)));
644		});
645
646		/*
647		 * Add a byte value to the data block.
648		 */
649		AddNative("data-add8", false, cpu => {
650			if (dataBlock == null) {
651				throw new Exception(
652					"No current data block");
653			}
654			int v = cpu.Pop();
655			if (v < 0 || v > 0xFF) {
656				throw new Exception(
657					"Byte value out of range: " + v);
658			}
659			dataBlock.Add8((byte)v);
660		});
661
662		/*
663		 * Set a byte value in the data block.
664		 */
665		AddNative("data-set8", false, cpu => {
666			TValue va = cpu.Pop();
667			TPointerBlob tb = va.ptr as TPointerBlob;
668			if (tb == null) {
669				throw new Exception(
670					"Address is not a data area");
671			}
672			int v = cpu.Pop();
673			if (v < 0 || v > 0xFF) {
674				throw new Exception(
675					"Byte value out of range: " + v);
676			}
677			tb.Blob.Set8(va.x, (byte)v);
678		});
679
680		/*
681		 * Get a byte value from a data block.
682		 */
683		AddNative("data-get8", false, new SType(1, 1), cpu => {
684			TValue va = cpu.Pop();
685			TPointerBlob tb = va.ptr as TPointerBlob;
686			if (tb == null) {
687				throw new Exception(
688					"Address is not a data area");
689			}
690			int v = tb.Blob.Read8(va.x);
691			cpu.Push(v);
692		});
693
694		/*
695		 *
696		 */
697		AddNative("compile-local-read", false, cpu => {
698			CheckCompiling();
699			wordBuilder.GetLocal(cpu.Pop().ToString());
700		});
701		AddNative("compile-local-write", false, cpu => {
702			CheckCompiling();
703			wordBuilder.PutLocal(cpu.Pop().ToString());
704		});
705
706		AddNative("ahead", true, cpu => {
707			CheckCompiling();
708			wordBuilder.Ahead();
709		});
710		AddNative("begin", true, cpu => {
711			CheckCompiling();
712			wordBuilder.Begin();
713		});
714		AddNative("again", true, cpu => {
715			CheckCompiling();
716			wordBuilder.Again();
717		});
718		AddNative("until", true, cpu => {
719			CheckCompiling();
720			wordBuilder.AgainIfNot();
721		});
722		AddNative("untilnot", true, cpu => {
723			CheckCompiling();
724			wordBuilder.AgainIf();
725		});
726		AddNative("if", true, cpu => {
727			CheckCompiling();
728			wordBuilder.AheadIfNot();
729		});
730		AddNative("ifnot", true, cpu => {
731			CheckCompiling();
732			wordBuilder.AheadIf();
733		});
734		AddNative("then", true, cpu => {
735			CheckCompiling();
736			wordBuilder.Then();
737		});
738		AddNative("cs-pick", false, cpu => {
739			CheckCompiling();
740			wordBuilder.CSPick(cpu.Pop());
741		});
742		AddNative("cs-roll", false, cpu => {
743			CheckCompiling();
744			wordBuilder.CSRoll(cpu.Pop());
745		});
746		AddNative("next-word", false, cpu => {
747			string s = Next();
748			if (s == null) {
749				throw new Exception("No next word (EOF)");
750			}
751			cpu.Push(StringToBlob(s));
752		});
753		AddNative("parse", false, cpu => {
754			int d = cpu.Pop();
755			string s = ReadTerm(d);
756			cpu.Push(StringToBlob(s));
757		});
758		AddNative("char", false, cpu => {
759			int c = NextChar();
760			if (c < 0) {
761				throw new Exception("No next character (EOF)");
762			}
763			cpu.Push(c);
764		});
765		AddNative("'", false, cpu => {
766			string name = Next();
767			cpu.Push(new TValue(0, new TPointerXT(name)));
768		});
769
770		/*
771		 * The "execute" word is valid in generated C code, but
772		 * since it jumps to a runtime pointer, its actual stack
773		 * effect cannot be computed in advance.
774		 */
775		AddNative("execute", false, cpu => {
776			cpu.Pop().Execute(this, cpu);
777		});
778
779		AddNative("[", true, cpu => {
780			CheckCompiling();
781			compiling = false;
782		});
783		AddNative("]", false, cpu => {
784			compiling = true;
785		});
786		AddNative("(local)", false, cpu => {
787			CheckCompiling();
788			wordBuilder.DefLocal(cpu.Pop().ToString());
789		});
790		AddNative("ret", true, cpu => {
791			CheckCompiling();
792			wordBuilder.Ret();
793		});
794
795		AddNative("drop", false, new SType(1, 0), cpu => {
796			cpu.Pop();
797		});
798		AddNative("dup", false, new SType(1, 2), cpu => {
799			cpu.Push(cpu.Peek(0));
800		});
801		AddNative("swap", false, new SType(2, 2), cpu => {
802			cpu.Rot(1);
803		});
804		AddNative("over", false, new SType(2, 3), cpu => {
805			cpu.Push(cpu.Peek(1));
806		});
807		AddNative("rot", false, new SType(3, 3), cpu => {
808			cpu.Rot(2);
809		});
810		AddNative("-rot", false, new SType(3, 3), cpu => {
811			cpu.NRot(2);
812		});
813
814		/*
815		 * "roll" and "pick" are special in that the stack slot
816		 * they inspect might be known only at runtime, so an
817		 * absolute stack effect cannot be attributed. Instead,
818		 * we simply hope that the caller knows what it is doing,
819		 * and we use a simple stack effect for just the count
820		 * value and picked value.
821		 */
822		AddNative("roll", false, new SType(1, 0), cpu => {
823			cpu.Rot(cpu.Pop());
824		});
825		AddNative("pick", false, new SType(1, 1), cpu => {
826			cpu.Push(cpu.Peek(cpu.Pop()));
827		});
828
829		AddNative("+", false, new SType(2, 1), cpu => {
830			TValue b = cpu.Pop();
831			TValue a = cpu.Pop();
832			if (b.ptr == null) {
833				a.x += (int)b;
834				cpu.Push(a);
835			} else if (a.ptr is TPointerBlob
836				&& b.ptr is TPointerBlob)
837			{
838				cpu.Push(StringToBlob(
839					a.ToString() + b.ToString()));
840			} else {
841				throw new Exception(string.Format(
842					"Cannot add '{0}' to '{1}'", b, a));
843			}
844		});
845		AddNative("-", false, new SType(2, 1), cpu => {
846			/*
847			 * We can subtract two pointers, provided that
848			 * they point to the same blob. Otherwise,
849			 * the subtraction second operand must be an
850			 * integer.
851			 */
852			TValue b = cpu.Pop();
853			TValue a = cpu.Pop();
854			TPointerBlob ap = a.ptr as TPointerBlob;
855			TPointerBlob bp = b.ptr as TPointerBlob;
856			if (ap != null && bp != null && ap.Blob == bp.Blob) {
857				cpu.Push(new TValue(a.x - b.x));
858				return;
859			}
860			int bx = b;
861			a.x -= bx;
862			cpu.Push(a);
863		});
864		AddNative("neg", false, new SType(1, 1), cpu => {
865			int ax = cpu.Pop();
866			cpu.Push(-ax);
867		});
868		AddNative("*", false, new SType(2, 1), cpu => {
869			int bx = cpu.Pop();
870			int ax = cpu.Pop();
871			cpu.Push(ax * bx);
872		});
873		AddNative("/", false, new SType(2, 1), cpu => {
874			int bx = cpu.Pop();
875			int ax = cpu.Pop();
876			cpu.Push(ax / bx);
877		});
878		AddNative("u/", false, new SType(2, 1), cpu => {
879			uint bx = cpu.Pop();
880			uint ax = cpu.Pop();
881			cpu.Push(ax / bx);
882		});
883		AddNative("%", false, new SType(2, 1), cpu => {
884			int bx = cpu.Pop();
885			int ax = cpu.Pop();
886			cpu.Push(ax % bx);
887		});
888		AddNative("u%", false, new SType(2, 1), cpu => {
889			uint bx = cpu.Pop();
890			uint ax = cpu.Pop();
891			cpu.Push(ax % bx);
892		});
893		AddNative("<", false, new SType(2, 1), cpu => {
894			int bx = cpu.Pop();
895			int ax = cpu.Pop();
896			cpu.Push(ax < bx);
897		});
898		AddNative("<=", false, new SType(2, 1), cpu => {
899			int bx = cpu.Pop();
900			int ax = cpu.Pop();
901			cpu.Push(ax <= bx);
902		});
903		AddNative(">", false, new SType(2, 1), cpu => {
904			int bx = cpu.Pop();
905			int ax = cpu.Pop();
906			cpu.Push(ax > bx);
907		});
908		AddNative(">=", false, new SType(2, 1), cpu => {
909			int bx = cpu.Pop();
910			int ax = cpu.Pop();
911			cpu.Push(ax >= bx);
912		});
913		AddNative("=", false, new SType(2, 1), cpu => {
914			TValue b = cpu.Pop();
915			TValue a = cpu.Pop();
916			cpu.Push(a.Equals(b));
917		});
918		AddNative("<>", false, new SType(2, 1), cpu => {
919			TValue b = cpu.Pop();
920			TValue a = cpu.Pop();
921			cpu.Push(!a.Equals(b));
922		});
923		AddNative("u<", false, new SType(2, 1), cpu => {
924			uint bx = cpu.Pop().UInt;
925			uint ax = cpu.Pop().UInt;
926			cpu.Push(new TValue(ax < bx));
927		});
928		AddNative("u<=", false, new SType(2, 1), cpu => {
929			uint bx = cpu.Pop().UInt;
930			uint ax = cpu.Pop().UInt;
931			cpu.Push(new TValue(ax <= bx));
932		});
933		AddNative("u>", false, new SType(2, 1), cpu => {
934			uint bx = cpu.Pop().UInt;
935			uint ax = cpu.Pop().UInt;
936			cpu.Push(new TValue(ax > bx));
937		});
938		AddNative("u>=", false, new SType(2, 1), cpu => {
939			uint bx = cpu.Pop();
940			uint ax = cpu.Pop();
941			cpu.Push(ax >= bx);
942		});
943		AddNative("and", false, new SType(2, 1), cpu => {
944			uint bx = cpu.Pop();
945			uint ax = cpu.Pop();
946			cpu.Push(ax & bx);
947		});
948		AddNative("or", false, new SType(2, 1), cpu => {
949			uint bx = cpu.Pop();
950			uint ax = cpu.Pop();
951			cpu.Push(ax | bx);
952		});
953		AddNative("xor", false, new SType(2, 1), cpu => {
954			uint bx = cpu.Pop();
955			uint ax = cpu.Pop();
956			cpu.Push(ax ^ bx);
957		});
958		AddNative("not", false, new SType(1, 1), cpu => {
959			uint ax = cpu.Pop();
960			cpu.Push(~ax);
961		});
962		AddNative("<<", false, new SType(2, 1), cpu => {
963			int count = cpu.Pop();
964			if (count < 0 || count > 31) {
965				throw new Exception("Invalid shift count");
966			}
967			uint ax = cpu.Pop();
968			cpu.Push(ax << count);
969		});
970		AddNative(">>", false, new SType(2, 1), cpu => {
971			int count = cpu.Pop();
972			if (count < 0 || count > 31) {
973				throw new Exception("Invalid shift count");
974			}
975			int ax = cpu.Pop();
976			cpu.Push(ax >> count);
977		});
978		AddNative("u>>", false, new SType(2, 1), cpu => {
979			int count = cpu.Pop();
980			if (count < 0 || count > 31) {
981				throw new Exception("Invalid shift count");
982			}
983			uint ax = cpu.Pop();
984			cpu.Push(ax >> count);
985		});
986
987		AddNative(".", false, new SType(1, 0), cpu => {
988			Console.Write(" {0}", cpu.Pop().ToString());
989		});
990		AddNative(".s", false, SType.BLANK, cpu => {
991			int n = cpu.Depth;
992			for (int i = n - 1; i >= 0; i --) {
993				Console.Write(" {0}", cpu.Peek(i).ToString());
994			}
995		});
996		AddNative("putc", false, new SType(1, 0), cpu => {
997			Console.Write((char)cpu.Pop());
998		});
999		AddNative("puts", false, new SType(1, 0), cpu => {
1000			Console.Write("{0}", cpu.Pop().ToString());
1001		});
1002		AddNative("cr", false, SType.BLANK, cpu => {
1003			Console.WriteLine();
1004		});
1005		AddNative("eqstr", false, new SType(2, 1), cpu => {
1006			string s2 = cpu.Pop().ToString();
1007			string s1 = cpu.Pop().ToString();
1008			cpu.Push(s1 == s2);
1009		});
1010	}
1011
1012	WordNative AddNative(string name, bool immediate,
1013		WordNative.NativeRun code)
1014	{
1015		return AddNative(name, immediate, SType.UNKNOWN, code);
1016	}
1017
1018	WordNative AddNative(string name, bool immediate, SType stackEffect,
1019		WordNative.NativeRun code)
1020	{
1021		if (words.ContainsKey(name)) {
1022			throw new Exception(
1023				"Word already defined: " + name);
1024		}
1025		WordNative w = new WordNative(this, name, code);
1026		w.Immediate = immediate;
1027		w.StackEffect = stackEffect;
1028		words[name] = w;
1029		return w;
1030	}
1031
1032	internal long NextBlobID()
1033	{
1034		return currentBlobID ++;
1035	}
1036
1037	int NextChar()
1038	{
1039		int c = delayedChar;
1040		if (c >= 0) {
1041			delayedChar = Int32.MinValue;
1042		} else if (c > Int32.MinValue) {
1043			delayedChar = -(c + 1);
1044			c = '\n';
1045		} else {
1046			c = currentInput.Read();
1047		}
1048		if (c == '\r') {
1049			if (delayedChar >= 0) {
1050				c = delayedChar;
1051				delayedChar = Int32.MinValue;
1052			} else {
1053				c = currentInput.Read();
1054			}
1055			if (c != '\n') {
1056				delayedChar = c;
1057				c = '\n';
1058			}
1059		}
1060		return c;
1061	}
1062
1063	/*
1064	 * Un-read the character value 'c'. That value MUST be the one
1065	 * that was obtained from NextChar().
1066	 */
1067	void Unread(int c)
1068	{
1069		if (c < 0) {
1070			return;
1071		}
1072		if (delayedChar < 0) {
1073			if (delayedChar != Int32.MinValue) {
1074				throw new Exception(
1075					"Already two delayed characters");
1076			}
1077			delayedChar = c;
1078		} else if (c != '\n') {
1079			throw new Exception("Cannot delay two characters");
1080		} else {
1081			delayedChar = -(delayedChar + 1);
1082		}
1083	}
1084
1085	string Next()
1086	{
1087		string r = delayedToken;
1088		if (r != null) {
1089			delayedToken = null;
1090			return r;
1091		}
1092		tokenBuilder.Length = 0;
1093		int c;
1094		for (;;) {
1095			c = NextChar();
1096			if (c < 0) {
1097				return null;
1098			}
1099			if (!IsWS(c)) {
1100				break;
1101			}
1102		}
1103		if (c == '"') {
1104			return ParseString();
1105		}
1106		for (;;) {
1107			tokenBuilder.Append((char)c);
1108			c = NextChar();
1109			if (c < 0 || IsWS(c)) {
1110				Unread(c);
1111				return tokenBuilder.ToString();
1112			}
1113		}
1114	}
1115
1116	string ParseCCode()
1117	{
1118		SType stackEffect;
1119		string r = ParseCCode(out stackEffect);
1120		if (stackEffect.IsKnown) {
1121			throw new Exception(
1122				"Stack effect forbidden in this declaration");
1123		}
1124		return r;
1125	}
1126
1127	string ParseCCode(out SType stackEffect)
1128	{
1129		string s = ParseCCodeNF(out stackEffect);
1130		if (s == null) {
1131			throw new Exception("Error while parsing C code");
1132		}
1133		return s;
1134	}
1135
1136	string ParseCCodeNF(out SType stackEffect)
1137	{
1138		stackEffect = SType.UNKNOWN;
1139		for (;;) {
1140			int c = NextChar();
1141			if (c < 0) {
1142				return null;
1143			}
1144			if (!IsWS(c)) {
1145				if (c == '(') {
1146					if (stackEffect.IsKnown) {
1147						Unread(c);
1148						return null;
1149					}
1150					stackEffect = ParseStackEffectNF();
1151					if (!stackEffect.IsKnown) {
1152						return null;
1153					}
1154					continue;
1155				} else if (c != '{') {
1156					Unread(c);
1157					return null;
1158				}
1159				break;
1160			}
1161		}
1162		StringBuilder sb = new StringBuilder();
1163		int count = 1;
1164		for (;;) {
1165			int c = NextChar();
1166			if (c < 0) {
1167				return null;
1168			}
1169			switch (c) {
1170			case '{':
1171				count ++;
1172				break;
1173			case '}':
1174				if (-- count == 0) {
1175					return sb.ToString();
1176				}
1177				break;
1178			}
1179			sb.Append((char)c);
1180		}
1181	}
1182
1183	/*
1184	 * Parse a stack effect declaration. This method assumes that the
1185	 * opening parenthesis has just been read. If the parsing fails,
1186	 * then this method returns SType.UNKNOWN.
1187	 */
1188	SType ParseStackEffectNF()
1189	{
1190		bool seenSep = false;
1191		bool seenBang = false;
1192		int din = 0, dout = 0;
1193		for (;;) {
1194			string t = Next();
1195			if (t == null) {
1196				return SType.UNKNOWN;
1197			}
1198			if (t == "--") {
1199				if (seenSep) {
1200					return SType.UNKNOWN;
1201				}
1202				seenSep = true;
1203			} else if (t == ")") {
1204				if (seenSep) {
1205					if (seenBang && dout == 1) {
1206						dout = -1;
1207					}
1208					return new SType(din, dout);
1209				} else {
1210					return SType.UNKNOWN;
1211				}
1212			} else {
1213				if (seenSep) {
1214					if (dout == 0 && t == "!") {
1215						seenBang = true;
1216					}
1217					dout ++;
1218				} else {
1219					din ++;
1220				}
1221			}
1222		}
1223	}
1224
1225	string ParseString()
1226	{
1227		StringBuilder sb = new StringBuilder();
1228		sb.Append('"');
1229		bool lcwb = false;
1230		int hexNum = 0;
1231		int acc = 0;
1232		for (;;) {
1233			int c = NextChar();
1234			if (c < 0) {
1235				throw new Exception(
1236					"Unfinished literal string");
1237			}
1238			if (hexNum > 0) {
1239				int d = HexVal(c);
1240				if (d < 0) {
1241					throw new Exception(String.Format(
1242						"not an hex digit: U+{0:X4}",
1243						c));
1244				}
1245				acc = (acc << 4) + d;
1246				if (-- hexNum == 0) {
1247					sb.Append((char)acc);
1248					acc = 0;
1249				}
1250			} else if (lcwb) {
1251				lcwb = false;
1252				switch (c) {
1253				case '\n': SkipNL(); break;
1254				case 'x':
1255					hexNum = 2;
1256					break;
1257				case 'u':
1258					hexNum = 4;
1259					break;
1260				default:
1261					sb.Append(SingleCharEscape(c));
1262					break;
1263				}
1264			} else {
1265				switch (c) {
1266				case '"':
1267					return sb.ToString();
1268				case '\\':
1269					lcwb = true;
1270					break;
1271				default:
1272					sb.Append((char)c);
1273					break;
1274				}
1275			}
1276		}
1277	}
1278
1279	static char SingleCharEscape(int c)
1280	{
1281		switch (c) {
1282		case 'n': return '\n';
1283		case 'r': return '\r';
1284		case 't': return '\t';
1285		case 's': return ' ';
1286		default:
1287			return (char)c;
1288		}
1289	}
1290
1291	/*
1292	 * A backslash+newline sequence occurred in a literal string; we
1293	 * check and consume the newline escape sequence (whitespace at
1294	 * start of next line, then a double-quote character).
1295	 */
1296	void SkipNL()
1297	{
1298		for (;;) {
1299			int c = NextChar();
1300			if (c < 0) {
1301				throw new Exception("EOF in literal string");
1302			}
1303			if (c == '\n') {
1304				throw new Exception(
1305					"Unescaped newline in literal string");
1306			}
1307			if (IsWS(c)) {
1308				continue;
1309			}
1310			if (c == '"') {
1311				return;
1312			}
1313			throw new Exception(
1314				"Invalid newline escape in literal string");
1315		}
1316	}
1317
1318	static char DecodeCharConst(string t)
1319	{
1320		if (t.Length == 1 && t[0] != '\\') {
1321			return t[0];
1322		}
1323		if (t.Length >= 2 && t[0] == '\\') {
1324			switch (t[1]) {
1325			case 'x':
1326				if (t.Length == 4) {
1327					int x = DecHex(t.Substring(2));
1328					if (x >= 0) {
1329						return (char)x;
1330					}
1331				}
1332				break;
1333			case 'u':
1334				if (t.Length == 6) {
1335					int x = DecHex(t.Substring(2));
1336					if (x >= 0) {
1337						return (char)x;
1338					}
1339				}
1340				break;
1341			default:
1342				if (t.Length == 2) {
1343					return SingleCharEscape(t[1]);
1344				}
1345				break;
1346			}
1347		}
1348		throw new Exception("Invalid literal char: `" + t);
1349	}
1350
1351	static int DecHex(string s)
1352	{
1353		int acc = 0;
1354		foreach (char c in s) {
1355			int d = HexVal(c);
1356			if (d < 0) {
1357				return -1;
1358			}
1359			acc = (acc << 4) + d;
1360		}
1361		return acc;
1362	}
1363
1364	static int HexVal(int c)
1365	{
1366		if (c >= '0' && c <= '9') {
1367			return c - '0';
1368		} else if (c >= 'A' && c <= 'F') {
1369			return c - ('A' - 10);
1370		} else if (c >= 'a' && c <= 'f') {
1371			return c - ('a' - 10);
1372		} else {
1373			return -1;
1374		}
1375	}
1376
1377	string ReadTerm(int ct)
1378	{
1379		StringBuilder sb = new StringBuilder();
1380		for (;;) {
1381			int c = NextChar();
1382			if (c < 0) {
1383				throw new Exception(String.Format(
1384					"EOF reached before U+{0:X4}", ct));
1385			}
1386			if (c == ct) {
1387				return sb.ToString();
1388			}
1389			sb.Append((char)c);
1390		}
1391	}
1392
1393	static bool IsWS(int c)
1394	{
1395		return c <= 32;
1396	}
1397
1398	void ProcessInput(TextReader tr)
1399	{
1400		this.currentInput = tr;
1401		delayedChar = -1;
1402		Word w = new WordNative(this, "toplevel",
1403			xcpu => { CompileStep(xcpu); });
1404		CPU cpu = new CPU();
1405		Opcode[] code = new Opcode[] {
1406			new OpcodeCall(w),
1407			new OpcodeJumpUncond(-2)
1408		};
1409		quitRunLoop = false;
1410		cpu.Enter(code, 0);
1411		for (;;) {
1412			if (quitRunLoop) {
1413				break;
1414			}
1415			Opcode op = cpu.ipBuf[cpu.ipOff ++];
1416			op.Run(cpu);
1417		}
1418	}
1419
1420	void CompileStep(CPU cpu)
1421	{
1422		string tt = Next();
1423		if (tt == null) {
1424			if (compiling) {
1425				throw new Exception("EOF while compiling");
1426			}
1427			quitRunLoop = true;
1428			return;
1429		}
1430		TValue v;
1431		bool isVal = TryParseLiteral(tt, out v);
1432		Word w = LookupNF(tt);
1433		if (isVal && w != null) {
1434			throw new Exception(String.Format(
1435				"Ambiguous: both defined word and literal: {0}",
1436				tt));
1437		}
1438		if (compiling) {
1439			if (isVal) {
1440				wordBuilder.Literal(v);
1441			} else if (w != null) {
1442				if (w.Immediate) {
1443					w.Run(cpu);
1444				} else {
1445					wordBuilder.CallExt(w);
1446				}
1447			} else {
1448				wordBuilder.Call(tt);
1449			}
1450		} else {
1451			if (isVal) {
1452				cpu.Push(v);
1453			} else if (w != null) {
1454				w.Run(cpu);
1455			} else {
1456				throw new Exception(String.Format(
1457					"Unknown word: '{0}'", tt));
1458			}
1459		}
1460	}
1461
1462	string GetCCode(string name)
1463	{
1464		string ccode;
1465		allCCode.TryGetValue(name, out ccode);
1466		return ccode;
1467	}
1468
1469	void Generate(string outBase, string coreRun,
1470		params string[] entryPoints)
1471	{
1472		/*
1473		 * Gather all words that are part of the generated
1474		 * code. This is done by exploring references
1475		 * transitively. All such words are thus implicitly
1476		 * resolved.
1477		 */
1478		IDictionary<string, Word> wordSet =
1479			new SortedDictionary<string, Word>(
1480				StringComparer.Ordinal);
1481		Queue<Word> tx = new Queue<Word>();
1482		foreach (string ep in entryPoints) {
1483			if (wordSet.ContainsKey(ep)) {
1484				continue;
1485			}
1486			Word w = Lookup(ep);
1487			wordSet[w.Name] = w;
1488			tx.Enqueue(w);
1489		}
1490		while (tx.Count > 0) {
1491			Word w = tx.Dequeue();
1492			foreach (Word w2 in w.GetReferences()) {
1493				if (wordSet.ContainsKey(w2.Name)) {
1494					continue;
1495				}
1496				wordSet[w2.Name] = w2;
1497				tx.Enqueue(w2);
1498			}
1499		}
1500
1501		/*
1502		 * Do flow analysis.
1503		 */
1504		if (enableFlowAnalysis) {
1505			foreach (string ep in entryPoints) {
1506				Word w = wordSet[ep];
1507				w.AnalyseFlow();
1508				Console.WriteLine("{0}: ds={1} rs={2}",
1509					ep, w.MaxDataStack, w.MaxReturnStack);
1510				if (w.MaxDataStack > dsLimit) {
1511					throw new Exception("'" + ep
1512						+ "' exceeds data stack limit");
1513				}
1514				if (w.MaxReturnStack > rsLimit) {
1515					throw new Exception("'" + ep
1516						+ "' exceeds return stack"
1517						+ " limit");
1518				}
1519			}
1520		}
1521
1522		/*
1523		 * Gather referenced data areas and compute their
1524		 * addresses in the generated data block. The address
1525		 * 0 in the data block is unaffected so that no
1526		 * valid runtime pointer is equal to null.
1527		 */
1528		IDictionary<long, ConstData> blocks =
1529			new SortedDictionary<long, ConstData>();
1530		foreach (Word w in wordSet.Values) {
1531			foreach (ConstData cd in w.GetDataBlocks()) {
1532				blocks[cd.ID] = cd;
1533			}
1534		}
1535		int dataLen = 1;
1536		foreach (ConstData cd in blocks.Values) {
1537			cd.Address = dataLen;
1538			dataLen += cd.Length;
1539		}
1540
1541		/*
1542		 * Generated code is a sequence of "slot numbers", each
1543		 * referencing either a piece of explicit C code, or an
1544		 * entry in the table of interpreted words.
1545		 *
1546		 * Opcodes other than "call" get the slots 0 to 6:
1547		 *
1548		 *   0   ret           no argument
1549		 *   1   const         signed value
1550		 *   2   get local     local number
1551		 *   3   put local     local number
1552		 *   4   jump          signed offset
1553		 *   5   jump if       signed offset
1554		 *   6   jump if not   signed offset
1555		 *
1556		 * The argument, if any, is in "7E" format: the value is
1557		 * encoded in 7-bit chunk, with big-endian signed
1558		 * convention. Each 7-bit chunk is encoded over one byte;
1559		 * the upper bit is 1 for all chunks except the last one.
1560		 *
1561		 * Words with explicit C code get the slot numbers
1562		 * immediately after 6. Interpreted words come afterwards.
1563		 */
1564		IDictionary<string, int> slots = new Dictionary<string, int>();
1565		int curSlot = 7;
1566
1567		/*
1568		 * Get explicit C code for words which have such code.
1569		 * We use string equality on C code so that words with
1570		 * identical implementations get merged.
1571		 *
1572		 * We also check that words with no explicit C code are
1573		 * interpreted.
1574		 */
1575		IDictionary<string, int> ccodeUni =
1576			new Dictionary<string, int>();
1577		IDictionary<int, string> ccodeNames =
1578			new Dictionary<int, string>();
1579		foreach (Word w in wordSet.Values) {
1580			string ccode = GetCCode(w.Name);
1581			if (ccode == null) {
1582				if (w is WordNative) {
1583					throw new Exception(String.Format(
1584						"No C code for native '{0}'",
1585						w.Name));
1586				}
1587				continue;
1588			}
1589			int sn;
1590			if (ccodeUni.ContainsKey(ccode)) {
1591				sn = ccodeUni[ccode];
1592				ccodeNames[sn] += " " + EscapeCComment(w.Name);
1593			} else {
1594				sn = curSlot ++;
1595				ccodeUni[ccode] = sn;
1596				ccodeNames[sn] = EscapeCComment(w.Name);
1597			}
1598			slots[w.Name] = sn;
1599			w.Slot = sn;
1600		}
1601
1602		/*
1603		 * Assign slot values to all remaining words; we know they
1604		 * are all interpreted.
1605		 */
1606		int slotInterpreted = curSlot;
1607		foreach (Word w in wordSet.Values) {
1608			if (GetCCode(w.Name) != null) {
1609				continue;
1610			}
1611			int sn = curSlot ++;
1612			slots[w.Name] = sn;
1613			w.Slot = sn;
1614		}
1615		int numInterpreted = curSlot - slotInterpreted;
1616
1617		/*
1618		 * Verify that all entry points are interpreted words.
1619		 */
1620		foreach (string ep in entryPoints) {
1621			if (GetCCode(ep) != null) {
1622				throw new Exception(
1623					"Non-interpreted entry point");
1624			}
1625		}
1626
1627		/*
1628		 * Compute the code block. Each word (without any C code)
1629		 * yields some CodeElement instances.
1630		 */
1631		List<CodeElement> gcodeList = new List<CodeElement>();
1632		CodeElement[] interpretedEntry =
1633			new CodeElement[numInterpreted];
1634		foreach (Word w in wordSet.Values) {
1635			if (GetCCode(w.Name) != null) {
1636				continue;
1637			}
1638			int n = gcodeList.Count;
1639			w.GenerateCodeElements(gcodeList);
1640			interpretedEntry[w.Slot - slotInterpreted] =
1641				gcodeList[n];
1642		}
1643		CodeElement[] gcode = gcodeList.ToArray();
1644
1645		/*
1646		 * If there are less than 256 words in total (C +
1647		 * interpreted) then we can use "one-byte code" which is
1648		 * more compact when the number of words is in the
1649		 * 128..255 range.
1650		 */
1651		bool oneByteCode;
1652		if (slotInterpreted + numInterpreted >= 256) {
1653			Console.WriteLine("WARNING: more than 255 words");
1654			oneByteCode = false;
1655		} else {
1656			oneByteCode = true;
1657		}
1658
1659		/*
1660		 * Compute all addresses and offsets. This loops until
1661		 * the addresses stabilize.
1662		 */
1663		int totalLen = -1;
1664		int[] gcodeLen = new int[gcode.Length];
1665		for (;;) {
1666			for (int i = 0; i < gcode.Length; i ++) {
1667				gcodeLen[i] = gcode[i].GetLength(oneByteCode);
1668			}
1669			int off = 0;
1670			for (int i = 0; i < gcode.Length; i ++) {
1671				gcode[i].Address = off;
1672				gcode[i].LastLength = gcodeLen[i];
1673				off += gcodeLen[i];
1674			}
1675			if (off == totalLen) {
1676				break;
1677			}
1678			totalLen = off;
1679		}
1680
1681		/*
1682		 * Produce output file.
1683		 */
1684		using (TextWriter tw = File.CreateText(outBase + ".c")) {
1685			tw.NewLine = "\n";
1686
1687			tw.WriteLine("{0}",
1688@"/* Automatically generated code; do not modify directly. */
1689
1690#include <stddef.h>
1691#include <stdint.h>
1692
1693typedef struct {
1694	uint32_t *dp;
1695	uint32_t *rp;
1696	const unsigned char *ip;
1697} t0_context;
1698
1699static uint32_t
1700t0_parse7E_unsigned(const unsigned char **p)
1701{
1702	uint32_t x;
1703
1704	x = 0;
1705	for (;;) {
1706		unsigned y;
1707
1708		y = *(*p) ++;
1709		x = (x << 7) | (uint32_t)(y & 0x7F);
1710		if (y < 0x80) {
1711			return x;
1712		}
1713	}
1714}
1715
1716static int32_t
1717t0_parse7E_signed(const unsigned char **p)
1718{
1719	int neg;
1720	uint32_t x;
1721
1722	neg = ((**p) >> 6) & 1;
1723	x = (uint32_t)-neg;
1724	for (;;) {
1725		unsigned y;
1726
1727		y = *(*p) ++;
1728		x = (x << 7) | (uint32_t)(y & 0x7F);
1729		if (y < 0x80) {
1730			if (neg) {
1731				return -(int32_t)~x - 1;
1732			} else {
1733				return (int32_t)x;
1734			}
1735		}
1736	}
1737}
1738
1739#define T0_VBYTE(x, n)   (unsigned char)((((uint32_t)(x) >> (n)) & 0x7F) | 0x80)
1740#define T0_FBYTE(x, n)   (unsigned char)(((uint32_t)(x) >> (n)) & 0x7F)
1741#define T0_SBYTE(x)      (unsigned char)((((uint32_t)(x) >> 28) + 0xF8) ^ 0xF8)
1742#define T0_INT1(x)       T0_FBYTE(x, 0)
1743#define T0_INT2(x)       T0_VBYTE(x, 7), T0_FBYTE(x, 0)
1744#define T0_INT3(x)       T0_VBYTE(x, 14), T0_VBYTE(x, 7), T0_FBYTE(x, 0)
1745#define T0_INT4(x)       T0_VBYTE(x, 21), T0_VBYTE(x, 14), T0_VBYTE(x, 7), T0_FBYTE(x, 0)
1746#define T0_INT5(x)       T0_SBYTE(x), T0_VBYTE(x, 21), T0_VBYTE(x, 14), T0_VBYTE(x, 7), T0_FBYTE(x, 0)
1747
1748/* static const unsigned char t0_datablock[]; */
1749");
1750
1751			/*
1752			 * Add declarations (not definitions) for the
1753			 * entry point initialisation functions, and the
1754			 * runner.
1755			 */
1756			tw.WriteLine();
1757			foreach (string ep in entryPoints) {
1758				tw.WriteLine("void {0}_init_{1}(void *t0ctx);",
1759					coreRun, ep);
1760			}
1761			tw.WriteLine();
1762			tw.WriteLine("void {0}_run(void *t0ctx);", coreRun);
1763
1764			/*
1765			 * Add preamble elements here. They may be needed
1766			 * for evaluating constant expressions in the
1767			 * code block.
1768			 */
1769			foreach (string pp in extraCode) {
1770				tw.WriteLine();
1771				tw.WriteLine("{0}", pp);
1772			}
1773
1774			BlobWriter bw;
1775			tw.WriteLine();
1776			tw.Write("static const unsigned char"
1777				+ " t0_datablock[] = {");
1778			bw = new BlobWriter(tw, 78, 1);
1779			bw.Append((byte)0);
1780			foreach (ConstData cd in blocks.Values) {
1781				cd.Encode(bw);
1782			}
1783			tw.WriteLine();
1784			tw.WriteLine("};");
1785
1786			tw.WriteLine();
1787			tw.Write("static const unsigned char"
1788				+ " t0_codeblock[] = {");
1789			bw = new BlobWriter(tw, 78, 1);
1790			foreach (CodeElement ce in gcode) {
1791				ce.Encode(bw, oneByteCode);
1792			}
1793			tw.WriteLine();
1794			tw.WriteLine("};");
1795
1796			tw.WriteLine();
1797			tw.Write("static const uint16_t t0_caddr[] = {");
1798			for (int i = 0; i < interpretedEntry.Length; i ++) {
1799				if (i != 0) {
1800					tw.Write(',');
1801				}
1802				tw.WriteLine();
1803				tw.Write("\t{0}", interpretedEntry[i].Address);
1804			}
1805			tw.WriteLine();
1806			tw.WriteLine("};");
1807
1808			tw.WriteLine();
1809			tw.WriteLine("#define T0_INTERPRETED   {0}",
1810				slotInterpreted);
1811			tw.WriteLine();
1812			tw.WriteLine("{0}",
1813@"#define T0_ENTER(ip, rp, slot)   do { \
1814		const unsigned char *t0_newip; \
1815		uint32_t t0_lnum; \
1816		t0_newip = &t0_codeblock[t0_caddr[(slot) - T0_INTERPRETED]]; \
1817		t0_lnum = t0_parse7E_unsigned(&t0_newip); \
1818		(rp) += t0_lnum; \
1819		*((rp) ++) = (uint32_t)((ip) - &t0_codeblock[0]) + (t0_lnum << 16); \
1820		(ip) = t0_newip; \
1821	} while (0)");
1822			tw.WriteLine();
1823			tw.WriteLine("{0}",
1824@"#define T0_DEFENTRY(name, slot) \
1825void \
1826name(void *ctx) \
1827{ \
1828	t0_context *t0ctx = ctx; \
1829	t0ctx->ip = &t0_codeblock[0]; \
1830	T0_ENTER(t0ctx->ip, t0ctx->rp, slot); \
1831}");
1832
1833			tw.WriteLine();
1834			foreach (string ep in entryPoints) {
1835				tw.WriteLine("T0_DEFENTRY({0}, {1})",
1836					coreRun + "_init_" + ep,
1837					wordSet[ep].Slot);
1838			}
1839			tw.WriteLine();
1840			if (oneByteCode) {
1841				tw.WriteLine("{0}",
1842@"#define T0_NEXT(t0ipp)   (*(*(t0ipp)) ++)");
1843			} else {
1844				tw.WriteLine("{0}",
1845@"#define T0_NEXT(t0ipp)   t0_parse7E_unsigned(t0ipp)");
1846			}
1847			tw.WriteLine();
1848			tw.WriteLine("void");
1849			tw.WriteLine("{0}_run(void *t0ctx)", coreRun);
1850			tw.WriteLine("{0}",
1851@"{
1852	uint32_t *dp, *rp;
1853	const unsigned char *ip;
1854
1855#define T0_LOCAL(x)    (*(rp - 2 - (x)))
1856#define T0_POP()       (*-- dp)
1857#define T0_POPi()      (*(int32_t *)(-- dp))
1858#define T0_PEEK(x)     (*(dp - 1 - (x)))
1859#define T0_PEEKi(x)    (*(int32_t *)(dp - 1 - (x)))
1860#define T0_PUSH(v)     do { *dp = (v); dp ++; } while (0)
1861#define T0_PUSHi(v)    do { *(int32_t *)dp = (v); dp ++; } while (0)
1862#define T0_RPOP()      (*-- rp)
1863#define T0_RPOPi()     (*(int32_t *)(-- rp))
1864#define T0_RPUSH(v)    do { *rp = (v); rp ++; } while (0)
1865#define T0_RPUSHi(v)   do { *(int32_t *)rp = (v); rp ++; } while (0)
1866#define T0_ROLL(x)     do { \
1867	size_t t0len = (size_t)(x); \
1868	uint32_t t0tmp = *(dp - 1 - t0len); \
1869	memmove(dp - t0len - 1, dp - t0len, t0len * sizeof *dp); \
1870	*(dp - 1) = t0tmp; \
1871} while (0)
1872#define T0_SWAP()      do { \
1873	uint32_t t0tmp = *(dp - 2); \
1874	*(dp - 2) = *(dp - 1); \
1875	*(dp - 1) = t0tmp; \
1876} while (0)
1877#define T0_ROT()       do { \
1878	uint32_t t0tmp = *(dp - 3); \
1879	*(dp - 3) = *(dp - 2); \
1880	*(dp - 2) = *(dp - 1); \
1881	*(dp - 1) = t0tmp; \
1882} while (0)
1883#define T0_NROT()       do { \
1884	uint32_t t0tmp = *(dp - 1); \
1885	*(dp - 1) = *(dp - 2); \
1886	*(dp - 2) = *(dp - 3); \
1887	*(dp - 3) = t0tmp; \
1888} while (0)
1889#define T0_PICK(x)      do { \
1890	uint32_t t0depth = (x); \
1891	T0_PUSH(T0_PEEK(t0depth)); \
1892} while (0)
1893#define T0_CO()         do { \
1894	goto t0_exit; \
1895} while (0)
1896#define T0_RET()        goto t0_next
1897
1898	dp = ((t0_context *)t0ctx)->dp;
1899	rp = ((t0_context *)t0ctx)->rp;
1900	ip = ((t0_context *)t0ctx)->ip;
1901	goto t0_next;
1902	for (;;) {
1903		uint32_t t0x;
1904
1905	t0_next:
1906		t0x = T0_NEXT(&ip);
1907		if (t0x < T0_INTERPRETED) {
1908			switch (t0x) {
1909				int32_t t0off;
1910
1911			case 0: /* ret */
1912				t0x = T0_RPOP();
1913				rp -= (t0x >> 16);
1914				t0x &= 0xFFFF;
1915				if (t0x == 0) {
1916					ip = NULL;
1917					goto t0_exit;
1918				}
1919				ip = &t0_codeblock[t0x];
1920				break;
1921			case 1: /* literal constant */
1922				T0_PUSHi(t0_parse7E_signed(&ip));
1923				break;
1924			case 2: /* read local */
1925				T0_PUSH(T0_LOCAL(t0_parse7E_unsigned(&ip)));
1926				break;
1927			case 3: /* write local */
1928				T0_LOCAL(t0_parse7E_unsigned(&ip)) = T0_POP();
1929				break;
1930			case 4: /* jump */
1931				t0off = t0_parse7E_signed(&ip);
1932				ip += t0off;
1933				break;
1934			case 5: /* jump if */
1935				t0off = t0_parse7E_signed(&ip);
1936				if (T0_POP()) {
1937					ip += t0off;
1938				}
1939				break;
1940			case 6: /* jump if not */
1941				t0off = t0_parse7E_signed(&ip);
1942				if (!T0_POP()) {
1943					ip += t0off;
1944				}
1945				break;");
1946
1947			SortedDictionary<int, string> nccode =
1948				new SortedDictionary<int, string>();
1949			foreach (string k in ccodeUni.Keys) {
1950				nccode[ccodeUni[k]] = k;
1951			}
1952			foreach (int sn in nccode.Keys) {
1953				tw.WriteLine(
1954@"			case {0}: {{
1955				/* {1} */
1956{2}
1957				}}
1958				break;", sn, ccodeNames[sn], nccode[sn]);
1959			}
1960
1961			tw.WriteLine(
1962@"			}
1963
1964		} else {
1965			T0_ENTER(ip, rp, t0x);
1966		}
1967	}
1968t0_exit:
1969	((t0_context *)t0ctx)->dp = dp;
1970	((t0_context *)t0ctx)->rp = rp;
1971	((t0_context *)t0ctx)->ip = ip;
1972}");
1973
1974			/*
1975			 * Add the "postamblr" elements here. These are
1976			 * elements that may need access to the data
1977			 * block or code block, so they must occur after
1978			 * their definition.
1979			 */
1980			foreach (string pp in extraCodeDefer) {
1981				tw.WriteLine();
1982				tw.WriteLine("{0}", pp);
1983			}
1984		}
1985
1986		int codeLen = 0;
1987		foreach (CodeElement ce in gcode) {
1988			codeLen += ce.GetLength(oneByteCode);
1989		}
1990		int dataBlockLen = 0;
1991		foreach (ConstData cd in blocks.Values) {
1992			dataBlockLen += cd.Length;
1993		}
1994
1995		/*
1996		 * Write some statistics on produced code.
1997		 */
1998		Console.WriteLine("code length: {0,6} byte(s)", codeLen);
1999		Console.WriteLine("data length: {0,6} byte(s)", dataLen);
2000		Console.WriteLine("total words: {0} (interpreted: {1})",
2001			slotInterpreted + numInterpreted, numInterpreted);
2002	}
2003
2004	internal Word Lookup(string name)
2005	{
2006		Word w = LookupNF(name);
2007		if (w != null) {
2008			return w;
2009		}
2010		throw new Exception(String.Format("No such word: '{0}'", name));
2011	}
2012
2013	internal Word LookupNF(string name)
2014	{
2015		Word w;
2016		words.TryGetValue(name, out w);
2017		return w;
2018	}
2019
2020	internal TValue StringToBlob(string s)
2021	{
2022		return new TValue(0, new TPointerBlob(this, s));
2023	}
2024
2025	internal bool TryParseLiteral(string tt, out TValue tv)
2026	{
2027		tv = new TValue(0);
2028		if (tt.StartsWith("\"")) {
2029			tv = StringToBlob(tt.Substring(1));
2030			return true;
2031		}
2032		if (tt.StartsWith("`")) {
2033			tv = DecodeCharConst(tt.Substring(1));
2034			return true;
2035		}
2036		bool neg = false;
2037		if (tt.StartsWith("-")) {
2038			neg = true;
2039			tt = tt.Substring(1);
2040		} else if (tt.StartsWith("+")) {
2041			tt = tt.Substring(1);
2042		}
2043		uint radix = 10;
2044		if (tt.StartsWith("0x") || tt.StartsWith("0X")) {
2045			radix = 16;
2046			tt = tt.Substring(2);
2047		} else if (tt.StartsWith("0b") || tt.StartsWith("0B")) {
2048			radix = 2;
2049			tt = tt.Substring(2);
2050		}
2051		if (tt.Length == 0) {
2052			return false;
2053		}
2054		uint acc = 0;
2055		bool overflow = false;
2056		uint maxV = uint.MaxValue / radix;
2057		foreach (char c in tt) {
2058			int d = HexVal(c);
2059			if (d < 0 || d >= radix) {
2060				return false;
2061			}
2062			if (acc > maxV) {
2063				overflow = true;
2064			}
2065			acc *= radix;
2066			if ((uint)d > uint.MaxValue - acc) {
2067				overflow = true;
2068			}
2069			acc += (uint)d;
2070		}
2071		int x = (int)acc;
2072		if (neg) {
2073			if (acc > (uint)0x80000000) {
2074				overflow = true;
2075			}
2076			x = -x;
2077		}
2078		if (overflow) {
2079			throw new Exception(
2080				"invalid literal integer (overflow)");
2081		}
2082		tv = x;
2083		return true;
2084	}
2085
2086	int ParseInteger(string tt)
2087	{
2088		TValue tv;
2089		if (!TryParseLiteral(tt, out tv)) {
2090			throw new Exception("not an integer: " + ToString());
2091		}
2092		return (int)tv;
2093	}
2094
2095	void CheckCompiling()
2096	{
2097		if (!compiling) {
2098			throw new Exception("Not in compilation mode");
2099		}
2100	}
2101
2102	static string EscapeCComment(string s)
2103	{
2104		StringBuilder sb = new StringBuilder();
2105		foreach (char c in s) {
2106			if (c >= 33 && c <= 126 && c != '%') {
2107				sb.Append(c);
2108			} else if (c < 0x100) {
2109				sb.AppendFormat("%{0:X2}", (int)c);
2110			} else if (c < 0x800) {
2111				sb.AppendFormat("%{0:X2}%{0:X2}",
2112					((int)c >> 6) | 0xC0,
2113					((int)c & 0x3F) | 0x80);
2114			} else {
2115				sb.AppendFormat("%{0:X2}%{0:X2}%{0:X2}",
2116					((int)c >> 12) | 0xE0,
2117					(((int)c >> 6) & 0x3F) | 0x80,
2118					((int)c & 0x3F) | 0x80);
2119			}
2120		}
2121		return sb.ToString().Replace("*/", "%2A/");
2122	}
2123}
2124