@herb-tools/node 0.1.0 → 0.2.0
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.
- package/CHANGELOG.md +8 -0
- package/dist/herb-node.esm.js +1 -1
- package/extension/extension_helpers.cpp +2 -3
- package/extension/herb.cpp +9 -0
- package/extension/libherb/analyze.c +138 -43
- package/extension/libherb/analyze.h +39 -0
- package/extension/libherb/analyze_helpers.c +44 -1
- package/extension/libherb/analyze_helpers.h +49 -0
- package/extension/libherb/analyzed_ruby.c +10 -1
- package/extension/libherb/analyzed_ruby.h +36 -0
- package/extension/libherb/array.h +33 -0
- package/extension/libherb/ast_node.h +35 -0
- package/extension/libherb/ast_nodes.c +103 -1
- package/extension/libherb/ast_nodes.h +335 -0
- package/extension/libherb/ast_pretty_print.c +60 -0
- package/extension/libherb/ast_pretty_print.h +17 -0
- package/extension/libherb/buffer.c +60 -27
- package/extension/libherb/buffer.h +39 -0
- package/extension/libherb/errors.h +125 -0
- package/extension/libherb/extract.c +57 -20
- package/extension/libherb/extract.h +20 -0
- package/extension/libherb/herb.h +32 -0
- package/extension/libherb/html_util.h +13 -0
- package/extension/libherb/include/analyze.h +3 -0
- package/extension/libherb/include/analyze_helpers.h +6 -0
- package/extension/libherb/include/analyzed_ruby.h +3 -0
- package/extension/libherb/include/ast_nodes.h +32 -0
- package/extension/libherb/include/buffer.h +5 -2
- package/extension/libherb/include/lexer_peek_helpers.h +2 -2
- package/extension/libherb/include/macros.h +2 -2
- package/extension/libherb/include/version.h +1 -1
- package/extension/libherb/io.h +9 -0
- package/extension/libherb/json.h +28 -0
- package/extension/libherb/lexer.c +1 -1
- package/extension/libherb/lexer.h +13 -0
- package/extension/libherb/lexer_peek_helpers.h +23 -0
- package/extension/libherb/lexer_struct.h +32 -0
- package/extension/libherb/location.h +25 -0
- package/extension/libherb/macros.h +10 -0
- package/extension/libherb/main.c +1 -1
- package/extension/libherb/memory.h +12 -0
- package/extension/libherb/parser.c +17 -7
- package/extension/libherb/parser.h +22 -0
- package/extension/libherb/parser_helpers.h +33 -0
- package/extension/libherb/position.h +22 -0
- package/extension/libherb/pretty_print.h +53 -0
- package/extension/libherb/prism_helpers.h +18 -0
- package/extension/libherb/range.h +23 -0
- package/extension/libherb/ruby_parser.h +6 -0
- package/extension/libherb/token.c +1 -1
- package/extension/libherb/token.h +25 -0
- package/extension/libherb/token_matchers.h +21 -0
- package/extension/libherb/token_struct.h +51 -0
- package/extension/libherb/util.c +3 -1
- package/extension/libherb/util.h +25 -0
- package/extension/libherb/version.h +6 -0
- package/extension/libherb/visitor.c +36 -0
- package/extension/libherb/visitor.h +11 -0
- package/extension/nodes.cpp +117 -0
- package/extension/nodes.h +3 -0
- package/package.json +11 -20
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
## 0.2.0 (2025-06-11)
|
|
2
|
+
|
|
3
|
+
This was a version bump only for @herb-tools/node to align it with other projects, there were no code changes.
|
|
4
|
+
|
|
5
|
+
## 0.1.1 (2025-04-20)
|
|
6
|
+
|
|
7
|
+
This was a version bump only for @herb-tools/node to align it with other projects, there were no code changes.
|
|
8
|
+
|
|
1
9
|
## 0.1.0 (2025-04-15)
|
|
2
10
|
|
|
3
11
|
This was a version bump only for @herb-tools/node to align it with other projects, there were no code changes.
|
package/dist/herb-node.esm.js
CHANGED
|
@@ -148,7 +148,9 @@ napi_value ReadFileToString(napi_env env, const char* file_path) {
|
|
|
148
148
|
}
|
|
149
149
|
|
|
150
150
|
napi_value result = CreateString(env, content);
|
|
151
|
+
|
|
151
152
|
free(content);
|
|
153
|
+
|
|
152
154
|
return result;
|
|
153
155
|
}
|
|
154
156
|
|
|
@@ -176,9 +178,6 @@ napi_value CreateLexResult(napi_env env, array_T* tokens, napi_value source) {
|
|
|
176
178
|
napi_set_named_property(env, result, "warnings", warnings_array);
|
|
177
179
|
napi_set_named_property(env, result, "errors", errors_array);
|
|
178
180
|
|
|
179
|
-
// Free tokens after creating JS objects
|
|
180
|
-
herb_free_tokens(&tokens);
|
|
181
|
-
|
|
182
181
|
return result;
|
|
183
182
|
}
|
|
184
183
|
|
package/extension/herb.cpp
CHANGED
|
@@ -34,7 +34,9 @@ napi_value Herb_lex(napi_env env, napi_callback_info info) {
|
|
|
34
34
|
array_T* tokens = herb_lex(string);
|
|
35
35
|
napi_value result = CreateLexResult(env, tokens, args[0]);
|
|
36
36
|
|
|
37
|
+
herb_free_tokens(&tokens);
|
|
37
38
|
free(string);
|
|
39
|
+
|
|
38
40
|
return result;
|
|
39
41
|
}
|
|
40
42
|
|
|
@@ -55,7 +57,9 @@ napi_value Herb_lex_file(napi_env env, napi_callback_info info) {
|
|
|
55
57
|
napi_value source_value = ReadFileToString(env, file_path);
|
|
56
58
|
napi_value result = CreateLexResult(env, tokens, source_value);
|
|
57
59
|
|
|
60
|
+
herb_free_tokens(&tokens);
|
|
58
61
|
free(file_path);
|
|
62
|
+
|
|
59
63
|
return result;
|
|
60
64
|
}
|
|
61
65
|
|
|
@@ -76,7 +80,9 @@ napi_value Herb_parse(napi_env env, napi_callback_info info) {
|
|
|
76
80
|
herb_analyze_parse_tree(root, string);
|
|
77
81
|
napi_value result = CreateParseResult(env, root, args[0]);
|
|
78
82
|
|
|
83
|
+
ast_node_free((AST_NODE_T *) root);
|
|
79
84
|
free(string);
|
|
85
|
+
|
|
80
86
|
return result;
|
|
81
87
|
}
|
|
82
88
|
|
|
@@ -104,8 +110,10 @@ napi_value Herb_parse_file(napi_env env, napi_callback_info info) {
|
|
|
104
110
|
AST_DOCUMENT_NODE_T* root = herb_parse(string);
|
|
105
111
|
napi_value result = CreateParseResult(env, root, source_value);
|
|
106
112
|
|
|
113
|
+
ast_node_free((AST_NODE_T *) root);
|
|
107
114
|
free(file_path);
|
|
108
115
|
free(string);
|
|
116
|
+
|
|
109
117
|
return result;
|
|
110
118
|
}
|
|
111
119
|
|
|
@@ -136,6 +144,7 @@ napi_value Herb_lex_to_json(napi_env env, napi_callback_info info) {
|
|
|
136
144
|
|
|
137
145
|
buffer_free(&output);
|
|
138
146
|
free(string);
|
|
147
|
+
|
|
139
148
|
return result;
|
|
140
149
|
}
|
|
141
150
|
|
|
@@ -25,6 +25,7 @@ static analyzed_ruby_T* herb_analyze_ruby(char* source) {
|
|
|
25
25
|
pm_visit_node(analyzed->root, search_if_nodes, analyzed);
|
|
26
26
|
pm_visit_node(analyzed->root, search_block_nodes, analyzed);
|
|
27
27
|
pm_visit_node(analyzed->root, search_case_nodes, analyzed);
|
|
28
|
+
pm_visit_node(analyzed->root, search_case_match_nodes, analyzed);
|
|
28
29
|
pm_visit_node(analyzed->root, search_while_nodes, analyzed);
|
|
29
30
|
pm_visit_node(analyzed->root, search_for_nodes, analyzed);
|
|
30
31
|
pm_visit_node(analyzed->root, search_until_nodes, analyzed);
|
|
@@ -35,8 +36,10 @@ static analyzed_ruby_T* herb_analyze_ruby(char* source) {
|
|
|
35
36
|
search_else_nodes(analyzed);
|
|
36
37
|
search_end_nodes(analyzed);
|
|
37
38
|
search_when_nodes(analyzed);
|
|
39
|
+
search_in_nodes(analyzed);
|
|
38
40
|
search_rescue_nodes(analyzed);
|
|
39
41
|
search_ensure_nodes(analyzed);
|
|
42
|
+
search_yield_nodes(analyzed);
|
|
40
43
|
search_block_closing_nodes(analyzed);
|
|
41
44
|
|
|
42
45
|
return analyzed;
|
|
@@ -84,7 +87,9 @@ static control_type_t detect_control_type(AST_ERB_CONTENT_NODE_T* erb_node) {
|
|
|
84
87
|
if (has_else_node(ruby)) { return CONTROL_TYPE_ELSE; }
|
|
85
88
|
if (has_end(ruby)) { return CONTROL_TYPE_END; }
|
|
86
89
|
if (has_case_node(ruby)) { return CONTROL_TYPE_CASE; }
|
|
90
|
+
if (has_case_match_node(ruby)) { return CONTROL_TYPE_CASE_MATCH; }
|
|
87
91
|
if (has_when_node(ruby)) { return CONTROL_TYPE_WHEN; }
|
|
92
|
+
if (has_in_node(ruby)) { return CONTROL_TYPE_IN; }
|
|
88
93
|
if (has_begin_node(ruby)) { return CONTROL_TYPE_BEGIN; }
|
|
89
94
|
if (has_rescue_node(ruby)) { return CONTROL_TYPE_RESCUE; }
|
|
90
95
|
if (has_ensure_node(ruby)) { return CONTROL_TYPE_ENSURE; }
|
|
@@ -93,6 +98,7 @@ static control_type_t detect_control_type(AST_ERB_CONTENT_NODE_T* erb_node) {
|
|
|
93
98
|
if (has_until_node(ruby)) { return CONTROL_TYPE_UNTIL; }
|
|
94
99
|
if (has_for_node(ruby)) { return CONTROL_TYPE_FOR; }
|
|
95
100
|
if (has_block_node(ruby)) { return CONTROL_TYPE_BLOCK; }
|
|
101
|
+
if (has_yield_node(ruby)) { return CONTROL_TYPE_YIELD; }
|
|
96
102
|
if (has_block_closing(ruby)) { return CONTROL_TYPE_BLOCK_CLOSE; }
|
|
97
103
|
|
|
98
104
|
return CONTROL_TYPE_UNKNOWN;
|
|
@@ -102,7 +108,8 @@ static bool is_subsequent_type(control_type_t parent_type, control_type_t child_
|
|
|
102
108
|
switch (parent_type) {
|
|
103
109
|
case CONTROL_TYPE_IF:
|
|
104
110
|
case CONTROL_TYPE_ELSIF: return child_type == CONTROL_TYPE_ELSIF || child_type == CONTROL_TYPE_ELSE;
|
|
105
|
-
case CONTROL_TYPE_CASE:
|
|
111
|
+
case CONTROL_TYPE_CASE:
|
|
112
|
+
case CONTROL_TYPE_CASE_MATCH: return child_type == CONTROL_TYPE_WHEN || child_type == CONTROL_TYPE_ELSE;
|
|
106
113
|
case CONTROL_TYPE_BEGIN:
|
|
107
114
|
return child_type == CONTROL_TYPE_RESCUE || child_type == CONTROL_TYPE_ELSE || child_type == CONTROL_TYPE_ENSURE;
|
|
108
115
|
case CONTROL_TYPE_RESCUE: return child_type == CONTROL_TYPE_RESCUE;
|
|
@@ -117,6 +124,7 @@ static bool is_terminator_type(control_type_t parent_type, control_type_t child_
|
|
|
117
124
|
|
|
118
125
|
switch (parent_type) {
|
|
119
126
|
case CONTROL_TYPE_WHEN: return child_type == CONTROL_TYPE_WHEN || child_type == CONTROL_TYPE_ELSE;
|
|
127
|
+
case CONTROL_TYPE_IN: return child_type == CONTROL_TYPE_IN || child_type == CONTROL_TYPE_ELSE;
|
|
120
128
|
case CONTROL_TYPE_BLOCK: return child_type == CONTROL_TYPE_BLOCK_CLOSE;
|
|
121
129
|
|
|
122
130
|
default: return is_subsequent_type(parent_type, child_type);
|
|
@@ -163,27 +171,46 @@ static AST_NODE_T* create_control_node(
|
|
|
163
171
|
return (AST_NODE_T*)
|
|
164
172
|
ast_erb_else_node_init(tag_opening, content, tag_closing, children, start_position, end_position, errors);
|
|
165
173
|
|
|
166
|
-
case CONTROL_TYPE_CASE:
|
|
174
|
+
case CONTROL_TYPE_CASE:
|
|
175
|
+
case CONTROL_TYPE_CASE_MATCH: {
|
|
167
176
|
AST_ERB_ELSE_NODE_T* else_node = NULL;
|
|
168
177
|
if (subsequent && subsequent->type == AST_ERB_ELSE_NODE) { else_node = (AST_ERB_ELSE_NODE_T*) subsequent; }
|
|
169
178
|
|
|
170
179
|
array_T* when_conditions = array_init(8);
|
|
171
|
-
array_T*
|
|
180
|
+
array_T* in_conditions = array_init(8);
|
|
181
|
+
array_T* non_when_non_in_children = array_init(8);
|
|
172
182
|
|
|
173
183
|
for (size_t i = 0; i < array_size(children); i++) {
|
|
174
184
|
AST_NODE_T* child = array_get(children, i);
|
|
175
185
|
if (child && child->type == AST_ERB_WHEN_NODE) {
|
|
176
186
|
array_append(when_conditions, child);
|
|
187
|
+
} else if (child && child->type == AST_ERB_IN_NODE) {
|
|
188
|
+
array_append(in_conditions, child);
|
|
177
189
|
} else {
|
|
178
|
-
array_append(
|
|
190
|
+
array_append(non_when_non_in_children, child);
|
|
179
191
|
}
|
|
180
192
|
}
|
|
181
193
|
|
|
194
|
+
if (array_size(in_conditions) > 0) {
|
|
195
|
+
return (AST_NODE_T*) ast_erb_case_match_node_init(
|
|
196
|
+
tag_opening,
|
|
197
|
+
content,
|
|
198
|
+
tag_closing,
|
|
199
|
+
non_when_non_in_children,
|
|
200
|
+
in_conditions,
|
|
201
|
+
else_node,
|
|
202
|
+
end_node,
|
|
203
|
+
start_position,
|
|
204
|
+
end_position,
|
|
205
|
+
errors
|
|
206
|
+
);
|
|
207
|
+
}
|
|
208
|
+
|
|
182
209
|
return (AST_NODE_T*) ast_erb_case_node_init(
|
|
183
210
|
tag_opening,
|
|
184
211
|
content,
|
|
185
212
|
tag_closing,
|
|
186
|
-
|
|
213
|
+
non_when_non_in_children,
|
|
187
214
|
when_conditions,
|
|
188
215
|
else_node,
|
|
189
216
|
end_node,
|
|
@@ -198,6 +225,11 @@ static AST_NODE_T* create_control_node(
|
|
|
198
225
|
ast_erb_when_node_init(tag_opening, content, tag_closing, children, start_position, end_position, errors);
|
|
199
226
|
}
|
|
200
227
|
|
|
228
|
+
case CONTROL_TYPE_IN: {
|
|
229
|
+
return (AST_NODE_T*)
|
|
230
|
+
ast_erb_in_node_init(tag_opening, content, tag_closing, children, start_position, end_position, errors);
|
|
231
|
+
}
|
|
232
|
+
|
|
201
233
|
case CONTROL_TYPE_BEGIN: {
|
|
202
234
|
AST_ERB_RESCUE_NODE_T* rescue_clause = NULL;
|
|
203
235
|
AST_ERB_ELSE_NODE_T* else_clause = NULL;
|
|
@@ -320,6 +352,11 @@ static AST_NODE_T* create_control_node(
|
|
|
320
352
|
);
|
|
321
353
|
}
|
|
322
354
|
|
|
355
|
+
case CONTROL_TYPE_YIELD: {
|
|
356
|
+
return (AST_NODE_T*)
|
|
357
|
+
ast_erb_yield_node_init(tag_opening, content, tag_closing, start_position, end_position, errors);
|
|
358
|
+
}
|
|
359
|
+
|
|
323
360
|
default: array_free(&errors); return NULL;
|
|
324
361
|
}
|
|
325
362
|
}
|
|
@@ -333,9 +370,10 @@ static size_t process_control_structure(
|
|
|
333
370
|
|
|
334
371
|
index++;
|
|
335
372
|
|
|
336
|
-
if (initial_type == CONTROL_TYPE_CASE) {
|
|
373
|
+
if (initial_type == CONTROL_TYPE_CASE || initial_type == CONTROL_TYPE_CASE_MATCH) {
|
|
337
374
|
array_T* when_conditions = array_init(8);
|
|
338
|
-
array_T*
|
|
375
|
+
array_T* in_conditions = array_init(8);
|
|
376
|
+
array_T* non_when_non_in_children = array_init(8);
|
|
339
377
|
|
|
340
378
|
while (index < array_size(array)) {
|
|
341
379
|
AST_NODE_T* next_node = array_get(array, index);
|
|
@@ -346,10 +384,10 @@ static size_t process_control_structure(
|
|
|
346
384
|
AST_ERB_CONTENT_NODE_T* erb_content = (AST_ERB_CONTENT_NODE_T*) next_node;
|
|
347
385
|
control_type_t next_type = detect_control_type(erb_content);
|
|
348
386
|
|
|
349
|
-
if (next_type == CONTROL_TYPE_WHEN) { break; }
|
|
387
|
+
if (next_type == CONTROL_TYPE_WHEN || next_type == CONTROL_TYPE_IN) { break; }
|
|
350
388
|
}
|
|
351
389
|
|
|
352
|
-
array_append(
|
|
390
|
+
array_append(non_when_non_in_children, next_node);
|
|
353
391
|
index++;
|
|
354
392
|
}
|
|
355
393
|
|
|
@@ -359,7 +397,7 @@ static size_t process_control_structure(
|
|
|
359
397
|
if (!next_node) { break; }
|
|
360
398
|
|
|
361
399
|
if (next_node->type != AST_ERB_CONTENT_NODE) {
|
|
362
|
-
array_append(
|
|
400
|
+
array_append(non_when_non_in_children, next_node);
|
|
363
401
|
index++;
|
|
364
402
|
continue;
|
|
365
403
|
}
|
|
@@ -380,7 +418,8 @@ static size_t process_control_structure(
|
|
|
380
418
|
AST_ERB_CONTENT_NODE_T* child_erb = (AST_ERB_CONTENT_NODE_T*) child;
|
|
381
419
|
control_type_t child_type = detect_control_type(child_erb);
|
|
382
420
|
|
|
383
|
-
if (child_type == CONTROL_TYPE_WHEN || child_type ==
|
|
421
|
+
if (child_type == CONTROL_TYPE_WHEN || child_type == CONTROL_TYPE_IN || child_type == CONTROL_TYPE_ELSE
|
|
422
|
+
|| child_type == CONTROL_TYPE_END) {
|
|
384
423
|
break;
|
|
385
424
|
}
|
|
386
425
|
}
|
|
@@ -401,11 +440,47 @@ static size_t process_control_structure(
|
|
|
401
440
|
|
|
402
441
|
array_append(when_conditions, (AST_NODE_T*) when_node);
|
|
403
442
|
|
|
443
|
+
continue;
|
|
444
|
+
} else if (next_type == CONTROL_TYPE_IN) {
|
|
445
|
+
array_T* in_statements = array_init(8);
|
|
446
|
+
index++;
|
|
447
|
+
|
|
448
|
+
while (index < array_size(array)) {
|
|
449
|
+
AST_NODE_T* child = array_get(array, index);
|
|
450
|
+
|
|
451
|
+
if (!child) { break; }
|
|
452
|
+
|
|
453
|
+
if (child->type == AST_ERB_CONTENT_NODE) {
|
|
454
|
+
AST_ERB_CONTENT_NODE_T* child_erb = (AST_ERB_CONTENT_NODE_T*) child;
|
|
455
|
+
control_type_t child_type = detect_control_type(child_erb);
|
|
456
|
+
|
|
457
|
+
if (child_type == CONTROL_TYPE_IN || child_type == CONTROL_TYPE_WHEN || child_type == CONTROL_TYPE_ELSE
|
|
458
|
+
|| child_type == CONTROL_TYPE_END) {
|
|
459
|
+
break;
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
array_append(in_statements, child);
|
|
464
|
+
index++;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
AST_ERB_IN_NODE_T* in_node = ast_erb_in_node_init(
|
|
468
|
+
erb_content->tag_opening,
|
|
469
|
+
erb_content->content,
|
|
470
|
+
erb_content->tag_closing,
|
|
471
|
+
in_statements,
|
|
472
|
+
erb_content->tag_opening->location->start,
|
|
473
|
+
erb_content->tag_closing->location->end,
|
|
474
|
+
array_init(8)
|
|
475
|
+
);
|
|
476
|
+
|
|
477
|
+
array_append(in_conditions, (AST_NODE_T*) in_node);
|
|
478
|
+
|
|
404
479
|
continue;
|
|
405
480
|
} else if (next_type == CONTROL_TYPE_ELSE || next_type == CONTROL_TYPE_END) {
|
|
406
481
|
break;
|
|
407
482
|
} else {
|
|
408
|
-
array_append(
|
|
483
|
+
array_append(non_when_non_in_children, next_node);
|
|
409
484
|
index++;
|
|
410
485
|
}
|
|
411
486
|
}
|
|
@@ -486,13 +561,34 @@ static size_t process_control_structure(
|
|
|
486
561
|
} else if (array_size(when_conditions) > 0) {
|
|
487
562
|
AST_NODE_T* last_when = array_get(when_conditions, array_size(when_conditions) - 1);
|
|
488
563
|
end_position = last_when->location->end;
|
|
564
|
+
} else if (array_size(in_conditions) > 0) {
|
|
565
|
+
AST_NODE_T* last_in = array_get(in_conditions, array_size(in_conditions) - 1);
|
|
566
|
+
end_position = last_in->location->end;
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
if (array_size(in_conditions) > 0) {
|
|
570
|
+
AST_ERB_CASE_MATCH_NODE_T* case_match_node = ast_erb_case_match_node_init(
|
|
571
|
+
erb_node->tag_opening,
|
|
572
|
+
erb_node->content,
|
|
573
|
+
erb_node->tag_closing,
|
|
574
|
+
non_when_non_in_children,
|
|
575
|
+
in_conditions,
|
|
576
|
+
else_clause,
|
|
577
|
+
end_node,
|
|
578
|
+
start_position,
|
|
579
|
+
end_position,
|
|
580
|
+
array_init(8)
|
|
581
|
+
);
|
|
582
|
+
|
|
583
|
+
array_append(output_array, (AST_NODE_T*) case_match_node);
|
|
584
|
+
return index;
|
|
489
585
|
}
|
|
490
586
|
|
|
491
587
|
AST_ERB_CASE_NODE_T* case_node = ast_erb_case_node_init(
|
|
492
588
|
erb_node->tag_opening,
|
|
493
589
|
erb_node->content,
|
|
494
590
|
erb_node->tag_closing,
|
|
495
|
-
|
|
591
|
+
non_when_non_in_children,
|
|
496
592
|
when_conditions,
|
|
497
593
|
else_clause,
|
|
498
594
|
end_node,
|
|
@@ -845,36 +941,15 @@ static size_t process_block_children(
|
|
|
845
941
|
|
|
846
942
|
if (is_terminator_type(parent_type, child_type)) { break; }
|
|
847
943
|
|
|
848
|
-
if (
|
|
849
|
-
|
|
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) {
|
|
944
|
+
if (child_type == CONTROL_TYPE_IF || child_type == CONTROL_TYPE_CASE || child_type == CONTROL_TYPE_CASE_MATCH
|
|
945
|
+
|| child_type == CONTROL_TYPE_BEGIN || child_type == CONTROL_TYPE_UNLESS || child_type == CONTROL_TYPE_WHILE
|
|
946
|
+
|| child_type == CONTROL_TYPE_UNTIL || child_type == CONTROL_TYPE_FOR || child_type == CONTROL_TYPE_BLOCK) {
|
|
872
947
|
array_T* temp_array = array_init(1);
|
|
873
948
|
size_t new_index = process_control_structure(node, array, index, temp_array, context, child_type);
|
|
874
949
|
|
|
875
950
|
if (array_size(temp_array) > 0) { array_append(children_array, array_get(temp_array, 0)); }
|
|
876
951
|
|
|
877
|
-
|
|
952
|
+
array_free(&temp_array);
|
|
878
953
|
|
|
879
954
|
index = new_index;
|
|
880
955
|
continue;
|
|
@@ -908,12 +983,14 @@ static array_T* rewrite_node_array(AST_NODE_T* node, array_T* array, analyze_rub
|
|
|
908
983
|
switch (type) {
|
|
909
984
|
case CONTROL_TYPE_IF:
|
|
910
985
|
case CONTROL_TYPE_CASE:
|
|
986
|
+
case CONTROL_TYPE_CASE_MATCH:
|
|
911
987
|
case CONTROL_TYPE_BEGIN:
|
|
912
988
|
case CONTROL_TYPE_UNLESS:
|
|
913
989
|
case CONTROL_TYPE_WHILE:
|
|
914
990
|
case CONTROL_TYPE_UNTIL:
|
|
915
991
|
case CONTROL_TYPE_FOR:
|
|
916
992
|
case CONTROL_TYPE_BLOCK:
|
|
993
|
+
case CONTROL_TYPE_YIELD:
|
|
917
994
|
index = process_control_structure(node, array, index, new_array, context, type);
|
|
918
995
|
continue;
|
|
919
996
|
|
|
@@ -933,22 +1010,30 @@ static bool transform_erb_nodes(const AST_NODE_T* node, void* data) {
|
|
|
933
1010
|
|
|
934
1011
|
if (node->type == AST_DOCUMENT_NODE) {
|
|
935
1012
|
AST_DOCUMENT_NODE_T* document_node = (AST_DOCUMENT_NODE_T*) node;
|
|
1013
|
+
array_T* old_array = document_node->children;
|
|
936
1014
|
document_node->children = rewrite_node_array((AST_NODE_T*) node, document_node->children, context);
|
|
1015
|
+
array_free(&old_array);
|
|
937
1016
|
}
|
|
938
1017
|
|
|
939
1018
|
if (node->type == AST_HTML_ELEMENT_NODE) {
|
|
940
1019
|
AST_HTML_ELEMENT_NODE_T* element_node = (AST_HTML_ELEMENT_NODE_T*) node;
|
|
1020
|
+
array_T* old_array = element_node->body;
|
|
941
1021
|
element_node->body = rewrite_node_array((AST_NODE_T*) node, element_node->body, context);
|
|
1022
|
+
array_free(&old_array);
|
|
942
1023
|
}
|
|
943
1024
|
|
|
944
1025
|
if (node->type == AST_HTML_OPEN_TAG_NODE) {
|
|
945
1026
|
AST_HTML_OPEN_TAG_NODE_T* open_tag = (AST_HTML_OPEN_TAG_NODE_T*) node;
|
|
1027
|
+
array_T* old_array = open_tag->children;
|
|
946
1028
|
open_tag->children = rewrite_node_array((AST_NODE_T*) node, open_tag->children, context);
|
|
1029
|
+
array_free(&old_array);
|
|
947
1030
|
}
|
|
948
1031
|
|
|
949
1032
|
if (node->type == AST_HTML_ATTRIBUTE_VALUE_NODE) {
|
|
950
1033
|
AST_HTML_ATTRIBUTE_VALUE_NODE_T* value_node = (AST_HTML_ATTRIBUTE_VALUE_NODE_T*) node;
|
|
1034
|
+
array_T* old_array = value_node->children;
|
|
951
1035
|
value_node->children = rewrite_node_array((AST_NODE_T*) node, value_node->children, context);
|
|
1036
|
+
array_free(&old_array);
|
|
952
1037
|
}
|
|
953
1038
|
|
|
954
1039
|
herb_visit_child_nodes(node, transform_erb_nodes, data);
|
|
@@ -968,6 +1053,7 @@ void herb_analyze_parse_tree(AST_DOCUMENT_NODE_T* document, const char* source)
|
|
|
968
1053
|
|
|
969
1054
|
herb_analyze_parse_errors(document, source);
|
|
970
1055
|
|
|
1056
|
+
array_free(&context->ruby_context_stack);
|
|
971
1057
|
free(context);
|
|
972
1058
|
}
|
|
973
1059
|
|
|
@@ -977,13 +1063,22 @@ void herb_analyze_parse_errors(AST_DOCUMENT_NODE_T* document, const char* source
|
|
|
977
1063
|
pm_parser_t parser;
|
|
978
1064
|
pm_parser_init(&parser, (const uint8_t*) extracted_ruby, strlen(extracted_ruby), NULL);
|
|
979
1065
|
|
|
980
|
-
pm_parse(&parser);
|
|
1066
|
+
pm_node_t* root = pm_parse(&parser);
|
|
981
1067
|
|
|
982
1068
|
for (const pm_diagnostic_t* error = (const pm_diagnostic_t*) parser.error_list.head; error != NULL;
|
|
983
1069
|
error = (const pm_diagnostic_t*) error->node.next) {
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
1070
|
+
|
|
1071
|
+
RUBY_PARSE_ERROR_T* parse_error = ruby_parse_error_from_prism_error(error, (AST_NODE_T*) document, source, &parser);
|
|
1072
|
+
|
|
1073
|
+
// TODO: ideally this shouldn't be hard-coded
|
|
1074
|
+
if (strcmp(parse_error->diagnostic_id, "invalid_yield") == 0) {
|
|
1075
|
+
error_free((ERROR_T*) parse_error);
|
|
1076
|
+
} else {
|
|
1077
|
+
array_append(document->base.errors, parse_error);
|
|
1078
|
+
}
|
|
988
1079
|
}
|
|
1080
|
+
|
|
1081
|
+
pm_node_destroy(&parser, root);
|
|
1082
|
+
pm_parser_free(&parser);
|
|
1083
|
+
free(extracted_ruby);
|
|
989
1084
|
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
#ifndef HERB_ANALYZE_H
|
|
2
|
+
#define HERB_ANALYZE_H
|
|
3
|
+
|
|
4
|
+
#include "analyzed_ruby.h"
|
|
5
|
+
#include "array.h"
|
|
6
|
+
#include "ast_nodes.h"
|
|
7
|
+
|
|
8
|
+
typedef struct ANALYZE_RUBY_CONTEXT_STRUCT {
|
|
9
|
+
AST_DOCUMENT_NODE_T* document;
|
|
10
|
+
AST_NODE_T* parent;
|
|
11
|
+
array_T* ruby_context_stack;
|
|
12
|
+
} analyze_ruby_context_T;
|
|
13
|
+
|
|
14
|
+
typedef enum {
|
|
15
|
+
CONTROL_TYPE_IF,
|
|
16
|
+
CONTROL_TYPE_ELSIF,
|
|
17
|
+
CONTROL_TYPE_ELSE,
|
|
18
|
+
CONTROL_TYPE_END,
|
|
19
|
+
CONTROL_TYPE_CASE,
|
|
20
|
+
CONTROL_TYPE_CASE_MATCH,
|
|
21
|
+
CONTROL_TYPE_WHEN,
|
|
22
|
+
CONTROL_TYPE_IN,
|
|
23
|
+
CONTROL_TYPE_BEGIN,
|
|
24
|
+
CONTROL_TYPE_RESCUE,
|
|
25
|
+
CONTROL_TYPE_ENSURE,
|
|
26
|
+
CONTROL_TYPE_UNLESS,
|
|
27
|
+
CONTROL_TYPE_WHILE,
|
|
28
|
+
CONTROL_TYPE_UNTIL,
|
|
29
|
+
CONTROL_TYPE_FOR,
|
|
30
|
+
CONTROL_TYPE_BLOCK,
|
|
31
|
+
CONTROL_TYPE_BLOCK_CLOSE,
|
|
32
|
+
CONTROL_TYPE_YIELD,
|
|
33
|
+
CONTROL_TYPE_UNKNOWN
|
|
34
|
+
} control_type_t;
|
|
35
|
+
|
|
36
|
+
void herb_analyze_parse_errors(AST_DOCUMENT_NODE_T* document, const char* source);
|
|
37
|
+
void herb_analyze_parse_tree(AST_DOCUMENT_NODE_T* document, const char* source);
|
|
38
|
+
|
|
39
|
+
#endif
|
|
@@ -32,10 +32,18 @@ bool has_case_node(analyzed_ruby_T* analyzed) {
|
|
|
32
32
|
return analyzed->has_case_node;
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
+
bool has_case_match_node(analyzed_ruby_T* analyzed) {
|
|
36
|
+
return analyzed->has_case_match_node;
|
|
37
|
+
}
|
|
38
|
+
|
|
35
39
|
bool has_when_node(analyzed_ruby_T* analyzed) {
|
|
36
40
|
return analyzed->has_when_node;
|
|
37
41
|
}
|
|
38
42
|
|
|
43
|
+
bool has_in_node(analyzed_ruby_T* analyzed) {
|
|
44
|
+
return analyzed->has_in_node;
|
|
45
|
+
}
|
|
46
|
+
|
|
39
47
|
bool has_for_node(analyzed_ruby_T* analyzed) {
|
|
40
48
|
return analyzed->has_for_node;
|
|
41
49
|
}
|
|
@@ -64,6 +72,10 @@ bool has_unless_node(analyzed_ruby_T* analyzed) {
|
|
|
64
72
|
return analyzed->has_unless_node;
|
|
65
73
|
}
|
|
66
74
|
|
|
75
|
+
bool has_yield_node(analyzed_ruby_T* analyzed) {
|
|
76
|
+
return analyzed->has_yield_node;
|
|
77
|
+
}
|
|
78
|
+
|
|
67
79
|
bool has_error_message(analyzed_ruby_T* anlayzed, const char* message) {
|
|
68
80
|
for (const pm_diagnostic_t* error = (const pm_diagnostic_t*) anlayzed->parser.error_list.head; error != NULL;
|
|
69
81
|
error = (const pm_diagnostic_t*) error->node.next) {
|
|
@@ -102,7 +114,7 @@ bool search_block_nodes(const pm_node_t* node, void* data) {
|
|
|
102
114
|
bool search_case_nodes(const pm_node_t* node, void* data) {
|
|
103
115
|
analyzed_ruby_T* analyzed = (analyzed_ruby_T*) data;
|
|
104
116
|
|
|
105
|
-
if (node->type ==
|
|
117
|
+
if (node->type == PM_CASE_NODE) {
|
|
106
118
|
analyzed->has_case_node = true;
|
|
107
119
|
return true;
|
|
108
120
|
} else {
|
|
@@ -112,6 +124,19 @@ bool search_case_nodes(const pm_node_t* node, void* data) {
|
|
|
112
124
|
return false;
|
|
113
125
|
}
|
|
114
126
|
|
|
127
|
+
bool search_case_match_nodes(const pm_node_t* node, void* data) {
|
|
128
|
+
analyzed_ruby_T* analyzed = (analyzed_ruby_T*) data;
|
|
129
|
+
|
|
130
|
+
if (node->type == PM_CASE_MATCH_NODE) {
|
|
131
|
+
analyzed->has_case_match_node = true;
|
|
132
|
+
return true;
|
|
133
|
+
} else {
|
|
134
|
+
pm_visit_child_nodes(node, search_case_match_nodes, analyzed);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
|
|
115
140
|
bool search_while_nodes(const pm_node_t* node, void* data) {
|
|
116
141
|
analyzed_ruby_T* analyzed = (analyzed_ruby_T*) data;
|
|
117
142
|
|
|
@@ -222,6 +247,15 @@ bool search_when_nodes(analyzed_ruby_T* analyzed) {
|
|
|
222
247
|
return false;
|
|
223
248
|
}
|
|
224
249
|
|
|
250
|
+
bool search_in_nodes(analyzed_ruby_T* analyzed) {
|
|
251
|
+
if (has_error_message(analyzed, "unexpected 'in', ignoring it")) {
|
|
252
|
+
analyzed->has_in_node = true;
|
|
253
|
+
return true;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
return false;
|
|
257
|
+
}
|
|
258
|
+
|
|
225
259
|
bool search_rescue_nodes(analyzed_ruby_T* analyzed) {
|
|
226
260
|
if (has_error_message(analyzed, "unexpected 'rescue', ignoring it")) {
|
|
227
261
|
analyzed->has_rescue_node = true;
|
|
@@ -239,3 +273,12 @@ bool search_ensure_nodes(analyzed_ruby_T* analyzed) {
|
|
|
239
273
|
|
|
240
274
|
return false;
|
|
241
275
|
}
|
|
276
|
+
|
|
277
|
+
bool search_yield_nodes(analyzed_ruby_T* analyzed) {
|
|
278
|
+
if (has_error_message(analyzed, "Invalid yield")) {
|
|
279
|
+
analyzed->has_yield_node = true;
|
|
280
|
+
return true;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
return false;
|
|
284
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
#ifndef HERB_ANALYZE_HELPERS_H
|
|
2
|
+
#define HERB_ANALYZE_HELPERS_H
|
|
3
|
+
|
|
4
|
+
#include <prism.h>
|
|
5
|
+
#include <stdbool.h>
|
|
6
|
+
|
|
7
|
+
#include "analyzed_ruby.h"
|
|
8
|
+
|
|
9
|
+
bool has_if_node(analyzed_ruby_T* analyzed);
|
|
10
|
+
bool has_elsif_node(analyzed_ruby_T* analyzed);
|
|
11
|
+
bool has_else_node(analyzed_ruby_T* analyzed);
|
|
12
|
+
bool has_end(analyzed_ruby_T* analyzed);
|
|
13
|
+
bool has_block_node(analyzed_ruby_T* analyzed);
|
|
14
|
+
bool has_block_closing(analyzed_ruby_T* analyzed);
|
|
15
|
+
bool has_case_node(analyzed_ruby_T* analyzed);
|
|
16
|
+
bool has_case_match_node(analyzed_ruby_T* analyzed);
|
|
17
|
+
bool has_when_node(analyzed_ruby_T* analyzed);
|
|
18
|
+
bool has_in_node(analyzed_ruby_T* analyzed);
|
|
19
|
+
bool has_for_node(analyzed_ruby_T* analyzed);
|
|
20
|
+
bool has_while_node(analyzed_ruby_T* analyzed);
|
|
21
|
+
bool has_until_node(analyzed_ruby_T* analyzed);
|
|
22
|
+
bool has_begin_node(analyzed_ruby_T* analyzed);
|
|
23
|
+
bool has_rescue_node(analyzed_ruby_T* analyzed);
|
|
24
|
+
bool has_ensure_node(analyzed_ruby_T* analyzed);
|
|
25
|
+
bool has_unless_node(analyzed_ruby_T* analyzed);
|
|
26
|
+
bool has_yield_node(analyzed_ruby_T* analyzed);
|
|
27
|
+
|
|
28
|
+
bool has_error_message(analyzed_ruby_T* anlayzed, const char* message);
|
|
29
|
+
|
|
30
|
+
bool search_if_nodes(const pm_node_t* node, void* data);
|
|
31
|
+
bool search_block_nodes(const pm_node_t* node, void* data);
|
|
32
|
+
bool search_case_nodes(const pm_node_t* node, void* data);
|
|
33
|
+
bool search_case_match_nodes(const pm_node_t* node, void* data);
|
|
34
|
+
bool search_while_nodes(const pm_node_t* node, void* data);
|
|
35
|
+
bool search_for_nodes(const pm_node_t* node, void* data);
|
|
36
|
+
bool search_until_nodes(const pm_node_t* node, void* data);
|
|
37
|
+
bool search_begin_nodes(const pm_node_t* node, void* data);
|
|
38
|
+
bool search_unless_nodes(const pm_node_t* node, void* data);
|
|
39
|
+
bool search_elsif_nodes(analyzed_ruby_T* analyzed);
|
|
40
|
+
bool search_else_nodes(analyzed_ruby_T* analyzed);
|
|
41
|
+
bool search_end_nodes(analyzed_ruby_T* analyzed);
|
|
42
|
+
bool search_block_closing_nodes(analyzed_ruby_T* analyzed);
|
|
43
|
+
bool search_when_nodes(analyzed_ruby_T* analyzed);
|
|
44
|
+
bool search_in_nodes(analyzed_ruby_T* analyzed);
|
|
45
|
+
bool search_rescue_nodes(analyzed_ruby_T* analyzed);
|
|
46
|
+
bool search_ensure_nodes(analyzed_ruby_T* analyzed);
|
|
47
|
+
bool search_yield_nodes(analyzed_ruby_T* analyzed);
|
|
48
|
+
|
|
49
|
+
#endif
|
|
@@ -18,7 +18,9 @@ analyzed_ruby_T* init_analyzed_ruby(char* source) {
|
|
|
18
18
|
analyzed->has_block_node = false;
|
|
19
19
|
analyzed->has_block_closing = false;
|
|
20
20
|
analyzed->has_case_node = false;
|
|
21
|
+
analyzed->has_case_match_node = false;
|
|
21
22
|
analyzed->has_when_node = false;
|
|
23
|
+
analyzed->has_in_node = false;
|
|
22
24
|
analyzed->has_for_node = false;
|
|
23
25
|
analyzed->has_while_node = false;
|
|
24
26
|
analyzed->has_until_node = false;
|
|
@@ -26,10 +28,17 @@ analyzed_ruby_T* init_analyzed_ruby(char* source) {
|
|
|
26
28
|
analyzed->has_rescue_node = false;
|
|
27
29
|
analyzed->has_ensure_node = false;
|
|
28
30
|
analyzed->has_unless_node = false;
|
|
31
|
+
analyzed->has_yield_node = false;
|
|
29
32
|
|
|
30
33
|
return analyzed;
|
|
31
34
|
}
|
|
32
35
|
|
|
33
36
|
void free_analyzed_ruby(analyzed_ruby_T* analyzed) {
|
|
34
|
-
|
|
37
|
+
if (!analyzed) { return; }
|
|
38
|
+
|
|
39
|
+
if (analyzed->parsed && analyzed->root != NULL) { pm_node_destroy(&analyzed->parser, analyzed->root); }
|
|
40
|
+
|
|
41
|
+
pm_parser_free(&analyzed->parser);
|
|
42
|
+
|
|
43
|
+
free(analyzed);
|
|
35
44
|
}
|