herb 0.9.5-arm-linux-gnu → 0.9.6-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 (47) hide show
  1. checksums.yaml +4 -4
  2. data/ext/herb/extension.c +8 -0
  3. data/ext/herb/extension_helpers.c +1 -0
  4. data/lib/herb/3.0/herb.so +0 -0
  5. data/lib/herb/3.1/herb.so +0 -0
  6. data/lib/herb/3.2/herb.so +0 -0
  7. data/lib/herb/3.3/herb.so +0 -0
  8. data/lib/herb/3.4/herb.so +0 -0
  9. data/lib/herb/4.0/herb.so +0 -0
  10. data/lib/herb/action_view/helper_registry.rb +8107 -0
  11. data/lib/herb/parser_options.rb +7 -2
  12. data/lib/herb/project.rb +2 -5
  13. data/lib/herb/version.rb +1 -1
  14. data/sig/herb/action_view/helper_registry.rbs +1144 -0
  15. data/sig/herb/parser_options.rbs +6 -2
  16. data/src/analyze/action_view/generated_handlers.c +355 -0
  17. data/src/analyze/action_view/generated_handlers.h +16 -0
  18. data/src/analyze/action_view/helper_registry.c +7244 -0
  19. data/src/analyze/action_view/image_tag.c +4 -31
  20. data/src/analyze/action_view/javascript_include_tag.c +1 -42
  21. data/src/analyze/action_view/javascript_tag.c +0 -52
  22. data/src/analyze/action_view/registry.c +2 -2
  23. data/src/analyze/action_view/tag_helpers.c +8 -120
  24. data/src/analyze/action_view/turbo_frame_tag.c +1 -36
  25. data/src/analyze/analyze.c +7 -0
  26. data/src/analyze/postfix_conditionals.c +326 -0
  27. data/src/analyze/ternary_conditionals.c +265 -0
  28. data/src/include/analyze/action_view/helper_registry.h +325 -0
  29. data/src/include/analyze/action_view/tag_helpers.h +0 -1
  30. data/src/include/analyze/postfix_conditionals.h +9 -0
  31. data/src/include/analyze/ternary_conditionals.h +15 -0
  32. data/src/include/parser/parser.h +1 -0
  33. data/src/include/version.h +1 -1
  34. data/src/parser.c +1 -0
  35. data/templates/java/org/herb/ast/HelperRegistry.java.erb +258 -0
  36. data/templates/javascript/packages/core/src/action-view-helpers.ts.erb +171 -0
  37. data/templates/javascript/packages/core/src/nodes.ts.erb +5 -1
  38. data/templates/lib/herb/action_view/helper_registry.rb.erb +288 -0
  39. data/templates/rust/src/action_view_helpers.rs.erb +154 -0
  40. data/templates/src/analyze/action_view/generated_handlers.c.erb +230 -0
  41. data/templates/src/analyze/action_view/generated_handlers.h.erb +12 -0
  42. data/templates/src/analyze/action_view/helper_registry.c.erb +114 -0
  43. data/templates/src/include/analyze/action_view/helper_registry.h.erb +82 -0
  44. data/templates/template.rb +338 -1
  45. metadata +19 -3
  46. data/src/analyze/action_view/content_tag.c +0 -78
  47. data/src/analyze/action_view/tag.c +0 -87
