herb 0.8.7 → 0.8.8

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 (46) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +7 -0
  3. data/config.yml +12 -0
  4. data/ext/herb/extconf.rb +0 -4
  5. data/ext/herb/nodes.c +17 -9
  6. data/lib/herb/ast/nodes.rb +32 -8
  7. data/lib/herb/engine/debug_visitor.rb +1 -1
  8. data/lib/herb/version.rb +1 -1
  9. data/lib/herb.rb +30 -3
  10. data/sig/herb/ast/nodes.rbs +16 -8
  11. data/sig/serialized_ast_nodes.rbs +4 -0
  12. data/src/analyze.c +110 -15
  13. data/src/analyze_helpers.c +80 -12
  14. data/src/analyzed_ruby.c +1 -0
  15. data/src/ast_nodes.c +12 -4
  16. data/src/ast_pretty_print.c +52 -0
  17. data/src/include/analyze_helpers.h +7 -0
  18. data/src/include/analyzed_ruby.h +1 -0
  19. data/src/include/ast_nodes.h +8 -4
  20. data/src/include/location.h +4 -0
  21. data/src/include/prism_helpers.h +6 -0
  22. data/src/include/version.h +1 -1
  23. data/src/location.c +16 -0
  24. data/src/prism_helpers.c +188 -0
  25. data/templates/ext/herb/nodes.c.erb +2 -0
  26. data/templates/java/nodes.c.erb +3 -1
  27. data/templates/java/org/herb/ast/Nodes.java.erb +11 -0
  28. data/templates/javascript/packages/core/src/nodes.ts.erb +14 -0
  29. data/templates/javascript/packages/node/extension/nodes.cpp.erb +9 -0
  30. data/templates/lib/herb/ast/nodes.rb.erb +4 -0
  31. data/templates/rust/src/ast/nodes.rs.erb +10 -0
  32. data/templates/rust/src/nodes.rs.erb +4 -0
  33. data/templates/src/ast_nodes.c.erb +4 -0
  34. data/templates/src/ast_pretty_print.c.erb +14 -0
  35. data/templates/template.rb +11 -0
  36. data/templates/wasm/nodes.cpp.erb +6 -0
  37. data/vendor/prism/include/prism/version.h +2 -2
  38. data/vendor/prism/src/prism.c +48 -27
  39. data/vendor/prism/templates/java/org/prism/Loader.java.erb +1 -1
  40. data/vendor/prism/templates/javascript/src/deserialize.js.erb +1 -1
  41. data/vendor/prism/templates/lib/prism/compiler.rb.erb +2 -2
  42. data/vendor/prism/templates/lib/prism/node.rb.erb +24 -1
  43. data/vendor/prism/templates/lib/prism/serialize.rb.erb +1 -1
  44. data/vendor/prism/templates/lib/prism/visitor.rb.erb +2 -2
  45. data/vendor/prism/templates/sig/prism/node.rbs.erb +1 -0
  46. metadata +1 -1
