@weborigami/language 0.6.3 → 0.6.5
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/main.js +1 -0
- package/package.json +2 -2
- package/src/compiler/optimize.js +19 -17
- package/src/compiler/origami.pegjs +237 -80
- package/src/compiler/parse.js +2361 -1169
- package/src/compiler/parserHelpers.js +283 -34
- package/src/project/jsGlobals.js +2 -2
- package/src/runtime/expressionObject.js +42 -6
- package/src/runtime/ops.js +44 -27
- package/test/compiler/compile.test.js +53 -0
- package/test/compiler/optimize.test.js +2 -1
- package/test/compiler/parse.test.js +543 -153
- package/test/runtime/expressionObject.test.js +11 -0
- package/test/runtime/ops.test.js +46 -8
package/main.js
CHANGED
|
@@ -12,6 +12,7 @@ export { formatError, highlightError } from "./src/runtime/errors.js";
|
|
|
12
12
|
export { default as evaluate } from "./src/runtime/evaluate.js";
|
|
13
13
|
export { default as EventTargetMixin } from "./src/runtime/EventTargetMixin.js";
|
|
14
14
|
export * as expressionFunction from "./src/runtime/expressionFunction.js";
|
|
15
|
+
export { default as expressionObject } from "./src/runtime/expressionObject.js";
|
|
15
16
|
export * from "./src/runtime/handleExtension.js";
|
|
16
17
|
export { default as handleExtension } from "./src/runtime/handleExtension.js";
|
|
17
18
|
export { default as HandleExtensionsTransform } from "./src/runtime/HandleExtensionsTransform.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@weborigami/language",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.5",
|
|
4
4
|
"description": "Web Origami expression language compiler and runtime",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./main.js",
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"typescript": "5.9.3"
|
|
12
12
|
},
|
|
13
13
|
"dependencies": {
|
|
14
|
-
"@weborigami/async-tree": "0.6.
|
|
14
|
+
"@weborigami/async-tree": "0.6.5",
|
|
15
15
|
"exif-parser": "0.1.12",
|
|
16
16
|
"watcher": "2.3.1",
|
|
17
17
|
"yaml": "2.8.1"
|
package/src/compiler/optimize.js
CHANGED
|
@@ -47,9 +47,9 @@ export default function optimize(code, options = {}) {
|
|
|
47
47
|
return resolvePath(code, globals, parent, locals, cache);
|
|
48
48
|
|
|
49
49
|
case ops.lambda:
|
|
50
|
-
const parameters = args[
|
|
50
|
+
const parameters = args[1];
|
|
51
51
|
if (parameters.length > 0) {
|
|
52
|
-
const paramNames = parameters.map((param) => param[
|
|
52
|
+
const paramNames = parameters.map((param) => param[0]);
|
|
53
53
|
locals.push({
|
|
54
54
|
type: REFERENCE_PARAM,
|
|
55
55
|
names: paramNames,
|
|
@@ -62,7 +62,10 @@ export default function optimize(code, options = {}) {
|
|
|
62
62
|
|
|
63
63
|
case ops.object:
|
|
64
64
|
const entries = args;
|
|
65
|
-
|
|
65
|
+
// Filter out computed property keys when determining local variables
|
|
66
|
+
const propertyNames = entries
|
|
67
|
+
.map((entry) => entryKey(entry))
|
|
68
|
+
.filter((key) => key !== null);
|
|
66
69
|
locals.push({
|
|
67
70
|
type: REFERENCE_INHERITED,
|
|
68
71
|
names: propertyNames,
|
|
@@ -73,22 +76,21 @@ export default function optimize(code, options = {}) {
|
|
|
73
76
|
// Optimize children
|
|
74
77
|
const optimized = annotate(
|
|
75
78
|
code.map((child, index) => {
|
|
76
|
-
|
|
77
|
-
if (op === ops.lambda && index === 1) {
|
|
78
|
-
return child;
|
|
79
|
-
} else if (op === ops.object && index > 0) {
|
|
79
|
+
if (op === ops.object && index > 0) {
|
|
80
80
|
const [key, value] = child;
|
|
81
81
|
const adjustedLocals = avoidLocalRecursion(locals, key);
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
key
|
|
85
|
-
optimize(/** @type {AnnotatedCode} */ (
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
82
|
+
const optimizedKey =
|
|
83
|
+
typeof key === "string"
|
|
84
|
+
? key
|
|
85
|
+
: optimize(/** @type {AnnotatedCode} */ (key), {
|
|
86
|
+
...options,
|
|
87
|
+
locals: adjustedLocals,
|
|
88
|
+
});
|
|
89
|
+
const optimizedValue = optimize(/** @type {AnnotatedCode} */ (value), {
|
|
90
|
+
...options,
|
|
91
|
+
locals: adjustedLocals,
|
|
92
|
+
});
|
|
93
|
+
return annotate([optimizedKey, optimizedValue], child.location);
|
|
92
94
|
} else if (Array.isArray(child) && "location" in child) {
|
|
93
95
|
// Review: Aside from ops.object (above), what non-instruction arrays
|
|
94
96
|
// does this descend into?
|
|
@@ -18,8 +18,10 @@ import {
|
|
|
18
18
|
makeArray,
|
|
19
19
|
makeBinaryOperation,
|
|
20
20
|
makeCall,
|
|
21
|
+
makeCallChain,
|
|
21
22
|
makeDeferredArguments,
|
|
22
23
|
makeDocument,
|
|
24
|
+
makeLambda,
|
|
23
25
|
makeObject,
|
|
24
26
|
makePath,
|
|
25
27
|
makePipeline,
|
|
@@ -83,7 +85,7 @@ arguments "function arguments"
|
|
|
83
85
|
/ pathArguments
|
|
84
86
|
/ propertyAccess
|
|
85
87
|
/ computedPropertyAccess
|
|
86
|
-
|
|
88
|
+
/ optional
|
|
87
89
|
/ templateLiteral
|
|
88
90
|
|
|
89
91
|
arrayLiteral "array"
|
|
@@ -100,18 +102,18 @@ arrayEntries
|
|
|
100
102
|
arrayEntry
|
|
101
103
|
= spreadElement
|
|
102
104
|
/ pipelineExpression
|
|
103
|
-
|
|
104
|
-
|
|
105
|
+
/ &separator {
|
|
106
|
+
// Missing value is allowed
|
|
105
107
|
return annotate([ops.literal, undefined], location());
|
|
106
108
|
}
|
|
107
109
|
|
|
108
110
|
arrowFunction
|
|
109
|
-
= ("async" __)? "(" __ parameters:
|
|
110
|
-
|
|
111
|
-
return
|
|
111
|
+
= ("async" __)? "(" __ parameters:paramList? __ ")" __ doubleArrow __ body:expectPipelineExpression {
|
|
112
|
+
parameters ??= annotate([], location());
|
|
113
|
+
return makeLambda(parameters, body, location());
|
|
112
114
|
}
|
|
113
|
-
/
|
|
114
|
-
return
|
|
115
|
+
/ parameters:paramSingleton __ doubleArrow __ body:expectPipelineExpression {
|
|
116
|
+
return makeLambda(parameters, body, location());
|
|
115
117
|
}
|
|
116
118
|
/ conditionalExpression
|
|
117
119
|
|
|
@@ -143,10 +145,7 @@ bitwiseXorOperator
|
|
|
143
145
|
// `fn(arg1)(arg2)(arg3)`.
|
|
144
146
|
callExpression "function call"
|
|
145
147
|
= head:uriExpression tail:arguments* {
|
|
146
|
-
return tail
|
|
147
|
-
(target, args) => makeCall(target, args, location()),
|
|
148
|
-
head
|
|
149
|
-
);
|
|
148
|
+
return makeCallChain(head, tail, location());
|
|
150
149
|
}
|
|
151
150
|
|
|
152
151
|
// A comma-separated list of expressions: `x, y, z`
|
|
@@ -168,9 +167,9 @@ computedPropertyAccess
|
|
|
168
167
|
return annotate([markers.property, expression], location());
|
|
169
168
|
}
|
|
170
169
|
|
|
171
|
-
// A space before a computed property access. This is allowed when
|
|
172
|
-
//
|
|
173
|
-
//
|
|
170
|
+
// A space before a computed property access. This is allowed when not in shell
|
|
171
|
+
// mode. In shell mode `foo [bar]` should parse as a function call with a single
|
|
172
|
+
// argument of an array, not as a property access.
|
|
174
173
|
computedPropertySpace
|
|
175
174
|
= shellMode
|
|
176
175
|
/ !shellMode __
|
|
@@ -191,9 +190,6 @@ conditionalExpression
|
|
|
191
190
|
deferred[1]
|
|
192
191
|
], location());
|
|
193
192
|
}
|
|
194
|
-
|
|
195
|
-
digits
|
|
196
|
-
= @[0-9]+
|
|
197
193
|
|
|
198
194
|
doubleArrow = "⇒" / "=>"
|
|
199
195
|
|
|
@@ -271,7 +267,13 @@ expectFrontDelimiter
|
|
|
271
267
|
error("Expected \"---\"");
|
|
272
268
|
}
|
|
273
269
|
|
|
274
|
-
|
|
270
|
+
expectLeftGuillemet
|
|
271
|
+
= '«'
|
|
272
|
+
/ .? {
|
|
273
|
+
error("Expected closing guillemet");
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
expectRightGuillemet
|
|
275
277
|
= '»'
|
|
276
278
|
/ .? {
|
|
277
279
|
error("Expected closing guillemet");
|
|
@@ -305,11 +307,6 @@ exponentiationExpression
|
|
|
305
307
|
expression
|
|
306
308
|
= __ @commaExpression __
|
|
307
309
|
|
|
308
|
-
floatLiteral "floating-point number"
|
|
309
|
-
= digits? "." digits {
|
|
310
|
-
return annotate([ops.literal, parseFloat(text())], location());
|
|
311
|
-
}
|
|
312
|
-
|
|
313
310
|
// Marker for the beginning or end of front matter
|
|
314
311
|
frontDelimiter
|
|
315
312
|
= "---\n"
|
|
@@ -338,19 +335,22 @@ group "parenthetical group"
|
|
|
338
335
|
}
|
|
339
336
|
|
|
340
337
|
guillemetString "guillemet string"
|
|
341
|
-
=
|
|
342
|
-
|
|
343
|
-
|
|
338
|
+
= "«" chars:guillemetStringChar* expectRightGuillemet {
|
|
339
|
+
return annotate([ops.literal, chars.join("")], location());
|
|
340
|
+
}
|
|
341
|
+
/ "»" chars:guillemetStringChar* expectLeftGuillemet {
|
|
342
|
+
return annotate([ops.literal, chars.join("")], location());
|
|
343
|
+
}
|
|
344
344
|
|
|
345
345
|
guillemetStringChar
|
|
346
|
-
= !(
|
|
346
|
+
= !("«" / "»" / newLine) @textChar
|
|
347
347
|
|
|
348
348
|
// A host identifier that may include a colon and port number: `example.com:80`.
|
|
349
349
|
// This is used as a special case at the head of a path, where we want to
|
|
350
350
|
// interpret a colon as part of a text identifier.
|
|
351
351
|
host "HTTP/HTTPS host"
|
|
352
|
-
= name:hostname port:
|
|
353
|
-
const portText = port
|
|
352
|
+
= name:hostname port:port? slashFollows:slashFollows? {
|
|
353
|
+
const portText = port ?? "";
|
|
354
354
|
const slashText = slashFollows ? "/" : "";
|
|
355
355
|
const host = name + portText + slashText;
|
|
356
356
|
return annotate([ops.literal, host], location());
|
|
@@ -383,26 +383,32 @@ identifierPart "JavaScript identifier continuation"
|
|
|
383
383
|
identifierStart "JavaScript identifier start"
|
|
384
384
|
= char:. &{ return char.match(/[$_\p{ID_Start}]/u) }
|
|
385
385
|
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
386
|
+
// A single argument for an implicit parens call. This differs from
|
|
387
|
+
// `parenthesesArgument` in that the term can't be a pipeline.
|
|
388
|
+
implicitParenthesesArgument
|
|
389
|
+
= "..." arg:shorthandFunction {
|
|
390
|
+
return annotate([markers.spread, arg], location());
|
|
391
|
+
}
|
|
392
|
+
/ shorthandFunction
|
|
393
|
+
|
|
394
|
+
// A separated list of arguments for an implicit parens call.
|
|
395
|
+
implicitParenthesesArgumentList "list"
|
|
396
|
+
= args:implicitParenthesesArgument|1.., separator| separator? {
|
|
397
|
+
return annotate(args, location());
|
|
389
398
|
}
|
|
390
399
|
|
|
391
|
-
// A separated list of values for an implicit parens call.
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
400
|
+
// A separated list of values for an implicit parens call.
|
|
401
|
+
implicitParenthesesArguments
|
|
402
|
+
= shellMode inlineSpace+ @implicitParenthesesArgumentList
|
|
403
|
+
|
|
404
|
+
implicitParenthesesCallExpression "function call with implicit parentheses"
|
|
405
|
+
= head:arrowFunction args:implicitParenthesesArguments? {
|
|
406
|
+
return args ? makeCall(head, args, location()) : head;
|
|
396
407
|
}
|
|
397
408
|
|
|
398
409
|
inlineSpace
|
|
399
410
|
= [ \t]
|
|
400
411
|
|
|
401
|
-
integerLiteral "integer"
|
|
402
|
-
= digits {
|
|
403
|
-
return annotate([ops.literal, parseInt(text())], location());
|
|
404
|
-
}
|
|
405
|
-
|
|
406
412
|
// A key in a path or an expression that looks like one
|
|
407
413
|
key
|
|
408
414
|
= keyCharStart keyChar* {
|
|
@@ -426,16 +432,10 @@ keyChar
|
|
|
426
432
|
keyCharStart
|
|
427
433
|
// All JS identifier characters
|
|
428
434
|
= char:. &{ return char.match(/[$_\p{ID_Continue}]/u) }
|
|
429
|
-
/ "."
|
|
435
|
+
/ "." !".." // a dot, but not a spread/rest operator
|
|
430
436
|
/ "@"
|
|
431
437
|
/ "~"
|
|
432
438
|
|
|
433
|
-
// A separated list of values
|
|
434
|
-
list "list"
|
|
435
|
-
= values:pipelineExpression|1.., separator| separator? {
|
|
436
|
-
return annotate(values, location());
|
|
437
|
-
}
|
|
438
|
-
|
|
439
439
|
logicalAndExpression
|
|
440
440
|
= head:bitwiseOrExpression tail:(__ "&&" __ @bitwiseOrExpression)* {
|
|
441
441
|
return tail.length === 0
|
|
@@ -491,10 +491,52 @@ newLine
|
|
|
491
491
|
/ "\r\n"
|
|
492
492
|
/ "\r"
|
|
493
493
|
|
|
494
|
+
number
|
|
495
|
+
= numberBigInt
|
|
496
|
+
/ numberStandard {
|
|
497
|
+
const stripped = text().replace("_", ""); // remove underscores
|
|
498
|
+
return Number(stripped);
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
numberBigInt
|
|
502
|
+
= digits:numberDecimalDigits "n" {
|
|
503
|
+
const stripped = digits.replace("_", ""); // remove underscores
|
|
504
|
+
return BigInt(stripped);
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
numberBinary
|
|
508
|
+
= "0" [bB] digits:[01_]+
|
|
509
|
+
|
|
510
|
+
numberDecimal
|
|
511
|
+
= numberDecimalDigits? "." numberDecimalDigits numberExponent?
|
|
512
|
+
/ numberDecimalDigits numberExponent?
|
|
513
|
+
|
|
514
|
+
numberDecimalDigits
|
|
515
|
+
= [0-9]([_]*[0-9])* {
|
|
516
|
+
return text();
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
numberExponent
|
|
520
|
+
= [eE] [+\-]? digits:numberDecimalDigits
|
|
521
|
+
|
|
522
|
+
numberHex
|
|
523
|
+
= "0" [xX] digits:[0-9a-fA-F_]+
|
|
524
|
+
|
|
494
525
|
// A number
|
|
495
|
-
|
|
496
|
-
=
|
|
497
|
-
|
|
526
|
+
numberLiteral "number"
|
|
527
|
+
= number:number {
|
|
528
|
+
return annotate([ops.literal, number], location());
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
numberOctal
|
|
532
|
+
= "0" [oO] digits:[0-7]+
|
|
533
|
+
|
|
534
|
+
// Any number that can be parsed with Number(); i.e., not a bigint
|
|
535
|
+
numberStandard
|
|
536
|
+
= numberBinary
|
|
537
|
+
/ numberOctal
|
|
538
|
+
/ numberHex
|
|
539
|
+
/ numberDecimal
|
|
498
540
|
|
|
499
541
|
nullishCoalescingExpression
|
|
500
542
|
= head:logicalAndExpression tail:(__ "??" __ @logicalAndExpression)* {
|
|
@@ -561,7 +603,8 @@ objectShorthandProperty "object identifier"
|
|
|
561
603
|
}
|
|
562
604
|
|
|
563
605
|
objectPublicKey
|
|
564
|
-
=
|
|
606
|
+
= "[" __ @pipelineExpression __ expectClosingBracket
|
|
607
|
+
/ key:key slash:"/"? {
|
|
565
608
|
return text();
|
|
566
609
|
}
|
|
567
610
|
/ string:stringLiteral {
|
|
@@ -569,31 +612,144 @@ objectPublicKey
|
|
|
569
612
|
return string[1];
|
|
570
613
|
}
|
|
571
614
|
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
615
|
+
// Optional chaining
|
|
616
|
+
optional
|
|
617
|
+
= __ "?." args:parenthesesArguments {
|
|
618
|
+
return annotate([ops.optional, args], location());
|
|
619
|
+
}
|
|
620
|
+
/ __ "?." access:computedPropertyAccess {
|
|
621
|
+
return annotate([ops.optional, access], location());
|
|
622
|
+
}
|
|
623
|
+
/ __ "?." whitespaceOptionalForProgram property:identifierLiteral {
|
|
624
|
+
const propertyAccess = annotate([markers.property, property], location());
|
|
625
|
+
return annotate([ops.optional, propertyAccess], location());
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
paramArray
|
|
629
|
+
= "[" __ entries:paramArrayEntries __ "]" {
|
|
630
|
+
return annotate([markers.paramArray, ...entries], location());
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
// A list of parameters inside array destructuring: `a, b` in `([a, b]) => a + b`
|
|
634
|
+
// Different than top-level parameters because elements can be elided: `a, , b`
|
|
635
|
+
paramArrayEntries
|
|
636
|
+
= entries:paramArrayEntry|1.., separator| rest:(separator @paramRest?)? {
|
|
637
|
+
if (rest) {
|
|
638
|
+
entries.push(rest);
|
|
639
|
+
}
|
|
640
|
+
return annotate(entries, location());
|
|
641
|
+
}
|
|
642
|
+
/ rest:paramRest {
|
|
643
|
+
return annotate([rest], location());
|
|
575
644
|
}
|
|
576
645
|
|
|
577
|
-
//
|
|
578
|
-
|
|
646
|
+
// Single parameter in a function's parameter list
|
|
647
|
+
param
|
|
648
|
+
= paramNameWithInitilializer
|
|
649
|
+
/ pattern:paramBindingPattern initializer:paramInitializer? {
|
|
650
|
+
return initializer
|
|
651
|
+
? annotate([markers.paramInitializer, pattern, initializer], location())
|
|
652
|
+
: pattern;
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
paramArrayEntry
|
|
656
|
+
= param
|
|
657
|
+
/ &separator {
|
|
658
|
+
// Missing value is allowed
|
|
659
|
+
return undefined;
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
paramBindingPattern
|
|
663
|
+
= paramArray
|
|
664
|
+
/ paramObject
|
|
665
|
+
|
|
666
|
+
paramInitializer
|
|
667
|
+
= __ "=" __ @pipelineExpression
|
|
668
|
+
|
|
669
|
+
// A list of lambda parameters inside the parentheses: `a, b` in `(a, b) => a + b`
|
|
670
|
+
paramList
|
|
671
|
+
= entries:param|1.., separator| rest:(separator @paramRest?)? {
|
|
672
|
+
if (rest) {
|
|
673
|
+
entries.push(rest);
|
|
674
|
+
}
|
|
675
|
+
return annotate(entries, location());
|
|
676
|
+
}
|
|
677
|
+
/ rest:paramRest {
|
|
678
|
+
return annotate([rest], location());
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
// A single name in a parameter list: `a` in `a, b`
|
|
682
|
+
paramName
|
|
579
683
|
= key:key {
|
|
580
|
-
return annotate([
|
|
684
|
+
return annotate([markers.paramName, key], location());
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
paramNameWithInitilializer
|
|
688
|
+
= name:paramName initializer:paramInitializer? {
|
|
689
|
+
return initializer
|
|
690
|
+
? annotate([markers.paramInitializer, name, initializer], location())
|
|
691
|
+
: name;
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
// Object binding pattern in function parameter: `{ a, b: c }`
|
|
695
|
+
paramObject
|
|
696
|
+
= "{" __ entries:paramObjectEntries? __ "}" {
|
|
697
|
+
return annotate([markers.paramObject, ...(entries ?? [])], location());
|
|
581
698
|
}
|
|
582
699
|
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
700
|
+
// A separated list of parameter object entries inside the curly braces
|
|
701
|
+
paramObjectEntries
|
|
702
|
+
= entries:paramObjectEntry|1.., separator| rest:(separator @paramRest)? {
|
|
703
|
+
if (rest) {
|
|
704
|
+
entries.push(rest);
|
|
705
|
+
}
|
|
706
|
+
return annotate(entries, location());
|
|
707
|
+
}
|
|
708
|
+
/ rest:paramRest {
|
|
709
|
+
return annotate([rest], location());
|
|
586
710
|
}
|
|
587
711
|
|
|
588
|
-
//
|
|
589
|
-
|
|
590
|
-
= param:
|
|
712
|
+
// An entry in a parameter object: `a: b` in `{ a: b }`
|
|
713
|
+
paramObjectEntry
|
|
714
|
+
= key:objectPublicKey __ ":" __ param:param {
|
|
715
|
+
return annotate([key, param], location());
|
|
716
|
+
}
|
|
717
|
+
/ name:paramName initializer:paramInitializer? {
|
|
718
|
+
const binding = initializer
|
|
719
|
+
? annotate([markers.paramInitializer, name, initializer], location())
|
|
720
|
+
: name;
|
|
721
|
+
return annotate([name[1], binding], location());
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
// Optional rest parameter for param array or object
|
|
725
|
+
paramRest
|
|
726
|
+
= "..." __ param:param {
|
|
727
|
+
return annotate([markers.paramRest, param], location());
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
// A lambda parameter list with a single identifier with no parentheses:
|
|
731
|
+
// `x` in `x => x + 1`
|
|
732
|
+
paramSingleton
|
|
733
|
+
= param:paramName {
|
|
591
734
|
return annotate([param], location());
|
|
592
735
|
}
|
|
593
736
|
|
|
737
|
+
// A single argument inside parentheses
|
|
738
|
+
parenthesesArgument
|
|
739
|
+
= "..." arg:pipelineExpression {
|
|
740
|
+
return annotate([markers.spread, arg], location());
|
|
741
|
+
}
|
|
742
|
+
/ pipelineExpression
|
|
743
|
+
|
|
744
|
+
// A separated list of arguments inside parentheses
|
|
745
|
+
parenthesesArgumentList "list"
|
|
746
|
+
= args:parenthesesArgument|1.., separator| separator? {
|
|
747
|
+
return annotate(args, location());
|
|
748
|
+
}
|
|
749
|
+
|
|
594
750
|
// Function arguments in parentheses
|
|
595
751
|
parenthesesArguments "function arguments in parentheses"
|
|
596
|
-
= "(" __ list:
|
|
752
|
+
= inlineSpace* "(" __ list:parenthesesArgumentList? __ expectClosingParenthesis {
|
|
597
753
|
return annotate(list ?? [undefined], location());
|
|
598
754
|
}
|
|
599
755
|
|
|
@@ -635,6 +791,11 @@ pipelineExpression
|
|
|
635
791
|
);
|
|
636
792
|
}
|
|
637
793
|
|
|
794
|
+
port
|
|
795
|
+
= ":" [0-9]+ {
|
|
796
|
+
return text();
|
|
797
|
+
}
|
|
798
|
+
|
|
638
799
|
primary
|
|
639
800
|
// The following start with distinct characters
|
|
640
801
|
= stringLiteral
|
|
@@ -646,7 +807,7 @@ primary
|
|
|
646
807
|
/ templateLiteral
|
|
647
808
|
|
|
648
809
|
// These are more ambiguous
|
|
649
|
-
/ @
|
|
810
|
+
/ @numberLiteral !keyChar // numbers + chars would be a key
|
|
650
811
|
/ pathLiteral
|
|
651
812
|
|
|
652
813
|
// Top-level Origami progam with possible shebang directive (which is ignored)
|
|
@@ -716,15 +877,13 @@ shiftOperator
|
|
|
716
877
|
// A shorthand lambda expression: `=foo(_)`
|
|
717
878
|
shorthandFunction "lambda function"
|
|
718
879
|
// Avoid a following equal sign (for an equality)
|
|
719
|
-
= (shellMode / programMode) "=" !"=" __
|
|
880
|
+
= (shellMode / programMode) "=" !"=" __ body:implicitParenthesesCallExpression {
|
|
720
881
|
if (options.mode === "program") {
|
|
721
882
|
throw new Error("Parse error: shorthand function syntax isn't allowed in Origami programs. Use arrow syntax instead.");
|
|
722
883
|
}
|
|
723
|
-
const
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
);
|
|
727
|
-
return annotate([ops.lambda, lambdaParameters, definition], location());
|
|
884
|
+
const underscore = annotate([markers.paramName, "_"], location());
|
|
885
|
+
const parameters = annotate([underscore], location());
|
|
886
|
+
return makeLambda(parameters, body, location());
|
|
728
887
|
}
|
|
729
888
|
/ implicitParenthesesCallExpression
|
|
730
889
|
|
|
@@ -764,7 +923,7 @@ slashFollows
|
|
|
764
923
|
|
|
765
924
|
spreadElement
|
|
766
925
|
= ellipsis __ value:expectPipelineExpression {
|
|
767
|
-
return annotate([
|
|
926
|
+
return annotate([markers.spread, value], location());
|
|
768
927
|
}
|
|
769
928
|
|
|
770
929
|
stringLiteral "string"
|
|
@@ -799,11 +958,9 @@ templateDocument "template document"
|
|
|
799
958
|
if (options.front) {
|
|
800
959
|
return makeDocument(options.front, body, location());
|
|
801
960
|
}
|
|
802
|
-
const
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
);
|
|
806
|
-
return annotate([ops.lambda, lambdaParameters, body], location());
|
|
961
|
+
const underscore = annotate([markers.paramName, "_"], location());
|
|
962
|
+
const parameters = annotate([underscore], location());
|
|
963
|
+
return makeLambda(parameters, body, location());
|
|
807
964
|
}
|
|
808
965
|
|
|
809
966
|
// A backtick-quoted template literal
|