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