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