1 | use crate::{ |
2 | error::{ParseError, Reason}, |
3 | expr::{ |
4 | lexer::{Lexer, Token}, |
5 | ExprNode, Expression, Func, InnerPredicate, |
6 | }, |
7 | }; |
8 | use smallvec::SmallVec; |
9 | |
10 | impl Expression { |
11 | /// Given a `cfg()` expression (the cfg( and ) are optional), attempts to |
12 | /// parse it into a form where it can be evaluated |
13 | /// |
14 | /// ``` |
15 | /// assert!(cfg_expr::Expression::parse(r#"cfg(all(unix, target_arch = "x86_64"))"# ).is_ok()); |
16 | /// ``` |
17 | pub fn parse(original: &str) -> Result<Self, ParseError> { |
18 | let lexer = Lexer::new(original); |
19 | |
20 | // The lexer automatically trims any cfg( ), so reacquire |
21 | // the string before we start walking tokens |
22 | let original = lexer.inner; |
23 | |
24 | #[derive (Debug)] |
25 | struct FuncAndSpan { |
26 | func: Func, |
27 | parens_index: usize, |
28 | span: std::ops::Range<usize>, |
29 | predicates: SmallVec<[InnerPredicate; 5]>, |
30 | nest_level: u8, |
31 | } |
32 | |
33 | let mut func_stack = SmallVec::<[FuncAndSpan; 5]>::new(); |
34 | let mut expr_queue = SmallVec::<[ExprNode; 5]>::new(); |
35 | |
36 | // Keep track of the last token to simplify validation of the token stream |
37 | let mut last_token: Option<Token<'_>> = None; |
38 | |
39 | let parse_predicate = |key: (&str, std::ops::Range<usize>), |
40 | val: Option<(&str, std::ops::Range<usize>)>| |
41 | -> Result<InnerPredicate, ParseError> { |
42 | // Warning: It is possible for arbitrarily-set configuration |
43 | // options to have the same value as compiler-set configuration |
44 | // options. For example, it is possible to do rustc --cfg "unix" program.rs |
45 | // while compiling to a Windows target, and have both unix and windows |
46 | // configuration options set at the same time. It is unwise to actually |
47 | // do this. |
48 | // |
49 | // rustc is very permissive in this regard, but I'd rather be really |
50 | // strict, as it's much easier to loosen restrictions over time than add |
51 | // new ones |
52 | macro_rules! err_if_val { |
53 | () => { |
54 | if let Some((_, vspan)) = val { |
55 | return Err(ParseError { |
56 | original: original.to_owned(), |
57 | span: vspan, |
58 | reason: Reason::Unexpected(&[]), |
59 | }); |
60 | } |
61 | }; |
62 | } |
63 | |
64 | let span = key.1; |
65 | let key = key.0; |
66 | |
67 | use super::{InnerTarget, Which}; |
68 | |
69 | Ok(match key { |
70 | // These are special cases in the cfg language that are |
71 | // semantically the same as `target_family = "<family>"`, |
72 | // so we just make them not special |
73 | // NOTE: other target families like "wasm" are NOT allowed |
74 | // as naked predicates; they must be specified through |
75 | // `target_family` |
76 | "unix" | "windows" => { |
77 | err_if_val!(); |
78 | |
79 | InnerPredicate::Target(InnerTarget { |
80 | which: Which::Family, |
81 | span: Some(span), |
82 | }) |
83 | } |
84 | "test" => { |
85 | err_if_val!(); |
86 | InnerPredicate::Test |
87 | } |
88 | "debug_assertions" => { |
89 | err_if_val!(); |
90 | InnerPredicate::DebugAssertions |
91 | } |
92 | "proc_macro" => { |
93 | err_if_val!(); |
94 | InnerPredicate::ProcMacro |
95 | } |
96 | "feature" => { |
97 | // rustc allows bare feature without a value, but the only way |
98 | // such a predicate would ever evaluate to true would be if they |
99 | // explicitly set --cfg feature, which would be terrible, so we |
100 | // just error instead |
101 | match val { |
102 | Some((_, span)) => InnerPredicate::Feature(span), |
103 | None => { |
104 | return Err(ParseError { |
105 | original: original.to_owned(), |
106 | span, |
107 | reason: Reason::Unexpected(&["= \"<feature_name> \"" ]), |
108 | }); |
109 | } |
110 | } |
111 | } |
112 | "panic" => match val { |
113 | Some((_, vspan)) => InnerPredicate::Target(InnerTarget { |
114 | which: Which::Panic, |
115 | span: Some(vspan), |
116 | }), |
117 | None => { |
118 | return Err(ParseError { |
119 | original: original.to_owned(), |
120 | span, |
121 | reason: Reason::Unexpected(&["= \"<panic_strategy> \"" ]), |
122 | }); |
123 | } |
124 | }, |
125 | target_key if key.starts_with("target_" ) => { |
126 | let (val, vspan) = match val { |
127 | None => { |
128 | return Err(ParseError { |
129 | original: original.to_owned(), |
130 | span, |
131 | reason: Reason::Unexpected(&["= \"<target_cfg_value> \"" ]), |
132 | }); |
133 | } |
134 | Some((val, vspan)) => (val, vspan), |
135 | }; |
136 | |
137 | macro_rules! tp { |
138 | ($which:ident) => { |
139 | InnerTarget { |
140 | which: Which::$which, |
141 | span: Some(vspan), |
142 | } |
143 | }; |
144 | } |
145 | |
146 | let tp = match &target_key[7..] { |
147 | "abi" => tp!(Abi), |
148 | "arch" => tp!(Arch), |
149 | "feature" => { |
150 | if val.is_empty() { |
151 | return Err(ParseError { |
152 | original: original.to_owned(), |
153 | span: vspan, |
154 | reason: Reason::Unexpected(&["<feature>" ]), |
155 | }); |
156 | } |
157 | |
158 | return Ok(InnerPredicate::TargetFeature(vspan)); |
159 | } |
160 | "os" => tp!(Os), |
161 | "family" => tp!(Family), |
162 | "env" => tp!(Env), |
163 | "endian" => InnerTarget { |
164 | which: Which::Endian(val.parse().map_err(|_err| ParseError { |
165 | original: original.to_owned(), |
166 | span: vspan, |
167 | reason: Reason::InvalidInteger, |
168 | })?), |
169 | span: None, |
170 | }, |
171 | "has_atomic" => InnerTarget { |
172 | which: Which::HasAtomic(val.parse().map_err(|_err| ParseError { |
173 | original: original.to_owned(), |
174 | span: vspan, |
175 | reason: Reason::InvalidHasAtomic, |
176 | })?), |
177 | span: None, |
178 | }, |
179 | "pointer_width" => InnerTarget { |
180 | which: Which::PointerWidth(val.parse().map_err(|_err| ParseError { |
181 | original: original.to_owned(), |
182 | span: vspan, |
183 | reason: Reason::InvalidInteger, |
184 | })?), |
185 | span: None, |
186 | }, |
187 | "vendor" => tp!(Vendor), |
188 | _ => { |
189 | return Err(ParseError { |
190 | original: original.to_owned(), |
191 | span, |
192 | reason: Reason::Unexpected(&[ |
193 | "target_arch" , |
194 | "target_feature" , |
195 | "target_os" , |
196 | "target_family" , |
197 | "target_env" , |
198 | "target_endian" , |
199 | "target_has_atomic" , |
200 | "target_pointer_width" , |
201 | "target_vendor" , |
202 | ]), |
203 | }) |
204 | } |
205 | }; |
206 | |
207 | InnerPredicate::Target(tp) |
208 | } |
209 | _other => InnerPredicate::Other { |
210 | identifier: span, |
211 | value: val.map(|(_, span)| span), |
212 | }, |
213 | }) |
214 | }; |
215 | |
216 | macro_rules! token_err { |
217 | ($span:expr) => {{ |
218 | let expected: &[&str] = match last_token { |
219 | None => &["<key>" , "all" , "any" , "not" ], |
220 | Some(Token::All | Token::Any | Token::Not) => &["(" ], |
221 | Some(Token::CloseParen) => &[")" , "," ], |
222 | Some(Token::Comma) => &[")" , "<key>" ], |
223 | Some(Token::Equals) => &[" \"" ], |
224 | Some(Token::Key(_)) => &["=" , "," , ")" ], |
225 | Some(Token::Value(_)) => &["," , ")" ], |
226 | Some(Token::OpenParen) => &["<key>" , ")" , "all" , "any" , "not" ], |
227 | }; |
228 | |
229 | return Err(ParseError { |
230 | original: original.to_owned(), |
231 | span: $span, |
232 | reason: Reason::Unexpected(&expected), |
233 | }); |
234 | }}; |
235 | } |
236 | |
237 | let mut pred_key: Option<(&str, _)> = None; |
238 | let mut pred_val: Option<(&str, _)> = None; |
239 | |
240 | let mut root_predicate_count = 0; |
241 | |
242 | // Basic implementation of the https://en.wikipedia.org/wiki/Shunting-yard_algorithm |
243 | 'outer: for lt in lexer { |
244 | let lt = lt?; |
245 | match <.token { |
246 | Token::Key(k) => { |
247 | if matches!(last_token, None | Some(Token::OpenParen | Token::Comma)) { |
248 | pred_key = Some((k, lt.span.clone())); |
249 | } else { |
250 | token_err!(lt.span) |
251 | } |
252 | } |
253 | Token::Value(v) => { |
254 | if matches!(last_token, Some(Token::Equals)) { |
255 | // We only record the span for keys and values |
256 | // so that the expression doesn't need a lifetime |
257 | // but in the value case we need to strip off |
258 | // the quotes so that the proper raw string is |
259 | // provided to callers when evaluating the expression |
260 | pred_val = Some((v, lt.span.start + 1..lt.span.end - 1)); |
261 | } else { |
262 | token_err!(lt.span) |
263 | } |
264 | } |
265 | Token::Equals => { |
266 | if !matches!(last_token, Some(Token::Key(_))) { |
267 | token_err!(lt.span) |
268 | } |
269 | } |
270 | Token::All | Token::Any | Token::Not => { |
271 | if matches!(last_token, None | Some(Token::OpenParen | Token::Comma)) { |
272 | let new_fn = match lt.token { |
273 | // the 0 is a dummy value -- it will be substituted for the real |
274 | // number of predicates in the `CloseParen` branch below. |
275 | Token::All => Func::All(0), |
276 | Token::Any => Func::Any(0), |
277 | Token::Not => Func::Not, |
278 | _ => unreachable!(), |
279 | }; |
280 | |
281 | if let Some(fs) = func_stack.last_mut() { |
282 | fs.nest_level += 1; |
283 | } |
284 | |
285 | func_stack.push(FuncAndSpan { |
286 | func: new_fn, |
287 | span: lt.span, |
288 | parens_index: 0, |
289 | predicates: SmallVec::new(), |
290 | nest_level: 0, |
291 | }); |
292 | } else { |
293 | token_err!(lt.span) |
294 | } |
295 | } |
296 | Token::OpenParen => { |
297 | if matches!(last_token, Some(Token::All | Token::Any | Token::Not)) { |
298 | if let Some(ref mut fs) = func_stack.last_mut() { |
299 | fs.parens_index = lt.span.start; |
300 | } |
301 | } else { |
302 | token_err!(lt.span) |
303 | } |
304 | } |
305 | Token::CloseParen => { |
306 | if matches!( |
307 | last_token, |
308 | None | Some(Token::All | Token::Any | Token::Not | Token::Equals) |
309 | ) { |
310 | token_err!(lt.span) |
311 | } else { |
312 | if let Some(top) = func_stack.pop() { |
313 | let key = pred_key.take(); |
314 | let val = pred_val.take(); |
315 | |
316 | // In this context, the boolean to int conversion is confusing. |
317 | #[allow (clippy::bool_to_int_with_if)] |
318 | let num_predicates = top.predicates.len() |
319 | + if key.is_some() { 1 } else { 0 } |
320 | + top.nest_level as usize; |
321 | |
322 | let func = match top.func { |
323 | Func::All(_) => Func::All(num_predicates), |
324 | Func::Any(_) => Func::Any(num_predicates), |
325 | Func::Not => { |
326 | // not() doesn't take a predicate list, but only a single predicate, |
327 | // so ensure we have exactly 1 |
328 | if num_predicates != 1 { |
329 | return Err(ParseError { |
330 | original: original.to_owned(), |
331 | span: top.span.start..lt.span.end, |
332 | reason: Reason::InvalidNot(num_predicates), |
333 | }); |
334 | } |
335 | |
336 | Func::Not |
337 | } |
338 | }; |
339 | |
340 | for pred in top.predicates { |
341 | expr_queue.push(ExprNode::Predicate(pred)); |
342 | } |
343 | |
344 | if let Some(key) = key { |
345 | let inner_pred = parse_predicate(key, val)?; |
346 | expr_queue.push(ExprNode::Predicate(inner_pred)); |
347 | } |
348 | |
349 | expr_queue.push(ExprNode::Fn(func)); |
350 | |
351 | // This is the only place we go back to the top of the outer loop, |
352 | // so make sure we correctly record this token |
353 | last_token = Some(Token::CloseParen); |
354 | continue 'outer; |
355 | } |
356 | |
357 | // We didn't have an opening parentheses if we get here |
358 | return Err(ParseError { |
359 | original: original.to_owned(), |
360 | span: lt.span, |
361 | reason: Reason::UnopenedParens, |
362 | }); |
363 | } |
364 | } |
365 | Token::Comma => { |
366 | if matches!( |
367 | last_token, |
368 | None | Some( |
369 | Token::OpenParen | Token::All | Token::Any | Token::Not | Token::Equals |
370 | ) |
371 | ) { |
372 | token_err!(lt.span) |
373 | } else { |
374 | let key = pred_key.take(); |
375 | let val = pred_val.take(); |
376 | |
377 | let inner_pred = key.map(|key| parse_predicate(key, val)).transpose()?; |
378 | |
379 | match (inner_pred, func_stack.last_mut()) { |
380 | (Some(pred), Some(func)) => { |
381 | func.predicates.push(pred); |
382 | } |
383 | (Some(pred), None) => { |
384 | root_predicate_count += 1; |
385 | |
386 | expr_queue.push(ExprNode::Predicate(pred)); |
387 | } |
388 | _ => {} |
389 | } |
390 | } |
391 | } |
392 | } |
393 | |
394 | last_token = Some(lt.token); |
395 | } |
396 | |
397 | if let Some(Token::Equals) = last_token { |
398 | return Err(ParseError { |
399 | original: original.to_owned(), |
400 | span: original.len()..original.len(), |
401 | reason: Reason::Unexpected(&[" \"<value> \"" ]), |
402 | }); |
403 | } |
404 | |
405 | // If we still have functions on the stack, it means we have an unclosed parens |
406 | if let Some(top) = func_stack.pop() { |
407 | if top.parens_index != 0 { |
408 | Err(ParseError { |
409 | original: original.to_owned(), |
410 | span: top.parens_index..original.len(), |
411 | reason: Reason::UnclosedParens, |
412 | }) |
413 | } else { |
414 | Err(ParseError { |
415 | original: original.to_owned(), |
416 | span: top.span, |
417 | reason: Reason::Unexpected(&["(" ]), |
418 | }) |
419 | } |
420 | } else { |
421 | let key = pred_key.take(); |
422 | let val = pred_val.take(); |
423 | |
424 | if let Some(key) = key { |
425 | root_predicate_count += 1; |
426 | expr_queue.push(ExprNode::Predicate(parse_predicate(key, val)?)); |
427 | } |
428 | |
429 | if expr_queue.is_empty() { |
430 | Err(ParseError { |
431 | original: original.to_owned(), |
432 | span: 0..original.len(), |
433 | reason: Reason::Empty, |
434 | }) |
435 | } else if root_predicate_count > 1 { |
436 | Err(ParseError { |
437 | original: original.to_owned(), |
438 | span: 0..original.len(), |
439 | reason: Reason::MultipleRootPredicates, |
440 | }) |
441 | } else { |
442 | Ok(Expression { |
443 | original: original.to_owned(), |
444 | expr: expr_queue, |
445 | }) |
446 | } |
447 | } |
448 | } |
449 | } |
450 | |