herb 0.8.10-arm-linux-gnu → 0.9.0-arm-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 (209) hide show
  1. checksums.yaml +4 -4
  2. data/Makefile +11 -3
  3. data/README.md +64 -34
  4. data/Rakefile +48 -40
  5. data/config.yml +317 -34
  6. data/ext/herb/error_helpers.c +367 -140
  7. data/ext/herb/error_helpers.h +1 -0
  8. data/ext/herb/extconf.rb +67 -28
  9. data/ext/herb/extension.c +317 -51
  10. data/ext/herb/extension.h +1 -0
  11. data/ext/herb/extension_helpers.c +23 -14
  12. data/ext/herb/extension_helpers.h +2 -2
  13. data/ext/herb/nodes.c +537 -270
  14. data/ext/herb/nodes.h +1 -0
  15. data/herb.gemspec +3 -2
  16. data/lib/herb/3.0/herb.so +0 -0
  17. data/lib/herb/3.1/herb.so +0 -0
  18. data/lib/herb/3.2/herb.so +0 -0
  19. data/lib/herb/3.3/herb.so +0 -0
  20. data/lib/herb/3.4/herb.so +0 -0
  21. data/lib/herb/4.0/herb.so +0 -0
  22. data/lib/herb/ast/helpers.rb +3 -3
  23. data/lib/herb/ast/node.rb +15 -2
  24. data/lib/herb/ast/nodes.rb +1132 -157
  25. data/lib/herb/bootstrap.rb +87 -0
  26. data/lib/herb/cli.rb +341 -31
  27. data/lib/herb/configuration.rb +248 -0
  28. data/lib/herb/defaults.yml +32 -0
  29. data/lib/herb/engine/compiler.rb +78 -11
  30. data/lib/herb/engine/debug_visitor.rb +13 -3
  31. data/lib/herb/engine/error_formatter.rb +13 -9
  32. data/lib/herb/engine/parser_error_overlay.rb +10 -6
  33. data/lib/herb/engine/validator.rb +8 -3
  34. data/lib/herb/engine/validators/nesting_validator.rb +2 -2
  35. data/lib/herb/engine.rb +82 -35
  36. data/lib/herb/errors.rb +563 -88
  37. data/lib/herb/lex_result.rb +1 -0
  38. data/lib/herb/location.rb +7 -3
  39. data/lib/herb/parse_result.rb +12 -2
  40. data/lib/herb/parser_options.rb +57 -0
  41. data/lib/herb/position.rb +1 -0
  42. data/lib/herb/prism_inspect.rb +116 -0
  43. data/lib/herb/project.rb +923 -331
  44. data/lib/herb/range.rb +1 -0
  45. data/lib/herb/token.rb +7 -1
  46. data/lib/herb/version.rb +1 -1
  47. data/lib/herb/visitor.rb +37 -2
  48. data/lib/herb/warnings.rb +6 -1
  49. data/lib/herb.rb +35 -3
  50. data/sig/herb/ast/helpers.rbs +2 -2
  51. data/sig/herb/ast/node.rbs +12 -2
  52. data/sig/herb/ast/nodes.rbs +641 -128
  53. data/sig/herb/bootstrap.rbs +31 -0
  54. data/sig/herb/configuration.rbs +89 -0
  55. data/sig/herb/engine/compiler.rbs +9 -1
  56. data/sig/herb/engine/debug_visitor.rbs +2 -0
  57. data/sig/herb/engine/validator.rbs +5 -1
  58. data/sig/herb/engine.rbs +17 -3
  59. data/sig/herb/errors.rbs +258 -63
  60. data/sig/herb/location.rbs +4 -0
  61. data/sig/herb/parse_result.rbs +4 -2
  62. data/sig/herb/parser_options.rbs +42 -0
  63. data/sig/herb/position.rbs +1 -0
  64. data/sig/herb/prism_inspect.rbs +28 -0
  65. data/sig/herb/range.rbs +1 -0
  66. data/sig/herb/token.rbs +6 -0
  67. data/sig/herb/visitor.rbs +25 -4
  68. data/sig/herb/warnings.rbs +6 -1
  69. data/sig/herb.rbs +14 -0
  70. data/sig/herb_c_extension.rbs +5 -2
  71. data/sig/serialized_ast_errors.rbs +54 -6
  72. data/sig/serialized_ast_nodes.rbs +60 -6
  73. data/src/analyze/action_view/attribute_extraction_helpers.c +290 -0
  74. data/src/analyze/action_view/content_tag.c +70 -0
  75. data/src/analyze/action_view/link_to.c +143 -0
  76. data/src/analyze/action_view/registry.c +60 -0
  77. data/src/analyze/action_view/tag.c +64 -0
  78. data/src/analyze/action_view/tag_helper_node_builders.c +305 -0
  79. data/src/analyze/action_view/tag_helpers.c +748 -0
  80. data/src/analyze/action_view/turbo_frame_tag.c +88 -0
  81. data/src/analyze/analyze.c +882 -0
  82. data/src/{analyzed_ruby.c → analyze/analyzed_ruby.c} +13 -11
  83. data/src/analyze/builders.c +343 -0
  84. data/src/analyze/conditional_elements.c +594 -0
  85. data/src/analyze/conditional_open_tags.c +640 -0
  86. data/src/analyze/control_type.c +250 -0
  87. data/src/{analyze_helpers.c → analyze/helpers.c} +48 -23
  88. data/src/analyze/invalid_structures.c +193 -0
  89. data/src/{analyze_missing_end.c → analyze/missing_end.c} +33 -22
  90. data/src/analyze/parse_errors.c +84 -0
  91. data/src/analyze/prism_annotate.c +397 -0
  92. data/src/{analyze_transform.c → analyze/transform.c} +17 -3
  93. data/src/ast_node.c +17 -7
  94. data/src/ast_nodes.c +662 -387
  95. data/src/ast_pretty_print.c +190 -6
  96. data/src/errors.c +1076 -520
  97. data/src/extract.c +145 -49
  98. data/src/herb.c +52 -34
  99. data/src/html_util.c +241 -12
  100. data/src/include/analyze/action_view/attribute_extraction_helpers.h +36 -0
  101. data/src/include/analyze/action_view/tag_helper_handler.h +41 -0
  102. data/src/include/analyze/action_view/tag_helper_node_builders.h +70 -0
  103. data/src/include/analyze/action_view/tag_helpers.h +38 -0
  104. data/src/include/{analyze.h → analyze/analyze.h} +14 -4
  105. data/src/include/{analyzed_ruby.h → analyze/analyzed_ruby.h} +3 -3
  106. data/src/include/analyze/builders.h +27 -0
  107. data/src/include/analyze/conditional_elements.h +9 -0
  108. data/src/include/analyze/conditional_open_tags.h +9 -0
  109. data/src/include/analyze/control_type.h +14 -0
  110. data/src/include/{analyze_helpers.h → analyze/helpers.h} +4 -2
  111. data/src/include/analyze/invalid_structures.h +11 -0
  112. data/src/include/analyze/prism_annotate.h +16 -0
  113. data/src/include/ast_node.h +11 -5
  114. data/src/include/ast_nodes.h +117 -38
  115. data/src/include/ast_pretty_print.h +5 -0
  116. data/src/include/element_source.h +3 -8
  117. data/src/include/errors.h +148 -55
  118. data/src/include/extract.h +21 -5
  119. data/src/include/herb.h +18 -6
  120. data/src/include/herb_prism_node.h +13 -0
  121. data/src/include/html_util.h +7 -2
  122. data/src/include/io.h +3 -1
  123. data/src/include/lex_helpers.h +29 -0
  124. data/src/include/lexer.h +1 -1
  125. data/src/include/lexer_peek_helpers.h +87 -13
  126. data/src/include/lexer_struct.h +2 -0
  127. data/src/include/location.h +2 -1
  128. data/src/include/parser.h +27 -2
  129. data/src/include/parser_helpers.h +19 -3
  130. data/src/include/pretty_print.h +10 -5
  131. data/src/include/prism_context.h +45 -0
  132. data/src/include/prism_helpers.h +10 -7
  133. data/src/include/prism_serialized.h +12 -0
  134. data/src/include/token.h +16 -4
  135. data/src/include/token_struct.h +10 -3
  136. data/src/include/utf8.h +2 -1
  137. data/src/include/util/hb_allocator.h +78 -0
  138. data/src/include/util/hb_arena.h +6 -1
  139. data/src/include/util/hb_arena_debug.h +12 -1
  140. data/src/include/util/hb_array.h +7 -3
  141. data/src/include/util/hb_buffer.h +6 -4
  142. data/src/include/util/hb_foreach.h +79 -0
  143. data/src/include/util/hb_narray.h +8 -4
  144. data/src/include/util/hb_string.h +56 -9
  145. data/src/include/util.h +6 -3
  146. data/src/include/version.h +1 -1
  147. data/src/io.c +3 -2
  148. data/src/lexer.c +42 -30
  149. data/src/lexer_peek_helpers.c +12 -74
  150. data/src/location.c +2 -2
  151. data/src/main.c +53 -28
  152. data/src/parser.c +783 -247
  153. data/src/parser_helpers.c +110 -23
  154. data/src/parser_match_tags.c +109 -48
  155. data/src/pretty_print.c +29 -24
  156. data/src/prism_helpers.c +30 -27
  157. data/src/ruby_parser.c +2 -0
  158. data/src/token.c +151 -66
  159. data/src/token_matchers.c +0 -1
  160. data/src/utf8.c +7 -6
  161. data/src/util/hb_allocator.c +341 -0
  162. data/src/util/hb_arena.c +81 -56
  163. data/src/util/hb_arena_debug.c +32 -17
  164. data/src/util/hb_array.c +30 -15
  165. data/src/util/hb_buffer.c +17 -21
  166. data/src/util/hb_narray.c +22 -7
  167. data/src/util/hb_string.c +49 -35
  168. data/src/util.c +21 -11
  169. data/src/visitor.c +47 -0
  170. data/templates/ext/herb/error_helpers.c.erb +24 -11
  171. data/templates/ext/herb/error_helpers.h.erb +1 -0
  172. data/templates/ext/herb/nodes.c.erb +50 -16
  173. data/templates/ext/herb/nodes.h.erb +1 -0
  174. data/templates/java/error_helpers.c.erb +1 -1
  175. data/templates/java/nodes.c.erb +30 -8
  176. data/templates/java/org/herb/ast/Errors.java.erb +24 -1
  177. data/templates/java/org/herb/ast/Nodes.java.erb +80 -21
  178. data/templates/javascript/packages/core/src/errors.ts.erb +16 -3
  179. data/templates/javascript/packages/core/src/node-type-guards.ts.erb +3 -1
  180. data/templates/javascript/packages/core/src/nodes.ts.erb +109 -32
  181. data/templates/javascript/packages/node/extension/error_helpers.cpp.erb +13 -4
  182. data/templates/javascript/packages/node/extension/nodes.cpp.erb +43 -4
  183. data/templates/lib/herb/ast/nodes.rb.erb +88 -31
  184. data/templates/lib/herb/errors.rb.erb +15 -3
  185. data/templates/lib/herb/visitor.rb.erb +2 -2
  186. data/templates/rust/src/ast/nodes.rs.erb +97 -44
  187. data/templates/rust/src/errors.rs.erb +2 -1
  188. data/templates/rust/src/nodes.rs.erb +167 -15
  189. data/templates/rust/src/union_types.rs.erb +60 -0
  190. data/templates/rust/src/visitor.rs.erb +81 -0
  191. data/templates/src/{analyze_missing_end.c.erb → analyze/missing_end.c.erb} +9 -6
  192. data/templates/src/{analyze_transform.c.erb → analyze/transform.c.erb} +2 -2
  193. data/templates/src/ast_nodes.c.erb +34 -26
  194. data/templates/src/ast_pretty_print.c.erb +24 -5
  195. data/templates/src/errors.c.erb +60 -54
  196. data/templates/src/include/ast_nodes.h.erb +6 -2
  197. data/templates/src/include/ast_pretty_print.h.erb +5 -0
  198. data/templates/src/include/errors.h.erb +15 -11
  199. data/templates/src/include/util/hb_foreach.h.erb +20 -0
  200. data/templates/src/parser_match_tags.c.erb +10 -4
  201. data/templates/src/visitor.c.erb +2 -2
  202. data/templates/template.rb +204 -29
  203. data/templates/wasm/error_helpers.cpp.erb +9 -5
  204. data/templates/wasm/nodes.cpp.erb +41 -4
  205. metadata +57 -16
  206. data/src/analyze.c +0 -1608
  207. data/src/element_source.c +0 -12
  208. data/src/include/util/hb_system.h +0 -9
  209. data/src/util/hb_system.c +0 -30
