@weborigami/language 0.6.9 → 0.6.11

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.
Files changed (39) hide show
  1. package/index.ts +19 -4
  2. package/main.js +5 -2
  3. package/package.json +2 -2
  4. package/src/compiler/compile.js +11 -3
  5. package/src/compiler/origami.pegjs +4 -2
  6. package/src/compiler/parse.js +77 -68
  7. package/src/compiler/parserHelpers.js +16 -17
  8. package/src/handlers/getPackedPath.js +17 -0
  9. package/src/handlers/jpeg_handler.js +5 -0
  10. package/src/handlers/js_handler.js +3 -3
  11. package/src/handlers/json_handler.js +3 -1
  12. package/src/handlers/tsv_handler.js +1 -1
  13. package/src/handlers/yaml_handler.js +9 -3
  14. package/src/project/jsGlobals.js +3 -12
  15. package/src/protocols/package.js +20 -11
  16. package/src/runtime/asyncStorage.js +7 -0
  17. package/src/runtime/codeFragment.js +4 -3
  18. package/src/runtime/errors.js +86 -128
  19. package/src/runtime/evaluate.js +8 -77
  20. package/src/runtime/execute.js +85 -0
  21. package/src/runtime/explainReferenceError.js +248 -0
  22. package/src/runtime/explainTraverseError.js +77 -0
  23. package/src/runtime/expressionFunction.js +8 -7
  24. package/src/runtime/expressionObject.js +4 -3
  25. package/src/runtime/handleExtension.js +22 -8
  26. package/src/runtime/internal.js +1 -1
  27. package/src/runtime/interop.js +15 -0
  28. package/src/runtime/ops.js +51 -21
  29. package/src/runtime/symbols.js +0 -1
  30. package/src/runtime/typos.js +22 -3
  31. package/test/compiler/compile.test.js +7 -103
  32. package/test/compiler/parse.test.js +42 -39
  33. package/test/project/fixtures/withPackageJson/subfolder/README.md +1 -0
  34. package/test/runtime/errors.test.js +296 -0
  35. package/test/runtime/evaluate.test.js +110 -34
  36. package/test/runtime/execute.test.js +41 -0
  37. package/test/runtime/expressionObject.test.js +3 -3
  38. package/test/runtime/ops.test.js +42 -35
  39. package/test/runtime/typos.test.js +2 -0
@@ -1,4 +1,3 @@
1
- import { Tree } from "@weborigami/async-tree";
2
1
  import assert from "node:assert";
3
2
  import { describe, test } from "node:test";
4
3
  import * as compile from "../../src/compiler/compile.js";
@@ -10,89 +9,13 @@ const globals = {
10
9
  name: "Alice",
11
10
  };
12
11
 
