| 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 | |