@weborigami/language 0.3.1 → 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 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/taggedTemplateIndent.js";
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.1",
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.1",
15
- "@weborigami/types": "0.3.1",
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
  },
@@ -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:(whitespace @additiveOperator whitespace @multiplicativeExpression)* {
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:(whitespace @multiplicativeOperator whitespace @exponentiationExpression)* {
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
- return identifier + (slash ?? "");
490
- }
531
+ return identifier + (slash ?? "");
532
+ }
491
533
  / string:stringLiteral {
492
- // Remove `ops.literal` from the string code
493
- return string[1];
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
- = literal
612
+ = numericLiteral
613
+ / stringLiteral
566
614
  / arrayLiteral
567
615
  / objectLiteral
568
616
  / group
569
617
  / templateLiteral
570
- / inherited
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
- return annotate(applyMacro(front, "@template", body), location());
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
- return makeTemplate(ops.template, head, tail, location());
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 __