12
+ /**
13
+ * More complex tests for the compile() function
14
+ *
15
+ * The evaluate2.test.js file contains more basic tests, covering more language
16
+ * features, which compare the output of evaluate2() against an expected result.
17
+ */
13
18
  describe("compile", () => {
14
- test("array", async () => {
15
- await assertCompile("[]", []);
16
- await assertCompile("[ 1, 2, 3, ]", [1, 2, 3]);
17
- await assertCompile("[\n'a'\n'b'\n'c'\n]", ["a", "b", "c"]);
18
- });
19
-
20
- test("functionComposition", async () => {
21
- await assertCompile("greet()", "Hello, undefined!");
22
- await assertCompile("greet(name)", "Hello, Alice!");
23
- await assertCompile("greet 'world'", "Hello, world!", { mode: "shell" });
24
- });
25
-
26
- test("function call with spread", async () => {
27
- await assertCompile(
28
- `concat("Hello", ...[", ", name], "!")`,
29
- "Hello, Alice!"
30
- );
31
- });
32
-
33
- test("angle bracket path", async () => {
34
- await assertCompile("<data>", "Bob", {
35
- target: {
36
- data: "Bob",
37
- },
38
- });
39
- });
40
-
41
- test("object literal", async () => {
42
- await assertCompile("{ message = greet(name) }", {
43
- message: "Hello, Alice!",
44
- });
45
- await assertCompile(
46
- "{ message = greet(name) }",
47
- {
48
- message: "Hello, Alice!",
49
- },
50
- { mode: "shell" }
51
- );
52
- });
53
-
54
- test("object with computed property key", async () => {
55
- await assertCompile("{ [name] = greet(name) }", {
56
- Alice: "Hello, Alice!",
57
- });
58
- });
59
-
60
- test("merge", async () => {
61
- {
62
- const globals = {
63
- more: {
64
- b: 2,
65
- },
66
- };
67
- const fn = compile.expression(
68
- `
69
- {
70
- a: 1
71
- ...more
72
- c: a
73
- }
74
- `,
75
- { globals }
76
- );
77
- const result = await fn();
78
- assert.deepEqual(await Tree.plain(result), {
79
- a: 1,
80
- b: 2,
81
- c: 1,
82
- });
83
- }
84
- });
85
-
86
- test("number", async () => {
87
- await assertCompile("1", 1);
88
- await assertCompile("3.14159", 3.14159);
89
- await assertCompile("-1", -1);
90
- });
91
-
92
- test("sync object", async () => {
93
- await assertCompile("{a:1, b:2}", { a: 1, b: 2 });
94
- await assertCompile("{ a: { b: { c: 0 } } }", { a: { b: { c: 0 } } });
95
- });
96
19
 
97
20
  test("async object", async () => {
98
21
  const fn = compile.expression("{ a: { b = name }}", { globals });
@@ -141,21 +64,13 @@ describe("compile", () => {
141
64
 
142
65
  test("templateDocument", async () => {
143
66
  const defineTemplateFn = compile.templateDocument(
144
- "Documents can contain ` backticks"
67
+ "Documents can contain ` backticks",
145
68
  );
146
69
  const templateFn = await defineTemplateFn();
147
70
  const value = await templateFn();
148
71
  assert.deepEqual(value, "Documents can contain ` backticks");
149
72
  });
150
73
 
151
- test("templateLiteral", async () => {
152
- await assertCompile("`Hello, ${name}!`", "Hello, Alice!");
153
- await assertCompile(
154
- "`escape characters with \\`backslash\\``",
155
- "escape characters with `backslash`"
156
- );
157
- });
158
-
159
74
  test("tagged template string array is identical across calls", async () => {
160
75
  let saved;
161
76
  const globals = {
@@ -177,14 +92,3 @@ describe("compile", () => {
177
92
  assert.equal(bob, "Hello, Bob!");
178
93
  });
179
94
  });
180
-
181
- async function assertCompile(text, expected, options = {}) {
182
- const mode = options.mode ?? "program";
183
- const parent = options.target ?? null;
184
- const fn = compile.expression(text, { globals, mode, parent });
185
- let result = await fn();
186
- if (Tree.isMaplike(result)) {
187
- result = await Tree.plain(result);
188
- }
189
- assert.deepEqual(result, expected);
190
- }
@@ -31,7 +31,7 @@ describe("Origami parser", () => {
31
31
  [
32
32
  markers.traverse,
33
33
  [markers.external, "Path with spaces (and parens).html"],
34
- ]
34
+ ],
35
35
  );
36
36
  assertParse("angleBracketLiteral", "<foo/bar/baz>", [
37
37
  markers.traverse,
@@ -113,7 +113,7 @@ describe("Origami parser", () => {
113
113
  2
114
114
  3
115
115
  ]`,
116
- [ops.array, [ops.literal, 1], [ops.literal, 2], [ops.literal, 3]]
116
+ [ops.array, [ops.literal, 1], [ops.literal, 2], [ops.literal, 3]],
117
117
  );
118
118
  });
119
119
 
@@ -270,7 +270,7 @@ describe("Origami parser", () => {
270
270
  assertThrows(
271
271
  "arrowFunction",
272
272
  "([a, a]) => a",
273
- `Duplicate parameter name "a"`
273
+ `Duplicate parameter name "a"`,
274
274
  );
275
275
  });
276
276
 
@@ -321,7 +321,7 @@ describe("Origami parser", () => {
321
321
  assertThrows(
322
322
  "arrowFunction",
323
323
  "({ a: c, b: c }) => c",
324
- `Duplicate parameter name "c"`
324
+ `Duplicate parameter name "c"`,
325
325
  );
326
326
  });
327
327
 
@@ -569,8 +569,8 @@ describe("Origami parser", () => {
569
569
 
570
570
  test("parentheses arguments with spreads", () => {
571
571
  assertParse("callExpression", "fn(a, ...b, ...c)", [
572
- [ops.property, [markers.traverse, [markers.reference, "fn"]], "apply"],
573
- null,
572
+ ops.apply,
573
+ [markers.traverse, [markers.reference, "fn"]],
574
574
  [
575
575
  ops.flat,
576
576
  [ops.array, [markers.traverse, [markers.reference, "a"]]],
@@ -739,7 +739,7 @@ describe("Origami parser", () => {
739
739
  ---
740
740
  Body`,
741
741
  'Expected "---"',
742
- { line: 2, column: 14 }
742
+ { line: 2, column: 14 },
743
743
  );
744
744
  });
745
745
 
@@ -752,10 +752,15 @@ a : 1
752
752
  ---
753
753
  Body`,
754
754
  "Unexpected flow-map-end token",
755
- { line: 3, column: 1 }
755
+ { line: 3, column: 1 },
756
756
  );
757
757
  });
758
758
 
759
+ test("escapedChar", () => {
760
+ assertParse("escapedChar", String.raw`\n`, "\n");
761
+ assertParse("escapedChar", String.raw`\\`, `\\`);
762
+ });
763
+
759
764
  test("exponentiationExpression", () => {
760
765
  assertParse("exponentiationExpression", "2 ** 2 ** 3", [
761
766
  ops.exponentiation,
@@ -848,7 +853,7 @@ Body`,
848
853
  "x //comment",
849
854
  [markers.traverse, [markers.reference, "x"]],
850
855
  "program",
851
- false
856
+ false,
852
857
  );
853
858
  });
854
859
 
@@ -940,7 +945,7 @@ Body`,
940
945
  assertParse("expression", "tag`Hello, ${name}!`", [
941
946
  [markers.traverse, [markers.reference, "tag"]],
942
947
  [ops.literal, ["Hello, ", "!"]],
943
- [ops.concat, [markers.traverse, [markers.reference, "name"]]],
948
+ [ops.deepText, [markers.traverse, [markers.reference, "name"]]],
944
949
  ]);
945
950
  assertParse("expression", "=tag`Hello, ${_}!`", [
946
951
  ops.lambda,
@@ -949,7 +954,7 @@ Body`,
949
954
  [
950
955
  [markers.traverse, [markers.reference, "tag"]],
951
956
  [ops.literal, ["Hello, ", "!"]],
952
- [ops.concat, [markers.traverse, [markers.reference, "_"]]],
957
+ [ops.deepText, [markers.traverse, [markers.reference, "_"]]],
953
958
  ],
954
959
  ]);
955
960
  assertParse("expression", "(post, slug) => fn.js(post, slug)", [
@@ -1013,7 +1018,7 @@ Body`,
1013
1018
  ],
1014
1019
  ],
1015
1020
  ],
1016
- ]
1021
+ ],
1017
1022
  );
1018
1023
  });
1019
1024
  });
@@ -1032,7 +1037,7 @@ Body`,
1032
1037
  [[markers.traverse, [markers.reference, "_template"]]],
1033
1038
  ],
1034
1039
  "program",
1035
- false
1040
+ false,
1036
1041
  );
1037
1042
  });
