diff options
Diffstat (limited to 'crates/ra_syntax/src/lexer/ptr.rs')
-rw-r--r-- | crates/ra_syntax/src/lexer/ptr.rs | 166 |
1 files changed, 166 insertions, 0 deletions
diff --git a/crates/ra_syntax/src/lexer/ptr.rs b/crates/ra_syntax/src/lexer/ptr.rs new file mode 100644 index 000000000..c9a5354ea --- /dev/null +++ b/crates/ra_syntax/src/lexer/ptr.rs | |||
@@ -0,0 +1,166 @@ | |||
1 | use TextUnit; | ||
2 | |||
3 | use std::str::Chars; | ||
4 | |||
5 | /// A simple view into the characters of a string. | ||
6 | pub(crate) struct Ptr<'s> { | ||
7 | text: &'s str, | ||
8 | len: TextUnit, | ||
9 | } | ||
10 | |||
11 | impl<'s> Ptr<'s> { | ||
12 | /// Creates a new `Ptr` from a string. | ||
13 | pub fn new(text: &'s str) -> Ptr<'s> { | ||
14 | Ptr { | ||
15 | text, | ||
16 | len: 0.into(), | ||
17 | } | ||
18 | } | ||
19 | |||
20 | /// Gets the length of the remaining string. | ||
21 | pub fn into_len(self) -> TextUnit { | ||
22 | self.len | ||
23 | } | ||
24 | |||
25 | /// Gets the current character, if one exists. | ||
26 | pub fn current(&self) -> Option<char> { | ||
27 | self.chars().next() | ||
28 | } | ||
29 | |||
30 | /// Gets the nth character from the current. | ||
31 | /// For example, 0 will return the current token, 1 will return the next, etc. | ||
32 | pub fn nth(&self, n: u32) -> Option<char> { | ||
33 | let mut chars = self.chars().peekable(); | ||
34 | chars.by_ref().skip(n as usize).next() | ||
35 | } | ||
36 | |||
37 | /// Checks whether the current character is `c`. | ||
38 | pub fn at(&self, c: char) -> bool { | ||
39 | self.current() == Some(c) | ||
40 | } | ||
41 | |||
42 | /// Checks whether the next characters match `s`. | ||
43 | pub fn at_str(&self, s: &str) -> bool { | ||
44 | let chars = self.chars(); | ||
45 | chars.as_str().starts_with(s) | ||
46 | } | ||
47 | |||
48 | /// Checks whether the current character satisfies the predicate `p`. | ||
49 | pub fn at_p<P: Fn(char) -> bool>(&self, p: P) -> bool { | ||
50 | self.current().map(p) == Some(true) | ||
51 | } | ||
52 | |||
53 | /// Checks whether the nth character satisfies the predicate `p`. | ||
54 | pub fn nth_is_p<P: Fn(char) -> bool>(&self, n: u32, p: P) -> bool { | ||
55 | self.nth(n).map(p) == Some(true) | ||
56 | } | ||
57 | |||
58 | /// Moves to the next character. | ||
59 | pub fn bump(&mut self) -> Option<char> { | ||
60 | let ch = self.chars().next()?; | ||
61 | self.len += TextUnit::of_char(ch); | ||
62 | Some(ch) | ||
63 | } | ||
64 | |||
65 | /// Moves to the next character as long as `pred` is satisfied. | ||
66 | pub fn bump_while<F: Fn(char) -> bool>(&mut self, pred: F) { | ||
67 | loop { | ||
68 | match self.current() { | ||
69 | Some(c) if pred(c) => { | ||
70 | self.bump(); | ||
71 | } | ||
72 | _ => return, | ||
73 | } | ||
74 | } | ||
75 | } | ||
76 | |||
77 | /// Returns the text up to the current point. | ||
78 | pub fn current_token_text(&self) -> &str { | ||
79 | let len: u32 = self.len.into(); | ||
80 | &self.text[..len as usize] | ||
81 | } | ||
82 | |||
83 | /// Returns an iterator over the remaining characters. | ||
84 | fn chars(&self) -> Chars { | ||
85 | let len: u32 = self.len.into(); | ||
86 | self.text[len as usize..].chars() | ||
87 | } | ||
88 | } | ||
89 | |||
90 | #[cfg(test)] | ||
91 | mod tests { | ||
92 | use super::*; | ||
93 | |||
94 | #[test] | ||
95 | fn test_current() { | ||
96 | let ptr = Ptr::new("test"); | ||
97 | assert_eq!(ptr.current(), Some('t')); | ||
98 | } | ||
99 | |||
100 | #[test] | ||
101 | fn test_nth() { | ||
102 | let ptr = Ptr::new("test"); | ||
103 | assert_eq!(ptr.nth(0), Some('t')); | ||
104 | assert_eq!(ptr.nth(1), Some('e')); | ||
105 | assert_eq!(ptr.nth(2), Some('s')); | ||
106 | assert_eq!(ptr.nth(3), Some('t')); | ||
107 | assert_eq!(ptr.nth(4), None); | ||
108 | } | ||
109 | |||
110 | #[test] | ||
111 | fn test_at() { | ||
112 | let ptr = Ptr::new("test"); | ||
113 | assert!(ptr.at('t')); | ||
114 | assert!(!ptr.at('a')); | ||
115 | } | ||
116 | |||
117 | #[test] | ||
118 | fn test_at_str() { | ||
119 | let ptr = Ptr::new("test"); | ||
120 | assert!(ptr.at_str("t")); | ||
121 | assert!(ptr.at_str("te")); | ||
122 | assert!(ptr.at_str("test")); | ||
123 | assert!(!ptr.at_str("tests")); | ||
124 | assert!(!ptr.at_str("rust")); | ||
125 | } | ||
126 | |||
127 | #[test] | ||
128 | fn test_at_p() { | ||
129 | let ptr = Ptr::new("test"); | ||
130 | assert!(ptr.at_p(|c| c == 't')); | ||
131 | assert!(!ptr.at_p(|c| c == 'e')); | ||
132 | } | ||
133 | |||
134 | #[test] | ||
135 | fn test_nth_is_p() { | ||
136 | let ptr = Ptr::new("test"); | ||
137 | assert!(ptr.nth_is_p(0,|c| c == 't')); | ||
138 | assert!(!ptr.nth_is_p(1,|c| c == 't')); | ||
139 | assert!(ptr.nth_is_p(3,|c| c == 't')); | ||
140 | assert!(!ptr.nth_is_p(150,|c| c == 't')); | ||
141 | } | ||
142 | |||
143 | #[test] | ||
144 | fn test_bump() { | ||
145 | let mut ptr = Ptr::new("test"); | ||
146 | assert_eq!(ptr.current(), Some('t')); | ||
147 | ptr.bump(); | ||
148 | assert_eq!(ptr.current(), Some('e')); | ||
149 | ptr.bump(); | ||
150 | assert_eq!(ptr.current(), Some('s')); | ||
151 | ptr.bump(); | ||
152 | assert_eq!(ptr.current(), Some('t')); | ||
153 | ptr.bump(); | ||
154 | assert_eq!(ptr.current(), None); | ||
155 | ptr.bump(); | ||
156 | assert_eq!(ptr.current(), None); | ||
157 | } | ||
158 | |||
159 | #[test] | ||
160 | fn test_bump_while() { | ||
161 | let mut ptr = Ptr::new("test"); | ||
162 | assert_eq!(ptr.current(), Some('t')); | ||
163 | ptr.bump_while(|c| c != 's'); | ||
164 | assert_eq!(ptr.current(), Some('s')); | ||
165 | } | ||
166 | } | ||