@graffiticode/parser 0.1.0 → 0.1.1

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 (67) 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 +2 -4
  59. package/src/ast.js +724 -0
  60. package/src/env.js +46 -0
  61. package/src/folder.js +244 -0
  62. package/src/parse.js +35 -935
  63. package/src/parse.ts~ +1037 -0
  64. package/src/{parser.js~ → parser.ts~} +25 -7
  65. package/src/stringstream.js +22 -0
  66. package/src/fold.js +0 -235
  67. package/src/parser.spec.js~ +0 -175
package/src/parse.js CHANGED
@@ -11,8 +11,10 @@
11
11
  -- compiler checkers report these errors in the err callback arg
12
12
  */
13
13
 
14
- import assert from "assert";
15
- import { folder } from "./fold.js";
14
+ import { folder } from "./folder.js";
15
+ import { Ast } from "./ast.js";
16
+ import { Env } from "./env.js";
17
+ import { StringStream } from "./stringstream.js";
16
18
 
17
19
  let CodeMirror;
18
20
  if (typeof CodeMirror === "undefined") {
@@ -35,907 +37,6 @@ if (typeof window === "undefined") {
35
37
  };
36
38
  }
37
39
 
38
- // ast module
39
-
40
- export const Ast = (function () {
41
- const ASSERT = true;
42
- const assert = function (val, str) {
43
- if (!ASSERT) {
44
- return;
45
- }
46
- if (str === undefined) {
47
- str = "failed!";
48
- }
49
- if (!val) {
50
- throw new Error(str);
51
- }
52
- };
53
-
54
- const AstClass = function () { };
55
-
56
- AstClass.prototype = {
57
- intern,
58
- node,
59
- dump,
60
- dumpAll,
61
- poolToJSON,
62
- number,
63
- string,
64
- name,
65
- apply,
66
- fold,
67
- expr,
68
- binaryExpr,
69
- unaryExpr,
70
- parenExpr,
71
- prefixExpr,
72
- lambda,
73
- applyLate,
74
- letDef,
75
- ifExpr,
76
- caseExpr,
77
- ofClause,
78
- record,
79
- binding,
80
- exprs,
81
- program,
82
- pop,
83
- topNode,
84
- peek,
85
- push,
86
- mod,
87
- add,
88
- sub,
89
- // mul,
90
- div,
91
- pow,
92
- concat,
93
- eq,
94
- ne,
95
- lt,
96
- gt,
97
- le,
98
- ge,
99
- neg,
100
- list,
101
- bool,
102
- nul,
103
- error,
104
- };
105
-
106
- return new AstClass();
107
-
108
- // private implementation
109
-
110
- function push(ctx, node) {
111
- let nid;
112
- if (typeof node === "number") { // if already interned
113
- nid = node;
114
- } else {
115
- nid = intern(ctx, node);
116
- }
117
- ctx.state.nodeStack.push(nid);
118
- }
119
-
120
- function topNode(ctx) {
121
- const nodeStack = ctx.state.nodeStack;
122
- return nodeStack[nodeStack.length - 1];
123
- }
124
-
125
- function pop(ctx) {
126
- const nodeStack = ctx.state.nodeStack;
127
- return nodeStack.pop();
128
- }
129
-
130
- function peek(ctx, n) {
131
- if (n === undefined) {
132
- n = 0;
133
- }
134
- const nodeStack = ctx.state.nodeStack;
135
- return nodeStack[nodeStack.length - 1 - n];
136
- }
137
-
138
- function intern(ctx, n) {
139
- if (!n) {
140
- return 0;
141
- }
142
- const nodeMap = ctx.state.nodeMap;
143
- const nodePool = ctx.state.nodePool;
144
- const tag = n.tag;
145
- let elts = "";
146
- const count = n.elts.length;
147
- for (let i = 0; i < count; i++) {
148
- if (typeof n.elts[i] === "object") {
149
- n.elts[i] = intern(ctx, n.elts[i]);
150
- }
151
- elts += n.elts[i];
152
- }
153
- const key = tag + count + elts;
154
- let nid = nodeMap[key];
155
- if (nid === undefined) {
156
- nodePool.push({ tag, elts: n.elts });
157
- nid = nodePool.length - 1;
158
- nodeMap[key] = nid;
159
- if (n.coord) {
160
- ctx.state.coords[nid] = n.coord;
161
- }
162
- }
163
- return nid;
164
- }
165
-
166
- function node(ctx, nid) {
167
- const n = ctx.state.nodePool[nid];
168
- if (!nid) {
169
- return null;
170
- } else if (!n) {
171
- return {};
172
- }
173
- const elts = [];
174
- switch (n.tag) {
175
- case "NULL":
176
- break;
177
- case "NUM":
178
- case "STR":
179
- case "IDENT":
180
- case "BOOL":
181
- elts[0] = n.elts[0];
182
- break;
183
- default:
184
- for (let i = 0; i < n.elts.length; i++) {
185
- elts[i] = node(ctx, n.elts[i]);
186
- }
187
- break;
188
- }
189
- return {
190
- tag: n.tag,
191
- elts,
192
- coord: getCoord(ctx)
193
- };
194
- }
195
-
196
- function dumpAll(ctx) {
197
- const nodePool = ctx.state.nodePool;
198
- let s = "\n{";
199
- for (let i = 1; i < nodePool.length; i++) {
200
- const n = nodePool[i];
201
- s = s + "\n " + i + ": " + dump(n) + ",";
202
- }
203
- s += "\n root: " + (nodePool.length - 1);
204
- s += "\n}\n";
205
- return s;
206
- }
207
-
208
- function poolToJSON(ctx) {
209
- const nodePool = ctx.state.nodePool;
210
- const obj = {};
211
- for (let i = 1; i < nodePool.length; i++) {
212
- const n = nodePool[i];
213
- obj[i] = nodeToJSON(n);
214
- }
215
- obj.root = (nodePool.length - 1);
216
- return obj;
217
- }
218
-
219
- function nodeToJSON(n) {
220
- let obj;
221
- if (typeof n === "object") {
222
- switch (n.tag) {
223
- case "num":
224
- obj = n.elts[0];
225
- break;
226
- case "str":
227
- obj = n.elts[0];
228
- break;
229
- default:
230
- obj = {};
231
- obj.tag = n.tag;
232
- obj.elts = [];
233
- for (let i = 0; i < n.elts.length; i++) {
234
- obj.elts[i] = nodeToJSON(n.elts[i]);
235
- }
236
- break;
237
- }
238
- } else if (typeof n === "string") {
239
- obj = n;
240
- } else {
241
- obj = n;
242
- }
243
- return obj;
244
- }
245
-
246
- function dump(n) {
247
- let s;
248
- if (typeof n === "object") {
249
- switch (n.tag) {
250
- case "num":
251
- s = n.elts[0];
252
- break;
253
- case "str":
254
- s = "\"" + n.elts[0] + "\"";
255
- break;
256
- default:
257
- if (!n.elts) {
258
- s += "<invalid>";
259
- } else {
260
- s = "{ tag: \"" + n.tag + "\", elts: [ ";
261
- for (let i = 0; i < n.elts.length; i++) {
262
- if (i > 0) {
263
- s += " , ";
264
- }
265
- s += dump(n.elts[i]);
266
- }
267
- s += " ] }";
268
- }
269
- break;
270
- }
271
- } else if (typeof n === "string") {
272
- s = "\"" + n + "\"";
273
- } else {
274
- s = n;
275
- }
276
- return s;
277
- }
278
-
279
- function fold(ctx, fn, args) {
280
- // Local defs:
281
- // -- put bindings in env
282
- // Three cases:
283
- // -- full application, all args are available at parse time
284
- // -- partial application, only some args are available at parse time
285
- // -- late application, args are available at compile time (not parse time)
286
- // apply <[x y]: add x y> data..
287
- // x: val 0 data
288
- // y: val 1 data
289
- env.enterEnv(ctx, fn.name);
290
- if (fn.env) {
291
- const lexicon = fn.env.lexicon;
292
- const pattern = Ast.node(ctx, fn.env.pattern);
293
- let outerEnv = null;
294
- // let isListPattern;
295
- // setup inner environment record (lexicon)
296
- if (pattern && pattern.elts &&
297
- pattern.elts.length === 1 &&
298
- pattern.elts[0].tag === "LIST") {
299
- // For now we only support one pattern per param list.
300
- // isListPattern = true;
301
- }
302
- for (const id in lexicon) {
303
- // For each parameter, get its definition assign the value of the argument
304
- // used on the current function application.
305
- if (!id) continue;
306
- const word = JSON.parse(JSON.stringify(lexicon[id])); // poor man's copy.
307
- const index = args.length - word.offset - 1;
308
- // TODO we currently ignore list patterns
309
- // if (isListPattern) {
310
- // // <[x y]: ...> foo..
311
- // word.nid = Ast.intern(ctx, {
312
- // tag: "VAL",
313
- // elts: [{
314
- // tag: "NUM",
315
- // elts: [
316
- // String(word.offset),
317
- // ]}, {
318
- // tag: "ARG",
319
- // elts: [{
320
- // tag: "NUM",
321
- // elts: ["0"]
322
- // }]
323
- // }]
324
- // });
325
- // } else
326
- if (index >= 0 && index < args.length) {
327
- word.nid = args[index];
328
- }
329
- if (index < 0) {
330
- // We've got an unbound variable or a variable with a default value,
331
- // so add it to the new variable list.
332
- // <x:x> => <x:x>
333
- // (<x y: add x y> 10) => <y: add 10 y>
334
- // (<y: let x = 10.. add x y>) => <y: add 10 y>
335
- if (!outerEnv) {
336
- outerEnv = {};
337
- }
338
- outerEnv[id] = word;
339
- }
340
- env.addWord(ctx, id, word);
341
- }
342
- folder.fold(ctx, fn.nid);
343
- if (outerEnv) {
344
- lambda(ctx, {
345
- lexicon: outerEnv,
346
- pattern // FIXME need to trim pattern if some args where applied.
347
- }, pop(ctx));
348
- }
349
- }
350
- env.exitEnv(ctx);
351
- }
352
-
353
- function applyLate(ctx, count) {
354
- // Ast.applyLate
355
- const elts = [];
356
- for (let i = count; i > 0; i--) {
357
- elts.push(pop(ctx));
358
- }
359
- push(ctx, {
360
- tag: "APPLY",
361
- elts
362
- });
363
- }
364
-
365
- function apply(ctx, fnId, argc) {
366
- // Construct function and apply available arguments.
367
- const fn = node(ctx, fnId);
368
- // if (fn.tag !== "LAMBDA") {
369
- // // Construct an APPLY node for compiling later.
370
- // return {
371
- // tag: "APPLY",
372
- // elts: [
373
- // fnId,
374
- // ]
375
- // };
376
- // }
377
- // Construct a lexicon
378
- const lexicon = {};
379
- let paramc = 0;
380
- fn.elts[0].elts.forEach(function (n, i) {
381
- const name = n.elts[0];
382
- const nid = Ast.intern(ctx, fn.elts[3].elts[i]);
383
- lexicon[name] = {
384
- cls: "val",
385
- name,
386
- offset: i,
387
- nid
388
- };
389
- if (!nid) {
390
- // Parameters don't have nids.
391
- // assert that there are parameters after a binding without a nid.
392
- paramc++;
393
- }
394
- });
395
- const def = {
396
- name: "lambda",
397
- nid: Ast.intern(ctx, fn.elts[1]),
398
- env: {
399
- lexicon,
400
- pattern: Ast.intern(ctx, fn.elts[2])
401
- }
402
- };
403
- const elts = [];
404
- // While there are args on the stack, pop them.
405
- while (argc-- > 0 && paramc-- > 0) {
406
- const elt = pop(ctx);
407
- elts.unshift(elt); // Get the order right.
408
- }
409
- fold(ctx, def, elts);
410
- }
411
-
412
- // Node constructors
413
-
414
- function error(ctx, str, coord) {
415
- console.log(
416
- "error()",
417
- "str=" + str,
418
- "coord=" + JSON.stringify(coord),
419
- );
420
- const from = coord?.from !== undefined ? coord.from : -1;
421
- const to = coord?.to !== undefined ? coord.to : -1;
422
- number(ctx, to);
423
- number(ctx, from);
424
- string(ctx, str, coord);
425
- push(ctx, {
426
- tag: "ERROR",
427
- elts: [
428
- pop(ctx),
429
- pop(ctx),
430
- pop(ctx),
431
- ],
432
- coord
433
- });
434
- }
435
-
436
- function bool(ctx, val) {
437
- let b;
438
- if (val) {
439
- b = true;
440
- } else {
441
- b = false;
442
- }
443
- push(ctx, {
444
- tag: "BOOL",
445
- elts: [b]
446
- });
447
- }
448
-
449
- function nul(ctx) {
450
- push(ctx, {
451
- tag: "NULL",
452
- elts: []
453
- });
454
- }
455
-
456
- function number(ctx, num, coord) {
457
- assert(typeof num === "string" || typeof num === "number");
458
- push(ctx, {
459
- tag: "NUM",
460
- elts: [String(num)],
461
- coord
462
- });
463
- }
464
-
465
- function string(ctx, str, coord) {
466
- push(ctx, {
467
- tag: "STR",
468
- elts: [str],
469
- coord
470
- });
471
- }
472
-
473
- function name(ctx, name, coord) {
474
- push(ctx, {
475
- tag: "IDENT",
476
- elts: [name],
477
- coord
478
- });
479
- }
480
-
481
- function expr(ctx, argc) {
482
- // Ast.expr -- construct a expr node for the compiler.
483
- const elts = [];
484
- const pos = getPos(ctx);
485
- console.trace(
486
- "expr()",
487
- "argc=" + argc,
488
- "nodeStack=" + JSON.stringify(ctx.state.nodeStack, null, 2),
489
- );
490
- assertErr(ctx, argc <= ctx.state.nodeStack.length - 1, `Too few arguments. Expected ${argc} got ${ctx.state.nodeStack.length - 1}.`, {
491
- from: pos - 1, to: pos
492
- });
493
- while (argc--) {
494
- const elt = pop(ctx);
495
- elts.push(elt);
496
- }
497
- const nameId = pop(ctx);
498
- console.log(
499
- "expr()",
500
- "nameId=" + nameId,
501
- );
502
- assertErr(ctx, nameId, "Ill formed node.");
503
- const e = node(ctx, nameId).elts;
504
- assertErr(ctx, e && e.length > 0, "Ill formed node.");
505
- const name = e[0];
506
- push(ctx, {
507
- tag: name,
508
- elts,
509
- coord: getCoord(ctx)
510
- });
511
- }
512
-
513
- function parenExpr(ctx, coord) {
514
- // Ast.parenExpr
515
- const elts = [];
516
- const elt = pop(ctx);
517
- elts.push(elt);
518
- push(ctx, {
519
- tag: "PAREN",
520
- elts,
521
- coord
522
- });
523
- }
524
-
525
- function list(ctx, count, coord, reverse) {
526
- // Ast.list
527
- const elts = [];
528
- for (let i = count; i > 0; i--) {
529
- const elt = pop(ctx);
530
- if (elt !== undefined) {
531
- elts.push(elt);
532
- }
533
- }
534
- push(ctx, {
535
- tag: "LIST",
536
- elts: reverse ? elts : elts.reverse(),
537
- coord
538
- });
539
- }
540
-
541
- function binaryExpr(ctx, name) {
542
- const elts = [];
543
- // args are in the order produced by the parser
544
- elts.push(pop(ctx));
545
- elts.push(pop(ctx));
546
- push(ctx, {
547
- tag: name,
548
- elts: elts.reverse()
549
- });
550
- }
551
- function unaryExpr(ctx, name) {
552
- const elts = [];
553
- elts.push(pop(ctx));
554
- push(ctx, {
555
- tag: name,
556
- elts
557
- });
558
- }
559
-
560
- function prefixExpr(ctx, name) {
561
- const elts = [];
562
- elts.push(pop(ctx));
563
- push(ctx, {
564
- tag: name,
565
- elts
566
- });
567
- }
568
-
569
- function neg(ctx) {
570
- const v1 = +node(ctx, pop(ctx)).elts[0];
571
- number(ctx, -1 * v1);
572
- }
573
-
574
- function add(ctx, coord) {
575
- const n2 = node(ctx, pop(ctx));
576
- const n1 = node(ctx, pop(ctx));
577
- const v2 = n2.elts[0];
578
- const v1 = n1.elts[0];
579
- if (n1.tag !== "NUM" || n2.tag !== "NUM") {
580
- push(ctx, {
581
- tag: "ADD",
582
- elts: [n1, n2],
583
- coord
584
- });
585
- } else {
586
- number(ctx, +v1 + +v2);
587
- }
588
- }
589
-
590
- function sub(ctx) {
591
- const n2 = node(ctx, pop(ctx));
592
- const n1 = node(ctx, pop(ctx));
593
- const v2 = n2.elts[0];
594
- const v1 = n1.elts[0];
595
- if (n1.tag !== "NUM" || n2.tag !== "NUM") {
596
- push(ctx, { tag: "SUB", elts: [n1, n2] });
597
- } else {
598
- number(ctx, +v1 - +v2);
599
- }
600
- }
601
-
602
- // function mul(ctx) {
603
- // let n2 = node(ctx, pop(ctx));
604
- // let n1 = node(ctx, pop(ctx));
605
- // const v2 = n2.elts[0];
606
- // const v1 = n1.elts[0];
607
- // if (n1.tag === undefined) {
608
- // n1 = n1.elts[0];
609
- // }
610
- // if (n2.tag === undefined) {
611
- // n2 = n2.elts[0];
612
- // }
613
- // if (n1.tag !== "NUM" || n2.tag !== "NUM") {
614
- // push(ctx, { tag: "MUL", elts: [n2, n1] });
615
- // } else {
616
- // number(ctx, +v1 * +v2);
617
- // }
618
- // }
619
-
620
- function div(ctx) {
621
- const n2 = node(ctx, pop(ctx));
622
- const n1 = node(ctx, pop(ctx));
623
- const v2 = n2.elts[0];
624
- const v1 = n1.elts[0];
625
- if (n1.tag !== "NUM" || n2.tag !== "NUM") {
626
- push(ctx, { tag: "DIV", elts: [n1, n2] });
627
- } else {
628
- number(ctx, +v1 / +v2);
629
- }
630
- }
631
-
632
- function mod(ctx) {
633
- const n2 = node(ctx, pop(ctx));
634
- const n1 = node(ctx, pop(ctx));
635
- const v1 = n1.elts[0];
636
- const v2 = n2.elts[0];
637
- if (n1.tag !== "NUM" || n2.tag !== "NUM") {
638
- push(ctx, { tag: "MOD", elts: [n1, n2] });
639
- } else {
640
- number(ctx, +v1 % +v2);
641
- }
642
- }
643
-
644
- function pow(ctx) {
645
- const n2 = node(ctx, pop(ctx));
646
- const n1 = node(ctx, pop(ctx));
647
- const v2 = n2.elts[0];
648
- const v1 = n1.elts[0];
649
- if (n1.tag !== "NUM" || n2.tag !== "NUM") {
650
- push(ctx, { tag: "POW", elts: [n1, n2] });
651
- } else {
652
- number(ctx, Math.pow(+v1, +v2));
653
- }
654
- }
655
-
656
- function concat(ctx) {
657
- const n1 = node(ctx, pop(ctx));
658
- push(ctx, {
659
- tag: "CONCAT",
660
- elts: [n1]
661
- });
662
- }
663
-
664
- function eq(ctx) {
665
- const v2 = node(ctx, pop(ctx)).elts[0];
666
- const v1 = node(ctx, pop(ctx)).elts[0];
667
- bool(ctx, v1 === v2);
668
- }
669
-
670
- function ne(ctx) {
671
- const v2 = +node(ctx, pop(ctx)).elts[0];
672
- const v1 = +node(ctx, pop(ctx)).elts[0];
673
- bool(ctx, v1 !== v2);
674
- }
675
-
676
- function lt(ctx) {
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 gt(ctx) {
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 le(ctx) {
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
- function ge(ctx) {
694
- const v2 = +node(ctx, pop(ctx)).elts[0];
695
- const v1 = +node(ctx, pop(ctx)).elts[0];
696
- bool(ctx, v1 >= v2);
697
- }
698
- function ifExpr(ctx) {
699
- const elts = [];
700
- elts.push(pop(ctx)); // if
701
- elts.push(pop(ctx)); // then
702
- elts.push(pop(ctx)); // else
703
- push(ctx, { tag: "IF", elts: elts.reverse() });
704
- }
705
- function caseExpr(ctx, n) {
706
- const elts = [];
707
- elts.push(pop(ctx)); // val
708
- for (let i = n; i > 0; i--) {
709
- elts.push(pop(ctx)); // of
710
- }
711
- push(ctx, { tag: "CASE", elts: elts.reverse() });
712
- }
713
- function ofClause(ctx) {
714
- const elts = [];
715
- elts.push(pop(ctx));
716
- elts.push(pop(ctx));
717
- push(ctx, { tag: "OF", elts: elts.reverse() });
718
- }
719
-
720
- function record(ctx) {
721
- // Ast.record
722
- const count = ctx.state.exprc;
723
- const elts = [];
724
- for (let i = count; i > 0; i--) {
725
- const elt = pop(ctx);
726
- if (elt !== undefined) {
727
- elts.push(elt);
728
- }
729
- }
730
- push(ctx, {
731
- tag: "RECORD",
732
- elts
733
- });
734
- }
735
-
736
- function binding(ctx) {
737
- // Ast.binding
738
- const elts = [];
739
- elts.push(pop(ctx));
740
- elts.push(pop(ctx));
741
- push(ctx, {
742
- tag: "BINDING",
743
- elts: elts.reverse()
744
- });
745
- }
746
-
747
- function lambda(ctx, env, nid) {
748
- // Ast.lambda
749
- const names = [];
750
- const nids = [];
751
- for (const id in env.lexicon) {
752
- const word = env.lexicon[id];
753
- names.push({
754
- tag: "IDENT",
755
- elts: [word.name],
756
- coord: getCoord(ctx)
757
- });
758
- nids.push(word.nid || 0);
759
- }
760
- const pattern = env.pattern;
761
- push(ctx, {
762
- tag: "LAMBDA",
763
- elts: [{
764
- tag: "LIST",
765
- elts: names
766
- }, nid, {
767
- tag: "LIST",
768
- elts: pattern
769
- }, {
770
- tag: "LIST",
771
- elts: nids
772
- }]
773
- });
774
- }
775
-
776
- function exprs(ctx, count, inReverse) {
777
- // Ast.exprs
778
- let elts = [];
779
- assert(ctx.state.nodeStack.length >= count);
780
- if (inReverse) {
781
- for (let i = count; i > 0; i--) {
782
- const elt = pop(ctx);
783
- elts.push(elt); // Reverse order.
784
- }
785
- } else {
786
- for (let i = count; i > 0; i--) {
787
- const elt = pop(ctx);
788
- elts.push(elt); // Reverse order.
789
- }
790
- elts = elts.reverse();
791
- }
792
- push(ctx, {
793
- tag: "EXPRS",
794
- elts
795
- });
796
- }
797
-
798
- function letDef(ctx) {
799
- // Clean up stack and produce initializer.
800
- pop(ctx); // body
801
- pop(ctx); // name
802
- for (let i = 0; i < ctx.state.paramc; i++) {
803
- pop(ctx); // params
804
- }
805
- ctx.state.exprc--; // don't count as expr.
806
- }
807
-
808
- function program(ctx) {
809
- const elts = [];
810
- elts.push(pop(ctx));
811
- push(ctx, {
812
- tag: "PROG",
813
- elts
814
- });
815
- }
816
- })();
817
-
818
- // The following code for StreamString was copied from CodeMirror.
819
-
820
- const StringStream = (function () {
821
- // The character stream used by a mode's parser.
822
- function StringStream(string, tabSize) {
823
- this.pos = this.start = 0;
824
- this.string = string;
825
- this.tabSize = tabSize || 8;
826
- }
827
-
828
- StringStream.prototype = {
829
- eol: function () { return this.pos >= this.string.length; },
830
- sol: function () { return this.pos === 0; },
831
- peek: function () { return this.string.charAt(this.pos) || undefined; },
832
- next: function () {
833
- if (this.pos < this.string.length) { return this.string.charAt(this.pos++); }
834
- },
835
- eat: function (match) {
836
- const ch = this.string.charAt(this.pos);
837
- let ok;
838
- if (typeof match === "string") {
839
- ok = ch === match;
840
- } else {
841
- ok = ch && (match.test ? match.test(ch) : match(ch));
842
- }
843
- if (ok) { ++this.pos; return ch; }
844
- },
845
- eatWhile: function (match) {
846
- const start = this.pos;
847
- while (this.eat(match));
848
- return this.pos > start;
849
- },
850
- eatSpace: function () {
851
- const start = this.pos;
852
- while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) ++this.pos;
853
- return this.pos > start;
854
- },
855
- skipToEnd: function () { this.pos = this.string.length; },
856
- skipTo: function (ch) {
857
- const found = this.string.indexOf(ch, this.pos);
858
- if (found > -1) { this.pos = found; return true; }
859
- },
860
- backUp: function (n) { this.pos -= n; },
861
- match: function (pattern, consume, caseInsensitive) {
862
- assert(false, "Should not get here");
863
- if (typeof pattern === "string") {
864
- const cased = function (str) { return caseInsensitive ? str.toLowerCase() : str; };
865
- if (cased(this.string).indexOf(cased(pattern), this.pos) === this.pos) {
866
- if (consume !== false) this.pos += pattern.length;
867
- return true;
868
- }
869
- } else {
870
- const match = this.string.slice(this.pos).match(pattern);
871
- if (match && match.index > 0) return null;
872
- if (match && consume !== false) this.pos += match[0].length;
873
- return match;
874
- }
875
- },
876
- current: function () { return this.string.slice(this.start, this.pos); }
877
- };
878
-
879
- return StringStream;
880
- })();
881
-
882
- // env
883
-
884
- export const env = (function () {
885
- return {
886
- findWord,
887
- addWord,
888
- enterEnv,
889
- exitEnv,
890
- addPattern
891
- };
892
-
893
- // private functions
894
-
895
- function findWord(ctx, lexeme) {
896
- const env = ctx.state.env;
897
- for (let i = env.length - 1; i >= 0; i--) {
898
- const word = env[i].lexicon[lexeme];
899
- if (word) {
900
- return word;
901
- }
902
- }
903
- return null;
904
- }
905
-
906
- function addWord(ctx, lexeme, entry) {
907
- window.gcexports.topEnv(ctx).lexicon[lexeme] = entry;
908
- return null;
909
- }
910
-
911
- function addPattern(ctx, pattern) {
912
- window.gcexports.topEnv(ctx).pattern.push(pattern);
913
- }
914
-
915
- function enterEnv(ctx, name) {
916
- // recursion guard
917
- if (ctx.state.env.length > 380) {
918
- console.trace(
919
- "enterEnv()",
920
- "name=" + name,
921
- );
922
- // return; // just stop recursing
923
- throw new Error("runaway recursion");
924
- }
925
- window.gcexports.topEnv(ctx).paramc = ctx.state.paramc;
926
- ctx.state.env.push({
927
- name,
928
- lexicon: {},
929
- pattern: []
930
- });
931
- }
932
-
933
- function exitEnv(ctx) {
934
- ctx.state.env.pop();
935
- ctx.state.paramc = window.gcexports.topEnv(ctx).paramc;
936
- }
937
- })();
938
-
939
40
  let scanTime = 0;
940
41
  let scanCount = 0;
941
42
  window.gcexports.scanTime = function () {
@@ -957,24 +58,20 @@ window.gcexports.parseCount = function () {
957
58
  };
958
59
 
959
60
  function getCoord(ctx) {
960
- return {
961
- from: ctx.scan.stream.start,
962
- to: ctx.scan.stream.pos,
963
- };
61
+ return ctx.state.nextTokenCoord;
964
62
  }
965
63
 
966
64
  function getPos(ctx) {
967
65
  return ctx.scan.stream.pos;
968
66
  }
969
67
 
970
- function assertErr(ctx, b, str, coord) {
971
- console.log(
972
- "assertErr()",
973
- "str=" + str,
974
- );
68
+ export function assertErr(ctx, b, str, coord) {
975
69
  if (!b) {
976
- const pos = getPos(ctx);
977
- Ast.error(ctx, str, { from: pos - 1, to: pos });
70
+ console.log(
71
+ "assertErr()",
72
+ "str=" + str,
73
+ );
74
+ Ast.error(ctx, str, coord);
978
75
  throw new Error(str);
979
76
  }
980
77
  }
@@ -1108,7 +205,7 @@ export const parse = (function () {
1108
205
  if (nextToken !== tk) {
1109
206
  const to = getPos(ctx);
1110
207
  const from = to - lexeme.length;
1111
- Ast.error(ctx, "Expecting " + tokenToLexeme(tk) +
208
+ assertErr(ctx, false, "Expecting " + tokenToLexeme(tk) +
1112
209
  ", found " + tokenToLexeme(nextToken) + ".", { from, to });
1113
210
  next(ctx); // Advance past error.
1114
211
  throw new Error("Expecting " + tokenToLexeme(tk) +
@@ -1135,11 +232,14 @@ export const parse = (function () {
1135
232
  let tk;
1136
233
  const nextToken = ctx.state.nextToken;
1137
234
  if (nextToken < 0) {
235
+ const from = getPos(ctx);
1138
236
  const t0 = new Date();
1139
237
  tk = ctx.scan.start(ctx);
1140
238
  const t1 = new Date();
1141
239
  scanTime += (t1 - t0);
1142
240
  ctx.state.nextToken = tk;
241
+ const to = getPos(ctx);
242
+ ctx.state.nextTokenCoord = {from, to};
1143
243
  } else {
1144
244
  tk = nextToken;
1145
245
  }
@@ -1282,7 +382,7 @@ export const parse = (function () {
1282
382
  return defList(ctx, cc);
1283
383
  } else {
1284
384
  eat(ctx, TK_IDENT);
1285
- env.addWord(ctx, lexeme, {
385
+ Env.addWord(ctx, lexeme, {
1286
386
  tk: TK_IDENT,
1287
387
  cls: "val",
1288
388
  name: lexeme,
@@ -1299,7 +399,7 @@ export const parse = (function () {
1299
399
  const to = getPos(ctx);
1300
400
  const from = to - lexeme.length;
1301
401
  const coord = { from, to };
1302
- const word = env.findWord(ctx, lexeme);
402
+ const word = Env.findWord(ctx, lexeme);
1303
403
  if (word) {
1304
404
  cc.cls = word.cls;
1305
405
  if (word.cls === "number" && word.val) {
@@ -1315,11 +415,6 @@ export const parse = (function () {
1315
415
  }
1316
416
  } else {
1317
417
  cc.cls = "error";
1318
- console.log(
1319
- "name()",
1320
- "lexeme=" + lexeme,
1321
- "coord=" + JSON.stringify(coord),
1322
- );
1323
418
  Ast.error(ctx, "Name '" + lexeme + "' not found.", coord);
1324
419
  }
1325
420
  // assert(cc, "name");
@@ -1376,7 +471,7 @@ export const parse = (function () {
1376
471
  eat(ctx, TK_LEFTANGLE);
1377
472
  const ret = function (ctx) {
1378
473
  ctx.state.paramc = 0;
1379
- env.enterEnv(ctx, "lambda");
474
+ Env.enterEnv(ctx, "lambda");
1380
475
  return params(ctx, TK_COLON, function (ctx) {
1381
476
  eat(ctx, TK_COLON);
1382
477
  const ret = function (ctx) {
@@ -1384,7 +479,7 @@ export const parse = (function () {
1384
479
  eat(ctx, TK_RIGHTANGLE);
1385
480
  const nid = Ast.pop(ctx); // save body node id for aliased code
1386
481
  Ast.lambda(ctx, topEnv(ctx), nid);
1387
- env.exitEnv(ctx);
482
+ Env.exitEnv(ctx);
1388
483
  return cc;
1389
484
  });
1390
485
  };
@@ -1523,7 +618,7 @@ export const parse = (function () {
1523
618
  if (match(ctx, TK_BINOP)) {
1524
619
  eat(ctx, TK_BINOP);
1525
620
  const ret = function (ctx) {
1526
- let op = env.findWord(ctx, lexeme).name;
621
+ let op = Env.findWord(ctx, lexeme).name;
1527
622
  if (getPrecedence(prevOp) < getPrecedence(op)) {
1528
623
  return binaryExpr(ctx, op, function (ctx, prevOp) {
1529
624
  // This continuation's purpose is to construct a right recursive
@@ -1764,7 +859,7 @@ export const parse = (function () {
1764
859
  let nid;
1765
860
  while (Ast.peek(ctx) !== nid) {
1766
861
  nid = Ast.pop(ctx);
1767
- folder.fold(ctx, nid); // fold the exprs on top
862
+ folder.fold(ctx, nid); // Fold the exprs on top
1768
863
  }
1769
864
  Ast.exprs(ctx, ctx.state.nodeStack.length, true);
1770
865
  Ast.program(ctx);
@@ -1788,7 +883,7 @@ export const parse = (function () {
1788
883
  const ret = defName(ctx, function (ctx) {
1789
884
  const name = Ast.node(ctx, Ast.pop(ctx)).elts[0];
1790
885
  // nid=0 means def not finished yet
1791
- env.addWord(ctx, name, {
886
+ Env.addWord(ctx, name, {
1792
887
  tk: TK_IDENT,
1793
888
  cls: "function",
1794
889
  length: 0,
@@ -1796,17 +891,17 @@ export const parse = (function () {
1796
891
  name
1797
892
  });
1798
893
  ctx.state.paramc = 0;
1799
- env.enterEnv(ctx, name); // FIXME need to link to outer env
894
+ Env.enterEnv(ctx, name); // FIXME need to link to outer env
1800
895
  return params(ctx, TK_EQUAL, function (ctx) {
1801
- const func = env.findWord(ctx, topEnv(ctx).name);
896
+ const func = Env.findWord(ctx, topEnv(ctx).name);
1802
897
  func.length = ctx.state.paramc;
1803
898
  func.env = topEnv(ctx);
1804
899
  eat(ctx, TK_EQUAL);
1805
900
  const ret = function (ctx) {
1806
901
  return exprsStart(ctx, TK_DOT, function (ctx) {
1807
- const def = env.findWord(ctx, topEnv(ctx).name);
902
+ const def = Env.findWord(ctx, topEnv(ctx).name);
1808
903
  def.nid = Ast.peek(ctx); // save node id for aliased code
1809
- env.exitEnv(ctx);
904
+ Env.exitEnv(ctx);
1810
905
  Ast.letDef(ctx); // Clean up stack
1811
906
  return cc;
1812
907
  });
@@ -1885,21 +980,24 @@ export const parse = (function () {
1885
980
  }
1886
981
  }
1887
982
  while ((c = stream.peek()) &&
1888
- (c === " " || c === "\t")) {
983
+ (c === " " || c === "\t" || c === "\n")) {
1889
984
  stream.next();
1890
985
  }
986
+ if (cc && !stream.peek()) {
987
+ assertErr(ctx, false, "End of progam reached.", getCoord(ctx));
988
+ }
1891
989
  } catch (x) {
1892
- console.log("catch() x=" + x);
990
+ // console.log("catch() x=" + x);
1893
991
  if (x instanceof Error) {
1894
992
  if (x.message === "comment") {
1895
993
  cls = x;
1896
994
  } else {
1897
995
  console.log("catch() x=" + x.stack);
1898
996
  // next(ctx);
997
+ state.cc = null; // done for now.
1899
998
  return Ast.poolToJSON(ctx);
1900
- // state.cc = null; // done for now.
1901
999
  // cls = "error";
1902
- // throw new Error(JSON.stringify(window.gcexports.errors, null, 2));
1000
+ // throw new Error(JSON.stringify(window.gcexports.errors, null, 2));
1903
1001
  }
1904
1002
  } else {
1905
1003
  // throw x
@@ -2068,6 +1166,8 @@ export const parse = (function () {
2068
1166
  c = nextCC();
2069
1167
  }
2070
1168
  }
1169
+ const coord = {from: getPos(ctx) - lexeme.length, to: getPos(ctx)};
1170
+ assertErr(ctx, c !== 0, `Unterminated string: ${lexeme}`, coord);
2071
1171
  if (quoteChar === CC_BACKTICK && c === CC_DOLLAR &&
2072
1172
  peekCC() === CC_LEFTBRACE) {
2073
1173
  nextCC(); // Eat CC_LEFTBRACE