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