@weborigami/language 0.3.4-jse.8 → 0.3.4-jse.9

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.3.4-jse.8",
3
+ "version": "0.3.4-jse.9",
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.8.2"
12
12
  },
13
13
  "dependencies": {
14
- "@weborigami/async-tree": "0.3.4-jse.8",
15
- "@weborigami/types": "0.3.4-jse.8",
14
+ "@weborigami/async-tree": "0.3.4-jse.9",
15
+ "@weborigami/types": "0.3.4-jse.9",
16
16
  "watcher": "2.3.1",
17
17
  "yaml": "2.7.0"
18
18
  },
@@ -56,7 +56,7 @@ export default function optimize(code, options = {}) {
56
56
 
57
57
  case ops.object:
58
58
  const entries = args;
59
- const keys = entries.map(entryKey);
59
+ const keys = entries.map((entry) => entryKey(entry));
60
60
  locals.push(keys);
61
61
  break;
62
62
  }
@@ -70,13 +70,16 @@ export default function optimize(code, options = {}) {
70
70
  } else if (op === ops.object && index > 0) {
71
71
  const [key, value] = child;
72
72
  const adjustedLocals = avoidLocalRecursion(locals, key);
73
- return [
74
- key,
75
- optimize(/** @type {AnnotatedCode} */ (value), {
76
- ...options,
77
- locals: adjustedLocals,
78
- }),
79
- ];
73
+ return annotate(
74
+ [
75
+ key,
76
+ optimize(/** @type {AnnotatedCode} */ (value), {
77
+ ...options,
78
+ locals: adjustedLocals,
79
+ }),
80
+ ],
81
+ child.location
82
+ );
80
83
  } else if (Array.isArray(child) && "location" in child) {
81
84
  // Review: Aside from ops.object (above), what non-instruction arrays
82
85
  // does this descend into?
@@ -191,6 +194,7 @@ function inlineLiteral(code) {
191
194
  function localReference(key, locals, location) {
192
195
  const normalized = trailingSlash.remove(key);
193
196
  const depth = getLocalReferenceDepth(locals, normalized);
197
+ /** @type {any[]} */
194
198
  const context = [ops.context];
195
199
  if (depth > 0) {
196
200
  context.push(depth);
@@ -310,6 +314,7 @@ function resolvePath(code, globals, locals, cache) {
310
314
 
311
315
  function scopeCall(locals, location) {
312
316
  const depth = locals.length;
317
+ /** @type {any[]} */
313
318
  const code = [ops.scope];
314
319
  if (depth > 0) {
315
320
  // Add context for appropriate depth to scope call
@@ -866,8 +866,8 @@ uriKey
866
866
  // A single character in a URI key
867
867
  uriKeyChar
868
868
  // Accept anything that doesn't end the URI key or path
869
- // Note: Peggy doesn't support `/s` so we list space and tab explicitly.
870
- = [^/,\)\]\} \t]
869
+ // Reject whitespace; see notes for `whitespace` term
870
+ = char:[^/,\)\]\}] !&{ return /\s/.test(char); } { return char; }
871
871
  / escapedChar
872
872
 
873
873
  // A slash-separated path of keys: `a/b/c`
@@ -285,7 +285,7 @@ function peg$parse(input, options) {
285
285
  var peg$r7 = /^[gimuy]/;
286
286
  var peg$r8 = /^[^\/\n\r]/;
287
287
  var peg$r9 = /^[^\n\r]/;
288
- var peg$r10 = /^[^\/,)\]} \t]/;
288
+ var peg$r10 = /^[^\/,)\]}]/;
289
289
  var peg$r11 = /^[a-z]/;
290
290
  var peg$r12 = /^[a-z0-9+-.]/;
291
291
  var peg$r13 = /^[:]/;