@@ -5,8 +5,10 @@
5
5
  #include "location.h"
6
6
  #include "position.h"
7
7
  #include "token.h"
8
+ #include "util/hb_allocator.h"
8
9
  #include "util/hb_array.h"
9
10
  #include "util/hb_buffer.h"
11
+ #include "util/hb_string.h"
10
12
 
11
13
  typedef enum {
12
14
  <%- errors.each do |error| -%>
@@ -17,7 +19,7 @@ typedef enum {
17
19
  typedef struct ERROR_STRUCT {
18
20
  error_type_T type;
19
21
  location_T location;
20
- char* message;
22
+ hb_string_T message;
21
23
  } ERROR_T;
22
24
 
23
25
  <%- errors.each do |error| -%>
@@ -31,7 +33,7 @@ typedef struct {
31
33
 
32
34
  <%- errors.each do |error| -%>
33
35
  <%- error_arguments = error.fields.any? ? error.fields.map { |field| [field.c_type, " ", field.name].join } : [] -%>
34
- <%- arguments = error_arguments + ["position_T start", "position_T end"] -%>
36
+ <%- arguments = error_arguments + ["position_T start", "position_T end", "hb_allocator_T* allocator"] -%>
35
37
  <%= error.struct_type %>* <%= error.human %>_init(<%= arguments.join(", ") %>);
36
38
  void append_<%= error.human %>(<%= (arguments << "hb_array_T* errors").join(", ") %>);
37
39
  <%- end -%>
@@ -41,18 +43,20 @@ void error_init(ERROR_T* error, error_type_T type, position_T start, position_T
41
43
  size_t error_sizeof(void);
42
44
  error_type_T error_type(ERROR_T* error);
43
45
 
44
- char* error_message(ERROR_T* error);
46
+ hb_string_T error_message(ERROR_T* error);
45
47
 
46
- const char* error_type_to_string(ERROR_T* error);
47
- const char* error_human_type(ERROR_T* error);
48
+ hb_string_T error_type_to_string(ERROR_T* error);
49
+ hb_string_T error_human_type(ERROR_T* error);
48
50
 
49
- void error_free(ERROR_T* error);
51
+ void error_free(ERROR_T* error, hb_allocator_T* allocator);
50
52
 
51
- void error_pretty_print(ERROR_T* error, size_t indent, size_t relative_indent, hb_buffer_T* buffer);
53
+ #ifndef HERB_EXCLUDE_PRETTYPRINT
54
+ void error_pretty_print(ERROR_T* error, size_t indent, size_t relative_indent, hb_buffer_T* buffer);
52
55
 
53
- void error_pretty_print_array(
54
- const char* name, hb_array_T* array, size_t indent, size_t relative_indent, bool last_property,
55
- hb_buffer_T* buffer
56
- );
56
+ void error_pretty_print_array(
57
+ const char* name, hb_array_T* array, size_t indent, size_t relative_indent, bool last_property,
58
+ hb_buffer_T* buffer
59
+ );
60
+ #endif
57
61
 
58
62
  #endif
@@ -0,0 +1,20 @@
1
+ <% max = 64 -%>
2
+ #ifndef HB_FOREACH_H
3
+ #define HB_FOREACH_H
4
+
5
+ #define HB_ARG<%= max %>(<%= (1..max).map { |i| "_#{i}" }.join(",") %>,count,...) count
6
+ #define HB_NARGS(...) HB_ARG<%= max %>(__VA_ARGS__,<%= max.downto(1).to_a.join(",") %>)
7
+
8
+ <% (1..max).each do |i| -%>
9
+ <% if i == 1 -%>
10
+ #define HB_FOR_EACH_1(macro, head) macro(head)
11
+ <% else -%>
12
+ #define HB_FOR_EACH_<%= i %>(macro, head, ...) macro(head), HB_FOR_EACH_<%= i - 1 %>(macro, __VA_ARGS__)
13
+ <% end -%>
14
+ <% end -%>
15
+
16
+ #define HB_CONCAT(left, right) left ## right
17
+ #define HB_FOR_EACH_N(count) HB_CONCAT(HB_FOR_EACH_, count)
18
+ #define HB_FOR_EACH(macro, ...) HB_FOR_EACH_N(HB_NARGS(__VA_ARGS__))(macro, __VA_ARGS__)
19
+
20
+ #endif
@@ -4,14 +4,20 @@
4
4
  #include "include/visitor.h"
5
5
 
6
6
  bool match_tags_visitor(const AST_NODE_T* node, void* data) {
7
- hb_array_T* errors = (hb_array_T*) data;
7
+ match_tags_context_T* context = (match_tags_context_T*) data;
8
8
 
9
9
  if (node == NULL) { return false; }
10
10
 
11
11
  switch (node->type) {
12
12
  <%- nodes.each do |node| -%>
13
13
  <%- array_fields = node.fields.select { |f| f.is_a?(Herb::Template::ArrayField) && f.name != "errors" } -%>
14
- <%- single_node_fields = node.fields.select { |f| f.is_a?(Herb::Template::NodeField) } -%>
14
+
15
+ <%- skip_fields = case node.name
16
+ when "HTMLConditionalElementNode" then ["open_tag", "close_tag", "open_conditional", "close_conditional"]
17
+ when "HTMLConditionalOpenTagNode" then ["conditional"]
18
+ else []
19
+ end -%>
20
+ <%- single_node_fields = node.fields.select { |f| f.is_a?(Herb::Template::NodeField) && !skip_fields.include?(f.name) } -%>
15
21
 
16
22
  <%- if array_fields.any? || single_node_fields.any? -%>
17
23
  case <%= node.type %>: {
@@ -19,12 +25,12 @@ bool match_tags_visitor(const AST_NODE_T* node, void* data) {
19
25
 
20
26
  <%- array_fields.each do |field| -%>
21
27
  if (<%= node.human %>-><%= field.name %> != NULL) {
22
- match_tags_in_node_array(<%= node.human %>-><%= field.name %>, errors);
28
+ match_tags_in_node_array(<%= node.human %>-><%= field.name %>, context->errors, context->options, context->allocator);
23
29
  }
24
30
  <%- end -%>
25
31
  <%- single_node_fields.each do |field| -%>
26
32
  if (<%= node.human %>-><%= field.name %> != NULL) {
27
- herb_visit_node((AST_NODE_T*) <%= node.human %>-><%= field.name %>, match_tags_visitor, errors);
33
+ herb_visit_node((AST_NODE_T*) <%= node.human %>-><%= field.name %>, match_tags_visitor, context);
28
34
  }
29
35
  <%- end -%>
30
36
  } break;
@@ -18,13 +18,13 @@ void herb_visit_child_nodes(const AST_NODE_T *node, bool (*visitor)(const AST_NO
18
18
 
19
19
  switch (node->type) {
20
20
  <%- nodes.each do |node| -%>
21
- <%- if node.fields.count { |field| [Herb::Template::NodeField, Herb::Template::ArrayField].include?(field.class) }.positive? -%>
21
+ <%- if node.fields.count { |field| [Herb::Template::NodeField, Herb::Template::BorrowedNodeField, Herb::Template::ArrayField].include?(field.class) }.positive? -%>
22
22
  case <%= node.type %>: {
23
23
  const <%= node.struct_type %>* <%= node.human %> = ((const <%= node.struct_type %> *) node);
24
24
 
25
25
  <%- node.fields.each do |field| -%>
26
26
  <%- case field -%>
27
- <%- when Herb::Template::NodeField -%>
27
+ <%- when Herb::Template::NodeField, Herb::Template::BorrowedNodeField -%>
28
28
  if (<%= node.human %>-><%= field.name %> != NULL) {
29
29
  herb_visit_node((AST_NODE_T *) <%= node.human %>-><%= field.name %>, visitor, data);
30
30
  }
@@ -7,6 +7,16 @@ require "yaml"
7
7
 
8
8
  module Herb
9
9
  module Template
10
+ def self.underscore(name)
11
+ name
12
+ .gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
13
+ .gsub(/([a-z\d])([A-Z])/, '\1_\2')
14
+ end
15
+
16
+ ALWAYS_INVISIBLE_FIELD_CLASSES = ["PrismContextField", "AnalyzedRubyField"].freeze
17
+ CONDITIONALLY_INVISIBLE_FIELD_CLASSES = ["PrismSerializedField", "PrismNodeField"].freeze
18
+ ALL_INVISIBLE_FIELD_CLASSES = (ALWAYS_INVISIBLE_FIELD_CLASSES + CONDITIONALLY_INVISIBLE_FIELD_CLASSES).freeze
19
+
10
20
  class Field
11
21
  attr_reader :name, :options
12
22
 
@@ -14,6 +24,45 @@ module Herb
14
24
  @name = name
15
25
  @options = options
16
26
  end
27
+
28
+ def always_invisible?
29
+ ALWAYS_INVISIBLE_FIELD_CLASSES.include?(self.class.name.split("::").last)
30
+ end
31
+
32
+ def conditionally_invisible?
33
+ CONDITIONALLY_INVISIBLE_FIELD_CLASSES.include?(self.class.name.split("::").last)
34
+ end
35
+
36
+ def invisible?
37
+ ALL_INVISIBLE_FIELD_CLASSES.include?(self.class.name.split("::").last)
38
+ end
39
+ end
40
+
41
+ class FieldVisibility
42
+ attr_reader :field, :prism_field_name
43
+
44
+ def initialize(field:, static_last:, dynamic_last:, prism_field_name:)
45
+ @field = field
46
+ @static_last = static_last
47
+ @dynamic_last = dynamic_last
48
+ @prism_field_name = prism_field_name
49
+ end
50
+
51
+ def static_last?
52
+ @static_last
53
+ end
54
+
55
+ def dynamic_last?
56
+ @dynamic_last
57
+ end
58
+
59
+ def symbol
60
+ @static_last ? "└── " : "├── "
61
+ end
62
+
63
+ def prefix
64
+ @static_last ? " " : "│ "
65
+ end
17
66
  end
18
67
 
19
68
  class ArrayField < Field
@@ -23,10 +72,17 @@ module Herb
23
72
  end
24
73
 
25
74
  def ruby_type
26
- return "Array" unless specific_kind
27
- return "Array[Herb::AST::#{specific_kind}]" if specific_kind.end_with?("Node")
28
-
29
- "Array[#{specific_kind}]"
75
+ if specific_kind
76
+ if specific_kind.end_with?("Node")
77
+ "Array[Herb::AST::#{specific_kind}]"
78
+ else
79
+ "Array[#{specific_kind}]"
80
+ end
81
+ elsif union_kind
82
+ "Array[(#{union_kind.map { |k| "Herb::AST::#{k}" }.join(" | ")})]"
83
+ else
84
+ "Array"
85
+ end
30
86
  end
31
87
 
32
88
  def c_type
@@ -35,7 +91,7 @@ module Herb
35
91
 
36
92
  def c_item_type
37
93
  if specific_kind
38
- "AST_#{specific_kind.gsub(/(?<=[a-zA-Z])(?=[A-Z][a-z])/, "_").upcase}_T*"
94
+ "AST_#{Template.underscore(specific_kind).upcase}_T*"
39
95
  else
40
96
  "void*"
41
97
  end
@@ -58,14 +114,20 @@ module Herb
58
114
 
59
115
  def c_type
60
116
  if specific_kind
61
- "struct AST_#{specific_kind.gsub(/(?<=[a-zA-Z])(?=[A-Z][a-z])/, "_").upcase}_STRUCT*"
117
+ "struct AST_#{Template.underscore(specific_kind).upcase}_STRUCT*"
62
118
  else
63
119
  "AST_NODE_T*"
64
120
  end
65
121
  end
66
122
 
67
123
  def ruby_type
68
- "Herb::AST::#{specific_kind || "Node"}"
124
+ if specific_kind
125
+ "Herb::AST::#{specific_kind}"
126
+ elsif union_kind
127
+ "(#{union_kind.map { |k| "Herb::AST::#{k}" }.join(" | ")})"
128
+ else
129
+ "Herb::AST::Node"
130
+ end
69
131
  end
70
132
 
71
133
  def specific_kind
@@ -75,6 +137,29 @@ module Herb
75
137
  def union_kind
76
138
  @kind if @kind.is_a?(Array)
77
139
  end
140
+
141
+ def union_type_name
142
+ return nil unless union_kind
143
+
144
+ union_kind.sort.join("Or")
145
+ end
146
+
147
+ def rust_type
148
+ if specific_kind && specific_kind != "Node"
149
+ "Option<Box<#{specific_kind}>>"
150
+ elsif union_kind
151
+ "Option<#{union_type_name}>"
152
+ else
153
+ "Option<Box<AnyNode>>"
154
+ end
155
+ end
156
+
157
+ def java_type
158
+ specific_kind || "Node" # Java uses base type for union_kind, could use sealed interface in future
159
+ end
160
+ end
161
+
162
+ class BorrowedNodeField < NodeField
78
163
  end
79
164
 
80
165
  class TokenField < Field
@@ -103,7 +188,7 @@ module Herb
103
188
  end
104
189
 
105
190
  def c_type
106
- "const char*"
191
+ "hb_string_T"
107
192
  end
108
193
  end
109
194
 
@@ -113,7 +198,7 @@ module Herb
113
198
  end
114
199
 
115
200
  def c_type
116
- "position_T*"
201
+ "position_T"
117
202
  end
118
203
  end
119
204
 
@@ -159,11 +244,33 @@ module Herb
159
244
 
160
245
  class PrismNodeField < Field
161
246
  def ruby_type
162
- "Prism::Node"
247
+ "String"
248
+ end
249
+
250
+ def c_type
251
+ "herb_prism_node_T"
252
+ end
253
+
254
+ def rust_type
255
+ "Option<Vec<u8>>"
256
+ end
257
+
258
+ def java_type
259
+ "byte[]"
260
+ end
261
+
262
+ def js_type
263
+ "Uint8Array | null"
264
+ end
265
+ end
266
+
267
+ class PrismContextField < Field
268
+ def ruby_type
269
+ "nil"
163
270
  end
164
271
 
165
272
  def c_type
166
- "pm_node_t*"
273
+ "herb_prism_context_T*"
167
274
  end
168
275
  end
169
276
 
@@ -187,13 +294,35 @@ module Herb
187
294
  end
188
295
  end
189
296
 
297
+ class PrismSerializedField < Field
298
+ def ruby_type
299
+ "String"
300
+ end
301
+
302
+ def c_type
303
+ "prism_serialized_T"
304
+ end
305
+
306
+ def java_type
307
+ "byte[]"
308
+ end
309
+
310
+ def rust_type
311
+ "Option<Vec<u8>>"
312
+ end
313
+
314
+ def js_type
315
+ "Uint8Array | null"
316
+ end
317
+ end
318
+
190
319
  class ElementSourceField < Field
191
320
  def ruby_type
192
321
  "String"
193
322
  end
194
323
 
195
324
  def c_type
196
- "element_source_t"
325
+ "hb_string_T"
197
326
  end
198
327
  end
199
328
 
@@ -214,19 +343,22 @@ module Herb
214
343
 
215
344
  def field_type_for(name)
216
345
  case name
217
- when "array" then ArrayField
218
- when "node" then NodeField
219
- when "token" then TokenField
220
- when "token_type" then TokenTypeField
221
- when "string" then StringField
222
- when "position" then PositionField
223
- when "location" then LocationField
224
- when "size_t" then SizeTField
225
- when "boolean" then BooleanField
226
- when "prism_node" then PrismNodeField
227
- when "analyzed_ruby" then AnalyzedRubyField
228
- when "element_source" then ElementSourceField
229
- when "void*" then VoidPointerField
346
+ when "array" then ArrayField
347
+ when "node" then NodeField
348
+ when "borrowed_node" then BorrowedNodeField
349
+ when "token" then TokenField
350
+ when "token_type" then TokenTypeField
351
+ when "string" then StringField
352
+ when "position" then PositionField
353
+ when "location" then LocationField
354
+ when "size_t" then SizeTField
355
+ when "boolean" then BooleanField
356
+ when "prism_node" then PrismNodeField
357
+ when "prism_context" then PrismContextField
358
+ when "analyzed_ruby" then AnalyzedRubyField
359
+ when "prism_serialized" then PrismSerializedField
360
+ when "element_source" then ElementSourceField
361
+ when "void*" then VoidPointerField
230
362
  else raise("Unknown field type: #{name.inspect}")
231
363
  end
232
364
  end
@@ -242,7 +374,7 @@ module Herb
242
374
  @message_template = config.dig("message", "template")
243
375
  @message_arguments = config.dig("message", "arguments")
244
376
 
245
- camelized = @name.gsub(/(?<=[a-zA-Z])(?=[A-Z][a-z])/, "_")
377
+ camelized = Template.underscore(@name)
246
378
  @type = camelized.upcase
247
379
  @struct_type = "#{camelized.upcase}_T"
248
380
  @struct_name = "#{camelized.upcase}_STRUCT"
@@ -269,7 +401,7 @@ module Herb
269
401
 
270
402
  def initialize(config)
271
403
  @name = config.fetch("name")
272
- camelized = @name.gsub(/(?<=[a-zA-Z])(?=[A-Z][a-z])/, "_")
404
+ camelized = Template.underscore(@name)
273
405
  @type = "AST_#{camelized.upcase}"
274
406
  @struct_type = "AST_#{camelized.upcase}_T"
275
407
  @struct_name = "AST_#{camelized.upcase}_STRUCT"
@@ -287,6 +419,34 @@ module Herb
287
419
  def c_type
288
420
  @struct_type
289
421
  end
422
+
423
+ def field_visibilities
424
+ @field_visibilities ||= begin
425
+ prism_fields = @fields.select(&:conditionally_invisible?)
426
+ last_prism_field_name = prism_fields.last&.name
427
+
428
+ @fields.each_with_index.map do |field, index|
429
+ remaining = @fields[(index + 1)..] || []
430
+ all_remaining_always_invisible = remaining.all?(&:always_invisible?)
431
+ all_remaining_invisible = remaining.all?(&:invisible?)
432
+ any_remaining_conditionally_invisible = remaining.any?(&:conditionally_invisible?)
433
+
434
+ static_last = all_remaining_always_invisible
435
+ dynamic_last = !static_last && all_remaining_invisible && any_remaining_conditionally_invisible
436
+
437
+ FieldVisibility.new(
438
+ field: field,
439
+ static_last: static_last,
440
+ dynamic_last: dynamic_last,
441
+ prism_field_name: dynamic_last ? last_prism_field_name : nil
442
+ )
443
+ end
444
+ end
445
+ end
446
+
447
+ def field_visibility(index)
448
+ field_visibilities[index]
449
+ end
290
450
  end
291
451
 
292
452
  class PrintfMessageTemplate
@@ -308,7 +468,7 @@ module Herb
308
468
  base_length = template.length
309
469
  total_size = base_length
310
470
 
311
- format_specifiers = template.scan(/%[sdulfz]/)
471
+ format_specifiers = template.scan(/%(?:zu|llu|lf|ld|[sdulf])/)
312
472
 
313
473
  format_specifiers.each_with_index do |specifier, _i|
314
474
  estimated_size = ESTIMATED_SIZES[specifier] || 16 # Default extra buffer
@@ -381,7 +541,7 @@ module Herb
381
541
  )
382
542
  end
383
543
 
384
- rendered_template = read_template(template_path.to_s).result_with_hash({ nodes: nodes, errors: errors })
544
+ rendered_template = read_template(template_path.to_s).result_with_hash({ nodes: nodes, errors: errors, union_kinds: union_kinds })
385
545
  content = heading_for(name, template_file) + rendered_template
386
546
 
387
547
  check_gitignore(name)
@@ -425,6 +585,21 @@ module Herb
425
585
  (config.dig("nodes", "types") || []).map { |node| NodeType.new(node) }
426
586
  end
427
587
 
588
+ # Collect all unique union kinds from node fields
589
+ def self.union_kinds
590
+ union_kinds_set = Set.new
591
+
592
+ nodes.each do |node|
593
+ node.fields.each do |field|
594
+ if field.respond_to?(:union_kind) && field.union_kind
595
+ union_kinds_set.add(field.union_kind.sort)
596
+ end
597
+ end
598
+ end
599
+
600
+ union_kinds_set.to_a.sort
601
+ end
602
+
428
603
  def self.errors
429
604
  (config.dig("errors", "types") || []).map { |node| ErrorType.new(node) }
430
605
  end
@@ -18,7 +18,7 @@ extern "C" {
18
18
 
19
19
  using namespace emscripten;
20
20
 
21
- val CreateLocation(location_T* location);
21
+ val CreateLocation(location_T location);
22
22
  val CreateToken(token_T* token);
23
23
  val NodeFromCStruct(AST_NODE_T* node);
24
24
  val NodesArrayFromCArray(hb_array_T* array);
@@ -34,21 +34,25 @@ val <%= error.name %>FromCStruct(<%= error.struct_type %>* <%= error.human %>) {
34
34
  val Object = val::global("Object");
35
35
  val result = Object.new_();
36
36
 
37
- result.set("type", CreateString(error_type_to_string(&<%= error.human %>->base)));
38
- result.set("message", CreateString(<%= error.human %>->base.message));
37
+ result.set("type", CreateStringFromHbString(error_type_to_string(&<%= error.human %>->base)));
38
+ result.set("message", CreateStringFromHbString(<%= error.human %>->base.message));
39
39
  result.set("location", CreateLocation(<%= error.human %>->base.location));
40
40
  <%- error.fields.each do |field| -%>
41
41
  <%- case field -%>
42
42
  <%- when Herb::Template::StringField -%>
43
- result.set("<%= field.name %>", CreateString(<%= error.human %>-><%= field.name %>));
43
+ result.set("<%= field.name %>", CreateStringFromHbString(<%= error.human %>-><%= field.name %>));
44
44
  <%- when Herb::Template::NodeField -%>
45
45
  result.set("<%= field.name %>", NodeFromCStruct((AST_NODE_T*)<%= error.human %>-><%= field.name %>));
46
46
  <%- when Herb::Template::TokenField -%>
47
47
  result.set("<%= field.name %>", CreateToken(<%= error.human %>-><%= field.name %>));
48
48
  <%- when Herb::Template::TokenTypeField -%>
49
- result.set("<%= field.name %>", CreateString(token_type_to_string(<%= error.human %>-><%= field.name %>)));
49
+ result.set("<%= field.name %>", CreateStringFromHbString(token_type_to_string(<%= error.human %>-><%= field.name %>)));
50
50
  <%- when Herb::Template::BooleanField -%>
51
51
  result.set("<%= field.name %>", <%= error.human %>-><%= field.name %>);
52
+ <%- when Herb::Template::PositionField -%>
53
+ result.set("<%= field.name %>", CreatePosition(<%= error.human %>-><%= field.name %>));
54
+ <%- when Herb::Template::SizeTField -%>
55
+ result.set("<%= field.name %>", val((double)<%= error.human %>-><%= field.name %>));
52
56
  <%- when Herb::Template::ArrayField -%>
53
57
  result.set("<%= field.name %>", NodesArrayFromCArray(<%= error.human %>-><%= field.name %>));
54
58
  <%- else -%>
@@ -28,8 +28,8 @@ val <%= node.name %>FromCStruct(<%= node.struct_type %>* <%= node.human %>) {
28
28
  <%- node.fields.each do |field| -%>
29
29
  <%- case field -%>
30
30
  <%- when Herb::Template::StringField -%>
31
- result.set("<%= field.name %>", CreateString(<%= node.human %>-><%= field.name %>));
32
- <%- when Herb::Template::NodeField -%>
31
+ result.set("<%= field.name %>", CreateStringFromHbString(<%= node.human %>-><%= field.name %>));
32
+ <%- when Herb::Template::NodeField, Herb::Template::BorrowedNodeField -%>
33
33
  result.set("<%= field.name %>", NodeFromCStruct((AST_NODE_T*) <%= node.human %>-><%= field.name %>));
34
34
  <%- when Herb::Template::TokenField -%>
35
35
  result.set("<%= field.name %>", CreateToken(<%= node.human %>-><%= field.name %>));
@@ -38,14 +38,51 @@ val <%= node.name %>FromCStruct(<%= node.struct_type %>* <%= node.human %>) {
38
38
  <%- when Herb::Template::ArrayField -%>
39
39
  result.set("<%= field.name %>", NodesArrayFromCArray(<%= node.human %>-><%= field.name %>));
40
40
  <%- when Herb::Template::ElementSourceField -%>
41
- result.set("<%= field.name %>", CreateStringFromHbString(element_source_to_string(<%= node.human %>-><%= field.name %>)));
41
+ result.set("<%= field.name %>", CreateStringFromHbString(<%= node.human %>-><%= field.name %>));
42
42
  <%- when Herb::Template::LocationField -%>
43
43
  if (<%= node.human %>-><%= field.name %>) {
44
44
  result.set("<%= field.name %>", CreateLocation(*<%= node.human %>-><%= field.name %>));
45
45
  } else {
46
46
  result.set("<%= field.name %>", val::null());
47
47
  }
48
+ <%- when Herb::Template::PrismSerializedField -%>
49
+ if (<%= node.human %>-><%= field.name %>.data != NULL && <%= node.human %>-><%= field.name %>.length > 0) {
50
+ val jsArray = val::array();
51
+
52
+ for (size_t i = 0; i < <%= node.human %>-><%= field.name %>.length; i++) {
53
+ jsArray.call<void>("push", (int)<%= node.human %>-><%= field.name %>.data[i]);
54
+ }
55
+
56
+ result.set("<%= field.name %>", jsArray);
57
+ } else {
58
+ result.set("<%= field.name %>", val::null());
59
+ }
60
+ <%- when Herb::Template::PrismNodeField -%>
61
+ if (<%= node.human %>-><%= field.name %>.node != NULL && <%= node.human %>-><%= field.name %>.parser != NULL) {
62
+ pm_buffer_t pm_buffer = { 0 };
63
+ pm_serialize(<%= node.human %>-><%= field.name %>.parser, <%= node.human %>-><%= field.name %>.node, &pm_buffer);
64
+
65
+ if (pm_buffer.length > 0) {
66
+ val jsArray = val::array();
67
+
68
+ for (size_t i = 0; i < pm_buffer.length; i++) {
69
+ jsArray.call<void>("push", (int)((uint8_t*)pm_buffer.value)[i]);
70
+ }
71
+
72
+ result.set("<%= field.name %>", jsArray);
73
+ } else {
74
+ result.set("<%= field.name %>", val::null());
75
+ }
76
+
77
+ pm_buffer_free(&pm_buffer);
78
+ } else {
79
+ result.set("<%= field.name %>", val::null());
80
+ }
81
+ <%- when Herb::Template::AnalyzedRubyField, Herb::Template::PrismContextField, Herb::Template::VoidPointerField -%>
82
+ /* <%= field.name %> is internal parser state, not exposed to JavaScript */
83
+ result.set("<%= field.name %>", val::null());
48
84
  <%- else -%>
85
+ /* Unhandled field type: <%= field.class.name %> */
49
86
  result.set("<%= field.name %>", val::null());
50
87
  <%- end -%>
51
88
  <%- end -%>
@@ -77,7 +114,7 @@ val NodesArrayFromCArray(hb_array_T* array) {
77
114
  AST_NODE_T* child_node = (AST_NODE_T*) hb_array_get(array, i);
78
115
 
79
116
  if (child_node) {
80
- jsArray.set(i, NodeFromCStruct(child_node));
117
+ jsArray.call<void>("push", NodeFromCStruct(child_node));
81
118
  }
82
119
  }
83
120