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: NonNull<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: NonNull<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: NonNull<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: NonNull<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 pool = &(*self.parser.as_ptr()).constant_pool;
137            let constant = &(*pool.constants.add((self.id - 1).try_into().unwrap()));
138            std::slice::from_raw_parts(constant.start, constant.length)
139        }
140    }
141}
142
143impl std::fmt::Debug for ConstantId<'_> {
144    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
145        write!(f, "{:?}", self.id)
146    }
147}
148
149/// An iterator over the constants in a list.
150pub struct ConstantListIter<'pr> {
151    pub(crate) parser: NonNull<pm_parser_t>,
152    pub(crate) pointer: NonNull<pm_constant_id_list_t>,
153    pub(crate) index: usize,
154    pub(crate) marker: PhantomData<&'pr mut pm_constant_id_list_t>,
155}
156
157impl<'pr> Iterator for ConstantListIter<'pr> {
158    type Item = ConstantId<'pr>;
159
160    fn next(&mut self) -> Option<Self::Item> {
161        if self.index >= unsafe { self.pointer.as_ref().size } {
162            None
163        } else {
164            let constant_id: pm_constant_id_t = unsafe { *(self.pointer.as_ref().ids.add(self.index)) };
165            self.index += 1;
166            Some(ConstantId::new(self.parser, constant_id))
167        }
168    }
169}
170
171/// A list of constants.
172pub struct ConstantList<'pr> {
173    /// The raw pointer to the parser where this list came from.
174    pub(crate) parser: NonNull<pm_parser_t>,
175
176    /// The raw pointer to the list allocated by prism.
177    pub(crate) pointer: NonNull<pm_constant_id_list_t>,
178
179    /// The marker to indicate the lifetime of the pointer.
180    pub(crate) marker: PhantomData<&'pr mut pm_constant_id_list_t>,
181}
182
183impl<'pr> ConstantList<'pr> {
184    const unsafe fn at(&self, index: usize) -> ConstantId<'pr> {
185        let constant_id: pm_constant_id_t = *(self.pointer.as_ref().ids.add(index));
186        ConstantId::new(self.parser, constant_id)
187    }
188
189    /// Returns an iterator over the constants in the list.
190    #[must_use]
191    pub const fn iter(&self) -> ConstantListIter<'pr> {
192        ConstantListIter {
193            parser: self.parser,
194            pointer: self.pointer,
195            index: 0,
196            marker: PhantomData,
197        }
198    }
199
200    /// Returns the length of the list.
201    #[must_use]
202    pub const fn len(&self) -> usize {
203        unsafe { self.pointer.as_ref().size }
204    }
205
206    /// Returns whether the list is empty.
207    #[must_use]
208    pub const fn is_empty(&self) -> bool {
209        self.len() == 0
210    }
211
212    /// Returns the first element of the list, or `None` if it is empty.
213    #[must_use]
214    pub const fn first(&self) -> Option<ConstantId<'pr>> {
215        if self.is_empty() {
216            None
217        } else {
218            Some(unsafe { self.at(0) })
219        }
220    }
221
222    /// Returns the last element of the list, or `None` if it is empty.
223    #[must_use]
224    pub const fn last(&self) -> Option<ConstantId<'pr>> {
225        if self.is_empty() {
226            None
227        } else {
228            Some(unsafe { self.at(self.len() - 1) })
229        }
230    }
231}
232
233impl<'pr> IntoIterator for &ConstantList<'pr> {
234    type Item = ConstantId<'pr>;
235    type IntoIter = ConstantListIter<'pr>;
236    fn into_iter(self) -> Self::IntoIter {
237        self.iter()
238    }
239}
240
241impl std::fmt::Debug for ConstantList<'_> {
242    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
243        write!(f, "{:?}", self.iter().collect::<Vec<_>>())
244    }
245}
246
247// ============================================================================
248// Integer
249// ============================================================================
250
251/// A handle for an arbitrarily-sized integer.
252pub struct Integer<'pr> {
253    /// The raw pointer to the integer allocated by prism.
254    pub(crate) pointer: *const pm_integer_t,
255
256    /// The marker to indicate the lifetime of the pointer.
257    pub(crate) marker: PhantomData<&'pr mut pm_constant_id_t>,
258}
259
260impl Integer<'_> {
261    pub(crate) const fn new(pointer: *const pm_integer_t) -> Self {
262        Integer { pointer, marker: PhantomData }
263    }
264
265    /// Returns the sign and the u32 digits representation of the integer,
266    /// ordered least significant digit first.
267    #[must_use]
268    pub const fn to_u32_digits(&self) -> (bool, &[u32]) {
269        let negative = unsafe { (*self.pointer).negative };
270        let length = unsafe { (*self.pointer).length };
271        let values = unsafe { (*self.pointer).values };
272
273        if values.is_null() {
274            let value_ptr = unsafe { std::ptr::addr_of!((*self.pointer).value) };
275            let slice = unsafe { std::slice::from_raw_parts(value_ptr, 1) };
276            (negative, slice)
277        } else {
278            let slice = unsafe { std::slice::from_raw_parts(values, length) };
279            (negative, slice)
280        }
281    }
282}
283
284impl std::fmt::Debug for Integer<'_> {
285    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
286        write!(f, "{:?}", self.pointer)
287    }
288}
289
290impl TryInto<i32> for Integer<'_> {
291    type Error = ();
292
293    fn try_into(self) -> Result<i32, Self::Error> {
294        let negative = unsafe { (*self.pointer).negative };
295        let length = unsafe { (*self.pointer).length };
296
297        if length == 0 {
298            i32::try_from(unsafe { (*self.pointer).value }).map_or(Err(()), |value| if negative { Ok(-value) } else { Ok(value) })
299        } else {
300            Err(())
301        }
302    }
303}