@vituum/vite-plugin-latte 0.1.8

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.
Files changed (201) hide show
  1. package/README.md +39 -0
  2. package/handler.js +9 -0
  3. package/index.js +138 -0
  4. package/index.php +173 -0
  5. package/latte/AssetFilter.php +11 -0
  6. package/latte/FetchFunction.php +28 -0
  7. package/latte/IconFilter.php +14 -0
  8. package/latte/JsonTag.php +64 -0
  9. package/latte/PlaceholderFunction.php +11 -0
  10. package/latte/RandomColorFunction.php +14 -0
  11. package/latte/TelFilter.php +22 -0
  12. package/package.json +35 -0
  13. package/vendor/autoload.php +7 -0
  14. package/vendor/bin/latte-lint +107 -0
  15. package/vendor/composer/ClassLoader.php +572 -0
  16. package/vendor/composer/InstalledVersions.php +350 -0
  17. package/vendor/composer/LICENSE +21 -0
  18. package/vendor/composer/autoload_classmap.php +183 -0
  19. package/vendor/composer/autoload_namespaces.php +9 -0
  20. package/vendor/composer/autoload_psr4.php +9 -0
  21. package/vendor/composer/autoload_real.php +57 -0
  22. package/vendor/composer/autoload_static.php +193 -0
  23. package/vendor/composer/installed.json +93 -0
  24. package/vendor/composer/installed.php +32 -0
  25. package/vendor/composer/platform_check.php +26 -0
  26. package/vendor/latte/latte/bin/latte-lint +29 -0
  27. package/vendor/latte/latte/composer.json +53 -0
  28. package/vendor/latte/latte/license.md +60 -0
  29. package/vendor/latte/latte/ncs.php +14 -0
  30. package/vendor/latte/latte/ncs.xml +9 -0
  31. package/vendor/latte/latte/readme.md +21 -0
  32. package/vendor/latte/latte/src/Bridges/Tracy/BlueScreenPanel.php +114 -0
  33. package/vendor/latte/latte/src/Bridges/Tracy/LattePanel.php +99 -0
  34. package/vendor/latte/latte/src/Bridges/Tracy/templates/LattePanel.panel.phtml +98 -0
  35. package/vendor/latte/latte/src/Bridges/Tracy/templates/LattePanel.tab.phtml +15 -0
  36. package/vendor/latte/latte/src/Latte/Compiler/Block.php +44 -0
  37. package/vendor/latte/latte/src/Latte/Compiler/Escaper.php +244 -0
  38. package/vendor/latte/latte/src/Latte/Compiler/ExpressionBuilder.php +128 -0
  39. package/vendor/latte/latte/src/Latte/Compiler/Node.php +28 -0
  40. package/vendor/latte/latte/src/Latte/Compiler/NodeHelpers.php +132 -0
  41. package/vendor/latte/latte/src/Latte/Compiler/NodeTraverser.php +77 -0
  42. package/vendor/latte/latte/src/Latte/Compiler/Nodes/AreaNode.php +17 -0
  43. package/vendor/latte/latte/src/Latte/Compiler/Nodes/AuxiliaryNode.php +27 -0
  44. package/vendor/latte/latte/src/Latte/Compiler/Nodes/FragmentNode.php +59 -0
  45. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Html/AttributeNode.php +48 -0
  46. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Html/BogusTagNode.php +46 -0
  47. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Html/CommentNode.php +39 -0
  48. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Html/ElementNode.php +129 -0
  49. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Html/QuotedValue.php +53 -0
  50. package/vendor/latte/latte/src/Latte/Compiler/Nodes/NopNode.php +21 -0
  51. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/ArgumentNode.php +45 -0
  52. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/ComplexTypeNode.php +17 -0
  53. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/ArrayAccessNode.php +41 -0
  54. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/ArrayItemNode.php +52 -0
  55. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/ArrayNode.php +94 -0
  56. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/AssignNode.php +39 -0
  57. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/AssignOpNode.php +45 -0
  58. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/BinaryOpNode.php +65 -0
  59. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/CastNode.php +43 -0
  60. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/ClassConstantFetchNode.php +42 -0
  61. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/CloneNode.php +36 -0
  62. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/ClosureNode.php +73 -0
  63. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/ClosureUseNode.php +37 -0
  64. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/ConstantFetchNode.php +37 -0
  65. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/EmptyNode.php +36 -0
  66. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/ErrorSuppressNode.php +36 -0
  67. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/FilterCallNode.php +39 -0
  68. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/FunctionCallNode.php +45 -0
  69. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/FunctionCallableNode.php +39 -0
  70. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/InRangeNode.php +42 -0
  71. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/InstanceofNode.php +40 -0
  72. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/IssetNode.php +40 -0
  73. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/MatchNode.php +49 -0
  74. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/MethodCallNode.php +50 -0
  75. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/MethodCallableNode.php +42 -0
  76. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/NewNode.php +45 -0
  77. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/NotNode.php +36 -0
  78. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/PostOpNode.php +43 -0
  79. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/PreOpNode.php +43 -0
  80. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/PropertyFetchNode.php +42 -0
  81. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/StaticCallNode.php +55 -0
  82. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/StaticCallableNode.php +47 -0
  83. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/StaticPropertyFetchNode.php +42 -0
  84. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/TernaryNode.php +50 -0
  85. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/UnaryOpNode.php +45 -0
  86. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/VariableNode.php +40 -0
  87. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/ExpressionNode.php +17 -0
  88. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/FilterNode.php +67 -0
  89. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/IdentifierNode.php +36 -0
  90. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/IntersectionTypeNode.php +39 -0
  91. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/MatchArmNode.php +45 -0
  92. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/ModifierNode.php +107 -0
  93. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/NameNode.php +89 -0
  94. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/NullableTypeNode.php +35 -0
  95. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/ParameterNode.php +54 -0
  96. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Scalar/BooleanNode.php +30 -0
  97. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Scalar/EncapsedStringNode.php +80 -0
  98. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Scalar/EncapsedStringPartNode.php +30 -0
  99. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Scalar/FloatNode.php +63 -0
  100. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Scalar/IntegerNode.php +70 -0
  101. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Scalar/NullNode.php +29 -0
  102. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Scalar/StringNode.php +40 -0
  103. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/ScalarNode.php +15 -0
  104. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/SuperiorTypeNode.php +29 -0
  105. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/UnionTypeNode.php +39 -0
  106. package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/VarLikeIdentifierNode.php +21 -0
  107. package/vendor/latte/latte/src/Latte/Compiler/Nodes/StatementNode.php +15 -0
  108. package/vendor/latte/latte/src/Latte/Compiler/Nodes/TemplateNode.php +34 -0
  109. package/vendor/latte/latte/src/Latte/Compiler/Nodes/TextNode.php +37 -0
  110. package/vendor/latte/latte/src/Latte/Compiler/PhpHelpers.php +227 -0
  111. package/vendor/latte/latte/src/Latte/Compiler/Position.php +43 -0
  112. package/vendor/latte/latte/src/Latte/Compiler/PrintContext.php +334 -0
  113. package/vendor/latte/latte/src/Latte/Compiler/Tag.php +117 -0
  114. package/vendor/latte/latte/src/Latte/Compiler/TagLexer.php +399 -0
  115. package/vendor/latte/latte/src/Latte/Compiler/TagParser.php +388 -0
  116. package/vendor/latte/latte/src/Latte/Compiler/TagParserData.php +580 -0
  117. package/vendor/latte/latte/src/Latte/Compiler/TemplateGenerator.php +206 -0
  118. package/vendor/latte/latte/src/Latte/Compiler/TemplateLexer.php +391 -0
  119. package/vendor/latte/latte/src/Latte/Compiler/TemplateParser.php +448 -0
  120. package/vendor/latte/latte/src/Latte/Compiler/TemplateParserHtml.php +506 -0
  121. package/vendor/latte/latte/src/Latte/Compiler/Token.php +259 -0
  122. package/vendor/latte/latte/src/Latte/Compiler/TokenStream.php +151 -0
  123. package/vendor/latte/latte/src/Latte/ContentType.php +22 -0
  124. package/vendor/latte/latte/src/Latte/Engine.php +591 -0
  125. package/vendor/latte/latte/src/Latte/Essential/Blueprint.php +160 -0
  126. package/vendor/latte/latte/src/Latte/Essential/CachingIterator.php +232 -0
  127. package/vendor/latte/latte/src/Latte/Essential/CoreExtension.php +232 -0
  128. package/vendor/latte/latte/src/Latte/Essential/Filters.php +591 -0
  129. package/vendor/latte/latte/src/Latte/Essential/Nodes/BlockNode.php +161 -0
  130. package/vendor/latte/latte/src/Latte/Essential/Nodes/CaptureNode.php +98 -0
  131. package/vendor/latte/latte/src/Latte/Essential/Nodes/ContentTypeNode.php +74 -0
  132. package/vendor/latte/latte/src/Latte/Essential/Nodes/DebugbreakNode.php +53 -0
  133. package/vendor/latte/latte/src/Latte/Essential/Nodes/DefineNode.php +134 -0
  134. package/vendor/latte/latte/src/Latte/Essential/Nodes/DoNode.php +49 -0
  135. package/vendor/latte/latte/src/Latte/Essential/Nodes/DumpNode.php +58 -0
  136. package/vendor/latte/latte/src/Latte/Essential/Nodes/EmbedNode.php +125 -0
  137. package/vendor/latte/latte/src/Latte/Essential/Nodes/ExtendsNode.php +60 -0
  138. package/vendor/latte/latte/src/Latte/Essential/Nodes/FirstLastSepNode.php +81 -0
  139. package/vendor/latte/latte/src/Latte/Essential/Nodes/ForNode.php +91 -0
  140. package/vendor/latte/latte/src/Latte/Essential/Nodes/ForeachNode.php +155 -0
  141. package/vendor/latte/latte/src/Latte/Essential/Nodes/IfChangedNode.php +141 -0
  142. package/vendor/latte/latte/src/Latte/Essential/Nodes/IfContentNode.php +82 -0
  143. package/vendor/latte/latte/src/Latte/Essential/Nodes/IfNode.php +180 -0
  144. package/vendor/latte/latte/src/Latte/Essential/Nodes/ImportNode.php +49 -0
  145. package/vendor/latte/latte/src/Latte/Essential/Nodes/IncludeBlockNode.php +140 -0
  146. package/vendor/latte/latte/src/Latte/Essential/Nodes/IncludeFileNode.php +81 -0
  147. package/vendor/latte/latte/src/Latte/Essential/Nodes/IterateWhileNode.php +94 -0
  148. package/vendor/latte/latte/src/Latte/Essential/Nodes/NAttrNode.php +105 -0
  149. package/vendor/latte/latte/src/Latte/Essential/Nodes/NClassNode.php +54 -0
  150. package/vendor/latte/latte/src/Latte/Essential/Nodes/NTagNode.php +79 -0
  151. package/vendor/latte/latte/src/Latte/Essential/Nodes/ParametersNode.php +84 -0
  152. package/vendor/latte/latte/src/Latte/Essential/Nodes/PrintNode.php +70 -0
  153. package/vendor/latte/latte/src/Latte/Essential/Nodes/RawPhpNode.php +46 -0
  154. package/vendor/latte/latte/src/Latte/Essential/Nodes/RollbackNode.php +37 -0
  155. package/vendor/latte/latte/src/Latte/Essential/Nodes/SkipNode.php +71 -0
  156. package/vendor/latte/latte/src/Latte/Essential/Nodes/SpacelessNode.php +62 -0
  157. package/vendor/latte/latte/src/Latte/Essential/Nodes/SwitchNode.php +118 -0
  158. package/vendor/latte/latte/src/Latte/Essential/Nodes/TemplatePrintNode.php +39 -0
  159. package/vendor/latte/latte/src/Latte/Essential/Nodes/TemplateTypeNode.php +38 -0
  160. package/vendor/latte/latte/src/Latte/Essential/Nodes/TraceNode.php +35 -0
  161. package/vendor/latte/latte/src/Latte/Essential/Nodes/TranslateNode.php +104 -0
  162. package/vendor/latte/latte/src/Latte/Essential/Nodes/TryNode.php +75 -0
  163. package/vendor/latte/latte/src/Latte/Essential/Nodes/VarNode.php +106 -0
  164. package/vendor/latte/latte/src/Latte/Essential/Nodes/VarPrintNode.php +40 -0
  165. package/vendor/latte/latte/src/Latte/Essential/Nodes/VarTypeNode.php +36 -0
  166. package/vendor/latte/latte/src/Latte/Essential/Nodes/WhileNode.php +81 -0
  167. package/vendor/latte/latte/src/Latte/Essential/Passes.php +115 -0
  168. package/vendor/latte/latte/src/Latte/Essential/RawPhpExtension.php +28 -0
  169. package/vendor/latte/latte/src/Latte/Essential/RollbackException.php +16 -0
  170. package/vendor/latte/latte/src/Latte/Essential/Tracer.php +117 -0
  171. package/vendor/latte/latte/src/Latte/Essential/TranslatorExtension.php +101 -0
  172. package/vendor/latte/latte/src/Latte/Extension.php +97 -0
  173. package/vendor/latte/latte/src/Latte/Helpers.php +96 -0
  174. package/vendor/latte/latte/src/Latte/Loader.php +37 -0
  175. package/vendor/latte/latte/src/Latte/Loaders/FileLoader.php +95 -0
  176. package/vendor/latte/latte/src/Latte/Loaders/StringLoader.php +76 -0
  177. package/vendor/latte/latte/src/Latte/Policy.php +24 -0
  178. package/vendor/latte/latte/src/Latte/PositionAwareException.php +50 -0
  179. package/vendor/latte/latte/src/Latte/Runtime/Block.php +24 -0
  180. package/vendor/latte/latte/src/Latte/Runtime/FilterExecutor.php +151 -0
  181. package/vendor/latte/latte/src/Latte/Runtime/FilterInfo.php +39 -0
  182. package/vendor/latte/latte/src/Latte/Runtime/Filters.php +234 -0
  183. package/vendor/latte/latte/src/Latte/Runtime/Html.php +35 -0
  184. package/vendor/latte/latte/src/Latte/Runtime/HtmlStringable.php +17 -0
  185. package/vendor/latte/latte/src/Latte/Runtime/Template.php +381 -0
  186. package/vendor/latte/latte/src/Latte/Sandbox/Nodes/FunctionCallNode.php +30 -0
  187. package/vendor/latte/latte/src/Latte/Sandbox/Nodes/FunctionCallableNode.php +29 -0
  188. package/vendor/latte/latte/src/Latte/Sandbox/Nodes/MethodCallNode.php +32 -0
  189. package/vendor/latte/latte/src/Latte/Sandbox/Nodes/MethodCallableNode.php +30 -0
  190. package/vendor/latte/latte/src/Latte/Sandbox/Nodes/PropertyFetchNode.php +32 -0
  191. package/vendor/latte/latte/src/Latte/Sandbox/Nodes/SandboxNode.php +73 -0
  192. package/vendor/latte/latte/src/Latte/Sandbox/Nodes/StaticCallNode.php +31 -0
  193. package/vendor/latte/latte/src/Latte/Sandbox/Nodes/StaticCallableNode.php +30 -0
  194. package/vendor/latte/latte/src/Latte/Sandbox/Nodes/StaticPropertyFetchNode.php +32 -0
  195. package/vendor/latte/latte/src/Latte/Sandbox/RuntimeChecker.php +116 -0
  196. package/vendor/latte/latte/src/Latte/Sandbox/SandboxExtension.php +132 -0
  197. package/vendor/latte/latte/src/Latte/Sandbox/SecurityPolicy.php +185 -0
  198. package/vendor/latte/latte/src/Latte/Strict.php +101 -0
  199. package/vendor/latte/latte/src/Latte/attributes.php +24 -0
  200. package/vendor/latte/latte/src/Latte/exceptions.php +69 -0
  201. package/vendor/latte/latte/src/Tools/Linter.php +175 -0
