@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 CHANGED
@@ -1,10 +1,12 @@
1
1
  {
2
2
  "name": "@graffiticode/basis",
3
3
  "type": "module",
4
- "version": "1.0.9",
4
+ "version": "1.0.12",
5
5
  "description": "The basis library for creating Graffiticode languages",
6
- "main": "compiler.js",
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(nodePool) {
13
- this.nodePool = 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.nodePool.root;
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, function (err1, val1) {
106
- this.visit(node.elts[1], options, function (err2, val2) {
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, function (err1, val1) {
136
- this.visit(node.elts[1], options, function (err2, val2) {
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, function (err1, val1) {
151
- this.visit(node.elts[1], options, function (err2, val2) {
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.nodePool.root;
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, function (err, val) {
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, function (err1, val1) {
407
- this.visit(node.elts[1], options, function (err2, val2) {
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 = node;
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, function (err1, val1) {
422
- this.visit(node.elts[1], options, function (err2, val2) {
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
- const err = [];
437
- const val = node;
438
- resume(err, val);
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, function (e0, v0) {
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, function (e0, v0) {
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, expr) => {
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 (expr === val.pattern) {
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, function (err0, pattern) {
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
  }