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
100
101
102
103
104
105
106
107
use std::fmt::{self, Debug, Display};
use std::prelude::v1::*;
pub struct Punctuated<'a, T: Iterator + Clone>(pub T, pub &'a str);
macro_rules! impl_punctuated {
($($req:ident => $fmt:literal),*$(,)?) => {$(
impl<'a, T: Iterator + Clone> $req for Punctuated<'a, T> where <T as Iterator>::Item: $req {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut vals = self.0.clone();
if let Some(first) = vals.next() {
write!(f, $fmt, first)?;
for rest in vals {
write!(f, concat!("{}", $fmt), self.1, rest)?;
}
}
Ok(())
}
}
)*}
}
impl_punctuated! { Debug => "{:?}", Display => "{}" }
pub fn indent(code: &str) -> String {
Punctuated(code.lines().map(|s| format!(" {}", s)), "\n").to_string()
}
#[test]
fn test_indent() {
assert_eq!(indent(""), "");
assert_eq!(indent("hello"), " hello");
assert_eq!(indent("hello\nworld"), " hello\n world");
}
pub fn escape(raw: &str) -> String {
let mut res = String::with_capacity(raw.len());
for c in raw.chars() {
match c {
'\"' => res += "\\\"",
'\\' => res += "\\\\",
'\'' => res += "\\'",
'\n' => res += "\\n",
'\r' => res += "\\r",
'\t' => res += "\\t",
_ => res.push(c),
}
}
res
}
#[test]
fn test_escape() {
assert_eq!(escape("hello world"), "hello world");
assert_eq!(escape("hello\n\r\t\\'\"world"), "hello\\n\\r\\t\\\\\\'\\\"world");
}
pub fn normalize_space(raw: &str) -> String {
let mut res = String::new();
let mut chars = raw.trim().chars();
while let Some(c) = chars.next() {
if c.is_whitespace() {
res.push(' ');
for cc in chars.by_ref() {
if !cc.is_whitespace() {
res.push(cc);
break
}
}
}
else { res.push(c) }
}
res
}
#[test]
fn test_normalize_space() {
assert_eq!(normalize_space(" \t hello \r\n \r\n\n \t\t \t world \t\t "), "hello world");
}
pub fn c_ident(raw: &str) -> Result<String, ()> {
let cleaned: String = raw.chars().map(|ch| match ch {
'_' | 'a'..='z' | 'A'..='Z' | '0'..='9' => ch,
_ => ' ',
}).collect();
let res = Punctuated(cleaned.split_ascii_whitespace(), "_").to_string();
match res.chars().next() {
None => Err(()),
Some(v) => Ok(if ('0'..='9').contains(&v) { format!("var_{}", res) } else { res })
}
}
#[test]
fn test_c_ident() {
assert_eq!(c_ident("foo").unwrap(), "foo");
assert_eq!(c_ident("foo!").unwrap(), "foo");
assert_eq!(c_ident("foo[]").unwrap(), "foo");
assert_eq!(c_ident("(foo)").unwrap(), "foo");
assert_eq!(c_ident(" (foo) ").unwrap(), "foo");
assert_eq!(c_ident(" (foo-bar) ").unwrap(), "foo_bar");
assert_eq!(c_ident(" (foo bar 27) ").unwrap(), "foo_bar_27");
assert_eq!(c_ident(" (foo bar*} 27)[]{} ").unwrap(), "foo_bar_27");
assert_eq!(c_ident(" ( foo bar*} 27)[]{} ").unwrap(), "foo_bar_27");
assert_eq!(c_ident(" ( foo ba*[]}r*} 27)[]{} ").unwrap(), "foo_ba_r_27");
assert_eq!(c_ident("foo's parent").unwrap(), "foo_s_parent");
assert_eq!(c_ident("6foo").unwrap(), "var_6foo");
assert_eq!(c_ident("[6foo").unwrap(), "var_6foo");
assert_eq!(c_ident("[ 6foo").unwrap(), "var_6foo");
}