scarf-syntax 0.2.1

A helper crate of scarf for expressing a SystemVerilog concrete syntax tree
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
// =======================================================================
// metadata.rs
// =======================================================================
//! Extra metadata attached to leaf nodes to encode a CST

use crate::*;
use core::ops::Range;

/// The start and end bytes of a particular portion of a source file
pub type ByteSpan = Range<usize>;

/// A representation of a unique location in a source file
///
/// If the file was included from another file (using the `` `include ``
/// directive), [`Span::included_from`] will reference the [`Span`]
/// of the include directive
///
/// If the [`Span`] is part of a `` `define `` directive, each expanded
/// text macro will have the original [`Span`] of the (now expanded) macro,
/// with [`Span::expanded_from`] referencing the original token (in the
/// `` `define `` directive) before expansion
#[derive(Default, Debug, Clone, PartialEq, Eq)]
pub struct Span<'a> {
    pub file: &'a str,
    pub bytes: ByteSpan,
    pub expanded_from: Option<&'a Span<'a>>,
    pub included_from: Option<&'a Span<'a>>,
}

/// A relationship between two [`Span`]s, ordering them relative to
/// each other in declaration order
#[derive(Debug, PartialEq)]
pub enum SpanRelation {
    Earlier,
    Later,
    Same,
}

impl<'a> Span<'a> {
    fn include_indeces(&self) -> Vec<usize> {
        match &self.included_from {
            None => {
                vec![self.bytes.start]
            }
            Some(inner_span) => {
                let mut nested_byte_indeces = inner_span.include_indeces();
                nested_byte_indeces.push(self.bytes.start);
                nested_byte_indeces
            }
        }
    }
    fn indeces_to_compare(&self) -> Vec<Vec<usize>> {
        let mut indeces = vec![self.include_indeces()];
        match self.expanded_from {
            Some(expanded_span) => {
                indeces.extend(expanded_span.indeces_to_compare())
            }
            None => (),
        };
        indeces
    }
    /// Compare two [`Span`]s, returning the relationship of the first relative to the second
    ///
    /// ```rust
    /// # use scarf_syntax::*;
    /// let span1 = Span {
    ///     file: "test",
    ///     bytes: (0..2),
    ///     expanded_from: None,
    ///     included_from: None
    /// };
    /// let span2 = Span {
    ///     file: "test",
    ///     bytes: (6..8),
    ///     expanded_from: None,
    ///     included_from: None
    /// };
    /// assert_eq!(span1.compare(&span2), SpanRelation::Earlier)
    /// ```
    ///
    /// Expanded [`Span`]s will be compared starting at their expansion
    /// point, and working backwards through `#define`s. Included [`Span`]s
    /// will be compared starting at their first `#include` and working
    /// through the include hierarchy to their final token. If a [`Span`]
    /// is included with an `` `include `` directive **AND** expanded from
    /// a `` `define `` directive, the former is used before the latter.
    pub fn compare(&self, other: &Self) -> SpanRelation {
        let mut idx = 0;
        let self_include_byte_indeces = self.indeces_to_compare();
        let other_include_byte_indeces = other.indeces_to_compare();
        loop {
            match (
                self_include_byte_indeces.get(idx),
                other_include_byte_indeces.get(idx),
            ) {
                (Some(self_idxs), Some(other_idxs)) => {
                    let mut nested_idx = 0;
                    'match_define: loop {
                        match (
                            self_idxs.get(nested_idx),
                            other_idxs.get(nested_idx),
                        ) {
                            (Some(self_idx), Some(other_idx)) => {
                                if self_idx < other_idx {
                                    return SpanRelation::Earlier;
                                } else if self_idx > other_idx {
                                    return SpanRelation::Later;
                                } else {
                                    nested_idx += 1;
                                }
                            }
                            (None, None) => {
                                break 'match_define;
                            }
                            _ => {
                                panic!(
                                    "Internal error comparing spans {:?} and {:?}",
                                    self, other
                                )
                            }
                        }
                    }
                    idx += 1;
                }
                (None, None) => {
                    break SpanRelation::Same;
                }
                _ => {
                    panic!(
                        "Internal error comparing spans {:?} and {:?}",
                        self, other
                    )
                }
            }
        }
    }

    /// How many preprocessor macros this [`Span`] was expanded from
    pub const fn expansion_depth(&self) -> usize {
        let mut curr_span: &Span = self;
        let mut depth = 0;
        loop {
            if let Some(expanded_from_span) = curr_span.expanded_from {
                curr_span = expanded_from_span;
                depth += 1;
            } else {
                break;
            }
        }
        depth
    }

    /// How many `` `include `` macros this [`Span`] was included with
    pub const fn inclusion_depth(&self) -> usize {
        let mut curr_span: &Span = self;
        let mut depth = 0;
        loop {
            if let Some(included_from_span) = curr_span.included_from {
                curr_span = included_from_span;
                depth += 1;
            } else {
                break;
            }
        }
        depth
    }
}

