@graffiticode/parser 0.1.0

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.
package/src/parse.js ADDED
@@ -0,0 +1,2177 @@
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
+
17
+ let CodeMirror;
18
+ if (typeof CodeMirror === "undefined") {
19
+ CodeMirror = {
20
+ Pos: function () {
21
+ return {};
22
+ }
23
+ };
24
+ }
25
+
26
+ let window;
27
+ if (typeof window === "undefined") {
28
+ window = {};
29
+ window = {
30
+ gcexports: {
31
+ coords: {},
32
+ errors: []
33
+ },
34
+ isSynthetic: true
35
+ };
36
+ }
37
+
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
+ let scanTime = 0;
940
+ let scanCount = 0;
941
+ window.gcexports.scanTime = function () {
942
+ return scanTime;
943
+ };
944
+ window.gcexports.scanCount = function () {
945
+ return scanCount;
946
+ };
947
+
948
+ let parseTime = 0;
949
+
950
+ window.gcexports.parseTime = function () {
951
+ return parseTime;
952
+ };
953
+
954
+ let parseCount = 0;
955
+ window.gcexports.parseCount = function () {
956
+ return parseCount;
957
+ };
958
+
959
+ function getCoord(ctx) {
960
+ return {
961
+ from: ctx.scan.stream.start,
962
+ to: ctx.scan.stream.pos,
963
+ };
964
+ }
965
+
966
+ function getPos(ctx) {
967
+ return ctx.scan.stream.pos;
968
+ }
969
+
970
+ function assertErr(ctx, b, str, coord) {
971
+ console.log(
972
+ "assertErr()",
973
+ "str=" + str,
974
+ );
975
+ if (!b) {
976
+ const pos = getPos(ctx);
977
+ Ast.error(ctx, str, { from: pos - 1, to: pos });
978
+ throw new Error(str);
979
+ }
980
+ }
981
+
982
+ export const parse = (function () {
983
+ function assert(b, str) {
984
+ if (!b) {
985
+ throw new Error(str);
986
+ }
987
+ }
988
+
989
+ const keywords = window.gcexports.keywords = {
990
+ let: { tk: 0x12, cls: "keyword" },
991
+ if: { tk: 0x05, cls: "keyword" },
992
+ then: { tk: 0x06, cls: "keyword" },
993
+ else: { tk: 0x07, cls: "keyword" },
994
+ case: { tk: 0x0F, cls: "keyword" },
995
+ of: { tk: 0x10, cls: "keyword" },
996
+ end: { tk: 0x11, cls: "keyword", length: 0 },
997
+ true: { tk: 0x14, cls: "val", length: 0 },
998
+ false: { tk: 0x14, cls: "val", length: 0 },
999
+ null: { tk: 0x15, cls: "val", length: 0 },
1000
+ val: { tk: 1, name: "VAL", cls: "function", length: 2, arity: 2 },
1001
+ key: { tk: 1, name: "KEY", cls: "function", length: 2, arity: 2 },
1002
+ len: { tk: 1, name: "LEN", cls: "function", length: 1, arity: 1 },
1003
+ concat: { tk: 1, name: "CONCAT", cls: "function", length: 1, arity: 1 },
1004
+ add: { tk: 1, name: "ADD", cls: "function", length: 2, arity: 2 },
1005
+ mul: { tk: 1, name: "MUL", cls: "function", length: 2, arity: 2 },
1006
+ pow: { tk: 1, name: "POW", cls: "function", length: 2, arity: 2 },
1007
+ style: { tk: 1, name: "STYLE", cls: "function", length: 2, arity: 2 },
1008
+ map: { tk: 1, name: "MAP", cls: "function", length: 2, arity: 2 },
1009
+ apply: { tk: 1, name: "APPLY", cls: "function", length: 2, arity: 2 },
1010
+ in: { tk: 1, name: "IN", cls: "function", length: 0, arity: 0 },
1011
+ arg: { tk: 1, name: "ARG", cls: "function", length: 1, arity: 1 },
1012
+ data: { tk: 1, name: "DATA", cls: "function", length: 1, arity: 1 },
1013
+ json: { tk: 1, name: "JSON", cls: "function", length: 1, arity: 1 },
1014
+ };
1015
+
1016
+ const CC_DOUBLEQUOTE = 0x22;
1017
+ const CC_DOLLAR = 0x24;
1018
+ const CC_SINGLEQUOTE = 0x27;
1019
+ const CC_BACKTICK = 0x60;
1020
+ const CC_LEFTBRACE = 0x7B;
1021
+ // const CC_RIGHTBRACE = 0x7D;
1022
+
1023
+ const TK_IDENT = 0x01;
1024
+ const TK_NUM = 0x02;
1025
+ const TK_STR = 0x03;
1026
+ const TK_EQUAL = 0x04;
1027
+ const TK_IF = 0x05;
1028
+ const TK_THEN = 0x06;
1029
+ const TK_ELSE = 0x07;
1030
+ const TK_RETURN = 0x08;
1031
+ const TK_IS = 0x09;
1032
+ const TK_POSTOP = 0x0A;
1033
+ const TK_PREOP = 0x0B;
1034
+ const TK_FUN = 0x0C;
1035
+ const TK_VAL = 0x0D;
1036
+ const TK_BINOP = 0x0E;
1037
+ const TK_CASE = 0x0F;
1038
+ const TK_OF = 0x10;
1039
+ const TK_END = 0x11;
1040
+ const TK_LET = 0x12;
1041
+ const TK_OR = 0x13;
1042
+ const TK_BOOL = 0x14;
1043
+ const TK_NULL = 0x15;
1044
+ // const TK_IN = 0x16;
1045
+
1046
+ const TK_LEFTPAREN = 0xA1;
1047
+ const TK_RIGHTPAREN = 0xA2;
1048
+ const TK_LEFTBRACKET = 0xA3;
1049
+ const TK_RIGHTBRACKET = 0xA4;
1050
+ const TK_LEFTBRACE = 0xA5;
1051
+ const TK_RIGHTBRACE = 0xA6;
1052
+ const TK_PLUS = 0xA7;
1053
+ const TK_MINUS = 0xA8;
1054
+ const TK_DOT = 0xA9;
1055
+ const TK_COLON = 0xAA;
1056
+ const TK_COMMA = 0xAB;
1057
+ const TK_BACKQUOTE = 0xAC;
1058
+ const TK_COMMENT = 0xAD;
1059
+ const TK_LEFTANGLE = 0xAE;
1060
+ const TK_RIGHTANGLE = 0xAF;
1061
+ // const TK_DOUBLELEFTBRACE = 0xB0;
1062
+ // const TK_DOUBLERIGHTBRACE = 0xB1;
1063
+ const TK_STRPREFIX = 0xB2;
1064
+ const TK_STRMIDDLE = 0xB3;
1065
+ const TK_STRSUFFIX = 0xB4;
1066
+
1067
+ function tokenToLexeme(tk) {
1068
+ switch (tk) {
1069
+ case TK_EQUAL: return "a '=' symbol";
1070
+ case TK_IF: return "the 'if' keyword";
1071
+ case TK_THEN: return "the 'then' keyword";
1072
+ case TK_ELSE: return "the 'else' keyword";
1073
+ case TK_RETURN: return "the 'return' keyword";
1074
+ case TK_IS: return "the 'is' keyword";
1075
+ case TK_FUN: return "the 'fun' keyword";
1076
+ case TK_VAL: return "the 'val' keyword";
1077
+ case TK_CASE: return "the 'case' keyword";
1078
+ case TK_OF: return "the 'of' keyword";
1079
+ case TK_END: return "the 'end' keyword";
1080
+ case TK_LET: return "the 'let' keyword";
1081
+ case TK_OR: return "the 'or' keyword";
1082
+ case TK_POSTOP:
1083
+ case TK_PREOP:
1084
+ case TK_BINOP:
1085
+ return "an operator";
1086
+ case TK_LEFTPAREN: return "a '('";
1087
+ case TK_RIGHTPAREN: return "a ')'";
1088
+ case TK_LEFTBRACKET: return "a '['";
1089
+ case TK_RIGHTBRACKET: return "a ']'";
1090
+ case TK_LEFTBRACE: return "a '{'";
1091
+ case TK_RIGHTBRACE: return "a '}'";
1092
+ case TK_LEFTANGLE: return "a '<'";
1093
+ case TK_RIGHTANGLE: return "a '>'";
1094
+ case TK_PLUS: return "a '+'";
1095
+ case TK_MINUS: return "a '-'";
1096
+ case TK_DOT: return "a '.'";
1097
+ case TK_COLON: return "a ':'";
1098
+ case TK_COMMA: return "a ','";
1099
+ case TK_BACKQUOTE: return "a '`'";
1100
+ case TK_COMMENT: return "a comment";
1101
+ case 0: return "the end of the program";
1102
+ }
1103
+ return "an expression";
1104
+ }
1105
+
1106
+ function eat(ctx, tk) {
1107
+ const nextToken = next(ctx);
1108
+ if (nextToken !== tk) {
1109
+ const to = getPos(ctx);
1110
+ const from = to - lexeme.length;
1111
+ Ast.error(ctx, "Expecting " + tokenToLexeme(tk) +
1112
+ ", found " + tokenToLexeme(nextToken) + ".", { from, to });
1113
+ next(ctx); // Advance past error.
1114
+ throw new Error("Expecting " + tokenToLexeme(tk) +
1115
+ ", found " + tokenToLexeme(nextToken) + ".");
1116
+ }
1117
+ }
1118
+
1119
+ function match(ctx, tk) {
1120
+ if (peek(ctx) === tk) {
1121
+ return true;
1122
+ } else {
1123
+ return false;
1124
+ }
1125
+ }
1126
+
1127
+ function next(ctx) {
1128
+ const tk = peek(ctx);
1129
+ ctx.state.nextToken = -1;
1130
+ scanCount++;
1131
+ return tk;
1132
+ }
1133
+
1134
+ function peek(ctx) {
1135
+ let tk;
1136
+ const nextToken = ctx.state.nextToken;
1137
+ if (nextToken < 0) {
1138
+ const t0 = new Date();
1139
+ tk = ctx.scan.start(ctx);
1140
+ const t1 = new Date();
1141
+ scanTime += (t1 - t0);
1142
+ ctx.state.nextToken = tk;
1143
+ } else {
1144
+ tk = nextToken;
1145
+ }
1146
+ return tk;
1147
+ }
1148
+
1149
+ // Parsing functions -- each parsing function consumes a single token and
1150
+ // returns a continuation function for parsing the rest of the string.
1151
+
1152
+ function nul(ctx, cc) {
1153
+ eat(ctx, TK_NULL);
1154
+ cc.cls = "number";
1155
+ Ast.nul(ctx);
1156
+ return cc;
1157
+ }
1158
+
1159
+ function bool(ctx, cc) {
1160
+ eat(ctx, TK_BOOL);
1161
+ cc.cls = "number";
1162
+ Ast.bool(ctx, lexeme === "true");
1163
+ return cc;
1164
+ }
1165
+
1166
+ function number(ctx, cc) {
1167
+ eat(ctx, TK_NUM);
1168
+ cc.cls = "number";
1169
+ Ast.number(ctx, lexeme, getCoord(ctx));
1170
+ return cc;
1171
+ }
1172
+
1173
+ /*
1174
+ Str :
1175
+ STR
1176
+ STRPREFIX StrSuffix
1177
+
1178
+ StrSuffix :
1179
+ Expr STRMIDDLE StrSuffix
1180
+ Expr STRSUFFIX
1181
+ */
1182
+
1183
+ function str(ctx, cc) {
1184
+ let coord;
1185
+ if (match(ctx, TK_STR)) {
1186
+ eat(ctx, TK_STR);
1187
+ coord = getCoord(ctx);
1188
+ Ast.string(ctx, lexeme, coord); // strip quotes;
1189
+ cc.cls = "string";
1190
+ return cc;
1191
+ } else if (match(ctx, TK_STRPREFIX)) {
1192
+ ctx.state.inStr++;
1193
+ eat(ctx, TK_STRPREFIX);
1194
+ startCounter(ctx);
1195
+ coord = getCoord(ctx);
1196
+ Ast.string(ctx, lexeme, coord); // strip quotes;
1197
+ countCounter(ctx);
1198
+ const ret = function (ctx) {
1199
+ return strSuffix(ctx, function (ctx) {
1200
+ ctx.state.inStr--;
1201
+ eat(ctx, TK_STRSUFFIX);
1202
+ const coord = getCoord(ctx);
1203
+ Ast.string(ctx, lexeme, coord); // strip quotes;
1204
+ countCounter(ctx);
1205
+ Ast.list(ctx, ctx.state.exprc, getCoord(ctx));
1206
+ stopCounter(ctx);
1207
+ Ast.concat(ctx);
1208
+ cc.cls = "string";
1209
+ return cc;
1210
+ });
1211
+ };
1212
+ ret.cls = "string";
1213
+ return ret;
1214
+ }
1215
+ assert(false);
1216
+ }
1217
+ function strSuffix(ctx, resume) {
1218
+ if (match(ctx, TK_STRSUFFIX)) {
1219
+ // We have a STRSUFFIX so we are done.
1220
+ return resume;
1221
+ }
1222
+ return strPart(ctx, function (ctx) {
1223
+ let ret;
1224
+ if (match(ctx, TK_STRMIDDLE)) {
1225
+ // Not done yet.
1226
+ eat(ctx, TK_STRMIDDLE);
1227
+ const coord = getCoord(ctx);
1228
+ Ast.string(ctx, lexeme, coord); // strip quotes;
1229
+ countCounter(ctx);
1230
+ ret = function (ctx) {
1231
+ return strSuffix(ctx, resume);
1232
+ };
1233
+ ret.cls = "string";
1234
+ return ret;
1235
+ }
1236
+ ret = function (ctx) {
1237
+ return strSuffix(ctx, resume);
1238
+ };
1239
+ ret.cls = "string";
1240
+ return ret;
1241
+ });
1242
+ }
1243
+ function strPart(ctx, resume) {
1244
+ return expr(ctx, function (ctx) {
1245
+ countCounter(ctx);
1246
+ return resume(ctx);
1247
+ });
1248
+ }
1249
+ function ident(ctx, cc) {
1250
+ eat(ctx, TK_IDENT);
1251
+ Ast.name(ctx, lexeme, getCoord(ctx));
1252
+ cc.cls = "variable";
1253
+ return cc;
1254
+ }
1255
+ function bindingName(ctx, cc) {
1256
+ if (match(ctx, TK_IDENT)) {
1257
+ eat(ctx, TK_IDENT);
1258
+ Ast.string(ctx, lexeme, getCoord(ctx));
1259
+ cc.cls = "variable";
1260
+ return cc;
1261
+ }
1262
+ if (match(ctx, TK_NUM)) {
1263
+ return number(ctx, cc);
1264
+ }
1265
+ return str(ctx, cc);
1266
+ }
1267
+ function defList(ctx, cc) {
1268
+ eat(ctx, TK_LEFTBRACKET);
1269
+ const ret = (ctx) => {
1270
+ return params(ctx, TK_RIGHTBRACKET, (ctx) => {
1271
+ eat(ctx, TK_RIGHTBRACKET);
1272
+ Ast.list(ctx, ctx.state.paramc, null, true);
1273
+ ctx.state.paramc = 1;
1274
+ return cc;
1275
+ });
1276
+ };
1277
+ ret.cls = "punc";
1278
+ return ret;
1279
+ }
1280
+ function defName(ctx, cc) {
1281
+ if (match(ctx, TK_LEFTBRACKET)) {
1282
+ return defList(ctx, cc);
1283
+ } else {
1284
+ eat(ctx, TK_IDENT);
1285
+ env.addWord(ctx, lexeme, {
1286
+ tk: TK_IDENT,
1287
+ cls: "val",
1288
+ name: lexeme,
1289
+ offset: ctx.state.paramc,
1290
+ nid: 0
1291
+ });
1292
+ Ast.name(ctx, lexeme, getCoord(ctx));
1293
+ cc.cls = "val";
1294
+ return cc;
1295
+ }
1296
+ }
1297
+ function name(ctx, cc) {
1298
+ eat(ctx, TK_IDENT);
1299
+ const to = getPos(ctx);
1300
+ const from = to - lexeme.length;
1301
+ const coord = { from, to };
1302
+ const word = env.findWord(ctx, lexeme);
1303
+ if (word) {
1304
+ cc.cls = word.cls;
1305
+ if (word.cls === "number" && word.val) {
1306
+ Ast.number(ctx, word.val, coord);
1307
+ } else if (word.cls === "string" && word.val) {
1308
+ Ast.string(ctx, word.val, coord);
1309
+ } else {
1310
+ if (word.nid) {
1311
+ Ast.push(ctx, word.nid);
1312
+ } else {
1313
+ Ast.name(ctx, lexeme, coord);
1314
+ }
1315
+ }
1316
+ } else {
1317
+ cc.cls = "error";
1318
+ console.log(
1319
+ "name()",
1320
+ "lexeme=" + lexeme,
1321
+ "coord=" + JSON.stringify(coord),
1322
+ );
1323
+ Ast.error(ctx, "Name '" + lexeme + "' not found.", coord);
1324
+ }
1325
+ // assert(cc, "name");
1326
+ return cc;
1327
+ }
1328
+ function record(ctx, cc) {
1329
+ // Parse record
1330
+ eat(ctx, TK_LEFTBRACE);
1331
+ startCounter(ctx);
1332
+ const ret = function (ctx) {
1333
+ return bindings(ctx, function (ctx) {
1334
+ eat(ctx, TK_RIGHTBRACE);
1335
+ Ast.record(ctx);
1336
+ stopCounter(ctx);
1337
+ cc.cls = "punc";
1338
+ return cc;
1339
+ });
1340
+ };
1341
+ ret.cls = "punc";
1342
+ return ret;
1343
+ }
1344
+ function bindings(ctx, cc) {
1345
+ if (match(ctx, TK_RIGHTBRACE)) {
1346
+ return cc;
1347
+ }
1348
+ return binding(ctx, function (ctx) {
1349
+ if (match(ctx, TK_COMMA)) {
1350
+ eat(ctx, TK_COMMA);
1351
+ Ast.binding(ctx);
1352
+ const ret = function (ctx) {
1353
+ return bindings(ctx, cc);
1354
+ };
1355
+ ret.cls = "punc";
1356
+ return ret;
1357
+ }
1358
+ return function (ctx) {
1359
+ Ast.binding(ctx);
1360
+ return bindings(ctx, cc);
1361
+ };
1362
+ });
1363
+ }
1364
+ function binding(ctx, cc) {
1365
+ return bindingName(ctx, function (ctx) {
1366
+ eat(ctx, TK_COLON);
1367
+ const ret = function (ctx) {
1368
+ countCounter(ctx);
1369
+ return expr(ctx, cc);
1370
+ };
1371
+ ret.cls = "punc";
1372
+ return ret;
1373
+ });
1374
+ }
1375
+ function lambda(ctx, cc) {
1376
+ eat(ctx, TK_LEFTANGLE);
1377
+ const ret = function (ctx) {
1378
+ ctx.state.paramc = 0;
1379
+ env.enterEnv(ctx, "lambda");
1380
+ return params(ctx, TK_COLON, function (ctx) {
1381
+ eat(ctx, TK_COLON);
1382
+ const ret = function (ctx) {
1383
+ return exprsStart(ctx, TK_RIGHTANGLE, function (ctx) {
1384
+ eat(ctx, TK_RIGHTANGLE);
1385
+ const nid = Ast.pop(ctx); // save body node id for aliased code
1386
+ Ast.lambda(ctx, topEnv(ctx), nid);
1387
+ env.exitEnv(ctx);
1388
+ return cc;
1389
+ });
1390
+ };
1391
+ ret.cls = "punc";
1392
+ return ret;
1393
+ });
1394
+ };
1395
+ return ret;
1396
+ }
1397
+ function parenExpr(ctx, cc) {
1398
+ const coord = getCoord(ctx);
1399
+ eat(ctx, TK_LEFTPAREN);
1400
+ const ret = function (ctx) {
1401
+ return exprsStart(ctx, TK_RIGHTPAREN, function (ctx) {
1402
+ eat(ctx, TK_RIGHTPAREN);
1403
+ coord.to = getCoord(ctx).to;
1404
+ Ast.parenExpr(ctx, coord);
1405
+ cc.cls = "punc";
1406
+ return cc;
1407
+ });
1408
+ };
1409
+ ret.cls = "punc";
1410
+ return ret;
1411
+ }
1412
+ function list(ctx, cc) {
1413
+ const coord = getCoord(ctx);
1414
+ eat(ctx, TK_LEFTBRACKET);
1415
+ startCounter(ctx);
1416
+ const ret = function (ctx) {
1417
+ return elements(ctx, function (ctx) {
1418
+ eat(ctx, TK_RIGHTBRACKET);
1419
+ coord.to = getCoord(ctx).to;
1420
+ Ast.list(ctx, ctx.state.exprc, coord);
1421
+ stopCounter(ctx);
1422
+ cc.cls = "punc";
1423
+ return cc;
1424
+ });
1425
+ };
1426
+ ret.cls = "punc";
1427
+ return ret;
1428
+ }
1429
+ function elements(ctx, cc) {
1430
+ if (match(ctx, TK_RIGHTBRACKET)) {
1431
+ return cc;
1432
+ }
1433
+ return element(ctx, function (ctx) {
1434
+ if (match(ctx, TK_COMMA)) {
1435
+ eat(ctx, TK_COMMA);
1436
+ const ret = function (ctx) {
1437
+ return elements(ctx, cc);
1438
+ };
1439
+ ret.cls = "punc";
1440
+ return ret;
1441
+ }
1442
+ return function (ctx) {
1443
+ return elements(ctx, cc);
1444
+ };
1445
+ });
1446
+ }
1447
+ function element(ctx, cc) {
1448
+ return expr(ctx, function (ctx) {
1449
+ countCounter(ctx);
1450
+ return cc(ctx);
1451
+ });
1452
+ }
1453
+ function primaryExpr(ctx, cc) {
1454
+ if (match(ctx, TK_NUM)) {
1455
+ return number(ctx, cc);
1456
+ } else if (match(ctx, TK_STR) || match(ctx, TK_STRPREFIX)) {
1457
+ return str(ctx, cc);
1458
+ } else if (match(ctx, TK_BOOL)) {
1459
+ return bool(ctx, cc);
1460
+ } else if (match(ctx, TK_NULL)) {
1461
+ return nul(ctx, cc);
1462
+ } else if (match(ctx, TK_LEFTBRACE)) {
1463
+ return record(ctx, cc);
1464
+ } else if (match(ctx, TK_LEFTPAREN)) {
1465
+ return parenExpr(ctx, cc);
1466
+ } else if (match(ctx, TK_LEFTBRACKET)) {
1467
+ return list(ctx, cc);
1468
+ } else if (match(ctx, TK_LEFTANGLE)) {
1469
+ return lambda(ctx, cc);
1470
+ }
1471
+ return name(ctx, cc);
1472
+ }
1473
+ function postfixExpr(ctx, cc) {
1474
+ return primaryExpr(ctx, function (ctx) {
1475
+ if (match(ctx, TK_POSTOP)) {
1476
+ eat(ctx, TK_POSTOP);
1477
+ cc.cls = "operator";
1478
+ Ast.postfixExpr(ctx, lexeme);
1479
+ return cc;
1480
+ }
1481
+ return cc(ctx);
1482
+ });
1483
+ }
1484
+
1485
+ function prefixExpr(ctx, cc) {
1486
+ if (match(ctx, TK_MINUS)) {
1487
+ eat(ctx, TK_MINUS);
1488
+ const ret = function (ctx) {
1489
+ return postfixExpr(ctx, function (ctx) {
1490
+ Ast.prefixExpr(ctx, "NEG");
1491
+ return cc;
1492
+ });
1493
+ };
1494
+ ret.cls = "number"; // use number because of convention
1495
+ return ret;
1496
+ }
1497
+ return postfixExpr(ctx, cc);
1498
+ }
1499
+
1500
+ function getPrecedence(op) {
1501
+ return {
1502
+ "": 0,
1503
+ OR: 1,
1504
+ AND: 2,
1505
+ EQ: 3,
1506
+ NE: 3,
1507
+ LT: 4,
1508
+ GT: 4,
1509
+ LE: 4,
1510
+ GE: 4,
1511
+ CONCAT: 5,
1512
+ ADD: 5,
1513
+ SUB: 5,
1514
+ MUL: 6,
1515
+ DIV: 6,
1516
+ MOD: 6,
1517
+ POW: 7
1518
+ }[op];
1519
+ }
1520
+
1521
+ function binaryExpr(ctx, prevOp, cc) {
1522
+ return prefixExpr(ctx, function (ctx) {
1523
+ if (match(ctx, TK_BINOP)) {
1524
+ eat(ctx, TK_BINOP);
1525
+ const ret = function (ctx) {
1526
+ let op = env.findWord(ctx, lexeme).name;
1527
+ if (getPrecedence(prevOp) < getPrecedence(op)) {
1528
+ return binaryExpr(ctx, op, function (ctx, prevOp) {
1529
+ // This continuation's purpose is to construct a right recursive
1530
+ // binary expression node. If the previous node is a binary node
1531
+ // with equal or higher precedence, then we get here from the left
1532
+ // recursive branch below and there is no way to know the current
1533
+ // operator unless it gets passed as an argument, which is what
1534
+ // prevOp is for.
1535
+ if (prevOp !== undefined) {
1536
+ op = prevOp;
1537
+ }
1538
+ Ast.binaryExpr(ctx, op);
1539
+ return cc(ctx);
1540
+ });
1541
+ } else {
1542
+ Ast.binaryExpr(ctx, prevOp);
1543
+ return binaryExpr(ctx, op, function (ctx, prevOp) {
1544
+ if (prevOp !== undefined) {
1545
+ op = prevOp;
1546
+ }
1547
+ return cc(ctx, op);
1548
+ });
1549
+ }
1550
+ };
1551
+ ret.cls = "operator";
1552
+ return ret;
1553
+ }
1554
+ return cc(ctx);
1555
+ });
1556
+ }
1557
+
1558
+ function relationalExpr(ctx, cc) {
1559
+ return binaryExpr(ctx, "", function (ctx) {
1560
+ return cc(ctx);
1561
+ });
1562
+ }
1563
+
1564
+ function condExpr(ctx, cc) {
1565
+ if (match(ctx, TK_CASE)) {
1566
+ return caseExpr(ctx, cc);
1567
+ }
1568
+ if (match(ctx, TK_IF)) {
1569
+ return ifExpr(ctx, cc);
1570
+ }
1571
+ return relationalExpr(ctx, cc);
1572
+ }
1573
+
1574
+ function ifExpr(ctx, cc) {
1575
+ eat(ctx, TK_IF);
1576
+ const ret = function (ctx) {
1577
+ return exprsStart(ctx, TK_THEN, function (ctx) {
1578
+ eat(ctx, TK_THEN);
1579
+ return exprsStart(ctx, TK_ELSE, function (ctx) {
1580
+ eat(ctx, TK_ELSE);
1581
+ return expr(ctx, function (ctx) {
1582
+ Ast.ifExpr(ctx);
1583
+ cc.cls = "keyword";
1584
+ return cc;
1585
+ });
1586
+ });
1587
+ });
1588
+ };
1589
+ ret.cls = "keyword";
1590
+ return ret;
1591
+ }
1592
+
1593
+ function caseExpr(ctx, cc) {
1594
+ eat(ctx, TK_CASE);
1595
+ const ret = function (ctx) {
1596
+ return exprsStart(ctx, TK_OF, function (ctx) {
1597
+ eat(ctx, TK_OF);
1598
+ startCounter(ctx);
1599
+ return ofClauses(ctx, function (ctx) {
1600
+ Ast.caseExpr(ctx, ctx.state.exprc);
1601
+ stopCounter(ctx);
1602
+ eat(ctx, TK_END);
1603
+ cc.cls = "keyword";
1604
+ return cc;
1605
+ });
1606
+ });
1607
+ };
1608
+ ret.cls = "keyword";
1609
+ return ret;
1610
+ }
1611
+
1612
+ function ofClauses(ctx, cc) {
1613
+ return ofClause(ctx, function (ctx) {
1614
+ countCounter(ctx);
1615
+ if (!match(ctx, TK_END)) {
1616
+ return ofClauses(ctx, cc);
1617
+ }
1618
+ return cc(ctx);
1619
+ });
1620
+ }
1621
+
1622
+ function ofClause(ctx, cc) {
1623
+ const ret = function (ctx) {
1624
+ return pattern(ctx, function (ctx) {
1625
+ eat(ctx, TK_COLON);
1626
+ const ret = function (ctx) {
1627
+ return expr(ctx, function (ctx) {
1628
+ Ast.ofClause(ctx);
1629
+ return cc(ctx);
1630
+ });
1631
+ };
1632
+ ret.cls = "punc";
1633
+ return ret;
1634
+ });
1635
+ };
1636
+ ret.cls = "keyword";
1637
+ return ret;
1638
+ }
1639
+
1640
+ function pattern(ctx, cc) {
1641
+ // FIXME only matches idents and literals for now
1642
+ if (match(ctx, TK_IDENT)) {
1643
+ return ident(ctx, cc);
1644
+ }
1645
+ if (match(ctx, TK_NUM)) {
1646
+ return number(ctx, cc);
1647
+ }
1648
+ if (match(ctx, TK_BOOL)) {
1649
+ return bool(ctx, cc);
1650
+ }
1651
+ return str(ctx, cc);
1652
+ }
1653
+
1654
+ // function thenClause(ctx, cc) {
1655
+ // eat(ctx, TK_THEN);
1656
+ // const ret = function (ctx) {
1657
+ // return exprsStart(ctx, TK_ELSE, function (ctx) {
1658
+ // if (match(ctx, TK_ELSE)) {
1659
+ // return elseClause(ctx, cc);
1660
+ // } else {
1661
+ // return cc(ctx);
1662
+ // }
1663
+ // });
1664
+ // };
1665
+ // ret.cls = "keyword";
1666
+ // return ret;
1667
+ // }
1668
+
1669
+ // function elseClause(ctx, cc) {
1670
+ // eat(ctx, TK_ELSE);
1671
+ // const ret = function (ctx) {
1672
+ // return exprsStart(ctx, TK_END, cc);
1673
+ // };
1674
+ // ret.cls = "keyword";
1675
+ // return ret;
1676
+ // }
1677
+
1678
+ function expr(ctx, cc) {
1679
+ let ret;
1680
+ if (match(ctx, TK_LET)) {
1681
+ ret = letDef(ctx, cc);
1682
+ } else {
1683
+ ret = condExpr(ctx, cc);
1684
+ }
1685
+ return ret;
1686
+ }
1687
+
1688
+ function emptyInput(ctx) {
1689
+ return peek(ctx) === 0;
1690
+ }
1691
+
1692
+ function emptyExpr(ctx) {
1693
+ return emptyInput(ctx) ||
1694
+ match(ctx, TK_THEN) ||
1695
+ match(ctx, TK_ELSE) ||
1696
+ match(ctx, TK_OR) ||
1697
+ match(ctx, TK_END) ||
1698
+ match(ctx, TK_DOT);
1699
+ }
1700
+
1701
+ function countCounter(ctx) {
1702
+ ctx.state.exprc++;
1703
+ }
1704
+
1705
+ function startCounter(ctx) {
1706
+ ctx.state.exprcStack.push(ctx.state.exprc);
1707
+ ctx.state.exprc = 0;
1708
+ }
1709
+
1710
+ function stopCounter(ctx) {
1711
+ ctx.state.exprc = ctx.state.exprcStack.pop();
1712
+ }
1713
+
1714
+ function exprsStart(ctx, brk, cc) {
1715
+ startCounter(ctx);
1716
+ return exprs(ctx, brk, cc);
1717
+ }
1718
+
1719
+ function exprsFinish(ctx, cc) {
1720
+ Ast.exprs(ctx, ctx.state.exprc);
1721
+ stopCounter(ctx);
1722
+ return cc(ctx);
1723
+ }
1724
+
1725
+ function exprs(ctx, brk, cc) {
1726
+ if (match(ctx, TK_DOT)) { // second dot
1727
+ eat(ctx, TK_DOT);
1728
+ const ret = function (ctx) {
1729
+ return exprsFinish(ctx, cc);
1730
+ };
1731
+ ret.cls = "punc";
1732
+ return ret;
1733
+ }
1734
+ return expr(ctx, function (ctx) {
1735
+ countCounter(ctx);
1736
+ let ret;
1737
+ if (match(ctx, TK_DOT)) {
1738
+ eat(ctx, TK_DOT);
1739
+ ret = function (ctx) {
1740
+ if (emptyInput(ctx) || emptyExpr(ctx)) {
1741
+ return exprsFinish(ctx, cc);
1742
+ }
1743
+ return exprs(ctx, brk, cc);
1744
+ };
1745
+ ret.cls = "punc";
1746
+ return ret;
1747
+ } else if (match(ctx, brk)) {
1748
+ ret = function (ctx) {
1749
+ return exprsFinish(ctx, cc);
1750
+ };
1751
+ ret.cls = "punc";
1752
+ return ret;
1753
+ } else {
1754
+ if (emptyInput(ctx) || emptyExpr(ctx)) {
1755
+ return exprsFinish(ctx, cc);
1756
+ }
1757
+ return exprs(ctx, brk, cc);
1758
+ }
1759
+ });
1760
+ }
1761
+
1762
+ function program(ctx, cc) {
1763
+ return exprsStart(ctx, TK_DOT, function (ctx) {
1764
+ let nid;
1765
+ while (Ast.peek(ctx) !== nid) {
1766
+ nid = Ast.pop(ctx);
1767
+ folder.fold(ctx, nid); // fold the exprs on top
1768
+ }
1769
+ Ast.exprs(ctx, ctx.state.nodeStack.length, true);
1770
+ Ast.program(ctx);
1771
+ assert(cc === null, "internal error, expecting null continuation");
1772
+ return cc;
1773
+ });
1774
+ }
1775
+
1776
+ window.gcexports.program = program;
1777
+
1778
+ /*
1779
+
1780
+ fn = { head, body }
1781
+
1782
+ */
1783
+
1784
+ function letDef(ctx, cc) {
1785
+ if (match(ctx, TK_LET)) {
1786
+ eat(ctx, TK_LET);
1787
+ const ret = function (ctx) {
1788
+ const ret = defName(ctx, function (ctx) {
1789
+ const name = Ast.node(ctx, Ast.pop(ctx)).elts[0];
1790
+ // nid=0 means def not finished yet
1791
+ env.addWord(ctx, name, {
1792
+ tk: TK_IDENT,
1793
+ cls: "function",
1794
+ length: 0,
1795
+ nid: 0,
1796
+ name
1797
+ });
1798
+ ctx.state.paramc = 0;
1799
+ env.enterEnv(ctx, name); // FIXME need to link to outer env
1800
+ return params(ctx, TK_EQUAL, function (ctx) {
1801
+ const func = env.findWord(ctx, topEnv(ctx).name);
1802
+ func.length = ctx.state.paramc;
1803
+ func.env = topEnv(ctx);
1804
+ eat(ctx, TK_EQUAL);
1805
+ const ret = function (ctx) {
1806
+ return exprsStart(ctx, TK_DOT, function (ctx) {
1807
+ const def = env.findWord(ctx, topEnv(ctx).name);
1808
+ def.nid = Ast.peek(ctx); // save node id for aliased code
1809
+ env.exitEnv(ctx);
1810
+ Ast.letDef(ctx); // Clean up stack
1811
+ return cc;
1812
+ });
1813
+ };
1814
+ ret.cls = "punc";
1815
+ return ret;
1816
+ });
1817
+ });
1818
+ ret.cls = "def";
1819
+ return ret;
1820
+ };
1821
+ ret.cls = "keyword";
1822
+ return ret;
1823
+ }
1824
+ return name(ctx, cc);
1825
+ }
1826
+
1827
+ // TODO add argument for specifying the break token.
1828
+ // e.g. TK_EQUAL | TK_VERTICALBAR
1829
+ // params(ctx, brk, cc) {..}
1830
+ function params(ctx, brk, cc) {
1831
+ if (match(ctx, brk)) {
1832
+ return cc;
1833
+ }
1834
+ const ret = function (ctx) {
1835
+ const ret = defName(ctx, (ctx) => {
1836
+ Ast.pop(ctx); // Throw away name.
1837
+ ctx.state.paramc++;
1838
+ return params(ctx, brk, cc);
1839
+ });
1840
+ ret.cls = "param";
1841
+ return ret;
1842
+ };
1843
+ ret.cls = "param";
1844
+ return ret;
1845
+ }
1846
+
1847
+ function topEnv(ctx) {
1848
+ return ctx.state.env[ctx.state.env.length - 1];
1849
+ }
1850
+
1851
+ window.gcexports.topEnv = topEnv;
1852
+ window.gcexports.firstTime = true;
1853
+ function parse(stream, state) {
1854
+ const ctx = {
1855
+ scan: scanner(stream, state.env[0].lexicon),
1856
+ state
1857
+ };
1858
+ let cls;
1859
+ let t0;
1860
+ try {
1861
+ let c;
1862
+ while ((c = stream.peek()) && (c === " " || c === "\t")) {
1863
+ stream.next();
1864
+ }
1865
+ // if this is a blank line, treat it as a comment
1866
+ if (stream.peek() === undefined) {
1867
+ throw new Error("comment");
1868
+ }
1869
+ // call the continuation and store the next continuation
1870
+ if (state.cc === null) {
1871
+ next(ctx);
1872
+ return "comment";
1873
+ }
1874
+ t0 = new Date();
1875
+ const cc = state.cc = state.cc(ctx, null);
1876
+ if (cc) {
1877
+ cls = cc.cls;
1878
+ }
1879
+ if (cc === null) {
1880
+ // FIXME make all paths go through a resume function.
1881
+ if (state.errors.length > 0) {
1882
+ throw new Error(state.errors);
1883
+ } else {
1884
+ return Ast.poolToJSON(ctx);
1885
+ }
1886
+ }
1887
+ while ((c = stream.peek()) &&
1888
+ (c === " " || c === "\t")) {
1889
+ stream.next();
1890
+ }
1891
+ } catch (x) {
1892
+ console.log("catch() x=" + x);
1893
+ if (x instanceof Error) {
1894
+ if (x.message === "comment") {
1895
+ cls = x;
1896
+ } else {
1897
+ console.log("catch() x=" + x.stack);
1898
+ // next(ctx);
1899
+ return Ast.poolToJSON(ctx);
1900
+ // state.cc = null; // done for now.
1901
+ // cls = "error";
1902
+ // throw new Error(JSON.stringify(window.gcexports.errors, null, 2));
1903
+ }
1904
+ } else {
1905
+ // throw x
1906
+ next(ctx);
1907
+ cls = "error";
1908
+ console.log(x.stack);
1909
+ }
1910
+ }
1911
+ const t1 = new Date();
1912
+ parseCount++;
1913
+ parseTime += t1 - t0;
1914
+ window.gcexports.coords = state.coords;
1915
+ return cls;
1916
+ }
1917
+
1918
+ let lexeme = "";
1919
+
1920
+ function scanner(stream, globalLexicon) {
1921
+ return {
1922
+ start,
1923
+ stream,
1924
+ lexeme: function () {
1925
+ return lexeme;
1926
+ }
1927
+ };
1928
+
1929
+ // begin private functions
1930
+
1931
+ function peekCC() {
1932
+ return stream.peek() && stream.peek().charCodeAt(0) || 0;
1933
+ }
1934
+
1935
+ function nextCC() {
1936
+ return stream.peek() && stream.next().charCodeAt(0) || 0;
1937
+ }
1938
+
1939
+ function start(ctx) {
1940
+ let c;
1941
+ lexeme = "";
1942
+ while (stream.peek() !== undefined) {
1943
+ switch ((c = stream.next().charCodeAt(0))) {
1944
+ case 32: // space
1945
+ case 9: // tab
1946
+ case 10: // new line
1947
+ case 13: // carriage return
1948
+ c = " ";
1949
+ continue;
1950
+ case 46: // dot
1951
+ if (isNumeric(stream.peek())) {
1952
+ return number(c);
1953
+ }
1954
+ lexeme += String.fromCharCode(c);
1955
+ return TK_DOT;
1956
+ case 44: // comma
1957
+ lexeme += String.fromCharCode(c);
1958
+ return TK_COMMA;
1959
+ case 58: // colon
1960
+ lexeme += String.fromCharCode(c);
1961
+ return TK_COLON;
1962
+ case 61: // equal
1963
+ lexeme += String.fromCharCode(c);
1964
+ return TK_EQUAL;
1965
+ case 40: // left paren
1966
+ lexeme += String.fromCharCode(c);
1967
+ return TK_LEFTPAREN;
1968
+ case 41: // right paren
1969
+ lexeme += String.fromCharCode(c);
1970
+ return TK_RIGHTPAREN;
1971
+ case 45: // dash
1972
+ lexeme += String.fromCharCode(c);
1973
+ return TK_MINUS;
1974
+ case 60: // left angle
1975
+ lexeme += String.fromCharCode(c);
1976
+ return TK_LEFTANGLE;
1977
+ case 62: // right angle
1978
+ lexeme += String.fromCharCode(c);
1979
+ return TK_RIGHTANGLE;
1980
+ case 91: // left bracket
1981
+ lexeme += String.fromCharCode(c);
1982
+ return TK_LEFTBRACKET;
1983
+ case 93: // right bracket
1984
+ lexeme += String.fromCharCode(c);
1985
+ return TK_RIGHTBRACKET;
1986
+ case 123: // left brace
1987
+ lexeme += String.fromCharCode(c);
1988
+ return TK_LEFTBRACE;
1989
+ case 125: // right brace
1990
+ lexeme += String.fromCharCode(c);
1991
+ if (ctx.state.inStr) {
1992
+ return stringSuffix(ctx);
1993
+ }
1994
+ return TK_RIGHTBRACE;
1995
+ case CC_DOUBLEQUOTE:
1996
+ case CC_SINGLEQUOTE:
1997
+ case CC_BACKTICK:
1998
+ return string(ctx, c);
1999
+ case 96: // backquote
2000
+ case 47: // slash
2001
+ case 92: // backslash
2002
+ case 33: // !
2003
+ case 124: // |
2004
+ comment(c);
2005
+ throw new Error("comment");
2006
+ case 94: // caret
2007
+ case 42: // asterisk
2008
+ lexeme += String.fromCharCode(c);
2009
+ return c; // char code is the token id
2010
+ default:
2011
+ if ((c >= "A".charCodeAt(0) && c <= "Z".charCodeAt(0)) ||
2012
+ (c >= "a".charCodeAt(0) && c <= "z".charCodeAt(0)) ||
2013
+ (c === "_".charCodeAt(0))) {
2014
+ return ident(c);
2015
+ } else if (isNumeric(c) || c === ".".charCodeAt(0) && isNumeric(stream.peek())) {
2016
+ // lex += String.fromCharCode(c);
2017
+ // c = src.charCodeAt(curIndex++);
2018
+ // return TK_NUM;
2019
+ return number(c);
2020
+ } else {
2021
+ return 0;
2022
+ }
2023
+ }
2024
+ }
2025
+
2026
+ return 0;
2027
+ }
2028
+
2029
+ function isNumeric(c) {
2030
+ if (typeof c === "string") {
2031
+ c = c.charCodeAt(0);
2032
+ }
2033
+ return c >= "0".charCodeAt(0) && c <= "9".charCodeAt(0);
2034
+ }
2035
+
2036
+ function number(c) {
2037
+ // 123, 1.23, .123
2038
+ while (isNumeric(c) || c === ".".charCodeAt(0) && isNumeric(stream.peek())) {
2039
+ lexeme += String.fromCharCode(c);
2040
+ const s = stream.next();
2041
+ c = s ? s.charCodeAt(0) : 0;
2042
+ }
2043
+ if (c) {
2044
+ stream.backUp(1);
2045
+ } // otherwise, we are at the end of stream
2046
+ return TK_NUM;
2047
+ }
2048
+
2049
+ // `abc` --> "abc"
2050
+ // `a${x}c` --> concat ["a", x, "b"]
2051
+ function string(ctx, c) {
2052
+ const quoteChar = c;
2053
+ ctx.state.quoteCharStack.push(c);
2054
+ lexeme += String.fromCharCode(c);
2055
+ c = nextCC();
2056
+ const inTemplateLiteral = quoteChar === CC_BACKTICK;
2057
+ if (inTemplateLiteral) {
2058
+ while (
2059
+ c !== quoteChar &&
2060
+ c !== 0 &&
2061
+ !(c === CC_DOLLAR && peekCC() === CC_LEFTBRACE)) {
2062
+ lexeme += String.fromCharCode(c);
2063
+ c = nextCC();
2064
+ }
2065
+ } else {
2066
+ while (c !== quoteChar && c !== 0) {
2067
+ lexeme += String.fromCharCode(c);
2068
+ c = nextCC();
2069
+ }
2070
+ }
2071
+ if (quoteChar === CC_BACKTICK && c === CC_DOLLAR &&
2072
+ peekCC() === CC_LEFTBRACE) {
2073
+ nextCC(); // Eat CC_LEFTBRACE
2074
+ lexeme = lexeme.substring(1); // Strip off punct.
2075
+ return TK_STRPREFIX;
2076
+ } else if (c) {
2077
+ lexeme = lexeme.substring(1); // Strip off leading quote.
2078
+ return TK_STR;
2079
+ } else {
2080
+ return 0;
2081
+ }
2082
+ }
2083
+
2084
+ function stringSuffix(ctx) {
2085
+ let c;
2086
+ const quoteCharStack = ctx.state.quoteCharStack;
2087
+ const quoteChar = quoteCharStack[quoteCharStack.length - 1];
2088
+ c = nextCC();
2089
+ const inTemplateLiteral = quoteChar === CC_BACKTICK;
2090
+ if (inTemplateLiteral) {
2091
+ while (c !== quoteChar && c !== 0 &&
2092
+ !(c === CC_DOLLAR &&
2093
+ peekCC() === CC_LEFTBRACE)) {
2094
+ lexeme += String.fromCharCode(c);
2095
+ c = nextCC();
2096
+ }
2097
+ } else {
2098
+ while (c !== quoteChar && c !== 0) {
2099
+ lexeme += String.fromCharCode(c);
2100
+ c = nextCC();
2101
+ }
2102
+ }
2103
+ if (quoteChar === CC_BACKTICK && c === CC_DOLLAR &&
2104
+ peekCC() === CC_LEFTBRACE) {
2105
+ nextCC(); // Eat brace.
2106
+ lexeme = lexeme.substring(1); // Strip off leading brace and trailing brace.
2107
+ return TK_STRMIDDLE;
2108
+ } else if (c) {
2109
+ quoteCharStack.pop();
2110
+ lexeme = lexeme.substring(1); // Strip off leading braces.
2111
+ return TK_STRSUFFIX;
2112
+ } else {
2113
+ return 0;
2114
+ }
2115
+ }
2116
+
2117
+ function comment(c) {
2118
+ const quoteChar = c;
2119
+ let s = stream.next();
2120
+ c = (s && s.charCodeAt(0)) || 0;
2121
+
2122
+ while (c !== quoteChar && c !== 10 && c !== 13 && c !== 0) {
2123
+ s = stream.next();
2124
+ c = (s && s.charCodeAt(0)) || 0;
2125
+ }
2126
+
2127
+ return TK_COMMENT;
2128
+ }
2129
+
2130
+ function ident(c) {
2131
+ while ((c >= "A".charCodeAt(0) && c <= "Z".charCodeAt(0)) ||
2132
+ (c >= "a".charCodeAt(0) && c <= "z".charCodeAt(0)) ||
2133
+ (c === "-".charCodeAt(0)) ||
2134
+ (c === "@".charCodeAt(0)) ||
2135
+ (c === "+".charCodeAt(0)) ||
2136
+ (c === "#".charCodeAt(0)) ||
2137
+ (c === "_".charCodeAt(0)) ||
2138
+ (c === "~".charCodeAt(0)) ||
2139
+ (c >= "0".charCodeAt(0) && c <= "9".charCodeAt(0))) {
2140
+ lexeme += String.fromCharCode(c);
2141
+ c = stream.peek() ? stream.next().charCodeAt(0) : 0;
2142
+ }
2143
+
2144
+ if (c) {
2145
+ stream.backUp(1);
2146
+ } // otherwise, we are at the end of stream
2147
+
2148
+ let tk = TK_IDENT;
2149
+ if (keywords[lexeme]) {
2150
+ tk = keywords[lexeme].tk;
2151
+ } else if (globalLexicon[lexeme]) {
2152
+ tk = globalLexicon[lexeme].tk;
2153
+ }
2154
+ return tk;
2155
+ }
2156
+ }
2157
+
2158
+ const parser = {
2159
+ token: function (stream, state) {
2160
+ return parse(stream, state);
2161
+ },
2162
+
2163
+ parse,
2164
+ program,
2165
+ StringStream
2166
+ };
2167
+
2168
+ window.gcexports.parse = parser.parse;
2169
+ if (window.isSynthetic) {
2170
+ // Export in node.
2171
+ // exports.parse = window.gcexports.parse;
2172
+ // exports.StringStream = window.gcexports.StringStream;
2173
+ // exports.program = program;
2174
+ }
2175
+
2176
+ return parser;
2177
+ })(); // end parser