@@ -183,6 +183,7 @@ typedef struct AST_ERB_IF_NODE_STRUCT {
183
183
  token_T* tag_opening;
184
184
  token_T* content;
185
185
  token_T* tag_closing;
186
+ location_T* then_keyword;
186
187
  hb_array_T* statements;
187
188
  struct AST_NODE_STRUCT* subsequent;
188
189
  struct AST_ERB_END_NODE_STRUCT* end_node;
@@ -202,6 +203,7 @@ typedef struct AST_ERB_WHEN_NODE_STRUCT {
202
203
  token_T* tag_opening;
203
204
  token_T* content;
204
205
  token_T* tag_closing;
206
+ location_T* then_keyword;
205
207
  hb_array_T* statements;
206
208
  } AST_ERB_WHEN_NODE_T;
207
209
 
@@ -288,6 +290,7 @@ typedef struct AST_ERB_UNLESS_NODE_STRUCT {
288
290
  token_T* tag_opening;
289
291
  token_T* content;
290
292
  token_T* tag_closing;
293
+ location_T* then_keyword;
291
294
  hb_array_T* statements;
292
295
  struct AST_ERB_ELSE_NODE_STRUCT* else_clause;
293
296
  struct AST_ERB_END_NODE_STRUCT* end_node;
@@ -305,6 +308,7 @@ typedef struct AST_ERB_IN_NODE_STRUCT {
305
308
  token_T* tag_opening;
306
309
  token_T* content;
307
310
  token_T* tag_closing;
311
+ location_T* then_keyword;
308
312
  hb_array_T* statements;
309
313
  } AST_ERB_IN_NODE_T;
310
314
 
@@ -325,9 +329,9 @@ AST_WHITESPACE_NODE_T* ast_whitespace_node_init(token_T* value, position_T start
325
329
  AST_ERB_CONTENT_NODE_T* ast_erb_content_node_init(token_T* tag_opening, token_T* content, token_T* tag_closing, analyzed_ruby_T* analyzed_ruby, bool parsed, bool valid, position_T start_position, position_T end_position, hb_array_T* errors);
326
330
  AST_ERB_END_NODE_T* ast_erb_end_node_init(token_T* tag_opening, token_T* content, token_T* tag_closing, position_T start_position, position_T end_position, hb_array_T* errors);
327
331
  AST_ERB_ELSE_NODE_T* ast_erb_else_node_init(token_T* tag_opening, token_T* content, token_T* tag_closing, hb_array_T* statements, position_T start_position, position_T end_position, hb_array_T* errors);
328
- AST_ERB_IF_NODE_T* ast_erb_if_node_init(token_T* tag_opening, token_T* content, token_T* tag_closing, hb_array_T* statements, struct AST_NODE_STRUCT* subsequent, struct AST_ERB_END_NODE_STRUCT* end_node, position_T start_position, position_T end_position, hb_array_T* errors);
332
+ AST_ERB_IF_NODE_T* ast_erb_if_node_init(token_T* tag_opening, token_T* content, token_T* tag_closing, location_T* then_keyword, hb_array_T* statements, struct AST_NODE_STRUCT* subsequent, struct AST_ERB_END_NODE_STRUCT* end_node, position_T start_position, position_T end_position, hb_array_T* errors);
329
333
  AST_ERB_BLOCK_NODE_T* ast_erb_block_node_init(token_T* tag_opening, token_T* content, token_T* tag_closing, hb_array_T* body, struct AST_ERB_END_NODE_STRUCT* end_node, position_T start_position, position_T end_position, hb_array_T* errors);
330
- AST_ERB_WHEN_NODE_T* ast_erb_when_node_init(token_T* tag_opening, token_T* content, token_T* tag_closing, hb_array_T* statements, position_T start_position, position_T end_position, hb_array_T* errors);
334
+ AST_ERB_WHEN_NODE_T* ast_erb_when_node_init(token_T* tag_opening, token_T* content, token_T* tag_closing, location_T* then_keyword, hb_array_T* statements, position_T start_position, position_T end_position, hb_array_T* errors);
331
335
  AST_ERB_CASE_NODE_T* ast_erb_case_node_init(token_T* tag_opening, token_T* content, token_T* tag_closing, hb_array_T* children, hb_array_T* conditions, struct AST_ERB_ELSE_NODE_STRUCT* else_clause, struct AST_ERB_END_NODE_STRUCT* end_node, position_T start_position, position_T end_position, hb_array_T* errors);
332
336
  AST_ERB_CASE_MATCH_NODE_T* ast_erb_case_match_node_init(token_T* tag_opening, token_T* content, token_T* tag_closing, hb_array_T* children, hb_array_T* conditions, struct AST_ERB_ELSE_NODE_STRUCT* else_clause, struct AST_ERB_END_NODE_STRUCT* end_node, position_T start_position, position_T end_position, hb_array_T* errors);
333
337
  AST_ERB_WHILE_NODE_T* ast_erb_while_node_init(token_T* tag_opening, token_T* content, token_T* tag_closing, hb_array_T* statements, struct AST_ERB_END_NODE_STRUCT* end_node, position_T start_position, position_T end_position, hb_array_T* errors);
@@ -336,9 +340,9 @@ AST_ERB_FOR_NODE_T* ast_erb_for_node_init(token_T* tag_opening, token_T* content
336
340
  AST_ERB_RESCUE_NODE_T* ast_erb_rescue_node_init(token_T* tag_opening, token_T* content, token_T* tag_closing, hb_array_T* statements, struct AST_ERB_RESCUE_NODE_STRUCT* subsequent, position_T start_position, position_T end_position, hb_array_T* errors);
337
341
  AST_ERB_ENSURE_NODE_T* ast_erb_ensure_node_init(token_T* tag_opening, token_T* content, token_T* tag_closing, hb_array_T* statements, position_T start_position, position_T end_position, hb_array_T* errors);
338
342
  AST_ERB_BEGIN_NODE_T* ast_erb_begin_node_init(token_T* tag_opening, token_T* content, token_T* tag_closing, hb_array_T* statements, struct AST_ERB_RESCUE_NODE_STRUCT* rescue_clause, struct AST_ERB_ELSE_NODE_STRUCT* else_clause, struct AST_ERB_ENSURE_NODE_STRUCT* ensure_clause, struct AST_ERB_END_NODE_STRUCT* end_node, position_T start_position, position_T end_position, hb_array_T* errors);
339
- AST_ERB_UNLESS_NODE_T* ast_erb_unless_node_init(token_T* tag_opening, token_T* content, token_T* tag_closing, hb_array_T* statements, struct AST_ERB_ELSE_NODE_STRUCT* else_clause, struct AST_ERB_END_NODE_STRUCT* end_node, position_T start_position, position_T end_position, hb_array_T* errors);
343
+ AST_ERB_UNLESS_NODE_T* ast_erb_unless_node_init(token_T* tag_opening, token_T* content, token_T* tag_closing, location_T* then_keyword, hb_array_T* statements, struct AST_ERB_ELSE_NODE_STRUCT* else_clause, struct AST_ERB_END_NODE_STRUCT* end_node, position_T start_position, position_T end_position, hb_array_T* errors);
340
344
  AST_ERB_YIELD_NODE_T* ast_erb_yield_node_init(token_T* tag_opening, token_T* content, token_T* tag_closing, position_T start_position, position_T end_position, hb_array_T* errors);
341
- AST_ERB_IN_NODE_T* ast_erb_in_node_init(token_T* tag_opening, token_T* content, token_T* tag_closing, hb_array_T* statements, position_T start_position, position_T end_position, hb_array_T* errors);
345
+ AST_ERB_IN_NODE_T* ast_erb_in_node_init(token_T* tag_opening, token_T* content, token_T* tag_closing, location_T* then_keyword, hb_array_T* statements, position_T start_position, position_T end_position, hb_array_T* errors);
342
346
 
343
347
  hb_string_T ast_node_type_to_string(AST_NODE_T* node);
344
348
  hb_string_T ast_node_human_type(AST_NODE_T* node);
@@ -19,4 +19,8 @@ void location_from(
19
19
  uint32_t end_column
20
20
  );
21
21
 
22
+ void location_from_positions(location_T* location, position_T start, position_T end);
23
+
24
+ location_T* location_create(position_T start, position_T end);
25
+
22
26
  #endif
@@ -1,8 +1,10 @@
1
1
  #ifndef HERB_PRISM_HELPERS_H
2
2
  #define HERB_PRISM_HELPERS_H
3
3
 
4
+ #include "analyzed_ruby.h"
4
5
  #include "ast_nodes.h"
5
6
  #include "errors.h"
7
+ #include "location.h"
6
8
  #include "position.h"
7
9
 
8
10
  #include <prism.h>
@@ -22,4 +24,8 @@ RUBY_PARSE_ERROR_T* ruby_parse_error_from_prism_error_with_positions(
22
24
  position_T end
23
25
  );
24
26
 
27
+ location_T* get_then_keyword_location(analyzed_ruby_T* analyzed, const char* source);
28
+ location_T* get_then_keyword_location_wrapped(const char* source, bool is_in_clause);
29
+ location_T* get_then_keyword_location_elsif_wrapped(const char* source);
30
+
25
31
  #endif
@@ -1,6 +1,6 @@
1
1
  #ifndef HERB_VERSION_H
2
2
  #define HERB_VERSION_H
3
3
 
4
- #define HERB_VERSION "0.8.7"
4
+ #define HERB_VERSION "0.8.8"
5
5
 
6
6
  #endif
data/src/location.c CHANGED
@@ -11,3 +11,19 @@ void location_from(
11
11
  location->start = (position_T) { .line = start_line, .column = start_column };
12
12
  location->end = (position_T) { .line = end_line, .column = end_column };
13
13
  }
14
+
15
+ void location_from_positions(location_T* location, position_T start, position_T end) {
16
+ location->start = start;
17
+ location->end = end;
18
+ }
19
+
20
+ location_T* location_create(position_T start, position_T end) {
21
+ location_T* location = malloc(sizeof(location_T));
22
+
23
+ if (location != NULL) {
24
+ location->start = start;
25
+ location->end = end;
26
+ }
27
+
28
+ return location;
29
+ }
data/src/prism_helpers.c CHANGED
@@ -1,10 +1,14 @@
1
1
  #include "include/prism_helpers.h"
2
2
  #include "include/ast_nodes.h"
3
3
  #include "include/errors.h"
4
+ #include "include/location.h"
4
5
  #include "include/position.h"
5
6
  #include "include/util.h"
7
+ #include "include/util/hb_buffer.h"
6
8
 
7
9
  #include <prism.h>
10
+ #include <stdlib.h>
11
+ #include <string.h>
8
12
 
9
13
  const char* pm_error_level_to_string(pm_error_level_t level) {
10
14
  switch (level) {
@@ -50,3 +54,187 @@ RUBY_PARSE_ERROR_T* ruby_parse_error_from_prism_error_with_positions(
50
54
  end
51
55
  );
52
56
  }
57
+
58
+ typedef struct {
59
+ pm_location_t then_keyword_loc;
60
+ bool found;
61
+ } then_keyword_search_context_T;
62
+
63
+ static bool has_pm_location(pm_location_t location) {
64
+ return location.start != NULL && location.end != NULL && (location.end - location.start) > 0;
65
+ }
66
+
67
+ static bool search_then_keyword_location(const pm_node_t* node, void* data) {
68
+ then_keyword_search_context_T* context = (then_keyword_search_context_T*) data;
69
+
70
+ if (context->found) { return false; }
71
+
72
+ switch (node->type) {
73
+ case PM_IF_NODE: {
74
+ const pm_if_node_t* if_node = (const pm_if_node_t*) node;
75
+ if (has_pm_location(if_node->then_keyword_loc)) {
76
+ context->then_keyword_loc = if_node->then_keyword_loc;
77
+ context->found = true;
78
+ return false;
79
+ }
80
+ break;
81
+ }
82
+
83
+ case PM_UNLESS_NODE: {
84
+ const pm_unless_node_t* unless_node = (const pm_unless_node_t*) node;
85
+ if (has_pm_location(unless_node->then_keyword_loc)) {
86
+ context->then_keyword_loc = unless_node->then_keyword_loc;
87
+ context->found = true;
88
+ return false;
89
+ }
90
+ break;
91
+ }
92
+
93
+ case PM_WHEN_NODE: {
94
+ const pm_when_node_t* when_node = (const pm_when_node_t*) node;
95
+ if (has_pm_location(when_node->then_keyword_loc)) {
96
+ context->then_keyword_loc = when_node->then_keyword_loc;
97
+ context->found = true;
98
+ return false;
99
+ }
100
+ break;
101
+ }
102
+
103
+ case PM_IN_NODE: {
104
+ const pm_in_node_t* in_node = (const pm_in_node_t*) node;
105
+ if (has_pm_location(in_node->then_loc)) {
106
+ context->then_keyword_loc = in_node->then_loc;
107
+ context->found = true;
108
+ return false;
109
+ }
110
+ break;
111
+ }
112
+
113
+ default: break;
114
+ }
115
+
116
+ pm_visit_child_nodes(node, search_then_keyword_location, context);
117
+
118
+ return false;
119
+ }
120
+
121
+ location_T* get_then_keyword_location(analyzed_ruby_T* analyzed, const char* source) {
122
+ if (analyzed == NULL || analyzed->root == NULL || source == NULL) { return NULL; }
123
+
124
+ then_keyword_search_context_T context = { .then_keyword_loc = { .start = NULL, .end = NULL }, .found = false };
125
+
126
+ pm_visit_child_nodes(analyzed->root, search_then_keyword_location, &context);
127
+
128
+ if (!context.found) { return NULL; }
129
+
130
+ size_t start_offset = (size_t) (context.then_keyword_loc.start - analyzed->parser.start);
131
+ size_t end_offset = (size_t) (context.then_keyword_loc.end - analyzed->parser.start);
132
+
133
+ position_T start_position = position_from_source_with_offset(source, start_offset);
134
+ position_T end_position = position_from_source_with_offset(source, end_offset);
135
+
136
+ return location_create(start_position, end_position);
137
+ }
138
+
139
+ static location_T* parse_wrapped_and_find_then_keyword(
140
+ hb_buffer_T* buffer,
141
+ const char* source,
142
+ size_t source_length,
143
+ size_t prefix_length,
144
+ size_t adjustment_threshold,
145
+ size_t adjustment_amount
146
+ ) {
147
+ pm_parser_t parser;
148
+ pm_parser_init(&parser, (const uint8_t*) hb_buffer_value(buffer), hb_buffer_length(buffer), NULL);
149
+ pm_node_t* root = pm_parse(&parser);
150
+
151
+ if (root == NULL) {
152
+ pm_parser_free(&parser);
153
+
154
+ return NULL;
155
+ }
156
+
157
+ then_keyword_search_context_T context = { .then_keyword_loc = { .start = NULL, .end = NULL }, .found = false };
158
+
159
+ pm_visit_child_nodes(root, search_then_keyword_location, &context);
160
+
161
+ location_T* location = NULL;
162
+
163
+ if (context.found) {
164
+ size_t start_offset = (size_t) (context.then_keyword_loc.start - parser.start);
165
+ size_t end_offset = (size_t) (context.then_keyword_loc.end - parser.start);
166
+
167
+ if (start_offset >= prefix_length && end_offset >= prefix_length) {
168
+ start_offset -= prefix_length;
169
+ end_offset -= prefix_length;
170
+
171
+ if (start_offset > adjustment_threshold) {
172
+ start_offset += adjustment_amount;
173
+ end_offset += adjustment_amount;
174
+ }
175
+
176
+ if (start_offset <= source_length && end_offset <= source_length) {
177
+ position_T start_position = position_from_source_with_offset(source, start_offset);
178
+ position_T end_position = position_from_source_with_offset(source, end_offset);
179
+
180
+ location = location_create(start_position, end_position);
181
+ }
182
+ }
183
+ }
184
+
185
+ pm_node_destroy(&parser, root);
186
+ pm_parser_free(&parser);
187
+
188
+ return location;
189
+ }
190
+
191
+ location_T* get_then_keyword_location_wrapped(const char* source, bool is_in_clause) {
192
+ if (source == NULL) { return NULL; }
193
+
194
+ size_t source_length = strlen(source);
195
+
196
+ hb_buffer_T buffer;
197
+
198
+ if (!hb_buffer_init(&buffer, source_length + 16)) { return NULL; }
199
+
200
+ hb_buffer_append(&buffer, "case x\n");
201
+ size_t prefix_length = hb_buffer_length(&buffer);
202
+ hb_buffer_append(&buffer, source);
203
+ hb_buffer_append(&buffer, "\nend");
204
+
205
+ location_T* location =
206
+ parse_wrapped_and_find_then_keyword(&buffer, source, source_length, prefix_length, SIZE_MAX, 0);
207
+
208
+ free(buffer.value);
209
+
210
+ return location;
211
+ }
212
+
213
+ location_T* get_then_keyword_location_elsif_wrapped(const char* source) {
214
+ if (source == NULL) { return NULL; }
215
+
216
+ const char* elsif_position = strstr(source, "elsif");
217
+
218
+ if (elsif_position == NULL) { return NULL; }
219
+
220
+ size_t source_length = strlen(source);
221
+ size_t elsif_offset = (size_t) (elsif_position - source);
222
+ size_t replacement_diff = strlen("elsif") - strlen("if");
223
+
224
+ hb_buffer_T buffer;
225
+
226
+ if (!hb_buffer_init(&buffer, source_length + 8)) { return NULL; }
227
+
228
+ hb_buffer_append_with_length(&buffer, source, elsif_offset);
229
+ hb_buffer_append(&buffer, "if");
230
+ size_t if_end_offset = hb_buffer_length(&buffer);
231
+ hb_buffer_append(&buffer, source + elsif_offset + strlen("elsif"));
232
+ hb_buffer_append(&buffer, "\nend");
233
+
234
+ location_T* location =
235
+ parse_wrapped_and_find_then_keyword(&buffer, source, source_length, 0, if_end_offset, replacement_diff);
236
+
237
+ free(buffer.value);
238
+
239
+ return location;
240
+ }
@@ -45,6 +45,8 @@ static VALUE rb_<%= node.human %>_from_c_struct(<%= node.struct_type %>* <%= nod
45
45
  hb_string_T element_source_string = element_source_to_string(<%= node.human %>-><%= field.name %>);
46
46
  <%= node.human %>_<%= field.name %> = rb_utf8_str_new(element_source_string.data, element_source_string.length);
47
47
  }
48
+ <%- when Herb::Template::LocationField -%>
49
+ VALUE <%= node.human %>_<%= field.name %> = (<%= node.human %>-><%= field.name %> != NULL) ? rb_location_from_c_struct(*<%= node.human %>-><%= field.name %>) : Qnil;
48
50
  <%- else -%>
49
51
  /* <%= field.inspect %> */
50
52
  VALUE <%= node.human %>_<%= field.name %> = Qnil;
@@ -37,12 +37,14 @@ jobject <%= node.name %>FromCStruct(JNIEnv* env, <%= node.struct_type %>* <%= no
37
37
  <%- elsif field.is_a?(Herb::Template::ElementSourceField) -%>
38
38
  // TODO: Convert element_source to string
39
39
  jstring <%= field.name %> = (*env)->NewStringUTF(env, "");
40
+ <%- elsif field.is_a?(Herb::Template::LocationField) -%>
41
+ jobject <%= field.name %> = <%= node.human %>-><%= field.name %> ? CreateLocation(env, *<%= node.human %>-><%= field.name %>) : NULL;
40
42
  <%- elsif field.is_a?(Herb::Template::AnalyzedRubyField) || field.is_a?(Herb::Template::PrismNodeField) -%>
41
43
  // Skip <%= field.name %> (<%= field.class.name.split('::').last %>) - not supported in Java yet
42
44
  <%- end -%>
43
45
  <%- end -%>
44
46
 
45
- const char* signature = "(Ljava/lang/String;Lorg/herb/Location;Ljava/util/List;<%- node.fields.each do |f| -%><%- unless f.is_a?(Herb::Template::AnalyzedRubyField) || f.is_a?(Herb::Template::PrismNodeField) -%><%- if f.is_a?(Herb::Template::StringField) -%>Ljava/lang/String;<%- elsif f.is_a?(Herb::Template::TokenField) -%>Lorg/herb/Token;<%- elsif f.is_a?(Herb::Template::BooleanField) -%>Z<%- elsif f.is_a?(Herb::Template::ArrayField) -%>Ljava/util/List;<%- elsif f.is_a?(Herb::Template::NodeField) -%>Lorg/herb/ast/<%= f.specific_kind || 'Node' %>;<%- elsif f.is_a?(Herb::Template::ElementSourceField) -%>Ljava/lang/String;<%- end -%><%- end -%><%- end -%>)V";
47
+ const char* signature = "(Ljava/lang/String;Lorg/herb/Location;Ljava/util/List;<%- node.fields.each do |f| -%><%- unless f.is_a?(Herb::Template::AnalyzedRubyField) || f.is_a?(Herb::Template::PrismNodeField) -%><%- if f.is_a?(Herb::Template::StringField) -%>Ljava/lang/String;<%- elsif f.is_a?(Herb::Template::TokenField) -%>Lorg/herb/Token;<%- elsif f.is_a?(Herb::Template::BooleanField) -%>Z<%- elsif f.is_a?(Herb::Template::ArrayField) -%>Ljava/util/List;<%- elsif f.is_a?(Herb::Template::NodeField) -%>Lorg/herb/ast/<%= f.specific_kind || 'Node' %>;<%- elsif f.is_a?(Herb::Template::ElementSourceField) -%>Ljava/lang/String;<%- elsif f.is_a?(Herb::Template::LocationField) -%>Lorg/herb/Location;<%- end -%><%- end -%><%- end -%>)V";
46
48
  jmethodID constructor = (*env)->GetMethodID(env, nodeClass, "<init>", signature);
47
49
  if (!constructor) { return NULL; }
48
50
 
@@ -26,6 +26,8 @@ class <%= node.name %> extends BaseNode {
26
26
  <%- end -%>
27
27
  <%- elsif field.is_a?(Herb::Template::ElementSourceField) -%>
28
28
  private final String <%= field.name %>;
29
+ <%- elsif field.is_a?(Herb::Template::LocationField) -%>
30
+ private final Location <%= field.name %>;
29
31
  <%- else -%>
30
32
  // private final Object <%= field.name %>;
31
33
  <%- end -%>
@@ -50,6 +52,8 @@ class <%= node.name %> extends BaseNode {
50
52
  <%= field.specific_kind || 'Node' %> <%= field.name %><%- if node.fields.last != field %>,<% end %>
51
53
  <%- elsif field.is_a?(Herb::Template::ElementSourceField) -%>
52
54
  String <%= field.name %><%- if node.fields.last != field %>,<% end %>
55
+ <%- elsif field.is_a?(Herb::Template::LocationField) -%>
56
+ Location <%= field.name %><%- if node.fields.last != field %>,<% end %>
53
57
  <%- else -%>
54
58
  // <%= field.c_type %> <%= field.name %>
55
59
  <%- end -%>
@@ -101,6 +105,11 @@ class <%= node.name %> extends BaseNode {
101
105
  return <%= field.name %>;
102
106
  }
103
107
 
108
+ <%- elsif field.is_a?(Herb::Template::LocationField) -%>
109
+ public Location get<%= field.name.split('_').map(&:capitalize).join %>() {
110
+ return <%= field.name %>;
111
+ }
112
+
104
113
  <%- else -%>
105
114
  /*
106
115
  public Object get<%= field.name.split('_').map(&:capitalize).join %>() {
@@ -157,6 +166,8 @@ class <%= node.name %> extends BaseNode {
157
166
  output.append("<%= symbol %><%= field.name %>: ").append(<%= field.name %> != null ? <%= field.name %>.treeInspect() : "∅").append("\n");
158
167
  <%- elsif field.is_a?(Herb::Template::BooleanField) -%>
159
168
  output.append("<%= symbol %><%= field.name %>: ").append(<%= field.name %>).append("\n");
169
+ <%- elsif field.is_a?(Herb::Template::LocationField) -%>
170
+ output.append("<%= symbol %><%= field.name %>: ").append(<%= field.name %> != null ? "(location: " + <%= field.name %>.treeInspect() + ")" : "∅").append("\n");
160
171
  <%- elsif field.is_a?(Herb::Template::ArrayField) -%>
161
172
  output.append("<%= symbol %><%= field.name %>: ").append(inspectArray(<%= field.name %>, "<%= prefix %>"));
162
173
  <%- elsif field.is_a?(Herb::Template::NodeField) -%>
@@ -143,6 +143,8 @@ export interface Serialized<%= node.name %> extends SerializedNode {
143
143
  <%= field.name %>: boolean;
144
144
  <%- when Herb::Template::ElementSourceField -%>
145
145
  <%= field.name %>: string;
146
+ <%- when Herb::Template::LocationField -%>
147
+ <%= field.name %>: SerializedLocation | null;
146
148
  <%- when Herb::Template::NodeField -%>
147
149
  <%- if field.specific_kind -%>
148
150
  <%= field.name %>: Serialized<%= field.specific_kind %> | null;
@@ -170,6 +172,8 @@ export interface <%= node.name %>Props extends BaseNodeProps {
170
172
  <%= field.name %>: boolean;
171
173
  <%- when Herb::Template::ElementSourceField -%>
172
174
  <%= field.name %>: string;
175
+ <%- when Herb::Template::LocationField -%>
176
+ <%= field.name %>: Location | null;
173
177
  <%- when Herb::Template::NodeField -%>
174
178
  <%- if field.specific_kind -%>
175
179
  <%= field.name %>: <%= field.specific_kind %> | null;
@@ -201,6 +205,8 @@ export class <%= node.name %> extends Node {
201
205
  readonly <%= field.name %>: boolean;
202
206
  <%- when Herb::Template::ElementSourceField -%>
203
207
  readonly <%= field.name %>: string;
208
+ <%- when Herb::Template::LocationField -%>
209
+ readonly <%= field.name %>: Location | null;
204
210
  <%- when Herb::Template::NodeField -%>
205
211
  <%- if field.specific_kind -%>
206
212
  readonly <%= field.name %>: <%= field.specific_kind %> | null;
@@ -239,6 +245,8 @@ export class <%= node.name %> extends Node {
239
245
  <%= field.name %>: data.<%= field.name %>,
240
246
  <%- when Herb::Template::ElementSourceField -%>
241
247
  <%= field.name %>: data.<%= field.name %>,
248
+ <%- when Herb::Template::LocationField -%>
249
+ <%= field.name %>: data.<%= field.name %> ? Location.from(data.<%= field.name %>) : null,
242
250
  <%- when Herb::Template::NodeField -%>
243
251
  <%= field.name %>: data.<%= field.name %> ? fromSerializedNode((data.<%= field.name %>)) : null,
244
252
  <%- when Herb::Template::ArrayField -%>
@@ -268,6 +276,8 @@ export class <%= node.name %> extends Node {
268
276
  this.<%= field.name %> = props.<%= field.name %>;
269
277
  <%- when Herb::Template::ElementSourceField -%>
270
278
  this.<%= field.name %> = props.<%= field.name %>;
279
+ <%- when Herb::Template::LocationField -%>
280
+ this.<%= field.name %> = props.<%= field.name %>;
271
281
  <%- when Herb::Template::PrismNodeField, Herb::Template::AnalyzedRubyField -%>
272
282
  // no-op for <%= field.name %>
273
283
  <%- else -%>
@@ -325,6 +335,8 @@ export class <%= node.name %> extends Node {
325
335
  <%= field.name %>: this.<%= field.name %>,
326
336
  <%- when Herb::Template::ElementSourceField -%>
327
337
  <%= field.name %>: this.<%= field.name %>,
338
+ <%- when Herb::Template::LocationField -%>
339
+ <%= field.name %>: this.<%= field.name %> ? this.<%= field.name %>.toJSON() : null,
328
340
  <%- when Herb::Template::NodeField -%>
329
341
  <%= field.name %>: this.<%= field.name %> ? this.<%= field.name %>.toJSON() : null,
330
342
  <%- when Herb::Template::ArrayField -%>
@@ -355,6 +367,8 @@ export class <%= node.name %> extends Node {
355
367
  output += `<%= name %>${typeof this.<%= field.name %> === 'boolean' ? String(this.<%= field.name %>) : "∅"}\n`;
356
368
  <%- when Herb::Template::ElementSourceField -%>
357
369
  output += `<%= name %>${this.<%= field.name %> ? JSON.stringify(this.<%= field.name %>) : "∅"}\n`;
370
+ <%- when Herb::Template::LocationField -%>
371
+ output += `<%= name %>${this.<%= field.name %> ? "(location: " + this.<%= field.name %>.treeInspect() + ")" : "∅"}\n`;
358
372
  <%- when Herb::Template::NodeField -%>
359
373
  output += `<%= name %>${this.inspectNode(this.<%= field.name %>, "<%= (node.fields.last == field) ? " " : "│ " %>")}`;
360
374
  <%- when Herb::Template::ArrayField -%>
@@ -61,6 +61,15 @@ napi_value <%= node.human %>NodeFromCStruct(napi_env env, <%= node.struct_type %
61
61
  napi_value <%= field.name %> = CreateStringFromHbString(env, element_source_to_string(<%= node.human %>-><%= field.name %>));
62
62
  napi_set_named_property(env, result, "<%= field.name %>", <%= field.name %>);
63
63
 
64
+ <%- when Herb::Template::LocationField -%>
65
+ napi_value <%= field.name %>;
66
+ if (<%= node.human %>-><%= field.name %> != NULL) {
67
+ <%= field.name %> = CreateLocation(env, *<%= node.human %>-><%= field.name %>);
68
+ } else {
69
+ napi_get_null(env, &<%= field.name %>);
70
+ }
71
+ napi_set_named_property(env, result, "<%= field.name %>", <%= field.name %>);
72
+
64
73
  <%- else -%>
65
74
  napi_value <%= field.name %>;
66
75
  napi_get_null(env, &<%= field.name %>);
@@ -85,6 +85,10 @@ module Herb
85
85
  output += white("<%= symbol %> <%= field.name %>: ")
86
86
  output += [true, false].include?(<%= field.name %>) ? bold(magenta(<%= field.name %>.to_s)) : magenta("∅")
87
87
  output += "\n"
88
+ <%- when Herb::Template::LocationField -%>
89
+ output += white("<%= symbol %> <%= field.name %>: ")
90
+ output += <%= field.name %> ? dimmed("(location: #{<%= field.name %>.tree_inspect})") : magenta("∅")
91
+ output += "\n"
88
92
  <%- when Herb::Template::ElementSourceField -%>
89
93
  output += white("<%= symbol %> <%= field.name %>: #{green(<%= field.name %>.inspect)}\n")
90
94
  <%- when Herb::Template::PrismNodeField -%>
@@ -105,6 +105,14 @@ unsafe fn convert_token_field(token_ptr: *mut token_T) -> Option<crate::Token> {
105
105
  }
106
106
  }
107
107
 
108
+ unsafe fn convert_location_field(location_ptr: *const location_T) -> Option<Location> {
109
+ if location_ptr.is_null() {
110
+ None
111
+ } else {
112
+ Some(convert_location(*location_ptr))
113
+ }
114
+ }
115
+
108
116
  unsafe fn convert_children(children_array: *mut hb_array_T) -> Vec<AnyNode> {
109
117
  if children_array.is_null() {
110
118
  return Vec::new();
@@ -212,6 +220,8 @@ unsafe fn convert_node(node_ptr: *const c_void) -> Option<AnyNode> {
212
220
  <%- else -%>
213
221
  <%= field.name %>: convert_node_field((*c_node_ptr).<%= field.name %> as *mut c_void),
214
222
  <%- end -%>
223
+ <%- when Herb::Template::LocationField -%>
224
+ <%= field.name %>: convert_location_field((*c_node_ptr).<%= field.name %>),
215
225
  <%- end -%>
216
226
  <%- end -%>
217
227
  })
@@ -253,6 +253,8 @@ pub struct <%= node.name %> {
253
253
  <%- end -%>
254
254
  <%- when Herb::Template::ElementSourceField -%>
255
255
  pub <%= field.name %>: String,
256
+ <%- when Herb::Template::LocationField -%>
257
+ pub <%= field.name %>: Option<Location>,
256
258
  <%- end -%>
257
259
  <%- end -%>
258
260
  }
@@ -361,6 +363,8 @@ impl Node for <%= node.name %> {
361
363
  output.push_str(&format!("{}{}: {}", "<%= symbol %>".white(), "<%= field.name %>".white(), format_array_value(&self.<%= field.name %>, &"<%= is_last ? " " : "│ " %>".white().to_string())));
362
364
  <%- when Herb::Template::NodeField -%>
363
365
  output.push_str(&format!("{}{}: {}", "<%= symbol %>".white(), "<%= field.name %>".white(), format_node_value(&self.<%= field.name %>, &"<%= is_last ? " " : "│ " %>".white().to_string(), <%= !is_last %>)));
366
+ <%- when Herb::Template::LocationField -%>
367
+ output.push_str(&format!("{}{}: {}\n", "<%= symbol %>".white(), "<%= field.name %>".white(), self.<%= field.name %>.as_ref().map(|l| format!("(location: {})", l).dimmed().to_string()).unwrap_or_else(|| "∅".magenta().to_string())));
364
368
  <%- end -%>
365
369
  <%- end -%>
366
370
  <%- else -%>
@@ -40,6 +40,8 @@
40
40
  <%= node.human %>-><%= field.name %> = <%= field.name %>;
41
41
  <%- when Herb::Template::VoidPointerField -%>
42
42
  <%= node.human %>-><%= field.name %> = <%= field.name %>;
43
+ <%- when Herb::Template::LocationField -%>
44
+ <%= node.human %>-><%= field.name %> = <%= field.name %>;
43
45
  <%- else -%>
44
46
  <%= field.inspect %>
45
47
  <%- end -%>
@@ -123,6 +125,8 @@ static void ast_free_<%= node.human %>(<%= node.struct_type %>* <%= node.human %
123
125
  free(<%= node.human %>-><%= field.name %>);
124
126
  <%- when Herb::Template::BooleanField -%>
125
127
  <%- when Herb::Template::ElementSourceField -%>
128
+ <%- when Herb::Template::LocationField -%>
129
+ if (<%= node.human %>-><%= field.name %> != NULL) { free(<%= node.human %>-><%= field.name %>); }
126
130
  <%- else -%>
127
131
  <%= field.inspect %>
128
132
  <%- end -%>
@@ -87,6 +87,20 @@ void ast_pretty_print_node(AST_NODE_T* node, const size_t indent, const size_t r
87
87
  pretty_print_label(hb_string("<%= field.name %>"), indent, relative_indent, <%= last %>, buffer);
88
88
  hb_buffer_append(buffer, " ?\n");
89
89
 
90
+ <%- when Herb::Template::LocationField -%>
91
+ pretty_print_label(hb_string("<%= field.name %>"), indent, relative_indent, <%= last %>, buffer);
92
+ if (<%= node.human %>-><%= field.name %>) {
93
+ char <%= field.name %>_location_string[128];
94
+ sprintf(<%= field.name %>_location_string, " (location: (%u:%u)-(%u:%u))\n",
95
+ <%= node.human %>-><%= field.name %>->start.line,
96
+ <%= node.human %>-><%= field.name %>->start.column,
97
+ <%= node.human %>-><%= field.name %>->end.line,
98
+ <%= node.human %>-><%= field.name %>->end.column);
99
+ hb_buffer_append(buffer, <%= field.name %>_location_string);
100
+ } else {
101
+ hb_buffer_append(buffer, " ∅\n");
102
+ }
103
+
90
104
  <%- else -%>
91
105
  <%= field.inspect %>
92
106
  <%- end -%>
@@ -117,6 +117,16 @@ module Herb
117
117
  end
118
118
  end
119
119
 
120
+ class LocationField < Field
121
+ def ruby_type
122
+ "Herb::Location"
123
+ end
124
+
125
+ def c_type
126
+ "location_T*"
127
+ end
128
+ end
129
+
120
130
  class IntegerField < Field
121
131
  def ruby_type
122
132
  "Integer"
@@ -210,6 +220,7 @@ module Herb
210
220
  when "token_type" then TokenTypeField
211
221
  when "string" then StringField
212
222
  when "position" then PositionField
223
+ when "location" then LocationField
213
224
  when "size_t" then SizeTField
214
225
  when "boolean" then BooleanField
215
226
  when "prism_node" then PrismNodeField
@@ -39,6 +39,12 @@ val <%= node.name %>FromCStruct(<%= node.struct_type %>* <%= node.human %>) {
39
39
  result.set("<%= field.name %>", NodesArrayFromCArray(<%= node.human %>-><%= field.name %>));
40
40
  <%- when Herb::Template::ElementSourceField -%>
41
41
  result.set("<%= field.name %>", CreateStringFromHbString(element_source_to_string(<%= node.human %>-><%= field.name %>)));
42
+ <%- when Herb::Template::LocationField -%>
43
+ if (<%= node.human %>-><%= field.name %>) {
44
+ result.set("<%= field.name %>", CreateLocation(*<%= node.human %>-><%= field.name %>));
45
+ } else {
46
+ result.set("<%= field.name %>", val::null());
47
+ }
42
48
  <%- else -%>
43
49
  result.set("<%= field.name %>", val::null());
44
50
  <%- end -%>
@@ -14,7 +14,7 @@
14
14
  /**
15
15
  * The minor version of the Prism library as an int.
16
16
  */
17
- #define PRISM_VERSION_MINOR 7
17
+ #define PRISM_VERSION_MINOR 8
18
18
 
19
19
  /**
20
20
  * The patch version of the Prism library as an int.
@@ -24,6 +24,6 @@
24
24
  /**
25
25
  * The version of the Prism library as a constant string.
26
26
  */
27
- #define PRISM_VERSION "1.7.0"
27
+ #define PRISM_VERSION "1.8.0"
28
28
 
29
29
  #endif