@weborigami/language 0.3.3-jse.2 → 0.3.3

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.
@@ -204,8 +204,7 @@ export function makeCall(target, args) {
204
204
  }
205
205
 
206
206
  let fnCall;
207
- const op = args[0];
208
- if (op === ops.traverse || op === ops.optionalTraverse) {
207
+ if (args[0] === ops.traverse) {
209
208
  let tree = target;
210
209
 
211
210
  if (tree[0] === undetermined) {
@@ -220,12 +219,12 @@ export function makeCall(target, args) {
220
219
  if (args.length > 1) {
221
220
  // Regular traverse
222
221
  const keys = args.slice(1);
223
- fnCall = [op, tree, ...keys];
222
+ fnCall = [ops.traverse, tree, ...keys];
224
223
  } else {
225
224
  // Traverse without arguments equates to unpack
226
225
  fnCall = [ops.unpack, tree];
227
226
  }
228
- } else if (op === ops.templateStandard || op === ops.templateTree) {
227
+ } else if (args[0] === ops.template) {
229
228
  // Tagged template
230
229
  const strings = args[1];
231
230
  const values = args.slice(2);
@@ -276,15 +275,6 @@ export function makeDeferredArguments(args) {
276
275
  });
277
276
  }
278
277
 
279
- export function makeJsPropertyAccess(expression, property) {
280
- const location = {
281
- source: expression.location.source,
282
- start: expression.location.start,
283
- end: property.location.end,
284
- };
285
- return annotate([expression, property], location);
286
- }
287
-
288
278
  /**
289
279
  * Make an object.
290
280
  *
@@ -374,6 +364,15 @@ export function makeProperty(key, value) {
374
364
  return [key, modified];
375
365
  }
376
366
 
367
+ export function makeJsPropertyAccess(expression, property) {
368
+ const location = {
369
+ source: expression.location.source,
370
+ start: expression.location.start,
371
+ end: property.location.end,
372
+ };
373
+ return annotate([expression, property], location);
374
+ }
375
+
377
376
  export function makeReference(identifier) {
378
377
  // We can't know for sure that an identifier is a builtin reference until we
379
378
  // see whether it's being called as a function.
@@ -74,12 +74,9 @@ export async function handleExtension(parent, value, key) {
74
74
  key = trailingSlash.remove(key);
75
75
  }
76
76
 
77
- // Special cases: `.ori.<ext>` extensions are Origami documents,
78
- // `.jse.<ext>` are JSE documents.
77
+ // Special case: `.ori.<ext>` extensions are Origami documents.
79
78
  const extname = key.match(/\.ori\.\S+$/)
80
79
  ? ".oridocument"
81
- : key.match(/\.jse\.\S+$/)
82
- ? ".jsedocument"
83
80
  : extension.extname(key);
84
81
  if (extname) {
85
82
  const handler = await getExtensionHandler(parent, extname);
@@ -12,17 +12,16 @@ import {
12
12
  isUnpackable,
13
13
  scope as scopeFn,
14
14
  setParent,
15
- text as templateFunctionTree,
15
+ concat as treeConcat,
16
16
  } from "@weborigami/async-tree";
17
17
  import os from "node:os";
18
+ import taggedTemplateIndent from "../../src/runtime/taggedTemplateIndent.js";
18
19
  import { builtinReferenceError, scopeReferenceError } from "./errors.js";
19
20
  import expressionObject from "./expressionObject.js";
20
21
  import { evaluate } from "./internal.js";
21
22
  import mergeTrees from "./mergeTrees.js";
22
23
  import OrigamiFiles from "./OrigamiFiles.js";
23
24
  import { codeSymbol } from "./symbols.js";
24
- import templateFunctionIndent from "./templateIndent.js";
25
- import templateFunctionStandard from "./templateStandard.js";
26
25
 
27
26
  function addOpLabel(op, label) {
28
27
  Object.defineProperty(op, "toString", {
@@ -106,7 +105,7 @@ addOpLabel(comma, "«ops.comma»");
106
105
  * @param {any[]} args
107
106
  */
108
107
  export async function concat(...args) {
109
- return deepText.call(this, args);
108
+ return treeConcat.call(this, args);
110
109
  }
111
110
  addOpLabel(concat, "«ops.concat»");
112
111
 
@@ -115,13 +114,6 @@ export async function conditional(condition, truthy, falsy) {
115
114
  return value instanceof Function ? await value() : value;
116
115
  }
117
116
 
118
- export async function construct(constructor, ...args) {
119
- if (isUnpackable(constructor)) {
120
- constructor = await constructor.unpack();
121
- }
122
- return Reflect.construct(constructor, args);
123
- }
124
-
125
117
  /**
126
118
  * Construct a document object by invoking the body code (a lambda) and adding
127
119
  * the resulting text to the front data.
@@ -480,14 +472,6 @@ export async function object(...entries) {
480
472
  addOpLabel(object, "«ops.object»");
481
473
  object.unevaluatedArgs = true;
482
474
 
483
- export function optionalTraverse(treelike, key) {
484
- if (!treelike) {
485
- return undefined;
486
- }
487
- return Tree.traverseOrThrow(treelike, key);
488
- }
489
- addOpLabel(optionalTraverse, "«ops.optionalTraverse");
490
-
491
475
  export function remainder(a, b) {
492
476
  return a % b;
493
477
  }
@@ -562,29 +546,21 @@ export function subtraction(a, b) {
562
546
  }
563
547
  addOpLabel(subtraction, "«ops.subtraction»");
564
548
 
565
- /**
566
- * Apply the tree indent tagged template function.
567
- */
568
- export async function templateIndent(strings, ...values) {
569
- return templateFunctionIndent(strings, ...values);
570
- }
571
- addOpLabel(templateIndent, "«ops.templateIndent»");
572
-
573
549
  /**
574
550
  * Apply the default tagged template function.
575
551
  */
576
- export function templateStandard(strings, ...values) {
577
- return templateFunctionStandard(strings, ...values);
552
+ export async function template(strings, ...values) {
553
+ return deepText(strings, ...values);
578
554
  }
579
- addOpLabel(templateStandard, "«ops.templateStandard»");
555
+ addOpLabel(template, "«ops.template»");
580
556
 
581
557
  /**
582
- * Apply the tree tagged template function.
558
+ * Apply the tagged template indent function.
583
559
  */
584
- export async function templateTree(strings, ...values) {
585
- return templateFunctionTree(strings, ...values);
560
+ export async function templateIndent(strings, ...values) {
561
+ return taggedTemplateIndent(strings, ...values);
586
562
  }
587
- addOpLabel(templateTree, "«ops.templateTree»");
563
+ addOpLabel(templateIndent, "«ops.templateIndent");
588
564
 
589
565
  /**
590
566
  * Traverse a path of keys through a tree.
@@ -1,4 +1,4 @@
1
- import { deepText, toString, Tree } from "@weborigami/async-tree";
1
+ import { concat, toString, Tree } from "@weborigami/async-tree";
2
2
 
3
3
  const lastLineWhitespaceRegex = /\n(?<indent>[ \t]*)$/;
4
4
 
@@ -19,7 +19,7 @@ export default async function indent(strings, ...values) {
19
19
  }
20
20
  const { blockIndentations, strings: modifiedStrings } = modified;
21
21
  const valueTexts = await Promise.all(
22
- values.map((value) => (Tree.isTreelike(value) ? deepText(value) : value))
22
+ values.map((value) => (Tree.isTreelike(value) ? concat(value) : value))
23
23
  );
24
24
  return joinBlocks(modifiedStrings, valueTexts, blockIndentations);
25
25
  }
@@ -19,46 +19,6 @@ describe("Origami parser", () => {
19
19
  ]);
20
20
  });
21
21
 
22
- test("angleBracketLiteral", () => {
23
- assertParse(
24
- "angleBracketLiteral",
25
- "<index.html>",
26
- [ops.scope, "index.html"],
27
- "jse",
28
- false
29
- );
30
- assertParse(
31
- "angleBracketLiteral",
32
- "<foo/bar/baz>",
33
- [
34
- ops.traverse,
35
- [ops.scope, "foo/"],
36
- [ops.literal, "bar/"],
37
- [ops.literal, "baz"],
38
- ],
39
- "jse",
40
- false
41
- );
42
- assertParse("angleBracketLiteral", "<files:src/assets>", [
43
- ops.traverse,
44
- [
45
- [ops.builtin, "files:"],
46
- [ops.literal, "src/"],
47
- ],
48
- [ops.literal, "assets"],
49
- ]);
50
- assertParse(
51
- "angleBracketLiteral",
52
- "<https://example.com/>",
53
- [
54
- [ops.builtin, "https:"],
55
- [ops.literal, "example.com/"],
56
- ],
57
- "jse",
58
- false
59
- );
60
- });
61
-
62
22
  test("arrayLiteral", () => {
63
23
  assertParse("arrayLiteral", "[]", [ops.array]);
64
24
  assertParse("arrayLiteral", "[1, 2, 3]", [
@@ -439,8 +399,7 @@ Body`,
439
399
  ]);
440
400
 
441
401
  // Consecutive slashes at start of something = comment
442
- assertParse("expression", "x //comment", [ops.scope, "x"], "jse", false);
443
-
402
+ assertParse("expression", "path //comment", [ops.scope, "path"], false);
444
403
  assertParse("expression", "page.ori(mdHtml:(about.md))", [
445
404
  [ops.scope, "page.ori"],
446
405
  [
@@ -487,11 +446,7 @@ Body`,
487
446
  ]);
488
447
  assertParse("expression", "fn =`x`", [
489
448
  [ops.builtin, "fn"],
490
- [
491
- ops.lambda,
492
- [[ops.literal, "_"]],
493
- [ops.templateTree, [ops.literal, ["x"]]],
494
- ],
449
+ [ops.lambda, [[ops.literal, "_"]], [ops.template, [ops.literal, ["x"]]]],
495
450
  ]);
496
451
  assertParse("expression", "copy app.js(formulas), files:snapshot", [
497
452
  [ops.builtin, "copy"],
@@ -509,7 +464,7 @@ Body`,
509
464
  [
510
465
  ops.lambda,
511
466
  [[ops.literal, "_"]],
512
- [ops.templateTree, [ops.literal, ["<li>", "</li>"]], [ops.scope, "_"]],
467
+ [ops.template, [ops.literal, ["<li>", "</li>"]], [ops.scope, "_"]],
513
468
  ],
514
469
  ]);
515
470
  assertParse("expression", `https://example.com/about/`, [
@@ -568,23 +523,6 @@ Body`,
568
523
  ]);
569
524
  });
570
525
 
571
- test("frontMatterExpression", () => {
572
- assertParse(
573
- "frontMatterExpression",
574
- `---
575
- (name) => _template()
576
- ---
577
- `,
578
- [
579
- ops.lambda,
580
- [[ops.literal, "name"]],
581
- [[ops.scope, "_template"], undefined],
582
- ],
583
- "jse",
584
- false
585
- );
586
- });
587
-
588
526
  test("group", () => {
589
527
  assertParse("group", "(hello)", [ops.scope, "hello"]);
590
528
  assertParse("group", "(((nested)))", [ops.scope, "nested"]);
@@ -606,10 +544,10 @@ Body`,
606
544
  });
607
545
 
608
546
  test("identifier", () => {
609
- assertParse("identifier", "abc", "abc", "shell", false);
610
- assertParse("identifier", "index.html", "index.html", "shell", false);
611
- assertParse("identifier", "foo\\ bar", "foo bar", "shell", false);
612
- assertParse("identifier", "x-y-z", "x-y-z", "shell", false);
547
+ assertParse("identifier", "abc", "abc", false);
548
+ assertParse("identifier", "index.html", "index.html", false);
549
+ assertParse("identifier", "foo\\ bar", "foo bar", false);
550
+ assertParse("identifier", "x-y-z", "x-y-z", false);
613
551
  });
614
552
 
615
553
  test("implicitParenthesesCallExpression", () => {
@@ -648,8 +586,8 @@ Body`,
648
586
  });
649
587
 
650
588
  test("jsIdentifier", () => {
651
- assertParse("jsIdentifier", "foo", "foo", "jse", false);
652
- assertParse("jsIdentifier", "$Δelta", "$Δelta", "jse", false);
589
+ assertParse("jsIdentifier", "foo", "foo", false);
590
+ assertParse("jsIdentifier", "$Δelta", "$Δelta", false);
653
591
  assertThrows(
654
592
  "jsIdentifier",
655
593
  "1stCharacterIsNumber",
@@ -689,7 +627,6 @@ Body`,
689
627
  [ops.literal, 2],
690
628
  [ops.literal, 3],
691
629
  ]);
692
- assertThrows("list", "1\n2\n3", `but "\\n" found`, 0, "jse");
693
630
  assertParse("list", "'a' , 'b' , 'c'", [
694
631
  [ops.literal, "a"],
695
632
  [ops.literal, "b"],
@@ -725,13 +662,7 @@ Body`,
725
662
  });
726
663
 
727
664
  test("multiLineComment", () => {
728
- assertParse(
729
- "multiLineComment",
730
- "/*\nHello, world!\n*/",
731
- null,
732
- "jse",
733
- false
734
- );
665
+ assertParse("multiLineComment", "/*\nHello, world!\n*/", null, false);
735
666
  });
736
667
 
737
668
  test("multiplicativeExpression", () => {
@@ -756,13 +687,6 @@ Body`,
756
687
  assertParse("namespace", "js:", [ops.builtin, "js:"]);
757
688
  });
758
689
 
759
- test("newExpression", () => {
760
- assertParse("newExpression", "new Foo()", [
761
- ops.construct,
762
- [ops.scope, "Foo"],
763
- ]);
764
- });
765
-
766
690
  test("nullishCoalescingExpression", () => {
767
691
  assertParse("nullishCoalescingExpression", "a ?? b", [
768
692
  ops.nullishCoalescing,
@@ -915,16 +839,9 @@ Body`,
915
839
  });
916
840
 
917
841
  test("objectPublicKey", () => {
918
- assertParse("objectPublicKey", "a", "a", "jse", false);
919
- assertParse("objectPublicKey", "markdown/", "markdown/", "jse", false);
920
- assertParse("objectPublicKey", "foo\\ bar", "foo bar", "jse", false);
921
- });
922
-
923
- test("optionalChaining", () => {
924
- assertParse("optionalChaining", "?.key", [
925
- ops.optionalTraverse,
926
- [ops.literal, "key"],
927
- ]);
842
+ assertParse("objectPublicKey", "a", "a", false);
843
+ assertParse("objectPublicKey", "markdown/", "markdown/", false);
844
+ assertParse("objectPublicKey", "foo\\ bar", "foo bar", false);
928
845
  });
929
846
 
930
847
  test("parenthesesArguments", () => {
@@ -994,15 +911,6 @@ Body`,
994
911
  [ops.literal, 1],
995
912
  [ops.literal, 2],
996
913
  ]);
997
- // Only in JSE
998
- assertParse(
999
- "primary",
1000
- "<index.html>",
1001
- [ops.scope, "index.html"],
1002
- "jse",
1003
- false
1004
- );
1005
- assertThrows("primary", "<index.html>", `but "<" found`, 0, "shell");
1006
914
  });
1007
915
 
1008
916
  test("program", () => {
@@ -1012,7 +920,6 @@ Body`,
1012
920
  'Hello'
1013
921
  `,
1014
922
  [ops.literal, "Hello"],
1015
- "jse",
1016
923
  false
1017
924
  );
1018
925
  });
@@ -1057,10 +964,6 @@ Body`,
1057
964
  ]);
1058
965
  });
