herb 0.8.10-arm-linux-gnu → 0.9.1-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 (212) 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 +473 -34
  6. data/ext/herb/error_helpers.c +535 -140
  7. data/ext/herb/error_helpers.h +1 -0
  8. data/ext/herb/extconf.rb +67 -28
  9. data/ext/herb/extension.c +321 -51
  10. data/ext/herb/extension.h +1 -0
  11. data/ext/herb/extension_helpers.c +24 -14
  12. data/ext/herb/extension_helpers.h +2 -2
  13. data/ext/herb/nodes.c +647 -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 +1530 -179
  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 +119 -43
  36. data/lib/herb/errors.rb +808 -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 +62 -0
  41. data/lib/herb/position.rb +1 -0
  42. data/lib/herb/prism_inspect.rb +120 -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 +47 -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 +773 -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 +21 -3
  59. data/sig/herb/errors.rbs +372 -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 +46 -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 +31 -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/rubyvm.rbs +5 -0
  72. data/sig/serialized_ast_errors.rbs +82 -6
  73. data/sig/serialized_ast_nodes.rbs +91 -6
  74. data/src/analyze/action_view/attribute_extraction_helpers.c +303 -0
  75. data/src/analyze/action_view/content_tag.c +78 -0
  76. data/src/analyze/action_view/link_to.c +167 -0
  77. data/src/analyze/action_view/registry.c +83 -0
  78. data/src/analyze/action_view/tag.c +70 -0
  79. data/src/analyze/action_view/tag_helper_node_builders.c +305 -0
  80. data/src/analyze/action_view/tag_helpers.c +815 -0
  81. data/src/analyze/action_view/turbo_frame_tag.c +88 -0
  82. data/src/analyze/analyze.c +885 -0
  83. data/src/{analyzed_ruby.c → analyze/analyzed_ruby.c} +13 -11
  84. data/src/analyze/builders.c +343 -0
  85. data/src/analyze/conditional_elements.c +594 -0
  86. data/src/analyze/conditional_open_tags.c +640 -0
  87. data/src/analyze/control_type.c +250 -0
  88. data/src/{analyze_helpers.c → analyze/helpers.c} +48 -23
  89. data/src/analyze/invalid_structures.c +193 -0
  90. data/src/{analyze_missing_end.c → analyze/missing_end.c} +33 -22
  91. data/src/analyze/parse_errors.c +84 -0
  92. data/src/analyze/prism_annotate.c +399 -0
  93. data/src/analyze/render_nodes.c +761 -0
  94. data/src/{analyze_transform.c → analyze/transform.c} +24 -3
  95. data/src/ast_node.c +17 -7
  96. data/src/ast_nodes.c +759 -387
  97. data/src/ast_pretty_print.c +264 -6
  98. data/src/errors.c +1454 -519
  99. data/src/extract.c +145 -49
  100. data/src/herb.c +52 -34
  101. data/src/html_util.c +241 -12
  102. data/src/include/analyze/action_view/attribute_extraction_helpers.h +36 -0
  103. data/src/include/analyze/action_view/tag_helper_handler.h +43 -0
  104. data/src/include/analyze/action_view/tag_helper_node_builders.h +70 -0
  105. data/src/include/analyze/action_view/tag_helpers.h +38 -0
  106. data/src/include/{analyze.h → analyze/analyze.h} +14 -4
  107. data/src/include/{analyzed_ruby.h → analyze/analyzed_ruby.h} +3 -3
  108. data/src/include/analyze/builders.h +27 -0
  109. data/src/include/analyze/conditional_elements.h +9 -0
  110. data/src/include/analyze/conditional_open_tags.h +9 -0
  111. data/src/include/analyze/control_type.h +14 -0
  112. data/src/include/{analyze_helpers.h → analyze/helpers.h} +4 -2
  113. data/src/include/analyze/invalid_structures.h +11 -0
  114. data/src/include/analyze/prism_annotate.h +16 -0
  115. data/src/include/analyze/render_nodes.h +11 -0
  116. data/src/include/ast_node.h +11 -5
  117. data/src/include/ast_nodes.h +154 -38
  118. data/src/include/ast_pretty_print.h +5 -0
  119. data/src/include/element_source.h +3 -8
  120. data/src/include/errors.h +206 -55
  121. data/src/include/extract.h +21 -5
  122. data/src/include/herb.h +18 -6
  123. data/src/include/herb_prism_node.h +13 -0
  124. data/src/include/html_util.h +7 -2
  125. data/src/include/io.h +3 -1
  126. data/src/include/lex_helpers.h +29 -0
  127. data/src/include/lexer.h +1 -1
  128. data/src/include/lexer_peek_helpers.h +87 -13
  129. data/src/include/lexer_struct.h +2 -0
  130. data/src/include/location.h +2 -1
  131. data/src/include/parser.h +28 -2
  132. data/src/include/parser_helpers.h +19 -3
  133. data/src/include/pretty_print.h +10 -5
  134. data/src/include/prism_context.h +45 -0
  135. data/src/include/prism_helpers.h +10 -7
  136. data/src/include/prism_serialized.h +12 -0
  137. data/src/include/token.h +16 -4
  138. data/src/include/token_struct.h +10 -3
  139. data/src/include/utf8.h +2 -1
  140. data/src/include/util/hb_allocator.h +78 -0
  141. data/src/include/util/hb_arena.h +6 -1
  142. data/src/include/util/hb_arena_debug.h +12 -1
  143. data/src/include/util/hb_array.h +7 -3
  144. data/src/include/util/hb_buffer.h +6 -4
  145. data/src/include/util/hb_foreach.h +79 -0
  146. data/src/include/util/hb_narray.h +8 -4
  147. data/src/include/util/hb_string.h +56 -9
  148. data/src/include/util.h +6 -3
  149. data/src/include/version.h +1 -1
  150. data/src/io.c +3 -2
  151. data/src/lexer.c +42 -30
  152. data/src/lexer_peek_helpers.c +12 -74
  153. data/src/location.c +2 -2
  154. data/src/main.c +53 -28
  155. data/src/parser.c +784 -247
  156. data/src/parser_helpers.c +110 -23
  157. data/src/parser_match_tags.c +129 -48
  158. data/src/pretty_print.c +29 -24
  159. data/src/prism_helpers.c +30 -27
  160. data/src/ruby_parser.c +2 -0
  161. data/src/token.c +151 -66
  162. data/src/token_matchers.c +0 -1
  163. data/src/utf8.c +7 -6
  164. data/src/util/hb_allocator.c +341 -0
  165. data/src/util/hb_arena.c +81 -56
  166. data/src/util/hb_arena_debug.c +32 -17
  167. data/src/util/hb_array.c +30 -15
  168. data/src/util/hb_buffer.c +17 -21
  169. data/src/util/hb_narray.c +22 -7
  170. data/src/util/hb_string.c +49 -35
  171. data/src/util.c +21 -11
  172. data/src/visitor.c +67 -0
  173. data/templates/ext/herb/error_helpers.c.erb +24 -11
  174. data/templates/ext/herb/error_helpers.h.erb +1 -0
  175. data/templates/ext/herb/nodes.c.erb +50 -16
  176. data/templates/ext/herb/nodes.h.erb +1 -0
  177. data/templates/java/error_helpers.c.erb +1 -1
  178. data/templates/java/nodes.c.erb +30 -8
  179. data/templates/java/org/herb/ast/Errors.java.erb +24 -1
  180. data/templates/java/org/herb/ast/Nodes.java.erb +80 -21
  181. data/templates/javascript/packages/core/src/errors.ts.erb +16 -3
  182. data/templates/javascript/packages/core/src/node-type-guards.ts.erb +3 -1
  183. data/templates/javascript/packages/core/src/nodes.ts.erb +109 -32
  184. data/templates/javascript/packages/node/extension/error_helpers.cpp.erb +13 -4
  185. data/templates/javascript/packages/node/extension/nodes.cpp.erb +43 -4
  186. data/templates/lib/herb/ast/nodes.rb.erb +95 -32
  187. data/templates/lib/herb/errors.rb.erb +15 -3
  188. data/templates/lib/herb/visitor.rb.erb +2 -2
  189. data/templates/rust/src/ast/nodes.rs.erb +97 -44
  190. data/templates/rust/src/errors.rs.erb +2 -1
  191. data/templates/rust/src/nodes.rs.erb +168 -16
  192. data/templates/rust/src/union_types.rs.erb +60 -0
  193. data/templates/rust/src/visitor.rs.erb +81 -0
  194. data/templates/src/{analyze_missing_end.c.erb → analyze/missing_end.c.erb} +9 -6
  195. data/templates/src/{analyze_transform.c.erb → analyze/transform.c.erb} +2 -2
  196. data/templates/src/ast_nodes.c.erb +34 -26
  197. data/templates/src/ast_pretty_print.c.erb +24 -5
  198. data/templates/src/errors.c.erb +60 -54
  199. data/templates/src/include/ast_nodes.h.erb +6 -2
  200. data/templates/src/include/ast_pretty_print.h.erb +5 -0
  201. data/templates/src/include/errors.h.erb +15 -11
  202. data/templates/src/include/util/hb_foreach.h.erb +20 -0
  203. data/templates/src/parser_match_tags.c.erb +10 -4
  204. data/templates/src/visitor.c.erb +2 -2
  205. data/templates/template.rb +204 -29
  206. data/templates/wasm/error_helpers.cpp.erb +9 -5
  207. data/templates/wasm/nodes.cpp.erb +41 -4
  208. metadata +60 -16
  209. data/src/analyze.c +0 -1608
  210. data/src/element_source.c +0 -12
  211. data/src/include/util/hb_system.h +0 -9
  212. data/src/util/hb_system.c +0 -30
