herb 0.8.0 → 0.8.3
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/README.md +6 -1
- data/ext/herb/extconf.rb +1 -1
- data/ext/herb/extension.c +1 -1
- data/ext/herb/nodes.c +1 -1
- data/lib/herb/engine/error_formatter.rb +13 -5
- data/lib/herb/version.rb +1 -1
- data/src/analyze.c +184 -46
- data/src/analyze_helpers.c +12 -4
- data/src/ast_node.c +29 -0
- data/src/extract.c +5 -71
- data/src/herb.c +7 -7
- data/src/include/ast_node.h +2 -0
- data/src/include/extract.h +0 -1
- data/src/include/herb.h +8 -7
- data/src/include/macros.h +8 -0
- data/src/include/position.h +5 -0
- data/src/include/prism_helpers.h +5 -1
- data/src/include/util/hb_buffer.h +3 -1
- data/src/include/version.h +1 -1
- data/src/parser.c +5 -7
- data/src/position.c +27 -0
- data/src/prism_helpers.c +14 -15
- data/src/util/hb_buffer.c +18 -10
- data/src/util/hb_system.c +3 -3
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a3908d930c973519ff35367b6e870825d296b040fc7b24d063cac2063bc3f444
|
|
4
|
+
data.tar.gz: 8088699f58e77d1bce02721b1808840bac6b9799e8e8ea1c978abf967c4e371a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 0a5702c77e7e99b6c8d9d27af6d970d0fda38e8391e14b778f6a25e2f76b17099ccbec7370263fd78ab1f6dd8446fd4820eaa625013650b1530222847672b4f8
|
|
7
|
+
data.tar.gz: fae00c8ed586d665d32f4133a1e12f4a44535e276f3924dd60524d549aa7904a65d3093994d07bae2e59b9a146bcc9638a14d8f6dcd7cb4620fa821b394b487f
|
data/README.md
CHANGED
|
@@ -10,8 +10,13 @@
|
|
|
10
10
|
|
|
11
11
|
<p align="center">
|
|
12
12
|
<a href="https://rubygems.org/gems/herb"><img alt="Gem Version" src="https://img.shields.io/gem/v/herb"></a>
|
|
13
|
+
<a href="https://crates.io/crates/herb"><img alt="Crates.io Version" src="https://img.shields.io/crates/v/herb"></a>
|
|
14
|
+
<a href="https://www.npmjs.com/package/@herb-tools/core"><img alt="npm Version" src="https://img.shields.io/npm/v/@herb-tools/core"></a>
|
|
15
|
+
<a href="https://marketplace.visualstudio.com/items?itemName=marcoroth.herb-lsp"><img alt="VS Code Marketplace" src="https://img.shields.io/visual-studio-marketplace/v/marcoroth.herb-lsp"></a>
|
|
16
|
+
<a href="https://open-vsx.org/extension/marcoroth/herb-lsp"><img alt="Open VSX" src="https://img.shields.io/open-vsx/v/marcoroth/herb-lsp"></a>
|
|
13
17
|
<a href="https://herb-tools.dev"><img alt="Documentation" src="https://img.shields.io/badge/documentation-available-green"></a>
|
|
14
18
|
<a href="https://herb-tools.dev/playground"><img alt="playground" src="https://img.shields.io/badge/playground-Try_it_in_the_browser!-green"></a>
|
|
19
|
+
<a href="https://pkg.pr.new/~/marcoroth/herb"><img alt="pkg.pr.new" src="https://pkg.pr.new/badge/marcoroth/herb"></a>
|
|
15
20
|
<a href="https://github.com/marcoroth/herb/blob/main/LICENSE.txt"><img alt="License" src="https://img.shields.io/github/license/marcoroth/herb"></a>
|
|
16
21
|
<a href="https://github.com/marcoroth/herb/issues"><img alt="Issues" src="https://img.shields.io/github/issues/marcoroth/herb"></a>
|
|
17
22
|
</p>
|
|
@@ -99,7 +104,7 @@ Commands:
|
|
|
99
104
|
bundle exec herb version Prints the versions of the Herb gem and the libherb library.
|
|
100
105
|
```
|
|
101
106
|
|
|
102
|
-
For detailed information, like how you can use Herb
|
|
107
|
+
For detailed information, like how you can use Herb programmatically in Ruby and JavaScript, visit the [documentation site](https://herb-tools.dev/bindings/ruby/reference).
|
|
103
108
|
|
|
104
109
|
|
|
105
110
|
## Background and Talk
|
data/ext/herb/extconf.rb
CHANGED
|
@@ -24,7 +24,7 @@ $INCFLAGS << " -I#{include_path}"
|
|
|
24
24
|
$INCFLAGS << " -I#{prism_src_path}"
|
|
25
25
|
$INCFLAGS << " -I#{prism_src_path}/util"
|
|
26
26
|
|
|
27
|
-
$CFLAGS << " -
|
|
27
|
+
$CFLAGS << " -fvisibility=hidden"
|
|
28
28
|
|
|
29
29
|
herb_src_files = Dir.glob("#{$srcdir}/../../src/**/*.c").map { |file| file.delete_prefix("../../../../ext/herb/") }.sort
|
|
30
30
|
|
data/ext/herb/extension.c
CHANGED
|
@@ -124,7 +124,7 @@ static VALUE Herb_version(VALUE self) {
|
|
|
124
124
|
return rb_funcall(rb_mKernel, rb_intern("sprintf"), 4, format_string, gem_version, libprism_version, libherb_version);
|
|
125
125
|
}
|
|
126
126
|
|
|
127
|
-
void Init_herb(void) {
|
|
127
|
+
__attribute__((__visibility__("default"))) void Init_herb(void) {
|
|
128
128
|
mHerb = rb_define_module("Herb");
|
|
129
129
|
cPosition = rb_define_class_under(mHerb, "Position", rb_cObject);
|
|
130
130
|
cLocation = rb_define_class_under(mHerb, "Location", rb_cObject);
|
data/ext/herb/nodes.c
CHANGED
|
@@ -464,7 +464,7 @@ static VALUE rb_erb_content_node_from_c_struct(AST_ERB_CONTENT_NODE_T* erb_conte
|
|
|
464
464
|
VALUE erb_content_node_tag_opening = rb_token_from_c_struct(erb_content_node->tag_opening);
|
|
465
465
|
VALUE erb_content_node_content = rb_token_from_c_struct(erb_content_node->content);
|
|
466
466
|
VALUE erb_content_node_tag_closing = rb_token_from_c_struct(erb_content_node->tag_closing);
|
|
467
|
-
/* #<Herb::Template::AnalyzedRubyField:
|
|
467
|
+
/* #<Herb::Template::AnalyzedRubyField:0x00007f1c2e6c7a68 @name="analyzed_ruby", @options={kind: nil}> */
|
|
468
468
|
VALUE erb_content_node_analyzed_ruby = Qnil;
|
|
469
469
|
VALUE erb_content_node_parsed = (erb_content_node->parsed) ? Qtrue : Qfalse;
|
|
470
470
|
VALUE erb_content_node_valid = (erb_content_node->valid) ? Qtrue : Qfalse;
|
|
@@ -98,16 +98,24 @@ module Herb
|
|
|
98
98
|
def format_error(error, number)
|
|
99
99
|
output = String.new
|
|
100
100
|
|
|
101
|
-
|
|
101
|
+
error_name = if error.is_a?(Hash)
|
|
102
|
+
error[:code] || "UnknownError"
|
|
103
|
+
else
|
|
104
|
+
error.class.name.split("::").last.gsub(/Error$/, "")
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
output << "Error ##{number}: #{error_name}\n"
|
|
102
108
|
output << ("-" * 40) << "\n"
|
|
103
109
|
|
|
104
|
-
|
|
110
|
+
location = error.is_a?(Hash) ? error[:location] : error.location
|
|
111
|
+
if location
|
|
105
112
|
output << " File: #{@filename}\n"
|
|
106
|
-
output << " Location: Line #{
|
|
113
|
+
output << " Location: Line #{location.start.line}, Column #{location.start.column}\n"
|
|
107
114
|
end
|
|
108
115
|
|
|
109
|
-
|
|
110
|
-
output <<
|
|
116
|
+
error_message = error.is_a?(Hash) ? error[:message] : error.message
|
|
117
|
+
output << " Message: #{error_message}\n\n"
|
|
118
|
+
output << format_source_context(error) if location
|
|
111
119
|
output << format_error_details(error)
|
|
112
120
|
|
|
113
121
|
output
|
data/lib/herb/version.rb
CHANGED
data/src/analyze.c
CHANGED
|
@@ -90,6 +90,138 @@ static size_t process_subsequent_block(
|
|
|
90
90
|
control_type_t parent_type
|
|
91
91
|
);
|
|
92
92
|
|
|
93
|
+
typedef struct {
|
|
94
|
+
control_type_t type;
|
|
95
|
+
uint32_t offset;
|
|
96
|
+
bool found;
|
|
97
|
+
} earliest_control_keyword_t;
|
|
98
|
+
|
|
99
|
+
typedef struct {
|
|
100
|
+
earliest_control_keyword_t* result;
|
|
101
|
+
const uint8_t* source_start;
|
|
102
|
+
} location_walker_context_t;
|
|
103
|
+
|
|
104
|
+
static bool find_earliest_control_keyword_walker(const pm_node_t* node, void* data) {
|
|
105
|
+
if (!node) { return true; }
|
|
106
|
+
|
|
107
|
+
location_walker_context_t* context = (location_walker_context_t*) data;
|
|
108
|
+
earliest_control_keyword_t* result = context->result;
|
|
109
|
+
|
|
110
|
+
control_type_t current_type = CONTROL_TYPE_UNKNOWN;
|
|
111
|
+
uint32_t keyword_offset = UINT32_MAX;
|
|
112
|
+
|
|
113
|
+
switch (node->type) {
|
|
114
|
+
case PM_IF_NODE: {
|
|
115
|
+
pm_if_node_t* if_node = (pm_if_node_t*) node;
|
|
116
|
+
current_type = CONTROL_TYPE_IF;
|
|
117
|
+
keyword_offset = (uint32_t) (if_node->if_keyword_loc.start - context->source_start);
|
|
118
|
+
break;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
case PM_UNLESS_NODE: {
|
|
122
|
+
pm_unless_node_t* unless_node = (pm_unless_node_t*) node;
|
|
123
|
+
current_type = CONTROL_TYPE_UNLESS;
|
|
124
|
+
keyword_offset = (uint32_t) (unless_node->keyword_loc.start - context->source_start);
|
|
125
|
+
break;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
case PM_CASE_NODE: {
|
|
129
|
+
pm_case_node_t* case_node = (pm_case_node_t*) node;
|
|
130
|
+
current_type = CONTROL_TYPE_CASE;
|
|
131
|
+
keyword_offset = (uint32_t) (case_node->case_keyword_loc.start - context->source_start);
|
|
132
|
+
break;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
case PM_CASE_MATCH_NODE: {
|
|
136
|
+
pm_case_match_node_t* case_match_node = (pm_case_match_node_t*) node;
|
|
137
|
+
current_type = CONTROL_TYPE_CASE_MATCH;
|
|
138
|
+
keyword_offset = (uint32_t) (case_match_node->case_keyword_loc.start - context->source_start);
|
|
139
|
+
break;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
case PM_WHILE_NODE: {
|
|
143
|
+
pm_while_node_t* while_node = (pm_while_node_t*) node;
|
|
144
|
+
current_type = CONTROL_TYPE_WHILE;
|
|
145
|
+
keyword_offset = (uint32_t) (while_node->keyword_loc.start - context->source_start);
|
|
146
|
+
break;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
case PM_UNTIL_NODE: {
|
|
150
|
+
pm_until_node_t* until_node = (pm_until_node_t*) node;
|
|
151
|
+
current_type = CONTROL_TYPE_UNTIL;
|
|
152
|
+
keyword_offset = (uint32_t) (until_node->keyword_loc.start - context->source_start);
|
|
153
|
+
break;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
case PM_FOR_NODE: {
|
|
157
|
+
pm_for_node_t* for_node = (pm_for_node_t*) node;
|
|
158
|
+
current_type = CONTROL_TYPE_FOR;
|
|
159
|
+
keyword_offset = (uint32_t) (for_node->for_keyword_loc.start - context->source_start);
|
|
160
|
+
break;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
case PM_BEGIN_NODE: {
|
|
164
|
+
pm_begin_node_t* begin_node = (pm_begin_node_t*) node;
|
|
165
|
+
current_type = CONTROL_TYPE_BEGIN;
|
|
166
|
+
|
|
167
|
+
if (begin_node->begin_keyword_loc.start != NULL) {
|
|
168
|
+
keyword_offset = (uint32_t) (begin_node->begin_keyword_loc.start - context->source_start);
|
|
169
|
+
} else {
|
|
170
|
+
keyword_offset = (uint32_t) (node->location.start - context->source_start);
|
|
171
|
+
}
|
|
172
|
+
break;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
case PM_YIELD_NODE: {
|
|
176
|
+
current_type = CONTROL_TYPE_YIELD;
|
|
177
|
+
keyword_offset = (uint32_t) (node->location.start - context->source_start);
|
|
178
|
+
break;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
case PM_CALL_NODE: {
|
|
182
|
+
pm_call_node_t* call = (pm_call_node_t*) node;
|
|
183
|
+
|
|
184
|
+
if (call->block != NULL) {
|
|
185
|
+
current_type = CONTROL_TYPE_BLOCK;
|
|
186
|
+
keyword_offset = (uint32_t) (node->location.start - context->source_start);
|
|
187
|
+
}
|
|
188
|
+
break;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
case PM_NEXT_NODE:
|
|
192
|
+
case PM_BREAK_NODE:
|
|
193
|
+
case PM_RETURN_NODE: {
|
|
194
|
+
current_type = CONTROL_TYPE_UNKNOWN;
|
|
195
|
+
keyword_offset = (uint32_t) (node->location.start - context->source_start);
|
|
196
|
+
break;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
default: break;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
if (keyword_offset != UINT32_MAX) {
|
|
203
|
+
if (!result->found || keyword_offset < result->offset) {
|
|
204
|
+
result->type = current_type;
|
|
205
|
+
result->offset = keyword_offset;
|
|
206
|
+
result->found = true;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
return true;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
static control_type_t find_earliest_control_keyword(pm_node_t* root, const uint8_t* source_start) {
|
|
214
|
+
if (!root) { return CONTROL_TYPE_UNKNOWN; }
|
|
215
|
+
|
|
216
|
+
earliest_control_keyword_t result = { .type = CONTROL_TYPE_UNKNOWN, .offset = UINT32_MAX, .found = false };
|
|
217
|
+
|
|
218
|
+
location_walker_context_t context = { .result = &result, .source_start = source_start };
|
|
219
|
+
|
|
220
|
+
pm_visit_node(root, find_earliest_control_keyword_walker, &context);
|
|
221
|
+
|
|
222
|
+
return result.found ? result.type : CONTROL_TYPE_UNKNOWN;
|
|
223
|
+
}
|
|
224
|
+
|
|
93
225
|
static control_type_t detect_control_type(AST_ERB_CONTENT_NODE_T* erb_node) {
|
|
94
226
|
if (!erb_node || erb_node->base.type != AST_ERB_CONTENT_NODE) { return CONTROL_TYPE_UNKNOWN; }
|
|
95
227
|
|
|
@@ -99,26 +231,18 @@ static control_type_t detect_control_type(AST_ERB_CONTENT_NODE_T* erb_node) {
|
|
|
99
231
|
|
|
100
232
|
if (ruby->valid) { return CONTROL_TYPE_UNKNOWN; }
|
|
101
233
|
|
|
102
|
-
|
|
103
|
-
|
|
234
|
+
pm_node_t* root = ruby->root;
|
|
235
|
+
|
|
104
236
|
if (has_elsif_node(ruby)) { return CONTROL_TYPE_ELSIF; }
|
|
105
237
|
if (has_else_node(ruby)) { return CONTROL_TYPE_ELSE; }
|
|
106
238
|
if (has_end(ruby)) { return CONTROL_TYPE_END; }
|
|
107
|
-
if (has_case_node(ruby)) { return CONTROL_TYPE_CASE; }
|
|
108
|
-
if (has_case_match_node(ruby)) { return CONTROL_TYPE_CASE_MATCH; }
|
|
109
239
|
if (has_when_node(ruby)) { return CONTROL_TYPE_WHEN; }
|
|
110
240
|
if (has_in_node(ruby)) { return CONTROL_TYPE_IN; }
|
|
111
|
-
if (has_begin_node(ruby)) { return CONTROL_TYPE_BEGIN; }
|
|
112
241
|
if (has_rescue_node(ruby)) { return CONTROL_TYPE_RESCUE; }
|
|
113
242
|
if (has_ensure_node(ruby)) { return CONTROL_TYPE_ENSURE; }
|
|
114
|
-
if (has_unless_node(ruby)) { return CONTROL_TYPE_UNLESS; }
|
|
115
|
-
if (has_while_node(ruby)) { return CONTROL_TYPE_WHILE; }
|
|
116
|
-
if (has_until_node(ruby)) { return CONTROL_TYPE_UNTIL; }
|
|
117
|
-
if (has_for_node(ruby)) { return CONTROL_TYPE_FOR; }
|
|
118
243
|
if (has_block_closing(ruby)) { return CONTROL_TYPE_BLOCK_CLOSE; }
|
|
119
|
-
if (has_yield_node(ruby)) { return CONTROL_TYPE_YIELD; }
|
|
120
244
|
|
|
121
|
-
return
|
|
245
|
+
return find_earliest_control_keyword(root, ruby->parser.start);
|
|
122
246
|
}
|
|
123
247
|
|
|
124
248
|
static bool is_subsequent_type(control_type_t parent_type, control_type_t child_type) {
|
|
@@ -164,7 +288,7 @@ static AST_NODE_T* create_control_node(
|
|
|
164
288
|
if (end_node) {
|
|
165
289
|
end_position = end_node->base.location.end;
|
|
166
290
|
} else if (children && hb_array_size(children) > 0) {
|
|
167
|
-
AST_NODE_T* last_child =
|
|
291
|
+
AST_NODE_T* last_child = hb_array_last(children);
|
|
168
292
|
end_position = last_child->location.end;
|
|
169
293
|
} else if (subsequent) {
|
|
170
294
|
end_position = subsequent->location.end;
|
|
@@ -515,21 +639,7 @@ static size_t process_control_structure(
|
|
|
515
639
|
|
|
516
640
|
index++;
|
|
517
641
|
|
|
518
|
-
|
|
519
|
-
AST_NODE_T* child = hb_array_get(array, index);
|
|
520
|
-
|
|
521
|
-
if (!child) { break; }
|
|
522
|
-
|
|
523
|
-
if (child->type == AST_ERB_CONTENT_NODE) {
|
|
524
|
-
AST_ERB_CONTENT_NODE_T* child_erb = (AST_ERB_CONTENT_NODE_T*) child;
|
|
525
|
-
control_type_t child_type = detect_control_type(child_erb);
|
|
526
|
-
|
|
527
|
-
if (child_type == CONTROL_TYPE_END) { break; }
|
|
528
|
-
}
|
|
529
|
-
|
|
530
|
-
hb_array_append(else_children, child);
|
|
531
|
-
index++;
|
|
532
|
-
}
|
|
642
|
+
index = process_block_children(node, array, index, else_children, context, initial_type);
|
|
533
643
|
|
|
534
644
|
hb_array_T* else_errors = next_erb->base.errors;
|
|
535
645
|
next_erb->base.errors = NULL;
|
|
@@ -585,10 +695,10 @@ static size_t process_control_structure(
|
|
|
585
695
|
} else if (else_clause) {
|
|
586
696
|
end_position = else_clause->base.location.end;
|
|
587
697
|
} else if (hb_array_size(when_conditions) > 0) {
|
|
588
|
-
AST_NODE_T* last_when =
|
|
698
|
+
AST_NODE_T* last_when = hb_array_last(when_conditions);
|
|
589
699
|
end_position = last_when->location.end;
|
|
590
700
|
} else if (hb_array_size(in_conditions) > 0) {
|
|
591
|
-
AST_NODE_T* last_in =
|
|
701
|
+
AST_NODE_T* last_in = hb_array_last(in_conditions);
|
|
592
702
|
end_position = last_in->location.end;
|
|
593
703
|
}
|
|
594
704
|
|
|
@@ -677,21 +787,7 @@ static size_t process_control_structure(
|
|
|
677
787
|
|
|
678
788
|
index++;
|
|
679
789
|
|
|
680
|
-
|
|
681
|
-
AST_NODE_T* child = hb_array_get(array, index);
|
|
682
|
-
|
|
683
|
-
if (!child) { break; }
|
|
684
|
-
|
|
685
|
-
if (child->type == AST_ERB_CONTENT_NODE) {
|
|
686
|
-
AST_ERB_CONTENT_NODE_T* child_erb = (AST_ERB_CONTENT_NODE_T*) child;
|
|
687
|
-
control_type_t child_type = detect_control_type(child_erb);
|
|
688
|
-
|
|
689
|
-
if (child_type == CONTROL_TYPE_ENSURE || child_type == CONTROL_TYPE_END) { break; }
|
|
690
|
-
}
|
|
691
|
-
|
|
692
|
-
hb_array_append(else_children, child);
|
|
693
|
-
index++;
|
|
694
|
-
}
|
|
790
|
+
index = process_block_children(node, array, index, else_children, context, initial_type);
|
|
695
791
|
|
|
696
792
|
hb_array_T* else_errors = next_erb->base.errors;
|
|
697
793
|
next_erb->base.errors = NULL;
|
|
@@ -859,7 +955,7 @@ static size_t process_control_structure(
|
|
|
859
955
|
if (end_node) {
|
|
860
956
|
end_position = end_node->base.location.end;
|
|
861
957
|
} else if (children && hb_array_size(children) > 0) {
|
|
862
|
-
AST_NODE_T* last_child =
|
|
958
|
+
AST_NODE_T* last_child = hb_array_last(children);
|
|
863
959
|
end_position = last_child->location.end;
|
|
864
960
|
}
|
|
865
961
|
|
|
@@ -1043,7 +1139,7 @@ static size_t process_block_children(
|
|
|
1043
1139
|
hb_array_T* temp_array = hb_array_init(1);
|
|
1044
1140
|
size_t new_index = process_control_structure(node, array, index, temp_array, context, child_type);
|
|
1045
1141
|
|
|
1046
|
-
if (hb_array_size(temp_array) > 0) { hb_array_append(children_array,
|
|
1142
|
+
if (hb_array_size(temp_array) > 0) { hb_array_append(children_array, hb_array_first(temp_array)); }
|
|
1047
1143
|
|
|
1048
1144
|
hb_array_free(&temp_array);
|
|
1049
1145
|
|
|
@@ -1313,6 +1409,35 @@ void herb_analyze_parse_tree(AST_DOCUMENT_NODE_T* document, const char* source)
|
|
|
1313
1409
|
free(invalid_context);
|
|
1314
1410
|
}
|
|
1315
1411
|
|
|
1412
|
+
static void parse_erb_content_errors(AST_NODE_T* erb_node, const char* source) {
|
|
1413
|
+
if (!erb_node || erb_node->type != AST_ERB_CONTENT_NODE) { return; }
|
|
1414
|
+
AST_ERB_CONTENT_NODE_T* content_node = (AST_ERB_CONTENT_NODE_T*) erb_node;
|
|
1415
|
+
|
|
1416
|
+
if (!content_node->content || !content_node->content->value) { return; }
|
|
1417
|
+
|
|
1418
|
+
const char* content = content_node->content->value;
|
|
1419
|
+
if (strlen(content) == 0) { return; }
|
|
1420
|
+
|
|
1421
|
+
pm_parser_t parser;
|
|
1422
|
+
pm_options_t options = { 0, .partial_script = true };
|
|
1423
|
+
pm_parser_init(&parser, (const uint8_t*) content, strlen(content), &options);
|
|
1424
|
+
|
|
1425
|
+
pm_node_t* root = pm_parse(&parser);
|
|
1426
|
+
|
|
1427
|
+
const pm_diagnostic_t* error = (const pm_diagnostic_t*) parser.error_list.head;
|
|
1428
|
+
|
|
1429
|
+
if (error != NULL) {
|
|
1430
|
+
RUBY_PARSE_ERROR_T* parse_error =
|
|
1431
|
+
ruby_parse_error_from_prism_error_with_positions(error, erb_node->location.start, erb_node->location.end);
|
|
1432
|
+
|
|
1433
|
+
hb_array_append(erb_node->errors, parse_error);
|
|
1434
|
+
}
|
|
1435
|
+
|
|
1436
|
+
pm_node_destroy(&parser, root);
|
|
1437
|
+
pm_parser_free(&parser);
|
|
1438
|
+
pm_options_free(&options);
|
|
1439
|
+
}
|
|
1440
|
+
|
|
1316
1441
|
void herb_analyze_parse_errors(AST_DOCUMENT_NODE_T* document, const char* source) {
|
|
1317
1442
|
char* extracted_ruby = herb_extract_ruby_with_semicolons(source);
|
|
1318
1443
|
|
|
@@ -1326,6 +1451,19 @@ void herb_analyze_parse_errors(AST_DOCUMENT_NODE_T* document, const char* source
|
|
|
1326
1451
|
|
|
1327
1452
|
for (const pm_diagnostic_t* error = (const pm_diagnostic_t*) parser.error_list.head; error != NULL;
|
|
1328
1453
|
error = (const pm_diagnostic_t*) error->node.next) {
|
|
1454
|
+
size_t error_offset = (size_t) (error->location.start - parser.start);
|
|
1455
|
+
|
|
1456
|
+
if (strstr(error->message, "unexpected ';'") != NULL) {
|
|
1457
|
+
if (error_offset < strlen(extracted_ruby) && extracted_ruby[error_offset] == ';') {
|
|
1458
|
+
if (error_offset >= strlen(source) || source[error_offset] != ';') {
|
|
1459
|
+
AST_NODE_T* erb_node = find_erb_content_at_offset(document, source, error_offset);
|
|
1460
|
+
|
|
1461
|
+
if (erb_node) { parse_erb_content_errors(erb_node, source); }
|
|
1462
|
+
|
|
1463
|
+
continue;
|
|
1464
|
+
}
|
|
1465
|
+
}
|
|
1466
|
+
}
|
|
1329
1467
|
|
|
1330
1468
|
RUBY_PARSE_ERROR_T* parse_error = ruby_parse_error_from_prism_error(error, (AST_NODE_T*) document, source, &parser);
|
|
1331
1469
|
hb_array_append(document->base.errors, parse_error);
|
data/src/analyze_helpers.c
CHANGED
|
@@ -110,12 +110,20 @@ bool search_block_nodes(const pm_node_t* node, void* data) {
|
|
|
110
110
|
analyzed_ruby_T* analyzed = (analyzed_ruby_T*) data;
|
|
111
111
|
|
|
112
112
|
if (node->type == PM_BLOCK_NODE) {
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
113
|
+
pm_block_node_t* block_node = (pm_block_node_t*) node;
|
|
114
|
+
|
|
115
|
+
size_t opening_length = block_node->opening_loc.end - block_node->opening_loc.start;
|
|
116
|
+
|
|
117
|
+
if ((opening_length == 2 && block_node->opening_loc.start[0] == 'd' && block_node->opening_loc.start[1] == 'o')
|
|
118
|
+
|| (opening_length == 1 && block_node->opening_loc.start[0] == '{')) {
|
|
119
|
+
analyzed->has_block_node = true;
|
|
120
|
+
|
|
121
|
+
return true;
|
|
122
|
+
}
|
|
117
123
|
}
|
|
118
124
|
|
|
125
|
+
pm_visit_child_nodes(node, search_block_nodes, analyzed);
|
|
126
|
+
|
|
119
127
|
return false;
|
|
120
128
|
}
|
|
121
129
|
|
data/src/ast_node.c
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
#include "include/ast_node.h"
|
|
2
2
|
#include "include/ast_nodes.h"
|
|
3
3
|
#include "include/errors.h"
|
|
4
|
+
#include "include/position.h"
|
|
4
5
|
#include "include/token.h"
|
|
5
6
|
#include "include/util.h"
|
|
7
|
+
#include "include/visitor.h"
|
|
6
8
|
|
|
7
9
|
#include <prism.h>
|
|
8
10
|
#include <stdio.h>
|
|
@@ -76,3 +78,30 @@ void ast_node_set_positions_from_token(AST_NODE_T* node, const token_T* token) {
|
|
|
76
78
|
bool ast_node_is(const AST_NODE_T* node, const ast_node_type_T type) {
|
|
77
79
|
return node->type == type;
|
|
78
80
|
}
|
|
81
|
+
|
|
82
|
+
typedef struct {
|
|
83
|
+
position_T position;
|
|
84
|
+
AST_NODE_T* found_node;
|
|
85
|
+
} find_erb_at_position_context_T;
|
|
86
|
+
|
|
87
|
+
static bool find_erb_at_position_visitor(const AST_NODE_T* node, void* data) {
|
|
88
|
+
find_erb_at_position_context_T* context = (find_erb_at_position_context_T*) data;
|
|
89
|
+
|
|
90
|
+
if (node->type == AST_ERB_CONTENT_NODE) {
|
|
91
|
+
if (position_is_within_range(context->position, node->location.start, node->location.end)) {
|
|
92
|
+
context->found_node = (AST_NODE_T*) node;
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return true;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
AST_NODE_T* find_erb_content_at_offset(AST_DOCUMENT_NODE_T* document, const char* source, size_t offset) {
|
|
101
|
+
position_T position = position_from_source_with_offset(source, offset);
|
|
102
|
+
find_erb_at_position_context_T context = { .position = position, .found_node = NULL };
|
|
103
|
+
|
|
104
|
+
herb_visit_node((AST_NODE_T*) document, find_erb_at_position_visitor, &context);
|
|
105
|
+
|
|
106
|
+
return context.found_node;
|
|
107
|
+
}
|
data/src/extract.c
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
#include <stdlib.h>
|
|
8
8
|
#include <string.h>
|
|
9
9
|
|
|
10
|
-
void
|
|
10
|
+
void herb_extract_ruby_to_buffer(const char* source, hb_buffer_T* output) {
|
|
11
11
|
hb_array_T* tokens = herb_lex(source);
|
|
12
12
|
bool skip_erb_content = false;
|
|
13
13
|
bool is_comment_tag = false;
|
|
@@ -76,75 +76,9 @@ void herb_extract_ruby_to_buffer_with_semicolons(const char* source, hb_buffer_T
|
|
|
76
76
|
break;
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
for (size_t j = i + 1; j < hb_array_size(tokens); j++) {
|
|
83
|
-
const token_T* next_token = hb_array_get(tokens, j);
|
|
84
|
-
|
|
85
|
-
if (next_token->type == TOKEN_NEWLINE) { break; }
|
|
86
|
-
|
|
87
|
-
if (next_token->type == TOKEN_ERB_START && next_token->location.start.line == current_line) {
|
|
88
|
-
needs_semicolon = true;
|
|
89
|
-
break;
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
if (needs_semicolon) {
|
|
94
|
-
hb_buffer_append_char(output, ' ');
|
|
95
|
-
hb_buffer_append_char(output, ';');
|
|
96
|
-
hb_buffer_append_whitespace(output, range_length(token->range) - 2);
|
|
97
|
-
} else {
|
|
98
|
-
hb_buffer_append_whitespace(output, range_length(token->range));
|
|
99
|
-
}
|
|
100
|
-
break;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
default: {
|
|
104
|
-
hb_buffer_append_whitespace(output, range_length(token->range));
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
herb_free_tokens(&tokens);
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
void herb_extract_ruby_to_buffer(const char* source, hb_buffer_T* output) {
|
|
113
|
-
hb_array_T* tokens = herb_lex(source);
|
|
114
|
-
bool skip_erb_content = false;
|
|
115
|
-
|
|
116
|
-
for (size_t i = 0; i < hb_array_size(tokens); i++) {
|
|
117
|
-
const token_T* token = hb_array_get(tokens, i);
|
|
118
|
-
|
|
119
|
-
switch (token->type) {
|
|
120
|
-
case TOKEN_NEWLINE: {
|
|
121
|
-
hb_buffer_append(output, token->value);
|
|
122
|
-
break;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
case TOKEN_ERB_START: {
|
|
126
|
-
if (strcmp(token->value, "<%#") == 0 || strcmp(token->value, "<%%") == 0 || strcmp(token->value, "<%%=") == 0) {
|
|
127
|
-
skip_erb_content = true;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
hb_buffer_append_whitespace(output, range_length(token->range));
|
|
131
|
-
break;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
case TOKEN_ERB_CONTENT: {
|
|
135
|
-
if (skip_erb_content == false) {
|
|
136
|
-
hb_buffer_append(output, token->value);
|
|
137
|
-
} else {
|
|
138
|
-
hb_buffer_append_whitespace(output, range_length(token->range));
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
break;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
case TOKEN_ERB_END: {
|
|
145
|
-
skip_erb_content = false;
|
|
146
|
-
|
|
147
|
-
hb_buffer_append_whitespace(output, range_length(token->range));
|
|
79
|
+
hb_buffer_append_char(output, ' ');
|
|
80
|
+
hb_buffer_append_char(output, ';');
|
|
81
|
+
hb_buffer_append_whitespace(output, range_length(token->range) - 2);
|
|
148
82
|
break;
|
|
149
83
|
}
|
|
150
84
|
|
|
@@ -180,7 +114,7 @@ char* herb_extract_ruby_with_semicolons(const char* source) {
|
|
|
180
114
|
hb_buffer_T output;
|
|
181
115
|
hb_buffer_init(&output, strlen(source));
|
|
182
116
|
|
|
183
|
-
|
|
117
|
+
herb_extract_ruby_to_buffer(source, &output);
|
|
184
118
|
|
|
185
119
|
return output.value;
|
|
186
120
|
}
|
data/src/herb.c
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
#include <prism.h>
|
|
11
11
|
#include <stdlib.h>
|
|
12
12
|
|
|
13
|
-
hb_array_T* herb_lex(const char* source) {
|
|
13
|
+
HERB_EXPORTED_FUNCTION hb_array_T* herb_lex(const char* source) {
|
|
14
14
|
lexer_T lexer = { 0 };
|
|
15
15
|
lexer_init(&lexer, source);
|
|
16
16
|
|
|
@@ -26,7 +26,7 @@ hb_array_T* herb_lex(const char* source) {
|
|
|
26
26
|
return tokens;
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
AST_DOCUMENT_NODE_T* herb_parse(const char* source, parser_options_T* options) {
|
|
29
|
+
HERB_EXPORTED_FUNCTION AST_DOCUMENT_NODE_T* herb_parse(const char* source, parser_options_T* options) {
|
|
30
30
|
if (!source) { source = ""; }
|
|
31
31
|
|
|
32
32
|
lexer_T lexer = { 0 };
|
|
@@ -46,7 +46,7 @@ AST_DOCUMENT_NODE_T* herb_parse(const char* source, parser_options_T* options) {
|
|
|
46
46
|
return document;
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
hb_array_T* herb_lex_file(const char* path) {
|
|
49
|
+
HERB_EXPORTED_FUNCTION hb_array_T* herb_lex_file(const char* path) {
|
|
50
50
|
char* source = herb_read_file(path);
|
|
51
51
|
hb_array_T* tokens = herb_lex(source);
|
|
52
52
|
|
|
@@ -55,7 +55,7 @@ hb_array_T* herb_lex_file(const char* path) {
|
|
|
55
55
|
return tokens;
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
-
void herb_lex_to_buffer(const char* source, hb_buffer_T* output) {
|
|
58
|
+
HERB_EXPORTED_FUNCTION void herb_lex_to_buffer(const char* source, hb_buffer_T* output) {
|
|
59
59
|
hb_array_T* tokens = herb_lex(source);
|
|
60
60
|
|
|
61
61
|
for (size_t i = 0; i < hb_array_size(tokens); i++) {
|
|
@@ -71,7 +71,7 @@ void herb_lex_to_buffer(const char* source, hb_buffer_T* output) {
|
|
|
71
71
|
herb_free_tokens(&tokens);
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
-
void herb_free_tokens(hb_array_T** tokens) {
|
|
74
|
+
HERB_EXPORTED_FUNCTION void herb_free_tokens(hb_array_T** tokens) {
|
|
75
75
|
if (!tokens || !*tokens) { return; }
|
|
76
76
|
|
|
77
77
|
for (size_t i = 0; i < hb_array_size(*tokens); i++) {
|
|
@@ -82,10 +82,10 @@ void herb_free_tokens(hb_array_T** tokens) {
|
|
|
82
82
|
hb_array_free(tokens);
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
-
const char* herb_version(void) {
|
|
85
|
+
HERB_EXPORTED_FUNCTION const char* herb_version(void) {
|
|
86
86
|
return HERB_VERSION;
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
-
const char* herb_prism_version(void) {
|
|
89
|
+
HERB_EXPORTED_FUNCTION const char* herb_prism_version(void) {
|
|
90
90
|
return PRISM_VERSION;
|
|
91
91
|
}
|
data/src/include/ast_node.h
CHANGED
|
@@ -32,4 +32,6 @@ void ast_node_set_positions_from_token(AST_NODE_T* node, const token_T* token);
|
|
|
32
32
|
|
|
33
33
|
bool ast_node_is(const AST_NODE_T* node, ast_node_type_T type);
|
|
34
34
|
|
|
35
|
+
AST_NODE_T* find_erb_content_at_offset(AST_DOCUMENT_NODE_T* document, const char* source, size_t offset);
|
|
36
|
+
|
|
35
37
|
#endif
|
data/src/include/extract.h
CHANGED
|
@@ -12,7 +12,6 @@ void herb_extract_ruby_to_buffer(const char* source, hb_buffer_T* output);
|
|
|
12
12
|
void herb_extract_html_to_buffer(const char* source, hb_buffer_T* output);
|
|
13
13
|
|
|
14
14
|
char* herb_extract_ruby_with_semicolons(const char* source);
|
|
15
|
-
void herb_extract_ruby_to_buffer_with_semicolons(const char* source, hb_buffer_T* output);
|
|
16
15
|
|
|
17
16
|
char* herb_extract(const char* source, herb_extract_language_T language);
|
|
18
17
|
char* herb_extract_from_file(const char* path, herb_extract_language_T language);
|
data/src/include/herb.h
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
#include "ast_node.h"
|
|
5
5
|
#include "extract.h"
|
|
6
|
+
#include "macros.h"
|
|
6
7
|
#include "parser.h"
|
|
7
8
|
#include "util/hb_array.h"
|
|
8
9
|
#include "util/hb_buffer.h"
|
|
@@ -13,17 +14,17 @@
|
|
|
13
14
|
extern "C" {
|
|
14
15
|
#endif
|
|
15
16
|
|
|
16
|
-
void herb_lex_to_buffer(const char* source, hb_buffer_T* output);
|
|
17
|
+
HERB_EXPORTED_FUNCTION void herb_lex_to_buffer(const char* source, hb_buffer_T* output);
|
|
17
18
|
|
|
18
|
-
hb_array_T* herb_lex(const char* source);
|
|
19
|
-
hb_array_T* herb_lex_file(const char* path);
|
|
19
|
+
HERB_EXPORTED_FUNCTION hb_array_T* herb_lex(const char* source);
|
|
20
|
+
HERB_EXPORTED_FUNCTION hb_array_T* herb_lex_file(const char* path);
|
|
20
21
|
|
|
21
|
-
AST_DOCUMENT_NODE_T* herb_parse(const char* source, parser_options_T* options);
|
|
22
|
+
HERB_EXPORTED_FUNCTION AST_DOCUMENT_NODE_T* herb_parse(const char* source, parser_options_T* options);
|
|
22
23
|
|
|
23
|
-
const char* herb_version(void);
|
|
24
|
-
const char* herb_prism_version(void);
|
|
24
|
+
HERB_EXPORTED_FUNCTION const char* herb_version(void);
|
|
25
|
+
HERB_EXPORTED_FUNCTION const char* herb_prism_version(void);
|
|
25
26
|
|
|
26
|
-
void herb_free_tokens(hb_array_T** tokens);
|
|
27
|
+
HERB_EXPORTED_FUNCTION void herb_free_tokens(hb_array_T** tokens);
|
|
27
28
|
|
|
28
29
|
#ifdef __cplusplus
|
|
29
30
|
}
|
data/src/include/macros.h
CHANGED
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
#ifndef HERB_MACROS_H
|
|
2
2
|
#define HERB_MACROS_H
|
|
3
3
|
|
|
4
|
+
#ifndef HERB_EXPORTED_FUNCTION
|
|
5
|
+
# ifdef HERB_EXPORT_SYMBOLS
|
|
6
|
+
# define HERB_EXPORTED_FUNCTION __attribute__((__visibility__("default"))) extern
|
|
7
|
+
# else
|
|
8
|
+
# define HERB_EXPORTED_FUNCTION
|
|
9
|
+
# endif
|
|
10
|
+
#endif
|
|
11
|
+
|
|
4
12
|
#define MAX(a, b) ((a) > (b) ? (a) : (b))
|
|
5
13
|
|
|
6
14
|
#define MIN(a, b) ((a) < (b) ? (a) : (b))
|
data/src/include/position.h
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
#ifndef HERB_POSITION_H
|
|
2
2
|
#define HERB_POSITION_H
|
|
3
3
|
|
|
4
|
+
#include <stdbool.h>
|
|
5
|
+
#include <stddef.h>
|
|
4
6
|
#include <stdint.h>
|
|
5
7
|
|
|
6
8
|
typedef struct POSITION_STRUCT {
|
|
@@ -8,4 +10,7 @@ typedef struct POSITION_STRUCT {
|
|
|
8
10
|
uint32_t column;
|
|
9
11
|
} position_T;
|
|
10
12
|
|
|
13
|
+
position_T position_from_source_with_offset(const char* source, size_t offset);
|
|
14
|
+
bool position_is_within_range(position_T position, position_T start, position_T end);
|
|
15
|
+
|
|
11
16
|
#endif
|
data/src/include/prism_helpers.h
CHANGED
|
@@ -16,6 +16,10 @@ RUBY_PARSE_ERROR_T* ruby_parse_error_from_prism_error(
|
|
|
16
16
|
pm_parser_t* parser
|
|
17
17
|
);
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
RUBY_PARSE_ERROR_T* ruby_parse_error_from_prism_error_with_positions(
|
|
20
|
+
const pm_diagnostic_t* error,
|
|
21
|
+
position_T start,
|
|
22
|
+
position_T end
|
|
23
|
+
);
|
|
20
24
|
|
|
21
25
|
#endif
|
|
@@ -1,18 +1,21 @@
|
|
|
1
1
|
#ifndef HERB_BUFFER_H
|
|
2
2
|
#define HERB_BUFFER_H
|
|
3
3
|
|
|
4
|
+
#include "hb_arena.h"
|
|
4
5
|
#include "hb_string.h"
|
|
5
6
|
|
|
6
7
|
#include <stdbool.h>
|
|
7
8
|
#include <stdlib.h>
|
|
8
9
|
|
|
9
10
|
typedef struct HB_BUFFER_STRUCT {
|
|
11
|
+
hb_arena_T* allocator;
|
|
10
12
|
char* value;
|
|
11
13
|
size_t length;
|
|
12
14
|
size_t capacity;
|
|
13
15
|
} hb_buffer_T;
|
|
14
16
|
|
|
15
17
|
bool hb_buffer_init(hb_buffer_T* buffer, size_t capacity);
|
|
18
|
+
bool hb_buffer_init_arena(hb_buffer_T* buffer, hb_arena_T* allocator, size_t capacity);
|
|
16
19
|
|
|
17
20
|
void hb_buffer_append(hb_buffer_T* buffer, const char* text);
|
|
18
21
|
void hb_buffer_append_with_length(hb_buffer_T* buffer, const char* text, size_t length);
|
|
@@ -29,6 +32,5 @@ size_t hb_buffer_capacity(const hb_buffer_T* buffer);
|
|
|
29
32
|
size_t hb_buffer_sizeof(void);
|
|
30
33
|
|
|
31
34
|
void hb_buffer_clear(hb_buffer_T* buffer);
|
|
32
|
-
void hb_buffer_free(hb_buffer_T** buffer);
|
|
33
35
|
|
|
34
36
|
#endif
|
data/src/include/version.h
CHANGED
data/src/parser.c
CHANGED
|
@@ -315,8 +315,8 @@ static AST_HTML_ATTRIBUTE_NAME_NODE_T* parser_parse_html_attribute_name(parser_T
|
|
|
315
315
|
position_T node_end = { 0 };
|
|
316
316
|
|
|
317
317
|
if (children->size > 0) {
|
|
318
|
-
AST_NODE_T* first_child =
|
|
319
|
-
AST_NODE_T* last_child =
|
|
318
|
+
AST_NODE_T* first_child = hb_array_first(children);
|
|
319
|
+
AST_NODE_T* last_child = hb_array_last(children);
|
|
320
320
|
|
|
321
321
|
node_start = first_child->location.start;
|
|
322
322
|
node_end = last_child->location.end;
|
|
@@ -1301,17 +1301,15 @@ void match_tags_in_node_array(hb_array_T* nodes, hb_array_T* errors) {
|
|
|
1301
1301
|
|
|
1302
1302
|
hb_array_T* processed = parser_build_elements_from_tags(nodes, errors);
|
|
1303
1303
|
|
|
1304
|
-
|
|
1305
|
-
hb_array_remove(nodes, 0);
|
|
1306
|
-
}
|
|
1304
|
+
nodes->size = 0;
|
|
1307
1305
|
|
|
1308
|
-
for (size_t i = 0; i <
|
|
1306
|
+
for (size_t i = 0; i < processed->size; i++) {
|
|
1309
1307
|
hb_array_append(nodes, hb_array_get(processed, i));
|
|
1310
1308
|
}
|
|
1311
1309
|
|
|
1312
1310
|
hb_array_free(&processed);
|
|
1313
1311
|
|
|
1314
|
-
for (size_t i = 0; i <
|
|
1312
|
+
for (size_t i = 0; i < nodes->size; i++) {
|
|
1315
1313
|
AST_NODE_T* node = (AST_NODE_T*) hb_array_get(nodes, i);
|
|
1316
1314
|
if (node == NULL) { continue; }
|
|
1317
1315
|
|
data/src/position.c
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
#include "include/position.h"
|
|
2
|
+
#include "include/util.h"
|
|
3
|
+
|
|
4
|
+
position_T position_from_source_with_offset(const char* source, size_t offset) {
|
|
5
|
+
position_T position = { .line = 1, .column = 0 };
|
|
6
|
+
|
|
7
|
+
for (size_t i = 0; i < offset; i++) {
|
|
8
|
+
if (is_newline(source[i])) {
|
|
9
|
+
position.line++;
|
|
10
|
+
position.column = 0;
|
|
11
|
+
} else {
|
|
12
|
+
position.column++;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return position;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
bool position_is_within_range(position_T position, position_T start, position_T end) {
|
|
20
|
+
if (position.line < start.line) { return false; }
|
|
21
|
+
if (position.line == start.line && position.column < start.column) { return false; }
|
|
22
|
+
|
|
23
|
+
if (position.line > end.line) { return false; }
|
|
24
|
+
if (position.line == end.line && position.column > end.column) { return false; }
|
|
25
|
+
|
|
26
|
+
return true;
|
|
27
|
+
}
|
data/src/prism_helpers.c
CHANGED
|
@@ -16,21 +16,6 @@ const char* pm_error_level_to_string(pm_error_level_t level) {
|
|
|
16
16
|
}
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
position_T position_from_source_with_offset(const char* source, size_t offset) {
|
|
20
|
-
position_T position = { .line = 1, .column = 0 };
|
|
21
|
-
|
|
22
|
-
for (size_t i = 0; i < offset; i++) {
|
|
23
|
-
if (is_newline(source[i])) {
|
|
24
|
-
position.line++;
|
|
25
|
-
position.column = 0;
|
|
26
|
-
} else {
|
|
27
|
-
position.column++;
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
return position;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
19
|
RUBY_PARSE_ERROR_T* ruby_parse_error_from_prism_error(
|
|
35
20
|
const pm_diagnostic_t* error,
|
|
36
21
|
const AST_NODE_T* node,
|
|
@@ -51,3 +36,17 @@ RUBY_PARSE_ERROR_T* ruby_parse_error_from_prism_error(
|
|
|
51
36
|
end
|
|
52
37
|
);
|
|
53
38
|
}
|
|
39
|
+
|
|
40
|
+
RUBY_PARSE_ERROR_T* ruby_parse_error_from_prism_error_with_positions(
|
|
41
|
+
const pm_diagnostic_t* error,
|
|
42
|
+
position_T start,
|
|
43
|
+
position_T end
|
|
44
|
+
) {
|
|
45
|
+
return ruby_parse_error_init(
|
|
46
|
+
error->message,
|
|
47
|
+
pm_diagnostic_id_human(error->diag_id),
|
|
48
|
+
pm_error_level_to_string(error->level),
|
|
49
|
+
start,
|
|
50
|
+
end
|
|
51
|
+
);
|
|
52
|
+
}
|
data/src/util/hb_buffer.c
CHANGED
|
@@ -22,8 +22,14 @@ static bool hb_buffer_resize(hb_buffer_T* buffer, const size_t new_capacity) {
|
|
|
22
22
|
fprintf(stderr, "Error: Buffer capacity would overflow system limits.\n");
|
|
23
23
|
exit(1);
|
|
24
24
|
}
|
|
25
|
+
char* new_value = NULL;
|
|
25
26
|
|
|
26
|
-
|
|
27
|
+
if (buffer->allocator == NULL) {
|
|
28
|
+
new_value = realloc(buffer->value, new_capacity + 1);
|
|
29
|
+
} else {
|
|
30
|
+
new_value = hb_arena_alloc(buffer->allocator, new_capacity + 1);
|
|
31
|
+
memcpy(new_value, buffer->value, buffer->capacity + 1);
|
|
32
|
+
}
|
|
27
33
|
|
|
28
34
|
if (unlikely(new_value == NULL)) {
|
|
29
35
|
fprintf(stderr, "Error: Failed to resize buffer to %zu.\n", new_capacity);
|
|
@@ -61,6 +67,7 @@ static bool hb_buffer_expand_if_needed(hb_buffer_T* buffer, const size_t require
|
|
|
61
67
|
}
|
|
62
68
|
|
|
63
69
|
bool hb_buffer_init(hb_buffer_T* buffer, const size_t capacity) {
|
|
70
|
+
buffer->allocator = NULL;
|
|
64
71
|
buffer->capacity = capacity;
|
|
65
72
|
buffer->length = 0;
|
|
66
73
|
buffer->value = malloc(sizeof(char) * (buffer->capacity + 1));
|
|
@@ -76,6 +83,16 @@ bool hb_buffer_init(hb_buffer_T* buffer, const size_t capacity) {
|
|
|
76
83
|
return true;
|
|
77
84
|
}
|
|
78
85
|
|
|
86
|
+
bool hb_buffer_init_arena(hb_buffer_T* buffer, hb_arena_T* allocator, size_t capacity) {
|
|
87
|
+
buffer->allocator = allocator;
|
|
88
|
+
buffer->capacity = capacity;
|
|
89
|
+
buffer->length = 0;
|
|
90
|
+
buffer->value = hb_arena_alloc(allocator, sizeof(char) * (buffer->capacity + 1));
|
|
91
|
+
buffer->value[0] = '\0';
|
|
92
|
+
|
|
93
|
+
return true;
|
|
94
|
+
}
|
|
95
|
+
|
|
79
96
|
char* hb_buffer_value(const hb_buffer_T* buffer) {
|
|
80
97
|
return buffer->value;
|
|
81
98
|
}
|
|
@@ -192,12 +209,3 @@ void hb_buffer_clear(hb_buffer_T* buffer) {
|
|
|
192
209
|
buffer->length = 0;
|
|
193
210
|
buffer->value[0] = '\0';
|
|
194
211
|
}
|
|
195
|
-
|
|
196
|
-
void hb_buffer_free(hb_buffer_T** buffer) {
|
|
197
|
-
if (!buffer || !*buffer) { return; }
|
|
198
|
-
|
|
199
|
-
if ((*buffer)->value != NULL) { free((*buffer)->value); }
|
|
200
|
-
|
|
201
|
-
free(*buffer);
|
|
202
|
-
*buffer = NULL;
|
|
203
|
-
}
|
data/src/util/hb_system.c
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
#include "../include/util/hb_system.h"
|
|
2
2
|
|
|
3
3
|
#ifdef __linux__
|
|
4
|
-
#define _GNU_SOURCE
|
|
4
|
+
# define _GNU_SOURCE
|
|
5
5
|
#endif
|
|
6
6
|
|
|
7
7
|
#ifdef HB_USE_MALLOC
|
|
8
|
-
#include <stdlib.h>
|
|
8
|
+
# include <stdlib.h>
|
|
9
9
|
#else
|
|
10
|
-
#include <sys/mman.h>
|
|
10
|
+
# include <sys/mman.h>
|
|
11
11
|
#endif
|
|
12
12
|
|
|
13
13
|
void* hb_system_allocate_memory(size_t size) {
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: herb
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.8.
|
|
4
|
+
version: 0.8.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Marco Roth
|
|
@@ -157,6 +157,7 @@ files:
|
|
|
157
157
|
- src/parser.c
|
|
158
158
|
- src/parser_helpers.c
|
|
159
159
|
- src/parser_match_tags.c
|
|
160
|
+
- src/position.c
|
|
160
161
|
- src/pretty_print.c
|
|
161
162
|
- src/prism_helpers.c
|
|
162
163
|
- src/range.c
|