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 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 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 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 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 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 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 working_set.enter_scope();
495
496 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 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 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 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 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 let (signature, amt_to_skip): (Option<(Box<Signature>, Span)>, usize) = match output.first() {
717 Some(Token {
718 contents: TokenContents::Pipe,
719 span,
720 }) => {
721 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 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 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 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 | 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 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_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 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 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 let mut lhs = parse_expression(working_set, lhs_spans, None);
1057 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 _ => { }
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 b"def" => parse_def(working_set, lite_command, None).0,
1728 b"extern" => parse_extern(working_set, lite_command, None),
1729 b"export" => parse_export_in_block(working_set, lite_command),
1731 b"export-env" => parse_export_env(working_set, &lite_command.parts).0,
1732 _ 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 #[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 if let Expression {
1796 expr: Expr::Call(call),
1797 ..
1798 } = &element.expr
1799 {
1800 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 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 _ => { }
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
1871fn 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 continue;
1945 }
1946 if lex_n_tokens(&mut lex_state, additional_whitespace, &[b':'], true, 1) < 1 {
1948 break;
1949 };
1950 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 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 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 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 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}