1038
1043
 
@@ -1065,17 +1070,17 @@ Body`,
1065
1070
  assertThrows(
1066
1071
  "identifier",
1067
1072
  "1stCharacterIsNumber",
1068
- "Expected JavaScript identifier start"
1073
+ "Expected JavaScript identifier start",
1069
1074
  );
1070
1075
  assertThrows(
1071
1076
  "identifier",
1072
1077
  "has space",
1073
- "Expected JavaScript identifier continuation"
1078
+ "Expected JavaScript identifier continuation",
1074
1079
  );
1075
1080
  assertThrows(
1076
1081
  "identifier",
1077
1082
  "foo.bar",
1078
- "Expected JavaScript identifier continuation"
1083
+ "Expected JavaScript identifier continuation",
1079
1084
  );
1080
1085
  });
1081
1086
 
@@ -1108,7 +1113,7 @@ Body`,
1108
1113
  [markers.traverse, [markers.reference, "tree/"], [ops.literal, "key"]],
1109
1114
  [markers.traverse, [markers.reference, "arg"]],
1110
1115
  ],
1111
- "shell"
1116
+ "shell",
1112
1117
  );
1113
1118
  assertParse("implicitParenthesesCallExpression", "foo.js bar.ori 'arg'", [
1114
1119
  [markers.traverse, [markers.reference, "foo.js"]],
@@ -1121,19 +1126,15 @@ Body`,
1121
1126
  "implicitParenthesesCallExpression",
1122
1127
  "concat a.json, ...b.json, c.json",
1123
1128
  [
1124
- [
1125
- ops.property,
1126
- [markers.traverse, [markers.reference, "concat"]],
1127
- "apply",
1128
- ],
1129
- null,
1129
+ ops.apply,
1130
+ [markers.traverse, [markers.reference, "concat"]],
1130
1131
  [
1131
1132
  ops.flat,
1132
1133
  [ops.array, [markers.traverse, [markers.reference, "a.json"]]],
1133
1134
  [markers.traverse, [markers.reference, "b.json"]],
1134
1135
  [ops.array, [markers.traverse, [markers.reference, "c.json"]]],
1135
1136
  ],
1136
- ]
1137
+ ],
1137
1138
  );
1138
1139
  });
1139
1140
 
@@ -1183,7 +1184,7 @@ Body`,
1183
1184
  "/*\nHello, world!\n*/",
1184
1185
  null,
1185
1186
  "program",
1186
- false
1187
+ false,
1187
1188
  );
1188
1189
  });
