@graffiticode/parser 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/dist/api/src/config/config.d.ts +3 -0
  2. package/dist/api/src/config/config.js +4 -0
  3. package/dist/api/src/config/config.js.map +1 -0
  4. package/dist/api/src/config/index.d.ts +1 -0
  5. package/dist/api/src/config/index.js +3 -0
  6. package/dist/api/src/config/index.js.map +1 -0
  7. package/dist/api/src/lang/base-url.d.ts +7 -0
  8. package/dist/api/src/lang/base-url.js +24 -0
  9. package/dist/api/src/lang/base-url.js.map +1 -0
  10. package/dist/api/src/lang/compile.d.ts +4 -0
  11. package/dist/api/src/lang/compile.js +6 -0
  12. package/dist/api/src/lang/compile.js.map +1 -0
  13. package/dist/api/src/lang/get-asset.d.ts +4 -0
  14. package/dist/api/src/lang/get-asset.js +9 -0
  15. package/dist/api/src/lang/get-asset.js.map +1 -0
  16. package/dist/api/src/lang/index.d.ts +4 -0
  17. package/dist/api/src/lang/index.js +22 -0
  18. package/dist/api/src/lang/index.js.map +1 -0
  19. package/dist/api/src/lang/ping-lang.d.ts +5 -0
  20. package/dist/api/src/lang/ping-lang.js +31 -0
  21. package/dist/api/src/lang/ping-lang.js.map +1 -0
  22. package/dist/api/src/util.d.ts +23 -0
  23. package/dist/api/src/util.js +187 -0
  24. package/dist/api/src/util.js.map +1 -0
  25. package/dist/parser/src/ast.d.ts +58 -0
  26. package/dist/parser/src/ast.js +683 -0
  27. package/dist/parser/src/ast.js.map +1 -0
  28. package/dist/parser/src/env.d.ts +8 -0
  29. package/dist/parser/src/env.js +38 -0
  30. package/dist/parser/src/env.js.map +1 -0
  31. package/dist/parser/src/fold.d.ts +4 -0
  32. package/dist/parser/src/fold.js +217 -0
  33. package/dist/parser/src/fold.js.map +1 -0
  34. package/dist/parser/src/folder.d.ts +30 -0
  35. package/dist/parser/src/folder.js +231 -0
  36. package/dist/parser/src/folder.js.map +1 -0
  37. package/dist/parser/src/index.d.ts +5 -0
  38. package/dist/parser/src/index.js +6 -0
  39. package/dist/parser/src/index.js.map +1 -0
  40. package/dist/parser/src/parse.d.ts +56 -0
  41. package/dist/parser/src/parse.js +902 -0
  42. package/dist/parser/src/parse.js.map +1 -0
  43. package/dist/parser/src/parser.d.ts +17 -0
  44. package/dist/parser/src/parser.js +89 -0
  45. package/dist/parser/src/parser.js.map +1 -0
  46. package/dist/parser/src/parserForTests.d.ts +3 -0
  47. package/dist/parser/src/parserForTests.js +4 -0
  48. package/dist/parser/src/parserForTests.js.map +1 -0
  49. package/dist/parser/src/stringstream.d.ts +10 -0
  50. package/dist/parser/src/stringstream.js +21 -0
  51. package/dist/parser/src/stringstream.js.map +1 -0
  52. package/dist/parser/src/testing/index.d.ts +2 -0
  53. package/dist/parser/src/testing/index.js +17 -0
  54. package/dist/parser/src/testing/index.js.map +1 -0
  55. package/dist/parser/src/types.d.ts +44 -0
  56. package/dist/parser/src/types.js +2 -0
  57. package/dist/parser/src/types.js.map +1 -0
  58. package/package.json +5 -9
  59. package/src/ast.js +730 -0
  60. package/src/env.js +50 -0
  61. package/src/folder.js +249 -0
  62. package/src/folder.spec.js +42 -0
  63. package/src/parse.js +35 -935
  64. package/src/parse.spec.js +118 -0
  65. package/src/parse.spec.js~ +119 -0
  66. package/src/parse.ts~ +1037 -0
  67. package/src/parser.spec.js +11 -35
  68. package/src/{parser.js~ → parser.ts~} +25 -7
  69. package/src/stringstream.js +22 -0
  70. package/src/fold.js +0 -235
  71. package/src/parser.spec.js~ +0 -175
