@weborigami/language 0.3.3-jse.3 → 0.3.4-jse.4
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 +3 -3
- package/src/compiler/compile.js +5 -3
- package/src/compiler/isOrigamiFrontMatter.js +4 -3
- package/src/compiler/optimize.js +225 -112
- package/src/compiler/origami.pegjs +214 -201
- package/src/compiler/parse.js +1403 -1473
- package/src/compiler/parserHelpers.js +65 -157
- package/src/runtime/expressionObject.js +13 -7
- package/src/runtime/handlers.js +1 -1
- package/src/runtime/jsGlobals.js +7 -0
- package/src/runtime/ops.js +4 -18
- package/test/compiler/compile.test.js +14 -9
- package/test/compiler/optimize.test.js +234 -64
- package/test/compiler/parse.test.js +618 -476
- package/test/runtime/expressionObject.test.js +2 -2
- package/test/runtime/handlers.test.js +2 -2
- package/src/runtime/templateStandard.js +0 -13
- package/test/runtime/templateText.test.js +0 -18
|
@@ -15,16 +15,14 @@ import * as ops from "../runtime/ops.js";
|
|
|
15
15
|
import {
|
|
16
16
|
annotate,
|
|
17
17
|
applyMacro,
|
|
18
|
-
downgradeReference,
|
|
19
18
|
makeArray,
|
|
20
19
|
makeBinaryOperation,
|
|
21
20
|
makeCall,
|
|
22
21
|
makeDeferredArguments,
|
|
23
22
|
makeDocument,
|
|
24
|
-
makeJsPropertyAccess,
|
|
25
23
|
makeObject,
|
|
24
|
+
makePath,
|
|
26
25
|
makePipeline,
|
|
27
|
-
makeProperty,
|
|
28
26
|
makeTemplate,
|
|
29
27
|
makeUnaryOperation,
|
|
30
28
|
makeYamlObject,
|
|
@@ -41,58 +39,51 @@ __
|
|
|
41
39
|
}
|
|
42
40
|
|
|
43
41
|
additiveExpression
|
|
44
|
-
= head:multiplicativeExpression tail:(
|
|
42
|
+
= head:multiplicativeExpression tail:(whitespace @additiveOperator whitespace @multiplicativeExpression)* {
|
|
45
43
|
return tail.reduce(makeBinaryOperation, head);
|
|
46
44
|
}
|
|
47
45
|
|
|
48
46
|
additiveOperator
|
|
49
47
|
= "+"
|
|
50
|
-
/
|
|
48
|
+
/ minus
|
|
51
49
|
|
|
52
50
|
angleBracketLiteral
|
|
53
|
-
= "<"
|
|
54
|
-
return annotate([
|
|
51
|
+
= "<" scheme:uriScheme "//"? path:angleBracketPath ">" {
|
|
52
|
+
return annotate([scheme, ...path], location());
|
|
55
53
|
}
|
|
56
|
-
/ "
|
|
57
|
-
const
|
|
58
|
-
return
|
|
54
|
+
/ "</" path:angleBracketPath ">" {
|
|
55
|
+
const external = annotate([markers.external, "/"], location());
|
|
56
|
+
return annotate([markers.traverse, external, ...path], location());
|
|
59
57
|
}
|
|
60
|
-
/ "<"
|
|
61
|
-
const
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
/ "<" __ path:angleBracketPath __ ">" {
|
|
65
|
-
// Angle bracket paths always reference scope
|
|
66
|
-
const scope = annotate([ops.scope], location());
|
|
67
|
-
return annotate([scope, ...path], location());
|
|
58
|
+
/ "<" path:angleBracketPath ">" {
|
|
59
|
+
const [head, ...tail] = path;
|
|
60
|
+
const external = annotate([markers.external, head[1]], location());
|
|
61
|
+
return annotate([markers.traverse, external, ...tail], location());
|
|
68
62
|
}
|
|
69
63
|
|
|
70
64
|
angleBracketPath
|
|
71
|
-
= @
|
|
65
|
+
= @angleBracketKey|0.., "/"| "/"?
|
|
72
66
|
|
|
73
|
-
|
|
67
|
+
// Single key in an angle bracket path, possibly with a trailing slash
|
|
68
|
+
angleBracketKey
|
|
74
69
|
= chars:angleBracketPathChar+ slashFollows:slashFollows? {
|
|
75
70
|
// Append a trailing slash if one follows (but don't consume it)
|
|
76
71
|
const key = chars.join("") + (slashFollows ? "/" : "");
|
|
77
72
|
return annotate([ops.literal, key], location());
|
|
78
73
|
}
|
|
79
74
|
|
|
80
|
-
// A single character in
|
|
75
|
+
// A single character in an angle bracket key
|
|
81
76
|
angleBracketPathChar
|
|
82
|
-
|
|
77
|
+
// Accept anything that doesn't end the angle bracket key or path
|
|
78
|
+
= [^/>\t\n\r]
|
|
83
79
|
/ escapedChar
|
|
84
80
|
|
|
85
|
-
angleBracketProtocol
|
|
86
|
-
= protocol:jsIdentifier ":" {
|
|
87
|
-
return annotate([markers.global, `${protocol[1]}:`], location());
|
|
88
|
-
}
|
|
89
|
-
|
|
90
81
|
arguments "function arguments"
|
|
91
82
|
= parenthesesArguments
|
|
92
|
-
/
|
|
93
|
-
/
|
|
83
|
+
/ pathArguments
|
|
84
|
+
/ propertyAccess
|
|
94
85
|
/ computedPropertyAccess
|
|
95
|
-
/ optionalChaining
|
|
86
|
+
// / optionalChaining
|
|
96
87
|
/ templateLiteral
|
|
97
88
|
|
|
98
89
|
arrayLiteral "array"
|
|
@@ -125,7 +116,7 @@ arrowFunction
|
|
|
125
116
|
/ conditionalExpression
|
|
126
117
|
|
|
127
118
|
bitwiseAndExpression
|
|
128
|
-
= head:equalityExpression tail:(
|
|
119
|
+
= head:equalityExpression tail:(whitespace @bitwiseAndOperator whitespace @equalityExpression)* {
|
|
129
120
|
return tail.reduce(makeBinaryOperation, head);
|
|
130
121
|
}
|
|
131
122
|
|
|
@@ -133,7 +124,7 @@ bitwiseAndOperator
|
|
|
133
124
|
= @"&" !"&"
|
|
134
125
|
|
|
135
126
|
bitwiseOrExpression
|
|
136
|
-
= head:bitwiseXorExpression tail:(
|
|
127
|
+
= head:bitwiseXorExpression tail:(whitespace @bitwiseOrOperator whitespace @bitwiseXorExpression)* {
|
|
137
128
|
return tail.reduce(makeBinaryOperation, head);
|
|
138
129
|
}
|
|
139
130
|
|
|
@@ -141,7 +132,7 @@ bitwiseOrOperator
|
|
|
141
132
|
= @"|" !"|"
|
|
142
133
|
|
|
143
134
|
bitwiseXorExpression
|
|
144
|
-
= head:bitwiseAndExpression tail:(
|
|
135
|
+
= head:bitwiseAndExpression tail:(whitespace @bitwiseXorOperator whitespace @bitwiseAndExpression)* {
|
|
145
136
|
return tail.reduce(makeBinaryOperation, head);
|
|
146
137
|
}
|
|
147
138
|
|
|
@@ -151,8 +142,11 @@ bitwiseXorOperator
|
|
|
151
142
|
// A function call: `fn(arg)`, possibly part of a chain of function calls, like
|
|
152
143
|
// `fn(arg1)(arg2)(arg3)`.
|
|
153
144
|
callExpression "function call"
|
|
154
|
-
= head:
|
|
155
|
-
return tail.reduce(
|
|
145
|
+
= head:uriExpression tail:arguments* {
|
|
146
|
+
return tail.reduce(
|
|
147
|
+
(target, args) => makeCall(target, args, location()),
|
|
148
|
+
head
|
|
149
|
+
);
|
|
156
150
|
}
|
|
157
151
|
|
|
158
152
|
// A comma-separated list of expressions: `x, y, z`
|
|
@@ -171,7 +165,7 @@ comment "comment"
|
|
|
171
165
|
|
|
172
166
|
computedPropertyAccess
|
|
173
167
|
= __ "[" expression:expression expectClosingBracket {
|
|
174
|
-
return annotate([markers.
|
|
168
|
+
return annotate([markers.property, expression], location());
|
|
175
169
|
}
|
|
176
170
|
|
|
177
171
|
conditionalExpression
|
|
@@ -185,9 +179,9 @@ conditionalExpression
|
|
|
185
179
|
const deferred = makeDeferredArguments(tail);
|
|
186
180
|
return annotate([
|
|
187
181
|
ops.conditional,
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
182
|
+
condition,
|
|
183
|
+
deferred[0],
|
|
184
|
+
deferred[1]
|
|
191
185
|
], location());
|
|
192
186
|
}
|
|
193
187
|
|
|
@@ -289,7 +283,7 @@ expectPipelineExpression
|
|
|
289
283
|
}
|
|
290
284
|
|
|
291
285
|
exponentiationExpression
|
|
292
|
-
= left:unaryExpression right:(
|
|
286
|
+
= left:unaryExpression right:(whitespace "**" whitespace @exponentiationExpression)? {
|
|
293
287
|
return right ? annotate([ops.exponentiation, left, right], location()) : left;
|
|
294
288
|
}
|
|
295
289
|
|
|
@@ -326,7 +320,7 @@ frontMatterYaml "YAML front matter"
|
|
|
326
320
|
// An expression in parentheses: `(foo)`
|
|
327
321
|
group "parenthetical group"
|
|
328
322
|
= "(" expression:expression expectClosingParenthesis {
|
|
329
|
-
return annotate(
|
|
323
|
+
return annotate(expression, location());
|
|
330
324
|
}
|
|
331
325
|
|
|
332
326
|
guillemetString "guillemet string"
|
|
@@ -337,34 +331,47 @@ guillemetString "guillemet string"
|
|
|
337
331
|
guillemetStringChar
|
|
338
332
|
= !('»' / newLine) @textChar
|
|
339
333
|
|
|
340
|
-
// The user's home directory: `~`
|
|
341
|
-
homeDirectory
|
|
342
|
-
= "~" {
|
|
343
|
-
return annotate([ops.homeDirectory], location());
|
|
344
|
-
}
|
|
345
|
-
|
|
346
334
|
// A host identifier that may include a colon and port number: `example.com:80`.
|
|
347
335
|
// This is used as a special case at the head of a path, where we want to
|
|
348
336
|
// interpret a colon as part of a text identifier.
|
|
349
337
|
host "HTTP/HTTPS host"
|
|
350
|
-
=
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
338
|
+
= name:hostname port:(":" @integerLiteral)? slashFollows:slashFollows? {
|
|
339
|
+
const portText = port ? `:${port[1]}` : "";
|
|
340
|
+
const slashText = slashFollows ? "/" : "";
|
|
341
|
+
const host = name + portText + slashText;
|
|
342
|
+
return annotate([ops.literal, host], location());
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
hostname
|
|
346
|
+
= key {
|
|
347
|
+
return text();
|
|
355
348
|
}
|
|
356
349
|
|
|
357
|
-
|
|
358
|
-
|
|
350
|
+
// JavaScript-compatible identifier
|
|
351
|
+
identifier
|
|
352
|
+
= id:$( identifierStart identifierPart* ) {
|
|
353
|
+
return id;
|
|
354
|
+
}
|
|
359
355
|
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
356
|
+
// Identifier as a literal
|
|
357
|
+
identifierLiteral
|
|
358
|
+
= id:identifier {
|
|
359
|
+
return annotate([ops.literal, id], location());
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// Continuation of a JavaScript identifier
|
|
363
|
+
// https://tc39.es/ecma262/multipage/ecmascript-language-lexical-grammar.html#prod-IdentifierPart
|
|
364
|
+
identifierPart "JavaScript identifier continuation"
|
|
365
|
+
= char:. &{ return char.match(/[$_\p{ID_Continue}]/u) }
|
|
366
|
+
|
|
367
|
+
// Start of a JavaScript identifier
|
|
368
|
+
// https://tc39.es/ecma262/multipage/ecmascript-language-lexical-grammar.html#prod-IdentifierStart
|
|
369
|
+
identifierStart "JavaScript identifier start"
|
|
370
|
+
= char:. &{ return char.match(/[$_\p{ID_Start}]/u) }
|
|
364
371
|
|
|
365
372
|
implicitParenthesesCallExpression "function call with implicit parentheses"
|
|
366
373
|
= head:arrowFunction args:(inlineSpace+ @implicitParensthesesArguments)? {
|
|
367
|
-
return args ? makeCall(head, args,
|
|
374
|
+
return args ? makeCall(head, args, location()) : head;
|
|
368
375
|
}
|
|
369
376
|
|
|
370
377
|
// A separated list of values for an implicit parens call. This differs from
|
|
@@ -382,33 +389,32 @@ integerLiteral "integer"
|
|
|
382
389
|
return annotate([ops.literal, parseInt(text())], location());
|
|
383
390
|
}
|
|
384
391
|
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
= id:$( jsIdentifierStart jsIdentifierPart* ) {
|
|
390
|
-
return annotate([ops.literal, id], location());
|
|
392
|
+
// A key in a path or an expression that looks like one
|
|
393
|
+
key
|
|
394
|
+
= keyCharStart keyChar* {
|
|
395
|
+
return text();
|
|
391
396
|
}
|
|
392
397
|
|
|
393
|
-
//
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
return annotate([markers.traverse, property], location());
|
|
406
|
-
}
|
|
398
|
+
// Character after the first in a key
|
|
399
|
+
keyChar
|
|
400
|
+
= keyCharStart
|
|
401
|
+
// Also allow some math operators (not slash)
|
|
402
|
+
/ "!"
|
|
403
|
+
/ "+"
|
|
404
|
+
/ minus
|
|
405
|
+
/ "*"
|
|
406
|
+
/ "%"
|
|
407
|
+
/ "&"
|
|
408
|
+
/ "|"
|
|
409
|
+
/ "^"
|
|
407
410
|
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
411
|
+
// First character in a key
|
|
412
|
+
keyCharStart
|
|
413
|
+
// All JS identifier characters
|
|
414
|
+
= char:. &{ return char.match(/[$_\p{ID_Continue}]/u) }
|
|
415
|
+
/ "."
|
|
416
|
+
/ "~"
|
|
417
|
+
/ "@"
|
|
412
418
|
|
|
413
419
|
// A separated list of values
|
|
414
420
|
list "list"
|
|
@@ -421,7 +427,7 @@ logicalAndExpression
|
|
|
421
427
|
return tail.length === 0
|
|
422
428
|
? head
|
|
423
429
|
: annotate(
|
|
424
|
-
[ops.logicalAnd,
|
|
430
|
+
[ops.logicalAnd, head, ...makeDeferredArguments(tail)],
|
|
425
431
|
location()
|
|
426
432
|
);
|
|
427
433
|
}
|
|
@@ -431,16 +437,22 @@ logicalOrExpression
|
|
|
431
437
|
return tail.length === 0
|
|
432
438
|
? head
|
|
433
439
|
: annotate(
|
|
434
|
-
[ops.logicalOr,
|
|
440
|
+
[ops.logicalOr, head, ...makeDeferredArguments(tail)],
|
|
435
441
|
location()
|
|
436
442
|
);
|
|
437
443
|
}
|
|
438
444
|
|
|
445
|
+
// Unary or binary minus operator
|
|
446
|
+
minus
|
|
447
|
+
// Don't match a front matter delimiter or pipeline operator. For some reason,
|
|
448
|
+
// the negative lookahead !"--\n" doesn't work.
|
|
449
|
+
= @"-" !"-\n" !">"
|
|
450
|
+
|
|
439
451
|
multiLineComment
|
|
440
452
|
= "/*" (!"*/" .)* "*/" { return null; }
|
|
441
453
|
|
|
442
454
|
multiplicativeExpression
|
|
443
|
-
= head:exponentiationExpression tail:(
|
|
455
|
+
= head:exponentiationExpression tail:(whitespace @multiplicativeOperator whitespace @exponentiationExpression)* {
|
|
444
456
|
return tail.reduce(makeBinaryOperation, head);
|
|
445
457
|
}
|
|
446
458
|
|
|
@@ -449,19 +461,13 @@ multiplicativeOperator
|
|
|
449
461
|
/ "/"
|
|
450
462
|
/ "%"
|
|
451
463
|
|
|
452
|
-
// A namespace reference is a string of letters only, followed by a colon.
|
|
453
|
-
namespace
|
|
454
|
-
= chars:[A-Za-z]+ ":" {
|
|
455
|
-
return annotate([markers.global, chars.join("") + ":"], location());
|
|
456
|
-
}
|
|
457
|
-
|
|
458
464
|
// A new expression: `new Foo()`
|
|
459
465
|
newExpression
|
|
460
|
-
= "new" __ head:
|
|
466
|
+
= "new" __ head:pathLiteral tail:parenthesesArguments? {
|
|
461
467
|
const args = tail?.[0] !== undefined ? tail : [];
|
|
462
468
|
return annotate([ops.construct, head, ...args], location());
|
|
463
469
|
}
|
|
464
|
-
/ "new:" head:
|
|
470
|
+
/ "new:" head:pathLiteral tail:parenthesesArguments {
|
|
465
471
|
const args = tail?.[0] !== undefined ? tail : [];
|
|
466
472
|
return annotate([ops.construct, head, ...args], location());
|
|
467
473
|
}
|
|
@@ -481,7 +487,7 @@ nullishCoalescingExpression
|
|
|
481
487
|
return tail.length === 0
|
|
482
488
|
? head
|
|
483
489
|
: annotate(
|
|
484
|
-
[ops.nullishCoalescing,
|
|
490
|
+
[ops.nullishCoalescing, head, ...makeDeferredArguments(tail)],
|
|
485
491
|
location()
|
|
486
492
|
);
|
|
487
493
|
}
|
|
@@ -507,10 +513,8 @@ objectEntry
|
|
|
507
513
|
// A getter definition inside an object literal: `foo = 1`
|
|
508
514
|
objectGetter "object getter"
|
|
509
515
|
= key:objectKey __ "=" __ pipeline:expectPipelineExpression {
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
location()
|
|
513
|
-
);
|
|
516
|
+
const getter = annotate([ops.getter, pipeline], location());
|
|
517
|
+
return annotate([key, getter], location());
|
|
514
518
|
}
|
|
515
519
|
|
|
516
520
|
objectHiddenKey
|
|
@@ -523,16 +527,17 @@ objectKey "object key"
|
|
|
523
527
|
// A property definition in an object literal: `x: 1`
|
|
524
528
|
objectProperty "object property"
|
|
525
529
|
= key:objectKey __ ":" __ pipeline:expectPipelineExpression {
|
|
526
|
-
return annotate(
|
|
530
|
+
return annotate([key, pipeline], location());
|
|
527
531
|
}
|
|
528
532
|
|
|
529
533
|
// A shorthand reference inside an object literal: `foo`
|
|
530
534
|
objectShorthandProperty "object identifier"
|
|
531
535
|
= key:objectPublicKey {
|
|
532
536
|
const reference = annotate([markers.reference, key], location());
|
|
533
|
-
|
|
537
|
+
const traverse = annotate([markers.traverse, reference], location());
|
|
538
|
+
return annotate([key, traverse], location());
|
|
534
539
|
}
|
|
535
|
-
/
|
|
540
|
+
/ path:angleBracketLiteral {
|
|
536
541
|
let lastKey = path.at(-1);
|
|
537
542
|
if (lastKey instanceof Array) {
|
|
538
543
|
lastKey = lastKey[1]; // get scope identifier or literal
|
|
@@ -541,23 +546,23 @@ objectShorthandProperty "object identifier"
|
|
|
541
546
|
}
|
|
542
547
|
|
|
543
548
|
objectPublicKey
|
|
544
|
-
=
|
|
545
|
-
|
|
546
|
-
|
|
549
|
+
= key:key slash:"/"? {
|
|
550
|
+
return text();
|
|
551
|
+
}
|
|
547
552
|
/ string:stringLiteral {
|
|
548
553
|
// Remove `ops.literal` from the string code
|
|
549
554
|
return string[1];
|
|
550
555
|
}
|
|
551
556
|
|
|
552
557
|
optionalChaining
|
|
553
|
-
= __ "?." __ property:
|
|
558
|
+
= __ "?." __ property:identifier {
|
|
554
559
|
return annotate([ops.optionalTraverse, property], location());
|
|
555
560
|
}
|
|
556
|
-
|
|
561
|
+
|
|
562
|
+
// Name of a unction parameter
|
|
557
563
|
parameter
|
|
558
|
-
=
|
|
559
|
-
|
|
560
|
-
return annotate([ops.literal, identifier], location());
|
|
564
|
+
= key:key {
|
|
565
|
+
return annotate([ops.literal, key], location());
|
|
561
566
|
}
|
|
562
567
|
|
|
563
568
|
parameterList
|
|
@@ -567,11 +572,8 @@ parameterList
|
|
|
567
572
|
|
|
568
573
|
// A list with a single identifier
|
|
569
574
|
parameterSingleton
|
|
570
|
-
=
|
|
571
|
-
return annotate(
|
|
572
|
-
[annotate([ops.literal, identifier], location())],
|
|
573
|
-
location()
|
|
574
|
-
);
|
|
575
|
+
= param:parameter {
|
|
576
|
+
return annotate([param], location());
|
|
575
577
|
}
|
|
576
578
|
|
|
577
579
|
// Function arguments in parentheses
|
|
@@ -580,90 +582,66 @@ parenthesesArguments "function arguments in parentheses"
|
|
|
580
582
|
return annotate(list ?? [undefined], location());
|
|
581
583
|
}
|
|
582
584
|
|
|
583
|
-
// A slash-separated path of keys
|
|
584
|
-
|
|
585
|
-
// Path with at least a tail
|
|
586
|
-
= segments:pathSegment|1..| {
|
|
587
|
-
// Drop empty segments that represent consecutive or final slashes
|
|
588
|
-
segments = segments.filter(segment => segment);
|
|
589
|
-
return annotate(segments, location());
|
|
590
|
-
}
|
|
591
|
-
|
|
592
|
-
// A slash-separated path of keys that follows a call target
|
|
585
|
+
// A slash-separated path of keys that follows a call target, such as the path
|
|
586
|
+
// after the slash in `(x)/y/z`
|
|
593
587
|
pathArguments
|
|
594
|
-
=
|
|
595
|
-
|
|
588
|
+
= "/" keys:pathKeys? {
|
|
589
|
+
const args = keys ?? [];
|
|
590
|
+
return annotate([markers.traverse, ...args], location());
|
|
596
591
|
}
|
|
597
592
|
|
|
598
|
-
//
|
|
599
|
-
|
|
600
|
-
=
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
593
|
+
// Sequence of keys that may each have trailing slashes
|
|
594
|
+
pathKeys
|
|
595
|
+
= pathSegment|1..|
|
|
596
|
+
|
|
597
|
+
// A path without angle brackets
|
|
598
|
+
pathLiteral
|
|
599
|
+
= keys:pathKeys {
|
|
600
|
+
return makePath(keys);
|
|
601
|
+
}
|
|
605
602
|
|
|
603
|
+
// A path key with an optional trailing slash
|
|
606
604
|
pathSegment
|
|
607
|
-
= "/"
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
/ escapedChar
|
|
605
|
+
= key:key "/"? {
|
|
606
|
+
return annotate([ops.literal, text()], location());
|
|
607
|
+
}
|
|
608
|
+
// A single slash is a path key
|
|
609
|
+
/ "/" {
|
|
610
|
+
return annotate([ops.literal, text()], location());
|
|
611
|
+
}
|
|
615
612
|
|
|
616
613
|
// A pipeline that starts with a value and optionally applies a series of
|
|
617
614
|
// functions to it.
|
|
618
615
|
pipelineExpression
|
|
619
616
|
= head:shorthandFunction tail:(__ singleArrow __ @shorthandFunction)* {
|
|
620
617
|
return annotate(
|
|
621
|
-
tail.reduce((arg, fn) => makePipeline(arg, fn,
|
|
618
|
+
tail.reduce((arg, fn) => makePipeline(arg, fn, location()), head),
|
|
622
619
|
location()
|
|
623
620
|
);
|
|
624
621
|
}
|
|
625
622
|
|
|
626
623
|
primary
|
|
627
|
-
|
|
628
|
-
|
|
624
|
+
// The following start with distinct characters
|
|
625
|
+
= stringLiteral
|
|
629
626
|
/ arrayLiteral
|
|
630
627
|
/ objectLiteral
|
|
631
628
|
/ group
|
|
632
|
-
/
|
|
633
|
-
/ shellMode @primaryShell
|
|
634
|
-
/ jseMode @primaryJse
|
|
635
|
-
|
|
636
|
-
// Primary allowed in JSE mode
|
|
637
|
-
primaryJse
|
|
638
|
-
= angleBracketLiteral
|
|
639
|
-
/ jsReference
|
|
629
|
+
/ angleBracketLiteral
|
|
640
630
|
/ regexLiteral
|
|
631
|
+
/ templateLiteral
|
|
641
632
|
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
/
|
|
645
|
-
/ qualifiedReference
|
|
646
|
-
/ namespace
|
|
647
|
-
/ scopeReference
|
|
633
|
+
// These are more ambiguous
|
|
634
|
+
/ @numericLiteral !keyChar // numbers + chars would be a key
|
|
635
|
+
/ pathLiteral
|
|
648
636
|
|
|
649
637
|
// Top-level Origami progam with possible shebang directive (which is ignored)
|
|
650
638
|
program "Origami program"
|
|
651
639
|
= shebang? @expression
|
|
652
640
|
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
return makeCall(fn, keys, options.mode);
|
|
658
|
-
}
|
|
659
|
-
/ newExpression
|
|
660
|
-
/ primary
|
|
661
|
-
|
|
662
|
-
// A namespace followed by a key: `foo:x`
|
|
663
|
-
qualifiedReference
|
|
664
|
-
= fn:namespace reference:scopeReference {
|
|
665
|
-
return makeCall(fn, [reference[1]], options.mode);
|
|
666
|
-
}
|
|
641
|
+
propertyAccess
|
|
642
|
+
= __ "." __ property:identifierLiteral {
|
|
643
|
+
return annotate([markers.property, property], location());
|
|
644
|
+
}
|
|
667
645
|
|
|
668
646
|
regexFlags
|
|
669
647
|
= flags:[gimuy]* {
|
|
@@ -695,19 +673,6 @@ relationalOperator
|
|
|
695
673
|
/ ">="
|
|
696
674
|
/ ">"
|
|
697
675
|
|
|
698
|
-
// The root folder: `/`
|
|
699
|
-
rootDirectory
|
|
700
|
-
= &("/" !"/") {
|
|
701
|
-
return annotate([ops.rootDirectory], location());
|
|
702
|
-
}
|
|
703
|
-
|
|
704
|
-
scopeReference "scope reference"
|
|
705
|
-
= identifier:identifier slashFollows:slashFollows? {
|
|
706
|
-
const id = identifier + (slashFollows ? "/" : "");
|
|
707
|
-
const idCode = annotate([ops.literal, identifier], location());
|
|
708
|
-
return annotate([markers.reference, idCode], location());
|
|
709
|
-
}
|
|
710
|
-
|
|
711
676
|
separator
|
|
712
677
|
= __ "," __
|
|
713
678
|
/ @whitespaceWithNewLine
|
|
@@ -761,6 +726,12 @@ slash
|
|
|
761
726
|
return annotate([ops.literal, "/"], location());
|
|
762
727
|
}
|
|
763
728
|
|
|
729
|
+
// One or more consecutive slashes
|
|
730
|
+
slashes
|
|
731
|
+
= "/"+ {
|
|
732
|
+
return annotate([ops.literal, "/"], location());
|
|
733
|
+
}
|
|
734
|
+
|
|
764
735
|
// Check whether next character is a slash without consuming input
|
|
765
736
|
slashFollows
|
|
766
737
|
// This expression returned `undefined` if successful; we convert to `true`
|
|
@@ -796,13 +767,16 @@ templateBodyText "template text"
|
|
|
796
767
|
|
|
797
768
|
templateDocument "template document"
|
|
798
769
|
= front:frontMatterExpression __ body:templateBody {
|
|
799
|
-
const macroName =
|
|
770
|
+
const macroName = text().includes("@template") ? "@template" : "_template";
|
|
800
771
|
return annotate(applyMacro(front, macroName, body), location());
|
|
801
772
|
}
|
|
802
773
|
/ front:frontMatterYaml body:templateBody {
|
|
803
|
-
return makeDocument(
|
|
774
|
+
return makeDocument(front, body, location());
|
|
804
775
|
}
|
|
805
776
|
/ body:templateBody {
|
|
777
|
+
if (options.front) {
|
|
778
|
+
return makeDocument(options.front, body, location());
|
|
779
|
+
}
|
|
806
780
|
const lambdaParameters = annotate(
|
|
807
781
|
[annotate([ops.literal, "_"], location())],
|
|
808
782
|
location()
|
|
@@ -813,8 +787,7 @@ templateDocument "template document"
|
|
|
813
787
|
// A backtick-quoted template literal
|
|
814
788
|
templateLiteral "template literal"
|
|
815
789
|
= "`" head:templateLiteralText tail:(templateSubstitution templateLiteralText)* expectBacktick {
|
|
816
|
-
|
|
817
|
-
return makeTemplate(op, head, tail, location());
|
|
790
|
+
return makeTemplate(ops.templateTree, head, tail, location());
|
|
818
791
|
}
|
|
819
792
|
|
|
820
793
|
templateLiteralChar
|
|
@@ -838,29 +811,69 @@ textChar
|
|
|
838
811
|
|
|
839
812
|
// A unary prefix operator: `!x`
|
|
840
813
|
unaryExpression
|
|
841
|
-
= operator:unaryOperator __ expression:
|
|
814
|
+
= operator:unaryOperator __ expression:expectExpression {
|
|
842
815
|
return makeUnaryOperation(operator, expression, location());
|
|
843
816
|
}
|
|
844
817
|
/ callExpression
|
|
845
818
|
|
|
819
|
+
// URI
|
|
820
|
+
uri
|
|
821
|
+
// Double slashes after colon: `https://example.com/index.html`
|
|
822
|
+
= scheme:uriScheme "//" host:host path:("/" uriPath)? {
|
|
823
|
+
const rest = path ? path[1] : [];
|
|
824
|
+
const keys = annotate([host, ...rest], location());
|
|
825
|
+
return makeCall(scheme, keys, location());
|
|
826
|
+
}
|
|
827
|
+
// No slashes after colon: `files:assets`
|
|
828
|
+
/ scheme:uriScheme keys:pathKeys {
|
|
829
|
+
return makeCall(scheme, keys, location());
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
// URI expression
|
|
833
|
+
uriExpression
|
|
834
|
+
= uri
|
|
835
|
+
/ newExpression
|
|
836
|
+
/ primary
|
|
837
|
+
|
|
838
|
+
// A single key in a path, possibly with trailing slash: `a/`, `b`
|
|
839
|
+
uriKey
|
|
840
|
+
= chars:uriKeyChar+ "/"? {
|
|
841
|
+
return annotate([ops.literal, text()], location());
|
|
842
|
+
}
|
|
843
|
+
/ "/" {
|
|
844
|
+
// A single slash is a path key
|
|
845
|
+
return annotate([ops.literal, ""], location());
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
// A single character in a URI key
|
|
849
|
+
uriKeyChar
|
|
850
|
+
// Accept anything that doesn't end the URI key or path
|
|
851
|
+
= [^/,\)\]\}\s]
|
|
852
|
+
/ escapedChar
|
|
853
|
+
|
|
854
|
+
// A slash-separated path of keys: `a/b/c`
|
|
855
|
+
uriPath "slash-separated path"
|
|
856
|
+
= keys:uriKey|1..| {
|
|
857
|
+
return annotate(keys, location());
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
// URI scheme, commonly called a protocol
|
|
861
|
+
// See https://datatracker.ietf.org/doc/html/rfc3986#section-3.1
|
|
862
|
+
uriScheme
|
|
863
|
+
= [a-z][a-z0-9+-.]*[:] {
|
|
864
|
+
return annotate([markers.global, text()], location());
|
|
865
|
+
}
|
|
866
|
+
|
|
846
867
|
unaryOperator
|
|
847
868
|
= "!"
|
|
848
869
|
/ "+"
|
|
849
|
-
//
|
|
850
|
-
|
|
851
|
-
/ @"-" !"-\n"
|
|
852
|
-
// Don't match a path that starts with a tilde: ~/foo
|
|
853
|
-
/ @"~" !"/"
|
|
870
|
+
/ @"~" ![\/\)\]\}] // don't match `~/` or end of term
|
|
871
|
+
/ minus
|
|
854
872
|
|
|
855
873
|
whitespace
|
|
856
874
|
= inlineSpace
|
|
857
875
|
/ newLine
|
|
858
876
|
/ comment
|
|
859
877
|
|
|
860
|
-
// Whitespace required in shell mode, optional in JSE mode
|
|
861
|
-
whitespaceShell
|
|
862
|
-
= shellMode whitespace
|
|
863
|
-
/ jseMode __
|
|
864
|
-
|
|
865
878
|
whitespaceWithNewLine
|
|
866
879
|
= inlineSpace* comment? newLine __
|