lib_ruby_parser_ast/
loc.rs

1use std::convert::TryInto;
2
3/// Representation of any location in the given input
4#[repr(C)]
5#[derive(Clone, PartialEq, Eq, Copy, Default)]
6pub struct Loc {
7    /// Begin of the `Loc` range
8    pub begin: usize,
9
10    /// End of the `Loc` range
11    pub end: usize,
12}
13
14impl Loc {
15    /// Converts location to a range
16    pub fn to_range(&self) -> std::ops::Range<usize> {
17        self.begin..self.end
18    }
19
20    /// Returns size of the `Loc` (i.e. `end - begin`)
21    pub fn size(&self) -> usize {
22        self.end - self.begin
23    }
24
25    /// Returns a new `Loc` with given `begin` and current `end`
26    pub fn with_begin(&self, begin: usize) -> Loc {
27        Self {
28            begin,
29            end: self.end,
30        }
31    }
32
33    /// Returns a new `Loc` with given `end` and current `begin`
34    pub fn with_end(&self, end: usize) -> Loc {
35        Self {
36            begin: self.begin,
37            end,
38        }
39    }
40
41    /// Adds given `delta` to `begin`
42    pub fn adjust_begin(&self, delta: i32) -> Loc {
43        let begin: i32 = self
44            .begin
45            .try_into()
46            .expect("failed to convert location to i32 (is it too big?)");
47        let begin: usize = (begin + delta)
48            .try_into()
49            .expect("failed to convert location to usize (is it negative?)");
50        Self {
51            begin,
52            end: self.end,
53        }
54    }
55
56    /// Adds given `delta` to `end`
57    pub fn adjust_end(&self, d: i32) -> Loc {
58        let end: i32 = self
59            .end
60            .try_into()
61            .expect("failed to convert location to i32 (is it too big?)");
62        let end: usize = (end + d)
63            .try_into()
64            .expect("failed to convert location to usize (is it negative?)");
65        Self {
66            begin: self.begin,
67            end,
68        }
69    }
70
71    /// Returns a new `Loc` with the same `begin`, but adjusted `end`,
72    /// so that its size is equal to given `new_size`
73    pub fn resize(&self, new_size: usize) -> Loc {
74        self.with_end(self.begin + new_size)
75    }
76
77    /// Joins two `Loc`s by choosing `min(begin)` + `max(end)`
78    pub fn join(&self, other: &Self) -> Loc {
79        Self {
80            begin: std::cmp::min(self.begin, other.begin),
81            end: std::cmp::max(self.end, other.end),
82        }
83    }
84
85    pub fn maybe_join(&self, other: &Option<Loc>) -> Loc {
86        match other.as_ref() {
87            Some(other) => self.join(other),
88            None => *self,
89        }
90    }
91
92    /// Returns true if `Loc` is empty (i.e. `begin` == `end`)
93    pub fn is_empty(&self) -> bool {
94        self.begin == self.end
95    }
96
97    pub fn print(&self, name: &str) {
98        println!(
99            "{}{} {}",
100            " ".repeat(self.begin),
101            "~".repeat(self.size()),
102            name
103        )
104    }
105}
106
107impl std::fmt::Debug for Loc {
108    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
109        f.write_str(&format!("{}...{}", self.begin, self.end))
110    }
111}
112
113#[test]
114fn test_to_range() {
115    assert_eq!(Loc { begin: 10, end: 20 }.to_range(), 10..20)
116}
117
118#[test]
119fn test_fmt() {
120    assert_eq!(format!("{:?}", Loc { begin: 10, end: 20 }), "10...20")
121}
122
123#[test]
124fn test_is_empty() {
125    assert!(Loc { begin: 1, end: 1 }.is_empty());
126    assert!(!Loc { begin: 1, end: 2 }.is_empty());
127}