(expr: E)
| 412 | } |
| 413 | |
| 414 | fn func_call<'a, I, E>(expr: E) -> impl Parser<'a, I, Expr, ParserError<'a>> + Clone + 'a |
| 415 | where |
| 416 | I: Input<'a, Token = lr::Token, Span = Span> + BorrowInput<'a>, |
| 417 | E: Parser<'a, I, Expr, ParserError<'a>> + Clone + 'a, |
| 418 | { |
| 419 | let func_name = expr.clone(); |
| 420 | |
| 421 | let named_arg = ident_part() |
| 422 | .map(Some) |
| 423 | .then_ignore(ctrl(':')) |
| 424 | .then(expr.clone()); |
| 425 | |
| 426 | // TODO: I think this possibly should be restructured. Currently in the case |
| 427 | // of `derive x = 5`, the `x` is an alias of a single positional argument. |
| 428 | // That then means we incorrectly allow something like `derive x = 5 y = 6`, |
| 429 | // since there are two positional arguments each with an alias. This then |
| 430 | // leads to quite confusing error messages. |
| 431 | // |
| 432 | // Instead, we could only allow a single alias per function call as the |
| 433 | // first positional argument? (I worry that not simple though...). |
| 434 | // Alternatively we could change the language to enforce tuples, so `derive |
| 435 | // {x = 5}` were required. But we still need to account for the `join` |
| 436 | // example below, which doesn't work so well in a tuple; so I'm not sure |
| 437 | // this helps much. |
| 438 | // |
| 439 | // As a reminder, we need to account for `derive x = 5` and `join a=artists |
| 440 | // (id==album_id)`. |
| 441 | let positional_arg = maybe_aliased(expr.clone()).map(|e| (None, e)); |
| 442 | |
| 443 | func_name |
| 444 | .then(named_arg.or(positional_arg).repeated().collect::<Vec<_>>()) |
| 445 | .validate( |
| 446 | |(name, args): (Expr, Vec<(Option<String>, Expr)>), extra, emit| { |
| 447 | let span = extra.span(); |
| 448 | if args.is_empty() { |
| 449 | return name.kind; |
| 450 | } |
| 451 | |
| 452 | let mut named_args = HashMap::new(); |
| 453 | let mut positional = Vec::new(); |
| 454 | |
| 455 | for (name, arg) in args { |
| 456 | if let Some(name) = name { |
| 457 | match named_args.entry(name) { |
| 458 | Entry::Occupied(entry) => { |
| 459 | emit.emit(Rich::custom( |
| 460 | span, |
| 461 | format!("argument '{}' is used multiple times", entry.key()), |
| 462 | )); |
| 463 | } |
| 464 | Entry::Vacant(entry) => { |
| 465 | entry.insert(arg); |
| 466 | } |
| 467 | } |
| 468 | } else { |
| 469 | positional.push(arg); |
| 470 | } |
| 471 | } |
no test coverage detected