lang_util/
position.rs

1//! Input position representation types
2
3use std::{
4    convert::{TryFrom, TryInto},
5    fmt::Display,
6};
7
8use text_size::{TextRange, TextSize};
9
10use crate::FileId;
11
12/// A position in the lexer's input
13#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
14#[cfg_attr(feature = "serde", derive(rserde::Serialize, rserde::Deserialize))]
15#[cfg_attr(feature = "serde", serde(crate = "rserde"))]
16pub struct LexerPosition {
17    /// Source id
18    pub source_id: FileId,
19    /// Raw byte offset
20    pub offset: TextSize,
21}
22
23impl LexerPosition {
24    /// Create a new [LexerPosition]
25    ///
26    /// # Parameters
27    ///
28    /// * `source_id`: source id
29    /// * `offset`: byte offset in the input
30    pub fn new(source_id: FileId, offset: TextSize) -> Self {
31        Self { source_id, offset }
32    }
33
34    /// Create a new [LexerPosition]
35    ///
36    /// # Parameters
37    ///
38    /// * `source_id`: source id
39    /// * `offset`: raw byte offset in the input
40    ///
41    /// # Panics
42    ///
43    /// Panics if the offset can't be converted to a u32.
44    pub fn new_raw<E: std::fmt::Debug>(
45        source_id: FileId,
46        offset: impl TryInto<u32, Error = E>,
47    ) -> Self {
48        Self {
49            source_id,
50            offset: offset.try_into().expect("input too large").into(),
51        }
52    }
53}
54
55impl Display for LexerPosition {
56    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
57        write!(f, "{}:{}", self.source_id, u32::from(self.offset))
58    }
59}
60
61impl From<LexerPosition> for FileId {
62    fn from(value: LexerPosition) -> Self {
63        value.source_id
64    }
65}
66
67impl From<LexerPosition> for TextSize {
68    fn from(value: LexerPosition) -> Self {
69        value.offset
70    }
71}
72
73/// Span information for a node, constructed from a pair of LexerPositions
74#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
75#[cfg_attr(feature = "serde", derive(rserde::Serialize, rserde::Deserialize))]
76#[cfg_attr(feature = "serde", serde(crate = "rserde"))]
77pub struct NodeSpan {
78    /// The index of this span into the list of parsed units. This is used to
79    /// identify which source string this span refers to when combining multiple ASTs
80    source_id: FileId,
81
82    /// Range of the node in the input slice
83    range: TextRange,
84}
85
86impl NodeSpan {
87    /// Create a new node span
88    pub fn new(source_id: FileId, range: TextRange) -> Self {
89        Self { source_id, range }
90    }
91
92    /// Create a new node span from two lexer positions
93    pub fn from_lexer(start: LexerPosition, end: LexerPosition) -> Self {
94        Self {
95            source_id: start.source_id,
96            range: TextRange::new(start.offset, end.offset),
97        }
98    }
99
100    /// Return a 0-length span located at the start of the given source
101    ///
102    /// This may be used in span range queries.
103    pub fn new_start(source_id: FileId) -> Self {
104        Self {
105            source_id,
106            range: TextRange::default(),
107        }
108    }
109
110    /// Return a 0-length span located at the end of the given source (as indicated by the offset)
111    ///
112    /// This may be used in span range queries.
113    pub fn new_end(source_id: FileId, length: usize) -> Self {
114        let length = TextSize::try_from(length).expect("length is too large");
115
116        Self {
117            source_id,
118            range: TextRange::new(length, length),
119        }
120    }
121
122    /// Return the source identifier for this node span
123    pub fn source_id(&self) -> FileId {
124        self.source_id
125    }
126
127    /// Return the span range
128    pub fn range(&self) -> TextRange {
129        self.range
130    }
131
132    /// Return true if this range is empty
133    pub fn is_empty(&self) -> bool {
134        self.range.is_empty()
135    }
136
137    /// Return the length of this span
138    pub fn len(&self) -> TextSize {
139        self.range.len()
140    }
141
142    /// Return the start of this span as a LexerPosition
143    pub fn start(&self) -> LexerPosition {
144        LexerPosition::new(self.source_id, self.range.start())
145    }
146
147    /// Return the end of this span as a LexerPosition
148    pub fn end(&self) -> LexerPosition {
149        LexerPosition::new(self.source_id, self.range.end())
150    }
151}
152
153impl PartialOrd for NodeSpan {
154    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
155        Some(self.cmp(other))
156    }
157}
158
159impl Ord for NodeSpan {
160    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
161        self.source_id
162            .cmp(&other.source_id)
163            .then(self.range.start().cmp(&other.range.start()))
164            .then(self.range.len().cmp(&other.range.len()))
165    }
166}
167
168impl From<NodeSpan> for FileId {
169    fn from(value: NodeSpan) -> Self {
170        value.source_id
171    }
172}
173
174impl From<NodeSpan> for TextRange {
175    fn from(value: NodeSpan) -> Self {
176        value.range
177    }
178}