herb 0.1.1-x86_64-linux-gnu → 0.2.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 (80) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +20 -2
  3. data/ext/herb/extension.c +46 -14
  4. data/ext/herb/extension.h +9 -0
  5. data/ext/herb/extension_helpers.c +9 -27
  6. data/ext/herb/nodes.c +104 -1
  7. data/herb.gemspec +2 -1
  8. data/lib/herb/3.0/herb.so +0 -0
  9. data/lib/herb/3.1/herb.so +0 -0
  10. data/lib/herb/3.2/herb.so +0 -0
  11. data/lib/herb/3.3/herb.so +0 -0
  12. data/lib/herb/3.4/herb.so +0 -0
  13. data/lib/herb/ast/node.rb +40 -4
  14. data/lib/herb/ast/nodes.rb +892 -140
  15. data/lib/herb/ast.rb +1 -0
  16. data/lib/herb/cli.rb +4 -1
  17. data/lib/herb/errors.rb +84 -33
  18. data/lib/herb/lex_result.rb +4 -1
  19. data/lib/herb/libherb/array.rb +3 -0
  20. data/lib/herb/libherb/ast_node.rb +3 -0
  21. data/lib/herb/libherb/buffer.rb +3 -0
  22. data/lib/herb/libherb/extract_result.rb +3 -0
  23. data/lib/herb/libherb/lex_result.rb +3 -0
  24. data/lib/herb/libherb/libherb.rb +3 -0
  25. data/lib/herb/libherb/parse_result.rb +3 -0
  26. data/lib/herb/libherb/token.rb +3 -0
  27. data/lib/herb/libherb.rb +3 -0
  28. data/lib/herb/location.rb +15 -6
  29. data/lib/herb/parse_result.rb +10 -1
  30. data/lib/herb/position.rb +17 -8
  31. data/lib/herb/project.rb +13 -4
  32. data/lib/herb/range.rb +18 -8
  33. data/lib/herb/result.rb +7 -1
  34. data/lib/herb/token.rb +14 -4
  35. data/lib/herb/token_list.rb +10 -1
  36. data/lib/herb/version.rb +2 -1
  37. data/lib/herb/visitor.rb +175 -0
  38. data/lib/herb/warnings.rb +43 -0
  39. data/lib/herb.rb +6 -1
  40. data/sig/herb/ast/node.rbs +48 -0
  41. data/sig/herb/ast/nodes.rbs +941 -0
  42. data/sig/herb/ast.rbs +6 -0
  43. data/sig/herb/errors.rbs +193 -0
  44. data/sig/herb/lex_result.rbs +16 -0
  45. data/sig/herb/location.rbs +30 -0
  46. data/sig/herb/parse_result.rbs +22 -0
  47. data/sig/herb/position.rbs +30 -0
  48. data/sig/herb/range.rbs +33 -0
  49. data/sig/herb/result.rbs +20 -0
  50. data/sig/herb/token.rbs +31 -0
  51. data/sig/herb/token_list.rbs +13 -0
  52. data/sig/herb/version.rbs +5 -0
  53. data/sig/herb/visitor.rbs +104 -0
  54. data/sig/herb/warnings.rbs +28 -0
  55. data/sig/herb.rbs +4 -0
  56. data/sig/serialized.rbs +9 -0
  57. data/sig/serialized_ast_errors.rbs +53 -0
  58. data/sig/serialized_ast_nodes.rbs +221 -0
  59. data/src/analyze.c +138 -43
  60. data/src/analyze_helpers.c +44 -1
  61. data/src/analyzed_ruby.c +10 -1
  62. data/src/ast_nodes.c +103 -1
  63. data/src/ast_pretty_print.c +60 -0
  64. data/src/buffer.c +60 -27
  65. data/src/extract.c +57 -20
  66. data/src/include/analyze.h +3 -0
  67. data/src/include/analyze_helpers.h +6 -0
  68. data/src/include/analyzed_ruby.h +3 -0
  69. data/src/include/ast_nodes.h +32 -0
  70. data/src/include/buffer.h +5 -2
  71. data/src/include/lexer_peek_helpers.h +2 -2
  72. data/src/include/macros.h +2 -2
  73. data/src/include/version.h +1 -1
  74. data/src/lexer.c +1 -1
  75. data/src/parser.c +17 -7
  76. data/src/token.c +1 -1
  77. data/src/util.c +3 -1
  78. data/src/visitor.c +36 -0
  79. metadata +24 -3
  80. /data/{License.txt → LICENSE.txt} +0 -0
@@ -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: return child_type == CONTROL_TYPE_WHEN || child_type == CONTROL_TYPE_ELSE;
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* non_when_children = array_init(8);
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(non_when_children, child);
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
- non_when_children,
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* non_when_children = array_init(8);
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(non_when_children, next_node);
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(non_when_children, next_node);
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 == CONTROL_TYPE_ELSE || child_type == CONTROL_TYPE_END) {
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(non_when_children, next_node);
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
- non_when_children,
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 (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) {
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
- free(temp_array);
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
- array_append(
985
- document->base.errors,
986
- ruby_parse_error_from_prism_error(error, (AST_NODE_T*) document, source, &parser)
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
  }
@@ -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 == PM_CASE_MATCH_NODE) {
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
+ }