herb 0.8.10-arm-linux-gnu → 0.9.0-arm-linux-gnu

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 (209) hide show
  1. checksums.yaml +4 -4
  2. data/Makefile +11 -3
  3. data/README.md +64 -34
  4. data/Rakefile +48 -40
  5. data/config.yml +317 -34
  6. data/ext/herb/error_helpers.c +367 -140
  7. data/ext/herb/error_helpers.h +1 -0
  8. data/ext/herb/extconf.rb +67 -28
  9. data/ext/herb/extension.c +317 -51
  10. data/ext/herb/extension.h +1 -0
  11. data/ext/herb/extension_helpers.c +23 -14
  12. data/ext/herb/extension_helpers.h +2 -2
  13. data/ext/herb/nodes.c +537 -270
  14. data/ext/herb/nodes.h +1 -0
  15. data/herb.gemspec +3 -2
  16. data/lib/herb/3.0/herb.so +0 -0
  17. data/lib/herb/3.1/herb.so +0 -0
  18. data/lib/herb/3.2/herb.so +0 -0
  19. data/lib/herb/3.3/herb.so +0 -0
  20. data/lib/herb/3.4/herb.so +0 -0
  21. data/lib/herb/4.0/herb.so +0 -0
  22. data/lib/herb/ast/helpers.rb +3 -3
  23. data/lib/herb/ast/node.rb +15 -2
  24. data/lib/herb/ast/nodes.rb +1132 -157
  25. data/lib/herb/bootstrap.rb +87 -0
  26. data/lib/herb/cli.rb +341 -31
  27. data/lib/herb/configuration.rb +248 -0
  28. data/lib/herb/defaults.yml +32 -0
  29. data/lib/herb/engine/compiler.rb +78 -11
  30. data/lib/herb/engine/debug_visitor.rb +13 -3
  31. data/lib/herb/engine/error_formatter.rb +13 -9
  32. data/lib/herb/engine/parser_error_overlay.rb +10 -6
  33. data/lib/herb/engine/validator.rb +8 -3
  34. data/lib/herb/engine/validators/nesting_validator.rb +2 -2
  35. data/lib/herb/engine.rb +82 -35
  36. data/lib/herb/errors.rb +563 -88
  37. data/lib/herb/lex_result.rb +1 -0
  38. data/lib/herb/location.rb +7 -3
  39. data/lib/herb/parse_result.rb +12 -2
  40. data/lib/herb/parser_options.rb +57 -0
  41. data/lib/herb/position.rb +1 -0
  42. data/lib/herb/prism_inspect.rb +116 -0
  43. data/lib/herb/project.rb +923 -331
  44. data/lib/herb/range.rb +1 -0
  45. data/lib/herb/token.rb +7 -1
  46. data/lib/herb/version.rb +1 -1
  47. data/lib/herb/visitor.rb +37 -2
  48. data/lib/herb/warnings.rb +6 -1
  49. data/lib/herb.rb +35 -3
  50. data/sig/herb/ast/helpers.rbs +2 -2
  51. data/sig/herb/ast/node.rbs +12 -2
  52. data/sig/herb/ast/nodes.rbs +641 -128
  53. data/sig/herb/bootstrap.rbs +31 -0
  54. data/sig/herb/configuration.rbs +89 -0
  55. data/sig/herb/engine/compiler.rbs +9 -1
  56. data/sig/herb/engine/debug_visitor.rbs +2 -0
  57. data/sig/herb/engine/validator.rbs +5 -1
  58. data/sig/herb/engine.rbs +17 -3
  59. data/sig/herb/errors.rbs +258 -63
  60. data/sig/herb/location.rbs +4 -0
  61. data/sig/herb/parse_result.rbs +4 -2
  62. data/sig/herb/parser_options.rbs +42 -0
  63. data/sig/herb/position.rbs +1 -0
  64. data/sig/herb/prism_inspect.rbs +28 -0
  65. data/sig/herb/range.rbs +1 -0
  66. data/sig/herb/token.rbs +6 -0
  67. data/sig/herb/visitor.rbs +25 -4
  68. data/sig/herb/warnings.rbs +6 -1
  69. data/sig/herb.rbs +14 -0
  70. data/sig/herb_c_extension.rbs +5 -2
  71. data/sig/serialized_ast_errors.rbs +54 -6
  72. data/sig/serialized_ast_nodes.rbs +60 -6
  73. data/src/analyze/action_view/attribute_extraction_helpers.c +290 -0
  74. data/src/analyze/action_view/content_tag.c +70 -0
  75. data/src/analyze/action_view/link_to.c +143 -0
  76. data/src/analyze/action_view/registry.c +60 -0
  77. data/src/analyze/action_view/tag.c +64 -0
  78. data/src/analyze/action_view/tag_helper_node_builders.c +305 -0
  79. data/src/analyze/action_view/tag_helpers.c +748 -0
  80. data/src/analyze/action_view/turbo_frame_tag.c +88 -0
  81. data/src/analyze/analyze.c +882 -0
  82. data/src/{analyzed_ruby.c → analyze/analyzed_ruby.c} +13 -11
  83. data/src/analyze/builders.c +343 -0
  84. data/src/analyze/conditional_elements.c +594 -0
  85. data/src/analyze/conditional_open_tags.c +640 -0
  86. data/src/analyze/control_type.c +250 -0
  87. data/src/{analyze_helpers.c → analyze/helpers.c} +48 -23
  88. data/src/analyze/invalid_structures.c +193 -0
  89. data/src/{analyze_missing_end.c → analyze/missing_end.c} +33 -22
  90. data/src/analyze/parse_errors.c +84 -0
  91. data/src/analyze/prism_annotate.c +397 -0
  92. data/src/{analyze_transform.c → analyze/transform.c} +17 -3
  93. data/src/ast_node.c +17 -7
  94. data/src/ast_nodes.c +662 -387
  95. data/src/ast_pretty_print.c +190 -6
  96. data/src/errors.c +1076 -520
  97. data/src/extract.c +145 -49
  98. data/src/herb.c +52 -34
  99. data/src/html_util.c +241 -12
  100. data/src/include/analyze/action_view/attribute_extraction_helpers.h +36 -0
  101. data/src/include/analyze/action_view/tag_helper_handler.h +41 -0
  102. data/src/include/analyze/action_view/tag_helper_node_builders.h +70 -0
  103. data/src/include/analyze/action_view/tag_helpers.h +38 -0
  104. data/src/include/{analyze.h → analyze/analyze.h} +14 -4
  105. data/src/include/{analyzed_ruby.h → analyze/analyzed_ruby.h} +3 -3
  106. data/src/include/analyze/builders.h +27 -0
  107. data/src/include/analyze/conditional_elements.h +9 -0
  108. data/src/include/analyze/conditional_open_tags.h +9 -0
  109. data/src/include/analyze/control_type.h +14 -0
  110. data/src/include/{analyze_helpers.h → analyze/helpers.h} +4 -2
  111. data/src/include/analyze/invalid_structures.h +11 -0
  112. data/src/include/analyze/prism_annotate.h +16 -0
  113. data/src/include/ast_node.h +11 -5
  114. data/src/include/ast_nodes.h +117 -38
  115. data/src/include/ast_pretty_print.h +5 -0
  116. data/src/include/element_source.h +3 -8
  117. data/src/include/errors.h +148 -55
  118. data/src/include/extract.h +21 -5
  119. data/src/include/herb.h +18 -6
  120. data/src/include/herb_prism_node.h +13 -0
  121. data/src/include/html_util.h +7 -2
  122. data/src/include/io.h +3 -1
  123. data/src/include/lex_helpers.h +29 -0
  124. data/src/include/lexer.h +1 -1
  125. data/src/include/lexer_peek_helpers.h +87 -13
  126. data/src/include/lexer_struct.h +2 -0
  127. data/src/include/location.h +2 -1
  128. data/src/include/parser.h +27 -2
  129. data/src/include/parser_helpers.h +19 -3
  130. data/src/include/pretty_print.h +10 -5
  131. data/src/include/prism_context.h +45 -0
  132. data/src/include/prism_helpers.h +10 -7
  133. data/src/include/prism_serialized.h +12 -0
  134. data/src/include/token.h +16 -4
  135. data/src/include/token_struct.h +10 -3
  136. data/src/include/utf8.h +2 -1
  137. data/src/include/util/hb_allocator.h +78 -0
  138. data/src/include/util/hb_arena.h +6 -1
  139. data/src/include/util/hb_arena_debug.h +12 -1
  140. data/src/include/util/hb_array.h +7 -3
  141. data/src/include/util/hb_buffer.h +6 -4
  142. data/src/include/util/hb_foreach.h +79 -0
  143. data/src/include/util/hb_narray.h +8 -4
  144. data/src/include/util/hb_string.h +56 -9
  145. data/src/include/util.h +6 -3
  146. data/src/include/version.h +1 -1
  147. data/src/io.c +3 -2
  148. data/src/lexer.c +42 -30
  149. data/src/lexer_peek_helpers.c +12 -74
  150. data/src/location.c +2 -2
  151. data/src/main.c +53 -28
  152. data/src/parser.c +783 -247
  153. data/src/parser_helpers.c +110 -23
  154. data/src/parser_match_tags.c +109 -48
  155. data/src/pretty_print.c +29 -24
  156. data/src/prism_helpers.c +30 -27
  157. data/src/ruby_parser.c +2 -0
  158. data/src/token.c +151 -66
  159. data/src/token_matchers.c +0 -1
  160. data/src/utf8.c +7 -6
  161. data/src/util/hb_allocator.c +341 -0
  162. data/src/util/hb_arena.c +81 -56
  163. data/src/util/hb_arena_debug.c +32 -17
  164. data/src/util/hb_array.c +30 -15
  165. data/src/util/hb_buffer.c +17 -21
  166. data/src/util/hb_narray.c +22 -7
  167. data/src/util/hb_string.c +49 -35
  168. data/src/util.c +21 -11
  169. data/src/visitor.c +47 -0
  170. data/templates/ext/herb/error_helpers.c.erb +24 -11
  171. data/templates/ext/herb/error_helpers.h.erb +1 -0
  172. data/templates/ext/herb/nodes.c.erb +50 -16
  173. data/templates/ext/herb/nodes.h.erb +1 -0
  174. data/templates/java/error_helpers.c.erb +1 -1
  175. data/templates/java/nodes.c.erb +30 -8
  176. data/templates/java/org/herb/ast/Errors.java.erb +24 -1
  177. data/templates/java/org/herb/ast/Nodes.java.erb +80 -21
  178. data/templates/javascript/packages/core/src/errors.ts.erb +16 -3
  179. data/templates/javascript/packages/core/src/node-type-guards.ts.erb +3 -1
  180. data/templates/javascript/packages/core/src/nodes.ts.erb +109 -32
  181. data/templates/javascript/packages/node/extension/error_helpers.cpp.erb +13 -4
  182. data/templates/javascript/packages/node/extension/nodes.cpp.erb +43 -4
  183. data/templates/lib/herb/ast/nodes.rb.erb +88 -31
  184. data/templates/lib/herb/errors.rb.erb +15 -3
  185. data/templates/lib/herb/visitor.rb.erb +2 -2
  186. data/templates/rust/src/ast/nodes.rs.erb +97 -44
  187. data/templates/rust/src/errors.rs.erb +2 -1
  188. data/templates/rust/src/nodes.rs.erb +167 -15
  189. data/templates/rust/src/union_types.rs.erb +60 -0
  190. data/templates/rust/src/visitor.rs.erb +81 -0
  191. data/templates/src/{analyze_missing_end.c.erb → analyze/missing_end.c.erb} +9 -6
  192. data/templates/src/{analyze_transform.c.erb → analyze/transform.c.erb} +2 -2
  193. data/templates/src/ast_nodes.c.erb +34 -26
  194. data/templates/src/ast_pretty_print.c.erb +24 -5
  195. data/templates/src/errors.c.erb +60 -54
  196. data/templates/src/include/ast_nodes.h.erb +6 -2
  197. data/templates/src/include/ast_pretty_print.h.erb +5 -0
  198. data/templates/src/include/errors.h.erb +15 -11
  199. data/templates/src/include/util/hb_foreach.h.erb +20 -0
  200. data/templates/src/parser_match_tags.c.erb +10 -4
  201. data/templates/src/visitor.c.erb +2 -2
  202. data/templates/template.rb +204 -29
  203. data/templates/wasm/error_helpers.cpp.erb +9 -5
  204. data/templates/wasm/nodes.cpp.erb +41 -4
  205. metadata +57 -16
  206. data/src/analyze.c +0 -1608
  207. data/src/element_source.c +0 -12
  208. data/src/include/util/hb_system.h +0 -9
  209. data/src/util/hb_system.c +0 -30
