gbas/
span.rs

1use codebase::{BufId, BufRange};
2use std::cmp;
3use std::fmt::Debug;
4use std::rc::Rc;
5
6pub trait Span: Clone + Debug + PartialEq {
7    fn extend(&self, other: &Self) -> Self;
8}
9
10#[cfg(test)]
11impl Span for () {
12    fn extend(&self, _: &Self) -> Self {}
13}
14
15pub trait Source {
16    type Span: Span;
17    fn span(&self) -> Self::Span;
18}
19
20pub trait TokenTracker {
21    type Span: Span;
22    type BufContext: Clone + LexemeRefFactory<Span = Self::Span>;
23    fn mk_buf_context(
24        &mut self,
25        buf_id: BufId,
26        included_from: Option<Self::Span>,
27    ) -> Self::BufContext;
28}
29
30pub trait LexemeRefFactory {
31    type Span;
32    fn mk_lexeme_ref(&self, range: BufRange) -> Self::Span;
33}
34
35#[derive(Clone, Debug, PartialEq)]
36pub enum TokenRefData {
37    Lexeme {
38        range: BufRange,
39        context: Rc<BufContextData>,
40    },
41}
42
43#[derive(Debug, PartialEq)]
44pub struct BufContextData {
45    pub buf_id: BufId,
46    pub included_from: Option<TokenRefData>,
47}
48
49pub struct SimpleTokenTracker;
50
51impl TokenTracker for SimpleTokenTracker {
52    type Span = TokenRefData;
53    type BufContext = SimpleBufTokenRefFactory;
54    fn mk_buf_context(
55        &mut self,
56        buf_id: BufId,
57        included_from: Option<Self::Span>,
58    ) -> Self::BufContext {
59        let context = Rc::new(BufContextData {
60            buf_id,
61            included_from,
62        });
63        SimpleBufTokenRefFactory { context }
64    }
65}
66
67#[derive(Clone)]
68pub struct SimpleBufTokenRefFactory {
69    context: Rc<BufContextData>,
70}
71
72impl LexemeRefFactory for SimpleBufTokenRefFactory {
73    type Span = TokenRefData;
74    fn mk_lexeme_ref(&self, range: BufRange) -> Self::Span {
75        TokenRefData::Lexeme {
76            range,
77            context: self.context.clone(),
78        }
79    }
80}
81
82impl Span for TokenRefData {
83    fn extend(&self, other: &Self) -> Self {
84        use self::TokenRefData::*;
85        match (self, other) {
86            (
87                Lexeme { range, context },
88                Lexeme {
89                    range: other_range,
90                    context: other_context,
91                },
92            )
93                if Rc::ptr_eq(context, other_context) =>
94            {
95                Lexeme {
96                    range: cmp::min(range.start, other_range.start)
97                        ..cmp::max(range.end, other_range.end),
98                    context: (*context).clone(),
99                }
100            }
101            _ => unreachable!(),
102        }
103    }
104}
105
106#[cfg(test)]
107mod tests {
108    use super::*;
109    use codebase::TextCache;
110
111    #[test]
112    fn extend_span() {
113        let mut codebase = TextCache::new();
114        let src = "left right";
115        let buf_id = codebase.add_src_buf("/my/file", src);
116        let context = Rc::new(BufContextData {
117            buf_id,
118            included_from: None,
119        });
120        let left = TokenRefData::Lexeme {
121            range: BufRange::from(0..4),
122            context: context.clone(),
123        };
124        let right = TokenRefData::Lexeme {
125            range: BufRange::from(5..10),
126            context: context.clone(),
127        };
128        let combined = left.extend(&right);
129        assert_eq!(
130            combined,
131            TokenRefData::Lexeme {
132                range: BufRange::from(0..10),
133                context
134            }
135        )
136    }
137}