herb 0.1.0-x86_64-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 (110) hide show
  1. checksums.yaml +7 -0
  2. data/License.txt +21 -0
  3. data/Makefile +121 -0
  4. data/README.md +166 -0
  5. data/Rakefile +184 -0
  6. data/exe/herb +5 -0
  7. data/ext/herb/error_helpers.c +302 -0
  8. data/ext/herb/error_helpers.h +15 -0
  9. data/ext/herb/extconf.rb +75 -0
  10. data/ext/herb/extension.c +110 -0
  11. data/ext/herb/extension.h +6 -0
  12. data/ext/herb/extension_helpers.c +117 -0
  13. data/ext/herb/extension_helpers.h +24 -0
  14. data/ext/herb/nodes.c +936 -0
  15. data/ext/herb/nodes.h +12 -0
  16. data/herb.gemspec +49 -0
  17. data/lib/herb/3.0/herb.so +0 -0
  18. data/lib/herb/3.1/herb.so +0 -0
  19. data/lib/herb/3.2/herb.so +0 -0
  20. data/lib/herb/3.3/herb.so +0 -0
  21. data/lib/herb/3.4/herb.so +0 -0
  22. data/lib/herb/ast/node.rb +61 -0
  23. data/lib/herb/ast/nodes.rb +1542 -0
  24. data/lib/herb/ast.rb +6 -0
  25. data/lib/herb/cli.rb +164 -0
  26. data/lib/herb/errors.rb +352 -0
  27. data/lib/herb/lex_result.rb +20 -0
  28. data/lib/herb/libherb/array.rb +48 -0
  29. data/lib/herb/libherb/ast_node.rb +47 -0
  30. data/lib/herb/libherb/buffer.rb +53 -0
  31. data/lib/herb/libherb/extract_result.rb +17 -0
  32. data/lib/herb/libherb/lex_result.rb +29 -0
  33. data/lib/herb/libherb/libherb.rb +49 -0
  34. data/lib/herb/libherb/parse_result.rb +17 -0
  35. data/lib/herb/libherb/token.rb +43 -0
  36. data/lib/herb/libherb.rb +32 -0
  37. data/lib/herb/location.rb +42 -0
  38. data/lib/herb/parse_result.rb +26 -0
  39. data/lib/herb/position.rb +36 -0
  40. data/lib/herb/project.rb +361 -0
  41. data/lib/herb/range.rb +40 -0
  42. data/lib/herb/result.rb +21 -0
  43. data/lib/herb/token.rb +43 -0
  44. data/lib/herb/token_list.rb +11 -0
  45. data/lib/herb/version.rb +5 -0
  46. data/lib/herb.rb +32 -0
  47. data/src/analyze.c +989 -0
  48. data/src/analyze_helpers.c +241 -0
  49. data/src/analyzed_ruby.c +35 -0
  50. data/src/array.c +137 -0
  51. data/src/ast_node.c +81 -0
  52. data/src/ast_nodes.c +866 -0
  53. data/src/ast_pretty_print.c +588 -0
  54. data/src/buffer.c +199 -0
  55. data/src/errors.c +740 -0
  56. data/src/extract.c +110 -0
  57. data/src/herb.c +103 -0
  58. data/src/html_util.c +143 -0
  59. data/src/include/analyze.h +36 -0
  60. data/src/include/analyze_helpers.h +43 -0
  61. data/src/include/analyzed_ruby.h +33 -0
  62. data/src/include/array.h +33 -0
  63. data/src/include/ast_node.h +35 -0
  64. data/src/include/ast_nodes.h +303 -0
  65. data/src/include/ast_pretty_print.h +17 -0
  66. data/src/include/buffer.h +36 -0
  67. data/src/include/errors.h +125 -0
  68. data/src/include/extract.h +20 -0
  69. data/src/include/herb.h +32 -0
  70. data/src/include/html_util.h +13 -0
  71. data/src/include/io.h +9 -0
  72. data/src/include/json.h +28 -0
  73. data/src/include/lexer.h +13 -0
  74. data/src/include/lexer_peek_helpers.h +23 -0
  75. data/src/include/lexer_struct.h +32 -0
  76. data/src/include/location.h +25 -0
  77. data/src/include/macros.h +10 -0
  78. data/src/include/memory.h +12 -0
  79. data/src/include/parser.h +22 -0
  80. data/src/include/parser_helpers.h +33 -0
  81. data/src/include/position.h +22 -0
  82. data/src/include/pretty_print.h +53 -0
  83. data/src/include/prism_helpers.h +18 -0
  84. data/src/include/range.h +23 -0
  85. data/src/include/ruby_parser.h +6 -0
  86. data/src/include/token.h +25 -0
  87. data/src/include/token_matchers.h +21 -0
  88. data/src/include/token_struct.h +51 -0
  89. data/src/include/util.h +25 -0
  90. data/src/include/version.h +6 -0
  91. data/src/include/visitor.h +11 -0
  92. data/src/io.c +30 -0
  93. data/src/json.c +205 -0
  94. data/src/lexer.c +284 -0
  95. data/src/lexer_peek_helpers.c +59 -0
  96. data/src/location.c +41 -0
  97. data/src/main.c +162 -0
  98. data/src/memory.c +53 -0
  99. data/src/parser.c +704 -0
  100. data/src/parser_helpers.c +161 -0
  101. data/src/position.c +33 -0
  102. data/src/pretty_print.c +242 -0
  103. data/src/prism_helpers.c +50 -0
  104. data/src/range.c +38 -0
  105. data/src/ruby_parser.c +47 -0
  106. data/src/token.c +194 -0
  107. data/src/token_matchers.c +32 -0
  108. data/src/util.c +128 -0
  109. data/src/visitor.c +321 -0
  110. metadata +159 -0
