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