@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 +4 -4
- package/src/compiler/optimize.js +9 -7
- package/src/compiler/origami.pegjs +90 -46
- package/src/compiler/parse.js +874 -696
- package/src/compiler/parserHelpers.js +4 -1
- package/src/runtime/EventTargetMixin.d.ts +1 -5
- package/src/runtime/ops.js +2 -1
- package/test/compiler/optimize.test.js +1 -1
- package/test/compiler/parse.test.js +50 -15
- package/test/runtime/ops.test.js +4 -1
package/package.json
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@weborigami/language",
|
|
3
|
-
"version": "0.2.
|
|
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.
|
|
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.
|
|
16
|
-
"@weborigami/types": "0.2.
|
|
15
|
+
"@weborigami/async-tree": "0.2.10",
|
|
16
|
+
"@weborigami/types": "0.2.10",
|
|
17
17
|
"watcher": "2.3.1"
|
|
18
18
|
},
|
|
19
19
|
"scripts": {
|
package/src/compiler/optimize.js
CHANGED
|
@@ -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
|
-
|
|
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
|
|
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.
|
|
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? __
|
|
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:
|
|
73
|
-
const lambdaParameters =
|
|
72
|
+
= "(" __ parameters:parameterList? __ ")" __ doubleArrow __ pipeline:expectPipelineExpression {
|
|
73
|
+
const lambdaParameters = parameters ?? annotate([], location());
|
|
74
74
|
return annotate([ops.lambda, lambdaParameters, pipeline], location());
|
|
75
75
|
}
|
|
76
|
-
/
|
|
77
|
-
|
|
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
|
|
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? __
|
|
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:
|
|
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:
|
|
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(
|
|
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(
|
|
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
|
|