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