@@ -399,7 +399,7 @@ function peg$parse(input, options) {
399
399
  var peg$e105 = peg$otherExpectation("template document");
400
400
  var peg$e106 = peg$otherExpectation("template literal");
401
401
  var peg$e107 = peg$otherExpectation("template substitution");
402
- var peg$e108 = peg$classExpectation(["/", ",", ")", "]", "}", " ", "\t"], true, false);
402
+ var peg$e108 = peg$classExpectation(["/", ",", ")", "]", "}"], true, false);
403
403
  var peg$e109 = peg$otherExpectation("slash-separated path");
404
404
  var peg$e110 = peg$classExpectation([["a", "z"]], false, false);
405
405
  var peg$e111 = peg$classExpectation([["a", "z"], ["0", "9"], ["+", "."]], false, false);
@@ -780,14 +780,16 @@ function peg$parse(input, options) {
780
780
  // A single slash is a path key
781
781
  return annotate([ops.literal, ""], location());
782
782
  };
783
- var peg$f110 = function(keys) {
783
+ var peg$f110 = function(char) { return /\s/.test(char); };
784
+ var peg$f111 = function(char) { return char; };
785
+ var peg$f112 = function(keys) {
784
786
  return annotate(keys, location());
785
787
  };
786
- var peg$f111 = function() {
788
+ var peg$f113 = function() {
787
789
  return annotate([markers.global, text()], location());
788
790
  };
789
- var peg$f112 = function(char) { return /\s/.test(char); };
790
- var peg$f113 = function(char) { return char; };
791
+ var peg$f114 = function(char) { return /\s/.test(char); };
792
+ var peg$f115 = function(char) { return char; };
791
793
  var peg$currPos = options.peg$currPos | 0;
792
794
  var peg$savedPos = peg$currPos;
793
795
  var peg$posDetailsCache = [{ line: 1, column: 1 }];
@@ -6050,15 +6052,44 @@ function peg$parse(input, options) {
6050
6052
  }
6051
6053
 
6052
6054
  function peg$parseuriKeyChar() {
6053
- var s0;
6055
+ var s0, s1, s2, s3;
6054
6056
 
6055
- s0 = input.charAt(peg$currPos);
6056
- if (peg$r10.test(s0)) {
6057
+ s0 = peg$currPos;
6058
+ s1 = input.charAt(peg$currPos);
6059
+ if (peg$r10.test(s1)) {
6057
6060
  peg$currPos++;
6058
6061
  } else {
6059
- s0 = peg$FAILED;
6062
+ s1 = peg$FAILED;
6060
6063
  if (peg$silentFails === 0) { peg$fail(peg$e108); }
6061
6064
  }
6065
+ if (s1 !== peg$FAILED) {
6066
+ s2 = peg$currPos;
6067
+ peg$silentFails++;
6068
+ peg$savedPos = peg$currPos;
6069
+ s3 = peg$f110(s1);
6070
+ if (s3) {
6071
+ s3 = undefined;
6072
+ } else {
6073
+ s3 = peg$FAILED;
6074
+ }
6075
+ peg$silentFails--;
6076
+ if (s3 === peg$FAILED) {
6077
+ s2 = undefined;
6078
+ } else {
6079
+ peg$currPos = s2;
6080
+ s2 = peg$FAILED;
6081
+ }
6082
+ if (s2 !== peg$FAILED) {
6083
+ peg$savedPos = s0;
6084
+ s0 = peg$f111(s1);
6085
+ } else {
6086
+ peg$currPos = s0;
6087
+ s0 = peg$FAILED;
6088
+ }
6089
+ } else {
6090
+ peg$currPos = s0;
6091
+ s0 = peg$FAILED;
6092
+ }
6062
6093
  if (s0 === peg$FAILED) {
6063
6094
  s0 = peg$parseescapedChar();
6064
6095
  }
@@ -6086,7 +6117,7 @@ function peg$parse(input, options) {
6086
6117
  }
6087
6118
  if (s1 !== peg$FAILED) {
6088
6119
  peg$savedPos = s0;
6089
- s1 = peg$f110(s1);
6120
+ s1 = peg$f112(s1);
6090
6121
  }
6091
6122
  s0 = s1;
6092
6123
  peg$silentFails--;
@@ -6137,7 +6168,7 @@ function peg$parse(input, options) {
6137
6168
  }
6138
6169
  if (s3 !== peg$FAILED) {
6139
6170
  peg$savedPos = s0;
6140
- s0 = peg$f111();
6171
+ s0 = peg$f113();
6141
6172
  } else {
6142
6173
  peg$currPos = s0;
6143
6174
  s0 = peg$FAILED;
@@ -6217,7 +6248,7 @@ function peg$parse(input, options) {
6217
6248
  }
6218
6249
  if (s1 !== peg$FAILED) {
6219
6250
  peg$savedPos = peg$currPos;
6220
- s2 = peg$f112(s1);
6251
+ s2 = peg$f114(s1);
6221
6252
  if (s2) {
6222
6253
  s2 = undefined;
6223
6254
  } else {
@@ -6225,7 +6256,7 @@ function peg$parse(input, options) {
6225
6256
  }
6226
6257
  if (s2 !== peg$FAILED) {
6227
6258
  peg$savedPos = s0;
6228
- s0 = peg$f113(s1);
6259
+ s0 = peg$f115(s1);
6229
6260
  } else {
6230
6261
  peg$currPos = s0;
6231
6262
  s0 = peg$FAILED;
@@ -28,7 +28,7 @@ export const markers = {
28
28
  * If a parse result is an object that will be evaluated at runtime, attach the
29
29
  * location of the source code that produced it for debugging and error messages.
30
30
  *
31
- * @param {Code[]} code
31
+ * @param {any[]} code
32
32
  * @param {CodeLocation} location
33
33
  */
34
34
  export function annotate(code, location) {
@@ -375,11 +375,12 @@ export function makePath(keys) {
375
375
  const reference = annotate([markers.reference, headKey], head.location);
376
376
 
377
377
  let code = [markers.traverse, reference, ...tail];
378
- code.location = spanLocations(code);
378
+ const location = spanLocations(code);
379
+ code = annotate(code, location);
379
380
 
380
381
  // Last key has trailing slash implies unpack operation
381
382
  if (trailingSlash.has(args.at(-1)[1])) {
382
- code = annotate([ops.unpack, code], code.location);
383
+ code = annotate([ops.unpack, code], location);
383
384
  }
384
385
 
385
386
  return code;
@@ -6,5 +6,5 @@ export default function getHandlers(tree) {
6
6
  return null;
7
7
  }
8
8
  const root = Tree.root(tree);
9
- return root.handlers;
9
+ return /** @type {any} */ (root).handlers;
10
10
  }
@@ -204,7 +204,7 @@ addOpLabel(greaterThanOrEqual, "«ops.greaterThanOrEqual»");
204
204
  export async function homeDirectory(...keys) {
205
205
  const tree = new OrigamiFiles(os.homedir());
206
206
  // Use the same handlers as the current tree
207
- tree.handlers = getHandlers(this);
207
+ /** @type {any} */ (tree).handlers = getHandlers(this);
208
208
  return keys.length > 0 ? Tree.traverse(tree, ...keys) : tree;
209
209
  }
210
210
  addOpLabel(homeDirectory, "«ops.homeDirectory»");
@@ -424,7 +424,7 @@ addOpLabel(remainder, "«ops.remainder»");
424
424
  export async function rootDirectory(...keys) {
425
425
  const tree = new OrigamiFiles("/");
426
426
  // Use the same handlers as the current tree
427
- tree.handlers = getHandlers(this);
427
+ /** @type {any} */ (tree).handlers = getHandlers(this);
428
428
  return keys.length > 0 ? Tree.traverse(tree, ...keys) : tree;
429
429
  }
430
430
  addOpLabel(rootDirectory, "«ops.rootDirectory»");
@@ -7,6 +7,15 @@ export function assertCodeEqual(actual, expected) {
7
7
  assert.deepStrictEqual(actualStripped, expectedStripped);
8
8
  }
9
9
 
10
+ export function assertCodeLocations(code) {
11
+ assert(code.location, "no location");
12
+ for (const item of code) {
13
+ if (Array.isArray(item)) {
14
+ assertCodeLocations(item);
15
+ }
16
+ }
17
+ }
18
+
10
19
  /**
11
20
  * Adds a fake source to code.
12
21
  *
@@ -4,7 +4,11 @@ import * as compile from "../../src/compiler/compile.js";
4
4
  import optimize from "../../src/compiler/optimize.js";
5
5
  import { markers } from "../../src/compiler/parserHelpers.js";
6
6
  import { ops } from "../../src/runtime/internal.js";
7
- import { assertCodeEqual, createCode } from "./codeHelpers.js";
7
+ import {
8
+ assertCodeEqual,
9
+ assertCodeLocations,
10
+ createCode,
11
+ } from "./codeHelpers.js";
8
12
 
9
13
  describe("optimize", () => {
10
14
  test("change local references to context references", () => {
@@ -277,5 +281,6 @@ function assertCompile(expression, expected, mode = "shell") {
277
281
  const globals = new ObjectTree({});
278
282
  const fn = compile.expression(expression, { globals, mode, parent });
279
283
  const actual = fn.code;
284
+ assertCodeLocations(actual);
280
285
  assertCodeEqual(actual, expected);
281
286
  }
@@ -3,7 +3,7 @@ import { describe, test } from "node:test";
3
3
  import { parse } from "../../src/compiler/parse.js";
4
4
  import { markers } from "../../src/compiler/parserHelpers.js";
5
5
  import * as ops from "../../src/runtime/ops.js";
6
- import { assertCodeEqual } from "./codeHelpers.js";
6
+ import { assertCodeEqual, assertCodeLocations } from "./codeHelpers.js";
7
7
 
8
8
  describe("Origami parser", () => {
9
9
  test("additiveExpression", () => {
@@ -1596,16 +1596,8 @@ function assertParse(
1596
1596
  assertCodeEqual(code, expected);
1597
1597
  }
1598
1598
 
1599
- function assertCodeLocations(code) {
1600
- assert(code.location, "no location");
1601
- for (const item of code) {
1602
- if (Array.isArray(item)) {
1603
- assertCodeLocations(item);
1604
- }
1605
- }
1606
- }
1607
-
1608
1599
  function assertThrows(startRule, source, message, position, mode = "shell") {
1600
+ // @ts-ignore We declare this so we can inspect it in debugger
1609
1601
  let code;
1610
1602
  try {
1611
1603
  code = parse(source, {
@@ -54,7 +54,7 @@ describe("expressionObject", () => {
54
54
  test("returned object values can be unpacked", async () => {
55
55
  const entries = [["data.json", `{ "a": 1 }`]];
56
56
  const parent = new ObjectTree({});
57
- parent.handlers = {
57
+ /** @type {any} */ (parent).handlers = {
58
58
  "json.handler": {
59
59
  unpack: JSON.parse,
60
60
  },
@@ -358,7 +358,7 @@ describe("ops", () => {
358
358
  const a = await tree.get("a");
359
359
  const b = await a.get("b");
360
360
  const scope = await ops.scope.call(b);
361
- assert.equal(await scope.get("c"), 1);
361
+ assert.equal(await scope?.get("c"), 1);
362
362
  });
363
363
 
364
364
  test("accepts an optional context", async () => {
@@ -372,7 +372,7 @@ describe("ops", () => {
372
372
  const a = await tree.get("a");
373
373
  const b = await a.get("b");
374
374
  const scope = await ops.scope.call(b, tree);
375
- assert.equal(await scope.get("c"), 1);
375
+ assert.equal(await scope?.get("c"), 1);
376
376
  });
377
377
  });
378
378