@weborigami/language 0.2.9 → 0.2.10

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,19 +1,19 @@
1
1
  {
2
2
  "name": "@weborigami/language",
3
- "version": "0.2.9",
3
+ "version": "0.2.10",
4
4
  "description": "Web Origami expression language compiler and runtime",
5
5
  "type": "module",
6
6
  "main": "./main.js",
7
7
  "types": "./index.ts",
8
8
  "devDependencies": {
9
- "@types/node": "22.13.5",
9
+ "@types/node": "22.13.13",
10
10
  "peggy": "4.2.0.",
11
11
  "typescript": "5.8.2",
12
12
  "yaml": "2.7.0"
13
13
  },
14
14
  "dependencies": {
15
- "@weborigami/async-tree": "0.2.9",
16
- "@weborigami/types": "0.2.9",
15
+ "@weborigami/async-tree": "0.2.10",
16
+ "@weborigami/types": "0.2.10",
17
17
  "watcher": "2.3.1"
18
18
  },
19
19
  "scripts": {
@@ -34,7 +34,7 @@ export default function optimize(
34
34
  switch (fn) {
35
35
  case ops.lambda:
36
36
  const parameters = args[0];
37
- additionalLocalNames = parameters;
37
+ additionalLocalNames = parameters.map((param) => param[1]);
38
38
  break;
39
39
 
40
40
  case ops.literal:
@@ -61,7 +61,7 @@ export default function optimize(
61
61
  } else if (enableCaching && !locals[normalizedKey]) {
62
62
  // Upgrade to cached external scope reference
63
63
  return annotate(
64
- [ops.external, key, [ops.scope, key], cache],
64
+ [ops.external, key, annotate([ops.scope, key], code.location), cache],
65
65
  code.location
66
66
  );
67
67
  } else if (fn === undetermined) {
@@ -110,13 +110,15 @@ export default function optimize(
110
110
  }
111
111
 
112
112
  // Optimize children
113
- const optimized = code.map((child) => {
114
- if (Array.isArray(child) && "location" in child) {
113
+ const optimized = code.map((child, index) => {
114
+ // Don't optimize lambda parameter names
115
+ if (fn === ops.lambda && index === 1) {
116
+ return child;
117
+ } else if (Array.isArray(child) && "location" in child) {
115
118
  // Review: This currently descends into arrays that are not instructions,
116
- // such as the parameters of a lambda. This should be harmless, but it'd
119
+ // such as the entries of an ops.object. This should be harmless, but it'd
117
120
  // be preferable to only descend into instructions. This would require
118
- // surrounding ops.lambda parameters with ops.literal, and ops.object
119
- // entries with ops.array.
121
+ // surrounding ops.object entries with ops.array.
120
122
  return optimize(
121
123
  /** @type {AnnotatedCode} */ (child),
122
124
  enableCaching,
@@ -50,7 +50,7 @@ arguments "function arguments"
50
50
  / templateLiteral
51
51
 
52
52
  arrayLiteral "array"
53
- = "[" __ entries:arrayEntries? __ closingBracket {
53
+ = "[" __ entries:arrayEntries? __ expectClosingBracket {
54
54
  return makeArray(entries ?? [], location());
55
55
  }
56
56
 
@@ -69,13 +69,12 @@ arrayEntry
69
69
  }
70
70
 
71
71
  arrowFunction
72
- = "(" __ parameters:identifierList? __ ")" __ doubleArrow __ pipeline:pipelineExpression {
73
- const lambdaParameters = annotate(parameters ?? [], location());
72
+ = "(" __ parameters:parameterList? __ ")" __ doubleArrow __ pipeline:expectPipelineExpression {
73
+ const lambdaParameters = parameters ?? annotate([], location());
74
74
  return annotate([ops.lambda, lambdaParameters, pipeline], location());
75
75
  }
76
- / identifier:identifier __ doubleArrow __ pipeline:pipelineExpression {
77
- const lambdaParameters = annotate([identifier], location())
78
- return annotate([ops.lambda, lambdaParameters, pipeline], location());
76
+ / parameter:parameterSingleton __ doubleArrow __ pipeline:expectPipelineExpression {
77
+ return annotate([ops.lambda, parameter, pipeline], location());
79
78
  }
80
79
  / conditionalExpression
81
80
 
@@ -110,30 +109,6 @@ callExpression "function call"
110
109
  return tail.reduce(makeCall, head);
111
110
  }
112
111
 
113
- // Required closing curly brace. We use this for the `object` term: if the
114
- // parser sees a left curly brace, here we must see a right curly brace.
115
- closingBrace
116
- = "}"
117
- / .? {
118
- error(`An object ended without a closing brace, or contained something that wasn't expected.\nThe top level of an object can only contain definitions ("a: b" or "a = b") or spreads ("...a").`);
119
- }
120
-
121
- // Required closing bracket
122
- closingBracket
123
- = "]"
124
- / .? {
125
- error("Expected right bracket");
126
- }
127
-
128
- // Required closing parenthesis. We use this for the `group` term: it's the last
129
- // term in the `step` parser that starts with a parenthesis, so if that parser
130
- // sees a left parenthesis, here we must see a right parenthesis.
131
- closingParenthesis
132
- = ")"
133
- / .? {
134
- error("Expected right parenthesis");
135
- }
136
-
137
112
  // A comma-separated list of expressions: `x, y, z`
138
113
  commaExpression
139
114
  // The commas are required, but the list can have a single item.
@@ -171,7 +146,7 @@ digits
171
146
  doubleArrow = "⇒" / "=>"
172
147
 
173
148
  doubleQuoteString "double quote string"
174
- = '"' chars:doubleQuoteStringChar* '"' {
149
+ = '"' chars:doubleQuoteStringChar* expectDoubleQuote {
175
150
  return annotate([ops.literal, chars.join("")], location());
176
151
  }
177
152
 
@@ -201,6 +176,55 @@ escapedChar "backslash-escaped character"
201
176
  / "\\v" { return "\v"; }
202
177
  / "\\" @.
203
178
 
179
+ expectBacktick
180
+ = "`"
181
+ / .? {
182
+ error("Expected closing backtick");
183
+ }
184
+
185
+ expectClosingBrace
186
+ = "}"
187
+ / .? {
188
+ error(`An object ended without a closing brace, or contained something that wasn't expected.\nThe top level of an object can only contain definitions ("a: b" or "a = b") or spreads ("...a").`);
189
+ }
190
+
191
+ expectClosingBracket
192
+ = "]"
193
+ / .? {
194
+ error("Expected right bracket");
195
+ }
196
+
197
+ expectClosingParenthesis
198
+ = ")"
199
+ / .? {
200
+ error("Expected right parenthesis");
201
+ }
202
+
203
+ expectDoubleQuote
204
+ = '"'
205
+ / .? {
206
+ error("Expected closing quote");
207
+ }
208
+
209
+ expectGuillemet
210
+ = '»'
211
+ / .? {
212
+ error("Expected closing guillemet");
213
+ }
214
+
215
+ expectSingleQuote
216
+ = "'"
217
+ / .? {
218
+ error("Expected closing quote");
219
+ }
220
+
221
+ // Required expression
222
+ expectPipelineExpression
223
+ = pipelineExpression
224
+ / .? {
225
+ error("Expected an expression");
226
+ }
227
+
204
228
  exponentiationExpression
205
229
  = left:unaryExpression right:(__ "**" __ @exponentiationExpression)? {
206
230
  return right ? annotate([ops.exponentiation, left, right], location()) : left;
@@ -217,12 +241,12 @@ floatLiteral "floating-point number"
217
241
 
218
242
  // An expression in parentheses: `(foo)`
219
243
  group "parenthetical group"
220
- = "(" expression:expression closingParenthesis {
244
+ = "(" expression:expression expectClosingParenthesis {
221
245
  return annotate(downgradeReference(expression), location());
222
246
  }
223
247
 
224
248
  guillemetString "guillemet string"
225
- = '«' chars:guillemetStringChar* '»' {
249
+ = '«' chars:guillemetStringChar* expectGuillemet {
226
250
  return annotate([ops.literal, chars.join("")], location());
227
251
  }
228
252
 
@@ -254,11 +278,6 @@ identifierChar
254
278
  / @'-' !'>' // Accept a hyphen but not in a single arrow combination
255
279
  / escapedChar
256
280
 
257
- identifierList
258
- = list:identifier|1.., separator| separator? {
259
- return annotate(list, location());
260
- }
261
-
262
281
  implicitParenthesesCallExpression "function call with implicit parentheses"
263
282
  = head:arrowFunction args:(inlineSpace+ @implicitParensthesesArguments)? {
264
283
  return args ? makeCall(head, args) : head;
@@ -350,7 +369,7 @@ nullishCoalescingExpression
350
369
 
351
370
  // An object literal: `{foo: 1, bar: 2}`
352
371
  objectLiteral "object literal"
353
- = "{" __ entries:objectEntries? __ closingBrace {
372
+ = "{" __ entries:objectEntries? __ expectClosingBrace {
354
373
  return makeObject(entries ?? [], location());
355
374
  }
356
375
 
@@ -368,7 +387,7 @@ objectEntry
368
387
 
369
388
  // A getter definition inside an object literal: `foo = 1`
370
389
  objectGetter "object getter"
371
- = key:objectKey __ "=" __ pipeline:pipelineExpression {
390
+ = key:objectKey __ "=" __ pipeline:expectPipelineExpression {
372
391
  return annotate(
373
392
  makeProperty(key, annotate([ops.getter, pipeline], location())),
374
393
  location()
@@ -384,7 +403,7 @@ objectKey "object key"
384
403
 
385
404
  // A property definition in an object literal: `x: 1`
386
405
  objectProperty "object property"
387
- = key:objectKey __ ":" __ pipeline:pipelineExpression {
406
+ = key:objectKey __ ":" __ pipeline:expectPipelineExpression {
388
407
  return annotate(makeProperty(key, pipeline), location());
389
408
  }
390
409
 
@@ -404,9 +423,28 @@ objectPublicKey
404
423
  return string[1];
405
424
  }
406
425
 
426
+ parameter
427
+ = identifier:identifier {
428
+ return annotate([ops.literal, identifier], location());
429
+ }
430
+
431
+ parameterList
432
+ = list:parameter|1.., separator| separator? {
433
+ return annotate(list, location());
434
+ }
435
+
436
+ // A list with a single identifier
437
+ parameterSingleton
438
+ = identifier:identifier {
439
+ return annotate(
440
+ [annotate([ops.literal, identifier], location())],
441
+ location()
442
+ );
443
+ }
444
+
407
445
  // Function arguments in parentheses
408
446
  parenthesesArguments "function arguments in parentheses"
409
- = "(" __ list:list? __ ")" {
447
+ = "(" __ list:list? __ expectClosingParenthesis {
410
448
  return annotate(list ?? [undefined], location());
411
449
  }
412
450
 
@@ -535,7 +573,10 @@ shiftOperator
535
573
  shorthandFunction "lambda function"
536
574
  // Avoid a following equal sign (for an equality)
537
575
  = "=" !"=" __ definition:implicitParenthesesCallExpression {
538
- const lambdaParameters = annotate(["_"], location());
576
+ const lambdaParameters = annotate(
577
+ [annotate([ops.literal, "_"], location())],
578
+ location()
579
+ );
539
580
  return annotate([ops.lambda, lambdaParameters, definition], location());
540
581
  }
541
582
  / implicitParenthesesCallExpression
@@ -548,7 +589,7 @@ singleLineComment
548
589
  = "//" [^\n\r]* { return null; }
549
590
 
550
591
  singleQuoteString "single quote string"
551
- = "'" chars:singleQuoteStringChar* "'" {
592
+ = "'" chars:singleQuoteStringChar* expectSingleQuote {
552
593
  return annotate([ops.literal, chars.join("")], location());
553
594
  }
554
595
 
@@ -582,7 +623,10 @@ stringLiteral "string"
582
623
  // literal, but can contain backticks at the top level.
583
624
  templateDocument "template"
584
625
  = head:templateDocumentText tail:(templateSubstitution templateDocumentText)* {
585
- const lambdaParameters = annotate(["_"], location());
626
+ const lambdaParameters = annotate(
627
+ [annotate([ops.literal, "_"], location())],
628
+ location()
629
+ );
586
630
  return annotate(
587
631
  [ops.lambda, lambdaParameters, makeTemplate(ops.templateIndent, head, tail, location())],
588
632
  location()
@@ -600,7 +644,7 @@ templateDocumentText "template text"
600
644
 
601
645
  // A backtick-quoted template literal
602
646
  templateLiteral "template literal"
603
- = "`" head:templateLiteralText tail:(templateSubstitution templateLiteralText)* "`" {
647
+ = "`" head:templateLiteralText tail:(templateSubstitution templateLiteralText)* expectBacktick {
604
648
  return makeTemplate(ops.template, head, tail, location());
605
649
  }
606
650