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}