@@ -1,5 +1,10 @@
1
- <%- base_arguments = [["type", "String"], ["location", "Location"], ["message", "String"]] -%>
1
+ <%- base_arguments = [["type", "String"], ["location", "Location?"], ["message", "String"]] -%>
2
2
  module Herb
3
+ #: type serialized_error = {
4
+ #| type: String,
5
+ #| location: serialized_location?,
6
+ #| message: String
7
+ #| }
3
8
  module Errors
4
9
  class Error
5
10
  <%- base_arguments.each do |argument, type| -%>
@@ -49,7 +54,12 @@ module Herb
49
54
  <%- if error.fields.any? -%>
50
55
 
51
56
  <%- error.fields.each do |field| -%>
52
- attr_reader :<%= field.name %> #: <%= field.ruby_type %>
57
+ #| <%= "#{field.name}: #{field.ruby_type}?," %>
58
+ <%- end -%>
59
+ #| }
60
+
61
+ <%- error.fields.each do |field| -%>
62
+ attr_reader :<%= field.name %> #: <%= field.ruby_type %>?
53
63
  <%- end -%>
54
64
 
55
65
  #: (<%= [*base_arguments.map(&:last), *error.fields.map(&:ruby_type)].join(", ") %>) -> void
@@ -81,7 +91,7 @@ module Herb
81
91
  def tree_inspect(indent: 0, depth: 0, depth_limit: 25)
82
92
  output = +""
83
93
 