#[test]
fn basic_span_comparison() {
    let span1 = Span {
        file: "",
        bytes: (0..2),
        expanded_from: None,
        included_from: None,
    };
    let span2 = Span {
        file: "",
        bytes: (2..4),
        expanded_from: None,
        included_from: None,
    };
    assert_eq!(span1.compare(&span2), SpanRelation::Earlier)
}

#[test]
fn include_span_comparison() {
    let span1 = Span {
        file: "",
        bytes: (2..4),
        expanded_from: None,
        included_from: None,
    };
    let included_span = Span {
        file: "",
        bytes: (0..2),
        expanded_from: None,
        included_from: None,
    };
    // Included from an earlier point
    let span2 = Span {
        file: "",
        bytes: (4..6),
        expanded_from: None,
        included_from: Some(&included_span),
    };
    assert_eq!(span1.compare(&span2), SpanRelation::Later)
}

#[test]
fn define_span_comparison() {
    let span1 = Span {
        file: "",
        bytes: (2..4),
        expanded_from: None,
        included_from: None,
    };
    let definition_span = Span {
        file: "",
        bytes: (0..2),
        expanded_from: None,
        included_from: None,
    };
    // Defined earlier, but should still occur later
    let span2 = Span {
        file: "",
        bytes: (4..6),
        expanded_from: Some(&definition_span),
        included_from: None,
    };
    assert_eq!(span1.compare(&span2), SpanRelation::Earlier)
}

#[test]
fn mixed_define_span_comparison() {
    let definition_span1 = Span {
        file: "",
        bytes: (102..104),
        expanded_from: None,
        included_from: None,
    };
    let span1 = Span {
        file: "",
        bytes: (0..2),
        expanded_from: Some(&definition_span1),
        included_from: None,
    };
    let definition_span2 = Span {
        file: "",
        bytes: (100..102),
        expanded_from: None,
        included_from: None,
    };
    // Defined earlier, but should still occur later
    let span2 = Span {
        file: "",
        bytes: (2..4),
        expanded_from: Some(&definition_span2),
        included_from: None,
    };
    assert_eq!(span1.compare(&span2), SpanRelation::Earlier)
}

#[test]
fn same_definition_span_comparison() {
    let definition_span1 = Span {
        file: "",
        bytes: (2..4),
        expanded_from: None,
        included_from: None,
    };
    let span1 = Span {
        file: "",
        bytes: (100..104),
        expanded_from: Some(&definition_span1),
        included_from: None,
    };
    let definition_span2 = Span {
        file: "",
        bytes: (0..2),
        expanded_from: None,
        included_from: None,
    };
    let span2 = Span {
        file: "",
        bytes: (100..104),
        expanded_from: Some(&definition_span2),
        included_from: None,
    };
    assert_eq!(span1.compare(&span2), SpanRelation::Later)
}

#[test]
fn define_include_span_comparison() {
    // Prefer inclusion spots over expansion initially
    let definition_span1 = Span {
        file: "",
        bytes: (2..4),
        expanded_from: None,
        included_from: None,
    };
    let inclusion_span1 = Span {
        file: "",
        bytes: (50..52),
        expanded_from: None,
        included_from: None,
    };
    let span1 = Span {
        file: "",
        bytes: (100..104),
        expanded_from: Some(&definition_span1),
        included_from: Some(&inclusion_span1),
    };
    let definition_span2 = Span {
        file: "",
        bytes: (0..2),
        expanded_from: None,
        included_from: None,
    };
    let inclusion_span2 = Span {
        file: "",
        bytes: (52..54),
        expanded_from: None,
        included_from: None,
    };
    let span2 = Span {
        file: "",
        bytes: (100..104),
        expanded_from: Some(&definition_span2),
        included_from: Some(&inclusion_span2),
    };
    assert_eq!(span1.compare(&span2), SpanRelation::Earlier)
}

