]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/bearssl/T0/T0Comp.cs
Optionally bind ktls threads to NUMA domains
[FreeBSD/FreeBSD.git] / contrib / bearssl / T0 / T0Comp.cs
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
25 using System;
26 using System.Collections.Generic;
27 using System.IO;
28 using System.Reflection;
29 using System.Text;
30
31 /*
32  * This is the main compiler class.
33  */
34
35 public 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
1693 typedef struct {
1694         uint32_t *dp;
1695         uint32_t *rp;
1696         const unsigned char *ip;
1697 } t0_context;
1698
1699 static uint32_t
1700 t0_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
1716 static int32_t
1717 t0_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) \
1825 void \
1826 name(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         }
1968 t0_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 }