data/src/analyze.c ADDED
@@ -0,0 +1,989 @@
1
+ #include "include/analyze.h"
2
+ #include "include/analyze_helpers.h"
3
+ #include "include/analyzed_ruby.h"
4
+ #include "include/array.h"
5
+ #include "include/ast_nodes.h"
6
+ #include "include/errors.h"
7
+ #include "include/extract.h"
8
+ #include "include/location.h"
9
+ #include "include/position.h"
10
+ #include "include/pretty_print.h"
11
+ #include "include/prism_helpers.h"
12
+ #include "include/token_struct.h"
13
+ #include "include/util.h"
14
+ #include "include/visitor.h"
15
+
16
+ #include <prism.h>
17
+ #include <stdbool.h>
18
+ #include <stdio.h>
19
+ #include <stdlib.h>
20
+ #include <string.h>
21
+
22
+ static analyzed_ruby_T* herb_analyze_ruby(char* source) {
23
+ analyzed_ruby_T* analyzed = init_analyzed_ruby(source);
24
+
25
+ pm_visit_node(analyzed->root, search_if_nodes, analyzed);
26
+ pm_visit_node(analyzed->root, search_block_nodes, analyzed);
27
+ pm_visit_node(analyzed->root, search_case_nodes, analyzed);
28
+ pm_visit_node(analyzed->root, search_while_nodes, analyzed);
29
+ pm_visit_node(analyzed->root, search_for_nodes, analyzed);
30
+ pm_visit_node(analyzed->root, search_until_nodes, analyzed);
31
+ pm_visit_node(analyzed->root, search_begin_nodes, analyzed);
32
+ pm_visit_node(analyzed->root, search_unless_nodes, analyzed);
33
+
34
+ search_elsif_nodes(analyzed);
35
+ search_else_nodes(analyzed);
36
+ search_end_nodes(analyzed);
37
+ search_when_nodes(analyzed);
38
+ search_rescue_nodes(analyzed);
39
+ search_ensure_nodes(analyzed);
40
+ search_block_closing_nodes(analyzed);
41
+
42
+ return analyzed;
43
+ }
44
+
45
+ static bool analyze_erb_content(const AST_NODE_T* node, void* data) {
46
+ if (node->type == AST_ERB_CONTENT_NODE) {
47
+ AST_ERB_CONTENT_NODE_T* erb_content_node = (AST_ERB_CONTENT_NODE_T*) node;
48
+
49
+ analyzed_ruby_T* analyzed = herb_analyze_ruby(erb_content_node->content->value);
50
+
51
+ if (false) { pretty_print_analyed_ruby(analyzed, erb_content_node->content->value); }
52
+
53
+ erb_content_node->parsed = true;
54
+ erb_content_node->valid = analyzed->valid;
55
+ erb_content_node->analyzed_ruby = analyzed;
56
+ }
57
+
58
+ herb_visit_child_nodes(node, analyze_erb_content, data);
59
+
60
+ return false;
61
+ }
62
+
63
+ static size_t process_block_children(
64
+ AST_NODE_T* node, array_T* array, size_t index, array_T* children_array, analyze_ruby_context_T* context,
65
+ control_type_t parent_type
66
+ );
67
+
68
+ static size_t process_subsequent_block(
69
+ AST_NODE_T* node, array_T* array, size_t index, AST_NODE_T** subsequent_out, analyze_ruby_context_T* context,
70
+ control_type_t parent_type
71
+ );
72
+
73
+ static control_type_t detect_control_type(AST_ERB_CONTENT_NODE_T* erb_node) {
74
+ if (!erb_node || erb_node->base.type != AST_ERB_CONTENT_NODE) { return CONTROL_TYPE_UNKNOWN; }
75
+
76
+ analyzed_ruby_T* ruby = erb_node->analyzed_ruby;
77
+
78
+ if (!ruby) { return CONTROL_TYPE_UNKNOWN; }
79
+
80
+ if (ruby->valid) { return CONTROL_TYPE_UNKNOWN; }
81
+
82
+ if (has_if_node(ruby)) { return CONTROL_TYPE_IF; }
83
+ if (has_elsif_node(ruby)) { return CONTROL_TYPE_ELSIF; }
84
+ if (has_else_node(ruby)) { return CONTROL_TYPE_ELSE; }
85
+ if (has_end(ruby)) { return CONTROL_TYPE_END; }
86
+ if (has_case_node(ruby)) { return CONTROL_TYPE_CASE; }
87
+ if (has_when_node(ruby)) { return CONTROL_TYPE_WHEN; }
88
+ if (has_begin_node(ruby)) { return CONTROL_TYPE_BEGIN; }
89
+ if (has_rescue_node(ruby)) { return CONTROL_TYPE_RESCUE; }
90
+ if (has_ensure_node(ruby)) { return CONTROL_TYPE_ENSURE; }
91
+ if (has_unless_node(ruby)) { return CONTROL_TYPE_UNLESS; }
92
+ if (has_while_node(ruby)) { return CONTROL_TYPE_WHILE; }
93
+ if (has_until_node(ruby)) { return CONTROL_TYPE_UNTIL; }
94
+ if (has_for_node(ruby)) { return CONTROL_TYPE_FOR; }
95
+ if (has_block_node(ruby)) { return CONTROL_TYPE_BLOCK; }
96
+ if (has_block_closing(ruby)) { return CONTROL_TYPE_BLOCK_CLOSE; }
97
+
98
+ return CONTROL_TYPE_UNKNOWN;
99
+ }
100
+
101
+ static bool is_subsequent_type(control_type_t parent_type, control_type_t child_type) {
102
+ switch (parent_type) {
103
+ case CONTROL_TYPE_IF:
104
+ case CONTROL_TYPE_ELSIF: return child_type == CONTROL_TYPE_ELSIF || child_type == CONTROL_TYPE_ELSE;
105
+ case CONTROL_TYPE_CASE: return child_type == CONTROL_TYPE_WHEN || child_type == CONTROL_TYPE_ELSE;
106
+ case CONTROL_TYPE_BEGIN:
107
+ return child_type == CONTROL_TYPE_RESCUE || child_type == CONTROL_TYPE_ELSE || child_type == CONTROL_TYPE_ENSURE;
108
+ case CONTROL_TYPE_RESCUE: return child_type == CONTROL_TYPE_RESCUE;
109
+ case CONTROL_TYPE_UNLESS: return child_type == CONTROL_TYPE_ELSE;
110
+
111
+ default: return false;
112
+ }
113
+ }
114
+
115
+ static bool is_terminator_type(control_type_t parent_type, control_type_t child_type) {
116
+ if (child_type == CONTROL_TYPE_END) { return true; }
117
+
118
+ switch (parent_type) {
119
+ case CONTROL_TYPE_WHEN: return child_type == CONTROL_TYPE_WHEN || child_type == CONTROL_TYPE_ELSE;
120
+ case CONTROL_TYPE_BLOCK: return child_type == CONTROL_TYPE_BLOCK_CLOSE;
121
+
122
+ default: return is_subsequent_type(parent_type, child_type);
123
+ }
124
+ }
125
+
126
+ static AST_NODE_T* create_control_node(
127
+ AST_ERB_CONTENT_NODE_T* erb_node, array_T* children, AST_NODE_T* subsequent, AST_ERB_END_NODE_T* end_node,
128
+ control_type_t control_type
129
+ ) {
130
+ array_T* errors = array_init(8);
131
+ position_T* start_position = erb_node->tag_opening->location->start;
132
+ position_T* end_position = erb_node->tag_closing->location->end;
133
+
134
+ if (end_node) {
135
+ end_position = end_node->base.location->end;
136
+ } else if (children && array_size(children) > 0) {
137
+ AST_NODE_T* last_child = array_get(children, array_size(children) - 1);
138
+ end_position = last_child->location->end;
139
+ } else if (subsequent) {
140
+ end_position = subsequent->location->end;
141
+ }
142
+
143
+ token_T* tag_opening = erb_node->tag_opening;
144
+ token_T* content = erb_node->content;
145
+ token_T* tag_closing = erb_node->tag_closing;
146
+
147
+ switch (control_type) {
148
+ case CONTROL_TYPE_IF:
149
+ case CONTROL_TYPE_ELSIF:
150
+ return (AST_NODE_T*) ast_erb_if_node_init(
151
+ tag_opening,
152
+ content,
153
+ tag_closing,
154
+ children,
155
+ subsequent,
156
+ end_node,
157
+ start_position,
158
+ end_position,
159
+ errors
160
+ );
161
+
162
+ case CONTROL_TYPE_ELSE:
163
+ return (AST_NODE_T*)
164
+ ast_erb_else_node_init(tag_opening, content, tag_closing, children, start_position, end_position, errors);
165
+
166
+ case CONTROL_TYPE_CASE: {
167
+ AST_ERB_ELSE_NODE_T* else_node = NULL;
168
+ if (subsequent && subsequent->type == AST_ERB_ELSE_NODE) { else_node = (AST_ERB_ELSE_NODE_T*) subsequent; }
169
+
170
+ array_T* when_conditions = array_init(8);
171
+ array_T* non_when_children = array_init(8);
172
+
173
+ for (size_t i = 0; i < array_size(children); i++) {
174
+ AST_NODE_T* child = array_get(children, i);
175
+ if (child && child->type == AST_ERB_WHEN_NODE) {
176
+ array_append(when_conditions, child);
177
+ } else {
178
+ array_append(non_when_children, child);
179
+ }
180
+ }
181
+
182
+ return (AST_NODE_T*) ast_erb_case_node_init(
183
+ tag_opening,
184
+ content,
185
+ tag_closing,
186
+ non_when_children,
187
+ when_conditions,
188
+ else_node,
189
+ end_node,
190
+ start_position,
191
+ end_position,
192
+ errors
193
+ );
194
+ }
195
+
196
+ case CONTROL_TYPE_WHEN: {
197
+ return (AST_NODE_T*)
198
+ ast_erb_when_node_init(tag_opening, content, tag_closing, children, start_position, end_position, errors);
199
+ }
200
+
201
+ case CONTROL_TYPE_BEGIN: {
202
+ AST_ERB_RESCUE_NODE_T* rescue_clause = NULL;
203
+ AST_ERB_ELSE_NODE_T* else_clause = NULL;
204
+ AST_ERB_ENSURE_NODE_T* ensure_clause = NULL;
205
+
206
+ if (subsequent) {
207
+ if (subsequent->type == AST_ERB_RESCUE_NODE) {
208
+ rescue_clause = (AST_ERB_RESCUE_NODE_T*) subsequent;
209
+ } else if (subsequent->type == AST_ERB_ELSE_NODE) {
210
+ else_clause = (AST_ERB_ELSE_NODE_T*) subsequent;
211
+ } else if (subsequent->type == AST_ERB_ENSURE_NODE) {
212
+ ensure_clause = (AST_ERB_ENSURE_NODE_T*) subsequent;
213
+ }
214
+ }
215
+
216
+ return (AST_NODE_T*) ast_erb_begin_node_init(
217
+ tag_opening,
218
+ content,
219
+ tag_closing,
220
+ children,
221
+ rescue_clause,
222
+ else_clause,
223
+ ensure_clause,
224
+ end_node,
225
+ start_position,
226
+ end_position,
227
+ errors
228
+ );
229
+ }
230
+
231
+ case CONTROL_TYPE_RESCUE: {
232
+ AST_ERB_RESCUE_NODE_T* rescue_node = NULL;
233
+
234
+ if (rescue_node && subsequent->type == AST_ERB_RESCUE_NODE) { rescue_node = (AST_ERB_RESCUE_NODE_T*) subsequent; }
235
+
236
+ return (AST_NODE_T*) ast_erb_rescue_node_init(
237
+ tag_opening,
238
+ content,
239
+ tag_closing,
240
+ children,
241
+ rescue_node,
242
+ start_position,
243
+ end_position,
244
+ errors
245
+ );
246
+ }
247
+
248
+ case CONTROL_TYPE_ENSURE: {
249
+ return (AST_NODE_T*)
250
+ ast_erb_ensure_node_init(tag_opening, content, tag_closing, children, start_position, end_position, errors);
251
+ }
252
+
253
+ case CONTROL_TYPE_UNLESS: {
254
+ AST_ERB_ELSE_NODE_T* else_clause = NULL;
255
+
256
+ if (subsequent && subsequent->type == AST_ERB_ELSE_NODE) { else_clause = (AST_ERB_ELSE_NODE_T*) subsequent; }
257
+
258
+ return (AST_NODE_T*) ast_erb_unless_node_init(
259
+ tag_opening,
260
+ content,
261
+ tag_closing,
262
+ children,
263
+ else_clause,
264
+ end_node,
265
+ start_position,
266
+ end_position,
267
+ errors
268
+ );
269
+ }
270
+
271
+ case CONTROL_TYPE_WHILE: {
272
+ return (AST_NODE_T*) ast_erb_while_node_init(
273
+ tag_opening,
274
+ content,
275
+ tag_closing,
276
+ children,
277
+ end_node,
278
+ start_position,
279
+ end_position,
280
+ errors
281
+ );
282
+ }
283
+
284
+ case CONTROL_TYPE_UNTIL: {
285
+ return (AST_NODE_T*) ast_erb_until_node_init(
286
+ tag_opening,
287
+ content,
288
+ tag_closing,
289
+ children,
290
+ end_node,
291
+ start_position,
292
+ end_position,
293
+ errors
294
+ );
295
+ }
296
+
297
+ case CONTROL_TYPE_FOR: {
298
+ return (AST_NODE_T*) ast_erb_for_node_init(
299
+ tag_opening,
300
+ content,
301
+ tag_closing,
302
+ children,
303
+ end_node,
304
+ start_position,
305
+ end_position,
306
+ errors
307
+ );
308
+ }
309
+
310
+ case CONTROL_TYPE_BLOCK: {
311
+ return (AST_NODE_T*) ast_erb_block_node_init(
312
+ tag_opening,
313
+ content,
314
+ tag_closing,
315
+ children,
316
+ end_node,
317
+ start_position,
318
+ end_position,
319
+ errors
320
+ );
321
+ }
322
+
323
+ default: array_free(&errors); return NULL;
324
+ }
325
+ }
326
+
327
+ static size_t process_control_structure(
328
+ AST_NODE_T* node, array_T* array, size_t index, array_T* output_array, analyze_ruby_context_T* context,
329
+ control_type_t initial_type
330
+ ) {
331
+ AST_ERB_CONTENT_NODE_T* erb_node = (AST_ERB_CONTENT_NODE_T*) array_get(array, index);
332
+ array_T* children = array_init(8);
333
+
334
+ index++;
335
+
336
+ if (initial_type == CONTROL_TYPE_CASE) {
337
+ array_T* when_conditions = array_init(8);
338
+ array_T* non_when_children = array_init(8);
339
+
340
+ while (index < array_size(array)) {
341
+ AST_NODE_T* next_node = array_get(array, index);
342
+
343
+ if (!next_node) { break; }
344
+
345
+ if (next_node->type == AST_ERB_CONTENT_NODE) {
346
+ AST_ERB_CONTENT_NODE_T* erb_content = (AST_ERB_CONTENT_NODE_T*) next_node;
347
+ control_type_t next_type = detect_control_type(erb_content);
348
+
349
+ if (next_type == CONTROL_TYPE_WHEN) { break; }
350
+ }
351
+
352
+ array_append(non_when_children, next_node);
353
+ index++;
354
+ }
355
+
356
+ while (index < array_size(array)) {
357
+ AST_NODE_T* next_node = array_get(array, index);
358
+
359
+ if (!next_node) { break; }
360
+
361
+ if (next_node->type != AST_ERB_CONTENT_NODE) {
362
+ array_append(non_when_children, next_node);
363
+ index++;
364
+ continue;
365
+ }
366
+
367
+ AST_ERB_CONTENT_NODE_T* erb_content = (AST_ERB_CONTENT_NODE_T*) next_node;
368
+ control_type_t next_type = detect_control_type(erb_content);
369
+
370
+ if (next_type == CONTROL_TYPE_WHEN) {
371
+ array_T* when_statements = array_init(8);
372
+ index++;
373
+
374
+ while (index < array_size(array)) {
375
+ AST_NODE_T* child = array_get(array, index);
376
+
377
+ if (!child) { break; }
378
+
379
+ if (child->type == AST_ERB_CONTENT_NODE) {
380
+ AST_ERB_CONTENT_NODE_T* child_erb = (AST_ERB_CONTENT_NODE_T*) child;
381
+ control_type_t child_type = detect_control_type(child_erb);
382
+
383
+ if (child_type == CONTROL_TYPE_WHEN || child_type == CONTROL_TYPE_ELSE || child_type == CONTROL_TYPE_END) {
384
+ break;
385
+ }
386
+ }
387
+
388
+ array_append(when_statements, child);
389
+ index++;
390
+ }
391
+
392
+ AST_ERB_WHEN_NODE_T* when_node = ast_erb_when_node_init(
393
+ erb_content->tag_opening,
394
+ erb_content->content,
395
+ erb_content->tag_closing,
396
+ when_statements,
397
+ erb_content->tag_opening->location->start,
398
+ erb_content->tag_closing->location->end,
399
+ array_init(8)
400
+ );
401
+
402
+ array_append(when_conditions, (AST_NODE_T*) when_node);
403
+
404
+ continue;
405
+ } else if (next_type == CONTROL_TYPE_ELSE || next_type == CONTROL_TYPE_END) {
406
+ break;
407
+ } else {
408
+ array_append(non_when_children, next_node);
409
+ index++;
410
+ }
411
+ }
412
+
413
+ AST_ERB_ELSE_NODE_T* else_clause = NULL;
414
+
415
+ if (index < array_size(array)) {
416
+ AST_NODE_T* next_node = array_get(array, index);
417
+
418
+ if (next_node && next_node->type == AST_ERB_CONTENT_NODE) {
419
+ AST_ERB_CONTENT_NODE_T* next_erb = (AST_ERB_CONTENT_NODE_T*) next_node;
420
+ control_type_t next_type = detect_control_type(next_erb);
421
+
422
+ if (next_type == CONTROL_TYPE_ELSE) {
423
+ array_T* else_children = array_init(8);
424
+
425
+ index++;
426
+
427
+ while (index < array_size(array)) {
428
+ AST_NODE_T* child = array_get(array, index);
429
+
430
+ if (!child) { break; }
431
+
432
+ if (child->type == AST_ERB_CONTENT_NODE) {
433
+ AST_ERB_CONTENT_NODE_T* child_erb = (AST_ERB_CONTENT_NODE_T*) child;
434
+ control_type_t child_type = detect_control_type(child_erb);
435
+
436
+ if (child_type == CONTROL_TYPE_END) { break; }
437
+ }
438
+
439
+ array_append(else_children, child);
440
+ index++;
441
+ }
442
+
443
+ else_clause = ast_erb_else_node_init(
444
+ next_erb->tag_opening,
445
+ next_erb->content,
446
+ next_erb->tag_closing,
447
+ else_children,
448
+ next_erb->tag_opening->location->start,
449
+ next_erb->tag_closing->location->end,
450
+ array_init(8)
451
+ );
452
+ }
453
+ }
454
+ }
455
+
456
+ AST_ERB_END_NODE_T* end_node = NULL;
457
+
458
+ if (index < array_size(array)) {
459
+ AST_NODE_T* potential_end = array_get(array, index);
460
+
461
+ if (potential_end && potential_end->type == AST_ERB_CONTENT_NODE) {
462
+ AST_ERB_CONTENT_NODE_T* end_erb = (AST_ERB_CONTENT_NODE_T*) potential_end;
463
+
464
+ if (detect_control_type(end_erb) == CONTROL_TYPE_END) {
465
+ end_node = ast_erb_end_node_init(
466
+ end_erb->tag_opening,
467
+ end_erb->content,
468
+ end_erb->tag_closing,
469
+ end_erb->tag_opening->location->start,
470
+ end_erb->tag_closing->location->end,
471
+ end_erb->base.errors
472
+ );
473
+
474
+ index++;
475
+ }
476
+ }
477
+ }
478
+
479
+ position_T* start_position = erb_node->tag_opening->location->start;
480
+ position_T* end_position = erb_node->tag_closing->location->end;
481
+
482
+ if (end_node) {
483
+ end_position = end_node->base.location->end;
484
+ } else if (else_clause) {
485
+ end_position = else_clause->base.location->end;
486
+ } else if (array_size(when_conditions) > 0) {
487
+ AST_NODE_T* last_when = array_get(when_conditions, array_size(when_conditions) - 1);
488
+ end_position = last_when->location->end;
489
+ }
490
+
491
+ AST_ERB_CASE_NODE_T* case_node = ast_erb_case_node_init(
492
+ erb_node->tag_opening,
493
+ erb_node->content,
494
+ erb_node->tag_closing,
495
+ non_when_children,
496
+ when_conditions,
497
+ else_clause,
498
+ end_node,
499
+ start_position,
500
+ end_position,
501
+ array_init(8)
502
+ );
503
+
504
+ array_append(output_array, (AST_NODE_T*) case_node);
505
+ return index;
506
+ }
507
+
508
+ if (initial_type == CONTROL_TYPE_BEGIN) {
509
+ index = process_block_children(node, array, index, children, context, initial_type);
510
+
511
+ AST_ERB_RESCUE_NODE_T* rescue_clause = NULL;
512
+ AST_ERB_ELSE_NODE_T* else_clause = NULL;
513
+ AST_ERB_ENSURE_NODE_T* ensure_clause = NULL;
514
+
515
+ if (index < array_size(array)) {
516
+ AST_NODE_T* next_node = array_get(array, index);
517
+
518
+ if (next_node && next_node->type == AST_ERB_CONTENT_NODE) {
519
+ AST_ERB_CONTENT_NODE_T* next_erb = (AST_ERB_CONTENT_NODE_T*) next_node;
520
+ control_type_t next_type = detect_control_type(next_erb);
521
+
522
+ if (next_type == CONTROL_TYPE_RESCUE) {
523
+ AST_NODE_T* rescue_node = NULL;
524
+ index = process_subsequent_block(node, array, index, &rescue_node, context, initial_type);
525
+ rescue_clause = (AST_ERB_RESCUE_NODE_T*) rescue_node;
526
+ }
527
+ }
528
+ }
529
+
530
+ if (index < array_size(array)) {
531
+ AST_NODE_T* next_node = array_get(array, index);
532
+
533
+ if (next_node && next_node->type == AST_ERB_CONTENT_NODE) {
534
+ AST_ERB_CONTENT_NODE_T* next_erb = (AST_ERB_CONTENT_NODE_T*) next_node;
535
+ control_type_t next_type = detect_control_type(next_erb);
536
+
537
+ if (next_type == CONTROL_TYPE_ELSE) {
538
+ array_T* else_children = array_init(8);
539
+
540
+ index++;
541
+
542
+ while (index < array_size(array)) {
543
+ AST_NODE_T* child = array_get(array, index);
544
+
545
+ if (!child) { break; }
546
+
547
+ if (child->type == AST_ERB_CONTENT_NODE) {
548
+ AST_ERB_CONTENT_NODE_T* child_erb = (AST_ERB_CONTENT_NODE_T*) child;
549
+ control_type_t child_type = detect_control_type(child_erb);
550
+
551
+ if (child_type == CONTROL_TYPE_ENSURE || child_type == CONTROL_TYPE_END) { break; }
552
+ }
553
+
554
+ array_append(else_children, child);
555
+ index++;
556
+ }
557
+
558
+ else_clause = ast_erb_else_node_init(
559
+ next_erb->tag_opening,
560
+ next_erb->content,
561
+ next_erb->tag_closing,
562
+ else_children,
563
+ next_erb->tag_opening->location->start,
564
+ next_erb->tag_closing->location->end,
565
+ array_init(8)
566
+ );
567
+ }
568
+ }
569
+ }
570
+
571
+ if (index < array_size(array)) {
572
+ AST_NODE_T* next_node = array_get(array, index);
573
+
574
+ if (next_node && next_node->type == AST_ERB_CONTENT_NODE) {
575
+ AST_ERB_CONTENT_NODE_T* next_erb = (AST_ERB_CONTENT_NODE_T*) next_node;
576
+ control_type_t next_type = detect_control_type(next_erb);
577
+
578
+ if (next_type == CONTROL_TYPE_ENSURE) {
579
+ array_T* ensure_children = array_init(8);
580
+
581
+ index++;
582
+
583
+ while (index < array_size(array)) {
584
+ AST_NODE_T* child = array_get(array, index);
585
+
586
+ if (!child) { break; }
587
+
588
+ if (child->type == AST_ERB_CONTENT_NODE) {
589
+ AST_ERB_CONTENT_NODE_T* child_erb = (AST_ERB_CONTENT_NODE_T*) child;
590
+ control_type_t child_type = detect_control_type(child_erb);
591
+
592
+ if (child_type == CONTROL_TYPE_END) { break; }
593
+ }
594
+
595
+ array_append(ensure_children, child);
596
+ index++;
597
+ }
598
+
599
+ ensure_clause = ast_erb_ensure_node_init(
600
+ next_erb->tag_opening,
601
+ next_erb->content,
602
+ next_erb->tag_closing,
603
+ ensure_children,
604
+ next_erb->tag_opening->location->start,
605
+ next_erb->tag_closing->location->end,
606
+ array_init(8)
607
+ );
608
+ }
609
+ }
610
+ }
611
+
612
+ AST_ERB_END_NODE_T* end_node = NULL;
613
+
614
+ if (index < array_size(array)) {
615
+ AST_NODE_T* potential_end = array_get(array, index);
616
+
617
+ if (potential_end && potential_end->type == AST_ERB_CONTENT_NODE) {
618
+ AST_ERB_CONTENT_NODE_T* end_erb = (AST_ERB_CONTENT_NODE_T*) potential_end;
619
+
620
+ if (detect_control_type(end_erb) == CONTROL_TYPE_END) {
621
+ end_node = ast_erb_end_node_init(
622
+ end_erb->tag_opening,
623
+ end_erb->content,
624
+ end_erb->tag_closing,
625
+ end_erb->tag_opening->location->start,
626
+ end_erb->tag_closing->location->end,
627
+ end_erb->base.errors
628
+ );
629
+
630
+ index++;
631
+ }
632
+ }
633
+ }
634
+
635
+ position_T* start_position = erb_node->tag_opening->location->start;
636
+ position_T* end_position = erb_node->tag_closing->location->end;
637
+
638
+ if (end_node) {
639
+ end_position = end_node->base.location->end;
640
+ } else if (ensure_clause) {
641
+ end_position = ensure_clause->base.location->end;
642
+ } else if (else_clause) {
643
+ end_position = else_clause->base.location->end;
644
+ } else if (rescue_clause) {
645
+ end_position = rescue_clause->base.location->end;
646
+ }
647
+
648
+ AST_ERB_BEGIN_NODE_T* begin_node = ast_erb_begin_node_init(
649
+ erb_node->tag_opening,
650
+ erb_node->content,
651
+ erb_node->tag_closing,
652
+ children,
653
+ rescue_clause,
654
+ else_clause,
655
+ ensure_clause,
656
+ end_node,
657
+ start_position,
658
+ end_position,
659
+ array_init(8)
660
+ );
661
+
662
+ array_append(output_array, (AST_NODE_T*) begin_node);
663
+ return index;
664
+ }
665
+
666
+ if (initial_type == CONTROL_TYPE_BLOCK) {
667
+ index = process_block_children(node, array, index, children, context, initial_type);
668
+
669
+ AST_ERB_END_NODE_T* end_node = NULL;
670
+
671
+ if (index < array_size(array)) {
672
+ AST_NODE_T* potential_close = array_get(array, index);
673
+
674
+ if (potential_close && potential_close->type == AST_ERB_CONTENT_NODE) {
675
+ AST_ERB_CONTENT_NODE_T* close_erb = (AST_ERB_CONTENT_NODE_T*) potential_close;
676
+ control_type_t close_type = detect_control_type(close_erb);
677
+
678
+ if (close_type == CONTROL_TYPE_BLOCK_CLOSE || close_type == CONTROL_TYPE_END) {
679
+ end_node = ast_erb_end_node_init(
680
+ close_erb->tag_opening,
681
+ close_erb->content,
682
+ close_erb->tag_closing,
683
+ close_erb->tag_opening->location->start,
684
+ close_erb->tag_closing->location->end,
685
+ close_erb->base.errors
686
+ );
687
+
688
+ index++;
689
+ }
690
+ }
691
+ }
692
+
693
+ position_T* start_position = erb_node->tag_opening->location->start;
694
+ position_T* end_position = erb_node->tag_closing->location->end;
695
+
696
+ if (end_node) {
697
+ end_position = end_node->base.location->end;
698
+ } else if (children && array_size(children) > 0) {
699
+ AST_NODE_T* last_child = array_get(children, array_size(children) - 1);
700
+ end_position = last_child->location->end;
701
+ }
702
+
703
+ AST_ERB_BLOCK_NODE_T* block_node = ast_erb_block_node_init(
704
+ erb_node->tag_opening,
705
+ erb_node->content,
706
+ erb_node->tag_closing,
707
+ children,
708
+ end_node,
709
+ start_position,
710
+ end_position,
711
+ array_init(8)
712
+ );
713
+
714
+ array_append(output_array, (AST_NODE_T*) block_node);
715
+ return index;
716
+ }
717
+
718
+ index = process_block_children(node, array, index, children, context, initial_type);
719
+
720
+ AST_NODE_T* subsequent = NULL;
721
+ AST_ERB_END_NODE_T* end_node = NULL;
722
+
723
+ if (index < array_size(array)) {
724
+ AST_NODE_T* next_node = array_get(array, index);
725
+
726
+ if (next_node && next_node->type == AST_ERB_CONTENT_NODE) {
727
+ AST_ERB_CONTENT_NODE_T* next_erb = (AST_ERB_CONTENT_NODE_T*) next_node;
728
+ control_type_t next_type = detect_control_type(next_erb);
729
+
730
+ if (is_subsequent_type(initial_type, next_type)) {
731
+ index = process_subsequent_block(node, array, index, &subsequent, context, initial_type);
732
+ }
733
+ }
734
+ }
735
+
736
+ if (index < array_size(array)) {
737
+ AST_NODE_T* potential_end = array_get(array, index);
738
+
739
+ if (potential_end && potential_end->type == AST_ERB_CONTENT_NODE) {
740
+ AST_ERB_CONTENT_NODE_T* end_erb = (AST_ERB_CONTENT_NODE_T*) potential_end;
741
+
742
+ if (detect_control_type(end_erb) == CONTROL_TYPE_END) {
743
+ end_node = ast_erb_end_node_init(
744
+ end_erb->tag_opening,
745
+ end_erb->content,
746
+ end_erb->tag_closing,
747
+ end_erb->tag_opening->location->start,
748
+ end_erb->tag_closing->location->end,
749
+ end_erb->base.errors
750
+ );
751
+
752
+ index++;
753
+ }
754
+ }
755
+ }
756
+
757
+ AST_NODE_T* control_node = create_control_node(erb_node, children, subsequent, end_node, initial_type);
758
+
759
+ if (control_node) { array_append(output_array, control_node); }
760
+
761
+ return index;
762
+ }
763
+
764
+ static size_t process_subsequent_block(
765
+ AST_NODE_T* node, array_T* array, size_t index, AST_NODE_T** subsequent_out, analyze_ruby_context_T* context,
766
+ control_type_t parent_type
767
+ ) {
768
+ AST_ERB_CONTENT_NODE_T* erb_node = (AST_ERB_CONTENT_NODE_T*) array_get(array, index);
769
+ control_type_t type = detect_control_type(erb_node);
770
+ array_T* children = array_init(8);
771
+
772
+ index++;
773
+
774
+ index = process_block_children(node, array, index, children, context, parent_type);
775
+
776
+ AST_NODE_T* subsequent_node = create_control_node(erb_node, children, NULL, NULL, type);
777
+
778
+ if (index < array_size(array)) {
779
+ AST_NODE_T* next_node = array_get(array, index);
780
+
781
+ if (next_node && next_node->type == AST_ERB_CONTENT_NODE) {
782
+ AST_ERB_CONTENT_NODE_T* next_erb = (AST_ERB_CONTENT_NODE_T*) next_node;
783
+ control_type_t next_type = detect_control_type(next_erb);
784
+
785
+ if (is_subsequent_type(parent_type, next_type)
786
+ && !(type == CONTROL_TYPE_RESCUE && (next_type == CONTROL_TYPE_ELSE || next_type == CONTROL_TYPE_ENSURE))) {
787
+
788
+ AST_NODE_T** next_subsequent = NULL;
789
+
790
+ switch (type) {
791
+ case CONTROL_TYPE_ELSIF: {
792
+ if (subsequent_node->type == AST_ERB_IF_NODE) {
793
+ next_subsequent = &(((AST_ERB_IF_NODE_T*) subsequent_node)->subsequent);
794
+ }
795
+
796
+ break;
797
+ }
798
+
799
+ case CONTROL_TYPE_RESCUE: {
800
+ if (subsequent_node->type == AST_ERB_RESCUE_NODE && next_type == CONTROL_TYPE_RESCUE) {
801
+ AST_NODE_T* next_rescue_node = NULL;
802
+ index = process_subsequent_block(node, array, index, &next_rescue_node, context, parent_type);
803
+
804
+ if (next_rescue_node) {
805
+ ((AST_ERB_RESCUE_NODE_T*) subsequent_node)->subsequent = (AST_ERB_RESCUE_NODE_T*) next_rescue_node;
806
+ }
807
+
808
+ next_subsequent = NULL;
809
+ }
810
+
811
+ break;
812
+ }
813
+
814
+ default: break;
815
+ }
816
+
817
+ if (next_subsequent) {
818
+ index = process_subsequent_block(node, array, index, next_subsequent, context, parent_type);
819
+ }
820
+ }
821
+ }
822
+ }
823
+
824
+ *subsequent_out = subsequent_node;
825
+ return index;
826
+ }
827
+
828
+ static size_t process_block_children(
829
+ AST_NODE_T* node, array_T* array, size_t index, array_T* children_array, analyze_ruby_context_T* context,
830
+ control_type_t parent_type
831
+ ) {
832
+ while (index < array_size(array)) {
833
+ AST_NODE_T* child = array_get(array, index);
834
+
835
+ if (!child) { break; }
836
+
837
+ if (child->type != AST_ERB_CONTENT_NODE) {
838
+ array_append(children_array, child);
839
+ index++;
840
+ continue;
841
+ }
842
+
843
+ AST_ERB_CONTENT_NODE_T* erb_content = (AST_ERB_CONTENT_NODE_T*) child;
844
+ control_type_t child_type = detect_control_type(erb_content);
845
+
846
+ if (is_terminator_type(parent_type, child_type)) { break; }
847
+
848
+ if (parent_type == CONTROL_TYPE_CASE && child_type == CONTROL_TYPE_WHEN) {
849
+ array_T* when_statements = array_init(8);
850
+
851
+ index++;
852
+
853
+ index = process_block_children(node, array, index, when_statements, context, CONTROL_TYPE_WHEN);
854
+
855
+ AST_ERB_WHEN_NODE_T* when_node = ast_erb_when_node_init(
856
+ erb_content->tag_opening,
857
+ erb_content->content,
858
+ erb_content->tag_closing,
859
+ when_statements,
860
+ erb_content->tag_opening->location->start,
861
+ erb_content->tag_closing->location->end,
862
+ array_init(8)
863
+ );
864
+
865
+ array_append(children_array, (AST_NODE_T*) when_node);
866
+ continue;
867
+ }
868
+
869
+ if (child_type == CONTROL_TYPE_IF || child_type == CONTROL_TYPE_CASE || child_type == CONTROL_TYPE_BEGIN
870
+ || child_type == CONTROL_TYPE_UNLESS || child_type == CONTROL_TYPE_WHILE || child_type == CONTROL_TYPE_UNTIL
871
+ || child_type == CONTROL_TYPE_FOR || child_type == CONTROL_TYPE_BLOCK) {
872
+ array_T* temp_array = array_init(1);
873
+ size_t new_index = process_control_structure(node, array, index, temp_array, context, child_type);
874
+
875
+ if (array_size(temp_array) > 0) { array_append(children_array, array_get(temp_array, 0)); }
876
+
877
+ free(temp_array);
878
+
879
+ index = new_index;
880
+ continue;
881
+ }
882
+
883
+ array_append(children_array, child);
884
+ index++;
885
+ }
886
+
887
+ return index;
888
+ }
889
+
890
+ static array_T* rewrite_node_array(AST_NODE_T* node, array_T* array, analyze_ruby_context_T* context) {
891
+ array_T* new_array = array_init(array_size(array));
892
+ size_t index = 0;
893
+
894
+ while (index < array_size(array)) {
895
+ AST_NODE_T* item = array_get(array, index);
896
+
897
+ if (!item) { break; }
898
+
899
+ if (item->type != AST_ERB_CONTENT_NODE) {
900
+ array_append(new_array, item);
901
+ index++;
902
+ continue;
903
+ }
904
+
905
+ AST_ERB_CONTENT_NODE_T* erb_node = (AST_ERB_CONTENT_NODE_T*) item;
906
+ control_type_t type = detect_control_type(erb_node);
907
+
908
+ switch (type) {
909
+ case CONTROL_TYPE_IF:
910
+ case CONTROL_TYPE_CASE:
911
+ case CONTROL_TYPE_BEGIN:
912
+ case CONTROL_TYPE_UNLESS:
913
+ case CONTROL_TYPE_WHILE:
914
+ case CONTROL_TYPE_UNTIL:
915
+ case CONTROL_TYPE_FOR:
916
+ case CONTROL_TYPE_BLOCK:
917
+ index = process_control_structure(node, array, index, new_array, context, type);
918
+ continue;
919
+
920
+ default:
921
+ array_append(new_array, item);
922
+ index++;
923
+ break;
924
+ }
925
+ }
926
+
927
+ return new_array;
928
+ }
929
+
930
+ static bool transform_erb_nodes(const AST_NODE_T* node, void* data) {
931
+ analyze_ruby_context_T* context = (analyze_ruby_context_T*) data;
932
+ context->parent = (AST_NODE_T*) node;
933
+
934
+ if (node->type == AST_DOCUMENT_NODE) {
935
+ AST_DOCUMENT_NODE_T* document_node = (AST_DOCUMENT_NODE_T*) node;
936
+ document_node->children = rewrite_node_array((AST_NODE_T*) node, document_node->children, context);
937
+ }
938
+
939
+ if (node->type == AST_HTML_ELEMENT_NODE) {
940
+ AST_HTML_ELEMENT_NODE_T* element_node = (AST_HTML_ELEMENT_NODE_T*) node;
941
+ element_node->body = rewrite_node_array((AST_NODE_T*) node, element_node->body, context);
942
+ }
943
+
944
+ if (node->type == AST_HTML_OPEN_TAG_NODE) {
945
+ AST_HTML_OPEN_TAG_NODE_T* open_tag = (AST_HTML_OPEN_TAG_NODE_T*) node;
946
+ open_tag->children = rewrite_node_array((AST_NODE_T*) node, open_tag->children, context);
947
+ }
948
+
949
+ if (node->type == AST_HTML_ATTRIBUTE_VALUE_NODE) {
950
+ AST_HTML_ATTRIBUTE_VALUE_NODE_T* value_node = (AST_HTML_ATTRIBUTE_VALUE_NODE_T*) node;
951
+ value_node->children = rewrite_node_array((AST_NODE_T*) node, value_node->children, context);
952
+ }
953
+
954
+ herb_visit_child_nodes(node, transform_erb_nodes, data);
955
+
956
+ return false;
957
+ }
958
+
959
+ void herb_analyze_parse_tree(AST_DOCUMENT_NODE_T* document, const char* source) {
960
+ herb_visit_node((AST_NODE_T*) document, analyze_erb_content, NULL);
961
+
962
+ analyze_ruby_context_T* context = malloc(sizeof(analyze_ruby_context_T));
963
+ context->document = document;
964
+ context->parent = NULL;
965
+ context->ruby_context_stack = array_init(8);
966
+
967
+ herb_visit_node((AST_NODE_T*) document, transform_erb_nodes, context);
968
+
969
+ herb_analyze_parse_errors(document, source);
970
+
971
+ free(context);
972
+ }
973
+
974
+ void herb_analyze_parse_errors(AST_DOCUMENT_NODE_T* document, const char* source) {
975
+ char* extracted_ruby = herb_extract_ruby_with_semicolons(source);
976
+
977
+ pm_parser_t parser;
978
+ pm_parser_init(&parser, (const uint8_t*) extracted_ruby, strlen(extracted_ruby), NULL);
979
+
980
+ pm_parse(&parser);
981
+
982
+ for (const pm_diagnostic_t* error = (const pm_diagnostic_t*) parser.error_list.head; error != NULL;
983
+ error = (const pm_diagnostic_t*) error->node.next) {
984
+ array_append(
985
+ document->base.errors,
986
+ ruby_parse_error_from_prism_error(error, (AST_NODE_T*) document, source, &parser)
987
+ );
988
+ }
989
+ }