@graffiticode/basis 1.3.0 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/compiler.js +15 -0
- package/src/#compiler.js# +0 -739
package/package.json
CHANGED
package/src/compiler.js
CHANGED
|
@@ -137,6 +137,14 @@ export class Checker extends Visitor {
|
|
|
137
137
|
const val = node;
|
|
138
138
|
resume(err, val);
|
|
139
139
|
}
|
|
140
|
+
JSON(node, options, resume) {
|
|
141
|
+
this.visit(node.elts[0], options, (e0, v0) => {
|
|
142
|
+
assert(v0.tag === "STR");
|
|
143
|
+
const err = [];
|
|
144
|
+
const val = node;
|
|
145
|
+
resume(err, val);
|
|
146
|
+
});
|
|
147
|
+
}
|
|
140
148
|
CONCAT(node, options, resume) {
|
|
141
149
|
this.visit(node.elts[0], options, (e0, v0) => {
|
|
142
150
|
const err = [];
|
|
@@ -484,6 +492,13 @@ export class Transformer extends Visitor {
|
|
|
484
492
|
const val = node.elts[0];
|
|
485
493
|
resume(err, val);
|
|
486
494
|
}
|
|
495
|
+
JSON(node, options, resume) {
|
|
496
|
+
this.visit(node.elts[0], options, (e0, v0) => {
|
|
497
|
+
const err = [];
|
|
498
|
+
const val = JSON.parse(v0);
|
|
499
|
+
resume(err, val);
|
|
500
|
+
});
|
|
501
|
+
}
|
|
487
502
|
CONCAT(node, options, resume) {
|
|
488
503
|
this.visit(node.elts[0], options, (e0, v0) => {
|
|
489
504
|
const err = [];
|
package/src/#compiler.js#
DELETED
|
@@ -1,739 +0,0 @@
|
|
|
1
|
-
/* Copyright (c) 2021, ARTCOMPILER INC */
|
|
2
|
-
import {assert, message, messages, reserveCodeRange} from "./share.js";
|
|
3
|
-
reserveCodeRange(1000, 1999, "compile");
|
|
4
|
-
messages[1001] = "Node ID %1 not found in pool.";
|
|
5
|
-
messages[1002] = "Invalid tag in node with Node ID %1.";
|
|
6
|
-
messages[1003] = "No async callback provided.";
|
|
7
|
-
messages[1004] = "No visitor method defined for '%1'.";
|
|
8
|
-
|
|
9
|
-
function error(msg, arg) {
|
|
10
|
-
return msg + arg;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
function newNode(tag, elts) {
|
|
14
|
-
return {
|
|
15
|
-
tag: tag,
|
|
16
|
-
elts: elts,
|
|
17
|
-
};
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
const ASYNC = true;
|
|
21
|
-
|
|
22
|
-
class Visitor {
|
|
23
|
-
constructor(code) {
|
|
24
|
-
this.nodePool = code;
|
|
25
|
-
this.root = code.root;
|
|
26
|
-
}
|
|
27
|
-
visit(nid, options, resume) {
|
|
28
|
-
assert(nid);
|
|
29
|
-
let node;
|
|
30
|
-
if (typeof nid === "object") {
|
|
31
|
-
node = nid;
|
|
32
|
-
} else {
|
|
33
|
-
node = this.nodePool[nid];
|
|
34
|
-
}
|
|
35
|
-
assert(node && node.tag && node.elts, "2000: Visitor.visit() tag=" + node.tag + " elts= " + JSON.stringify(node.elts));
|
|
36
|
-
assert(this[node.tag], "2000: Visitor function not defined for: " + node.tag);
|
|
37
|
-
assert(typeof resume === "function", message(1003));
|
|
38
|
-
if (!options.SYNC && ASYNC) {
|
|
39
|
-
setTimeout(() => this[node.tag](node, options, resume), 0);
|
|
40
|
-
} else {
|
|
41
|
-
this[node.tag](node, options, resume);
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
node(nid) {
|
|
45
|
-
var n = this.nodePool[nid];
|
|
46
|
-
if (!nid) {
|
|
47
|
-
return null;
|
|
48
|
-
} else if (!n) {
|
|
49
|
-
return {};
|
|
50
|
-
}
|
|
51
|
-
var elts = [];
|
|
52
|
-
switch (n.tag) {
|
|
53
|
-
case "NULL":
|
|
54
|
-
break;
|
|
55
|
-
case "NUM":
|
|
56
|
-
case "STR":
|
|
57
|
-
case "IDENT":
|
|
58
|
-
case "BOOL":
|
|
59
|
-
elts[0] = n.elts[0];
|
|
60
|
-
break;
|
|
61
|
-
default:
|
|
62
|
-
for (var i=0; i < n.elts.length; i++) {
|
|
63
|
-
elts[i] = this.node(n.elts[i]);
|
|
64
|
-
}
|
|
65
|
-
break;
|
|
66
|
-
}
|
|
67
|
-
return {
|
|
68
|
-
tag: n.tag,
|
|
69
|
-
elts: elts,
|
|
70
|
-
};
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
export class Checker extends Visitor {
|
|
75
|
-
constructor(nodePool) {
|
|
76
|
-
super(nodePool);
|
|
77
|
-
}
|
|
78
|
-
check(options, resume) {
|
|
79
|
-
const nid = this.root;
|
|
80
|
-
this.visit(nid, options, (err, data) => {
|
|
81
|
-
resume(err, data);
|
|
82
|
-
});
|
|
83
|
-
}
|
|
84
|
-
PROG(node, options, resume) {
|
|
85
|
-
this.visit(node.elts[0], options, (e0, v0) => {
|
|
86
|
-
const err = [];
|
|
87
|
-
const val = node;
|
|
88
|
-
resume(err, val);
|
|
89
|
-
});
|
|
90
|
-
}
|
|
91
|
-
EXPRS(node, options, resume) {
|
|
92
|
-
let err = [];
|
|
93
|
-
let val = [];
|
|
94
|
-
for (let elt of node.elts) {
|
|
95
|
-
this.visit(elt, options, (e0, v0) => {
|
|
96
|
-
err = err.concat(e0);
|
|
97
|
-
val = val.concat(v0);
|
|
98
|
-
if (val.length === node.elts.length) {
|
|
99
|
-
resume(err, val);
|
|
100
|
-
}
|
|
101
|
-
});
|
|
102
|
-
}
|
|
103
|
-
if (node.elts.length === 0) {
|
|
104
|
-
resume(err, val);
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
NUM(node, options, resume) {
|
|
108
|
-
const err = [];
|
|
109
|
-
const val = node;
|
|
110
|
-
resume(err, val);
|
|
111
|
-
}
|
|
112
|
-
LAMBDA(node, options, resume) {
|
|
113
|
-
const err = [];
|
|
114
|
-
const val = node;
|
|
115
|
-
this.visit(node.elts[0], options, (e0, v0) => {
|
|
116
|
-
resume(err, val);
|
|
117
|
-
});
|
|
118
|
-
}
|
|
119
|
-
LIST(node, options, resume) {
|
|
120
|
-
const err = [];
|
|
121
|
-
const val = node;
|
|
122
|
-
if (node.elts.length === 0) {
|
|
123
|
-
resume(err, val);
|
|
124
|
-
} else {
|
|
125
|
-
this.visit(node.elts[0], options, (e0, v0) => {
|
|
126
|
-
resume(err, val);
|
|
127
|
-
});
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
IDENT(node, options, resume) {
|
|
131
|
-
const err = [];
|
|
132
|
-
const val = node;
|
|
133
|
-
resume(err, val);
|
|
134
|
-
}
|
|
135
|
-
STR(node, options, resume) {
|
|
136
|
-
const err = [];
|
|
137
|
-
const val = node;
|
|
138
|
-
resume(err, val);
|
|
139
|
-
}
|
|
140
|
-
CONCAT(node, options, resume) {
|
|
141
|
-
this.visit(node.elts[0], options, (e0, v0) => {
|
|
142
|
-
const err = [];
|
|
143
|
-
const val = node;
|
|
144
|
-
resume(err, val);
|
|
145
|
-
});
|
|
146
|
-
}
|
|
147
|
-
ADD(node, options, resume) {
|
|
148
|
-
this.visit(node.elts[0], options, (err1, val1) => {
|
|
149
|
-
this.visit(node.elts[1], options, (err2, val2) => {
|
|
150
|
-
if (isNaN(+val1)) {
|
|
151
|
-
err1 = err1.concat(error("Argument must be a number.", node.elts[0]));
|
|
152
|
-
}
|
|
153
|
-
if (isNaN(+val2)) {
|
|
154
|
-
err2 = err2.concat(error("Argument must be a number.", node.elts[1]));
|
|
155
|
-
}
|
|
156
|
-
const err = [].concat(err1).concat(err2);
|
|
157
|
-
const val = node;
|
|
158
|
-
resume(err, val);
|
|
159
|
-
});
|
|
160
|
-
});
|
|
161
|
-
}
|
|
162
|
-
BOOL(node, options, resume) {
|
|
163
|
-
const err = [];
|
|
164
|
-
const val = node;
|
|
165
|
-
resume(err, val);
|
|
166
|
-
}
|
|
167
|
-
NULL(node, options, resume) {
|
|
168
|
-
const err = [];
|
|
169
|
-
const val = node;
|
|
170
|
-
resume(err, val);
|
|
171
|
-
}
|
|
172
|
-
RECORD(node, options, resume) {
|
|
173
|
-
const err = [];
|
|
174
|
-
const val = node;
|
|
175
|
-
resume(err, val);
|
|
176
|
-
}
|
|
177
|
-
BINDING(node, options, resume) {
|
|
178
|
-
const err = [];
|
|
179
|
-
const val = node;
|
|
180
|
-
resume(err, val);
|
|
181
|
-
}
|
|
182
|
-
MUL(node, options, resume) {
|
|
183
|
-
this.visit(node.elts[0], options, (err1, val1) => {
|
|
184
|
-
this.visit(node.elts[1], options, (err2, val2) => {
|
|
185
|
-
if (isNaN(+val1)) {
|
|
186
|
-
err1 = err1.concat(error("Argument must be a number.", node.elts[0]));
|
|
187
|
-
}
|
|
188
|
-
if (isNaN(+val2)) {
|
|
189
|
-
err2 = err2.concat(error("Argument must be a number.", node.elts[1]));
|
|
190
|
-
}
|
|
191
|
-
const err = [].concat(err1).concat(err2);
|
|
192
|
-
const val = node;
|
|
193
|
-
resume(err, val);
|
|
194
|
-
});
|
|
195
|
-
});
|
|
196
|
-
}
|
|
197
|
-
POW(node, options, resume) {
|
|
198
|
-
this.visit(node.elts[0], options, (err1, val1) => {
|
|
199
|
-
this.visit(node.elts[1], options, (err2, val2) => {
|
|
200
|
-
if (isNaN(+val1)) {
|
|
201
|
-
err1 = err1.concat(error("Argument must be a number.", node.elts[0]));
|
|
202
|
-
}
|
|
203
|
-
if (isNaN(+val2)) {
|
|
204
|
-
err2 = err2.concat(error("Argument must be a number.", node.elts[1]));
|
|
205
|
-
}
|
|
206
|
-
const err = [].concat(err1).concat(err2);
|
|
207
|
-
const val = node;
|
|
208
|
-
resume(err, val);
|
|
209
|
-
});
|
|
210
|
-
});
|
|
211
|
-
}
|
|
212
|
-
VAL(node, options, resume) {
|
|
213
|
-
const err = [];
|
|
214
|
-
const val = node;
|
|
215
|
-
resume(err, val);
|
|
216
|
-
}
|
|
217
|
-
KEY(node, options, resume) {
|
|
218
|
-
const err = [];
|
|
219
|
-
const val = node;
|
|
220
|
-
resume(err, val);
|
|
221
|
-
}
|
|
222
|
-
LEN(node, options, resume) {
|
|
223
|
-
const err = [];
|
|
224
|
-
const val = node;
|
|
225
|
-
resume(err, val);
|
|
226
|
-
}
|
|
227
|
-
ARG(node, options, resume) {
|
|
228
|
-
const err = [];
|
|
229
|
-
const val = node;
|
|
230
|
-
resume(err, val);
|
|
231
|
-
}
|
|
232
|
-
DATA(node, options, resume) {
|
|
233
|
-
const err = [];
|
|
234
|
-
const val = node;
|
|
235
|
-
resume(err, val);
|
|
236
|
-
}
|
|
237
|
-
PAREN(node, options, resume) {
|
|
238
|
-
const err = [];
|
|
239
|
-
const val = node;
|
|
240
|
-
resume(err, val);
|
|
241
|
-
}
|
|
242
|
-
APPLY(node, options, resume) {
|
|
243
|
-
const err = [];
|
|
244
|
-
const val = node;
|
|
245
|
-
resume(err, val);
|
|
246
|
-
}
|
|
247
|
-
MAP(node, options, resume) {
|
|
248
|
-
const err = [];
|
|
249
|
-
const val = node;
|
|
250
|
-
resume(err, val);
|
|
251
|
-
}
|
|
252
|
-
STYLE(node, options, resume) {
|
|
253
|
-
const err = [];
|
|
254
|
-
const val = node;
|
|
255
|
-
resume(err, val);
|
|
256
|
-
}
|
|
257
|
-
CASE(node, options, resume) {
|
|
258
|
-
const err = [];
|
|
259
|
-
const val = node;
|
|
260
|
-
resume(err, val);
|
|
261
|
-
}
|
|
262
|
-
OF(node, options, resume) {
|
|
263
|
-
const err = [];
|
|
264
|
-
const val = node;
|
|
265
|
-
resume(err, val);
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
function enterEnv(ctx, name, paramc) {
|
|
270
|
-
if (!ctx.env) {
|
|
271
|
-
ctx.env = [];
|
|
272
|
-
}
|
|
273
|
-
// recursion guard
|
|
274
|
-
if (ctx.env.length > 380) {
|
|
275
|
-
//return; // just stop recursing
|
|
276
|
-
throw new Error("runaway recursion");
|
|
277
|
-
}
|
|
278
|
-
ctx.env.push({
|
|
279
|
-
name: name,
|
|
280
|
-
paramc: paramc,
|
|
281
|
-
lexicon: {},
|
|
282
|
-
pattern: [],
|
|
283
|
-
});
|
|
284
|
-
}
|
|
285
|
-
function exitEnv(ctx) {
|
|
286
|
-
ctx.env.pop();
|
|
287
|
-
}
|
|
288
|
-
function findWord(ctx, lexeme) {
|
|
289
|
-
let env = ctx.env;
|
|
290
|
-
if (!env) {
|
|
291
|
-
return null;
|
|
292
|
-
}
|
|
293
|
-
for (var i = env.length-1; i >= 0; i--) {
|
|
294
|
-
var word = env[i].lexicon[lexeme];
|
|
295
|
-
if (word) {
|
|
296
|
-
return word;
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
return null;
|
|
300
|
-
}
|
|
301
|
-
function addWord(ctx, lexeme, entry) {
|
|
302
|
-
topEnv(ctx).lexicon[lexeme] = entry;
|
|
303
|
-
return null;
|
|
304
|
-
}
|
|
305
|
-
function topEnv(ctx) {
|
|
306
|
-
return ctx.env[ctx.env.length-1]
|
|
307
|
-
}
|
|
308
|
-
export class Transformer extends Visitor {
|
|
309
|
-
constructor(nodePool) {
|
|
310
|
-
super(nodePool);
|
|
311
|
-
this.patternNodePool = ['unused'];
|
|
312
|
-
this.patternNodeMap = {};
|
|
313
|
-
}
|
|
314
|
-
transform(options, resume) {
|
|
315
|
-
const nid = this.root;
|
|
316
|
-
this.visit(nid, options, (err, data) => {
|
|
317
|
-
resume(err, data);
|
|
318
|
-
});
|
|
319
|
-
}
|
|
320
|
-
internPattern(n) {
|
|
321
|
-
if (!n) {
|
|
322
|
-
return 0;
|
|
323
|
-
}
|
|
324
|
-
const nodeMap = this.patternNodeMap;
|
|
325
|
-
const nodePool = this.patternNodePool;
|
|
326
|
-
const tag = n.tag;
|
|
327
|
-
const elts_nids = [];
|
|
328
|
-
const count = n.elts.length;
|
|
329
|
-
let elts = "";
|
|
330
|
-
for (let i = 0; i < count; i++) {
|
|
331
|
-
if (typeof n.elts[i] === "object") {
|
|
332
|
-
n.elts[i] = this.internPattern(n.elts[i]);
|
|
333
|
-
}
|
|
334
|
-
elts += n.elts[i];
|
|
335
|
-
}
|
|
336
|
-
const key = tag+count+elts;
|
|
337
|
-
let nid = nodeMap[key];
|
|
338
|
-
if (nid === void 0) {
|
|
339
|
-
nodePool.push({tag: tag, elts: n.elts});
|
|
340
|
-
nid = nodePool.length - 1;
|
|
341
|
-
nodeMap[key] = nid;
|
|
342
|
-
if (n.coord) {
|
|
343
|
-
ctx.state.coords[nid] = n.coord;
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
return nid;
|
|
347
|
-
}
|
|
348
|
-
match(options, patterns, node) {
|
|
349
|
-
if (patterns.size === 0 || node === undefined) {
|
|
350
|
-
return false;
|
|
351
|
-
}
|
|
352
|
-
let matches = patterns.filter((pattern) => {
|
|
353
|
-
if (pattern.tag === undefined || node.tag === undefined) {
|
|
354
|
-
return false;
|
|
355
|
-
}
|
|
356
|
-
const patternNid = this.internPattern(pattern);
|
|
357
|
-
if (patternNid === this.internPattern(node) ||
|
|
358
|
-
patternNid === this.internPattern(newNode('IDENT', ['_']))) {
|
|
359
|
-
return true;
|
|
360
|
-
}
|
|
361
|
-
if (pattern.tag === node.tag) {
|
|
362
|
-
if (pattern.elts.length === node.elts.length) {
|
|
363
|
-
// Same number of args, so see if each matches.
|
|
364
|
-
return pattern.elts.every((arg, i) => {
|
|
365
|
-
if (pattern.tag === 'VAR') {
|
|
366
|
-
if (arg === node.elts[i]) {
|
|
367
|
-
return true;
|
|
368
|
-
}
|
|
369
|
-
return false;
|
|
370
|
-
}
|
|
371
|
-
let result = this.match(options, [arg], node.elts[i]);
|
|
372
|
-
return result.length === 1;
|
|
373
|
-
});
|
|
374
|
-
} else if (pattern.elts.length < node.elts.length) {
|
|
375
|
-
// Different number of args, then see if there is a wildcard match.
|
|
376
|
-
let nargs = node.elts.slice(1);
|
|
377
|
-
if (pattern.elts.length === 2) {
|
|
378
|
-
// Binary node pattern
|
|
379
|
-
let result = (
|
|
380
|
-
this.match(options, [pattern.elts[0]], node.elts[0]).length > 0 &&
|
|
381
|
-
this.match(options, [pattern.elts[1]], newNode(node.tag, nargs)).length > 0
|
|
382
|
-
// Match rest of the node against the second pattern argument.
|
|
383
|
-
);
|
|
384
|
-
return result;
|
|
385
|
-
}
|
|
386
|
-
}
|
|
387
|
-
}
|
|
388
|
-
return false;
|
|
389
|
-
});
|
|
390
|
-
// if (true || matches.length > 0) {
|
|
391
|
-
// console.log("match() node: " + JSON.stringify(node, null, 2));
|
|
392
|
-
// console.log("match() matches: " + JSON.stringify(matches, null, 2));
|
|
393
|
-
// }
|
|
394
|
-
return matches;
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
PROG(node, options, resume) {
|
|
399
|
-
if (!options) {
|
|
400
|
-
options = {};
|
|
401
|
-
}
|
|
402
|
-
this.visit(node.elts[0], options, (e0, v0) => {
|
|
403
|
-
const err = e0;
|
|
404
|
-
const val = v0.pop(); // Return the value of the last expression.
|
|
405
|
-
resume(err, val);
|
|
406
|
-
});
|
|
407
|
-
}
|
|
408
|
-
EXPRS(node, options, resume) {
|
|
409
|
-
let err = [];
|
|
410
|
-
let val = [];
|
|
411
|
-
for (let elt of node.elts) {
|
|
412
|
-
this.visit(elt, options, (e0, v0) => {
|
|
413
|
-
err = err.concat(e0);
|
|
414
|
-
val.push(v0);
|
|
415
|
-
if (val.length === node.elts.length) {
|
|
416
|
-
resume(err, val);
|
|
417
|
-
}
|
|
418
|
-
});
|
|
419
|
-
}
|
|
420
|
-
if (node.elts.length === 0) {
|
|
421
|
-
val.push("");
|
|
422
|
-
resume(err, val);
|
|
423
|
-
}
|
|
424
|
-
}
|
|
425
|
-
NUM(node, options, resume) {
|
|
426
|
-
const err = [];
|
|
427
|
-
const val = +node.elts[0];
|
|
428
|
-
resume(err, val);
|
|
429
|
-
}
|
|
430
|
-
LAMBDA(node, options, resume) {
|
|
431
|
-
// Return a function value.
|
|
432
|
-
this.visit(node.elts[0], options, (err0, params) => {
|
|
433
|
-
let args = [].concat(options.args);
|
|
434
|
-
enterEnv(options, "lambda", params.length);
|
|
435
|
-
params.forEach((param, i) => {
|
|
436
|
-
// let inits = this.nodePool[node.elts[3]].elts;
|
|
437
|
-
if (args[i]) {
|
|
438
|
-
// Got an arg so use it.
|
|
439
|
-
addWord(options, param, {
|
|
440
|
-
name: param,
|
|
441
|
-
val: args[i],
|
|
442
|
-
});
|
|
443
|
-
// } else {
|
|
444
|
-
// // Don't got an arg so use the init.
|
|
445
|
-
// this.visit(inits[i], options, (err, val) => {
|
|
446
|
-
// addWord(options, param, {
|
|
447
|
-
// name: param,
|
|
448
|
-
// val: val,
|
|
449
|
-
// });
|
|
450
|
-
// });
|
|
451
|
-
}
|
|
452
|
-
});
|
|
453
|
-
this.visit(node.elts[1], options, (err, val) => {
|
|
454
|
-
exitEnv(options);
|
|
455
|
-
resume([].concat(err0).concat(err).concat(err), val)
|
|
456
|
-
});
|
|
457
|
-
});
|
|
458
|
-
}
|
|
459
|
-
LIST(node, options, resume) {
|
|
460
|
-
let err = [];
|
|
461
|
-
let val = [];
|
|
462
|
-
if (node.elts.length === 0) {
|
|
463
|
-
resume(err, val);
|
|
464
|
-
} else {
|
|
465
|
-
for (let elt of node.elts) {
|
|
466
|
-
this.visit(elt, options, (e0, v0) => {
|
|
467
|
-
err = err.concat(e0);
|
|
468
|
-
val.push(v0);
|
|
469
|
-
if (val.length === node.elts.length) {
|
|
470
|
-
resume(err, val);
|
|
471
|
-
}
|
|
472
|
-
});
|
|
473
|
-
}
|
|
474
|
-
}
|
|
475
|
-
}
|
|
476
|
-
IDENT(node, options, resume) {
|
|
477
|
-
let word = findWord(options, node.elts[0]);
|
|
478
|
-
const err = [];
|
|
479
|
-
const val = word && word.val || node.elts[0];
|
|
480
|
-
resume(err, val);
|
|
481
|
-
}
|
|
482
|
-
STR(node, options, resume) {
|
|
483
|
-
const err = [];
|
|
484
|
-
const val = node.elts[0];
|
|
485
|
-
resume(err, val);
|
|
486
|
-
}
|
|
487
|
-
CONCAT(node, options, resume) {
|
|
488
|
-
this.visit(node.elts[0], options, (e0, v0) => {
|
|
489
|
-
const err = [];
|
|
490
|
-
const val = ([].concat(v0)).join('');
|
|
491
|
-
resume(err, val);
|
|
492
|
-
});
|
|
493
|
-
}
|
|
494
|
-
ADD(node, options, resume) {
|
|
495
|
-
this.visit(node.elts[0], options, (e0, v0) => {
|
|
496
|
-
this.visit(node.elts[1], options, (e1, v1) => {
|
|
497
|
-
const err = [].concat(e0).concat(e1);
|
|
498
|
-
const val = +v0 + +v1;
|
|
499
|
-
resume(err, val);
|
|
500
|
-
});
|
|
501
|
-
});
|
|
502
|
-
}
|
|
503
|
-
BOOL(node, options, resume) {
|
|
504
|
-
const err = [];
|
|
505
|
-
const val = node;
|
|
506
|
-
resume(err, val);
|
|
507
|
-
}
|
|
508
|
-
NULL(node, options, resume) {
|
|
509
|
-
const err = [];
|
|
510
|
-
const val = null;
|
|
511
|
-
resume(err, val);
|
|
512
|
-
}
|
|
513
|
-
BINDING(node, options, resume) {
|
|
514
|
-
const err = [];
|
|
515
|
-
const val = node;
|
|
516
|
-
this.visit(node.elts[0], options, (err1, val1) => {
|
|
517
|
-
this.visit(node.elts[1], options, (err2, val2) => {
|
|
518
|
-
resume([].concat(err1).concat(err2), {key: val1, val: val2});
|
|
519
|
-
});
|
|
520
|
-
});
|
|
521
|
-
}
|
|
522
|
-
RECORD(node, options, resume) {
|
|
523
|
-
let err = [];
|
|
524
|
-
let val = {};
|
|
525
|
-
let len = 0;
|
|
526
|
-
if (node.elts.length === 0) {
|
|
527
|
-
resume(err, val);
|
|
528
|
-
} else {
|
|
529
|
-
for (let elt of node.elts.reverse()) {
|
|
530
|
-
// For historical reasons, the bindings are reversed in the AST.
|
|
531
|
-
this.visit(elt, options, (e0, v0) => {
|
|
532
|
-
err = err.concat(e0);
|
|
533
|
-
val[v0.key] = v0.val;
|
|
534
|
-
if (++len === node.elts.length) {
|
|
535
|
-
resume(err, val);
|
|
536
|
-
}
|
|
537
|
-
});
|
|
538
|
-
}
|
|
539
|
-
}
|
|
540
|
-
}
|
|
541
|
-
MUL(node, options, resume) {
|
|
542
|
-
this.visit(node.elts[0], options, (err1, val1) => {
|
|
543
|
-
this.visit(node.elts[1], options, (err2, val2) => {
|
|
544
|
-
if (isNaN(+val1)) {
|
|
545
|
-
err1 = err1.concat(error("Argument must be a number.", node.elts[0]));
|
|
546
|
-
}
|
|
547
|
-
if (isNaN(+val2)) {
|
|
548
|
-
err2 = err2.concat(error("Argument must be a number.", node.elts[1]));
|
|
549
|
-
}
|
|
550
|
-
const err = [].concat(err1).concat(err2);
|
|
551
|
-
const val = +val1 * +val2;
|
|
552
|
-
resume(err, val);
|
|
553
|
-
});
|
|
554
|
-
});
|
|
555
|
-
}
|
|
556
|
-
POW(node, options, resume) {
|
|
557
|
-
this.visit(node.elts[0], options, (err1, val1) => {
|
|
558
|
-
this.visit(node.elts[1], options, (err2, val2) => {
|
|
559
|
-
if (isNaN(+val1)) {
|
|
560
|
-
err1 = err1.concat(error("Argument must be a number.", node.elts[0]));
|
|
561
|
-
}
|
|
562
|
-
if (isNaN(+val2)) {
|
|
563
|
-
err2 = err2.concat(error("Argument must be a number.", node.elts[1]));
|
|
564
|
-
}
|
|
565
|
-
const err = [].concat(err1).concat(err2);
|
|
566
|
-
const val = node;
|
|
567
|
-
resume(err, val);
|
|
568
|
-
});
|
|
569
|
-
});
|
|
570
|
-
}
|
|
571
|
-
VAL(node, options, resume) {
|
|
572
|
-
this.visit(node.elts[0], options, (e0, v0) => {
|
|
573
|
-
this.visit(node.elts[1], options, (e1, v1) => {
|
|
574
|
-
const err = [].concat(e0).concat(e1);
|
|
575
|
-
const val = v1[v0];
|
|
576
|
-
resume(err, val);
|
|
577
|
-
});
|
|
578
|
-
});
|
|
579
|
-
}
|
|
580
|
-
KEY(node, options, resume) {
|
|
581
|
-
const err = [];
|
|
582
|
-
const val = node;
|
|
583
|
-
resume(err, val);
|
|
584
|
-
}
|
|
585
|
-
LEN(node, options, resume) {
|
|
586
|
-
const err = [];
|
|
587
|
-
const val = node;
|
|
588
|
-
resume(err, val);
|
|
589
|
-
}
|
|
590
|
-
ARG(node, options, resume) {
|
|
591
|
-
const err = [];
|
|
592
|
-
const val = node;
|
|
593
|
-
resume(err, val);
|
|
594
|
-
}
|
|
595
|
-
DATA(node, options, resume) {
|
|
596
|
-
if (options.data && Object.keys(options.data).length !== 0) {
|
|
597
|
-
// Got external data, so use it.
|
|
598
|
-
const err = [];
|
|
599
|
-
const val = options.data;
|
|
600
|
-
resume(err, val);
|
|
601
|
-
} else {
|
|
602
|
-
// Otherwise, use the default data.
|
|
603
|
-
this.visit(node.elts[0], options, (e0, v0) => {
|
|
604
|
-
const err = e0;
|
|
605
|
-
const val = v0;
|
|
606
|
-
resume(err, val);
|
|
607
|
-
});
|
|
608
|
-
}
|
|
609
|
-
}
|
|
610
|
-
PAREN(node, options, resume) {
|
|
611
|
-
this.visit(node.elts[0], options, (e0, v0) => {
|
|
612
|
-
const err = [].concat(e0);
|
|
613
|
-
const val = v0;
|
|
614
|
-
resume(err, val);
|
|
615
|
-
});
|
|
616
|
-
}
|
|
617
|
-
APPLY(node, options, resume) {
|
|
618
|
-
// Apply a function to arguments.
|
|
619
|
-
this.visit(node.elts[1], options, (e1, v1) => {
|
|
620
|
-
options.args = v1;
|
|
621
|
-
this.visit(node.elts[0], options, (e0, v0) => {
|
|
622
|
-
//exitEnv(options);
|
|
623
|
-
const err = [].concat(e1).concat(e0);
|
|
624
|
-
const val = v0;
|
|
625
|
-
resume(err, val);
|
|
626
|
-
});
|
|
627
|
-
});
|
|
628
|
-
}
|
|
629
|
-
MAP(node, options, resume) {
|
|
630
|
-
this.visit(node.elts[1], options, (e1, v1) => {
|
|
631
|
-
let err = [];
|
|
632
|
-
let val = [];
|
|
633
|
-
v1.forEach(args => {
|
|
634
|
-
options.args = args;
|
|
635
|
-
options = JSON.parse(JSON.stringify(options)); // Copy option arg support async.
|
|
636
|
-
this.visit(node.elts[0], options, (e0, v0) => {
|
|
637
|
-
val.push(v0);
|
|
638
|
-
err = err.concat(e0);
|
|
639
|
-
if (val.length === v1.length) {
|
|
640
|
-
resume(err, val);
|
|
641
|
-
}
|
|
642
|
-
});
|
|
643
|
-
});
|
|
644
|
-
});
|
|
645
|
-
}
|
|
646
|
-
STYLE(node, options, resume) {
|
|
647
|
-
const err = [];
|
|
648
|
-
const val = node;
|
|
649
|
-
resume(err, val);
|
|
650
|
-
}
|
|
651
|
-
CASE(node, options, resume) {
|
|
652
|
-
// FIXME this isn't ASYNC compatible
|
|
653
|
-
options.SYNC = true;
|
|
654
|
-
this.visit(node.elts[0], options, (err, e0) => {
|
|
655
|
-
const e0Node = this.node(node.elts[0]);
|
|
656
|
-
const expr = (e0Node.tag === 'NUM' || e0Node.tag === 'NUM') && e0Node || {tag: 'STR', elts: [`${e0}`]};
|
|
657
|
-
let foundMatch = false;
|
|
658
|
-
const patterns = [];
|
|
659
|
-
for (var i = 1; i < node.elts.length; i++) {
|
|
660
|
-
this.visit(node.elts[i], options, (err, val) => {
|
|
661
|
-
if (this.match(options, [this.node(node.elts[i]).elts[0]], expr).length) {
|
|
662
|
-
this.visit(val.exprElt, options, resume);
|
|
663
|
-
foundMatch = true;
|
|
664
|
-
}
|
|
665
|
-
});
|
|
666
|
-
if (foundMatch) {
|
|
667
|
-
return;
|
|
668
|
-
}
|
|
669
|
-
}
|
|
670
|
-
if (!foundMatch) {
|
|
671
|
-
resume([], {})
|
|
672
|
-
}
|
|
673
|
-
});
|
|
674
|
-
options.SYNC = false;
|
|
675
|
-
}
|
|
676
|
-
OF(node, options, resume) {
|
|
677
|
-
this.visit(node.elts[0], options, (err0, pattern) => {
|
|
678
|
-
resume([].concat(err0), {
|
|
679
|
-
pattern: pattern,
|
|
680
|
-
exprElt: node.elts[1],
|
|
681
|
-
});
|
|
682
|
-
});
|
|
683
|
-
}
|
|
684
|
-
}
|
|
685
|
-
|
|
686
|
-
export class Renderer {
|
|
687
|
-
constructor(data) {
|
|
688
|
-
this.data = data;
|
|
689
|
-
}
|
|
690
|
-
render(options, resume) {
|
|
691
|
-
// Do some rendering here.
|
|
692
|
-
const err = [];
|
|
693
|
-
const val = this.data;
|
|
694
|
-
resume(err, val);
|
|
695
|
-
}
|
|
696
|
-
}
|
|
697
|
-
|
|
698
|
-
export class Compiler {
|
|
699
|
-
constructor(config) {
|
|
700
|
-
this.langID = config.langID;
|
|
701
|
-
this.version = config.version;
|
|
702
|
-
this.Checker = config.Checker || Checker;
|
|
703
|
-
this.Transformer = config.Transformer || Transformer;
|
|
704
|
-
this.Renderer = config.Renderer || Renderer;
|
|
705
|
-
}
|
|
706
|
-
compile(code, data, config, resume) {
|
|
707
|
-
// Compiler takes an AST in the form of a node pool (code) and transforms it
|
|
708
|
-
// into an object to be rendered on the client by the viewer for this
|
|
709
|
-
// language.
|
|
710
|
-
try {
|
|
711
|
-
let options = {
|
|
712
|
-
data: data,
|
|
713
|
-
config: config,
|
|
714
|
-
result: '',
|
|
715
|
-
};
|
|
716
|
-
const checker = new this.Checker(code);
|
|
717
|
-
checker.check(options, (err, val) => {
|
|
718
|
-
const transformer = new this.Transformer(code);
|
|
719
|
-
transformer.transform(options, (err, val) => {
|
|
720
|
-
if (err && err.length) {
|
|
721
|
-
resume(err, val);
|
|
722
|
-
} else {
|
|
723
|
-
const renderer = new this.Renderer(val);
|
|
724
|
-
renderer.render(options, (err, val) => {
|
|
725
|
-
resume(err, val);
|
|
726
|
-
});
|
|
727
|
-
}
|
|
728
|
-
});
|
|
729
|
-
});
|
|
730
|
-
} catch (x) {
|
|
731
|
-
console.log("ERROR with code");
|
|
732
|
-
console.log(x.stack);
|
|
733
|
-
resume([{
|
|
734
|
-
statusCode: 500,
|
|
735
|
-
error: "Compiler error"
|
|
736
|
-
}]);
|
|
737
|
-
}
|
|
738
|
-
}
|
|
739
|
-
}
|