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