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