Skip to main content

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