1189
1190
 
@@ -1286,7 +1287,7 @@ Body`,
1286
1287
  ops.object,
1287
1288
  ["a", [ops.getter, [markers.traverse, [markers.reference, "b"]]]],
1288
1289
  ["b", [ops.literal, 2]],
1289
- ]
1290
+ ],
1290
1291
  );
1291
1292
  assertParse("objectLiteral", "{ a: { b: 1 } }", [
1292
1293
  ops.object,
@@ -1544,7 +1545,7 @@ Body`,
1544
1545
  "tree/",
1545
1546
  [[ops.literal, "tree/"]],
1546
1547
  undefined,
1547
- false
1548
+ false,
1548
1549
  );
1549
1550
  assertParse(
1550
1551
  "pathKeys",
@@ -1554,7 +1555,7 @@ Body`,
1554
1555
  [ops.literal, "12"],
1555
1556
  ],
1556
1557
  undefined,
1557
- false
1558
+ false,
1558
1559
  );
1559
1560
  assertParse(
1560
1561
  "pathKeys",
@@ -1565,7 +1566,7 @@ Body`,
1565
1566
  [ops.literal, "bar"],
1566
1567
  ],
1567
1568
  undefined,
1568
- false
1569
+ false,
1569
1570
  );
1570
1571
  assertParse(
1571
1572
  "pathKeys",
@@ -1577,7 +1578,7 @@ Body`,
1577
1578
  [ops.literal, "b"],
1578
1579
  ],
1579
1580
  undefined,
1580
- false
1581
+ false,
1581
1582
  );
1582
1583
  });
1583
1584
 
@@ -1674,12 +1675,14 @@ Body`,
1674
1675
  `,
1675
1676
  [ops.literal, "Hello"],
1676
1677
  "program",
1677
- false
1678
+ false,
1678
1679
  );
1679
1680
  });
1680
1681
 
1681
1682
  test("regexLiteral", () => {
1682
1683
  assertParse("regexLiteral", "/abc+/g", [ops.literal, /abc+/g]);
1684
+ assertParse("regexLiteral", String.raw`/\//`, [ops.literal, /\//]);
1685
+ assertParse("regexLiteral", String.raw`/\d+/`, [ops.literal, /\d+/]);
1683
1686
  });