#[test]
fn same_define_include_span_comparison() {
    // Use expansion locations if inclusion spots are the same
    let definition_span1 = Span {
        file: "",
        bytes: (2..4),
        expanded_from: None,
        included_from: None,
    };
    let inclusion_span1 = Span {
        file: "",
        bytes: (50..52),
        expanded_from: None,
        included_from: None,
    };
    let span1 = Span {
        file: "",
        bytes: (100..104),
        expanded_from: Some(&definition_span1),
        included_from: Some(&inclusion_span1),
    };
    let definition_span2 = Span {
        file: "",
        bytes: (0..2),
        expanded_from: None,
        included_from: None,
    };
    let inclusion_span2 = Span {
        file: "",
        bytes: (50..52),
        expanded_from: None,
        included_from: None,
    };
    let span2 = Span {
        file: "",
        bytes: (100..104),
        expanded_from: Some(&definition_span2),
        included_from: Some(&inclusion_span2),
    };
    assert_eq!(span1.compare(&span2), SpanRelation::Later)
}

/// Metadata for a given syntax token.
///
/// This includes the [`Span`] of the token. With the `lossless` feature,
/// [`Metadata`] also includes `non_trivia`, which stores non-trivia tokens
/// such as whitespace and comments - see [`NonTriviaToken`]
#[cfg(feature = "lossless")]
#[derive(Default, Clone, Debug)]
pub struct Metadata<'a> {
    pub span: Span<'a>,
    pub non_trivia: Vec<NonTriviaToken<'a>>,
}

/// Metadata for a given syntax token.
///
/// This includes the [`Span`] of the token. With the `lossless` feature,
/// [`Metadata`] also includes `non_trivia`, which stores non-trivia tokens
/// such as whitespace and comments - see [`NonTriviaToken`]
#[cfg(not(feature = "lossless"))]
#[derive(Default, Clone, Debug)]
pub struct Metadata<'a> {
    pub span: Span<'a>,
}

impl<'a> Metadata<'a> {
    /// Construct a new [`Metadata`]. If the `lossless` feature isn't enabled,
    /// `non_trivia` is discarded.
    #[cfg(feature = "lossless")]
    pub fn new(span: Span<'a>, non_trivia: Vec<NonTriviaToken<'a>>) -> Self {
        Self { span, non_trivia }
    }

    /// Construct a new [`Metadata`]. If the `lossless` feature isn't enabled,
    /// `non_trivia` is discarded.
    #[cfg(not(feature = "lossless"))]
    #[allow(unused_variables)]
    pub fn new(span: Span<'a>, non_trivia: Vec<NonTriviaToken<'a>>) -> Self {
        Self { span }
    }
}

impl<'a> PartialEq for Metadata<'a> {
    fn eq(&self, _: &Self) -> bool {
        // Allows checking of overall CST structure without checking
        // exact whitespace
        true
    }
}

// Metadata never returns any Nodes while iterating
impl<'a, 'b> IntoIterator for &'b Metadata<'a> {
    type Item = Node<'a, 'b>;
    type IntoIter = std::iter::Empty<Node<'a, 'b>>;
    fn into_iter(self) -> Self::IntoIter {
        std::iter::empty()
    }
}

impl<'a, 'b> IntoIterator for &'b mut Metadata<'a> {
    type Item = Node<'a, 'b>;
    type IntoIter = std::iter::Empty<Node<'a, 'b>>;
    fn into_iter(self) -> Self::IntoIter {
        std::iter::empty()
    }
}

/// A non-trivia token from the source file
#[derive(Clone, Debug, PartialEq)]
pub enum NonTriviaToken<'a> {
    OnelineComment(&'a str, Span<'a>),
    BlockComment(&'a str, Span<'a>),
    Newline,
}