Skip to main content

scarf_syntax/
metadata.rs

1// =======================================================================
2// metadata.rs
3// =======================================================================
4// Extra metadata attached to leaf nodes to encode a CST
5
6use crate::*;
7use core::ops::Range;
8
9/// The start and end bytes of a particular portion of a source file
10pub type ByteSpan = Range<usize>;
11
12/// A representation of a unique location in a source file
13///
14/// If the file was included from another file (using the `#include`
15/// directive), [`Span::included_from`] will reference the [`Span`]
16/// of the include directive
17///
18/// If the [`Span`] is part of a `#define` directive, each expanded
19/// text macro will have the original [`Span`] of the `#define` token,
20/// with [`Span::expanded_from`] referencing the macro expansion
21/// directive
22#[derive(Default, Debug, Clone, PartialEq, Eq)]
23pub struct Span<'a> {
24    pub file: &'a str,
25    pub bytes: ByteSpan,
26    pub expanded_from: Option<&'a Span<'a>>,
27    pub included_from: Option<&'a Span<'a>>,
28}
29
30/// A relationship between two [`Span`]s, ordering them relative to
31/// each other in declaration order
32#[derive(Debug, PartialEq)]
33pub enum SpanRelation {
34    Earlier,
35    Later,
36    Same,
37}
38
39impl<'a> Span<'a> {
40    fn include_indeces(&self) -> Vec<usize> {
41        match &self.included_from {
42            None => {
43                vec![self.bytes.start]
44            }
45            Some(inner_span) => {
46                let mut nested_byte_indeces = inner_span.include_indeces();
47                nested_byte_indeces.push(self.bytes.start);
48                nested_byte_indeces
49            }
50        }
51    }
52    fn indeces_to_compare(&self) -> Vec<Vec<usize>> {
53        let mut indeces = match self.expanded_from {
54            Some(expanded_span) => expanded_span.indeces_to_compare(),
55            None => vec![],
56        };
57        indeces.push(self.include_indeces());
58        indeces
59    }
60    /// An empty [`Span`]
61    pub const fn empty() -> Span<'a> {
62        Span {
63            file: "",
64            bytes: ByteSpan { start: 0, end: 0 },
65            expanded_from: None,
66            included_from: None,
67        }
68    }
69    /// Compare two [`Span`]s, returning the relationship of the first to the second
70    ///
71    /// ```rust
72    /// # use scarf_syntax::*;
73    /// let span1 = Span {
74    ///     file: "test",
75    ///     bytes: ByteSpan { start: 0, end: 2 },
76    ///     expanded_from: None,
77    ///     included_from: None
78    /// };
79    /// let span2 = Span {
80    ///     file: "test",
81    ///     bytes: ByteSpan { start: 6, end: 8 },
82    ///     expanded_from: None,
83    ///     included_from: None
84    /// };
85    /// assert_eq!(span1.compare(&span2), SpanRelation::Earlier)
86    /// ```
87    ///
88    /// Expanded [`Span`]s will be compared starting at their expansion
89    /// point, and working backwards through `#define`s. Included [`Span`]s
90    /// will be compared starting at their first `#include` and working
91    /// through the include hierarchy to their final token.
92    pub fn compare(&self, other: &Self) -> SpanRelation {
93        let mut idx = 0;
94        let self_include_byte_indeces = self.indeces_to_compare();
95        let other_include_byte_indeces = other.indeces_to_compare();
96        loop {
97            match (
98                self_include_byte_indeces.get(idx),
99                other_include_byte_indeces.get(idx),
100            ) {
101                (Some(self_idxs), Some(other_idxs)) => {
102                    let mut nested_idx = 0;
103                    'match_define: loop {
104                        match (
105                            self_idxs.get(nested_idx),
106                            other_idxs.get(nested_idx),
107                        ) {
108                            (Some(self_idx), Some(other_idx)) => {
109                                if self_idx < other_idx {
110                                    return SpanRelation::Earlier;
111                                } else if self_idx > other_idx {
112                                    return SpanRelation::Later;
113                                } else {
114                                    nested_idx += 1;
115                                }
116                            }
117                            (None, None) => {
118                                break 'match_define;
119                            }
120                            _ => {
121                                panic!(
122                                    "Internal error comparing spans {:?} and {:?}",
123                                    self, other
124                                )
125                            }
126                        }
127                    }
128                    idx += 1;
129                }
130                (None, None) => {
131                    break SpanRelation::Same;
132                }
133                _ => {
134                    panic!(
135                        "Internal error comparing spans {:?} and {:?}",
136                        self, other
137                    )
138                }
139            }
140        }
141    }
142}
143
144/// Metadata for a given syntax token.
145///
146/// This includes the [`Span`] of the token. With the `lossless` feature,
147/// [`Metadata`] also includes `non_trivia`, which stores non-trivia tokens
148/// such as whitespace and comments
149#[cfg(feature = "lossless")]
150#[derive(Default, Clone, Debug)]
151pub struct Metadata<'a> {
152    pub span: Span<'a>,
153    pub non_trivia: Vec<NonTriviaToken<'a>>,
154}
155
156/// Metadata for a given syntax token.
157///
158/// This includes the [`Span`] of the token. With the `lossless` feature,
159/// [`Metadata`] also includes `non_trivia`, which stores non-trivia tokens
160/// such as whitespace and comments
161#[cfg(not(feature = "lossless"))]
162#[derive(Default, Clone, Debug)]
163pub struct Metadata<'a> {
164    pub span: Span<'a>,
165}
166
167impl<'a> Metadata<'a> {
168    /// Construct a new [`Metadata`]. If the `lossless` feature isn't enabled,
169    /// `non_trivia` is discarded.
170    #[cfg(feature = "lossless")]
171    pub fn new(span: Span<'a>, non_trivia: Vec<NonTriviaToken<'a>>) -> Self {
172        Self { span, non_trivia }
173    }
174
175    /// Construct a new [`Metadata`]. If the `lossless` feature isn't enabled,
176    /// `non_trivia` is discarded.
177    #[cfg(not(feature = "lossless"))]
178    pub fn new(span: Span<'a>, _: Vec<NonTriviaToken<'a>>) -> Self {
179        Self { span }
180    }
181}
182
183impl<'a> PartialEq for Metadata<'a> {
184    fn eq(&self, _: &Self) -> bool {
185        // Allows checking of overall CST structure without checking
186        // exact whitespace
187        true
188    }
189}
190
191// Metadata never returns any Nodes while iterating
192impl<'a, 'b> IntoIterator for &'b Metadata<'a> {
193    type Item = Node<'a, 'b>;
194    type IntoIter = std::iter::Empty<Node<'a, 'b>>;
195    fn into_iter(self) -> Self::IntoIter {
196        std::iter::empty()
197    }
198}
199
200impl<'a, 'b> IntoIterator for &'b mut Metadata<'a> {
201    type Item = Node<'a, 'b>;
202    type IntoIter = std::iter::Empty<Node<'a, 'b>>;
203    fn into_iter(self) -> Self::IntoIter {
204        std::iter::empty()
205    }
206}
207
208/// A non-trivia token from the source file
209#[derive(Clone, Debug, PartialEq)]
210pub enum NonTriviaToken<'a> {
211    OnelineComment((&'a str, Span<'a>)),
212    BlockComment((&'a str, Span<'a>)),
213    Newline,
214}