nu_parser/
parser.rs

1#![allow(clippy::byte_char_slices)]
2
3use crate::{
4    Token, TokenContents,
5    lex::{LexState, is_assignment_operator, lex, lex_n_tokens, lex_signature},
6    lite_parser::{LiteCommand, LitePipeline, LiteRedirection, LiteRedirectionTarget, lite_parse},
7    parse_keywords::*,
8    parse_patterns::parse_pattern,
9    parse_shape_specs::{parse_completer, parse_shape_name, parse_type},
10    type_check::{self, check_range_types, math_result_type, type_compatible},
11};
12use itertools::Itertools;
13use log::trace;
14use nu_engine::DIR_VAR_PARSER_INFO;
15use nu_protocol::{
16    BlockId, DeclId, DidYouMean, ENV_VARIABLE_ID, FilesizeUnit, Flag, IN_VARIABLE_ID, ParseError,
17    PositionalArg, ShellError, Signature, Span, Spanned, SyntaxShape, Type, Value, VarId, ast::*,
18    casing::Casing, did_you_mean, engine::StateWorkingSet, eval_const::eval_constant,
19};
20use std::{
21    collections::{HashMap, HashSet},
22    str,
23    sync::Arc,
24};
25
26pub fn garbage(working_set: &mut StateWorkingSet, span: Span) -> Expression {
27    Expression::garbage(working_set, span)
28}
29
30pub fn garbage_pipeline(working_set: &mut StateWorkingSet, spans: &[Span]) -> Pipeline {
31    Pipeline::from_vec(vec![garbage(working_set, Span::concat(spans))])
32}
33
34fn is_identifier_byte(b: u8) -> bool {
35    b != b'.'
36        && b != b'['
37        && b != b'('
38        && b != b'{'
39        && b != b'+'
40        && b != b'-'
41        && b != b'*'
42        && b != b'^'
43        && b != b'/'
44        && b != b'='
45        && b != b'!'
46        && b != b'<'
47        && b != b'>'
48        && b != b'&'
49        && b != b'|'
50}
51
52pub fn is_math_expression_like(working_set: &mut StateWorkingSet, span: Span) -> bool {
53    let bytes = working_set.get_span_contents(span);
54    if bytes.is_empty() {
55        return false;
56    }
57
58    if bytes == b"true"
59        || bytes == b"false"
60        || bytes == b"null"
61        || bytes == b"not"
62        || bytes == b"if"
63        || bytes == b"match"
64    {
65        return true;
66    }
67
68    let b = bytes[0];
69
70    // check for raw string
71    if bytes.starts_with(b"r#") {
72        return true;
73    }
74
75    if b == b'(' || b == b'{' || b == b'[' || b == b'$' || b == b'"' || b == b'\'' || b == b'-' {
76        return true;
77    }
78
79    let starting_error_count = working_set.parse_errors.len();
80
81    // Number
82    parse_number(working_set, span);
83    if working_set.parse_errors.len() == starting_error_count {
84        return true;
85    }
86    working_set.parse_errors.truncate(starting_error_count);
87
88    // Filesize
89    parse_filesize(working_set, span);
90    if working_set.parse_errors.len() == starting_error_count {
91        return true;
92    }
93    working_set.parse_errors.truncate(starting_error_count);
94
95    parse_duration(working_set, span);
96    if working_set.parse_errors.len() == starting_error_count {
97        return true;
98    }
99    working_set.parse_errors.truncate(starting_error_count);
100
101    parse_datetime(working_set, span);
102    if working_set.parse_errors.len() == starting_error_count {
103        return true;
104    }
105    working_set.parse_errors.truncate(starting_error_count);
106
107    parse_binary(working_set, span);
108    // We need an additional negate match to check if the last error was unexpected
109    // or more specifically, if it was `ParseError::InvalidBinaryString`.
110    // If so, we suppress the error and stop parsing to the next (which is `parse_range()`).
111    if working_set.parse_errors.len() == starting_error_count {
112        return true;
113    } else if !matches!(
114        working_set.parse_errors.last(),
115        Some(ParseError::Expected(_, _))
116    ) {
117        working_set.parse_errors.truncate(starting_error_count);
118        return true;
119    }
120    working_set.parse_errors.truncate(starting_error_count);
121
122    let is_range = parse_range(working_set, span).is_some();
123    working_set.parse_errors.truncate(starting_error_count);
124    is_range
125}
126
127fn is_env_variable_name(bytes: &[u8]) -> bool {
128    if bytes.is_empty() {
129        return false;
130    }
131
132    let first = bytes[0];
133    if !first.is_ascii_alphabetic() && first != b'_' {
134        return false;
135    }
136
137    bytes
138        .iter()
139        .skip(1)
140        .all(|&b| b.is_ascii_alphanumeric() || b == b'_')
141}
142
143fn is_identifier(bytes: &[u8]) -> bool {
144    bytes.iter().all(|x| is_identifier_byte(*x))
145}
146
147pub fn is_variable(bytes: &[u8]) -> bool {
148    if bytes.len() > 1 && bytes[0] == b'$' {
149        is_identifier(&bytes[1..])
150    } else {
151        is_identifier(bytes)
152    }
153}
154
155pub fn trim_quotes(bytes: &[u8]) -> &[u8] {
156    if (bytes.starts_with(b"\"") && bytes.ends_with(b"\"") && bytes.len() > 1)
157        || (bytes.starts_with(b"\'") && bytes.ends_with(b"\'") && bytes.len() > 1)
158        || (bytes.starts_with(b"`") && bytes.ends_with(b"`") && bytes.len() > 1)
159    {
160        &bytes[1..(bytes.len() - 1)]
161    } else {
162        bytes
163    }
164}
165
166pub fn trim_quotes_str(s: &str) -> &str {
167    if (s.starts_with('"') && s.ends_with('"') && s.len() > 1)
168        || (s.starts_with('\'') && s.ends_with('\'') && s.len() > 1)
169        || (s.starts_with('`') && s.ends_with('`') && s.len() > 1)
170    {
171        &s[1..(s.len() - 1)]
172    } else {
173        s
174    }
175}
176
177/// Return type of `check_call`
178#[derive(Debug, PartialEq, Eq)]
179pub enum CallKind {
180    Help,
181    Valid,
182    Invalid,
183}
184
185pub(crate) fn check_call(
186    working_set: &mut StateWorkingSet,
187    command: Span,
188    sig: &Signature,
189    call: &Call,
190) -> CallKind {
191    // Allow the call to pass if they pass in the help flag
192    if call.named_iter().any(|(n, _, _)| n.item == "help") {
193        return CallKind::Help;
194    }
195
196    if call.positional_len() < sig.required_positional.len() {
197        let end_offset = call
198            .positional_iter()
199            .last()
200            .map(|last| last.span.end)
201            .unwrap_or(command.end);
202        // Comparing the types of all signature positional arguments against the parsed
203        // expressions found in the call. If one type is not found then it could be assumed
204        // that that positional argument is missing from the parsed call
205        for argument in &sig.required_positional {
206            let found = call.positional_iter().fold(false, |ac, expr| {
207                if argument.shape.to_type() == expr.ty || argument.shape == SyntaxShape::Any {
208                    true
209                } else {
210                    ac
211                }
212            });
213            if !found {
214                working_set.error(ParseError::MissingPositional(
215                    argument.name.clone(),
216                    Span::new(end_offset, end_offset),
217                    sig.call_signature(),
218                ));
219                return CallKind::Invalid;
220            }
221        }
222
223        let missing = &sig.required_positional[call.positional_len()];
224        working_set.error(ParseError::MissingPositional(
225            missing.name.clone(),
226            Span::new(end_offset, end_offset),
227            sig.call_signature(),
228        ));
229        return CallKind::Invalid;
230    } else {
231        for req_flag in sig.named.iter().filter(|x| x.required) {
232            if call.named_iter().all(|(n, _, _)| n.item != req_flag.long) {
233                working_set.error(ParseError::MissingRequiredFlag(
234                    req_flag.long.clone(),
235                    command,
236                ));
237                return CallKind::Invalid;
238            }
239        }
240    }
241    CallKind::Valid
242}
243
244/// Parses an unknown argument for the given signature. This handles the parsing as appropriate to
245/// the rest type of the command.
246fn parse_unknown_arg(
247    working_set: &mut StateWorkingSet,
248    span: Span,
249    signature: &Signature,
250) -> Expression {
251    let shape = signature
252        .rest_positional
253        .as_ref()
254        .map(|arg| arg.shape.clone())
255        .unwrap_or(SyntaxShape::Any);
256
257    parse_value(working_set, span, &shape)
258}
259
260/// Parses a string in the arg or head position of an external call.
261///
262/// If the string begins with `r#`, it is parsed as a raw string. If it doesn't contain any quotes
263/// or parentheses, it is parsed as a glob pattern so that tilde and glob expansion can be handled
264/// by `run-external`. Otherwise, we use a custom state machine to put together an interpolated
265/// string, where each balanced pair of quotes is parsed as a separate part of the string, and then
266/// concatenated together.
267///
268/// For example, `-foo="bar\nbaz"` becomes `$"-foo=bar\nbaz"`
269fn parse_external_string(working_set: &mut StateWorkingSet, span: Span) -> Expression {
270    let contents = working_set.get_span_contents(span);
271
272    if contents.starts_with(b"r#") {
273        parse_raw_string(working_set, span)
274    } else if contents
275        .iter()
276        .any(|b| matches!(b, b'"' | b'\'' | b'(' | b')' | b'`'))
277    {
278        enum State {
279            Bare {
280                from: usize,
281            },
282            BackTickQuote {
283                from: usize,
284            },
285            Quote {
286                from: usize,
287                quote_char: u8,
288                escaped: bool,
289            },
290            Parenthesized {
291                from: usize,
292                depth: usize,
293            },
294        }
295        // Find the spans of parts of the string that can be parsed as their own strings for
296        // concatenation.
297        //
298        // By passing each of these parts to `parse_string()`, we can eliminate the quotes and also
299        // handle string interpolation.
300        let make_span = |from: usize, index: usize| Span {
301            start: span.start + from,
302            end: span.start + index,
303        };
304        let mut spans = vec![];
305        let mut state = State::Bare { from: 0 };
306        let mut index = 0;
307        while index < contents.len() {
308            let ch = contents[index];
309            match &mut state {
310                State::Bare { from } => match ch {
311                    b'"' | b'\'' => {
312                        // Push bare string
313                        if index != *from {
314                            spans.push(make_span(*from, index));
315                        }
316                        // then transition to other state
317                        state = State::Quote {
318                            from: index,
319                            quote_char: ch,
320                            escaped: false,
321                        };
322                    }
323                    b'$' => {
324                        if let Some(&quote_char @ (b'"' | b'\'')) = contents.get(index + 1) {
325                            // Start a dollar quote (interpolated string)
326                            if index != *from {
327                                spans.push(make_span(*from, index));
328                            }
329                            state = State::Quote {
330                                from: index,
331                                quote_char,
332                                escaped: false,
333                            };
334                            // Skip over two chars (the dollar sign and the quote)
335                            index += 2;
336                            continue;
337                        }
338                    }
339                    b'`' => {
340                        if index != *from {
341                            spans.push(make_span(*from, index))
342                        }
343                        state = State::BackTickQuote { from: index }
344                    }
345                    b'(' => {
346                        if index != *from {
347                            spans.push(make_span(*from, index))
348                        }
349                        state = State::Parenthesized {
350                            from: index,
351                            depth: 1,
352                        }
353                    }
354                    // Continue to consume
355                    _ => (),
356                },
357                State::Quote {
358                    from,
359                    quote_char,
360                    escaped,
361                } => match ch {
362                    ch if ch == *quote_char && !*escaped => {
363                        // quoted string ended, just make a new span for it.
364                        spans.push(make_span(*from, index + 1));
365                        // go back to Bare state.
366                        state = State::Bare { from: index + 1 };
367                    }
368                    b'\\' if !*escaped && *quote_char == b'"' => {
369                        // The next token is escaped so it doesn't count (only for double quote)
370                        *escaped = true;
371                    }
372                    _ => {
373                        *escaped = false;
374                    }
375                },
376                State::BackTickQuote { from } => {
377                    if ch == b'`' {
378                        spans.push(make_span(*from, index + 1));
379                        state = State::Bare { from: index + 1 };
380                    }
381                }
382                State::Parenthesized { from, depth } => {
383                    if ch == b')' {
384                        if *depth == 1 {
385                            spans.push(make_span(*from, index + 1));
386                            state = State::Bare { from: index + 1 };
387                        } else {
388                            *depth -= 1;
389                        }
390                    } else if ch == b'(' {
391                        *depth += 1;
392                    }
393                }
394            }
395            index += 1;
396        }
397
398        // Add the final span
399        match state {
400            State::Bare { from }
401            | State::Quote { from, .. }
402            | State::Parenthesized { from, .. }
403            | State::BackTickQuote { from, .. } => {
404                if from < contents.len() {
405                    spans.push(make_span(from, contents.len()));
406                }
407            }
408        }
409
410        // Log the spans that will be parsed
411        if log::log_enabled!(log::Level::Trace) {
412            let contents = spans
413                .iter()
414                .map(|span| String::from_utf8_lossy(working_set.get_span_contents(*span)))
415                .collect::<Vec<_>>();
416
417            trace!("parsing: external string, parts: {contents:?}")
418        }
419
420        // Check if the whole thing is quoted. If not, it should be a glob
421        let quoted =
422            (contents.len() >= 3 && contents.starts_with(b"$\"") && contents.ends_with(b"\""))
423                || is_quoted(contents);
424
425        // Parse each as its own string
426        let exprs: Vec<Expression> = spans
427            .into_iter()
428            .map(|span| parse_string(working_set, span))
429            .collect();
430
431        if exprs
432            .iter()
433            .all(|expr| matches!(expr.expr, Expr::String(..)))
434        {
435            // If the exprs are all strings anyway, just collapse into a single string.
436            let string = exprs
437                .into_iter()
438                .map(|expr| {
439                    let Expr::String(contents) = expr.expr else {
440                        unreachable!("already checked that this was a String")
441                    };
442                    contents
443                })
444                .collect::<String>();
445            if quoted {
446                Expression::new(working_set, Expr::String(string), span, Type::String)
447            } else {
448                Expression::new(
449                    working_set,
450                    Expr::GlobPattern(string, false),
451                    span,
452                    Type::Glob,
453                )
454            }
455        } else {
456            // Flatten any string interpolations contained with the exprs.
457            let exprs = exprs
458                .into_iter()
459                .flat_map(|expr| match expr.expr {
460                    Expr::StringInterpolation(subexprs) => subexprs,
461                    _ => vec![expr],
462                })
463                .collect();
464            // Make an interpolation out of the expressions. Use `GlobInterpolation` if it's a bare
465            // word, so that the unquoted state can get passed through to `run-external`.
466            if quoted {
467                Expression::new(
468                    working_set,
469                    Expr::StringInterpolation(exprs),
470                    span,
471                    Type::String,
472                )
473            } else {
474                Expression::new(
475                    working_set,
476                    Expr::GlobInterpolation(exprs, false),
477                    span,
478                    Type::Glob,
479                )
480            }
481        }
482    } else {
483        parse_glob_pattern(working_set, span)
484    }
485}
486
487fn parse_external_arg(working_set: &mut StateWorkingSet, span: Span) -> ExternalArgument {
488    let contents = working_set.get_span_contents(span);
489
490    if contents.len() > 3
491        && contents.starts_with(b"...")
492        && (contents[3] == b'$' || contents[3] == b'[' || contents[3] == b'(')
493    {
494        ExternalArgument::Spread(parse_value(
495            working_set,
496            Span::new(span.start + 3, span.end),
497            &SyntaxShape::List(Box::new(SyntaxShape::Any)),
498        ))
499    } else {
500        ExternalArgument::Regular(parse_regular_external_arg(working_set, span))
501    }
502}
503
504fn parse_regular_external_arg(working_set: &mut StateWorkingSet, span: Span) -> Expression {
505    let contents = working_set.get_span_contents(span);
506
507    if contents.starts_with(b"$") {
508        parse_dollar_expr(working_set, span)
509    } else if contents.starts_with(b"(") {
510        parse_paren_expr(working_set, span, &SyntaxShape::Any)
511    } else if contents.starts_with(b"[") {
512        parse_list_expression(working_set, span, &SyntaxShape::Any)
513    } else {
514        parse_external_string(working_set, span)
515    }
516}
517
518pub fn parse_external_call(working_set: &mut StateWorkingSet, spans: &[Span]) -> Expression {
519    trace!("parse external");
520
521    let head_contents = working_set.get_span_contents(spans[0]);
522
523    let head_span = if head_contents.starts_with(b"^") {
524        Span::new(spans[0].start + 1, spans[0].end)
525    } else {
526        spans[0]
527    };
528
529    let head_contents = working_set.get_span_contents(head_span).to_vec();
530
531    let head = if head_contents.starts_with(b"$") || head_contents.starts_with(b"(") {
532        // the expression is inside external_call, so it's a subexpression
533        let arg = parse_expression(working_set, &[head_span]);
534        Box::new(arg)
535    } else {
536        Box::new(parse_external_string(working_set, head_span))
537    };
538
539    let args = spans[1..]
540        .iter()
541        .map(|&span| parse_external_arg(working_set, span))
542        .collect();
543
544    Expression::new(
545        working_set,
546        Expr::ExternalCall(head, args),
547        Span::concat(spans),
548        Type::Any,
549    )
550}
551
552fn ensure_flag_arg_type(
553    working_set: &mut StateWorkingSet,
554    arg_name: String,
555    arg: Expression,
556    arg_shape: &SyntaxShape,
557    long_name_span: Span,
558) -> (Spanned<String>, Expression) {
559    if !type_compatible(&arg.ty, &arg_shape.to_type()) {
560        working_set.error(ParseError::TypeMismatch(
561            arg_shape.to_type(),
562            arg.ty,
563            arg.span,
564        ));
565        (
566            Spanned {
567                item: arg_name,
568                span: long_name_span,
569            },
570            Expression::garbage(working_set, arg.span),
571        )
572    } else {
573        (
574            Spanned {
575                item: arg_name,
576                span: long_name_span,
577            },
578            arg,
579        )
580    }
581}
582
583fn parse_long_flag(
584    working_set: &mut StateWorkingSet,
585    spans: &[Span],
586    spans_idx: &mut usize,
587    sig: &Signature,
588) -> (Option<Spanned<String>>, Option<Expression>) {
589    let arg_span = spans[*spans_idx];
590    let arg_contents = working_set.get_span_contents(arg_span);
591
592    if arg_contents.starts_with(b"--") {
593        // FIXME: only use the first flag you find?
594        let split: Vec<_> = arg_contents.split(|x| *x == b'=').collect();
595        let long_name = String::from_utf8(split[0].into());
596        if let Ok(long_name) = long_name {
597            let long_name = long_name[2..].to_string();
598            if let Some(flag) = sig.get_long_flag(&long_name) {
599                if let Some(arg_shape) = &flag.arg {
600                    if split.len() > 1 {
601                        // and we also have the argument
602                        let long_name_len = long_name.len();
603                        let mut span = arg_span;
604                        span.start += long_name_len + 3; //offset by long flag and '='
605
606                        let arg = parse_value(working_set, span, arg_shape);
607                        let (arg_name, val_expression) = ensure_flag_arg_type(
608                            working_set,
609                            long_name,
610                            arg,
611                            arg_shape,
612                            Span::new(arg_span.start, arg_span.start + long_name_len + 2),
613                        );
614                        (Some(arg_name), Some(val_expression))
615                    } else if let Some(arg) = spans.get(*spans_idx + 1) {
616                        let arg = parse_value(working_set, *arg, arg_shape);
617
618                        *spans_idx += 1;
619                        let (arg_name, val_expression) =
620                            ensure_flag_arg_type(working_set, long_name, arg, arg_shape, arg_span);
621                        (Some(arg_name), Some(val_expression))
622                    } else {
623                        working_set.error(ParseError::MissingFlagParam(
624                            arg_shape.to_string(),
625                            arg_span,
626                        ));
627                        // NOTE: still need to cover this incomplete flag in the final expression
628                        // see https://github.com/nushell/nushell/issues/16375
629                        (
630                            Some(Spanned {
631                                item: long_name,
632                                span: arg_span,
633                            }),
634                            None,
635                        )
636                    }
637                } else {
638                    // A flag with no argument
639                    // It can also takes a boolean value like --x=true
640                    if split.len() > 1 {
641                        // and we also have the argument
642                        let long_name_len = long_name.len();
643                        let mut span = arg_span;
644                        span.start += long_name_len + 3; //offset by long flag and '='
645
646                        let arg = parse_value(working_set, span, &SyntaxShape::Boolean);
647
648                        let (arg_name, val_expression) = ensure_flag_arg_type(
649                            working_set,
650                            long_name,
651                            arg,
652                            &SyntaxShape::Boolean,
653                            Span::new(arg_span.start, arg_span.start + long_name_len + 2),
654                        );
655                        (Some(arg_name), Some(val_expression))
656                    } else {
657                        (
658                            Some(Spanned {
659                                item: long_name,
660                                span: arg_span,
661                            }),
662                            None,
663                        )
664                    }
665                }
666            } else {
667                let suggestion = did_you_mean(sig.get_names(), &long_name)
668                    .map(|name| format!("Did you mean: `--{name}`?"))
669                    .unwrap_or("Use `--help` to see available flags".to_owned());
670                working_set.error(ParseError::UnknownFlag(
671                    sig.name.clone(),
672                    long_name.clone(),
673                    arg_span,
674                    suggestion,
675                ));
676                (
677                    Some(Spanned {
678                        item: long_name.clone(),
679                        span: arg_span,
680                    }),
681                    None,
682                )
683            }
684        } else {
685            working_set.error(ParseError::NonUtf8(arg_span));
686            (
687                Some(Spanned {
688                    item: "--".into(),
689                    span: arg_span,
690                }),
691                None,
692            )
693        }
694    } else {
695        (None, None)
696    }
697}
698
699fn parse_short_flags(
700    working_set: &mut StateWorkingSet,
701    spans: &[Span],
702    spans_idx: &mut usize,
703    positional_idx: usize,
704    sig: &Signature,
705) -> Option<Vec<Flag>> {
706    let arg_span = spans[*spans_idx];
707
708    let arg_contents = working_set.get_span_contents(arg_span);
709
710    if let Ok(arg_contents_uft8_ref) = str::from_utf8(arg_contents) {
711        if arg_contents_uft8_ref.starts_with('-') && arg_contents_uft8_ref.len() > 1 {
712            let short_flags = &arg_contents_uft8_ref[1..];
713            let num_chars = short_flags.chars().count();
714            let mut found_short_flags = vec![];
715            let mut unmatched_short_flags = vec![];
716            for (offset, short_flag) in short_flags.char_indices() {
717                let short_flag_span = Span::new(
718                    arg_span.start + 1 + offset,
719                    arg_span.start + 1 + offset + short_flag.len_utf8(),
720                );
721                if let Some(flag) = sig.get_short_flag(short_flag) {
722                    // Allow args in short flag batches as long as it is the last flag.
723                    if flag.arg.is_some() && offset < num_chars - 1 {
724                        working_set
725                            .error(ParseError::OnlyLastFlagInBatchCanTakeArg(short_flag_span));
726                        break;
727                    }
728                    found_short_flags.push(flag);
729                } else {
730                    unmatched_short_flags.push(short_flag_span);
731                }
732            }
733
734            if found_short_flags.is_empty()
735                // check to see if we have a negative number
736                && matches!(
737                    sig.get_positional(positional_idx),
738                    Some(PositionalArg {
739                        shape: SyntaxShape::Int | SyntaxShape::Number | SyntaxShape::Float,
740                        ..
741                    })
742                )
743                && String::from_utf8_lossy(working_set.get_span_contents(arg_span))
744                    .parse::<f64>()
745                    .is_ok()
746            {
747                return None;
748            } else if let Some(first) = unmatched_short_flags.first() {
749                let contents = working_set.get_span_contents(*first);
750                working_set.error(ParseError::UnknownFlag(
751                    sig.name.clone(),
752                    format!("-{}", String::from_utf8_lossy(contents)),
753                    *first,
754                    "Use `--help` to see available flags".to_owned(),
755                ));
756            }
757
758            Some(found_short_flags)
759        } else {
760            None
761        }
762    } else {
763        working_set.error(ParseError::NonUtf8(arg_span));
764        None
765    }
766}
767
768fn first_kw_idx(
769    working_set: &StateWorkingSet,
770    signature: &Signature,
771    spans: &[Span],
772    spans_idx: usize,
773    positional_idx: usize,
774) -> (Option<usize>, usize) {
775    for idx in (positional_idx + 1)..signature.num_positionals() {
776        if let Some(PositionalArg {
777            shape: SyntaxShape::Keyword(kw, ..),
778            ..
779        }) = signature.get_positional(idx)
780        {
781            for (span_idx, &span) in spans.iter().enumerate().skip(spans_idx) {
782                let contents = working_set.get_span_contents(span);
783
784                if contents == kw {
785                    return (Some(idx), span_idx);
786                }
787            }
788        }
789    }
790    (None, spans.len())
791}
792
793fn calculate_end_span(
794    working_set: &StateWorkingSet,
795    signature: &Signature,
796    spans: &[Span],
797    spans_idx: usize,
798    positional_idx: usize,
799) -> usize {
800    if signature.rest_positional.is_some() {
801        spans.len()
802    } else {
803        let (kw_pos, kw_idx) =
804            first_kw_idx(working_set, signature, spans, spans_idx, positional_idx);
805
806        if let Some(kw_pos) = kw_pos {
807            // We found a keyword. Keywords, once found, create a guidepost to
808            // show us where the positionals will lay into the arguments. Because they're
809            // keywords, they get to set this by being present
810
811            let positionals_between = kw_pos - positional_idx - 1;
812            if positionals_between >= (kw_idx - spans_idx) {
813                kw_idx
814            } else {
815                kw_idx - positionals_between
816            }
817        } else {
818            // Make space for the remaining require positionals, if we can
819            // spans_idx < spans.len() is an invariant
820            let remaining_spans = spans.len() - (spans_idx + 1);
821            // positional_idx can be larger than required_positional.len() if we have optional args
822            let remaining_positional = signature
823                .required_positional
824                .len()
825                .saturating_sub(positional_idx + 1);
826            // Saturates to 0 when we have too few args
827            let extra_spans = remaining_spans.saturating_sub(remaining_positional);
828            spans_idx + 1 + extra_spans
829        }
830    }
831}
832
833fn parse_oneof(
834    working_set: &mut StateWorkingSet,
835    spans: &[Span],
836    spans_idx: &mut usize,
837    possible_shapes: &Vec<SyntaxShape>,
838    multispan: bool,
839) -> Expression {
840    let starting_spans_idx = *spans_idx;
841    let mut best_guess = None;
842    let mut best_guess_errors = Vec::new();
843    let mut max_first_error_offset = 0;
844    let mut propagate_error = false;
845    for shape in possible_shapes {
846        let starting_error_count = working_set.parse_errors.len();
847        *spans_idx = starting_spans_idx;
848        let value = match multispan {
849            true => parse_multispan_value(working_set, spans, spans_idx, shape),
850            false => parse_value(working_set, spans[*spans_idx], shape),
851        };
852
853        let new_errors = working_set.parse_errors[starting_error_count..].to_vec();
854        // no new errors found means success
855        let Some(first_error_offset) = new_errors.iter().map(|e| e.span().start).min() else {
856            return value;
857        };
858
859        if first_error_offset > max_first_error_offset {
860            // while trying the possible shapes, ignore Expected type errors
861            // unless they're inside a block, closure, or expression
862            propagate_error = match working_set.parse_errors.last() {
863                Some(ParseError::Expected(_, error_span))
864                | Some(ParseError::ExpectedWithStringMsg(_, error_span)) => {
865                    matches!(
866                        shape,
867                        SyntaxShape::Block | SyntaxShape::Closure(_) | SyntaxShape::Expression
868                    ) && *error_span != spans[*spans_idx]
869                }
870                _ => true,
871            };
872            max_first_error_offset = first_error_offset;
873            best_guess = Some(value);
874            best_guess_errors = new_errors;
875        }
876        working_set.parse_errors.truncate(starting_error_count);
877    }
878
879    // if best_guess results in new errors further than current span, then accept it
880    // or propagate_error is marked as true for it
881    if max_first_error_offset > spans[starting_spans_idx].start || propagate_error {
882        working_set.parse_errors.extend(best_guess_errors);
883        best_guess.expect("best_guess should not be None here!")
884    } else {
885        working_set.error(ParseError::ExpectedWithStringMsg(
886            format!("one of a list of accepted shapes: {possible_shapes:?}"),
887            spans[starting_spans_idx],
888        ));
889        Expression::garbage(working_set, spans[starting_spans_idx])
890    }
891}
892
893pub fn parse_multispan_value(
894    working_set: &mut StateWorkingSet,
895    spans: &[Span],
896    spans_idx: &mut usize,
897    shape: &SyntaxShape,
898) -> Expression {
899    trace!("parse multispan value");
900    match shape {
901        SyntaxShape::VarWithOptType => {
902            trace!("parsing: var with opt type");
903
904            parse_var_with_opt_type(working_set, spans, spans_idx, false).0
905        }
906        SyntaxShape::RowCondition => {
907            trace!("parsing: row condition");
908            let arg = parse_row_condition(working_set, &spans[*spans_idx..]);
909            *spans_idx = spans.len() - 1;
910
911            arg
912        }
913        SyntaxShape::MathExpression => {
914            trace!("parsing: math expression");
915
916            let arg = parse_math_expression(working_set, &spans[*spans_idx..], None);
917            *spans_idx = spans.len() - 1;
918
919            arg
920        }
921        SyntaxShape::OneOf(possible_shapes) => {
922            parse_oneof(working_set, spans, spans_idx, possible_shapes, true)
923        }
924
925        SyntaxShape::Expression => {
926            trace!("parsing: expression");
927
928            // is it subexpression?
929            // Not sure, but let's make it not, so the behavior is the same as previous version of nushell.
930            let arg = parse_expression(working_set, &spans[*spans_idx..]);
931            *spans_idx = spans.len() - 1;
932
933            arg
934        }
935        SyntaxShape::Signature => {
936            trace!("parsing: signature");
937
938            let sig = parse_full_signature(working_set, &spans[*spans_idx..]);
939            *spans_idx = spans.len() - 1;
940
941            sig
942        }
943        SyntaxShape::Keyword(keyword, arg) => {
944            trace!(
945                "parsing: keyword({}) {:?}",
946                String::from_utf8_lossy(keyword),
947                arg
948            );
949            let arg_span = spans[*spans_idx];
950
951            let arg_contents = working_set.get_span_contents(arg_span);
952
953            if arg_contents != keyword {
954                // When keywords mismatch, this is a strong indicator of something going wrong.
955                // We won't often override the current error, but as this is a strong indicator
956                // go ahead and override the current error and tell the user about the missing
957                // keyword/literal.
958                working_set.error(ParseError::ExpectedKeyword(
959                    String::from_utf8_lossy(keyword).into(),
960                    arg_span,
961                ))
962            }
963
964            *spans_idx += 1;
965            if *spans_idx >= spans.len() {
966                working_set.error(ParseError::KeywordMissingArgument(
967                    arg.to_string(),
968                    String::from_utf8_lossy(keyword).into(),
969                    Span::new(spans[*spans_idx - 1].end, spans[*spans_idx - 1].end),
970                ));
971                let keyword = Keyword {
972                    keyword: keyword.as_slice().into(),
973                    span: spans[*spans_idx - 1],
974                    expr: Expression::garbage(working_set, arg_span),
975                };
976                return Expression::new(
977                    working_set,
978                    Expr::Keyword(Box::new(keyword)),
979                    arg_span,
980                    Type::Any,
981                );
982            }
983
984            let keyword = Keyword {
985                keyword: keyword.as_slice().into(),
986                span: spans[*spans_idx - 1],
987                expr: parse_multispan_value(working_set, spans, spans_idx, arg),
988            };
989
990            Expression::new(
991                working_set,
992                Expr::Keyword(Box::new(keyword.clone())),
993                keyword.span.merge(keyword.expr.span),
994                keyword.expr.ty,
995            )
996        }
997        _ => {
998            // All other cases are single-span values
999            let arg_span = spans[*spans_idx];
1000
1001            parse_value(working_set, arg_span, shape)
1002        }
1003    }
1004}
1005
1006pub struct ParsedInternalCall {
1007    pub call: Box<Call>,
1008    pub output: Type,
1009    pub call_kind: CallKind,
1010}
1011
1012pub fn parse_internal_call(
1013    working_set: &mut StateWorkingSet,
1014    command_span: Span,
1015    spans: &[Span],
1016    decl_id: DeclId,
1017) -> ParsedInternalCall {
1018    trace!("parsing: internal call (decl id: {})", decl_id.get());
1019
1020    let mut call = Call::new(command_span);
1021    call.decl_id = decl_id;
1022    call.head = command_span;
1023    let _ = working_set.add_span(call.head);
1024
1025    let decl = working_set.get_decl(decl_id);
1026    let signature = working_set.get_signature(decl);
1027    let output = signature.get_output_type();
1028
1029    let deprecation = decl.deprecation_info();
1030
1031    // storing the var ID for later due to borrowing issues
1032    let lib_dirs_var_id = match decl.name() {
1033        "use" | "overlay use" | "source-env" if decl.is_keyword() => {
1034            find_dirs_var(working_set, LIB_DIRS_VAR)
1035        }
1036        "nu-check" if decl.is_builtin() => find_dirs_var(working_set, LIB_DIRS_VAR),
1037        _ => None,
1038    };
1039
1040    // The index into the positional parameter in the definition
1041    let mut positional_idx = 0;
1042
1043    // The index into the spans of argument data given to parse
1044    // Starting at the first argument
1045    let mut spans_idx = 0;
1046
1047    if let Some(alias) = decl.as_alias() {
1048        if let Expression {
1049            expr: Expr::Call(wrapped_call),
1050            ..
1051        } = &alias.wrapped_call
1052        {
1053            // Replace this command's call with the aliased call, but keep the alias name
1054            call = *wrapped_call.clone();
1055            call.head = command_span;
1056            // Skip positionals passed to aliased call
1057            positional_idx = call.positional_len();
1058        } else {
1059            working_set.error(ParseError::UnknownState(
1060                "Alias does not point to internal call.".to_string(),
1061                command_span,
1062            ));
1063            return ParsedInternalCall {
1064                call: Box::new(call),
1065                output: Type::Any,
1066                call_kind: CallKind::Invalid,
1067            };
1068        }
1069    }
1070
1071    if let Some(var_id) = lib_dirs_var_id {
1072        call.set_parser_info(
1073            DIR_VAR_PARSER_INFO.to_owned(),
1074            Expression::new(working_set, Expr::Var(var_id), call.head, Type::Any),
1075        );
1076    }
1077
1078    if signature.creates_scope {
1079        working_set.enter_scope();
1080    }
1081
1082    while spans_idx < spans.len() {
1083        let arg_span = spans[spans_idx];
1084
1085        let starting_error_count = working_set.parse_errors.len();
1086        // Check if we're on a long flag, if so, parse
1087        let (long_name, arg) = parse_long_flag(working_set, spans, &mut spans_idx, &signature);
1088
1089        if let Some(long_name) = long_name {
1090            // We found a long flag, like --bar
1091            if working_set.parse_errors[starting_error_count..]
1092                .iter()
1093                .any(|x| matches!(x, ParseError::UnknownFlag(_, _, _, _)))
1094                && signature.allows_unknown_args
1095            {
1096                working_set.parse_errors.truncate(starting_error_count);
1097                let arg = parse_unknown_arg(working_set, arg_span, &signature);
1098
1099                call.add_unknown(arg);
1100            } else {
1101                call.add_named((long_name, None, arg));
1102            }
1103
1104            spans_idx += 1;
1105            continue;
1106        }
1107
1108        let starting_error_count = working_set.parse_errors.len();
1109
1110        // Check if we're on a short flag or group of short flags, if so, parse
1111        let short_flags = parse_short_flags(
1112            working_set,
1113            spans,
1114            &mut spans_idx,
1115            positional_idx,
1116            &signature,
1117        );
1118
1119        if let Some(mut short_flags) = short_flags {
1120            if short_flags.is_empty() {
1121                // workaround for completions (PR #6067)
1122                short_flags.push(Flag {
1123                    long: "".to_string(),
1124                    short: Some('a'),
1125                    arg: None,
1126                    required: false,
1127                    desc: "".to_string(),
1128                    var_id: None,
1129                    default_value: None,
1130                    completion: None,
1131                })
1132            }
1133
1134            if working_set.parse_errors[starting_error_count..]
1135                .iter()
1136                .any(|x| matches!(x, ParseError::UnknownFlag(_, _, _, _)))
1137                && signature.allows_unknown_args
1138            {
1139                working_set.parse_errors.truncate(starting_error_count);
1140                let arg = parse_unknown_arg(working_set, arg_span, &signature);
1141
1142                call.add_unknown(arg);
1143            } else {
1144                for flag in short_flags {
1145                    let _ = working_set.add_span(spans[spans_idx]);
1146
1147                    if let Some(arg_shape) = flag.arg {
1148                        if let Some(arg) = spans.get(spans_idx + 1) {
1149                            let arg = parse_value(working_set, *arg, &arg_shape);
1150                            let (arg_name, val_expression) = ensure_flag_arg_type(
1151                                working_set,
1152                                flag.long.clone(),
1153                                arg.clone(),
1154                                &arg_shape,
1155                                spans[spans_idx],
1156                            );
1157
1158                            if flag.long.is_empty() {
1159                                if let Some(short) = flag.short {
1160                                    call.add_named((
1161                                        arg_name,
1162                                        Some(Spanned {
1163                                            item: short.to_string(),
1164                                            span: spans[spans_idx],
1165                                        }),
1166                                        Some(val_expression),
1167                                    ));
1168                                }
1169                            } else {
1170                                call.add_named((arg_name, None, Some(val_expression)));
1171                            }
1172                            spans_idx += 1;
1173                        } else {
1174                            working_set.error(ParseError::MissingFlagParam(
1175                                arg_shape.to_string(),
1176                                arg_span,
1177                            ));
1178                            // NOTE: still need to cover this incomplete flag in the final expression
1179                            // see https://github.com/nushell/nushell/issues/16375
1180                            call.add_named((
1181                                Spanned {
1182                                    item: String::new(),
1183                                    span: spans[spans_idx],
1184                                },
1185                                None,
1186                                None,
1187                            ));
1188                        }
1189                    } else if flag.long.is_empty() {
1190                        if let Some(short) = flag.short {
1191                            call.add_named((
1192                                Spanned {
1193                                    item: String::new(),
1194                                    span: spans[spans_idx],
1195                                },
1196                                Some(Spanned {
1197                                    item: short.to_string(),
1198                                    span: spans[spans_idx],
1199                                }),
1200                                None,
1201                            ));
1202                        }
1203                    } else {
1204                        call.add_named((
1205                            Spanned {
1206                                item: flag.long.clone(),
1207                                span: spans[spans_idx],
1208                            },
1209                            None,
1210                            None,
1211                        ));
1212                    }
1213                }
1214            }
1215
1216            spans_idx += 1;
1217            continue;
1218        }
1219
1220        {
1221            let contents = working_set.get_span_contents(spans[spans_idx]);
1222
1223            if contents.len() > 3
1224                && contents.starts_with(b"...")
1225                && (contents[3] == b'$' || contents[3] == b'[' || contents[3] == b'(')
1226            {
1227                if signature.rest_positional.is_none() && !signature.allows_unknown_args {
1228                    working_set.error(ParseError::UnexpectedSpreadArg(
1229                        signature.call_signature(),
1230                        arg_span,
1231                    ));
1232                    call.add_positional(Expression::garbage(working_set, arg_span));
1233                } else if positional_idx < signature.required_positional.len() {
1234                    working_set.error(ParseError::MissingPositional(
1235                        signature.required_positional[positional_idx].name.clone(),
1236                        Span::new(spans[spans_idx].start, spans[spans_idx].start),
1237                        signature.call_signature(),
1238                    ));
1239                    call.add_positional(Expression::garbage(working_set, arg_span));
1240                } else {
1241                    let rest_shape = match &signature.rest_positional {
1242                        Some(arg) if matches!(arg.shape, SyntaxShape::ExternalArgument) => {
1243                            // External args aren't parsed inside lists in spread position.
1244                            SyntaxShape::Any
1245                        }
1246                        Some(arg) => arg.shape.clone(),
1247                        None => SyntaxShape::Any,
1248                    };
1249                    // Parse list of arguments to be spread
1250                    let args = parse_value(
1251                        working_set,
1252                        Span::new(arg_span.start + 3, arg_span.end),
1253                        &SyntaxShape::List(Box::new(rest_shape)),
1254                    );
1255
1256                    call.add_spread(args);
1257                    // Let the parser know that it's parsing rest arguments now
1258                    positional_idx =
1259                        signature.required_positional.len() + signature.optional_positional.len();
1260                }
1261
1262                spans_idx += 1;
1263                continue;
1264            }
1265        }
1266
1267        // Parse a positional arg if there is one
1268        if let Some(positional) = signature.get_positional(positional_idx) {
1269            let end = calculate_end_span(working_set, &signature, spans, spans_idx, positional_idx);
1270
1271            // Missing arguments before next keyword
1272            if end == spans_idx {
1273                let prev_span = if spans_idx == 0 {
1274                    command_span
1275                } else {
1276                    spans[spans_idx - 1]
1277                };
1278                let whitespace_span = Span::new(prev_span.end, spans[spans_idx].start);
1279                working_set.error(ParseError::MissingPositional(
1280                    positional.name.clone(),
1281                    whitespace_span,
1282                    signature.call_signature(),
1283                ));
1284                call.add_positional(Expression::garbage(working_set, whitespace_span));
1285                positional_idx += 1;
1286                continue;
1287            }
1288            debug_assert!(end <= spans.len());
1289
1290            if spans[..end].is_empty() || spans_idx == end {
1291                working_set.error(ParseError::MissingPositional(
1292                    positional.name.clone(),
1293                    Span::new(spans[spans_idx].end, spans[spans_idx].end),
1294                    signature.call_signature(),
1295                ));
1296                positional_idx += 1;
1297                continue;
1298            }
1299
1300            let compile_error_count = working_set.compile_errors.len();
1301
1302            let arg = parse_multispan_value(
1303                working_set,
1304                &spans[..end],
1305                &mut spans_idx,
1306                &positional.shape,
1307            );
1308
1309            // HACK: try-catch's signature defines the catch block as a Closure, even though it's
1310            // used like a Block. Because closures are compiled eagerly, this ends up making the
1311            // following code technically invalid:
1312            // ```nu
1313            // loop { try { } catch {|e| break } }
1314            // ```
1315            // Thus, we discard the compilation error here
1316            if let SyntaxShape::Keyword(ref keyword, ..) = positional.shape
1317                && keyword == b"catch"
1318                && let [nu_protocol::CompileError::NotInALoop { .. }] =
1319                    &working_set.compile_errors[compile_error_count..]
1320            {
1321                working_set.compile_errors.truncate(compile_error_count);
1322            }
1323
1324            let arg = if !type_compatible(&positional.shape.to_type(), &arg.ty) {
1325                working_set.error(ParseError::TypeMismatch(
1326                    positional.shape.to_type(),
1327                    arg.ty,
1328                    arg.span,
1329                ));
1330                Expression::garbage(working_set, arg.span)
1331            } else {
1332                arg
1333            };
1334            call.add_positional(arg);
1335            positional_idx += 1;
1336        } else if signature.allows_unknown_args {
1337            let arg = parse_unknown_arg(working_set, arg_span, &signature);
1338
1339            call.add_unknown(arg);
1340        } else {
1341            call.add_positional(Expression::garbage(working_set, arg_span));
1342            working_set.error(ParseError::ExtraPositional(
1343                signature.call_signature(),
1344                arg_span,
1345            ))
1346        }
1347
1348        spans_idx += 1;
1349    }
1350
1351    // TODO: Inline `check_call`,
1352    // move missing positional checking into the while loop above with two pointers.
1353    // Maybe more `CallKind::Invalid` if errors found during argument parsing.
1354    let call_kind = check_call(working_set, command_span, &signature, &call);
1355
1356    deprecation
1357        .into_iter()
1358        .filter_map(|entry| entry.parse_warning(&signature.name, &call))
1359        .for_each(|warning| {
1360            // FIXME: if two flags are deprecated and both are used in one command,
1361            // the second flag's deprecation won't show until the first flag is removed
1362            // (but it won't be flagged as reported until it is actually reported)
1363            working_set.warning(warning);
1364        });
1365
1366    if signature.creates_scope {
1367        working_set.exit_scope();
1368    }
1369
1370    ParsedInternalCall {
1371        call: Box::new(call),
1372        output,
1373        call_kind,
1374    }
1375}
1376
1377pub fn parse_call(working_set: &mut StateWorkingSet, spans: &[Span], head: Span) -> Expression {
1378    trace!("parsing: call");
1379
1380    if spans.is_empty() {
1381        working_set.error(ParseError::UnknownState(
1382            "Encountered command with zero spans".into(),
1383            Span::concat(spans),
1384        ));
1385        return garbage(working_set, head);
1386    }
1387
1388    let (cmd_start, pos, _name, maybe_decl_id) = find_longest_decl(working_set, spans);
1389
1390    if let Some(decl_id) = maybe_decl_id {
1391        // Before the internal parsing we check if there is no let or alias declarations
1392        // that are missing their name, e.g.: let = 1 or alias = 2
1393        if spans.len() > 1 {
1394            let test_equal = working_set.get_span_contents(spans[1]);
1395
1396            if test_equal == [b'='] {
1397                trace!("incomplete statement");
1398
1399                working_set.error(ParseError::UnknownState(
1400                    "Incomplete statement".into(),
1401                    Span::concat(spans),
1402                ));
1403                return garbage(working_set, Span::concat(spans));
1404            }
1405        }
1406
1407        let decl = working_set.get_decl(decl_id);
1408
1409        let parsed_call = if let Some(alias) = decl.as_alias() {
1410            if let Expression {
1411                expr: Expr::ExternalCall(head, args),
1412                span: _,
1413                span_id: _,
1414                ty,
1415            } = &alias.clone().wrapped_call
1416            {
1417                trace!("parsing: alias of external call");
1418
1419                let mut head = head.clone();
1420                head.span = Span::concat(&spans[cmd_start..pos]); // replacing the spans preserves syntax highlighting
1421
1422                let mut final_args = args.clone().into_vec();
1423                for arg_span in &spans[pos..] {
1424                    let arg = parse_external_arg(working_set, *arg_span);
1425                    final_args.push(arg);
1426                }
1427
1428                let expression = Expression::new(
1429                    working_set,
1430                    Expr::ExternalCall(head, final_args.into()),
1431                    Span::concat(spans),
1432                    ty.clone(),
1433                );
1434
1435                return expression;
1436            } else {
1437                trace!("parsing: alias of internal call");
1438                parse_internal_call(
1439                    working_set,
1440                    Span::concat(&spans[cmd_start..pos]),
1441                    &spans[pos..],
1442                    decl_id,
1443                )
1444            }
1445        } else {
1446            trace!("parsing: internal call");
1447            parse_internal_call(
1448                working_set,
1449                Span::concat(&spans[cmd_start..pos]),
1450                &spans[pos..],
1451                decl_id,
1452            )
1453        };
1454
1455        Expression::new(
1456            working_set,
1457            Expr::Call(parsed_call.call),
1458            Span::concat(spans),
1459            parsed_call.output,
1460        )
1461    } else {
1462        // We might be parsing left-unbounded range ("..10")
1463        let bytes = working_set.get_span_contents(spans[0]);
1464        trace!("parsing: range {bytes:?}");
1465        if let (Some(b'.'), Some(b'.')) = (bytes.first(), bytes.get(1)) {
1466            trace!("-- found leading range indicator");
1467            let starting_error_count = working_set.parse_errors.len();
1468
1469            if let Some(range_expr) = parse_range(working_set, spans[0]) {
1470                trace!("-- successfully parsed range");
1471                return range_expr;
1472            }
1473            working_set.parse_errors.truncate(starting_error_count);
1474        }
1475        trace!("parsing: external call");
1476
1477        // Otherwise, try external command
1478        parse_external_call(working_set, spans)
1479    }
1480}
1481
1482pub fn find_longest_decl(
1483    working_set: &mut StateWorkingSet<'_>,
1484    spans: &[Span],
1485) -> (
1486    usize,
1487    usize,
1488    Vec<u8>,
1489    Option<nu_protocol::Id<nu_protocol::marker::Decl>>,
1490) {
1491    find_longest_decl_with_prefix(working_set, spans, b"")
1492}
1493
1494pub fn find_longest_decl_with_prefix(
1495    working_set: &mut StateWorkingSet<'_>,
1496    spans: &[Span],
1497    prefix: &[u8],
1498) -> (
1499    usize,
1500    usize,
1501    Vec<u8>,
1502    Option<nu_protocol::Id<nu_protocol::marker::Decl>>,
1503) {
1504    let mut pos = 0;
1505    let cmd_start = pos;
1506    let mut name_spans = vec![];
1507    let mut name = vec![];
1508    name.extend(prefix);
1509
1510    for word_span in spans[cmd_start..].iter() {
1511        // Find the longest group of words that could form a command
1512
1513        name_spans.push(*word_span);
1514
1515        let name_part = working_set.get_span_contents(*word_span);
1516        if name.is_empty() {
1517            name.extend(name_part);
1518        } else {
1519            name.push(b' ');
1520            name.extend(name_part);
1521        }
1522
1523        pos += 1;
1524    }
1525
1526    let mut maybe_decl_id = working_set.find_decl(&name);
1527
1528    while maybe_decl_id.is_none() {
1529        // Find the longest command match
1530        if name_spans.len() <= 1 {
1531            // Keep the first word even if it does not match -- could be external command
1532            break;
1533        }
1534
1535        name_spans.pop();
1536        pos -= 1;
1537
1538        // TODO: Refactor to avoid recreating name with an inner loop.
1539        name.clear();
1540        name.extend(prefix);
1541        for name_span in &name_spans {
1542            let name_part = working_set.get_span_contents(*name_span);
1543            if name.is_empty() {
1544                name.extend(name_part);
1545            } else {
1546                name.push(b' ');
1547                name.extend(name_part);
1548            }
1549        }
1550        maybe_decl_id = working_set.find_decl(&name);
1551    }
1552    (cmd_start, pos, name, maybe_decl_id)
1553}
1554
1555pub fn parse_attribute(
1556    working_set: &mut StateWorkingSet,
1557    lite_command: &LiteCommand,
1558) -> (Attribute, Option<String>) {
1559    let _ = lite_command
1560        .parts
1561        .first()
1562        .filter(|s| working_set.get_span_contents(**s).starts_with(b"@"))
1563        .expect("Attributes always start with an `@`");
1564
1565    assert!(
1566        lite_command.attribute_idx.is_empty(),
1567        "attributes can't have attributes"
1568    );
1569
1570    let mut spans = lite_command.parts.clone();
1571    if let Some(first) = spans.first_mut() {
1572        first.start += 1;
1573    }
1574    let spans = spans.as_slice();
1575    let attr_span = Span::concat(spans);
1576
1577    let (cmd_start, cmd_end, mut name, decl_id) =
1578        find_longest_decl_with_prefix(working_set, spans, b"attr");
1579
1580    debug_assert!(name.starts_with(b"attr "));
1581    let _ = name.drain(..(b"attr ".len()));
1582
1583    let name_span = Span::concat(&spans[cmd_start..cmd_end]);
1584
1585    let Ok(name) = String::from_utf8(name) else {
1586        working_set.error(ParseError::NonUtf8(name_span));
1587        return (
1588            Attribute {
1589                expr: garbage(working_set, attr_span),
1590            },
1591            None,
1592        );
1593    };
1594
1595    let Some(decl_id) = decl_id else {
1596        working_set.error(ParseError::UnknownCommand(name_span));
1597        return (
1598            Attribute {
1599                expr: garbage(working_set, attr_span),
1600            },
1601            None,
1602        );
1603    };
1604
1605    let decl = working_set.get_decl(decl_id);
1606
1607    let parsed_call = match decl.as_alias() {
1608        // TODO: Once `const def` is available, we should either disallow aliases as attributes OR
1609        // allow them but rather than using the aliases' name, use the name of the aliased command
1610        Some(alias) => match &alias.clone().wrapped_call {
1611            Expression {
1612                expr: Expr::ExternalCall(..),
1613                ..
1614            } => {
1615                let shell_error = ShellError::NotAConstCommand { span: name_span };
1616                working_set.error(shell_error.wrap(working_set, attr_span));
1617                return (
1618                    Attribute {
1619                        expr: garbage(working_set, Span::concat(spans)),
1620                    },
1621                    None,
1622                );
1623            }
1624            _ => {
1625                trace!("parsing: alias of internal call");
1626                parse_internal_call(working_set, name_span, &spans[cmd_end..], decl_id)
1627            }
1628        },
1629        None => {
1630            trace!("parsing: internal call");
1631            parse_internal_call(working_set, name_span, &spans[cmd_end..], decl_id)
1632        }
1633    };
1634
1635    (
1636        Attribute {
1637            expr: Expression::new(
1638                working_set,
1639                Expr::Call(parsed_call.call),
1640                Span::concat(spans),
1641                parsed_call.output,
1642            ),
1643        },
1644        Some(name),
1645    )
1646}
1647
1648pub fn parse_binary(working_set: &mut StateWorkingSet, span: Span) -> Expression {
1649    trace!("parsing: binary");
1650    let contents = working_set.get_span_contents(span);
1651    if contents.starts_with(b"0x[") {
1652        parse_binary_with_base(working_set, span, 16, 2, b"0x[", b"]")
1653    } else if contents.starts_with(b"0o[") {
1654        parse_binary_with_base(working_set, span, 8, 3, b"0o[", b"]")
1655    } else if contents.starts_with(b"0b[") {
1656        parse_binary_with_base(working_set, span, 2, 8, b"0b[", b"]")
1657    } else {
1658        working_set.error(ParseError::Expected("binary", span));
1659        garbage(working_set, span)
1660    }
1661}
1662
1663fn parse_binary_with_base(
1664    working_set: &mut StateWorkingSet,
1665    span: Span,
1666    base: u32,
1667    min_digits_per_byte: usize,
1668    prefix: &[u8],
1669    suffix: &[u8],
1670) -> Expression {
1671    let token = working_set.get_span_contents(span);
1672
1673    if let Some(token) = token.strip_prefix(prefix)
1674        && let Some(token) = token.strip_suffix(suffix)
1675    {
1676        let (lexed, err) = lex(
1677            token,
1678            span.start + prefix.len(),
1679            &[b',', b'\r', b'\n'],
1680            &[],
1681            true,
1682        );
1683        if let Some(err) = err {
1684            working_set.error(err);
1685        }
1686
1687        let mut binary_value = vec![];
1688        for token in lexed {
1689            match token.contents {
1690                TokenContents::Item => {
1691                    let contents = working_set.get_span_contents(token.span);
1692
1693                    binary_value.extend_from_slice(contents);
1694                }
1695                TokenContents::Pipe
1696                | TokenContents::PipePipe
1697                | TokenContents::ErrGreaterPipe
1698                | TokenContents::OutGreaterThan
1699                | TokenContents::OutErrGreaterPipe
1700                | TokenContents::OutGreaterGreaterThan
1701                | TokenContents::ErrGreaterThan
1702                | TokenContents::ErrGreaterGreaterThan
1703                | TokenContents::OutErrGreaterThan
1704                | TokenContents::OutErrGreaterGreaterThan
1705                | TokenContents::AssignmentOperator => {
1706                    working_set.error(ParseError::Expected("binary", span));
1707                    return garbage(working_set, span);
1708                }
1709                TokenContents::Comment | TokenContents::Semicolon | TokenContents::Eol => {}
1710            }
1711        }
1712
1713        let required_padding =
1714            (min_digits_per_byte - binary_value.len() % min_digits_per_byte) % min_digits_per_byte;
1715
1716        if required_padding != 0 {
1717            binary_value = {
1718                let mut tail = binary_value;
1719                let mut binary_value: Vec<u8> = vec![b'0'; required_padding];
1720                binary_value.append(&mut tail);
1721                binary_value
1722            };
1723        }
1724
1725        let str = String::from_utf8_lossy(&binary_value).to_string();
1726
1727        match decode_with_base(&str, base, min_digits_per_byte) {
1728            Ok(v) => return Expression::new(working_set, Expr::Binary(v), span, Type::Binary),
1729            Err(help) => {
1730                working_set.error(ParseError::InvalidBinaryString(span, help.to_string()));
1731                return garbage(working_set, span);
1732            }
1733        }
1734    }
1735
1736    working_set.error(ParseError::Expected("binary", span));
1737    garbage(working_set, span)
1738}
1739
1740fn decode_with_base(s: &str, base: u32, digits_per_byte: usize) -> Result<Vec<u8>, &str> {
1741    s.chars()
1742        .chunks(digits_per_byte)
1743        .into_iter()
1744        .map(|chunk| {
1745            let str: String = chunk.collect();
1746            u8::from_str_radix(&str, base).map_err(|_| match base {
1747                2 => "binary strings may contain only 0 or 1.",
1748                8 => "octal strings must have a length that is a multiple of three and contain values between 0o000 and 0o377.",
1749                16 => "hexadecimal strings may contain only the characters 0–9 and A–F.",
1750                _ => "internal error: radix other than 2, 8, or 16 is not allowed."
1751            })
1752        })
1753        .collect()
1754}
1755
1756fn strip_underscores(token: &[u8]) -> String {
1757    String::from_utf8_lossy(token)
1758        .chars()
1759        .filter(|c| *c != '_')
1760        .collect()
1761}
1762
1763pub fn parse_int(working_set: &mut StateWorkingSet, span: Span) -> Expression {
1764    let token = working_set.get_span_contents(span);
1765
1766    fn extract_int(
1767        working_set: &mut StateWorkingSet,
1768        token: &str,
1769        span: Span,
1770        radix: u32,
1771    ) -> Expression {
1772        // Parse as a u64, then cast to i64, otherwise, for numbers like "0xffffffffffffffef",
1773        // you'll get `Error parsing hex string: number too large to fit in target type`.
1774        if let Ok(num) = u64::from_str_radix(token, radix).map(|val| val as i64) {
1775            Expression::new(working_set, Expr::Int(num), span, Type::Int)
1776        } else {
1777            working_set.error(ParseError::InvalidLiteral(
1778                format!("invalid digits for radix {radix}"),
1779                "int".into(),
1780                span,
1781            ));
1782
1783            garbage(working_set, span)
1784        }
1785    }
1786
1787    let token = strip_underscores(token);
1788
1789    if token.is_empty() {
1790        working_set.error(ParseError::Expected("int", span));
1791        return garbage(working_set, span);
1792    }
1793
1794    if let Some(num) = token.strip_prefix("0b") {
1795        extract_int(working_set, num, span, 2)
1796    } else if let Some(num) = token.strip_prefix("0o") {
1797        extract_int(working_set, num, span, 8)
1798    } else if let Some(num) = token.strip_prefix("0x") {
1799        extract_int(working_set, num, span, 16)
1800    } else if let Ok(num) = token.parse::<i64>() {
1801        Expression::new(working_set, Expr::Int(num), span, Type::Int)
1802    } else {
1803        working_set.error(ParseError::Expected("int", span));
1804        garbage(working_set, span)
1805    }
1806}
1807
1808pub fn parse_float(working_set: &mut StateWorkingSet, span: Span) -> Expression {
1809    let token = working_set.get_span_contents(span);
1810    let token = strip_underscores(token);
1811
1812    if let Ok(x) = token.parse::<f64>() {
1813        Expression::new(working_set, Expr::Float(x), span, Type::Float)
1814    } else {
1815        working_set.error(ParseError::Expected("float", span));
1816
1817        garbage(working_set, span)
1818    }
1819}
1820
1821pub fn parse_number(working_set: &mut StateWorkingSet, span: Span) -> Expression {
1822    let starting_error_count = working_set.parse_errors.len();
1823
1824    let result = parse_int(working_set, span);
1825    if starting_error_count == working_set.parse_errors.len() {
1826        return result;
1827    } else if !matches!(
1828        working_set.parse_errors.last(),
1829        Some(ParseError::Expected(_, _))
1830    ) {
1831    } else {
1832        working_set.parse_errors.truncate(starting_error_count);
1833    }
1834
1835    let result = parse_float(working_set, span);
1836
1837    if starting_error_count == working_set.parse_errors.len() {
1838        return result;
1839    }
1840    working_set.parse_errors.truncate(starting_error_count);
1841
1842    working_set.error(ParseError::Expected("number", span));
1843    garbage(working_set, span)
1844}
1845
1846pub fn parse_range(working_set: &mut StateWorkingSet, span: Span) -> Option<Expression> {
1847    trace!("parsing: range");
1848    let starting_error_count = working_set.parse_errors.len();
1849
1850    // Range follows the following syntax: [<from>][<next_operator><next>]<range_operator>[<to>]
1851    //   where <next_operator> is ".."
1852    //   and  <range_operator> is "..", "..=" or "..<"
1853    //   and one of the <from> or <to> bounds must be present (just '..' is not allowed since it
1854    //     looks like parent directory)
1855    //bugbug range cannot be [..] because that looks like parent directory
1856
1857    let contents = working_set.get_span_contents(span);
1858
1859    let token = if let Ok(s) = String::from_utf8(contents.into()) {
1860        s
1861    } else {
1862        working_set.error(ParseError::NonUtf8(span));
1863        return None;
1864    };
1865
1866    if token.starts_with("...") {
1867        working_set.error(ParseError::Expected(
1868            "range operator ('..'), got spread ('...')",
1869            span,
1870        ));
1871        return None;
1872    }
1873
1874    if !token.contains("..") {
1875        working_set.error(ParseError::Expected("at least one range bound set", span));
1876        return None;
1877    }
1878
1879    // First, figure out what exact operators are used and determine their positions
1880    let dotdot_pos: Vec<_> = token.match_indices("..").map(|(pos, _)| pos).collect();
1881
1882    let (next_op_pos, range_op_pos) = match dotdot_pos.len() {
1883        1 => (None, dotdot_pos[0]),
1884        2 => (Some(dotdot_pos[0]), dotdot_pos[1]),
1885        _ => {
1886            working_set.error(ParseError::Expected(
1887                "one range operator ('..' or '..<') and optionally one next operator ('..')",
1888                span,
1889            ));
1890            return None;
1891        }
1892    };
1893    // Avoid calling sub-parsers on unmatched parens, to prevent quadratic time on things like ((((1..2))))
1894    // No need to call the expensive parse_value on "((((1"
1895    if dotdot_pos[0] > 0 {
1896        let (_tokens, err) = lex(
1897            &contents[..dotdot_pos[0]],
1898            span.start,
1899            &[],
1900            &[b'.', b'?', b'!'],
1901            true,
1902        );
1903        if let Some(_err) = err {
1904            working_set.error(ParseError::Expected("Valid expression before ..", span));
1905            return None;
1906        }
1907    }
1908
1909    let (inclusion, range_op_str, range_op_span) = if let Some(pos) = token.find("..<") {
1910        if pos == range_op_pos {
1911            let op_str = "..<";
1912            let op_span = Span::new(
1913                span.start + range_op_pos,
1914                span.start + range_op_pos + op_str.len(),
1915            );
1916            (RangeInclusion::RightExclusive, "..<", op_span)
1917        } else {
1918            working_set.error(ParseError::Expected(
1919                "inclusive operator preceding second range bound",
1920                span,
1921            ));
1922            return None;
1923        }
1924    } else {
1925        let op_str = if token[range_op_pos..].starts_with("..=") {
1926            "..="
1927        } else {
1928            ".."
1929        };
1930
1931        let op_span = Span::new(
1932            span.start + range_op_pos,
1933            span.start + range_op_pos + op_str.len(),
1934        );
1935        (RangeInclusion::Inclusive, op_str, op_span)
1936    };
1937
1938    // Now, based on the operator positions, figure out where the bounds & next are located and
1939    // parse them
1940    // TODO: Actually parse the next number in the range
1941    let from = if token.starts_with("..") {
1942        // token starts with either next operator, or range operator -- we don't care which one
1943        None
1944    } else {
1945        let from_span = Span::new(span.start, span.start + dotdot_pos[0]);
1946        Some(parse_value(working_set, from_span, &SyntaxShape::Number))
1947    };
1948
1949    let to = if token.ends_with(range_op_str) {
1950        None
1951    } else {
1952        let to_span = Span::new(range_op_span.end, span.end);
1953        Some(parse_value(working_set, to_span, &SyntaxShape::Number))
1954    };
1955
1956    trace!("-- from: {from:?} to: {to:?}");
1957
1958    if let (None, None) = (&from, &to) {
1959        working_set.error(ParseError::Expected("at least one range bound set", span));
1960        return None;
1961    }
1962
1963    let (next, next_op_span) = if let Some(pos) = next_op_pos {
1964        let next_op_span = Span::new(span.start + pos, span.start + pos + "..".len());
1965        let next_span = Span::new(next_op_span.end, range_op_span.start);
1966
1967        (
1968            Some(parse_value(working_set, next_span, &SyntaxShape::Number)),
1969            next_op_span,
1970        )
1971    } else {
1972        (None, span)
1973    };
1974
1975    if working_set.parse_errors.len() != starting_error_count {
1976        return None;
1977    }
1978
1979    let operator = RangeOperator {
1980        inclusion,
1981        span: range_op_span,
1982        next_op_span,
1983    };
1984
1985    let mut range = Range {
1986        from,
1987        next,
1988        to,
1989        operator,
1990    };
1991
1992    check_range_types(working_set, &mut range);
1993
1994    Some(Expression::new(
1995        working_set,
1996        Expr::Range(Box::new(range)),
1997        span,
1998        Type::Range,
1999    ))
2000}
2001
2002pub(crate) fn parse_dollar_expr(working_set: &mut StateWorkingSet, span: Span) -> Expression {
2003    trace!("parsing: dollar expression");
2004    let contents = working_set.get_span_contents(span);
2005
2006    if contents.starts_with(b"$\"") || contents.starts_with(b"$'") {
2007        parse_string_interpolation(working_set, span)
2008    } else if contents.starts_with(b"$.") {
2009        parse_simple_cell_path(working_set, Span::new(span.start + 2, span.end))
2010    } else {
2011        let starting_error_count = working_set.parse_errors.len();
2012
2013        if let Some(expr) = parse_range(working_set, span) {
2014            expr
2015        } else {
2016            working_set.parse_errors.truncate(starting_error_count);
2017            parse_full_cell_path(working_set, None, span)
2018        }
2019    }
2020}
2021
2022pub fn parse_raw_string(working_set: &mut StateWorkingSet, span: Span) -> Expression {
2023    trace!("parsing: raw-string, with required delimiters");
2024
2025    let bytes = working_set.get_span_contents(span);
2026
2027    let prefix_sharp_cnt = if bytes.starts_with(b"r#") {
2028        // actually `sharp_cnt` is always `index - 1`
2029        // but create a variable here to make it clearer.
2030        let mut sharp_cnt = 1;
2031        let mut index = 2;
2032        while index < bytes.len() && bytes[index] == b'#' {
2033            index += 1;
2034            sharp_cnt += 1;
2035        }
2036        sharp_cnt
2037    } else {
2038        working_set.error(ParseError::Expected("r#", span));
2039        return garbage(working_set, span);
2040    };
2041    let expect_postfix_sharp_cnt = prefix_sharp_cnt;
2042    // check the length of whole raw string.
2043    // the whole raw string should contains at least
2044    // 1(r) + prefix_sharp_cnt + 1(') + 1(') + postfix_sharp characters
2045    if bytes.len() < prefix_sharp_cnt + expect_postfix_sharp_cnt + 3 {
2046        working_set.error(ParseError::Unclosed('\''.into(), span));
2047        return garbage(working_set, span);
2048    }
2049
2050    // check for unbalanced # and single quotes.
2051    let postfix_bytes = &bytes[bytes.len() - expect_postfix_sharp_cnt..bytes.len()];
2052    if postfix_bytes.iter().any(|b| *b != b'#') {
2053        working_set.error(ParseError::Unbalanced(
2054            "prefix #".to_string(),
2055            "postfix #".to_string(),
2056            span,
2057        ));
2058        return garbage(working_set, span);
2059    }
2060    // check for unblanaced single quotes.
2061    if bytes[1 + prefix_sharp_cnt] != b'\''
2062        || bytes[bytes.len() - expect_postfix_sharp_cnt - 1] != b'\''
2063    {
2064        working_set.error(ParseError::Unclosed('\''.into(), span));
2065        return garbage(working_set, span);
2066    }
2067
2068    let bytes = &bytes[prefix_sharp_cnt + 1 + 1..bytes.len() - 1 - prefix_sharp_cnt];
2069    if let Ok(token) = String::from_utf8(bytes.into()) {
2070        Expression::new(working_set, Expr::RawString(token), span, Type::String)
2071    } else {
2072        working_set.error(ParseError::Expected("utf8 raw-string", span));
2073        garbage(working_set, span)
2074    }
2075}
2076
2077pub fn parse_paren_expr(
2078    working_set: &mut StateWorkingSet,
2079    span: Span,
2080    shape: &SyntaxShape,
2081) -> Expression {
2082    let starting_error_count = working_set.parse_errors.len();
2083
2084    if let Some(expr) = parse_range(working_set, span) {
2085        return expr;
2086    }
2087
2088    working_set.parse_errors.truncate(starting_error_count);
2089
2090    if let SyntaxShape::Signature = shape {
2091        return parse_signature(working_set, span);
2092    }
2093
2094    let fcp_expr = parse_full_cell_path(working_set, None, span);
2095    let fcp_error_count = working_set.parse_errors.len();
2096    if fcp_error_count > starting_error_count {
2097        let malformed_subexpr = working_set.parse_errors[starting_error_count..]
2098            .first()
2099            .is_some_and(|e| match e {
2100                ParseError::Unclosed(right, _) if (right == ")") => true,
2101                ParseError::Unbalanced(left, right, _) if left == "(" && right == ")" => true,
2102                _ => false,
2103            });
2104        if malformed_subexpr {
2105            working_set.parse_errors.truncate(starting_error_count);
2106            parse_string_interpolation(working_set, span)
2107        } else {
2108            fcp_expr
2109        }
2110    } else {
2111        fcp_expr
2112    }
2113}
2114
2115pub fn parse_brace_expr(
2116    working_set: &mut StateWorkingSet,
2117    span: Span,
2118    shape: &SyntaxShape,
2119) -> Expression {
2120    // Try to detect what kind of value we're about to parse
2121    // FIXME: In the future, we should work over the token stream so we only have to do this once
2122    // before parsing begins
2123
2124    // FIXME: we're still using the shape because we rely on it to know how to handle syntax where
2125    // the parse is ambiguous. We'll need to update the parts of the grammar where this is ambiguous
2126    // and then revisit the parsing.
2127
2128    if span.end <= (span.start + 1) {
2129        working_set.error(ParseError::ExpectedWithStringMsg(
2130            format!("non-block value: {shape}"),
2131            span,
2132        ));
2133        return Expression::garbage(working_set, span);
2134    }
2135
2136    let bytes = working_set.get_span_contents(Span::new(span.start + 1, span.end - 1));
2137    let (tokens, _) = lex(bytes, span.start + 1, &[b'\r', b'\n', b'\t'], &[b':'], true);
2138
2139    match tokens.as_slice() {
2140        // If we're empty, that means an empty record or closure
2141        [] => match shape {
2142            SyntaxShape::Closure(_) => parse_closure_expression(working_set, shape, span),
2143            SyntaxShape::Block => parse_block_expression(working_set, span),
2144            SyntaxShape::MatchBlock => parse_match_block_expression(working_set, span),
2145            _ => parse_record(working_set, span),
2146        },
2147        [
2148            Token {
2149                contents: TokenContents::Pipe | TokenContents::PipePipe,
2150                ..
2151            },
2152            ..,
2153        ] => {
2154            if let SyntaxShape::Block = shape {
2155                working_set.error(ParseError::Mismatch("block".into(), "closure".into(), span));
2156                return Expression::garbage(working_set, span);
2157            }
2158            parse_closure_expression(working_set, shape, span)
2159        }
2160        [_, third, ..] if working_set.get_span_contents(third.span) == b":" => {
2161            parse_full_cell_path(working_set, None, span)
2162        }
2163        [second, ..] => {
2164            let second_bytes = working_set.get_span_contents(second.span);
2165            match shape {
2166                SyntaxShape::Closure(_) => parse_closure_expression(working_set, shape, span),
2167                SyntaxShape::Block => parse_block_expression(working_set, span),
2168                SyntaxShape::MatchBlock => parse_match_block_expression(working_set, span),
2169                _ if second_bytes.starts_with(b"...")
2170                    && second_bytes.get(3).is_some_and(|c| b"${(".contains(c)) =>
2171                {
2172                    parse_record(working_set, span)
2173                }
2174                SyntaxShape::Any => parse_closure_expression(working_set, shape, span),
2175                _ => {
2176                    working_set.error(ParseError::ExpectedWithStringMsg(
2177                        format!("non-block value: {shape}"),
2178                        span,
2179                    ));
2180
2181                    Expression::garbage(working_set, span)
2182                }
2183            }
2184        }
2185    }
2186}
2187
2188pub fn parse_string_interpolation(working_set: &mut StateWorkingSet, span: Span) -> Expression {
2189    #[derive(PartialEq, Eq, Debug)]
2190    enum InterpolationMode {
2191        String,
2192        Expression,
2193    }
2194
2195    let contents = working_set.get_span_contents(span);
2196
2197    let mut double_quote = false;
2198
2199    let (start, end) = if contents.starts_with(b"$\"") {
2200        double_quote = true;
2201        let end = if contents.ends_with(b"\"") && contents.len() > 2 {
2202            span.end - 1
2203        } else {
2204            span.end
2205        };
2206        (span.start + 2, end)
2207    } else if contents.starts_with(b"$'") {
2208        let end = if contents.ends_with(b"'") && contents.len() > 2 {
2209            span.end - 1
2210        } else {
2211            span.end
2212        };
2213        (span.start + 2, end)
2214    } else {
2215        (span.start, span.end)
2216    };
2217
2218    let inner_span = Span::new(start, end);
2219    let contents = working_set.get_span_contents(inner_span).to_vec();
2220
2221    let mut output = vec![];
2222    let mut mode = InterpolationMode::String;
2223    let mut token_start = start;
2224    let mut delimiter_stack = vec![];
2225
2226    let mut consecutive_backslashes: usize = 0;
2227
2228    let mut b = start;
2229
2230    while b != end {
2231        let current_byte = contents[b - start];
2232
2233        if mode == InterpolationMode::String {
2234            let preceding_consecutive_backslashes = consecutive_backslashes;
2235
2236            let is_backslash = current_byte == b'\\';
2237            consecutive_backslashes = if is_backslash {
2238                preceding_consecutive_backslashes + 1
2239            } else {
2240                0
2241            };
2242
2243            if current_byte == b'('
2244                && (!double_quote || preceding_consecutive_backslashes.is_multiple_of(2))
2245            {
2246                mode = InterpolationMode::Expression;
2247                if token_start < b {
2248                    let span = Span::new(token_start, b);
2249                    let str_contents = working_set.get_span_contents(span);
2250
2251                    let (str_contents, err) = if double_quote {
2252                        unescape_string(str_contents, span)
2253                    } else {
2254                        (str_contents.to_vec(), None)
2255                    };
2256                    if let Some(err) = err {
2257                        working_set.error(err);
2258                    }
2259
2260                    output.push(Expression::new(
2261                        working_set,
2262                        Expr::String(String::from_utf8_lossy(&str_contents).to_string()),
2263                        span,
2264                        Type::String,
2265                    ));
2266                    token_start = b;
2267                }
2268            }
2269        }
2270
2271        if mode == InterpolationMode::Expression {
2272            let byte = current_byte;
2273            if let Some(b'\'') = delimiter_stack.last() {
2274                if byte == b'\'' {
2275                    delimiter_stack.pop();
2276                }
2277            } else if let Some(b'"') = delimiter_stack.last() {
2278                if byte == b'"' {
2279                    delimiter_stack.pop();
2280                }
2281            } else if let Some(b'`') = delimiter_stack.last() {
2282                if byte == b'`' {
2283                    delimiter_stack.pop();
2284                }
2285            } else if byte == b'\'' {
2286                delimiter_stack.push(b'\'')
2287            } else if byte == b'"' {
2288                delimiter_stack.push(b'"');
2289            } else if byte == b'`' {
2290                delimiter_stack.push(b'`')
2291            } else if byte == b'(' {
2292                delimiter_stack.push(b')');
2293            } else if byte == b')' {
2294                if let Some(b')') = delimiter_stack.last() {
2295                    delimiter_stack.pop();
2296                }
2297                if delimiter_stack.is_empty() {
2298                    mode = InterpolationMode::String;
2299
2300                    if token_start < b {
2301                        let span = Span::new(token_start, b + 1);
2302
2303                        let expr = parse_full_cell_path(working_set, None, span);
2304                        output.push(expr);
2305                    }
2306
2307                    token_start = b + 1;
2308                    continue;
2309                }
2310            }
2311        }
2312        b += 1;
2313    }
2314
2315    match mode {
2316        InterpolationMode::String => {
2317            if token_start < end {
2318                let span = Span::new(token_start, end);
2319                let str_contents = working_set.get_span_contents(span);
2320
2321                let (str_contents, err) = if double_quote {
2322                    unescape_string(str_contents, span)
2323                } else {
2324                    (str_contents.to_vec(), None)
2325                };
2326                if let Some(err) = err {
2327                    working_set.error(err);
2328                }
2329
2330                output.push(Expression::new(
2331                    working_set,
2332                    Expr::String(String::from_utf8_lossy(&str_contents).to_string()),
2333                    span,
2334                    Type::String,
2335                ));
2336            }
2337        }
2338        InterpolationMode::Expression => {
2339            if token_start < end {
2340                let span = Span::new(token_start, end);
2341
2342                if delimiter_stack.is_empty() {
2343                    let expr = parse_full_cell_path(working_set, None, span);
2344                    output.push(expr);
2345                }
2346            }
2347        }
2348    }
2349
2350    Expression::new(
2351        working_set,
2352        Expr::StringInterpolation(output),
2353        span,
2354        Type::String,
2355    )
2356}
2357
2358pub fn parse_variable_expr(working_set: &mut StateWorkingSet, span: Span) -> Expression {
2359    let contents = working_set.get_span_contents(span);
2360
2361    if contents == b"$nu" {
2362        return Expression::new(
2363            working_set,
2364            Expr::Var(nu_protocol::NU_VARIABLE_ID),
2365            span,
2366            Type::Any,
2367        );
2368    } else if contents == b"$in" {
2369        return Expression::new(
2370            working_set,
2371            Expr::Var(nu_protocol::IN_VARIABLE_ID),
2372            span,
2373            Type::Any,
2374        );
2375    } else if contents == b"$env" {
2376        return Expression::new(
2377            working_set,
2378            Expr::Var(nu_protocol::ENV_VARIABLE_ID),
2379            span,
2380            Type::Any,
2381        );
2382    }
2383
2384    let name = if contents.starts_with(b"$") {
2385        String::from_utf8_lossy(&contents[1..]).to_string()
2386    } else {
2387        String::from_utf8_lossy(contents).to_string()
2388    };
2389
2390    let bytes = working_set.get_span_contents(span);
2391    let suggestion = || {
2392        DidYouMean::new(
2393            &working_set.list_variables(),
2394            working_set.get_span_contents(span),
2395        )
2396    };
2397    if !is_variable(bytes) {
2398        working_set.error(ParseError::ExpectedWithDidYouMean(
2399            "valid variable name",
2400            suggestion(),
2401            span,
2402        ));
2403        garbage(working_set, span)
2404    } else if let Some(id) = working_set.find_variable(bytes) {
2405        Expression::new(
2406            working_set,
2407            Expr::Var(id),
2408            span,
2409            working_set.get_variable(id).ty.clone(),
2410        )
2411    } else if working_set.get_env_var(&name).is_some() {
2412        working_set.error(ParseError::EnvVarNotVar(name, span));
2413        garbage(working_set, span)
2414    } else {
2415        working_set.error(ParseError::VariableNotFound(suggestion(), span));
2416        garbage(working_set, span)
2417    }
2418}
2419
2420pub fn parse_cell_path(
2421    working_set: &mut StateWorkingSet,
2422    tokens: impl Iterator<Item = Token>,
2423    expect_dot: bool,
2424) -> Vec<PathMember> {
2425    enum TokenType {
2426        Dot,              // .
2427        DotOrSign,        // . or ? or !
2428        DotOrExclamation, // . or !
2429        DotOrQuestion,    // . or ?
2430        PathMember,       // an int or string, like `1` or `foo`
2431    }
2432
2433    enum ModifyMember {
2434        No,
2435        Optional,
2436        Insensitive,
2437    }
2438
2439    impl TokenType {
2440        fn expect(&mut self, byte: u8) -> Result<ModifyMember, &'static str> {
2441            match (&*self, byte) {
2442                (Self::PathMember, _) => {
2443                    *self = Self::DotOrSign;
2444                    Ok(ModifyMember::No)
2445                }
2446                (
2447                    Self::Dot | Self::DotOrSign | Self::DotOrExclamation | Self::DotOrQuestion,
2448                    b'.',
2449                ) => {
2450                    *self = Self::PathMember;
2451                    Ok(ModifyMember::No)
2452                }
2453                (Self::DotOrSign, b'!') => {
2454                    *self = Self::DotOrQuestion;
2455                    Ok(ModifyMember::Insensitive)
2456                }
2457                (Self::DotOrSign, b'?') => {
2458                    *self = Self::DotOrExclamation;
2459                    Ok(ModifyMember::Optional)
2460                }
2461                (Self::DotOrSign, _) => Err(". or ! or ?"),
2462                (Self::DotOrExclamation, b'!') => {
2463                    *self = Self::Dot;
2464                    Ok(ModifyMember::Insensitive)
2465                }
2466                (Self::DotOrExclamation, _) => Err(". or !"),
2467                (Self::DotOrQuestion, b'?') => {
2468                    *self = Self::Dot;
2469                    Ok(ModifyMember::Optional)
2470                }
2471                (Self::DotOrQuestion, _) => Err(". or ?"),
2472                (Self::Dot, _) => Err("."),
2473            }
2474        }
2475    }
2476
2477    // Parsing a cell path is essentially a state machine, and this is the state
2478    let mut expected_token = if expect_dot {
2479        TokenType::Dot
2480    } else {
2481        TokenType::PathMember
2482    };
2483
2484    let mut tail = vec![];
2485
2486    for path_element in tokens {
2487        let bytes = working_set.get_span_contents(path_element.span);
2488
2489        // both parse_int and parse_string require their source to be non-empty
2490        // all cases where `bytes` is empty is an error
2491        let Some((&first, rest)) = bytes.split_first() else {
2492            working_set.error(ParseError::Expected("string", path_element.span));
2493            return tail;
2494        };
2495        let single_char = rest.is_empty();
2496
2497        if let TokenType::PathMember = expected_token {
2498            let starting_error_count = working_set.parse_errors.len();
2499
2500            let expr = parse_int(working_set, path_element.span);
2501            working_set.parse_errors.truncate(starting_error_count);
2502
2503            match expr {
2504                Expression {
2505                    expr: Expr::Int(val),
2506                    span,
2507                    ..
2508                } => tail.push(PathMember::Int {
2509                    val: val as usize,
2510                    span,
2511                    optional: false,
2512                }),
2513                _ => {
2514                    let result = parse_string(working_set, path_element.span);
2515                    match result {
2516                        Expression {
2517                            expr: Expr::String(string),
2518                            span,
2519                            ..
2520                        } => {
2521                            tail.push(PathMember::String {
2522                                val: string,
2523                                span,
2524                                optional: false,
2525                                casing: Casing::Sensitive,
2526                            });
2527                        }
2528                        _ => {
2529                            working_set.error(ParseError::Expected("string", path_element.span));
2530                            return tail;
2531                        }
2532                    }
2533                }
2534            }
2535            expected_token = TokenType::DotOrSign;
2536        } else {
2537            match expected_token.expect(if single_char { first } else { b' ' }) {
2538                Ok(modify) => {
2539                    if let Some(last) = tail.last_mut() {
2540                        match modify {
2541                            ModifyMember::No => {}
2542                            ModifyMember::Optional => last.make_optional(),
2543                            ModifyMember::Insensitive => last.make_insensitive(),
2544                        }
2545                    };
2546                }
2547                Err(expected) => {
2548                    working_set.error(ParseError::Expected(expected, path_element.span));
2549                    return tail;
2550                }
2551            }
2552        }
2553    }
2554
2555    tail
2556}
2557
2558pub fn parse_simple_cell_path(working_set: &mut StateWorkingSet, span: Span) -> Expression {
2559    let source = working_set.get_span_contents(span);
2560
2561    let (tokens, err) = lex(
2562        source,
2563        span.start,
2564        &[b'\n', b'\r'],
2565        &[b'.', b'?', b'!'],
2566        true,
2567    );
2568    if let Some(err) = err {
2569        working_set.error(err)
2570    }
2571
2572    let tokens = tokens.into_iter().peekable();
2573
2574    let cell_path = parse_cell_path(working_set, tokens, false);
2575
2576    Expression::new(
2577        working_set,
2578        Expr::CellPath(CellPath { members: cell_path }),
2579        span,
2580        Type::CellPath,
2581    )
2582}
2583
2584pub fn parse_full_cell_path(
2585    working_set: &mut StateWorkingSet,
2586    implicit_head: Option<VarId>,
2587    span: Span,
2588) -> Expression {
2589    trace!("parsing: full cell path");
2590    let full_cell_span = span;
2591    let source = working_set.get_span_contents(span);
2592
2593    let (tokens, err) = lex(
2594        source,
2595        span.start,
2596        &[b'\n', b'\r'],
2597        &[b'.', b'?', b'!'],
2598        true,
2599    );
2600    if let Some(err) = err {
2601        working_set.error(err)
2602    }
2603
2604    let mut tokens = tokens.into_iter().peekable();
2605    if let Some(head) = tokens.peek() {
2606        let bytes = working_set.get_span_contents(head.span);
2607        let (head, expect_dot) = if bytes.starts_with(b"(") {
2608            trace!("parsing: paren-head of full cell path");
2609
2610            let head_span = head.span;
2611            let mut start = head.span.start;
2612            let mut end = head.span.end;
2613            let mut is_closed = true;
2614
2615            if bytes.starts_with(b"(") {
2616                start += 1;
2617            }
2618            if bytes.ends_with(b")") {
2619                end -= 1;
2620            } else {
2621                working_set.error(ParseError::Unclosed(")".into(), Span::new(end, end)));
2622                is_closed = false;
2623            }
2624
2625            let span = Span::new(start, end);
2626
2627            let source = working_set.get_span_contents(span);
2628
2629            let (output, err) = lex(source, span.start, &[b'\n', b'\r'], &[], true);
2630            if let Some(err) = err {
2631                working_set.error(err)
2632            }
2633
2634            // Creating a Type scope to parse the new block. This will keep track of
2635            // the previous input type found in that block
2636            let output = parse_block(working_set, &output, span, is_closed, true);
2637
2638            let ty = output.output_type();
2639
2640            let block_id = working_set.add_block(Arc::new(output));
2641            tokens.next();
2642
2643            (
2644                Expression::new(working_set, Expr::Subexpression(block_id), head_span, ty),
2645                true,
2646            )
2647        } else if bytes.starts_with(b"[") {
2648            trace!("parsing: table head of full cell path");
2649
2650            let output = parse_table_expression(working_set, head.span, &SyntaxShape::Any);
2651
2652            tokens.next();
2653
2654            (output, true)
2655        } else if bytes.starts_with(b"{") {
2656            trace!("parsing: record head of full cell path");
2657            let output = parse_record(working_set, head.span);
2658
2659            tokens.next();
2660
2661            (output, true)
2662        } else if bytes.starts_with(b"$") {
2663            trace!("parsing: $variable head of full cell path");
2664
2665            let out = parse_variable_expr(working_set, head.span);
2666
2667            tokens.next();
2668
2669            (out, true)
2670        } else if let Some(var_id) = implicit_head {
2671            trace!("parsing: implicit head of full cell path");
2672            (
2673                Expression::new(working_set, Expr::Var(var_id), head.span, Type::Any),
2674                false,
2675            )
2676        } else {
2677            working_set.error(ParseError::Mismatch(
2678                "variable or subexpression".into(),
2679                String::from_utf8_lossy(bytes).to_string(),
2680                span,
2681            ));
2682            return garbage(working_set, span);
2683        };
2684
2685        let tail = parse_cell_path(working_set, tokens, expect_dot);
2686        // FIXME: Get the type of the data at the tail using follow_cell_path() (or something)
2687        let ty = if !tail.is_empty() {
2688            // Until the aforementioned fix is implemented, this is necessary to allow mutable list upserts
2689            // such as $a.1 = 2 to work correctly.
2690            Type::Any
2691        } else {
2692            head.ty.clone()
2693        };
2694
2695        Expression::new(
2696            working_set,
2697            Expr::FullCellPath(Box::new(FullCellPath { head, tail })),
2698            full_cell_span,
2699            ty,
2700        )
2701    } else {
2702        garbage(working_set, span)
2703    }
2704}
2705
2706pub fn parse_directory(working_set: &mut StateWorkingSet, span: Span) -> Expression {
2707    let bytes = working_set.get_span_contents(span);
2708    let quoted = is_quoted(bytes);
2709    let (token, err) = unescape_unquote_string(bytes, span);
2710    trace!("parsing: directory");
2711
2712    if err.is_none() {
2713        trace!("-- found {token}");
2714
2715        Expression::new(
2716            working_set,
2717            Expr::Directory(token, quoted),
2718            span,
2719            Type::String,
2720        )
2721    } else {
2722        working_set.error(ParseError::Expected("directory", span));
2723
2724        garbage(working_set, span)
2725    }
2726}
2727
2728pub fn parse_filepath(working_set: &mut StateWorkingSet, span: Span) -> Expression {
2729    let bytes = working_set.get_span_contents(span);
2730    let quoted = is_quoted(bytes);
2731    let (token, err) = unescape_unquote_string(bytes, span);
2732    trace!("parsing: filepath");
2733
2734    if err.is_none() {
2735        trace!("-- found {token}");
2736
2737        Expression::new(
2738            working_set,
2739            Expr::Filepath(token, quoted),
2740            span,
2741            Type::String,
2742        )
2743    } else {
2744        working_set.error(ParseError::Expected("filepath", span));
2745
2746        garbage(working_set, span)
2747    }
2748}
2749
2750/// Parse a datetime type, eg '2022-02-02'
2751pub fn parse_datetime(working_set: &mut StateWorkingSet, span: Span) -> Expression {
2752    trace!("parsing: datetime");
2753
2754    let bytes = working_set.get_span_contents(span);
2755
2756    if bytes.len() < 6
2757        || !bytes[0].is_ascii_digit()
2758        || !bytes[1].is_ascii_digit()
2759        || !bytes[2].is_ascii_digit()
2760        || !bytes[3].is_ascii_digit()
2761        || bytes[4] != b'-'
2762    {
2763        working_set.error(ParseError::Expected("datetime", span));
2764        return garbage(working_set, span);
2765    }
2766
2767    let token = String::from_utf8_lossy(bytes).to_string();
2768
2769    if let Ok(datetime) = chrono::DateTime::parse_from_rfc3339(&token) {
2770        return Expression::new(working_set, Expr::DateTime(datetime), span, Type::Date);
2771    }
2772
2773    // Just the date
2774    let just_date = token.clone() + "T00:00:00+00:00";
2775    if let Ok(datetime) = chrono::DateTime::parse_from_rfc3339(&just_date) {
2776        return Expression::new(working_set, Expr::DateTime(datetime), span, Type::Date);
2777    }
2778
2779    // Date and time, assume UTC
2780    let datetime = token + "+00:00";
2781    if let Ok(datetime) = chrono::DateTime::parse_from_rfc3339(&datetime) {
2782        return Expression::new(working_set, Expr::DateTime(datetime), span, Type::Date);
2783    }
2784
2785    working_set.error(ParseError::Expected("datetime", span));
2786
2787    garbage(working_set, span)
2788}
2789
2790/// Parse a duration type, eg '10day'
2791pub fn parse_duration(working_set: &mut StateWorkingSet, span: Span) -> Expression {
2792    trace!("parsing: duration");
2793
2794    let bytes = working_set.get_span_contents(span);
2795
2796    match parse_unit_value(bytes, span, DURATION_UNIT_GROUPS, Type::Duration, |x| x) {
2797        Some(Ok(expr)) => {
2798            let span_id = working_set.add_span(span);
2799            expr.with_span_id(span_id)
2800        }
2801        Some(Err(mk_err_for)) => {
2802            working_set.error(mk_err_for("duration"));
2803            garbage(working_set, span)
2804        }
2805        None => {
2806            working_set.error(ParseError::Expected("duration with valid units", span));
2807            garbage(working_set, span)
2808        }
2809    }
2810}
2811
2812/// Parse a unit type, eg '10kb'
2813pub fn parse_filesize(working_set: &mut StateWorkingSet, span: Span) -> Expression {
2814    trace!("parsing: filesize");
2815
2816    let bytes = working_set.get_span_contents(span);
2817
2818    // the hex digit `b` might be mistaken for the unit `b`, so check that first
2819    if bytes.starts_with(b"0x") {
2820        working_set.error(ParseError::Expected("filesize with valid units", span));
2821        return garbage(working_set, span);
2822    }
2823
2824    match parse_unit_value(bytes, span, FILESIZE_UNIT_GROUPS, Type::Filesize, |x| {
2825        x.to_ascii_uppercase()
2826    }) {
2827        Some(Ok(expr)) => {
2828            let span_id = working_set.add_span(span);
2829            expr.with_span_id(span_id)
2830        }
2831        Some(Err(mk_err_for)) => {
2832            working_set.error(mk_err_for("filesize"));
2833            garbage(working_set, span)
2834        }
2835        None => {
2836            working_set.error(ParseError::Expected("filesize with valid units", span));
2837            garbage(working_set, span)
2838        }
2839    }
2840}
2841
2842type ParseUnitResult<'res> = Result<Expression, Box<dyn Fn(&'res str) -> ParseError>>;
2843type UnitGroup<'unit> = (Unit, &'unit str, Option<(Unit, i64)>);
2844
2845pub fn parse_unit_value<'res>(
2846    bytes: &[u8],
2847    span: Span,
2848    unit_groups: &[UnitGroup],
2849    ty: Type,
2850    transform: fn(String) -> String,
2851) -> Option<ParseUnitResult<'res>> {
2852    if bytes.len() < 2
2853        || !(bytes[0].is_ascii_digit() || (bytes[0] == b'-' && bytes[1].is_ascii_digit()))
2854    {
2855        return None;
2856    }
2857
2858    // Bail if not UTF-8
2859    let value = transform(str::from_utf8(bytes).ok()?.into());
2860
2861    if let Some((unit, name, convert)) = unit_groups.iter().find(|x| value.ends_with(x.1)) {
2862        let lhs_len = value.len() - name.len();
2863        let lhs = strip_underscores(&value.as_bytes()[..lhs_len]);
2864        let lhs_span = Span::new(span.start, span.start + lhs_len);
2865        let unit_span = Span::new(span.start + lhs_len, span.end);
2866        if lhs.ends_with('$') {
2867            // If `parse_unit_value` has higher precedence over `parse_range`,
2868            // a variable with the name of a unit could otherwise not be used as the end of a range.
2869            return None;
2870        }
2871
2872        let (decimal_part, number_part) = modf(match lhs.parse::<f64>() {
2873            Ok(it) => it,
2874            Err(_) => {
2875                let mk_err = move |name| {
2876                    ParseError::LabeledError(
2877                        format!("{name} value must be a number"),
2878                        "not a number".into(),
2879                        lhs_span,
2880                    )
2881                };
2882                return Some(Err(Box::new(mk_err)));
2883            }
2884        });
2885
2886        let mut unit = match convert {
2887            Some(convert_to) => convert_to.0,
2888            None => *unit,
2889        };
2890
2891        let num_float = match convert {
2892            Some(convert_to) => {
2893                (number_part * convert_to.1 as f64) + (decimal_part * convert_to.1 as f64)
2894            }
2895            None => number_part,
2896        };
2897
2898        // Convert all durations to nanoseconds, and filesizes to bytes,
2899        // to minimize loss of precision
2900        let factor = match ty {
2901            Type::Filesize => unit_to_byte_factor(&unit),
2902            Type::Duration => unit_to_ns_factor(&unit),
2903            _ => None,
2904        };
2905
2906        let num = match factor {
2907            Some(factor) => {
2908                let num_base = num_float * factor;
2909                if i64::MIN as f64 <= num_base && num_base <= i64::MAX as f64 {
2910                    unit = if ty == Type::Filesize {
2911                        Unit::Filesize(FilesizeUnit::B)
2912                    } else {
2913                        Unit::Nanosecond
2914                    };
2915                    num_base as i64
2916                } else {
2917                    // not safe to convert, because of the overflow
2918                    num_float as i64
2919                }
2920            }
2921            None => num_float as i64,
2922        };
2923
2924        trace!("-- found {num} {unit:?}");
2925        let value = ValueWithUnit {
2926            expr: Expression::new_unknown(Expr::Int(num), lhs_span, Type::Number),
2927            unit: Spanned {
2928                item: unit,
2929                span: unit_span,
2930            },
2931        };
2932        let expr = Expression::new_unknown(Expr::ValueWithUnit(Box::new(value)), span, ty);
2933
2934        Some(Ok(expr))
2935    } else {
2936        None
2937    }
2938}
2939
2940pub const FILESIZE_UNIT_GROUPS: &[UnitGroup] = &[
2941    (
2942        Unit::Filesize(FilesizeUnit::KB),
2943        "KB",
2944        Some((Unit::Filesize(FilesizeUnit::B), 1000)),
2945    ),
2946    (
2947        Unit::Filesize(FilesizeUnit::MB),
2948        "MB",
2949        Some((Unit::Filesize(FilesizeUnit::KB), 1000)),
2950    ),
2951    (
2952        Unit::Filesize(FilesizeUnit::GB),
2953        "GB",
2954        Some((Unit::Filesize(FilesizeUnit::MB), 1000)),
2955    ),
2956    (
2957        Unit::Filesize(FilesizeUnit::TB),
2958        "TB",
2959        Some((Unit::Filesize(FilesizeUnit::GB), 1000)),
2960    ),
2961    (
2962        Unit::Filesize(FilesizeUnit::PB),
2963        "PB",
2964        Some((Unit::Filesize(FilesizeUnit::TB), 1000)),
2965    ),
2966    (
2967        Unit::Filesize(FilesizeUnit::EB),
2968        "EB",
2969        Some((Unit::Filesize(FilesizeUnit::PB), 1000)),
2970    ),
2971    (
2972        Unit::Filesize(FilesizeUnit::KiB),
2973        "KIB",
2974        Some((Unit::Filesize(FilesizeUnit::B), 1024)),
2975    ),
2976    (
2977        Unit::Filesize(FilesizeUnit::MiB),
2978        "MIB",
2979        Some((Unit::Filesize(FilesizeUnit::KiB), 1024)),
2980    ),
2981    (
2982        Unit::Filesize(FilesizeUnit::GiB),
2983        "GIB",
2984        Some((Unit::Filesize(FilesizeUnit::MiB), 1024)),
2985    ),
2986    (
2987        Unit::Filesize(FilesizeUnit::TiB),
2988        "TIB",
2989        Some((Unit::Filesize(FilesizeUnit::GiB), 1024)),
2990    ),
2991    (
2992        Unit::Filesize(FilesizeUnit::PiB),
2993        "PIB",
2994        Some((Unit::Filesize(FilesizeUnit::TiB), 1024)),
2995    ),
2996    (
2997        Unit::Filesize(FilesizeUnit::EiB),
2998        "EIB",
2999        Some((Unit::Filesize(FilesizeUnit::PiB), 1024)),
3000    ),
3001    (Unit::Filesize(FilesizeUnit::B), "B", None),
3002];
3003
3004pub const DURATION_UNIT_GROUPS: &[UnitGroup] = &[
3005    (Unit::Nanosecond, "ns", None),
3006    // todo start adding aliases for duration units here
3007    (Unit::Microsecond, "us", Some((Unit::Nanosecond, 1000))),
3008    (
3009        // µ Micro Sign
3010        Unit::Microsecond,
3011        "\u{00B5}s",
3012        Some((Unit::Nanosecond, 1000)),
3013    ),
3014    (
3015        // μ Greek small letter Mu
3016        Unit::Microsecond,
3017        "\u{03BC}s",
3018        Some((Unit::Nanosecond, 1000)),
3019    ),
3020    (Unit::Millisecond, "ms", Some((Unit::Microsecond, 1000))),
3021    (Unit::Second, "sec", Some((Unit::Millisecond, 1000))),
3022    (Unit::Minute, "min", Some((Unit::Second, 60))),
3023    (Unit::Hour, "hr", Some((Unit::Minute, 60))),
3024    (Unit::Day, "day", Some((Unit::Minute, 1440))),
3025    (Unit::Week, "wk", Some((Unit::Day, 7))),
3026];
3027
3028fn unit_to_ns_factor(unit: &Unit) -> Option<f64> {
3029    match unit {
3030        Unit::Nanosecond => Some(1.0),
3031        Unit::Microsecond => Some(1_000.0),
3032        Unit::Millisecond => Some(1_000_000.0),
3033        Unit::Second => Some(1_000_000_000.0),
3034        Unit::Minute => Some(60.0 * 1_000_000_000.0),
3035        Unit::Hour => Some(60.0 * 60.0 * 1_000_000_000.0),
3036        Unit::Day => Some(24.0 * 60.0 * 60.0 * 1_000_000_000.0),
3037        Unit::Week => Some(7.0 * 24.0 * 60.0 * 60.0 * 1_000_000_000.0),
3038        _ => None,
3039    }
3040}
3041
3042fn unit_to_byte_factor(unit: &Unit) -> Option<f64> {
3043    match unit {
3044        Unit::Filesize(FilesizeUnit::B) => Some(1.0),
3045        Unit::Filesize(FilesizeUnit::KB) => Some(1_000.0),
3046        Unit::Filesize(FilesizeUnit::MB) => Some(1_000_000.0),
3047        Unit::Filesize(FilesizeUnit::GB) => Some(1_000_000_000.0),
3048        Unit::Filesize(FilesizeUnit::TB) => Some(1_000_000_000_000.0),
3049        Unit::Filesize(FilesizeUnit::PB) => Some(1_000_000_000_000_000.0),
3050        Unit::Filesize(FilesizeUnit::EB) => Some(1_000_000_000_000_000_000.0),
3051        Unit::Filesize(FilesizeUnit::KiB) => Some(1024.0),
3052        Unit::Filesize(FilesizeUnit::MiB) => Some(1024.0 * 1024.0),
3053        Unit::Filesize(FilesizeUnit::GiB) => Some(1024.0 * 1024.0 * 1024.0),
3054        Unit::Filesize(FilesizeUnit::TiB) => Some(1024.0 * 1024.0 * 1024.0 * 1024.0),
3055        Unit::Filesize(FilesizeUnit::PiB) => Some(1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0),
3056        Unit::Filesize(FilesizeUnit::EiB) => {
3057            Some(1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0)
3058        }
3059        _ => None,
3060    }
3061}
3062
3063// Borrowed from libm at https://github.com/rust-lang/libm/blob/master/src/math/modf.rs
3064fn modf(x: f64) -> (f64, f64) {
3065    let rv2: f64;
3066    let mut u = x.to_bits();
3067    let e = (((u >> 52) & 0x7ff) as i32) - 0x3ff;
3068
3069    /* no fractional part */
3070    if e >= 52 {
3071        rv2 = x;
3072        if e == 0x400 && (u << 12) != 0 {
3073            /* nan */
3074            return (x, rv2);
3075        }
3076        u &= 1 << 63;
3077        return (f64::from_bits(u), rv2);
3078    }
3079
3080    /* no integral part*/
3081    if e < 0 {
3082        u &= 1 << 63;
3083        rv2 = f64::from_bits(u);
3084        return (x, rv2);
3085    }
3086
3087    let mask = ((!0) >> 12) >> e;
3088    if (u & mask) == 0 {
3089        rv2 = x;
3090        u &= 1 << 63;
3091        return (f64::from_bits(u), rv2);
3092    }
3093    u &= !mask;
3094    rv2 = f64::from_bits(u);
3095    (x - rv2, rv2)
3096}
3097
3098pub fn parse_glob_pattern(working_set: &mut StateWorkingSet, span: Span) -> Expression {
3099    let bytes = working_set.get_span_contents(span);
3100    let quoted = is_quoted(bytes);
3101    let (token, err) = unescape_unquote_string(bytes, span);
3102    trace!("parsing: glob pattern");
3103
3104    if err.is_none() {
3105        trace!("-- found {token}");
3106
3107        Expression::new(
3108            working_set,
3109            Expr::GlobPattern(token, quoted),
3110            span,
3111            Type::Glob,
3112        )
3113    } else {
3114        working_set.error(ParseError::Expected("glob pattern string", span));
3115
3116        garbage(working_set, span)
3117    }
3118}
3119
3120pub fn unescape_string(bytes: &[u8], span: Span) -> (Vec<u8>, Option<ParseError>) {
3121    let mut output = Vec::new();
3122    let mut error = None;
3123
3124    let mut idx = 0;
3125
3126    if !bytes.contains(&b'\\') {
3127        return (bytes.to_vec(), None);
3128    }
3129
3130    'us_loop: while idx < bytes.len() {
3131        if bytes[idx] == b'\\' {
3132            // We're in an escape
3133            idx += 1;
3134
3135            match bytes.get(idx) {
3136                Some(b'"') => {
3137                    output.push(b'"');
3138                    idx += 1;
3139                }
3140                Some(b'\'') => {
3141                    output.push(b'\'');
3142                    idx += 1;
3143                }
3144                Some(b'\\') => {
3145                    output.push(b'\\');
3146                    idx += 1;
3147                }
3148                Some(b'/') => {
3149                    output.push(b'/');
3150                    idx += 1;
3151                }
3152                Some(b'(') => {
3153                    output.push(b'(');
3154                    idx += 1;
3155                }
3156                Some(b')') => {
3157                    output.push(b')');
3158                    idx += 1;
3159                }
3160                Some(b'{') => {
3161                    output.push(b'{');
3162                    idx += 1;
3163                }
3164                Some(b'}') => {
3165                    output.push(b'}');
3166                    idx += 1;
3167                }
3168                Some(b'$') => {
3169                    output.push(b'$');
3170                    idx += 1;
3171                }
3172                Some(b'^') => {
3173                    output.push(b'^');
3174                    idx += 1;
3175                }
3176                Some(b'#') => {
3177                    output.push(b'#');
3178                    idx += 1;
3179                }
3180                Some(b'|') => {
3181                    output.push(b'|');
3182                    idx += 1;
3183                }
3184                Some(b'~') => {
3185                    output.push(b'~');
3186                    idx += 1;
3187                }
3188                Some(b'a') => {
3189                    output.push(0x7);
3190                    idx += 1;
3191                }
3192                Some(b'b') => {
3193                    output.push(0x8);
3194                    idx += 1;
3195                }
3196                Some(b'e') => {
3197                    output.push(0x1b);
3198                    idx += 1;
3199                }
3200                Some(b'f') => {
3201                    output.push(0xc);
3202                    idx += 1;
3203                }
3204                Some(b'n') => {
3205                    output.push(b'\n');
3206                    idx += 1;
3207                }
3208                Some(b'r') => {
3209                    output.push(b'\r');
3210                    idx += 1;
3211                }
3212                Some(b't') => {
3213                    output.push(b'\t');
3214                    idx += 1;
3215                }
3216                Some(b'u') => {
3217                    let mut digits = String::with_capacity(10);
3218                    let mut cur_idx = idx + 1; // index of first beyond current end of token
3219
3220                    if let Some(b'{') = bytes.get(idx + 1) {
3221                        cur_idx = idx + 2;
3222                        loop {
3223                            match bytes.get(cur_idx) {
3224                                Some(b'}') => {
3225                                    cur_idx += 1;
3226                                    break;
3227                                }
3228                                Some(c) => {
3229                                    digits.push(*c as char);
3230                                    cur_idx += 1;
3231                                }
3232                                _ => {
3233                                    error = error.or(Some(ParseError::InvalidLiteral(
3234                                        "missing '}' for unicode escape '\\u{X...}'".into(),
3235                                        "string".into(),
3236                                        Span::new(span.start + idx, span.end),
3237                                    )));
3238                                    break 'us_loop;
3239                                }
3240                            }
3241                        }
3242                    }
3243
3244                    if (1..=6).contains(&digits.len()) {
3245                        let int = u32::from_str_radix(&digits, 16);
3246
3247                        if let Ok(int) = int
3248                            && int <= 0x10ffff
3249                        {
3250                            let result = char::from_u32(int);
3251
3252                            if let Some(result) = result {
3253                                let mut buffer = [0; 4];
3254                                let result = result.encode_utf8(&mut buffer);
3255
3256                                for elem in result.bytes() {
3257                                    output.push(elem);
3258                                }
3259
3260                                idx = cur_idx;
3261                                continue 'us_loop;
3262                            }
3263                        }
3264                    }
3265                    // fall through -- escape not accepted above, must be error.
3266                    error = error.or(Some(ParseError::InvalidLiteral(
3267                            "invalid unicode escape '\\u{X...}', must be 1-6 hex digits, max value 10FFFF".into(),
3268                            "string".into(),
3269                            Span::new(span.start + idx, span.end),
3270                    )));
3271                    break 'us_loop;
3272                }
3273
3274                _ => {
3275                    error = error.or(Some(ParseError::InvalidLiteral(
3276                        "unrecognized escape after '\\'".into(),
3277                        "string".into(),
3278                        Span::new(span.start + idx, span.end),
3279                    )));
3280                    break 'us_loop;
3281                }
3282            }
3283        } else {
3284            output.push(bytes[idx]);
3285            idx += 1;
3286        }
3287    }
3288
3289    (output, error)
3290}
3291
3292pub fn unescape_unquote_string(bytes: &[u8], span: Span) -> (String, Option<ParseError>) {
3293    if bytes.starts_with(b"\"") {
3294        // Needs unescaping
3295        let bytes = trim_quotes(bytes);
3296
3297        let (bytes, err) = unescape_string(bytes, span);
3298
3299        if let Ok(token) = String::from_utf8(bytes) {
3300            (token, err)
3301        } else {
3302            (String::new(), Some(ParseError::Expected("string", span)))
3303        }
3304    } else {
3305        let bytes = trim_quotes(bytes);
3306
3307        if let Ok(token) = String::from_utf8(bytes.into()) {
3308            (token, None)
3309        } else {
3310            (String::new(), Some(ParseError::Expected("string", span)))
3311        }
3312    }
3313}
3314
3315pub fn parse_string(working_set: &mut StateWorkingSet, span: Span) -> Expression {
3316    trace!("parsing: string");
3317
3318    let bytes = working_set.get_span_contents(span);
3319
3320    if bytes.is_empty() {
3321        working_set.error(ParseError::Expected("String", span));
3322        return Expression::garbage(working_set, span);
3323    }
3324
3325    // Check for bare word interpolation
3326    if bytes[0] != b'\'' && bytes[0] != b'"' && bytes[0] != b'`' && bytes.contains(&b'(') {
3327        return parse_string_interpolation(working_set, span);
3328    }
3329    // Check for unbalanced quotes:
3330    {
3331        if bytes.starts_with(b"\"")
3332            && (bytes.iter().filter(|ch| **ch == b'"').count() > 1 && !bytes.ends_with(b"\""))
3333        {
3334            let close_delimiter_index = bytes
3335                .iter()
3336                .skip(1)
3337                .position(|ch| *ch == b'"')
3338                .expect("Already check input bytes contains at least two double quotes");
3339            // needs `+2` rather than `+1`, because we have skip 1 to find close_delimiter_index before.
3340            let span = Span::new(span.start + close_delimiter_index + 2, span.end);
3341            working_set.error(ParseError::ExtraTokensAfterClosingDelimiter(span));
3342            return garbage(working_set, span);
3343        }
3344
3345        if bytes.starts_with(b"\'")
3346            && (bytes.iter().filter(|ch| **ch == b'\'').count() > 1 && !bytes.ends_with(b"\'"))
3347        {
3348            let close_delimiter_index = bytes
3349                .iter()
3350                .skip(1)
3351                .position(|ch| *ch == b'\'')
3352                .expect("Already check input bytes contains at least two double quotes");
3353            // needs `+2` rather than `+1`, because we have skip 1 to find close_delimiter_index before.
3354            let span = Span::new(span.start + close_delimiter_index + 2, span.end);
3355            working_set.error(ParseError::ExtraTokensAfterClosingDelimiter(span));
3356            return garbage(working_set, span);
3357        }
3358    }
3359
3360    let (s, err) = unescape_unquote_string(bytes, span);
3361    if let Some(err) = err {
3362        working_set.error(err);
3363    }
3364
3365    Expression::new(working_set, Expr::String(s), span, Type::String)
3366}
3367
3368fn is_quoted(bytes: &[u8]) -> bool {
3369    (bytes.starts_with(b"\"") && bytes.ends_with(b"\"") && bytes.len() > 1)
3370        || (bytes.starts_with(b"\'") && bytes.ends_with(b"\'") && bytes.len() > 1)
3371}
3372
3373pub fn parse_string_strict(working_set: &mut StateWorkingSet, span: Span) -> Expression {
3374    trace!("parsing: string, with required delimiters");
3375
3376    let bytes = working_set.get_span_contents(span);
3377
3378    // Check for unbalanced quotes:
3379    {
3380        let bytes = if bytes.starts_with(b"$") {
3381            &bytes[1..]
3382        } else {
3383            bytes
3384        };
3385        if bytes.starts_with(b"\"") && (bytes.len() == 1 || !bytes.ends_with(b"\"")) {
3386            working_set.error(ParseError::Unclosed("\"".into(), span));
3387            return garbage(working_set, span);
3388        }
3389        if bytes.starts_with(b"\'") && (bytes.len() == 1 || !bytes.ends_with(b"\'")) {
3390            working_set.error(ParseError::Unclosed("\'".into(), span));
3391            return garbage(working_set, span);
3392        }
3393        if bytes.starts_with(b"r#") && (bytes.len() == 1 || !bytes.ends_with(b"#")) {
3394            working_set.error(ParseError::Unclosed("r#".into(), span));
3395            return garbage(working_set, span);
3396        }
3397    }
3398
3399    let (bytes, quoted) = if (bytes.starts_with(b"\"") && bytes.ends_with(b"\"") && bytes.len() > 1)
3400        || (bytes.starts_with(b"\'") && bytes.ends_with(b"\'") && bytes.len() > 1)
3401    {
3402        (&bytes[1..(bytes.len() - 1)], true)
3403    } else if (bytes.starts_with(b"$\"") && bytes.ends_with(b"\"") && bytes.len() > 2)
3404        || (bytes.starts_with(b"$\'") && bytes.ends_with(b"\'") && bytes.len() > 2)
3405    {
3406        (&bytes[2..(bytes.len() - 1)], true)
3407    } else {
3408        (bytes, false)
3409    };
3410
3411    if let Ok(token) = String::from_utf8(bytes.into()) {
3412        trace!("-- found {token}");
3413
3414        if quoted {
3415            Expression::new(working_set, Expr::String(token), span, Type::String)
3416        } else if token.contains(' ') {
3417            working_set.error(ParseError::Expected("string", span));
3418
3419            garbage(working_set, span)
3420        } else {
3421            Expression::new(working_set, Expr::String(token), span, Type::String)
3422        }
3423    } else {
3424        working_set.error(ParseError::Expected("string", span));
3425        garbage(working_set, span)
3426    }
3427}
3428
3429pub fn parse_import_pattern(working_set: &mut StateWorkingSet, spans: &[Span]) -> Expression {
3430    let Some(head_span) = spans.first() else {
3431        working_set.error(ParseError::WrongImportPattern(
3432            "needs at least one component of import pattern".to_string(),
3433            Span::concat(spans),
3434        ));
3435        return garbage(working_set, Span::concat(spans));
3436    };
3437
3438    let head_expr = parse_value(working_set, *head_span, &SyntaxShape::Any);
3439
3440    let (maybe_module_id, head_name) = match eval_constant(working_set, &head_expr) {
3441        Ok(Value::Nothing { .. }) => {
3442            return Expression::new(
3443                working_set,
3444                Expr::Nothing,
3445                Span::concat(spans),
3446                Type::Nothing,
3447            );
3448        }
3449        Ok(val) => match val.coerce_into_string() {
3450            Ok(s) => (working_set.find_module(s.as_bytes()), s.into_bytes()),
3451            Err(err) => {
3452                working_set.error(err.wrap(working_set, Span::concat(spans)));
3453                return garbage(working_set, Span::concat(spans));
3454            }
3455        },
3456        Err(err) => {
3457            working_set.error(err.wrap(working_set, Span::concat(spans)));
3458            return garbage(working_set, Span::concat(spans));
3459        }
3460    };
3461
3462    let mut import_pattern = ImportPattern {
3463        head: ImportPatternHead {
3464            name: head_name,
3465            id: maybe_module_id,
3466            span: *head_span,
3467        },
3468        members: vec![],
3469        hidden: HashSet::new(),
3470        constants: vec![],
3471    };
3472
3473    if spans.len() > 1 {
3474        let mut leaf_member_span = None;
3475
3476        for tail_span in spans[1..].iter() {
3477            if let Some(prev_span) = leaf_member_span {
3478                let what = if working_set.get_span_contents(prev_span) == b"*" {
3479                    "glob"
3480                } else {
3481                    "list"
3482                };
3483                working_set.error(ParseError::WrongImportPattern(
3484                    format!("{what} member can be only at the end of an import pattern"),
3485                    prev_span,
3486                ));
3487                return Expression::new(
3488                    working_set,
3489                    Expr::ImportPattern(Box::new(import_pattern)),
3490                    prev_span,
3491                    Type::List(Box::new(Type::String)),
3492                );
3493            }
3494
3495            let tail = working_set.get_span_contents(*tail_span);
3496
3497            if tail == b"*" {
3498                import_pattern
3499                    .members
3500                    .push(ImportPatternMember::Glob { span: *tail_span });
3501
3502                leaf_member_span = Some(*tail_span);
3503            } else if tail.starts_with(b"[") {
3504                let result = parse_list_expression(working_set, *tail_span, &SyntaxShape::String);
3505
3506                let mut output = vec![];
3507
3508                if let Expression {
3509                    expr: Expr::List(list),
3510                    ..
3511                } = result
3512                {
3513                    for item in list {
3514                        match item {
3515                            ListItem::Item(expr) => {
3516                                let contents = working_set.get_span_contents(expr.span);
3517                                output.push((trim_quotes(contents).to_vec(), expr.span));
3518                            }
3519                            ListItem::Spread(_, spread) => {
3520                                working_set.error(ParseError::WrongImportPattern(
3521                                    "cannot spread in an import pattern".into(),
3522                                    spread.span,
3523                                ))
3524                            }
3525                        }
3526                    }
3527
3528                    import_pattern
3529                        .members
3530                        .push(ImportPatternMember::List { names: output });
3531                } else {
3532                    working_set.error(ParseError::ExportNotFound(result.span));
3533                    return Expression::new(
3534                        working_set,
3535                        Expr::ImportPattern(Box::new(import_pattern)),
3536                        Span::concat(spans),
3537                        Type::List(Box::new(Type::String)),
3538                    );
3539                }
3540
3541                leaf_member_span = Some(*tail_span);
3542            } else {
3543                let tail = trim_quotes(tail);
3544
3545                import_pattern.members.push(ImportPatternMember::Name {
3546                    name: tail.to_vec(),
3547                    span: *tail_span,
3548                });
3549            }
3550        }
3551    }
3552
3553    Expression::new(
3554        working_set,
3555        Expr::ImportPattern(Box::new(import_pattern)),
3556        Span::concat(&spans[1..]),
3557        Type::List(Box::new(Type::String)),
3558    )
3559}
3560
3561/// Parse `spans[spans_idx..]` into a variable, with optional type annotation.
3562/// If the name of the variable ends with a colon (no space in-between allowed), then a type annotation
3563/// can appear after the variable, in which case the colon is stripped from the name of the variable.
3564/// `spans_idx` is updated to point to the last span that has been parsed.
3565pub fn parse_var_with_opt_type(
3566    working_set: &mut StateWorkingSet,
3567    spans: &[Span],
3568    spans_idx: &mut usize,
3569    mutable: bool,
3570) -> (Expression, Option<Type>) {
3571    let name_span = spans[*spans_idx];
3572    let bytes = working_set.get_span_contents(name_span).to_vec();
3573
3574    if bytes.contains(&b' ')
3575        || bytes.contains(&b'"')
3576        || bytes.contains(&b'\'')
3577        || bytes.contains(&b'`')
3578    {
3579        working_set.error(ParseError::VariableNotValid(spans[*spans_idx]));
3580        return (garbage(working_set, spans[*spans_idx]), None);
3581    }
3582
3583    if bytes.ends_with(b":") {
3584        let name_span = Span::new(name_span.start, name_span.end - 1);
3585        let var_name = bytes[0..(bytes.len() - 1)].to_vec();
3586
3587        // We end with colon, so the next span should be the type
3588        if *spans_idx + 1 < spans.len() {
3589            *spans_idx += 1;
3590            // signature like record<a: int b: int> is broken into multiple spans due to
3591            // whitespaces. Collect the rest into one span and work on it
3592            let full_span = Span::concat(&spans[*spans_idx..]);
3593            let type_bytes = working_set.get_span_contents(full_span).to_vec();
3594
3595            let (tokens, parse_error) =
3596                lex_signature(&type_bytes, full_span.start, &[], &[b','], true);
3597
3598            if let Some(parse_error) = parse_error {
3599                working_set.error(parse_error);
3600            }
3601
3602            let ty = parse_type(working_set, &type_bytes, tokens[0].span);
3603            *spans_idx = spans.len() - 1;
3604
3605            if !is_variable(&var_name) {
3606                working_set.error(ParseError::Expected(
3607                    "valid variable name",
3608                    spans[*spans_idx - 1],
3609                ));
3610                return (garbage(working_set, spans[*spans_idx - 1]), None);
3611            }
3612
3613            let id = working_set.add_variable(var_name, spans[*spans_idx - 1], ty.clone(), mutable);
3614
3615            (
3616                Expression::new(working_set, Expr::VarDecl(id), name_span, ty.clone()),
3617                Some(ty),
3618            )
3619        } else {
3620            if !is_variable(&var_name) {
3621                working_set.error(ParseError::Expected(
3622                    "valid variable name",
3623                    spans[*spans_idx],
3624                ));
3625                return (garbage(working_set, spans[*spans_idx]), None);
3626            }
3627
3628            let id = working_set.add_variable(var_name, spans[*spans_idx], Type::Any, mutable);
3629
3630            working_set.error(ParseError::MissingType(spans[*spans_idx]));
3631            (
3632                Expression::new(working_set, Expr::VarDecl(id), spans[*spans_idx], Type::Any),
3633                None,
3634            )
3635        }
3636    } else {
3637        let var_name = bytes;
3638
3639        if !is_variable(&var_name) {
3640            working_set.error(ParseError::Expected(
3641                "valid variable name",
3642                spans[*spans_idx],
3643            ));
3644            return (garbage(working_set, spans[*spans_idx]), None);
3645        }
3646
3647        let id = working_set.add_variable(
3648            var_name,
3649            Span::concat(&spans[*spans_idx..*spans_idx + 1]),
3650            Type::Any,
3651            mutable,
3652        );
3653
3654        (
3655            Expression::new(working_set, Expr::VarDecl(id), spans[*spans_idx], Type::Any),
3656            None,
3657        )
3658    }
3659}
3660
3661pub fn expand_to_cell_path(
3662    working_set: &mut StateWorkingSet,
3663    expression: &mut Expression,
3664    var_id: VarId,
3665) {
3666    trace!("parsing: expanding to cell path");
3667    if let Expression {
3668        expr: Expr::String(_),
3669        span,
3670        ..
3671    } = expression
3672    {
3673        // Re-parse the string as if it were a cell-path
3674        let new_expression = parse_full_cell_path(working_set, Some(var_id), *span);
3675
3676        *expression = new_expression;
3677    }
3678
3679    if let Expression {
3680        expr: Expr::UnaryNot(inner),
3681        ..
3682    } = expression
3683    {
3684        expand_to_cell_path(working_set, inner, var_id);
3685    }
3686}
3687
3688pub fn parse_input_output_types(
3689    working_set: &mut StateWorkingSet,
3690    spans: &[Span],
3691) -> Vec<(Type, Type)> {
3692    let mut full_span = Span::concat(spans);
3693
3694    let mut bytes = working_set.get_span_contents(full_span);
3695
3696    if bytes.starts_with(b"[") {
3697        bytes = &bytes[1..];
3698        full_span.start += 1;
3699    }
3700
3701    if bytes.ends_with(b"]") {
3702        bytes = &bytes[..(bytes.len() - 1)];
3703        full_span.end -= 1;
3704    }
3705
3706    let (tokens, parse_error) =
3707        lex_signature(bytes, full_span.start, &[b'\n', b'\r', b','], &[], true);
3708
3709    if let Some(parse_error) = parse_error {
3710        working_set.error(parse_error);
3711    }
3712
3713    let mut output = vec![];
3714
3715    let mut idx = 0;
3716    while idx < tokens.len() {
3717        let type_bytes = working_set.get_span_contents(tokens[idx].span).to_vec();
3718        let input_type = parse_type(working_set, &type_bytes, tokens[idx].span);
3719
3720        idx += 1;
3721        if idx >= tokens.len() {
3722            working_set.error(ParseError::Expected(
3723                "arrow (->)",
3724                Span::new(tokens[idx - 1].span.end, tokens[idx - 1].span.end),
3725            ));
3726            break;
3727        }
3728
3729        let arrow = working_set.get_span_contents(tokens[idx].span);
3730        if arrow != b"->" {
3731            working_set.error(ParseError::Expected("arrow (->)", tokens[idx].span));
3732        }
3733
3734        idx += 1;
3735        if idx >= tokens.len() {
3736            working_set.error(ParseError::MissingType(Span::new(
3737                tokens[idx - 1].span.end,
3738                tokens[idx - 1].span.end,
3739            )));
3740            break;
3741        }
3742
3743        let type_bytes = working_set.get_span_contents(tokens[idx].span).to_vec();
3744        let output_type = parse_type(working_set, &type_bytes, tokens[idx].span);
3745
3746        output.push((input_type, output_type));
3747
3748        idx += 1;
3749    }
3750
3751    output
3752}
3753
3754pub fn parse_full_signature(working_set: &mut StateWorkingSet, spans: &[Span]) -> Expression {
3755    match spans.len() {
3756        // This case should never happen. It corresponds to declarations like `def foo {}`,
3757        // which should throw a 'Missing required positional argument.' before getting to this point
3758        0 => {
3759            working_set.error(ParseError::InternalError(
3760                "failed to catch missing positional arguments".to_string(),
3761                Span::concat(spans),
3762            ));
3763            garbage(working_set, Span::concat(spans))
3764        }
3765
3766        // e.g. `[ b"[foo: string]" ]`
3767        1 => parse_signature(working_set, spans[0]),
3768
3769        // This case is needed to distinguish between e.g.
3770        // `[ b"[]", b"{ true }" ]` vs `[ b"[]:", b"int" ]`
3771        2 if working_set.get_span_contents(spans[1]).starts_with(b"{") => {
3772            parse_signature(working_set, spans[0])
3773        }
3774
3775        // This should handle every other case, e.g.
3776        // `[ b"[]:", b"int" ]`
3777        // `[ b"[]", b":", b"int" ]`
3778        // `[ b"[]", b":", b"int", b"->", b"bool" ]`
3779        _ => {
3780            let (mut arg_signature, input_output_types_pos) =
3781                if working_set.get_span_contents(spans[0]).ends_with(b":") {
3782                    (
3783                        parse_signature(working_set, Span::new(spans[0].start, spans[0].end - 1)),
3784                        1,
3785                    )
3786                } else if working_set.get_span_contents(spans[1]) == b":" {
3787                    (parse_signature(working_set, spans[0]), 2)
3788                } else {
3789                    // This should be an error case, but we call parse_signature anyway
3790                    // so it can handle the various possible errors
3791                    // e.g. `[ b"[]", b"int" ]` or `[
3792                    working_set.error(ParseError::Expected(
3793                        "colon (:) before type signature",
3794                        Span::concat(&spans[1..]),
3795                    ));
3796                    // (garbage(working_set, Span::concat(spans)), 1)
3797
3798                    (parse_signature(working_set, spans[0]), 1)
3799                };
3800
3801            let input_output_types =
3802                parse_input_output_types(working_set, &spans[input_output_types_pos..]);
3803
3804            if let Expression {
3805                expr: Expr::Signature(sig),
3806                span: expr_span,
3807                ..
3808            } = &mut arg_signature
3809            {
3810                sig.input_output_types = input_output_types;
3811                expr_span.end = Span::concat(&spans[input_output_types_pos..]).end;
3812            }
3813            arg_signature
3814        }
3815    }
3816}
3817
3818pub fn parse_row_condition(working_set: &mut StateWorkingSet, spans: &[Span]) -> Expression {
3819    let pos = spans.first().map(|s| s.start).unwrap_or(0);
3820    let var_id = working_set.add_variable(b"$it".to_vec(), Span::new(pos, pos), Type::Any, false);
3821    let expression = parse_math_expression(working_set, spans, Some(var_id));
3822    let span = Span::concat(spans);
3823
3824    let block_id = match expression.expr {
3825        Expr::Block(block_id) => block_id,
3826        Expr::Closure(block_id) => block_id,
3827        Expr::FullCellPath(ref box_fcp) if box_fcp.head.as_var().is_some_and(|id| id != var_id) => {
3828            let mut expression = expression;
3829            expression.ty = Type::Any;
3830            return expression;
3831        }
3832        Expr::Var(arg_var_id) if arg_var_id != var_id => {
3833            let mut expression = expression;
3834            expression.ty = Type::Any;
3835            return expression;
3836        }
3837        _ => {
3838            // We have an expression, check that it's compatible with bool
3839            if !type_compatible(&Type::Bool, &expression.ty) {
3840                working_set.error(ParseError::TypeMismatch(
3841                    Type::Bool,
3842                    expression.ty.clone(),
3843                    expression.span,
3844                ));
3845                return Expression::garbage(working_set, expression.span);
3846            }
3847
3848            // Convert this expression into a block.
3849            let mut block = Block::new();
3850            let mut pipeline = Pipeline::new();
3851            pipeline.elements.push(PipelineElement {
3852                pipe: None,
3853                expr: expression,
3854                redirection: None,
3855            });
3856
3857            block.pipelines.push(pipeline);
3858
3859            block.signature.required_positional.push(PositionalArg {
3860                name: "$it".into(),
3861                desc: "row condition".into(),
3862                shape: SyntaxShape::Any,
3863                var_id: Some(var_id),
3864                default_value: None,
3865                completion: None,
3866            });
3867
3868            compile_block(working_set, &mut block);
3869
3870            working_set.add_block(Arc::new(block))
3871        }
3872    };
3873
3874    Expression::new(working_set, Expr::RowCondition(block_id), span, Type::Bool)
3875}
3876
3877pub fn parse_signature(working_set: &mut StateWorkingSet, span: Span) -> Expression {
3878    let bytes = working_set.get_span_contents(span);
3879
3880    let mut start = span.start;
3881    let mut end = span.end;
3882
3883    let mut has_paren = false;
3884
3885    if bytes.starts_with(b"[") {
3886        start += 1;
3887    } else if bytes.starts_with(b"(") {
3888        has_paren = true;
3889        start += 1;
3890    } else {
3891        working_set.error(ParseError::Expected("[ or (", Span::new(start, start + 1)));
3892        return garbage(working_set, span);
3893    }
3894
3895    if (has_paren && bytes.ends_with(b")")) || (!has_paren && bytes.ends_with(b"]")) {
3896        end -= 1;
3897    } else {
3898        working_set.error(ParseError::Unclosed("] or )".into(), Span::new(end, end)));
3899    }
3900
3901    let sig = parse_signature_helper(working_set, Span::new(start, end));
3902
3903    Expression::new(working_set, Expr::Signature(sig), span, Type::Any)
3904}
3905
3906pub fn parse_signature_helper(working_set: &mut StateWorkingSet, span: Span) -> Box<Signature> {
3907    enum ParseMode {
3908        Arg,
3909        AfterCommaArg,
3910        Type,
3911        AfterType,
3912        DefaultValue,
3913    }
3914
3915    #[derive(Debug)]
3916    enum Arg {
3917        Positional {
3918            arg: PositionalArg,
3919            required: bool,
3920            type_annotated: bool,
3921        },
3922        RestPositional(PositionalArg),
3923        Flag {
3924            flag: Flag,
3925            type_annotated: bool,
3926        },
3927    }
3928
3929    let source = working_set.get_span_contents(span);
3930
3931    let (output, err) = lex_signature(
3932        source,
3933        span.start,
3934        &[b'\n', b'\r'],
3935        &[b':', b'=', b','],
3936        false,
3937    );
3938    if let Some(err) = err {
3939        working_set.error(err);
3940    }
3941
3942    let mut args: Vec<Arg> = vec![];
3943    let mut parse_mode = ParseMode::Arg;
3944
3945    for (index, token) in output.iter().enumerate() {
3946        let last_token = index == output.len() - 1;
3947
3948        match token {
3949            Token {
3950                contents: crate::TokenContents::Item | crate::TokenContents::AssignmentOperator,
3951                span,
3952            } => {
3953                let span = *span;
3954                let contents = working_set.get_span_contents(span).to_vec();
3955
3956                // The : symbol separates types
3957                if contents == b":" {
3958                    match parse_mode {
3959                        ParseMode::Arg if last_token => working_set
3960                            .error(ParseError::Expected("type", Span::new(span.end, span.end))),
3961                        ParseMode::Arg => {
3962                            parse_mode = ParseMode::Type;
3963                        }
3964                        ParseMode::AfterCommaArg | ParseMode::AfterType => {
3965                            working_set.error(ParseError::Expected("parameter or flag", span));
3966                        }
3967                        ParseMode::Type | ParseMode::DefaultValue => {
3968                            // We're seeing two types for the same thing for some reason, error
3969                            working_set.error(ParseError::Expected("type", span));
3970                        }
3971                    }
3972                }
3973                // The = symbol separates a variable from its default value
3974                else if contents == b"=" {
3975                    match parse_mode {
3976                        ParseMode::Arg | ParseMode::AfterType if last_token => working_set.error(
3977                            ParseError::Expected("default value", Span::new(span.end, span.end)),
3978                        ),
3979                        ParseMode::Arg | ParseMode::AfterType => {
3980                            parse_mode = ParseMode::DefaultValue;
3981                        }
3982                        ParseMode::Type => {
3983                            working_set.error(ParseError::Expected("type", span));
3984                        }
3985                        ParseMode::AfterCommaArg => {
3986                            working_set.error(ParseError::Expected("parameter or flag", span));
3987                        }
3988                        ParseMode::DefaultValue => {
3989                            // We're seeing two default values for some reason, error
3990                            working_set.error(ParseError::Expected("default value", span));
3991                        }
3992                    }
3993                }
3994                // The , symbol separates params only
3995                else if contents == b"," {
3996                    match parse_mode {
3997                        ParseMode::Arg | ParseMode::AfterType => {
3998                            parse_mode = ParseMode::AfterCommaArg
3999                        }
4000                        ParseMode::AfterCommaArg => {
4001                            working_set.error(ParseError::Expected("parameter or flag", span));
4002                        }
4003                        ParseMode::Type => {
4004                            working_set.error(ParseError::Expected("type", span));
4005                        }
4006                        ParseMode::DefaultValue => {
4007                            working_set.error(ParseError::Expected("default value", span));
4008                        }
4009                    }
4010                } else {
4011                    match parse_mode {
4012                        ParseMode::Arg | ParseMode::AfterCommaArg | ParseMode::AfterType => {
4013                            // Long flag with optional short form following with no whitespace, e.g. --output, --age(-a)
4014                            if contents.starts_with(b"--") && contents.len() > 2 {
4015                                // Split the long flag from the short flag with the ( character as delimiter.
4016                                // The trailing ) is removed further down.
4017                                let flags: Vec<_> = contents.split(|x| x == &b'(').collect();
4018
4019                                let long = String::from_utf8_lossy(&flags[0][2..]).to_string();
4020                                let mut variable_name = flags[0][2..].to_vec();
4021                                // Replace the '-' in a variable name with '_'
4022                                for byte in variable_name.iter_mut() {
4023                                    if *byte == b'-' {
4024                                        *byte = b'_';
4025                                    }
4026                                }
4027
4028                                if !is_variable(&variable_name) {
4029                                    working_set.error(ParseError::Expected(
4030                                        "valid variable name for this long flag",
4031                                        span,
4032                                    ))
4033                                }
4034
4035                                let var_id =
4036                                    working_set.add_variable(variable_name, span, Type::Any, false);
4037
4038                                // If there's no short flag, exit now. Otherwise, parse it.
4039                                if flags.len() == 1 {
4040                                    args.push(Arg::Flag {
4041                                        flag: Flag {
4042                                            arg: None,
4043                                            desc: String::new(),
4044                                            long,
4045                                            short: None,
4046                                            required: false,
4047                                            var_id: Some(var_id),
4048                                            default_value: None,
4049                                            completion: None,
4050                                        },
4051                                        type_annotated: false,
4052                                    });
4053                                } else if flags.len() >= 3 {
4054                                    working_set.error(ParseError::Expected(
4055                                        "only one short flag alternative",
4056                                        span,
4057                                    ));
4058                                } else {
4059                                    let short_flag = &flags[1];
4060                                    let short_flag = if !short_flag.starts_with(b"-")
4061                                        || !short_flag.ends_with(b")")
4062                                    {
4063                                        working_set.error(ParseError::Expected(
4064                                            "short flag alternative for the long flag",
4065                                            span,
4066                                        ));
4067                                        short_flag
4068                                    } else {
4069                                        // Obtain the flag's name by removing the starting - and trailing )
4070                                        &short_flag[1..(short_flag.len() - 1)]
4071                                    };
4072                                    // Note that it is currently possible to make a short flag with non-alphanumeric characters,
4073                                    // like -).
4074
4075                                    let short_flag =
4076                                        String::from_utf8_lossy(short_flag).to_string();
4077                                    let chars: Vec<char> = short_flag.chars().collect();
4078
4079                                    if chars.len() == 1 {
4080                                        args.push(Arg::Flag {
4081                                            flag: Flag {
4082                                                arg: None,
4083                                                desc: String::new(),
4084                                                long,
4085                                                short: Some(chars[0]),
4086                                                required: false,
4087                                                var_id: Some(var_id),
4088                                                default_value: None,
4089                                                completion: None,
4090                                            },
4091                                            type_annotated: false,
4092                                        });
4093                                    } else {
4094                                        working_set.error(ParseError::Expected("short flag", span));
4095                                    }
4096                                }
4097                                parse_mode = ParseMode::Arg;
4098                            }
4099                            // Mandatory short flag, e.g. -e (must be one character)
4100                            else if contents.starts_with(b"-") && contents.len() > 1 {
4101                                let short_flag = &contents[1..];
4102                                let short_flag = String::from_utf8_lossy(short_flag).to_string();
4103                                let chars: Vec<char> = short_flag.chars().collect();
4104
4105                                if chars.len() > 1 {
4106                                    working_set.error(ParseError::Expected("short flag", span));
4107                                }
4108
4109                                let mut encoded_var_name = [0u8; 4];
4110                                let len = chars[0].encode_utf8(&mut encoded_var_name).len();
4111                                let variable_name = encoded_var_name[0..len].to_vec();
4112
4113                                if !is_variable(&variable_name) {
4114                                    working_set.error(ParseError::Expected(
4115                                        "valid variable name for this short flag",
4116                                        span,
4117                                    ))
4118                                }
4119
4120                                let var_id =
4121                                    working_set.add_variable(variable_name, span, Type::Any, false);
4122
4123                                args.push(Arg::Flag {
4124                                    flag: Flag {
4125                                        arg: None,
4126                                        desc: String::new(),
4127                                        long: String::new(),
4128                                        short: Some(chars[0]),
4129                                        required: false,
4130                                        var_id: Some(var_id),
4131                                        default_value: None,
4132                                        completion: None,
4133                                    },
4134                                    type_annotated: false,
4135                                });
4136                                parse_mode = ParseMode::Arg;
4137                            }
4138                            // Short flag alias for long flag, e.g. --b (-a)
4139                            // This is the same as the short flag in --b(-a)
4140                            else if let Some(short_flag) = contents.strip_prefix(b"(-") {
4141                                if let ParseMode::AfterCommaArg = parse_mode {
4142                                    working_set
4143                                        .error(ParseError::Expected("parameter or flag", span));
4144                                }
4145
4146                                let short_flag = if !short_flag.ends_with(b")") {
4147                                    working_set.error(ParseError::Expected("short flag", span));
4148                                    short_flag
4149                                } else {
4150                                    &short_flag[..(short_flag.len() - 1)]
4151                                };
4152
4153                                let short_flag = String::from_utf8_lossy(short_flag).to_string();
4154                                let chars: Vec<char> = short_flag.chars().collect();
4155
4156                                if chars.len() == 1 {
4157                                    match args.last_mut() {
4158                                        Some(Arg::Flag { flag, .. }) => {
4159                                            if flag.short.is_some() {
4160                                                working_set.error(ParseError::Expected(
4161                                                    "one short flag",
4162                                                    span,
4163                                                ));
4164                                            } else {
4165                                                flag.short = Some(chars[0]);
4166                                            }
4167                                        }
4168                                        _ => {
4169                                            working_set
4170                                                .error(ParseError::Expected("unknown flag", span));
4171                                        }
4172                                    }
4173                                } else {
4174                                    working_set.error(ParseError::Expected("short flag", span));
4175                                }
4176                            }
4177                            // Positional arg, optional
4178                            else if let Some(optional_param) = contents.strip_suffix(b"?") {
4179                                let name = String::from_utf8_lossy(optional_param).to_string();
4180
4181                                if !is_variable(optional_param) {
4182                                    working_set.error(ParseError::Expected(
4183                                        "valid variable name for this optional parameter",
4184                                        span,
4185                                    ))
4186                                }
4187
4188                                let var_id = working_set.add_variable(
4189                                    optional_param.to_vec(),
4190                                    span,
4191                                    Type::Any,
4192                                    false,
4193                                );
4194
4195                                args.push(Arg::Positional {
4196                                    arg: PositionalArg {
4197                                        desc: String::new(),
4198                                        name,
4199                                        shape: SyntaxShape::Any,
4200                                        var_id: Some(var_id),
4201                                        default_value: None,
4202                                        completion: None,
4203                                    },
4204                                    required: false,
4205                                    type_annotated: false,
4206                                });
4207                                parse_mode = ParseMode::Arg;
4208                            }
4209                            // Rest param
4210                            else if let Some(contents) = contents.strip_prefix(b"...") {
4211                                let name = String::from_utf8_lossy(contents).to_string();
4212                                let contents_vec: Vec<u8> = contents.to_vec();
4213
4214                                if !is_variable(&contents_vec) {
4215                                    working_set.error(ParseError::Expected(
4216                                        "valid variable name for this rest parameter",
4217                                        span,
4218                                    ))
4219                                }
4220
4221                                let var_id =
4222                                    working_set.add_variable(contents_vec, span, Type::Any, false);
4223
4224                                args.push(Arg::RestPositional(PositionalArg {
4225                                    desc: String::new(),
4226                                    name,
4227                                    shape: SyntaxShape::Any,
4228                                    var_id: Some(var_id),
4229                                    default_value: None,
4230                                    completion: None,
4231                                }));
4232                                parse_mode = ParseMode::Arg;
4233                            }
4234                            // Normal param
4235                            else {
4236                                let name = String::from_utf8_lossy(&contents).to_string();
4237                                let contents_vec = contents.to_vec();
4238
4239                                if !is_variable(&contents_vec) {
4240                                    working_set.error(ParseError::Expected(
4241                                        "valid variable name for this parameter",
4242                                        span,
4243                                    ))
4244                                }
4245
4246                                let var_id =
4247                                    working_set.add_variable(contents_vec, span, Type::Any, false);
4248
4249                                // Positional arg, required
4250                                args.push(Arg::Positional {
4251                                    arg: PositionalArg {
4252                                        desc: String::new(),
4253                                        name,
4254                                        shape: SyntaxShape::Any,
4255                                        var_id: Some(var_id),
4256                                        default_value: None,
4257                                        completion: None,
4258                                    },
4259                                    required: true,
4260                                    type_annotated: false,
4261                                });
4262                                parse_mode = ParseMode::Arg;
4263                            }
4264                        }
4265                        ParseMode::Type => {
4266                            if let Some(last) = args.last_mut() {
4267                                let (syntax_shape, completer) = if contents.contains(&b'@') {
4268                                    let mut split = contents.splitn(2, |b| b == &b'@');
4269
4270                                    let shape_name = split
4271                                        .next()
4272                                        .expect("If `bytes` contains `@` splitn returns 2 slices");
4273                                    let shape_span =
4274                                        Span::new(span.start, span.start + shape_name.len());
4275                                    let cmd_span =
4276                                        Span::new(span.start + shape_name.len() + 1, span.end);
4277                                    let cmd_name = split
4278                                        .next()
4279                                        .expect("If `bytes` contains `@` splitn returns 2 slices");
4280                                    (
4281                                        parse_shape_name(working_set, shape_name, shape_span),
4282                                        parse_completer(working_set, cmd_name, cmd_span),
4283                                    )
4284                                } else {
4285                                    (parse_shape_name(working_set, &contents, span), None)
4286                                };
4287                                //TODO check if we're replacing a custom parameter already
4288                                match last {
4289                                    Arg::Positional {
4290                                        arg:
4291                                            PositionalArg {
4292                                                shape,
4293                                                var_id,
4294                                                completion,
4295                                                ..
4296                                            },
4297                                        required: _,
4298                                        type_annotated,
4299                                    } => {
4300                                        working_set.set_variable_type(
4301                                            var_id.expect(
4302                                                "internal error: all custom parameters must have \
4303                                                 var_ids",
4304                                            ),
4305                                            syntax_shape.to_type(),
4306                                        );
4307                                        *completion = completer;
4308                                        *shape = syntax_shape;
4309                                        *type_annotated = true;
4310                                    }
4311                                    Arg::RestPositional(PositionalArg {
4312                                        shape,
4313                                        var_id,
4314                                        completion,
4315                                        ..
4316                                    }) => {
4317                                        working_set.set_variable_type(
4318                                            var_id.expect(
4319                                                "internal error: all custom parameters must have \
4320                                                 var_ids",
4321                                            ),
4322                                            Type::List(Box::new(syntax_shape.to_type())),
4323                                        );
4324                                        *completion = completer;
4325                                        *shape = syntax_shape;
4326                                    }
4327                                    Arg::Flag {
4328                                        flag:
4329                                            Flag {
4330                                                arg,
4331                                                var_id,
4332                                                completion,
4333                                                ..
4334                                            },
4335                                        type_annotated,
4336                                    } => {
4337                                        working_set.set_variable_type(var_id.expect("internal error: all custom parameters must have var_ids"), syntax_shape.to_type());
4338                                        if syntax_shape == SyntaxShape::Boolean {
4339                                            working_set.error(ParseError::LabeledError(
4340                                                "Type annotations are not allowed for boolean switches.".to_string(),
4341                                                "Remove the `: bool` type annotation.".to_string(),
4342                                                span,
4343                                            ));
4344                                        }
4345                                        *completion = completer;
4346                                        *arg = Some(syntax_shape);
4347                                        *type_annotated = true;
4348                                    }
4349                                }
4350                            }
4351                            parse_mode = ParseMode::AfterType;
4352                        }
4353                        ParseMode::DefaultValue => {
4354                            if let Some(last) = args.last_mut() {
4355                                let expression = parse_value(working_set, span, &SyntaxShape::Any);
4356
4357                                //TODO check if we're replacing a custom parameter already
4358                                match last {
4359                                    Arg::Positional {
4360                                        arg:
4361                                            PositionalArg {
4362                                                shape,
4363                                                var_id,
4364                                                default_value,
4365                                                ..
4366                                            },
4367                                        required,
4368                                        type_annotated,
4369                                    } => {
4370                                        let var_id = var_id.expect("internal error: all custom parameters must have var_ids");
4371                                        let var_type = &working_set.get_variable(var_id).ty;
4372                                        match var_type {
4373                                            Type::Any => {
4374                                                if !*type_annotated {
4375                                                    working_set.set_variable_type(
4376                                                        var_id,
4377                                                        expression.ty.clone(),
4378                                                    );
4379                                                }
4380                                            }
4381                                            _ => {
4382                                                if !type_compatible(var_type, &expression.ty) {
4383                                                    working_set.error(
4384                                                        ParseError::AssignmentMismatch(
4385                                                            "Default value wrong type".into(),
4386                                                            format!(
4387                                                            "expected default value to be `{var_type}`"
4388                                                        ),
4389                                                            expression.span,
4390                                                        ),
4391                                                    )
4392                                                }
4393                                            }
4394                                        }
4395
4396                                        *default_value = if let Ok(constant) =
4397                                            eval_constant(working_set, &expression)
4398                                        {
4399                                            Some(constant)
4400                                        } else {
4401                                            working_set.error(ParseError::NonConstantDefaultValue(
4402                                                expression.span,
4403                                            ));
4404                                            None
4405                                        };
4406
4407                                        if !*type_annotated {
4408                                            *shape = expression.ty.to_shape();
4409                                        }
4410                                        *required = false;
4411                                    }
4412                                    Arg::RestPositional(..) => {
4413                                        working_set.error(ParseError::AssignmentMismatch(
4414                                            "Rest parameter was given a default value".into(),
4415                                            "can't have default value".into(),
4416                                            expression.span,
4417                                        ))
4418                                    }
4419                                    Arg::Flag {
4420                                        flag:
4421                                            Flag {
4422                                                arg,
4423                                                var_id,
4424                                                default_value,
4425                                                ..
4426                                            },
4427                                        type_annotated,
4428                                    } => {
4429                                        let expression_span = expression.span;
4430
4431                                        *default_value = if let Ok(value) =
4432                                            eval_constant(working_set, &expression)
4433                                        {
4434                                            Some(value)
4435                                        } else {
4436                                            working_set.error(ParseError::NonConstantDefaultValue(
4437                                                expression_span,
4438                                            ));
4439                                            None
4440                                        };
4441
4442                                        let var_id = var_id.expect("internal error: all custom parameters must have var_ids");
4443                                        let var_type = &working_set.get_variable(var_id).ty;
4444                                        let expression_ty = expression.ty.clone();
4445
4446                                        // Flags with no TypeMode are just present/not-present switches
4447                                        // in the case, `var_type` is any.
4448                                        match var_type {
4449                                            Type::Any => {
4450                                                if !*type_annotated {
4451                                                    *arg = Some(expression_ty.to_shape());
4452                                                    working_set
4453                                                        .set_variable_type(var_id, expression_ty);
4454                                                }
4455                                            }
4456                                            t => {
4457                                                if !type_compatible(t, &expression_ty) {
4458                                                    working_set.error(
4459                                                        ParseError::AssignmentMismatch(
4460                                                            "Default value is the wrong type"
4461                                                                .into(),
4462                                                            format!(
4463                                                                "expected default value to be `{t}`"
4464                                                            ),
4465                                                            expression_span,
4466                                                        ),
4467                                                    )
4468                                                }
4469                                            }
4470                                        }
4471                                    }
4472                                }
4473                            }
4474                            parse_mode = ParseMode::Arg;
4475                        }
4476                    }
4477                }
4478            }
4479            Token {
4480                contents: crate::TokenContents::Comment,
4481                span,
4482            } => {
4483                let contents = working_set.get_span_contents(Span::new(span.start + 1, span.end));
4484
4485                let mut contents = String::from_utf8_lossy(contents).to_string();
4486                contents = contents.trim().into();
4487
4488                if let Some(last) = args.last_mut() {
4489                    match last {
4490                        Arg::Flag { flag, .. } => {
4491                            if !flag.desc.is_empty() {
4492                                flag.desc.push('\n');
4493                            }
4494                            flag.desc.push_str(&contents);
4495                        }
4496                        Arg::Positional {
4497                            arg: positional, ..
4498                        } => {
4499                            if !positional.desc.is_empty() {
4500                                positional.desc.push('\n');
4501                            }
4502                            positional.desc.push_str(&contents);
4503                        }
4504                        Arg::RestPositional(positional) => {
4505                            if !positional.desc.is_empty() {
4506                                positional.desc.push('\n');
4507                            }
4508                            positional.desc.push_str(&contents);
4509                        }
4510                    }
4511                }
4512            }
4513            _ => {}
4514        }
4515    }
4516
4517    let mut sig = Signature::new(String::new());
4518
4519    for arg in args {
4520        match arg {
4521            Arg::Positional {
4522                arg: positional,
4523                required,
4524                ..
4525            } => {
4526                if required {
4527                    if !sig.optional_positional.is_empty() {
4528                        working_set.error(ParseError::RequiredAfterOptional(
4529                            positional.name.clone(),
4530                            span,
4531                        ))
4532                    }
4533                    sig.required_positional.push(positional)
4534                } else {
4535                    sig.optional_positional.push(positional)
4536                }
4537            }
4538            Arg::Flag { flag, .. } => sig.named.push(flag),
4539            Arg::RestPositional(positional) => {
4540                if positional.name.is_empty() {
4541                    working_set.error(ParseError::RestNeedsName(span))
4542                } else if sig.rest_positional.is_none() {
4543                    sig.rest_positional = Some(PositionalArg {
4544                        name: positional.name,
4545                        ..positional
4546                    })
4547                } else {
4548                    // Too many rest params
4549                    working_set.error(ParseError::MultipleRestParams(span))
4550                }
4551            }
4552        }
4553    }
4554
4555    Box::new(sig)
4556}
4557
4558pub fn parse_list_expression(
4559    working_set: &mut StateWorkingSet,
4560    span: Span,
4561    element_shape: &SyntaxShape,
4562) -> Expression {
4563    let bytes = working_set.get_span_contents(span);
4564
4565    let mut start = span.start;
4566    let mut end = span.end;
4567
4568    if bytes.starts_with(b"[") {
4569        start += 1;
4570    }
4571    if bytes.ends_with(b"]") {
4572        end -= 1;
4573    } else {
4574        working_set.error(ParseError::Unclosed("]".into(), Span::new(end, end)));
4575    }
4576
4577    let inner_span = Span::new(start, end);
4578    let source = working_set.get_span_contents(inner_span);
4579
4580    let (output, err) = lex(source, inner_span.start, &[b'\n', b'\r', b','], &[], true);
4581    if let Some(err) = err {
4582        working_set.error(err)
4583    }
4584
4585    let (mut output, err) = lite_parse(&output, working_set);
4586    if let Some(err) = err {
4587        working_set.error(err)
4588    }
4589
4590    let mut args = vec![];
4591
4592    let mut contained_type: Option<Type> = None;
4593
4594    if !output.block.is_empty() {
4595        for mut command in output.block.remove(0).commands {
4596            let mut spans_idx = 0;
4597
4598            while spans_idx < command.parts.len() {
4599                let curr_span = command.parts[spans_idx];
4600                let curr_tok = working_set.get_span_contents(curr_span);
4601                let (arg, ty) = if curr_tok.starts_with(b"...")
4602                    && curr_tok.len() > 3
4603                    && (curr_tok[3] == b'$' || curr_tok[3] == b'[' || curr_tok[3] == b'(')
4604                {
4605                    // Parse the spread operator
4606                    // Remove "..." before parsing argument to spread operator
4607                    command.parts[spans_idx] = Span::new(curr_span.start + 3, curr_span.end);
4608                    let spread_arg = parse_multispan_value(
4609                        working_set,
4610                        &command.parts,
4611                        &mut spans_idx,
4612                        &SyntaxShape::List(Box::new(element_shape.clone())),
4613                    );
4614                    let elem_ty = match &spread_arg.ty {
4615                        Type::List(elem_ty) => *elem_ty.clone(),
4616                        _ => Type::Any,
4617                    };
4618                    let span = Span::new(curr_span.start, curr_span.start + 3);
4619                    (ListItem::Spread(span, spread_arg), elem_ty)
4620                } else {
4621                    let arg = parse_multispan_value(
4622                        working_set,
4623                        &command.parts,
4624                        &mut spans_idx,
4625                        element_shape,
4626                    );
4627                    let ty = arg.ty.clone();
4628                    (ListItem::Item(arg), ty)
4629                };
4630
4631                contained_type = match contained_type {
4632                    Some(ctype) => Some(ctype.widen(ty)),
4633                    None => Some(ty),
4634                };
4635
4636                args.push(arg);
4637
4638                spans_idx += 1;
4639            }
4640        }
4641    }
4642
4643    Expression::new(
4644        working_set,
4645        Expr::List(args),
4646        span,
4647        Type::List(Box::new(if let Some(ty) = contained_type {
4648            ty
4649        } else {
4650            Type::Any
4651        })),
4652    )
4653}
4654
4655fn parse_table_row(
4656    working_set: &mut StateWorkingSet,
4657    span: Span,
4658) -> Result<(Vec<Expression>, Span), Span> {
4659    let list = parse_list_expression(working_set, span, &SyntaxShape::Any);
4660    let Expression {
4661        expr: Expr::List(list),
4662        span,
4663        ..
4664    } = list
4665    else {
4666        unreachable!("the item must be a list")
4667    };
4668
4669    list.into_iter()
4670        .map(|item| match item {
4671            ListItem::Item(expr) => Ok(expr),
4672            ListItem::Spread(_, spread) => Err(spread.span),
4673        })
4674        .collect::<Result<_, _>>()
4675        .map(|exprs| (exprs, span))
4676}
4677
4678fn parse_table_expression(
4679    working_set: &mut StateWorkingSet,
4680    span: Span,
4681    list_element_shape: &SyntaxShape,
4682) -> Expression {
4683    let bytes = working_set.get_span_contents(span);
4684    let inner_span = {
4685        let start = if bytes.starts_with(b"[") {
4686            span.start + 1
4687        } else {
4688            span.start
4689        };
4690
4691        let end = if bytes.ends_with(b"]") {
4692            span.end - 1
4693        } else {
4694            let end = span.end;
4695            working_set.error(ParseError::Unclosed("]".into(), Span::new(end, end)));
4696            span.end
4697        };
4698
4699        Span::new(start, end)
4700    };
4701
4702    let source = working_set.get_span_contents(inner_span);
4703    let (tokens, err) = lex(source, inner_span.start, &[b'\n', b'\r', b','], &[], true);
4704    if let Some(err) = err {
4705        working_set.error(err);
4706    }
4707
4708    // Check that we have all arguments first, before trying to parse the first
4709    // in order to avoid exponential parsing time
4710    let [first, second, rest @ ..] = &tokens[..] else {
4711        return parse_list_expression(working_set, span, list_element_shape);
4712    };
4713    if !working_set.get_span_contents(first.span).starts_with(b"[")
4714        || second.contents != TokenContents::Semicolon
4715        || rest.is_empty()
4716    {
4717        return parse_list_expression(working_set, span, list_element_shape);
4718    };
4719    let head = parse_table_row(working_set, first.span);
4720
4721    let errors = working_set.parse_errors.len();
4722
4723    let (head, rows) = match head {
4724        Ok((head, _)) => {
4725            let rows = rest
4726                .iter()
4727                .filter_map(|it| {
4728                    use std::cmp::Ordering;
4729
4730                    match working_set.get_span_contents(it.span) {
4731                        b"," => None,
4732                        text if !text.starts_with(b"[") => {
4733                            let err = ParseError::LabeledErrorWithHelp {
4734                                error: String::from("Table item not list"),
4735                                label: String::from("not a list"),
4736                                span: it.span,
4737                                help: String::from("All table items must be lists"),
4738                            };
4739                            working_set.error(err);
4740                            None
4741                        }
4742                        _ => match parse_table_row(working_set, it.span) {
4743                            Ok((list, span)) => {
4744                                match list.len().cmp(&head.len()) {
4745                                    Ordering::Less => {
4746                                        let err = ParseError::MissingColumns(head.len(), span);
4747                                        working_set.error(err);
4748                                    }
4749                                    Ordering::Greater => {
4750                                        let span = {
4751                                            let start = list[head.len()].span.start;
4752                                            let end = span.end;
4753                                            Span::new(start, end)
4754                                        };
4755                                        let err = ParseError::ExtraColumns(head.len(), span);
4756                                        working_set.error(err);
4757                                    }
4758                                    Ordering::Equal => {}
4759                                }
4760                                Some(list)
4761                            }
4762                            Err(span) => {
4763                                let err = ParseError::LabeledError(
4764                                    String::from("Cannot spread in a table row"),
4765                                    String::from("invalid spread here"),
4766                                    span,
4767                                );
4768                                working_set.error(err);
4769                                None
4770                            }
4771                        },
4772                    }
4773                })
4774                .collect();
4775
4776            (head, rows)
4777        }
4778        Err(span) => {
4779            let err = ParseError::LabeledError(
4780                String::from("Cannot spread in a table row"),
4781                String::from("invalid spread here"),
4782                span,
4783            );
4784            working_set.error(err);
4785            (Vec::new(), Vec::new())
4786        }
4787    };
4788
4789    let ty = if working_set.parse_errors.len() == errors {
4790        let (ty, errs) = table_type(&head, &rows);
4791        working_set.parse_errors.extend(errs);
4792        ty
4793    } else {
4794        Type::table()
4795    };
4796
4797    let table = Table {
4798        columns: head.into(),
4799        rows: rows.into_iter().map(Into::into).collect(),
4800    };
4801
4802    Expression::new(working_set, Expr::Table(table), span, ty)
4803}
4804
4805fn table_type(head: &[Expression], rows: &[Vec<Expression>]) -> (Type, Vec<ParseError>) {
4806    let mut errors = vec![];
4807    let mut rows = rows.to_vec();
4808    let mut mk_ty = || -> Type {
4809        rows.iter_mut()
4810            .map(|row| row.pop().map(|x| x.ty).unwrap_or_default())
4811            .reduce(|acc, ty| -> Type { acc.widen(ty) })
4812            .unwrap_or_default()
4813    };
4814
4815    let mk_error = |span| ParseError::LabeledErrorWithHelp {
4816        error: "Table column name not string".into(),
4817        label: "must be a string".into(),
4818        help: "Table column names should be able to be converted into strings".into(),
4819        span,
4820    };
4821
4822    let mut ty = head
4823        .iter()
4824        .rev()
4825        // Include only known column names in type
4826        .filter_map(|expr| {
4827            if !Type::String.is_subtype_of(&expr.ty) {
4828                errors.push(mk_error(expr.span));
4829                None
4830            } else {
4831                expr.as_string()
4832            }
4833        })
4834        .map(|title| (title, mk_ty()))
4835        .collect_vec();
4836
4837    ty.reverse();
4838
4839    (Type::Table(ty.into()), errors)
4840}
4841
4842pub fn parse_block_expression(working_set: &mut StateWorkingSet, span: Span) -> Expression {
4843    trace!("parsing: block expression");
4844
4845    let bytes = working_set.get_span_contents(span);
4846
4847    let mut start = span.start;
4848    let mut end = span.end;
4849    let mut is_closed = true;
4850
4851    if bytes.starts_with(b"{") {
4852        start += 1;
4853    } else {
4854        working_set.error(ParseError::Expected("block", span));
4855        return garbage(working_set, span);
4856    }
4857    if bytes.ends_with(b"}") {
4858        end -= 1;
4859    } else {
4860        working_set.error(ParseError::Unclosed("}".into(), Span::new(end, end)));
4861        is_closed = false;
4862    }
4863
4864    let inner_span = Span::new(start, end);
4865
4866    let source = working_set.get_span_contents(inner_span);
4867
4868    let (output, err) = lex(source, start, &[], &[], false);
4869    if let Some(err) = err {
4870        working_set.error(err);
4871    }
4872
4873    working_set.enter_scope();
4874
4875    // Check to see if we have parameters
4876    let (signature, amt_to_skip): (Option<(Box<Signature>, Span)>, usize) = match output.first() {
4877        Some(Token {
4878            contents: TokenContents::Pipe,
4879            span,
4880        }) => {
4881            working_set.error(ParseError::Expected("block but found closure", *span));
4882            (None, 0)
4883        }
4884        _ => (None, 0),
4885    };
4886
4887    let mut output = parse_block(working_set, &output[amt_to_skip..], span, false, false);
4888
4889    if let Some(signature) = signature {
4890        output.signature = signature.0;
4891    }
4892
4893    output.span = Some(span);
4894
4895    if is_closed {
4896        working_set.exit_scope();
4897    }
4898
4899    let block_id = working_set.add_block(Arc::new(output));
4900
4901    Expression::new(working_set, Expr::Block(block_id), span, Type::Block)
4902}
4903
4904pub fn parse_match_block_expression(working_set: &mut StateWorkingSet, span: Span) -> Expression {
4905    let bytes = working_set.get_span_contents(span);
4906
4907    let mut start = span.start;
4908    let mut end = span.end;
4909    let mut is_closed = true;
4910
4911    if bytes.starts_with(b"{") {
4912        start += 1;
4913    } else {
4914        working_set.error(ParseError::Expected("closure", span));
4915        return garbage(working_set, span);
4916    }
4917    if bytes.ends_with(b"}") {
4918        end -= 1;
4919    } else {
4920        working_set.error(ParseError::Unclosed("}".into(), Span::new(end, end)));
4921        is_closed = false;
4922    }
4923
4924    let inner_span = Span::new(start, end);
4925
4926    let source = working_set.get_span_contents(inner_span);
4927
4928    let (output, err) = lex(source, start, &[b' ', b'\r', b'\n', b',', b'|'], &[], true);
4929    if let Some(err) = err {
4930        working_set.error(err);
4931    }
4932
4933    let mut position = 0;
4934
4935    let mut output_matches = vec![];
4936
4937    while position < output.len() {
4938        // Each match gets its own scope
4939
4940        working_set.enter_scope();
4941
4942        // First parse the pattern
4943        let mut pattern = parse_pattern(working_set, output[position].span);
4944
4945        position += 1;
4946
4947        if position >= output.len() {
4948            working_set.error(ParseError::Mismatch(
4949                "=>".into(),
4950                "end of input".into(),
4951                Span::new(output[position - 1].span.end, output[position - 1].span.end),
4952            ));
4953
4954            working_set.exit_scope();
4955            break;
4956        }
4957
4958        let mut connector = working_set.get_span_contents(output[position].span);
4959
4960        // Multiple patterns connected by '|'
4961        if connector == b"|" && position < output.len() {
4962            let mut or_pattern = vec![pattern];
4963
4964            while connector == b"|" && position < output.len() {
4965                connector = b"";
4966
4967                position += 1;
4968
4969                if position >= output.len() {
4970                    working_set.error(ParseError::Mismatch(
4971                        "pattern".into(),
4972                        "end of input".into(),
4973                        Span::new(output[position - 1].span.end, output[position - 1].span.end),
4974                    ));
4975                    break;
4976                }
4977
4978                let pattern = parse_pattern(working_set, output[position].span);
4979                or_pattern.push(pattern);
4980
4981                position += 1;
4982                if position >= output.len() {
4983                    working_set.error(ParseError::Mismatch(
4984                        "=>".into(),
4985                        "end of input".into(),
4986                        Span::new(output[position - 1].span.end, output[position - 1].span.end),
4987                    ));
4988                    break;
4989                } else {
4990                    connector = working_set.get_span_contents(output[position].span);
4991                }
4992            }
4993
4994            let start = or_pattern
4995                .first()
4996                .expect("internal error: unexpected state of or-pattern")
4997                .span
4998                .start;
4999            let end = or_pattern
5000                .last()
5001                .expect("internal error: unexpected state of or-pattern")
5002                .span
5003                .end;
5004
5005            pattern = MatchPattern {
5006                pattern: Pattern::Or(or_pattern),
5007                guard: None,
5008                span: Span::new(start, end),
5009            }
5010        // A match guard
5011        } else if connector == b"if" {
5012            let if_end = {
5013                let end = output[position].span.end;
5014                Span::new(end, end)
5015            };
5016
5017            position += 1;
5018
5019            let mk_err = || ParseError::LabeledErrorWithHelp {
5020                error: "Match guard without an expression".into(),
5021                label: "expected an expression".into(),
5022                help: "The `if` keyword must be followed with an expression".into(),
5023                span: if_end,
5024            };
5025
5026            if output.get(position).is_none() {
5027                working_set.error(mk_err());
5028                return garbage(working_set, span);
5029            };
5030
5031            let (tokens, found) = if let Some((pos, _)) = output[position..]
5032                .iter()
5033                .find_position(|t| working_set.get_span_contents(t.span) == b"=>")
5034            {
5035                if position + pos == position {
5036                    working_set.error(mk_err());
5037                    return garbage(working_set, span);
5038                }
5039
5040                (&output[position..position + pos], true)
5041            } else {
5042                (&output[position..], false)
5043            };
5044
5045            let mut start = 0;
5046            let guard = parse_multispan_value(
5047                working_set,
5048                &tokens.iter().map(|tok| tok.span).collect_vec(),
5049                &mut start,
5050                &SyntaxShape::MathExpression,
5051            );
5052
5053            pattern.guard = Some(Box::new(guard));
5054            position += if found { start + 1 } else { start };
5055            connector = working_set.get_span_contents(output[position].span);
5056        }
5057        // Then the `=>` arrow
5058        if connector != b"=>" {
5059            working_set.error(ParseError::Mismatch(
5060                "=>".into(),
5061                "end of input".into(),
5062                Span::new(output[position - 1].span.end, output[position - 1].span.end),
5063            ));
5064        } else {
5065            position += 1;
5066        }
5067
5068        // Finally, the value/expression/block that we will run to produce the result
5069        if position >= output.len() {
5070            working_set.error(ParseError::Mismatch(
5071                "match result".into(),
5072                "end of input".into(),
5073                Span::new(output[position - 1].span.end, output[position - 1].span.end),
5074            ));
5075
5076            working_set.exit_scope();
5077            break;
5078        }
5079
5080        let result = parse_multispan_value(
5081            working_set,
5082            &[output[position].span],
5083            &mut 0,
5084            &SyntaxShape::OneOf(vec![SyntaxShape::Block, SyntaxShape::Expression]),
5085        );
5086        position += 1;
5087        if is_closed {
5088            working_set.exit_scope();
5089        }
5090
5091        output_matches.push((pattern, result));
5092    }
5093
5094    Expression::new(
5095        working_set,
5096        Expr::MatchBlock(output_matches),
5097        span,
5098        Type::Any,
5099    )
5100}
5101
5102pub fn parse_closure_expression(
5103    working_set: &mut StateWorkingSet,
5104    shape: &SyntaxShape,
5105    span: Span,
5106) -> Expression {
5107    trace!("parsing: closure expression");
5108
5109    let bytes = working_set.get_span_contents(span);
5110
5111    let mut start = span.start;
5112    let mut end = span.end;
5113    let mut is_closed = true;
5114
5115    if bytes.starts_with(b"{") {
5116        start += 1;
5117    } else {
5118        working_set.error(ParseError::Expected("closure", span));
5119        return garbage(working_set, span);
5120    }
5121    if bytes.ends_with(b"}") {
5122        end -= 1;
5123    } else {
5124        working_set.error(ParseError::Unclosed("}".into(), Span::new(end, end)));
5125        is_closed = false;
5126    }
5127
5128    let inner_span = Span::new(start, end);
5129
5130    let source = working_set.get_span_contents(inner_span);
5131
5132    let (output, err) = lex(source, start, &[], &[], false);
5133    if let Some(err) = err {
5134        working_set.error(err);
5135    }
5136
5137    working_set.enter_scope();
5138
5139    // Check to see if we have parameters
5140    let (signature, amt_to_skip): (Option<(Box<Signature>, Span)>, usize) = match output.first() {
5141        Some(Token {
5142            contents: TokenContents::Pipe,
5143            span,
5144        }) => {
5145            // We've found a parameter list
5146            let start_point = span.start;
5147            let mut token_iter = output.iter().enumerate().skip(1);
5148            let mut end_span = None;
5149            let mut amt_to_skip = 1;
5150
5151            for token in &mut token_iter {
5152                if let Token {
5153                    contents: TokenContents::Pipe,
5154                    span,
5155                } = token.1
5156                {
5157                    end_span = Some(span);
5158                    amt_to_skip += token.0;
5159                    break;
5160                }
5161            }
5162
5163            let end_point = if let Some(span) = end_span {
5164                span.end
5165            } else {
5166                working_set.error(ParseError::Unclosed("|".into(), Span::new(end, end)));
5167                end
5168            };
5169
5170            let signature_span = Span::new(start_point, end_point);
5171            let signature = parse_signature_helper(working_set, signature_span);
5172
5173            (Some((signature, signature_span)), amt_to_skip)
5174        }
5175        Some(Token {
5176            contents: TokenContents::PipePipe,
5177            span,
5178        }) => (
5179            Some((Box::new(Signature::new("closure".to_string())), *span)),
5180            1,
5181        ),
5182        _ => (None, 0),
5183    };
5184
5185    // TODO: Finish this
5186    if let SyntaxShape::Closure(Some(v)) = shape
5187        && let Some((sig, sig_span)) = &signature
5188    {
5189        if sig.num_positionals() > v.len() {
5190            working_set.error(ParseError::ExpectedWithStringMsg(
5191                format!(
5192                    "{} closure parameter{}",
5193                    v.len(),
5194                    if v.len() > 1 { "s" } else { "" }
5195                ),
5196                *sig_span,
5197            ));
5198        }
5199
5200        for (expected, PositionalArg { name, shape, .. }) in
5201            v.iter().zip(sig.required_positional.iter())
5202        {
5203            if expected != shape && *shape != SyntaxShape::Any {
5204                working_set.error(ParseError::ParameterMismatchType(
5205                    name.to_owned(),
5206                    expected.to_string(),
5207                    shape.to_string(),
5208                    *sig_span,
5209                ));
5210            }
5211        }
5212    }
5213
5214    let mut output = parse_block(working_set, &output[amt_to_skip..], span, false, false);
5215
5216    // NOTE: closures need to be compiled eagerly due to these reasons:
5217    //  - their `Block`s (which contains their `IrBlock`) are stored in the working_set
5218    //  - Ir compiler does not have mutable access to the working_set and can't attach `IrBlock`s
5219    //  to existing `Block`s
5220    // so they can't be compiled as part of their parent `Block`'s compilation
5221    //
5222    // If the compiler used a mechanism similar to the `EngineState`/`StateWorkingSet` divide, we
5223    // could defer all compilation and apply the generated delta to `StateWorkingSet` afterwards.
5224    if working_set.parse_errors.is_empty() {
5225        compile_block(working_set, &mut output);
5226    }
5227
5228    if let Some(signature) = signature {
5229        output.signature = signature.0;
5230    }
5231
5232    output.span = Some(span);
5233
5234    if is_closed {
5235        working_set.exit_scope();
5236    }
5237
5238    let block_id = working_set.add_block(Arc::new(output));
5239
5240    Expression::new(working_set, Expr::Closure(block_id), span, Type::Closure)
5241}
5242
5243pub fn parse_value(
5244    working_set: &mut StateWorkingSet,
5245    span: Span,
5246    shape: &SyntaxShape,
5247) -> Expression {
5248    trace!("parsing: value: {shape}");
5249
5250    let bytes = working_set.get_span_contents(span);
5251
5252    if bytes.is_empty() {
5253        working_set.error(ParseError::IncompleteParser(span));
5254        return garbage(working_set, span);
5255    }
5256
5257    // Check for reserved keyword values
5258    match bytes {
5259        b"true" => {
5260            if matches!(shape, SyntaxShape::Boolean) || matches!(shape, SyntaxShape::Any) {
5261                return Expression::new(working_set, Expr::Bool(true), span, Type::Bool);
5262            } else {
5263                working_set.error(ParseError::ExpectedWithStringMsg(shape.to_string(), span));
5264                return Expression::garbage(working_set, span);
5265            }
5266        }
5267        b"false" => {
5268            if matches!(shape, SyntaxShape::Boolean) || matches!(shape, SyntaxShape::Any) {
5269                return Expression::new(working_set, Expr::Bool(false), span, Type::Bool);
5270            } else {
5271                working_set.error(ParseError::ExpectedWithStringMsg(shape.to_string(), span));
5272                return Expression::garbage(working_set, span);
5273            }
5274        }
5275        b"null" => {
5276            return Expression::new(working_set, Expr::Nothing, span, Type::Nothing);
5277        }
5278        b"-inf" | b"inf" | b"NaN" => {
5279            return parse_float(working_set, span);
5280        }
5281        _ => {}
5282    }
5283
5284    match bytes[0] {
5285        b'$' => return parse_dollar_expr(working_set, span),
5286        b'(' => return parse_paren_expr(working_set, span, shape),
5287        b'{' => return parse_brace_expr(working_set, span, shape),
5288        b'[' => match shape {
5289            SyntaxShape::Any
5290            | SyntaxShape::List(_)
5291            | SyntaxShape::Table(_)
5292            | SyntaxShape::Signature
5293            | SyntaxShape::Filepath
5294            | SyntaxShape::String
5295            | SyntaxShape::GlobPattern
5296            | SyntaxShape::ExternalArgument => {}
5297            SyntaxShape::OneOf(possible_shapes) => {
5298                if !possible_shapes
5299                    .iter()
5300                    .any(|s| matches!(s, SyntaxShape::List(_)))
5301                {
5302                    working_set.error(ParseError::Expected("non-[] value", span));
5303                    return Expression::garbage(working_set, span);
5304                }
5305            }
5306            _ => {
5307                working_set.error(ParseError::Expected("non-[] value", span));
5308                return Expression::garbage(working_set, span);
5309            }
5310        },
5311        b'r' if bytes.len() > 1 && bytes[1] == b'#' => {
5312            return parse_raw_string(working_set, span);
5313        }
5314        _ => {}
5315    }
5316
5317    match shape {
5318        SyntaxShape::Number => parse_number(working_set, span),
5319        SyntaxShape::Float => parse_float(working_set, span),
5320        SyntaxShape::Int => parse_int(working_set, span),
5321        SyntaxShape::Duration => parse_duration(working_set, span),
5322        SyntaxShape::DateTime => parse_datetime(working_set, span),
5323        SyntaxShape::Filesize => parse_filesize(working_set, span),
5324        SyntaxShape::Range => {
5325            parse_range(working_set, span).unwrap_or_else(|| garbage(working_set, span))
5326        }
5327        SyntaxShape::Filepath => parse_filepath(working_set, span),
5328        SyntaxShape::Directory => parse_directory(working_set, span),
5329        SyntaxShape::GlobPattern => parse_glob_pattern(working_set, span),
5330        SyntaxShape::String => parse_string(working_set, span),
5331        SyntaxShape::Binary => parse_binary(working_set, span),
5332        SyntaxShape::Signature => {
5333            if bytes.starts_with(b"[") {
5334                parse_signature(working_set, span)
5335            } else {
5336                working_set.error(ParseError::Expected("signature", span));
5337
5338                Expression::garbage(working_set, span)
5339            }
5340        }
5341        SyntaxShape::List(elem) => {
5342            if bytes.starts_with(b"[") {
5343                parse_table_expression(working_set, span, elem)
5344            } else {
5345                working_set.error(ParseError::Expected("list", span));
5346
5347                Expression::garbage(working_set, span)
5348            }
5349        }
5350        SyntaxShape::Table(_) => {
5351            if bytes.starts_with(b"[") {
5352                parse_table_expression(working_set, span, &SyntaxShape::Any)
5353            } else {
5354                working_set.error(ParseError::Expected("table", span));
5355
5356                Expression::garbage(working_set, span)
5357            }
5358        }
5359        SyntaxShape::CellPath => parse_simple_cell_path(working_set, span),
5360        SyntaxShape::Boolean => {
5361            // Redundant, though we catch bad boolean parses here
5362            if bytes == b"true" || bytes == b"false" {
5363                Expression::new(working_set, Expr::Bool(true), span, Type::Bool)
5364            } else {
5365                working_set.error(ParseError::Expected("bool", span));
5366
5367                Expression::garbage(working_set, span)
5368            }
5369        }
5370
5371        // Be sure to return ParseError::Expected(..) if invoked for one of these shapes, but lex
5372        // stream doesn't start with '{'} -- parsing in SyntaxShape::Any arm depends on this error variant.
5373        SyntaxShape::Block | SyntaxShape::Closure(..) | SyntaxShape::Record(_) => {
5374            working_set.error(ParseError::Expected("block, closure or record", span));
5375
5376            Expression::garbage(working_set, span)
5377        }
5378
5379        SyntaxShape::ExternalArgument => parse_regular_external_arg(working_set, span),
5380        SyntaxShape::OneOf(possible_shapes) => {
5381            parse_oneof(working_set, &[span], &mut 0, possible_shapes, false)
5382        }
5383
5384        SyntaxShape::Any => {
5385            if bytes.starts_with(b"[") {
5386                //parse_value(working_set, span, &SyntaxShape::Table)
5387                parse_full_cell_path(working_set, None, span)
5388            } else {
5389                let shapes = [
5390                    SyntaxShape::Binary,
5391                    SyntaxShape::Range,
5392                    SyntaxShape::Filesize,
5393                    SyntaxShape::Duration,
5394                    SyntaxShape::DateTime,
5395                    SyntaxShape::Int,
5396                    SyntaxShape::Number,
5397                    SyntaxShape::String,
5398                ];
5399                for shape in shapes.iter() {
5400                    let starting_error_count = working_set.parse_errors.len();
5401
5402                    let s = parse_value(working_set, span, shape);
5403
5404                    if starting_error_count == working_set.parse_errors.len() {
5405                        return s;
5406                    } else {
5407                        match working_set.parse_errors.get(starting_error_count) {
5408                            Some(
5409                                ParseError::Expected(_, _)
5410                                | ParseError::ExpectedWithStringMsg(_, _),
5411                            ) => {
5412                                working_set.parse_errors.truncate(starting_error_count);
5413                                continue;
5414                            }
5415                            _ => {
5416                                return s;
5417                            }
5418                        }
5419                    }
5420                }
5421                working_set.error(ParseError::Expected("any shape", span));
5422                garbage(working_set, span)
5423            }
5424        }
5425        x => {
5426            working_set.error(ParseError::ExpectedWithStringMsg(
5427                x.to_type().to_string(),
5428                span,
5429            ));
5430            garbage(working_set, span)
5431        }
5432    }
5433}
5434
5435pub fn parse_assignment_operator(working_set: &mut StateWorkingSet, span: Span) -> Expression {
5436    let contents = working_set.get_span_contents(span);
5437
5438    let operator = match contents {
5439        b"=" => Operator::Assignment(Assignment::Assign),
5440        b"+=" => Operator::Assignment(Assignment::AddAssign),
5441        b"-=" => Operator::Assignment(Assignment::SubtractAssign),
5442        b"*=" => Operator::Assignment(Assignment::MultiplyAssign),
5443        b"/=" => Operator::Assignment(Assignment::DivideAssign),
5444        b"++=" => Operator::Assignment(Assignment::ConcatenateAssign),
5445        _ => {
5446            working_set.error(ParseError::Expected("assignment operator", span));
5447            return garbage(working_set, span);
5448        }
5449    };
5450
5451    Expression::new(working_set, Expr::Operator(operator), span, Type::Any)
5452}
5453
5454pub fn parse_assignment_expression(
5455    working_set: &mut StateWorkingSet,
5456    spans: &[Span],
5457) -> Expression {
5458    trace!("parsing: assignment expression");
5459    let expr_span = Span::concat(spans);
5460
5461    // Assignment always has the most precedence, and its right-hand side can be a pipeline
5462    let Some(op_index) = spans
5463        .iter()
5464        .position(|span| is_assignment_operator(working_set.get_span_contents(*span)))
5465    else {
5466        working_set.error(ParseError::Expected("assignment expression", expr_span));
5467        return garbage(working_set, expr_span);
5468    };
5469
5470    let lhs_spans = &spans[0..op_index];
5471    let op_span = spans[op_index];
5472    let rhs_spans = &spans[(op_index + 1)..];
5473
5474    if lhs_spans.is_empty() {
5475        working_set.error(ParseError::Expected(
5476            "left hand side of assignment",
5477            op_span,
5478        ));
5479        return garbage(working_set, expr_span);
5480    }
5481
5482    if rhs_spans.is_empty() {
5483        working_set.error(ParseError::Expected(
5484            "right hand side of assignment",
5485            op_span,
5486        ));
5487        return garbage(working_set, expr_span);
5488    }
5489
5490    // Parse the lhs and operator as usual for a math expression
5491    let mut lhs = parse_expression(working_set, lhs_spans);
5492    // make sure that lhs is a mutable variable.
5493    match &lhs.expr {
5494        Expr::FullCellPath(p) => {
5495            if let Expr::Var(var_id) = p.head.expr
5496                && var_id != nu_protocol::ENV_VARIABLE_ID
5497                && !working_set.get_variable(var_id).mutable
5498            {
5499                working_set.error(ParseError::AssignmentRequiresMutableVar(lhs.span))
5500            }
5501        }
5502        _ => working_set.error(ParseError::AssignmentRequiresVar(lhs.span)),
5503    }
5504
5505    let mut operator = parse_assignment_operator(working_set, op_span);
5506
5507    // Re-parse the right-hand side as a subexpression
5508    let rhs_span = Span::concat(rhs_spans);
5509
5510    let (rhs_tokens, rhs_error) = lex(
5511        working_set.get_span_contents(rhs_span),
5512        rhs_span.start,
5513        &[],
5514        &[],
5515        false,
5516    );
5517    working_set.parse_errors.extend(rhs_error);
5518
5519    trace!("parsing: assignment right-hand side subexpression");
5520    let rhs_block = parse_block(working_set, &rhs_tokens, rhs_span, false, true);
5521    let rhs_ty = rhs_block.output_type();
5522
5523    // TEMP: double-check that if the RHS block starts with an external call, it must start with a
5524    // caret. This is to mitigate the change in assignment parsing introduced in 0.97.0 which could
5525    // result in unintentional execution of commands.
5526    if let Some(Expr::ExternalCall(head, ..)) = rhs_block
5527        .pipelines
5528        .first()
5529        .and_then(|pipeline| pipeline.elements.first())
5530        .map(|element| &element.expr.expr)
5531    {
5532        let contents = working_set.get_span_contents(Span {
5533            start: head.span.start - 1,
5534            end: head.span.end,
5535        });
5536        if !contents.starts_with(b"^") {
5537            working_set.parse_errors.push(ParseError::LabeledErrorWithHelp {
5538                error: "External command calls must be explicit in assignments".into(),
5539                label: "add a caret (^) before the command name if you intended to run and capture its output".into(),
5540                help: "the parsing of assignments was changed in 0.97.0, and this would have previously been treated as a string. Alternatively, quote the string with single or double quotes to avoid it being interpreted as a command name. This restriction may be removed in a future release.".into(),
5541                span: head.span,
5542            });
5543        }
5544    }
5545
5546    let rhs_block_id = working_set.add_block(Arc::new(rhs_block));
5547    let mut rhs = Expression::new(
5548        working_set,
5549        Expr::Subexpression(rhs_block_id),
5550        rhs_span,
5551        rhs_ty,
5552    );
5553
5554    let (result_ty, err) = math_result_type(working_set, &mut lhs, &mut operator, &mut rhs);
5555    if let Some(err) = err {
5556        working_set.parse_errors.push(err);
5557    }
5558
5559    Expression::new(
5560        working_set,
5561        Expr::BinaryOp(Box::new(lhs), Box::new(operator), Box::new(rhs)),
5562        expr_span,
5563        result_ty,
5564    )
5565}
5566
5567pub fn parse_operator(working_set: &mut StateWorkingSet, span: Span) -> Expression {
5568    let contents = working_set.get_span_contents(span);
5569
5570    let operator = match contents {
5571        b"==" => Operator::Comparison(Comparison::Equal),
5572        b"!=" => Operator::Comparison(Comparison::NotEqual),
5573        b"<" => Operator::Comparison(Comparison::LessThan),
5574        b"<=" => Operator::Comparison(Comparison::LessThanOrEqual),
5575        b">" => Operator::Comparison(Comparison::GreaterThan),
5576        b">=" => Operator::Comparison(Comparison::GreaterThanOrEqual),
5577        b"=~" | b"like" => Operator::Comparison(Comparison::RegexMatch),
5578        b"!~" | b"not-like" => Operator::Comparison(Comparison::NotRegexMatch),
5579        b"in" => Operator::Comparison(Comparison::In),
5580        b"not-in" => Operator::Comparison(Comparison::NotIn),
5581        b"has" => Operator::Comparison(Comparison::Has),
5582        b"not-has" => Operator::Comparison(Comparison::NotHas),
5583        b"starts-with" => Operator::Comparison(Comparison::StartsWith),
5584        b"not-starts-with" => Operator::Comparison(Comparison::NotStartsWith),
5585        b"ends-with" => Operator::Comparison(Comparison::EndsWith),
5586        b"not-ends-with" => Operator::Comparison(Comparison::NotEndsWith),
5587        b"+" => Operator::Math(Math::Add),
5588        b"-" => Operator::Math(Math::Subtract),
5589        b"*" => Operator::Math(Math::Multiply),
5590        b"/" => Operator::Math(Math::Divide),
5591        b"//" => Operator::Math(Math::FloorDivide),
5592        b"mod" => Operator::Math(Math::Modulo),
5593        b"**" => Operator::Math(Math::Pow),
5594        b"++" => Operator::Math(Math::Concatenate),
5595        b"bit-or" => Operator::Bits(Bits::BitOr),
5596        b"bit-xor" => Operator::Bits(Bits::BitXor),
5597        b"bit-and" => Operator::Bits(Bits::BitAnd),
5598        b"bit-shl" => Operator::Bits(Bits::ShiftLeft),
5599        b"bit-shr" => Operator::Bits(Bits::ShiftRight),
5600        b"or" => Operator::Boolean(Boolean::Or),
5601        b"xor" => Operator::Boolean(Boolean::Xor),
5602        b"and" => Operator::Boolean(Boolean::And),
5603        // WARNING: not actual operators below! Error handling only
5604        pow @ (b"^" | b"pow") => {
5605            working_set.error(ParseError::UnknownOperator(
5606                match pow {
5607                    b"^" => "^",
5608                    b"pow" => "pow",
5609                    _ => unreachable!(),
5610                },
5611                "Use '**' for exponentiation or 'bit-xor' for bitwise XOR.",
5612                span,
5613            ));
5614            return garbage(working_set, span);
5615        }
5616        equality @ (b"is" | b"===") => {
5617            working_set.error(ParseError::UnknownOperator(
5618                match equality {
5619                    b"is" => "is",
5620                    b"===" => "===",
5621                    _ => unreachable!(),
5622                },
5623                "Did you mean '=='?",
5624                span,
5625            ));
5626            return garbage(working_set, span);
5627        }
5628        b"contains" => {
5629            working_set.error(ParseError::UnknownOperator(
5630                "contains",
5631                "Did you mean 'has'?",
5632                span,
5633            ));
5634            return garbage(working_set, span);
5635        }
5636        b"%" => {
5637            working_set.error(ParseError::UnknownOperator(
5638                "%",
5639                "Did you mean 'mod'?",
5640                span,
5641            ));
5642            return garbage(working_set, span);
5643        }
5644        b"&" => {
5645            working_set.error(ParseError::UnknownOperator(
5646                "&",
5647                "Did you mean 'bit-and'?",
5648                span,
5649            ));
5650            return garbage(working_set, span);
5651        }
5652        b"<<" => {
5653            working_set.error(ParseError::UnknownOperator(
5654                "<<",
5655                "Did you mean 'bit-shl'?",
5656                span,
5657            ));
5658            return garbage(working_set, span);
5659        }
5660        b">>" => {
5661            working_set.error(ParseError::UnknownOperator(
5662                ">>",
5663                "Did you mean 'bit-shr'?",
5664                span,
5665            ));
5666            return garbage(working_set, span);
5667        }
5668        bits @ (b"bits-and" | b"bits-xor" | b"bits-or" | b"bits-shl" | b"bits-shr") => {
5669            working_set.error(ParseError::UnknownOperator(
5670                match bits {
5671                    b"bits-and" => "bits-and",
5672                    b"bits-xor" => "bits-xor",
5673                    b"bits-or" => "bits-or",
5674                    b"bits-shl" => "bits-shl",
5675                    b"bits-shr" => "bits-shr",
5676                    _ => unreachable!(),
5677                },
5678                match bits {
5679                    b"bits-and" => "Did you mean 'bit-and'?",
5680                    b"bits-xor" => "Did you mean 'bit-xor'?",
5681                    b"bits-or" => "Did you mean 'bit-or'?",
5682                    b"bits-shl" => "Did you mean 'bit-shl'?",
5683                    b"bits-shr" => "Did you mean 'bit-shr'?",
5684                    _ => unreachable!(),
5685                },
5686                span,
5687            ));
5688            return garbage(working_set, span);
5689        }
5690        op if is_assignment_operator(op) => {
5691            working_set.error(ParseError::Expected("a non-assignment operator", span));
5692            return garbage(working_set, span);
5693        }
5694        _ => {
5695            working_set.error(ParseError::Expected("operator", span));
5696            return garbage(working_set, span);
5697        }
5698    };
5699
5700    Expression::new(working_set, Expr::Operator(operator), span, Type::Any)
5701}
5702
5703pub fn parse_math_expression(
5704    working_set: &mut StateWorkingSet,
5705    spans: &[Span],
5706    lhs_row_var_id: Option<VarId>,
5707) -> Expression {
5708    trace!("parsing: math expression");
5709
5710    // As the expr_stack grows, we increase the required precedence to grow larger
5711    // If, at any time, the operator we're looking at is the same or lower precedence
5712    // of what is in the expression stack, we collapse the expression stack.
5713    //
5714    // This leads to an expression stack that grows under increasing precedence and collapses
5715    // under decreasing/sustained precedence
5716    //
5717    // The end result is a stack that we can fold into binary operations as right associations
5718    // safely.
5719
5720    let mut expr_stack: Vec<Expression> = vec![];
5721
5722    let mut idx = 0;
5723    let mut last_prec = u8::MAX;
5724
5725    let first_span = working_set.get_span_contents(spans[0]);
5726
5727    let mut not_start_spans = vec![];
5728
5729    if first_span == b"if" || first_span == b"match" {
5730        // If expression
5731        if spans.len() > 1 {
5732            return parse_call(working_set, spans, spans[0]);
5733        } else {
5734            working_set.error(ParseError::Expected(
5735                "expression",
5736                Span::new(spans[0].end, spans[0].end),
5737            ));
5738            return garbage(working_set, spans[0]);
5739        }
5740    } else if first_span == b"not" {
5741        not_start_spans.push(spans[idx].start);
5742        idx += 1;
5743        while idx < spans.len() {
5744            let next_value = working_set.get_span_contents(spans[idx]);
5745
5746            if next_value == b"not" {
5747                not_start_spans.push(spans[idx].start);
5748                idx += 1;
5749            } else {
5750                break;
5751            }
5752        }
5753
5754        if idx == spans.len() {
5755            working_set.error(ParseError::Expected(
5756                "expression",
5757                Span::new(spans[idx - 1].end, spans[idx - 1].end),
5758            ));
5759            return garbage(working_set, spans[idx - 1]);
5760        }
5761    }
5762
5763    let mut lhs = parse_value(working_set, spans[idx], &SyntaxShape::Any);
5764
5765    for not_start_span in not_start_spans.iter().rev() {
5766        lhs = Expression::new(
5767            working_set,
5768            Expr::UnaryNot(Box::new(lhs)),
5769            Span::new(*not_start_span, spans[idx].end),
5770            Type::Bool,
5771        );
5772    }
5773    not_start_spans.clear();
5774
5775    idx += 1;
5776
5777    if idx >= spans.len() {
5778        // We already found the one part of our expression, so let's expand
5779        if let Some(row_var_id) = lhs_row_var_id {
5780            expand_to_cell_path(working_set, &mut lhs, row_var_id);
5781        }
5782    }
5783
5784    expr_stack.push(lhs);
5785
5786    while idx < spans.len() {
5787        let op = parse_operator(working_set, spans[idx]);
5788
5789        let op_prec = op.precedence();
5790
5791        idx += 1;
5792
5793        if idx == spans.len() {
5794            // Handle broken math expr `1 +` etc
5795            working_set.error(ParseError::IncompleteMathExpression(spans[idx - 1]));
5796
5797            expr_stack.push(Expression::garbage(working_set, spans[idx - 1]));
5798            let missing_span = Span::new(spans[idx - 1].end, spans[idx - 1].end);
5799            expr_stack.push(Expression::garbage(working_set, missing_span));
5800
5801            break;
5802        }
5803
5804        let content = working_set.get_span_contents(spans[idx]);
5805        // allow `if` to be a special value for assignment.
5806
5807        if content == b"if" || content == b"match" {
5808            let rhs = parse_call(working_set, &spans[idx..], spans[0]);
5809            expr_stack.push(op);
5810            expr_stack.push(rhs);
5811            break;
5812        } else if content == b"not" {
5813            not_start_spans.push(spans[idx].start);
5814            idx += 1;
5815            while idx < spans.len() {
5816                let next_value = working_set.get_span_contents(spans[idx]);
5817
5818                if next_value == b"not" {
5819                    not_start_spans.push(spans[idx].start);
5820                    idx += 1;
5821                } else {
5822                    break;
5823                }
5824            }
5825
5826            if idx == spans.len() {
5827                working_set.error(ParseError::Expected(
5828                    "expression",
5829                    Span::new(spans[idx - 1].end, spans[idx - 1].end),
5830                ));
5831                return garbage(working_set, spans[idx - 1]);
5832            }
5833        }
5834        let mut rhs = parse_value(working_set, spans[idx], &SyntaxShape::Any);
5835
5836        for not_start_span in not_start_spans.iter().rev() {
5837            rhs = Expression::new(
5838                working_set,
5839                Expr::UnaryNot(Box::new(rhs)),
5840                Span::new(*not_start_span, spans[idx].end),
5841                Type::Bool,
5842            );
5843        }
5844        not_start_spans.clear();
5845
5846        // Parsing power must be right-associative unlike most operations which are left
5847        // Hence, we should not collapse if the last and current operations are both power
5848        let is_left_associative =
5849            op.expr != Expr::Operator(Operator::Math(Math::Pow)) && op_prec <= last_prec;
5850
5851        while is_left_associative && expr_stack.len() > 1 {
5852            // Collapse the right associated operations first
5853            // so that we can get back to a stack with a lower precedence
5854            let mut rhs = expr_stack
5855                .pop()
5856                .expect("internal error: expression stack empty");
5857            let mut op = expr_stack
5858                .pop()
5859                .expect("internal error: expression stack empty");
5860
5861            last_prec = op.precedence();
5862
5863            if last_prec < op_prec {
5864                expr_stack.push(op);
5865                expr_stack.push(rhs);
5866                break;
5867            }
5868
5869            let mut lhs = expr_stack
5870                .pop()
5871                .expect("internal error: expression stack empty");
5872
5873            if let Some(row_var_id) = lhs_row_var_id {
5874                expand_to_cell_path(working_set, &mut lhs, row_var_id);
5875            }
5876
5877            let (result_ty, err) = math_result_type(working_set, &mut lhs, &mut op, &mut rhs);
5878            if let Some(err) = err {
5879                working_set.error(err);
5880            }
5881
5882            let op_span = Span::append(lhs.span, rhs.span);
5883            expr_stack.push(Expression::new(
5884                working_set,
5885                Expr::BinaryOp(Box::new(lhs), Box::new(op), Box::new(rhs)),
5886                op_span,
5887                result_ty,
5888            ));
5889        }
5890        expr_stack.push(op);
5891        expr_stack.push(rhs);
5892
5893        last_prec = op_prec;
5894
5895        idx += 1;
5896    }
5897
5898    while expr_stack.len() != 1 {
5899        let mut rhs = expr_stack
5900            .pop()
5901            .expect("internal error: expression stack empty");
5902        let mut op = expr_stack
5903            .pop()
5904            .expect("internal error: expression stack empty");
5905        let mut lhs = expr_stack
5906            .pop()
5907            .expect("internal error: expression stack empty");
5908
5909        if let Some(row_var_id) = lhs_row_var_id {
5910            expand_to_cell_path(working_set, &mut lhs, row_var_id);
5911        }
5912
5913        let (result_ty, err) = math_result_type(working_set, &mut lhs, &mut op, &mut rhs);
5914        if let Some(err) = err {
5915            working_set.error(err)
5916        }
5917
5918        let binary_op_span = Span::append(lhs.span, rhs.span);
5919        expr_stack.push(Expression::new(
5920            working_set,
5921            Expr::BinaryOp(Box::new(lhs), Box::new(op), Box::new(rhs)),
5922            binary_op_span,
5923            result_ty,
5924        ));
5925    }
5926
5927    expr_stack
5928        .pop()
5929        .expect("internal error: expression stack empty")
5930}
5931
5932pub fn parse_expression(working_set: &mut StateWorkingSet, spans: &[Span]) -> Expression {
5933    trace!("parsing: expression");
5934
5935    let mut pos = 0;
5936    let mut shorthand = vec![];
5937
5938    while pos < spans.len() {
5939        // Check if there is any environment shorthand
5940        let name = working_set.get_span_contents(spans[pos]);
5941
5942        let split: Vec<_> = name.splitn(2, |x| *x == b'=').collect();
5943        if split.len() != 2 || !is_env_variable_name(split[0]) {
5944            break;
5945        }
5946
5947        let point = split[0].len() + 1;
5948        let starting_error_count = working_set.parse_errors.len();
5949
5950        let rhs = if spans[pos].start + point < spans[pos].end {
5951            let rhs_span = Span::new(spans[pos].start + point, spans[pos].end);
5952            if split[1].starts_with(b"$") {
5953                parse_dollar_expr(working_set, rhs_span)
5954            } else {
5955                parse_string_strict(working_set, rhs_span)
5956            }
5957        } else {
5958            Expression::new(
5959                working_set,
5960                Expr::String(String::new()),
5961                Span::unknown(),
5962                Type::Nothing,
5963            )
5964        };
5965
5966        let lhs_span = Span::new(spans[pos].start, spans[pos].start + point - 1);
5967        let lhs = parse_string_strict(working_set, lhs_span);
5968
5969        if starting_error_count == working_set.parse_errors.len() {
5970            shorthand.push((lhs, rhs));
5971            pos += 1;
5972        } else {
5973            working_set.parse_errors.truncate(starting_error_count);
5974            break;
5975        }
5976    }
5977
5978    if pos == spans.len() {
5979        working_set.error(ParseError::UnknownCommand(spans[0]));
5980        return garbage(working_set, Span::concat(spans));
5981    }
5982
5983    let output = if spans[pos..]
5984        .iter()
5985        .any(|span| is_assignment_operator(working_set.get_span_contents(*span)))
5986    {
5987        parse_assignment_expression(working_set, &spans[pos..])
5988    } else if is_math_expression_like(working_set, spans[pos]) {
5989        parse_math_expression(working_set, &spans[pos..], None)
5990    } else {
5991        let bytes = working_set.get_span_contents(spans[pos]).to_vec();
5992
5993        // For now, check for special parses of certain keywords
5994        match bytes.as_slice() {
5995            b"def" | b"extern" | b"for" | b"module" | b"use" | b"source" | b"alias" | b"export"
5996            | b"export-env" | b"hide" => {
5997                working_set.error(ParseError::BuiltinCommandInPipeline(
5998                    String::from_utf8(bytes)
5999                        .expect("builtin commands bytes should be able to convert to string"),
6000                    spans[0],
6001                ));
6002
6003                parse_call(working_set, &spans[pos..], spans[0])
6004            }
6005            b"let" | b"const" | b"mut" => {
6006                working_set.error(ParseError::AssignInPipeline(
6007                    String::from_utf8(bytes)
6008                        .expect("builtin commands bytes should be able to convert to string"),
6009                    String::from_utf8_lossy(match spans.len() {
6010                        1..=3 => b"value",
6011                        _ => working_set.get_span_contents(spans[3]),
6012                    })
6013                    .to_string(),
6014                    String::from_utf8_lossy(match spans.len() {
6015                        1 => b"variable",
6016                        _ => working_set.get_span_contents(spans[1]),
6017                    })
6018                    .to_string(),
6019                    spans[0],
6020                ));
6021                parse_call(working_set, &spans[pos..], spans[0])
6022            }
6023            b"overlay" => {
6024                if spans.len() > 1 && working_set.get_span_contents(spans[1]) == b"list" {
6025                    // whitelist 'overlay list'
6026                    parse_call(working_set, &spans[pos..], spans[0])
6027                } else {
6028                    working_set.error(ParseError::BuiltinCommandInPipeline(
6029                        "overlay".into(),
6030                        spans[0],
6031                    ));
6032
6033                    parse_call(working_set, &spans[pos..], spans[0])
6034                }
6035            }
6036            b"where" => parse_where_expr(working_set, &spans[pos..]),
6037            #[cfg(feature = "plugin")]
6038            b"plugin" => {
6039                if spans.len() > 1 && working_set.get_span_contents(spans[1]) == b"use" {
6040                    // only 'plugin use' is banned
6041                    working_set.error(ParseError::BuiltinCommandInPipeline(
6042                        "plugin use".into(),
6043                        spans[0],
6044                    ));
6045                }
6046
6047                parse_call(working_set, &spans[pos..], spans[0])
6048            }
6049
6050            _ => parse_call(working_set, &spans[pos..], spans[0]),
6051        }
6052    };
6053
6054    if !shorthand.is_empty() {
6055        let with_env = working_set.find_decl(b"with-env");
6056        if let Some(decl_id) = with_env {
6057            let mut block = Block::default();
6058            let ty = output.ty.clone();
6059            block.pipelines = vec![Pipeline::from_vec(vec![output])];
6060            block.span = Some(Span::concat(spans));
6061
6062            compile_block(working_set, &mut block);
6063
6064            let block_id = working_set.add_block(Arc::new(block));
6065
6066            let mut env_vars = vec![];
6067            for sh in shorthand {
6068                env_vars.push(RecordItem::Pair(sh.0, sh.1));
6069            }
6070
6071            let arguments = vec![
6072                Argument::Positional(Expression::new(
6073                    working_set,
6074                    Expr::Record(env_vars),
6075                    Span::concat(&spans[..pos]),
6076                    Type::Any,
6077                )),
6078                Argument::Positional(Expression::new(
6079                    working_set,
6080                    Expr::Closure(block_id),
6081                    Span::concat(&spans[pos..]),
6082                    Type::Closure,
6083                )),
6084            ];
6085
6086            let expr = Expr::Call(Box::new(Call {
6087                head: Span::unknown(),
6088                decl_id,
6089                arguments,
6090                parser_info: HashMap::new(),
6091            }));
6092
6093            Expression::new(working_set, expr, Span::concat(spans), ty)
6094        } else {
6095            output
6096        }
6097    } else {
6098        output
6099    }
6100}
6101
6102pub fn parse_builtin_commands(
6103    working_set: &mut StateWorkingSet,
6104    lite_command: &LiteCommand,
6105) -> Pipeline {
6106    trace!("parsing: builtin commands");
6107    if !is_math_expression_like(working_set, lite_command.parts[0])
6108        && !is_unaliasable_parser_keyword(working_set, &lite_command.parts)
6109    {
6110        trace!("parsing: not math expression or unaliasable parser keyword");
6111        let name = working_set.get_span_contents(lite_command.parts[0]);
6112        if let Some(decl_id) = working_set.find_decl(name) {
6113            let cmd = working_set.get_decl(decl_id);
6114            if cmd.is_alias() {
6115                // Parse keywords that can be aliased. Note that we check for "unaliasable" keywords
6116                // because alias can have any name, therefore, we can't check for "aliasable" keywords.
6117                let call_expr = parse_call(working_set, &lite_command.parts, lite_command.parts[0]);
6118
6119                if let Expression {
6120                    expr: Expr::Call(call),
6121                    ..
6122                } = call_expr
6123                {
6124                    // Apply parse keyword side effects
6125                    let cmd = working_set.get_decl(call.decl_id);
6126                    match cmd.name() {
6127                        "overlay hide" => return parse_overlay_hide(working_set, call),
6128                        "overlay new" => return parse_overlay_new(working_set, call),
6129                        "overlay use" => return parse_overlay_use(working_set, call),
6130                        _ => { /* this alias is not a parser keyword */ }
6131                    }
6132                }
6133            }
6134        }
6135    }
6136
6137    trace!("parsing: checking for keywords");
6138    let name = lite_command
6139        .command_parts()
6140        .first()
6141        .map(|s| working_set.get_span_contents(*s))
6142        .unwrap_or(b"");
6143
6144    match name {
6145        // `parse_def` and `parse_extern` work both with and without attributes
6146        b"def" => parse_def(working_set, lite_command, None).0,
6147        b"extern" => parse_extern(working_set, lite_command, None),
6148        // `parse_export_in_block` also handles attributes by itself
6149        b"export" => parse_export_in_block(working_set, lite_command),
6150        b"export-env" => parse_export_env(working_set, &lite_command.parts).0,
6151        // Other definitions can't have attributes, so we handle attributes here with parse_attribute_block
6152        _ if lite_command.has_attributes() => parse_attribute_block(working_set, lite_command),
6153        b"let" => parse_let(
6154            working_set,
6155            &lite_command
6156                .parts_including_redirection()
6157                .collect::<Vec<Span>>(),
6158        ),
6159        b"const" => parse_const(working_set, &lite_command.parts).0,
6160        b"mut" => parse_mut(
6161            working_set,
6162            &lite_command
6163                .parts_including_redirection()
6164                .collect::<Vec<Span>>(),
6165        ),
6166        b"for" => {
6167            let expr = parse_for(working_set, lite_command);
6168            Pipeline::from_vec(vec![expr])
6169        }
6170        b"alias" => parse_alias(working_set, lite_command, None),
6171        b"module" => parse_module(working_set, lite_command, None).0,
6172        b"use" => parse_use(working_set, lite_command, None).0,
6173        b"overlay" => {
6174            if let Some(redirection) = lite_command.redirection.as_ref() {
6175                working_set.error(redirecting_builtin_error("overlay", redirection));
6176                return garbage_pipeline(working_set, &lite_command.parts);
6177            }
6178            parse_keyword(working_set, lite_command)
6179        }
6180        b"source" | b"source-env" => parse_source(working_set, lite_command),
6181        b"hide" => parse_hide(working_set, lite_command),
6182        b"where" => parse_where(working_set, lite_command),
6183        // Only "plugin use" is a keyword
6184        #[cfg(feature = "plugin")]
6185        b"plugin"
6186            if lite_command
6187                .parts
6188                .get(1)
6189                .is_some_and(|span| working_set.get_span_contents(*span) == b"use") =>
6190        {
6191            if let Some(redirection) = lite_command.redirection.as_ref() {
6192                working_set.error(redirecting_builtin_error("plugin use", redirection));
6193                return garbage_pipeline(working_set, &lite_command.parts);
6194            }
6195            parse_keyword(working_set, lite_command)
6196        }
6197        _ => {
6198            let element = parse_pipeline_element(working_set, lite_command);
6199
6200            // There is still a chance to make `parse_pipeline_element` parse into
6201            // some keyword that should apply side effects first, Example:
6202            //
6203            // module a { export alias b = overlay use first.nu };
6204            // use a
6205            // a b
6206            //
6207            // In this case, `a b` will be parsed as a pipeline element, which leads
6208            // to the `overlay use` command.
6209            // In this case, we need to ensure that the side effects of these keywords
6210            // are applied.
6211            if let Expression {
6212                expr: Expr::Call(call),
6213                ..
6214            } = &element.expr
6215            {
6216                // Apply parse keyword side effects
6217                let cmd = working_set.get_decl(call.decl_id);
6218                match cmd.name() {
6219                    "overlay hide" => return parse_overlay_hide(working_set, call.clone()),
6220                    "overlay new" => return parse_overlay_new(working_set, call.clone()),
6221                    "overlay use" => return parse_overlay_use(working_set, call.clone()),
6222                    _ => { /* this alias is not a parser keyword */ }
6223                }
6224            }
6225            Pipeline {
6226                elements: vec![element],
6227            }
6228        }
6229    }
6230}
6231
6232fn check_record_key_or_value(
6233    working_set: &StateWorkingSet,
6234    expr: &Expression,
6235    position: &str,
6236) -> Option<ParseError> {
6237    let bareword_error = |string_value: &Expression| {
6238        working_set
6239            .get_span_contents(string_value.span)
6240            .iter()
6241            .find_position(|b| **b == b':')
6242            .map(|(i, _)| {
6243                let colon_position = i + string_value.span.start;
6244                ParseError::InvalidLiteral(
6245                    "colon".to_string(),
6246                    format!("bare word specifying record {position}"),
6247                    Span::new(colon_position, colon_position + 1),
6248                )
6249            })
6250    };
6251    let value_span = working_set.get_span_contents(expr.span);
6252    match expr.expr {
6253        Expr::String(_) => {
6254            if ![b'"', b'\'', b'`'].contains(&value_span[0]) {
6255                bareword_error(expr)
6256            } else {
6257                None
6258            }
6259        }
6260        Expr::StringInterpolation(ref expressions) => {
6261            if value_span[0] != b'$' {
6262                expressions
6263                    .iter()
6264                    .filter(|expr| matches!(expr.expr, Expr::String(_)))
6265                    .filter_map(bareword_error)
6266                    .next()
6267            } else {
6268                None
6269            }
6270        }
6271        _ => None,
6272    }
6273}
6274
6275pub fn parse_record(working_set: &mut StateWorkingSet, span: Span) -> Expression {
6276    let bytes = working_set.get_span_contents(span);
6277
6278    let mut start = span.start;
6279    let mut end = span.end;
6280
6281    if bytes.starts_with(b"{") {
6282        start += 1;
6283    } else {
6284        working_set.error(ParseError::Expected("{", Span::new(start, start + 1)));
6285        return garbage(working_set, span);
6286    }
6287
6288    let mut unclosed = false;
6289    let mut extra_tokens = false;
6290    if bytes.ends_with(b"}") {
6291        end -= 1;
6292    } else {
6293        unclosed = true;
6294    }
6295
6296    let inner_span = Span::new(start, end);
6297
6298    let mut lex_state = LexState {
6299        input: working_set.get_span_contents(inner_span),
6300        output: Vec::new(),
6301        error: None,
6302        span_offset: start,
6303    };
6304    while !lex_state.input.is_empty() {
6305        if let Some(ParseError::Unbalanced(left, right, _)) = lex_state.error.as_ref()
6306            && left == "{"
6307            && right == "}"
6308        {
6309            extra_tokens = true;
6310            unclosed = false;
6311            break;
6312        }
6313        let additional_whitespace = &[b'\n', b'\r', b','];
6314        if lex_n_tokens(&mut lex_state, additional_whitespace, &[b':'], true, 1) < 1 {
6315            break;
6316        };
6317        let span = lex_state
6318            .output
6319            .last()
6320            .expect("should have gotten 1 token")
6321            .span;
6322        let contents = working_set.get_span_contents(span);
6323        if contents.len() > 3
6324            && contents.starts_with(b"...")
6325            && (contents[3] == b'$' || contents[3] == b'{' || contents[3] == b'(')
6326        {
6327            // This was a spread operator, so there's no value
6328            continue;
6329        }
6330        // Get token for colon
6331        if lex_n_tokens(&mut lex_state, additional_whitespace, &[b':'], true, 1) < 1 {
6332            break;
6333        };
6334        // Get token for value
6335        if lex_n_tokens(&mut lex_state, additional_whitespace, &[], true, 1) < 1 {
6336            break;
6337        };
6338    }
6339    let (tokens, err) = (lex_state.output, lex_state.error);
6340
6341    if unclosed {
6342        working_set.error(ParseError::Unclosed("}".into(), Span::new(end, end)));
6343    } else if extra_tokens {
6344        working_set.error(ParseError::ExtraTokensAfterClosingDelimiter(Span::new(
6345            lex_state.span_offset,
6346            end,
6347        )));
6348    }
6349
6350    if let Some(err) = err {
6351        working_set.error(err);
6352    }
6353
6354    let mut output = vec![];
6355    let mut idx = 0;
6356
6357    let mut field_types = Some(vec![]);
6358    while idx < tokens.len() {
6359        let curr_span = tokens[idx].span;
6360        let curr_tok = working_set.get_span_contents(curr_span);
6361        if curr_tok.starts_with(b"...")
6362            && curr_tok.len() > 3
6363            && (curr_tok[3] == b'$' || curr_tok[3] == b'{' || curr_tok[3] == b'(')
6364        {
6365            // Parse spread operator
6366            let inner = parse_value(
6367                working_set,
6368                Span::new(curr_span.start + 3, curr_span.end),
6369                &SyntaxShape::Record(vec![]),
6370            );
6371            idx += 1;
6372
6373            match &inner.ty {
6374                Type::Record(inner_fields) => {
6375                    if let Some(fields) = &mut field_types {
6376                        for (field, ty) in inner_fields.as_ref() {
6377                            fields.push((field.clone(), ty.clone()));
6378                        }
6379                    }
6380                }
6381                _ => {
6382                    // We can't properly see all the field types
6383                    // so fall back to the Any type later
6384                    field_types = None;
6385                }
6386            }
6387            output.push(RecordItem::Spread(
6388                Span::new(curr_span.start, curr_span.start + 3),
6389                inner,
6390            ));
6391        } else {
6392            // Normal key-value pair
6393            let field_token = &tokens[idx];
6394            let field = if field_token.contents != TokenContents::Item {
6395                working_set.error(ParseError::Expected(
6396                    "item in record key position",
6397                    Span::new(field_token.span.start, field_token.span.end),
6398                ));
6399                garbage(working_set, curr_span)
6400            } else {
6401                let field = parse_value(working_set, curr_span, &SyntaxShape::Any);
6402                if let Some(error) = check_record_key_or_value(working_set, &field, "key") {
6403                    working_set.error(error);
6404                    garbage(working_set, field.span)
6405                } else {
6406                    field
6407                }
6408            };
6409
6410            idx += 1;
6411            if idx == tokens.len() {
6412                working_set.error(ParseError::Expected(
6413                    "':'",
6414                    Span::new(curr_span.end, curr_span.end),
6415                ));
6416                output.push(RecordItem::Pair(
6417                    garbage(working_set, curr_span),
6418                    garbage(working_set, Span::new(curr_span.end, curr_span.end)),
6419                ));
6420                break;
6421            }
6422            let colon_span = tokens[idx].span;
6423            let colon = working_set.get_span_contents(colon_span);
6424            idx += 1;
6425            if colon != b":" {
6426                working_set.error(ParseError::Expected(
6427                    "':'",
6428                    Span::new(colon_span.start, colon_span.start),
6429                ));
6430                output.push(RecordItem::Pair(
6431                    field,
6432                    garbage(
6433                        working_set,
6434                        Span::new(colon_span.start, tokens[tokens.len() - 1].span.end),
6435                    ),
6436                ));
6437                break;
6438            }
6439            if idx == tokens.len() {
6440                working_set.error(ParseError::Expected(
6441                    "value for record field",
6442                    Span::new(colon_span.end, colon_span.end),
6443                ));
6444                output.push(RecordItem::Pair(
6445                    garbage(working_set, Span::new(curr_span.start, colon_span.end)),
6446                    garbage(
6447                        working_set,
6448                        Span::new(colon_span.end, tokens[tokens.len() - 1].span.end),
6449                    ),
6450                ));
6451                break;
6452            }
6453
6454            let value_token = &tokens[idx];
6455            let value = if value_token.contents != TokenContents::Item {
6456                working_set.error(ParseError::Expected(
6457                    "item in record value position",
6458                    Span::new(value_token.span.start, value_token.span.end),
6459                ));
6460                garbage(
6461                    working_set,
6462                    Span::new(value_token.span.start, value_token.span.end),
6463                )
6464            } else {
6465                let value = parse_value(working_set, tokens[idx].span, &SyntaxShape::Any);
6466                if let Some(parse_error) = check_record_key_or_value(working_set, &value, "value") {
6467                    working_set.error(parse_error);
6468                    garbage(working_set, value.span)
6469                } else {
6470                    value
6471                }
6472            };
6473            idx += 1;
6474
6475            if let Some(field) = field.as_string() {
6476                if let Some(fields) = &mut field_types {
6477                    fields.push((field, value.ty.clone()));
6478                }
6479            } else {
6480                // We can't properly see all the field types
6481                // so fall back to the Any type later
6482                field_types = None;
6483            }
6484            output.push(RecordItem::Pair(field, value));
6485        }
6486    }
6487
6488    Expression::new(
6489        working_set,
6490        Expr::Record(output),
6491        span,
6492        if let Some(fields) = field_types {
6493            Type::Record(fields.into())
6494        } else {
6495            Type::Any
6496        },
6497    )
6498}
6499
6500fn parse_redirection_target(
6501    working_set: &mut StateWorkingSet,
6502    target: &LiteRedirectionTarget,
6503) -> RedirectionTarget {
6504    match target {
6505        LiteRedirectionTarget::File {
6506            connector,
6507            file,
6508            append,
6509        } => RedirectionTarget::File {
6510            expr: parse_value(working_set, *file, &SyntaxShape::Any),
6511            append: *append,
6512            span: *connector,
6513        },
6514        LiteRedirectionTarget::Pipe { connector } => RedirectionTarget::Pipe { span: *connector },
6515    }
6516}
6517
6518pub(crate) fn parse_redirection(
6519    working_set: &mut StateWorkingSet,
6520    target: &LiteRedirection,
6521) -> PipelineRedirection {
6522    match target {
6523        LiteRedirection::Single { source, target } => PipelineRedirection::Single {
6524            source: *source,
6525            target: parse_redirection_target(working_set, target),
6526        },
6527        LiteRedirection::Separate { out, err } => PipelineRedirection::Separate {
6528            out: parse_redirection_target(working_set, out),
6529            err: parse_redirection_target(working_set, err),
6530        },
6531    }
6532}
6533
6534fn parse_pipeline_element(
6535    working_set: &mut StateWorkingSet,
6536    command: &LiteCommand,
6537) -> PipelineElement {
6538    trace!("parsing: pipeline element");
6539
6540    let expr = parse_expression(working_set, &command.parts);
6541
6542    let redirection = command
6543        .redirection
6544        .as_ref()
6545        .map(|r| parse_redirection(working_set, r));
6546
6547    PipelineElement {
6548        pipe: command.pipe,
6549        expr,
6550        redirection,
6551    }
6552}
6553
6554pub(crate) fn redirecting_builtin_error(
6555    name: &'static str,
6556    redirection: &LiteRedirection,
6557) -> ParseError {
6558    match redirection {
6559        LiteRedirection::Single { target, .. } => {
6560            ParseError::RedirectingBuiltinCommand(name, target.connector(), None)
6561        }
6562        LiteRedirection::Separate { out, err } => ParseError::RedirectingBuiltinCommand(
6563            name,
6564            out.connector().min(err.connector()),
6565            Some(out.connector().max(err.connector())),
6566        ),
6567    }
6568}
6569
6570pub fn parse_pipeline(working_set: &mut StateWorkingSet, pipeline: &LitePipeline) -> Pipeline {
6571    if pipeline.commands.len() > 1 {
6572        // Parse a normal multi command pipeline
6573        let elements: Vec<_> = pipeline
6574            .commands
6575            .iter()
6576            .enumerate()
6577            .map(|(index, element)| {
6578                let element = parse_pipeline_element(working_set, element);
6579                // Handle $in for pipeline elements beyond the first one
6580                if index > 0 && element.has_in_variable(working_set) {
6581                    wrap_element_with_collect(working_set, element.clone())
6582                } else {
6583                    element
6584                }
6585            })
6586            .collect();
6587
6588        Pipeline { elements }
6589    } else {
6590        // If there's only one command in the pipeline, this could be a builtin command
6591        parse_builtin_commands(working_set, &pipeline.commands[0])
6592    }
6593}
6594
6595pub fn parse_block(
6596    working_set: &mut StateWorkingSet,
6597    tokens: &[Token],
6598    span: Span,
6599    scoped: bool,
6600    is_subexpression: bool,
6601) -> Block {
6602    let (lite_block, err) = lite_parse(tokens, working_set);
6603    if let Some(err) = err {
6604        working_set.error(err);
6605    }
6606
6607    trace!("parsing block: {lite_block:?}");
6608
6609    if scoped {
6610        working_set.enter_scope();
6611    }
6612
6613    // Pre-declare any definition so that definitions
6614    // that share the same block can see each other
6615    for pipeline in &lite_block.block {
6616        if pipeline.commands.len() == 1 {
6617            parse_def_predecl(working_set, pipeline.commands[0].command_parts())
6618        }
6619    }
6620
6621    let mut block = Block::new_with_capacity(lite_block.block.len());
6622    block.span = Some(span);
6623
6624    for lite_pipeline in &lite_block.block {
6625        let pipeline = parse_pipeline(working_set, lite_pipeline);
6626        block.pipelines.push(pipeline);
6627    }
6628
6629    // If this is not a subexpression and there are any pipelines where the first element has $in,
6630    // we can wrap the whole block in collect so that they all reference the same $in
6631    if !is_subexpression
6632        && block
6633            .pipelines
6634            .iter()
6635            .flat_map(|pipeline| pipeline.elements.first())
6636            .any(|element| element.has_in_variable(working_set))
6637    {
6638        // Move the block out to prepare it to become a subexpression
6639        let inner_block = std::mem::take(&mut block);
6640        block.span = inner_block.span;
6641        let ty = inner_block.output_type();
6642        let block_id = working_set.add_block(Arc::new(inner_block));
6643
6644        // Now wrap it in a Collect expression, and put it in the block as the only pipeline
6645        let subexpression = Expression::new(working_set, Expr::Subexpression(block_id), span, ty);
6646        let collect = wrap_expr_with_collect(working_set, subexpression);
6647
6648        block.pipelines.push(Pipeline {
6649            elements: vec![PipelineElement {
6650                pipe: None,
6651                expr: collect,
6652                redirection: None,
6653            }],
6654        });
6655    }
6656
6657    if scoped {
6658        working_set.exit_scope();
6659    }
6660
6661    let errors = type_check::check_block_input_output(working_set, &block);
6662    if !errors.is_empty() {
6663        working_set.parse_errors.extend_from_slice(&errors);
6664    }
6665
6666    block
6667}
6668
6669/// Compile an [IrBlock][nu_protocol::ir::IrBlock] for the [Block], adding a compile error on
6670/// failure.
6671///
6672/// To compile a block that's already in the [StateWorkingSet] use [compile_block_with_id]
6673pub fn compile_block(working_set: &mut StateWorkingSet<'_>, block: &mut Block) {
6674    if !working_set.parse_errors.is_empty() {
6675        // This means there might be a bug in the parser, since calling this function while parse
6676        // errors are present is a logic error. However, it's not fatal and it's best to continue
6677        // without doing anything.
6678        log::error!("compile_block called with parse errors");
6679        return;
6680    }
6681
6682    match nu_engine::compile(working_set, block) {
6683        Ok(ir_block) => {
6684            block.ir_block = Some(ir_block);
6685        }
6686        Err(err) => working_set.compile_errors.push(err),
6687    }
6688}
6689
6690/// Compile an [IrBlock][nu_protocol::ir::IrBlock] for a [Block] that's already in the
6691/// [StateWorkingSet] using its id, adding a compile error on failure.
6692pub fn compile_block_with_id(working_set: &mut StateWorkingSet<'_>, block_id: BlockId) {
6693    if !working_set.parse_errors.is_empty() {
6694        // This means there might be a bug in the parser, since calling this function while parse
6695        // errors are present is a logic error. However, it's not fatal and it's best to continue
6696        // without doing anything.
6697        log::error!("compile_block_with_id called with parse errors");
6698        return;
6699    }
6700
6701    match nu_engine::compile(working_set, working_set.get_block(block_id)) {
6702        Ok(ir_block) => {
6703            working_set.get_block_mut(block_id).ir_block = Some(ir_block);
6704        }
6705        Err(err) => {
6706            working_set.compile_errors.push(err);
6707        }
6708    };
6709}
6710
6711pub fn discover_captures_in_closure(
6712    working_set: &StateWorkingSet,
6713    block: &Block,
6714    seen: &mut Vec<VarId>,
6715    seen_blocks: &mut HashMap<BlockId, Vec<(VarId, Span)>>,
6716    output: &mut Vec<(VarId, Span)>,
6717) -> Result<(), ParseError> {
6718    for flag in &block.signature.named {
6719        if let Some(var_id) = flag.var_id {
6720            seen.push(var_id);
6721        }
6722    }
6723
6724    for positional in &block.signature.required_positional {
6725        if let Some(var_id) = positional.var_id {
6726            seen.push(var_id);
6727        }
6728    }
6729    for positional in &block.signature.optional_positional {
6730        if let Some(var_id) = positional.var_id {
6731            seen.push(var_id);
6732        }
6733    }
6734    if let Some(positional) = &block.signature.rest_positional
6735        && let Some(var_id) = positional.var_id
6736    {
6737        seen.push(var_id);
6738    }
6739
6740    for pipeline in &block.pipelines {
6741        discover_captures_in_pipeline(working_set, pipeline, seen, seen_blocks, output)?;
6742    }
6743
6744    Ok(())
6745}
6746
6747fn discover_captures_in_pipeline(
6748    working_set: &StateWorkingSet,
6749    pipeline: &Pipeline,
6750    seen: &mut Vec<VarId>,
6751    seen_blocks: &mut HashMap<BlockId, Vec<(VarId, Span)>>,
6752    output: &mut Vec<(VarId, Span)>,
6753) -> Result<(), ParseError> {
6754    for element in &pipeline.elements {
6755        discover_captures_in_pipeline_element(working_set, element, seen, seen_blocks, output)?;
6756    }
6757
6758    Ok(())
6759}
6760
6761// Closes over captured variables
6762pub fn discover_captures_in_pipeline_element(
6763    working_set: &StateWorkingSet,
6764    element: &PipelineElement,
6765    seen: &mut Vec<VarId>,
6766    seen_blocks: &mut HashMap<BlockId, Vec<(VarId, Span)>>,
6767    output: &mut Vec<(VarId, Span)>,
6768) -> Result<(), ParseError> {
6769    discover_captures_in_expr(working_set, &element.expr, seen, seen_blocks, output)?;
6770
6771    if let Some(redirection) = element.redirection.as_ref() {
6772        match redirection {
6773            PipelineRedirection::Single { target, .. } => {
6774                if let Some(expr) = target.expr() {
6775                    discover_captures_in_expr(working_set, expr, seen, seen_blocks, output)?;
6776                }
6777            }
6778            PipelineRedirection::Separate { out, err } => {
6779                if let Some(expr) = out.expr() {
6780                    discover_captures_in_expr(working_set, expr, seen, seen_blocks, output)?;
6781                }
6782                if let Some(expr) = err.expr() {
6783                    discover_captures_in_expr(working_set, expr, seen, seen_blocks, output)?;
6784                }
6785            }
6786        }
6787    }
6788
6789    Ok(())
6790}
6791
6792pub fn discover_captures_in_pattern(pattern: &MatchPattern, seen: &mut Vec<VarId>) {
6793    match &pattern.pattern {
6794        Pattern::Variable(var_id) => seen.push(*var_id),
6795        Pattern::List(items) => {
6796            for item in items {
6797                discover_captures_in_pattern(item, seen)
6798            }
6799        }
6800        Pattern::Record(items) => {
6801            for item in items {
6802                discover_captures_in_pattern(&item.1, seen)
6803            }
6804        }
6805        Pattern::Or(patterns) => {
6806            for pattern in patterns {
6807                discover_captures_in_pattern(pattern, seen)
6808            }
6809        }
6810        Pattern::Rest(var_id) => seen.push(*var_id),
6811        Pattern::Expression(_)
6812        | Pattern::Value(_)
6813        | Pattern::IgnoreValue
6814        | Pattern::IgnoreRest
6815        | Pattern::Garbage => {}
6816    }
6817}
6818
6819// Closes over captured variables
6820pub fn discover_captures_in_expr(
6821    working_set: &StateWorkingSet,
6822    expr: &Expression,
6823    seen: &mut Vec<VarId>,
6824    seen_blocks: &mut HashMap<BlockId, Vec<(VarId, Span)>>,
6825    output: &mut Vec<(VarId, Span)>,
6826) -> Result<(), ParseError> {
6827    match &expr.expr {
6828        Expr::AttributeBlock(ab) => {
6829            discover_captures_in_expr(working_set, &ab.item, seen, seen_blocks, output)?;
6830        }
6831        Expr::BinaryOp(lhs, _, rhs) => {
6832            discover_captures_in_expr(working_set, lhs, seen, seen_blocks, output)?;
6833            discover_captures_in_expr(working_set, rhs, seen, seen_blocks, output)?;
6834        }
6835        Expr::UnaryNot(expr) => {
6836            discover_captures_in_expr(working_set, expr, seen, seen_blocks, output)?;
6837        }
6838        Expr::Closure(block_id) => {
6839            let block = working_set.get_block(*block_id);
6840            let results = {
6841                let mut seen = vec![];
6842                let mut results = vec![];
6843
6844                discover_captures_in_closure(
6845                    working_set,
6846                    block,
6847                    &mut seen,
6848                    seen_blocks,
6849                    &mut results,
6850                )?;
6851
6852                for (var_id, span) in results.iter() {
6853                    if !seen.contains(var_id)
6854                        && let Some(variable) = working_set.get_variable_if_possible(*var_id)
6855                        && variable.mutable
6856                    {
6857                        return Err(ParseError::CaptureOfMutableVar(*span));
6858                    }
6859                }
6860
6861                results
6862            };
6863            seen_blocks.insert(*block_id, results.clone());
6864            for (var_id, span) in results.into_iter() {
6865                if !seen.contains(&var_id) {
6866                    output.push((var_id, span))
6867                }
6868            }
6869        }
6870        Expr::Block(block_id) => {
6871            let block = working_set.get_block(*block_id);
6872            // FIXME: is this correct?
6873            let results = {
6874                let mut seen = vec![];
6875                let mut results = vec![];
6876                discover_captures_in_closure(
6877                    working_set,
6878                    block,
6879                    &mut seen,
6880                    seen_blocks,
6881                    &mut results,
6882                )?;
6883                results
6884            };
6885
6886            seen_blocks.insert(*block_id, results.clone());
6887            for (var_id, span) in results.into_iter() {
6888                if !seen.contains(&var_id) {
6889                    output.push((var_id, span))
6890                }
6891            }
6892        }
6893        Expr::Binary(_) => {}
6894        Expr::Bool(_) => {}
6895        Expr::Call(call) => {
6896            let decl = working_set.get_decl(call.decl_id);
6897            if let Some(block_id) = decl.block_id() {
6898                match seen_blocks.get(&block_id) {
6899                    Some(capture_list) => {
6900                        // Push captures onto the outer closure that aren't created by that outer closure
6901                        for capture in capture_list {
6902                            if !seen.contains(&capture.0) {
6903                                output.push(*capture);
6904                            }
6905                        }
6906                    }
6907                    None => {
6908                        let block = working_set.get_block(block_id);
6909                        if !block.captures.is_empty() {
6910                            for (capture, span) in &block.captures {
6911                                if !seen.contains(capture) {
6912                                    output.push((*capture, *span));
6913                                }
6914                            }
6915                        } else {
6916                            let result = {
6917                                let mut seen = vec![];
6918                                seen_blocks.insert(block_id, output.clone());
6919
6920                                let mut result = vec![];
6921                                discover_captures_in_closure(
6922                                    working_set,
6923                                    block,
6924                                    &mut seen,
6925                                    seen_blocks,
6926                                    &mut result,
6927                                )?;
6928
6929                                result
6930                            };
6931                            // Push captures onto the outer closure that aren't created by that outer closure
6932                            for capture in &result {
6933                                if !seen.contains(&capture.0) {
6934                                    output.push(*capture);
6935                                }
6936                            }
6937
6938                            seen_blocks.insert(block_id, result);
6939                        }
6940                    }
6941                }
6942            }
6943
6944            for arg in &call.arguments {
6945                match arg {
6946                    Argument::Named(named) => {
6947                        if let Some(arg) = &named.2 {
6948                            discover_captures_in_expr(working_set, arg, seen, seen_blocks, output)?;
6949                        }
6950                    }
6951                    Argument::Positional(expr)
6952                    | Argument::Unknown(expr)
6953                    | Argument::Spread(expr) => {
6954                        discover_captures_in_expr(working_set, expr, seen, seen_blocks, output)?;
6955                    }
6956                }
6957            }
6958        }
6959        Expr::CellPath(_) => {}
6960        Expr::DateTime(_) => {}
6961        Expr::ExternalCall(head, args) => {
6962            discover_captures_in_expr(working_set, head, seen, seen_blocks, output)?;
6963
6964            for ExternalArgument::Regular(expr) | ExternalArgument::Spread(expr) in args.as_ref() {
6965                discover_captures_in_expr(working_set, expr, seen, seen_blocks, output)?;
6966            }
6967        }
6968        Expr::Filepath(_, _) => {}
6969        Expr::Directory(_, _) => {}
6970        Expr::Float(_) => {}
6971        Expr::FullCellPath(cell_path) => {
6972            discover_captures_in_expr(working_set, &cell_path.head, seen, seen_blocks, output)?;
6973        }
6974        Expr::ImportPattern(_) => {}
6975        Expr::Overlay(_) => {}
6976        Expr::Garbage => {}
6977        Expr::Nothing => {}
6978        Expr::GlobPattern(_, _) => {}
6979        Expr::Int(_) => {}
6980        Expr::Keyword(kw) => {
6981            discover_captures_in_expr(working_set, &kw.expr, seen, seen_blocks, output)?;
6982        }
6983        Expr::List(list) => {
6984            for item in list {
6985                discover_captures_in_expr(working_set, item.expr(), seen, seen_blocks, output)?;
6986            }
6987        }
6988        Expr::Operator(_) => {}
6989        Expr::Range(range) => {
6990            if let Some(from) = &range.from {
6991                discover_captures_in_expr(working_set, from, seen, seen_blocks, output)?;
6992            }
6993            if let Some(next) = &range.next {
6994                discover_captures_in_expr(working_set, next, seen, seen_blocks, output)?;
6995            }
6996            if let Some(to) = &range.to {
6997                discover_captures_in_expr(working_set, to, seen, seen_blocks, output)?;
6998            }
6999        }
7000        Expr::Record(items) => {
7001            for item in items {
7002                match item {
7003                    RecordItem::Pair(field_name, field_value) => {
7004                        discover_captures_in_expr(
7005                            working_set,
7006                            field_name,
7007                            seen,
7008                            seen_blocks,
7009                            output,
7010                        )?;
7011                        discover_captures_in_expr(
7012                            working_set,
7013                            field_value,
7014                            seen,
7015                            seen_blocks,
7016                            output,
7017                        )?;
7018                    }
7019                    RecordItem::Spread(_, record) => {
7020                        discover_captures_in_expr(working_set, record, seen, seen_blocks, output)?;
7021                    }
7022                }
7023            }
7024        }
7025        Expr::Signature(sig) => {
7026            // Something with a declaration, similar to a var decl, will introduce more VarIds into the stack at eval
7027            for pos in &sig.required_positional {
7028                if let Some(var_id) = pos.var_id {
7029                    seen.push(var_id);
7030                }
7031            }
7032            for pos in &sig.optional_positional {
7033                if let Some(var_id) = pos.var_id {
7034                    seen.push(var_id);
7035                }
7036            }
7037            if let Some(rest) = &sig.rest_positional
7038                && let Some(var_id) = rest.var_id
7039            {
7040                seen.push(var_id);
7041            }
7042            for named in &sig.named {
7043                if let Some(var_id) = named.var_id {
7044                    seen.push(var_id);
7045                }
7046            }
7047        }
7048        Expr::String(_) => {}
7049        Expr::RawString(_) => {}
7050        Expr::StringInterpolation(exprs) | Expr::GlobInterpolation(exprs, _) => {
7051            for expr in exprs {
7052                discover_captures_in_expr(working_set, expr, seen, seen_blocks, output)?;
7053            }
7054        }
7055        Expr::MatchBlock(match_block) => {
7056            for match_ in match_block {
7057                discover_captures_in_pattern(&match_.0, seen);
7058                discover_captures_in_expr(working_set, &match_.1, seen, seen_blocks, output)?;
7059            }
7060        }
7061        Expr::Collect(var_id, expr) => {
7062            seen.push(*var_id);
7063            discover_captures_in_expr(working_set, expr, seen, seen_blocks, output)?
7064        }
7065        Expr::RowCondition(block_id) | Expr::Subexpression(block_id) => {
7066            let block = working_set.get_block(*block_id);
7067
7068            let results = {
7069                let mut results = vec![];
7070                let mut seen = vec![];
7071                discover_captures_in_closure(
7072                    working_set,
7073                    block,
7074                    &mut seen,
7075                    seen_blocks,
7076                    &mut results,
7077                )?;
7078                results
7079            };
7080
7081            seen_blocks.insert(*block_id, results.clone());
7082            for (var_id, span) in results.into_iter() {
7083                if !seen.contains(&var_id) {
7084                    output.push((var_id, span))
7085                }
7086            }
7087        }
7088        Expr::Table(table) => {
7089            for header in table.columns.as_ref() {
7090                discover_captures_in_expr(working_set, header, seen, seen_blocks, output)?;
7091            }
7092            for row in table.rows.as_ref() {
7093                for cell in row.as_ref() {
7094                    discover_captures_in_expr(working_set, cell, seen, seen_blocks, output)?;
7095                }
7096            }
7097        }
7098        Expr::ValueWithUnit(value) => {
7099            discover_captures_in_expr(working_set, &value.expr, seen, seen_blocks, output)?;
7100        }
7101        Expr::Var(var_id) => {
7102            if (*var_id > ENV_VARIABLE_ID || *var_id == IN_VARIABLE_ID) && !seen.contains(var_id) {
7103                output.push((*var_id, expr.span));
7104            }
7105        }
7106        Expr::VarDecl(var_id) => {
7107            seen.push(*var_id);
7108        }
7109    }
7110    Ok(())
7111}
7112
7113fn wrap_redirection_with_collect(
7114    working_set: &mut StateWorkingSet,
7115    target: RedirectionTarget,
7116) -> RedirectionTarget {
7117    match target {
7118        RedirectionTarget::File { expr, append, span } => RedirectionTarget::File {
7119            expr: wrap_expr_with_collect(working_set, expr),
7120            span,
7121            append,
7122        },
7123        RedirectionTarget::Pipe { span } => RedirectionTarget::Pipe { span },
7124    }
7125}
7126
7127fn wrap_element_with_collect(
7128    working_set: &mut StateWorkingSet,
7129    element: PipelineElement,
7130) -> PipelineElement {
7131    PipelineElement {
7132        pipe: element.pipe,
7133        expr: wrap_expr_with_collect(working_set, element.expr),
7134        redirection: element.redirection.map(|r| match r {
7135            PipelineRedirection::Single { source, target } => PipelineRedirection::Single {
7136                source,
7137                target: wrap_redirection_with_collect(working_set, target),
7138            },
7139            PipelineRedirection::Separate { out, err } => PipelineRedirection::Separate {
7140                out: wrap_redirection_with_collect(working_set, out),
7141                err: wrap_redirection_with_collect(working_set, err),
7142            },
7143        }),
7144    }
7145}
7146
7147fn wrap_expr_with_collect(working_set: &mut StateWorkingSet, expr: Expression) -> Expression {
7148    let span = expr.span;
7149
7150    // IN_VARIABLE_ID should get replaced with a unique variable, so that we don't have to
7151    // execute as a closure
7152    let var_id = working_set.add_variable(
7153        b"$in".into(),
7154        Span::new(span.start, span.start),
7155        Type::Any,
7156        false,
7157    );
7158    let mut expr = expr.clone();
7159    expr.replace_in_variable(working_set, var_id);
7160
7161    // Bind the custom `$in` variable for that particular expression
7162    let ty = expr.ty.clone();
7163    Expression::new(
7164        working_set,
7165        Expr::Collect(var_id, Box::new(expr)),
7166        span,
7167        // We can expect it to have the same result type
7168        ty,
7169    )
7170}
7171
7172// Parses a vector of u8 to create an AST Block. If a file name is given, then
7173// the name is stored in the working set. When parsing a source without a file
7174// name, the source of bytes is stored as "source"
7175pub fn parse(
7176    working_set: &mut StateWorkingSet,
7177    fname: Option<&str>,
7178    contents: &[u8],
7179    scoped: bool,
7180) -> Arc<Block> {
7181    trace!("parse");
7182    let name = match fname {
7183        Some(fname) => {
7184            // use the canonical name for this filename
7185            nu_path::expand_to_real_path(fname)
7186                .to_string_lossy()
7187                .to_string()
7188        }
7189        None => "source".to_string(),
7190    };
7191
7192    let file_id = working_set.add_file(name, contents);
7193    let new_span = working_set.get_span_for_file(file_id);
7194
7195    let previously_parsed_block = working_set.find_block_by_span(new_span);
7196
7197    let mut output = {
7198        if let Some(block) = previously_parsed_block {
7199            return block;
7200        } else {
7201            let (output, err) = lex(contents, new_span.start, &[], &[], false);
7202            if let Some(err) = err {
7203                working_set.error(err)
7204            }
7205
7206            Arc::new(parse_block(working_set, &output, new_span, scoped, false))
7207        }
7208    };
7209
7210    // Top level `Block`s are compiled eagerly, as they don't have a parent which would cause them
7211    // to be compiled later.
7212    if working_set.parse_errors.is_empty() {
7213        compile_block(working_set, Arc::make_mut(&mut output));
7214    }
7215
7216    let mut seen = vec![];
7217    let mut seen_blocks = HashMap::new();
7218
7219    let mut captures = vec![];
7220    match discover_captures_in_closure(
7221        working_set,
7222        &output,
7223        &mut seen,
7224        &mut seen_blocks,
7225        &mut captures,
7226    ) {
7227        Ok(_) => {
7228            Arc::make_mut(&mut output).captures = captures;
7229        }
7230        Err(err) => working_set.error(err),
7231    }
7232
7233    // Also check other blocks that might have been imported
7234    let mut errors = vec![];
7235    for (block_idx, block) in working_set.delta.blocks.iter().enumerate() {
7236        let block_id = block_idx + working_set.permanent_state.num_blocks();
7237        let block_id = BlockId::new(block_id);
7238
7239        if !seen_blocks.contains_key(&block_id) {
7240            let mut captures = vec![];
7241
7242            match discover_captures_in_closure(
7243                working_set,
7244                block,
7245                &mut seen,
7246                &mut seen_blocks,
7247                &mut captures,
7248            ) {
7249                Ok(_) => {
7250                    seen_blocks.insert(block_id, captures);
7251                }
7252                Err(err) => {
7253                    errors.push(err);
7254                }
7255            }
7256        }
7257    }
7258    for err in errors {
7259        working_set.error(err)
7260    }
7261
7262    for (block_id, captures) in seen_blocks.into_iter() {
7263        // In theory, we should only be updating captures where we have new information
7264        // the only place where this is possible would be blocks that are newly created
7265        // by our working set delta. If we ever tried to modify the permanent state, we'd
7266        // panic (again, in theory, this shouldn't be possible)
7267        let block = working_set.get_block(block_id);
7268        let block_captures_empty = block.captures.is_empty();
7269        // need to check block_id >= working_set.permanent_state.num_blocks()
7270        // to avoid mutate a block that is in the permanent state.
7271        // this can happened if user defines a function with recursive call
7272        // and pipe a variable to the command, e.g:
7273        // def px [] { if true { 42 } else { px } };    # the block px is saved in permanent state.
7274        // let x = 3
7275        // $x | px
7276        // If we don't guard for `block_id`, it will change captures of `px`, which is
7277        // already saved in permanent state
7278        if !captures.is_empty()
7279            && block_captures_empty
7280            && block_id.get() >= working_set.permanent_state.num_blocks()
7281        {
7282            let block = working_set.get_block_mut(block_id);
7283            block.captures = captures;
7284        }
7285    }
7286
7287    output
7288}