@@ -0,0 +1,326 @@
1
+ #include "../include/analyze/postfix_conditionals.h"
2
+ #include "../include/analyze/action_view/tag_helper_node_builders.h"
3
+ #include "../include/analyze/analyze.h"
4
+ #include "../include/analyze/analyzed_ruby.h"
5
+ #include "../include/analyze/ternary_conditionals.h"
6
+ #include "../include/ast/ast_nodes.h"
7
+ #include "../include/lib/hb_allocator.h"
8
+ #include "../include/lib/hb_array.h"
9
+ #include "../include/lib/hb_string.h"
10
+ #include "../include/prism/herb_prism_node.h"
11
+ #include "../include/visitor.h"
12
+
13
+ #include <prism.h>
14
+ #include <stdbool.h>
15
+ #include <string.h>
16
+
17
+ static bool is_erb_output_tag(AST_ERB_CONTENT_NODE_T* erb_node) {
18
+ if (!erb_node || !erb_node->tag_opening) { return false; }
19
+
20
+ hb_string_T opening = erb_node->tag_opening->value;
21
+
22
+ return opening.length >= 3 && opening.data[0] == '<' && opening.data[1] == '%' && opening.data[2] == '=';
23
+ }
24
+
25
+ static pm_node_t* find_postfix_conditional_statement(analyzed_ruby_T* analyzed) {
26
+ if (!analyzed || !analyzed->valid || !analyzed->root) { return NULL; }
27
+
28
+ if (analyzed->root->type != PM_PROGRAM_NODE) { return NULL; }
29
+ pm_program_node_t* program = (pm_program_node_t*) analyzed->root;
30
+
31
+ if (!program->statements || program->statements->body.size != 1) { return NULL; }
32
+ pm_node_t* statement = program->statements->body.nodes[0];
33
+
34
+ if (statement->type == PM_IF_NODE) {
35
+ pm_if_node_t* if_node = (pm_if_node_t*) statement;
36
+
37
+ if (if_node->if_keyword_loc.start != NULL && if_node->end_keyword_loc.start == NULL) { return statement; }
38
+ } else if (statement->type == PM_UNLESS_NODE) {
39
+ pm_unless_node_t* unless_node = (pm_unless_node_t*) statement;
40
+
41
+ if (unless_node->keyword_loc.start != NULL && unless_node->end_keyword_loc.start == NULL) { return statement; }
42
+ }
43
+
44
+ return NULL;
45
+ }
46
+
47
+ typedef struct {
48
+ char* source;
49
+ size_t offset_in_content;
50
+ size_t length;
51
+ } body_info_T;
52
+
53
+ static body_info_T extract_statements_body_info(
54
+ pm_statements_node_t* statements,
55
+ analyzed_ruby_T* analyzed,
56
+ hb_allocator_T* allocator
57
+ ) {
58
+ body_info_T info = { .source = NULL, .offset_in_content = 0, .length = 0 };
59
+
60
+ if (!statements || statements->body.size == 0) { return info; }
61
+
62
+ pm_node_t* first = statements->body.nodes[0];
63
+ pm_node_t* last = statements->body.nodes[statements->body.size - 1];
64
+
65
+ const uint8_t* start = first->location.start;
66
+ const uint8_t* end = last->location.end;
67
+
68
+ const uint8_t* parser_start = analyzed->parser.start;
69
+ size_t source_length = (size_t) (analyzed->parser.end - parser_start);
70
+
71
+ if (start < parser_start || end > parser_start + source_length) { return info; }
72
+
73
+ const uint8_t* body_start = start;
74
+ const uint8_t* body_end = end;
75
+
76
+ if (body_start > parser_start && *(body_start - 1) == ' ') { body_start--; }
77
+ if (body_end < parser_start + source_length && *body_end == ' ') { body_end++; }
78
+
79
+ info.offset_in_content = (size_t) (body_start - parser_start);
80
+ info.length = (size_t) (body_end - body_start);
81
+ info.source = hb_allocator_strndup(allocator, (const char*) body_start, info.length);
82
+
83
+ return info;
84
+ }
85
+
86
+ static body_info_T extract_body_info(
87
+ pm_node_t* conditional_node,
88
+ analyzed_ruby_T* analyzed,
89
+ hb_allocator_T* allocator
90
+ ) {
91
+ pm_statements_node_t* statements = NULL;
92
+
93
+ if (conditional_node->type == PM_IF_NODE) {
94
+ statements = ((pm_if_node_t*) conditional_node)->statements;
95
+ } else if (conditional_node->type == PM_UNLESS_NODE) {
96
+ statements = ((pm_unless_node_t*) conditional_node)->statements;
97
+ }
98
+
99
+ body_info_T info = extract_statements_body_info(statements, analyzed, allocator);
100
+
101
+ if (info.source) {
102
+ info.length = info.offset_in_content + info.length;
103
+ info.offset_in_content = 0;
104
+ info.source = hb_allocator_strndup(allocator, (const char*) analyzed->parser.start, info.length);
105
+ }
106
+
107
+ return info;
108
+ }
109
+
110
+ static char* extract_condition_source(pm_node_t* conditional_node, hb_allocator_T* allocator) {
111
+ pm_node_t* predicate = NULL;
112
+
113
+ if (conditional_node->type == PM_IF_NODE) {
114
+ predicate = ((pm_if_node_t*) conditional_node)->predicate;
115
+ } else if (conditional_node->type == PM_UNLESS_NODE) {
116
+ predicate = ((pm_unless_node_t*) conditional_node)->predicate;
117
+ }
118
+
119
+ if (!predicate) { return NULL; }
120
+
121
+ size_t length = (size_t) (predicate->location.end - predicate->location.start);
122
+
123
+ return hb_allocator_strndup(allocator, (const char*) predicate->location.start, length);
124
+ }
125
+
126
+ static const char* condition_keyword(pm_node_t* conditional_node) {
127
+ if (conditional_node->type == PM_IF_NODE) { return "if"; }
128
+ if (conditional_node->type == PM_UNLESS_NODE) { return "unless"; }
129
+
130
+ return NULL;
131
+ }
132
+
133
+ static pm_if_node_t* find_nested_ternary(pm_node_t* conditional_node) {
134
+ pm_statements_node_t* statements = NULL;
135
+
136
+ if (conditional_node->type == PM_IF_NODE) {
137
+ statements = ((pm_if_node_t*) conditional_node)->statements;
138
+ } else if (conditional_node->type == PM_UNLESS_NODE) {
139
+ statements = ((pm_unless_node_t*) conditional_node)->statements;
140
+ }
141
+
142
+ if (!statements || statements->body.size != 1) { return NULL; }
143
+
144
+ pm_node_t* body_node = statements->body.nodes[0];
145
+
146
+ if (body_node->type == PM_PARENTHESES_NODE) {
147
+ pm_parentheses_node_t* parens = (pm_parentheses_node_t*) body_node;
148
+
149
+ if (!parens->body || parens->body->type != PM_STATEMENTS_NODE) { return NULL; }
150
+
151
+ pm_statements_node_t* inner_statements = (pm_statements_node_t*) parens->body;
152
+
153
+ if (inner_statements->body.size != 1) { return NULL; }
154
+
155
+ body_node = inner_statements->body.nodes[0];
156
+ }
157
+
158
+ if (body_node->type != PM_IF_NODE) { return NULL; }
159
+
160
+ pm_if_node_t* if_node = (pm_if_node_t*) body_node;
161
+
162
+ if (if_node->if_keyword_loc.start == NULL && if_node->subsequent != NULL) { return if_node; }
163
+
164
+ return NULL;
165
+ }
166
+
167
+ static AST_NODE_T* transform_conditional(
168
+ AST_ERB_CONTENT_NODE_T* erb_node,
169
+ pm_node_t* conditional_node,
170
+ hb_allocator_T* allocator
171
+ ) {
172
+ body_info_T body_info = extract_body_info(conditional_node, erb_node->analyzed_ruby, allocator);
173
+ if (!body_info.source) { return NULL; }
174
+
175
+ char* condition_source = extract_condition_source(conditional_node, allocator);
176
+ if (!condition_source) { return NULL; }
177
+
178
+ const char* keyword = condition_keyword(conditional_node);
179
+ if (!keyword) { return NULL; }
180
+
181
+ position_T start = erb_node->base.location.start;
182
+ position_T end = erb_node->base.location.end;
183
+ position_T content_start = erb_node->content->location.start;
184
+
185
+ position_T body_content_start = { .line = content_start.line,
186
+ .column = content_start.column + (uint32_t) body_info.offset_in_content };
187
+
188
+ position_T body_content_end = { .line = content_start.line,
189
+ .column = content_start.column
190
+ + (uint32_t) (body_info.offset_in_content + body_info.length) };
191
+
192
+ token_T* body_content =
193
+ create_synthetic_token(allocator, body_info.source, TOKEN_ERB_CONTENT, body_content_start, body_content_end);
194
+
195
+ AST_ERB_CONTENT_NODE_T* body_erb_node = ast_erb_content_node_init(
196
+ erb_node->tag_opening,
197
+ body_content,
198
+ erb_node->tag_closing,
199
+ NULL,
200
+ false,
201
+ true,
202
+ HERB_PRISM_NODE_EMPTY,
203
+ start,
204
+ end,
205
+ hb_array_init(0, allocator),
206
+ allocator
207
+ );
208
+
209
+ if (!body_erb_node) { return NULL; }
210
+
211
+ hb_array_T* statements = hb_array_init(1, allocator);
212
+ hb_array_append(statements, (AST_NODE_T*) body_erb_node);
213
+
214
+ pm_if_node_t* nested_ternary = find_nested_ternary(conditional_node);
215
+
216
+ if (nested_ternary) {
217
+ AST_NODE_T* ternary_replacement = transform_ternary_expression(erb_node, nested_ternary, allocator);
218
+
219
+ if (ternary_replacement) { hb_array_set(statements, 0, ternary_replacement); }
220
+ }
221
+
222
+ hb_buffer_T condition_buffer;
223
+ hb_buffer_init(&condition_buffer, 64, allocator);
224
+ hb_buffer_append(&condition_buffer, " ");
225
+ hb_buffer_append(&condition_buffer, keyword);
226
+ hb_buffer_append(&condition_buffer, " ");
227
+ hb_buffer_append(&condition_buffer, condition_source);
228
+ hb_buffer_append(&condition_buffer, " ");
229
+
230
+ const char* condition_content = hb_buffer_value(&condition_buffer);
231
+
232
+ token_T* tag_opening = create_synthetic_token(allocator, "<%", TOKEN_ERB_START, start, start);
233
+ token_T* content_token = create_synthetic_token(allocator, condition_content, TOKEN_ERB_CONTENT, start, end);
234
+ token_T* tag_closing = create_synthetic_token(allocator, "%>", TOKEN_ERB_END, end, end);
235
+
236
+ token_T* end_opening = create_synthetic_token(allocator, "<%", TOKEN_ERB_START, end, end);
237
+ token_T* end_content = create_synthetic_token(allocator, " end ", TOKEN_ERB_CONTENT, end, end);
238
+ token_T* end_closing = create_synthetic_token(allocator, "%>", TOKEN_ERB_END, end, end);
239
+
240
+ AST_ERB_END_NODE_T* end_node =
241
+ ast_erb_end_node_init(end_opening, end_content, end_closing, end, end, hb_array_init(0, allocator), allocator);
242
+
243
+ herb_prism_node_T empty_prism_node = HERB_PRISM_NODE_EMPTY;
244
+
245
+ if (conditional_node->type == PM_IF_NODE) {
246
+ AST_ERB_IF_NODE_T* if_node = ast_erb_if_node_init(
247
+ tag_opening,
248
+ content_token,
249
+ tag_closing,
250
+ NULL,
251
+ empty_prism_node,
252
+ statements,
253
+ NULL,
254
+ end_node,
255
+ start,
256
+ end,
257
+ hb_array_init(0, allocator),
258
+ allocator
259
+ );
260
+
261
+ return (AST_NODE_T*) if_node;
262
+ } else {
263
+ AST_ERB_UNLESS_NODE_T* unless_node = ast_erb_unless_node_init(
264
+ tag_opening,
265
+ content_token,
266
+ tag_closing,
267
+ NULL,
268
+ empty_prism_node,
269
+ statements,
270
+ NULL,
271
+ end_node,
272
+ start,
273
+ end,
274
+ hb_array_init(0, allocator),
275
+ allocator
276
+ );
277
+
278
+ return (AST_NODE_T*) unless_node;
279
+ }
280
+ }
281
+
282
+ static void transform_conditional_array(hb_array_T* array, analyze_ruby_context_T* context) {
283
+ if (!array || !context) { return; }
284
+
285
+ for (size_t i = 0; i < hb_array_size(array); i++) {
286
+ AST_NODE_T* child = hb_array_get(array, i);
287
+ if (!child) { continue; }
288
+ if (child->type != AST_ERB_CONTENT_NODE) { continue; }
289
+
290
+ AST_ERB_CONTENT_NODE_T* erb_node = (AST_ERB_CONTENT_NODE_T*) child;
291
+ if (!is_erb_output_tag(erb_node)) { continue; }
292
+ if (!erb_node->analyzed_ruby) { continue; }
293
+
294
+ pm_node_t* conditional_node = find_postfix_conditional_statement(erb_node->analyzed_ruby);
295
+ if (!conditional_node) { continue; }
296
+
297
+ AST_NODE_T* replacement = transform_conditional(erb_node, conditional_node, context->allocator);
298
+ if (replacement) { hb_array_set(array, i, replacement); }
299
+ }
300
+ }
301
+
302
+ static void transform_conditional_blocks(const AST_NODE_T* node, analyze_ruby_context_T* context) {
303
+ if (!node || !context) { return; }
304
+
305
+ switch (node->type) {
306
+ case AST_DOCUMENT_NODE: transform_conditional_array(((AST_DOCUMENT_NODE_T*) node)->children, context); break;
307
+ case AST_HTML_ELEMENT_NODE: transform_conditional_array(((AST_HTML_ELEMENT_NODE_T*) node)->body, context); break;
308
+ case AST_HTML_OPEN_TAG_NODE:
309
+ transform_conditional_array(((AST_HTML_OPEN_TAG_NODE_T*) node)->children, context);
310
+ break;
311
+ case AST_HTML_ATTRIBUTE_VALUE_NODE:
312
+ transform_conditional_array(((AST_HTML_ATTRIBUTE_VALUE_NODE_T*) node)->children, context);
313
+ break;
314
+ default: break;
315
+ }
316
+ }
317
+
318
+ bool transform_conditional_nodes(const AST_NODE_T* node, void* data) {
319
+ analyze_ruby_context_T* context = (analyze_ruby_context_T*) data;
320
+
321
+ transform_conditional_blocks(node, context);
322
+
323
+ herb_visit_child_nodes(node, transform_conditional_nodes, data);
324
+
325
+ return false;
326
+ }
@@ -0,0 +1,265 @@
1
+ #include "../include/analyze/ternary_conditionals.h"
2
+ #include "../include/analyze/action_view/tag_helper_node_builders.h"
3
+ #include "../include/analyze/analyze.h"
4
+ #include "../include/analyze/analyzed_ruby.h"
5
+ #include "../include/ast/ast_nodes.h"
6
+ #include "../include/lib/hb_allocator.h"
7
+ #include "../include/lib/hb_array.h"
8
+ #include "../include/lib/hb_string.h"
9
+ #include "../include/prism/herb_prism_node.h"
10
+ #include "../include/visitor.h"
11
+
12
+ #include <prism.h>
13
+ #include <stdbool.h>
14
+ #include <string.h>
15
+
16
+ static bool is_erb_output_tag(AST_ERB_CONTENT_NODE_T* erb_node) {
17
+ if (!erb_node || !erb_node->tag_opening) { return false; }
18
+
19
+ hb_string_T opening = erb_node->tag_opening->value;
20
+
21
+ return opening.length >= 3 && opening.data[0] == '<' && opening.data[1] == '%' && opening.data[2] == '=';
22
+ }
23
+
24
+ static pm_node_t* find_ternary_statement(analyzed_ruby_T* analyzed) {
25
+ if (!analyzed || !analyzed->valid || !analyzed->root) { return NULL; }
26
+
27
+ if (analyzed->root->type != PM_PROGRAM_NODE) { return NULL; }
28
+ pm_program_node_t* program = (pm_program_node_t*) analyzed->root;
29
+
30
+ if (!program->statements || program->statements->body.size != 1) { return NULL; }
31
+ pm_node_t* statement = program->statements->body.nodes[0];
32
+
33
+ if (statement->type != PM_IF_NODE) { return NULL; }
34
+ pm_if_node_t* if_node = (pm_if_node_t*) statement;
35
+
36
+ if (if_node->if_keyword_loc.start == NULL && if_node->subsequent != NULL) { return statement; }
37
+
38
+ return NULL;
39
+ }
40
+
41
+ typedef struct {
42
+ char* source;
43
+ size_t offset_in_content;
44
+ size_t length;
45
+ } body_info_T;
46
+
47
+ static body_info_T extract_statements_body_info(
48
+ pm_statements_node_t* statements,
49
+ analyzed_ruby_T* analyzed,
50
+ hb_allocator_T* allocator
51
+ ) {
52
+ body_info_T info = { .source = NULL, .offset_in_content = 0, .length = 0 };
53
+
54
+ if (!statements || statements->body.size == 0) { return info; }
55
+
56
+ pm_node_t* first = statements->body.nodes[0];
57
+ pm_node_t* last = statements->body.nodes[statements->body.size - 1];
58
+
59
+ const uint8_t* start = first->location.start;
60
+ const uint8_t* end = last->location.end;
61
+
62
+ const uint8_t* parser_start = analyzed->parser.start;
63
+ size_t source_length = (size_t) (analyzed->parser.end - parser_start);
64
+
65
+ if (start < parser_start || end > parser_start + source_length) { return info; }
66
+
67
+ const uint8_t* body_start = start;
68
+ const uint8_t* body_end = end;
69
+
70
+ if (body_start > parser_start && *(body_start - 1) == ' ') { body_start--; }
71
+ if (body_end < parser_start + source_length && *body_end == ' ') { body_end++; }
72
+
73
+ info.offset_in_content = (size_t) (body_start - parser_start);
74
+ info.length = (size_t) (body_end - body_start);
75
+ info.source = hb_allocator_strndup(allocator, (const char*) body_start, info.length);
76
+
77
+ return info;
78
+ }
79
+
80
+ static char* extract_condition_source(pm_if_node_t* if_node, hb_allocator_T* allocator) {
81
+ pm_node_t* predicate = if_node->predicate;
82
+
83
+ if (!predicate) { return NULL; }
84
+
85
+ size_t length = (size_t) (predicate->location.end - predicate->location.start);
86
+
87
+ return hb_allocator_strndup(allocator, (const char*) predicate->location.start, length);
88
+ }
89
+
90
+ AST_NODE_T* transform_ternary_expression(
91
+ AST_ERB_CONTENT_NODE_T* erb_node,
92
+ pm_if_node_t* if_node,
93
+ hb_allocator_T* allocator
94
+ ) {
95
+ body_info_T true_info = extract_statements_body_info(if_node->statements, erb_node->analyzed_ruby, allocator);
96
+ if (!true_info.source) { return NULL; }
97
+
98
+ pm_else_node_t* else_node = (pm_else_node_t*) if_node->subsequent;
99
+ if (!else_node) { return NULL; }
100
+
101
+ body_info_T false_info = extract_statements_body_info(else_node->statements, erb_node->analyzed_ruby, allocator);
102
+ if (!false_info.source) { return NULL; }
103
+
104
+ char* condition_source = extract_condition_source(if_node, allocator);
105
+ if (!condition_source) { return NULL; }
106
+
107
+ position_T start = erb_node->base.location.start;
108
+ position_T end = erb_node->base.location.end;
109
+ position_T content_start = erb_node->content->location.start;
110
+
111
+ position_T true_content_start = { .line = content_start.line,
112
+ .column = content_start.column + (uint32_t) true_info.offset_in_content };
113
+
114
+ position_T true_content_end = { .line = content_start.line,
115
+ .column = content_start.column
116
+ + (uint32_t) (true_info.offset_in_content + true_info.length) };
117
+
118
+ token_T* true_content =
119
+ create_synthetic_token(allocator, true_info.source, TOKEN_ERB_CONTENT, true_content_start, true_content_end);
120
+
121
+ AST_ERB_CONTENT_NODE_T* true_erb_node = ast_erb_content_node_init(
122
+ erb_node->tag_opening,
123
+ true_content,
124
+ erb_node->tag_closing,
125
+ NULL,
126
+ false,
127
+ true,
128
+ HERB_PRISM_NODE_EMPTY,
129
+ start,
130
+ end,
131
+ hb_array_init(0, allocator),
132
+ allocator
133
+ );
134
+
135
+ if (!true_erb_node) { return NULL; }
136
+
137
+ position_T false_content_start = { .line = content_start.line,
138
+ .column = content_start.column + (uint32_t) false_info.offset_in_content };
139
+
140
+ position_T false_content_end = { .line = content_start.line,
141
+ .column = content_start.column
142
+ + (uint32_t) (false_info.offset_in_content + false_info.length) };
143
+
144
+ token_T* false_content =
145
+ create_synthetic_token(allocator, false_info.source, TOKEN_ERB_CONTENT, false_content_start, false_content_end);
146
+
147
+ AST_ERB_CONTENT_NODE_T* false_erb_node = ast_erb_content_node_init(
148
+ erb_node->tag_opening,
149
+ false_content,
150
+ erb_node->tag_closing,
151
+ NULL,
152
+ false,
153
+ true,
154
+ HERB_PRISM_NODE_EMPTY,
155
+ start,
156
+ end,
157
+ hb_array_init(0, allocator),
158
+ allocator
159
+ );
160
+
161
+ if (!false_erb_node) { return NULL; }
162
+
163
+ hb_array_T* true_statements = hb_array_init(1, allocator);
164
+ hb_array_T* false_statements = hb_array_init(1, allocator);
165
+
166
+ hb_array_append(true_statements, (AST_NODE_T*) true_erb_node);
167
+ hb_array_append(false_statements, (AST_NODE_T*) false_erb_node);
168
+
169
+ token_T* else_opening = create_synthetic_token(allocator, "<%", TOKEN_ERB_START, end, end);
170
+ token_T* else_content_token = create_synthetic_token(allocator, " else ", TOKEN_ERB_CONTENT, end, end);
171
+ token_T* else_closing = create_synthetic_token(allocator, "%>", TOKEN_ERB_END, end, end);
172
+
173
+ AST_ERB_ELSE_NODE_T* erb_else_node = ast_erb_else_node_init(
174
+ else_opening,
175
+ else_content_token,
176
+ else_closing,
177
+ false_statements,
178
+ end,
179
+ end,
180
+ hb_array_init(0, allocator),
181
+ allocator
182
+ );
183
+
184
+ hb_buffer_T condition_buffer;
185
+ hb_buffer_init(&condition_buffer, 64, allocator);
186
+ hb_buffer_append(&condition_buffer, " if ");
187
+ hb_buffer_append(&condition_buffer, condition_source);
188
+ hb_buffer_append(&condition_buffer, " ");
189
+
190
+ const char* condition_content = hb_buffer_value(&condition_buffer);
191
+
192
+ token_T* if_opening = create_synthetic_token(allocator, "<%", TOKEN_ERB_START, start, start);
193
+ token_T* if_content = create_synthetic_token(allocator, condition_content, TOKEN_ERB_CONTENT, start, end);
194
+ token_T* if_closing = create_synthetic_token(allocator, "%>", TOKEN_ERB_END, end, end);
195
+
196
+ token_T* end_opening = create_synthetic_token(allocator, "<%", TOKEN_ERB_START, end, end);
197
+ token_T* end_content = create_synthetic_token(allocator, " end ", TOKEN_ERB_CONTENT, end, end);
198
+ token_T* end_closing = create_synthetic_token(allocator, "%>", TOKEN_ERB_END, end, end);
199
+
200
+ AST_ERB_END_NODE_T* end_node =
201
+ ast_erb_end_node_init(end_opening, end_content, end_closing, end, end, hb_array_init(0, allocator), allocator);
202
+
203
+ herb_prism_node_T empty_prism_node = HERB_PRISM_NODE_EMPTY;
204
+
205
+ AST_ERB_IF_NODE_T* result_if_node = ast_erb_if_node_init(
206
+ if_opening,
207
+ if_content,
208
+ if_closing,
209
+ NULL,
210
+ empty_prism_node,
211
+ true_statements,
212
+ (AST_NODE_T*) erb_else_node,
213
+ end_node,
214
+ start,
215
+ end,
216
+ hb_array_init(0, allocator),
217
+ allocator
218
+ );
219
+
220
+ return (AST_NODE_T*) result_if_node;
221
+ }
222
+
223
+ static void transform_ternary_array(hb_array_T* array, analyze_ruby_context_T* context) {
224
+ if (!array || !context) { return; }
225
+
226
+ for (size_t i = 0; i < hb_array_size(array); i++) {
227
+ AST_NODE_T* child = hb_array_get(array, i);
228
+ if (!child) { continue; }
229
+ if (child->type != AST_ERB_CONTENT_NODE) { continue; }
230
+
231
+ AST_ERB_CONTENT_NODE_T* erb_node = (AST_ERB_CONTENT_NODE_T*) child;
232
+ if (!is_erb_output_tag(erb_node)) { continue; }
233
+ if (!erb_node->analyzed_ruby) { continue; }
234
+
235
+ pm_node_t* ternary_node = find_ternary_statement(erb_node->analyzed_ruby);
236
+ if (!ternary_node) { continue; }
237
+
238
+ AST_NODE_T* replacement = transform_ternary_expression(erb_node, (pm_if_node_t*) ternary_node, context->allocator);
239
+ if (replacement) { hb_array_set(array, i, replacement); }
240
+ }
241
+ }
242
+
243
+ static void transform_ternary_blocks(const AST_NODE_T* node, analyze_ruby_context_T* context) {
244
+ if (!node || !context) { return; }
245
+
246
+ switch (node->type) {
247
+ case AST_DOCUMENT_NODE: transform_ternary_array(((AST_DOCUMENT_NODE_T*) node)->children, context); break;
248
+ case AST_HTML_ELEMENT_NODE: transform_ternary_array(((AST_HTML_ELEMENT_NODE_T*) node)->body, context); break;
249
+ case AST_HTML_OPEN_TAG_NODE: transform_ternary_array(((AST_HTML_OPEN_TAG_NODE_T*) node)->children, context); break;
250
+ case AST_HTML_ATTRIBUTE_VALUE_NODE:
251
+ transform_ternary_array(((AST_HTML_ATTRIBUTE_VALUE_NODE_T*) node)->children, context);
252
+ break;
253
+ default: break;
254
+ }
255
+ }
256
+
257
+ bool transform_ternary_conditional_nodes(const AST_NODE_T* node, void* data) {
258
+ analyze_ruby_context_T* context = (analyze_ruby_context_T*) data;
259
+
260
+ transform_ternary_blocks(node, context);
261
+
262
+ herb_visit_child_nodes(node, transform_ternary_conditional_nodes, data);
263
+
264
+ return false;
265
+ }