@weborigami/language 0.3.2 → 0.3.3-jse.1
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 -1
- package/package.json +3 -3
- package/src/compiler/compile.js +2 -0
- package/src/compiler/origami.pegjs +118 -28
- package/src/compiler/parse.js +1506 -822
- package/src/compiler/parserHelpers.js +13 -12
- package/src/runtime/handlers.js +4 -1
- package/src/runtime/ops.js +34 -10
- package/src/runtime/{taggedTemplateIndent.js → templateIndent.js} +2 -2
- package/src/runtime/templateStandard.js +13 -0
- package/test/compiler/parse.test.js +142 -24
- package/test/runtime/ops.test.js +11 -2
- package/test/runtime/taggedTemplateIndent.test.js +1 -1
- package/test/runtime/templateText.test.js +18 -0
package/main.js
CHANGED
|
@@ -14,6 +14,6 @@ export { default as InvokeFunctionsTransform } from "./src/runtime/InvokeFunctio
|
|
|
14
14
|
export * as moduleCache from "./src/runtime/moduleCache.js";
|
|
15
15
|
export { default as OrigamiFiles } from "./src/runtime/OrigamiFiles.js";
|
|
16
16
|
export * as symbols from "./src/runtime/symbols.js";
|
|
17
|
-
export { default as taggedTemplateIndent } from "./src/runtime/
|
|
17
|
+
export { default as taggedTemplateIndent } from "./src/runtime/templateIndent.js";
|
|
18
18
|
export { default as TreeEvent } from "./src/runtime/TreeEvent.js";
|
|
19
19
|
export { default as WatchFilesMixin } from "./src/runtime/WatchFilesMixin.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@weborigami/language",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.3-jse.1",
|
|
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.
|
|
15
|
-
"@weborigami/types": "0.3.
|
|
14
|
+
"@weborigami/async-tree": "0.3.3-jse.1",
|
|
15
|
+
"@weborigami/types": "0.3.3-jse.1",
|
|
16
16
|
"watcher": "2.3.1",
|
|
17
17
|
"yaml": "2.7.0"
|
|
18
18
|
},
|
package/src/compiler/compile.js
CHANGED
|
@@ -4,12 +4,14 @@ import { parse } from "./parse.js";
|
|
|
4
4
|
|
|
5
5
|
function compile(source, options) {
|
|
6
6
|
const { macros, startRule } = options;
|
|
7
|
+
const mode = options.mode ?? "shell";
|
|
7
8
|
const enableCaching = options.scopeCaching ?? true;
|
|
8
9
|
if (typeof source === "string") {
|
|
9
10
|
source = { text: source };
|
|
10
11
|
}
|
|
11
12
|
const code = parse(source.text, {
|
|
12
13
|
grammarSource: source,
|
|
14
|
+
mode,
|
|
13
15
|
startRule,
|
|
14
16
|
});
|
|
15
17
|
const optimized = optimize(code, enableCaching, macros);
|
|
@@ -40,7 +40,7 @@ __
|
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
additiveExpression
|
|
43
|
-
= head:multiplicativeExpression tail:(
|
|
43
|
+
= head:multiplicativeExpression tail:(whitespaceShell @additiveOperator whitespaceShell @multiplicativeExpression)* {
|
|
44
44
|
return tail.reduce(makeBinaryOperation, head);
|
|
45
45
|
}
|
|
46
46
|
|
|
@@ -48,11 +48,49 @@ additiveOperator
|
|
|
48
48
|
= "+"
|
|
49
49
|
/ "-"
|
|
50
50
|
|
|
51
|
+
angleBracketLiteral
|
|
52
|
+
= "<" __ protocol:angleBracketProtocol "//" path:angleBracketPath __ ">" {
|
|
53
|
+
return annotate([protocol, ...path], location());
|
|
54
|
+
}
|
|
55
|
+
/ "<" protocol:angleBracketProtocol path:angleBracketPath ">" {
|
|
56
|
+
const [head, ...tail] = path;
|
|
57
|
+
const root = annotate([protocol, head], location());
|
|
58
|
+
return annotate([ops.traverse, root, ...tail], location());
|
|
59
|
+
}
|
|
60
|
+
/ "<" __ path:angleBracketPath __ ">" {
|
|
61
|
+
const [head, ...tail] = path;
|
|
62
|
+
const root = annotate([ops.scope, head[1]], location());
|
|
63
|
+
return tail.length === 0
|
|
64
|
+
? root
|
|
65
|
+
: annotate([ops.traverse, root, ...tail], location())
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
angleBracketPath
|
|
69
|
+
= @angleBracketPathKey|0.., "/"| "/"?
|
|
70
|
+
|
|
71
|
+
angleBracketPathKey
|
|
72
|
+
= chars:angleBracketPathChar+ slashFollows:slashFollows? {
|
|
73
|
+
// Append a trailing slash if one follows (but don't consume it)
|
|
74
|
+
const key = chars.join("") + (slashFollows ? "/" : "");
|
|
75
|
+
return annotate([ops.literal, key], location());
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// A single character in a slash-separated path segment
|
|
79
|
+
angleBracketPathChar
|
|
80
|
+
= [^/:<>] // Much more permissive than an identifier
|
|
81
|
+
/ escapedChar
|
|
82
|
+
|
|
83
|
+
angleBracketProtocol
|
|
84
|
+
= protocol:jsIdentifier ":" {
|
|
85
|
+
return annotate([ops.builtin, `${protocol}:`], location());
|
|
86
|
+
}
|
|
87
|
+
|
|
51
88
|
arguments "function arguments"
|
|
52
89
|
= parenthesesArguments
|
|
53
|
-
/ pathArguments
|
|
90
|
+
/ shellMode @pathArguments
|
|
54
91
|
/ jsPropertyAccess
|
|
55
92
|
/ computedPropertyAccess
|
|
93
|
+
/ optionalChaining
|
|
56
94
|
/ templateLiteral
|
|
57
95
|
|
|
58
96
|
arrayLiteral "array"
|
|
@@ -330,17 +368,10 @@ implicitParenthesesCallExpression "function call with implicit parentheses"
|
|
|
330
368
|
// A separated list of values for an implicit parens call. This differs from
|
|
331
369
|
// `list` in that the value term can't be a pipeline.
|
|
332
370
|
implicitParensthesesArguments
|
|
333
|
-
= values:shorthandFunction|1.., separator| separator? {
|
|
371
|
+
= shellMode values:shorthandFunction|1.., separator| separator? {
|
|
334
372
|
return annotate(values, location());
|
|
335
373
|
}
|
|
336
374
|
|
|
337
|
-
inherited
|
|
338
|
-
= rootDirectory
|
|
339
|
-
/ homeDirectory
|
|
340
|
-
/ qualifiedReference
|
|
341
|
-
/ namespace
|
|
342
|
-
/ scopeReference
|
|
343
|
-
|
|
344
375
|
inlineSpace
|
|
345
376
|
= [ \t]
|
|
346
377
|
|
|
@@ -349,6 +380,9 @@ integerLiteral "integer"
|
|
|
349
380
|
return annotate([ops.literal, parseInt(text())], location());
|
|
350
381
|
}
|
|
351
382
|
|
|
383
|
+
jseMode
|
|
384
|
+
= &{ return options.mode === "jse" }
|
|
385
|
+
|
|
352
386
|
jsIdentifier
|
|
353
387
|
= $( jsIdentifierStart jsIdentifierPart* )
|
|
354
388
|
|
|
@@ -368,16 +402,17 @@ jsPropertyAccess
|
|
|
368
402
|
return annotate([ops.traverse, literal], location());
|
|
369
403
|
}
|
|
370
404
|
|
|
405
|
+
jsReference "identifier reference"
|
|
406
|
+
= id:jsIdentifier {
|
|
407
|
+
return annotate([ops.scope, id], location());
|
|
408
|
+
}
|
|
409
|
+
|
|
371
410
|
// A separated list of values
|
|
372
411
|
list "list"
|
|
373
412
|
= values:pipelineExpression|1.., separator| separator? {
|
|
374
413
|
return annotate(values, location());
|
|
375
414
|
}
|
|
376
415
|
|
|
377
|
-
literal
|
|
378
|
-
= numericLiteral
|
|
379
|
-
/ stringLiteral
|
|
380
|
-
|
|
381
416
|
logicalAndExpression
|
|
382
417
|
= head:bitwiseOrExpression tail:(__ "&&" __ @bitwiseOrExpression)* {
|
|
383
418
|
return tail.length === 0
|
|
@@ -402,7 +437,7 @@ multiLineComment
|
|
|
402
437
|
= "/*" (!"*/" .)* "*/" { return null; }
|
|
403
438
|
|
|
404
439
|
multiplicativeExpression
|
|
405
|
-
= head:exponentiationExpression tail:(
|
|
440
|
+
= head:exponentiationExpression tail:(whitespaceShell @multiplicativeOperator whitespaceShell @exponentiationExpression)* {
|
|
406
441
|
return tail.reduce(makeBinaryOperation, head);
|
|
407
442
|
}
|
|
408
443
|
|
|
@@ -417,6 +452,13 @@ namespace
|
|
|
417
452
|
return annotate([ops.builtin, chars.join("") + ":"], location());
|
|
418
453
|
}
|
|
419
454
|
|
|
455
|
+
// A new expression: `new Foo()`
|
|
456
|
+
newExpression
|
|
457
|
+
= "new" __ head:jsReference tail:parenthesesArguments {
|
|
458
|
+
const args = tail?.[0] !== undefined ? tail : [];
|
|
459
|
+
return annotate([ops.construct, head, ...args], location());
|
|
460
|
+
}
|
|
461
|
+
|
|
420
462
|
newLine
|
|
421
463
|
= "\n"
|
|
422
464
|
/ "\r\n"
|
|
@@ -486,13 +528,18 @@ objectShorthandProperty "object identifier"
|
|
|
486
528
|
|
|
487
529
|
objectPublicKey
|
|
488
530
|
= identifier:identifier slash:"/"? {
|
|
489
|
-
|
|
490
|
-
|
|
531
|
+
return identifier + (slash ?? "");
|
|
532
|
+
}
|
|
491
533
|
/ string:stringLiteral {
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
534
|
+
// Remove `ops.literal` from the string code
|
|
535
|
+
return string[1];
|
|
536
|
+
}
|
|
495
537
|
|
|
538
|
+
optionalChaining
|
|
539
|
+
= __ "?." __ property:jsIdentifier {
|
|
540
|
+
const literal = annotate([ops.literal, property], location());
|
|
541
|
+
return annotate([ops.optionalTraverse, literal], location());
|
|
542
|
+
}
|
|
496
543
|
parameter
|
|
497
544
|
= identifier:identifier {
|
|
498
545
|
return annotate([ops.literal, identifier], location());
|
|
@@ -562,12 +609,27 @@ pipelineExpression
|
|
|
562
609
|
}
|
|
563
610
|
|
|
564
611
|
primary
|
|
565
|
-
=
|
|
612
|
+
= numericLiteral
|
|
613
|
+
/ stringLiteral
|
|
566
614
|
/ arrayLiteral
|
|
567
615
|
/ objectLiteral
|
|
568
616
|
/ group
|
|
569
617
|
/ templateLiteral
|
|
570
|
-
/
|
|
618
|
+
/ shellMode @primaryShell
|
|
619
|
+
/ jseMode @primaryJse
|
|
620
|
+
|
|
621
|
+
// Primary allowed in JSE mode
|
|
622
|
+
primaryJse
|
|
623
|
+
= angleBracketLiteral
|
|
624
|
+
/ jsReference
|
|
625
|
+
/ regexLiteral
|
|
626
|
+
|
|
627
|
+
primaryShell
|
|
628
|
+
= rootDirectory
|
|
629
|
+
/ homeDirectory
|
|
630
|
+
/ qualifiedReference
|
|
631
|
+
/ namespace
|
|
632
|
+
/ scopeReference
|
|
571
633
|
|
|
572
634
|
// Top-level Origami progam with possible shebang directive (which is ignored)
|
|
573
635
|
program "Origami program"
|
|
@@ -579,6 +641,7 @@ protocolExpression
|
|
|
579
641
|
const keys = annotate([host, ...(path ?? [])], location());
|
|
580
642
|
return makeCall(fn, keys);
|
|
581
643
|
}
|
|
644
|
+
/ newExpression
|
|
582
645
|
/ primary
|
|
583
646
|
|
|
584
647
|
// A namespace followed by a key: `foo:x`
|
|
@@ -588,6 +651,21 @@ qualifiedReference
|
|
|
588
651
|
return makeCall(fn, [literal]);
|
|
589
652
|
}
|
|
590
653
|
|
|
654
|
+
regexFlags
|
|
655
|
+
= flags:[gimuy]* {
|
|
656
|
+
return flags.join("");
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
regexLiteral
|
|
660
|
+
= "/" chars:regexLiteralChar* "/" flags:regexFlags? {
|
|
661
|
+
const regex = new RegExp(chars.join(""), flags);
|
|
662
|
+
return annotate([ops.literal, regex], location());
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
regexLiteralChar
|
|
666
|
+
= [^/\n\r] // No unescaped slashes or newlines
|
|
667
|
+
/ escapedChar
|
|
668
|
+
|
|
591
669
|
relationalExpression
|
|
592
670
|
= head:shiftExpression tail:(__ @relationalOperator __ @shiftExpression)* {
|
|
593
671
|
return tail.reduce(makeBinaryOperation, head);
|
|
@@ -617,11 +695,14 @@ scopeReference "scope reference"
|
|
|
617
695
|
|
|
618
696
|
separator
|
|
619
697
|
= __ "," __
|
|
620
|
-
/ whitespaceWithNewLine
|
|
698
|
+
/ shellMode @whitespaceWithNewLine
|
|
621
699
|
|
|
622
700
|
shebang
|
|
623
701
|
= "#!" [^\n\r]* { return null; }
|
|
624
702
|
|
|
703
|
+
shellMode
|
|
704
|
+
= &{ return options.mode === "shell" }
|
|
705
|
+
|
|
625
706
|
shiftExpression
|
|
626
707
|
= head:additiveExpression tail:(__ @shiftOperator __ @additiveExpression)* {
|
|
627
708
|
return tail.reduce(makeBinaryOperation, head);
|
|
@@ -635,7 +716,7 @@ shiftOperator
|
|
|
635
716
|
// A shorthand lambda expression: `=foo(_)`
|
|
636
717
|
shorthandFunction "lambda function"
|
|
637
718
|
// Avoid a following equal sign (for an equality)
|
|
638
|
-
= "=" !"=" __ definition:implicitParenthesesCallExpression {
|
|
719
|
+
= shellMode "=" !"=" __ definition:implicitParenthesesCallExpression {
|
|
639
720
|
const lambdaParameters = annotate(
|
|
640
721
|
[annotate([ops.literal, "_"], location())],
|
|
641
722
|
location()
|
|
@@ -680,7 +761,7 @@ spreadElement
|
|
|
680
761
|
stringLiteral "string"
|
|
681
762
|
= doubleQuoteString
|
|
682
763
|
/ singleQuoteString
|
|
683
|
-
/ guillemetString
|
|
764
|
+
/ shellMode @guillemetString
|
|
684
765
|
|
|
685
766
|
// The body of a template document is a kind of template literal that can
|
|
686
767
|
// contain backticks at the top level.
|
|
@@ -707,7 +788,8 @@ templateBodyText "template text"
|
|
|
707
788
|
|
|
708
789
|
templateDocument "template document"
|
|
709
790
|
= front:frontMatterExpression __ body:templateBody {
|
|
710
|
-
|
|
791
|
+
const macroName = options.mode === "jse" ? "_template" : "@template";
|
|
792
|
+
return annotate(applyMacro(front, macroName, body), location());
|
|
711
793
|
}
|
|
712
794
|
/ front:frontMatterYaml? body:templateBody {
|
|
713
795
|
return front
|
|
@@ -718,7 +800,8 @@ templateDocument "template document"
|
|
|
718
800
|
// A backtick-quoted template literal
|
|
719
801
|
templateLiteral "template literal"
|
|
720
802
|
= "`" head:templateLiteralText tail:(templateSubstitution templateLiteralText)* expectBacktick {
|
|
721
|
-
|
|
803
|
+
const op = options.mode === "jse" ? ops.templateStandard : ops.templateTree;
|
|
804
|
+
return makeTemplate(op, head, tail, location());
|
|
722
805
|
}
|
|
723
806
|
|
|
724
807
|
templateLiteralChar
|
|
@@ -750,7 +833,9 @@ unaryExpression
|
|
|
750
833
|
unaryOperator
|
|
751
834
|
= "!"
|
|
752
835
|
/ "+"
|
|
753
|
-
|
|
836
|
+
// Don't match a front matter delimiter. For some reason, the negative
|
|
837
|
+
// lookahead !"--\n" doesn't work.
|
|
838
|
+
/ @"-" !"-\n"
|
|
754
839
|
/ "~"
|
|
755
840
|
|
|
756
841
|
whitespace
|
|
@@ -758,5 +843,10 @@ whitespace
|
|
|
758
843
|
/ newLine
|
|
759
844
|
/ comment
|
|
760
845
|
|
|
846
|
+
// Whitespace requires in shell mode, optional in JSE mode
|
|
847
|
+
whitespaceShell
|
|
848
|
+
= shellMode whitespace
|
|
849
|
+
/ jseMode __
|
|
850
|
+
|
|
761
851
|
whitespaceWithNewLine
|
|
762
852
|
= inlineSpace* comment? newLine __
|