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
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
#[macro_use] extern crate nom;
use std::str::FromStr;
mod display;
use nom::types::CompleteStr as ComStr;
pub use nom::Err;
pub use nom::ErrorKind;
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum Cfg {
Any(Vec<Cfg>),
All(Vec<Cfg>),
Not(Box<Cfg>),
Equal(String, String),
Is(String)
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum Target {
Triple {
arch: String,
vendor: String,
os: String,
env: Option<String>,
},
Cfg(Cfg),
}
pub fn parse_cfg(cfg_str: &str) -> Result<Cfg, nom::Err<ComStr>> {
cfg(ComStr(cfg_str)).map(|(_, res)| res)
}
pub fn parse_target(target_str: &str) -> Result<Target, nom::Err<ComStr>> {
target(ComStr(target_str)).map(|(_, res)| res)
}
named!(cfg<ComStr, Cfg>,
preceded!(
ws!(tag!("cfg")),
delimited!(
ws!(char!('(')),
expr,
terminated!(
ws!(char!(')')),
eof!()))));
named!(expr<ComStr, Cfg>,
ws!(alt!(any | all | not | equal | is)));
named!(any<ComStr, Cfg>,
map!(
preceded!(
tag!("any"),
expr_list),
|e| Cfg::Any(e)));
named!(all<ComStr, Cfg>,
map!(
preceded!(
tag!("all"),
expr_list),
|e| Cfg::All(e)));
named!(not<ComStr, Cfg>,
map!(
preceded!(
tag!("not"),
delimited!(
ws!(char!('(')),
expr,
ws!(char!(')')))),
|e| Cfg::Not(Box::new(e))));
named!(expr_list<ComStr, Vec<Cfg>>,
delimited!(
ws!(char!('(')),
separated_list!(
char!(','),
expr),
ws!(char!(')'))));
named!(equal<ComStr, Cfg>,
map!(
separated_pair!(
bareword,
ws!(char!('=')),
alt!(literal | bareword)),
|(a,b)| Cfg::Equal(a,b)));
named!(bareword<ComStr, String>,
map!(take_while1!(|c: char| c.is_alphanumeric() || c == '_' || c == '-'), |r| r.to_string()));
named!(is<ComStr, Cfg>,
map!(bareword, |s| Cfg::Is(s)));
named!(literal<ComStr, String>,
map!(
delimited!(
char!('"'),
escaped!(is_not!("\"\\"), '\\', one_of!("\"n\\")),
char!('"')),
|s| s.to_string()));
named!(target<ComStr, Target>,
alt!(
terminated!(ws!(triple), eof!()) |
map!(
cfg,
|c| Target::Cfg(c))));
named!(alnum<ComStr, String>,
map!(take_while1!(|c: char| c.is_alphanumeric() || c == '_'), |r| r.to_string()));
named!(triple<ComStr, Target>,
do_parse!(
arch: alnum >>
char!('-') >>
vendor: alnum >>
char!('-') >>
os: alnum >>
env: opt!(preceded!(
char!('-'),
alnum)) >>
(Target::Triple {arch, vendor, os, env})));
impl FromStr for Cfg {
type Err = nom::ErrorKind;
fn from_str(cfg_str: &str) -> Result<Cfg, Self::Err> {
parse_cfg(cfg_str).map_err(|e| e.into_error_kind())
}
}
impl FromStr for Target {
type Err = nom::ErrorKind;
fn from_str(cfg_str: &str) -> Result<Target, Self::Err> {
parse_target(cfg_str).map_err(|e| e.into_error_kind())
}
}
#[test]
fn parses() {
assert_eq!(Ok(Cfg::Is("a".to_string())), parse_cfg(r#"cfg(a)"#));
assert_eq!(Ok(Target::Cfg(Cfg::Is("a".to_string()))), parse_target(r#"cfg(a)"#));
assert_eq!(Ok(Target::Triple{
arch: "sparcv9".to_string(),
vendor: "sun".to_string(),
os: "solaris".to_string(),
env: None,
}), parse_target("sparcv9-sun-solaris"));
assert_eq!(Ok(Target::Triple {
arch: "armv5te".to_string(),
vendor: "unknown".to_string(),
os: "linux".to_string(),
env: Some("musleabi".to_string()),
}), " armv5te-unknown-linux-musleabi ".parse());
assert_eq!(Ok(Cfg::Any(vec![Cfg::Is("ha".to_string())])), r#"cfg(any(ha))"#.parse());
assert_eq!(Ok(Cfg::All(vec![Cfg::Is("ha".to_string())])), parse_cfg(r#"cfg(all(ha))"#));
assert_eq!(Ok(Cfg::Not(Box::new(Cfg::Is("ha".to_string())))), parse_cfg(r#"cfg(not(ha))"#));
assert_eq!(Ok(Cfg::Any(vec![
Cfg::Is("a".to_string()), Cfg::Is("b".to_string())
])), parse_cfg(r#"cfg( any ( a, b))"#));
assert_eq!(Ok(Cfg::Is("a".to_string())), r#"cfg ( a )"#.parse());
assert_eq!(Ok(Cfg::Equal("a".to_string(),"b".to_string())), parse_cfg(r#" cfg(a=b) "#));
assert_eq!(Ok(Cfg::Equal("a".to_string(),"b".to_string())), parse_cfg(r#" cfg( a = b ) "#));
assert_eq!(Ok(Cfg::Any(vec![Cfg::Equal("a".to_string(),"b".to_string())])), parse_cfg(r#"cfg(any(a=b))"#));
assert_eq!(Ok(Cfg::All(vec![Cfg::Equal("a".to_string(),"b".to_string())])), parse_cfg(r#"cfg(all(a=b))"#));
assert_eq!(Ok(Cfg::Not(Box::new(Cfg::Equal("a".to_string(),"b".to_string())))), parse_cfg(r#"cfg(not(a=b))"#));
assert_eq!(Ok(Cfg::Not(Box::new(Cfg::Equal("a_b".to_string(),"b".to_string())))), parse_cfg(r#" cfg( not( a_b = b ) ) "#));
assert_eq!(Ok(Cfg::Not(Box::new(Cfg::Equal("a".to_string(),"b".to_string())))), parse_cfg(r#" cfg( not( a = "b" ) ) "#));
assert!(parse_cfg(r#" cfg( not( a = "b\"\\" ) ) "#).is_ok());
}
#[test]
fn targets() {
for t in ["aarch64-apple-ios", "aarch64-linux-android", "aarch64-unknown-fuchsia",
"aarch64-unknown-linux-gnu", "aarch64-unknown-linux-musl", "arm-linux-androideabi",
"arm-unknown-linux-gnueabi", "arm-unknown-linux-gnueabihf", "arm-unknown-linux-musleabi",
"arm-unknown-linux-musleabihf", "armv5te-unknown-linux-gnueabi",
"armv5te-unknown-linux-musleabi", "armv7-apple-ios", "armv7-linux-androideabi",
"armv7-unknown-linux-gnueabihf", "armv7-unknown-linux-musleabihf", "armv7s-apple-ios",
"asmjs-unknown-emscripten", "i386-apple-ios", "i586-pc-windows-msvc",
"i586-unknown-linux-gnu", "i586-unknown-linux-musl", "i686-apple-darwin",
"i686-linux-android", "i686-pc-windows-gnu", "i686-pc-windows-msvc",
"i686-unknown-freebsd", "i686-unknown-linux-gnu", "i686-unknown-linux-musl",
"mips-unknown-linux-gnu", "mips-unknown-linux-musl", "mips64-unknown-linux-gnuabi64",
"mips64el-unknown-linux-gnuabi64", "mipsel-unknown-linux-gnu", "mipsel-unknown-linux-musl",
"powerpc-unknown-linux-gnu", "powerpc64-unknown-linux-gnu", "powerpc64le-unknown-linux-gnu",
"s390x-unknown-linux-gnu", "sparc64-unknown-linux-gnu", "sparcv9-sun-solaris",
"thumbv6m-none-eabi", "thumbv7em-none-eabi", "thumbv7em-none-eabihf",
"thumbv7m-none-eabi", "wasm32-unknown-emscripten", "wasm32-unknown-unknown",
"x86_64-apple-darwin", "x86_64-apple-ios", "x86_64-linux-android",
"x86_64-pc-windows-gnu", "x86_64-pc-windows-msvc", "x86_64-rumprun-netbsd",
"x86_64-sun-solaris", "x86_64-unknown-cloudabi", "x86_64-unknown-freebsd",
"x86_64-unknown-fuchsia", "x86_64-unknown-linux-gnu", "x86_64-unknown-linux-gnux32",
"x86_64-unknown-linux-musl", "x86_64-unknown-netbsd", "x86_64-unknown-redox"].iter() {
assert!(parse_target(t).is_ok(), t);
}
}
#[test]
fn garbage() {
assert!(parse_target("x86_64-pc-windows-gnu!(#$@#)").is_err());
assert!(parse_target("cfg(ok)--not ok").is_err());
}