@weborigami/language 0.0.67-beta.2 → 0.0.69

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,6 +1,6 @@
1
1
  {
2
2
  "name": "@weborigami/language",
3
- "version": "0.0.67-beta.2",
3
+ "version": "0.0.69",
4
4
  "description": "Web Origami expression language compiler and runtime",
5
5
  "type": "module",
6
6
  "main": "./main.js",
@@ -11,8 +11,8 @@
11
11
  "typescript": "5.6.2"
12
12
  },
13
13
  "dependencies": {
14
- "@weborigami/async-tree": "0.0.67-beta.2",
15
- "@weborigami/types": "0.0.67-beta.2",
14
+ "@weborigami/async-tree": "0.0.69",
15
+ "@weborigami/types": "0.0.69",
16
16
  "watcher": "2.3.1"
17
17
  },
18
18
  "scripts": {
@@ -13,6 +13,7 @@ import {
13
13
  makeFunctionCall,
14
14
  makeObject,
15
15
  makePipeline,
16
+ makeProperty,
16
17
  makeTemplate
17
18
  } from "./parserHelpers.js";
18
19
 
@@ -249,7 +250,7 @@ objectEntry
249
250
  // A getter definition inside an object literal: `foo = 1`
250
251
  objectGetter "object getter"
251
252
  = key:objectKey __ "=" __ value:expr {
252
- return annotate([key, [ops.getter, value]], location());
253
+ return annotate(makeProperty(key, [ops.getter, value]), location());
253
254
  }
254
255
 
255
256
  objectHiddenKey
@@ -262,7 +263,7 @@ objectKey "object key"
262
263
  // A property definition in an object literal: `x: 1`
263
264
  objectProperty "object property"
264
265
  = key:objectKey __ ":" __ value:expr {
265
- return annotate([key, value], location());
266
+ return annotate(makeProperty(key, value), location());
266
267
  }
267
268
 
268
269
  // A shorthand reference inside an object literal: `foo`
@@ -18,6 +18,7 @@ import {
18
18
  makeFunctionCall,
19
19
  makeObject,
20
20
  makePipeline,
21
+ makeProperty,
21
22
  makeTemplate
22
23
  } from "./parserHelpers.js";
23
24
 
@@ -425,11 +426,11 @@ function peg$parse(input, options) {
425
426
  return annotate(entries, location());
426
427
  };
427
428
  var peg$f31 = function(key, value) {
428
- return annotate([key, [ops.getter, value]], location());
429
+ return annotate(makeProperty(key, [ops.getter, value]), location());
429
430
  };
430
431
  var peg$f32 = function(hiddenKey) { return hiddenKey.join(""); };
431
432
  var peg$f33 = function(key, value) {
432
- return annotate([key, value], location());
433
+ return annotate(makeProperty(key, value), location());
433
434
  };
434
435
  var peg$f34 = function(key) {
435
436
  return annotate([key, [ops.inherited, key]], location());
@@ -13,8 +13,28 @@ export function annotate(parseResult, location) {
13
13
  return parseResult;
14
14
  }
15
15
 
16
+ // The indicated code is being used to define a property named by the given key.
17
+ // Rewrite any [ops.scope, key] calls to be [ops.inherited, key] to avoid
18
+ // infinite recursion.
19
+ function avoidRecursivePropertyCalls(code, key) {
20
+ if (!(code instanceof Array)) {
21
+ return code;
22
+ }
23
+ let modified;
24
+ if (code[0] === ops.scope && code[1] === key) {
25
+ // Rewrite to avoid recursion
26
+ modified = [ops.inherited, key];
27
+ } else {
28
+ // Process any nested code
29
+ modified = code.map((value) => avoidRecursivePropertyCalls(value, key));
30
+ }
31
+ // @ts-ignore
32
+ modified.location = code.location;
33
+ return modified;
34
+ }
35
+
16
36
  // Return true if the code will generate an async object.
17
- function isAsyncObject(code) {
37
+ function isCodeForAsyncObject(code) {
18
38
  if (!(code instanceof Array)) {
19
39
  return false;
20
40
  }
@@ -120,28 +140,29 @@ export function makeObject(entries, op) {
120
140
 
121
141
  for (let [key, value] of entries) {
122
142
  if (key === ops.spread) {
123
- // Accumulate spread entry
143
+ // Spread entry; accumulate
124
144
  if (currentEntries.length > 0) {
125
145
  spreads.push([op, ...currentEntries]);
126
146
  currentEntries = [];
127
147
  }
128
148
  spreads.push(value);
129
- } else {
130
- if (
131
- value instanceof Array &&
132
- value[0] === ops.getter &&
133
- value[1] instanceof Array &&
134
- value[1][0] === ops.primitive
135
- ) {
136
- // Simplify a getter for a primitive value to a regular property
137
- value = value[1];
138
- } else if (isAsyncObject(value)) {
139
- // Add a trailing slash to key if value is an async object
140
- key = key + "/";
141
- }
149
+ continue;
150
+ }
142
151
 
143
- currentEntries.push([key, value]);
152
+ if (
153
+ value instanceof Array &&
154
+ value[0] === ops.getter &&
155
+ value[1] instanceof Array &&
156
+ value[1][0] === ops.primitive
157
+ ) {
158
+ // Simplify a getter for a primitive value to a regular property
159
+ value = value[1];
160
+ } else if (isCodeForAsyncObject(value)) {
161
+ // Add a trailing slash to key to indicate value is a subtree
162
+ key = key + "/";
144
163
  }
164
+
165
+ currentEntries.push([key, value]);
145
166
  }
146
167
 
147
168
  // Finish any current entries.
@@ -170,6 +191,12 @@ export function makePipeline(steps) {
170
191
  return value;
171
192
  }
172
193
 
194
+ // Define a property on an object.
195
+ export function makeProperty(key, value) {
196
+ const modified = avoidRecursivePropertyCalls(value, key);
197
+ return [key, modified];
198
+ }
199
+
173
200
  export function makeTemplate(parts) {
174
201
  // Drop empty/null strings.
175
202
  const filtered = parts.filter((part) => part);
@@ -9,6 +9,7 @@ import {
9
9
  SiteTree,
10
10
  Tree,
11
11
  isUnpackable,
12
+ pathFromKeys,
12
13
  scope as scopeFn,
13
14
  trailingSlash,
14
15
  concat as treeConcat,
@@ -77,12 +78,11 @@ constructor.toString = () => "«ops.constructor»";
77
78
  *
78
79
  * @param {string} protocol
79
80
  * @param {string} host
80
- * @param {...string|Symbol} keys
81
+ * @param {string[]} keys
81
82
  */
82
83
  function constructHref(protocol, host, ...keys) {
83
- // Remove trailing slashes
84
- const baseKeys = keys.map((key) => trailingSlash.remove(key));
85
- let href = [host, ...baseKeys].join("/");
84
+ const path = pathFromKeys(keys);
85
+ let href = [host, path].join("/");
86
86
  if (!href.startsWith(protocol)) {
87
87
  if (!href.startsWith("//")) {
88
88
  href = `//${href}`;
@@ -99,7 +99,7 @@ function constructHref(protocol, host, ...keys) {
99
99
  * @param {import("../../index.ts").Constructor<AsyncTree>} treeClass
100
100
  * @param {AsyncTree|null} parent
101
101
  * @param {string} host
102
- * @param {...string|Symbol} keys
102
+ * @param {string[]} keys
103
103
  */
104
104
  async function constructSiteTree(protocol, treeClass, parent, host, ...keys) {
105
105
  // If the last key doesn't end in a slash, remove it for now.
@@ -165,7 +165,7 @@ export async function filesRoot() {
165
165
  *
166
166
  * @this {AsyncTree|null}
167
167
  * @param {string} host
168
- * @param {...string|Symbol} keys
168
+ * @param {...string} keys
169
169
  */
170
170
  export async function http(host, ...keys) {
171
171
  const href = constructHref("http:", host, ...keys);
@@ -178,7 +178,7 @@ http.toString = () => "«ops.http»";
178
178
  *
179
179
  * @this {AsyncTree|null}
180
180
  * @param {string} host
181
- * @param {...string|Symbol} keys
181
+ * @param {...string} keys
182
182
  */
183
183
  export function https(host, ...keys) {
184
184
  const href = constructHref("https:", host, ...keys);
@@ -289,7 +289,7 @@ object.toString = () => "«ops.object»";
289
289
  *
290
290
  * @this {AsyncTree|null}
291
291
  * @param {string} host
292
- * @param {...string|Symbol} keys
292
+ * @param {...string} keys
293
293
  */
294
294
  export function explorableSite(host, ...keys) {
295
295
  return constructSiteTree("https:", ExplorableSiteTree, this, host, ...keys);
@@ -339,7 +339,7 @@ export const traverse = Tree.traverseOrThrow;
339
339
  *
340
340
  * @this {AsyncTree|null}
341
341
  * @param {string} host
342
- * @param {...string|Symbol} keys
342
+ * @param {...string} keys
343
343
  */
344
344
  export function treeHttp(host, ...keys) {
345
345
  return constructSiteTree("http:", SiteTree, this, host, ...keys);
@@ -351,7 +351,7 @@ treeHttp.toString = () => "«ops.treeHttp»";
351
351
  *
352
352
  * @this {AsyncTree|null}
353
353
  * @param {string} host
354
- * @param {...string|Symbol} keys
354
+ * @param {...string} keys
355
355
  */
356
356
  export function treeHttps(host, ...keys) {
357
357
  return constructSiteTree("https:", SiteTree, this, host, ...keys);
@@ -417,6 +417,7 @@ describe("Origami parser", () => {
417
417
  test("objectEntry", () => {
418
418
  assertParse("objectEntry", "foo", ["foo", [ops.inherited, "foo"]]);
419
419
  assertParse("objectEntry", "x: y", ["x", [ops.scope, "y"]]);
420
+ assertParse("objectEntry", "a: a", ["a", [ops.inherited, "a"]]);
420
421
  });
421
422
 
422
423
  test("objectGetter", () => {