1059
966
 
1060
- test("regexLiteral", () => {
1061
- assertParse("regexLiteral", "/abc+/g", [ops.literal, /abc+/g]);
1062
- });
1063
-
1064
967
  test("relationalExpression", () => {
1065
968
  assertParse("relationalExpression", "1 < 2", [
1066
969
  ops.lessThan,
@@ -1122,7 +1025,7 @@ Body`,
1122
1025
  assertParse("shorthandFunction", "=`Hello, ${name}.`", [
1123
1026
  ops.lambda,
1124
1027
  [[ops.literal, "_"]],
1125
- [ops.templateTree, [ops.literal, ["Hello, ", "."]], [ops.scope, "name"]],
1028
+ [ops.template, [ops.literal, ["Hello, ", "."]], [ops.scope, "name"]],
1126
1029
  ]);
1127
1030
  assertParse("shorthandFunction", "=indent`hello`", [
1128
1031
  ops.lambda,
@@ -1135,7 +1038,7 @@ Body`,
1135
1038
  });
1136
1039
 
1137
1040
  test("singleLineComment", () => {
1138
- assertParse("singleLineComment", "// Hello, world!", null, "jse", false);
1041
+ assertParse("singleLineComment", "// Hello, world!", null, false);
1139
1042
  });
1140
1043
 
1141
1044
  test("spreadElement", () => {
@@ -1239,27 +1142,21 @@ Body text`,
1239
1142
 
1240
1143
  test("templateLiteral", () => {
1241
1144
  assertParse("templateLiteral", "`Hello, world.`", [
1242
- ops.templateTree,
1145
+ ops.template,
1243
1146
  [ops.literal, ["Hello, world."]],
1244
1147
  ]);
1245
- assertParse(
1246
- "templateLiteral",
1247
- "`Hello, world.`",
1248
- [ops.templateStandard, [ops.literal, ["Hello, world."]]],
1249
- "jse"
1250
- );
1251
1148
  assertParse("templateLiteral", "`foo ${x} bar`", [
1252
- ops.templateTree,
1149
+ ops.template,
1253
1150
  [ops.literal, ["foo ", " bar"]],
1254
1151
  [ops.scope, "x"],
1255
1152
  ]);
1256
1153
  assertParse("templateLiteral", "`${`nested`}`", [
1257
- ops.templateTree,
1154
+ ops.template,
1258
1155
  [ops.literal, ["", ""]],
1259
- [ops.templateTree, [ops.literal, ["nested"]]],
1156
+ [ops.template, [ops.literal, ["nested"]]],
1260
1157
  ]);
1261
1158
  assertParse("templateLiteral", "`${ map:(people, =`${name}`) }`", [
1262
- ops.templateTree,
1159
+ ops.template,
1263
1160
  [ops.literal, ["", ""]],
1264
1161
  [
1265
1162
  [ops.builtin, "map:"],
@@ -1267,20 +1164,14 @@ Body text`,
1267
1164
  [
1268
1165
  ops.lambda,
1269
1166
  [[ops.literal, "_"]],
1270
- [ops.templateTree, [ops.literal, ["", ""]], [ops.scope, "name"]],
1167
+ [ops.template, [ops.literal, ["", ""]], [ops.scope, "name"]],
1271
1168
  ],
1272
1169
  ],
1273
1170
  ]);
1274
1171
  });
