dhall/syntax/ast/
span.rs

1use std::rc::Rc;
2
3/// A location in the source text
4#[derive(Debug, Clone)]
5pub struct ParsedSpan {
6    input: Rc<str>,
7    /// # Safety
8    ///
9    /// Must be a valid character boundary index into `input`.
10    start: usize,
11    /// # Safety
12    ///
13    /// Must be a valid character boundary index into `input`.
14    end: usize,
15}
16
17#[derive(Debug, Clone)]
18pub enum Span {
19    /// A location in the source text
20    Parsed(ParsedSpan),
21    /// Desugarings
22    DuplicateRecordFieldsSugar(Box<Span>, Box<Span>),
23    DottedFieldSugar,
24    RecordPunSugar,
25    /// For expressions obtained from decoding binary
26    Decoded,
27    /// For expressions constructed during normalization/typecheck
28    Artificial,
29}
30
31impl ParsedSpan {
32    pub fn to_input(&self) -> String {
33        self.input.to_string()
34    }
35    /// Convert to a char range for consumption by annotate_snippets.
36    /// This compensates for  https://github.com/rust-lang/annotate-snippets-rs/issues/24
37    pub fn as_char_range(&self) -> (usize, usize) {
38        (
39            char_idx_from_byte_idx(&self.input, self.start),
40            char_idx_from_byte_idx(&self.input, self.end),
41        )
42    }
43}
44
45impl Span {
46    pub fn make(input: Rc<str>, sp: pest::Span) -> Self {
47        Span::Parsed(ParsedSpan {
48            input,
49            start: sp.start(),
50            end: sp.end(),
51        })
52    }
53
54    /// Takes the union of the two spans, i.e. the range of input covered by the two spans plus any
55    /// input between them. Assumes that the spans come from the same input. Fails if one of the
56    /// spans does not point to an input location.
57    pub fn union(&self, other: &Span) -> Self {
58        use std::cmp::{max, min};
59        use Span::*;
60        match (self, other) {
61            (Parsed(x), Parsed(y)) if Rc::ptr_eq(&x.input, &y.input) => {
62                Parsed(ParsedSpan {
63                    input: x.input.clone(),
64                    start: min(x.start, y.start),
65                    end: max(x.end, y.end),
66                })
67            }
68            (Parsed(_), Parsed(_)) => panic!(
69                "Tried to union incompatible spans: {:?} and {:?}",
70                self, other
71            ),
72            (Parsed(x), _) => Parsed(x.clone()),
73            (_, Parsed(x)) => Parsed(x.clone()),
74            _ => panic!(
75                "Tried to union incompatible spans: {:?} and {:?}",
76                self, other
77            ),
78        }
79    }
80}
81
82/// Convert a byte idx into a string into a char idx for consumption by annotate_snippets.
83/// The byte idx must be at a char boundary.
84fn char_idx_from_byte_idx(input: &str, idx: usize) -> usize {
85    use std::iter::once;
86    input
87        .char_indices()
88        .map(|(byte_i, _)| byte_i) // We don't care about the char
89        .chain(once(input.len())) // In case the idx points to the end of the string
90        .enumerate()
91        .find(|(_, byte_i)| *byte_i == idx)
92        .map(|(char_i, _)| char_i)
93        .unwrap()
94}