1684
1687
 
1685
1688
  test("relationalExpression", () => {
@@ -1757,7 +1760,7 @@ Body`,
1757
1760
  "// Hello, world!",
1758
1761
  null,
1759
1762
  "program",
1760
- false
1763
+ false,
1761
1764
  );
1762
1765
  });
1763
1766
 
@@ -1815,7 +1818,7 @@ Body text`,
1815
1818
  ops.object,
1816
1819
  ["title", [ops.literal, "Title goes here"]],
1817
1820
  ["_body", [ops.templateIndent, [ops.literal, ["Body text"]]]],
1818
- ]
1821
+ ],
1819
1822
  );
1820
1823
  });
1821
1824
 
@@ -1842,7 +1845,7 @@ Body text`,
1842
1845
  ],
1843
1846
  ],
1844
1847
  ],
1845
- "shell"
1848
+ "shell",
1846
1849
  );
1847
1850
  });
1848
1851
 
@@ -1891,7 +1894,7 @@ Body text`,
1891
1894
  "${foo}",
1892
1895
  [markers.traverse, [markers.reference, "foo"]],
1893
1896
  "shell",
1894
- false
1897
+ false,
1895
1898
  );
1896
1899
  });
1897
1900
 
@@ -1965,7 +1968,7 @@ Body text`,
1965
1968
  `,
1966
1969
  null,
1967
1970
  "program",
1968
- false
1971
+ false,
1969
1972
  );
1970
1973
  });
1971
1974
  });
@@ -1975,7 +1978,7 @@ function assertParse(
1975
1978
  source,
1976
1979
  expected,
1977
1980
  mode = "shell",
1978
- checkLocation = true
1981
+ checkLocation = true,
1979
1982
  ) {
1980
1983
  const code = parse(source, {
1981
1984
  grammarSource: { text: source },
@@ -1990,7 +1993,7 @@ function assertParse(
1990
1993
  assertCodeLocations(code);
1991
1994
  const resultSource = code.location.source.text.slice(
1992
1995
  code.location.start.offset,
1993
- code.location.end.offset
1996
+ code.location.end.offset,
1994
1997
  );
1995
1998
  assert.equal(resultSource, source);
1996
1999
  }
@@ -2010,7 +2013,7 @@ function assertThrows(startRule, source, message, position, mode = "program") {
2010
2013
  } catch (/** @type {any} */ error) {
2011
2014
  assert(
2012
2015
  error.message.includes(message),
2013
- `Error message incorrect:\n expected: "${message}"\n actual: "${error.message}"`
2016
+ `Error message incorrect:\n expected: "${message}"\n actual: "${error.message}"`,
2014
2017
  );
2015
2018
  if (position) {
2016
2019
  assert.equal(error.location.start.line, position.line);
@@ -0,0 +1 @@
1
+ This file exists to force creation of the containing folder.