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_next, pm_diagnostic_next, pm_line_offset_list_line_column, pm_location_t, pm_magic_comment_next, pm_node_t, pm_parser_comments_first, pm_parser_comments_size, pm_parser_data_loc, pm_parser_errors_first, pm_parser_errors_size, pm_parser_free,
10 pm_parser_frozen_string_literal, pm_parser_line_offsets, pm_parser_magic_comments_first, pm_parser_magic_comments_size, pm_parser_start, pm_parser_start_line, pm_parser_t, pm_parser_warnings_first, 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 fn collect<T>(size: usize, first: *const T, next: unsafe extern "C" fn(*const T) -> *const T) -> Vec<*const T> {
146 let mut ptrs = Vec::with_capacity(size);
147 let mut current = first;
148 while !current.is_null() {
149 ptrs.push(current);
150 current = next(current);
151 }
152 ptrs
153}
154
155#[derive(Debug)]
157pub struct ParseResult<'pr> {
158 source: &'pr [u8],
159 arena: *mut pm_arena_t,
160 parser: *mut pm_parser_t,
161 node: NonNull<pm_node_t>,
162}
163
164impl<'pr> ParseResult<'pr> {
165 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 {
166 ParseResult { source, arena, parser, node }
167 }
168
169 #[must_use]
171 pub const fn source(&self) -> &'pr [u8] {
172 self.source
173 }
174
175 #[must_use]
177 pub fn frozen_string_literals(&self) -> bool {
178 unsafe { pm_parser_frozen_string_literal(self.parser) == 1 }
179 }
180
181 #[must_use]
184 pub fn as_slice(&self, location: &Location<'pr>) -> &'pr [u8] {
185 let start = location.start as usize;
186 let end = start + location.length as usize;
187 &self.source[start..end]
188 }
189
190 #[must_use]
193 pub fn line_offsets(&self) -> &'pr [u32] {
194 unsafe {
195 let list = &*pm_parser_line_offsets(self.parser);
196 std::slice::from_raw_parts(list.offsets, list.size)
197 }
198 }
199
200 #[must_use]
203 pub fn errors(&self) -> Diagnostics<'_> {
204 let ptrs = unsafe { collect(pm_parser_errors_size(self.parser), pm_parser_errors_first(self.parser), pm_diagnostic_next) };
205 Diagnostics::new(ptrs, self.parser)
206 }
207
208 #[must_use]
211 pub fn warnings(&self) -> Diagnostics<'_> {
212 let ptrs = unsafe { collect(pm_parser_warnings_size(self.parser), pm_parser_warnings_first(self.parser), pm_diagnostic_next) };
213 Diagnostics::new(ptrs, self.parser)
214 }
215
216 #[must_use]
219 pub fn comments(&self) -> Comments<'_> {
220 let ptrs = unsafe { collect(pm_parser_comments_size(self.parser), pm_parser_comments_first(self.parser), pm_comment_next) };
221 Comments::new(ptrs, self.parser)
222 }
223
224 #[must_use]
227 pub fn magic_comments(&self) -> MagicComments<'_> {
228 let ptrs = unsafe { collect(pm_parser_magic_comments_size(self.parser), pm_parser_magic_comments_first(self.parser), pm_magic_comment_next) };
229 MagicComments::new(ptrs, self.parser)
230 }
231
232 #[must_use]
234 pub fn data_loc(&self) -> Option<Location<'_>> {
235 let location = unsafe { &*pm_parser_data_loc(self.parser) };
236 if location.length == 0 {
237 None
238 } else {
239 Some(Location::new(self.parser, location))
240 }
241 }
242
243 #[must_use]
245 pub fn node(&self) -> Node<'_> {
246 Node::new(self.parser, self.node.as_ptr())
247 }
248
249 #[must_use]
252 pub fn is_success(&self) -> bool {
253 self.errors().next().is_none()
254 }
255
256 #[must_use]
259 pub fn is_failure(&self) -> bool {
260 !self.is_success()
261 }
262}
263
264impl Drop for ParseResult<'_> {
265 fn drop(&mut self) {
266 unsafe {
267 pm_parser_free(self.parser);
268 pm_arena_free(self.arena);
269 }
270 }
271}
272
273#[cfg(test)]
274mod tests {
275 use crate::parse;
276
277 #[test]
278 fn test_is_success() {
279 let result = parse(b"1 + 1");
280 assert!(result.is_success());
281 assert!(!result.is_failure());
282 }
283
284 #[test]
285 fn test_is_failure() {
286 let result = parse(b"<>");
287 assert!(result.is_failure());
288 assert!(!result.is_success());
289 }
290}