herb 0.7.5 → 0.8.0

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 (161) hide show
  1. checksums.yaml +4 -4
  2. data/Makefile +8 -5
  3. data/config.yml +26 -6
  4. data/ext/herb/error_helpers.c +57 -3
  5. data/ext/herb/error_helpers.h +1 -1
  6. data/ext/herb/extconf.rb +1 -0
  7. data/ext/herb/extension.c +10 -24
  8. data/ext/herb/extension_helpers.c +3 -3
  9. data/ext/herb/extension_helpers.h +1 -1
  10. data/ext/herb/nodes.c +72 -37
  11. data/herb.gemspec +0 -2
  12. data/lib/herb/ast/helpers.rb +11 -0
  13. data/lib/herb/ast/node.rb +15 -6
  14. data/lib/herb/ast/nodes.rb +609 -392
  15. data/lib/herb/cli.rb +31 -0
  16. data/lib/herb/colors.rb +82 -0
  17. data/lib/herb/engine/compiler.rb +140 -14
  18. data/lib/herb/engine/debug_visitor.rb +1 -5
  19. data/lib/herb/engine/parser_error_overlay.rb +1 -1
  20. data/lib/herb/engine.rb +8 -14
  21. data/lib/herb/errors.rb +166 -56
  22. data/lib/herb/location.rb +2 -2
  23. data/lib/herb/project.rb +86 -21
  24. data/lib/herb/token.rb +14 -2
  25. data/lib/herb/version.rb +1 -1
  26. data/lib/herb.rb +1 -0
  27. data/sig/herb/ast/helpers.rbs +3 -0
  28. data/sig/herb/ast/node.rbs +12 -5
  29. data/sig/herb/ast/nodes.rbs +124 -62
  30. data/sig/herb/colors.rbs +35 -0
  31. data/sig/herb/engine/compiler.rbs +23 -1
  32. data/sig/herb/errors.rbs +74 -20
  33. data/sig/herb/token.rbs +8 -0
  34. data/sig/herb_c_extension.rbs +1 -1
  35. data/sig/serialized_ast_errors.rbs +8 -0
  36. data/src/analyze.c +420 -171
  37. data/src/analyze_helpers.c +5 -0
  38. data/src/analyze_missing_end.c +147 -0
  39. data/src/analyze_transform.c +196 -0
  40. data/src/analyzed_ruby.c +23 -2
  41. data/src/ast_node.c +5 -5
  42. data/src/ast_nodes.c +179 -179
  43. data/src/ast_pretty_print.c +232 -232
  44. data/src/element_source.c +7 -6
  45. data/src/errors.c +246 -126
  46. data/src/extract.c +92 -34
  47. data/src/herb.c +37 -49
  48. data/src/html_util.c +34 -96
  49. data/src/include/analyze.h +10 -2
  50. data/src/include/analyze_helpers.h +3 -0
  51. data/src/include/analyzed_ruby.h +4 -2
  52. data/src/include/ast_node.h +2 -2
  53. data/src/include/ast_nodes.h +67 -66
  54. data/src/include/ast_pretty_print.h +2 -2
  55. data/src/include/element_source.h +3 -1
  56. data/src/include/errors.h +30 -14
  57. data/src/include/extract.h +4 -4
  58. data/src/include/herb.h +6 -7
  59. data/src/include/html_util.h +4 -5
  60. data/src/include/lexer.h +1 -3
  61. data/src/include/lexer_peek_helpers.h +14 -14
  62. data/src/include/lexer_struct.h +3 -2
  63. data/src/include/macros.h +4 -0
  64. data/src/include/parser.h +12 -6
  65. data/src/include/parser_helpers.h +25 -15
  66. data/src/include/pretty_print.h +38 -28
  67. data/src/include/token.h +5 -8
  68. data/src/include/utf8.h +3 -2
  69. data/src/include/util/hb_arena.h +31 -0
  70. data/src/include/util/hb_arena_debug.h +8 -0
  71. data/src/include/util/hb_array.h +33 -0
  72. data/src/include/util/hb_buffer.h +34 -0
  73. data/src/include/util/hb_string.h +29 -0
  74. data/src/include/util/hb_system.h +9 -0
  75. data/src/include/util.h +3 -14
  76. data/src/include/version.h +1 -1
  77. data/src/include/visitor.h +1 -1
  78. data/src/io.c +7 -4
  79. data/src/lexer.c +61 -88
  80. data/src/lexer_peek_helpers.c +35 -37
  81. data/src/main.c +19 -23
  82. data/src/parser.c +282 -201
  83. data/src/parser_helpers.c +46 -40
  84. data/src/parser_match_tags.c +316 -0
  85. data/src/pretty_print.c +82 -106
  86. data/src/token.c +18 -65
  87. data/src/utf8.c +4 -4
  88. data/src/util/hb_arena.c +179 -0
  89. data/src/util/hb_arena_debug.c +237 -0
  90. data/src/{array.c → util/hb_array.c} +26 -27
  91. data/src/util/hb_buffer.c +203 -0
  92. data/src/util/hb_string.c +85 -0
  93. data/src/util/hb_system.c +30 -0
  94. data/src/util.c +29 -99
  95. data/src/visitor.c +54 -54
  96. data/templates/ext/herb/error_helpers.c.erb +3 -3
  97. data/templates/ext/herb/error_helpers.h.erb +1 -1
  98. data/templates/ext/herb/nodes.c.erb +11 -6
  99. data/templates/java/error_helpers.c.erb +75 -0
  100. data/templates/java/error_helpers.h.erb +20 -0
  101. data/templates/java/nodes.c.erb +97 -0
  102. data/templates/java/nodes.h.erb +23 -0
  103. data/templates/java/org/herb/ast/Errors.java.erb +121 -0
  104. data/templates/java/org/herb/ast/NodeVisitor.java.erb +14 -0
  105. data/templates/java/org/herb/ast/Nodes.java.erb +220 -0
  106. data/templates/java/org/herb/ast/Visitor.java.erb +56 -0
  107. data/templates/javascript/packages/node/extension/error_helpers.cpp.erb +8 -8
  108. data/templates/javascript/packages/node/extension/error_helpers.h.erb +1 -1
  109. data/templates/javascript/packages/node/extension/nodes.cpp.erb +9 -9
  110. data/templates/javascript/packages/node/extension/nodes.h.erb +1 -1
  111. data/templates/lib/herb/ast/nodes.rb.erb +28 -16
  112. data/templates/lib/herb/errors.rb.erb +17 -12
  113. data/templates/rust/src/ast/nodes.rs.erb +220 -0
  114. data/templates/rust/src/errors.rs.erb +216 -0
  115. data/templates/rust/src/nodes.rs.erb +374 -0
  116. data/templates/src/analyze_missing_end.c.erb +36 -0
  117. data/templates/src/analyze_transform.c.erb +24 -0
  118. data/templates/src/ast_nodes.c.erb +14 -14
  119. data/templates/src/ast_pretty_print.c.erb +36 -36
  120. data/templates/src/errors.c.erb +31 -31
  121. data/templates/src/include/ast_nodes.h.erb +10 -9
  122. data/templates/src/include/ast_pretty_print.h.erb +2 -2
  123. data/templates/src/include/errors.h.erb +6 -6
  124. data/templates/src/parser_match_tags.c.erb +38 -0
  125. data/templates/src/visitor.c.erb +4 -4
  126. data/templates/template.rb +22 -3
  127. data/templates/wasm/error_helpers.cpp.erb +9 -9
  128. data/templates/wasm/error_helpers.h.erb +1 -1
  129. data/templates/wasm/nodes.cpp.erb +9 -9
  130. data/templates/wasm/nodes.h.erb +1 -1
  131. data/vendor/prism/Rakefile +4 -1
  132. data/vendor/prism/config.yml +2 -1
  133. data/vendor/prism/include/prism/ast.h +31 -1
  134. data/vendor/prism/include/prism/diagnostic.h +1 -0
  135. data/vendor/prism/include/prism/version.h +3 -3
  136. data/vendor/prism/src/diagnostic.c +3 -1
  137. data/vendor/prism/src/prism.c +130 -71
  138. data/vendor/prism/src/util/pm_string.c +6 -8
  139. data/vendor/prism/templates/include/prism/ast.h.erb +2 -0
  140. data/vendor/prism/templates/java/org/prism/Loader.java.erb +2 -2
  141. data/vendor/prism/templates/javascript/src/deserialize.js.erb +2 -2
  142. data/vendor/prism/templates/lib/prism/serialize.rb.erb +2 -2
  143. data/vendor/prism/templates/sig/prism.rbs.erb +4 -0
  144. data/vendor/prism/templates/src/diagnostic.c.erb +1 -0
  145. metadata +34 -20
  146. data/lib/herb/libherb/array.rb +0 -51
  147. data/lib/herb/libherb/ast_node.rb +0 -50
  148. data/lib/herb/libherb/buffer.rb +0 -56
  149. data/lib/herb/libherb/extract_result.rb +0 -20
  150. data/lib/herb/libherb/lex_result.rb +0 -32
  151. data/lib/herb/libherb/libherb.rb +0 -52
  152. data/lib/herb/libherb/parse_result.rb +0 -20
  153. data/lib/herb/libherb/token.rb +0 -46
  154. data/lib/herb/libherb.rb +0 -35
  155. data/src/buffer.c +0 -241
  156. data/src/include/array.h +0 -33
  157. data/src/include/buffer.h +0 -39
  158. data/src/include/json.h +0 -28
  159. data/src/include/memory.h +0 -12
  160. data/src/json.c +0 -205
  161. data/src/memory.c +0 -53
