@graffiticode/basis 1.0.9 → 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 +161 -26
- package/src/parse.js +2426 -0
- package/src/style.css +1 -597
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
|
}
|
|
@@ -433,9 +562,13 @@ export class Transformer extends Visitor {
|
|
|
433
562
|
});
|
|
434
563
|
}
|
|
435
564
|
VAL(node, options, resume) {
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
565
|
+
this.visit(node.elts[0], options, (e0, v0) => {
|
|
566
|
+
this.visit(node.elts[1], options, (e1, v1) => {
|
|
567
|
+
const err = [].concat(e0).concat(e1);
|
|
568
|
+
const val = v1[v0];
|
|
569
|
+
resume(err, val);
|
|
570
|
+
});
|
|
571
|
+
});
|
|
439
572
|
}
|
|
440
573
|
KEY(node, options, resume) {
|
|
441
574
|
const err = [];
|
|
@@ -460,7 +593,7 @@ export class Transformer extends Visitor {
|
|
|
460
593
|
resume(err, val);
|
|
461
594
|
} else {
|
|
462
595
|
// Otherwise, use the default data.
|
|
463
|
-
this.visit(node.elts[0], options,
|
|
596
|
+
this.visit(node.elts[0], options, (e0, v0) => {
|
|
464
597
|
const err = e0;
|
|
465
598
|
const val = v0;
|
|
466
599
|
resume(err, val);
|
|
@@ -468,7 +601,7 @@ export class Transformer extends Visitor {
|
|
|
468
601
|
}
|
|
469
602
|
}
|
|
470
603
|
PAREN(node, options, resume) {
|
|
471
|
-
this.visit(node.elts[0], options,
|
|
604
|
+
this.visit(node.elts[0], options, (e0, v0) => {
|
|
472
605
|
const err = [].concat(e0);
|
|
473
606
|
const val = v0;
|
|
474
607
|
resume(err, val);
|
|
@@ -511,11 +644,14 @@ export class Transformer extends Visitor {
|
|
|
511
644
|
CASE(node, options, resume) {
|
|
512
645
|
// FIXME this isn't ASYNC compatible
|
|
513
646
|
options.SYNC = true;
|
|
514
|
-
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}`]};
|
|
515
650
|
let foundMatch = false;
|
|
651
|
+
const patterns = [];
|
|
516
652
|
for (var i = 1; i < node.elts.length; i++) {
|
|
517
653
|
this.visit(node.elts[i], options, (err, val) => {
|
|
518
|
-
if (
|
|
654
|
+
if (this.match(options, [this.node(node.elts[i]).elts[0]], expr).length) {
|
|
519
655
|
this.visit(val.exprElt, options, resume);
|
|
520
656
|
foundMatch = true;
|
|
521
657
|
}
|
|
@@ -531,7 +667,7 @@ export class Transformer extends Visitor {
|
|
|
531
667
|
options.SYNC = false;
|
|
532
668
|
}
|
|
533
669
|
OF(node, options, resume) {
|
|
534
|
-
this.visit(node.elts[0], options,
|
|
670
|
+
this.visit(node.elts[0], options, (err0, pattern) => {
|
|
535
671
|
resume([].concat(err0), {
|
|
536
672
|
pattern: pattern,
|
|
537
673
|
exprElt: node.elts[1],
|
|
@@ -579,7 +715,6 @@ export class Compiler {
|
|
|
579
715
|
} else {
|
|
580
716
|
const renderer = new this.Renderer(val);
|
|
581
717
|
renderer.render(options, (err, val) => {
|
|
582
|
-
val = !(val instanceof Array) && [val] || val;
|
|
583
718
|
resume(err, val);
|
|
584
719
|
});
|
|
585
720
|
}
|