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