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