Skip to main content

nu_parser/
parse_expressions.rs

1#![allow(clippy::byte_char_slices)]
2
3use crate::{
4    Token, TokenContents,
5    lex::{LexState, is_assignment_operator, lex, lex_n_tokens},
6    lite_parser::{LiteCommand, lite_parse},
7    parse_helpers::{
8        PERCENT_FORCED_BUILTIN_PARSER_INFO, extract_spread_list, extract_spread_record, garbage,
9        garbage_pipeline,
10    },
11    parse_keywords::{
12        is_unaliasable_parser_keyword, parse_alias, parse_attribute_block, parse_const, parse_def,
13        parse_export_env, parse_export_in_block, parse_extern, parse_for, parse_hide,
14        parse_keyword, parse_let, parse_module, parse_mut, parse_overlay_hide, parse_overlay_new,
15        parse_overlay_use, parse_run, parse_run_expr, parse_source, parse_use, parse_where,
16        parse_where_expr,
17    },
18    parse_patterns::parse_pattern,
19    parse_pipelines::{parse_block, parse_pipeline_element, redirecting_builtin_error},
20    parser::{
21        compile_block, expand_to_cell_path, parse_binary, parse_brace_expr, parse_call,
22        parse_datetime, parse_directory, parse_dollar_expr, parse_duration, parse_filepath,
23        parse_filesize, parse_float, parse_full_cell_path, parse_glob_pattern, parse_int,
24        parse_multispan_value, parse_number, parse_oneof, parse_paren_expr, parse_range,
25        parse_raw_string, parse_regular_external_arg, parse_signature, parse_signature_helper,
26        parse_simple_cell_path, parse_string, parse_string_strict,
27    },
28    type_check::math_result_type,
29};
30use itertools::Itertools;
31use log::trace;
32use nu_protocol::{
33    CompareTypes, IntoSpanned, ParseError, PositionalArg, Signature, Span, Spanned, SyntaxShape,
34    Type, TypeSet, VarId, ast::*, engine::StateWorkingSet,
35};
36use std::{collections::HashMap, sync::Arc};
37
38pub fn is_math_expression_like(working_set: &mut StateWorkingSet, span: Span) -> bool {
39    let bytes = working_set.get_span_contents(span);
40    match bytes {
41        [] => return false,
42        b"true" | b"false" | b"null" | b"not" | b"if" | b"match" => return true,
43        [b'r', b'#', ..] => return true,
44        [b'(' | b'{' | b'[' | b'$' | b'"' | b'\'' | b'-', ..] => return true,
45        _ => {}
46    }
47
48    let starting_error_count = working_set.parse_errors.len();
49
50    // Number
51    parse_number(working_set, span);
52    if working_set.parse_errors.len() == starting_error_count {
53        return true;
54    }
55    working_set.parse_errors.truncate(starting_error_count);
56
57    // Filesize
58    parse_filesize(working_set, span);
59    if working_set.parse_errors.len() == starting_error_count {
60        return true;
61    }
62    working_set.parse_errors.truncate(starting_error_count);
63
64    parse_duration(working_set, span);
65    if working_set.parse_errors.len() == starting_error_count {
66        return true;
67    }
68    working_set.parse_errors.truncate(starting_error_count);
69
70    parse_datetime(working_set, span);
71    if working_set.parse_errors.len() == starting_error_count {
72        return true;
73    }
74    working_set.parse_errors.truncate(starting_error_count);
75
76    parse_binary(working_set, span);
77    // We need an additional negate match to check if the last error was unexpected
78    // or more specifically, if it was `ParseError::InvalidBinaryString`.
79    // If so, we suppress the error and stop parsing to the next (which is `parse_range()`).
80    if working_set.parse_errors.len() == starting_error_count {
81        return true;
82    } else if !matches!(
83        working_set.parse_errors.last(),
84        Some(ParseError::Expected(_, _))
85    ) {
86        working_set.parse_errors.truncate(starting_error_count);
87        return true;
88    }
89    working_set.parse_errors.truncate(starting_error_count);
90
91    let is_range = parse_range(working_set, span).is_some();
92    working_set.parse_errors.truncate(starting_error_count);
93    is_range
94}
95
96fn is_env_variable_name(bytes: &[u8]) -> bool {
97    match bytes {
98        [first, rest @ ..] if first == &b'_' || first.is_ascii_alphabetic() => {
99            rest.iter().all(|&b| b.is_ascii_alphanumeric() || b == b'_')
100        }
101        _ => false,
102    }
103}
104
105pub fn parse_list_expression(
106    working_set: &mut StateWorkingSet,
107    span: Span,
108    element_shape: &SyntaxShape,
109) -> Expression {
110    let bytes = working_set.get_span_contents(span);
111
112    let mut start = span.start;
113    let mut end = span.end;
114
115    if bytes.starts_with(b"[") {
116        start += 1;
117    }
118    if bytes.ends_with(b"]") {
119        end -= 1;
120    } else {
121        working_set.error(ParseError::Unclosed("]", Span::new(end, end)));
122    }
123
124    let inner_span = Span::new(start, end);
125    let source = working_set.get_span_contents(inner_span);
126
127    let (output, err) = lex(source, inner_span.start, &[b'\n', b'\r', b','], &[], true);
128    if let Some(err) = err {
129        working_set.error(err)
130    }
131
132    let (mut output, err) = lite_parse(&output, working_set);
133    if let Some(err) = err {
134        working_set.error(err)
135    }
136
137    let mut args = vec![];
138
139    let mut contained_type: Option<Type> = None;
140
141    if !output.block.is_empty() {
142        for mut command in output.block.remove(0).commands {
143            let mut spans_idx = 0;
144
145            while spans_idx < command.parts.len() {
146                let curr_span = command.parts[spans_idx];
147                let curr_tok = working_set.get_span_contents(curr_span);
148                let (arg, ty) = if let Some(Spanned {
149                    span: trimmed_span, ..
150                }) = extract_spread_list(curr_tok.into_spanned(curr_span))
151                {
152                    // Parse the spread operator
153                    // Remove "..." before parsing argument to spread operator
154                    command.parts[spans_idx] = trimmed_span;
155                    let spread_arg = parse_multispan_value(
156                        working_set,
157                        &command.parts,
158                        &mut spans_idx,
159                        &SyntaxShape::List(Box::new(element_shape.clone())),
160                        None,
161                    );
162                    let elem_ty = match &spread_arg.ty {
163                        Type::List(elem_ty) => *elem_ty.clone(),
164                        _ => Type::Any,
165                    };
166                    let span = Span::new(curr_span.start, curr_span.start + 3);
167                    (ListItem::Spread(span, spread_arg), elem_ty)
168                } else {
169                    let arg = parse_multispan_value(
170                        working_set,
171                        &command.parts,
172                        &mut spans_idx,
173                        element_shape,
174                        None,
175                    );
176                    let ty = arg.ty.clone();
177                    (ListItem::Item(arg), ty)
178                };
179
180                contained_type = match contained_type {
181                    Some(ctype) => Some(ctype.union(ty)),
182                    None => Some(ty),
183                };
184
185                args.push(arg);
186
187                spans_idx += 1;
188            }
189        }
190    }
191
192    Expression::new(
193        working_set,
194        Expr::List(args),
195        span,
196        Type::List(Box::new(if let Some(ty) = contained_type {
197            ty
198        } else {
199            Type::Any
200        })),
201    )
202}
203
204fn parse_table_row(
205    working_set: &mut StateWorkingSet,
206    span: Span,
207) -> Result<(Vec<Expression>, Span), Span> {
208    let list = parse_list_expression(working_set, span, &SyntaxShape::Any);
209    let Expression {
210        expr: Expr::List(list),
211        span,
212        ..
213    } = list
214    else {
215        unreachable!("the item must be a list")
216    };
217
218    list.into_iter()
219        .map(|item| match item {
220            ListItem::Item(expr) => Ok(expr),
221            ListItem::Spread(_, spread) => Err(spread.span),
222        })
223        .collect::<Result<_, _>>()
224        .map(|exprs| (exprs, span))
225}
226
227pub(crate) fn parse_table_expression(
228    working_set: &mut StateWorkingSet,
229    span: Span,
230    list_element_shape: &SyntaxShape,
231) -> Expression {
232    let bytes = working_set.get_span_contents(span);
233    let inner_span = {
234        let start = if bytes.starts_with(b"[") {
235            span.start + 1
236        } else {
237            span.start
238        };
239
240        let end = if bytes.ends_with(b"]") {
241            span.end - 1
242        } else {
243            let end = span.end;
244            working_set.error(ParseError::Unclosed("]", Span::new(end, end)));
245            span.end
246        };
247
248        Span::new(start, end)
249    };
250
251    let source = working_set.get_span_contents(inner_span);
252    let (tokens, err) = lex(source, inner_span.start, &[b'\n', b'\r', b','], &[], true);
253    if let Some(err) = err {
254        working_set.error(err);
255    }
256
257    // Check that we have all arguments first, before trying to parse the first
258    // in order to avoid exponential parsing time
259    let [first, second, rest @ ..] = &tokens[..] else {
260        return parse_list_expression(working_set, span, list_element_shape);
261    };
262    if !working_set.get_span_contents(first.span).starts_with(b"[")
263        || second.contents != TokenContents::Semicolon
264        || rest.is_empty()
265    {
266        return parse_list_expression(working_set, span, list_element_shape);
267    };
268    let head = parse_table_row(working_set, first.span);
269
270    let errors = working_set.parse_errors.len();
271
272    let (head, rows) = match head {
273        Ok((head, _)) => {
274            let rows = rest
275                .iter()
276                .filter_map(|it| {
277                    use std::cmp::Ordering;
278
279                    match working_set.get_span_contents(it.span) {
280                        b"," => None,
281                        text if !text.starts_with(b"[") => {
282                            let err = ParseError::LabeledErrorWithHelp {
283                                error: String::from("Table item not list"),
284                                label: String::from("not a list"),
285                                span: it.span,
286                                help: String::from("All table items must be lists"),
287                            };
288                            working_set.error(err);
289                            None
290                        }
291                        _ => match parse_table_row(working_set, it.span) {
292                            Ok((list, span)) => {
293                                match list.len().cmp(&head.len()) {
294                                    Ordering::Less => {
295                                        let err = ParseError::MissingColumns(head.len(), span);
296                                        working_set.error(err);
297                                    }
298                                    Ordering::Greater => {
299                                        let span = {
300                                            let start = list[head.len()].span.start;
301                                            let end = span.end;
302                                            Span::new(start, end)
303                                        };
304                                        let err = ParseError::ExtraColumns(head.len(), span);
305                                        working_set.error(err);
306                                    }
307                                    Ordering::Equal => {}
308                                }
309                                Some(list)
310                            }
311                            Err(span) => {
312                                let err = ParseError::LabeledError(
313                                    String::from("Cannot spread in a table row"),
314                                    String::from("invalid spread here"),
315                                    span,
316                                );
317                                working_set.error(err);
318                                None
319                            }
320                        },
321                    }
322                })
323                .collect();
324
325            (head, rows)
326        }
327        Err(span) => {
328            let err = ParseError::LabeledError(
329                String::from("Cannot spread in a table row"),
330                String::from("invalid spread here"),
331                span,
332            );
333            working_set.error(err);
334            (Vec::new(), Vec::new())
335        }
336    };
337
338    let ty = if working_set.parse_errors.len() == errors {
339        let (ty, errs) = table_type(&head, &rows);
340        working_set.parse_errors.extend(errs);
341        ty
342    } else {
343        Type::table()
344    };
345
346    let table = Table {
347        columns: head.into(),
348        rows: rows.into_iter().map(Into::into).collect(),
349    };
350
351    Expression::new(working_set, Expr::Table(table), span, ty)
352}
353
354fn table_type(head: &[Expression], rows: &[Vec<Expression>]) -> (Type, Vec<ParseError>) {
355    let mut errors = vec![];
356    let mut rows: Vec<_> = rows.iter().map(|row| row.iter()).collect();
357
358    let column_types = std::iter::from_fn(move || {
359        let column = rows
360            .iter_mut()
361            .filter_map(|row| row.next())
362            .map(|col| col.ty.clone());
363        Some(Type::supertype_of(column).unwrap_or(Type::Any))
364    });
365
366    let mk_error = |span| ParseError::LabeledErrorWithHelp {
367        error: "Table column name not string".into(),
368        label: "must be a string".into(),
369        help: "Table column names should be able to be converted into strings".into(),
370        span,
371    };
372
373    let ty = head
374        .iter()
375        .zip(column_types)
376        .filter_map(|(expr, col_ty)| {
377            if !Type::String.is_subtype_of(&expr.ty) {
378                errors.push(mk_error(expr.span));
379                None
380            } else {
381                expr.as_string().zip(Some(col_ty))
382            }
383        })
384        .collect();
385
386    (Type::Table(ty), errors)
387}
388
389pub fn parse_block_expression(
390    working_set: &mut StateWorkingSet,
391    span: Span,
392    input_type: Option<&Type>,
393) -> Expression {
394    trace!("parsing: block expression");
395
396    let bytes = working_set.get_span_contents(span);
397
398    let mut start = span.start;
399    let mut end = span.end;
400    let mut is_closed = true;
401
402    if bytes.starts_with(b"{") {
403        start += 1;
404    } else {
405        working_set.error(ParseError::Expected("block", span));
406        return garbage(working_set, span);
407    }
408    if bytes.ends_with(b"}") {
409        end -= 1;
410    } else {
411        working_set.error(ParseError::Unclosed("}", Span::new(end, end)));
412        is_closed = false;
413    }
414
415    let inner_span = Span::new(start, end);
416
417    let source = working_set.get_span_contents(inner_span);
418
419    let (output, err) = lex(source, start, &[], &[], false);
420    if let Some(err) = err {
421        working_set.error(err);
422    }
423
424    working_set.enter_scope();
425
426    // Check to see if we have closure parameters
427    if let Some(Token {
428        contents: TokenContents::Pipe,
429        span,
430    }) = output.first()
431    {
432        working_set.error(ParseError::Expected("block but found closure", *span));
433    }
434
435    let mut output = parse_block(working_set, &output, span, false, false, input_type);
436
437    output.span = Some(span);
438
439    if is_closed {
440        working_set.exit_scope();
441    }
442
443    let block_id = working_set.add_block(Arc::new(output));
444
445    Expression::new(working_set, Expr::Block(block_id), span, Type::Block)
446}
447
448pub fn parse_match_block_expression(
449    working_set: &mut StateWorkingSet,
450    span: Span,
451    input_type: Option<&Type>,
452) -> Expression {
453    let bytes = working_set.get_span_contents(span);
454
455    let mut start = span.start;
456    let mut end = span.end;
457    let mut is_closed = true;
458
459    if bytes.starts_with(b"{") {
460        start += 1;
461    } else {
462        working_set.error(ParseError::Expected("closure", span));
463        return garbage(working_set, span);
464    }
465    if bytes.ends_with(b"}") {
466        end -= 1;
467    } else {
468        working_set.error(ParseError::Unclosed("}", Span::new(end, end)));
469        is_closed = false;
470    }
471
472    let inner_span = Span::new(start, end);
473
474    let source = working_set.get_span_contents(inner_span);
475
476    let (output, err) = lex(source, start, &[b' ', b'\r', b'\n', b',', b'|'], &[], true);
477    if let Some(err) = err {
478        working_set.error(err);
479    }
480
481    let mut position = 0;
482
483    let mut output_matches = vec![];
484    let mut output_type = Type::one_of([]);
485
486    while position < output.len() {
487        // Each match gets its own scope
488
489        working_set.enter_scope();
490
491        // First parse the pattern
492        let mut pattern = parse_pattern(working_set, output[position].span);
493
494        position += 1;
495
496        if position >= output.len() {
497            working_set.error(ParseError::Mismatch(
498                "=>".into(),
499                "end of input".into(),
500                Span::new(output[position - 1].span.end, output[position - 1].span.end),
501            ));
502
503            working_set.exit_scope();
504            break;
505        }
506
507        let mut connector = working_set.get_span_contents(output[position].span);
508
509        // Multiple patterns connected by '|'
510        if connector == b"|" && position < output.len() {
511            let mut or_pattern = vec![pattern];
512
513            while connector == b"|" && position < output.len() {
514                connector = b"";
515
516                position += 1;
517
518                if position >= output.len() {
519                    working_set.error(ParseError::Mismatch(
520                        "pattern".into(),
521                        "end of input".into(),
522                        Span::new(output[position - 1].span.end, output[position - 1].span.end),
523                    ));
524                    break;
525                }
526
527                let pattern = parse_pattern(working_set, output[position].span);
528                or_pattern.push(pattern);
529
530                position += 1;
531                if position >= output.len() {
532                    working_set.error(ParseError::Mismatch(
533                        "=>".into(),
534                        "end of input".into(),
535                        Span::new(output[position - 1].span.end, output[position - 1].span.end),
536                    ));
537                    break;
538                } else {
539                    connector = working_set.get_span_contents(output[position].span);
540                }
541            }
542
543            let start = or_pattern
544                .first()
545                .expect("internal error: unexpected state of or-pattern")
546                .span
547                .start;
548            let end = or_pattern
549                .last()
550                .expect("internal error: unexpected state of or-pattern")
551                .span
552                .end;
553
554            pattern = MatchPattern {
555                pattern: Pattern::Or(or_pattern),
556                guard: None,
557                span: Span::new(start, end),
558            }
559        }
560        // A match guard
561        if connector == b"if" {
562            let if_end = {
563                let end = output[position].span.end;
564                Span::new(end, end)
565            };
566
567            position += 1;
568
569            let mk_err = || ParseError::LabeledErrorWithHelp {
570                error: "Match guard without an expression".into(),
571                label: "expected an expression".into(),
572                help: "The `if` keyword must be followed with an expression".into(),
573                span: if_end,
574            };
575
576            if output.get(position).is_none() {
577                working_set.error(mk_err());
578                return garbage(working_set, span);
579            };
580
581            let (tokens, found) = if let Some((pos, _)) = output[position..]
582                .iter()
583                .find_position(|t| working_set.get_span_contents(t.span) == b"=>")
584            {
585                if position + pos == position {
586                    working_set.error(mk_err());
587                    return garbage(working_set, span);
588                }
589
590                (&output[position..position + pos], true)
591            } else {
592                (&output[position..], false)
593            };
594
595            let mut start = 0;
596            let guard = parse_multispan_value(
597                working_set,
598                &tokens.iter().map(|tok| tok.span).collect_vec(),
599                &mut start,
600                &SyntaxShape::MathExpression,
601                None,
602            );
603
604            pattern.guard = Some(Box::new(guard));
605            position += if found { start + 1 } else { start };
606            connector = working_set.get_span_contents(output[position].span);
607        }
608        // Then the `=>` arrow
609        if connector != b"=>" {
610            working_set.error(ParseError::Mismatch(
611                "=>".into(),
612                "end of input".into(),
613                Span::new(output[position - 1].span.end, output[position - 1].span.end),
614            ));
615        } else {
616            position += 1;
617        }
618
619        // Finally, the value/expression/block that we will run to produce the result
620        if position >= output.len() {
621            working_set.error(ParseError::Mismatch(
622                "match result".into(),
623                "end of input".into(),
624                Span::new(output[position - 1].span.end, output[position - 1].span.end),
625            ));
626
627            working_set.exit_scope();
628            break;
629        }
630
631        let result = parse_multispan_value(
632            working_set,
633            &[output[position].span],
634            &mut 0,
635            &SyntaxShape::OneOf(vec![SyntaxShape::Block, SyntaxShape::Expression]),
636            input_type,
637        );
638        position += 1;
639        if is_closed {
640            working_set.exit_scope();
641        }
642
643        let branch_output_type = match &result.expr {
644            Expr::Block(block_id) => {
645                let block = working_set.get_block(*block_id);
646                match block.pipelines.is_empty() {
647                    false => block.output_type(),
648                    true => input_type.cloned().unwrap_or(Type::Any),
649                }
650            }
651            _ => result.ty.clone(),
652        };
653        output_type = output_type.union(branch_output_type);
654
655        output_matches.push((pattern, result));
656    }
657
658    let has_wildcard = output_matches.iter().any(|(pat, _)| pat.is_wildcard());
659    if !has_wildcard {
660        output_type = output_type.union(Type::Nothing);
661    }
662
663    Expression::new(
664        working_set,
665        Expr::MatchBlock(output_matches),
666        span,
667        output_type,
668    )
669}
670
671pub fn parse_closure_expression(
672    working_set: &mut StateWorkingSet,
673    shape: &SyntaxShape,
674    span: Span,
675    input_type: Option<&Type>,
676) -> Expression {
677    trace!("parsing: closure expression");
678
679    let bytes = working_set.get_span_contents(span);
680
681    let mut start = span.start;
682    let mut end = span.end;
683    let mut is_closed = true;
684
685    if bytes.starts_with(b"{") {
686        start += 1;
687    } else {
688        working_set.error(ParseError::Expected("closure", span));
689        return garbage(working_set, span);
690    }
691    if bytes.ends_with(b"}") {
692        end -= 1;
693    } else {
694        working_set.error(ParseError::Unclosed("}", Span::new(end, end)));
695        is_closed = false;
696    }
697
698    let inner_span = Span::new(start, end);
699
700    let source = working_set.get_span_contents(inner_span);
701
702    let (output, err) = lex(source, start, &[], &[], false);
703    if let Some(err) = err {
704        working_set.error(err);
705    }
706
707    working_set.enter_scope();
708
709    // Check to see if we have parameters
710    let (signature, amt_to_skip): (Option<(Box<Signature>, Span)>, usize) = match output.first() {
711        Some(Token {
712            contents: TokenContents::Pipe,
713            span,
714        }) => {
715            // We've found a parameter list
716            let start_point = span.start;
717            let mut token_iter = output.iter().enumerate().skip(1);
718            let mut end_span = None;
719            let mut amt_to_skip = 1;
720
721            for token in &mut token_iter {
722                if let Token {
723                    contents: TokenContents::Pipe,
724                    span,
725                } = token.1
726                {
727                    end_span = Some(span);
728                    amt_to_skip += token.0;
729                    break;
730                }
731            }
732
733            let end_point = if let Some(span) = end_span {
734                span.end
735            } else {
736                working_set.error(ParseError::Unclosed("|", Span::new(end, end)));
737                end
738            };
739
740            let signature_span = Span::new(start_point, end_point);
741            let signature = parse_signature_helper(working_set, signature_span, false);
742
743            (Some((signature, signature_span)), amt_to_skip)
744        }
745        Some(Token {
746            contents: TokenContents::PipePipe,
747            span,
748        }) => (
749            Some((Box::new(Signature::new("closure".to_string())), *span)),
750            1,
751        ),
752        _ => (None, 0),
753    };
754
755    // TODO: Finish this
756    if let SyntaxShape::Closure(Some(v)) = shape
757        && let Some((sig, sig_span)) = &signature
758    {
759        if sig.num_positionals() > v.len() {
760            working_set.error(ParseError::ExpectedWithStringMsg(
761                format!(
762                    "{} closure parameter{}",
763                    v.len(),
764                    if v.len() > 1 { "s" } else { "" }
765                ),
766                *sig_span,
767            ));
768        }
769
770        for (expected, PositionalArg { name, shape, .. }) in
771            v.iter().zip(sig.required_positional.iter())
772        {
773            if expected != shape && *shape != SyntaxShape::Any {
774                working_set.error(ParseError::ParameterMismatchType(
775                    name.to_owned(),
776                    expected.to_string(),
777                    shape.to_string(),
778                    *sig_span,
779                ));
780            }
781        }
782    }
783
784    let mut output = parse_block(
785        working_set,
786        &output[amt_to_skip..],
787        span,
788        false,
789        false,
790        input_type,
791    );
792
793    // NOTE: closures need to be compiled eagerly due to these reasons:
794    //  - their `Block`s (which contains their `IrBlock`) are stored in the working_set
795    //  - Ir compiler does not have mutable access to the working_set and can't attach `IrBlock`s
796    //  to existing `Block`s
797    // so they can't be compiled as part of their parent `Block`'s compilation
798    //
799    // If the compiler used a mechanism similar to the `EngineState`/`StateWorkingSet` divide, we
800    // could defer all compilation and apply the generated delta to `StateWorkingSet` afterwards.
801    if working_set.parse_errors.is_empty() {
802        compile_block(working_set, &mut output);
803    }
804
805    if let Some(signature) = signature {
806        output.signature = signature.0;
807    }
808
809    output.span = Some(span);
810
811    if is_closed {
812        working_set.exit_scope();
813    }
814
815    let block_id = working_set.add_block(Arc::new(output));
816
817    Expression::new(working_set, Expr::Closure(block_id), span, Type::Closure)
818}
819
820pub fn parse_value(
821    working_set: &mut StateWorkingSet,
822    span: Span,
823    shape: &SyntaxShape,
824    input_type: Option<&Type>,
825) -> Expression {
826    trace!("parsing: value: {shape}");
827
828    let bytes = working_set.get_span_contents(span);
829
830    if bytes.is_empty() {
831        working_set.error(ParseError::IncompleteParser(span));
832        return garbage(working_set, span);
833    }
834
835    if let SyntaxShape::OneOf(possible_shapes) = shape {
836        return parse_oneof(
837            working_set,
838            &[span],
839            &mut 0,
840            possible_shapes,
841            false,
842            input_type,
843        );
844    }
845
846    match bytes[0] {
847        b'$' => return parse_dollar_expr(working_set, span, shape, input_type),
848        b'(' => return parse_paren_expr(working_set, span, shape),
849        b'{' => return parse_brace_expr(working_set, span, shape, input_type),
850        b'[' => match shape {
851            SyntaxShape::Any
852            | SyntaxShape::List(_)
853            | SyntaxShape::Table(_)
854            | SyntaxShape::Signature
855            | SyntaxShape::ExternalSignature
856            | SyntaxShape::Filepath
857            | SyntaxShape::String
858            | SyntaxShape::GlobPattern
859            | SyntaxShape::ExternalArgument => {}
860
861            _ => {
862                working_set.error(ParseError::ExpectedWithStringMsg(shape.to_string(), span));
863                return Expression::garbage(working_set, span);
864            }
865        },
866        b'r' if bytes.len() > 1 && bytes[1] == b'#' => {
867            return parse_raw_string(working_set, span);
868        }
869        _ => {}
870    }
871
872    match shape {
873        SyntaxShape::Number => parse_number(working_set, span),
874        SyntaxShape::Float => parse_float(working_set, span),
875        SyntaxShape::Int => parse_int(working_set, span),
876        SyntaxShape::Duration => parse_duration(working_set, span),
877        SyntaxShape::DateTime => parse_datetime(working_set, span),
878        SyntaxShape::Filesize => parse_filesize(working_set, span),
879        SyntaxShape::Range => {
880            parse_range(working_set, span).unwrap_or_else(|| garbage(working_set, span))
881        }
882        // Check for reserved keyword values
883        SyntaxShape::Nothing | SyntaxShape::Any if bytes == b"null" => {
884            Expression::new(working_set, Expr::Nothing, span, Type::Nothing)
885        }
886        SyntaxShape::Boolean | SyntaxShape::Any if bytes == b"true" => {
887            Expression::new(working_set, Expr::Bool(true), span, Type::Bool)
888        }
889        SyntaxShape::Boolean | SyntaxShape::Any if bytes == b"false" => {
890            Expression::new(working_set, Expr::Bool(false), span, Type::Bool)
891        }
892        SyntaxShape::Filepath
893        | SyntaxShape::Directory
894        | SyntaxShape::GlobPattern
895        // TODO: this serves for backward compatibility.
896        // As a consequence, for commands like `def foo [foo: string] {}`,
897        // it forbids usage like `foo true`, have to call it explicitly with `foo "true"`.
898        // On the other hand, given current `SyntaxShape` based `parse_value`, `foo 10.0` doesn't raise any error.
899        // We want to fix this discrepancy in the future.
900        | SyntaxShape::String
901            if matches!(bytes, b"true" | b"false" | b"null") =>
902        {
903            working_set.error(ParseError::ExpectedWithStringMsg(shape.to_string(), span));
904            garbage(working_set, span)
905        }
906        SyntaxShape::Filepath => parse_filepath(working_set, span),
907        SyntaxShape::Directory => parse_directory(working_set, span),
908        SyntaxShape::GlobPattern => parse_glob_pattern(working_set, span),
909        SyntaxShape::String => parse_string(working_set, span),
910        SyntaxShape::Binary => parse_binary(working_set, span),
911        SyntaxShape::Signature if bytes.starts_with(b"[") => parse_signature(working_set, span, false),
912        SyntaxShape::ExternalSignature if bytes.starts_with(b"[") => parse_signature(working_set, span, true),
913        SyntaxShape::List(elem) if bytes.starts_with(b"[") => {
914            parse_table_expression(working_set, span, elem)
915        }
916        SyntaxShape::Table(_) if bytes.starts_with(b"[") => {
917            parse_table_expression(working_set, span, &SyntaxShape::Any)
918        }
919        SyntaxShape::CellPath => parse_simple_cell_path(working_set, span),
920
921        // Be sure to return ParseError::Expected(..) if invoked for one of these shapes, but lex
922        // stream doesn't start with '{'} -- parsing in SyntaxShape::Any arm depends on this error variant.
923        SyntaxShape::Block | SyntaxShape::Closure(..) | SyntaxShape::Record(_) => {
924            working_set.error(ParseError::Expected("block, closure or record", span));
925
926            Expression::garbage(working_set, span)
927        }
928
929        SyntaxShape::ExternalArgument => parse_regular_external_arg(working_set, span),
930
931        SyntaxShape::Any => {
932            if bytes.starts_with(b"[") {
933                //parse_value(working_set, span, &SyntaxShape::Table)
934                parse_full_cell_path(working_set, None, span, None)
935            } else {
936                let shapes = [
937                    SyntaxShape::Binary,
938                    SyntaxShape::Range,
939                    SyntaxShape::Filesize,
940                    SyntaxShape::Duration,
941                    SyntaxShape::DateTime,
942                    SyntaxShape::Int,
943                    SyntaxShape::Number,
944                    SyntaxShape::String,
945                ];
946                for shape in shapes.iter() {
947                    let starting_error_count = working_set.parse_errors.len();
948
949                    let s = parse_value(working_set, span, shape, None);
950
951                    if starting_error_count == working_set.parse_errors.len() {
952                        return s;
953                    } else {
954                        match working_set.parse_errors.get(starting_error_count) {
955                            Some(
956                                ParseError::Expected(_, _)
957                                | ParseError::ExpectedWithStringMsg(_, _),
958                            ) => {
959                                working_set.parse_errors.truncate(starting_error_count);
960                                continue;
961                            }
962                            _ => {
963                                return s;
964                            }
965                        }
966                    }
967                }
968                working_set.error(ParseError::Expected("any shape", span));
969                garbage(working_set, span)
970            }
971        }
972        _ => {
973            working_set.error(ParseError::ExpectedWithStringMsg(shape.to_string(), span));
974            garbage(working_set, span)
975        }
976    }
977}
978
979pub fn parse_assignment_operator(working_set: &mut StateWorkingSet, span: Span) -> Expression {
980    let contents = working_set.get_span_contents(span);
981
982    let operator = match contents {
983        b"=" => Operator::Assignment(Assignment::Assign),
984        b"+=" => Operator::Assignment(Assignment::AddAssign),
985        b"-=" => Operator::Assignment(Assignment::SubtractAssign),
986        b"*=" => Operator::Assignment(Assignment::MultiplyAssign),
987        b"/=" => Operator::Assignment(Assignment::DivideAssign),
988        b"++=" => Operator::Assignment(Assignment::ConcatenateAssign),
989        _ => {
990            working_set.error(ParseError::Expected("assignment operator", span));
991            return garbage(working_set, span);
992        }
993    };
994
995    Expression::new(working_set, Expr::Operator(operator), span, Type::Any)
996}
997
998pub fn parse_assignment_expression(
999    working_set: &mut StateWorkingSet,
1000    spans: &[Span],
1001    input_type: Option<&Type>,
1002) -> Expression {
1003    trace!("parsing: assignment expression");
1004    let expr_span = Span::concat(spans);
1005
1006    // Assignment always has the most precedence, and its right-hand side can be a pipeline
1007    let Some(op_index) = spans
1008        .iter()
1009        .position(|span| is_assignment_operator(working_set.get_span_contents(*span)))
1010    else {
1011        working_set.error(ParseError::Expected("assignment expression", expr_span));
1012        return garbage(working_set, expr_span);
1013    };
1014
1015    let lhs_spans = &spans[0..op_index];
1016    let op_span = spans[op_index];
1017    let rhs_spans = &spans[(op_index + 1)..];
1018
1019    if lhs_spans.is_empty() {
1020        working_set.error(ParseError::Expected(
1021            "left hand side of assignment",
1022            op_span,
1023        ));
1024        return garbage(working_set, expr_span);
1025    }
1026
1027    if rhs_spans.is_empty() {
1028        working_set.error(ParseError::Expected(
1029            "right hand side of assignment",
1030            op_span,
1031        ));
1032        return garbage(working_set, expr_span);
1033    }
1034
1035    // Parse the lhs and operator as usual for a math expression
1036    let mut lhs = parse_expression(working_set, lhs_spans, None);
1037    // make sure that lhs is a mutable variable.
1038    match &lhs.expr {
1039        Expr::FullCellPath(p) => {
1040            if let Expr::Var(var_id) = p.head.expr
1041                && var_id != nu_protocol::ENV_VARIABLE_ID
1042                && !working_set.get_variable(var_id).mutable
1043            {
1044                working_set.error(ParseError::AssignmentRequiresMutableVar(lhs.span))
1045            }
1046        }
1047        _ => working_set.error(ParseError::AssignmentRequiresVar(lhs.span)),
1048    }
1049
1050    let mut operator = parse_assignment_operator(working_set, op_span);
1051
1052    // Re-parse the right-hand side as a subexpression
1053    let rhs_span = Span::concat(rhs_spans);
1054
1055    let (rhs_tokens, rhs_error) = lex(
1056        working_set.get_span_contents(rhs_span),
1057        rhs_span.start,
1058        &[],
1059        &[],
1060        false,
1061    );
1062    working_set.parse_errors.extend(rhs_error);
1063
1064    trace!("parsing: assignment right-hand side subexpression");
1065    let rhs_block = parse_block(working_set, &rhs_tokens, rhs_span, false, true, input_type);
1066    let rhs_ty = rhs_block.output_type();
1067
1068    // TEMP: double-check that if the RHS block starts with an external call, it must start with a
1069    // caret. This is to mitigate the change in assignment parsing introduced in 0.97.0 which could
1070    // result in unintentional execution of commands.
1071    if let Some(Expr::ExternalCall(head, ..)) = rhs_block
1072        .pipelines
1073        .first()
1074        .and_then(|pipeline| pipeline.elements.first())
1075        .map(|element| &element.expr.expr)
1076    {
1077        let contents = working_set.get_span_contents(Span {
1078            start: head.span.start - 1,
1079            end: head.span.end,
1080        });
1081        if !contents.starts_with(b"^") {
1082            working_set.parse_errors.push(ParseError::LabeledErrorWithHelp {
1083                error: "External command calls must be explicit in assignments".into(),
1084                label: "add a caret (^) before the command name if you intended to run and capture its output".into(),
1085                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(),
1086                span: head.span,
1087            });
1088        }
1089    }
1090
1091    let rhs_block_id = working_set.add_block(Arc::new(rhs_block));
1092    let mut rhs = Expression::new(
1093        working_set,
1094        Expr::Subexpression(rhs_block_id),
1095        rhs_span,
1096        rhs_ty,
1097    );
1098
1099    let (result_ty, err) = math_result_type(working_set, &mut lhs, &mut operator, &mut rhs);
1100    if let Some(err) = err {
1101        working_set.parse_errors.push(err);
1102    }
1103
1104    Expression::new(
1105        working_set,
1106        Expr::BinaryOp(Box::new(lhs), Box::new(operator), Box::new(rhs)),
1107        expr_span,
1108        result_ty,
1109    )
1110}
1111
1112pub fn parse_operator(working_set: &mut StateWorkingSet, span: Span) -> Expression {
1113    let contents = working_set.get_span_contents(span);
1114
1115    let operator = match contents {
1116        b"==" => Operator::Comparison(Comparison::Equal),
1117        b"!=" => Operator::Comparison(Comparison::NotEqual),
1118        b"<" => Operator::Comparison(Comparison::LessThan),
1119        b"<=" => Operator::Comparison(Comparison::LessThanOrEqual),
1120        b">" => Operator::Comparison(Comparison::GreaterThan),
1121        b">=" => Operator::Comparison(Comparison::GreaterThanOrEqual),
1122        b"=~" | b"like" => Operator::Comparison(Comparison::RegexMatch),
1123        b"!~" | b"not-like" => Operator::Comparison(Comparison::NotRegexMatch),
1124        b"in" => Operator::Comparison(Comparison::In),
1125        b"not-in" => Operator::Comparison(Comparison::NotIn),
1126        b"has" => Operator::Comparison(Comparison::Has),
1127        b"not-has" => Operator::Comparison(Comparison::NotHas),
1128        b"starts-with" => Operator::Comparison(Comparison::StartsWith),
1129        b"not-starts-with" => Operator::Comparison(Comparison::NotStartsWith),
1130        b"ends-with" => Operator::Comparison(Comparison::EndsWith),
1131        b"not-ends-with" => Operator::Comparison(Comparison::NotEndsWith),
1132        b"+" => Operator::Math(Math::Add),
1133        b"-" => Operator::Math(Math::Subtract),
1134        b"*" => Operator::Math(Math::Multiply),
1135        b"/" => Operator::Math(Math::Divide),
1136        b"//" => Operator::Math(Math::FloorDivide),
1137        b"mod" => Operator::Math(Math::Modulo),
1138        b"**" => Operator::Math(Math::Pow),
1139        b"++" => Operator::Math(Math::Concatenate),
1140        b"bit-or" => Operator::Bits(Bits::BitOr),
1141        b"bit-xor" => Operator::Bits(Bits::BitXor),
1142        b"bit-and" => Operator::Bits(Bits::BitAnd),
1143        b"bit-shl" => Operator::Bits(Bits::ShiftLeft),
1144        b"bit-shr" => Operator::Bits(Bits::ShiftRight),
1145        b"or" => Operator::Boolean(Boolean::Or),
1146        b"xor" => Operator::Boolean(Boolean::Xor),
1147        b"and" => Operator::Boolean(Boolean::And),
1148        // WARNING: not actual operators below! Error handling only
1149        pow @ (b"^" | b"pow") => {
1150            working_set.error(ParseError::UnknownOperator(
1151                match pow {
1152                    b"^" => "^",
1153                    b"pow" => "pow",
1154                    _ => unreachable!(),
1155                },
1156                "Use '**' for exponentiation or 'bit-xor' for bitwise XOR.",
1157                span,
1158            ));
1159            return garbage(working_set, span);
1160        }
1161        equality @ (b"is" | b"===") => {
1162            working_set.error(ParseError::UnknownOperator(
1163                match equality {
1164                    b"is" => "is",
1165                    b"===" => "===",
1166                    _ => unreachable!(),
1167                },
1168                "Did you mean '=='?",
1169                span,
1170            ));
1171            return garbage(working_set, span);
1172        }
1173        b"contains" => {
1174            working_set.error(ParseError::UnknownOperator(
1175                "contains",
1176                "Did you mean 'has'?",
1177                span,
1178            ));
1179            return garbage(working_set, span);
1180        }
1181        b"%" => {
1182            working_set.error(ParseError::UnknownOperator(
1183                "%",
1184                "Did you mean 'mod'?",
1185                span,
1186            ));
1187            return garbage(working_set, span);
1188        }
1189        b"&" => {
1190            working_set.error(ParseError::UnknownOperator(
1191                "&",
1192                "Did you mean 'bit-and'?",
1193                span,
1194            ));
1195            return garbage(working_set, span);
1196        }
1197        b"<<" => {
1198            working_set.error(ParseError::UnknownOperator(
1199                "<<",
1200                "Did you mean 'bit-shl'?",
1201                span,
1202            ));
1203            return garbage(working_set, span);
1204        }
1205        b">>" => {
1206            working_set.error(ParseError::UnknownOperator(
1207                ">>",
1208                "Did you mean 'bit-shr'?",
1209                span,
1210            ));
1211            return garbage(working_set, span);
1212        }
1213        bits @ (b"bits-and" | b"bits-xor" | b"bits-or" | b"bits-shl" | b"bits-shr") => {
1214            working_set.error(ParseError::UnknownOperator(
1215                match bits {
1216                    b"bits-and" => "bits-and",
1217                    b"bits-xor" => "bits-xor",
1218                    b"bits-or" => "bits-or",
1219                    b"bits-shl" => "bits-shl",
1220                    b"bits-shr" => "bits-shr",
1221                    _ => unreachable!(),
1222                },
1223                match bits {
1224                    b"bits-and" => "Did you mean 'bit-and'?",
1225                    b"bits-xor" => "Did you mean 'bit-xor'?",
1226                    b"bits-or" => "Did you mean 'bit-or'?",
1227                    b"bits-shl" => "Did you mean 'bit-shl'?",
1228                    b"bits-shr" => "Did you mean 'bit-shr'?",
1229                    _ => unreachable!(),
1230                },
1231                span,
1232            ));
1233            return garbage(working_set, span);
1234        }
1235        op if is_assignment_operator(op) => {
1236            working_set.error(ParseError::Expected("a non-assignment operator", span));
1237            return garbage(working_set, span);
1238        }
1239        _ => {
1240            working_set.error(ParseError::Expected("operator", span));
1241            return garbage(working_set, span);
1242        }
1243    };
1244
1245    Expression::new(working_set, Expr::Operator(operator), span, Type::Any)
1246}
1247
1248pub fn parse_math_expression(
1249    working_set: &mut StateWorkingSet,
1250    spans: &[Span],
1251    lhs_row_var_id: Option<VarId>,
1252    input_type: Option<&Type>,
1253) -> Expression {
1254    trace!("parsing: math expression");
1255
1256    // As the expr_stack grows, we increase the required precedence to grow larger
1257    // If, at any time, the operator we're looking at is the same or lower precedence
1258    // of what is in the expression stack, we collapse the expression stack.
1259    //
1260    // This leads to an expression stack that grows under increasing precedence and collapses
1261    // under decreasing/sustained precedence
1262    //
1263
1264    // The end result is a stack that we can fold into binary operations as right associations
1265    // safely.
1266
1267    let mut expr_stack: Vec<Expression> = vec![];
1268
1269    let mut idx = 0;
1270    let mut last_prec = u8::MAX;
1271
1272    let first_span = working_set.get_span_contents(spans[0]);
1273
1274    let mut not_start_spans = vec![];
1275
1276    if first_span == b"if" || first_span == b"match" {
1277        // If expression
1278        if spans.len() > 1 {
1279            return parse_call(working_set, spans, spans[0], input_type);
1280        } else {
1281            working_set.error(ParseError::Expected(
1282                "expression",
1283                Span::new(spans[0].end, spans[0].end),
1284            ));
1285            return garbage(working_set, spans[0]);
1286        }
1287    } else if first_span == b"not" {
1288        not_start_spans.push(spans[idx].start);
1289        idx += 1;
1290        while idx < spans.len() {
1291            let next_value = working_set.get_span_contents(spans[idx]);
1292
1293            if next_value == b"not" {
1294                not_start_spans.push(spans[idx].start);
1295                idx += 1;
1296            } else {
1297                break;
1298            }
1299        }
1300
1301        if idx == spans.len() {
1302            working_set.error(ParseError::Expected(
1303                "expression",
1304                Span::new(spans[idx - 1].end, spans[idx - 1].end),
1305            ));
1306            return garbage(working_set, spans[idx - 1]);
1307        }
1308    }
1309
1310    let mut lhs = parse_value(working_set, spans[idx], &SyntaxShape::Any, input_type);
1311
1312    for not_start_span in not_start_spans.iter().rev() {
1313        lhs = Expression::new(
1314            working_set,
1315            Expr::UnaryNot(Box::new(lhs)),
1316            Span::new(*not_start_span, spans[idx].end),
1317            Type::Bool,
1318        );
1319    }
1320    not_start_spans.clear();
1321
1322    idx += 1;
1323
1324    if idx >= spans.len() {
1325        // We already found the one part of our expression, so let's expand
1326        if let Some(row_var_id) = lhs_row_var_id {
1327            expand_to_cell_path(working_set, &mut lhs, row_var_id, input_type);
1328        }
1329    }
1330
1331    expr_stack.push(lhs);
1332
1333    while idx < spans.len() {
1334        let op = parse_operator(working_set, spans[idx]);
1335
1336        let op_prec = op.precedence();
1337
1338        idx += 1;
1339
1340        if idx == spans.len() {
1341            // Handle broken math expr `1 +` etc
1342            working_set.error(ParseError::IncompleteMathExpression(spans[idx - 1]));
1343
1344            expr_stack.push(Expression::garbage(working_set, spans[idx - 1]));
1345            let missing_span = Span::new(spans[idx - 1].end, spans[idx - 1].end);
1346            expr_stack.push(Expression::garbage(working_set, missing_span));
1347
1348            break;
1349        }
1350
1351        let content = working_set.get_span_contents(spans[idx]);
1352        // allow `if` to be a special value for assignment.
1353
1354        if content == b"if" || content == b"match" {
1355            let rhs = parse_call(working_set, &spans[idx..], spans[0], None);
1356            expr_stack.push(op);
1357            expr_stack.push(rhs);
1358            break;
1359        } else if content == b"not" {
1360            not_start_spans.push(spans[idx].start);
1361            idx += 1;
1362            while idx < spans.len() {
1363                let next_value = working_set.get_span_contents(spans[idx]);
1364
1365                if next_value == b"not" {
1366                    not_start_spans.push(spans[idx].start);
1367                    idx += 1;
1368                } else {
1369                    break;
1370                }
1371            }
1372
1373            if idx == spans.len() {
1374                working_set.error(ParseError::Expected(
1375                    "expression",
1376                    Span::new(spans[idx - 1].end, spans[idx - 1].end),
1377                ));
1378                return garbage(working_set, spans[idx - 1]);
1379            }
1380        }
1381        let mut rhs = parse_value(working_set, spans[idx], &SyntaxShape::Any, None);
1382
1383        for not_start_span in not_start_spans.iter().rev() {
1384            rhs = Expression::new(
1385                working_set,
1386                Expr::UnaryNot(Box::new(rhs)),
1387                Span::new(*not_start_span, spans[idx].end),
1388                Type::Bool,
1389            );
1390        }
1391        not_start_spans.clear();
1392
1393        // Parsing power must be right-associative unlike most operations which are left
1394        // Hence, we should not collapse if the last and current operations are both power
1395        let is_left_associative =
1396            op.expr != Expr::Operator(Operator::Math(Math::Pow)) && op_prec <= last_prec;
1397
1398        while is_left_associative && expr_stack.len() > 1 {
1399            // Collapse the right associated operations first
1400            // so that we can get back to a stack with a lower precedence
1401            let mut rhs = expr_stack
1402                .pop()
1403                .expect("internal error: expression stack empty");
1404            let mut op = expr_stack
1405                .pop()
1406                .expect("internal error: expression stack empty");
1407
1408            last_prec = op.precedence();
1409
1410            if last_prec < op_prec {
1411                expr_stack.push(op);
1412                expr_stack.push(rhs);
1413                break;
1414            }
1415
1416            let mut lhs = expr_stack
1417                .pop()
1418                .expect("internal error: expression stack empty");
1419
1420            if let Some(row_var_id) = lhs_row_var_id {
1421                expand_to_cell_path(working_set, &mut lhs, row_var_id, None);
1422            }
1423
1424            let (result_ty, err) = math_result_type(working_set, &mut lhs, &mut op, &mut rhs);
1425            if let Some(err) = err {
1426                working_set.error(err);
1427            }
1428
1429            let op_span = Span::append(lhs.span, rhs.span);
1430            expr_stack.push(Expression::new(
1431                working_set,
1432                Expr::BinaryOp(Box::new(lhs), Box::new(op), Box::new(rhs)),
1433                op_span,
1434                result_ty,
1435            ));
1436        }
1437        expr_stack.push(op);
1438        expr_stack.push(rhs);
1439
1440        last_prec = op_prec;
1441
1442        idx += 1;
1443    }
1444
1445    while expr_stack.len() != 1 {
1446        let mut rhs = expr_stack
1447            .pop()
1448            .expect("internal error: expression stack empty");
1449        let mut op = expr_stack
1450            .pop()
1451            .expect("internal error: expression stack empty");
1452        let mut lhs = expr_stack
1453            .pop()
1454            .expect("internal error: expression stack empty");
1455
1456        if let Some(row_var_id) = lhs_row_var_id {
1457            expand_to_cell_path(working_set, &mut lhs, row_var_id, None);
1458        }
1459
1460        let (result_ty, err) = math_result_type(working_set, &mut lhs, &mut op, &mut rhs);
1461        if let Some(err) = err {
1462            working_set.error(err)
1463        }
1464
1465        let binary_op_span = Span::append(lhs.span, rhs.span);
1466        expr_stack.push(Expression::new(
1467            working_set,
1468            Expr::BinaryOp(Box::new(lhs), Box::new(op), Box::new(rhs)),
1469            binary_op_span,
1470            result_ty,
1471        ));
1472    }
1473
1474    expr_stack
1475        .pop()
1476        .expect("internal error: expression stack empty")
1477}
1478
1479pub fn parse_expression(
1480    working_set: &mut StateWorkingSet,
1481    spans: &[Span],
1482    input_type: Option<&Type>,
1483) -> Expression {
1484    trace!("parsing: expression");
1485
1486    let mut pos = 0;
1487    let mut shorthand = vec![];
1488
1489    while pos < spans.len() {
1490        // Check if there is any environment shorthand
1491        let name = working_set.get_span_contents(spans[pos]);
1492
1493        let split: Vec<_> = name.splitn(2, |x| *x == b'=').collect();
1494        if split.len() != 2 || !is_env_variable_name(split[0]) {
1495            break;
1496        }
1497
1498        let point = split[0].len() + 1;
1499        let starting_error_count = working_set.parse_errors.len();
1500
1501        let rhs = if spans[pos].start + point < spans[pos].end {
1502            let rhs_span = Span::new(spans[pos].start + point, spans[pos].end);
1503            if split[1].starts_with(b"$") {
1504                parse_dollar_expr(working_set, rhs_span, &SyntaxShape::Any, None)
1505            } else {
1506                parse_string_strict(working_set, rhs_span)
1507            }
1508        } else {
1509            Expression::new(
1510                working_set,
1511                Expr::String(String::new()),
1512                Span::unknown(),
1513                Type::Nothing,
1514            )
1515        };
1516
1517        let lhs_span = Span::new(spans[pos].start, spans[pos].start + point - 1);
1518        let lhs = parse_string_strict(working_set, lhs_span);
1519
1520        if starting_error_count == working_set.parse_errors.len() {
1521            shorthand.push((lhs, rhs));
1522            pos += 1;
1523        } else {
1524            working_set.parse_errors.truncate(starting_error_count);
1525            break;
1526        }
1527    }
1528
1529    if pos == spans.len() {
1530        working_set.error(ParseError::UnknownCommand(spans[0]));
1531        return garbage(working_set, Span::concat(spans));
1532    }
1533
1534    let output = if spans[pos..]
1535        .iter()
1536        .any(|span| is_assignment_operator(working_set.get_span_contents(*span)))
1537    {
1538        parse_assignment_expression(working_set, &spans[pos..], input_type)
1539    } else if is_math_expression_like(working_set, spans[pos]) {
1540        parse_math_expression(working_set, &spans[pos..], None, input_type)
1541    } else {
1542        let bytes = working_set.get_span_contents(spans[pos]).to_vec();
1543
1544        // For now, check for special parses of certain keywords
1545        match bytes.as_slice() {
1546            b"def" | b"extern" | b"for" | b"module" | b"use" | b"source" | b"alias" | b"export"
1547            | b"export-env" | b"hide" => {
1548                working_set.error(ParseError::BuiltinCommandInPipeline(
1549                    String::from_utf8(bytes)
1550                        .expect("builtin commands bytes should be able to convert to string"),
1551                    spans[0],
1552                ));
1553
1554                parse_call(working_set, &spans[pos..], spans[0], input_type)
1555            }
1556            b"const" | b"mut" => {
1557                working_set.error(ParseError::AssignInPipeline(
1558                    String::from_utf8(bytes)
1559                        .expect("builtin commands bytes should be able to convert to string"),
1560                    String::from_utf8_lossy(match spans.len() {
1561                        1..=3 => b"value",
1562                        _ => working_set.get_span_contents(spans[3]),
1563                    })
1564                    .to_string(),
1565                    String::from_utf8_lossy(match spans.len() {
1566                        1 => b"variable",
1567                        _ => working_set.get_span_contents(spans[1]),
1568                    })
1569                    .to_string(),
1570                    spans[0],
1571                ));
1572                parse_call(working_set, &spans[pos..], spans[0], input_type)
1573            }
1574            b"overlay" => {
1575                if spans.len() > 1 && working_set.get_span_contents(spans[1]) == b"list" {
1576                    // whitelist 'overlay list'
1577                    parse_call(working_set, &spans[pos..], spans[0], input_type)
1578                } else {
1579                    working_set.error(ParseError::BuiltinCommandInPipeline(
1580                        "overlay".into(),
1581                        spans[0],
1582                    ));
1583
1584                    parse_call(working_set, &spans[pos..], spans[0], input_type)
1585                }
1586            }
1587            b"where" => parse_where_expr(working_set, &spans[pos..]),
1588            b"run" => parse_run_expr(working_set, &spans[pos..]),
1589            #[cfg(feature = "plugin")]
1590            b"plugin" => {
1591                if spans.len() > 1 && working_set.get_span_contents(spans[1]) == b"use" {
1592                    // only 'plugin use' is banned
1593                    working_set.error(ParseError::BuiltinCommandInPipeline(
1594                        "plugin use".into(),
1595                        spans[0],
1596                    ));
1597                }
1598
1599                parse_call(working_set, &spans[pos..], spans[0], input_type)
1600            }
1601
1602            _ => parse_call(working_set, &spans[pos..], spans[0], input_type),
1603        }
1604    };
1605
1606    if !shorthand.is_empty() {
1607        let with_env = working_set.find_decl(b"with-env");
1608        if let Some(decl_id) = with_env {
1609            let mut block = Block::default();
1610            let ty = output.ty.clone();
1611            block.pipelines = vec![Pipeline::from_vec(vec![output])];
1612            block.span = Some(Span::concat(spans));
1613
1614            compile_block(working_set, &mut block);
1615
1616            let block_id = working_set.add_block(Arc::new(block));
1617
1618            let mut env_vars = vec![];
1619            for sh in shorthand {
1620                env_vars.push(RecordItem::Pair(sh.0, sh.1));
1621            }
1622
1623            let arguments = vec![
1624                Argument::Positional(Expression::new(
1625                    working_set,
1626                    Expr::Record(env_vars),
1627                    Span::concat(&spans[..pos]),
1628                    Type::Any,
1629                )),
1630                Argument::Positional(Expression::new(
1631                    working_set,
1632                    Expr::Closure(block_id),
1633                    Span::concat(&spans[pos..]),
1634                    Type::Closure,
1635                )),
1636            ];
1637
1638            let expr = Expr::Call(Box::new(Call {
1639                head: Span::unknown(),
1640                decl_id,
1641                arguments,
1642                parser_info: HashMap::new(),
1643            }));
1644
1645            Expression::new(working_set, expr, Span::concat(spans), ty)
1646        } else {
1647            output
1648        }
1649    } else {
1650        output
1651    }
1652}
1653
1654pub fn parse_builtin_commands(
1655    working_set: &mut StateWorkingSet,
1656    lite_command: &LiteCommand,
1657    input_type: Option<&Type>,
1658) -> Pipeline {
1659    trace!("parsing: builtin commands");
1660    if !is_math_expression_like(working_set, lite_command.parts[0])
1661        && !is_unaliasable_parser_keyword(working_set, &lite_command.parts)
1662    {
1663        trace!("parsing: not math expression or unaliasable parser keyword");
1664        let name = working_set.get_span_contents(lite_command.parts[0]);
1665        if let Some(decl_id) = working_set.find_decl(name) {
1666            let cmd = working_set.get_decl(decl_id);
1667            if cmd.is_alias() {
1668                // Parse keywords that can be aliased. Note that we check for "unaliasable" keywords
1669                // because alias can have any name, therefore, we can't check for "aliasable" keywords.
1670                let call_expr = parse_call(
1671                    working_set,
1672                    &lite_command.parts,
1673                    lite_command.parts[0],
1674                    None,
1675                );
1676
1677                if let Expression {
1678                    expr: Expr::Call(call),
1679                    ..
1680                } = call_expr
1681                    && !call
1682                        .parser_info
1683                        .contains_key(PERCENT_FORCED_BUILTIN_PARSER_INFO)
1684                {
1685                    // Apply parse keyword side effects
1686                    let cmd = working_set.get_decl(call.decl_id);
1687                    match cmd.name() {
1688                        "overlay hide" => return parse_overlay_hide(working_set, call),
1689                        "overlay new" => return parse_overlay_new(working_set, call),
1690                        "overlay use" => return parse_overlay_use(working_set, call),
1691                        _ => { /* this alias is not a parser keyword */ }
1692                    }
1693                }
1694            }
1695        }
1696    }
1697
1698    trace!("parsing: checking for keywords");
1699    let name = lite_command
1700        .command_parts()
1701        .first()
1702        .map(|s| working_set.get_span_contents(*s))
1703        .unwrap_or(b"");
1704
1705    match name {
1706        // `parse_def` and `parse_extern` work both with and without attributes
1707        b"def" => parse_def(working_set, lite_command, None).0,
1708        b"extern" => parse_extern(working_set, lite_command, None),
1709        // `parse_export_in_block` also handles attributes by itself
1710        b"export" => parse_export_in_block(working_set, lite_command),
1711        b"export-env" => parse_export_env(working_set, &lite_command.parts).0,
1712        // Other definitions can't have attributes, so we handle attributes here with parse_attribute_block
1713        _ if lite_command.has_attributes() => parse_attribute_block(working_set, lite_command),
1714        b"let" => parse_let(
1715            working_set,
1716            &lite_command
1717                .parts_including_redirection()
1718                .collect::<Vec<Span>>(),
1719            input_type,
1720        ),
1721        b"const" => parse_const(working_set, &lite_command.parts).0,
1722        b"mut" => parse_mut(
1723            working_set,
1724            &lite_command
1725                .parts_including_redirection()
1726                .collect::<Vec<Span>>(),
1727        ),
1728        b"for" => {
1729            let expr = parse_for(working_set, lite_command);
1730            Pipeline::from_vec(vec![expr])
1731        }
1732        b"alias" => parse_alias(working_set, lite_command, None),
1733        b"module" => parse_module(working_set, lite_command, None).0,
1734        b"use" => parse_use(working_set, lite_command, None).0,
1735        b"overlay" => {
1736            if let Some(redirection) = lite_command.redirection.as_ref() {
1737                working_set.error(redirecting_builtin_error("overlay", redirection));
1738                return garbage_pipeline(working_set, &lite_command.parts);
1739            }
1740            parse_keyword(working_set, lite_command)
1741        }
1742        b"source" | b"source-env" => parse_source(working_set, lite_command),
1743        b"run" => parse_run(working_set, lite_command),
1744        b"hide" => parse_hide(working_set, lite_command),
1745        b"where" => parse_where(working_set, lite_command),
1746        // Only "plugin use" is a keyword
1747        #[cfg(feature = "plugin")]
1748        b"plugin"
1749            if lite_command
1750                .parts
1751                .get(1)
1752                .is_some_and(|span| working_set.get_span_contents(*span) == b"use") =>
1753        {
1754            if let Some(redirection) = lite_command.redirection.as_ref() {
1755                working_set.error(redirecting_builtin_error("plugin use", redirection));
1756                return garbage_pipeline(working_set, &lite_command.parts);
1757            }
1758            parse_keyword(working_set, lite_command)
1759        }
1760        _ => {
1761            let element =
1762                parse_pipeline_element(working_set, lite_command, input_type.unwrap_or(&Type::Any));
1763
1764            // There is still a chance to make `parse_pipeline_element` parse into
1765            // some keyword that should apply side effects first, Example:
1766            //
1767            // module a { export alias b = overlay use first.nu };
1768            // use a
1769            // a b
1770            //
1771            // In this case, `a b` will be parsed as a pipeline element, which leads
1772            // to the `overlay use` command.
1773            // In this case, we need to ensure that the side effects of these keywords
1774            // are applied.
1775            if let Expression {
1776                expr: Expr::Call(call),
1777                ..
1778            } = &element.expr
1779            {
1780                // Dynamic percent dispatch stores a placeholder call plus parser
1781                // metadata for later IR rewrite. Skip parser-keyword side-effects lookup here,
1782                // because there is no declaration to resolve yet.
1783                if call
1784                    .parser_info
1785                    .contains_key(PERCENT_FORCED_BUILTIN_PARSER_INFO)
1786                {
1787                    return Pipeline {
1788                        elements: vec![element],
1789                    };
1790                }
1791
1792                // Apply parse keyword side effects
1793                let cmd = working_set.get_decl(call.decl_id);
1794                match cmd.name() {
1795                    "overlay hide" => return parse_overlay_hide(working_set, call.clone()),
1796                    "overlay new" => return parse_overlay_new(working_set, call.clone()),
1797                    "overlay use" => return parse_overlay_use(working_set, call.clone()),
1798                    _ => { /* this alias is not a parser keyword */ }
1799                }
1800            }
1801            Pipeline {
1802                elements: vec![element],
1803            }
1804        }
1805    }
1806}
1807
1808fn check_record_key_or_value(
1809    working_set: &StateWorkingSet,
1810    expr: &Expression,
1811    position: &str,
1812) -> Option<ParseError> {
1813    let bareword_error = |string_value: &Expression| {
1814        working_set
1815            .get_span_contents(string_value.span)
1816            .iter()
1817            .find_position(|b| **b == b':')
1818            .map(|(i, _)| {
1819                let colon_position = i + string_value.span.start;
1820                ParseError::InvalidLiteral(
1821                    "colon".to_string(),
1822                    format!("bare word specifying record {position}"),
1823                    Span::new(colon_position, colon_position + 1),
1824                )
1825            })
1826    };
1827    let value_span = working_set.get_span_contents(expr.span);
1828    match expr.expr {
1829        Expr::String(_) => {
1830            if ![b'"', b'\'', b'`'].contains(&value_span[0]) {
1831                bareword_error(expr)
1832            } else {
1833                None
1834            }
1835        }
1836        Expr::StringInterpolation(ref expressions) => {
1837            if value_span[0] != b'$' {
1838                expressions
1839                    .iter()
1840                    .filter(|expr| matches!(expr.expr, Expr::String(_)))
1841                    .filter_map(bareword_error)
1842                    .next()
1843            } else {
1844                None
1845            }
1846        }
1847        _ => None,
1848    }
1849}
1850
1851pub fn parse_record(working_set: &mut StateWorkingSet, span: Span) -> Expression {
1852    let bytes = working_set.get_span_contents(span);
1853
1854    let mut start = span.start;
1855    let mut end = span.end;
1856
1857    if bytes.starts_with(b"{") {
1858        start += 1;
1859    } else {
1860        working_set.error(ParseError::Expected("{", Span::new(start, start + 1)));
1861        return garbage(working_set, span);
1862    }
1863
1864    let mut unclosed = false;
1865    let mut extra_tokens = false;
1866    if bytes.ends_with(b"}") {
1867        end -= 1;
1868    } else {
1869        unclosed = true;
1870    }
1871
1872    let inner_span = Span::new(start, end);
1873
1874    let mut lex_state = LexState {
1875        input: working_set.get_span_contents(inner_span),
1876        output: Vec::new(),
1877        error: None,
1878        span_offset: start,
1879    };
1880    while !lex_state.input.is_empty() {
1881        if let Some(ParseError::Unbalanced(left, right, _)) = lex_state.error.as_ref()
1882            && *left == "{"
1883            && *right == "}"
1884        {
1885            extra_tokens = true;
1886            unclosed = false;
1887            break;
1888        }
1889        let additional_whitespace = &[b'\n', b'\r', b','];
1890        if lex_n_tokens(&mut lex_state, additional_whitespace, &[b':'], true, 1) < 1 {
1891            break;
1892        };
1893        let span = lex_state
1894            .output
1895            .last()
1896            .expect("should have gotten 1 token")
1897            .span;
1898        let contents = working_set.get_span_contents(span);
1899        if extract_spread_record(contents.into_spanned(span)).is_some() {
1900            // This was a spread operator, so there's no value
1901            continue;
1902        }
1903        // Get token for colon
1904        if lex_n_tokens(&mut lex_state, additional_whitespace, &[b':'], true, 1) < 1 {
1905            break;
1906        };
1907        // Get token for value
1908        if lex_n_tokens(&mut lex_state, additional_whitespace, &[], true, 1) < 1 {
1909            break;
1910        };
1911    }
1912    let (tokens, err) = (lex_state.output, lex_state.error);
1913
1914    if unclosed {
1915        working_set.error(ParseError::Unclosed("}", Span::new(end, end)));
1916    } else if extra_tokens {
1917        working_set.error(ParseError::ExtraTokensAfterClosingDelimiter(Span::new(
1918            lex_state.span_offset,
1919            end,
1920        )));
1921    }
1922
1923    if let Some(err) = err {
1924        working_set.error(err);
1925    }
1926
1927    let mut output = vec![];
1928    let mut idx = 0;
1929
1930    let mut field_types = Some(vec![]);
1931    while idx < tokens.len() {
1932        let curr_span = tokens[idx].span;
1933        let curr_tok = working_set.get_span_contents(curr_span);
1934        if let Some(Spanned { span, .. }) = extract_spread_record(curr_tok.into_spanned(curr_span))
1935        {
1936            // Parse spread operator
1937            let inner = parse_value(working_set, span, &SyntaxShape::record(), None);
1938            idx += 1;
1939
1940            match &inner.ty {
1941                Type::Record(inner_fields) => {
1942                    if let Some(fields) = &mut field_types {
1943                        for (field, ty) in inner_fields.iter() {
1944                            fields.push((field.clone(), ty.clone()));
1945                        }
1946                    }
1947                }
1948                _ => {
1949                    // We can't properly see all the field types
1950                    // so fall back to the Any type later
1951                    field_types = None;
1952                }
1953            }
1954            output.push(RecordItem::Spread(
1955                Span::new(curr_span.start, curr_span.start + 3),
1956                inner,
1957            ));
1958        } else {
1959            // Normal key-value pair
1960            let field_token = &tokens[idx];
1961            let field = if field_token.contents != TokenContents::Item {
1962                working_set.error(ParseError::Expected(
1963                    "item in record key position",
1964                    Span::new(field_token.span.start, field_token.span.end),
1965                ));
1966                garbage(working_set, curr_span)
1967            } else {
1968                let field = parse_value(working_set, curr_span, &SyntaxShape::String, None);
1969                if let Some(error) = check_record_key_or_value(working_set, &field, "key") {
1970                    working_set.error(error);
1971                    garbage(working_set, field.span)
1972                } else {
1973                    field
1974                }
1975            };
1976
1977            idx += 1;
1978            if idx == tokens.len() {
1979                working_set.error(ParseError::Expected(
1980                    "':'",
1981                    Span::new(curr_span.end, curr_span.end),
1982                ));
1983                output.push(RecordItem::Pair(
1984                    garbage(working_set, curr_span),
1985                    garbage(working_set, Span::new(curr_span.end, curr_span.end)),
1986                ));
1987                break;
1988            }
1989            let colon_span = tokens[idx].span;
1990            let colon = working_set.get_span_contents(colon_span);
1991            idx += 1;
1992            if colon != b":" {
1993                working_set.error(ParseError::Expected(
1994                    "':'",
1995                    Span::new(colon_span.start, colon_span.start),
1996                ));
1997                output.push(RecordItem::Pair(
1998                    field,
1999                    garbage(
2000                        working_set,
2001                        Span::new(colon_span.start, tokens[tokens.len() - 1].span.end),
2002                    ),
2003                ));
2004                break;
2005            }
2006            if idx == tokens.len() {
2007                working_set.error(ParseError::Expected(
2008                    "value for record field",
2009                    Span::new(colon_span.end, colon_span.end),
2010                ));
2011                output.push(RecordItem::Pair(
2012                    garbage(working_set, Span::new(curr_span.start, colon_span.end)),
2013                    garbage(
2014                        working_set,
2015                        Span::new(colon_span.end, tokens[tokens.len() - 1].span.end),
2016                    ),
2017                ));
2018                break;
2019            }
2020
2021            let value_token = &tokens[idx];
2022            let value = if value_token.contents != TokenContents::Item {
2023                working_set.error(ParseError::Expected(
2024                    "item in record value position",
2025                    Span::new(value_token.span.start, value_token.span.end),
2026                ));
2027                garbage(
2028                    working_set,
2029                    Span::new(value_token.span.start, value_token.span.end),
2030                )
2031            } else {
2032                let value = parse_value(working_set, tokens[idx].span, &SyntaxShape::Any, None);
2033                if let Some(parse_error) = check_record_key_or_value(working_set, &value, "value") {
2034                    working_set.error(parse_error);
2035                    garbage(working_set, value.span)
2036                } else {
2037                    value
2038                }
2039            };
2040            idx += 1;
2041
2042            if let Some(field) = field.as_string() {
2043                if let Some(fields) = &mut field_types {
2044                    fields.push((field, value.ty.clone()));
2045                }
2046            } else {
2047                // We can't properly see all the field types
2048                // so fall back to the Any type later
2049                field_types = None;
2050            }
2051            output.push(RecordItem::Pair(field, value));
2052        }
2053    }
2054
2055    Expression::new(
2056        working_set,
2057        Expr::Record(output),
2058        span,
2059        if let Some(fields) = field_types {
2060            Type::Record(fields.into())
2061        } else {
2062            Type::Any
2063        },
2064    )
2065}