84
- output += white("@ #{bold(red(error_name))} #{dimmed("(location: #{location.tree_inspect})\n")}")
94
+ output += white("@ #{bold(red(error_name))} #{dimmed("(location: #{location&.tree_inspect})\n")}")
85
95
  <%- symbol = error.fields.none? ? "└──" : "├──" -%>
86
96
  output += white("<%= symbol %> message: #{green(message.inspect)}\n")
87
97
  <%- error.fields.each do |field| -%>
@@ -99,6 +109,8 @@ module Herb
99
109
  output += white("<%= symbol %> <%= field.name %>: #{green(<%= field.name %>.inspect)}\n")
100
110
  <%- when Herb::Template::StringField -%>
101
111
  output += white("<%= symbol %> <%= field.name %>: #{green(<%= field.name %>.inspect)}\n")
112
+ <%- when Herb::Template::SizeTField -%>
113
+ output += white("<%= symbol %> <%= field.name %>: #{green(<%= field.name %>.inspect)}\n")
102
114
  <%- else -%>
103
115
  output += white("<%= symbol %> <%= field.name %>: ") + '#{<%= field.name %>.class}'\n"
104
116
  <%- end -%>
@@ -2,12 +2,12 @@ module Herb
2
2
  class Visitor
3
3
  include AST::Helpers
4
4
 
5
- #: (Herb::AST::Node) -> void
5
+ #: (Herb::AST::Node?) -> void
6
6
  def visit(node)
7
7
  node&.accept(self)
8
8
  end
9
9
 
10
- #: (Array[Herb::AST::Node]) -> void
10
+ #: (Array[Herb::AST::Node?]) -> void
11
11
  def visit_all(nodes)
12
12
  nodes.each { |node| node&.accept(self) }
13
13
  end
@@ -1,10 +1,10 @@
1
1
  use crate::bindings::*;
2
- use crate::convert::token_from_c;
2
+ use crate::convert::{token_from_c, string_from_hb_string};
3
3
  use crate::errors::*;
4
4
  use crate::nodes::*;
5
+ use crate::union_types::*;
5
6
  use crate::{Location, Position};
6
- use std::ffi::CStr;
7
- use std::os::raw::{c_char, c_void};
7
+ use std::os::raw::c_void;
8
8
 
9
9
  unsafe fn convert_location(c_location: location_T) -> Location {
10
10
  Location::new(
@@ -13,15 +13,15 @@ unsafe fn convert_location(c_location: location_T) -> Location {
13
13
  )
14
14
  }
15
15
 
16
+ unsafe fn convert_position(c_position: position_T) -> Position {
17
+ Position::new(c_position.line, c_position.column)
18
+ }
19
+
16
20
  <%- errors.each do |error| -%>
17
- <%- snake_name = error.name.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2').gsub(/([a-z\d])([A-Z])/, '\1_\2').downcase -%>
21
+ <%- snake_name = Herb::Template.underscore(error.name).downcase -%>
18
22
  unsafe fn convert_<%= snake_name %>(error_ptr: *const <%= error.c_type %>) -> <%= error.name %> {
19
23
  let error_ref = &*error_ptr;
20
- let message = if error_ref.base.message.is_null() {
21
- String::new()
22
- } else {
23
- CStr::from_ptr(error_ref.base.message).to_string_lossy().into_owned()
24
- };
24
+ let message = string_from_hb_string(error_ref.base.message);
25
25
  let location = convert_location(error_ref.base.location);
26
26
 
27
27
  <%= error.name %>::new(
@@ -37,8 +37,12 @@ unsafe fn convert_<%= snake_name %>(error_ptr: *const <%= error.c_type %>) -> <%
37
37
  if error_ref.<%= field.name %> == u32::MAX {
38
38
  None
39
39
  } else {
40
- Some(CStr::from_ptr(crate::ffi::token_type_to_string(error_ref.<%= field.name %>)).to_string_lossy().into_owned())
40
+ Some(string_from_hb_string(crate::ffi::token_type_to_string(error_ref.<%= field.name %>)))
41
41
  },
42
+ <%- when Herb::Template::SizeTField -%>
43
+ error_ref.<%= field.name %>,
44
+ <%- when Herb::Template::PositionField -%>
45
+ Some(convert_position(error_ref.<%= field.name %>)),
42
46
  <%- end -%>
43
47
  <%- end -%>
44
48
  )
@@ -64,7 +68,7 @@ unsafe fn convert_errors(errors_array: *mut hb_array_T) -> Vec<AnyError> {
64
68
 
65
69
  let error = match error_base.type_ {
66
70
  <%- errors.each do |error| -%>
67
- <%- snake_name = error.name.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2').gsub(/([a-z\d])([A-Z])/, '\1_\2').downcase -%>
71
+ <%- snake_name = Herb::Template.underscore(error.name).downcase -%>
68
72
  <%= error.type %> => {
69
73
  let error_ptr = error_base_ptr as *const <%= error.c_type %>;
70
74
  AnyError::<%= error.name %>(convert_<%= snake_name %>(error_ptr))
@@ -79,16 +83,11 @@ unsafe fn convert_errors(errors_array: *mut hb_array_T) -> Vec<AnyError> {
79
83
  errors
80
84
  }
81
85
 
82
- unsafe fn get_string_field(string_ptr: *const c_char) -> String {
83
- if string_ptr.is_null() {
84
- String::new()
85
- } else {
86
- CStr::from_ptr(string_ptr).to_string_lossy().into_owned()
87
- }
86
+ unsafe fn get_string_field(hb_string: hb_string_T) -> String {
87
+ string_from_hb_string(hb_string)
88
88
  }
89
89
 
90
- unsafe fn convert_element_source(source: u32) -> String {
91
- let hb_string = crate::ffi::element_source_to_string(source);
90
+ unsafe fn convert_element_source(hb_string: hb_string_T) -> String {
92
91
  if hb_string.data.is_null() {
93
92
  String::new()
94
93
  } else {
@@ -133,20 +132,21 @@ unsafe fn convert_children(children_array: *mut hb_array_T) -> Vec<AnyNode> {
133
132
  children
134
133
  }
135
134
 
136
- unsafe fn convert_node_field(node_ptr: *mut c_void) -> Option<Box<AnyNode>> {
137
- if node_ptr.is_null() {
135
+ #[allow(dead_code)]
136
+ unsafe fn convert_node_field(node_pointer: *mut c_void) -> Option<Box<AnyNode>> {
137
+ if node_pointer.is_null() {
138
138
  None
139
139
  } else {
140
- convert_node(node_ptr).map(Box::new)
140
+ convert_node(node_pointer).map(Box::new)
141
141
  }
142
142
  }
143
143
 
144
144
  macro_rules! convert_specific_node_field {
145
- ($node_ptr:expr, $expected_type:expr, $convert_fn:ident, $node_type:ty) => {
146
- if $node_ptr.is_null() {
145
+ ($node_pointer:expr, $expected_type:expr, $convert_fn:ident, $node_type:ty) => {
146
+ if $node_pointer.is_null() {
147
147
  None
148
148
  } else {
149
- let ast_base_ptr = $node_ptr as *const AST_NODE_T;
149
+ let ast_base_ptr = $node_pointer as *const AST_NODE_T;
150
150
  let ast_base_ref = &*ast_base_ptr;
151
151
  let node_type = ast_base_ref.type_;
152
152
 
@@ -154,24 +154,24 @@ macro_rules! convert_specific_node_field {
154
154
  eprintln!("Warning: Expected node type {} but got {}", $expected_type, node_type);
155
155
  None
156
156
  } else {
157
- $convert_fn($node_ptr as *const c_void).map(Box::new)
157
+ $convert_fn($node_pointer as *const c_void).map(Box::new)
158
158
  }
159
159
  }
160
160
  };
161
161
  }
162
162
 
163
- unsafe fn convert_node(node_ptr: *const c_void) -> Option<AnyNode> {
164
- if node_ptr.is_null() {
163
+ unsafe fn convert_node(node_pointer: *const c_void) -> Option<AnyNode> {
164
+ if node_pointer.is_null() {
165
165
  return None;
166
166
  }
167
167
 
168
- let ast_base_ptr = node_ptr as *const AST_NODE_T;
168
+ let ast_base_ptr = node_pointer as *const AST_NODE_T;
169
169
  let ast_base_ref = &*ast_base_ptr;
170
170
  let node_type = ast_base_ref.type_;
171
171
 
172
172
  match node_type {
173
173
  <%- nodes.each do |node| -%>
174
- <%= node.type %> => convert_<%= node.human %>(node_ptr).map(AnyNode::<%= node.name %>),
174
+ <%= node.type %> => convert_<%= node.human %>(node_pointer).map(|n| AnyNode::<%= node.name %>(Box::new(n))),
175
175
  <%- end -%>
176
176
  _ => {
177
177
  eprintln!("Warning: Unknown node type {}", node_type);
@@ -186,16 +186,16 @@ unsafe fn convert_node(node_ptr: *const c_void) -> Option<AnyNode> {
186
186
  ///
187
187
  /// # Safety
188
188
  ///
189
- /// The caller must ensure that `node_ptr` is a valid pointer to a C document node
189
+ /// The caller must ensure that `node_pointer` is a valid pointer to a C document node
190
190
  /// structure with properly initialized fields.
191
191
  <%- end -%>
192
- <%= node.name == "DocumentNode" ? "pub " : "" %>unsafe fn convert_<%= node.human %>(node_ptr: *const c_void) -> Option<<%= node.name %>> {
193
- if node_ptr.is_null() {
192
+ <%= node.name == "DocumentNode" ? "pub " : "" %>unsafe fn convert_<%= node.human %>(node_pointer: *const c_void) -> Option<<%= node.name %>> {
193
+ if node_pointer.is_null() {
194
194
  return None;
195
195
  }
196
196
 
197
- let c_node_ptr = node_ptr as *const <%= node.c_type %>;
198
- let node_base_ref = &(*c_node_ptr).base;
197
+ let c_node_pointer = node_pointer as *const <%= node.c_type %>;
198
+ let node_base_ref = &(*c_node_pointer).base;
199
199
 
200
200
  Some(<%= node.name %> {
201
201
  node_type: "<%= node.name %>".to_string(),
@@ -204,27 +204,80 @@ unsafe fn convert_node(node_ptr: *const c_void) -> Option<AnyNode> {
204
204
  <%- node.fields.each do |field| -%>
205
205
  <%- case field -%>
206
206
  <%- when Herb::Template::StringField -%>
207
- <%= field.name %>: get_string_field((*c_node_ptr).<%= field.name %>),
207
+ <%= field.name %>: get_string_field((*c_node_pointer).<%= field.name %>),
208
208
  <%- when Herb::Template::ElementSourceField -%>
209
- <%= field.name %>: convert_element_source((*c_node_ptr).<%= field.name %>),
209
+ <%= field.name %>: convert_element_source((*c_node_pointer).<%= field.name %>),
210
210
  <%- when Herb::Template::TokenField -%>
211
- <%= field.name %>: convert_token_field((*c_node_ptr).<%= field.name %>),
211
+ <%= field.name %>: convert_token_field((*c_node_pointer).<%= field.name %>),
212
212
  <%- when Herb::Template::BooleanField -%>
213
- <%= field.name %>: (*c_node_ptr).<%= field.name %>,
213
+ <%= field.name %>: (*c_node_pointer).<%= field.name %>,
214
214
  <%- when Herb::Template::ArrayField -%>
215
- <%= field.name %>: convert_children((*c_node_ptr).<%= field.name %>),
216
- <%- when Herb::Template::NodeField -%>
215
+ <%= field.name %>: convert_children((*c_node_pointer).<%= field.name %>),
216
+ <%- when Herb::Template::NodeField, Herb::Template::BorrowedNodeField -%>
217
217
  <%- if field.specific_kind && field.specific_kind != "Node" -%>
218
218
  <%- specific_node = nodes.find { |n| n.name == field.specific_kind } -%>
219
- <%= field.name %>: convert_specific_node_field!((*c_node_ptr).<%= field.name %>, <%= specific_node.type %>, convert_<%= specific_node.human %>, <%= field.specific_kind %>),
219
+ <%= field.name %>: convert_specific_node_field!((*c_node_pointer).<%= field.name %>, <%= specific_node.type %>, convert_<%= specific_node.human %>, <%= field.specific_kind %>),
220
+ <%- elsif field.union_kind -%>
221
+ <%- snake_name = Herb::Template.underscore(field.union_type_name).downcase -%>
222
+ <%= field.name %>: convert_<%= snake_name %>_field((*c_node_pointer).<%= field.name %> as *mut c_void),
220
223
  <%- else -%>
221
- <%= field.name %>: convert_node_field((*c_node_ptr).<%= field.name %> as *mut c_void),
224
+ <%= field.name %>: convert_node_field((*c_node_pointer).<%= field.name %> as *mut c_void),
222
225
  <%- end -%>
223
226
  <%- when Herb::Template::LocationField -%>
224
- <%= field.name %>: convert_location_field((*c_node_ptr).<%= field.name %>),
227
+ <%= field.name %>: convert_location_field((*c_node_pointer).<%= field.name %>),
228
+ <%- when Herb::Template::PrismSerializedField -%>
229
+ <%= field.name %>: if (*c_node_pointer).<%= field.name %>.data.is_null() || (*c_node_pointer).<%= field.name %>.length == 0 {
230
+ None
231
+ } else {
232
+ Some(std::slice::from_raw_parts((*c_node_pointer).<%= field.name %>.data, (*c_node_pointer).<%= field.name %>.length).to_vec())
233
+ },
234
+ <%- when Herb::Template::PrismNodeField -%>
235
+ <%= field.name %>: if (*c_node_pointer).<%= field.name %>.node.is_null() || (*c_node_pointer).<%= field.name %>.parser.is_null() {
236
+ None
237
+ } else {
238
+ let mut pm_buffer: crate::bindings::pm_buffer_t = std::mem::zeroed();
239
+ crate::bindings::pm_prettyprint(&mut pm_buffer, (*c_node_pointer).<%= field.name %>.parser, (*c_node_pointer).<%= field.name %>.node);
240
+
241
+ if pm_buffer.length > 0 && !pm_buffer.value.is_null() {
242
+ let s = std::str::from_utf8(std::slice::from_raw_parts(pm_buffer.value as *const u8, pm_buffer.length)).unwrap_or("").to_string();
243
+ crate::bindings::pm_buffer_free(&mut pm_buffer);
244
+ Some(s)
245
+ } else {
246
+ crate::bindings::pm_buffer_free(&mut pm_buffer);
247
+ None
248
+ }
249
+ },
250
+ <%- when Herb::Template::AnalyzedRubyField, Herb::Template::PrismContextField -%>
251
+ // skip internal field: <%= field.name %>
225
252
  <%- end -%>
226
253
  <%- end -%>
227
254
  })
228
255
  }
229
256
 
230
257
  <%- end -%>
258
+ <%- union_kinds.each do |kinds| -%>
259
+ <%-
260
+ enum_name = kinds.sort.join("Or")
261
+ snake_name = Herb::Template.underscore(enum_name).downcase
262
+ -%>
263
+ #[allow(dead_code)]
264
+ unsafe fn convert_<%= snake_name %>_field(node_pointer: *mut c_void) -> Option<<%= enum_name %>> {
265
+ if node_pointer.is_null() {
266
+ return None;
267
+ }
268
+
269
+ let base_node = node_pointer as *const AST_NODE_STRUCT;
270
+ let node_type = (*base_node).type_;
271
+
272
+ match node_type {
273
+ <%- kinds.each do |kind| -%>
274
+ <%- node = nodes.find { |n| n.name == kind } -%>
275
+ <%= node.type %> => {
276
+ Some(<%= enum_name %>::<%= kind %>(Box::new(convert_<%= node.human %>(node_pointer as *const c_void)?)))
277
+ }
278
+ <%- end -%>
279
+ _ => None,
280
+ }
281
+ }
282
+
283
+ <%- end -%>
@@ -1,4 +1,4 @@
1
- use crate::{Location, Token};
1
+ use crate::{Location, Position, Token};
2
2
  use colored::*;
3
3
 
4
4
  fn escape_string(s: &str) -> String {
@@ -136,6 +136,7 @@ pub struct <%= error.name %> {
136
136
  <%- end -%>
137
137
  }
138
138
 
139
+ #[allow(clippy::too_many_arguments)]
139
140
  impl <%= error.name %> {
140
141
  pub fn new(
141
142
  message: String,
@@ -1,7 +1,29 @@
1
1
  use crate::errors::{AnyError, ErrorNode};
2
2
  use crate::{Location, Token};
3
+ use crate::union_types::*;
3
4
  use colored::*;
4
5
 
6
+ pub(crate) fn prettify_prism_tree(input: &str) -> String {
7
+ let input = input.replace("+-- ", "├── ").replace("| ", "│ ");
8
+ let lines: Vec<&str> = input.lines().collect();
9
+ let mut result: Vec<String> = lines.iter().map(|l| l.to_string()).collect();
10
+
11
+ for i in 0..result.len() {
12
+ if let Some(position) = result[i].find("├── ") {
13
+ let indent = &result[i][..position];
14
+ let is_last = !lines[i + 1..].iter().any(|next| {
15
+ next.starts_with(&format!("{}├── ", indent)) || next.starts_with(&format!("{}│ ", indent))
16
+ });
17
+
18
+ if is_last {
19
+ result[i] = format!("{}└── {}", indent, &result[i][position + "├── ".len()..]);
20
+ }
21
+ }
22
+ }
23
+
24
+ result.join("\n")
25
+ }
26
+
5
27
  fn escape_string(s: &str) -> String {
6
28
  s.replace('\\', "\\\\")
7
29
  .replace('\n', "\\n")
@@ -36,6 +58,38 @@ fn format_node_value<T: Node + ?Sized>(node: &Option<Box<T>>, prefix: &str, add_
36
58
  }
37
59
  }
38
60
 
61
+ fn format_union_node_value<T>(node: &Option<T>, prefix: &str, add_spacing: bool, tree_inspect_fn: impl Fn(&T) -> String) -> String {
62
+ if let Some(ref n) = node {
63
+ let mut output = String::new();
64
+ output.push('\n');
65
+ let tree = tree_inspect_fn(n);
66
+ let lines: Vec<&str> = tree.lines().collect();
67
+ if !lines.is_empty() {
68
+ output.push_str(prefix);
69
+ output.push_str(&"└── ".white().to_string());
70
+ output.push_str(lines[0]);
71
+ output.push('\n');
72
+ for line in lines.iter().skip(1) {
73
+ if line.is_empty() {
74
+ output.push_str(prefix);
75
+ output.push('\n');
76
+ } else {
77
+ output.push_str(prefix);
78
+ output.push_str(" ");
79
+ output.push_str(line);
80
+ output.push('\n');
81
+ }
82
+ }
83
+ }
84
+ if add_spacing {
85
+ output.push_str(&format!("{}\n", prefix));
86
+ }
87
+ output
88
+ } else {
89
+ format!("{}\n", "∅".magenta())
90
+ }
91
+ }
92
+
39
93
  fn format_array_value(array: &[AnyNode], prefix: &str) -> String {
40
94
  inspect_array(array, prefix)
41
95
  }
@@ -146,10 +200,17 @@ pub trait Node {
146
200
  fn tree_inspect(&self) -> String;
147
201
  }
148
202
 
203
+ pub trait ERBNode {
204
+ fn tag_opening(&self) -> &Option<Token>;
205
+ fn content(&self) -> &Option<Token>;
206
+ fn tag_closing(&self) -> &Option<Token>;
207
+ fn location(&self) -> &Location;
208
+ }
209
+
149
210
  #[derive(Debug, Clone)]
150
211
  pub enum AnyNode {
151
212
  <%- nodes.each do |node| -%>
152
- <%= node.name %>(<%= node.name %>),
213
+ <%= node.name %>(Box<<%= node.name %>>),
153
214
  <%- end -%>
154
215
  }
155
216
 
@@ -245,9 +306,11 @@ pub struct <%= node.name %> {
245
306
  pub <%= field.name %>: bool,
246
307
  <%- when Herb::Template::ArrayField -%>
247
308
  pub <%= field.name %>: Vec<AnyNode>,
248
- <%- when Herb::Template::NodeField -%>
309
+ <%- when Herb::Template::NodeField, Herb::Template::BorrowedNodeField -%>
249
310
  <%- if field.specific_kind && field.specific_kind != "Node" -%>
250
311
  pub <%= field.name %>: Option<Box<<%= field.specific_kind %>>>,
312
+ <%- elsif field.union_kind -%>
313
+ pub <%= field.name %>: Option<<%= field.union_type_name %>>,
251
314
  <%- else -%>
252
315
  pub <%= field.name %>: Option<Box<AnyNode>>,
253
316
  <%- end -%>
@@ -255,6 +318,12 @@ pub struct <%= node.name %> {
255
318
  pub <%= field.name %>: String,
256
319
  <%- when Herb::Template::LocationField -%>
257
320
  pub <%= field.name %>: Option<Location>,
321
+ <%- when Herb::Template::PrismSerializedField -%>
322
+ pub <%= field.name %>: Option<Vec<u8>>,
323
+ <%- when Herb::Template::PrismNodeField -%>
324
+ pub <%= field.name %>: Option<String>,
325
+ <%- when Herb::Template::AnalyzedRubyField, Herb::Template::PrismContextField -%>
326
+ // no-op for <%= field.name %>
258
327
  <%- end -%>
259
328
  <%- end -%>
260
329
  }
@@ -277,7 +346,7 @@ impl Node for <%= node.name %> {
277
346
  case field
278
347
  when Herb::Template::ArrayField
279
348
  true
280
- when Herb::Template::NodeField
349
+ when Herb::Template::BorrowedNodeField, Herb::Template::NodeField
281
350
  true
282
351
  else
283
352
  false
@@ -289,10 +358,12 @@ impl Node for <%= node.name %> {
289
358
  <%- case field -%>
290
359
  <%- when Herb::Template::ArrayField -%>
291
360
  children.extend(self.<%= field.name %>.iter().map(|n| n as &dyn Node));
292
- <%- when Herb::Template::NodeField -%>
361
+ <%- when Herb::Template::NodeField, Herb::Template::BorrowedNodeField -%>
293
362
  if let Some(ref node) = self.<%= field.name %> {
294
363
  <%- if field.specific_kind && field.specific_kind != "Node" -%>
295
364
  children.push(node.as_ref() as &dyn Node);
365
+ <%- elsif field.union_kind -%>
366
+ children.push(node.as_node());
296
367
  <%- else -%>
297
368
  children.push(node.as_ref() as &dyn Node);
298
369
  <%- end -%>
@@ -310,7 +381,7 @@ impl Node for <%= node.name %> {
310
381
  case field
311
382
  when Herb::Template::ArrayField
312
383
  true
313
- when Herb::Template::NodeField
384
+ when Herb::Template::BorrowedNodeField, Herb::Template::NodeField
314
385
  true
315
386
  else
316
387
  false
@@ -326,7 +397,7 @@ impl Node for <%= node.name %> {
326
397
  for child in &self.<%= field.name %> {
327
398
  all_errors.extend(child.recursive_errors());
328
399
  }
329
- <%- when Herb::Template::NodeField -%>
400
+ <%- when Herb::Template::NodeField, Herb::Template::BorrowedNodeField -%>
330
401
  if let Some(ref node) = self.<%= field.name %> {
331
402
  all_errors.extend(node.recursive_errors());
332
403
  }
@@ -349,22 +420,83 @@ impl Node for <%= node.name %> {
349
420
  ));
350
421
  output.push_str(&format_errors_field(&self.errors, &<%- if node.fields.any? -%>"│ "<%- else -%>" "<%- end -%>.white().to_string()));
351
422
  <%- if node.fields.any? -%>
352
- <%- node.fields.each_with_index do |field, index| -%>
353
- <%- is_last = index == node.fields.length - 1 -%>
354
- <%- symbol = is_last ? "└── " : "├── " -%>
423
+ <%- node.field_visibilities.each do |visibility| -%>
424
+ <%- field = visibility.field -%>
425
+ <%- next if field.always_invisible? -%>
426
+ <%- if field.conditionally_invisible? -%>
427
+ <%- case field -%>
428
+ <%- when Herb::Template::PrismSerializedField -%>
429
+ output.push_str(&format!("{}{}: {}\n", "<%= visibility.symbol %>".white(), "<%= field.name %>".white(), self.<%= field.name %>.as_ref().map(|b| format!("({} bytes)", b.len()).dimmed().to_string()).unwrap_or_else(|| "∅".magenta().to_string())));
430
+ <%- when Herb::Template::PrismNodeField -%>
431
+ if let Some(ref prism_string) = self.<%= field.name %> {
432
+ let prefix = "<%= visibility.prefix %>";
433
+ output.push_str(&format!("{}{}: \n", "<%= visibility.symbol %>".white(), "<%= field.name %>".white()));
434
+ output.push_str(&format!("{}└── ", prefix));
435
+ let prettified = prettify_prism_tree(prism_string);
436
+ let indented = prettified.lines().enumerate().map(|(i, line)| {
437
+ if i == 0 { line.to_string() } else { format!("{} {}", prefix, line) }
438
+ }).collect::<Vec<_>>().join("\n");
439
+ output.push_str(&indented);
440
+ output.push('\n');
441
+ }
442
+ <%- end -%>
443
+ <%- else -%>
444
+ <%- if visibility.dynamic_last? -%>
445
+ { let is_last = self.<%= visibility.prism_field_name %>.is_none(); let symbol = if is_last { "└── " } else { "├── " };
446
+ <%- end -%>
355
447
  <%- case field -%>
356
448
  <%- when Herb::Template::StringField, Herb::Template::ElementSourceField -%>
357
- output.push_str(&format!("{}{}: {}\n", "<%= symbol %>".white(), "<%= field.name %>".white(), format_string_value(&self.<%= field.name %>)));
449
+ <%- if visibility.dynamic_last? -%>
450
+ output.push_str(&format!("{}{}: {}\n", symbol.white(), "<%= field.name %>".white(), format_string_value(&self.<%= field.name %>)));
451
+ <%- else -%>
452
+ output.push_str(&format!("{}{}: {}\n", "<%= visibility.symbol %>".white(), "<%= field.name %>".white(), format_string_value(&self.<%= field.name %>)));
453
+ <%- end -%>
358
454
  <%- when Herb::Template::TokenField -%>
359
- output.push_str(&format!("{}{}: {}\n", "<%= symbol %>".white(), "<%= field.name %>".white(), format_token_value(&self.<%= field.name %>)));
455
+ <%- if visibility.dynamic_last? -%>
456
+ output.push_str(&format!("{}{}: {}\n", symbol.white(), "<%= field.name %>".white(), format_token_value(&self.<%= field.name %>)));
457
+ <%- else -%>
458
+ output.push_str(&format!("{}{}: {}\n", "<%= visibility.symbol %>".white(), "<%= field.name %>".white(), format_token_value(&self.<%= field.name %>)));
459
+ <%- end -%>
360
460
  <%- when Herb::Template::BooleanField -%>
361
- output.push_str(&format!("{}{}: {}\n", "<%= symbol %>".white(), "<%= field.name %>".white(), format_bool_value(self.<%= field.name %>)));
461
+ <%- if visibility.dynamic_last? -%>
462
+ output.push_str(&format!("{}{}: {}\n", symbol.white(), "<%= field.name %>".white(), format_bool_value(self.<%= field.name %>)));
463
+ <%- else -%>
464
+ output.push_str(&format!("{}{}: {}\n", "<%= visibility.symbol %>".white(), "<%= field.name %>".white(), format_bool_value(self.<%= field.name %>)));
465
+ <%- end -%>
362
466
  <%- when Herb::Template::ArrayField -%>
363
- output.push_str(&format!("{}{}: {}", "<%= symbol %>".white(), "<%= field.name %>".white(), format_array_value(&self.<%= field.name %>, &"<%= is_last ? " " : "│ " %>".white().to_string())));
364
- <%- when Herb::Template::NodeField -%>
365
- output.push_str(&format!("{}{}: {}", "<%= symbol %>".white(), "<%= field.name %>".white(), format_node_value(&self.<%= field.name %>, &"<%= is_last ? " " : "│ " %>".white().to_string(), <%= !is_last %>)));
467
+ <%- if visibility.dynamic_last? -%>
468
+ let child_prefix = if is_last { " " } else { "│ " };
469
+ output.push_str(&format!("{}{}: {}", symbol.white(), "<%= field.name %>".white(), format_array_value(&self.<%= field.name %>, &child_prefix.white().to_string())));
470
+ <%- else -%>
471
+ output.push_str(&format!("{}{}: {}", "<%= visibility.symbol %>".white(), "<%= field.name %>".white(), format_array_value(&self.<%= field.name %>, &"<%= visibility.prefix %>".white().to_string())));
472
+ <%- end -%>
473
+ <%- when Herb::Template::NodeField, Herb::Template::BorrowedNodeField -%>
474
+ <%- if visibility.dynamic_last? -%>
475
+ let child_prefix = if is_last { " " } else { "│ " };
476
+ <%- if field.union_kind -%>
477
+ output.push_str(&format!("{}{}: {}", symbol.white(), "<%= field.name %>".white(), format_union_node_value(&self.<%= field.name %>, &child_prefix.white().to_string(), !is_last, |n: &<%= field.union_type_name %>| n.tree_inspect())));
478
+ <%- else -%>
479
+ output.push_str(&format!("{}{}: {}", symbol.white(), "<%= field.name %>".white(), format_node_value(&self.<%= field.name %>, &child_prefix.white().to_string(), !is_last)));
480
+ <%- end -%>
481
+ <%- else -%>
482
+ <%- if field.union_kind -%>
483
+ output.push_str(&format!("{}{}: {}", "<%= visibility.symbol %>".white(), "<%= field.name %>".white(), format_union_node_value(&self.<%= field.name %>, &"<%= visibility.prefix %>".white().to_string(), <%= !visibility.static_last? %>, |n: &<%= field.union_type_name %>| n.tree_inspect())));
484
+ <%- else -%>
485
+ output.push_str(&format!("{}{}: {}", "<%= visibility.symbol %>".white(), "<%= field.name %>".white(), format_node_value(&self.<%= field.name %>, &"<%= visibility.prefix %>".white().to_string(), <%= !visibility.static_last? %>)));
486
+ <%- end -%>
487
+ <%- end -%>
366
488
  <%- 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())));
489
+ <%- if visibility.dynamic_last? -%>
490
+ 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())));
491
+ <%- else -%>
492
+ output.push_str(&format!("{}{}: {}\n", "<%= visibility.symbol %>".white(), "<%= field.name %>".white(), self.<%= field.name %>.as_ref().map(|l| format!("(location: {})", l).dimmed().to_string()).unwrap_or_else(|| "∅".magenta().to_string())));
493
+ <%- end -%>
494
+ <%- else -%>
495
+ <% raise "Unhandled class #{field.class}" %>
496
+ <%- end -%>
497
+ <%- if visibility.dynamic_last? -%>
498
+ }
499
+ <%- end -%>
368
500
  <%- end -%>
369
501
  <%- end -%>
370
502
  <%- else -%>
@@ -375,4 +507,24 @@ impl Node for <%= node.name %> {
375
507
  }
376
508
  }
377
509
 
510
+ <%- if node.name.start_with?("ERB") -%>
511
+ impl ERBNode for <%= node.name %> {
512
+ fn tag_opening(&self) -> &Option<Token> {
513
+ &self.tag_opening
514
+ }
515
+
516
+ fn content(&self) -> &Option<Token> {
517
+ &self.content
518
+ }
519
+
520
+ fn tag_closing(&self) -> &Option<Token> {
521
+ &self.tag_closing
522
+ }
523
+
524
+ fn location(&self) -> &Location {
525
+ &self.location
526
+ }
527
+ }
528
+
529
+ <%- end -%>
378
530
  <%- end -%>
@@ -0,0 +1,60 @@
1
+ use crate::nodes::*;
2
+ use crate::errors::ErrorNode;
3
+ use crate::Location;
4
+
5
+ <%- union_kinds.each do |kinds| -%>
6
+ <%-
7
+ enum_name = kinds.sort.join("Or")
8
+ snake_name = Herb::Template.underscore(enum_name).downcase
9
+ -%>
10
+ /// Union type for <%= kinds.join(" | ") %>
11
+ #[derive(Debug, Clone)]
12
+ pub enum <%= enum_name %> {
13
+ <%- kinds.each do |kind| -%>
14
+ <%= kind %>(Box<<%= kind %>>),
15
+ <%- end -%>
16
+ }
17
+
18
+ impl <%= enum_name %> {
19
+ pub fn node_type(&self) -> &str {
20
+ match self {
21
+ <%- kinds.each do |kind| -%>
22
+ <%= enum_name %>::<%= kind %>(n) => &n.node_type,
23
+ <%- end -%>
24
+ }
25
+ }
26
+
27
+ pub fn location(&self) -> &Location {
28
+ match self {
29
+ <%- kinds.each do |kind| -%>
30
+ <%= enum_name %>::<%= kind %>(n) => &n.location,
31
+ <%- end -%>
32
+ }
33
+ }
34
+
35
+ pub fn as_node(&self) -> &dyn Node {
36
+ match self {
37
+ <%- kinds.each do |kind| -%>
38
+ <%= enum_name %>::<%= kind %>(n) => n.as_ref(),
39
+ <%- end -%>
40
+ }
41
+ }
42
+
43
+ pub fn tree_inspect(&self) -> String {
44
+ match self {
45
+ <%- kinds.each do |kind| -%>
46
+ <%= enum_name %>::<%= kind %>(n) => n.tree_inspect(),
47
+ <%- end -%>
48
+ }
49
+ }
50
+
51
+ pub fn recursive_errors(&self) -> Vec<&dyn ErrorNode> {
52
+ match self {
53
+ <%- kinds.each do |kind| -%>
54
+ <%= enum_name %>::<%= kind %>(n) => n.recursive_errors(),
55
+ <%- end -%>
56
+ }
57
+ }
58
+ }
59
+
60
+ <%- end -%>