1275
1172
 
1276
1173
  test("templateSubtitution", () => {
1277
- assertParse(
1278
- "templateSubstitution",
1279
- "${foo}",
1280
- [ops.scope, "foo"],
1281
- "shell",
1282
- false
1283
- );
1174
+ assertParse("templateSubstitution", "${foo}", [ops.scope, "foo"], false);
1284
1175
  });
1285
1176
 
1286
1177
  test("whitespace block", () => {
@@ -1291,7 +1182,6 @@ Body text`,
1291
1182
  // Second comment
1292
1183
  `,
1293
1184
  null,
1294
- "jse",
1295
1185
  false
1296
1186
  );
1297
1187
  });
@@ -1307,16 +1197,9 @@ Body text`,
1307
1197
  });
1308
1198
  });
1309
1199
 
1310
- function assertParse(
1311
- startRule,
1312
- source,
1313
- expected,
1314
- mode = "shell",
1315
- checkLocation = true
1316
- ) {
1200
+ function assertParse(startRule, source, expected, checkLocation = true) {
1317
1201
  const code = parse(source, {
1318
1202
  grammarSource: { text: source },
1319
- mode,
1320
1203
  startRule,
1321
1204
  });
1322
1205
 
@@ -1344,11 +1227,10 @@ function assertCodeLocations(code) {
1344
1227
  }
1345
1228
  }
1346
1229
 
1347
- function assertThrows(startRule, source, message, position, mode = "shell") {
1230
+ function assertThrows(startRule, source, message, position) {
1348
1231
  try {
1349
1232
  parse(source, {
1350
1233
  grammarSource: { text: source },
1351
- mode,
1352
1234
  startRule,
1353
1235
  });
1354
1236
  } catch (/** @type {any} */ error) {
@@ -80,11 +80,7 @@ describe("ops", () => {
80
80
  assert.strictEqual(await ops.conditional(false, errorFn, trueFn), true);
81
81
  });
82
82
 
83
- test("ops.construct", async () => {
84
- assert.equal(await ops.construct(String, "hello"), "hello");
85
- });
86
-
87
- test("ops.document", async () => {
83
+ test("ops.documentFunction", async () => {
88
84
  const code = createCode([
89
85
  ops.document,
90
86
  {
@@ -94,7 +90,7 @@ describe("ops", () => {
94
90
  ops.lambda,
95
91
  [["_"]],
96
92
  [
97
- ops.templateIndent,
93
+ ops.template,
98
94
  [ops.literal, ["a = ", ""]],
99
95
  [ops.concat, [ops.scope, "a"]],
100
96
  ],
@@ -297,11 +293,6 @@ describe("ops", () => {
297
293
  assert.strictEqual(ops.multiplication("foo", 2), NaN);
298
294
  });
299
295
 
300
- test("ops.optionalTraverse", async () => {
301
- assert.equal(await ops.optionalTraverse(null, "a"), undefined);
302
- assert.equal(await ops.optionalTraverse({ a: 1 }, "a"), 1);
303
- });
304
-
305
296
  test("ops.notEqual", () => {
306
297
  assert(!ops.notEqual(1, 1));
307
298
  assert(ops.notEqual(1, 2));
@@ -1,6 +1,6 @@
1
1
  import assert from "node:assert";
2
2
  import { describe, test } from "node:test";
3
- import indent from "../../src/runtime/templateIndent.js";
3
+ import indent from "../../src/runtime/taggedTemplateIndent.js";
4
4
 
5
5
  describe("taggedTemplateIndent", () => {
6
6
  test("joins strings and values together if template isn't a block template", async () => {
@@ -1,13 +0,0 @@
1
- /**
2
- * Concatenate the strings with a standard tagged template function that works
3
- * just like normal JavaScript templates. This is: a) synchronous, b) does not
4
- * convert treelike objects to strings.
5
- */
6
- export default function standardTemplate(strings, ...values) {
7
- let result = strings[0];
8
- for (let i = 0; i < values.length; i++) {
9
- result += values[i];
10
- result += strings[i + 1];
11
- }
12
- return result;
13
- }
@@ -1,18 +0,0 @@
1
- import assert from "node:assert";
2
- import { describe, test } from "node:test";
3
- import templateText from "../../src/runtime/templateStandard.js";
4
-
5
- describe("templateText", () => {
6
- test("joins strings and values together like JavaScript", async () => {
7
- const a = 1;
8
- const b = 2;
9
- const result = await templateText`-${a} ${b}-`;
10
- assert.equal(result, "-1 2-");
11
- });
12
-
13
- test("renders an object like JavaScript", async () => {
14
- const object = { a: 1 };
15
- const result = await templateText`-${object}-`;
16
- assert.equal(result, "-[object Object]-");
17
- });
18
- });