@@ -0,0 +1,594 @@
1
+ #include "../include/analyze/conditional_elements.h"
2
+ #include "../include/ast_nodes.h"
3
+ #include "../include/element_source.h"
4
+ #include "../include/errors.h"
5
+ #include "../include/token_struct.h"
6
+ #include "../include/util/hb_allocator.h"
7
+ #include "../include/util/hb_array.h"
8
+ #include "../include/util/hb_string.h"
9
+ #include "../include/visitor.h"
10
+
11
+ #include <stdbool.h>
12
+ #include <stdlib.h>
13
+ #include <string.h>
14
+
15
+ typedef struct {
16
+ hb_array_T* document_errors;
17
+ hb_allocator_T* allocator;
18
+ } conditional_elements_context_T;
19
+
20
+ static hb_string_T extract_condition_from_erb_content(AST_NODE_T* erb_node, bool* is_if) {
21
+ if (!erb_node) { return HB_STRING_NULL; }
22
+
23
+ token_T* content_token = NULL;
24
+
25
+ if (erb_node->type == AST_ERB_IF_NODE) {
26
+ AST_ERB_IF_NODE_T* if_node = (AST_ERB_IF_NODE_T*) erb_node;
27
+ content_token = if_node->content;
28
+ *is_if = true;
29
+ } else if (erb_node->type == AST_ERB_UNLESS_NODE) {
30
+ AST_ERB_UNLESS_NODE_T* unless_node = (AST_ERB_UNLESS_NODE_T*) erb_node;
31
+ content_token = unless_node->content;
32
+ *is_if = false;
33
+ } else {
34
+ return HB_STRING_NULL;
35
+ }
36
+
37
+ if (!content_token || hb_string_is_empty(content_token->value)) { return HB_STRING_NULL; }
38
+
39
+ hb_string_T value = hb_string_trim_start(content_token->value);
40
+
41
+ if (*is_if) {
42
+ if (hb_string_starts_with(value, hb_string("if "))) { value = hb_string_slice(value, 3); }
43
+ } else {
44
+ if (hb_string_starts_with(value, hb_string("unless "))) { value = hb_string_slice(value, 7); }
45
+ }
46
+
47
+ value = hb_string_trim(value);
48
+
49
+ if (hb_string_is_empty(value)) { return HB_STRING_NULL; }
50
+
51
+ return value;
52
+ }
53
+
54
+ static bool is_simple_erb_conditional(AST_NODE_T* node) {
55
+ if (node->type == AST_ERB_IF_NODE) {
56
+ AST_ERB_IF_NODE_T* if_node = (AST_ERB_IF_NODE_T*) node;
57
+ return if_node->subsequent == NULL;
58
+ } else if (node->type == AST_ERB_UNLESS_NODE) {
59
+ AST_ERB_UNLESS_NODE_T* unless_node = (AST_ERB_UNLESS_NODE_T*) node;
60
+ return unless_node->else_clause == NULL;
61
+ }
62
+
63
+ return false;
64
+ }
65
+
66
+ static hb_array_T* get_erb_conditional_statements(AST_NODE_T* node) {
67
+ if (node->type == AST_ERB_IF_NODE) {
68
+ return ((AST_ERB_IF_NODE_T*) node)->statements;
69
+ } else if (node->type == AST_ERB_UNLESS_NODE) {
70
+ return ((AST_ERB_UNLESS_NODE_T*) node)->statements;
71
+ }
72
+
73
+ return NULL;
74
+ }
75
+
76
+ static bool contains_single_open_tag(hb_array_T* statements, AST_HTML_OPEN_TAG_NODE_T** out_tag) {
77
+ if (!statements || hb_array_size(statements) == 0) { return false; }
78
+
79
+ *out_tag = NULL;
80
+ size_t tag_count = 0;
81
+
82
+ for (size_t statement_index = 0; statement_index < hb_array_size(statements); statement_index++) {
83
+ AST_NODE_T* child = (AST_NODE_T*) hb_array_get(statements, statement_index);
84
+
85
+ if (!child) { continue; }
86
+ if (child->type == AST_WHITESPACE_NODE) { continue; }
87
+
88
+ if (child->type == AST_HTML_TEXT_NODE) {
89
+ AST_HTML_TEXT_NODE_T* text = (AST_HTML_TEXT_NODE_T*) child;
90
+ if (hb_string_is_blank(text->content)) { continue; }
91
+
92
+ return false;
93
+ }
94
+
95
+ if (child->type == AST_HTML_OPEN_TAG_NODE) {
96
+ AST_HTML_OPEN_TAG_NODE_T* open_tag = (AST_HTML_OPEN_TAG_NODE_T*) child;
97
+
98
+ if (open_tag->is_void) { return false; }
99
+
100
+ *out_tag = open_tag;
101
+ tag_count++;
102
+
103
+ if (tag_count > 1) { return false; }
104
+
105
+ continue;
106
+ }
107
+
108
+ return false;
109
+ }
110
+
111
+ return tag_count == 1;
112
+ }
113
+
114
+ static bool contains_single_close_tag(hb_array_T* statements, AST_HTML_CLOSE_TAG_NODE_T** out_tag) {
115
+ if (!statements || hb_array_size(statements) == 0) { return false; }
116
+
117
+ *out_tag = NULL;
118
+ size_t tag_count = 0;
119
+
120
+ for (size_t statement_index = 0; statement_index < hb_array_size(statements); statement_index++) {
121
+ AST_NODE_T* child = (AST_NODE_T*) hb_array_get(statements, statement_index);
122
+ if (!child) { continue; }
123
+
124
+ if (child->type == AST_WHITESPACE_NODE) { continue; }
125
+
126
+ if (child->type == AST_HTML_TEXT_NODE) {
127
+ AST_HTML_TEXT_NODE_T* text = (AST_HTML_TEXT_NODE_T*) child;
128
+ if (hb_string_is_blank(text->content)) { continue; }
129
+
130
+ return false;
131
+ }
132
+
133
+ if (child->type == AST_HTML_CLOSE_TAG_NODE) {
134
+ *out_tag = (AST_HTML_CLOSE_TAG_NODE_T*) child;
135
+ tag_count++;
136
+
137
+ if (tag_count > 1) { return false; }
138
+
139
+ continue;
140
+ }
141
+
142
+ return false;
143
+ }
144
+
145
+ return tag_count == 1;
146
+ }
147
+
148
+ static size_t count_nodes_of_type(hb_array_T* array, ast_node_type_T type) {
149
+ if (!array || hb_array_size(array) == 0) { return 0; }
150
+
151
+ size_t count = 0;
152
+
153
+ for (size_t index = 0; index < hb_array_size(array); index++) {
154
+ AST_NODE_T* node = (AST_NODE_T*) hb_array_get(array, index);
155
+ if (!node) { continue; }
156
+
157
+ if (node->type == type) {
158
+ if (type == AST_HTML_OPEN_TAG_NODE) {
159
+ AST_HTML_OPEN_TAG_NODE_T* open_tag = (AST_HTML_OPEN_TAG_NODE_T*) node;
160
+ if (!open_tag->is_void) { count++; }
161
+ } else {
162
+ count++;
163
+ }
164
+ }
165
+ }
166
+
167
+ return count;
168
+ }
169
+
170
+ static bool conditions_are_equivalent(hb_string_T a, hb_string_T b) {
171
+ if (hb_string_is_empty(a) || hb_string_is_empty(b)) { return false; }
172
+
173
+ return hb_string_equals(a, b);
174
+ }
175
+
176
+ typedef struct {
177
+ size_t open_index;
178
+ AST_NODE_T* open_conditional;
179
+ AST_HTML_OPEN_TAG_NODE_T* open_tag;
180
+ hb_string_T tag_name;
181
+ hb_string_T condition;
182
+ bool is_if;
183
+ } conditional_open_tag_T;
184
+
185
+ static void rewrite_conditional_elements(hb_array_T* nodes, hb_array_T* document_errors, hb_allocator_T* allocator) {
186
+ if (!nodes || hb_array_size(nodes) == 0) { return; }
187
+ if (!document_errors) { return; }
188
+
189
+ for (size_t open_index = 0; open_index < hb_array_size(nodes); open_index++) {
190
+ AST_NODE_T* open_node = (AST_NODE_T*) hb_array_get(nodes, open_index);
191
+
192
+ if (!open_node) { continue; }
193
+ if (open_node->type != AST_ERB_IF_NODE && open_node->type != AST_ERB_UNLESS_NODE) { continue; }
194
+ if (!is_simple_erb_conditional(open_node)) { continue; }
195
+
196
+ hb_array_T* open_statements = get_erb_conditional_statements(open_node);
197
+
198
+ size_t open_tag_count = count_nodes_of_type(open_statements, AST_HTML_OPEN_TAG_NODE);
199
+ if (open_tag_count < 2) { continue; }
200
+
201
+ size_t open_close_tag_count = count_nodes_of_type(open_statements, AST_HTML_CLOSE_TAG_NODE);
202
+ if (open_tag_count <= open_close_tag_count) { continue; }
203
+
204
+ bool open_is_if;
205
+ hb_string_T open_condition = extract_condition_from_erb_content(open_node, &open_is_if);
206
+
207
+ if (hb_string_is_null(open_condition)) { continue; }
208
+
209
+ for (size_t close_index = open_index + 1; close_index < hb_array_size(nodes); close_index++) {
210
+ AST_NODE_T* close_node = (AST_NODE_T*) hb_array_get(nodes, close_index);
211
+
212
+ if (!close_node) { continue; }
213
+ if (close_node->type != AST_ERB_IF_NODE && close_node->type != AST_ERB_UNLESS_NODE) { continue; }
214
+ if (!is_simple_erb_conditional(close_node)) { continue; }
215
+
216
+ hb_array_T* close_statements = get_erb_conditional_statements(close_node);
217
+
218
+ size_t close_tag_count = count_nodes_of_type(close_statements, AST_HTML_CLOSE_TAG_NODE);
219
+ if (close_tag_count < 2) { continue; }
220
+
221
+ size_t close_open_tag_count = count_nodes_of_type(close_statements, AST_HTML_OPEN_TAG_NODE);
222
+ if (close_tag_count <= close_open_tag_count) { continue; }
223
+
224
+ bool close_is_if;
225
+ hb_string_T close_condition = extract_condition_from_erb_content(close_node, &close_is_if);
226
+
227
+ if (hb_string_is_null(close_condition)) { continue; }
228
+
229
+ if (open_is_if == close_is_if && conditions_are_equivalent(open_condition, close_condition)) {
230
+ CONDITIONAL_ELEMENT_MULTIPLE_TAGS_ERROR_T* multiple_tags_error = conditional_element_multiple_tags_error_init(
231
+ open_node->location.start.line,
232
+ open_node->location.start.column,
233
+ open_node->location.start,
234
+ open_node->location.end,
235
+ allocator
236
+ );
237
+
238
+ hb_array_append(document_errors, multiple_tags_error);
239
+ break;
240
+ }
241
+ }
242
+ }
243
+
244
+ hb_array_T* open_stack = hb_array_init(8, allocator);
245
+
246
+ for (size_t node_index = 0; node_index < hb_array_size(nodes); node_index++) {
247
+ AST_NODE_T* node = (AST_NODE_T*) hb_array_get(nodes, node_index);
248
+
249
+ if (!node) { continue; }
250
+ if (node->type != AST_ERB_IF_NODE && node->type != AST_ERB_UNLESS_NODE) { continue; }
251
+ if (!is_simple_erb_conditional(node)) { continue; }
252
+
253
+ hb_array_T* statements = get_erb_conditional_statements(node);
254
+ AST_HTML_OPEN_TAG_NODE_T* open_tag = NULL;
255
+
256
+ if (contains_single_open_tag(statements, &open_tag)) {
257
+ conditional_open_tag_T* entry = malloc(sizeof(conditional_open_tag_T));
258
+
259
+ if (!entry) { continue; }
260
+
261
+ entry->open_index = node_index;
262
+ entry->open_conditional = node;
263
+ entry->open_tag = open_tag;
264
+ entry->tag_name = open_tag->tag_name->value;
265
+
266
+ bool is_if;
267
+ entry->condition = extract_condition_from_erb_content(node, &is_if);
268
+ entry->is_if = is_if;
269
+
270
+ hb_array_append(open_stack, entry);
271
+ }
272
+ }
273
+
274
+ hb_array_T* consumed_indices = hb_array_init(8, allocator);
275
+
276
+ for (size_t node_index = 0; node_index < hb_array_size(nodes); node_index++) {
277
+ AST_NODE_T* node = (AST_NODE_T*) hb_array_get(nodes, node_index);
278
+
279
+ if (!node) { continue; }
280
+ if (node->type != AST_ERB_IF_NODE && node->type != AST_ERB_UNLESS_NODE) { continue; }
281
+ if (!is_simple_erb_conditional(node)) { continue; }
282
+
283
+ hb_array_T* statements = get_erb_conditional_statements(node);
284
+ AST_HTML_CLOSE_TAG_NODE_T* close_tag = NULL;
285
+
286
+ if (!contains_single_close_tag(statements, &close_tag)) { continue; }
287
+
288
+ conditional_open_tag_T* matched_open = NULL;
289
+ size_t matched_stack_index = (size_t) -1;
290
+
291
+ conditional_open_tag_T* mismatched_open = NULL;
292
+ hb_string_T mismatched_close_condition = HB_STRING_NULL;
293
+
294
+ for (size_t stack_index = hb_array_size(open_stack); stack_index > 0; stack_index--) {
295
+ conditional_open_tag_T* entry = (conditional_open_tag_T*) hb_array_get(open_stack, stack_index - 1);
296
+
297
+ if (!entry) { continue; }
298
+ if (!hb_string_equals_case_insensitive(entry->tag_name, close_tag->tag_name->value)) { continue; }
299
+
300
+ bool close_is_if;
301
+ hb_string_T close_condition = extract_condition_from_erb_content(node, &close_is_if);
302
+
303
+ if (entry->is_if != close_is_if) { continue; }
304
+
305
+ if (!conditions_are_equivalent(entry->condition, close_condition)) {
306
+ if (!mismatched_open && entry->open_index < node_index) {
307
+ mismatched_open = entry;
308
+ mismatched_close_condition = close_condition;
309
+ }
310
+
311
+ continue;
312
+ }
313
+
314
+ if (!hb_string_is_null(mismatched_close_condition)) {
315
+ mismatched_close_condition = HB_STRING_NULL;
316
+ mismatched_open = NULL;
317
+ }
318
+
319
+ if (entry->open_index >= node_index) { continue; }
320
+
321
+ matched_open = entry;
322
+ matched_stack_index = stack_index - 1;
323
+
324
+ break;
325
+ }
326
+
327
+ if (!matched_open && mismatched_open && !hb_string_is_null(mismatched_close_condition)) {
328
+ CONDITIONAL_ELEMENT_CONDITION_MISMATCH_ERROR_T* mismatch_error =
329
+ conditional_element_condition_mismatch_error_init(
330
+ mismatched_open->tag_name,
331
+ mismatched_open->condition,
332
+ mismatched_open->open_conditional->location.start.line,
333
+ mismatched_open->open_conditional->location.start.column,
334
+ mismatched_close_condition,
335
+ node->location.start.line,
336
+ node->location.start.column,
337
+ mismatched_open->open_conditional->location.start,
338
+ node->location.end,
339
+ allocator
340
+ );
341
+
342
+ hb_array_append(document_errors, mismatch_error);
343
+ continue;
344
+ }
345
+
346
+ if (!matched_open) { continue; }
347
+
348
+ hb_array_T* body = hb_array_init(8, allocator);
349
+
350
+ for (size_t body_index = matched_open->open_index + 1; body_index < node_index; body_index++) {
351
+ AST_NODE_T* body_node = (AST_NODE_T*) hb_array_get(nodes, body_index);
352
+
353
+ if (body_node) { hb_array_append(body, body_node); }
354
+ }
355
+
356
+ position_T start_position = matched_open->open_conditional->location.start;
357
+ position_T end_position = node->location.end;
358
+ hb_array_T* errors = hb_array_init(0, allocator);
359
+
360
+ AST_HTML_CONDITIONAL_ELEMENT_NODE_T* conditional_element = ast_html_conditional_element_node_init(
361
+ matched_open->condition,
362
+ matched_open->open_conditional,
363
+ matched_open->open_tag,
364
+ body,
365
+ (AST_NODE_T*) close_tag,
366
+ node,
367
+ matched_open->open_tag->tag_name,
368
+ ELEMENT_SOURCE_HTML,
369
+ start_position,
370
+ end_position,
371
+ errors,
372
+ allocator
373
+ );
374
+
375
+ for (size_t body_index = matched_open->open_index + 1; body_index < node_index; body_index++) {
376
+ size_t* consumed_index = malloc(sizeof(size_t));
377
+
378
+ if (consumed_index) {
379
+ *consumed_index = body_index;
380
+ hb_array_append(consumed_indices, consumed_index);
381
+ }
382
+
383
+ hb_array_set(nodes, body_index, NULL);
384
+ }
385
+
386
+ hb_array_set(nodes, matched_open->open_index, conditional_element);
387
+
388
+ size_t* close_index = malloc(sizeof(size_t));
389
+
390
+ if (close_index) {
391
+ *close_index = node_index;
392
+ hb_array_append(consumed_indices, close_index);
393
+ }
394
+ hb_array_set(nodes, node_index, NULL);
395
+
396
+ free(matched_open);
397
+ hb_array_set(open_stack, matched_stack_index, NULL);
398
+ }
399
+
400
+ for (size_t stack_index = 0; stack_index < hb_array_size(open_stack); stack_index++) {
401
+ conditional_open_tag_T* entry = (conditional_open_tag_T*) hb_array_get(open_stack, stack_index);
402
+
403
+ if (entry) { free(entry); }
404
+ }
405
+
406
+ hb_array_free(&open_stack);
407
+
408
+ if (hb_array_size(consumed_indices) > 0) {
409
+ hb_array_T* new_nodes = hb_array_init(hb_array_size(nodes), allocator);
410
+
411
+ for (size_t node_index = 0; node_index < hb_array_size(nodes); node_index++) {
412
+ bool consumed = false;
413
+
414
+ for (size_t consumed_index = 0; consumed_index < hb_array_size(consumed_indices); consumed_index++) {
415
+ size_t* stored_index = (size_t*) hb_array_get(consumed_indices, consumed_index);
416
+
417
+ if (stored_index && *stored_index == node_index) {
418
+ consumed = true;
419
+ break;
420
+ }
421
+ }
422
+
423
+ if (!consumed) {
424
+ AST_NODE_T* node = (AST_NODE_T*) hb_array_get(nodes, node_index);
425
+ if (node) { hb_array_append(new_nodes, node); }
426
+ }
427
+ }
428
+
429
+ nodes->size = 0;
430
+
431
+ for (size_t new_index = 0; new_index < hb_array_size(new_nodes); new_index++) {
432
+ hb_array_append(nodes, hb_array_get(new_nodes, new_index));
433
+ }
434
+
435
+ hb_array_free(&new_nodes);
436
+ }
437
+
438
+ for (size_t i = 0; i < hb_array_size(consumed_indices); i++) {
439
+ size_t* index = (size_t*) hb_array_get(consumed_indices, i);
440
+
441
+ if (index) { free(index); }
442
+ }
443
+
444
+ hb_array_free(&consumed_indices);
445
+ }
446
+
447
+ static bool transform_conditional_elements_visitor(const AST_NODE_T* node, void* data);
448
+
449
+ static void transform_conditional_elements_in_array(hb_array_T* array, conditional_elements_context_T* context) {
450
+ if (!array) { return; }
451
+
452
+ for (size_t i = 0; i < hb_array_size(array); i++) {
453
+ AST_NODE_T* child = (AST_NODE_T*) hb_array_get(array, i);
454
+
455
+ if (child) { herb_visit_node(child, transform_conditional_elements_visitor, context); }
456
+ }
457
+
458
+ rewrite_conditional_elements(array, context->document_errors, context->allocator);
459
+ }
460
+
461
+ static bool transform_conditional_elements_visitor(const AST_NODE_T* node, void* data) {
462
+ if (!node) { return false; }
463
+
464
+ conditional_elements_context_T* context = (conditional_elements_context_T*) data;
465
+
466
+ switch (node->type) {
467
+ case AST_DOCUMENT_NODE: {
468
+ AST_DOCUMENT_NODE_T* doc = (AST_DOCUMENT_NODE_T*) node;
469
+ transform_conditional_elements_in_array(doc->children, context);
470
+ return false;
471
+ }
472
+
473
+ case AST_HTML_ELEMENT_NODE: {
474
+ AST_HTML_ELEMENT_NODE_T* element = (AST_HTML_ELEMENT_NODE_T*) node;
475
+ transform_conditional_elements_in_array(element->body, context);
476
+ return false;
477
+ }
478
+
479
+ case AST_ERB_IF_NODE: {
480
+ AST_ERB_IF_NODE_T* if_node = (AST_ERB_IF_NODE_T*) node;
481
+ transform_conditional_elements_in_array(if_node->statements, context);
482
+
483
+ if (if_node->subsequent) { herb_visit_node(if_node->subsequent, transform_conditional_elements_visitor, data); }
484
+
485
+ return false;
486
+ }
487
+
488
+ case AST_ERB_ELSE_NODE: {
489
+ AST_ERB_ELSE_NODE_T* else_node = (AST_ERB_ELSE_NODE_T*) node;
490
+ transform_conditional_elements_in_array(else_node->statements, context);
491
+ return false;
492
+ }
493
+
494
+ case AST_ERB_UNLESS_NODE: {
495
+ AST_ERB_UNLESS_NODE_T* unless_node = (AST_ERB_UNLESS_NODE_T*) node;
496
+ transform_conditional_elements_in_array(unless_node->statements, context);
497
+
498
+ if (unless_node->else_clause) {
499
+ herb_visit_node((AST_NODE_T*) unless_node->else_clause, transform_conditional_elements_visitor, data);
500
+ }
501
+
502
+ return false;
503
+ }
504
+
505
+ case AST_ERB_BLOCK_NODE: {
506
+ AST_ERB_BLOCK_NODE_T* block_node = (AST_ERB_BLOCK_NODE_T*) node;
507
+ transform_conditional_elements_in_array(block_node->body, context);
508
+ return false;
509
+ }
510
+
511
+ case AST_ERB_WHILE_NODE: {
512
+ AST_ERB_WHILE_NODE_T* while_node = (AST_ERB_WHILE_NODE_T*) node;
513
+ transform_conditional_elements_in_array(while_node->statements, context);
514
+ return false;
515
+ }
516
+
517
+ case AST_ERB_UNTIL_NODE: {
518
+ AST_ERB_UNTIL_NODE_T* until_node = (AST_ERB_UNTIL_NODE_T*) node;
519
+ transform_conditional_elements_in_array(until_node->statements, context);
520
+ return false;
521
+ }
522
+
523
+ case AST_ERB_FOR_NODE: {
524
+ AST_ERB_FOR_NODE_T* for_node = (AST_ERB_FOR_NODE_T*) node;
525
+ transform_conditional_elements_in_array(for_node->statements, context);
526
+ return false;
527
+ }
528
+
529
+ case AST_ERB_CASE_NODE: {
530
+ AST_ERB_CASE_NODE_T* case_node = (AST_ERB_CASE_NODE_T*) node;
531
+ transform_conditional_elements_in_array(case_node->children, context);
532
+
533
+ for (size_t i = 0; i < hb_array_size(case_node->conditions); i++) {
534
+ AST_NODE_T* when = (AST_NODE_T*) hb_array_get(case_node->conditions, i);
535
+ if (when) { herb_visit_node(when, transform_conditional_elements_visitor, data); }
536
+ }
537
+
538
+ if (case_node->else_clause) {
539
+ herb_visit_node((AST_NODE_T*) case_node->else_clause, transform_conditional_elements_visitor, data);
540
+ }
541
+
542
+ return false;
543
+ }
544
+
545
+ case AST_ERB_WHEN_NODE: {
546
+ AST_ERB_WHEN_NODE_T* when_node = (AST_ERB_WHEN_NODE_T*) node;
547
+ transform_conditional_elements_in_array(when_node->statements, context);
548
+ return false;
549
+ }
550
+
551
+ case AST_ERB_BEGIN_NODE: {
552
+ AST_ERB_BEGIN_NODE_T* begin_node = (AST_ERB_BEGIN_NODE_T*) node;
553
+ transform_conditional_elements_in_array(begin_node->statements, context);
554
+
555
+ if (begin_node->rescue_clause) {
556
+ herb_visit_node((AST_NODE_T*) begin_node->rescue_clause, transform_conditional_elements_visitor, data);
557
+ }
558
+
559
+ if (begin_node->else_clause) {
560
+ herb_visit_node((AST_NODE_T*) begin_node->else_clause, transform_conditional_elements_visitor, data);
561
+ }
562
+
563
+ if (begin_node->ensure_clause) {
564
+ herb_visit_node((AST_NODE_T*) begin_node->ensure_clause, transform_conditional_elements_visitor, data);
565
+ }
566
+
567
+ return false;
568
+ }
569
+
570
+ case AST_ERB_RESCUE_NODE: {
571
+ AST_ERB_RESCUE_NODE_T* rescue_node = (AST_ERB_RESCUE_NODE_T*) node;
572
+ transform_conditional_elements_in_array(rescue_node->statements, context);
573
+
574
+ if (rescue_node->subsequent) {
575
+ herb_visit_node((AST_NODE_T*) rescue_node->subsequent, transform_conditional_elements_visitor, data);
576
+ }
577
+
578
+ return false;
579
+ }
580
+
581
+ case AST_ERB_ENSURE_NODE: {
582
+ AST_ERB_ENSURE_NODE_T* ensure_node = (AST_ERB_ENSURE_NODE_T*) node;
583
+ transform_conditional_elements_in_array(ensure_node->statements, context);
584
+ return false;
585
+ }
586
+
587
+ default: return true;
588
+ }
589
+ }
590
+
591
+ void herb_transform_conditional_elements(AST_DOCUMENT_NODE_T* document, hb_allocator_T* allocator) {
592
+ conditional_elements_context_T context = { .document_errors = document->base.errors, .allocator = allocator };
593
+ herb_visit_node((AST_NODE_T*) document, transform_conditional_elements_visitor, &context);
594
+ }