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