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