ruby_prism/parse_result/
mod.rs1mod comments;
4mod diagnostics;
5
6use std::ptr::NonNull;
7
8use ruby_prism_sys::{
9 pm_arena_free, pm_arena_t, pm_comment_t, pm_diagnostic_t, pm_line_offset_list_line_column, pm_location_t, pm_magic_comment_t, pm_node_t, pm_parser_comments_each, pm_parser_comments_size, pm_parser_data_loc, pm_parser_errors_each, pm_parser_errors_size, pm_parser_free,
10 pm_parser_frozen_string_literal, pm_parser_line_offsets, pm_parser_magic_comments_each, pm_parser_magic_comments_size, pm_parser_start, pm_parser_start_line, pm_parser_t, pm_parser_warnings_each, pm_parser_warnings_size,
11};
12
13pub use self::comments::{Comment, CommentType, Comments, MagicComment, MagicComments};
14pub use self::diagnostics::{Diagnostic, Diagnostics};
15
16use crate::Node;
17
18pub struct Location<'pr> {
20 pub(crate) parser: *const pm_parser_t,
21 pub(crate) start: u32,
22 pub(crate) length: u32,
23 marker: std::marker::PhantomData<&'pr [u8]>,
24}
25
26impl<'pr> Location<'pr> {
27 #[must_use]
29 pub fn as_slice(&self) -> &'pr [u8] {
30 unsafe {
31 let parser_start = pm_parser_start(self.parser);
32 std::slice::from_raw_parts(parser_start.add(self.start as usize), self.length as usize)
33 }
34 }
35
36 #[must_use]
38 pub(crate) const fn new(parser: *const pm_parser_t, location: &'pr pm_location_t) -> Self {
39 Location {
40 parser,
41 start: location.start,
42 length: location.length,
43 marker: std::marker::PhantomData,
44 }
45 }
46
47 #[must_use]
49 pub const fn start(&self) -> u32 {
50 self.start
51 }
52
53 #[must_use]
55 pub const fn end(&self) -> u32 {
56 self.start + self.length
57 }
58
59 #[must_use]
63 pub fn join(&self, other: &Self) -> Option<Self> {
64 if self.parser != other.parser || self.start > other.start {
65 None
66 } else {
67 Some(Location {
68 parser: self.parser,
69 start: self.start,
70 length: other.end() - self.start,
71 marker: std::marker::PhantomData,
72 })
73 }
74 }
75
76 #[must_use]
78 pub const fn chop(&self) -> Self {
79 Location {
80 parser: self.parser,
81 start: self.start,
82 length: if self.length == 0 { 0 } else { self.length - 1 },
83 marker: std::marker::PhantomData,
84 }
85 }
86}
87
88impl Location<'_> {
89 #[must_use]
91 pub fn start_line(&self) -> i32 {
92 self.line_column(self.start).0
93 }
94
95 #[must_use]
98 pub fn start_column(&self) -> u32 {
99 self.line_column(self.start).1
100 }
101
102 #[must_use]
104 pub fn end_line(&self) -> i32 {
105 self.line_column(self.end()).0
106 }
107
108 #[must_use]
111 pub fn end_column(&self) -> u32 {
112 self.line_column(self.end()).1
113 }
114
115 fn line_column(&self, cursor: u32) -> (i32, u32) {
117 unsafe {
118 let line_offsets = pm_parser_line_offsets(self.parser);
119 let start_line = pm_parser_start_line(self.parser);
120 let result = pm_line_offset_list_line_column(line_offsets, cursor, start_line);
121 (result.line, result.column)
122 }
123 }
124}
125
126impl std::fmt::Debug for Location<'_> {
127 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
128 let slice: &[u8] = self.as_slice();
129
130 let mut visible = String::new();
131 visible.push('"');
132
133 for &byte in slice {
134 let part: Vec<u8> = std::ascii::escape_default(byte).collect();
135 visible.push_str(std::str::from_utf8(&part).unwrap());
136 }
137
138 visible.push('"');
139 write!(f, "{visible}")
140 }
141}
142
143unsafe extern "C" fn collect_comment(comment: *const pm_comment_t, data: *mut std::ffi::c_void) {
145 let vec = &mut *(data.cast::<Vec<*const pm_comment_t>>());
146 vec.push(comment);
147}
148
149unsafe extern "C" fn collect_magic_comment(comment: *const pm_magic_comment_t, data: *mut std::ffi::c_void) {
151 let vec = &mut *(data.cast::<Vec<*const pm_magic_comment_t>>());
152 vec.push(comment);
153}
154
155unsafe extern "C" fn collect_diagnostic(diagnostic: *const pm_diagnostic_t, data: *mut std::ffi::c_void) {
157 let vec = &mut *(data.cast::<Vec<*const pm_diagnostic_t>>());
158 vec.push(diagnostic);
159}
160
161#[derive(Debug)]
163pub struct ParseResult<'pr> {
164 source: &'pr [u8],
165 arena: *mut pm_arena_t,
166 parser: *mut pm_parser_t,
167 node: NonNull<pm_node_t>,
168}
169
170impl<'pr> ParseResult<'pr> {
171 pub(crate) const unsafe fn new(source: &'pr [u8], arena: *mut pm_arena_t, parser: *mut pm_parser_t, node: NonNull<pm_node_t>) -> Self {
172 ParseResult { source, arena, parser, node }
173 }
174
175 #[must_use]
177 pub const fn source(&self) -> &'pr [u8] {
178 self.source
179 }
180
181 #[must_use]
183 pub fn frozen_string_literals(&self) -> bool {
184 unsafe { pm_parser_frozen_string_literal(self.parser) == 1 }
185 }
186
187 #[must_use]
190 pub fn as_slice(&self, location: &Location<'pr>) -> &'pr [u8] {
191 let start = location.start as usize;
192 let end = start + location.length as usize;
193 &self.source[start..end]
194 }
195
196 #[must_use]
199 pub fn line_offsets(&self) -> &'pr [u32] {
200 unsafe {
201 let list = &*pm_parser_line_offsets(self.parser);
202 std::slice::from_raw_parts(list.offsets, list.size)
203 }
204 }
205
206 #[must_use]
209 pub fn errors(&self) -> Diagnostics<'_> {
210 let size = unsafe { pm_parser_errors_size(self.parser) };
211 let mut ptrs: Vec<*const pm_diagnostic_t> = Vec::with_capacity(size);
212 unsafe {
213 pm_parser_errors_each(self.parser, Some(collect_diagnostic), (&raw mut ptrs).cast());
214 }
215 Diagnostics::new(ptrs, self.parser)
216 }
217
218 #[must_use]
221 pub fn warnings(&self) -> Diagnostics<'_> {
222 let size = unsafe { pm_parser_warnings_size(self.parser) };
223 let mut ptrs: Vec<*const pm_diagnostic_t> = Vec::with_capacity(size);
224 unsafe {
225 pm_parser_warnings_each(self.parser, Some(collect_diagnostic), (&raw mut ptrs).cast());
226 }
227 Diagnostics::new(ptrs, self.parser)
228 }
229
230 #[must_use]
233 pub fn comments(&self) -> Comments<'_> {
234 let size = unsafe { pm_parser_comments_size(self.parser) };
235 let mut ptrs: Vec<*const pm_comment_t> = Vec::with_capacity(size);
236 unsafe {
237 pm_parser_comments_each(self.parser, Some(collect_comment), (&raw mut ptrs).cast());
238 }
239 Comments::new(ptrs, self.parser)
240 }
241
242 #[must_use]
245 pub fn magic_comments(&self) -> MagicComments<'_> {
246 let size = unsafe { pm_parser_magic_comments_size(self.parser) };
247 let mut ptrs: Vec<*const pm_magic_comment_t> = Vec::with_capacity(size);
248 unsafe {
249 pm_parser_magic_comments_each(self.parser, Some(collect_magic_comment), (&raw mut ptrs).cast());
250 }
251 MagicComments::new(ptrs, self.parser)
252 }
253
254 #[must_use]
256 pub fn data_loc(&self) -> Option<Location<'_>> {
257 let location = unsafe { &*pm_parser_data_loc(self.parser) };
258 if location.length == 0 {
259 None
260 } else {
261 Some(Location::new(self.parser, location))
262 }
263 }
264
265 #[must_use]
267 pub fn node(&self) -> Node<'_> {
268 Node::new(self.parser, self.node.as_ptr())
269 }
270
271 #[must_use]
274 pub fn is_success(&self) -> bool {
275 self.errors().next().is_none()
276 }
277
278 #[must_use]
281 pub fn is_failure(&self) -> bool {
282 !self.is_success()
283 }
284}
285
286impl Drop for ParseResult<'_> {
287 fn drop(&mut self) {
288 unsafe {
289 pm_parser_free(self.parser);
290 pm_arena_free(self.arena);
291 }
292 }
293}
294
295#[cfg(test)]
296mod tests {
297 use crate::parse;
298
299 #[test]
300 fn test_is_success() {
301 let result = parse(b"1 + 1");
302 assert!(result.is_success());
303 assert!(!result.is_failure());
304 }
305
306 #[test]
307 fn test_is_failure() {
308 let result = parse(b"<>");
309 assert!(result.is_failure());
310 assert!(!result.is_success());
311 }
312}