@@ -0,0 +1,220 @@
1
+ use crate::bindings::*;
2
+ use crate::convert::token_from_c;
3
+ use crate::errors::*;
4
+ use crate::nodes::*;
5
+ use crate::{Location, Position};
6
+ use std::ffi::CStr;
7
+ use std::os::raw::{c_char, c_void};
8
+
9
+ unsafe fn convert_location(c_location: location_T) -> Location {
10
+ Location::new(
11
+ Position::new(c_location.start.line, c_location.start.column),
12
+ Position::new(c_location.end.line, c_location.end.column),
13
+ )
14
+ }
15
+
16
+ <%- 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 -%>
18
+ unsafe fn convert_<%= snake_name %>(error_ptr: *const <%= error.c_type %>) -> <%= error.name %> {
19
+ 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
+ };
25
+ let location = convert_location(error_ref.base.location);
26
+
27
+ <%= error.name %>::new(
28
+ message,
29
+ location,
30
+ <%- error.fields.each do |field| -%>
31
+ <%- case field -%>
32
+ <%- when Herb::Template::StringField -%>
33
+ get_string_field(error_ref.<%= field.name %>),
34
+ <%- when Herb::Template::TokenField -%>
35
+ convert_token_field(error_ref.<%= field.name %>),
36
+ <%- when Herb::Template::TokenTypeField -%>
37
+ if error_ref.<%= field.name %> == u32::MAX {
38
+ None
39
+ } else {
40
+ Some(CStr::from_ptr(crate::ffi::token_type_to_string(error_ref.<%= field.name %>)).to_string_lossy().into_owned())
41
+ },
42
+ <%- end -%>
43
+ <%- end -%>
44
+ )
45
+ }
46
+
47
+ <%- end -%>
48
+
49
+ unsafe fn convert_errors(errors_array: *mut hb_array_T) -> Vec<AnyError> {
50
+ if errors_array.is_null() {
51
+ return Vec::new();
52
+ }
53
+
54
+ let count = hb_array_size(errors_array);
55
+ let mut errors = Vec::with_capacity(count);
56
+
57
+ for index in 0..count {
58
+ let error_base_ptr = hb_array_get(errors_array, index) as *const ERROR_T;
59
+ if error_base_ptr.is_null() {
60
+ continue;
61
+ }
62
+
63
+ let error_base = &*error_base_ptr;
64
+
65
+ let error = match error_base.type_ {
66
+ <%- 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 -%>
68
+ <%= error.type %> => {
69
+ let error_ptr = error_base_ptr as *const <%= error.c_type %>;
70
+ AnyError::<%= error.name %>(convert_<%= snake_name %>(error_ptr))
71
+ }
72
+ <%- end -%>
73
+ _ => continue,
74
+ };
75
+
76
+ errors.push(error);
77
+ }
78
+
79
+ errors
80
+ }
81
+
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
+ }
88
+ }
89
+
90
+ unsafe fn convert_element_source(source: u32) -> String {
91
+ let hb_string = crate::ffi::element_source_to_string(source);
92
+ if hb_string.data.is_null() {
93
+ String::new()
94
+ } else {
95
+ let slice = std::slice::from_raw_parts(hb_string.data as *const u8, hb_string.length as usize);
96
+ String::from_utf8_lossy(slice).into_owned()
97
+ }
98
+ }
99
+
100
+ unsafe fn convert_token_field(token_ptr: *mut token_T) -> Option<crate::Token> {
101
+ if token_ptr.is_null() {
102
+ None
103
+ } else {
104
+ Some(token_from_c(token_ptr))
105
+ }
106
+ }
107
+
108
+ unsafe fn convert_children(children_array: *mut hb_array_T) -> Vec<AnyNode> {
109
+ if children_array.is_null() {
110
+ return Vec::new();
111
+ }
112
+
113
+ let count = hb_array_size(children_array);
114
+ let mut children = Vec::with_capacity(count);
115
+
116
+ for index in 0..count {
117
+ let child_ptr = hb_array_get(children_array, index);
118
+ if !child_ptr.is_null() {
119
+ if let Some(node) = convert_node(child_ptr as *const c_void) {
120
+ children.push(node);
121
+ }
122
+ }
123
+ }
124
+
125
+ children
126
+ }
127
+
128
+ unsafe fn convert_node_field(node_ptr: *mut c_void) -> Option<Box<AnyNode>> {
129
+ if node_ptr.is_null() {
130
+ None
131
+ } else {
132
+ convert_node(node_ptr).map(Box::new)
133
+ }
134
+ }
135
+
136
+ macro_rules! convert_specific_node_field {
137
+ ($node_ptr:expr, $expected_type:expr, $convert_fn:ident, $node_type:ty) => {
138
+ if $node_ptr.is_null() {
139
+ None
140
+ } else {
141
+ let ast_base_ptr = $node_ptr as *const AST_NODE_T;
142
+ let ast_base_ref = &*ast_base_ptr;
143
+ let node_type = ast_base_ref.type_;
144
+
145
+ if node_type != $expected_type {
146
+ eprintln!("Warning: Expected node type {} but got {}", $expected_type, node_type);
147
+ None
148
+ } else {
149
+ $convert_fn($node_ptr as *const c_void).map(Box::new)
150
+ }
151
+ }
152
+ };
153
+ }
154
+
155
+ unsafe fn convert_node(node_ptr: *const c_void) -> Option<AnyNode> {
156
+ if node_ptr.is_null() {
157
+ return None;
158
+ }
159
+
160
+ let ast_base_ptr = node_ptr as *const AST_NODE_T;
161
+ let ast_base_ref = &*ast_base_ptr;
162
+ let node_type = ast_base_ref.type_;
163
+
164
+ match node_type {
165
+ <%- nodes.each do |node| -%>
166
+ <%= node.type %> => convert_<%= node.human %>(node_ptr).map(AnyNode::<%= node.name %>),
167
+ <%- end -%>
168
+ _ => {
169
+ eprintln!("Warning: Unknown node type {}", node_type);
170
+ None
171
+ }
172
+ }
173
+ }
174
+
175
+ <%- nodes.each do |node| -%>
176
+ <%- if node.name == "DocumentNode" -%>
177
+ /// Converts a C document node pointer to a Rust DocumentNode.
178
+ ///
179
+ /// # Safety
180
+ ///
181
+ /// The caller must ensure that `node_ptr` is a valid pointer to a C document node
182
+ /// structure with properly initialized fields.
183
+ <%- end -%>
184
+ <%= node.name == "DocumentNode" ? "pub " : "" %>unsafe fn convert_<%= node.human %>(node_ptr: *const c_void) -> Option<<%= node.name %>> {
185
+ if node_ptr.is_null() {
186
+ return None;
187
+ }
188
+
189
+ let c_node_ptr = node_ptr as *const <%= node.c_type %>;
190
+ let node_base_ref = &(*c_node_ptr).base;
191
+
192
+ Some(<%= node.name %> {
193
+ node_type: "<%= node.name %>".to_string(),
194
+ location: convert_location(node_base_ref.location),
195
+ errors: convert_errors(node_base_ref.errors),
196
+ <%- node.fields.each do |field| -%>
197
+ <%- case field -%>
198
+ <%- when Herb::Template::StringField -%>
199
+ <%= field.name %>: get_string_field((*c_node_ptr).<%= field.name %>),
200
+ <%- when Herb::Template::ElementSourceField -%>
201
+ <%= field.name %>: convert_element_source((*c_node_ptr).<%= field.name %>),
202
+ <%- when Herb::Template::TokenField -%>
203
+ <%= field.name %>: convert_token_field((*c_node_ptr).<%= field.name %>),
204
+ <%- when Herb::Template::BooleanField -%>
205
+ <%= field.name %>: (*c_node_ptr).<%= field.name %>,
206
+ <%- when Herb::Template::ArrayField -%>
207
+ <%= field.name %>: convert_children((*c_node_ptr).<%= field.name %>),
208
+ <%- when Herb::Template::NodeField -%>
209
+ <%- if field.specific_kind && field.specific_kind != "Node" -%>
210
+ <%- specific_node = nodes.find { |n| n.name == field.specific_kind } -%>
211
+ <%= field.name %>: convert_specific_node_field!((*c_node_ptr).<%= field.name %>, <%= specific_node.type %>, convert_<%= specific_node.human %>, <%= field.specific_kind %>),
212
+ <%- else -%>
213
+ <%= field.name %>: convert_node_field((*c_node_ptr).<%= field.name %> as *mut c_void),
214
+ <%- end -%>
215
+ <%- end -%>
216
+ <%- end -%>
217
+ })
218
+ }
219
+
220
+ <%- end -%>
@@ -0,0 +1,216 @@
1
+ use crate::{Location, Token};
2
+ use colored::*;
3
+
4
+ fn escape_string(s: &str) -> String {
5
+ s.replace('\\', "\\\\")
6
+ .replace('\n', "\\n")
7
+ .replace('\r', "\\r")
8
+ .replace('\t', "\\t")
9
+ .replace('"', "\\\"")
10
+ }
11
+
12
+ fn format_string_value(value: &str) -> String {
13
+ format!("\"{}\"", escape_string(value)).green().to_string()
14
+ }
15
+
16
+ fn format_token_value(token: &Option<Token>) -> String {
17
+ token.as_ref().map(|t| t.tree_inspect()).unwrap_or_else(|| "∅".magenta().to_string())
18
+ }
19
+
20
+ fn format_token_type_value(token_type: &Option<String>) -> String {
21
+ token_type.as_ref().map(|t| format!("\"{}\"", t).green().to_string()).unwrap_or_else(|| "∅".magenta().to_string())
22
+ }
23
+
24
+ #[allow(dead_code)]
25
+ fn format_position_value(position: &Option<crate::Position>) -> String {
26
+ position.as_ref().map(|p| p.to_string()).unwrap_or_else(|| "∅".magenta().to_string())
27
+ }
28
+
29
+ #[allow(dead_code)]
30
+ fn format_size_value(value: usize) -> String {
31
+ value.to_string().magenta().bold().to_string()
32
+ }
33
+
34
+ #[derive(Debug, Clone, PartialEq)]
35
+ pub enum ErrorType {
36
+ <%- errors.each do |error| -%>
37
+ <%= error.name %>,
38
+ <%- end -%>
39
+ }
40
+
41
+ impl ErrorType {
42
+ pub fn as_str(&self) -> &str {
43
+ match self {
44
+ <%- errors.each do |error| -%>
45
+ ErrorType::<%= error.name %> => "<%= error.type %>",
46
+ <%- end -%>
47
+ }
48
+ }
49
+ }
50
+
51
+ pub trait ErrorNode {
52
+ fn error_type(&self) -> &str;
53
+ fn message(&self) -> &str;
54
+ fn location(&self) -> &Location;
55
+ fn tree_inspect(&self) -> String;
56
+ }
57
+
58
+ #[derive(Debug, Clone)]
59
+ pub enum AnyError {
60
+ <%- errors.each do |error| -%>
61
+ <%= error.name %>(<%= error.name %>),
62
+ <%- end -%>
63
+ }
64
+
65
+ impl AnyError {
66
+ pub fn error_type(&self) -> &str {
67
+ match self {
68
+ <%- errors.each do |error| -%>
69
+ AnyError::<%= error.name %>(e) => &e.error_type,
70
+ <%- end -%>
71
+ }
72
+ }
73
+
74
+ pub fn message(&self) -> &str {
75
+ match self {
76
+ <%- errors.each do |error| -%>
77
+ AnyError::<%= error.name %>(e) => &e.message,
78
+ <%- end -%>
79
+ }
80
+ }
81
+
82
+ pub fn location(&self) -> &Location {
83
+ match self {
84
+ <%- errors.each do |error| -%>
85
+ AnyError::<%= error.name %>(e) => &e.location,
86
+ <%- end -%>
87
+ }
88
+ }
89
+
90
+ pub fn tree_inspect(&self) -> String {
91
+ match self {
92
+ <%- errors.each do |error| -%>
93
+ AnyError::<%= error.name %>(e) => e.tree_inspect(),
94
+ <%- end -%>
95
+ }
96
+ }
97
+ }
98
+
99
+ impl ErrorNode for AnyError {
100
+ fn error_type(&self) -> &str {
101
+ self.error_type()
102
+ }
103
+
104
+ fn message(&self) -> &str {
105
+ self.message()
106
+ }
107
+
108
+ fn location(&self) -> &Location {
109
+ self.location()
110
+ }
111
+
112
+ fn tree_inspect(&self) -> String {
113
+ self.tree_inspect()
114
+ }
115
+ }
116
+
117
+ <%- errors.each do |error| -%>
118
+ #[derive(Debug, Clone)]
119
+ pub struct <%= error.name %> {
120
+ pub error_type: String,
121
+ pub message: String,
122
+ pub location: Location,
123
+ <%- error.fields.each do |field| -%>
124
+ <%- case field -%>
125
+ <%- when Herb::Template::StringField -%>
126
+ pub <%= field.name %>: String,
127
+ <%- when Herb::Template::TokenField -%>
128
+ pub <%= field.name %>: Option<Token>,
129
+ <%- when Herb::Template::TokenTypeField -%>
130
+ pub <%= field.name %>: Option<String>,
131
+ <%- when Herb::Template::PositionField -%>
132
+ pub <%= field.name %>: Option<Position>,
133
+ <%- when Herb::Template::SizeTField -%>
134
+ pub <%= field.name %>: usize,
135
+ <%- end -%>
136
+ <%- end -%>
137
+ }
138
+
139
+ impl <%= error.name %> {
140
+ pub fn new(
141
+ message: String,
142
+ location: Location,
143
+ <%- error.fields.each do |field| -%>
144
+ <%- case field -%>
145
+ <%- when Herb::Template::StringField -%>
146
+ <%= field.name %>: String,
147
+ <%- when Herb::Template::TokenField -%>
148
+ <%= field.name %>: Option<Token>,
149
+ <%- when Herb::Template::TokenTypeField -%>
150
+ <%= field.name %>: Option<String>,
151
+ <%- when Herb::Template::PositionField -%>
152
+ <%= field.name %>: Option<Position>,
153
+ <%- when Herb::Template::SizeTField -%>
154
+ <%= field.name %>: usize,
155
+ <%- end -%>
156
+ <%- end -%>
157
+ ) -> Self {
158
+ Self {
159
+ error_type: "<%= error.type %>".to_string(),
160
+ message,
161
+ location,
162
+ <%- error.fields.each do |field| -%>
163
+ <%= field.name %>,
164
+ <%- end -%>
165
+ }
166
+ }
167
+
168
+ pub fn tree_inspect(&self) -> String {
169
+ let mut output = String::new();
170
+
171
+ output.push_str(&format!("{} {} {}\n",
172
+ "@".white(),
173
+ "<%= error.name %>".red().bold(),
174
+ format!("(location: {})", self.location).dimmed()
175
+ ));
176
+ <%- symbol = error.fields.any? ? "├──" : "└──" -%>
177
+ output.push_str(&format!("{} {}: {}\n", "<%= symbol %>".white(), "message".white(), format_string_value(&self.message)));
178
+ <%- error.fields.each do |field| -%>
179
+ <%- symbol = error.fields.last == field ? "└──" : "├──" -%>
180
+ <%- case field -%>
181
+ <%- when Herb::Template::StringField -%>
182
+ output.push_str(&format!("{} {}: {}\n", "<%= symbol %>".white(), "<%= field.name %>".white(), format_string_value(&self.<%= field.name %>)));
183
+ <%- when Herb::Template::TokenField -%>
184
+ output.push_str(&format!("{} {}: {}\n", "<%= symbol %>".white(), "<%= field.name %>".white(), format_token_value(&self.<%= field.name %>)));
185
+ <%- when Herb::Template::TokenTypeField -%>
186
+ output.push_str(&format!("{} {}: {}\n", "<%= symbol %>".white(), "<%= field.name %>".white(), format_token_type_value(&self.<%= field.name %>)));
187
+ <%- when Herb::Template::PositionField -%>
188
+ output.push_str(&format!("{} {}: {}\n", "<%= symbol %>".white(), "<%= field.name %>".white(), format_position_value(&self.<%= field.name %>)));
189
+ <%- when Herb::Template::SizeTField -%>
190
+ output.push_str(&format!("{} {}: {}\n", "<%= symbol %>".white(), "<%= field.name %>".white(), format_size_value(self.<%= field.name %>)));
191
+ <%- end -%>
192
+ <%- end -%>
193
+
194
+ output
195
+ }
196
+ }
197
+
198
+ impl ErrorNode for <%= error.name %> {
199
+ fn error_type(&self) -> &str {
200
+ &self.error_type
201
+ }
202
+
203
+ fn message(&self) -> &str {
204
+ &self.message
205
+ }
206
+
207
+ fn location(&self) -> &Location {
208
+ &self.location
209
+ }
210
+
211
+ fn tree_inspect(&self) -> String {
212
+ <%= error.name %>::tree_inspect(self)
213
+ }
214
+ }
215
+
216
+ <%- end -%>