@graffiticode/basis 1.5.19 → 1.5.21

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,7 +1,7 @@
1
1
  {
2
2
  "name": "@graffiticode/basis",
3
3
  "type": "module",
4
- "version": "1.5.19",
4
+ "version": "1.5.21",
5
5
  "description": "The basis library for creating Graffiticode languages",
6
6
  "main": "index.js",
7
7
  "scripts": {
package/spec/spec.html CHANGED
@@ -64,6 +64,10 @@
64
64
  <li><a href="#sec-isEmpty"><span class="spec-secid">5.2.16</span>isEmpty</a></li>
65
65
  <li><a href="#sec-get"><span class="spec-secid">5.2.17</span>get</a></li>
66
66
  <li><a href="#sec-set"><span class="spec-secid">5.2.18</span>set</a></li>
67
+ <li><a href="#sec-not"><span class="spec-secid">5.2.19</span>not</a></li>
68
+ <li><a href="#sec-equiv"><span class="spec-secid">5.2.20</span>equiv</a></li>
69
+ <li><a href="#sec-or"><span class="spec-secid">5.2.21</span>or</a></li>
70
+ <li><a href="#sec-and"><span class="spec-secid">5.2.22</span>and</a></li>
67
71
  </ol>
68
72
  </li>
69
73
  </ol>
@@ -278,8 +282,8 @@ end
278
282
  </tr>
279
283
  <tr>
280
284
  <td align="left"><code>reduce</code></td>
281
- <td align="left"><code>&lt;function list: any&gt;</code></td>
282
- <td align="left">Combines list using a reducer</td>
285
+ <td align="left"><code>&lt;function any list: any&gt;</code></td>
286
+ <td align="left">Combines list using a reducer with initial value</td>
283
287
  </tr>
284
288
  <tr>
285
289
  <td align="left"><code>hd</code></td>
@@ -316,6 +320,26 @@ end
316
320
  <td align="left"><code>&lt;string any record: record&gt;</code></td>
317
321
  <td align="left">Returns a new record with a key set to a value</td>
318
322
  </tr>
323
+ <tr>
324
+ <td align="left"><code>not</code></td>
325
+ <td align="left"><code>&lt;bool: bool&gt;</code></td>
326
+ <td align="left">Logical NOT operation, inverts a boolean value</td>
327
+ </tr>
328
+ <tr>
329
+ <td align="left"><code>equiv</code></td>
330
+ <td align="left"><code>&lt;any any: bool&gt;</code></td>
331
+ <td align="left">Tests if two values are strictly equivalent</td>
332
+ </tr>
333
+ <tr>
334
+ <td align="left"><code>or</code></td>
335
+ <td align="left"><code>&lt;bool bool: bool&gt;</code></td>
336
+ <td align="left">Logical OR operation</td>
337
+ </tr>
338
+ <tr>
339
+ <td align="left"><code>and</code></td>
340
+ <td align="left"><code>&lt;bool bool: bool&gt;</code></td>
341
+ <td align="left">Logical AND operation</td>
342
+ </tr>
319
343
  </tbody>
320
344
  </table>
321
345
  <section id="sec-add" secid="5.2.1">
@@ -426,6 +450,41 @@ end
426
450
  <pre><code>set "a" 2 {a: 1} | returns {a: 2}
427
451
  </code></pre>
428
452
  </section>
453
+ <section id="sec-not" secid="5.2.19">
454
+ <h3><span class="spec-secid" title="link to this section"><a href="#sec-not">5.2.19</a></span>not</h3>
455
+ <p>Logical NOT that inverts a boolean value</p>
456
+ <pre><code>not true | returns false
457
+ not false | returns true
458
+ </code></pre>
459
+ </section>
460
+ <section id="sec-equiv" secid="5.2.20">
461
+ <h3><span class="spec-secid" title="link to this section"><a href="#sec-equiv">5.2.20</a></span>equiv</h3>
462
+ <p>Tests if two values are strictly equivalent</p>
463
+ <pre><code>equiv 1 1 | returns true
464
+ equiv "a" "a" | returns true
465
+ equiv true true | returns true
466
+ equiv 1 2 | returns false
467
+ equiv "a" "b" | returns false
468
+ </code></pre>
469
+ </section>
470
+ <section id="sec-or" secid="5.2.21">
471
+ <h3><span class="spec-secid" title="link to this section"><a href="#sec-or">5.2.21</a></span>or</h3>
472
+ <p>Logical OR operation</p>
473
+ <pre><code>or false false | returns false
474
+ or false true | returns true
475
+ or true false | returns true
476
+ or true true | returns true
477
+ </code></pre>
478
+ </section>
479
+ <section id="sec-and" secid="5.2.22">
480
+ <h3><span class="spec-secid" title="link to this section"><a href="#sec-and">5.2.22</a></span>and</h3>
481
+ <p>Logical AND operation</p>
482
+ <pre><code>and false false | returns false
483
+ and false true | returns false
484
+ and true false | returns false
485
+ and true true | returns true
486
+ </code></pre>
487
+ </section>
429
488
  </section>
430
489
  </section>
431
490
  <section id="sec-Program-Examples" secid="6">
@@ -518,6 +577,10 @@ Written in <a href="https://spec-md.com" target="_blank">Spec Markdown</a>.</foo
518
577
  <li id="_sidebar_5.2.16"><a href="#sec-isEmpty"><span class="spec-secid">5.2.16</span>isEmpty</a></li>
519
578
  <li id="_sidebar_5.2.17"><a href="#sec-get"><span class="spec-secid">5.2.17</span>get</a></li>
520
579
  <li id="_sidebar_5.2.18"><a href="#sec-set"><span class="spec-secid">5.2.18</span>set</a></li>
580
+ <li id="_sidebar_5.2.19"><a href="#sec-not"><span class="spec-secid">5.2.19</span>not</a></li>
581
+ <li id="_sidebar_5.2.20"><a href="#sec-equiv"><span class="spec-secid">5.2.20</span>equiv</a></li>
582
+ <li id="_sidebar_5.2.21"><a href="#sec-or"><span class="spec-secid">5.2.21</span>or</a></li>
583
+ <li id="_sidebar_5.2.22"><a href="#sec-and"><span class="spec-secid">5.2.22</span>and</a></li>
521
584
  </ol>
522
585
  </li>
523
586
  </ol>
package/spec/spec.md CHANGED
@@ -169,7 +169,7 @@ This approach draws inspiration from **Model-View-Update** (MVU) architectures,
169
169
  | `range` | `<number number number: list>` | Generates a range list |
170
170
  | `map` | `<function list: list>` | Applies function to each item |
171
171
  | `filter` | `<function list: list>` | Keeps items matching predicate |
172
- | `reduce` | `<function list: any>` | Combines list using a reducer |
172
+ | `reduce` | `<function any list: any>` | Combines list using a reducer with initial value |
173
173
  | `hd` | `<list: any>` | First item of list |
174
174
  | `tl` | `<list: list>` | All items except first |
175
175
  | `nth` | `<number list: any>` | Nth element of list |
@@ -177,6 +177,10 @@ This approach draws inspiration from **Model-View-Update** (MVU) architectures,
177
177
  | `isEmpty` | `<list: bool>` | Returns true if the list is empty |
178
178
  | `get` | `<string record: any>` | Retrieves a value from a record by key |
179
179
  | `set` | `<string any record: record>` | Returns a new record with a key set to a value |
180
+ | `not` | `<bool: bool>` | Logical NOT operation, inverts a boolean value |
181
+ | `equiv` | `<any any: bool>` | Tests if two values are strictly equivalent |
182
+ | `or` | `<bool bool: bool>` | Logical OR operation |
183
+ | `and` | `<bool bool: bool>` | Logical AND operation |
180
184
 
181
185
  ### add
182
186
 
@@ -322,6 +326,49 @@ Return a new record with an updated field
322
326
  set "a" 2 {a: 1} | returns {a: 2}
323
327
  ```
324
328
 
329
+ ### not
330
+
331
+ Logical NOT that inverts a boolean value
332
+
333
+ ```
334
+ not true | returns false
335
+ not false | returns true
336
+ ```
337
+
338
+ ### equiv
339
+
340
+ Tests if two values are strictly equivalent
341
+
342
+ ```
343
+ equiv 1 1 | returns true
344
+ equiv "a" "a" | returns true
345
+ equiv true true | returns true
346
+ equiv 1 2 | returns false
347
+ equiv "a" "b" | returns false
348
+ ```
349
+
350
+ ### or
351
+
352
+ Logical OR operation
353
+
354
+ ```
355
+ or false false | returns false
356
+ or false true | returns true
357
+ or true false | returns true
358
+ or true true | returns true
359
+ ```
360
+
361
+ ### and
362
+
363
+ Logical AND operation
364
+
365
+ ```
366
+ and false false | returns false
367
+ and false true | returns false
368
+ and true false | returns false
369
+ and true true | returns true
370
+ ```
371
+
325
372
  # Program Examples
326
373
 
327
374
  ```
package/src/compiler.js CHANGED
@@ -27,7 +27,7 @@ class Visitor {
27
27
  }
28
28
  visit(nid, options, resume) {
29
29
  try {
30
- assert(nid);
30
+ assert(nid, "Invalid nid=" + nid);
31
31
  let node;
32
32
  if (typeof nid === "object") {
33
33
  node = nid;
@@ -425,6 +425,86 @@ export class Checker extends Visitor {
425
425
  });
426
426
  });
427
427
  }
428
+ NOT(node, options, resume) {
429
+ this.visit(node.elts[0], options, (err1, val1) => {
430
+ let err = [].concat(err1);
431
+ if (typeof val1 !== "boolean" && val1 !== null && val1 !== undefined && val1 !== 0 && val1 !== "" && val1 !== false) {
432
+ err.push(`NOT operation requires a boolean argument, got ${typeof val1}`);
433
+ }
434
+ const val = node;
435
+ resume(err, val);
436
+ });
437
+ }
438
+ EQUIV(node, options, resume) {
439
+ this.visit(node.elts[0], options, (err1, val1) => {
440
+ this.visit(node.elts[1], options, (err2, val2) => {
441
+ let err = [].concat(err1).concat(err2);
442
+ const validTypes = ["boolean", "string", "number"];
443
+ if (!validTypes.includes(typeof val1) && val1 !== null) {
444
+ err.push(`EQUIV operation requires primitive arguments, got ${typeof val1} for first argument`);
445
+ }
446
+ if (!validTypes.includes(typeof val2) && val2 !== null) {
447
+ err.push(`EQUIV operation requires primitive arguments, got ${typeof val2} for second argument`);
448
+ }
449
+ const val = node;
450
+ resume(err, val);
451
+ });
452
+ });
453
+ }
454
+ OR(node, options, resume) {
455
+ this.visit(node.elts[0], options, (err1, val1) => {
456
+ this.visit(node.elts[1], options, (err2, val2) => {
457
+ let err = [].concat(err1).concat(err2);
458
+ if (typeof val1 !== "boolean" && val1 !== null && val1 !== undefined && val1 !== 0 && val1 !== "" && val1 !== false) {
459
+ err.push(`OR operation requires boolean arguments, got ${typeof val1} for first argument`);
460
+ }
461
+ if (typeof val2 !== "boolean" && val2 !== null && val2 !== undefined && val2 !== 0 && val2 !== "" && val2 !== false) {
462
+ err.push(`OR operation requires boolean arguments, got ${typeof val2} for second argument`);
463
+ }
464
+ const val = node;
465
+ resume(err, val);
466
+ });
467
+ });
468
+ }
469
+ AND(node, options, resume) {
470
+ this.visit(node.elts[0], options, (err1, val1) => {
471
+ this.visit(node.elts[1], options, (err2, val2) => {
472
+ let err = [].concat(err1).concat(err2);
473
+ if (typeof val1 !== "boolean" && val1 !== null && val1 !== undefined && val1 !== 0 && val1 !== "" && val1 !== false) {
474
+ err.push(`AND operation requires boolean arguments, got ${typeof val1} for first argument`);
475
+ }
476
+ if (typeof val2 !== "boolean" && val2 !== null && val2 !== undefined && val2 !== 0 && val2 !== "" && val2 !== false) {
477
+ err.push(`AND operation requires boolean arguments, got ${typeof val2} for second argument`);
478
+ }
479
+ const val = node;
480
+ resume(err, val);
481
+ });
482
+ });
483
+ }
484
+ HD(node, options, resume) {
485
+ this.visit(node.elts[0], options, (err1, val1) => {
486
+ let err = [].concat(err1);
487
+ // if (!Array.isArray(val1)) {
488
+ // err.push(`HD operation requires a list argument, got ${typeof val1}`);
489
+ // } else if (val1.length === 0) {
490
+ // err.push(`HD operation called on an empty list`);
491
+ // }
492
+ const val = node;
493
+ resume(err, val);
494
+ });
495
+ }
496
+ TL(node, options, resume) {
497
+ this.visit(node.elts[0], options, (err1, val1) => {
498
+ let err = [].concat(err1);
499
+ // if (!Array.isArray(val1)) {
500
+ // err.push(`TL operation requires a list argument, got ${typeof val1}`);
501
+ // } else if (val1.length === 0) {
502
+ // err.push(`TL operation called on an empty list`);
503
+ // }
504
+ const val = node;
505
+ resume(err, val);
506
+ });
507
+ }
428
508
  }