package/src/parse.ts~ ADDED
@@ -0,0 +1,1037 @@
1
+ // Copyright 2021, ARTCOMPILER INC
2
+ /*
3
+ Error handling
4
+ -- every range points to a node
5
+ -- multiple ranges can point to the same node
6
+ -- errors are associated with ranges
7
+ -- errors are determined by nodes in context
8
+
9
+ -- parser returns an ast which might compile to a list of errors of the form
10
+ {from, to, message, severity}
11
+ -- compiler checkers report these errors in the err callback arg
12
+ */
13
+
14
+ import assert from "assert";
15
+ import { folder } from "./fold.js";
16
+ import { Node, ParserContext, StringStream, Env, Word } from "./types.js";
17
+
18
+ // Global declarations for CodeMirror and window
19
+ let CodeMirror: { Pos: () => Record<string, any> };
20
+ if (typeof CodeMirror === "undefined") {
21
+ CodeMirror = {
22
+ Pos: function () {
23
+ return {};
24
+ }
25
+ };
26
+ }
27
+
28
+ let window: {
29
+ gcexports: {
30
+ coords: Record<string, any>;
31
+ errors: any[];
32
+ topEnv?: (ctx: ParserContext) => Env;
33
+ scanTime?: () => number;
34
+ scanCount?: () => number;
35
+ parseTime?: () => number;
36
+ parseCount?: () => number;
37
+ program?: Function;
38
+ keywords?: Record<string, Word>;
39
+ firstTime?: boolean;
40
+ };
41
+ isSynthetic?: boolean;
42
+ };
43
+
44
+ if (typeof window === "undefined") {
45
+ window = {
46
+ gcexports: {
47
+ coords: {},
48
+ errors: []
49
+ },
50
+ isSynthetic: true
51
+ };
52
+ }
53
+
54
+ // ast module
55
+ export const Ast = (function () {
56
+ const ASSERT = true;
57
+ const assert = function (val: any, str?: string): void {
58
+ if (!ASSERT) {
59
+ return;
60
+ }
61
+ if (str === undefined) {
62
+ str = "failed!";
63
+ }
64
+ if (!val) {
65
+ throw new Error(str);
66
+ }
67
+ };
68
+
69
+ class AstClass {
70
+ intern!: (ctx: ParserContext, n: any) => number;
71
+ node!: (ctx: ParserContext, nid: number) => Node | null;
72
+ dump!: (n: any) => string;
73
+ dumpAll!: (ctx: ParserContext) => string;
74
+ poolToJSON!: (ctx: ParserContext) => Record<string, any>;
75
+ number!: (ctx: ParserContext, num: string | number, coord?: any) => void;
76
+ string!: (ctx: ParserContext, str: string, coord?: any) => void;
77
+ name!: (ctx: ParserContext, name: string, coord?: any) => void;
78
+ apply!: (ctx: ParserContext, fnId: number, argc: number) => void;
79
+ fold!: (ctx: ParserContext, fn: any, args: any[]) => void;
80
+ expr!: (ctx: ParserContext, argc: number) => void;
81
+ binaryExpr!: (ctx: ParserContext, name: string) => void;
82
+ unaryExpr!: (ctx: ParserContext, name: string) => void;
83
+ parenExpr!: (ctx: ParserContext, coord?: any) => void;
84
+ prefixExpr!: (ctx: ParserContext, name: string) => void;
85
+ lambda!: (ctx: ParserContext, env: any, nid: number) => void;
86
+ applyLate!: (ctx: ParserContext, count: number) => void;
87
+ letDef!: (ctx: ParserContext) => void;
88
+ ifExpr!: (ctx: ParserContext) => void;
89
+ caseExpr!: (ctx: ParserContext, n: number) => void;
90
+ ofClause!: (ctx: ParserContext) => void;
91
+ record!: (ctx: ParserContext) => void;
92
+ binding!: (ctx: ParserContext) => void;
93
+ exprs!: (ctx: ParserContext, count: number, inReverse?: boolean) => void;
94
+ program!: (ctx: ParserContext) => void;
95
+ pop!: (ctx: ParserContext) => number;
96
+ topNode!: (ctx: ParserContext) => number;
97
+ peek!: (ctx: ParserContext, n?: number) => number;
98
+ push!: (ctx: ParserContext, node: any) => void;
99
+ mod!: (ctx: ParserContext) => void;
100
+ add!: (ctx: ParserContext, coord?: any) => void;
101
+ sub!: (ctx: ParserContext) => void;
102
+ div!: (ctx: ParserContext) => void;
103
+ pow!: (ctx: ParserContext) => void;
104
+ concat!: (ctx: ParserContext) => void;
105
+ eq!: (ctx: ParserContext) => void;
106
+ ne!: (ctx: ParserContext) => void;
107
+ lt!: (ctx: ParserContext) => void;
108
+ gt!: (ctx: ParserContext) => void;
109
+ le!: (ctx: ParserContext) => void;
110
+ ge!: (ctx: ParserContext) => void;
111
+ neg!: (ctx: ParserContext) => void;
112
+ list!: (ctx: ParserContext, count: number, coord?: any, reverse?: boolean) => void;
113
+ bool!: (ctx: ParserContext, val: any) => void;
114
+ nul!: (ctx: ParserContext) => void;
115
+ error!: (ctx: ParserContext, str: string, coord?: any) => void;
116
+ }
117
+
118
+ const astClass = new AstClass();
119
+
120
+ // Assign prototype methods to astClass
121
+ function push(ctx: ParserContext, node: any): void {
122
+ let nid: number;
123
+ if (typeof node === "number") { // if already interned
124
+ nid = node;
125
+ } else {
126
+ nid = intern(ctx, node);
127
+ }
128
+ ctx.state.nodeStack.push(nid);
129
+ }
130
+
131
+ function topNode(ctx: ParserContext): number {
132
+ const nodeStack = ctx.state.nodeStack;
133
+ return nodeStack[nodeStack.length - 1];
134
+ }
135
+
136
+ function pop(ctx: ParserContext): number {
137
+ const nodeStack = ctx.state.nodeStack;
138
+ return nodeStack.pop()!;
139
+ }
140
+
141
+ function peek(ctx: ParserContext, n?: number): number {
142
+ if (n === undefined) {
143
+ n = 0;
144
+ }
145
+ const nodeStack = ctx.state.nodeStack;
146
+ return nodeStack[nodeStack.length - 1 - n];
147
+ }
148
+
149
+ function intern(ctx: ParserContext, n: any): number {
150
+ if (!n) {
151
+ return 0;
152
+ }
153
+ const nodeMap = ctx.state.nodeMap;
154
+ const nodePool = ctx.state.nodePool;
155
+ const tag = n.tag;
156
+ let elts = "";
157
+ const count = n.elts.length;
158
+ for (let i = 0; i < count; i++) {
159
+ if (typeof n.elts[i] === "object") {
160
+ n.elts[i] = intern(ctx, n.elts[i]);
161
+ }
162
+ elts += n.elts[i];
163
+ }
164
+ const key = tag + count + elts;
165
+ let nid = nodeMap[key];
166
+ if (nid === undefined) {
167
+ nodePool.push({ tag, elts: n.elts });
168
+ nid = nodePool.length - 1;
169
+ nodeMap[key] = nid;
170
+ if (n.coord) {
171
+ ctx.state.coords[nid] = n.coord;
172
+ }
173
+ }
174
+ return nid;
175
+ }
176
+
177
+ function node(ctx: ParserContext, nid: number): Node | null {
178
+ const n = ctx.state.nodePool[nid];
179
+ if (!nid) {
180
+ return null;
181
+ } else if (!n) {
182
+ return {} as Node;
183
+ }
184
+ const elts: any[] = [];
185
+ switch (n.tag) {
186
+ case "NULL":
187
+ break;
188
+ case "NUM":
189
+ case "STR":
190
+ case "IDENT":
191
+ case "BOOL":
192
+ elts[0] = n.elts[0];
193
+ break;
194
+ default:
195
+ for (let i = 0; i < n.elts.length; i++) {
196
+ elts[i] = node(ctx, n.elts[i]);
197
+ }
198
+ break;
199
+ }
200
+ return {
201
+ tag: n.tag,
202
+ elts,
203
+ coord: getCoord(ctx)
204
+ };
205
+ }
206
+
207
+ function dumpAll(ctx: ParserContext): string {
208
+ const nodePool = ctx.state.nodePool;
209
+ let s = "\n{";
210
+ for (let i = 1; i < nodePool.length; i++) {
211
+ const n = nodePool[i];
212
+ s = s + "\n " + i + ": " + dump(n) + ",";
213
+ }
214
+ s += "\n root: " + (nodePool.length - 1);
215
+ s += "\n}\n";
216
+ return s;
217
+ }
218
+
219
+ function poolToJSON(ctx: ParserContext): Record<string, any> {
220
+ const nodePool = ctx.state.nodePool;
221
+ const obj: Record<string, any> = {};
222
+ for (let i = 1; i < nodePool.length; i++) {
223
+ const n = nodePool[i];
224
+ obj[i] = nodeToJSON(n);
225
+ }
226
+ obj.root = (nodePool.length - 1);
227
+ return obj;
228
+ }
229
+
230
+ function nodeToJSON(n: any): any {
231
+ let obj;
232
+ if (typeof n === "object") {
233
+ switch (n.tag) {
234
+ case "num":
235
+ obj = n.elts[0];
236
+ break;
237
+ case "str":
238
+ obj = n.elts[0];
239
+ break;
240
+ default:
241
+ obj = {};
242
+ obj.tag = n.tag;
243
+ obj.elts = [];
244
+ for (let i = 0; i < n.elts.length; i++) {
245
+ obj.elts[i] = nodeToJSON(n.elts[i]);
246
+ }
247
+ break;
248
+ }
249
+ } else if (typeof n === "string") {
250
+ obj = n;
251
+ } else {
252
+ obj = n;
253
+ }
254
+ return obj;
255
+ }
256
+
257
+ function dump(n: any): string {
258
+ let s = "";
259
+ if (typeof n === "object") {
260
+ switch (n.tag) {
261
+ case "num":
262
+ s = n.elts[0];
263
+ break;
264
+ case "str":
265
+ s = "\"" + n.elts[0] + "\"";
266
+ break;
267
+ default:
268
+ if (!n.elts) {
269
+ s += "<invalid>";
270
+ } else {
271
+ s = "{ tag: \"" + n.tag + "\", elts: [ ";
272
+ for (let i = 0; i < n.elts.length; i++) {
273
+ if (i > 0) {
274
+ s += " , ";
275
+ }
276
+ s += dump(n.elts[i]);
277
+ }
278
+ s += " ] }";
279
+ }
280
+ break;
281
+ }
282
+ } else if (typeof n === "string") {
283
+ s = "\"" + n + "\"";
284
+ } else {
285
+ s = String(n);
286
+ }
287
+ return s;
288
+ }
289
+
290
+ function fold(ctx: ParserContext, fn: any, args: any[]): void {
291
+ // Local defs:
292
+ // -- put bindings in env
293
+ // Three cases:
294
+ // -- full application, all args are available at parse time
295
+ // -- partial application, only some args are available at parse time
296
+ // -- late application, args are available at compile time (not parse time)
297
+ // apply <[x y]: add x y> data..
298
+ // x: val 0 data
299
+ // y: val 1 data
300
+ env.enterEnv(ctx, fn.name);
301
+ if (fn.env) {
302
+ const lexicon = fn.env.lexicon;
303
+ const pattern = Ast.node(ctx, fn.env.pattern);
304
+ let outerEnv: Record<string, any> | null = null;
305
+ // let isListPattern;
306
+ // setup inner environment record (lexicon)
307
+ if (pattern && pattern.elts &&
308
+ pattern.elts.length === 1 &&
309
+ pattern.elts[0].tag === "LIST") {
310
+ // For now we only support one pattern per param list.
311
+ // isListPattern = true;
312
+ }
313
+ for (const id in lexicon) {
314
+ // For each parameter, get its definition assign the value of the argument
315
+ // used on the current function application.
316
+ if (!id) continue;
317
+ const word = JSON.parse(JSON.stringify(lexicon[id])); // poor man's copy.
318
+ const index = args.length - word.offset - 1;
319
+ // TODO we currently ignore list patterns
320
+ // if (isListPattern) {
321
+ // // <[x y]: ...> foo..
322
+ // word.nid = Ast.intern(ctx, {
323
+ // tag: "VAL",
324
+ // elts: [{
325
+ // tag: "NUM",
326
+ // elts: [
327
+ // String(word.offset),
328
+ // ]}, {
329
+ // tag: "ARG",
330
+ // elts: [{
331
+ // tag: "NUM",
332
+ // elts: ["0"]
333
+ // }]
334
+ // }]
335
+ // });
336
+ // } else
337
+ if (index >= 0 && index < args.length) {
338
+ word.nid = args[index];
339
+ }
340
+ if (index < 0) {
341
+ // We've got an unbound variable or a variable with a default value,
342
+ // so add it to the new variable list.
343
+ // <x:x> => <x:x>
344
+ // (<x y: add x y> 10) => <y: add 10 y>
345
+ // (<y: let x = 10.. add x y>) => <y: add 10 y>
346
+ if (!outerEnv) {
347
+ outerEnv = {};
348
+ }
349
+ outerEnv[id] = word;
350
+ }
351
+ env.addWord(ctx, id, word);
352
+ }
353
+ folder.fold(ctx, fn.nid);
354
+ if (outerEnv) {
355
+ lambda(ctx, {
356
+ lexicon: outerEnv,
357
+ pattern // FIXME need to trim pattern if some args where applied.
358
+ }, pop(ctx));
359
+ }
360
+ }
361
+ env.exitEnv(ctx);
362
+ }
363
+
364
+ function applyLate(ctx: ParserContext, count: number): void {
365
+ // Ast.applyLate
366
+ const elts = [];
367
+ for (let i = count; i > 0; i--) {
368
+ elts.push(pop(ctx));
369
+ }
370
+ push(ctx, {
371
+ tag: "APPLY",
372
+ elts
373
+ });
374
+ }
375
+
376
+ function apply(ctx: ParserContext, fnId: number, argc: number): void {
377
+ // Construct function and apply available arguments.
378
+ const fn = node(ctx, fnId);
379
+ // if (fn.tag !== "LAMBDA") {
380
+ // // Construct an APPLY node for compiling later.
381
+ // return {
382
+ // tag: "APPLY",
383
+ // elts: [
384
+ // fnId,
385
+ // ]
386
+ // };
387
+ // }
388
+ // Construct a lexicon
389
+ const lexicon: Record<string, any> = {};
390
+ let paramc = 0;
391
+ fn!.elts[0].elts.forEach(function (n, i) {
392
+ const name = n.elts[0];
393
+ const nid = Ast.intern(ctx, fn!.elts[3].elts[i]);
394
+ lexicon[name] = {
395
+ cls: "val",
396
+ name,
397
+ offset: i,
398
+ nid
399
+ };
400
+ if (!nid) {
401
+ // Parameters don't have nids.
402
+ // assert that there are parameters after a binding without a nid.
403
+ paramc++;
404
+ }
405
+ });
406
+ const def = {
407
+ name: "lambda",
408
+ nid: Ast.intern(ctx, fn!.elts[1]),
409
+ env: {
410
+ lexicon,
411
+ pattern: Ast.intern(ctx, fn!.elts[2])
412
+ }
413
+ };
414
+ const elts = [];
415
+ // While there are args on the stack, pop them.
416
+ while (argc-- > 0 && paramc-- > 0) {
417
+ const elt = pop(ctx);
418
+ elts.unshift(elt); // Get the order right.
419
+ }
420
+ fold(ctx, def, elts);
421
+ }
422
+
423
+ // Node constructors
424
+
425
+ function error(ctx: ParserContext, str: string, coord?: any): void {
426
+ console.log(
427
+ "error()",
428
+ "str=" + str,
429
+ "coord=" + JSON.stringify(coord),
430
+ );
431
+ const from = coord?.from !== undefined ? coord.from : -1;
432
+ const to = coord?.to !== undefined ? coord.to : -1;
433
+ number(ctx, to);
434
+ number(ctx, from);
435
+ string(ctx, str, coord);
436
+ push(ctx, {
437
+ tag: "ERROR",
438
+ elts: [
439
+ pop(ctx),
440
+ pop(ctx),
441
+ pop(ctx),
442
+ ],
443
+ coord
444
+ });
445
+ }
446
+
447
+ function bool(ctx: ParserContext, val: any): void {
448
+ let b;
449
+ if (val) {
450
+ b = true;
451
+ } else {
452
+ b = false;
453
+ }
454
+ push(ctx, {
455
+ tag: "BOOL",
456
+ elts: [b]
457
+ });
458
+ }
459
+
460
+ function nul(ctx: ParserContext): void {
461
+ push(ctx, {
462
+ tag: "NULL",
463
+ elts: []
464
+ });
465
+ }
466
+
467
+ function number(ctx: ParserContext, num: string | number, coord?: any): void {
468
+ assert(typeof num === "string" || typeof num === "number");
469
+ push(ctx, {
470
+ tag: "NUM",
471
+ elts: [String(num)],
472
+ coord
473
+ });
474
+ }
475
+
476
+ function string(ctx: ParserContext, str: string, coord?: any): void {
477
+ push(ctx, {
478
+ tag: "STR",
479
+ elts: [str],
480
+ coord
481
+ });
482
+ }
483
+
484
+ function name(ctx: ParserContext, name: string, coord?: any): void {
485
+ push(ctx, {
486
+ tag: "IDENT",
487
+ elts: [name],
488
+ coord
489
+ });
490
+ }
491
+
492
+ function expr(ctx: ParserContext, argc: number): void {
493
+ // Ast.expr -- construct a expr node for the compiler.
494
+ const elts = [];
495
+ const pos = getPos(ctx);
496
+ console.trace(
497
+ "expr()",
498
+ "argc=" + argc,
499
+ "nodeStack=" + JSON.stringify(ctx.state.nodeStack, null, 2),
500
+ );
501
+ assertErr(ctx, argc <= ctx.state.nodeStack.length - 1, `Too few arguments. Expected ${argc} got ${ctx.state.nodeStack.length - 1}.`, {
502
+ from: pos - 1, to: pos
503
+ });
504
+ while (argc--) {
505
+ const elt = pop(ctx);
506
+ elts.push(elt);
507
+ }
508
+ const nameId = pop(ctx);
509
+ console.log(
510
+ "expr()",
511
+ "nameId=" + nameId,
512
+ );
513
+ assertErr(ctx, nameId, "Ill formed node.");
514
+ const e = node(ctx, nameId)!.elts;
515
+ assertErr(ctx, e && e.length > 0, "Ill formed node.");
516
+ const nodeName = e[0];
517
+ push(ctx, {
518
+ tag: nodeName,
519
+ elts,
520
+ coord: getCoord(ctx)
521
+ });
522
+ }
523
+
524
+ function parenExpr(ctx: ParserContext, coord?: any): void {
525
+ // Ast.parenExpr
526
+ const elts = [];
527
+ const elt = pop(ctx);
528
+ elts.push(elt);
529
+ push(ctx, {
530
+ tag: "PAREN",
531
+ elts,
532
+ coord
533
+ });
534
+ }
535
+
536
+ function list(ctx: ParserContext, count: number, coord?: any, reverse?: boolean): void {
537
+ // Ast.list
538
+ const elts = [];
539
+ for (let i = count; i > 0; i--) {
540
+ const elt = pop(ctx);
541
+ if (elt !== undefined) {
542
+ elts.push(elt);
543
+ }
544
+ }
545
+ push(ctx, {
546
+ tag: "LIST",
547
+ elts: reverse ? elts : elts.reverse(),
548
+ coord
549
+ });
550
+ }
551
+
552
+ function binaryExpr(ctx: ParserContext, name: string): void {
553
+ const elts = [];
554
+ // args are in the order produced by the parser
555
+ elts.push(pop(ctx));
556
+ elts.push(pop(ctx));
557
+ push(ctx, {
558
+ tag: name,
559
+ elts: elts.reverse()
560
+ });
561
+ }
562
+
563
+ function unaryExpr(ctx: ParserContext, name: string): void {
564
+ const elts = [];
565
+ elts.push(pop(ctx));
566
+ push(ctx, {
567
+ tag: name,
568
+ elts
569
+ });
570
+ }
571
+
572
+ function prefixExpr(ctx: ParserContext, name: string): void {
573
+ const elts = [];
574
+ elts.push(pop(ctx));
575
+ push(ctx, {
576
+ tag: name,
577
+ elts
578
+ });
579
+ }
580
+
581
+ function neg(ctx: ParserContext): void {
582
+ const v1 = +node(ctx, pop(ctx))!.elts[0];
583
+ number(ctx, -1 * v1);
584
+ }
585
+
586
+ function add(ctx: ParserContext, coord?: any): void {
587
+ const n2 = node(ctx, pop(ctx))!;
588
+ const n1 = node(ctx, pop(ctx))!;
589
+ const v2 = n2.elts[0];
590
+ const v1 = n1.elts[0];
591
+ if (n1.tag !== "NUM" || n2.tag !== "NUM") {
592
+ push(ctx, {
593
+ tag: "ADD",
594
+ elts: [n1, n2],
595
+ coord
596
+ });
597
+ } else {
598
+ number(ctx, +v1 + +v2);
599
+ }
600
+ }
601
+
602
+ function sub(ctx: ParserContext): void {
603
+ const n2 = node(ctx, pop(ctx))!;
604
+ const n1 = node(ctx, pop(ctx))!;
605
+ const v2 = n2.elts[0];
606
+ const v1 = n1.elts[0];
607
+ if (n1.tag !== "NUM" || n2.tag !== "NUM") {
608
+ push(ctx, { tag: "SUB", elts: [n1, n2] });
609
+ } else {
610
+ number(ctx, +v1 - +v2);
611
+ }
612
+ }
613
+
614
+ // function mul(ctx: ParserContext): void {
615
+ // let n2 = node(ctx, pop(ctx));
616
+ // let n1 = node(ctx, pop(ctx));
617
+ // const v2 = n2.elts[0];
618
+ // const v1 = n1.elts[0];
619
+ // if (n1.tag === undefined) {
620
+ // n1 = n1.elts[0];
621
+ // }
622
+ // if (n2.tag === undefined) {
623
+ // n2 = n2.elts[0];
624
+ // }
625
+ // if (n1.tag !== "NUM" || n2.tag !== "NUM") {
626
+ // push(ctx, { tag: "MUL", elts: [n2, n1] });
627
+ // } else {
628
+ // number(ctx, +v1 * +v2);
629
+ // }
630
+ // }
631
+
632
+ function div(ctx: ParserContext): void {
633
+ const n2 = node(ctx, pop(ctx))!;
634
+ const n1 = node(ctx, pop(ctx))!;
635
+ const v2 = n2.elts[0];
636
+ const v1 = n1.elts[0];
637
+ if (n1.tag !== "NUM" || n2.tag !== "NUM") {
638
+ push(ctx, { tag: "DIV", elts: [n1, n2] });
639
+ } else {
640
+ number(ctx, +v1 / +v2);
641
+ }
642
+ }
643
+
644
+ function mod(ctx: ParserContext): void {
645
+ const n2 = node(ctx, pop(ctx))!;
646
+ const n1 = node(ctx, pop(ctx))!;
647
+ const v1 = n1.elts[0];
648
+ const v2 = n2.elts[0];
649
+ if (n1.tag !== "NUM" || n2.tag !== "NUM") {
650
+ push(ctx, { tag: "MOD", elts: [n1, n2] });
651
+ } else {
652
+ number(ctx, +v1 % +v2);
653
+ }
654
+ }
655
+
656
+ function pow(ctx: ParserContext): void {
657
+ const n2 = node(ctx, pop(ctx))!;
658
+ const n1 = node(ctx, pop(ctx))!;
659
+ const v2 = n2.elts[0];
660
+ const v1 = n1.elts[0];
661
+ if (n1.tag !== "NUM" || n2.tag !== "NUM") {
662
+ push(ctx, { tag: "POW", elts: [n1, n2] });
663
+ } else {
664
+ number(ctx, Math.pow(+v1, +v2));
665
+ }
666
+ }
667
+
668
+ function concat(ctx: ParserContext): void {
669
+ const n1 = node(ctx, pop(ctx))!;
670
+ push(ctx, {
671
+ tag: "CONCAT",
672
+ elts: [n1]
673
+ });
674
+ }
675
+
676
+ function eq(ctx: ParserContext): void {
677
+ const v2 = node(ctx, pop(ctx))!.elts[0];
678
+ const v1 = node(ctx, pop(ctx))!.elts[0];
679
+ bool(ctx, v1 === v2);
680
+ }
681
+
682
+ function ne(ctx: ParserContext): void {
683
+ const v2 = +node(ctx, pop(ctx))!.elts[0];
684
+ const v1 = +node(ctx, pop(ctx))!.elts[0];
685
+ bool(ctx, v1 !== v2);
686
+ }
687
+
688
+ function lt(ctx: ParserContext): void {
689
+ const v2 = +node(ctx, pop(ctx))!.elts[0];
690
+ const v1 = +node(ctx, pop(ctx))!.elts[0];
691
+ bool(ctx, v1 < v2);
692
+ }
693
+
694
+ function gt(ctx: ParserContext): void {
695
+ const v2 = +node(ctx, pop(ctx))!.elts[0];
696
+ const v1 = +node(ctx, pop(ctx))!.elts[0];
697
+ bool(ctx, v1 > v2);
698
+ }
699
+
700
+ function le(ctx: ParserContext): void {
701
+ const v2 = +node(ctx, pop(ctx))!.elts[0];
702
+ const v1 = +node(ctx, pop(ctx))!.elts[0];
703
+ bool(ctx, v1 <= v2);
704
+ }
705
+
706
+ function ge(ctx: ParserContext): void {
707
+ const v2 = +node(ctx, pop(ctx))!.elts[0];
708
+ const v1 = +node(ctx, pop(ctx))!.elts[0];
709
+ bool(ctx, v1 >= v2);
710
+ }
711
+
712
+ function ifExpr(ctx: ParserContext): void {
713
+ const elts = [];
714
+ elts.push(pop(ctx)); // if
715
+ elts.push(pop(ctx)); // then
716
+ elts.push(pop(ctx)); // else
717
+ push(ctx, { tag: "IF", elts: elts.reverse() });
718
+ }
719
+
720
+ function caseExpr(ctx: ParserContext, n: number): void {
721
+ const elts = [];
722
+ elts.push(pop(ctx)); // val
723
+ for (let i = n; i > 0; i--) {
724
+ elts.push(pop(ctx)); // of
725
+ }
726
+ push(ctx, { tag: "CASE", elts: elts.reverse() });
727
+ }
728
+
729
+ function ofClause(ctx: ParserContext): void {
730
+ const elts = [];
731
+ elts.push(pop(ctx));
732
+ elts.push(pop(ctx));
733
+ push(ctx, { tag: "OF", elts: elts.reverse() });
734
+ }
735
+
736
+ function record(ctx: ParserContext): void {
737
+ // Ast.record
738
+ const count = ctx.state.exprc;
739
+ const elts = [];
740
+ for (let i = count; i > 0; i--) {
741
+ const elt = pop(ctx);
742
+ if (elt !== undefined) {
743
+ elts.push(elt);
744
+ }
745
+ }
746
+ push(ctx, {
747
+ tag: "RECORD",
748
+ elts
749
+ });
750
+ }
751
+
752
+ function binding(ctx: ParserContext): void {
753
+ // Ast.binding
754
+ const elts = [];
755
+ elts.push(pop(ctx));
756
+ elts.push(pop(ctx));
757
+ push(ctx, {
758
+ tag: "BINDING",
759
+ elts: elts.reverse()
760
+ });
761
+ }
762
+
763
+ function lambda(ctx: ParserContext, env: any, nid: number): void {
764
+ // Ast.lambda
765
+ const names = [];
766
+ const nids = [];
767
+ for (const id in env.lexicon) {
768
+ const word = env.lexicon[id];
769
+ names.push({
770
+ tag: "IDENT",
771
+ elts: [word.name],
772
+ coord: getCoord(ctx)
773
+ });
774
+ nids.push(word.nid || 0);
775
+ }
776
+ const pattern = env.pattern;
777
+ push(ctx, {
778
+ tag: "LAMBDA",
779
+ elts: [{
780
+ tag: "LIST",
781
+ elts: names
782
+ }, nid, {
783
+ tag: "LIST",
784
+ elts: pattern
785
+ }, {
786
+ tag: "LIST",
787
+ elts: nids
788
+ }]
789
+ });
790
+ }
791
+
792
+ function exprs(ctx: ParserContext, count: number, inReverse?: boolean): void {
793
+ // Ast.exprs
794
+ let elts = [];
795
+ assert(ctx.state.nodeStack.length >= count);
796
+ if (inReverse) {
797
+ for (let i = count; i > 0; i--) {
798
+ const elt = pop(ctx);
799
+ elts.push(elt); // Reverse order.
800
+ }
801
+ } else {
802
+ for (let i = count; i > 0; i--) {
803
+ const elt = pop(ctx);
804
+ elts.push(elt); // Reverse order.
805
+ }
806
+ elts = elts.reverse();
807
+ }
808
+ push(ctx, {
809
+ tag: "EXPRS",
810
+ elts
811
+ });
812
+ }
813
+
814
+ function letDef(ctx: ParserContext): void {
815
+ // Clean up stack and produce initializer.
816
+ pop(ctx); // body
817
+ pop(ctx); // name
818
+ for (let i = 0; i < ctx.state.paramc; i++) {
819
+ pop(ctx); // params
820
+ }
821
+ ctx.state.exprc--; // don't count as expr.
822
+ }
823
+
824
+ function program(ctx: ParserContext): void {
825
+ const elts = [];
826
+ elts.push(pop(ctx));
827
+ push(ctx, {
828
+ tag: "PROG",
829
+ elts
830
+ });
831
+ }
832
+
833
+ // Assign all the methods to astClass
834
+ astClass.intern = intern;
835
+ astClass.node = node;
836
+ astClass.dump = dump;
837
+ astClass.dumpAll = dumpAll;
838
+ astClass.poolToJSON = poolToJSON;
839
+ astClass.number = number;
840
+ astClass.string = string;
841
+ astClass.name = name;
842
+ astClass.apply = apply;
843
+ astClass.fold = fold;
844
+ astClass.expr = expr;
845
+ astClass.binaryExpr = binaryExpr;
846
+ astClass.unaryExpr = unaryExpr;
847
+ astClass.parenExpr = parenExpr;
848
+ astClass.prefixExpr = prefixExpr;
849
+ astClass.lambda = lambda;
850
+ astClass.applyLate = applyLate;
851
+ astClass.letDef = letDef;
852
+ astClass.ifExpr = ifExpr;
853
+ astClass.caseExpr = caseExpr;
854
+ astClass.ofClause = ofClause;
855
+ astClass.record = record;
856
+ astClass.binding = binding;
857
+ astClass.exprs = exprs;
858
+ astClass.program = program;
859
+ astClass.pop = pop;
860
+ astClass.topNode = topNode;
861
+ astClass.peek = peek;
862
+ astClass.push = push;
863
+ astClass.mod = mod;
864
+ astClass.add = add;
865
+ astClass.sub = sub;
866
+ astClass.div = div;
867
+ astClass.pow = pow;
868
+ astClass.concat = concat;
869
+ astClass.eq = eq;
870
+ astClass.ne = ne;
871
+ astClass.lt = lt;
872
+ astClass.gt = gt;
873
+ astClass.le = le;
874
+ astClass.ge = ge;
875
+ astClass.neg = neg;
876
+ astClass.list = list;
877
+ astClass.bool = bool;
878
+ astClass.nul = nul;
879
+ astClass.error = error;
880
+
881
+ return astClass;
882
+ })();
883
+
884
+ // The following code for StreamString was copied from CodeMirror.
885
+ class StringStream implements StringStream {
886
+ pos: number;
887
+ start: number;
888
+ string: string;
889
+ tabSize: number;
890
+
891
+ constructor(string: string, tabSize?: number) {
892
+ this.pos = this.start = 0;
893
+ this.string = string;
894
+ this.tabSize = tabSize || 8;
895
+ }
896
+
897
+ eol(): boolean {
898
+ return this.pos >= this.string.length;
899
+ }
900
+
901
+ sol(): boolean {
902
+ return this.pos === 0;
903
+ }
904
+
905
+ peek(): string | undefined {
906
+ return this.string.charAt(this.pos) || undefined;
907
+ }
908
+
909
+ next(): string | undefined {
910
+ if (this.pos < this.string.length) {
911
+ return this.string.charAt(this.pos++);
912
+ }
913
+ return undefined;
914
+ }
915
+
916
+ eat(match: string | RegExp | ((ch: string) => boolean)): string | undefined {
917
+ const ch = this.string.charAt(this.pos);
918
+ let ok;
919
+ if (typeof match === "string") {
920
+ ok = ch === match;
921
+ } else {
922
+ ok = ch && (match instanceof RegExp ? match.test(ch) : match(ch));
923
+ }
924
+ if (ok) {
925
+ ++this.pos;
926
+ return ch;
927
+ }
928
+ return undefined;
929
+ }
930
+
931
+ eatWhile(match: string | RegExp | ((ch: string) => boolean)): boolean {
932
+ const start = this.pos;
933
+ while (this.eat(match) !== undefined) {}
934
+ return this.pos > start;
935
+ }
936
+
937
+ eatSpace(): boolean {
938
+ const start = this.pos;
939
+ while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) ++this.pos;
940
+ return this.pos > start;
941
+ }
942
+
943
+ skipToEnd(): void {
944
+ this.pos = this.string.length;
945
+ }
946
+
947
+ skipTo(ch: string): boolean | undefined {
948
+ const found = this.string.indexOf(ch, this.pos);
949
+ if (found > -1) {
950
+ this.pos = found;
951
+ return true;
952
+ }
953
+ return undefined;
954
+ }
955
+
956
+ backUp(n: number): void {
957
+ this.pos -= n;
958
+ }
959
+
960
+ match(pattern: string | RegExp, consume?: boolean, caseInsensitive?: boolean): boolean | RegExpMatchArray | null {
961
+ assert(false, "Should not get here");
962
+ if (typeof pattern === "string") {
963
+ const cased = function (str: string) {
964
+ return caseInsensitive ? str.toLowerCase() : str;
965
+ };
966
+ if (cased(this.string).indexOf(cased(pattern), this.pos) === this.pos) {
967
+ if (consume !== false) this.pos += pattern.length;
968
+ return true;
969
+ }
970
+ } else {
971
+ const match = this.string.slice(this.pos).match(pattern);
972
+ if (match && match.index! > 0) return null;
973
+ if (match && consume !== false) this.pos += match[0].length;
974
+ return match;
975
+ }
976
+ return false;
977
+ }
978
+
979
+ current(): string {
980
+ return this.string.slice(this.start, this.pos);
981
+ }
982
+ }
983
+
984
+ // env module
985
+ export const env = (function () {
986
+ return {
987
+ findWord,
988
+ addWord,
989
+ enterEnv,
990
+ exitEnv,
991
+ addPattern
992
+ };
993
+
994
+ // private functions
995
+ function findWord(ctx: ParserContext, lexeme: string): any {
996
+ const env = ctx.state.env;
997
+ for (let i = env.length - 1; i >= 0; i--) {
998
+ const word = env[i].lexicon[lexeme];
999
+ if (word) {
1000
+ return word;
1001
+ }
1002
+ }
1003
+ return null;
1004
+ }
1005
+
1006
+ function addWord(ctx: ParserContext, lexeme: string, entry: any): null {
1007
+ window.gcexports.topEnv!(ctx).lexicon[lexeme] = entry;
1008
+ return null;
1009
+ }
1010
+
1011
+ function addPattern(ctx: ParserContext, pattern: any): void {
1012
+ window.gcexports.topEnv!(ctx).pattern.push(pattern);
1013
+ }
1014
+
1015
+ function enterEnv(ctx: ParserContext, name: string): void {
1016
+ // recursion guard
1017
+ if (ctx.state.env.length > 380) {
1018
+ console.trace(
1019
+ "enterEnv()",
1020
+ "name=" + name,
1021
+ );
1022
+ // return; // just stop recursing
1023
+ throw new Error("runaway recursion");
1024
+ }
1025
+ window.gcexports.topEnv!(ctx).paramc = ctx.state.paramc;
1026
+ ctx.state.env.push({
1027
+ name,
1028
+ lexicon: {},
1029
+ pattern: []
1030
+ });
1031
+ }
1032
+
1033
+ function exitEnv(ctx: ParserContext): void {
1034
+ ctx.state.env.pop();
1035
+ ctx.state.paramc = window.gcexports.topEnv!(ctx).paramc!;
1036
+ }
1037
+ })();