herb 0.9.4-aarch64-linux-gnu → 0.9.6-aarch64-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.
- checksums.yaml +4 -4
- data/config.yml +57 -21
- data/ext/herb/extension.c +8 -0
- data/ext/herb/extension_helpers.c +1 -0
- data/ext/herb/nodes.c +93 -55
- data/lib/herb/3.0/herb.so +0 -0
- data/lib/herb/3.1/herb.so +0 -0
- data/lib/herb/3.2/herb.so +0 -0
- data/lib/herb/3.3/herb.so +0 -0
- data/lib/herb/3.4/herb.so +0 -0
- data/lib/herb/4.0/herb.so +0 -0
- data/lib/herb/action_view/helper_registry.rb +8107 -0
- data/lib/herb/ast/nodes.rb +212 -78
- data/lib/herb/engine/compiler.rb +4 -6
- data/lib/herb/parser_options.rb +7 -2
- data/lib/herb/project.rb +2 -5
- data/lib/herb/version.rb +1 -1
- data/lib/herb/visitor.rb +8 -2
- data/sig/herb/action_view/helper_registry.rbs +1144 -0
- data/sig/herb/ast/nodes.rbs +85 -34
- data/sig/herb/parser_options.rbs +6 -2
- data/sig/herb/visitor.rbs +5 -2
- data/sig/serialized_ast_nodes.rbs +20 -9
- data/src/analyze/action_view/generated_handlers.c +355 -0
- data/src/analyze/action_view/generated_handlers.h +16 -0
- data/src/analyze/action_view/helper_registry.c +7244 -0
- data/src/analyze/action_view/image_tag.c +4 -31
- data/src/analyze/action_view/javascript_include_tag.c +1 -42
- data/src/analyze/action_view/javascript_tag.c +26 -40
- data/src/analyze/action_view/registry.c +2 -2
- data/src/analyze/action_view/tag_helper_node_builders.c +23 -2
- data/src/analyze/action_view/tag_helpers.c +61 -134
- data/src/analyze/action_view/turbo_frame_tag.c +1 -36
- data/src/analyze/analyze.c +28 -0
- data/src/analyze/analyze_helpers.c +406 -0
- data/src/analyze/builders.c +1 -0
- data/src/analyze/missing_end.c +16 -0
- data/src/analyze/parse_errors.c +3 -2
- data/src/analyze/postfix_conditionals.c +326 -0
- data/src/analyze/render_nodes.c +231 -35
- data/src/analyze/strict_locals.c +22 -338
- data/src/analyze/ternary_conditionals.c +265 -0
- data/src/analyze/transform.c +23 -2
- data/src/ast/ast_nodes.c +114 -57
- data/src/ast/ast_pretty_print.c +109 -25
- data/src/include/analyze/action_view/helper_registry.h +325 -0
- data/src/include/analyze/action_view/tag_helper_handler.h +3 -0
- data/src/include/analyze/action_view/tag_helper_node_builders.h +7 -0
- data/src/include/analyze/action_view/tag_helpers.h +0 -1
- data/src/include/analyze/helpers.h +18 -0
- data/src/include/analyze/postfix_conditionals.h +9 -0
- data/src/include/analyze/ternary_conditionals.h +15 -0
- data/src/include/ast/ast_nodes.h +27 -13
- data/src/include/parser/parser.h +1 -0
- data/src/include/version.h +1 -1
- data/src/parser/match_tags.c +37 -6
- data/src/parser.c +9 -0
- data/src/visitor.c +50 -7
- data/templates/java/org/herb/ast/HelperRegistry.java.erb +258 -0
- data/templates/javascript/packages/core/src/action-view-helpers.ts.erb +171 -0
- data/templates/javascript/packages/core/src/nodes.ts.erb +5 -1
- data/templates/lib/herb/action_view/helper_registry.rb.erb +288 -0
- data/templates/rust/src/action_view_helpers.rs.erb +154 -0
- data/templates/src/analyze/action_view/generated_handlers.c.erb +230 -0
- data/templates/src/analyze/action_view/generated_handlers.h.erb +12 -0
- data/templates/src/analyze/action_view/helper_registry.c.erb +114 -0
- data/templates/src/include/analyze/action_view/helper_registry.h.erb +82 -0
- data/templates/template.rb +338 -1
- metadata +19 -3
- data/src/analyze/action_view/content_tag.c +0 -78
- 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
|
+
}
|
data/src/analyze/render_nodes.c
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
#include "../include/analyze/action_view/tag_helper_node_builders.h"
|
|
3
3
|
#include "../include/analyze/action_view/tag_helpers.h"
|
|
4
4
|
#include "../include/analyze/analyze.h"
|
|
5
|
+
#include "../include/analyze/helpers.h"
|
|
5
6
|
#include "../include/ast/ast_nodes.h"
|
|
6
7
|
#include "../include/errors.h"
|
|
7
8
|
#include "../include/lib/hb_allocator.h"
|
|
@@ -14,6 +15,16 @@
|
|
|
14
15
|
#include <stdbool.h>
|
|
15
16
|
#include <string.h>
|
|
16
17
|
|
|
18
|
+
static token_T* create_token_from_prism_node(
|
|
19
|
+
pm_node_t* node,
|
|
20
|
+
const char* value,
|
|
21
|
+
token_type_T type,
|
|
22
|
+
const char* source,
|
|
23
|
+
size_t erb_content_offset,
|
|
24
|
+
const uint8_t* erb_content_source,
|
|
25
|
+
hb_allocator_T* allocator
|
|
26
|
+
);
|
|
27
|
+
|
|
17
28
|
typedef struct {
|
|
18
29
|
char* value;
|
|
19
30
|
pm_node_t* value_node;
|
|
@@ -62,6 +73,27 @@ static pm_call_node_t* find_render_call(pm_node_t* node, pm_parser_t* parser) {
|
|
|
62
73
|
return NULL;
|
|
63
74
|
}
|
|
64
75
|
|
|
76
|
+
static pm_block_node_t* find_block_on_render_call(pm_call_node_t* render_call) {
|
|
77
|
+
if (!render_call) { return NULL; }
|
|
78
|
+
|
|
79
|
+
if (render_call->block && render_call->block->type == PM_BLOCK_NODE) { return (pm_block_node_t*) render_call->block; }
|
|
80
|
+
|
|
81
|
+
pm_arguments_node_t* arguments = render_call->arguments;
|
|
82
|
+
if (!arguments || arguments->arguments.size == 0) { return NULL; }
|
|
83
|
+
|
|
84
|
+
pm_node_t* first_argument = arguments->arguments.nodes[0];
|
|
85
|
+
|
|
86
|
+
if (first_argument->type == PM_CALL_NODE) {
|
|
87
|
+
pm_call_node_t* argument_call = (pm_call_node_t*) first_argument;
|
|
88
|
+
|
|
89
|
+
if (argument_call->block && argument_call->block->type == PM_BLOCK_NODE) {
|
|
90
|
+
return (pm_block_node_t*) argument_call->block;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return NULL;
|
|
95
|
+
}
|
|
96
|
+
|
|
65
97
|
static char* extract_string_value(pm_node_t* node, hb_allocator_T* allocator) {
|
|
66
98
|
if (!node) { return NULL; }
|
|
67
99
|
|
|
@@ -274,6 +306,15 @@ static token_T* extract_keyword_token(
|
|
|
274
306
|
return token;
|
|
275
307
|
}
|
|
276
308
|
|
|
309
|
+
typedef struct {
|
|
310
|
+
hb_array_T* block_body;
|
|
311
|
+
hb_array_T* block_arguments;
|
|
312
|
+
AST_ERB_RESCUE_NODE_T* rescue_clause;
|
|
313
|
+
AST_ERB_ELSE_NODE_T* else_clause;
|
|
314
|
+
AST_ERB_ENSURE_NODE_T* ensure_clause;
|
|
315
|
+
AST_ERB_END_NODE_T* end_node;
|
|
316
|
+
} render_block_fields_T;
|
|
317
|
+
|
|
277
318
|
static AST_ERB_RENDER_NODE_T* create_render_node_from_call(
|
|
278
319
|
AST_ERB_CONTENT_NODE_T* erb_node,
|
|
279
320
|
pm_call_node_t* call_node,
|
|
@@ -281,9 +322,9 @@ static AST_ERB_RENDER_NODE_T* create_render_node_from_call(
|
|
|
281
322
|
const char* source,
|
|
282
323
|
size_t erb_content_offset,
|
|
283
324
|
const uint8_t* erb_content_source,
|
|
325
|
+
render_block_fields_T* block_fields,
|
|
284
326
|
hb_allocator_T* allocator
|
|
285
327
|
) {
|
|
286
|
-
(void) parser;
|
|
287
328
|
|
|
288
329
|
pm_arguments_node_t* arguments = call_node->arguments;
|
|
289
330
|
pm_keyword_hash_node_t* keyword_hash = arguments ? find_keyword_hash(arguments) : NULL;
|
|
@@ -626,12 +667,68 @@ static AST_ERB_RENDER_NODE_T* create_render_node_from_call(
|
|
|
626
667
|
|
|
627
668
|
herb_prism_node_T prism_node = erb_node->prism_node;
|
|
628
669
|
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
670
|
+
hb_array_T* render_block_body = block_fields ? block_fields->block_body : NULL;
|
|
671
|
+
hb_array_T* render_block_arguments = block_fields ? block_fields->block_arguments : NULL;
|
|
672
|
+
|
|
673
|
+
AST_ERB_RESCUE_NODE_T* render_rescue_clause = block_fields ? block_fields->rescue_clause : NULL;
|
|
674
|
+
AST_ERB_ELSE_NODE_T* render_else_clause = block_fields ? block_fields->else_clause : NULL;
|
|
675
|
+
AST_ERB_ENSURE_NODE_T* render_ensure_clause = block_fields ? block_fields->ensure_clause : NULL;
|
|
676
|
+
AST_ERB_END_NODE_T* render_end_node = block_fields ? block_fields->end_node : NULL;
|
|
677
|
+
|
|
678
|
+
if (!render_block_body) { render_block_body = hb_array_init(0, allocator); }
|
|
679
|
+
if (!render_block_arguments) { render_block_arguments = hb_array_init(0, allocator); }
|
|
680
|
+
|
|
681
|
+
if (!block_fields) {
|
|
682
|
+
pm_block_node_t* inline_block = find_block_on_render_call(call_node);
|
|
683
|
+
|
|
684
|
+
if (inline_block) {
|
|
685
|
+
const uint8_t* content_source = erb_content_source ? erb_content_source : (const uint8_t*) parser->start;
|
|
686
|
+
|
|
687
|
+
if (inline_block->parameters && inline_block->parameters->type == PM_BLOCK_PARAMETERS_NODE) {
|
|
688
|
+
pm_block_parameters_node_t* block_parameters = (pm_block_parameters_node_t*) inline_block->parameters;
|
|
689
|
+
|
|
690
|
+
render_block_arguments = extract_parameters_from_prism(
|
|
691
|
+
block_parameters->parameters,
|
|
692
|
+
parser,
|
|
693
|
+
source,
|
|
694
|
+
erb_content_offset,
|
|
695
|
+
content_source,
|
|
696
|
+
allocator
|
|
697
|
+
);
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
if (inline_block->body) {
|
|
701
|
+
size_t body_length = (size_t) (inline_block->body->location.end - inline_block->body->location.start);
|
|
702
|
+
char* body_source =
|
|
703
|
+
hb_allocator_strndup(allocator, (const char*) inline_block->body->location.start, body_length);
|
|
704
|
+
|
|
705
|
+
position_T body_start = { .line = 1, .column = 1 };
|
|
706
|
+
position_T body_end = { .line = 1, .column = 1 };
|
|
707
|
+
|
|
708
|
+
if (source && content_source) {
|
|
709
|
+
size_t start_offset = (size_t) (inline_block->body->location.start - content_source);
|
|
710
|
+
size_t end_offset = (size_t) (inline_block->body->location.end - content_source);
|
|
711
|
+
body_start = byte_offset_to_position(source, erb_content_offset + start_offset);
|
|
712
|
+
body_end = byte_offset_to_position(source, erb_content_offset + end_offset);
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
AST_RUBY_LITERAL_NODE_T* body_node = ast_ruby_literal_node_init(
|
|
716
|
+
hb_string_from_c_string(body_source),
|
|
717
|
+
body_start,
|
|
718
|
+
body_end,
|
|
719
|
+
hb_array_init(0, allocator),
|
|
720
|
+
allocator
|
|
721
|
+
);
|
|
722
|
+
|
|
723
|
+
render_block_body = hb_array_init(1, allocator);
|
|
724
|
+
hb_array_append(render_block_body, body_node);
|
|
725
|
+
|
|
726
|
+
hb_allocator_dealloc(allocator, body_source);
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
AST_RUBY_RENDER_KEYWORDS_NODE_T* keywords_node = ast_ruby_render_keywords_node_init(
|
|
635
732
|
partial,
|
|
636
733
|
template_path,
|
|
637
734
|
layout,
|
|
@@ -652,6 +749,25 @@ static AST_ERB_RENDER_NODE_T* create_render_node_from_call(
|
|
|
652
749
|
locals,
|
|
653
750
|
erb_node->base.location.start,
|
|
654
751
|
erb_node->base.location.end,
|
|
752
|
+
hb_array_init(0, allocator),
|
|
753
|
+
allocator
|
|
754
|
+
);
|
|
755
|
+
|
|
756
|
+
AST_ERB_RENDER_NODE_T* render_node = ast_erb_render_node_init(
|
|
757
|
+
erb_node->tag_opening,
|
|
758
|
+
erb_node->content,
|
|
759
|
+
erb_node->tag_closing,
|
|
760
|
+
erb_node->analyzed_ruby,
|
|
761
|
+
prism_node,
|
|
762
|
+
keywords_node,
|
|
763
|
+
render_block_body,
|
|
764
|
+
render_block_arguments,
|
|
765
|
+
render_rescue_clause,
|
|
766
|
+
render_else_clause,
|
|
767
|
+
render_ensure_clause,
|
|
768
|
+
render_end_node,
|
|
769
|
+
erb_node->base.location.start,
|
|
770
|
+
erb_node->base.location.end,
|
|
655
771
|
errors,
|
|
656
772
|
allocator
|
|
657
773
|
);
|
|
@@ -682,43 +798,123 @@ static size_t calculate_byte_offset_from_pos(const char* source, position_T posi
|
|
|
682
798
|
return offset;
|
|
683
799
|
}
|
|
684
800
|
|
|
685
|
-
static
|
|
686
|
-
if (!
|
|
801
|
+
static bool is_erb_comment_tag(token_T* tag_opening) {
|
|
802
|
+
if (!tag_opening || hb_string_is_empty(tag_opening->value)) { return false; }
|
|
687
803
|
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
804
|
+
const char* opening_string = tag_opening->value.data;
|
|
805
|
+
return opening_string && strstr(opening_string, "#") != NULL;
|
|
806
|
+
}
|
|
691
807
|
|
|
692
|
-
|
|
808
|
+
static AST_ERB_RENDER_NODE_T* try_transform_content_node(
|
|
809
|
+
AST_ERB_CONTENT_NODE_T* erb_node,
|
|
810
|
+
analyze_ruby_context_T* context
|
|
811
|
+
) {
|
|
812
|
+
if (!erb_node->analyzed_ruby || !erb_node->analyzed_ruby->valid || !erb_node->analyzed_ruby->parsed) { return NULL; }
|
|
813
|
+
if (is_erb_comment_tag(erb_node->tag_opening)) { return NULL; }
|
|
693
814
|
|
|
694
|
-
|
|
815
|
+
pm_call_node_t* render_call = find_render_call(erb_node->analyzed_ruby->root, &erb_node->analyzed_ruby->parser);
|
|
816
|
+
if (!render_call) { return NULL; }
|
|
695
817
|
|
|
696
|
-
|
|
818
|
+
size_t erb_content_offset = 0;
|
|
697
819
|
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
}
|
|
820
|
+
if (context->source && erb_node->content) {
|
|
821
|
+
erb_content_offset = calculate_byte_offset_from_pos(context->source, erb_node->content->location.start);
|
|
822
|
+
}
|
|
702
823
|
|
|
703
|
-
|
|
704
|
-
if (!render_call) { continue; }
|
|
824
|
+
const uint8_t* erb_content_source = (const uint8_t*) erb_node->analyzed_ruby->parser.start;
|
|
705
825
|
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
826
|
+
return create_render_node_from_call(
|
|
827
|
+
erb_node,
|
|
828
|
+
render_call,
|
|
829
|
+
&erb_node->analyzed_ruby->parser,
|
|
830
|
+
context->source,
|
|
831
|
+
erb_content_offset,
|
|
832
|
+
erb_content_source,
|
|
833
|
+
NULL,
|
|
834
|
+
context->allocator
|
|
835
|
+
);
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
static AST_ERB_RENDER_NODE_T* try_transform_block_node(
|
|
839
|
+
AST_ERB_BLOCK_NODE_T* block_node,
|
|
840
|
+
analyze_ruby_context_T* context
|
|
841
|
+
) {
|
|
842
|
+
if (!block_node->content || hb_string_is_empty(block_node->content->value)) { return NULL; }
|
|
710
843
|
|
|
711
|
-
|
|
844
|
+
const char* ruby_source = block_node->content->value.data;
|
|
845
|
+
size_t ruby_length = block_node->content->value.length;
|
|
712
846
|
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
);
|
|
847
|
+
pm_parser_t parser;
|
|
848
|
+
pm_parser_init(&parser, (const uint8_t*) ruby_source, ruby_length, NULL);
|
|
849
|
+
pm_node_t* root = pm_parse(&parser);
|
|
850
|
+
|
|
851
|
+
pm_call_node_t* render_call = find_render_call(root, &parser);
|
|
852
|
+
|
|
853
|
+
if (!render_call) {
|
|
854
|
+
pm_node_destroy(&parser, root);
|
|
855
|
+
pm_parser_free(&parser);
|
|
856
|
+
return NULL;
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
AST_ERB_CONTENT_NODE_T content_view = {
|
|
860
|
+
.base = block_node->base,
|
|
861
|
+
.tag_opening = block_node->tag_opening,
|
|
862
|
+
.content = block_node->content,
|
|
863
|
+
.tag_closing = block_node->tag_closing,
|
|
864
|
+
.analyzed_ruby = NULL,
|
|
865
|
+
.parsed = true,
|
|
866
|
+
.valid = true,
|
|
867
|
+
.prism_node = block_node->prism_node,
|
|
868
|
+
};
|
|
869
|
+
|
|
870
|
+
size_t erb_content_offset = 0;
|
|
871
|
+
|
|
872
|
+
if (context->source && block_node->content) {
|
|
873
|
+
erb_content_offset = calculate_byte_offset_from_pos(context->source, block_node->content->location.start);
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
const uint8_t* erb_content_source = (const uint8_t*) parser.start;
|
|
877
|
+
|
|
878
|
+
render_block_fields_T block_fields = {
|
|
879
|
+
.block_body = block_node->body,
|
|
880
|
+
.block_arguments = block_node->block_arguments,
|
|
881
|
+
.rescue_clause = block_node->rescue_clause,
|
|
882
|
+
.else_clause = block_node->else_clause,
|
|
883
|
+
.ensure_clause = block_node->ensure_clause,
|
|
884
|
+
.end_node = block_node->end_node,
|
|
885
|
+
};
|
|
886
|
+
|
|
887
|
+
AST_ERB_RENDER_NODE_T* render_node = create_render_node_from_call(
|
|
888
|
+
&content_view,
|
|
889
|
+
render_call,
|
|
890
|
+
&parser,
|
|
891
|
+
context->source,
|
|
892
|
+
erb_content_offset,
|
|
893
|
+
erb_content_source,
|
|
894
|
+
&block_fields,
|
|
895
|
+
context->allocator
|
|
896
|
+
);
|
|
897
|
+
|
|
898
|
+
pm_node_destroy(&parser, root);
|
|
899
|
+
pm_parser_free(&parser);
|
|
900
|
+
|
|
901
|
+
return render_node;
|
|
902
|
+
}
|
|
903
|
+
|
|
904
|
+
static void transform_render_nodes_in_array(hb_array_T* array, analyze_ruby_context_T* context) {
|
|
905
|
+
if (!array) { return; }
|
|
906
|
+
|
|
907
|
+
for (size_t index = 0; index < hb_array_size(array); index++) {
|
|
908
|
+
AST_NODE_T* child = hb_array_get(array, index);
|
|
909
|
+
if (!child) { continue; }
|
|
910
|
+
|
|
911
|
+
AST_ERB_RENDER_NODE_T* render_node = NULL;
|
|
912
|
+
|
|
913
|
+
if (child->type == AST_ERB_CONTENT_NODE) {
|
|
914
|
+
render_node = try_transform_content_node((AST_ERB_CONTENT_NODE_T*) child, context);
|
|
915
|
+
} else if (child->type == AST_ERB_BLOCK_NODE) {
|
|
916
|
+
render_node = try_transform_block_node((AST_ERB_BLOCK_NODE_T*) child, context);
|
|
917
|
+
}
|
|
722
918
|
|
|
723
919
|
if (render_node) { hb_array_set(array, index, render_node); }
|
|
724
920
|
}
|