aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_syntax/src/string_lexing/string.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_syntax/src/string_lexing/string.rs')
-rw-r--r--crates/ra_syntax/src/string_lexing/string.rs222
1 files changed, 0 insertions, 222 deletions
diff --git a/crates/ra_syntax/src/string_lexing/string.rs b/crates/ra_syntax/src/string_lexing/string.rs
deleted file mode 100644
index a4742a0d1..000000000
--- a/crates/ra_syntax/src/string_lexing/string.rs
+++ /dev/null
@@ -1,222 +0,0 @@
1use crate::{
2 TextRange,
3 string_lexing::{
4 parser::Parser,
5 StringComponent,
6}};
7
8pub fn parse_string_literal(src: &str) -> StringComponentIterator {
9 StringComponentIterator {
10 parser: Parser::new(src, b'"'),
11 has_closing_quote: false,
12 suffix: None,
13 prefix: None,
14 quote: b'"',
15 }
16}
17
18pub fn parse_byte_string_literal(src: &str) -> StringComponentIterator {
19 StringComponentIterator {
20 parser: Parser::new(src, b'"'),
21 has_closing_quote: false,
22 suffix: None,
23 prefix: Some(b'b'),
24 quote: b'"',
25 }
26}
27
28pub fn parse_char_literal(src: &str) -> StringComponentIterator {
29 StringComponentIterator {
30 parser: Parser::new(src, b'\''),
31 has_closing_quote: false,
32 suffix: None,
33 prefix: None,
34 quote: b'\'',
35 }
36}
37
38pub fn parse_byte_literal(src: &str) -> StringComponentIterator {
39 StringComponentIterator {
40 parser: Parser::new(src, b'\''),
41 has_closing_quote: false,
42 suffix: None,
43 prefix: Some(b'b'),
44 quote: b'\'',
45 }
46}
47
48pub struct StringComponentIterator<'a> {
49 parser: Parser<'a>,
50 pub has_closing_quote: bool,
51 pub suffix: Option<TextRange>,
52 prefix: Option<u8>,
53 quote: u8,
54}
55
56impl<'a> Iterator for StringComponentIterator<'a> {
57 type Item = StringComponent;
58 fn next(&mut self) -> Option<StringComponent> {
59 if self.parser.pos == 0 {
60 if let Some(prefix) = self.prefix {
61 assert!(
62 self.parser.advance() == prefix as char,
63 "literal should start with a {:?}",
64 prefix as char,
65 );
66 }
67 assert!(
68 self.parser.advance() == self.quote as char,
69 "literal should start with a {:?}",
70 self.quote as char,
71 );
72 }
73
74 if let Some(component) = self.parser.parse_component() {
75 return Some(component);
76 }
77
78 // We get here when there are no char components left to parse
79 if self.parser.peek() == Some(self.quote as char) {
80 self.parser.advance();
81 self.has_closing_quote = true;
82 if let Some(range) = self.parser.parse_suffix() {
83 self.suffix = Some(range);
84 }
85 }
86
87 assert!(
88 self.parser.peek() == None,
89 "literal should leave no unparsed input: src = {:?}, pos = {}, length = {}",
90 self.parser.src,
91 self.parser.pos,
92 self.parser.src.len()
93 );
94
95 None
96 }
97}
98
99#[cfg(test)]
100mod tests {
101 use rowan::TextRange;
102 use crate::string_lexing::{
103 StringComponent,
104 StringComponentKind::*,
105};
106
107 fn parse(src: &str) -> (bool, Vec<StringComponent>) {
108 let component_iterator = &mut super::parse_char_literal(src);
109 let components: Vec<_> = component_iterator.collect();
110 (component_iterator.has_closing_quote, components)
111 }
112
113 fn unclosed_char_component(src: &str) -> StringComponent {
114 let (has_closing_quote, components) = parse(src);
115 assert!(!has_closing_quote, "char should not have closing quote");
116 assert!(components.len() == 1);
117 components[0].clone()
118 }
119
120 fn closed_char_component(src: &str) -> StringComponent {
121 let (has_closing_quote, components) = parse(src);
122 assert!(has_closing_quote, "char should have closing quote");
123 assert!(components.len() == 1, "Literal: {}\nComponents: {:#?}", src, components);
124 components[0].clone()
125 }
126
127 fn closed_char_components(src: &str) -> Vec<StringComponent> {
128 let (has_closing_quote, components) = parse(src);
129 assert!(has_closing_quote, "char should have closing quote");
130 components
131 }
132
133 fn range_closed(src: &str) -> TextRange {
134 TextRange::from_to(1.into(), (src.len() as u32 - 1).into())
135 }
136
137 fn range_unclosed(src: &str) -> TextRange {
138 TextRange::from_to(1.into(), (src.len() as u32).into())
139 }
140
141 #[test]
142 fn test_unicode_escapes() {
143 let unicode_escapes = &[r"{DEAD}", "{BEEF}", "{FF}", "{}", ""];
144 for escape in unicode_escapes {
145 let escape_sequence = format!(r"'\u{}'", escape);
146 let component = closed_char_component(&escape_sequence);
147 let expected_range = range_closed(&escape_sequence);
148 assert_eq!(component.kind, UnicodeEscape);
149 assert_eq!(component.range, expected_range);
150 }
151 }
152
153 #[test]
154 fn test_unicode_escapes_unclosed() {
155 let unicode_escapes = &["{DEAD", "{BEEF", "{FF"];
156 for escape in unicode_escapes {
157 let escape_sequence = format!(r"'\u{}'", escape);
158 let component = unclosed_char_component(&escape_sequence);
159 let expected_range = range_unclosed(&escape_sequence);
160 assert_eq!(component.kind, UnicodeEscape);
161 assert_eq!(component.range, expected_range);
162 }
163 }
164
165 #[test]
166 fn test_empty_char() {
167 let (has_closing_quote, components) = parse("''");
168 assert!(has_closing_quote, "char should have closing quote");
169 assert!(components.len() == 0);
170 }
171
172 #[test]
173 fn test_unclosed_char() {
174 let component = unclosed_char_component("'a");
175 assert!(component.kind == CodePoint);
176 assert!(component.range == TextRange::from_to(1.into(), 2.into()));
177 }
178
179 #[test]
180 fn test_digit_escapes() {
181 let literals = &[r"", r"5", r"55"];
182
183 for literal in literals {
184 let lit_text = format!(r"'\x{}'", literal);
185 let component = closed_char_component(&lit_text);
186 assert!(component.kind == AsciiCodeEscape);
187 assert!(component.range == range_closed(&lit_text));
188 }
189
190 // More than 2 digits starts a new codepoint
191 let components = closed_char_components(r"'\x555'");
192 assert!(components.len() == 2);
193 assert!(components[1].kind == CodePoint);
194 }
195
196 #[test]
197 fn test_ascii_escapes() {
198 let literals = &[
199 r"\'", "\\\"", // equivalent to \"
200 r"\n", r"\r", r"\t", r"\\", r"\0",
201 ];
202
203 for literal in literals {
204 let lit_text = format!("'{}'", literal);
205 let component = closed_char_component(&lit_text);
206 assert!(component.kind == AsciiEscape);
207 assert!(component.range == range_closed(&lit_text));
208 }
209 }
210
211 #[test]
212 fn test_no_escapes() {
213 let literals = &['"', 'n', 'r', 't', '0', 'x', 'u'];
214
215 for &literal in literals {
216 let lit_text = format!("'{}'", literal);
217 let component = closed_char_component(&lit_text);
218 assert!(component.kind == CodePoint);
219 assert!(component.range == range_closed(&lit_text));
220 }
221 }
222}