@@ -0,0 +1,448 @@
1
+ <?php
2
+
3
+ /**
4
+ * This file is part of the Latte (https://latte.nette.org)
5
+ * Copyright (c) 2008 David Grudl (https://davidgrudl.com)
6
+ */
7
+
8
+ declare(strict_types=1);
9
+
10
+ namespace Latte\Compiler;
11
+
12
+ use Latte;
13
+ use Latte\CompileException;
14
+ use Latte\Compiler\Nodes\FragmentNode;
15
+ use Latte\ContentType;
16
+ use Latte\Helpers;
17
+ use Latte\Policy;
18
+ use Latte\Runtime\Template;
19
+ use Latte\SecurityViolationException;
20
+
21
+
22
+ final class TemplateParser
23
+ {
24
+ use Latte\Strict;
25
+
26
+ public const
27
+ LocationHead = 1,
28
+ LocationText = 2,
29
+ LocationTag = 3;
30
+
31
+ /** @var Block[][] */
32
+ public array $blocks = [[]];
33
+ public int $blockLayer = Template::LayerTop;
34
+ public int $location = self::LocationHead;
35
+ public ?Nodes\TextNode $lastIndentation = null;
36
+
37
+ /** @var array<string, callable(Tag, self): (Node|\Generator|void)> */
38
+ private array $tagParsers = [];
39
+
40
+ /** @var array<string, \stdClass> */
41
+ private array $attrParsersInfo = [];
42
+
43
+ private TemplateParserHtml $html;
44
+ private ?TokenStream $stream = null;
45
+ private ?TemplateLexer $lexer = null;
46
+ private ?Policy $policy = null;
47
+ private string $contentType = ContentType::Html;
48
+ private int $counter = 0;
49
+ private ?Tag $tag = null;
50
+ private $lastResolver;
51
+
52
+
53
+ /**
54
+ * Parses tokens to nodes.
55
+ * @throws CompileException
56
+ */
57
+ public function parse(string $template, TemplateLexer $lexer): Nodes\TemplateNode
58
+ {
59
+ $this->lexer = $lexer;
60
+ $this->html = new TemplateParserHtml($this, $this->completeAttrParsers());
61
+ $this->stream = new TokenStream($lexer->tokenize($template, $this->contentType));
62
+
63
+ $headLength = 0;
64
+ $findLength = function (FragmentNode $fragment) use (&$headLength) {
65
+ if ($this->location === self::LocationHead && !end($fragment->children) instanceof Nodes\TextNode) {
66
+ $headLength = count($fragment->children);
67
+ }
68
+ };
69
+
70
+ $node = new Nodes\TemplateNode;
71
+ $node->main = $this->parseFragment([$this->html, 'inTextResolve'], $findLength);
72
+ $node->head = new FragmentNode(array_splice($node->main->children, 0, $headLength));
73
+ $node->contentType = $this->contentType;
74
+
75
+ if (!$this->stream->peek()->isEnd()) {
76
+ $this->stream->throwUnexpectedException();
77
+ }
78
+
79
+ return $node;
80
+ }
81
+
82
+
83
+ public function parseFragment(callable $resolver, callable $after = null): FragmentNode
84
+ {
85
+ $res = new FragmentNode;
86
+ $save = [$this->lastResolver, $this->tag];
87
+ $this->lastResolver = $resolver;
88
+ try {
89
+ while (!$this->stream->peek()->isEnd()) {
90
+ if ($node = $resolver($res)) {
91
+ $res->append($node);
92
+ $after && $after($res);
93
+ } else {
94
+ break;
95
+ }
96
+ }
97
+
98
+ return $res;
99
+ } finally {
100
+ [$this->lastResolver, $this->tag] = $save;
101
+ }
102
+ }
103
+
104
+
105
+ public function inTextResolve(): ?Node
106
+ {
107
+ $token = $this->stream->peek();
108
+ return match ($token->type) {
109
+ Token::Text => $this->parseText(),
110
+ Token::Indentation => $this->parseIndentation(),
111
+ Token::Newline => $this->parseNewline(),
112
+ Token::Latte_TagOpen => $this->parseLatteStatement(),
113
+ Token::Latte_CommentOpen => $this->parseLatteComment(),
114
+ default => null,
115
+ };
116
+ }
117
+
118
+
119
+ public function parseText(): Nodes\TextNode
120
+ {
121
+ $token = $this->stream->consume(Token::Text, Token::Html_Name);
122
+ if ($this->location === self::LocationHead && trim($token->text) !== '') {
123
+ $this->location = self::LocationText;
124
+ }
125
+ $this->lastIndentation = null;
126
+ return new Nodes\TextNode($token->text, $token->position);
127
+ }
128
+
129
+
130
+ private function parseIndentation(): Nodes\TextNode
131
+ {
132
+ $token = $this->stream->consume(Token::Indentation);
133
+ return $this->lastIndentation = new Nodes\TextNode($token->text, $token->position);
134
+ }
135
+
136
+
137
+ private function parseNewline(): Nodes\AreaNode
138
+ {
139
+ $token = $this->stream->consume(Token::Newline);
140
+ if ($this->lastIndentation) { // drop indentation & newline
141
+ $this->lastIndentation->content = '';
142
+ $this->lastIndentation = null;
143
+ return new Nodes\NopNode;
144
+ } else {
145
+ return new Nodes\TextNode($token->text, $token->position);
146
+ }
147
+ }
148
+
149
+
150
+ public function parseLatteComment(): Nodes\NopNode
151
+ {
152
+ if (str_ends_with($this->stream->peek(-1)?->text ?? "\n", "\n")) {
153
+ $this->lastIndentation ??= new Nodes\TextNode('');
154
+ }
155
+ $this->stream->consume(Token::Latte_CommentOpen);
156
+ $this->stream->consume(Token::Text);
157
+ $this->stream->consume(Token::Latte_CommentClose);
158
+ return new Nodes\NopNode;
159
+ }
160
+
161
+
162
+ public function parseLatteStatement(): ?Node
163
+ {
164
+ if ($this->stream->peek(1)->is(Token::Slash)
165
+ || isset($this->tag->data->filters) && in_array($this->stream->peek(1)->text, $this->tag->data->filters, true)
166
+ ) {
167
+ return null; // go back to previous parseLatteStatement()
168
+ }
169
+
170
+ $token = $this->stream->peek();
171
+ $startTag = $this->pushTag($this->parseLatteTag());
172
+
173
+ $parser = $this->getTagParser($startTag->name, $token->position);
174
+ $res = $parser($startTag, $this);
175
+ if ($res instanceof \Generator) {
176
+ if (!$res->valid() && !$startTag->void) {
177
+ throw new \LogicException("Incorrect behavior of {{$startTag->name}} parser, yield call is expected (on line {$startTag->position->line})");
178
+ }
179
+
180
+ $this->ensureIsConsumed($startTag);
181
+ if ($startTag->outputMode === $startTag::OutputKeepIndentation) {
182
+ $this->lastIndentation = null;
183
+ }
184
+
185
+ if ($startTag->void) {
186
+ $res->send([new FragmentNode, $startTag]);
187
+ } else {
188
+ while ($res->valid()) {
189
+ $startTag->data->filters = $res->current() ?: null;
190
+ $content = $this->parseFragment($this->lastResolver);
191
+
192
+ if (!$this->stream->is(Token::Latte_TagOpen)) {
193
+ $this->checkEndTag($startTag, null);
194
+ $res->send([$content, null]);
195
+ break;
196
+ }
197
+
198
+ $tag = $this->parseLatteTag();
199
+
200
+ if ($startTag->outputMode === $startTag::OutputKeepIndentation) {
201
+ $this->lastIndentation = null;
202
+ }
203
+
204
+ if ($tag->closing) {
205
+ $this->checkEndTag($startTag, $tag);
206
+ $res->send([$content, $tag]);
207
+ $this->ensureIsConsumed($tag);
208
+ break;
209
+ } elseif (in_array($tag->name, $startTag->data->filters ?? [], true)) {
210
+ $this->pushTag($tag);
211
+ $res->send([$content, $tag]);
212
+ $this->ensureIsConsumed($tag);
213
+ $this->popTag();
214
+ } else {
215
+ throw new CompileException('Unexpected tag ' . substr($tag->getNotation(true), 0, -1) . '}', $tag->position);
216
+ }
217
+ }
218
+ }
219
+
220
+ if ($res->valid()) {
221
+ throw new \LogicException("Incorrect behavior of {{$startTag->name}} parser, more yield calls than expected (on line {$startTag->position->line})");
222
+ }
223
+
224
+ $node = $res->getReturn();
225
+
226
+ } elseif ($startTag->void) {
227
+ throw new CompileException('Unexpected /} in tag ' . substr($startTag->getNotation(true), 0, -1) . '/}', $startTag->position);
228
+
229
+ } else {
230
+ $this->ensureIsConsumed($startTag);
231
+ $node = $res;
232
+ if ($startTag->outputMode === $startTag::OutputKeepIndentation) {
233
+ $this->lastIndentation = null;
234
+ }
235
+ }
236
+
237
+ if (!$node instanceof Node) {
238
+ throw new \LogicException("Incorrect behavior of {{$startTag->name}} parser, unexpected returned value (on line {$startTag->position->line})");
239
+ }
240
+
241
+ if ($this->location === self::LocationHead && $startTag->outputMode !== $startTag::OutputNone) {
242
+ $this->location = self::LocationText;
243
+ }
244
+
245
+ $this->popTag();
246
+
247
+ $node->position = $startTag->position;
248
+ return $node;
249
+ }
250
+
251
+
252
+ private function parseLatteTag(): Tag
253
+ {
254
+ $stream = $this->stream;
255
+ if (str_ends_with($stream->peek(-1)?->text ?? "\n", "\n")) {
256
+ $this->lastIndentation ??= new Nodes\TextNode('');
257
+ }
258
+
259
+ $openToken = $stream->consume(Token::Latte_TagOpen);
260
+ $tag = new Tag(
261
+ position: $openToken->position,
262
+ closing: $closing = (bool) $stream->tryConsume(Token::Slash),
263
+ name: $stream->tryConsume(Token::Latte_Name)?->text ?? ($closing ? '' : '='),
264
+ tokens: $this->consumeTag(),
265
+ void: (bool) $stream->tryConsume(Token::Slash),
266
+ location: $this->location,
267
+ htmlElement: $this->html->getElement(),
268
+ );
269
+ $stream->consume(Token::Latte_TagClose);
270
+ return $tag;
271
+ }
272
+
273
+
274
+ private function consumeTag(): array
275
+ {
276
+ $res = [];
277
+ while ($this->stream->peek()->isPhpKind()) {
278
+ $res[] = $this->stream->consume();
279
+ }
280
+
281
+ $res[] = new Token(Token::End, '', $this->stream->peek()->position);
282
+ return $res;
283
+ }
284
+
285
+
286
+ /** @param array<string, \stdClass|callable(Tag, self): (Node|\Generator|void)> $parsers */
287
+ public function addTags(array $parsers): static
288
+ {
289
+ foreach ($parsers as $name => $info) {
290
+ $info = $info instanceof \stdClass ? $info : Latte\Extension::order($info);
291
+ if (str_starts_with($name, TemplateLexer::NPrefix)) {
292
+ $this->attrParsersInfo[substr($name, 2)] = $info;
293
+ } else {
294
+ $this->tagParsers[$name] = $info->subject;
295
+ if ($info->generator = Helpers::toReflection($info->subject)->isGenerator()) {
296
+ $this->attrParsersInfo[$name] = $info;
297
+ }
298
+ }
299
+ }
300
+
301
+ return $this;
302
+ }
303
+
304
+
305
+ /** @return callable(Tag, self): (Node|\Generator|void) */
306
+ private function getTagParser(string $name, Position $pos): callable
307
+ {
308
+ if (!isset($this->tagParsers[$name])) {
309
+ $hint = ($t = Helpers::getSuggestion(array_keys($this->tagParsers), $name))
310
+ ? ", did you mean {{$t}}?"
311
+ : '';
312
+ if ($this->contentType === ContentType::Html
313
+ && in_array($this->html->getElement()?->name, ['script', 'style'], true)
314
+ ) {
315
+ $hint .= ' (in JavaScript or CSS, try to put a space after bracket or use n:syntax=off)';
316
+ }
317
+ throw new CompileException("Unexpected tag {{$name}}$hint", $pos);
318
+ } elseif (!$this->isTagAllowed($name)) {
319
+ throw new SecurityViolationException("Tag {{$name}} is not allowed", $pos);
320
+ }
321
+
322
+ return $this->tagParsers[$name];
323
+ }
324
+
325
+
326
+ private function completeAttrParsers(): array
327
+ {
328
+ $list = Helpers::sortBeforeAfter($this->attrParsersInfo);
329
+ $parsers = [];
330
+ foreach ($list as $name => $info) {
331
+ $parsers[$name] = $info->subject;
332
+ if ($info->generator ?? false) {
333
+ $parsers[Tag::PrefixInner . '-' . $name] = $parsers[Tag::PrefixTag . '-' . $name] = $parsers[$name];
334
+ }
335
+ }
336
+
337
+ return $parsers;
338
+ }
339
+
340
+
341
+ private function checkEndTag(Tag $start, ?Tag $end): void
342
+ {
343
+ if (!$end) {
344
+ if ($start->name !== 'syntax' && ($start->name !== 'block' || $this->tag->parent)) { // TODO: hardcoded
345
+ $this->stream->throwUnexpectedException(expected: ["{/$start->name}"]);
346
+ }
347
+
348
+ } elseif (
349
+ ($end->name !== $start->name && $end->name !== '')
350
+ || !$end->closing
351
+ || $end->void
352
+ ) {
353
+ throw new CompileException("Unexpected {$end->getNotation()}, expecting {/$start->name}", $end->position);
354
+ }
355
+ }
356
+
357
+
358
+ public function ensureIsConsumed(Tag $tag): void
359
+ {
360
+ if (!$tag->parser->isEnd()) {
361
+ $end = $tag->isNAttribute() ? ['end of attribute'] : ['end of tag'];
362
+ $tag->parser->stream->throwUnexpectedException($end, addendum: ' in ' . $tag->getNotation());
363
+ }
364
+ }
365
+
366
+
367
+ public function checkBlockIsUnique(Block $block): void
368
+ {
369
+ if ($block->isDynamic() || !preg_match('#^[a-z]#iD', $name = (string) $block->name->value)) {
370
+ throw new CompileException(ucfirst($block->tag->name) . " name must start with letter a-z, '$name' given.", $block->tag->position);
371
+ }
372
+
373
+ if ($block->layer === Template::LayerSnippet
374
+ ? isset($this->blocks[$block->layer][$name])
375
+ : (isset($this->blocks[Template::LayerLocal][$name]) || isset($this->blocks[$this->blockLayer][$name]))
376
+ ) {
377
+ throw new CompileException("Cannot redeclare {$block->tag->name} '{$name}'", $block->tag->position);
378
+ }
379
+
380
+ $this->blocks[$block->layer][$name] = $block;
381
+ }
382
+
383
+
384
+ public function setPolicy(?Policy $policy): static
385
+ {
386
+ $this->policy = $policy;
387
+ return $this;
388
+ }
389
+
390
+
391
+ public function setContentType(string $type): static
392
+ {
393
+ $this->contentType = $type;
394
+ $this->lexer?->setContentType($type);
395
+ return $this;
396
+ }
397
+
398
+
399
+ public function getContentType(): string
400
+ {
401
+ return $this->contentType;
402
+ }
403
+
404
+
405
+ /** @internal */
406
+ public function getStream(): TokenStream
407
+ {
408
+ return $this->stream;
409
+ }
410
+
411
+
412
+ public function getLexer(): TemplateLexer
413
+ {
414
+ return $this->lexer;
415
+ }
416
+
417
+
418
+ public function peekTag(): ?Tag
419
+ {
420
+ return $this->tag;
421
+ }
422
+
423
+
424
+ public function pushTag(Tag $tag): Tag
425
+ {
426
+ $tag->parent = $this->tag;
427
+ $this->tag = $tag;
428
+ return $tag;
429
+ }
430
+
431
+
432
+ public function popTag(): void
433
+ {
434
+ $this->tag = $this->tag->parent;
435
+ }
436
+
437
+
438
+ public function generateId(): int
439
+ {
440
+ return $this->counter++;
441
+ }
442
+
443
+
444
+ public function isTagAllowed(string $name): bool
445
+ {
446
+ return !$this->policy || $this->policy->isTagAllowed($name);
447
+ }
448
+ }