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