429
509
 
430
510
  function enterEnv(ctx, name, paramc) {
@@ -724,7 +804,6 @@ export class Transformer extends Visitor {
724
804
  }
725
805
  BINDING(node, options, resume) {
726
806
  const err = [];
727
- const val = node;
728
807
  this.visit(node.elts[0], options, (err1, val1) => {
729
808
  this.visit(node.elts[1], options, (err2, val2) => {
730
809
  resume([].concat(err1).concat(err2), {key: val1, val: val2});
@@ -825,9 +904,11 @@ export class Transformer extends Visitor {
825
904
  resume(err, val);
826
905
  }
827
906
  LEN(node, options, resume) {
828
- const err = [];
829
- const val = node;
830
- resume(err, val);
907
+ this.visit(node.elts[0], options, (e0, v0) => {
908
+ const err = e0;
909
+ const val = Array.isArray(v0) && v0.length;
910
+ resume(err, val);
911
+ });
831
912
  }
832
913
  ARG(node, options, resume) {
833
914
  const err = [];
@@ -869,11 +950,8 @@ export class Transformer extends Visitor {
869
950
  this.visit(node.elts[1], options, (e1, v1) => {
870
951
  let err = [];
871
952
  let val = [];
872
- console.log(
873
- "MAP()",
874
- "v1=" + JSON.stringify(v1),
875
- );
876
953
  v1.forEach(args => {
954
+ options.SYNC = true;
877
955
  options.args = args;
878
956
  options = JSON.parse(JSON.stringify(options)); // Copy option arg support async.
879
957
  this.visit(node.elts[0], options, (e0, v0) => {
@@ -921,6 +999,7 @@ export class Transformer extends Visitor {
921
999
  let err = [];
922
1000
  let val = v1;
923
1001
  v2.forEach((args, index) => {
1002
+ options.SYNC = true;
924
1003
  options.args = [val, args];
925
1004
  options = JSON.parse(JSON.stringify(options)); // Copy option arg support async.
926
1005
  this.visit(node.elts[0], options, (e0, v0) => {
@@ -1150,7 +1229,6 @@ export class Transformer extends Visitor {
1150
1229
  const start = new Decimal(v0);
1151
1230
  const end = new Decimal(v1);
1152
1231
  const step = new Decimal(v2);
1153
-
1154
1232
  if (step.isZero()) {
1155
1233
  resume([...err, 'Error in RANGE operation: step cannot be zero'], []);
1156
1234
  return;
@@ -1176,6 +1254,115 @@ export class Transformer extends Visitor {
1176
1254
  });
1177
1255
  });
1178
1256
  }
1257
+ NOT(node, options, resume) {
1258
+ this.visit(node.elts[0], options, (e0, v0) => {
1259
+ const err = [].concat(e0);
1260
+ try {
1261
+ // Handle various falsy values explicitly
1262
+ if (v0 === null || v0 === undefined || v0 === 0 || v0 === "" || v0 === false) {
1263
+ resume(err, true);
1264
+ } else {
1265
+ resume(err, !v0);
1266
+ }
1267
+ } catch (e) {
1268
+ resume([...err, `Error in NOT operation: ${e.message}`], false);
1269
+ }
1270
+ });
1271
+ }
1272
+ EQUIV(node, options, resume) {
1273
+ this.visit(node.elts[0], options, (e0, v0) => {
1274
+ this.visit(node.elts[1], options, (e1, v1) => {
1275
+ const err = [].concat(e0).concat(e1);
1276
+ try {
1277
+ // Use strict equality for primitive comparison
1278
+ const val = v0 === v1;
1279
+ resume(err, val);
1280
+ } catch (e) {
1281
+ resume([...err, `Error in EQUIV operation: ${e.message}`], false);
1282
+ }
1283
+ });
1284
+ });
1285
+ }
1286
+ OR(node, options, resume) {
1287
+ this.visit(node.elts[0], options, (e0, v0) => {
1288
+ // Short-circuit evaluation - if first argument is truthy, return true immediately
1289
+ if (v0) {
1290
+ resume(e0, true);
1291
+ return;
1292
+ }
1293
+
1294
+ this.visit(node.elts[1], options, (e1, v1) => {
1295
+ const err = [].concat(e0).concat(e1);
1296
+ try {
1297
+ // Standard boolean OR operation
1298
+ const val = Boolean(v0) || Boolean(v1);
1299
+ resume(err, val);
1300
+ } catch (e) {
1301
+ resume([...err, `Error in OR operation: ${e.message}`], false);
1302
+ }
1303
+ });
1304
+ });
1305
+ }
1306
+ AND(node, options, resume) {
1307
+ this.visit(node.elts[0], options, (e0, v0) => {
1308
+ // Short-circuit evaluation - if first argument is falsy, return false immediately
1309
+ if (!v0) {
1310
+ resume(e0, false);
1311
+ return;
1312
+ }
1313
+
1314
+ this.visit(node.elts[1], options, (e1, v1) => {
1315
+ const err = [].concat(e0).concat(e1);
1316
+ try {
1317
+ // Standard boolean AND operation
1318
+ const val = Boolean(v0) && Boolean(v1);
1319
+ resume(err, val);
1320
+ } catch (e) {
1321
+ resume([...err, `Error in AND operation: ${e.message}`], false);
1322
+ }
1323
+ });
1324
+ });
1325
+ }
1326
+ HD(node, options, resume) {
1327
+ this.visit(node.elts[0], options, (e0, v0) => {
1328
+ const err = [].concat(e0);
1329
+ try {
1330
+ if (!Array.isArray(v0)) {
1331
+ resume([...err, `Error in HD operation: expected an array, got ${typeof v0}`], null);
1332
+ return;
1333
+ }
1334
+ if (v0.length === 0) {
1335
+ resume([...err, `Error in HD operation: empty array has no head`], null);
1336
+ return;
1337
+ }
1338
+ // Return the first element of the array
1339
+ const val = v0[0];
1340
+ resume(err, val);
1341
+ } catch (e) {
1342
+ resume([...err, `Error in HD operation: ${e.message}`], null);
1343
+ }
1344
+ });
1345
+ }
1346
+ TL(node, options, resume) {
1347
+ this.visit(node.elts[0], options, (e0, v0) => {
1348
+ const err = [].concat(e0);
1349
+ try {
1350
+ if (!Array.isArray(v0)) {
1351
+ resume([...err, `Error in TL operation: expected an array, got ${typeof v0}`], []);
1352
+ return;
1353
+ }
1354
+ if (v0.length === 0) {
1355
+ resume([...err, `Error in TL operation: empty array has no tail`], []);
1356
+ return;
1357
+ }
1358
+ // Return all elements except the first
1359
+ const val = v0.slice(1);
1360
+ resume(err, val);
1361
+ } catch (e) {
1362
+ resume([...err, `Error in TL operation: ${e.message}`], []);
1363
+ }
1364
+ });
1365
+ }
1179
1366
  }
1180
1367
 
1181
1368
  export class Renderer {
package/src/lexicon.js CHANGED
@@ -12,7 +12,6 @@ export const lexicon = {
12
12
  "cls": "function",
13
13
  "length": 2,
14
14
  "arity": 2,
15
- "args": ["key", "record"]
16
15
  },
17
16
  "set" : {
18
17
  "tk": 1,
@@ -20,7 +19,6 @@ export const lexicon = {
20
19
  "cls": "function",
21
20
  "length": 3,
22
21
  "arity": 3,
23
- "args": ["key", "value", "record"]
24
22
  },
25
23
  "nth" : {
26
24
  "tk": 1,
@@ -189,5 +187,47 @@ export const lexicon = {
189
187
  "cls": "function",
190
188
  "length": 3,
191
189
  "arity": 3
190
+ },
191
+ "not" : {
192
+ "tk": 1,
193
+ "name": "NOT",
194
+ "cls": "function",
195
+ "length": 1,
196
+ "arity": 1
197
+ },
198
+ "equiv" : {
199
+ "tk": 1,
200
+ "name": "EQUIV",
201
+ "cls": "function",
202
+ "length": 2,
203
+ "arity": 2
204
+ },
205
+ "or" : {
206
+ "tk": 1,
207
+ "name": "OR",
208
+ "cls": "function",
209
+ "length": 2,
210
+ "arity": 2
211
+ },
212
+ "and" : {
213
+ "tk": 1,
214
+ "name": "AND",
215
+ "cls": "function",
216
+ "length": 2,
217
+ "arity": 2
218
+ },
219
+ "hd" : {
220
+ "tk": 1,
221
+ "name": "HD",
222
+ "cls": "function",
223
+ "length": 1,
224
+ "arity": 1
225
+ },
226
+ "tl" : {
227
+ "tk": 1,
228
+ "name": "TL",
229
+ "cls": "function",
230
+ "length": 1,
231
+ "arity": 1
192
232
  }
193
233
  }