1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
use std::collections::VecDeque;
use std::char;
macro_rules! try_option {
($o:expr) => {
match $o {
Some(s) => s,
None => return None,
}
}
}
pub fn unescape(s: &str) -> Option<String> {
let mut queue : VecDeque<_> = String::from(s).chars().collect();
let mut s = String::new();
while let Some(c) = queue.pop_front() {
if c != '\\' {
s.push(c);
continue;
}
match queue.pop_front() {
Some('b') => s.push('\u{0008}'),
Some('f') => s.push('\u{000C}'),
Some('n') => s.push('\n'),
Some('r') => s.push('\r'),
Some('t') => s.push('\t'),
Some('\'') => s.push('\''),
Some('\"') => s.push('\"'),
Some('\\') => s.push('\\'),
Some('u') => s.push(try_option!(unescape_unicode(&mut queue))),
Some('x') => s.push(try_option!(unescape_byte(&mut queue))),
Some(c) if c.is_digit(8) => s.push(try_option!(unescape_octal(c, &mut queue))),
_ => return None
};
}
Some(s)
}
fn unescape_unicode(queue: &mut VecDeque<char>) -> Option<char> {
let mut s = String::new();
for _ in 0..4 {
s.push(try_option!(queue.pop_front()));
}
let u = try_option!(u32::from_str_radix(&s, 16).ok());
char::from_u32(u)
}
fn unescape_byte(queue: &mut VecDeque<char>) -> Option<char> {
let mut s = String::new();
for _ in 0..2 {
s.push(try_option!(queue.pop_front()));
}
let u = try_option!(u32::from_str_radix(&s, 16).ok());
char::from_u32(u)
}
fn unescape_octal(c: char, queue: &mut VecDeque<char>) -> Option<char> {
match unescape_octal_leading(c, queue) {
Some(ch) => {
let _ = queue.pop_front();
let _ = queue.pop_front();
Some(ch)
}
None => unescape_octal_no_leading(c, queue)
}
}
fn unescape_octal_leading(c: char, queue: &VecDeque<char>) -> Option<char> {
if c != '0' && c != '1' && c != '2' && c != '3' {
return None;
}
let mut s = String::new();
s.push(c);
s.push(*try_option!(queue.get(0)));
s.push(*try_option!(queue.get(1)));
let u = try_option!(u32::from_str_radix(&s, 8).ok());
char::from_u32(u)
}
fn unescape_octal_no_leading(c: char, queue: &mut VecDeque<char>) -> Option<char> {
let mut s = String::new();
s.push(c);
s.push(try_option!(queue.pop_front()));
let u = try_option!(u32::from_str_radix(&s, 8).ok());
char::from_u32(u)
}