nu_parser/
parser.rs

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