1use crate::{
2 lite_parser::LiteCommand,
3 parse_helpers::{PERCENT_FORCED_BUILTIN_PARSER_INFO, extract_spread_list, garbage},
4 parse_source::find_dirs_var,
5 type_check::type_compatible,
6};
7use log::trace;
8use nu_engine::DIR_VAR_PARSER_INFO;
9use nu_protocol::{
10 DeclId, Flag, IntoSpanned, ParseError, PositionalArg, ShellError, Signature, Span, Spanned,
11 SyntaxShape, Type, TypeSet,
12 ast::*,
13 did_you_mean,
14 engine::{CommandType, StateWorkingSet},
15};
16use std::str;
17
18#[derive(Debug, PartialEq, Eq)]
20pub(crate) enum CallKind {
21 Help,
22 Valid,
23 Invalid,
24}
25
26pub(crate) fn check_call(
27 working_set: &mut StateWorkingSet,
28 command: Span,
29 sig: &Signature,
30 call: &Call,
31) -> CallKind {
32 if call.named_iter().any(|(n, _, _)| n.item == "help") {
34 return CallKind::Help;
35 }
36
37 if call.positional_iter().count() < sig.required_positional.len() {
38 let end_offset = call
39 .positional_iter()
40 .last()
41 .map(|last| last.span.end)
42 .unwrap_or(command.end);
43 for argument in &sig.required_positional {
47 let found = call.positional_iter().fold(false, |ac, expr| {
48 if argument.shape.to_type() == expr.ty || argument.shape == SyntaxShape::Any {
49 true
50 } else {
51 ac
52 }
53 });
54 if !found {
55 working_set.error(ParseError::MissingPositional(
56 argument.name.clone(),
57 Span::new(end_offset, end_offset),
58 sig.call_signature(),
59 ));
60 return CallKind::Invalid;
61 }
62 }
63
64 let missing = &sig.required_positional[call.positional_iter().count()];
65 working_set.error(ParseError::MissingPositional(
66 missing.name.clone(),
67 Span::new(end_offset, end_offset),
68 sig.call_signature(),
69 ));
70 return CallKind::Invalid;
71 } else {
72 for req_flag in sig.named.iter().filter(|x| x.required) {
73 if call.named_iter().all(|(n, _, _)| n.item != req_flag.long) {
74 working_set.error(ParseError::MissingRequiredFlag(
75 req_flag.long.clone(),
76 command,
77 ));
78 return CallKind::Invalid;
79 }
80 }
81 }
82 CallKind::Valid
83}
84
85fn parse_unknown_arg(
86 working_set: &mut StateWorkingSet,
87 span: Span,
88 signature: &Signature,
89) -> Expression {
90 let shape = signature
91 .rest_positional
92 .as_ref()
93 .map(|arg| arg.shape.clone())
94 .unwrap_or(SyntaxShape::Any);
95
96 crate::parser::parse_value(working_set, span, &shape, None)
97}
98
99fn parse_external_string(working_set: &mut StateWorkingSet, span: Span) -> Expression {
100 let contents = working_set.get_span_contents(span);
101
102 if contents.starts_with(b"r#") {
103 crate::parser::parse_raw_string(working_set, span)
104 } else if contents
105 .iter()
106 .any(|b| matches!(b, b'"' | b'\'' | b'(' | b')' | b'`'))
107 {
108 enum State {
109 Bare {
110 from: usize,
111 },
112 BackTickQuote {
113 from: usize,
114 },
115 Quote {
116 from: usize,
117 quote_char: u8,
118 escaped: bool,
119 },
120 Parenthesized {
121 from: usize,
122 depth: usize,
123 },
124 }
125 let make_span = |from: usize, index: usize| Span {
131 start: span.start + from,
132 end: span.start + index,
133 };
134 let mut spans = vec![];
135 let mut state = State::Bare { from: 0 };
136 let mut index = 0;
137 while index < contents.len() {
138 let ch = contents[index];
139 match &mut state {
140 State::Bare { from } => match ch {
141 b'"' | b'\'' => {
142 if index != *from {
144 spans.push(make_span(*from, index));
145 }
146 state = State::Quote {
148 from: index,
149 quote_char: ch,
150 escaped: false,
151 };
152 }
153 b'$' => {
154 if let Some("e_char @ (b'"' | b'\'')) = contents.get(index + 1) {
155 if index != *from {
157 spans.push(make_span(*from, index));
158 }
159 state = State::Quote {
160 from: index,
161 quote_char,
162 escaped: false,
163 };
164 index += 2;
166 continue;
167 }
168 }
169 b'`' => {
170 if index != *from {
171 spans.push(make_span(*from, index))
172 }
173 state = State::BackTickQuote { from: index }
174 }
175 b'(' => {
176 if index != *from {
177 spans.push(make_span(*from, index))
178 }
179 state = State::Parenthesized {
180 from: index,
181 depth: 1,
182 }
183 }
184 _ => (),
186 },
187 State::Quote {
188 from,
189 quote_char,
190 escaped,
191 } => match ch {
192 ch if ch == *quote_char && !*escaped => {
193 spans.push(make_span(*from, index + 1));
195 state = State::Bare { from: index + 1 };
197 }
198 b'\\' if !*escaped && *quote_char == b'"' => {
199 *escaped = true;
201 }
202 _ => {
203 *escaped = false;
204 }
205 },
206 State::BackTickQuote { from } => {
207 if ch == b'`' {
208 spans.push(make_span(*from, index + 1));
209 state = State::Bare { from: index + 1 };
210 }
211 }
212 State::Parenthesized { from, depth } => {
213 if ch == b')' {
214 if *depth == 1 {
215 spans.push(make_span(*from, index + 1));
216 state = State::Bare { from: index + 1 };
217 } else {
218 *depth -= 1;
219 }
220 } else if ch == b'(' {
221 *depth += 1;
222 }
223 }
224 }
225 index += 1;
226 }
227
228 match state {
230 State::Bare { from }
231 | State::Quote { from, .. }
232 | State::Parenthesized { from, .. }
233 | State::BackTickQuote { from, .. } => {
234 if from < contents.len() {
235 spans.push(make_span(from, contents.len()));
236 }
237 }
238 }
239
240 if log::log_enabled!(log::Level::Trace) {
242 let contents = spans
243 .iter()
244 .map(|span| String::from_utf8_lossy(working_set.get_span_contents(*span)))
245 .collect::<Vec<_>>();
246
247 trace!("parsing: external string, parts: {contents:?}")
248 }
249
250 let quoted =
252 (contents.len() >= 3 && contents.starts_with(b"$\"") && contents.ends_with(b"\""))
253 || is_quoted(contents);
254
255 let exprs: Vec<Expression> = spans
257 .into_iter()
258 .map(|span| crate::parser::parse_string(working_set, span))
259 .collect();
260
261 if exprs
262 .iter()
263 .all(|expr| matches!(expr.expr, Expr::String(..)))
264 {
265 let string = exprs
267 .into_iter()
268 .map(|expr| {
269 let Expr::String(contents) = expr.expr else {
270 unreachable!("already checked that this was a String")
271 };
272 contents
273 })
274 .collect::<String>();
275 if quoted {
276 Expression::new(working_set, Expr::String(string), span, Type::String)
277 } else {
278 Expression::new(
279 working_set,
280 Expr::GlobPattern(string, false),
281 span,
282 Type::Glob,
283 )
284 }
285 } else {
286 let exprs = exprs
288 .into_iter()
289 .flat_map(|expr| match expr.expr {
290 Expr::StringInterpolation(subexprs) => subexprs,
291 _ => vec![expr],
292 })
293 .collect();
294 if quoted {
297 Expression::new(
298 working_set,
299 Expr::StringInterpolation(exprs),
300 span,
301 Type::String,
302 )
303 } else {
304 Expression::new(
305 working_set,
306 Expr::GlobInterpolation(exprs, false),
307 span,
308 Type::Glob,
309 )
310 }
311 }
312 } else {
313 crate::parser::parse_glob_pattern(working_set, span)
314 }
315}
316
317fn is_quoted(bytes: &[u8]) -> bool {
318 matches!(bytes, [b'\'', .., b'\''] | [b'"', .., b'"'])
319}
320
321fn parse_external_arg(working_set: &mut StateWorkingSet, span: Span) -> ExternalArgument {
322 let contents = working_set.get_span_contents(span);
323
324 if let Some(Spanned { item: _, span }) = extract_spread_list(contents.into_spanned(span)) {
325 ExternalArgument::Spread(crate::parser::parse_value(
326 working_set,
327 span,
328 &SyntaxShape::List(Box::new(SyntaxShape::Any)),
329 None,
330 ))
331 } else {
332 ExternalArgument::Regular(parse_regular_external_arg(working_set, span))
333 }
334}
335
336pub(crate) fn parse_regular_external_arg(
337 working_set: &mut StateWorkingSet,
338 span: Span,
339) -> Expression {
340 match working_set.get_span_contents(span) {
341 [b'$', ..] => crate::parser::parse_dollar_expr(working_set, span, &SyntaxShape::Any, None),
342 [b'(', ..] => crate::parser::parse_paren_expr(working_set, span, &SyntaxShape::Any),
343 [b'[', ..] => crate::parser::parse_list_expression(working_set, span, &SyntaxShape::Any),
344 _ => parse_external_string(working_set, span),
345 }
346}
347
348pub fn parse_external_call(
349 working_set: &mut StateWorkingSet,
350 spans: &[Span],
351 call_span: Span,
352) -> Expression {
353 trace!("parse external");
354
355 let head_span = spans[0];
356
357 let head_contents = working_set.get_span_contents(head_span);
358
359 let head = if let [b'$' | b'(', ..] = head_contents {
360 let arg = crate::parser::parse_expression(working_set, &[head_span], None);
362 Box::new(arg)
363 } else {
364 Box::new(parse_external_string(working_set, head_span))
365 };
366
367 let args = spans[1..]
368 .iter()
369 .map(|&span| parse_external_arg(working_set, span))
370 .collect();
371
372 Expression::new(
373 working_set,
374 Expr::ExternalCall(head, args),
375 call_span,
376 Type::Any,
377 )
378}
379
380fn ensure_flag_arg_type(
381 working_set: &mut StateWorkingSet,
382 arg_name: String,
383 arg: Expression,
384 arg_shape: &SyntaxShape,
385 long_name_span: Span,
386) -> (Spanned<String>, Expression) {
387 if !type_compatible(&arg_shape.to_type(), &arg.ty) {
388 working_set.error(ParseError::TypeMismatch(
389 arg_shape.to_type(),
390 arg.ty,
391 arg.span,
392 ));
393 (
394 Spanned {
395 item: arg_name,
396 span: long_name_span,
397 },
398 Expression::garbage(working_set, arg.span),
399 )
400 } else {
401 (
402 Spanned {
403 item: arg_name,
404 span: long_name_span,
405 },
406 arg,
407 )
408 }
409}
410
411enum LongFlagParseResult {
416 FoundFlag(Spanned<String>, Option<Expression>),
418 NoFlag,
420 EndOfOptions,
422}
423
424fn parse_long_flag(
425 working_set: &mut StateWorkingSet,
426 spans: &[Span],
427 spans_idx: &mut usize,
428 sig: &Signature,
429) -> LongFlagParseResult {
430 let arg_span = spans[*spans_idx];
431 let arg_contents = working_set.get_span_contents(arg_span);
432
433 if arg_contents.starts_with(b"--") {
434 if arg_contents == b"--" {
436 return LongFlagParseResult::EndOfOptions;
437 }
438
439 let split: Vec<_> = arg_contents.split(|x| *x == b'=').collect();
441 let long_name = String::from_utf8(split[0][2..].into());
443 if let Ok(long_name) = long_name {
444 if let Some(flag) = sig.get_long_flag(&long_name) {
445 if let Some(arg_shape) = &flag.arg {
446 if split.len() > 1 {
447 let long_name_len = long_name.len();
449 let mut span = arg_span;
450 span.start += long_name_len + 3; let arg = crate::parser::parse_value(working_set, span, arg_shape, None);
453 let (arg_name, val_expression) = ensure_flag_arg_type(
454 working_set,
455 long_name,
456 arg,
457 arg_shape,
458 Span::new(arg_span.start, arg_span.start + long_name_len + 2),
459 );
460 LongFlagParseResult::FoundFlag(arg_name, Some(val_expression))
461 } else if let Some(arg) = spans.get(*spans_idx + 1) {
462 let arg = crate::parser::parse_value(working_set, *arg, arg_shape, None);
463
464 *spans_idx += 1;
465 let (arg_name, val_expression) =
466 ensure_flag_arg_type(working_set, long_name, arg, arg_shape, arg_span);
467 LongFlagParseResult::FoundFlag(arg_name, Some(val_expression))
468 } else {
469 working_set.error(ParseError::MissingFlagParam(
470 arg_shape.to_string(),
471 arg_span,
472 ));
473 LongFlagParseResult::FoundFlag(
476 Spanned {
477 item: long_name,
478 span: arg_span,
479 },
480 None,
481 )
482 }
483 } else {
484 if split.len() > 1 {
487 let long_name_len = long_name.len();
489 let mut span = arg_span;
490 span.start += long_name_len + 3; let arg = crate::parser::parse_value(
493 working_set,
494 span,
495 &SyntaxShape::Boolean,
496 None,
497 );
498
499 let (arg_name, val_expression) = ensure_flag_arg_type(
500 working_set,
501 long_name,
502 arg,
503 &SyntaxShape::Boolean,
504 Span::new(arg_span.start, arg_span.start + long_name_len + 2),
505 );
506 LongFlagParseResult::FoundFlag(arg_name, Some(val_expression))
507 } else {
508 LongFlagParseResult::FoundFlag(
509 Spanned {
510 item: long_name,
511 span: arg_span,
512 },
513 None,
514 )
515 }
516 }
517 } else {
518 let suggestion = did_you_mean(sig.get_names(), &long_name)
519 .map(|name| format!("Did you mean: `--{name}`?"))
520 .unwrap_or("Use `--help` to see available flags".to_owned());
521 working_set.error(ParseError::UnknownFlag(
522 sig.name.clone(),
523 long_name.clone(),
524 arg_span,
525 suggestion,
526 ));
527 LongFlagParseResult::FoundFlag(
529 Spanned {
530 item: long_name,
531 span: arg_span,
532 },
533 None,
534 )
535 }
536 } else {
537 working_set.error(ParseError::NonUtf8(arg_span));
538 LongFlagParseResult::FoundFlag(
539 Spanned {
540 item: "--".into(),
541 span: arg_span,
542 },
543 None,
544 )
545 }
546 } else {
547 LongFlagParseResult::NoFlag
548 }
549}
550
551fn shape_allows_negative_number(shape: &SyntaxShape) -> bool {
554 matches!(
555 shape,
556 SyntaxShape::Int | SyntaxShape::Number | SyntaxShape::Float
557 ) || matches!(shape, SyntaxShape::OneOf(shapes) if shapes.iter().any(shape_allows_negative_number))
558}
559
560fn parse_short_flags(
561 working_set: &mut StateWorkingSet,
562 spans: &[Span],
563 spans_idx: &mut usize,
564 positional_idx: usize,
565 sig: &Signature,
566) -> Option<Vec<Flag>> {
567 let arg_span = spans[*spans_idx];
568
569 let arg_contents = working_set.get_span_contents(arg_span);
570
571 if let Ok(arg_contents_uft8_ref) = str::from_utf8(arg_contents) {
572 if arg_contents_uft8_ref.starts_with('-') && arg_contents_uft8_ref.len() > 1 {
573 let short_flags = &arg_contents_uft8_ref[1..];
574 let num_chars = short_flags.chars().count();
575 let mut found_short_flags = vec![];
576 let mut unmatched_short_flags = vec![];
577 for (offset, short_flag) in short_flags.char_indices() {
578 let short_flag_span = Span::new(
579 arg_span.start + 1 + offset,
580 arg_span.start + 1 + offset + short_flag.len_utf8(),
581 );
582 if let Some(flag) = sig.get_short_flag(short_flag) {
583 if flag.arg.is_some() && offset < num_chars - 1 {
585 working_set
586 .error(ParseError::OnlyLastFlagInBatchCanTakeArg(short_flag_span));
587 break;
588 }
589 found_short_flags.push(flag);
590 } else {
591 unmatched_short_flags.push(short_flag_span);
592 }
593 }
594
595 if found_short_flags.is_empty()
596 && sig
598 .get_positional(positional_idx)
599 .is_some_and(|p| shape_allows_negative_number(&p.shape))
600 && String::from_utf8_lossy(working_set.get_span_contents(arg_span))
601 .parse::<f64>()
602 .is_ok()
603 {
604 return None;
605 } else if let Some(first) = unmatched_short_flags.first() {
606 let contents = working_set.get_span_contents(*first);
607 working_set.error(ParseError::UnknownFlag(
608 sig.name.clone(),
609 format!("-{}", String::from_utf8_lossy(contents)),
610 *first,
611 "Use `--help` to see available flags".to_owned(),
612 ));
613 }
614
615 Some(found_short_flags)
616 } else {
617 None
618 }
619 } else {
620 working_set.error(ParseError::NonUtf8(arg_span));
621 None
622 }
623}
624
625fn first_kw_idx(
626 working_set: &StateWorkingSet,
627 signature: &Signature,
628 spans: &[Span],
629 spans_idx: usize,
630 positional_idx: usize,
631) -> (Option<usize>, usize) {
632 for idx in (positional_idx + 1)..signature.num_positionals() {
633 if let Some(PositionalArg {
634 shape: SyntaxShape::Keyword(kw, ..),
635 ..
636 }) = signature.get_positional(idx)
637 {
638 for (span_idx, &span) in spans.iter().enumerate().skip(spans_idx) {
639 let contents = working_set.get_span_contents(span);
640
641 if contents == kw {
642 return (Some(idx), span_idx);
643 }
644 }
645 }
646 }
647 (None, spans.len())
648}
649
650fn calculate_end_span(
651 working_set: &StateWorkingSet,
652 signature: &Signature,
653 spans: &[Span],
654 spans_idx: usize,
655 positional_idx: usize,
656) -> usize {
657 if signature.rest_positional.is_some() {
658 spans.len()
659 } else {
660 let (kw_pos, kw_idx) =
661 first_kw_idx(working_set, signature, spans, spans_idx, positional_idx);
662
663 if let Some(kw_pos) = kw_pos {
664 let positionals_between = kw_pos - positional_idx - 1;
669 if positionals_between >= (kw_idx - spans_idx) {
670 kw_idx
671 } else {
672 kw_idx - positionals_between
673 }
674 } else {
675 let remaining_spans = spans.len() - (spans_idx + 1);
678 let remaining_positional = signature
680 .required_positional
681 .len()
682 .saturating_sub(positional_idx + 1);
683 let extra_spans = remaining_spans.saturating_sub(remaining_positional);
685 spans_idx + 1 + extra_spans
686 }
687 }
688}
689
690pub(crate) fn parse_oneof(
691 working_set: &mut StateWorkingSet,
692 spans: &[Span],
693 spans_idx: &mut usize,
694 possible_shapes: &Vec<SyntaxShape>,
695 multispan: bool,
696 input_type: Option<&Type>,
697) -> Expression {
698 let starting_spans_idx = *spans_idx;
699 let mut best_guess = None;
700 let mut best_guess_errors = Vec::new();
701 let mut max_first_error_offset = 0;
702 let mut propagate_error = false;
703 for shape in possible_shapes {
704 let starting_error_count = working_set.parse_errors.len();
705 *spans_idx = starting_spans_idx;
706 let value = match multispan {
707 true => parse_multispan_value(working_set, spans, spans_idx, shape, input_type),
708 false => crate::parser::parse_value(working_set, spans[*spans_idx], shape, input_type),
709 };
710
711 let new_errors = &working_set.parse_errors[starting_error_count..];
712 let Some(first_error_offset) = new_errors.iter().map(|e| e.span().start).min() else {
714 return value;
715 };
716
717 if first_error_offset > max_first_error_offset {
718 propagate_error = match working_set.parse_errors.last() {
721 Some(ParseError::Expected(_, error_span))
722 | Some(ParseError::ExpectedWithStringMsg(_, error_span)) => {
723 matches!(
724 shape,
725 SyntaxShape::Block | SyntaxShape::Closure(_) | SyntaxShape::Expression
726 ) && *error_span != spans[*spans_idx]
727 }
728 _ => true,
729 };
730 max_first_error_offset = first_error_offset;
731 best_guess = Some(value);
732 best_guess_errors.clear();
733 best_guess_errors.extend_from_slice(new_errors);
734 }
735 working_set.parse_errors.truncate(starting_error_count);
736 }
737
738 if max_first_error_offset > spans[starting_spans_idx].start || propagate_error {
741 working_set.parse_errors.extend(best_guess_errors);
742 best_guess.expect("best_guess should not be None here!")
743 } else {
744 working_set.error(ParseError::ExpectedWithStringMsg(
745 format!("one of a list of accepted shapes: {possible_shapes:?}"),
746 spans[starting_spans_idx],
747 ));
748 Expression::garbage(working_set, spans[starting_spans_idx])
749 }
750}
751
752pub fn parse_multispan_value(
753 working_set: &mut StateWorkingSet,
754 spans: &[Span],
755 spans_idx: &mut usize,
756 shape: &SyntaxShape,
757 input_type: Option<&Type>,
758) -> Expression {
759 trace!("parse multispan value");
760 match shape {
761 SyntaxShape::VarWithOptType => {
762 trace!("parsing: var with opt type");
763
764 crate::parser::parse_var_with_opt_type(working_set, spans, spans_idx, false, input_type)
765 .0
766 }
767 SyntaxShape::RowCondition => {
768 trace!("parsing: row condition");
769 let arg = crate::parser::parse_row_condition(working_set, &spans[*spans_idx..]);
770 *spans_idx = spans.len() - 1;
771
772 arg
773 }
774 SyntaxShape::MathExpression => {
775 trace!("parsing: math expression");
776
777 let arg = crate::parser::parse_math_expression(
778 working_set,
779 &spans[*spans_idx..],
780 None,
781 input_type,
782 );
783 *spans_idx = spans.len() - 1;
784
785 arg
786 }
787 SyntaxShape::OneOf(possible_shapes) => parse_oneof(
788 working_set,
789 spans,
790 spans_idx,
791 possible_shapes,
792 true,
793 input_type,
794 ),
795
796 SyntaxShape::Expression => {
797 trace!("parsing: expression");
798
799 let arg =
802 crate::parser::parse_expression(working_set, &spans[*spans_idx..], input_type);
803 *spans_idx = spans.len().saturating_sub(1);
804
805 arg
806 }
807 SyntaxShape::Signature => {
808 trace!("parsing: signature");
809
810 let sig = crate::parser::parse_full_signature(working_set, &spans[*spans_idx..], false);
811 *spans_idx = spans.len().saturating_sub(1);
812
813 sig
814 }
815 SyntaxShape::ExternalSignature => {
816 trace!("parsing: external signature");
817
818 let sig = crate::parser::parse_full_signature(working_set, &spans[*spans_idx..], true);
819 *spans_idx = spans.len().saturating_sub(1);
820
821 sig
822 }
823 SyntaxShape::Keyword(keyword, arg) => {
824 trace!(
825 "parsing: keyword({}) {:?}",
826 String::from_utf8_lossy(keyword),
827 arg
828 );
829 let arg_span = spans[*spans_idx];
830
831 let arg_contents = working_set.get_span_contents(arg_span);
832
833 if arg_contents != keyword {
834 working_set.error(ParseError::ExpectedKeyword(
839 String::from_utf8_lossy(keyword).into(),
840 arg_span,
841 ))
842 }
843
844 *spans_idx += 1;
845 if *spans_idx >= spans.len() {
846 working_set.error(ParseError::KeywordMissingArgument(
847 arg.to_string(),
848 String::from_utf8_lossy(keyword).into(),
849 Span::new(spans[*spans_idx - 1].end, spans[*spans_idx - 1].end),
850 ));
851 let keyword = Keyword {
852 keyword: keyword.as_slice().into(),
853 span: spans[*spans_idx - 1],
854 expr: Expression::garbage(working_set, arg_span),
855 };
856 return Expression::new(
857 working_set,
858 Expr::Keyword(Box::new(keyword)),
859 arg_span,
860 Type::Any,
861 );
862 }
863
864 let keyword = Keyword {
865 keyword: keyword.as_slice().into(),
866 span: spans[*spans_idx - 1],
867 expr: parse_multispan_value(working_set, spans, spans_idx, arg, input_type),
868 };
869
870 let kw_span = keyword.span;
872 let expr_span = keyword.expr.span;
873 let ty = keyword.expr.ty.clone();
874 Expression::new(
875 working_set,
876 Expr::Keyword(Box::new(keyword)),
877 kw_span.merge(expr_span),
878 ty,
879 )
880 }
881 _ => {
882 let arg_span = spans[*spans_idx];
884
885 crate::parser::parse_value(working_set, arg_span, shape, input_type)
886 }
887 }
888}
889
890pub struct ParsedInternalCall {
891 pub call: Box<Call>,
892 pub output: Type,
893 pub call_kind: CallKind,
894}
895
896#[derive(Default)]
901pub enum ArgumentParsingLevel {
902 #[default]
903 Full,
904 FirstK { k: usize },
906}
907
908#[inline]
910fn short_spanned(short: char, span: Span) -> Spanned<String> {
911 Spanned {
912 item: short.to_string(),
913 span,
914 }
915}
916
917pub fn parse_internal_call(
918 working_set: &mut StateWorkingSet,
919 command_span: Span,
920 spans: &[Span],
921 decl_id: DeclId,
922 arg_parsing_level: ArgumentParsingLevel,
923 input_type: Option<&Type>,
924) -> ParsedInternalCall {
925 trace!("parsing: internal call (decl id: {})", decl_id.get());
926
927 let mut call = Call::new(command_span);
928 call.decl_id = decl_id;
929 call.head = command_span;
930 let _ = working_set.add_span(call.head);
931
932 let decl = working_set.get_decl(decl_id);
933 let signature = working_set.get_signature(decl);
934
935 enum SpecialCmd {
936 Let,
937 Def,
938 Match,
939 If,
940 }
941
942 impl SpecialCmd {
943 fn from_str(s: &str) -> Option<Self> {
944 Some(match s {
945 "let" => Self::Let,
946 "def" => Self::Def,
947 "match" => Self::Match,
948 "if" => Self::If,
949 _ => return None,
950 })
951 }
952 }
953
954 let special_cmd = decl
955 .is_keyword()
956 .then(|| decl.name())
957 .and_then(SpecialCmd::from_str);
958
959 let output = signature
967 .get_output_type(
968 input_type
969 .map(|ty| ty.clone().union(Type::Nothing))
970 .as_ref(),
971 )
972 .unwrap_or(Type::Error);
973
974 let mut output_override = None;
979
980 let deprecation = decl.deprecation_info();
981
982 let lib_dirs_var_id = match decl.name() {
984 "use" | "overlay use" | "source-env" if decl.is_keyword() => {
985 find_dirs_var(working_set, crate::parse_source::LIB_DIRS_VAR)
986 }
987 "nu-check" if decl.is_builtin() => {
988 find_dirs_var(working_set, crate::parse_source::LIB_DIRS_VAR)
989 }
990 _ => None,
991 };
992
993 let mut positional_idx = 0;
995
996 let mut spans_idx = 0;
999
1000 if let Some(alias) = decl.as_alias() {
1001 if let Expression {
1002 expr: Expr::Call(wrapped_call),
1003 ..
1004 } = &alias.wrapped_call
1005 {
1006 call = *wrapped_call.clone();
1008 call.head = command_span;
1009 positional_idx = call.positional_iter().count();
1011 } else {
1012 working_set.error(ParseError::UnknownState(
1013 "Alias does not point to internal call.".to_string(),
1014 command_span,
1015 ));
1016 return ParsedInternalCall {
1017 call: Box::new(call),
1018 output: Type::Any,
1019 call_kind: CallKind::Invalid,
1020 };
1021 }
1022 }
1023
1024 if let Some(var_id) = lib_dirs_var_id {
1025 call.set_parser_info(
1026 DIR_VAR_PARSER_INFO.to_owned(),
1027 Expression::new(working_set, Expr::Var(var_id), call.head, Type::Any),
1028 );
1029 }
1030
1031 if signature.creates_scope {
1032 working_set.enter_scope();
1033 }
1034
1035 let mut end_of_options = false;
1036
1037 while spans_idx < spans.len() {
1038 let arg_span = spans[spans_idx];
1039
1040 let starting_error_count = working_set.parse_errors.len();
1041
1042 if !end_of_options {
1044 let flag_parse_result = parse_long_flag(working_set, spans, &mut spans_idx, &signature);
1046
1047 match flag_parse_result {
1048 LongFlagParseResult::EndOfOptions => {
1049 end_of_options = true;
1051
1052 if signature.allows_unknown_args {
1053 let arg = parse_unknown_arg(working_set, arg_span, &signature);
1056 call.add_unknown(arg);
1057 }
1058
1059 spans_idx += 1;
1060 continue;
1061 }
1062 LongFlagParseResult::FoundFlag(long_name, arg) => {
1063 if working_set.parse_errors[starting_error_count..]
1065 .iter()
1066 .any(|x| matches!(x, ParseError::UnknownFlag(_, _, _, _)))
1067 && signature.allows_unknown_args
1068 {
1069 working_set.parse_errors.truncate(starting_error_count);
1070 let arg = parse_unknown_arg(working_set, arg_span, &signature);
1071
1072 call.add_unknown(arg);
1073 } else {
1074 call.add_named((long_name, None, arg));
1075 }
1076
1077 spans_idx += 1;
1078 continue;
1079 }
1080 LongFlagParseResult::NoFlag => {
1081 }
1083 }
1084 }
1085
1086 if !end_of_options {
1088 let starting_error_count = working_set.parse_errors.len();
1089
1090 let short_flags = parse_short_flags(
1092 working_set,
1093 spans,
1094 &mut spans_idx,
1095 positional_idx,
1096 &signature,
1097 );
1098
1099 if let Some(short_flags) = short_flags {
1100 if working_set.parse_errors[starting_error_count..]
1101 .iter()
1102 .any(|x| matches!(x, ParseError::UnknownFlag(_, _, _, _)))
1103 && signature.allows_unknown_args
1104 {
1105 working_set.parse_errors.truncate(starting_error_count);
1106 let arg = parse_unknown_arg(working_set, arg_span, &signature);
1107
1108 call.add_unknown(arg);
1109 } else {
1110 for flag in short_flags {
1111 let _ = working_set.add_span(spans[spans_idx]);
1112
1113 if let Some(arg_shape) = flag.arg {
1114 if let Some(arg) = spans.get(spans_idx + 1) {
1115 let arg =
1116 crate::parser::parse_value(working_set, *arg, &arg_shape, None);
1117 let (arg_name, val_expression) = ensure_flag_arg_type(
1118 working_set,
1119 flag.long.clone(),
1120 arg.clone(),
1121 &arg_shape,
1122 spans[spans_idx],
1123 );
1124
1125 if flag.long.is_empty() {
1126 if let Some(short) = flag.short {
1127 call.add_named((
1128 arg_name,
1129 Some(short_spanned(short, spans[spans_idx])),
1130 Some(val_expression),
1131 ));
1132 }
1133 } else {
1134 call.add_named((arg_name, None, Some(val_expression)));
1135 }
1136 spans_idx += 1;
1137 } else {
1138 working_set.error(ParseError::MissingFlagParam(
1139 arg_shape.to_string(),
1140 arg_span,
1141 ));
1142 call.add_named((
1147 Spanned {
1148 item: flag.long.clone(),
1149 span: spans[spans_idx],
1150 },
1151 flag.short
1152 .map(|short| short_spanned(short, spans[spans_idx])),
1153 None,
1154 ));
1155 }
1156 } else if flag.long.is_empty() {
1157 if let Some(short) = flag.short {
1158 call.add_named((
1159 Spanned {
1160 item: String::new(),
1161 span: spans[spans_idx],
1162 },
1163 Some(short_spanned(short, spans[spans_idx])),
1164 None,
1165 ));
1166 }
1167 } else {
1168 call.add_named((
1169 Spanned {
1170 item: flag.long.clone(),
1171 span: spans[spans_idx],
1172 },
1173 None,
1174 None,
1175 ));
1176 }
1177 }
1178 }
1179
1180 spans_idx += 1;
1181 continue;
1182 }
1183 } {
1186 let contents = working_set.get_span_contents(spans[spans_idx]);
1187
1188 if let Some(Spanned {
1189 span: spread_arg_span,
1190 ..
1191 }) = extract_spread_list(contents.into_spanned(spans[spans_idx]))
1192 {
1193 if signature.rest_positional.is_none() && !signature.allows_unknown_args {
1194 working_set.error(ParseError::UnexpectedSpreadArg(
1195 signature.call_signature(),
1196 arg_span,
1197 ));
1198 call.add_positional(Expression::garbage(working_set, arg_span));
1199 } else if positional_idx < signature.required_positional.len() {
1200 working_set.error(ParseError::MissingPositional(
1201 signature.required_positional[positional_idx].name.clone(),
1202 Span::new(spans[spans_idx].start, spans[spans_idx].start),
1203 signature.call_signature(),
1204 ));
1205 call.add_positional(Expression::garbage(working_set, arg_span));
1206 } else {
1207 let rest_shape = match &signature.rest_positional {
1208 Some(arg) if matches!(arg.shape, SyntaxShape::ExternalArgument) => {
1209 SyntaxShape::Any
1211 }
1212 Some(arg) => arg.shape.clone(),
1213 None => SyntaxShape::Any,
1214 };
1215 let args = crate::parser::parse_value(
1217 working_set,
1218 spread_arg_span,
1219 &SyntaxShape::List(Box::new(rest_shape)),
1220 None,
1221 );
1222
1223 call.add_spread(args);
1224 positional_idx =
1226 signature.required_positional.len() + signature.optional_positional.len();
1227 }
1228
1229 spans_idx += 1;
1230 continue;
1231 }
1232 }
1233
1234 if let Some(positional) = signature.get_positional(positional_idx) {
1236 let end = calculate_end_span(working_set, &signature, spans, spans_idx, positional_idx);
1237
1238 if end == spans_idx {
1240 let prev_span = if spans_idx == 0 {
1241 command_span
1242 } else {
1243 spans[spans_idx - 1]
1244 };
1245 let whitespace_span = Span::new(prev_span.end, spans[spans_idx].start);
1246 working_set.error(ParseError::MissingPositional(
1247 positional.name.clone(),
1248 whitespace_span,
1249 signature.call_signature(),
1250 ));
1251 call.add_positional(Expression::garbage(working_set, whitespace_span));
1252 positional_idx += 1;
1253 continue;
1254 }
1255 debug_assert!(end <= spans.len());
1256
1257 if spans[..end].is_empty() || spans_idx == end {
1258 working_set.error(ParseError::MissingPositional(
1259 positional.name.clone(),
1260 Span::new(spans[spans_idx].end, spans[spans_idx].end),
1261 signature.call_signature(),
1262 ));
1263 positional_idx += 1;
1264 continue;
1265 }
1266
1267 let compile_error_count = working_set.compile_errors.len();
1268
1269 let arg = match arg_parsing_level {
1272 ArgumentParsingLevel::FirstK { k } if k <= positional_idx => {
1273 Expression::garbage(working_set, spans[spans_idx])
1274 }
1275 _ => {
1276 let input_type: Option<Type> = match special_cmd {
1277 Some(SpecialCmd::Let)
1280 if let SyntaxShape::VarWithOptType = &positional.shape =>
1281 {
1282 output_override = input_type.cloned();
1283 input_type.cloned()
1284 }
1285 Some(SpecialCmd::Def) if &positional.name == "block" => {
1288 match call.arguments.last() {
1290 Some(Argument::Positional(Expression {
1291 expr: Expr::Signature(sig),
1292 ..
1293 })) => Some(sig.get_input_type()),
1294 _ => None,
1295 }
1296 }
1297 Some(SpecialCmd::If) if positional_idx >= 1 => input_type.cloned(),
1298 Some(SpecialCmd::Match) if &positional.name == "match_block" => {
1299 input_type.cloned()
1300 }
1301 _ => None,
1302 };
1303
1304 let expr = match special_cmd {
1315 Some(SpecialCmd::Def) if &positional.name == "block" => {
1316 let starting_error_count = working_set.parse_errors.len();
1317
1318 let out = crate::parse_expressions::parse_closure_expression(
1319 working_set,
1320 &positional.shape,
1321 spans[spans_idx],
1322 input_type.as_ref(),
1323 );
1324
1325 if let Expr::Closure(_) = out.expr {
1326 out
1327 } else {
1328 working_set.parse_errors.truncate(starting_error_count);
1331 parse_multispan_value(
1332 working_set,
1333 &spans[..end],
1334 &mut spans_idx,
1335 &positional.shape,
1336 input_type.as_ref(),
1337 )
1338 }
1339 }
1340 _ => parse_multispan_value(
1341 working_set,
1342 &spans[..end],
1343 &mut spans_idx,
1344 &positional.shape,
1345 input_type.as_ref(),
1346 ),
1347 };
1348
1349 match special_cmd {
1350 Some(SpecialCmd::Match) if &positional.name == "match_block" => {
1351 output_override = Some(expr.ty.clone());
1352 }
1353 Some(SpecialCmd::If)
1354 if positional_idx == 1
1355 && let Expr::Block(block_id) = &expr.expr =>
1356 {
1357 let block = working_set.get_block(*block_id);
1358 let ty = match block.pipelines.is_empty() {
1359 false => block.output_type(),
1360 true => input_type.unwrap_or(Type::Any),
1361 };
1362
1363 output_override = Some(match output_override {
1364 Some(existing_ty) => existing_ty.union(ty),
1365 None => ty,
1366 });
1367 }
1368 Some(SpecialCmd::If)
1369 if positional_idx == 2
1370 && let Expr::Keyword(kw) = &expr.expr =>
1371 {
1372 let ty = match &kw.expr.expr {
1373 Expr::Block(block_id) => {
1374 let block = working_set.get_block(*block_id);
1375 match block.pipelines.is_empty() {
1376 false => block.output_type(),
1377 true => input_type.unwrap_or(Type::Any),
1378 }
1379 }
1380 _ => kw.expr.ty.clone(),
1381 };
1382
1383 output_override = Some(match output_override {
1384 Some(existing_ty) => existing_ty.union(ty),
1385 None => ty,
1386 });
1387 }
1388 _ => {}
1389 };
1390
1391 expr
1392 }
1393 };
1394
1395 if let SyntaxShape::OneOf(ref shapes) = positional.shape {
1403 for one_shape in shapes {
1404 if let SyntaxShape::Keyword(keyword, ..) = one_shape
1405 && keyword == b"catch"
1406 && let [nu_protocol::CompileError::NotInALoop { .. }] =
1407 &working_set.compile_errors[compile_error_count..]
1408 {
1409 working_set.compile_errors.truncate(compile_error_count);
1410 }
1411 }
1412 }
1413
1414 let arg = if !type_compatible(&positional.shape.to_type(), &arg.ty) {
1415 working_set.error(ParseError::TypeMismatch(
1416 positional.shape.to_type(),
1417 arg.ty,
1418 arg.span,
1419 ));
1420 Expression::garbage(working_set, arg.span)
1421 } else {
1422 arg
1423 };
1424
1425 call.add_positional(arg);
1426 positional_idx += 1;
1427 } else if signature.allows_unknown_args {
1428 let arg = parse_unknown_arg(working_set, arg_span, &signature);
1429
1430 call.add_unknown(arg);
1431 } else {
1432 call.add_positional(Expression::garbage(working_set, arg_span));
1433 working_set.error(ParseError::ExtraPositional(
1434 signature.call_signature(),
1435 arg_span,
1436 ))
1437 }
1438
1439 spans_idx += 1;
1440 }
1441
1442 let call_kind = check_call(working_set, command_span, &signature, &call);
1446
1447 deprecation
1448 .into_iter()
1449 .filter_map(|entry| entry.parse_warning(&signature.name, &call))
1450 .for_each(|warning| {
1451 working_set.warning(warning);
1455 });
1456
1457 if signature.creates_scope {
1458 working_set.exit_scope();
1459 }
1460
1461 match special_cmd {
1462 Some(SpecialCmd::If) if call.arguments.len() < 3 => {
1464 output_override = output_override.map(|ty| ty.union(Type::Nothing))
1465 }
1466 _ => {}
1467 }
1468
1469 let output = output_override.unwrap_or(output);
1470
1471 ParsedInternalCall {
1472 call: Box::new(call),
1473 output,
1474 call_kind,
1475 }
1476}
1477
1478pub fn parse_call(
1479 working_set: &mut StateWorkingSet,
1480 spans: &[Span],
1481 head: Span,
1482 input_type: Option<&Type>,
1483) -> Expression {
1484 trace!("parsing: call");
1485 let call_span = Span::concat(spans);
1486
1487 if spans.is_empty() {
1488 working_set.error(ParseError::UnknownState(
1489 "Encountered command with zero spans".into(),
1490 call_span,
1491 ));
1492 return garbage(working_set, head);
1493 }
1494
1495 let call_sigil = match working_set.get_span_contents(spans[0]).first() {
1496 Some(b'^') => Some(b'^'),
1497 Some(b'%') => Some(b'%'),
1498 _ => None,
1499 };
1500
1501 let mut adjusted_spans = Vec::new();
1502 let resolution_spans = match call_sigil {
1503 Some(b'^') | Some(b'%') => {
1504 adjusted_spans.reserve(spans.len());
1505 adjusted_spans.push(Span::new(spans[0].start + 1, spans[0].end));
1506 adjusted_spans.extend_from_slice(&spans[1..]);
1507 adjusted_spans.as_slice()
1508 }
1509 _ => spans,
1510 };
1511
1512 if call_sigil == Some(b'^') {
1515 trace!("parsing: forced external call");
1516 return parse_external_call(working_set, resolution_spans, call_span);
1517 }
1518
1519 if call_sigil == Some(b'%') && !resolution_spans.is_empty() {
1525 let (head_idx, head_span) = {
1527 let first = working_set.get_span_contents(resolution_spans[0]);
1528 if first.is_empty() && resolution_spans.len() > 1 {
1529 (1, resolution_spans[1])
1530 } else {
1531 (0, resolution_spans[0])
1532 }
1533 };
1534
1535 let dynamic_head_contents = working_set.get_span_contents(head_span);
1536 let is_dynamic_head = !dynamic_head_contents.is_empty()
1537 && (dynamic_head_contents[0] == b'$' || dynamic_head_contents[0] == b'(');
1538
1539 if is_dynamic_head {
1540 trace!("parsing: dynamic percent builtin dispatch");
1541
1542 let head_expr = crate::parser::parse_expression(working_set, &[head_span], input_type);
1543
1544 let mut call = Call::new(call_span);
1546 call.decl_id = DeclId::new(0);
1547
1548 call.set_parser_info(PERCENT_FORCED_BUILTIN_PARSER_INFO.to_string(), head_expr);
1550
1551 for arg_span in resolution_spans.iter().skip(head_idx + 1) {
1554 let contents = working_set.get_span_contents(*arg_span);
1555 if let Some(Spanned { span: arg_span, .. }) =
1556 extract_spread_list(contents.into_spanned(*arg_span))
1557 {
1558 let spread_expr = crate::parser::parse_value(
1559 working_set,
1560 arg_span,
1561 &SyntaxShape::List(Box::new(SyntaxShape::Any)),
1562 None,
1563 );
1564 call.arguments.push(Argument::Spread(spread_expr));
1565 } else {
1566 let arg_expr =
1567 crate::parser::parse_value(working_set, *arg_span, &SyntaxShape::Any, None);
1568 call.arguments.push(Argument::Positional(arg_expr));
1569 }
1570 }
1571
1572 return Expression::new(
1573 working_set,
1574 Expr::Call(Box::new(call)),
1575 call_span,
1576 Type::Any,
1577 );
1578 }
1579 }
1580
1581 let (cmd_start, pos, _name, maybe_decl_id) = if call_sigil == Some(b'%') {
1582 find_longest_decl_with_command_type(working_set, resolution_spans, CommandType::Builtin)
1583 } else {
1584 find_longest_decl(working_set, resolution_spans)
1585 };
1586
1587 if let Some(decl_id) = maybe_decl_id {
1588 if resolution_spans.len() > 1 {
1591 let test_equal = working_set.get_span_contents(resolution_spans[1]);
1592
1593 if test_equal == *b"=" {
1594 trace!("incomplete statement");
1595
1596 working_set.error(ParseError::UnknownState(
1597 "Incomplete statement".into(),
1598 call_span,
1599 ));
1600 return garbage(working_set, call_span);
1601 }
1602 }
1603
1604 let decl = working_set.get_decl(decl_id);
1605
1606 let parsed_call = if let Some(alias) = decl.as_alias() {
1607 if let Expression {
1608 expr: Expr::ExternalCall(head, args),
1609 span: _,
1610 span_id: _,
1611 ty,
1612 } = &alias.clone().wrapped_call
1613 {
1614 trace!("parsing: alias of external call");
1615
1616 let mut head = head.clone();
1617 head.span = Span::concat(&resolution_spans[cmd_start..pos]); let mut final_args = args.clone().into_vec();
1620 for arg_span in &resolution_spans[pos..] {
1621 let arg = parse_external_arg(working_set, *arg_span);
1622 final_args.push(arg);
1623 }
1624
1625 let expression = Expression::new(
1626 working_set,
1627 Expr::ExternalCall(head, final_args.into()),
1628 Span::concat(spans),
1629 ty.clone(),
1630 );
1631
1632 return expression;
1633 } else {
1634 trace!("parsing: alias of internal call");
1635 parse_internal_call(
1636 working_set,
1637 Span::concat(&resolution_spans[cmd_start..pos]),
1638 &resolution_spans[pos..],
1639 decl_id,
1640 ArgumentParsingLevel::Full,
1641 input_type,
1642 )
1643 }
1644 } else {
1645 trace!("parsing: internal call");
1646 parse_internal_call(
1647 working_set,
1648 Span::concat(&resolution_spans[cmd_start..pos]),
1649 &resolution_spans[pos..],
1650 decl_id,
1651 ArgumentParsingLevel::Full,
1652 input_type,
1653 )
1654 };
1655
1656 Expression::new(
1657 working_set,
1658 Expr::Call(parsed_call.call),
1659 call_span,
1660 parsed_call.output,
1661 )
1662 } else {
1663 if call_sigil == Some(b'%') {
1664 working_set.error(ParseError::LabeledErrorWithHelp {
1665 error: "percent sigil requires a built-in command".into(),
1666 label: "unknown built-in command".into(),
1667 help:
1668 "remove `%` to use normal resolution, or use `^` to run an external command explicitly".into(),
1669 span: resolution_spans[0],
1670 });
1671
1672 return parse_external_call(working_set, resolution_spans, call_span);
1675 }
1676
1677 let bytes = working_set.get_span_contents(spans[0]);
1679 trace!("parsing: range {bytes:?}");
1680 if let (Some(b'.'), Some(b'.')) = (bytes.first(), bytes.get(1)) {
1681 trace!("-- found leading range indicator");
1682 let starting_error_count = working_set.parse_errors.len();
1683
1684 if let Some(range_expr) = crate::parser::parse_range(working_set, spans[0]) {
1685 trace!("-- successfully parsed range");
1686 return range_expr;
1687 }
1688 working_set.parse_errors.truncate(starting_error_count);
1689 }
1690 trace!("parsing: external call");
1691
1692 parse_external_call(working_set, spans, call_span)
1694 }
1695}
1696
1697fn find_decl_with_command_type(
1698 working_set: &StateWorkingSet<'_>,
1699 name: &[u8],
1700 command_type: CommandType,
1701) -> Option<DeclId> {
1702 for idx in (0..working_set.num_decls()).rev() {
1705 let decl_id = DeclId::new(idx);
1706 let decl = working_set.get_decl(decl_id);
1707 if decl.command_type() == command_type && decl.name().as_bytes() == name {
1708 return Some(decl_id);
1709 }
1710 }
1711
1712 None
1713}
1714
1715fn command_name_from_spans(
1716 working_set: &StateWorkingSet<'_>,
1717 spans: &[Span],
1718 prefix: &[u8],
1719) -> Vec<u8> {
1720 let mut name = Vec::with_capacity(prefix.len() + spans.len() * 2);
1721 name.extend(prefix);
1722
1723 for span in spans {
1724 let name_part = working_set.get_span_contents(*span);
1725 if name.is_empty() {
1726 name.extend(name_part);
1727 } else {
1728 name.push(b' ');
1729 name.extend(name_part);
1730 }
1731 }
1732
1733 name
1734}
1735
1736fn find_longest_decl_with_command_type(
1737 working_set: &StateWorkingSet<'_>,
1738 spans: &[Span],
1739 command_type: CommandType,
1740) -> (
1741 usize,
1742 usize,
1743 Vec<u8>,
1744 Option<nu_protocol::Id<nu_protocol::marker::Decl>>,
1745) {
1746 let mut pos = spans.len();
1747 let cmd_start = 0;
1748 let mut name_spans = spans.to_vec();
1749
1750 let mut name = command_name_from_spans(working_set, &name_spans, b"");
1751
1752 let mut maybe_decl_id = find_decl_with_command_type(working_set, &name, command_type);
1753
1754 while maybe_decl_id.is_none() {
1755 if name_spans.len() <= 1 {
1756 break;
1757 }
1758
1759 name_spans.pop();
1760 pos -= 1;
1761
1762 name = command_name_from_spans(working_set, &name_spans, b"");
1763
1764 maybe_decl_id = find_decl_with_command_type(working_set, &name, command_type);
1765 }
1766
1767 (cmd_start, pos, name, maybe_decl_id)
1768}
1769
1770pub fn find_longest_decl(
1771 working_set: &mut StateWorkingSet<'_>,
1772 spans: &[Span],
1773) -> (
1774 usize,
1775 usize,
1776 Vec<u8>,
1777 Option<nu_protocol::Id<nu_protocol::marker::Decl>>,
1778) {
1779 find_longest_decl_with_prefix(working_set, spans, b"")
1780}
1781
1782pub fn find_longest_decl_with_prefix(
1783 working_set: &mut StateWorkingSet<'_>,
1784 spans: &[Span],
1785 prefix: &[u8],
1786) -> (
1787 usize,
1788 usize,
1789 Vec<u8>,
1790 Option<nu_protocol::Id<nu_protocol::marker::Decl>>,
1791) {
1792 let mut pos = 0;
1793 let cmd_start = pos;
1794 let mut name_spans = vec![];
1795
1796 for word_span in spans[cmd_start..].iter() {
1797 name_spans.push(*word_span);
1800
1801 pos += 1;
1802 }
1803
1804 let mut name = command_name_from_spans(working_set, &name_spans, prefix);
1805
1806 let mut maybe_decl_id = working_set.find_decl(&name);
1807
1808 while maybe_decl_id.is_none() {
1809 if name_spans.len() <= 1 {
1811 break;
1813 }
1814
1815 name_spans.pop();
1816 pos -= 1;
1817
1818 name = command_name_from_spans(working_set, &name_spans, prefix);
1819 maybe_decl_id = working_set.find_decl(&name);
1820 }
1821
1822 if let Some(decl_id) = maybe_decl_id
1825 && pos < spans.len()
1826 {
1827 let decl = working_set.get_decl(decl_id);
1828 if let Some(alias) = decl.as_alias() {
1829 if let Expression {
1832 expr: Expr::Call(call),
1833 ..
1834 } = &alias.wrapped_call
1835 {
1836 let aliased_decl_id = call.decl_id;
1837 let aliased_name = working_set.get_decl(aliased_decl_id).name().to_string();
1838
1839 let (_, new_pos, new_name, new_decl_id) = find_longest_decl_with_prefix(
1841 working_set,
1842 &spans[pos..],
1843 aliased_name.as_bytes(),
1844 );
1845
1846 if new_decl_id.is_some() && new_pos > 0 {
1848 let total_pos = pos + new_pos;
1849 return (cmd_start, total_pos, new_name, new_decl_id);
1850 }
1851 }
1852 }
1853 }
1854
1855 (cmd_start, pos, name, maybe_decl_id)
1856}
1857
1858pub fn parse_shorter_head_reading(
1863 working_set: &mut StateWorkingSet,
1864 head: Span,
1865 input_type: Option<&Type>,
1866) -> Option<Expression> {
1867 let contents = working_set.get_span_contents(head).to_vec();
1868 let (tokens, _) = crate::lex::lex(&contents, head.start, &[], &[], true);
1869 let spans: Vec<Span> = tokens.into_iter().map(|token| token.span).collect();
1870
1871 let (_, greedy_pos, _, _) = find_longest_decl(working_set, &spans);
1873 if greedy_pos <= 1 {
1874 return None;
1875 }
1876
1877 let call_span = Span::concat(&spans);
1879 let (cmd_start, pos, _, maybe_decl_id) =
1880 find_longest_decl(working_set, &spans[..greedy_pos - 1]);
1881
1882 let expression = match maybe_decl_id {
1883 Some(decl_id) => {
1884 let parsed = parse_internal_call(
1885 working_set,
1886 Span::concat(&spans[cmd_start..pos]),
1887 &spans[pos..],
1888 decl_id,
1889 ArgumentParsingLevel::Full,
1890 input_type,
1891 );
1892 Expression::new(
1893 working_set,
1894 Expr::Call(parsed.call),
1895 call_span,
1896 parsed.output,
1897 )
1898 }
1899 None => parse_external_call(working_set, &spans, call_span),
1901 };
1902
1903 Some(expression)
1904}
1905
1906pub fn parse_attribute(
1907 working_set: &mut StateWorkingSet,
1908 lite_command: &LiteCommand,
1909) -> (Attribute, Option<String>) {
1910 let _ = lite_command
1911 .parts
1912 .first()
1913 .filter(|s| working_set.get_span_contents(**s).starts_with(b"@"))
1914 .expect("Attributes always start with an `@`");
1915
1916 assert!(
1917 lite_command.attribute_idx.is_empty(),
1918 "attributes can't have attributes"
1919 );
1920
1921 let mut spans = lite_command.parts.clone();
1922 if let Some(first) = spans.first_mut() {
1923 first.start += 1;
1924 }
1925 let spans = spans.as_slice();
1926 let attr_span = Span::concat(spans);
1927
1928 let (cmd_start, cmd_end, mut name, decl_id) =
1929 find_longest_decl_with_prefix(working_set, spans, b"attr");
1930
1931 debug_assert!(name.starts_with(b"attr "));
1932 let _ = name.drain(..(b"attr ".len()));
1933
1934 let name_span = Span::concat(&spans[cmd_start..cmd_end]);
1935
1936 let Ok(name) = String::from_utf8(name) else {
1937 working_set.error(ParseError::NonUtf8(name_span));
1938 return (
1939 Attribute {
1940 expr: garbage(working_set, attr_span),
1941 },
1942 None,
1943 );
1944 };
1945
1946 let Some(decl_id) = decl_id else {
1947 working_set.error(ParseError::UnknownCommand(name_span));
1948 return (
1949 Attribute {
1950 expr: garbage(working_set, attr_span),
1951 },
1952 None,
1953 );
1954 };
1955
1956 let decl = working_set.get_decl(decl_id);
1957
1958 let parsed_call = match decl.as_alias() {
1959 Some(alias) => match &alias.clone().wrapped_call {
1962 Expression {
1963 expr: Expr::ExternalCall(..),
1964 ..
1965 } => {
1966 let shell_error = ShellError::NotAConstCommand { span: name_span };
1967 working_set.error(shell_error.wrap(working_set, attr_span));
1968 return (
1969 Attribute {
1970 expr: garbage(working_set, Span::concat(spans)),
1971 },
1972 None,
1973 );
1974 }
1975 _ => {
1976 trace!("parsing: alias of internal call");
1977 parse_internal_call(
1978 working_set,
1979 name_span,
1980 &spans[cmd_end..],
1981 decl_id,
1982 ArgumentParsingLevel::Full,
1983 None,
1984 )
1985 }
1986 },
1987 None => {
1988 trace!("parsing: internal call");
1989 parse_internal_call(
1990 working_set,
1991 name_span,
1992 &spans[cmd_end..],
1993 decl_id,
1994 ArgumentParsingLevel::Full,
1995 None,
1996 )
1997 }
1998 };
1999
2000 (
2001 Attribute {
2002 expr: Expression::new(
2003 working_set,
2004 Expr::Call(parsed_call.call),
2005 Span::concat(spans),
2006 parsed_call.output,
2007 ),
2008 },
2009 Some(name),
2010 )
2011}