1#![allow(clippy::byte_char_slices)]
2
3use crate::{
4 Token, TokenContents,
5 lex::lex,
6 parse_helpers::{
7 SPREAD_OPERATOR_STR, extract_spread_record, garbage, is_variable, trim_quotes,
8 },
9 parse_pipelines::parse_block,
10 type_check::check_range_types,
11};
12use itertools::Itertools;
13use log::trace;
14use nu_protocol::{
15 DidYouMean, FilesizeUnit, IntoSpanned, ParseError, Span, Spanned, SyntaxShape, Type, Unit,
16 VarId, ast::*, casing::Casing, engine::StateWorkingSet,
17};
18use std::sync::Arc;
19
20use crate::parse_expressions::{
21 parse_block_expression, parse_closure_expression, parse_match_block_expression, parse_record,
22 parse_table_expression,
23};
24use crate::parse_signatures::parse_signature;
25
26pub fn parse_binary(working_set: &mut StateWorkingSet, span: Span) -> Expression {
27 trace!("parsing: binary");
28 let contents = working_set.get_span_contents(span);
29 if contents.starts_with(b"0x[") {
30 parse_binary_with_base(working_set, span, 16, 2, b"0x[", b"]")
31 } else if contents.starts_with(b"0o[") {
32 parse_binary_with_base(working_set, span, 8, 3, b"0o[", b"]")
33 } else if contents.starts_with(b"0b[") {
34 parse_binary_with_base(working_set, span, 2, 8, b"0b[", b"]")
35 } else {
36 working_set.error(ParseError::Expected("binary", span));
37 garbage(working_set, span)
38 }
39}
40
41fn parse_binary_with_base(
42 working_set: &mut StateWorkingSet,
43 span: Span,
44 base: u32,
45 min_digits_per_byte: usize,
46 prefix: &[u8],
47 suffix: &[u8],
48) -> Expression {
49 let token = working_set.get_span_contents(span);
50
51 if let Some(token) = token.strip_prefix(prefix)
52 && let Some(token) = token.strip_suffix(suffix)
53 {
54 let (lexed, err) = lex(
55 token,
56 span.start + prefix.len(),
57 &[b',', b'\r', b'\n'],
58 &[],
59 true,
60 );
61 if let Some(err) = err {
62 working_set.error(err);
63 }
64
65 let mut binary_value = vec![];
66 for token in lexed {
67 match token.contents {
68 TokenContents::Item => {
69 let contents = working_set.get_span_contents(token.span);
70
71 binary_value.extend_from_slice(contents);
72 }
73 TokenContents::Pipe
74 | TokenContents::PipePipe
75 | TokenContents::ErrGreaterPipe
76 | TokenContents::OutGreaterThan
77 | TokenContents::OutErrGreaterPipe
78 | TokenContents::OutGreaterGreaterThan
79 | TokenContents::ErrGreaterThan
80 | TokenContents::ErrGreaterGreaterThan
81 | TokenContents::OutErrGreaterThan
82 | TokenContents::OutErrGreaterGreaterThan
83 | TokenContents::AssignmentOperator => {
84 working_set.error(ParseError::Expected("binary", span));
85 return garbage(working_set, span);
86 }
87 TokenContents::Comment | TokenContents::Semicolon | TokenContents::Eol => {}
88 }
89 }
90
91 let required_padding =
92 (min_digits_per_byte - binary_value.len() % min_digits_per_byte) % min_digits_per_byte;
93
94 if required_padding != 0 {
95 binary_value = {
96 let mut tail = binary_value;
97 let mut binary_value: Vec<u8> = vec![b'0'; required_padding];
98 binary_value.append(&mut tail);
99 binary_value
100 };
101 }
102
103 let str = String::from_utf8_lossy(&binary_value).to_string();
104
105 match decode_with_base(&str, base, min_digits_per_byte) {
106 Ok(v) => return Expression::new(working_set, Expr::Binary(v), span, Type::Binary),
107 Err(help) => {
108 working_set.error(ParseError::InvalidBinaryString(span, help.to_string()));
109 return garbage(working_set, span);
110 }
111 }
112 }
113
114 working_set.error(ParseError::Expected("binary", span));
115 garbage(working_set, span)
116}
117
118fn decode_with_base(s: &str, base: u32, digits_per_byte: usize) -> Result<Vec<u8>, &str> {
119 s.chars()
120 .chunks(digits_per_byte)
121 .into_iter()
122 .map(|chunk| {
123 let str: String = chunk.collect();
124 u8::from_str_radix(&str, base).map_err(|_| match base {
125 2 => "binary strings may contain only 0 or 1.",
126 8 => "octal strings must have a length that is a multiple of three and contain values between 0o000 and 0o377.",
127 16 => "hexadecimal strings may contain only the characters 0–9 and A–F.",
128 _ => "internal error: radix other than 2, 8, or 16 is not allowed."
129 })
130 })
131 .collect()
132}
133
134fn strip_underscores(token: &[u8]) -> String {
135 String::from_utf8_lossy(token)
136 .chars()
137 .filter(|c| *c != '_')
138 .collect()
139}
140
141pub fn parse_int(working_set: &mut StateWorkingSet, span: Span) -> Expression {
142 let token = working_set.get_span_contents(span);
143
144 fn extract_int(
145 working_set: &mut StateWorkingSet,
146 token: &str,
147 span: Span,
148 radix: u32,
149 ) -> Expression {
150 if let Ok(num) = u64::from_str_radix(token, radix).map(|val| val as i64) {
153 Expression::new(working_set, Expr::Int(num), span, Type::Int)
154 } else {
155 working_set.error(ParseError::InvalidLiteral(
156 format!("invalid digits for radix {radix}"),
157 "int".into(),
158 span,
159 ));
160
161 garbage(working_set, span)
162 }
163 }
164
165 let token = strip_underscores(token);
166
167 if token.is_empty() {
168 working_set.error(ParseError::Expected("int", span));
169 return garbage(working_set, span);
170 }
171
172 if let Some(num) = token.strip_prefix("0b") {
173 extract_int(working_set, num, span, 2)
174 } else if let Some(num) = token.strip_prefix("0o") {
175 extract_int(working_set, num, span, 8)
176 } else if let Some(num) = token.strip_prefix("0x") {
177 extract_int(working_set, num, span, 16)
178 } else if let Ok(num) = token.parse::<i64>() {
179 Expression::new(working_set, Expr::Int(num), span, Type::Int)
180 } else {
181 working_set.error(ParseError::Expected("int", span));
182 garbage(working_set, span)
183 }
184}
185
186pub fn parse_float(working_set: &mut StateWorkingSet, span: Span) -> Expression {
187 let token = working_set.get_span_contents(span);
188 let token = strip_underscores(token);
189
190 if let Ok(x) = token.parse::<f64>() {
191 Expression::new(working_set, Expr::Float(x), span, Type::Float)
192 } else {
193 working_set.error(ParseError::Expected("float", span));
194
195 garbage(working_set, span)
196 }
197}
198
199pub fn parse_number(working_set: &mut StateWorkingSet, span: Span) -> Expression {
200 let starting_error_count = working_set.parse_errors.len();
201
202 let result = parse_int(working_set, span);
203 if starting_error_count == working_set.parse_errors.len() {
204 return result;
205 } else if let Some(ParseError::Expected(_, _)) = working_set.parse_errors.last() {
206 working_set.parse_errors.truncate(starting_error_count);
207 }
208
209 let result = parse_float(working_set, span);
210
211 if starting_error_count == working_set.parse_errors.len() {
212 return result;
213 }
214 working_set.parse_errors.truncate(starting_error_count);
215
216 working_set.error(ParseError::Expected("number", span));
217 garbage(working_set, span)
218}
219
220pub fn parse_range(working_set: &mut StateWorkingSet, span: Span) -> Option<Expression> {
221 trace!("parsing: range");
222 let starting_error_count = working_set.parse_errors.len();
223
224 let contents = working_set.get_span_contents(span);
232
233 let Ok(token) = String::from_utf8(contents.into()) else {
234 working_set.error(ParseError::NonUtf8(span));
235 return None;
236 };
237
238 if token.starts_with(SPREAD_OPERATOR_STR) {
239 working_set.error(ParseError::Expected(
240 "range operator ('..'), got spread ('...')",
241 span,
242 ));
243 return None;
244 }
245
246 if !token.contains("..") {
247 working_set.error(ParseError::Expected("at least one range bound set", span));
248 return None;
249 }
250
251 let dotdot_pos: Vec<_> = token
252 .match_indices("..")
253 .filter_map(|(pos, _)| {
254 let before = &token[..pos];
256 let paren_opened = before.chars().filter(|&c| c == '(').count();
257 let paren_closed = before.chars().filter(|&c| c == ')').count();
258 let paren_depth = paren_opened.checked_sub(paren_closed)?;
259 (paren_depth == 0).then_some(pos)
260 })
261 .collect();
262
263 let (next_op_pos, range_op_pos) = match dotdot_pos.len() {
264 1 => (None, dotdot_pos[0]),
265 2 => (Some(dotdot_pos[0]), dotdot_pos[1]),
266 _ => {
267 working_set.error(ParseError::Expected(
268 "one range operator ('..' or '..<') and optionally one next operator ('..')",
269 span,
270 ));
271 return None;
272 }
273 };
274 if dotdot_pos[0] > 0 {
277 let (_tokens, err) = lex(
278 &contents[..dotdot_pos[0]],
279 span.start,
280 &[],
281 &[b'.', b'?', b'!'],
282 true,
283 );
284 if let Some(_err) = err {
285 working_set.error(ParseError::Expected("Valid expression before ..", span));
286 return None;
287 }
288 }
289
290 let (inclusion, range_op_str, range_op_span) = if let Some(pos) = token.find("..<") {
291 if pos == range_op_pos {
292 let op_str = "..<";
293 let op_span = Span::new(
294 span.start + range_op_pos,
295 span.start + range_op_pos + op_str.len(),
296 );
297 (RangeInclusion::RightExclusive, "..<", op_span)
298 } else {
299 working_set.error(ParseError::Expected(
300 "inclusive operator preceding second range bound",
301 span,
302 ));
303 return None;
304 }
305 } else {
306 let op_str = if token[range_op_pos..].starts_with("..=") {
307 "..="
308 } else {
309 ".."
310 };
311
312 let op_span = Span::new(
313 span.start + range_op_pos,
314 span.start + range_op_pos + op_str.len(),
315 );
316 (RangeInclusion::Inclusive, op_str, op_span)
317 };
318
319 let from = if token.starts_with("..") {
323 None
325 } else {
326 let from_span = Span::new(span.start, span.start + dotdot_pos[0]);
327 Some(crate::parser::parse_value(
328 working_set,
329 from_span,
330 &SyntaxShape::Number,
331 None,
332 ))
333 };
334
335 let to = if token.ends_with(range_op_str) {
336 None
337 } else {
338 let to_span = Span::new(range_op_span.end, span.end);
339 Some(crate::parser::parse_value(
340 working_set,
341 to_span,
342 &SyntaxShape::Number,
343 None,
344 ))
345 };
346
347 trace!("-- from: {from:?} to: {to:?}");
348
349 if let (None, None) = (&from, &to) {
350 working_set.error(ParseError::Expected("at least one range bound set", span));
351 return None;
352 }
353
354 let (next, next_op_span) = if let Some(pos) = next_op_pos {
355 let next_op_span = Span::new(span.start + pos, span.start + pos + "..".len());
356 let next_span = Span::new(next_op_span.end, range_op_span.start);
357
358 (
359 Some(crate::parser::parse_value(
360 working_set,
361 next_span,
362 &SyntaxShape::Number,
363 None,
364 )),
365 next_op_span,
366 )
367 } else {
368 (None, span)
369 };
370
371 if working_set.parse_errors.len() != starting_error_count {
372 return None;
373 }
374
375 let operator = RangeOperator {
376 inclusion,
377 span: range_op_span,
378 next_op_span,
379 };
380
381 let mut range = Range {
382 from,
383 next,
384 to,
385 operator,
386 };
387
388 check_range_types(working_set, &mut range);
389
390 Some(Expression::new(
391 working_set,
392 Expr::Range(Box::new(range)),
393 span,
394 Type::Range,
395 ))
396}
397
398pub(crate) fn parse_dollar_expr(
399 working_set: &mut StateWorkingSet,
400 span: Span,
401 shape: &SyntaxShape,
402 input_type: Option<&Type>,
403) -> Expression {
404 trace!("parsing: dollar expression");
405 let contents = working_set.get_span_contents(span);
406
407 if contents.starts_with(b"$\"") || contents.starts_with(b"$'") {
408 if matches!(shape, SyntaxShape::GlobPattern) && is_bare_string_interpolation(contents) {
409 parse_glob_pattern(working_set, span)
410 } else {
411 parse_string_interpolation(working_set, span)
412 }
413 } else if contents.starts_with(b"$.") {
414 parse_simple_cell_path(working_set, Span::new(span.start + 2, span.end))
415 } else {
416 let starting_error_count = working_set.parse_errors.len();
417
418 if let Some(expr) = parse_range(working_set, span) {
419 expr
420 } else {
421 working_set.parse_errors.truncate(starting_error_count);
422 parse_full_cell_path(working_set, None, span, input_type)
423 }
424 }
425}
426
427pub fn parse_raw_string(working_set: &mut StateWorkingSet, span: Span) -> Expression {
428 trace!("parsing: raw-string, with required delimiters");
429
430 let bytes = working_set.get_span_contents(span);
431
432 let prefix_sharp_cnt = if bytes.starts_with(b"r#") {
433 let mut sharp_cnt = 1;
436 let mut index = 2;
437 while index < bytes.len() && bytes[index] == b'#' {
438 index += 1;
439 sharp_cnt += 1;
440 }
441 sharp_cnt
442 } else {
443 working_set.error(ParseError::Expected("r#", span));
444 return garbage(working_set, span);
445 };
446 let expect_postfix_sharp_cnt = prefix_sharp_cnt;
447 if bytes.len() < prefix_sharp_cnt + expect_postfix_sharp_cnt + 3 {
451 working_set.error(ParseError::Unclosed("'", span));
452 return garbage(working_set, span);
453 }
454
455 let postfix_bytes = &bytes[bytes.len() - expect_postfix_sharp_cnt..bytes.len()];
457 if postfix_bytes.iter().any(|b| *b != b'#') {
458 working_set.error(ParseError::Unbalanced("prefix #", "postfix #", span));
459 return garbage(working_set, span);
460 }
461 if bytes[1 + prefix_sharp_cnt] != b'\''
463 || bytes[bytes.len() - expect_postfix_sharp_cnt - 1] != b'\''
464 {
465 working_set.error(ParseError::Unclosed("'", span));
466 return garbage(working_set, span);
467 }
468
469 let bytes = &bytes[prefix_sharp_cnt + 1 + 1..bytes.len() - 1 - prefix_sharp_cnt];
470 if let Ok(token) = String::from_utf8(bytes.into()) {
471 Expression::new(working_set, Expr::RawString(token), span, Type::String)
472 } else {
473 working_set.error(ParseError::Expected("utf8 raw-string", span));
474 garbage(working_set, span)
475 }
476}
477
478pub fn parse_paren_expr(
479 working_set: &mut StateWorkingSet,
480 span: Span,
481 shape: &SyntaxShape,
482) -> Expression {
483 let starting_error_count = working_set.parse_errors.len();
484
485 if let Some(expr) = parse_range(working_set, span) {
486 return expr;
487 }
488
489 working_set.parse_errors.truncate(starting_error_count);
490
491 if let SyntaxShape::Signature = shape {
492 return parse_signature(working_set, span, false);
493 }
494
495 if let SyntaxShape::ExternalSignature = shape {
496 return parse_signature(working_set, span, true);
497 }
498
499 let fcp_expr = parse_full_cell_path(working_set, None, span, None);
500 let fcp_error_count = working_set.parse_errors.len();
501 if fcp_error_count > starting_error_count {
502 let malformed_subexpr = working_set.parse_errors[starting_error_count..]
503 .first()
504 .is_some_and(|e| match e {
505 ParseError::Unclosed(right, _) if (*right == ")") => true,
506 ParseError::Unbalanced(left, right, _) if *left == "(" && *right == ")" => true,
507 _ => false,
508 });
509 if malformed_subexpr {
510 working_set.parse_errors.truncate(starting_error_count);
511 if matches!(shape, SyntaxShape::GlobPattern) {
512 parse_glob_pattern(working_set, span)
513 } else {
514 parse_string_interpolation(working_set, span)
515 }
516 } else {
517 fcp_expr
518 }
519 } else {
520 fcp_expr
521 }
522}
523
524pub fn parse_brace_expr(
525 working_set: &mut StateWorkingSet,
526 span: Span,
527 shape: &SyntaxShape,
528 input_type: Option<&Type>,
529) -> Expression {
530 if span.end <= (span.start + 1) {
539 working_set.error(ParseError::ExpectedWithStringMsg(
540 format!("non-block value: {shape}"),
541 span,
542 ));
543 return Expression::garbage(working_set, span);
544 }
545 let bytes = working_set.get_span_contents(Span::new(span.start + 1, span.end - 1));
546 let (tokens, _) = lex(bytes, span.start + 1, &[b'\r', b'\n', b'\t'], &[b':'], true);
547
548 match tokens.as_slice() {
549 [] => match shape {
551 SyntaxShape::Closure(_) => parse_closure_expression(working_set, shape, span, None),
552 SyntaxShape::Block => parse_block_expression(working_set, span, input_type),
553 SyntaxShape::MatchBlock => parse_match_block_expression(working_set, span, input_type),
554 _ => parse_record(working_set, span),
555 },
556 [
557 Token {
558 contents: TokenContents::Pipe | TokenContents::PipePipe,
559 ..
560 },
561 ..,
562 ] => {
563 if let SyntaxShape::Block = shape {
564 working_set.error(ParseError::Mismatch("block".into(), "closure".into(), span));
565 return Expression::garbage(working_set, span);
566 }
567 parse_closure_expression(working_set, shape, span, None)
568 }
569 [_, third, ..] if working_set.get_span_contents(third.span) == b":" => {
570 parse_full_cell_path(working_set, None, span, None)
571 }
572 [second, ..] => {
573 let second_bytes = working_set.get_span_contents(second.span);
574 match shape {
575 SyntaxShape::Closure(_) => parse_closure_expression(working_set, shape, span, None),
576 SyntaxShape::Block => parse_block_expression(working_set, span, input_type),
577 SyntaxShape::MatchBlock => {
578 parse_match_block_expression(working_set, span, input_type)
579 }
580 _ if second_bytes == b"}" => parse_full_cell_path(working_set, None, span, None),
582 _ if extract_spread_record(second_bytes.into_spanned(second.span)).is_some() => {
583 parse_record(working_set, span)
584 }
585 SyntaxShape::Any => parse_closure_expression(working_set, shape, span, None),
586 _ => {
587 working_set.error(ParseError::ExpectedWithStringMsg(
588 format!("non-block value: {shape}"),
589 span,
590 ));
591
592 Expression::garbage(working_set, span)
593 }
594 }
595 }
596 }
597}
598
599pub fn parse_string_interpolation(working_set: &mut StateWorkingSet, span: Span) -> Expression {
600 #[derive(PartialEq, Eq, Debug)]
601 enum InterpolationMode {
602 String,
603 Expression,
604 }
605
606 let contents = working_set.get_span_contents(span);
607
608 let mut double_quote = false;
609
610 let (start, end) = if contents.starts_with(b"$\"") {
611 double_quote = true;
612
613 if let Err(err) = check_string_no_trailing_tokens(contents, span, 1, b'\"') {
614 working_set.error(err);
615 return garbage(working_set, span);
616 }
617
618 let end = if contents.ends_with(b"\"") && contents.len() > 2 {
619 span.end - 1
620 } else {
621 span.end
622 };
623 (span.start + 2, end)
624 } else if contents.starts_with(b"$'") {
625 if let Err(err) = check_string_no_trailing_tokens(contents, span, 1, b'\'') {
626 working_set.error(err);
627 return garbage(working_set, span);
628 }
629
630 let end = if contents.ends_with(b"'") && contents.len() > 2 {
631 span.end - 1
632 } else {
633 span.end
634 };
635 (span.start + 2, end)
636 } else {
637 (span.start, span.end)
638 };
639
640 let inner_span = Span::new(start, end);
641 let contents = working_set.get_span_contents(inner_span).to_vec();
642
643 let mut output = vec![];
644 let mut mode = InterpolationMode::String;
645 let mut token_start = start;
646
647 #[repr(u8)]
648 #[derive(Clone, Copy, PartialEq, Eq)]
649 enum Delimiter {
650 SingleQuote = b'\'',
651 DoubleQuote = b'"',
652 Backtick = b'`',
653 ParenLeft = b'(',
654 ParenRight = b')',
655 }
656
657 impl Delimiter {
658 const fn from_u8(b: u8) -> Option<Self> {
659 Some(match b {
660 b'\'' => Self::SingleQuote,
661 b'"' => Self::DoubleQuote,
662 b'`' => Self::Backtick,
663 b'(' => Self::ParenLeft,
664 b')' => Self::ParenRight,
665 _ => return None,
666 })
667 }
668 const fn is_paren(self) -> bool {
669 matches!(self, Self::ParenLeft | Self::ParenRight)
670 }
671 const fn pair(self) -> Self {
672 match self {
673 Self::ParenLeft => Self::ParenRight,
674 Self::ParenRight => Self::ParenLeft,
675 _ => self,
676 }
677 }
678 }
679 let mut delimiter_stack: Vec<Delimiter> = vec![];
680
681 let mut consecutive_backslashes: usize = 0;
682
683 let mut b = start;
684
685 while b != end {
686 let current_byte = contents[b - start];
687
688 if mode == InterpolationMode::String {
689 let preceding_consecutive_backslashes = consecutive_backslashes;
690
691 let is_backslash = current_byte == b'\\';
692 consecutive_backslashes = if is_backslash {
693 preceding_consecutive_backslashes + 1
694 } else {
695 0
696 };
697
698 if current_byte == b'('
699 && (!double_quote || preceding_consecutive_backslashes.is_multiple_of(2))
700 {
701 mode = InterpolationMode::Expression;
702 if token_start < b {
703 let span = Span::new(token_start, b);
704 let str_contents = working_set.get_span_contents(span);
705
706 let (str_contents, err) = if double_quote {
707 unescape_string(str_contents, span)
708 } else {
709 (str_contents.to_vec(), None)
710 };
711 if let Some(err) = err {
712 working_set.error(err);
713 }
714
715 output.push(Expression::new(
716 working_set,
717 Expr::String(String::from_utf8_lossy(&str_contents).to_string()),
718 span,
719 Type::String,
720 ));
721 token_start = b;
722 }
723 }
724 }
725
726 if mode == InterpolationMode::Expression {
727 let byte = Delimiter::from_u8(current_byte);
728 match (delimiter_stack.last().copied(), byte) {
729 (Some(d), Some(byte)) if !d.is_paren() => {
730 if byte == d {
731 delimiter_stack.pop();
732 }
733 }
734 (_, Some(byte)) if byte != Delimiter::ParenRight => {
735 delimiter_stack.push(byte.pair())
736 }
737 (d, Some(Delimiter::ParenRight)) => {
738 if let Some(Delimiter::ParenRight) = d {
739 delimiter_stack.pop();
740 }
741 if delimiter_stack.is_empty() {
742 mode = InterpolationMode::String;
743
744 if token_start < b {
745 let span = Span::new(token_start, b + 1);
746
747 let expr = parse_full_cell_path(working_set, None, span, None);
748 output.push(expr);
749 }
750
751 token_start = b + 1;
752 continue;
753 }
754 }
755 _ => (),
756 }
757 }
758 b += 1;
759 }
760
761 match mode {
762 InterpolationMode::String => {
763 if token_start < end {
764 let span = Span::new(token_start, end);
765 let str_contents = working_set.get_span_contents(span);
766
767 let (str_contents, err) = if double_quote {
768 unescape_string(str_contents, span)
769 } else {
770 (str_contents.to_vec(), None)
771 };
772 if let Some(err) = err {
773 working_set.error(err);
774 }
775
776 output.push(Expression::new(
777 working_set,
778 Expr::String(String::from_utf8_lossy(&str_contents).to_string()),
779 span,
780 Type::String,
781 ));
782 }
783 }
784 InterpolationMode::Expression => {
785 if token_start < end {
786 let span = Span::new(token_start, end);
787 let expr = parse_full_cell_path(working_set, None, span, None);
788 output.push(expr);
789 }
790 }
791 }
792
793 Expression::new(
794 working_set,
795 Expr::StringInterpolation(output),
796 span,
797 Type::String,
798 )
799}
800
801pub fn parse_variable_expr(
802 working_set: &mut StateWorkingSet,
803 span: Span,
804 input_type: Option<&Type>,
805) -> Expression {
806 let contents = working_set.get_span_contents(span);
807
808 if contents == b"$nu" {
809 return Expression::new(
810 working_set,
811 Expr::Var(nu_protocol::NU_VARIABLE_ID),
812 span,
813 Type::Any,
814 );
815 } else if contents == b"$in" {
816 return Expression::new(
817 working_set,
818 Expr::Var(nu_protocol::IN_VARIABLE_ID),
819 span,
820 input_type.cloned().unwrap_or(Type::Any),
821 );
822 } else if contents == b"$env" {
823 return Expression::new(
824 working_set,
825 Expr::Var(nu_protocol::ENV_VARIABLE_ID),
826 span,
827 Type::Any,
828 );
829 }
830
831 let name = if contents.starts_with(b"$") {
832 String::from_utf8_lossy(&contents[1..]).to_string()
833 } else {
834 String::from_utf8_lossy(contents).to_string()
835 };
836
837 let bytes = working_set.get_span_contents(span);
838 let suggestion = || {
839 DidYouMean::new(
840 &working_set.list_variables(),
841 working_set.get_span_contents(span),
842 )
843 };
844 if !is_variable(bytes) {
845 working_set.error(ParseError::ExpectedWithDidYouMean(
846 "valid variable name",
847 suggestion(),
848 span,
849 ));
850 garbage(working_set, span)
851 } else if let Some(id) = working_set.find_variable(bytes) {
852 Expression::new(
853 working_set,
854 Expr::Var(id),
855 span,
856 working_set.get_variable(id).ty.clone(),
857 )
858 } else if working_set.get_env_var(&name).is_some() {
859 working_set.error(ParseError::EnvVarNotVar(name, span));
860 garbage(working_set, span)
861 } else {
862 working_set.error(ParseError::VariableNotFound(suggestion(), span));
863 garbage(working_set, span)
864 }
865}
866
867pub fn parse_cell_path(
868 working_set: &mut StateWorkingSet,
869 tokens: impl Iterator<Item = Token>,
870 expect_dot: bool,
871) -> Vec<PathMember> {
872 enum TokenType {
873 Dot, DotOrSign, DotOrExclamation, DotOrQuestion, PathMember, }
879
880 enum ModifyMember {
881 No,
882 Optional,
883 Insensitive,
884 }
885
886 impl TokenType {
887 fn expect(&mut self, byte: u8) -> Result<ModifyMember, &'static str> {
888 match (&*self, byte) {
889 (Self::PathMember, _) => {
890 *self = Self::DotOrSign;
891 Ok(ModifyMember::No)
892 }
893 (
894 Self::Dot | Self::DotOrSign | Self::DotOrExclamation | Self::DotOrQuestion,
895 b'.',
896 ) => {
897 *self = Self::PathMember;
898 Ok(ModifyMember::No)
899 }
900 (Self::DotOrSign, b'!') => {
901 *self = Self::DotOrQuestion;
902 Ok(ModifyMember::Insensitive)
903 }
904 (Self::DotOrSign, b'?') => {
905 *self = Self::DotOrExclamation;
906 Ok(ModifyMember::Optional)
907 }
908 (Self::DotOrSign, _) => Err(". or ! or ?"),
909 (Self::DotOrExclamation, b'!') => {
910 *self = Self::Dot;
911 Ok(ModifyMember::Insensitive)
912 }
913 (Self::DotOrExclamation, _) => Err(". or !"),
914 (Self::DotOrQuestion, b'?') => {
915 *self = Self::Dot;
916 Ok(ModifyMember::Optional)
917 }
918 (Self::DotOrQuestion, _) => Err(". or ?"),
919 (Self::Dot, _) => Err("."),
920 }
921 }
922 }
923
924 let mut expected_token = if expect_dot {
926 TokenType::Dot
927 } else {
928 TokenType::PathMember
929 };
930
931 let mut tail = vec![];
932
933 for path_element in tokens {
934 let bytes = working_set.get_span_contents(path_element.span);
935
936 let Some((&first, rest)) = bytes.split_first() else {
939 working_set.error(ParseError::Expected("string", path_element.span));
940 return tail;
941 };
942 let single_char = rest.is_empty();
943
944 if let TokenType::PathMember = expected_token {
945 let starting_error_count = working_set.parse_errors.len();
946
947 let expr = parse_int(working_set, path_element.span);
948 working_set.parse_errors.truncate(starting_error_count);
949
950 match expr {
951 Expression {
952 expr: Expr::Int(val),
953 span,
954 ..
955 } => {
956 if val < 0 {
957 working_set.error(ParseError::InvalidLiteral(
958 "negative index is not supported".into(),
959 "cell path".into(),
960 span,
961 ));
962 return tail;
963 }
964 tail.push(PathMember::Int {
965 val: val as usize,
966 span,
967 optional: false,
968 })
969 }
970 _ => {
971 let result = parse_string(working_set, path_element.span);
972 match result {
973 Expression {
974 expr: Expr::String(string),
975 span,
976 ..
977 } => {
978 tail.push(PathMember::String {
979 val: string,
980 span,
981 optional: false,
982 casing: Casing::Sensitive,
983 });
984 }
985 _ => {
986 working_set.error(ParseError::Expected("string", path_element.span));
987 return tail;
988 }
989 }
990 }
991 }
992 expected_token = TokenType::DotOrSign;
993 } else {
994 match expected_token.expect(if single_char { first } else { b' ' }) {
995 Ok(modify) => {
996 if let Some(last) = tail.last_mut() {
997 match modify {
998 ModifyMember::No => {}
999 ModifyMember::Optional => last.make_optional(),
1000 ModifyMember::Insensitive => last.make_insensitive(),
1001 }
1002 };
1003 }
1004 Err(expected) => {
1005 working_set.error(ParseError::Expected(expected, path_element.span));
1006 return tail;
1007 }
1008 }
1009 }
1010 }
1011
1012 tail
1013}
1014
1015pub fn parse_simple_cell_path(working_set: &mut StateWorkingSet, span: Span) -> Expression {
1016 let source = working_set.get_span_contents(span);
1017
1018 let (tokens, err) = lex(
1019 source,
1020 span.start,
1021 &[b'\n', b'\r'],
1022 &[b'.', b'?', b'!'],
1023 true,
1024 );
1025 if let Some(err) = err {
1026 working_set.error(err)
1027 }
1028
1029 let tokens = tokens.into_iter().peekable();
1030
1031 let cell_path = parse_cell_path(working_set, tokens, false);
1032
1033 Expression::new(
1034 working_set,
1035 Expr::CellPath(CellPath { members: cell_path }),
1036 span,
1037 Type::CellPath,
1038 )
1039}
1040
1041pub fn parse_full_cell_path(
1042 working_set: &mut StateWorkingSet,
1043 implicit_head: Option<VarId>,
1044 span: Span,
1045 input_type: Option<&Type>,
1046) -> Expression {
1047 trace!("parsing: full cell path");
1048 let full_cell_span = span;
1049 let source = working_set.get_span_contents(span);
1050
1051 let (tokens, err) = lex(
1052 source,
1053 span.start,
1054 &[b'\n', b'\r'],
1055 &[b'.', b'?', b'!'],
1056 true,
1057 );
1058 if let Some(err) = err {
1059 working_set.error(err)
1060 }
1061
1062 let mut tokens = tokens.into_iter().peekable();
1063 if let Some(head) = tokens.peek() {
1064 let bytes = working_set.get_span_contents(head.span);
1065 let (head, expect_dot) = if bytes.starts_with(b"(") {
1066 trace!("parsing: paren-head of full cell path");
1067
1068 let head_span = head.span;
1069 let mut start = head.span.start;
1070 let mut end = head.span.end;
1071 let mut is_closed = true;
1072
1073 if bytes.starts_with(b"(") {
1074 start += 1;
1075 }
1076 if bytes.ends_with(b")") {
1077 end -= 1;
1078 } else {
1079 working_set.error(ParseError::Unclosed(")", Span::new(end, end)));
1080 is_closed = false;
1081 }
1082
1083 let span = Span::new(start, end);
1084
1085 let source = working_set.get_span_contents(span);
1086
1087 let (output, err) = lex(source, span.start, &[b'\n', b'\r'], &[], true);
1088 if let Some(err) = err {
1089 working_set.error(err)
1090 }
1091
1092 let output = parse_block(working_set, &output, span, is_closed, true, None);
1095
1096 let ty = output.output_type();
1097
1098 let block_id = working_set.add_block(Arc::new(output));
1099 tokens.next();
1100
1101 (
1102 Expression::new(working_set, Expr::Subexpression(block_id), head_span, ty),
1103 true,
1104 )
1105 } else if bytes.starts_with(b"[") {
1106 trace!("parsing: table head of full cell path");
1107
1108 let output = parse_table_expression(working_set, head.span, &SyntaxShape::Any);
1109
1110 tokens.next();
1111
1112 (output, true)
1113 } else if bytes.starts_with(b"{") {
1114 trace!("parsing: record head of full cell path");
1115 let output = parse_record(working_set, head.span);
1116
1117 tokens.next();
1118
1119 (output, true)
1120 } else if bytes.starts_with(b"$") {
1121 trace!("parsing: $variable head of full cell path");
1122
1123 let out = parse_variable_expr(working_set, head.span, input_type);
1124
1125 tokens.next();
1126
1127 (out, true)
1128 } else if let Some(var_id) = implicit_head {
1129 trace!("parsing: implicit head of full cell path");
1130 (
1131 Expression::new(working_set, Expr::Var(var_id), head.span, Type::Any),
1132 false,
1133 )
1134 } else {
1135 working_set.error(ParseError::Mismatch(
1136 "variable or subexpression".into(),
1137 String::from_utf8_lossy(bytes).to_string(),
1138 span,
1139 ));
1140 return garbage(working_set, span);
1141 };
1142
1143 let tail = parse_cell_path(working_set, tokens, expect_dot);
1144 let ty = if !tail.is_empty() {
1145 if nu_experimental::CELL_PATH_TYPES.get() {
1146 head.ty
1147 .follow_cell_path(&tail)
1148 .map(|ty| ty.into_owned())
1149 .unwrap_or(Type::Any)
1150 } else {
1151 Type::Any
1152 }
1153 } else {
1154 head.ty.clone()
1155 };
1156
1157 Expression::new(
1158 working_set,
1159 Expr::FullCellPath(Box::new(FullCellPath { head, tail })),
1160 full_cell_span,
1161 ty,
1162 )
1163 } else {
1164 garbage(working_set, span)
1165 }
1166}
1167
1168enum PathLikeKind {
1169 Directory,
1170 Filepath,
1171 Glob,
1172}
1173
1174impl PathLikeKind {
1175 fn trace_name(&self) -> &'static str {
1177 match self {
1178 PathLikeKind::Directory => "directory",
1179 PathLikeKind::Filepath => "filepath",
1180 PathLikeKind::Glob => "glob pattern",
1181 }
1182 }
1183
1184 fn error_msg(&self) -> &'static str {
1186 match self {
1187 PathLikeKind::Directory => "directory",
1188 PathLikeKind::Filepath => "filepath",
1189 PathLikeKind::Glob => "glob pattern string",
1190 }
1191 }
1192
1193 fn to_expr(&self, token: String, quoted: bool) -> (Expr, Type) {
1195 match self {
1196 PathLikeKind::Directory => (Expr::Directory(token, quoted), Type::String),
1197 PathLikeKind::Filepath => (Expr::Filepath(token, quoted), Type::String),
1198 PathLikeKind::Glob => (Expr::GlobPattern(token, quoted), Type::Glob),
1199 }
1200 }
1201
1202 fn to_interpolation_expr(&self, exprs: Vec<Expression>, quoted: bool) -> Expr {
1204 match self {
1205 PathLikeKind::Directory | PathLikeKind::Filepath => Expr::StringInterpolation(exprs),
1206 PathLikeKind::Glob => Expr::GlobInterpolation(exprs, quoted),
1207 }
1208 }
1209}
1210
1211fn parse_path_like(
1225 working_set: &mut StateWorkingSet,
1226 span: Span,
1227 kind: PathLikeKind,
1228) -> Expression {
1229 let bytes = working_set.get_span_contents(span);
1230 let quoted = is_quoted(bytes);
1231 trace!("parsing: {}", kind.trace_name());
1232
1233 if is_bare_string_interpolation(bytes) {
1235 let interpolation_expr = parse_string_interpolation(working_set, span);
1236
1237 if let Expr::StringInterpolation(exprs) = interpolation_expr.expr {
1239 return Expression::new(
1240 working_set,
1241 kind.to_interpolation_expr(exprs, quoted),
1242 span,
1243 interpolation_expr.ty.clone(),
1244 );
1245 }
1246
1247 return interpolation_expr;
1248 }
1249
1250 let (token, err) = unescape_unquote_string(bytes, span);
1251 let is_quoted_internal = is_quoted(bytes);
1252
1253 if err.is_none() {
1254 trace!("-- found {token}");
1255
1256 let (expr, ty) = kind.to_expr(token, is_quoted_internal);
1257
1258 Expression::new(working_set, expr, span, ty)
1259 } else {
1260 working_set.error(ParseError::Expected(kind.error_msg(), span));
1261
1262 garbage(working_set, span)
1263 }
1264}
1265
1266fn is_bare_string_interpolation(bytes: &[u8]) -> bool {
1267 match bytes {
1268 [] => false,
1269 [b'\'' | b'"' | b'`', ..] => false,
1270 _ => bytes.contains(&b'('),
1271 }
1272}
1273
1274pub fn parse_directory(working_set: &mut StateWorkingSet, span: Span) -> Expression {
1275 parse_path_like(working_set, span, PathLikeKind::Directory)
1276}
1277
1278pub fn parse_filepath(working_set: &mut StateWorkingSet, span: Span) -> Expression {
1279 parse_path_like(working_set, span, PathLikeKind::Filepath)
1280}
1281
1282pub fn parse_datetime(working_set: &mut StateWorkingSet, span: Span) -> Expression {
1283 trace!("parsing: datetime");
1284
1285 let bytes = working_set.get_span_contents(span);
1286
1287 if bytes.len() < 6
1288 || !bytes[0].is_ascii_digit()
1289 || !bytes[1].is_ascii_digit()
1290 || !bytes[2].is_ascii_digit()
1291 || !bytes[3].is_ascii_digit()
1292 || bytes[4] != b'-'
1293 {
1294 working_set.error(ParseError::Expected("datetime", span));
1295 return garbage(working_set, span);
1296 }
1297
1298 let token = String::from_utf8_lossy(bytes).to_string();
1299
1300 if let Ok(datetime) = chrono::DateTime::parse_from_rfc3339(&token) {
1301 return Expression::new(working_set, Expr::DateTime(datetime), span, Type::Date);
1302 }
1303
1304 let just_date = token.clone() + "T00:00:00+00:00";
1306 if let Ok(datetime) = chrono::DateTime::parse_from_rfc3339(&just_date) {
1307 return Expression::new(working_set, Expr::DateTime(datetime), span, Type::Date);
1308 }
1309
1310 let datetime = token + "+00:00";
1312 if let Ok(datetime) = chrono::DateTime::parse_from_rfc3339(&datetime) {
1313 return Expression::new(working_set, Expr::DateTime(datetime), span, Type::Date);
1314 }
1315
1316 working_set.error(ParseError::Expected("datetime", span));
1317
1318 garbage(working_set, span)
1319}
1320
1321pub fn parse_duration(working_set: &mut StateWorkingSet, span: Span) -> Expression {
1323 trace!("parsing: duration");
1324
1325 let bytes = working_set.get_span_contents(span);
1326
1327 match parse_unit_value(bytes, span, DURATION_UNIT_GROUPS, Type::Duration, |x| x) {
1328 Some(Ok(expr)) => {
1329 let span_id = working_set.add_span(span);
1330 expr.with_span_id(span_id)
1331 }
1332 Some(Err(mk_err_for)) => {
1333 working_set.error(mk_err_for("duration"));
1334 garbage(working_set, span)
1335 }
1336 None => {
1337 working_set.error(ParseError::Expected("duration with valid units", span));
1338 garbage(working_set, span)
1339 }
1340 }
1341}
1342
1343pub fn parse_filesize(working_set: &mut StateWorkingSet, span: Span) -> Expression {
1345 trace!("parsing: filesize");
1346
1347 let bytes = working_set.get_span_contents(span);
1348
1349 if bytes.starts_with(b"0x") {
1351 working_set.error(ParseError::Expected("filesize with valid units", span));
1352 return garbage(working_set, span);
1353 }
1354
1355 match parse_unit_value(bytes, span, FILESIZE_UNIT_GROUPS, Type::Filesize, |x| {
1356 x.to_ascii_uppercase()
1357 }) {
1358 Some(Ok(expr)) => {
1359 let span_id = working_set.add_span(span);
1360 expr.with_span_id(span_id)
1361 }
1362 Some(Err(mk_err_for)) => {
1363 working_set.error(mk_err_for("filesize"));
1364 garbage(working_set, span)
1365 }
1366 None => {
1367 working_set.error(ParseError::Expected("filesize with valid units", span));
1368 garbage(working_set, span)
1369 }
1370 }
1371}
1372
1373type ParseUnitResult<'res> = Result<Expression, Box<dyn Fn(&'res str) -> ParseError>>;
1374type UnitGroup<'unit> = (Unit, &'unit str, Option<(Unit, i64)>);
1375
1376pub fn parse_unit_value<'res>(
1377 bytes: &[u8],
1378 span: Span,
1379 unit_groups: &[UnitGroup],
1380 ty: Type,
1381 transform: fn(String) -> String,
1382) -> Option<ParseUnitResult<'res>> {
1383 if bytes.len() < 2
1384 || !(bytes[0].is_ascii_digit()
1385 || (bytes[0] == b'.' && bytes[1].is_ascii_digit())
1386 || (bytes[0] == b'-' && bytes[1].is_ascii_digit()))
1387 {
1388 return None;
1389 }
1390
1391 let value = transform(str::from_utf8(bytes).ok()?.into());
1393
1394 if let Some((unit, name, convert)) = unit_groups.iter().find(|x| value.ends_with(x.1)) {
1395 let lhs_len = value.len() - name.len();
1396 let lhs = strip_underscores(&value.as_bytes()[..lhs_len]);
1397 let lhs_span = Span::new(span.start, span.start + lhs_len);
1398 let unit_span = Span::new(span.start + lhs_len, span.end);
1399 if lhs.ends_with('$') {
1400 return None;
1403 }
1404
1405 let (decimal_part, number_part) = modf(match lhs.parse::<f64>() {
1406 Ok(it) => it,
1407 Err(_) => {
1408 let mk_err = move |name| {
1409 ParseError::LabeledError(
1410 format!("{name} value must be a number"),
1411 "not a number".into(),
1412 lhs_span,
1413 )
1414 };
1415 return Some(Err(Box::new(mk_err)));
1416 }
1417 });
1418
1419 let mut unit = match convert {
1420 Some(convert_to) => convert_to.0,
1421 None => *unit,
1422 };
1423
1424 let num_float = match convert {
1425 Some(convert_to) => {
1426 (number_part * convert_to.1 as f64) + (decimal_part * convert_to.1 as f64)
1427 }
1428 None => number_part,
1429 };
1430
1431 let factor = match ty {
1434 Type::Filesize => unit_to_byte_factor(&unit),
1435 Type::Duration => unit_to_ns_factor(&unit),
1436 _ => None,
1437 };
1438
1439 let num = match factor {
1440 Some(factor) => {
1441 let num_base = num_float * factor;
1442 if i64::MIN as f64 <= num_base && num_base <= i64::MAX as f64 {
1443 unit = if ty == Type::Filesize {
1444 Unit::Filesize(FilesizeUnit::B)
1445 } else {
1446 Unit::Nanosecond
1447 };
1448 num_base as i64
1449 } else {
1450 num_float as i64
1452 }
1453 }
1454 None => num_float as i64,
1455 };
1456
1457 trace!("-- found {num} {unit:?}");
1458 let value = ValueWithUnit {
1459 expr: Expression::new_unknown(Expr::Int(num), lhs_span, Type::Number),
1460 unit: Spanned {
1461 item: unit,
1462 span: unit_span,
1463 },
1464 };
1465 let expr = Expression::new_unknown(Expr::ValueWithUnit(Box::new(value)), span, ty);
1466
1467 Some(Ok(expr))
1468 } else {
1469 None
1470 }
1471}
1472
1473pub const FILESIZE_UNIT_GROUPS: &[UnitGroup] = &[
1474 (
1475 Unit::Filesize(FilesizeUnit::KB),
1476 "KB",
1477 Some((Unit::Filesize(FilesizeUnit::B), 1000)),
1478 ),
1479 (
1480 Unit::Filesize(FilesizeUnit::MB),
1481 "MB",
1482 Some((Unit::Filesize(FilesizeUnit::KB), 1000)),
1483 ),
1484 (
1485 Unit::Filesize(FilesizeUnit::GB),
1486 "GB",
1487 Some((Unit::Filesize(FilesizeUnit::MB), 1000)),
1488 ),
1489 (
1490 Unit::Filesize(FilesizeUnit::TB),
1491 "TB",
1492 Some((Unit::Filesize(FilesizeUnit::GB), 1000)),
1493 ),
1494 (
1495 Unit::Filesize(FilesizeUnit::PB),
1496 "PB",
1497 Some((Unit::Filesize(FilesizeUnit::TB), 1000)),
1498 ),
1499 (
1500 Unit::Filesize(FilesizeUnit::EB),
1501 "EB",
1502 Some((Unit::Filesize(FilesizeUnit::PB), 1000)),
1503 ),
1504 (
1505 Unit::Filesize(FilesizeUnit::KiB),
1506 "KIB",
1507 Some((Unit::Filesize(FilesizeUnit::B), 1024)),
1508 ),
1509 (
1510 Unit::Filesize(FilesizeUnit::MiB),
1511 "MIB",
1512 Some((Unit::Filesize(FilesizeUnit::KiB), 1024)),
1513 ),
1514 (
1515 Unit::Filesize(FilesizeUnit::GiB),
1516 "GIB",
1517 Some((Unit::Filesize(FilesizeUnit::MiB), 1024)),
1518 ),
1519 (
1520 Unit::Filesize(FilesizeUnit::TiB),
1521 "TIB",
1522 Some((Unit::Filesize(FilesizeUnit::GiB), 1024)),
1523 ),
1524 (
1525 Unit::Filesize(FilesizeUnit::PiB),
1526 "PIB",
1527 Some((Unit::Filesize(FilesizeUnit::TiB), 1024)),
1528 ),
1529 (
1530 Unit::Filesize(FilesizeUnit::EiB),
1531 "EIB",
1532 Some((Unit::Filesize(FilesizeUnit::PiB), 1024)),
1533 ),
1534 (Unit::Filesize(FilesizeUnit::B), "B", None),
1535];
1536
1537pub const DURATION_UNIT_GROUPS: &[UnitGroup] = &[
1538 (Unit::Nanosecond, "ns", None),
1539 (Unit::Microsecond, "us", Some((Unit::Nanosecond, 1000))),
1541 (
1542 Unit::Microsecond,
1544 "\u{00B5}s",
1545 Some((Unit::Nanosecond, 1000)),
1546 ),
1547 (
1548 Unit::Microsecond,
1550 "\u{03BC}s",
1551 Some((Unit::Nanosecond, 1000)),
1552 ),
1553 (Unit::Millisecond, "ms", Some((Unit::Microsecond, 1000))),
1554 (Unit::Second, "sec", Some((Unit::Millisecond, 1000))),
1555 (Unit::Minute, "min", Some((Unit::Second, 60))),
1556 (Unit::Hour, "hr", Some((Unit::Minute, 60))),
1557 (Unit::Day, "day", Some((Unit::Minute, 1440))),
1558 (Unit::Week, "wk", Some((Unit::Day, 7))),
1559];
1560
1561fn unit_to_ns_factor(unit: &Unit) -> Option<f64> {
1562 match unit {
1563 Unit::Nanosecond => Some(1.0),
1564 Unit::Microsecond => Some(1_000.0),
1565 Unit::Millisecond => Some(1_000_000.0),
1566 Unit::Second => Some(1_000_000_000.0),
1567 Unit::Minute => Some(60.0 * 1_000_000_000.0),
1568 Unit::Hour => Some(60.0 * 60.0 * 1_000_000_000.0),
1569 Unit::Day => Some(24.0 * 60.0 * 60.0 * 1_000_000_000.0),
1570 Unit::Week => Some(7.0 * 24.0 * 60.0 * 60.0 * 1_000_000_000.0),
1571 _ => None,
1572 }
1573}
1574
1575fn unit_to_byte_factor(unit: &Unit) -> Option<f64> {
1576 match unit {
1577 Unit::Filesize(FilesizeUnit::B) => Some(1.0),
1578 Unit::Filesize(FilesizeUnit::KB) => Some(1_000.0),
1579 Unit::Filesize(FilesizeUnit::MB) => Some(1_000_000.0),
1580 Unit::Filesize(FilesizeUnit::GB) => Some(1_000_000_000.0),
1581 Unit::Filesize(FilesizeUnit::TB) => Some(1_000_000_000_000.0),
1582 Unit::Filesize(FilesizeUnit::PB) => Some(1_000_000_000_000_000.0),
1583 Unit::Filesize(FilesizeUnit::EB) => Some(1_000_000_000_000_000_000.0),
1584 Unit::Filesize(FilesizeUnit::KiB) => Some(1024.0),
1585 Unit::Filesize(FilesizeUnit::MiB) => Some(1024.0 * 1024.0),
1586 Unit::Filesize(FilesizeUnit::GiB) => Some(1024.0 * 1024.0 * 1024.0),
1587 Unit::Filesize(FilesizeUnit::TiB) => Some(1024.0 * 1024.0 * 1024.0 * 1024.0),
1588 Unit::Filesize(FilesizeUnit::PiB) => Some(1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0),
1589 Unit::Filesize(FilesizeUnit::EiB) => {
1590 Some(1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0)
1591 }
1592 _ => None,
1593 }
1594}
1595
1596fn modf(x: f64) -> (f64, f64) {
1598 let rv2: f64;
1599 let mut u = x.to_bits();
1600 let e = (((u >> 52) & 0x7ff) as i32) - 0x3ff;
1601
1602 if e >= 52 {
1604 rv2 = x;
1605 if e == 0x400 && (u << 12) != 0 {
1606 return (x, rv2);
1608 }
1609 u &= 1 << 63;
1610 return (f64::from_bits(u), rv2);
1611 }
1612
1613 if e < 0 {
1615 u &= 1 << 63;
1616 rv2 = f64::from_bits(u);
1617 return (x, rv2);
1618 }
1619
1620 let mask = ((!0) >> 12) >> e;
1621 if (u & mask) == 0 {
1622 rv2 = x;
1623 u &= 1 << 63;
1624 return (f64::from_bits(u), rv2);
1625 }
1626 u &= !mask;
1627 rv2 = f64::from_bits(u);
1628 (x - rv2, rv2)
1629}
1630
1631pub fn parse_glob_pattern(working_set: &mut StateWorkingSet, span: Span) -> Expression {
1632 parse_path_like(working_set, span, PathLikeKind::Glob)
1633}
1634
1635fn parse_hex_escape(bytes: &[u8], start_idx: usize, span: Span) -> Result<(u8, usize), ParseError> {
1636 let hex_digits = bytes.get(start_idx + 1..start_idx + 3).ok_or_else(|| {
1637 ParseError::InvalidLiteral(
1638 "incomplete hex escape '\\xHH', expected 2 hex digits".into(),
1639 "string".into(),
1640 Span::new(span.start + start_idx, span.end),
1641 )
1642 })?;
1643 if !hex_digits.iter().all(u8::is_ascii_hexdigit) {
1644 return Err(ParseError::InvalidLiteral(
1645 "invalid hex escape '\\xHH', expected exactly 2 hex digits".into(),
1646 "string".into(),
1647 Span::new(span.start + start_idx, span.end),
1648 ));
1649 }
1650 str::from_utf8(hex_digits)
1651 .ok()
1652 .and_then(|s| u8::from_str_radix(s, 0x10).ok())
1653 .map(|byte_val| (byte_val, start_idx + 3))
1654 .ok_or_else(|| {
1655 ParseError::InvalidLiteral(
1656 "invalid hex escape '\\xHH'".into(),
1657 "string".into(),
1658 Span::new(span.start + start_idx, span.end),
1659 )
1660 })
1661}
1662
1663fn parse_unicode_escape(
1664 bytes: &[u8],
1665 start_idx: usize,
1666 span: Span,
1667) -> Result<(char, usize), ParseError> {
1668 let mut slice = &bytes[(start_idx + 1)..];
1669 let mut current_idx = start_idx + 1;
1670
1671 slice = slice.strip_prefix(b"{").ok_or_else(|| {
1677 ParseError::InvalidLiteral(
1678 "invalid unicode escape '\\u{...}', must be 1-6 hex digits, max codepoint 0x10FFFF"
1679 .into(),
1680 "string".into(),
1681 Span::new(span.start + start_idx, span.end),
1682 )
1683 })?;
1684 current_idx += 1;
1685
1686 let end = slice.iter().position(|b| *b == b'}').ok_or_else(|| {
1687 ParseError::InvalidLiteral(
1688 "incomplete unicode escape '\\u{...}', missing closing '}'".into(),
1689 "string".into(),
1690 Span::new(span.start + start_idx, span.end),
1691 )
1692 })?;
1693 let digits = &slice[..end];
1694 current_idx += end; current_idx += 1; let current_idx = current_idx;
1697
1698 let ch = Some(digits)
1699 .filter(|b| (1..=6).contains(&b.len()))
1700 .and_then(|b| str::from_utf8(b).ok())
1701 .and_then(|s| u32::from_str_radix(s, 0x10).ok())
1702 .and_then(char::from_u32)
1703 .ok_or_else(|| {
1704 ParseError::InvalidLiteral(
1705 "invalid unicode escape '\\u{...}', must be 1-6 hex digits, max codepoint 0x10FFFF"
1706 .into(),
1707 "string".into(),
1708 Span::new(span.start + start_idx, span.end),
1709 )
1710 })?;
1711
1712 Ok((ch, current_idx))
1713}
1714
1715pub fn unescape_string(bytes: &[u8], span: Span) -> (Vec<u8>, Option<ParseError>) {
1716 let mut output = Vec::new();
1717 let mut error = None;
1718
1719 let mut idx = 0;
1720
1721 if !bytes.contains(&b'\\') {
1722 return (bytes.to_vec(), None);
1723 }
1724
1725 'us_loop: while idx < bytes.len() {
1726 if bytes[idx] == b'\\' {
1727 idx += 1;
1729
1730 match bytes.get(idx) {
1731 Some(b'"') => {
1732 output.push(b'"');
1733 idx += 1;
1734 }
1735 Some(b'\'') => {
1736 output.push(b'\'');
1737 idx += 1;
1738 }
1739 Some(b'\\') => {
1740 output.push(b'\\');
1741 idx += 1;
1742 }
1743 Some(b'/') => {
1744 output.push(b'/');
1745 idx += 1;
1746 }
1747 Some(b'(') => {
1748 output.push(b'(');
1749 idx += 1;
1750 }
1751 Some(b')') => {
1752 output.push(b')');
1753 idx += 1;
1754 }
1755 Some(b'{') => {
1756 output.push(b'{');
1757 idx += 1;
1758 }
1759 Some(b'}') => {
1760 output.push(b'}');
1761 idx += 1;
1762 }
1763 Some(b'$') => {
1764 output.push(b'$');
1765 idx += 1;
1766 }
1767 Some(b'^') => {
1768 output.push(b'^');
1769 idx += 1;
1770 }
1771 Some(b'#') => {
1772 output.push(b'#');
1773 idx += 1;
1774 }
1775 Some(b'|') => {
1776 output.push(b'|');
1777 idx += 1;
1778 }
1779 Some(b'~') => {
1780 output.push(b'~');
1781 idx += 1;
1782 }
1783 Some(b'a') => {
1784 output.push(0x7);
1785 idx += 1;
1786 }
1787 Some(b'b') => {
1788 output.push(0x8);
1789 idx += 1;
1790 }
1791 Some(b'e') => {
1792 output.push(0x1b);
1793 idx += 1;
1794 }
1795 Some(b'f') => {
1796 output.push(0xc);
1797 idx += 1;
1798 }
1799 Some(b'n') => {
1800 output.push(b'\n');
1801 idx += 1;
1802 }
1803 Some(b'r') => {
1804 output.push(b'\r');
1805 idx += 1;
1806 }
1807 Some(b't') => {
1808 output.push(b'\t');
1809 idx += 1;
1810 }
1811 Some(b'0') => {
1812 output.push(b'\0');
1813 idx += 1;
1814 }
1815 Some(b'x') => {
1816 match parse_hex_escape(bytes, idx, span) {
1818 Ok((byte_val, new_idx)) => {
1819 output.push(byte_val);
1820 idx = new_idx;
1821 }
1822 Err(err) => {
1823 error = error.or(Some(err));
1824 break 'us_loop;
1825 }
1826 }
1827 }
1828 Some(b'u') => {
1829 match parse_unicode_escape(bytes, idx, span) {
1831 Ok((ch, new_idx)) => {
1832 let mut ch_buf = [0u8; 4];
1833 output.extend(ch.encode_utf8(&mut ch_buf).as_bytes());
1834 idx = new_idx;
1835 }
1836 Err(err) => {
1837 error = error.or(Some(err));
1838 break 'us_loop;
1839 }
1840 }
1841 }
1842
1843 Some(other) => {
1844 error = error.or(Some(ParseError::InvalidLiteral(
1845 format!("unrecognized escape sequence '\\{}'", *other as char),
1846 "string".into(),
1847 Span::new(span.start + idx, span.end),
1848 )));
1849 break 'us_loop;
1850 }
1851 None => {
1852 error = error.or(Some(ParseError::InvalidLiteral(
1853 "incomplete escape sequence after '\\'".into(),
1854 "string".into(),
1855 Span::new(span.end.saturating_sub(1), span.end),
1856 )));
1857 break 'us_loop;
1858 }
1859 }
1860 } else {
1861 output.push(bytes[idx]);
1862 idx += 1;
1863 }
1864 }
1865
1866 (output, error)
1867}
1868
1869pub fn unescape_unquote_string(bytes: &[u8], span: Span) -> (String, Option<ParseError>) {
1870 if bytes.starts_with(b"\"") {
1871 let bytes = trim_quotes(bytes);
1873
1874 let (bytes, err) = unescape_string(bytes, span);
1875
1876 if let Ok(token) = String::from_utf8(bytes) {
1877 (token, err)
1878 } else {
1879 (String::new(), Some(ParseError::Expected("string", span)))
1880 }
1881 } else {
1882 let bytes = trim_quotes(bytes);
1883
1884 if let Ok(token) = String::from_utf8(bytes.into()) {
1885 (token, None)
1886 } else {
1887 (String::new(), Some(ParseError::Expected("string", span)))
1888 }
1889 }
1890}
1891
1892fn check_string_no_trailing_tokens(
1893 bytes: &[u8],
1894 span: Span,
1895 opening_quote_pos: usize,
1896 quote: u8,
1897) -> Result<(), ParseError> {
1898 let pos = bytes
1899 .iter()
1900 .rposition(|ch| *ch == quote)
1901 .expect("string begins with quote");
1902 if pos == bytes.len() - 1 {
1903 Ok(())
1904 } else if pos == opening_quote_pos {
1905 Ok(())
1909 } else {
1910 let span = Span::new(span.start + pos + 1, span.end);
1911 Err(ParseError::ExtraTokensAfterClosingDelimiter(span))
1912 }
1913}
1914
1915pub fn parse_string(working_set: &mut StateWorkingSet, span: Span) -> Expression {
1916 trace!("parsing: string");
1917
1918 let bytes = working_set.get_span_contents(span);
1919
1920 if bytes.is_empty() {
1921 working_set.error(ParseError::Expected("String", span));
1922 return Expression::garbage(working_set, span);
1923 }
1924
1925 if is_bare_string_interpolation(bytes) {
1927 return parse_string_interpolation(working_set, span);
1928 }
1929
1930 for quote in [b'\"', b'\''] {
1932 if bytes[0] == quote
1933 && let Err(err) = check_string_no_trailing_tokens(bytes, span, 0, quote)
1934 {
1935 working_set.error(err);
1936 return garbage(working_set, span);
1937 }
1938 }
1939
1940 let (s, err) = unescape_unquote_string(bytes, span);
1941 if let Some(err) = err {
1942 working_set.error(err);
1943 }
1944
1945 Expression::new(working_set, Expr::String(s), span, Type::String)
1946}
1947
1948fn is_quoted(bytes: &[u8]) -> bool {
1949 matches!(bytes, [b'\'', .., b'\''] | [b'"', .., b'"'])
1950}
1951
1952pub fn parse_string_strict(working_set: &mut StateWorkingSet, span: Span) -> Expression {
1953 trace!("parsing: string, with required delimiters");
1954
1955 let bytes = working_set.get_span_contents(span);
1956
1957 {
1959 let bytes = if bytes.starts_with(b"$") {
1960 &bytes[1..]
1961 } else {
1962 bytes
1963 };
1964 if bytes.starts_with(b"\"") && (bytes.len() == 1 || !bytes.ends_with(b"\"")) {
1965 working_set.error(ParseError::Unclosed("\"", span));
1966 return garbage(working_set, span);
1967 }
1968 if bytes.starts_with(b"\'") && (bytes.len() == 1 || !bytes.ends_with(b"\'")) {
1969 working_set.error(ParseError::Unclosed("\'", span));
1970 return garbage(working_set, span);
1971 }
1972 if bytes.starts_with(b"r#") && (bytes.len() == 1 || !bytes.ends_with(b"#")) {
1973 working_set.error(ParseError::Unclosed("r#", span));
1974 return garbage(working_set, span);
1975 }
1976 }
1977
1978 let (bytes, quoted) = if (bytes.starts_with(b"\"") && bytes.ends_with(b"\"") && bytes.len() > 1)
1979 || (bytes.starts_with(b"\'") && bytes.ends_with(b"\'") && bytes.len() > 1)
1980 {
1981 (&bytes[1..(bytes.len() - 1)], true)
1982 } else if (bytes.starts_with(b"$\"") && bytes.ends_with(b"\"") && bytes.len() > 2)
1983 || (bytes.starts_with(b"$\'") && bytes.ends_with(b"\'") && bytes.len() > 2)
1984 {
1985 (&bytes[2..(bytes.len() - 1)], true)
1986 } else {
1987 (bytes, false)
1988 };
1989
1990 if let Ok(token) = String::from_utf8(bytes.into()) {
1991 trace!("-- found {token}");
1992
1993 if quoted {
1994 Expression::new(working_set, Expr::String(token), span, Type::String)
1995 } else if token.contains(' ') {
1996 working_set.error(ParseError::Expected("string", span));
1997
1998 garbage(working_set, span)
1999 } else {
2000 Expression::new(working_set, Expr::String(token), span, Type::String)
2001 }
2002 } else {
2003 working_set.error(ParseError::Expected("string", span));
2004 garbage(working_set, span)
2005 }
2006}