ruby_prism/
node.rs

1//! Node-related types for the prism parser.
2//!
3//! This module contains types for working with AST nodes, including node lists,
4//! constant IDs, and integer values.
5
6use std::marker::PhantomData;
7use std::ptr::NonNull;
8
9use ruby_prism_sys::{pm_constant_id_list_t, pm_constant_id_t, pm_integer_t, pm_node_list, pm_node_t, pm_parser_t};
10
11// Note: The `Node` enum is defined in the generated `bindings.rs` file.
12// We import it here via `crate::Node` to avoid circular dependencies.
13use crate::Node;
14
15// ============================================================================
16// NodeList
17// ============================================================================
18
19/// An iterator over the nodes in a list.
20pub struct NodeListIter<'pr> {
21    pub(crate) parser: *const pm_parser_t,
22    pub(crate) pointer: NonNull<pm_node_list>,
23    pub(crate) index: usize,
24    pub(crate) marker: PhantomData<&'pr mut pm_node_list>,
25}
26
27impl<'pr> Iterator for NodeListIter<'pr> {
28    type Item = Node<'pr>;
29
30    fn next(&mut self) -> Option<Self::Item> {
31        if self.index >= unsafe { self.pointer.as_ref().size } {
32            None
33        } else {
34            let node: *mut pm_node_t = unsafe { *(self.pointer.as_ref().nodes.add(self.index)) };
35            self.index += 1;
36            Some(Node::new(self.parser, node))
37        }
38    }
39}
40
41/// A list of nodes.
42pub struct NodeList<'pr> {
43    pub(crate) parser: *const pm_parser_t,
44    pub(crate) pointer: NonNull<pm_node_list>,
45    pub(crate) marker: PhantomData<&'pr mut pm_node_list>,
46}
47
48impl<'pr> NodeList<'pr> {
49    unsafe fn at(&self, index: usize) -> Node<'pr> {
50        let node: *mut pm_node_t = *(self.pointer.as_ref().nodes.add(index));
51        Node::new(self.parser, node)
52    }
53
54    /// Returns an iterator over the nodes.
55    #[must_use]
56    pub const fn iter(&self) -> NodeListIter<'pr> {
57        NodeListIter {
58            parser: self.parser,
59            pointer: self.pointer,
60            index: 0,
61            marker: PhantomData,
62        }
63    }
64
65    /// Returns the length of the list.
66    #[must_use]
67    pub const fn len(&self) -> usize {
68        unsafe { self.pointer.as_ref().size }
69    }
70
71    /// Returns whether the list is empty.
72    #[must_use]
73    pub const fn is_empty(&self) -> bool {
74        self.len() == 0
75    }
76
77    /// Returns the first element of the list, or `None` if it is empty.
78    #[must_use]
79    pub fn first(&self) -> Option<Node<'pr>> {
80        if self.is_empty() {
81            None
82        } else {
83            Some(unsafe { self.at(0) })
84        }
85    }
86
87    /// Returns the last element of the list, or `None` if it is empty.
88    #[must_use]
89    pub fn last(&self) -> Option<Node<'pr>> {
90        if self.is_empty() {
91            None
92        } else {
93            Some(unsafe { self.at(self.len() - 1) })
94        }
95    }
96}
97
98impl<'pr> IntoIterator for &NodeList<'pr> {
99    type Item = Node<'pr>;
100    type IntoIter = NodeListIter<'pr>;
101    fn into_iter(self) -> Self::IntoIter {
102        self.iter()
103    }
104}
105
106impl std::fmt::Debug for NodeList<'_> {
107    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
108        write!(f, "{:?}", self.iter().collect::<Vec<_>>())
109    }
110}
111
112// ============================================================================
113// ConstantId / ConstantList
114// ============================================================================
115
116/// A handle for a constant ID.
117pub struct ConstantId<'pr> {
118    pub(crate) parser: *const pm_parser_t,
119    pub(crate) id: pm_constant_id_t,
120    pub(crate) marker: PhantomData<&'pr mut pm_constant_id_t>,
121}
122
123impl<'pr> ConstantId<'pr> {
124    pub(crate) const fn new(parser: *const pm_parser_t, id: pm_constant_id_t) -> Self {
125        ConstantId { parser, id, marker: PhantomData }
126    }
127
128    /// Returns a byte slice for the constant ID.
129    ///
130    /// # Panics
131    ///
132    /// Panics if the constant ID is not found in the constant pool.
133    #[must_use]
134    pub fn as_slice(&self) -> &'pr [u8] {
135        unsafe {
136            let constant = ruby_prism_sys::pm_parser_constant(self.parser, self.id);
137            let start = ruby_prism_sys::pm_constant_start(constant);
138            let length = ruby_prism_sys::pm_constant_length(constant);
139            std::slice::from_raw_parts(start, length)
140        }
141    }
142}
143
144impl std::fmt::Debug for ConstantId<'_> {
145    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
146        write!(f, "{:?}", self.id)
147    }
148}
149
150/// An iterator over the constants in a list.
151pub struct ConstantListIter<'pr> {
152    pub(crate) parser: *const pm_parser_t,
153    pub(crate) pointer: NonNull<pm_constant_id_list_t>,
154    pub(crate) index: usize,
155    pub(crate) marker: PhantomData<&'pr mut pm_constant_id_list_t>,
156}
157
158impl<'pr> Iterator for ConstantListIter<'pr> {
159    type Item = ConstantId<'pr>;
160
161    fn next(&mut self) -> Option<Self::Item> {
162        if self.index >= unsafe { self.pointer.as_ref().size } {
163            None
164        } else {
165            let constant_id: pm_constant_id_t = unsafe { *(self.pointer.as_ref().ids.add(self.index)) };
166            self.index += 1;
167            Some(ConstantId::new(self.parser, constant_id))
168        }
169    }
170}
171
172/// A list of constants.
173pub struct ConstantList<'pr> {
174    /// The raw pointer to the parser where this list came from.
175    pub(crate) parser: *const pm_parser_t,
176
177    /// The raw pointer to the list allocated by prism.
178    pub(crate) pointer: NonNull<pm_constant_id_list_t>,
179
180    /// The marker to indicate the lifetime of the pointer.
181    pub(crate) marker: PhantomData<&'pr mut pm_constant_id_list_t>,
182}
183
184impl<'pr> ConstantList<'pr> {
185    const unsafe fn at(&self, index: usize) -> ConstantId<'pr> {
186        let constant_id: pm_constant_id_t = *(self.pointer.as_ref().ids.add(index));
187        ConstantId::new(self.parser, constant_id)
188    }
189
190    /// Returns an iterator over the constants in the list.
191    #[must_use]
192    pub const fn iter(&self) -> ConstantListIter<'pr> {
193        ConstantListIter {
194            parser: self.parser,
195            pointer: self.pointer,
196            index: 0,
197            marker: PhantomData,
198        }
199    }
200
201    /// Returns the length of the list.
202    #[must_use]
203    pub const fn len(&self) -> usize {
204        unsafe { self.pointer.as_ref().size }
205    }
206
207    /// Returns whether the list is empty.
208    #[must_use]
209    pub const fn is_empty(&self) -> bool {
210        self.len() == 0
211    }
212
213    /// Returns the first element of the list, or `None` if it is empty.
214    #[must_use]
215    pub const fn first(&self) -> Option<ConstantId<'pr>> {
216        if self.is_empty() {
217            None
218        } else {
219            Some(unsafe { self.at(0) })
220        }
221    }
222
223    /// Returns the last element of the list, or `None` if it is empty.
224    #[must_use]
225    pub const fn last(&self) -> Option<ConstantId<'pr>> {
226        if self.is_empty() {
227            None
228        } else {
229            Some(unsafe { self.at(self.len() - 1) })
230        }
231    }
232}
233
234impl<'pr> IntoIterator for &ConstantList<'pr> {
235    type Item = ConstantId<'pr>;
236    type IntoIter = ConstantListIter<'pr>;
237    fn into_iter(self) -> Self::IntoIter {
238        self.iter()
239    }
240}
241
242impl std::fmt::Debug for ConstantList<'_> {
243    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
244        write!(f, "{:?}", self.iter().collect::<Vec<_>>())
245    }
246}
247
248// ============================================================================
249// Integer
250// ============================================================================
251
252/// A handle for an arbitrarily-sized integer.
253pub struct Integer<'pr> {
254    /// The raw pointer to the integer allocated by prism.
255    pub(crate) pointer: *const pm_integer_t,
256
257    /// The marker to indicate the lifetime of the pointer.
258    pub(crate) marker: PhantomData<&'pr mut pm_constant_id_t>,
259}
260
261impl Integer<'_> {
262    pub(crate) const fn new(pointer: *const pm_integer_t) -> Self {
263        Integer { pointer, marker: PhantomData }
264    }
265
266    /// Returns the sign and the u32 digits representation of the integer,
267    /// ordered least significant digit first.
268    #[must_use]
269    pub const fn to_u32_digits(&self) -> (bool, &[u32]) {
270        let negative = unsafe { (*self.pointer).negative };
271        let length = unsafe { (*self.pointer).length };
272        let values = unsafe { (*self.pointer).values };
273
274        if values.is_null() {
275            let value_ptr = unsafe { std::ptr::addr_of!((*self.pointer).value) };
276            let slice = unsafe { std::slice::from_raw_parts(value_ptr, 1) };
277            (negative, slice)
278        } else {
279            let slice = unsafe { std::slice::from_raw_parts(values, length) };
280            (negative, slice)
281        }
282    }
283}
284
285impl std::fmt::Debug for Integer<'_> {
286    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
287        write!(f, "{:?}", self.pointer)
288    }
289}
290
291impl TryInto<i32> for Integer<'_> {
292    type Error = ();
293
294    fn try_into(self) -> Result<i32, Self::Error> {
295        let negative = unsafe { (*self.pointer).negative };
296        let length = unsafe { (*self.pointer).length };
297
298        if length == 0 {
299            i32::try_from(unsafe { (*self.pointer).value }).map_or(Err(()), |value| if negative { Ok(-value) } else { Ok(value) })
300        } else {
301            Err(())
302        }
303    }
304}