@weborigami/language 0.3.3-jse.2 → 0.3.3-jse.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.
@@ -8,7 +8,7 @@ import { createCode } from "../compiler/codeHelpers.js";
8
8
 
9
9
  describe("evaluate", () => {
10
10
  test("can retrieve values from scope", async () => {
11
- const code = createCode([ops.scope, "message"]);
11
+ const code = createCode([[ops.scope], "message"]);
12
12
  const parent = new ObjectTree({
13
13
  message: "Hello",
14
14
  });
@@ -21,8 +21,8 @@ describe("evaluate", () => {
21
21
  test("can invoke functions in scope", async () => {
22
22
  // Match the array representation of code generated by the parser.
23
23
  const code = createCode([
24
- [ops.scope, "greet"],
25
- [ops.scope, "name"],
24
+ [[ops.scope], "greet"],
25
+ [[ops.scope], "name"],
26
26
  ]);
27
27
 
28
28
  const tree = new ObjectTree({
@@ -37,7 +37,7 @@ describe("evaluate", () => {
37
37
  });
38
38
 
39
39
  test("passes context to invoked functions", async () => {
40
- const code = createCode([ops.scope, "fn"]);
40
+ const code = createCode([[ops.scope], "fn"]);
41
41
  const tree = new ObjectTree({
42
42
  async fn() {
43
43
  assert.equal(this, tree);
@@ -46,14 +46,6 @@ describe("evaluate", () => {
46
46
  await evaluate.call(tree, code);
47
47
  });
48
48
 
49
- test("evaluates a function with fixed number of arguments", async () => {
50
- const fn = (x, y) => ({
51
- c: `${x}${y}c`,
52
- });
53
- const code = createCode([ops.traverse, fn, "a", "b", "c"]);
54
- assert.equal(await evaluate.call(null, code), "abc");
55
- });
56
-
57
49
  test("if object in function position isn't a function, can unpack it", async () => {
58
50
  const fn = (...args) => args.join(",");
59
51
  const packed = new String();
@@ -62,12 +54,4 @@ describe("evaluate", () => {
62
54
  const result = await evaluate.call(null, code);
63
55
  assert.equal(result, "a,b,c");
64
56
  });
65
-
66
- test("by defalut sets the parent of a returned tree to the current tree", async () => {
67
- const fn = () => new ObjectTree({});
68
- const code = createCode([fn]);
69
- const tree = new ObjectTree({});
70
- const result = await evaluate.call(tree, code);
71
- assert.equal(result.parent, tree);
72
- });
73
57
  });
@@ -12,8 +12,8 @@ describe("expressionObject", () => {
12
12
  });
13
13
 
14
14
  const entries = [
15
- ["hello", [[ops.scope, "upper"], "hello"]],
16
- ["world", [[ops.scope, "upper"], "world"]],
15
+ ["hello", [[[ops.scope], "upper"], "hello"]],
16
+ ["world", [[[ops.scope], "upper"], "world"]],
17
17
  ];
18
18
 
19
19
  const object = await expressionObject(entries, scope);
@@ -40,7 +40,7 @@ describe("expressionObject", () => {
40
40
  test("can instantiate an Origami tree", async () => {
41
41
  const entries = [
42
42
  ["name", "world"],
43
- ["message", [ops.concat, "Hello, ", [ops.scope, "name"], "!"]],
43
+ ["message", [ops.concat, "Hello, ", [[ops.scope], "name"], "!"]],
44
44
  ];
45
45
  const parent = new ObjectTree({});
46
46
  const object = await expressionObject(entries, parent);
@@ -53,7 +53,8 @@ describe("expressionObject", () => {
53
53
 
54
54
  test("returned object values can be unpacked", async () => {
55
55
  const entries = [["data.json", `{ "a": 1 }`]];
56
- const parent = new ObjectTree({
56
+ const parent = new ObjectTree({});
57
+ parent.handlers = new ObjectTree({
57
58
  "json.handler": {
58
59
  unpack: JSON.parse,
59
60
  },
@@ -3,6 +3,12 @@ import assert from "node:assert";
3
3
  import { describe, test } from "node:test";
4
4
  import { handleExtension } from "../../src/runtime/handlers.js";
5
5
 
6
+ const handlers = new ObjectTree({
7
+ "json.handler": {
8
+ unpack: (buffer) => JSON.parse(String(buffer)),
9
+ },
10
+ });
11
+
6
12
  describe("handlers", () => {
7
13
  test("attaches an unpack method to a value with an extension", async () => {
8
14
  const fixture = createFixture();
@@ -10,7 +16,12 @@ describe("handlers", () => {
10
16
  assert(typeof numberValue === "number");
11
17
  assert.equal(numberValue, 1);
12
18
  const jsonFile = await fixture.get("bar.json");
13
- const withHandler = await handleExtension(fixture, jsonFile, "bar.json");
19
+ const withHandler = await handleExtension(
20
+ fixture,
21
+ jsonFile,
22
+ "bar.json",
23
+ handlers
24
+ );
14
25
  assert.equal(String(withHandler), `{ "bar": 2 }`);
15
26
  const data = await withHandler.unpack();
16
27
  assert.deepEqual(data, { bar: 2 });
@@ -19,21 +30,19 @@ describe("handlers", () => {
19
30
  test("immediately unpacks if key ends in slash", async () => {
20
31
  const fixture = createFixture();
21
32
  const jsonFile = await fixture.get("bar.json");
22
- const data = await handleExtension(fixture, jsonFile, "bar.json/");
33
+ const data = await handleExtension(
34
+ fixture,
35
+ jsonFile,
36
+ "bar.json/",
37
+ handlers
38
+ );
23
39
  assert.deepEqual(data, { bar: 2 });
24
40
  });
25
41
  });
26
42
 
27
43
  function createFixture() {
28
- const parent = new ObjectTree({
29
- "json.handler": {
30
- unpack: (buffer) => JSON.parse(String(buffer)),
31
- },
32
- });
33
- let tree = new ObjectTree({
44
+ return new ObjectTree({
34
45
  foo: 1, // No extension, should be left alone
35
46
  "bar.json": `{ "bar": 2 }`,
36
47
  });
37
- tree.parent = parent;
38
- return tree;
39
48
  }
@@ -41,11 +41,6 @@ describe("mergeTrees", () => {
41
41
  });
42
42
  });
43
43
 
44
- test("if all arguments are arrays, result is an array", async () => {
45
- const result = await mergeTrees.call(null, [1, 2], [3, 4]);
46
- assert.deepEqual(result, [1, 2, 3, 4]);
47
- });
48
-
49
44
  test("merges heterogenous arguments as trees", async () => {
50
45
  const tree = await mergeTrees.call(
51
46
  null,
@@ -1,4 +1,4 @@
1
- import { DeepObjectTree, ObjectTree } from "@weborigami/async-tree";
1
+ import { DeepObjectTree, ObjectTree, Tree } from "@weborigami/async-tree";
2
2
  import assert from "node:assert";
3
3
  import { describe, test } from "node:test";
4
4
 
@@ -42,17 +42,6 @@ describe("ops", () => {
42
42
  assert.strictEqual(ops.bitwiseXor(5, 3), 6);
43
43
  });
44
44
 
45
- test("ops.builtin gets a value from the top of the scope chain", async () => {
46
- const root = new ObjectTree({
47
- a: 1,
48
- });
49
- const tree = new ObjectTree({});
50
- tree.parent = root;
51
- const code = createCode([ops.builtin, "a"]);
52
- const result = await evaluate.call(tree, code);
53
- assert.strictEqual(result, 1);
54
- });
55
-
56
45
  test("ops.comma returns the last value", async () => {
57
46
  const code = createCode([ops.comma, 1, 2, 3]);
58
47
  const result = await evaluate.call(null, code);
@@ -64,7 +53,12 @@ describe("ops", () => {
64
53
  name: "world",
65
54
  });
66
55
 
67
- const code = createCode([ops.concat, "Hello, ", [ops.scope, "name"], "."]);
56
+ const code = createCode([
57
+ ops.concat,
58
+ "Hello, ",
59
+ [[ops.scope], "name"],
60
+ ".",
61
+ ]);
68
62
 
69
63
  const result = await evaluate.call(scope, code);
70
64
  assert.strictEqual(result, "Hello, world.");
@@ -84,27 +78,18 @@ describe("ops", () => {
84
78
  assert.equal(await ops.construct(String, "hello"), "hello");
85
79
  });
86
80
 
87
- test("ops.document", async () => {
88
- const code = createCode([
89
- ops.document,
90
- {
91
- a: 1,
81
+ test("ops.context", async () => {
82
+ const tree = new DeepObjectTree({
83
+ a: {
84
+ b: {
85
+ c: {},
86
+ },
92
87
  },
93
- [
94
- ops.lambda,
95
- [["_"]],
96
- [
97
- ops.templateIndent,
98
- [ops.literal, ["a = ", ""]],
99
- [ops.concat, [ops.scope, "a"]],
100
- ],
101
- ],
102
- ]);
103
- const result = await evaluate.call(null, code);
104
- assert.deepEqual(result, {
105
- a: 1,
106
- "@text": "a = 1",
107
88
  });
89
+ const b = await Tree.traverse(tree, "a", "b");
90
+ const c = await b.get("c");
91
+ assert.equal(ops.context.call(c), c);
92
+ assert.equal(ops.context.call(c, 1), b);
108
93
  });
109
94
 
110
95
  test("ops.division divides two numbers", async () => {
@@ -127,20 +112,21 @@ describe("ops", () => {
127
112
  assert.strictEqual(ops.exponentiation(2, 0), 1);
128
113
  });
129
114
 
130
- test("ops.external evaluates code and cache its result", async () => {
115
+ test("ops.cache evaluates code and cache its result", async () => {
131
116
  let count = 0;
132
117
  const tree = new DeepObjectTree({
133
118
  group: {
134
119
  get count() {
135
- return ++count;
120
+ // Use promise to test async behavior
121
+ return Promise.resolve(++count);
136
122
  },
137
123
  },
138
124
  });
139
125
  const code = createCode([
140
- ops.external,
141
- "group/count",
142
- [ops.traverse, [ops.scope, "group"], [ops.literal, "count"]],
126
+ ops.cache,
143
127
  {},
128
+ "group/count",
129
+ [[ops.scope], [ops.literal, "group"], [ops.literal, "count"]],
144
130
  ]);
145
131
  const result = await evaluate.call(tree, code);
146
132
  assert.strictEqual(result, 1);
@@ -148,6 +134,26 @@ describe("ops", () => {
148
134
  assert.strictEqual(result2, 1);
149
135
  });
150
136
 
137
+ describe("ops.flat", () => {
138
+ test("flattens arrays", async () => {
139
+ assert.deepEqual(await ops.flat(1, 2, [3]), [1, 2, 3]);
140
+ });
141
+
142
+ test("flattens treelike objects", async () => {
143
+ const object = {
144
+ a: 1,
145
+ b: 2,
146
+ };
147
+ const tree = new ObjectTree({
148
+ c: 3,
149
+ d: 4,
150
+ });
151
+ const array = [5, 6];
152
+ const result = await ops.flat(object, tree, array);
153
+ assert.deepEqual(result, [1, 2, 3, 4, 5, 6]);
154
+ });
155
+ });
156
+
151
157
  test("ops.greaterThan", () => {
152
158
  assert(ops.greaterThan(5, 3));
153
159
  assert(!ops.greaterThan(3, 3));
@@ -160,20 +166,6 @@ describe("ops", () => {
160
166
  assert(ops.greaterThanOrEqual("ab", "aa"));
161
167
  });
162
168
 
163
- test("ops.inherited searches inherited scope", async () => {
164
- const parent = new ObjectTree({
165
- a: 1, // This is the inherited value we want
166
- });
167
- /** @type {any} */
168
- const child = new ObjectTree({
169
- a: 2, // Should be ignored
170
- });
171
- child.parent = parent;
172
- const code = createCode([ops.inherited, "a"]);
173
- const result = await evaluate.call(child, code);
174
- assert.strictEqual(result, 1);
175
- });
176
-
177
169
  test("ops.lambda defines a function with no inputs", async () => {
178
170
  const code = createCode([ops.lambda, [], [ops.literal, "result"]]);
179
171
  const fn = await evaluate.call(null, code);
@@ -186,7 +178,7 @@ describe("ops", () => {
186
178
  message: "Hello",
187
179
  });
188
180
 
189
- const code = createCode([ops.lambda, ["_"], [ops.scope, "message"]]);
181
+ const code = createCode([ops.lambda, ["_"], [[ops.scope], "message"]]);
190
182
 
191
183
  const fn = await evaluate.call(scope, code);
192
184
  const result = await fn.call(scope);
@@ -200,7 +192,7 @@ describe("ops", () => {
200
192
  [ops.literal, "a"],
201
193
  [ops.literal, "b"],
202
194
  ],
203
- [ops.concat, [ops.scope, "b"], [ops.scope, "a"]],
195
+ [ops.concat, [[ops.scope], "b"], [[ops.scope], "a"]],
204
196
  ]);
205
197
  const fn = await evaluate.call(null, code);
206
198
  const result = await fn("x", "y");
@@ -254,40 +246,31 @@ describe("ops", () => {
254
246
  test("ops.merge", async () => {
255
247
  // {
256
248
  // a: 1
257
- // …fn(a)
258
- // }
259
- const scope = new ObjectTree({
260
- fn: (a) => ({ b: 2 * a }),
261
- });
262
- const code = createCode([
263
- ops.merge,
264
- [ops.object, ["a", [ops.literal, 1]]],
265
- [
266
- [ops.builtin, "fn"],
267
- [ops.scope, "a"],
268
- ],
269
- ]);
270
- const result = await evaluate.call(scope, code);
271
- assert.deepEqual(result, { a: 1, b: 2 });
272
- });
273
-
274
- test("ops.merge lets all direct properties see each other", async () => {
275
- // {
276
- // a: 1
277
- // ...more
249
+ // …more
278
250
  // c: a
279
251
  // }
280
252
  const scope = new ObjectTree({
281
253
  more: { b: 2 },
282
254
  });
283
255
  const code = createCode([
284
- ops.merge,
285
- [ops.object, ["a", [ops.literal, 1]]],
286
- [ops.scope, "more"],
287
- [ops.object, ["c", [ops.scope, "a"]]],
256
+ [
257
+ ops.object,
258
+ ["a", [ops.literal, 1]],
259
+ ["c", [[ops.context], "a"]],
260
+ [
261
+ "_result",
262
+ [
263
+ ops.merge,
264
+ [ops.object, ["a", [ops.getter, [[ops.context, 1], "a"]]]],
265
+ [[ops.scope], "more"],
266
+ [ops.object, ["c", [ops.getter, [[ops.context, 1], "c"]]]],
267
+ ],
268
+ ],
269
+ ],
270
+ "_result",
288
271
  ]);
289
272
  const result = await evaluate.call(scope, code);
290
- assert.deepEqual(result, { a: 1, b: 2, c: 1 });
273
+ assert.deepEqual(await Tree.plain(result), { a: 1, b: 2, c: 1 });
291
274
  });
292
275
 
293
276
  test("ops.multiplication multiplies two numbers", async () => {
@@ -334,8 +317,8 @@ describe("ops", () => {
334
317
 
335
318
  const code = createCode([
336
319
  ops.object,
337
- ["hello", [[ops.scope, "upper"], "hello"]],
338
- ["world", [[ops.scope, "upper"], "world"]],
320
+ ["hello", [[[ops.scope], "upper"], "hello"]],
321
+ ["world", [[[ops.scope], "upper"], "world"]],
339
322
  ]);
340
323
 
341
324
  const result = await evaluate.call(scope, code);
@@ -351,7 +334,7 @@ describe("ops", () => {
351
334
  ops.array,
352
335
  "Hello",
353
336
  1,
354
- [[ops.scope, "upper"], "world"],
337
+ [[[ops.scope], "upper"], "world"],
355
338
  ]);
356
339
  const result = await evaluate.call(scope, code);
357
340
  assert.deepEqual(result, ["Hello", 1, "WORLD"]);
@@ -364,6 +347,35 @@ describe("ops", () => {
364
347
  assert.strictEqual(ops.remainder(-4, 2), -0);
365
348
  });
366
349
 
350
+ describe("ops.scope", () => {
351
+ test("returns the scope of the current tree", async () => {
352
+ const tree = new DeepObjectTree({
353
+ a: {
354
+ b: {},
355
+ },
356
+ c: 1,
357
+ });
358
+ const a = await tree.get("a");
359
+ const b = await a.get("b");
360
+ const scope = await ops.scope.call(b);
361
+ assert.equal(await scope.get("c"), 1);
362
+ });
363
+
364
+ test("accepts an optional context", async () => {
365
+ const tree = new DeepObjectTree({
366
+ a: {
367
+ b: {},
368
+ c: 0, // shouldn't get this
369
+ },
370
+ c: 1,
371
+ });
372
+ const a = await tree.get("a");
373
+ const b = await a.get("b");
374
+ const scope = await ops.scope.call(b, tree);
375
+ assert.equal(await scope.get("c"), 1);
376
+ });
377
+ });
378
+
367
379
  test("ops.shiftLeft", () => {
368
380
  assert.strictEqual(ops.shiftLeft(5, 2), 20);
369
381
  });