herb 0.1.1 → 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.
- checksums.yaml +4 -4
- data/Rakefile +20 -2
- data/ext/herb/extension.c +46 -14
- data/ext/herb/extension.h +9 -0
- data/ext/herb/extension_helpers.c +9 -27
- data/ext/herb/nodes.c +104 -1
- data/herb.gemspec +2 -1
- data/lib/herb/ast/node.rb +40 -4
- data/lib/herb/ast/nodes.rb +892 -140
- data/lib/herb/ast.rb +1 -0
- data/lib/herb/cli.rb +4 -1
- data/lib/herb/errors.rb +84 -33
- data/lib/herb/lex_result.rb +4 -1
- data/lib/herb/libherb/array.rb +3 -0
- data/lib/herb/libherb/ast_node.rb +3 -0
- data/lib/herb/libherb/buffer.rb +3 -0
- data/lib/herb/libherb/extract_result.rb +3 -0
- data/lib/herb/libherb/lex_result.rb +3 -0
- data/lib/herb/libherb/libherb.rb +3 -0
- data/lib/herb/libherb/parse_result.rb +3 -0
- data/lib/herb/libherb/token.rb +3 -0
- data/lib/herb/libherb.rb +3 -0
- data/lib/herb/location.rb +15 -6
- data/lib/herb/parse_result.rb +10 -1
- data/lib/herb/position.rb +17 -8
- data/lib/herb/project.rb +13 -4
- data/lib/herb/range.rb +18 -8
- data/lib/herb/result.rb +7 -1
- data/lib/herb/token.rb +14 -4
- data/lib/herb/token_list.rb +10 -1
- data/lib/herb/version.rb +2 -1
- data/lib/herb/visitor.rb +175 -0
- data/lib/herb/warnings.rb +43 -0
- data/lib/herb.rb +6 -1
- data/sig/herb/ast/node.rbs +48 -0
- data/sig/herb/ast/nodes.rbs +941 -0
- data/sig/herb/ast.rbs +6 -0
- data/sig/herb/errors.rbs +193 -0
- data/sig/herb/lex_result.rbs +16 -0
- data/sig/herb/location.rbs +30 -0
- data/sig/herb/parse_result.rbs +22 -0
- data/sig/herb/position.rbs +30 -0
- data/sig/herb/range.rbs +33 -0
- data/sig/herb/result.rbs +20 -0
- data/sig/herb/token.rbs +31 -0
- data/sig/herb/token_list.rbs +13 -0
- data/sig/herb/version.rbs +5 -0
- data/sig/herb/visitor.rbs +104 -0
- data/sig/herb/warnings.rbs +28 -0
- data/sig/herb.rbs +4 -0
- data/sig/serialized.rbs +9 -0
- data/sig/serialized_ast_errors.rbs +53 -0
- data/sig/serialized_ast_nodes.rbs +221 -0
- data/src/analyze.c +138 -43
- data/src/analyze_helpers.c +44 -1
- data/src/analyzed_ruby.c +10 -1
- data/src/ast_nodes.c +103 -1
- data/src/ast_pretty_print.c +60 -0
- data/src/buffer.c +60 -27
- data/src/extract.c +57 -20
- data/src/include/analyze.h +3 -0
- data/src/include/analyze_helpers.h +6 -0
- data/src/include/analyzed_ruby.h +3 -0
- data/src/include/ast_nodes.h +32 -0
- data/src/include/buffer.h +5 -2
- data/src/include/lexer_peek_helpers.h +2 -2
- data/src/include/macros.h +2 -2
- data/src/include/version.h +1 -1
- data/src/lexer.c +1 -1
- data/src/parser.c +17 -7
- data/src/token.c +1 -1
- data/src/util.c +3 -1
- data/src/visitor.c +36 -0
- metadata +23 -2
@@ -0,0 +1,221 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# typed: true
|
3
|
+
|
4
|
+
# NOTE: This file is generated by the templates/template.rb script and should not be
|
5
|
+
# modified manually. See /Users/marcoroth/Development/herb-release/templates/sig/serialized_ast_nodes.rbs.erb
|
6
|
+
|
7
|
+
module Herb
|
8
|
+
type serialized_document_node = serialized_node & {
|
9
|
+
children: Array[Herb::AST::Node],
|
10
|
+
}
|
11
|
+
|
12
|
+
type serialized_literal_node = serialized_node & {
|
13
|
+
content: String,
|
14
|
+
}
|
15
|
+
|
16
|
+
type serialized_html_open_tag_node = serialized_node & {
|
17
|
+
tag_opening: Herb::Token,
|
18
|
+
tag_name: Herb::Token,
|
19
|
+
tag_closing: Herb::Token,
|
20
|
+
children: Array[Herb::AST::Node],
|
21
|
+
is_void: bool,
|
22
|
+
}
|
23
|
+
|
24
|
+
type serialized_html_close_tag_node = serialized_node & {
|
25
|
+
tag_opening: Herb::Token,
|
26
|
+
tag_name: Herb::Token,
|
27
|
+
tag_closing: Herb::Token,
|
28
|
+
}
|
29
|
+
|
30
|
+
type serialized_html_self_close_tag_node = serialized_node & {
|
31
|
+
tag_opening: Herb::Token,
|
32
|
+
tag_name: Herb::Token,
|
33
|
+
attributes: Array[Herb::AST::HTMLAttributeNode],
|
34
|
+
tag_closing: Herb::Token,
|
35
|
+
is_void: bool,
|
36
|
+
}
|
37
|
+
|
38
|
+
type serialized_html_element_node = serialized_node & {
|
39
|
+
open_tag: Herb::AST::HTMLOpenTagNode,
|
40
|
+
tag_name: Herb::Token,
|
41
|
+
body: Array[Herb::AST::Node],
|
42
|
+
close_tag: Herb::AST::HTMLCloseTagNode,
|
43
|
+
is_void: bool,
|
44
|
+
}
|
45
|
+
|
46
|
+
type serialized_html_attribute_value_node = serialized_node & {
|
47
|
+
open_quote: Herb::Token,
|
48
|
+
children: Array[Herb::AST::Node],
|
49
|
+
close_quote: Herb::Token,
|
50
|
+
quoted: bool,
|
51
|
+
}
|
52
|
+
|
53
|
+
type serialized_html_attribute_name_node = serialized_node & {
|
54
|
+
name: Herb::Token,
|
55
|
+
}
|
56
|
+
|
57
|
+
type serialized_html_attribute_node = serialized_node & {
|
58
|
+
name: Herb::AST::HTMLAttributeNameNode,
|
59
|
+
equals: Herb::Token,
|
60
|
+
value: Herb::AST::HTMLAttributeValueNode,
|
61
|
+
}
|
62
|
+
|
63
|
+
type serialized_html_text_node = serialized_node & {
|
64
|
+
content: String,
|
65
|
+
}
|
66
|
+
|
67
|
+
type serialized_html_comment_node = serialized_node & {
|
68
|
+
comment_start: Herb::Token,
|
69
|
+
children: Array[Herb::AST::Node],
|
70
|
+
comment_end: Herb::Token,
|
71
|
+
}
|
72
|
+
|
73
|
+
type serialized_html_doctype_node = serialized_node & {
|
74
|
+
tag_opening: Herb::Token,
|
75
|
+
children: Array[Herb::AST::Node],
|
76
|
+
tag_closing: Herb::Token,
|
77
|
+
}
|
78
|
+
|
79
|
+
type serialized_whitespace_node = serialized_node & {
|
80
|
+
value: Herb::Token,
|
81
|
+
}
|
82
|
+
|
83
|
+
type serialized_erb_content_node = serialized_node & {
|
84
|
+
tag_opening: Herb::Token,
|
85
|
+
content: Herb::Token,
|
86
|
+
tag_closing: Herb::Token,
|
87
|
+
analyzed_ruby: nil,
|
88
|
+
parsed: bool,
|
89
|
+
valid: bool,
|
90
|
+
}
|
91
|
+
|
92
|
+
type serialized_erb_end_node = serialized_node & {
|
93
|
+
tag_opening: Herb::Token,
|
94
|
+
content: Herb::Token,
|
95
|
+
tag_closing: Herb::Token,
|
96
|
+
}
|
97
|
+
|
98
|
+
type serialized_erb_else_node = serialized_node & {
|
99
|
+
tag_opening: Herb::Token,
|
100
|
+
content: Herb::Token,
|
101
|
+
tag_closing: Herb::Token,
|
102
|
+
statements: Array[Herb::AST::Node],
|
103
|
+
}
|
104
|
+
|
105
|
+
type serialized_erb_if_node = serialized_node & {
|
106
|
+
tag_opening: Herb::Token,
|
107
|
+
content: Herb::Token,
|
108
|
+
tag_closing: Herb::Token,
|
109
|
+
statements: Array[Herb::AST::Node],
|
110
|
+
subsequent: Herb::AST::Node,
|
111
|
+
end_node: Herb::AST::ERBEndNode,
|
112
|
+
}
|
113
|
+
|
114
|
+
type serialized_erb_block_node = serialized_node & {
|
115
|
+
tag_opening: Herb::Token,
|
116
|
+
content: Herb::Token,
|
117
|
+
tag_closing: Herb::Token,
|
118
|
+
body: Array[Herb::AST::Node],
|
119
|
+
end_node: Herb::AST::ERBEndNode,
|
120
|
+
}
|
121
|
+
|
122
|
+
type serialized_erb_when_node = serialized_node & {
|
123
|
+
tag_opening: Herb::Token,
|
124
|
+
content: Herb::Token,
|
125
|
+
tag_closing: Herb::Token,
|
126
|
+
statements: Array[Herb::AST::Node],
|
127
|
+
}
|
128
|
+
|
129
|
+
type serialized_erb_case_node = serialized_node & {
|
130
|
+
tag_opening: Herb::Token,
|
131
|
+
content: Herb::Token,
|
132
|
+
tag_closing: Herb::Token,
|
133
|
+
children: Array[Herb::AST::Node],
|
134
|
+
conditions: Array[Herb::AST::ERBWhenNode],
|
135
|
+
else_clause: Herb::AST::ERBElseNode,
|
136
|
+
end_node: Herb::AST::ERBEndNode,
|
137
|
+
}
|
138
|
+
|
139
|
+
type serialized_erb_case_match_node = serialized_node & {
|
140
|
+
tag_opening: Herb::Token,
|
141
|
+
content: Herb::Token,
|
142
|
+
tag_closing: Herb::Token,
|
143
|
+
children: Array[Herb::AST::Node],
|
144
|
+
conditions: Array[Herb::AST::ERBInNode],
|
145
|
+
else_clause: Herb::AST::ERBElseNode,
|
146
|
+
end_node: Herb::AST::ERBEndNode,
|
147
|
+
}
|
148
|
+
|
149
|
+
type serialized_erb_while_node = serialized_node & {
|
150
|
+
tag_opening: Herb::Token,
|
151
|
+
content: Herb::Token,
|
152
|
+
tag_closing: Herb::Token,
|
153
|
+
statements: Array[Herb::AST::Node],
|
154
|
+
end_node: Herb::AST::ERBEndNode,
|
155
|
+
}
|
156
|
+
|
157
|
+
type serialized_erb_until_node = serialized_node & {
|
158
|
+
tag_opening: Herb::Token,
|
159
|
+
content: Herb::Token,
|
160
|
+
tag_closing: Herb::Token,
|
161
|
+
statements: Array[Herb::AST::Node],
|
162
|
+
end_node: Herb::AST::ERBEndNode,
|
163
|
+
}
|
164
|
+
|
165
|
+
type serialized_erb_for_node = serialized_node & {
|
166
|
+
tag_opening: Herb::Token,
|
167
|
+
content: Herb::Token,
|
168
|
+
tag_closing: Herb::Token,
|
169
|
+
statements: Array[Herb::AST::Node],
|
170
|
+
end_node: Herb::AST::ERBEndNode,
|
171
|
+
}
|
172
|
+
|
173
|
+
type serialized_erb_rescue_node = serialized_node & {
|
174
|
+
tag_opening: Herb::Token,
|
175
|
+
content: Herb::Token,
|
176
|
+
tag_closing: Herb::Token,
|
177
|
+
statements: Array[Herb::AST::Node],
|
178
|
+
subsequent: Herb::AST::ERBRescueNode,
|
179
|
+
}
|
180
|
+
|
181
|
+
type serialized_erb_ensure_node = serialized_node & {
|
182
|
+
tag_opening: Herb::Token,
|
183
|
+
content: Herb::Token,
|
184
|
+
tag_closing: Herb::Token,
|
185
|
+
statements: Array[Herb::AST::Node],
|
186
|
+
}
|
187
|
+
|
188
|
+
type serialized_erb_begin_node = serialized_node & {
|
189
|
+
tag_opening: Herb::Token,
|
190
|
+
content: Herb::Token,
|
191
|
+
tag_closing: Herb::Token,
|
192
|
+
statements: Array[Herb::AST::Node],
|
193
|
+
rescue_clause: Herb::AST::ERBRescueNode,
|
194
|
+
else_clause: Herb::AST::ERBElseNode,
|
195
|
+
ensure_clause: Herb::AST::ERBEnsureNode,
|
196
|
+
end_node: Herb::AST::ERBEndNode,
|
197
|
+
}
|
198
|
+
|
199
|
+
type serialized_erb_unless_node = serialized_node & {
|
200
|
+
tag_opening: Herb::Token,
|
201
|
+
content: Herb::Token,
|
202
|
+
tag_closing: Herb::Token,
|
203
|
+
statements: Array[Herb::AST::Node],
|
204
|
+
else_clause: Herb::AST::ERBElseNode,
|
205
|
+
end_node: Herb::AST::ERBEndNode,
|
206
|
+
}
|
207
|
+
|
208
|
+
type serialized_erb_yield_node = serialized_node & {
|
209
|
+
tag_opening: Herb::Token,
|
210
|
+
content: Herb::Token,
|
211
|
+
tag_closing: Herb::Token,
|
212
|
+
}
|
213
|
+
|
214
|
+
type serialized_erb_in_node = serialized_node & {
|
215
|
+
tag_opening: Herb::Token,
|
216
|
+
content: Herb::Token,
|
217
|
+
tag_closing: Herb::Token,
|
218
|
+
statements: Array[Herb::AST::Node],
|
219
|
+
}
|
220
|
+
|
221
|
+
end
|
data/src/analyze.c
CHANGED
@@ -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
|
}
|
data/src/analyze_helpers.c
CHANGED
@@ -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
|
+
}
|