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