nu_parser/
parser.rs

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