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