@graffiticode/basis 1.0.11 → 1.0.12
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 +6 -3
- package/src/compiler.js +154 -22
- package/src/parse.js +2426 -0
package/package.json
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@graffiticode/basis",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.0.
|
|
4
|
+
"version": "1.0.12",
|
|
5
5
|
"description": "The basis library for creating Graffiticode languages",
|
|
6
|
-
"main": "
|
|
7
|
-
"scripts": {
|
|
6
|
+
"main": "index.js",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"test": "jest"
|
|
9
|
+
},
|
|
8
10
|
"repository": {
|
|
9
11
|
"type": "git",
|
|
10
12
|
"url": "git+https://github.com/graffiticode/basis.git"
|
|
@@ -19,6 +21,7 @@
|
|
|
19
21
|
"d3": "^6.6.2",
|
|
20
22
|
"hashids": "^2.2.8",
|
|
21
23
|
"https": "^1.0.0",
|
|
24
|
+
"jest": "^27.0.1",
|
|
22
25
|
"react": "^17.0.2"
|
|
23
26
|
}
|
|
24
27
|
}
|
package/src/compiler.js
CHANGED
|
@@ -6,11 +6,23 @@ messages[1002] = "Invalid tag in node with Node ID %1.";
|
|
|
6
6
|
messages[1003] = "No async callback provided.";
|
|
7
7
|
messages[1004] = "No visitor method defined for '%1'.";
|
|
8
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
|
+
|
|
9
20
|
const ASYNC = true;
|
|
10
21
|
|
|
11
22
|
class Visitor {
|
|
12
|
-
constructor(
|
|
13
|
-
this.nodePool =
|
|
23
|
+
constructor(code) {
|
|
24
|
+
this.nodePool = code;
|
|
25
|
+
this.root = code.root;
|
|
14
26
|
}
|
|
15
27
|
visit(nid, options, resume) {
|
|
16
28
|
assert(nid);
|
|
@@ -29,6 +41,34 @@ class Visitor {
|
|
|
29
41
|
this[node.tag](node, options, resume);
|
|
30
42
|
}
|
|
31
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
|
+
}
|
|
32
72
|
}
|
|
33
73
|
|
|
34
74
|
export class Checker extends Visitor {
|
|
@@ -36,7 +76,7 @@ export class Checker extends Visitor {
|
|
|
36
76
|
super(nodePool);
|
|
37
77
|
}
|
|
38
78
|
check(options, resume) {
|
|
39
|
-
const nid = this.
|
|
79
|
+
const nid = this.root;
|
|
40
80
|
this.visit(nid, options, (err, data) => {
|
|
41
81
|
resume(err, data);
|
|
42
82
|
});
|
|
@@ -102,8 +142,8 @@ export class Checker extends Visitor {
|
|
|
102
142
|
});
|
|
103
143
|
}
|
|
104
144
|
ADD(node, options, resume) {
|
|
105
|
-
this.visit(node.elts[0], options,
|
|
106
|
-
this.visit(node.elts[1], options,
|
|
145
|
+
this.visit(node.elts[0], options, (err1, val1) => {
|
|
146
|
+
this.visit(node.elts[1], options, (err2, val2) => {
|
|
107
147
|
if (isNaN(+val1)) {
|
|
108
148
|
err1 = err1.concat(error("Argument must be a number.", node.elts[0]));
|
|
109
149
|
}
|
|
@@ -121,6 +161,11 @@ export class Checker extends Visitor {
|
|
|
121
161
|
const val = node;
|
|
122
162
|
resume(err, val);
|
|
123
163
|
}
|
|
164
|
+
NULL(node, options, resume) {
|
|
165
|
+
const err = [];
|
|
166
|
+
const val = node;
|
|
167
|
+
resume(err, val);
|
|
168
|
+
}
|
|
124
169
|
RECORD(node, options, resume) {
|
|
125
170
|
const err = [];
|
|
126
171
|
const val = node;
|
|
@@ -132,8 +177,8 @@ export class Checker extends Visitor {
|
|
|
132
177
|
resume(err, val);
|
|
133
178
|
}
|
|
134
179
|
MUL(node, options, resume) {
|
|
135
|
-
this.visit(node.elts[0], options,
|
|
136
|
-
this.visit(node.elts[1], options,
|
|
180
|
+
this.visit(node.elts[0], options, (err1, val1) => {
|
|
181
|
+
this.visit(node.elts[1], options, (err2, val2) => {
|
|
137
182
|
if (isNaN(+val1)) {
|
|
138
183
|
err1 = err1.concat(error("Argument must be a number.", node.elts[0]));
|
|
139
184
|
}
|
|
@@ -147,8 +192,8 @@ export class Checker extends Visitor {
|
|
|
147
192
|
});
|
|
148
193
|
}
|
|
149
194
|
POW(node, options, resume) {
|
|
150
|
-
this.visit(node.elts[0], options,
|
|
151
|
-
this.visit(node.elts[1], options,
|
|
195
|
+
this.visit(node.elts[0], options, (err1, val1) => {
|
|
196
|
+
this.visit(node.elts[1], options, (err2, val2) => {
|
|
152
197
|
if (isNaN(+val1)) {
|
|
153
198
|
err1 = err1.concat(error("Argument must be a number.", node.elts[0]));
|
|
154
199
|
}
|
|
@@ -257,17 +302,96 @@ function addWord(ctx, lexeme, entry) {
|
|
|
257
302
|
function topEnv(ctx) {
|
|
258
303
|
return ctx.env[ctx.env.length-1]
|
|
259
304
|
}
|
|
260
|
-
|
|
261
305
|
export class Transformer extends Visitor {
|
|
262
306
|
constructor(nodePool) {
|
|
263
307
|
super(nodePool);
|
|
308
|
+
this.patternNodePool = ['unused'];
|
|
309
|
+
this.patternNodeMap = {};
|
|
264
310
|
}
|
|
265
311
|
transform(options, resume) {
|
|
266
|
-
const nid = this.
|
|
312
|
+
const nid = this.root;
|
|
267
313
|
this.visit(nid, options, (err, data) => {
|
|
268
314
|
resume(err, data);
|
|
269
315
|
});
|
|
270
316
|
}
|
|
317
|
+
internPattern(n) {
|
|
318
|
+
if (!n) {
|
|
319
|
+
return 0;
|
|
320
|
+
}
|
|
321
|
+
const nodeMap = this.patternNodeMap;
|
|
322
|
+
const nodePool = this.patternNodePool;
|
|
323
|
+
const tag = n.tag;
|
|
324
|
+
const elts_nids = [];
|
|
325
|
+
const count = n.elts.length;
|
|
326
|
+
let elts = "";
|
|
327
|
+
for (let i = 0; i < count; i++) {
|
|
328
|
+
if (typeof n.elts[i] === "object") {
|
|
329
|
+
n.elts[i] = this.internPattern(n.elts[i]);
|
|
330
|
+
}
|
|
331
|
+
elts += n.elts[i];
|
|
332
|
+
}
|
|
333
|
+
const key = tag+count+elts;
|
|
334
|
+
let nid = nodeMap[key];
|
|
335
|
+
if (nid === void 0) {
|
|
336
|
+
nodePool.push({tag: tag, elts: n.elts});
|
|
337
|
+
nid = nodePool.length - 1;
|
|
338
|
+
nodeMap[key] = nid;
|
|
339
|
+
if (n.coord) {
|
|
340
|
+
ctx.state.coords[nid] = n.coord;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
return nid;
|
|
344
|
+
}
|
|
345
|
+
match(options, patterns, node) {
|
|
346
|
+
if (patterns.size === 0 || node === undefined) {
|
|
347
|
+
return false;
|
|
348
|
+
}
|
|
349
|
+
let matches = patterns.filter((pattern) => {
|
|
350
|
+
if (pattern.tag === undefined || node.tag === undefined) {
|
|
351
|
+
return false;
|
|
352
|
+
}
|
|
353
|
+
const patternNid = this.internPattern(pattern);
|
|
354
|
+
if (patternNid === this.internPattern(node) ||
|
|
355
|
+
patternNid === this.internPattern(newNode('IDENT', ['_']))) {
|
|
356
|
+
return true;
|
|
357
|
+
}
|
|
358
|
+
if (pattern.tag === node.tag) {
|
|
359
|
+
if (pattern.elts.length === node.elts.length) {
|
|
360
|
+
// Same number of args, so see if each matches.
|
|
361
|
+
return pattern.elts.every((arg, i) => {
|
|
362
|
+
if (pattern.tag === 'VAR') {
|
|
363
|
+
if (arg === node.elts[i]) {
|
|
364
|
+
return true;
|
|
365
|
+
}
|
|
366
|
+
return false;
|
|
367
|
+
}
|
|
368
|
+
let result = this.match(options, [arg], node.elts[i]);
|
|
369
|
+
return result.length === 1;
|
|
370
|
+
});
|
|
371
|
+
} else if (pattern.elts.length < node.elts.length) {
|
|
372
|
+
// Different number of args, then see if there is a wildcard match.
|
|
373
|
+
let nargs = node.elts.slice(1);
|
|
374
|
+
if (pattern.elts.length === 2) {
|
|
375
|
+
// Binary node pattern
|
|
376
|
+
let result = (
|
|
377
|
+
this.match(options, [pattern.elts[0]], node.elts[0]).length > 0 &&
|
|
378
|
+
this.match(options, [pattern.elts[1]], newNode(node.tag, nargs)).length > 0
|
|
379
|
+
// Match rest of the node against the second pattern argument.
|
|
380
|
+
);
|
|
381
|
+
return result;
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
return false;
|
|
386
|
+
});
|
|
387
|
+
// if (true || matches.length > 0) {
|
|
388
|
+
// console.log("match() node: " + JSON.stringify(node, null, 2));
|
|
389
|
+
// console.log("match() matches: " + JSON.stringify(matches, null, 2));
|
|
390
|
+
// }
|
|
391
|
+
return matches;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
|
|
271
395
|
PROG(node, options, resume) {
|
|
272
396
|
if (!options) {
|
|
273
397
|
options = {};
|
|
@@ -319,7 +443,7 @@ export class Transformer extends Visitor {
|
|
|
319
443
|
// });
|
|
320
444
|
}
|
|
321
445
|
});
|
|
322
|
-
this.visit(node.elts[1], options,
|
|
446
|
+
this.visit(node.elts[1], options, (err, val) => {
|
|
323
447
|
exitEnv(options);
|
|
324
448
|
resume([].concat(err0).concat(err).concat(err), val)
|
|
325
449
|
});
|
|
@@ -374,6 +498,11 @@ export class Transformer extends Visitor {
|
|
|
374
498
|
const val = node;
|
|
375
499
|
resume(err, val);
|
|
376
500
|
}
|
|
501
|
+
NULL(node, options, resume) {
|
|
502
|
+
const err = [];
|
|
503
|
+
const val = null;
|
|
504
|
+
resume(err, val);
|
|
505
|
+
}
|
|
377
506
|
BINDING(node, options, resume) {
|
|
378
507
|
const err = [];
|
|
379
508
|
const val = node;
|
|
@@ -403,8 +532,8 @@ export class Transformer extends Visitor {
|
|
|
403
532
|
}
|
|
404
533
|
}
|
|
405
534
|
MUL(node, options, resume) {
|
|
406
|
-
this.visit(node.elts[0], options,
|
|
407
|
-
this.visit(node.elts[1], options,
|
|
535
|
+
this.visit(node.elts[0], options, (err1, val1) => {
|
|
536
|
+
this.visit(node.elts[1], options, (err2, val2) => {
|
|
408
537
|
if (isNaN(+val1)) {
|
|
409
538
|
err1 = err1.concat(error("Argument must be a number.", node.elts[0]));
|
|
410
539
|
}
|
|
@@ -412,14 +541,14 @@ export class Transformer extends Visitor {
|
|
|
412
541
|
err2 = err2.concat(error("Argument must be a number.", node.elts[1]));
|
|
413
542
|
}
|
|
414
543
|
const err = [].concat(err1).concat(err2);
|
|
415
|
-
const val =
|
|
544
|
+
const val = +val1 * +val2;
|
|
416
545
|
resume(err, val);
|
|
417
546
|
});
|
|
418
547
|
});
|
|
419
548
|
}
|
|
420
549
|
POW(node, options, resume) {
|
|
421
|
-
this.visit(node.elts[0], options,
|
|
422
|
-
this.visit(node.elts[1], options,
|
|
550
|
+
this.visit(node.elts[0], options, (err1, val1) => {
|
|
551
|
+
this.visit(node.elts[1], options, (err2, val2) => {
|
|
423
552
|
if (isNaN(+val1)) {
|
|
424
553
|
err1 = err1.concat(error("Argument must be a number.", node.elts[0]));
|
|
425
554
|
}
|
|
@@ -464,7 +593,7 @@ export class Transformer extends Visitor {
|
|
|
464
593
|
resume(err, val);
|
|
465
594
|
} else {
|
|
466
595
|
// Otherwise, use the default data.
|
|
467
|
-
this.visit(node.elts[0], options,
|
|
596
|
+
this.visit(node.elts[0], options, (e0, v0) => {
|
|
468
597
|
const err = e0;
|
|
469
598
|
const val = v0;
|
|
470
599
|
resume(err, val);
|
|
@@ -472,7 +601,7 @@ export class Transformer extends Visitor {
|
|
|
472
601
|
}
|
|
473
602
|
}
|
|
474
603
|
PAREN(node, options, resume) {
|
|
475
|
-
this.visit(node.elts[0], options,
|
|
604
|
+
this.visit(node.elts[0], options, (e0, v0) => {
|
|
476
605
|
const err = [].concat(e0);
|
|
477
606
|
const val = v0;
|
|
478
607
|
resume(err, val);
|
|
@@ -515,11 +644,14 @@ export class Transformer extends Visitor {
|
|
|
515
644
|
CASE(node, options, resume) {
|
|
516
645
|
// FIXME this isn't ASYNC compatible
|
|
517
646
|
options.SYNC = true;
|
|
518
|
-
this.visit(node.elts[0], options, (err,
|
|
647
|
+
this.visit(node.elts[0], options, (err, e0) => {
|
|
648
|
+
const e0Node = this.node(node.elts[0]);
|
|
649
|
+
const expr = (e0Node.tag === 'NUM' || e0Node.tag === 'NUM') && e0Node || {tag: 'STR', elts: [`${e0}`]};
|
|
519
650
|
let foundMatch = false;
|
|
651
|
+
const patterns = [];
|
|
520
652
|
for (var i = 1; i < node.elts.length; i++) {
|
|
521
653
|
this.visit(node.elts[i], options, (err, val) => {
|
|
522
|
-
if (
|
|
654
|
+
if (this.match(options, [this.node(node.elts[i]).elts[0]], expr).length) {
|
|
523
655
|
this.visit(val.exprElt, options, resume);
|
|
524
656
|
foundMatch = true;
|
|
525
657
|
}
|
|
@@ -535,7 +667,7 @@ export class Transformer extends Visitor {
|
|
|
535
667
|
options.SYNC = false;
|
|
536
668
|
}
|
|
537
669
|
OF(node, options, resume) {
|
|
538
|
-
this.visit(node.elts[0], options,
|
|
670
|
+
this.visit(node.elts[0], options, (err0, pattern) => {
|
|
539
671
|
resume([].concat(err0), {
|
|
540
672
|
pattern: pattern,
|
|
541
673
|
exprElt: node.elts[1],
|