@graffiticode/basis 1.5.7 → 1.5.8
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/package.json +1 -1
- package/src/compiler.js +2 -1
- package/src/parse.js +0 -2426
- package/src/style.css +0 -1
- package/src/viewer.jsx +0 -24
package/src/parse.js
DELETED
|
@@ -1,2426 +0,0 @@
|
|
|
1
|
-
// Copyright 2021, ARTCOMPILER INC
|
|
2
|
-
|
|
3
|
-
function assert(b, str) {
|
|
4
|
-
if (!b) throw str;
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
const Ast = (function () {
|
|
8
|
-
var ASSERT = true;
|
|
9
|
-
var assert = function (val, str) {
|
|
10
|
-
if ( !ASSERT ) {
|
|
11
|
-
return;
|
|
12
|
-
}
|
|
13
|
-
if ( str === void 0 ) {
|
|
14
|
-
str = "failed!";
|
|
15
|
-
}
|
|
16
|
-
if (!val) {
|
|
17
|
-
throw new Error(str);
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
var AstClass = function() { }
|
|
22
|
-
|
|
23
|
-
AstClass.prototype = {
|
|
24
|
-
intern: intern,
|
|
25
|
-
node: node,
|
|
26
|
-
dump: dump,
|
|
27
|
-
dumpAll: dumpAll,
|
|
28
|
-
poolToJSON: poolToJSON,
|
|
29
|
-
number: number,
|
|
30
|
-
string: string,
|
|
31
|
-
name: name,
|
|
32
|
-
apply: apply,
|
|
33
|
-
fold: fold,
|
|
34
|
-
expr: expr,
|
|
35
|
-
binaryExpr: binaryExpr,
|
|
36
|
-
unaryExpr: unaryExpr,
|
|
37
|
-
parenExpr: parenExpr,
|
|
38
|
-
prefixExpr: prefixExpr,
|
|
39
|
-
lambda: lambda,
|
|
40
|
-
applyLate: applyLate,
|
|
41
|
-
letDef: letDef,
|
|
42
|
-
caseExpr: caseExpr,
|
|
43
|
-
ofClause: ofClause,
|
|
44
|
-
record: record,
|
|
45
|
-
binding: binding,
|
|
46
|
-
exprs: exprs,
|
|
47
|
-
program: program,
|
|
48
|
-
pop: pop,
|
|
49
|
-
topNode: topNode,
|
|
50
|
-
peek: peek,
|
|
51
|
-
push: push,
|
|
52
|
-
mod: mod,
|
|
53
|
-
add: add,
|
|
54
|
-
sub: sub,
|
|
55
|
-
// mul: mul,
|
|
56
|
-
div: div,
|
|
57
|
-
pow: pow,
|
|
58
|
-
concat: concat,
|
|
59
|
-
orelse: orelse,
|
|
60
|
-
andalso: andalso,
|
|
61
|
-
eq: eq,
|
|
62
|
-
ne: ne,
|
|
63
|
-
lt: lt,
|
|
64
|
-
gt: gt,
|
|
65
|
-
le: le,
|
|
66
|
-
ge: ge,
|
|
67
|
-
neg: neg,
|
|
68
|
-
list: list,
|
|
69
|
-
bool: bool,
|
|
70
|
-
nul: nul,
|
|
71
|
-
};
|
|
72
|
-
|
|
73
|
-
return new AstClass;
|
|
74
|
-
|
|
75
|
-
// private implementation
|
|
76
|
-
|
|
77
|
-
function push(ctx, node) {
|
|
78
|
-
var nid;
|
|
79
|
-
if (typeof node === "number") { // if already interned
|
|
80
|
-
nid = node;
|
|
81
|
-
} else {
|
|
82
|
-
nid = intern(ctx, node);
|
|
83
|
-
}
|
|
84
|
-
ctx.state.nodeStack.push(nid);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
function topNode(ctx) {
|
|
88
|
-
var nodeStack = ctx.state.nodeStack;
|
|
89
|
-
return nodeStack[nodeStack.length-1];
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
function pop(ctx) {
|
|
93
|
-
var nodeStack = ctx.state.nodeStack;
|
|
94
|
-
return nodeStack.pop();
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
function peek(ctx, n) {
|
|
98
|
-
if (n === undefined) {
|
|
99
|
-
n = 0;
|
|
100
|
-
}
|
|
101
|
-
var nodeStack = ctx.state.nodeStack;
|
|
102
|
-
return nodeStack[nodeStack.length - 1 - n];
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
function intern(ctx, n) {
|
|
106
|
-
if (!n) {
|
|
107
|
-
return 0;
|
|
108
|
-
}
|
|
109
|
-
var nodeMap = ctx.state.nodeMap;
|
|
110
|
-
var nodePool = ctx.state.nodePool;
|
|
111
|
-
var tag = n.tag;
|
|
112
|
-
var elts = "";
|
|
113
|
-
var elts_nids = [ ];
|
|
114
|
-
var count = n.elts.length;
|
|
115
|
-
for (var i = 0; i < count; i++) {
|
|
116
|
-
if (typeof n.elts[i] === "object") {
|
|
117
|
-
n.elts[i] = intern(ctx, n.elts[i]);
|
|
118
|
-
}
|
|
119
|
-
elts += n.elts[i];
|
|
120
|
-
}
|
|
121
|
-
var key = tag+count+elts;
|
|
122
|
-
var nid = nodeMap[key];
|
|
123
|
-
if (nid === void 0) {
|
|
124
|
-
nodePool.push({tag: tag, elts: n.elts});
|
|
125
|
-
nid = nodePool.length - 1;
|
|
126
|
-
nodeMap[key] = nid;
|
|
127
|
-
if (n.coord) {
|
|
128
|
-
ctx.state.coords[nid] = n.coord;
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
return nid;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
function node(ctx, nid) {
|
|
135
|
-
var n = ctx.state.nodePool[nid];
|
|
136
|
-
if (!nid) {
|
|
137
|
-
return null;
|
|
138
|
-
} else if (!n) {
|
|
139
|
-
return {};
|
|
140
|
-
}
|
|
141
|
-
var elts = [];
|
|
142
|
-
switch (n.tag) {
|
|
143
|
-
case "NULL":
|
|
144
|
-
break;
|
|
145
|
-
case "NUM":
|
|
146
|
-
case "STR":
|
|
147
|
-
case "IDENT":
|
|
148
|
-
case "BOOL":
|
|
149
|
-
elts[0] = n.elts[0];
|
|
150
|
-
break;
|
|
151
|
-
default:
|
|
152
|
-
for (var i=0; i < n.elts.length; i++) {
|
|
153
|
-
elts[i] = node(ctx, n.elts[i]);
|
|
154
|
-
}
|
|
155
|
-
break;
|
|
156
|
-
}
|
|
157
|
-
return {
|
|
158
|
-
tag: n.tag,
|
|
159
|
-
elts: elts,
|
|
160
|
-
coord: getCoord(ctx),
|
|
161
|
-
};
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
function dumpAll(ctx) {
|
|
165
|
-
var nodePool = ctx.state.nodePool;
|
|
166
|
-
var s = "\n{"
|
|
167
|
-
for (var i=1; i < nodePool.length; i++) {
|
|
168
|
-
var n = nodePool[i];
|
|
169
|
-
s = s + "\n " + i+": "+dump(n) + ",";
|
|
170
|
-
}
|
|
171
|
-
s += "\n root: " + (nodePool.length-1);
|
|
172
|
-
s += "\n}\n";
|
|
173
|
-
return s;
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
function poolToJSON(ctx) {
|
|
177
|
-
var nodePool = ctx.state.nodePool;
|
|
178
|
-
var obj = { };
|
|
179
|
-
for (var i=1; i < nodePool.length; i++) {
|
|
180
|
-
var n = nodePool[i];
|
|
181
|
-
obj[i] = nodeToJSON(n);
|
|
182
|
-
}
|
|
183
|
-
obj.root = (nodePool.length-1);
|
|
184
|
-
obj.version = window.gcexports.version;
|
|
185
|
-
return obj;
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
function nodeToJSON(n) {
|
|
189
|
-
if (typeof n === "object") {
|
|
190
|
-
switch (n.tag) {
|
|
191
|
-
case "num":
|
|
192
|
-
var obj = n.elts[0];
|
|
193
|
-
break;
|
|
194
|
-
case "str":
|
|
195
|
-
var obj = n.elts[0];
|
|
196
|
-
break;
|
|
197
|
-
default:
|
|
198
|
-
var obj = {};
|
|
199
|
-
obj["tag"] = n.tag;
|
|
200
|
-
obj["elts"] = [];
|
|
201
|
-
for (var i=0; i < n.elts.length; i++) {
|
|
202
|
-
obj["elts"][i] = nodeToJSON(n.elts[i]);
|
|
203
|
-
}
|
|
204
|
-
break;
|
|
205
|
-
}
|
|
206
|
-
} else if (typeof n === "string") {
|
|
207
|
-
var obj = n;
|
|
208
|
-
} else {
|
|
209
|
-
var obj = n;
|
|
210
|
-
}
|
|
211
|
-
return obj;
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
function dump(n) {
|
|
215
|
-
if (typeof n === "object") {
|
|
216
|
-
switch (n.tag) {
|
|
217
|
-
case "num":
|
|
218
|
-
var s = n.elts[0];
|
|
219
|
-
break;
|
|
220
|
-
case "str":
|
|
221
|
-
var s = "\""+n.elts[0]+"\"";
|
|
222
|
-
break;
|
|
223
|
-
default:
|
|
224
|
-
if (!n.elts) {
|
|
225
|
-
s += "<invalid>";
|
|
226
|
-
} else {
|
|
227
|
-
var s = "{ tag: \"" + n.tag + "\", elts: [ ";
|
|
228
|
-
for (var i=0; i < n.elts.length; i++) {
|
|
229
|
-
if (i > 0) {
|
|
230
|
-
s += " , ";
|
|
231
|
-
}
|
|
232
|
-
s += dump(n.elts[i]);
|
|
233
|
-
}
|
|
234
|
-
s += " ] }";
|
|
235
|
-
}
|
|
236
|
-
break;
|
|
237
|
-
}
|
|
238
|
-
} else if (typeof n === "string") {
|
|
239
|
-
var s = "\""+n+"\"";
|
|
240
|
-
} else {
|
|
241
|
-
var s = n;
|
|
242
|
-
}
|
|
243
|
-
return s;
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
function fold(ctx, fn, args) {
|
|
247
|
-
// Local defs:
|
|
248
|
-
// -- put bindings in env
|
|
249
|
-
// Three cases:
|
|
250
|
-
// -- full application, all args are available at parse time
|
|
251
|
-
// -- partial application, only some args are available at parse time
|
|
252
|
-
// -- late application, args are available at compile time (not parse time)
|
|
253
|
-
// apply <[x y]: add x y> data..
|
|
254
|
-
// x: val 0 data
|
|
255
|
-
// y: val 1 data
|
|
256
|
-
env.enterEnv(ctx, fn.name);
|
|
257
|
-
if (fn.env) {
|
|
258
|
-
var lexicon = fn.env.lexicon;
|
|
259
|
-
var pattern = Ast.node(ctx, fn.env.pattern);
|
|
260
|
-
var outerEnv = null;
|
|
261
|
-
// setup inner environment record (lexicon)
|
|
262
|
-
if (pattern && pattern.elts &&
|
|
263
|
-
pattern.elts.length === 1 &&
|
|
264
|
-
pattern.elts[0].tag === "LIST") {
|
|
265
|
-
// For now we only support one pattern per param list.
|
|
266
|
-
var isListPattern = true;
|
|
267
|
-
}
|
|
268
|
-
for (var id in lexicon) {
|
|
269
|
-
// For each parameter, get its definition assign the value of the argument
|
|
270
|
-
// used on the current function application.
|
|
271
|
-
if (!id) continue;
|
|
272
|
-
var word = JSON.parse(JSON.stringify(lexicon[id])); // poor man's copy.
|
|
273
|
-
var index = args.length - word.offset - 1;
|
|
274
|
-
// TODO we currently ignore list patterns
|
|
275
|
-
// if (isListPattern) {
|
|
276
|
-
// // <[x y]: ...> foo..
|
|
277
|
-
// word.nid = Ast.intern(ctx, {
|
|
278
|
-
// tag: "VAL",
|
|
279
|
-
// elts: [{
|
|
280
|
-
// tag: "NUM",
|
|
281
|
-
// elts: [
|
|
282
|
-
// String(word.offset),
|
|
283
|
-
// ]}, {
|
|
284
|
-
// tag: "ARG",
|
|
285
|
-
// elts: [{
|
|
286
|
-
// tag: "NUM",
|
|
287
|
-
// elts: ["0"]
|
|
288
|
-
// }]
|
|
289
|
-
// }]
|
|
290
|
-
// });
|
|
291
|
-
// } else
|
|
292
|
-
if (index >= 0 && index < args.length) {
|
|
293
|
-
word.nid = args[index];
|
|
294
|
-
}
|
|
295
|
-
if (index < 0) {
|
|
296
|
-
// We've got an unbound variable or a variable with a default value,
|
|
297
|
-
// so add it to the new variable list.
|
|
298
|
-
// <x:x> => <x:x>
|
|
299
|
-
// (<x y: add x y> 10) => <y: add 10 y>
|
|
300
|
-
// (<y: let x = 10.. add x y>) => <y: add 10 y>
|
|
301
|
-
if (!outerEnv) {
|
|
302
|
-
outerEnv = {};
|
|
303
|
-
}
|
|
304
|
-
outerEnv[id] = word;
|
|
305
|
-
}
|
|
306
|
-
env.addWord(ctx, id, word);
|
|
307
|
-
}
|
|
308
|
-
folder.fold(ctx, fn.nid);
|
|
309
|
-
if (outerEnv) {
|
|
310
|
-
lambda(ctx, {
|
|
311
|
-
lexicon: outerEnv,
|
|
312
|
-
pattern: pattern, // FIXME need to trim pattern if some args where applied.
|
|
313
|
-
}, pop(ctx));
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
env.exitEnv(ctx);
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
function applyLate(ctx, count) {
|
|
320
|
-
// Ast.applyLate
|
|
321
|
-
var elts = [];
|
|
322
|
-
for (var i = count; i > 0; i--) {
|
|
323
|
-
elts.push(pop(ctx));
|
|
324
|
-
}
|
|
325
|
-
push(ctx, {
|
|
326
|
-
tag: "APPLY",
|
|
327
|
-
elts: elts,
|
|
328
|
-
});
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
function apply(ctx, fnId, argc) {
|
|
332
|
-
// Construct function and apply available arguments.
|
|
333
|
-
var fn = node(ctx, fnId);
|
|
334
|
-
// if (fn.tag !== "LAMBDA") {
|
|
335
|
-
// // Construct an APPLY node for compiling later.
|
|
336
|
-
// return {
|
|
337
|
-
// tag: "APPLY",
|
|
338
|
-
// elts: [
|
|
339
|
-
// fnId,
|
|
340
|
-
// ]
|
|
341
|
-
// };
|
|
342
|
-
// }
|
|
343
|
-
// Construct a lexicon
|
|
344
|
-
var lexicon = {};
|
|
345
|
-
var paramc = 0;
|
|
346
|
-
fn.elts[0].elts.forEach(function (n, i) {
|
|
347
|
-
var name = n.elts[0];
|
|
348
|
-
var nid = Ast.intern(ctx, fn.elts[3].elts[i]);
|
|
349
|
-
lexicon[name] = {
|
|
350
|
-
cls: "val",
|
|
351
|
-
name: name,
|
|
352
|
-
offset: i,
|
|
353
|
-
nid: nid,
|
|
354
|
-
};
|
|
355
|
-
if (!nid) {
|
|
356
|
-
// Parameters don't have nids.
|
|
357
|
-
// assert that there are parameters after a binding without a nid.
|
|
358
|
-
paramc++;
|
|
359
|
-
}
|
|
360
|
-
});
|
|
361
|
-
var def = {
|
|
362
|
-
name: "lambda",
|
|
363
|
-
nid: Ast.intern(ctx, fn.elts[1]),
|
|
364
|
-
env: {
|
|
365
|
-
lexicon: lexicon,
|
|
366
|
-
pattern: Ast.intern(ctx, fn.elts[2]),
|
|
367
|
-
},
|
|
368
|
-
};
|
|
369
|
-
var len = fn.elts[0].elts.length;
|
|
370
|
-
var elts = [];
|
|
371
|
-
// While there are args on the stack, pop them.
|
|
372
|
-
while (argc-- > 0 && paramc-- > 0) {
|
|
373
|
-
var elt = pop(ctx);
|
|
374
|
-
elts.unshift(elt); // Get the order right.
|
|
375
|
-
}
|
|
376
|
-
fold(ctx, def, elts);
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
// Node constructors
|
|
380
|
-
|
|
381
|
-
function bool(ctx, val) {
|
|
382
|
-
if (val) {
|
|
383
|
-
var b = true;
|
|
384
|
-
} else {
|
|
385
|
-
var b = false;
|
|
386
|
-
}
|
|
387
|
-
push(ctx, {
|
|
388
|
-
tag: "BOOL",
|
|
389
|
-
elts: [b]
|
|
390
|
-
});
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
function nul(ctx) {
|
|
394
|
-
push(ctx, {
|
|
395
|
-
tag: "NULL",
|
|
396
|
-
elts: []
|
|
397
|
-
});
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
function number(ctx, str, coord) {
|
|
401
|
-
assert(typeof str === "string" || typeof str === "number");
|
|
402
|
-
push(ctx, {
|
|
403
|
-
tag: "NUM",
|
|
404
|
-
elts: [String(str)],
|
|
405
|
-
coord: coord,
|
|
406
|
-
});
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
function string(ctx, str, coord) {
|
|
410
|
-
push(ctx, {
|
|
411
|
-
tag: "STR",
|
|
412
|
-
elts: [str],
|
|
413
|
-
coord: coord,
|
|
414
|
-
});
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
function name(ctx, name, coord) {
|
|
418
|
-
push(ctx, {
|
|
419
|
-
tag: "IDENT",
|
|
420
|
-
elts: [name],
|
|
421
|
-
coord: coord,
|
|
422
|
-
});
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
function expr(ctx, argc) {
|
|
426
|
-
// Ast.expr -- construct a expr node for the compiler.
|
|
427
|
-
var elts = [];
|
|
428
|
-
while (argc--) {
|
|
429
|
-
var elt = pop(ctx);
|
|
430
|
-
elts.push(elt);
|
|
431
|
-
}
|
|
432
|
-
var nameId = pop(ctx);
|
|
433
|
-
assert(nameId, "Ill formed node.");
|
|
434
|
-
var e = node(ctx, nameId).elts;
|
|
435
|
-
assert(e && e.length > 0, "Ill formed node.");
|
|
436
|
-
var name = e[0];
|
|
437
|
-
push(ctx, {
|
|
438
|
-
tag: name,
|
|
439
|
-
elts: elts,
|
|
440
|
-
coord: getCoord(ctx),
|
|
441
|
-
});
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
function parenExpr(ctx, coord) {
|
|
445
|
-
// Ast.parenExpr
|
|
446
|
-
var elts = [];
|
|
447
|
-
var elt = pop(ctx);
|
|
448
|
-
elts.push(elt);
|
|
449
|
-
push(ctx, {
|
|
450
|
-
tag: "PAREN",
|
|
451
|
-
elts: elts,
|
|
452
|
-
coord: coord,
|
|
453
|
-
});
|
|
454
|
-
}
|
|
455
|
-
|
|
456
|
-
function list(ctx, count, coord, reverse) {
|
|
457
|
-
// Ast.list
|
|
458
|
-
var elts = [];
|
|
459
|
-
for (var i = count; i > 0; i--) {
|
|
460
|
-
var elt = pop(ctx);
|
|
461
|
-
if (elt !== void 0) {
|
|
462
|
-
elts.push(elt);
|
|
463
|
-
}
|
|
464
|
-
}
|
|
465
|
-
push(ctx, {
|
|
466
|
-
tag: "LIST",
|
|
467
|
-
elts: reverse ? elts : elts.reverse(),
|
|
468
|
-
coord: coord,
|
|
469
|
-
});
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
function binaryExpr(ctx, name) {
|
|
473
|
-
var elts = [];
|
|
474
|
-
// args are in the order produced by the parser
|
|
475
|
-
elts.push(pop(ctx));
|
|
476
|
-
elts.push(pop(ctx));
|
|
477
|
-
push(ctx, {
|
|
478
|
-
tag: name,
|
|
479
|
-
elts: elts.reverse()
|
|
480
|
-
});
|
|
481
|
-
}
|
|
482
|
-
function unaryExpr(ctx, name) {
|
|
483
|
-
var elts = [];
|
|
484
|
-
elts.push(pop(ctx));
|
|
485
|
-
push(ctx, {
|
|
486
|
-
tag: name,
|
|
487
|
-
elts: elts
|
|
488
|
-
});
|
|
489
|
-
}
|
|
490
|
-
|
|
491
|
-
function prefixExpr(ctx, name) {
|
|
492
|
-
var elts = [];
|
|
493
|
-
elts.push(pop(ctx));
|
|
494
|
-
push(ctx, {
|
|
495
|
-
tag: name,
|
|
496
|
-
elts: elts
|
|
497
|
-
});
|
|
498
|
-
}
|
|
499
|
-
|
|
500
|
-
function neg(ctx) {
|
|
501
|
-
var v1 = +node(ctx, pop(ctx)).elts[0];
|
|
502
|
-
number(ctx, -1*v1);
|
|
503
|
-
}
|
|
504
|
-
|
|
505
|
-
function add(ctx, coord) {
|
|
506
|
-
var n2 = node(ctx, pop(ctx));
|
|
507
|
-
var n1 = node(ctx, pop(ctx));
|
|
508
|
-
var v2 = n2.elts[0];
|
|
509
|
-
var v1 = n1.elts[0];
|
|
510
|
-
if (n1.tag !== "NUM" || n2.tag !== "NUM") {
|
|
511
|
-
push(ctx, {
|
|
512
|
-
tag: "ADD",
|
|
513
|
-
elts: [n1, n2],
|
|
514
|
-
coord: coord
|
|
515
|
-
});
|
|
516
|
-
} else {
|
|
517
|
-
number(ctx, +v1 + +v2);
|
|
518
|
-
}
|
|
519
|
-
}
|
|
520
|
-
|
|
521
|
-
function sub(ctx) {
|
|
522
|
-
var n2 = node(ctx, pop(ctx));
|
|
523
|
-
var n1 = node(ctx, pop(ctx));
|
|
524
|
-
var v2 = n2.elts[0];
|
|
525
|
-
var v1 = n1.elts[0];
|
|
526
|
-
if (n1.tag !== "NUM" || n2.tag !== "NUM") {
|
|
527
|
-
push(ctx, {tag: "SUB", elts: [n1, n2]});
|
|
528
|
-
} else {
|
|
529
|
-
number(ctx, +v1 - +v2);
|
|
530
|
-
}
|
|
531
|
-
}
|
|
532
|
-
|
|
533
|
-
function mul(ctx) {
|
|
534
|
-
var n2 = node(ctx, pop(ctx));
|
|
535
|
-
var n1 = node(ctx, pop(ctx));
|
|
536
|
-
var v2 = n2.elts[0];
|
|
537
|
-
var v1 = n1.elts[0];
|
|
538
|
-
if (n1.tag === undefined) {
|
|
539
|
-
n1 = n1.elts[0];
|
|
540
|
-
}
|
|
541
|
-
if (n2.tag === undefined) {
|
|
542
|
-
n2 = n2.elts[0];
|
|
543
|
-
}
|
|
544
|
-
if (n1.tag !== "NUM" || n2.tag !== "NUM") {
|
|
545
|
-
push(ctx, {tag: "MUL", elts: [n2, n1]});
|
|
546
|
-
} else {
|
|
547
|
-
number(ctx, +v1 * +v2);
|
|
548
|
-
}
|
|
549
|
-
}
|
|
550
|
-
|
|
551
|
-
function div(ctx) {
|
|
552
|
-
var n2 = node(ctx, pop(ctx));
|
|
553
|
-
var n1 = node(ctx, pop(ctx));
|
|
554
|
-
var v2 = n2.elts[0];
|
|
555
|
-
var v1 = n1.elts[0];
|
|
556
|
-
if (n1.tag !== "NUM" || n2.tag !== "NUM") {
|
|
557
|
-
push(ctx, {tag: "DIV", elts: [n1, n2]});
|
|
558
|
-
} else {
|
|
559
|
-
number(ctx, +v1 / +v2);
|
|
560
|
-
}
|
|
561
|
-
}
|
|
562
|
-
|
|
563
|
-
function mod(ctx) {
|
|
564
|
-
var n2 = node(ctx, pop(ctx));
|
|
565
|
-
var n1 = node(ctx, pop(ctx));
|
|
566
|
-
var v1 = n1.elts[0];
|
|
567
|
-
var v2 = n2.elts[0];
|
|
568
|
-
if (n1.tag !== "NUM" || n2.tag !== "NUM") {
|
|
569
|
-
push(ctx, {tag: "MOD", elts: [n1, n2]});
|
|
570
|
-
} else {
|
|
571
|
-
number(ctx, +v1 % +v2);
|
|
572
|
-
}
|
|
573
|
-
}
|
|
574
|
-
|
|
575
|
-
function pow(ctx) {
|
|
576
|
-
var n2 = node(ctx, pop(ctx));
|
|
577
|
-
var n1 = node(ctx, pop(ctx));
|
|
578
|
-
var v2 = n2.elts[0];
|
|
579
|
-
var v1 = n1.elts[0];
|
|
580
|
-
if (n1.tag !== "NUM" || n2.tag !== "NUM") {
|
|
581
|
-
push(ctx, {tag: "POW", elts: [n1, n2]});
|
|
582
|
-
} else {
|
|
583
|
-
number(ctx, Math.pow(+v1, +v2));
|
|
584
|
-
}
|
|
585
|
-
}
|
|
586
|
-
|
|
587
|
-
function concat(ctx) {
|
|
588
|
-
var n1 = node(ctx, pop(ctx));
|
|
589
|
-
push(ctx, {
|
|
590
|
-
tag: "CONCAT",
|
|
591
|
-
elts: [n1]
|
|
592
|
-
});
|
|
593
|
-
}
|
|
594
|
-
|
|
595
|
-
function orelse(ctx) {
|
|
596
|
-
var v2 = +node(ctx, pop(ctx)).elts[0];
|
|
597
|
-
var v1 = +node(ctx, pop(ctx)).elts[0];
|
|
598
|
-
throw "not implemented";
|
|
599
|
-
}
|
|
600
|
-
|
|
601
|
-
function andalso(ctx) {
|
|
602
|
-
var v2 = +node(ctx, pop(ctx)).elts[0];
|
|
603
|
-
var v1 = +node(ctx, pop(ctx)).elts[0];
|
|
604
|
-
throw "not implemented";
|
|
605
|
-
}
|
|
606
|
-
|
|
607
|
-
function eq(ctx) {
|
|
608
|
-
var v2 = node(ctx, pop(ctx)).elts[0];
|
|
609
|
-
var v1 = node(ctx, pop(ctx)).elts[0];
|
|
610
|
-
bool(ctx, v1==v2);
|
|
611
|
-
}
|
|
612
|
-
|
|
613
|
-
function ne(ctx) {
|
|
614
|
-
var v2 = +node(ctx, pop(ctx)).elts[0];
|
|
615
|
-
var v1 = +node(ctx, pop(ctx)).elts[0];
|
|
616
|
-
bool(ctx, v1!=v2);
|
|
617
|
-
}
|
|
618
|
-
|
|
619
|
-
function lt(ctx) {
|
|
620
|
-
var v2 = +node(ctx, pop(ctx)).elts[0];
|
|
621
|
-
var v1 = +node(ctx, pop(ctx)).elts[0];
|
|
622
|
-
bool(ctx, v1<v2);
|
|
623
|
-
}
|
|
624
|
-
|
|
625
|
-
function gt(ctx) {
|
|
626
|
-
var v2 = +node(ctx, pop(ctx)).elts[0];
|
|
627
|
-
var v1 = +node(ctx, pop(ctx)).elts[0];
|
|
628
|
-
bool(ctx, v1>v2);
|
|
629
|
-
}
|
|
630
|
-
|
|
631
|
-
function le(ctx) {
|
|
632
|
-
var v2 = +node(ctx, pop(ctx)).elts[0];
|
|
633
|
-
var v1 = +node(ctx, pop(ctx)).elts[0];
|
|
634
|
-
bool(ctx, v1<=v2);
|
|
635
|
-
}
|
|
636
|
-
function ge(ctx) {
|
|
637
|
-
var v2 = +node(ctx, pop(ctx)).elts[0];
|
|
638
|
-
var v1 = +node(ctx, pop(ctx)).elts[0];
|
|
639
|
-
bool(ctx, v1>=v2);
|
|
640
|
-
}
|
|
641
|
-
function caseExpr(ctx, n) {
|
|
642
|
-
var elts = [];
|
|
643
|
-
for (var i = n; i > 0; i--) {
|
|
644
|
-
elts.push(pop(ctx)) // of
|
|
645
|
-
}
|
|
646
|
-
elts.push(pop(ctx)) // exprs
|
|
647
|
-
push(ctx, {tag: "CASE", elts: elts.reverse()});
|
|
648
|
-
}
|
|
649
|
-
function ofClause(ctx) {
|
|
650
|
-
var elts = [];
|
|
651
|
-
elts.push(pop(ctx));
|
|
652
|
-
elts.push(pop(ctx));
|
|
653
|
-
push(ctx, {tag: "OF", elts: elts.reverse()});
|
|
654
|
-
}
|
|
655
|
-
|
|
656
|
-
function record(ctx) {
|
|
657
|
-
// Ast.record
|
|
658
|
-
var count = ctx.state.exprc;
|
|
659
|
-
var elts = [];
|
|
660
|
-
for (var i = count; i > 0; i--) {
|
|
661
|
-
var elt = pop(ctx);
|
|
662
|
-
if (elt !== void 0) {
|
|
663
|
-
elts.push(elt);
|
|
664
|
-
}
|
|
665
|
-
}
|
|
666
|
-
push(ctx, {
|
|
667
|
-
tag: "RECORD",
|
|
668
|
-
elts: elts
|
|
669
|
-
});
|
|
670
|
-
}
|
|
671
|
-
|
|
672
|
-
function binding(ctx) {
|
|
673
|
-
// Ast.binding
|
|
674
|
-
var elts = [];
|
|
675
|
-
elts.push(pop(ctx));
|
|
676
|
-
elts.push(pop(ctx));
|
|
677
|
-
push(ctx, {
|
|
678
|
-
tag: "BINDING",
|
|
679
|
-
elts: elts.reverse()
|
|
680
|
-
});
|
|
681
|
-
}
|
|
682
|
-
|
|
683
|
-
function lambda(ctx, env, nid) {
|
|
684
|
-
// Ast.lambda
|
|
685
|
-
var names = [];
|
|
686
|
-
var nids = [];
|
|
687
|
-
for (var id in env.lexicon) {
|
|
688
|
-
var word = env.lexicon[id];
|
|
689
|
-
names.push({
|
|
690
|
-
tag: "IDENT",
|
|
691
|
-
elts: [word.name],
|
|
692
|
-
coord: getCoord(ctx),
|
|
693
|
-
});
|
|
694
|
-
nids.push(word.nid || 0);
|
|
695
|
-
}
|
|
696
|
-
var pattern = env.pattern;
|
|
697
|
-
push(ctx, {
|
|
698
|
-
tag: "LAMBDA",
|
|
699
|
-
elts: [{
|
|
700
|
-
tag: "LIST",
|
|
701
|
-
elts: names
|
|
702
|
-
}, nid, {
|
|
703
|
-
tag: "LIST",
|
|
704
|
-
elts: pattern
|
|
705
|
-
}, {
|
|
706
|
-
tag: "LIST",
|
|
707
|
-
elts: nids
|
|
708
|
-
}]
|
|
709
|
-
});
|
|
710
|
-
}
|
|
711
|
-
|
|
712
|
-
function exprs(ctx, count, inReverse) {
|
|
713
|
-
// Ast.exprs
|
|
714
|
-
var elts = [];
|
|
715
|
-
assert(ctx.state.nodeStack.length >= count);
|
|
716
|
-
if (inReverse) {
|
|
717
|
-
for (var i = count; i > 0; i--) {
|
|
718
|
-
var elt = pop(ctx);
|
|
719
|
-
var n;
|
|
720
|
-
if (false && (n = node(ctx, elt)) && n.tag === "EXPRS") {
|
|
721
|
-
elts = elts.concat(n.elts);
|
|
722
|
-
} else {
|
|
723
|
-
elts.push(elt); // Reverse order.
|
|
724
|
-
}
|
|
725
|
-
}
|
|
726
|
-
} else {
|
|
727
|
-
for (var i = count; i > 0; i--) {
|
|
728
|
-
var elt = pop(ctx);
|
|
729
|
-
var n;
|
|
730
|
-
if (false && (n = node(ctx, elt)) && n.tag === "EXPRS") {
|
|
731
|
-
elts = elts.concat(n.elts);
|
|
732
|
-
} else {
|
|
733
|
-
elts.push(elt); // Reverse order.
|
|
734
|
-
}
|
|
735
|
-
}
|
|
736
|
-
elts = elts.reverse();
|
|
737
|
-
}
|
|
738
|
-
push(ctx, {
|
|
739
|
-
tag: "EXPRS",
|
|
740
|
-
elts: elts
|
|
741
|
-
});
|
|
742
|
-
}
|
|
743
|
-
|
|
744
|
-
function letDef(ctx) {
|
|
745
|
-
// Clean up stack and produce initializer.
|
|
746
|
-
pop(ctx); // body
|
|
747
|
-
pop(ctx); // name
|
|
748
|
-
for (var i = 0; i < ctx.state.paramc; i++) {
|
|
749
|
-
pop(ctx); // params
|
|
750
|
-
}
|
|
751
|
-
ctx.state.exprc--; // don't count as expr.
|
|
752
|
-
}
|
|
753
|
-
|
|
754
|
-
function program(ctx) {
|
|
755
|
-
var elts = [];
|
|
756
|
-
elts.push(pop(ctx));
|
|
757
|
-
push(ctx, {
|
|
758
|
-
tag: "PROG",
|
|
759
|
-
elts: elts
|
|
760
|
-
});
|
|
761
|
-
}
|
|
762
|
-
})();
|
|
763
|
-
|
|
764
|
-
// The following code for StreamString was copied from CodeMirror.
|
|
765
|
-
|
|
766
|
-
window.gcexports.StringStream = (function () {
|
|
767
|
-
|
|
768
|
-
// The character stream used by a mode's parser.
|
|
769
|
-
function StringStream(string, tabSize) {
|
|
770
|
-
this.pos = this.start = 0;
|
|
771
|
-
this.string = string;
|
|
772
|
-
this.tabSize = tabSize || 8;
|
|
773
|
-
}
|
|
774
|
-
|
|
775
|
-
StringStream.prototype = {
|
|
776
|
-
eol: function() {return this.pos >= this.string.length;},
|
|
777
|
-
sol: function() {return this.pos == 0;},
|
|
778
|
-
peek: function() {return this.string.charAt(this.pos) || undefined;},
|
|
779
|
-
next: function() {
|
|
780
|
-
if (this.pos < this.string.length)
|
|
781
|
-
return this.string.charAt(this.pos++);
|
|
782
|
-
},
|
|
783
|
-
eat: function(match) {
|
|
784
|
-
var ch = this.string.charAt(this.pos);
|
|
785
|
-
if (typeof match == "string") {
|
|
786
|
-
var ok = ch == match;
|
|
787
|
-
} else {
|
|
788
|
-
var ok = ch && (match.test ? match.test(ch) : match(ch));
|
|
789
|
-
}
|
|
790
|
-
if (ok) {++this.pos; return ch;}
|
|
791
|
-
},
|
|
792
|
-
eatWhile: function(match) {
|
|
793
|
-
var start = this.pos;
|
|
794
|
-
while (this.eat(match)){}
|
|
795
|
-
return this.pos > start;
|
|
796
|
-
},
|
|
797
|
-
eatSpace: function() {
|
|
798
|
-
var start = this.pos;
|
|
799
|
-
while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) ++this.pos;
|
|
800
|
-
return this.pos > start;
|
|
801
|
-
},
|
|
802
|
-
skipToEnd: function() {this.pos = this.string.length;},
|
|
803
|
-
skipTo: function(ch) {
|
|
804
|
-
var found = this.string.indexOf(ch, this.pos);
|
|
805
|
-
if (found > -1) {this.pos = found; return true;}
|
|
806
|
-
},
|
|
807
|
-
backUp: function(n) {this.pos -= n;},
|
|
808
|
-
column: function() {return countColumn(this.string, this.start, this.tabSize);},
|
|
809
|
-
indentation: function() {return countColumn(this.string, null, this.tabSize);},
|
|
810
|
-
match: function(pattern, consume, caseInsensitive) {
|
|
811
|
-
if (typeof pattern == "string") {
|
|
812
|
-
var cased = function(str) {return caseInsensitive ? str.toLowerCase() : str;};
|
|
813
|
-
if (cased(this.string).indexOf(cased(pattern), this.pos) == this.pos) {
|
|
814
|
-
if (consume !== false) this.pos += pattern.length;
|
|
815
|
-
return true;
|
|
816
|
-
}
|
|
817
|
-
} else {
|
|
818
|
-
var match = this.string.slice(this.pos).match(pattern);
|
|
819
|
-
if (match && match.index > 0) return null;
|
|
820
|
-
if (match && consume !== false) this.pos += match[0].length;
|
|
821
|
-
return match;
|
|
822
|
-
}
|
|
823
|
-
},
|
|
824
|
-
current: function(){return this.string.slice(this.start, this.pos);}
|
|
825
|
-
}
|
|
826
|
-
|
|
827
|
-
return StringStream;
|
|
828
|
-
|
|
829
|
-
})();
|
|
830
|
-
|
|
831
|
-
// env
|
|
832
|
-
|
|
833
|
-
var env = (function () {
|
|
834
|
-
return {
|
|
835
|
-
findWord: findWord,
|
|
836
|
-
addWord: addWord,
|
|
837
|
-
enterEnv: enterEnv,
|
|
838
|
-
exitEnv: exitEnv,
|
|
839
|
-
addPattern: addPattern,
|
|
840
|
-
};
|
|
841
|
-
|
|
842
|
-
// private functions
|
|
843
|
-
|
|
844
|
-
function findWord(ctx, lexeme) {
|
|
845
|
-
var env = ctx.state.env;
|
|
846
|
-
for (var i = env.length-1; i >= 0; i--) {
|
|
847
|
-
var word = env[i].lexicon[lexeme];
|
|
848
|
-
if (word) {
|
|
849
|
-
return word;
|
|
850
|
-
}
|
|
851
|
-
}
|
|
852
|
-
return null;
|
|
853
|
-
}
|
|
854
|
-
|
|
855
|
-
function addWord(ctx, lexeme, entry) {
|
|
856
|
-
window.gcexports.topEnv(ctx).lexicon[lexeme] = entry;
|
|
857
|
-
return null;
|
|
858
|
-
}
|
|
859
|
-
|
|
860
|
-
function addPattern(ctx, pattern) {
|
|
861
|
-
window.gcexports.topEnv(ctx).pattern.push(pattern);
|
|
862
|
-
}
|
|
863
|
-
|
|
864
|
-
function enterEnv(ctx, name) {
|
|
865
|
-
// recursion guard
|
|
866
|
-
if (ctx.state.env.length > 380) {
|
|
867
|
-
//return; // just stop recursing
|
|
868
|
-
throw new Error("runaway recursion");
|
|
869
|
-
}
|
|
870
|
-
window.gcexports.topEnv(ctx).paramc = ctx.state.paramc;
|
|
871
|
-
ctx.state.env.push({
|
|
872
|
-
name: name,
|
|
873
|
-
lexicon: {},
|
|
874
|
-
pattern: [],
|
|
875
|
-
});
|
|
876
|
-
}
|
|
877
|
-
|
|
878
|
-
function exitEnv(ctx) {
|
|
879
|
-
ctx.state.env.pop();
|
|
880
|
-
ctx.state.paramc = window.gcexports.topEnv(ctx).paramc;
|
|
881
|
-
}
|
|
882
|
-
|
|
883
|
-
})();
|
|
884
|
-
|
|
885
|
-
var scanTime = 0;
|
|
886
|
-
var scanCount = 0;
|
|
887
|
-
window.gcexports.scanTime = function () {
|
|
888
|
-
return scanTime;
|
|
889
|
-
};
|
|
890
|
-
window.gcexports.scanCount = function () {
|
|
891
|
-
return scanCount;
|
|
892
|
-
};
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
var parseTime = 0;
|
|
896
|
-
|
|
897
|
-
window.gcexports.parseTime = function () {
|
|
898
|
-
return parseTime;
|
|
899
|
-
};
|
|
900
|
-
|
|
901
|
-
var parseCount = 0;
|
|
902
|
-
window.gcexports.parseCount = function () {
|
|
903
|
-
return parseCount;
|
|
904
|
-
};
|
|
905
|
-
|
|
906
|
-
function getCoord(ctx) {
|
|
907
|
-
let ln = ctx.scan.stream.lineOracle && ctx.scan.stream.lineOracle.line || 0;
|
|
908
|
-
return {
|
|
909
|
-
from: CodeMirror.Pos(ln, ctx.scan.stream.start),
|
|
910
|
-
to: CodeMirror.Pos(ln, ctx.scan.stream.pos),
|
|
911
|
-
};
|
|
912
|
-
}
|
|
913
|
-
|
|
914
|
-
// parser
|
|
915
|
-
window.gcexports.parser = (function () {
|
|
916
|
-
function assert(b, str) {
|
|
917
|
-
if (!b) {
|
|
918
|
-
throw new Error(str);
|
|
919
|
-
}
|
|
920
|
-
}
|
|
921
|
-
var keywords = window.gcexports.keywords = {
|
|
922
|
-
"let" : { "tk": 0x12, "cls": "keyword" },
|
|
923
|
-
"if" : { "tk": 0x05, "cls": "keyword" },
|
|
924
|
-
"then" : { "tk": 0x06, "cls": "keyword" },
|
|
925
|
-
"else" : { "tk": 0x07, "cls": "keyword" },
|
|
926
|
-
"case" : { "tk": 0x0F, "cls": "keyword" },
|
|
927
|
-
"of" : { "tk": 0x10, "cls": "keyword" },
|
|
928
|
-
"end" : { "tk": 0x11, "cls": "keyword", "length": 0 },
|
|
929
|
-
"true" : { "tk": 0x14, "cls": "val", "length": 0 },
|
|
930
|
-
"false" : { "tk": 0x14, "cls": "val", "length": 0 },
|
|
931
|
-
"null" : { "tk": 0x15, "cls": "val", "length": 0 },
|
|
932
|
-
};
|
|
933
|
-
function addError(ctx, str) {
|
|
934
|
-
let ln = ctx.scan.stream.lineOracle && ctx.scan.stream.lineOracle.line || 0;
|
|
935
|
-
window.gcexports.errors.push({
|
|
936
|
-
from: CodeMirror.Pos(ln, ctx.scan.stream.start),
|
|
937
|
-
to: CodeMirror.Pos(ln, ctx.scan.stream.pos),
|
|
938
|
-
message: str,
|
|
939
|
-
severity : "error",
|
|
940
|
-
});
|
|
941
|
-
}
|
|
942
|
-
|
|
943
|
-
var CC_DOUBLEQUOTE = 0x22;
|
|
944
|
-
var CC_DOLLAR = 0x24;
|
|
945
|
-
var CC_SINGLEQUOTE = 0x27;
|
|
946
|
-
var CC_BACKTICK = 0x60;
|
|
947
|
-
var CC_LEFTBRACE = 0x7B;
|
|
948
|
-
var CC_RIGHTBRACE = 0x7D;
|
|
949
|
-
|
|
950
|
-
var TK_IDENT = 0x01;
|
|
951
|
-
var TK_NUM = 0x02;
|
|
952
|
-
var TK_STR = 0x03;
|
|
953
|
-
var TK_EQUAL = 0x04;
|
|
954
|
-
var TK_IF = 0x05;
|
|
955
|
-
var TK_THEN = 0x06;
|
|
956
|
-
var TK_ELSE = 0x07;
|
|
957
|
-
var TK_RETURN = 0x08;
|
|
958
|
-
var TK_IS = 0x09;
|
|
959
|
-
var TK_POSTOP = 0x0A;
|
|
960
|
-
var TK_PREOP = 0x0B;
|
|
961
|
-
var TK_FUN = 0x0C;
|
|
962
|
-
var TK_VAL = 0x0D;
|
|
963
|
-
var TK_BINOP = 0x0E;
|
|
964
|
-
var TK_CASE = 0x0F;
|
|
965
|
-
var TK_OF = 0x10;
|
|
966
|
-
var TK_END = 0x11;
|
|
967
|
-
var TK_LET = 0x12;
|
|
968
|
-
var TK_OR = 0x13;
|
|
969
|
-
var TK_BOOL = 0x14;
|
|
970
|
-
var TK_NULL = 0x15;
|
|
971
|
-
var TK_IN = 0x16;
|
|
972
|
-
|
|
973
|
-
var TK_LEFTPAREN = 0xA1;
|
|
974
|
-
var TK_RIGHTPAREN = 0xA2;
|
|
975
|
-
var TK_LEFTBRACKET = 0xA3;
|
|
976
|
-
var TK_RIGHTBRACKET = 0xA4;
|
|
977
|
-
var TK_LEFTBRACE = 0xA5;
|
|
978
|
-
var TK_RIGHTBRACE = 0xA6;
|
|
979
|
-
var TK_PLUS = 0xA7;
|
|
980
|
-
var TK_MINUS = 0xA8;
|
|
981
|
-
var TK_DOT = 0xA9;
|
|
982
|
-
var TK_COLON = 0xAA;
|
|
983
|
-
var TK_COMMA = 0xAB;
|
|
984
|
-
var TK_BACKQUOTE = 0xAC;
|
|
985
|
-
var TK_COMMENT = 0xAD;
|
|
986
|
-
var TK_LEFTANGLE = 0xAE;
|
|
987
|
-
var TK_RIGHTANGLE = 0xAF;
|
|
988
|
-
var TK_DOUBLELEFTBRACE = 0xB0;
|
|
989
|
-
var TK_DOUBLERIGHTBRACE = 0xB1;
|
|
990
|
-
var TK_STRPREFIX = 0xB2;
|
|
991
|
-
var TK_STRMIDDLE = 0xB3;
|
|
992
|
-
var TK_STRSUFFIX = 0xB4;
|
|
993
|
-
|
|
994
|
-
function tokenToLexeme(tk) {
|
|
995
|
-
switch (tk) {
|
|
996
|
-
case TK_EQUAL: return "a '=' symbol";
|
|
997
|
-
case TK_IF: return "the 'if' keyword";
|
|
998
|
-
case TK_THEN: return "the 'then' keyword";
|
|
999
|
-
case TK_ELSE: return "the 'else' keyword";
|
|
1000
|
-
case TK_RETURN: return "the 'return' keyword";
|
|
1001
|
-
case TK_IS: return "the 'is' keyword";
|
|
1002
|
-
case TK_FUN: return "the 'fun' keyword";
|
|
1003
|
-
case TK_VAL: return "the 'val' keyword";
|
|
1004
|
-
case TK_CASE: return "the 'case' keyword";
|
|
1005
|
-
case TK_OF: return "the 'of' keyword";
|
|
1006
|
-
case TK_END: return "the 'end' keyword";
|
|
1007
|
-
case TK_LET: return "the 'let' keyword";
|
|
1008
|
-
case TK_OR: return "the 'or' keyword";
|
|
1009
|
-
case TK_POSTOP:
|
|
1010
|
-
case TK_PREOP:
|
|
1011
|
-
case TK_BINOP:
|
|
1012
|
-
return "an operator";
|
|
1013
|
-
case TK_LEFTPAREN: return "a '('";
|
|
1014
|
-
case TK_RIGHTPAREN: return "a ')'";
|
|
1015
|
-
case TK_LEFTBRACKET: return "a '['";
|
|
1016
|
-
case TK_RIGHTBRACKET: return "a ']'";
|
|
1017
|
-
case TK_LEFTBRACE: return "a '{'";
|
|
1018
|
-
case TK_RIGHTBRACE: return "a '}'";
|
|
1019
|
-
case TK_LEFTANGLE: return "a '<'";
|
|
1020
|
-
case TK_RIGHTANGLE: return "a '>'";
|
|
1021
|
-
case TK_PLUS: return "a '+'";
|
|
1022
|
-
case TK_MINUS: return "a '-'";
|
|
1023
|
-
case TK_DOT: return "a '.'";
|
|
1024
|
-
case TK_COLON: return "a ':'";
|
|
1025
|
-
case TK_COMMA: return "a ','";
|
|
1026
|
-
case TK_BACKQUOTE: return "a '`'";
|
|
1027
|
-
case TK_COMMENT: return "a comment";
|
|
1028
|
-
case 0: return "the end of the program";
|
|
1029
|
-
}
|
|
1030
|
-
return "an expression";
|
|
1031
|
-
}
|
|
1032
|
-
|
|
1033
|
-
function eat(ctx, tk) {
|
|
1034
|
-
var nextToken = next(ctx);
|
|
1035
|
-
if (nextToken !== tk) {
|
|
1036
|
-
throw new Error("Expecting " + tokenToLexeme(tk) +
|
|
1037
|
-
", found " + tokenToLexeme(nextToken) + ".");
|
|
1038
|
-
}
|
|
1039
|
-
}
|
|
1040
|
-
|
|
1041
|
-
function match(ctx, tk) {
|
|
1042
|
-
if (peek(ctx) === tk) {
|
|
1043
|
-
return true;
|
|
1044
|
-
} else {
|
|
1045
|
-
return false;
|
|
1046
|
-
}
|
|
1047
|
-
}
|
|
1048
|
-
|
|
1049
|
-
function next(ctx) {
|
|
1050
|
-
var tk = peek(ctx);
|
|
1051
|
-
ctx.state.nextToken = -1;
|
|
1052
|
-
scanCount++;
|
|
1053
|
-
return tk;
|
|
1054
|
-
}
|
|
1055
|
-
|
|
1056
|
-
function peek(ctx) {
|
|
1057
|
-
var tk;
|
|
1058
|
-
var nextToken = ctx.state.nextToken;
|
|
1059
|
-
if (nextToken < 0) {
|
|
1060
|
-
var t0 = new Date();
|
|
1061
|
-
tk = ctx.scan.start(ctx);
|
|
1062
|
-
var t1 = new Date();
|
|
1063
|
-
scanTime += (t1-t0);
|
|
1064
|
-
ctx.state.nextToken = tk;
|
|
1065
|
-
} else {
|
|
1066
|
-
tk = nextToken;
|
|
1067
|
-
}
|
|
1068
|
-
return tk;
|
|
1069
|
-
}
|
|
1070
|
-
|
|
1071
|
-
// Parsing functions -- each parsing function consumes a single token and
|
|
1072
|
-
// returns a continuation function for parsing the rest of the string.
|
|
1073
|
-
|
|
1074
|
-
function nul(ctx, cc) {
|
|
1075
|
-
eat(ctx, TK_NULL);
|
|
1076
|
-
cc.cls = "number";
|
|
1077
|
-
Ast.nul(ctx);
|
|
1078
|
-
return cc;
|
|
1079
|
-
}
|
|
1080
|
-
|
|
1081
|
-
function bool(ctx, cc) {
|
|
1082
|
-
eat(ctx, TK_BOOL);
|
|
1083
|
-
cc.cls = "number";
|
|
1084
|
-
Ast.bool(ctx, lexeme==="true");
|
|
1085
|
-
return cc;
|
|
1086
|
-
}
|
|
1087
|
-
|
|
1088
|
-
function number(ctx, cc) {
|
|
1089
|
-
eat(ctx, TK_NUM);
|
|
1090
|
-
cc.cls = "number";
|
|
1091
|
-
Ast.number(ctx, lexeme, getCoord(ctx));
|
|
1092
|
-
return cc;
|
|
1093
|
-
}
|
|
1094
|
-
|
|
1095
|
-
function string(ctx, cc) {
|
|
1096
|
-
eat(ctx, TK_STR);
|
|
1097
|
-
var coord = getCoord(ctx);
|
|
1098
|
-
cc.cls = "string";
|
|
1099
|
-
Ast.string(ctx, lexeme.substring(1,lexeme.length-1), coord) // strip quotes;
|
|
1100
|
-
return cc;
|
|
1101
|
-
}
|
|
1102
|
-
|
|
1103
|
-
/*
|
|
1104
|
-
Str :
|
|
1105
|
-
STR
|
|
1106
|
-
STRPREFIX StrSuffix
|
|
1107
|
-
|
|
1108
|
-
StrSuffix :
|
|
1109
|
-
Expr STRMIDDLE StrSuffix
|
|
1110
|
-
Expr STRSUFFIX
|
|
1111
|
-
*/
|
|
1112
|
-
|
|
1113
|
-
function str(ctx, cc) {
|
|
1114
|
-
if (match(ctx, TK_STR)) {
|
|
1115
|
-
eat(ctx, TK_STR);
|
|
1116
|
-
var coord = getCoord(ctx);
|
|
1117
|
-
Ast.string(ctx, lexeme, coord); // strip quotes;
|
|
1118
|
-
cc.cls = "string";
|
|
1119
|
-
return cc;
|
|
1120
|
-
} else if (match(ctx, TK_STRPREFIX)) {
|
|
1121
|
-
ctx.state.inStr++;
|
|
1122
|
-
eat(ctx, TK_STRPREFIX);
|
|
1123
|
-
startCounter(ctx);
|
|
1124
|
-
var coord = getCoord(ctx);
|
|
1125
|
-
Ast.string(ctx, lexeme, coord) // strip quotes;
|
|
1126
|
-
countCounter(ctx);
|
|
1127
|
-
var ret = function(ctx) {
|
|
1128
|
-
return strSuffix(ctx, function (ctx) {
|
|
1129
|
-
ctx.state.inStr--;
|
|
1130
|
-
eat(ctx, TK_STRSUFFIX);
|
|
1131
|
-
var coord = getCoord(ctx);
|
|
1132
|
-
Ast.string(ctx, lexeme, coord) // strip quotes;
|
|
1133
|
-
countCounter(ctx);
|
|
1134
|
-
Ast.list(ctx, ctx.state.exprc, getCoord(ctx));
|
|
1135
|
-
stopCounter(ctx);
|
|
1136
|
-
Ast.concat(ctx);
|
|
1137
|
-
cc.cls = "string";
|
|
1138
|
-
return cc;
|
|
1139
|
-
});
|
|
1140
|
-
}
|
|
1141
|
-
ret.cls = "string";
|
|
1142
|
-
return ret;
|
|
1143
|
-
}
|
|
1144
|
-
assert(false);
|
|
1145
|
-
}
|
|
1146
|
-
function strSuffix(ctx, resume) {
|
|
1147
|
-
if (match(ctx, TK_STRSUFFIX)) {
|
|
1148
|
-
// We have a STRSUFFIX so we are done.
|
|
1149
|
-
return resume;
|
|
1150
|
-
}
|
|
1151
|
-
return strPart(ctx, function (ctx) {
|
|
1152
|
-
if (match(ctx, TK_STRMIDDLE)) {
|
|
1153
|
-
// Not done yet.
|
|
1154
|
-
eat(ctx, TK_STRMIDDLE);
|
|
1155
|
-
var coord = getCoord(ctx);
|
|
1156
|
-
Ast.string(ctx, lexeme, coord) // strip quotes;
|
|
1157
|
-
countCounter(ctx);
|
|
1158
|
-
var ret = function (ctx) {
|
|
1159
|
-
return strSuffix(ctx, resume);
|
|
1160
|
-
};
|
|
1161
|
-
ret.cls = "string";
|
|
1162
|
-
return ret;
|
|
1163
|
-
}
|
|
1164
|
-
var ret = function (ctx) {
|
|
1165
|
-
return strSuffix(ctx, resume);
|
|
1166
|
-
};
|
|
1167
|
-
ret.cls = "string";
|
|
1168
|
-
return ret;
|
|
1169
|
-
});
|
|
1170
|
-
}
|
|
1171
|
-
function strPart(ctx, resume) {
|
|
1172
|
-
return expr(ctx, function(ctx) {
|
|
1173
|
-
countCounter(ctx);
|
|
1174
|
-
return resume(ctx);
|
|
1175
|
-
});
|
|
1176
|
-
}
|
|
1177
|
-
function ident(ctx, cc) {
|
|
1178
|
-
eat(ctx, TK_IDENT);
|
|
1179
|
-
Ast.name(ctx, lexeme, getCoord(ctx));
|
|
1180
|
-
cc.cls = "variable";
|
|
1181
|
-
return cc;
|
|
1182
|
-
}
|
|
1183
|
-
function identOrString(ctx, cc) {
|
|
1184
|
-
if (match(ctx, TK_IDENT)) {
|
|
1185
|
-
return ident(ctx, cc);
|
|
1186
|
-
}
|
|
1187
|
-
if (match(ctx, TK_NUM)) {
|
|
1188
|
-
return number(ctx, cc);
|
|
1189
|
-
}
|
|
1190
|
-
return str(ctx, cc);
|
|
1191
|
-
}
|
|
1192
|
-
function defList(ctx, resume) {
|
|
1193
|
-
eat(ctx, TK_LEFTBRACKET);
|
|
1194
|
-
var ret = (ctx) => {
|
|
1195
|
-
return params(ctx, TK_RIGHTBRACKET, (ctx) => {
|
|
1196
|
-
eat(ctx, TK_RIGHTBRACKET);
|
|
1197
|
-
Ast.list(ctx, ctx.state.paramc, null, true);
|
|
1198
|
-
ctx.state.paramc = 1;
|
|
1199
|
-
return resume;
|
|
1200
|
-
});
|
|
1201
|
-
};
|
|
1202
|
-
ret.cls = "punc";
|
|
1203
|
-
return ret;
|
|
1204
|
-
}
|
|
1205
|
-
function defName(ctx, cc) {
|
|
1206
|
-
if (match(ctx, TK_LEFTBRACKET)) {
|
|
1207
|
-
return defList(ctx, cc);
|
|
1208
|
-
} else {
|
|
1209
|
-
eat(ctx, TK_IDENT);
|
|
1210
|
-
env.addWord(ctx, lexeme, {
|
|
1211
|
-
tk: TK_IDENT,
|
|
1212
|
-
cls: "val",
|
|
1213
|
-
name: lexeme,
|
|
1214
|
-
offset: ctx.state.paramc,
|
|
1215
|
-
nid: 0,
|
|
1216
|
-
});
|
|
1217
|
-
Ast.name(ctx, lexeme, getCoord(ctx));
|
|
1218
|
-
cc.cls = "val";
|
|
1219
|
-
return cc;
|
|
1220
|
-
}
|
|
1221
|
-
}
|
|
1222
|
-
function name(ctx, cc) {
|
|
1223
|
-
eat(ctx, TK_IDENT);
|
|
1224
|
-
var coord = getCoord(ctx);
|
|
1225
|
-
var word = env.findWord(ctx, lexeme);
|
|
1226
|
-
if (word) {
|
|
1227
|
-
cc.cls = word.cls;
|
|
1228
|
-
if (word.cls==="number" && word.val) {
|
|
1229
|
-
Ast.number(ctx, word.val, coord);
|
|
1230
|
-
} else if (word.cls==="string" && word.val) {
|
|
1231
|
-
Ast.string(ctx, word.val, coord);
|
|
1232
|
-
} else {
|
|
1233
|
-
if (word.nid) {
|
|
1234
|
-
Ast.push(ctx, word.nid);
|
|
1235
|
-
} else {
|
|
1236
|
-
Ast.name(ctx, lexeme, coord);
|
|
1237
|
-
}
|
|
1238
|
-
}
|
|
1239
|
-
} else {
|
|
1240
|
-
cc.cls = "comment";
|
|
1241
|
-
addError(ctx, "Name '" + lexeme + "' not found.");
|
|
1242
|
-
}
|
|
1243
|
-
assert(cc, "name");
|
|
1244
|
-
return cc;
|
|
1245
|
-
}
|
|
1246
|
-
function record(ctx, cc) {
|
|
1247
|
-
// Parse record
|
|
1248
|
-
eat(ctx, TK_LEFTBRACE);
|
|
1249
|
-
startCounter(ctx);
|
|
1250
|
-
var ret = function(ctx) {
|
|
1251
|
-
return bindings(ctx, function (ctx) {
|
|
1252
|
-
eat(ctx, TK_RIGHTBRACE);
|
|
1253
|
-
Ast.record(ctx);
|
|
1254
|
-
stopCounter(ctx);
|
|
1255
|
-
cc.cls = "punc";
|
|
1256
|
-
return cc;
|
|
1257
|
-
})
|
|
1258
|
-
}
|
|
1259
|
-
ret.cls = "punc";
|
|
1260
|
-
return ret;
|
|
1261
|
-
}
|
|
1262
|
-
function bindings(ctx, cc) {
|
|
1263
|
-
if (match(ctx, TK_RIGHTBRACE)) {
|
|
1264
|
-
return cc;
|
|
1265
|
-
}
|
|
1266
|
-
return binding(ctx, function (ctx) {
|
|
1267
|
-
if (match(ctx, TK_COMMA)) {
|
|
1268
|
-
eat(ctx, TK_COMMA);
|
|
1269
|
-
Ast.binding(ctx);
|
|
1270
|
-
var ret = function (ctx) {
|
|
1271
|
-
return bindings(ctx, cc);
|
|
1272
|
-
};
|
|
1273
|
-
ret.cls = "punc";
|
|
1274
|
-
return ret;
|
|
1275
|
-
}
|
|
1276
|
-
return function (ctx) {
|
|
1277
|
-
Ast.binding(ctx);
|
|
1278
|
-
return bindings(ctx, cc);
|
|
1279
|
-
};
|
|
1280
|
-
})
|
|
1281
|
-
}
|
|
1282
|
-
function binding(ctx, cc) {
|
|
1283
|
-
return identOrString(ctx, function(ctx) {
|
|
1284
|
-
eat(ctx, TK_COLON);
|
|
1285
|
-
var ret = function(ctx) {
|
|
1286
|
-
countCounter(ctx);
|
|
1287
|
-
return expr(ctx, cc);
|
|
1288
|
-
}
|
|
1289
|
-
ret.cls = "punc";
|
|
1290
|
-
return ret;
|
|
1291
|
-
})
|
|
1292
|
-
}
|
|
1293
|
-
function lambda(ctx, cc) {
|
|
1294
|
-
eat(ctx, TK_LEFTANGLE);
|
|
1295
|
-
var ret = function (ctx) {
|
|
1296
|
-
ctx.state.paramc = 0;
|
|
1297
|
-
env.enterEnv(ctx, "lambda");
|
|
1298
|
-
return params(ctx, TK_COLON, function (ctx) {
|
|
1299
|
-
eat(ctx, TK_COLON);
|
|
1300
|
-
var ret = function(ctx) {
|
|
1301
|
-
return exprsStart(ctx, TK_RIGHTANGLE, function (ctx) {
|
|
1302
|
-
eat(ctx, TK_RIGHTANGLE);
|
|
1303
|
-
var nid = Ast.pop(ctx); // save body node id for aliased code
|
|
1304
|
-
Ast.lambda(ctx, topEnv(ctx), nid);
|
|
1305
|
-
env.exitEnv(ctx);
|
|
1306
|
-
return cc
|
|
1307
|
-
});
|
|
1308
|
-
};
|
|
1309
|
-
ret.cls = "punc"
|
|
1310
|
-
return ret
|
|
1311
|
-
});
|
|
1312
|
-
};
|
|
1313
|
-
return ret;
|
|
1314
|
-
}
|
|
1315
|
-
function parenExpr(ctx, cc) {
|
|
1316
|
-
let coord = getCoord(ctx);
|
|
1317
|
-
eat(ctx, TK_LEFTPAREN);
|
|
1318
|
-
var ret = function(ctx) {
|
|
1319
|
-
return exprsStart(ctx, TK_RIGHTPAREN, function (ctx) {
|
|
1320
|
-
eat(ctx, TK_RIGHTPAREN);
|
|
1321
|
-
coord.to = getCoord(ctx).to;
|
|
1322
|
-
Ast.parenExpr(ctx, coord);
|
|
1323
|
-
cc.cls = "punc";
|
|
1324
|
-
return cc;
|
|
1325
|
-
})
|
|
1326
|
-
}
|
|
1327
|
-
ret.cls = "punc";
|
|
1328
|
-
return ret;
|
|
1329
|
-
}
|
|
1330
|
-
function list(ctx, cc) {
|
|
1331
|
-
let coord = getCoord(ctx);
|
|
1332
|
-
eat(ctx, TK_LEFTBRACKET);
|
|
1333
|
-
startCounter(ctx);
|
|
1334
|
-
var ret = function(ctx) {
|
|
1335
|
-
return elements(ctx, function (ctx) {
|
|
1336
|
-
eat(ctx, TK_RIGHTBRACKET);
|
|
1337
|
-
coord.to = getCoord(ctx).to;
|
|
1338
|
-
Ast.list(ctx, ctx.state.exprc, coord);
|
|
1339
|
-
stopCounter(ctx);
|
|
1340
|
-
cc.cls = "punc";
|
|
1341
|
-
return cc;
|
|
1342
|
-
});
|
|
1343
|
-
}
|
|
1344
|
-
ret.cls = "punc";
|
|
1345
|
-
return ret;
|
|
1346
|
-
}
|
|
1347
|
-
function elements(ctx, resume) {
|
|
1348
|
-
if (match(ctx, TK_RIGHTBRACKET)) {
|
|
1349
|
-
return resume;
|
|
1350
|
-
}
|
|
1351
|
-
return element(ctx, function (ctx) {
|
|
1352
|
-
if (match(ctx, TK_COMMA)) {
|
|
1353
|
-
eat(ctx, TK_COMMA);
|
|
1354
|
-
var ret = function (ctx) {
|
|
1355
|
-
return elements(ctx, resume);
|
|
1356
|
-
};
|
|
1357
|
-
ret.cls = "punc";
|
|
1358
|
-
return ret;
|
|
1359
|
-
}
|
|
1360
|
-
return function (ctx) {
|
|
1361
|
-
return elements(ctx, resume);
|
|
1362
|
-
};
|
|
1363
|
-
});
|
|
1364
|
-
}
|
|
1365
|
-
function element(ctx, resume) {
|
|
1366
|
-
return expr(ctx, function(ctx) {
|
|
1367
|
-
countCounter(ctx);
|
|
1368
|
-
return resume(ctx);
|
|
1369
|
-
});
|
|
1370
|
-
}
|
|
1371
|
-
function primaryExpr(ctx, cc) {
|
|
1372
|
-
if (match(ctx, TK_NUM)) {
|
|
1373
|
-
return number(ctx, cc);
|
|
1374
|
-
} else if (match(ctx, TK_STR) || match(ctx, TK_STRPREFIX)) {
|
|
1375
|
-
return str(ctx, cc);
|
|
1376
|
-
} else if (match(ctx, TK_BOOL)) {
|
|
1377
|
-
return bool(ctx, cc);
|
|
1378
|
-
} else if (match(ctx, TK_NULL)) {
|
|
1379
|
-
return nul(ctx, cc);
|
|
1380
|
-
} else if (match(ctx, TK_LEFTBRACE)) {
|
|
1381
|
-
return record(ctx, cc);
|
|
1382
|
-
} else if (match(ctx, TK_LEFTPAREN)) {
|
|
1383
|
-
return parenExpr(ctx, cc);
|
|
1384
|
-
} else if (match(ctx, TK_LEFTBRACKET)) {
|
|
1385
|
-
return list(ctx, cc);
|
|
1386
|
-
} else if (match(ctx, TK_LEFTANGLE)) {
|
|
1387
|
-
return lambda(ctx, cc);
|
|
1388
|
-
}
|
|
1389
|
-
return name(ctx, cc);
|
|
1390
|
-
}
|
|
1391
|
-
function postfixExpr(ctx, cc) {
|
|
1392
|
-
return primaryExpr(ctx, function (ctx) {
|
|
1393
|
-
if (match(ctx, TK_POSTOP)) {
|
|
1394
|
-
eat(ctx, TK_POSTOP);
|
|
1395
|
-
cc.cls = "operator";
|
|
1396
|
-
Ast.postfixExpr(ctx, lexeme);
|
|
1397
|
-
return cc;
|
|
1398
|
-
}
|
|
1399
|
-
return cc(ctx);
|
|
1400
|
-
})
|
|
1401
|
-
}
|
|
1402
|
-
|
|
1403
|
-
function prefixExpr(ctx, cc) {
|
|
1404
|
-
if (match(ctx, TK_MINUS)) {
|
|
1405
|
-
eat(ctx, TK_MINUS);
|
|
1406
|
-
var ret = function(ctx) {
|
|
1407
|
-
return postfixExpr(ctx, function (ctx) {
|
|
1408
|
-
Ast.prefixExpr(ctx, "NEG");
|
|
1409
|
-
return cc;
|
|
1410
|
-
})
|
|
1411
|
-
}
|
|
1412
|
-
ret.cls = "number" // use number because of convention
|
|
1413
|
-
return ret;
|
|
1414
|
-
}
|
|
1415
|
-
return postfixExpr(ctx, cc);
|
|
1416
|
-
}
|
|
1417
|
-
|
|
1418
|
-
function getPrecedence(op) {
|
|
1419
|
-
return {
|
|
1420
|
-
"": 0
|
|
1421
|
-
, "OR": 1
|
|
1422
|
-
, "AND": 2
|
|
1423
|
-
, "EQ": 3
|
|
1424
|
-
, "NE": 3
|
|
1425
|
-
, "LT": 4
|
|
1426
|
-
, "GT": 4
|
|
1427
|
-
, "LE": 4
|
|
1428
|
-
, "GE": 4
|
|
1429
|
-
, "CONCAT": 5
|
|
1430
|
-
, "ADD": 5
|
|
1431
|
-
, "SUB": 5
|
|
1432
|
-
, "MUL": 6
|
|
1433
|
-
, "DIV": 6
|
|
1434
|
-
, "MOD": 6
|
|
1435
|
-
, "POW": 7
|
|
1436
|
-
}[op];
|
|
1437
|
-
}
|
|
1438
|
-
|
|
1439
|
-
function binaryExpr(ctx, prevOp, cc) {
|
|
1440
|
-
return prefixExpr(ctx, function (ctx) {
|
|
1441
|
-
if (match(ctx, TK_BINOP)) {
|
|
1442
|
-
eat(ctx, TK_BINOP)
|
|
1443
|
-
var ret = function (ctx) {
|
|
1444
|
-
var op = env.findWord(ctx, lexeme).name
|
|
1445
|
-
if (getPrecedence(prevOp) < getPrecedence(op)) {
|
|
1446
|
-
return binaryExpr(ctx, op, function(ctx, prevOp) {
|
|
1447
|
-
// This continuation's purpose is to construct a right recursive
|
|
1448
|
-
// binary expression node. If the previous node is a binary node
|
|
1449
|
-
// with equal or higher precedence, then we get here from the left
|
|
1450
|
-
// recursive branch below and there is no way to know the current
|
|
1451
|
-
// operator unless it gets passed as an argument, which is what
|
|
1452
|
-
// prevOp is for.
|
|
1453
|
-
if (prevOp !== void 0) {
|
|
1454
|
-
op = prevOp
|
|
1455
|
-
}
|
|
1456
|
-
Ast.binaryExpr(ctx, op)
|
|
1457
|
-
return cc(ctx)
|
|
1458
|
-
})
|
|
1459
|
-
} else {
|
|
1460
|
-
Ast.binaryExpr(ctx, prevOp)
|
|
1461
|
-
return binaryExpr(ctx, op, function(ctx, prevOp) {
|
|
1462
|
-
if (prevOp !== void 0) {
|
|
1463
|
-
op = prevOp
|
|
1464
|
-
}
|
|
1465
|
-
return cc(ctx, op)
|
|
1466
|
-
})
|
|
1467
|
-
}
|
|
1468
|
-
}
|
|
1469
|
-
ret.cls = "operator"
|
|
1470
|
-
return ret
|
|
1471
|
-
}
|
|
1472
|
-
return cc(ctx)
|
|
1473
|
-
})
|
|
1474
|
-
}
|
|
1475
|
-
|
|
1476
|
-
function relationalExpr(ctx, cc) {
|
|
1477
|
-
return binaryExpr(ctx, "", function (ctx) {
|
|
1478
|
-
return cc(ctx)
|
|
1479
|
-
})
|
|
1480
|
-
}
|
|
1481
|
-
|
|
1482
|
-
function condExpr(ctx, cc) {
|
|
1483
|
-
if (match(ctx, TK_CASE)) {
|
|
1484
|
-
return caseExpr(ctx, cc)
|
|
1485
|
-
}
|
|
1486
|
-
return relationalExpr(ctx, cc)
|
|
1487
|
-
}
|
|
1488
|
-
|
|
1489
|
-
function caseExpr(ctx, cc) {
|
|
1490
|
-
eat(ctx, TK_CASE);
|
|
1491
|
-
var ret = function (ctx) {
|
|
1492
|
-
return expr(ctx, function (ctx) {
|
|
1493
|
-
startCounter(ctx);
|
|
1494
|
-
return ofClauses(ctx, function (ctx) {
|
|
1495
|
-
Ast.caseExpr(ctx, ctx.state.exprc);
|
|
1496
|
-
stopCounter(ctx);
|
|
1497
|
-
eat(ctx, TK_END);
|
|
1498
|
-
cc.cls = "keyword";
|
|
1499
|
-
return cc;
|
|
1500
|
-
})
|
|
1501
|
-
})
|
|
1502
|
-
}
|
|
1503
|
-
ret.cls = "keyword";
|
|
1504
|
-
return ret;
|
|
1505
|
-
}
|
|
1506
|
-
|
|
1507
|
-
function ofClauses(ctx, cc) {
|
|
1508
|
-
if (match(ctx, TK_OF)) {
|
|
1509
|
-
return ofClause(ctx, function (ctx) {
|
|
1510
|
-
countCounter(ctx);
|
|
1511
|
-
if (match(ctx, TK_OF)) {
|
|
1512
|
-
return ofClauses(ctx, cc);
|
|
1513
|
-
}
|
|
1514
|
-
return cc(ctx);
|
|
1515
|
-
});
|
|
1516
|
-
}
|
|
1517
|
-
return cc(ctx);
|
|
1518
|
-
}
|
|
1519
|
-
|
|
1520
|
-
function ofClause (ctx, cc) {
|
|
1521
|
-
eat(ctx, TK_OF);
|
|
1522
|
-
var ret = function (ctx) {
|
|
1523
|
-
return pattern(ctx, function (ctx) {
|
|
1524
|
-
eat(ctx, TK_COLON);
|
|
1525
|
-
var ret = function(ctx) {
|
|
1526
|
-
return exprsStart(ctx, TK_OF, function(ctx) {
|
|
1527
|
-
Ast.ofClause(ctx);
|
|
1528
|
-
return cc(ctx);
|
|
1529
|
-
});
|
|
1530
|
-
}
|
|
1531
|
-
ret.cls = "punc";
|
|
1532
|
-
return ret;
|
|
1533
|
-
});
|
|
1534
|
-
}
|
|
1535
|
-
ret.cls = "keyword";
|
|
1536
|
-
return ret;
|
|
1537
|
-
}
|
|
1538
|
-
|
|
1539
|
-
function pattern(ctx, cc) {
|
|
1540
|
-
// FIXME only matches idents and literals for now
|
|
1541
|
-
return identOrString(ctx, cc);
|
|
1542
|
-
}
|
|
1543
|
-
|
|
1544
|
-
function thenClause(ctx, cc) {
|
|
1545
|
-
eat(ctx, TK_THEN)
|
|
1546
|
-
var ret = function (ctx) {
|
|
1547
|
-
return exprsStart(ctx, TK_ELSE, function (ctx) {
|
|
1548
|
-
if (match(ctx, TK_ELSE)) {
|
|
1549
|
-
return elseClause(ctx, cc)
|
|
1550
|
-
} else {
|
|
1551
|
-
return cc(ctx)
|
|
1552
|
-
}
|
|
1553
|
-
})
|
|
1554
|
-
}
|
|
1555
|
-
ret.cls = "keyword"
|
|
1556
|
-
return ret
|
|
1557
|
-
}
|
|
1558
|
-
|
|
1559
|
-
function elseClause(ctx, cc) {
|
|
1560
|
-
eat(ctx, TK_ELSE)
|
|
1561
|
-
var ret = function (ctx) {
|
|
1562
|
-
return exprsStart(ctx, TK_END, cc)
|
|
1563
|
-
}
|
|
1564
|
-
ret.cls = "keyword"
|
|
1565
|
-
return ret
|
|
1566
|
-
}
|
|
1567
|
-
|
|
1568
|
-
function expr(ctx, cc) {
|
|
1569
|
-
var ret;
|
|
1570
|
-
if (match(ctx, TK_LET)) {
|
|
1571
|
-
ret = letDef(ctx, cc);
|
|
1572
|
-
} else {
|
|
1573
|
-
ret = condExpr(ctx, cc);
|
|
1574
|
-
}
|
|
1575
|
-
return ret;
|
|
1576
|
-
}
|
|
1577
|
-
|
|
1578
|
-
function emptyInput(ctx) {
|
|
1579
|
-
return peek(ctx) === 0
|
|
1580
|
-
}
|
|
1581
|
-
|
|
1582
|
-
function emptyExpr(ctx) {
|
|
1583
|
-
return emptyInput(ctx)
|
|
1584
|
-
|| match(ctx, TK_THEN)
|
|
1585
|
-
|| match(ctx, TK_ELSE)
|
|
1586
|
-
|| match(ctx, TK_OR)
|
|
1587
|
-
|| match(ctx, TK_END)
|
|
1588
|
-
|| match(ctx, TK_DOT);
|
|
1589
|
-
}
|
|
1590
|
-
|
|
1591
|
-
function countCounter(ctx) {
|
|
1592
|
-
ctx.state.exprc++
|
|
1593
|
-
}
|
|
1594
|
-
|
|
1595
|
-
function startCounter(ctx) {
|
|
1596
|
-
ctx.state.exprcStack.push(ctx.state.exprc)
|
|
1597
|
-
ctx.state.exprc = 0
|
|
1598
|
-
}
|
|
1599
|
-
|
|
1600
|
-
function stopCounter(ctx) {
|
|
1601
|
-
ctx.state.exprc = ctx.state.exprcStack.pop()
|
|
1602
|
-
}
|
|
1603
|
-
|
|
1604
|
-
function exprsStart(ctx, brk, cc) {
|
|
1605
|
-
startCounter(ctx);
|
|
1606
|
-
return exprs(ctx, brk, cc);
|
|
1607
|
-
}
|
|
1608
|
-
|
|
1609
|
-
function exprsFinish(ctx, cc) {
|
|
1610
|
-
Ast.exprs(ctx, ctx.state.exprc)
|
|
1611
|
-
stopCounter(ctx)
|
|
1612
|
-
return cc(ctx)
|
|
1613
|
-
}
|
|
1614
|
-
|
|
1615
|
-
function exprs(ctx, brk, cc) {
|
|
1616
|
-
if (match(ctx, TK_DOT)) { // second dot
|
|
1617
|
-
eat(ctx, TK_DOT);
|
|
1618
|
-
var ret = function(ctx) {
|
|
1619
|
-
return exprsFinish(ctx, cc);
|
|
1620
|
-
}
|
|
1621
|
-
ret.cls = "punc";
|
|
1622
|
-
return ret;
|
|
1623
|
-
}
|
|
1624
|
-
return expr(ctx, function (ctx) {
|
|
1625
|
-
countCounter(ctx);
|
|
1626
|
-
if (match(ctx, TK_DOT)) {
|
|
1627
|
-
eat(ctx, TK_DOT);
|
|
1628
|
-
var ret = function (ctx) {
|
|
1629
|
-
if (emptyInput(ctx) || emptyExpr(ctx)) {
|
|
1630
|
-
return exprsFinish(ctx, cc);
|
|
1631
|
-
}
|
|
1632
|
-
return exprs(ctx, brk, cc);
|
|
1633
|
-
}
|
|
1634
|
-
ret.cls = "punc";
|
|
1635
|
-
return ret;
|
|
1636
|
-
} else if (match(ctx, brk)) {
|
|
1637
|
-
var ret = function (ctx) {
|
|
1638
|
-
return exprsFinish(ctx, cc);
|
|
1639
|
-
}
|
|
1640
|
-
ret.cls = "punc";
|
|
1641
|
-
return ret;
|
|
1642
|
-
} else {
|
|
1643
|
-
if (emptyInput(ctx) || emptyExpr(ctx)) {
|
|
1644
|
-
return exprsFinish(ctx, cc);
|
|
1645
|
-
}
|
|
1646
|
-
return exprs(ctx, brk, cc);
|
|
1647
|
-
}
|
|
1648
|
-
return exprsFinish(ctx, cc);
|
|
1649
|
-
});
|
|
1650
|
-
}
|
|
1651
|
-
|
|
1652
|
-
function program(ctx, cc) {
|
|
1653
|
-
return exprsStart(ctx, TK_DOT, function (ctx) {
|
|
1654
|
-
var nid;
|
|
1655
|
-
while (Ast.peek(ctx) !== nid) {
|
|
1656
|
-
var nid = Ast.pop(ctx);
|
|
1657
|
-
folder.fold(ctx, nid) // fold the exprs on top
|
|
1658
|
-
}
|
|
1659
|
-
Ast.exprs(ctx, ctx.state.nodeStack.length, true);
|
|
1660
|
-
Ast.program(ctx);
|
|
1661
|
-
assert(cc===null, "internal error, expecting null continuation");
|
|
1662
|
-
return cc;
|
|
1663
|
-
});
|
|
1664
|
-
}
|
|
1665
|
-
|
|
1666
|
-
window.gcexports.program = program;
|
|
1667
|
-
|
|
1668
|
-
/*
|
|
1669
|
-
|
|
1670
|
-
fn = { head, body }
|
|
1671
|
-
|
|
1672
|
-
*/
|
|
1673
|
-
|
|
1674
|
-
function letDef(ctx, cc) {
|
|
1675
|
-
if (match(ctx, TK_LET)) {
|
|
1676
|
-
eat(ctx, TK_LET);
|
|
1677
|
-
var ret = function (ctx) {
|
|
1678
|
-
var ret = defName(ctx, function (ctx) {
|
|
1679
|
-
var name = Ast.node(ctx, Ast.pop(ctx)).elts[0];
|
|
1680
|
-
// nid=0 means def not finished yet
|
|
1681
|
-
env.addWord(ctx, name, {
|
|
1682
|
-
tk: TK_IDENT,
|
|
1683
|
-
cls: "function",
|
|
1684
|
-
length: 0,
|
|
1685
|
-
nid: 0,
|
|
1686
|
-
name: name
|
|
1687
|
-
});
|
|
1688
|
-
ctx.state.paramc = 0;
|
|
1689
|
-
env.enterEnv(ctx, name); // FIXME need to link to outer env
|
|
1690
|
-
return params(ctx, TK_EQUAL, function (ctx) {
|
|
1691
|
-
var func = env.findWord(ctx, topEnv(ctx).name);
|
|
1692
|
-
func.length = ctx.state.paramc;
|
|
1693
|
-
func.env = topEnv(ctx);
|
|
1694
|
-
eat(ctx, TK_EQUAL);
|
|
1695
|
-
var ret = function(ctx) {
|
|
1696
|
-
return exprsStart(ctx, TK_DOT, function (ctx) {
|
|
1697
|
-
var def = env.findWord(ctx, topEnv(ctx).name);
|
|
1698
|
-
def.nid = Ast.peek(ctx); // save node id for aliased code
|
|
1699
|
-
env.exitEnv(ctx);
|
|
1700
|
-
Ast.letDef(ctx); // Clean up stack
|
|
1701
|
-
return cc;
|
|
1702
|
-
});
|
|
1703
|
-
}
|
|
1704
|
-
ret.cls = "punc";
|
|
1705
|
-
return ret;
|
|
1706
|
-
})
|
|
1707
|
-
})
|
|
1708
|
-
ret.cls = "def";
|
|
1709
|
-
return ret;
|
|
1710
|
-
}
|
|
1711
|
-
ret.cls = "keyword";
|
|
1712
|
-
return ret;
|
|
1713
|
-
}
|
|
1714
|
-
return name(ctx, cc);
|
|
1715
|
-
}
|
|
1716
|
-
|
|
1717
|
-
// TODO add argument for specifying the break token.
|
|
1718
|
-
// e.g. TK_EQUAL | TK_VERTICALBAR
|
|
1719
|
-
// params(ctx, brk, resume) {..}
|
|
1720
|
-
function params(ctx, brk, cc) {
|
|
1721
|
-
if (match(ctx, brk)) {
|
|
1722
|
-
return cc
|
|
1723
|
-
}
|
|
1724
|
-
var ret = function (ctx) {
|
|
1725
|
-
var ret = defName(ctx, (ctx) => {
|
|
1726
|
-
Ast.pop(ctx); // Throw away name.
|
|
1727
|
-
ctx.state.paramc++;
|
|
1728
|
-
return params(ctx, brk, cc);
|
|
1729
|
-
});
|
|
1730
|
-
ret.cls = "param";
|
|
1731
|
-
return ret;
|
|
1732
|
-
};
|
|
1733
|
-
ret.cls = "param";
|
|
1734
|
-
return ret;
|
|
1735
|
-
}
|
|
1736
|
-
|
|
1737
|
-
function param(ctx, cc) {
|
|
1738
|
-
return primaryExpr(ctx, function (ctx) {
|
|
1739
|
-
return cc
|
|
1740
|
-
});
|
|
1741
|
-
}
|
|
1742
|
-
|
|
1743
|
-
// Drive the parser
|
|
1744
|
-
|
|
1745
|
-
function topEnv(ctx) {
|
|
1746
|
-
return ctx.state.env[ctx.state.env.length-1];
|
|
1747
|
-
}
|
|
1748
|
-
|
|
1749
|
-
function parse(stream, state, resume) {
|
|
1750
|
-
var ctx = {
|
|
1751
|
-
scan: scanner(stream, state.env[0].lexicon),
|
|
1752
|
-
state: state,
|
|
1753
|
-
};
|
|
1754
|
-
var cls
|
|
1755
|
-
try {
|
|
1756
|
-
var c;
|
|
1757
|
-
while ((c = stream.peek()) && (c===' ' || c==='\t')) {
|
|
1758
|
-
stream.next()
|
|
1759
|
-
}
|
|
1760
|
-
// if this is a blank line, treat it as a comment
|
|
1761
|
-
if (stream.peek()===void 0) {
|
|
1762
|
-
throw "comment"
|
|
1763
|
-
}
|
|
1764
|
-
// call the continuation and store the next continuation
|
|
1765
|
-
if (state.cc === null) {
|
|
1766
|
-
next(ctx)
|
|
1767
|
-
return "comment"
|
|
1768
|
-
}
|
|
1769
|
-
var t0 = new Date;
|
|
1770
|
-
var lastCC = state.cc
|
|
1771
|
-
var cc = state.cc = state.cc(ctx, null)
|
|
1772
|
-
if (cc) {
|
|
1773
|
-
cls = cc.cls
|
|
1774
|
-
}
|
|
1775
|
-
if (cc === null) {
|
|
1776
|
-
if (resume) {
|
|
1777
|
-
// FIXME make all paths go through a resume function.
|
|
1778
|
-
if (state.errors.length > 0) {
|
|
1779
|
-
resume(state.errors);
|
|
1780
|
-
} else {
|
|
1781
|
-
resume(null, Ast.poolToJSON(ctx));
|
|
1782
|
-
}
|
|
1783
|
-
} else {
|
|
1784
|
-
window.gcexports.errors = state.errors;
|
|
1785
|
-
}
|
|
1786
|
-
}
|
|
1787
|
-
var c;
|
|
1788
|
-
while ((c = stream.peek()) &&
|
|
1789
|
-
(c===' ' || c==='\t')) {
|
|
1790
|
-
stream.next()
|
|
1791
|
-
}
|
|
1792
|
-
} catch (x) {
|
|
1793
|
-
if (x instanceof Error) {
|
|
1794
|
-
next(ctx)
|
|
1795
|
-
addError(ctx, x.message);
|
|
1796
|
-
state.cc = null; // done for now.
|
|
1797
|
-
cls = "error"
|
|
1798
|
-
console.log(x.stack);
|
|
1799
|
-
if (resume) {
|
|
1800
|
-
resume(window.gcexports.errors);
|
|
1801
|
-
}
|
|
1802
|
-
} else if (x === "comment") {
|
|
1803
|
-
cls = x
|
|
1804
|
-
} else {
|
|
1805
|
-
//throw x
|
|
1806
|
-
next(ctx)
|
|
1807
|
-
cls = "error"
|
|
1808
|
-
console.log(x.stack);
|
|
1809
|
-
}
|
|
1810
|
-
}
|
|
1811
|
-
var t1 = new Date;
|
|
1812
|
-
parseCount++
|
|
1813
|
-
parseTime += t1 - t0
|
|
1814
|
-
window.gcexports.coords = state.coords;
|
|
1815
|
-
return cls;
|
|
1816
|
-
}
|
|
1817
|
-
|
|
1818
|
-
var lexeme = ""
|
|
1819
|
-
|
|
1820
|
-
function scanner(stream, globalLexicon) {
|
|
1821
|
-
|
|
1822
|
-
return {
|
|
1823
|
-
start: start ,
|
|
1824
|
-
stream: stream,
|
|
1825
|
-
lexeme: function () {
|
|
1826
|
-
return lexeme
|
|
1827
|
-
}
|
|
1828
|
-
}
|
|
1829
|
-
|
|
1830
|
-
// begin private functions
|
|
1831
|
-
|
|
1832
|
-
function peekCC() {
|
|
1833
|
-
return stream.peek() && stream.peek().charCodeAt(0) || 0;
|
|
1834
|
-
}
|
|
1835
|
-
|
|
1836
|
-
function nextCC() {
|
|
1837
|
-
return stream.peek() && stream.next().charCodeAt(0) || 0;
|
|
1838
|
-
}
|
|
1839
|
-
|
|
1840
|
-
function start(ctx) {
|
|
1841
|
-
var c;
|
|
1842
|
-
lexeme = "";
|
|
1843
|
-
while (stream.peek() !== void 0) {
|
|
1844
|
-
switch ((c = stream.next().charCodeAt(0))) {
|
|
1845
|
-
case 32: // space
|
|
1846
|
-
case 9: // tab
|
|
1847
|
-
case 10: // new line
|
|
1848
|
-
case 13: // carriage return
|
|
1849
|
-
c = ' ';
|
|
1850
|
-
continue
|
|
1851
|
-
case 46: // dot
|
|
1852
|
-
if (isNumeric(stream.peek())) {
|
|
1853
|
-
return number(c);
|
|
1854
|
-
}
|
|
1855
|
-
lexeme += String.fromCharCode(c);
|
|
1856
|
-
return TK_DOT
|
|
1857
|
-
case 44: // comma
|
|
1858
|
-
lexeme += String.fromCharCode(c);
|
|
1859
|
-
return TK_COMMA
|
|
1860
|
-
case 58: // colon
|
|
1861
|
-
lexeme += String.fromCharCode(c);
|
|
1862
|
-
return TK_COLON
|
|
1863
|
-
case 61: // equal
|
|
1864
|
-
lexeme += String.fromCharCode(c);
|
|
1865
|
-
return TK_EQUAL
|
|
1866
|
-
case 40: // left paren
|
|
1867
|
-
lexeme += String.fromCharCode(c);
|
|
1868
|
-
return TK_LEFTPAREN
|
|
1869
|
-
case 41: // right paren
|
|
1870
|
-
lexeme += String.fromCharCode(c);
|
|
1871
|
-
return TK_RIGHTPAREN
|
|
1872
|
-
case 45: // dash
|
|
1873
|
-
lexeme += String.fromCharCode(c);
|
|
1874
|
-
return TK_MINUS
|
|
1875
|
-
case 60: // left angle
|
|
1876
|
-
lexeme += String.fromCharCode(c);
|
|
1877
|
-
return TK_LEFTANGLE
|
|
1878
|
-
case 62: // right angle
|
|
1879
|
-
lexeme += String.fromCharCode(c);
|
|
1880
|
-
return TK_RIGHTANGLE
|
|
1881
|
-
case 91: // left bracket
|
|
1882
|
-
lexeme += String.fromCharCode(c);
|
|
1883
|
-
return TK_LEFTBRACKET
|
|
1884
|
-
case 93: // right bracket
|
|
1885
|
-
lexeme += String.fromCharCode(c);
|
|
1886
|
-
return TK_RIGHTBRACKET
|
|
1887
|
-
case 123: // left brace
|
|
1888
|
-
lexeme += String.fromCharCode(c);
|
|
1889
|
-
return TK_LEFTBRACE
|
|
1890
|
-
case 125: // right brace
|
|
1891
|
-
lexeme += String.fromCharCode(c);
|
|
1892
|
-
if (ctx.state.inStr) {
|
|
1893
|
-
return stringSuffix(ctx);
|
|
1894
|
-
}
|
|
1895
|
-
return TK_RIGHTBRACE
|
|
1896
|
-
case CC_DOUBLEQUOTE:
|
|
1897
|
-
case CC_SINGLEQUOTE:
|
|
1898
|
-
case CC_BACKTICK:
|
|
1899
|
-
return string(ctx, c)
|
|
1900
|
-
|
|
1901
|
-
case 96: // backquote
|
|
1902
|
-
case 47: // slash
|
|
1903
|
-
case 92: // backslash
|
|
1904
|
-
case 33: // !
|
|
1905
|
-
case 124: // |
|
|
1906
|
-
comment(c)
|
|
1907
|
-
throw "comment"
|
|
1908
|
-
case 94: // caret
|
|
1909
|
-
case 44: // comma
|
|
1910
|
-
case 42: // asterisk
|
|
1911
|
-
lexeme += String.fromCharCode(c);
|
|
1912
|
-
return c; // char code is the token id
|
|
1913
|
-
default:
|
|
1914
|
-
if ((c >= 'A'.charCodeAt(0) && c <= 'Z'.charCodeAt(0)) ||
|
|
1915
|
-
(c >= 'a'.charCodeAt(0) && c <= 'z'.charCodeAt(0)) ||
|
|
1916
|
-
(c === '_'.charCodeAt(0))) {
|
|
1917
|
-
return ident(c);
|
|
1918
|
-
} else if (isNumeric(c) || c === '.'.charCodeAt(0) && isNumeric(stream.peek())) {
|
|
1919
|
-
//lex += String.fromCharCode(c);
|
|
1920
|
-
//c = src.charCodeAt(curIndex++);
|
|
1921
|
-
//return TK_NUM;
|
|
1922
|
-
return number(c);
|
|
1923
|
-
} else {
|
|
1924
|
-
return 0;
|
|
1925
|
-
}
|
|
1926
|
-
}
|
|
1927
|
-
}
|
|
1928
|
-
|
|
1929
|
-
return 0;
|
|
1930
|
-
}
|
|
1931
|
-
|
|
1932
|
-
function isNumeric(c) {
|
|
1933
|
-
if (typeof c === "string") {
|
|
1934
|
-
c = c.charCodeAt(0);
|
|
1935
|
-
}
|
|
1936
|
-
return c >= '0'.charCodeAt(0) && c <= '9'.charCodeAt(0);
|
|
1937
|
-
}
|
|
1938
|
-
|
|
1939
|
-
function number(c) {
|
|
1940
|
-
// 123, 1.23, .123
|
|
1941
|
-
while (isNumeric(c) || c === '.'.charCodeAt(0) && isNumeric(stream.peek())) {
|
|
1942
|
-
lexeme += String.fromCharCode(c);
|
|
1943
|
-
var s;
|
|
1944
|
-
c = (s = stream.next()) ? s.charCodeAt(0) : 0
|
|
1945
|
-
}
|
|
1946
|
-
if (c) {
|
|
1947
|
-
stream.backUp(1);
|
|
1948
|
-
} // otherwise, we are at the end of stream
|
|
1949
|
-
return TK_NUM;
|
|
1950
|
-
}
|
|
1951
|
-
|
|
1952
|
-
// "abc" --> "abc"
|
|
1953
|
-
// "a${x}c" --> concat ["a", x, "b"]
|
|
1954
|
-
function string(ctx, c) {
|
|
1955
|
-
var quoteChar = c;
|
|
1956
|
-
ctx.state.quoteCharStack.push(c);
|
|
1957
|
-
lexeme += String.fromCharCode(c)
|
|
1958
|
-
c = nextCC();
|
|
1959
|
-
while (c !== quoteChar && c !== 0 &&
|
|
1960
|
-
(quoteChar === CC_BACKTICK || !(c === CC_DOLLAR && peekCC() === CC_LEFTBRACE))) {
|
|
1961
|
-
lexeme += String.fromCharCode(c);
|
|
1962
|
-
var s;
|
|
1963
|
-
c = nextCC();
|
|
1964
|
-
}
|
|
1965
|
-
if (c === CC_DOLLAR &&
|
|
1966
|
-
peekCC() === CC_LEFTBRACE) {
|
|
1967
|
-
nextCC(); // Eat CC_LEFTBRACE
|
|
1968
|
-
lexeme = lexeme.substring(1); // Strip off punct.
|
|
1969
|
-
return TK_STRPREFIX;
|
|
1970
|
-
} else if (c) {
|
|
1971
|
-
lexeme = lexeme.substring(1); // Strip off leading quote.
|
|
1972
|
-
return TK_STR;
|
|
1973
|
-
} else {
|
|
1974
|
-
return 0
|
|
1975
|
-
}
|
|
1976
|
-
}
|
|
1977
|
-
|
|
1978
|
-
function stringSuffix(ctx) {
|
|
1979
|
-
var c, s;
|
|
1980
|
-
var quoteCharStack = ctx.state.quoteCharStack;
|
|
1981
|
-
var quoteChar = quoteCharStack[quoteCharStack.length - 1];
|
|
1982
|
-
c = nextCC();
|
|
1983
|
-
while (c !== quoteChar && c !== 0 &&
|
|
1984
|
-
!(c === CC_DOLLAR &&
|
|
1985
|
-
peekCC() === CC_LEFTBRACE)) {
|
|
1986
|
-
lexeme += String.fromCharCode(c);
|
|
1987
|
-
c = nextCC();
|
|
1988
|
-
}
|
|
1989
|
-
if (c === CC_DOLLAR &&
|
|
1990
|
-
peekCC() === CC_LEFTBRACE) {
|
|
1991
|
-
nextCC() ; // Eat brace.
|
|
1992
|
-
lexeme = lexeme.substring(1); // Strip off leading brace and trailing brace.
|
|
1993
|
-
return TK_STRMIDDLE;
|
|
1994
|
-
} else if (c) {
|
|
1995
|
-
quoteCharStack.pop();
|
|
1996
|
-
lexeme = lexeme.substring(1); // Strip off leading braces.
|
|
1997
|
-
return TK_STRSUFFIX;
|
|
1998
|
-
} else {
|
|
1999
|
-
return 0
|
|
2000
|
-
}
|
|
2001
|
-
}
|
|
2002
|
-
|
|
2003
|
-
function comment(c) {
|
|
2004
|
-
var quoteChar = c
|
|
2005
|
-
c = (s = stream.next()) ? s.charCodeAt(0) : 0
|
|
2006
|
-
|
|
2007
|
-
while (c !== quoteChar && c != 10 && c!= 13 && c !== 0) {
|
|
2008
|
-
var s;
|
|
2009
|
-
c = (s = stream.next()) ? s.charCodeAt(0) : 0
|
|
2010
|
-
}
|
|
2011
|
-
|
|
2012
|
-
return TK_COMMENT
|
|
2013
|
-
}
|
|
2014
|
-
|
|
2015
|
-
function ident(c) {
|
|
2016
|
-
while ((c >= 'A'.charCodeAt(0) && c <= 'Z'.charCodeAt(0)) ||
|
|
2017
|
-
(c >= 'a'.charCodeAt(0) && c <= 'z'.charCodeAt(0)) ||
|
|
2018
|
-
(c === '-'.charCodeAt(0)) ||
|
|
2019
|
-
(c === '@'.charCodeAt(0)) ||
|
|
2020
|
-
(c === '+'.charCodeAt(0)) ||
|
|
2021
|
-
(c === '#'.charCodeAt(0)) ||
|
|
2022
|
-
(c === '_'.charCodeAt(0)) ||
|
|
2023
|
-
(c === '~'.charCodeAt(0)) ||
|
|
2024
|
-
(c >= '0'.charCodeAt(0) && c <= '9'.charCodeAt(0)))
|
|
2025
|
-
{
|
|
2026
|
-
lexeme += String.fromCharCode(c);
|
|
2027
|
-
c = stream.peek() ? stream.next().charCodeAt(0) : 0
|
|
2028
|
-
}
|
|
2029
|
-
|
|
2030
|
-
if (c) {
|
|
2031
|
-
stream.backUp(1);
|
|
2032
|
-
} // otherwise, we are at the end of stream
|
|
2033
|
-
|
|
2034
|
-
var tk = TK_IDENT
|
|
2035
|
-
if (keywords[lexeme]) {
|
|
2036
|
-
tk = keywords[lexeme].tk;
|
|
2037
|
-
} else if (globalLexicon[lexeme]) {
|
|
2038
|
-
tk = globalLexicon[lexeme].tk
|
|
2039
|
-
}
|
|
2040
|
-
return tk;
|
|
2041
|
-
}
|
|
2042
|
-
}
|
|
2043
|
-
|
|
2044
|
-
var parser = {
|
|
2045
|
-
token: function(stream, state) {
|
|
2046
|
-
return parse(stream, state)
|
|
2047
|
-
},
|
|
2048
|
-
parse: parse,
|
|
2049
|
-
program: program,
|
|
2050
|
-
}
|
|
2051
|
-
|
|
2052
|
-
window.gcexports.parse = parser.parse
|
|
2053
|
-
return parser
|
|
2054
|
-
})(); // end parser
|
|
2055
|
-
|
|
2056
|
-
var foldTime = 0
|
|
2057
|
-
|
|
2058
|
-
window.gcexports.foldTime = function () {
|
|
2059
|
-
return foldTime
|
|
2060
|
-
}
|
|
2061
|
-
|
|
2062
|
-
var folder = function() {
|
|
2063
|
-
var _ = window.gcexports._;
|
|
2064
|
-
|
|
2065
|
-
var table = {
|
|
2066
|
-
"PROG" : program,
|
|
2067
|
-
"EXPRS" : exprs,
|
|
2068
|
-
"PAREN" : parenExpr,
|
|
2069
|
-
"IDENT" : ident,
|
|
2070
|
-
"BOOL" : bool,
|
|
2071
|
-
"NUM" : num,
|
|
2072
|
-
"STR" : str,
|
|
2073
|
-
"PARENS" : unaryExpr,
|
|
2074
|
-
"APPLY" : apply,
|
|
2075
|
-
"LAMBDA" : lambda,
|
|
2076
|
-
// "MUL": mul,
|
|
2077
|
-
// "DIV": div,
|
|
2078
|
-
// "SUB": sub,
|
|
2079
|
-
"ADD": add,
|
|
2080
|
-
"POW": pow,
|
|
2081
|
-
"MOD": mod,
|
|
2082
|
-
"CONCAT": concat,
|
|
2083
|
-
// "OR": orelse,
|
|
2084
|
-
// "AND": andalso,
|
|
2085
|
-
// "NE": ne,
|
|
2086
|
-
// "EQ": eq,
|
|
2087
|
-
// "LT": lt,
|
|
2088
|
-
// "GT": gt,
|
|
2089
|
-
// "LE": le,
|
|
2090
|
-
// "GE": ge,
|
|
2091
|
-
"NEG": neg,
|
|
2092
|
-
"LIST": list,
|
|
2093
|
-
// "CASE": caseExpr,
|
|
2094
|
-
// "OF": ofClause,
|
|
2095
|
-
};
|
|
2096
|
-
|
|
2097
|
-
var canvasWidth = 0;
|
|
2098
|
-
var canvasHeight = 0;
|
|
2099
|
-
|
|
2100
|
-
return {
|
|
2101
|
-
fold: fold,
|
|
2102
|
-
};
|
|
2103
|
-
|
|
2104
|
-
// CONTROL FLOW ENDS HERE
|
|
2105
|
-
|
|
2106
|
-
var nodePool;
|
|
2107
|
-
var ctx;
|
|
2108
|
-
|
|
2109
|
-
function fold(cx, nid) {
|
|
2110
|
-
ctx = cx;
|
|
2111
|
-
nodePool = ctx.state.nodePool;
|
|
2112
|
-
var t0 = new Date;
|
|
2113
|
-
visit(nid);
|
|
2114
|
-
var t1 = new Date;
|
|
2115
|
-
foldTime += (t1-t0);
|
|
2116
|
-
}
|
|
2117
|
-
|
|
2118
|
-
function visit(nid) {
|
|
2119
|
-
var node = nodePool[nid];
|
|
2120
|
-
if (node == null) {
|
|
2121
|
-
return null;
|
|
2122
|
-
}
|
|
2123
|
-
if (node.tag === void 0) {
|
|
2124
|
-
return [ ] // clean up stubs;
|
|
2125
|
-
} else if (isFunction(table[node.tag])) {
|
|
2126
|
-
// Have a primitive operation so apply it to construct a new node.
|
|
2127
|
-
var ret = table[node.tag](node);
|
|
2128
|
-
return ret;
|
|
2129
|
-
}
|
|
2130
|
-
expr(node);
|
|
2131
|
-
}
|
|
2132
|
-
|
|
2133
|
-
function isArray(v) {
|
|
2134
|
-
return v instanceof Array;
|
|
2135
|
-
}
|
|
2136
|
-
|
|
2137
|
-
function isString(v) {
|
|
2138
|
-
return typeof v === "string";
|
|
2139
|
-
}
|
|
2140
|
-
|
|
2141
|
-
function isPrimitive(v) {
|
|
2142
|
-
return (
|
|
2143
|
-
v === null ||
|
|
2144
|
-
typeof v === "string" ||
|
|
2145
|
-
typeof v === "number" ||
|
|
2146
|
-
typeof v === "boolean"
|
|
2147
|
-
);
|
|
2148
|
-
}
|
|
2149
|
-
|
|
2150
|
-
function isFunction(v) {
|
|
2151
|
-
return v instanceof Function;
|
|
2152
|
-
}
|
|
2153
|
-
|
|
2154
|
-
// BEGIN VISITOR METHODS
|
|
2155
|
-
|
|
2156
|
-
var edgesNode;
|
|
2157
|
-
|
|
2158
|
-
function program(node) {
|
|
2159
|
-
visit(node.elts[0]);
|
|
2160
|
-
Ast.program(ctx);
|
|
2161
|
-
}
|
|
2162
|
-
|
|
2163
|
-
function caseExpr(node) {
|
|
2164
|
-
visit(node.elts[node.elts.length-1]);
|
|
2165
|
-
var expr = Ast.pop(ctx);
|
|
2166
|
-
for (var i = node.elts.length-2; i >= 0; i--) {
|
|
2167
|
-
var ofNode = ctx.state.nodePool[node.elts[i]];
|
|
2168
|
-
var patternNode = ofNode.elts[1];
|
|
2169
|
-
visit(patternNode);
|
|
2170
|
-
var pattern = Ast.pop(ctx);
|
|
2171
|
-
// if (Ast.intern(expr) === Ast.intern(pattern)) {
|
|
2172
|
-
if (expr === pattern) {
|
|
2173
|
-
visit(ofNode.elts[0]);
|
|
2174
|
-
return;
|
|
2175
|
-
}
|
|
2176
|
-
}
|
|
2177
|
-
}
|
|
2178
|
-
|
|
2179
|
-
function ofClause(node) {
|
|
2180
|
-
for (var i = 0; i < node.elts.length; i++) {
|
|
2181
|
-
visit(node.elts[i]);
|
|
2182
|
-
}
|
|
2183
|
-
Ast.ofClause(ctx);
|
|
2184
|
-
}
|
|
2185
|
-
function pushNodeStack(ctx) {
|
|
2186
|
-
ctx.state.nodeStackStack.push(ctx.state.nodeStack);
|
|
2187
|
-
ctx.state.nodeStack = [];
|
|
2188
|
-
}
|
|
2189
|
-
function popNodeStack(ctx) {
|
|
2190
|
-
var stack = ctx.state.nodeStack;
|
|
2191
|
-
ctx.state.nodeStack = ctx.state.nodeStackStack.pop().concat(stack);
|
|
2192
|
-
}
|
|
2193
|
-
|
|
2194
|
-
function list(node) {
|
|
2195
|
-
// Fold list
|
|
2196
|
-
// for (var i = 0; i < node.elts.length; i++) {
|
|
2197
|
-
// visit(node.elts[i]);
|
|
2198
|
-
// }
|
|
2199
|
-
pushNodeStack(ctx);
|
|
2200
|
-
for (var i = node.elts.length - 1; i >= 0; i--) {
|
|
2201
|
-
visit(node.elts[i]); // Keep original order.
|
|
2202
|
-
}
|
|
2203
|
-
Ast.list(ctx, ctx.state.nodeStack.length, null, true);
|
|
2204
|
-
popNodeStack(ctx);
|
|
2205
|
-
}
|
|
2206
|
-
|
|
2207
|
-
function exprs(node) {
|
|
2208
|
-
// Fold exprs in reverse order to get precedence right.
|
|
2209
|
-
for (var i = node.elts.length - 1; i >= 0; i--) {
|
|
2210
|
-
visit(node.elts[i]); // Keep original order.
|
|
2211
|
-
}
|
|
2212
|
-
ctx.state.exprc = node.elts.length;
|
|
2213
|
-
}
|
|
2214
|
-
|
|
2215
|
-
function lambda(node) {
|
|
2216
|
-
// Fold initializers and apply args.
|
|
2217
|
-
var inits = Ast.node(ctx, node.elts[3]).elts;
|
|
2218
|
-
inits.forEach((init, i) => {
|
|
2219
|
-
if (init) {
|
|
2220
|
-
// If we have an init then fold it and replace in inits list.
|
|
2221
|
-
folder.fold(ctx, Ast.intern(ctx, init));
|
|
2222
|
-
inits[i] = Ast.pop(ctx);
|
|
2223
|
-
}
|
|
2224
|
-
});
|
|
2225
|
-
// FIXME don't patch old node. construct a new one.
|
|
2226
|
-
node.elts[3] = Ast.intern(ctx, {tag: "LIST", elts: inits});
|
|
2227
|
-
var fnId = Ast.intern(ctx, node);
|
|
2228
|
-
var argc = ctx.state.nodeStack.length;
|
|
2229
|
-
Ast.apply(ctx, fnId, argc);
|
|
2230
|
-
}
|
|
2231
|
-
|
|
2232
|
-
function apply(node) {
|
|
2233
|
-
for (var i = node.elts.length-1; i >= 0; i--) {
|
|
2234
|
-
visit(node.elts[i]);
|
|
2235
|
-
}
|
|
2236
|
-
Ast.applyLate(ctx, node.elts.length);
|
|
2237
|
-
}
|
|
2238
|
-
|
|
2239
|
-
function expr(node) {
|
|
2240
|
-
// Construct an expression node for the compiler.
|
|
2241
|
-
Ast.name(ctx, node.tag, getCoord(ctx));
|
|
2242
|
-
for (var i = node.elts.length-1; i >= 0; i--) {
|
|
2243
|
-
visit(node.elts[i]);
|
|
2244
|
-
}
|
|
2245
|
-
Ast.expr(ctx, node.elts.length);
|
|
2246
|
-
}
|
|
2247
|
-
|
|
2248
|
-
function neg(node) {
|
|
2249
|
-
visit(node.elts[0]);
|
|
2250
|
-
Ast.neg(ctx);
|
|
2251
|
-
}
|
|
2252
|
-
|
|
2253
|
-
function parenExpr(node) {
|
|
2254
|
-
pushNodeStack(ctx);
|
|
2255
|
-
visit(node.elts[0]);
|
|
2256
|
-
Ast.parenExpr(ctx);
|
|
2257
|
-
popNodeStack(ctx);
|
|
2258
|
-
}
|
|
2259
|
-
|
|
2260
|
-
function unaryExpr(node) {
|
|
2261
|
-
visit(node.elts[0]);
|
|
2262
|
-
Ast.unaryExpr(ctx, node.tag);
|
|
2263
|
-
}
|
|
2264
|
-
|
|
2265
|
-
function add(node) {
|
|
2266
|
-
visit(node.elts[0]);
|
|
2267
|
-
visit(node.elts[1]);
|
|
2268
|
-
Ast.add(ctx);
|
|
2269
|
-
}
|
|
2270
|
-
|
|
2271
|
-
function sub(node) {
|
|
2272
|
-
visit(node.elts[0]);
|
|
2273
|
-
visit(node.elts[1]);
|
|
2274
|
-
Ast.sub(ctx);
|
|
2275
|
-
}
|
|
2276
|
-
|
|
2277
|
-
function mul(node) {
|
|
2278
|
-
visit(node.elts[0]);
|
|
2279
|
-
visit(node.elts[1]);
|
|
2280
|
-
Ast.mul(ctx);
|
|
2281
|
-
}
|
|
2282
|
-
|
|
2283
|
-
function div(node) {
|
|
2284
|
-
visit(node.elts[0]);
|
|
2285
|
-
visit(node.elts[1]);
|
|
2286
|
-
Ast.div(ctx);
|
|
2287
|
-
}
|
|
2288
|
-
|
|
2289
|
-
function pow(node) {
|
|
2290
|
-
visit(node.elts[0]);
|
|
2291
|
-
visit(node.elts[1]);
|
|
2292
|
-
Ast.pow(ctx);
|
|
2293
|
-
}
|
|
2294
|
-
|
|
2295
|
-
function concat(node) {
|
|
2296
|
-
visit(node.elts[0]);
|
|
2297
|
-
Ast.concat(ctx);
|
|
2298
|
-
}
|
|
2299
|
-
|
|
2300
|
-
function mod(node) {
|
|
2301
|
-
visit(node.elts[0]);
|
|
2302
|
-
visit(node.elts[1]);
|
|
2303
|
-
Ast.mod(ctx);
|
|
2304
|
-
}
|
|
2305
|
-
|
|
2306
|
-
function orelse(node) {
|
|
2307
|
-
visit(node.elts[0]);
|
|
2308
|
-
visit(node.elts[1]);
|
|
2309
|
-
Ast.orelse(ctx);
|
|
2310
|
-
}
|
|
2311
|
-
|
|
2312
|
-
function andalso(node) {
|
|
2313
|
-
visit(node.elts[0]);
|
|
2314
|
-
visit(node.elts[1]);
|
|
2315
|
-
Ast.andalso(ctx);
|
|
2316
|
-
}
|
|
2317
|
-
|
|
2318
|
-
function eq(node) {
|
|
2319
|
-
visit(node.elts[0]);
|
|
2320
|
-
visit(node.elts[1]);
|
|
2321
|
-
Ast.eq(ctx);
|
|
2322
|
-
}
|
|
2323
|
-
|
|
2324
|
-
function ne(node) {
|
|
2325
|
-
visit(node.elts[0]);
|
|
2326
|
-
visit(node.elts[1]);
|
|
2327
|
-
Ast.ne(ctx);
|
|
2328
|
-
}
|
|
2329
|
-
|
|
2330
|
-
function lt(node) {
|
|
2331
|
-
visit(node.elts[0]);
|
|
2332
|
-
visit(node.elts[1]);
|
|
2333
|
-
Ast.lt(ctx);
|
|
2334
|
-
}
|
|
2335
|
-
|
|
2336
|
-
function gt(node) {
|
|
2337
|
-
visit(node.elts[0]);
|
|
2338
|
-
visit(node.elts[1]);
|
|
2339
|
-
Ast.gt(ctx);
|
|
2340
|
-
}
|
|
2341
|
-
|
|
2342
|
-
function le(node) {
|
|
2343
|
-
visit(node.elts[0]);
|
|
2344
|
-
visit(node.elts[1]);
|
|
2345
|
-
Ast.le(ctx);
|
|
2346
|
-
}
|
|
2347
|
-
|
|
2348
|
-
function ge(node) {
|
|
2349
|
-
visit(node.elts[0]);
|
|
2350
|
-
visit(node.elts[1]);
|
|
2351
|
-
Ast.ge(ctx);
|
|
2352
|
-
}
|
|
2353
|
-
|
|
2354
|
-
function ident(node) {
|
|
2355
|
-
var name = node.elts[0];
|
|
2356
|
-
var word = env.findWord(ctx, name);
|
|
2357
|
-
if (word) {
|
|
2358
|
-
if (word.cls==="val") {
|
|
2359
|
-
if (word.val) {
|
|
2360
|
-
Ast.push(ctx, word.val);
|
|
2361
|
-
visit(Ast.pop(ctx)); // reduce the val expr
|
|
2362
|
-
} else if (word.nid) {
|
|
2363
|
-
var wrd;
|
|
2364
|
-
if ((wrd = Ast.node(ctx, word.nid)).tag === "LAMBDA") {
|
|
2365
|
-
var argc = wrd.elts[0].elts.length;
|
|
2366
|
-
Ast.apply(ctx, word.nid, argc);
|
|
2367
|
-
} else {
|
|
2368
|
-
Ast.push(ctx, word.nid);
|
|
2369
|
-
}
|
|
2370
|
-
} else if (word.name) {
|
|
2371
|
-
Ast.push(ctx, node);
|
|
2372
|
-
} else {
|
|
2373
|
-
// push the original node to be resolved later.
|
|
2374
|
-
Ast.push(ctx, node);
|
|
2375
|
-
}
|
|
2376
|
-
} else if (word.cls==="function") {
|
|
2377
|
-
let coord = getCoord(ctx);
|
|
2378
|
-
var elts = [];
|
|
2379
|
-
for (var i = 0; i < word.length; i++) {
|
|
2380
|
-
var elt = Ast.pop(ctx);
|
|
2381
|
-
elts.push(elt);
|
|
2382
|
-
}
|
|
2383
|
-
if (word.nid) {
|
|
2384
|
-
Ast.fold(ctx, word, elts);
|
|
2385
|
-
} else {
|
|
2386
|
-
Ast.push(ctx, {
|
|
2387
|
-
tag: word.name,
|
|
2388
|
-
elts: elts,
|
|
2389
|
-
coord: coord,
|
|
2390
|
-
});
|
|
2391
|
-
folder.fold(ctx, Ast.pop(ctx));
|
|
2392
|
-
}
|
|
2393
|
-
} else {
|
|
2394
|
-
assert(false);
|
|
2395
|
-
}
|
|
2396
|
-
} else {
|
|
2397
|
-
//assert(false, "unresolved ident "+name);
|
|
2398
|
-
Ast.push(ctx, node);
|
|
2399
|
-
}
|
|
2400
|
-
}
|
|
2401
|
-
|
|
2402
|
-
function num(node) {
|
|
2403
|
-
Ast.number(ctx, node.elts[0]);
|
|
2404
|
-
}
|
|
2405
|
-
|
|
2406
|
-
function str(node) {
|
|
2407
|
-
Ast.string(ctx, node.elts[0]);
|
|
2408
|
-
}
|
|
2409
|
-
|
|
2410
|
-
function bool(node) {
|
|
2411
|
-
Ast.bool(ctx, node.elts[0]);
|
|
2412
|
-
}
|
|
2413
|
-
|
|
2414
|
-
function nul(node) {
|
|
2415
|
-
Ast.nul(ctx);
|
|
2416
|
-
}
|
|
2417
|
-
|
|
2418
|
-
function stub(node) {
|
|
2419
|
-
return "";
|
|
2420
|
-
}
|
|
2421
|
-
}();
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
if (typeof exports !== "undefined") {
|
|
2425
|
-
exports.parser = window.gcexports.parser;
|
|
2426
|
-
}
|