1#![allow(clippy::byte_char_slices)]
2
3use crate::{
4 Token, TokenContents,
5 lex::{interp_subexpr_step, 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 let open = ParseError::opener_span(span, 1);
452 working_set.error(ParseError::unclosed("'", open, span));
453 return garbage(working_set, span);
454 }
455
456 let postfix_bytes = &bytes[bytes.len() - expect_postfix_sharp_cnt..bytes.len()];
458 if postfix_bytes.iter().any(|b| *b != b'#') {
459 working_set.error(ParseError::unbalanced("prefix #", "postfix #", span));
460 return garbage(working_set, span);
461 }
462 if bytes[1 + prefix_sharp_cnt] != b'\''
464 || bytes[bytes.len() - expect_postfix_sharp_cnt - 1] != b'\''
465 {
466 let open = ParseError::opener_span(span, 1);
467 working_set.error(ParseError::unclosed("'", open, span));
468 return garbage(working_set, span);
469 }
470
471 let bytes = &bytes[prefix_sharp_cnt + 1 + 1..bytes.len() - 1 - prefix_sharp_cnt];
472 if let Ok(token) = String::from_utf8(bytes.into()) {
473 Expression::new(working_set, Expr::RawString(token), span, Type::String)
474 } else {
475 working_set.error(ParseError::Expected("utf8 raw-string", span));
476 garbage(working_set, span)
477 }
478}
479
480pub fn parse_paren_expr(
481 working_set: &mut StateWorkingSet,
482 span: Span,
483 shape: &SyntaxShape,
484) -> Expression {
485 let starting_error_count = working_set.parse_errors.len();
486
487 if let Some(expr) = parse_range(working_set, span) {
488 return expr;
489 }
490
491 working_set.parse_errors.truncate(starting_error_count);
492
493 if let SyntaxShape::Signature = shape {
494 return parse_signature(working_set, span, false);
495 }
496
497 if let SyntaxShape::ExternalSignature = shape {
498 return parse_signature(working_set, span, true);
499 }
500
501 let starting_scope_count = working_set.delta.scope.len();
502 let fcp_expr = parse_full_cell_path(working_set, None, span, None);
503 let fcp_error_count = working_set.parse_errors.len();
504 if fcp_error_count > starting_error_count {
505 let malformed_subexpr = working_set.parse_errors[starting_error_count..]
506 .first()
507 .is_some_and(|e| match e {
508 ParseError::Unclosed(right, ..) if (*right == ")") => true,
509 ParseError::Unbalanced(left, right, ..) if *left == "(" && *right == ")" => true,
510 _ => false,
511 });
512 if malformed_subexpr {
513 working_set.parse_errors.truncate(starting_error_count);
514 while working_set.delta.scope.len() > starting_scope_count {
515 working_set.exit_scope();
516 }
517 if matches!(shape, SyntaxShape::GlobPattern) {
518 parse_glob_pattern(working_set, span)
519 } else {
520 parse_string_interpolation(working_set, span)
521 }
522 } else {
523 fcp_expr
524 }
525 } else {
526 fcp_expr
527 }
528}
529
530pub fn parse_brace_expr(
531 working_set: &mut StateWorkingSet,
532 span: Span,
533 shape: &SyntaxShape,
534 input_type: Option<&Type>,
535) -> Expression {
536 if span.end <= (span.start + 1) {
545 working_set.error(ParseError::ExpectedWithStringMsg(
546 format!("non-block value: {shape}"),
547 span,
548 ));
549 return Expression::garbage(working_set, span);
550 }
551 let bytes = working_set.get_span_contents(Span::new(span.start + 1, span.end - 1));
552 let (tokens, _) = lex(bytes, span.start + 1, &[b'\r', b'\n', b'\t'], &[b':'], true);
553
554 match tokens.as_slice() {
555 [] => match shape {
557 SyntaxShape::Closure(_) => parse_closure_expression(working_set, shape, span, None),
558 SyntaxShape::Block => parse_block_expression(working_set, span, input_type),
559 SyntaxShape::MatchBlock => parse_match_block_expression(working_set, span, input_type),
560 _ => parse_record(working_set, span),
561 },
562 [
563 Token {
564 contents: TokenContents::Pipe | TokenContents::PipePipe,
565 ..
566 },
567 ..,
568 ] => {
569 if let SyntaxShape::Block = shape {
570 working_set.error(ParseError::Mismatch("block".into(), "closure".into(), span));
571 return Expression::garbage(working_set, span);
572 }
573 parse_closure_expression(working_set, shape, span, None)
574 }
575 [_, third, ..] if working_set.get_span_contents(third.span) == b":" => {
576 parse_full_cell_path(working_set, None, span, None)
577 }
578 [second, ..] => {
579 let second_bytes = working_set.get_span_contents(second.span);
580 match shape {
581 SyntaxShape::Closure(_) => parse_closure_expression(working_set, shape, span, None),
582 SyntaxShape::Block => parse_block_expression(working_set, span, input_type),
583 SyntaxShape::MatchBlock => {
584 parse_match_block_expression(working_set, span, input_type)
585 }
586 _ if second_bytes == b"}" => parse_full_cell_path(working_set, None, span, None),
588 _ if extract_spread_record(second_bytes.into_spanned(second.span)).is_some() => {
589 parse_record(working_set, span)
590 }
591 SyntaxShape::Any => parse_closure_expression(working_set, shape, span, None),
592 _ => {
593 working_set.error(ParseError::ExpectedWithStringMsg(
594 format!("non-block value: {shape}"),
595 span,
596 ));
597
598 Expression::garbage(working_set, span)
599 }
600 }
601 }
602 }
603}
604
605pub fn parse_string_interpolation(working_set: &mut StateWorkingSet, span: Span) -> Expression {
606 #[derive(PartialEq, Eq, Debug)]
607 enum InterpolationMode {
608 String,
609 Expression,
610 }
611
612 let contents = working_set.get_span_contents(span);
613
614 let mut double_quote = false;
615
616 let (start, end) = if contents.starts_with(b"$\"") {
617 double_quote = true;
618
619 if let Err(err) = check_string_no_trailing_tokens(contents, span, 1, b'\"') {
620 working_set.error(err);
621 return garbage(working_set, span);
622 }
623
624 let end = if contents.ends_with(b"\"") && contents.len() > 2 {
625 span.end - 1
626 } else {
627 span.end
628 };
629 (span.start + 2, end)
630 } else if contents.starts_with(b"$'") {
631 if let Err(err) = check_string_no_trailing_tokens(contents, span, 1, b'\'') {
632 working_set.error(err);
633 return garbage(working_set, span);
634 }
635
636 let end = if contents.ends_with(b"'") && contents.len() > 2 {
637 span.end - 1
638 } else {
639 span.end
640 };
641 (span.start + 2, end)
642 } else {
643 (span.start, span.end)
644 };
645
646 let inner_span = Span::new(start, end);
647 let contents = working_set.get_span_contents(inner_span).to_vec();
648
649 let mut output = vec![];
650 let mut mode = InterpolationMode::String;
651 let mut token_start = start;
652
653 let mut delimiter_stack: Vec<(u8, ())> = vec![];
657
658 let mut consecutive_backslashes: usize = 0;
659
660 let mut b = start;
661
662 while b != end {
663 let current_byte = contents[b - start];
664
665 if mode == InterpolationMode::String {
666 let preceding_consecutive_backslashes = consecutive_backslashes;
667
668 let is_backslash = current_byte == b'\\';
669 consecutive_backslashes = if is_backslash {
670 preceding_consecutive_backslashes + 1
671 } else {
672 0
673 };
674
675 if current_byte == b'('
676 && (!double_quote || preceding_consecutive_backslashes.is_multiple_of(2))
677 {
678 mode = InterpolationMode::Expression;
679 if token_start < b {
680 let span = Span::new(token_start, b);
681 let str_contents = working_set.get_span_contents(span);
682
683 let (str_contents, err) = if double_quote {
684 unescape_string(str_contents, span)
685 } else {
686 (str_contents.to_vec(), None)
687 };
688 if let Some(err) = err {
689 working_set.error(err);
690 }
691
692 output.push(Expression::new(
693 working_set,
694 Expr::String(String::from_utf8_lossy(&str_contents).to_string()),
695 span,
696 Type::String,
697 ));
698 token_start = b;
699 }
700 }
701 }
702
703 if mode == InterpolationMode::Expression {
704 if interp_subexpr_step(&mut delimiter_stack, current_byte, ()) && b + 1 < end {
705 b += 2;
708 continue;
709 }
710 if current_byte == b')' && delimiter_stack.is_empty() {
711 mode = InterpolationMode::String;
712
713 if token_start < b {
714 let span = Span::new(token_start, b + 1);
715
716 let expr = parse_full_cell_path(working_set, None, span, None);
717 output.push(expr);
718 }
719
720 token_start = b + 1;
721 continue;
722 }
723 }
724 b += 1;
725 }
726
727 match mode {
728 InterpolationMode::String => {
729 if token_start < end {
730 let span = Span::new(token_start, end);
731 let str_contents = working_set.get_span_contents(span);
732
733 let (str_contents, err) = if double_quote {
734 unescape_string(str_contents, span)
735 } else {
736 (str_contents.to_vec(), None)
737 };
738 if let Some(err) = err {
739 working_set.error(err);
740 }
741
742 output.push(Expression::new(
743 working_set,
744 Expr::String(String::from_utf8_lossy(&str_contents).to_string()),
745 span,
746 Type::String,
747 ));
748 }
749 }
750 InterpolationMode::Expression => {
751 if token_start < end {
752 let span = Span::new(token_start, end);
753 let expr = parse_full_cell_path(working_set, None, span, None);
754 output.push(expr);
755 }
756 }
757 }
758
759 Expression::new(
760 working_set,
761 Expr::StringInterpolation(output),
762 span,
763 Type::String,
764 )
765}
766
767pub fn parse_variable_expr(
768 working_set: &mut StateWorkingSet,
769 span: Span,
770 input_type: Option<&Type>,
771) -> Expression {
772 let contents = working_set.get_span_contents(span);
773
774 if contents == b"$nu" {
775 return Expression::new(
776 working_set,
777 Expr::Var(nu_protocol::NU_VARIABLE_ID),
778 span,
779 Type::Any,
780 );
781 } else if contents == b"$in" {
782 return Expression::new(
783 working_set,
784 Expr::Var(nu_protocol::IN_VARIABLE_ID),
785 span,
786 input_type.cloned().unwrap_or(Type::Any),
787 );
788 } else if contents == b"$env" {
789 return Expression::new(
790 working_set,
791 Expr::Var(nu_protocol::ENV_VARIABLE_ID),
792 span,
793 Type::Any,
794 );
795 } else if contents.strip_prefix(b"$") == Some(nu_protocol::LAST_RESULT_VAR_NAME.as_bytes()) {
796 return Expression::new(
799 working_set,
800 Expr::Var(nu_protocol::LAST_VARIABLE_ID),
801 span,
802 Type::Any,
803 );
804 }
805
806 let name = if contents.starts_with(b"$") {
807 String::from_utf8_lossy(&contents[1..]).to_string()
808 } else {
809 String::from_utf8_lossy(contents).to_string()
810 };
811
812 let bytes = working_set.get_span_contents(span);
813 let suggestion = || {
814 DidYouMean::new(
815 &working_set.list_variables(),
816 working_set.get_span_contents(span),
817 )
818 };
819
820 if bytes == b"$" {
822 working_set.error(ParseError::LabeledErrorWithHelp {
823 error: "Incomplete variable".into(),
824 label: "expected a variable name after `$`".into(),
825 help: "Variable names must be attached to `$` with no space (e.g. `$env`, `$in`). \
826 Did you mean `$env`?"
827 .into(),
828 span,
829 });
830 return garbage(working_set, span);
831 }
832
833 if !is_variable(bytes) {
834 working_set.error(ParseError::ExpectedWithDidYouMean(
835 "valid variable name",
836 suggestion(),
837 span,
838 ));
839 garbage(working_set, span)
840 } else if let Some(id) = working_set.find_variable(bytes) {
841 Expression::new(
842 working_set,
843 Expr::Var(id),
844 span,
845 working_set.get_variable(id).ty.clone(),
846 )
847 } else if working_set.get_env_var(&name).is_some() {
848 working_set.error(ParseError::EnvVarNotVar(name, span));
849 garbage(working_set, span)
850 } else {
851 working_set.error(ParseError::VariableNotFound(suggestion(), span));
852 garbage(working_set, span)
853 }
854}
855
856pub fn parse_cell_path(
857 working_set: &mut StateWorkingSet,
858 tokens: impl Iterator<Item = Token>,
859 expect_dot: bool,
860) -> Vec<PathMember> {
861 enum TokenType {
862 Dot, DotOrSign, DotOrExclamation, DotOrQuestion, PathMember, }
868
869 enum ModifyMember {
870 No,
871 Optional,
872 Insensitive,
873 }
874
875 impl TokenType {
876 fn expect(&mut self, byte: u8) -> Result<ModifyMember, &'static str> {
877 match (&*self, byte) {
878 (Self::PathMember, _) => {
879 *self = Self::DotOrSign;
880 Ok(ModifyMember::No)
881 }
882 (
883 Self::Dot | Self::DotOrSign | Self::DotOrExclamation | Self::DotOrQuestion,
884 b'.',
885 ) => {
886 *self = Self::PathMember;
887 Ok(ModifyMember::No)
888 }
889 (Self::DotOrSign, b'!') => {
890 *self = Self::DotOrQuestion;
891 Ok(ModifyMember::Insensitive)
892 }
893 (Self::DotOrSign, b'?') => {
894 *self = Self::DotOrExclamation;
895 Ok(ModifyMember::Optional)
896 }
897 (Self::DotOrSign, _) => Err(". or ! or ?"),
898 (Self::DotOrExclamation, b'!') => {
899 *self = Self::Dot;
900 Ok(ModifyMember::Insensitive)
901 }
902 (Self::DotOrExclamation, _) => Err(". or !"),
903 (Self::DotOrQuestion, b'?') => {
904 *self = Self::Dot;
905 Ok(ModifyMember::Optional)
906 }
907 (Self::DotOrQuestion, _) => Err(". or ?"),
908 (Self::Dot, _) => Err("."),
909 }
910 }
911 }
912
913 let mut expected_token = if expect_dot {
915 TokenType::Dot
916 } else {
917 TokenType::PathMember
918 };
919
920 let mut tail = vec![];
921
922 for path_element in tokens {
923 let bytes = working_set.get_span_contents(path_element.span);
924
925 let Some((&first, rest)) = bytes.split_first() else {
928 working_set.error(ParseError::Expected("string", path_element.span));
929 return tail;
930 };
931 let single_char = rest.is_empty();
932
933 if let TokenType::PathMember = expected_token {
934 let starting_error_count = working_set.parse_errors.len();
935
936 let expr = parse_int(working_set, path_element.span);
937 working_set.parse_errors.truncate(starting_error_count);
938
939 match expr {
940 Expression {
941 expr: Expr::Int(val),
942 span,
943 ..
944 } => {
945 if val < 0 {
946 working_set.error(ParseError::InvalidLiteral(
947 "negative index is not supported".into(),
948 "cell path".into(),
949 span,
950 ));
951 return tail;
952 }
953 tail.push(PathMember::Int {
954 val: val as usize,
955 span,
956 optional: false,
957 })
958 }
959 _ => {
960 let result = parse_string(working_set, path_element.span);
961 match result {
962 Expression {
963 expr: Expr::String(string),
964 span,
965 ..
966 } => {
967 tail.push(PathMember::String {
968 val: string,
969 span,
970 optional: false,
971 casing: Casing::Sensitive,
972 });
973 }
974 _ => {
975 working_set.error(ParseError::Expected("string", path_element.span));
976 return tail;
977 }
978 }
979 }
980 }
981 expected_token = TokenType::DotOrSign;
982 } else {
983 match expected_token.expect(if single_char { first } else { b' ' }) {
984 Ok(modify) => {
985 if let Some(last) = tail.last_mut() {
986 match modify {
987 ModifyMember::No => {}
988 ModifyMember::Optional => last.make_optional(),
989 ModifyMember::Insensitive => last.make_insensitive(),
990 }
991 };
992 }
993 Err(expected) => {
994 working_set.error(ParseError::Expected(expected, path_element.span));
995 return tail;
996 }
997 }
998 }
999 }
1000
1001 tail
1002}
1003
1004pub fn parse_simple_cell_path(working_set: &mut StateWorkingSet, span: Span) -> Expression {
1005 let source = working_set.get_span_contents(span);
1006
1007 let (tokens, err) = lex(
1008 source,
1009 span.start,
1010 &[b'\n', b'\r'],
1011 &[b'.', b'?', b'!'],
1012 true,
1013 );
1014 if let Some(err) = err {
1015 working_set.error(err)
1016 }
1017
1018 let tokens = tokens.into_iter().peekable();
1019
1020 let cell_path = parse_cell_path(working_set, tokens, false);
1021
1022 Expression::new(
1023 working_set,
1024 Expr::CellPath(CellPath { members: cell_path }),
1025 span,
1026 Type::CellPath,
1027 )
1028}
1029
1030pub fn parse_full_cell_path(
1031 working_set: &mut StateWorkingSet,
1032 implicit_head: Option<VarId>,
1033 span: Span,
1034 input_type: Option<&Type>,
1035) -> Expression {
1036 trace!("parsing: full cell path");
1037 let full_cell_span = span;
1038 let source = working_set.get_span_contents(span);
1039
1040 let (tokens, err) = lex(
1041 source,
1042 span.start,
1043 &[b'\n', b'\r'],
1044 &[b'.', b'?', b'!'],
1045 true,
1046 );
1047 if let Some(err) = err {
1048 working_set.error(err)
1049 }
1050
1051 let mut tokens = tokens.into_iter().peekable();
1052 if let Some(head) = tokens.peek() {
1053 let bytes = working_set.get_span_contents(head.span);
1054 let (head, expect_dot) = if bytes.starts_with(b"(") {
1055 trace!("parsing: paren-head of full cell path");
1056
1057 let head_span = head.span;
1058 let mut start = head.span.start;
1059 let mut end = head.span.end;
1060 let mut is_closed = true;
1061
1062 if bytes.starts_with(b"(") {
1063 start += 1;
1064 }
1065 if bytes.ends_with(b")") {
1066 end -= 1;
1067 } else {
1068 let open = Span::new(
1069 head_span.start,
1070 head_span.start.saturating_add(1).min(head_span.end),
1071 );
1072 working_set.error(ParseError::unclosed(")", open, Span::new(end, end)));
1073 is_closed = false;
1074 }
1075
1076 let span = Span::new(start, end);
1077
1078 let source = working_set.get_span_contents(span);
1079
1080 let (output, err) = lex(source, span.start, &[b'\n', b'\r'], &[], true);
1081 if let Some(err) = err {
1082 working_set.error(err)
1083 }
1084
1085 let output = parse_block(working_set, &output, span, is_closed, true, None);
1088
1089 let ty = output.output_type();
1090
1091 let block_id = working_set.add_block(Arc::new(output));
1092 tokens.next();
1093
1094 (
1095 Expression::new(working_set, Expr::Subexpression(block_id), head_span, ty),
1096 true,
1097 )
1098 } else if bytes.starts_with(b"[") {
1099 trace!("parsing: table head of full cell path");
1100
1101 let output = parse_table_expression(working_set, head.span, &SyntaxShape::Any);
1102
1103 tokens.next();
1104
1105 (output, true)
1106 } else if bytes.starts_with(b"{") {
1107 trace!("parsing: record head of full cell path");
1108 let output = parse_record(working_set, head.span);
1109
1110 tokens.next();
1111
1112 (output, true)
1113 } else if bytes.starts_with(b"$") {
1114 trace!("parsing: $variable head of full cell path");
1115
1116 let out = parse_variable_expr(working_set, head.span, input_type);
1117
1118 tokens.next();
1119
1120 (out, true)
1121 } else if let Some(var_id) = implicit_head {
1122 trace!("parsing: implicit head of full cell path");
1123 (
1124 Expression::new(working_set, Expr::Var(var_id), head.span, Type::Any),
1125 false,
1126 )
1127 } else {
1128 working_set.error(ParseError::Mismatch(
1129 "variable or subexpression".into(),
1130 String::from_utf8_lossy(bytes).to_string(),
1131 span,
1132 ));
1133 return garbage(working_set, span);
1134 };
1135
1136 let tail = parse_cell_path(working_set, tokens, expect_dot);
1137 let ty = if !tail.is_empty() {
1138 if nu_experimental::CELL_PATH_TYPES.get() {
1139 head.ty
1140 .follow_cell_path(&tail)
1141 .map(|ty| ty.into_owned())
1142 .unwrap_or(Type::Any)
1143 } else {
1144 Type::Any
1145 }
1146 } else {
1147 head.ty.clone()
1148 };
1149
1150 Expression::new(
1151 working_set,
1152 Expr::FullCellPath(Box::new(FullCellPath { head, tail })),
1153 full_cell_span,
1154 ty,
1155 )
1156 } else {
1157 garbage(working_set, span)
1158 }
1159}
1160
1161enum PathLikeKind {
1162 Directory,
1163 Filepath,
1164 Glob,
1165}
1166
1167impl PathLikeKind {
1168 fn trace_name(&self) -> &'static str {
1170 match self {
1171 PathLikeKind::Directory => "directory",
1172 PathLikeKind::Filepath => "filepath",
1173 PathLikeKind::Glob => "glob pattern",
1174 }
1175 }
1176
1177 fn error_msg(&self) -> &'static str {
1179 match self {
1180 PathLikeKind::Directory => "directory",
1181 PathLikeKind::Filepath => "filepath",
1182 PathLikeKind::Glob => "glob pattern string",
1183 }
1184 }
1185
1186 fn to_expr(&self, token: String, quoted: bool) -> (Expr, Type) {
1188 match self {
1189 PathLikeKind::Directory => (Expr::Directory(token, quoted), Type::String),
1190 PathLikeKind::Filepath => (Expr::Filepath(token, quoted), Type::String),
1191 PathLikeKind::Glob => (Expr::GlobPattern(token, quoted), Type::Glob),
1192 }
1193 }
1194
1195 fn to_interpolation_expr(&self, exprs: Vec<Expression>, quoted: bool) -> Expr {
1197 match self {
1198 PathLikeKind::Directory | PathLikeKind::Filepath => Expr::StringInterpolation(exprs),
1199 PathLikeKind::Glob => Expr::GlobInterpolation(exprs, quoted),
1200 }
1201 }
1202}
1203
1204fn parse_path_like(
1218 working_set: &mut StateWorkingSet,
1219 span: Span,
1220 kind: PathLikeKind,
1221) -> Expression {
1222 let bytes = working_set.get_span_contents(span);
1223 let quoted = is_quoted(bytes);
1224 trace!("parsing: {}", kind.trace_name());
1225
1226 if is_bare_string_interpolation(bytes) {
1228 let interpolation_expr = parse_string_interpolation(working_set, span);
1229
1230 if let Expr::StringInterpolation(exprs) = interpolation_expr.expr {
1232 return Expression::new(
1233 working_set,
1234 kind.to_interpolation_expr(exprs, quoted),
1235 span,
1236 interpolation_expr.ty.clone(),
1237 );
1238 }
1239
1240 return interpolation_expr;
1241 }
1242
1243 let (token, err) = unescape_unquote_string(bytes, span);
1244 let is_quoted_internal = is_quoted(bytes);
1245
1246 if err.is_none() {
1247 trace!("-- found {token}");
1248
1249 let (expr, ty) = kind.to_expr(token, is_quoted_internal);
1250
1251 Expression::new(working_set, expr, span, ty)
1252 } else {
1253 working_set.error(ParseError::Expected(kind.error_msg(), span));
1254
1255 garbage(working_set, span)
1256 }
1257}
1258
1259fn is_bare_string_interpolation(bytes: &[u8]) -> bool {
1260 match bytes {
1261 [] => false,
1262 [b'\'' | b'"' | b'`', ..] => false,
1263 _ => bytes.contains(&b'('),
1264 }
1265}
1266
1267pub fn parse_directory(working_set: &mut StateWorkingSet, span: Span) -> Expression {
1268 parse_path_like(working_set, span, PathLikeKind::Directory)
1269}
1270
1271pub fn parse_filepath(working_set: &mut StateWorkingSet, span: Span) -> Expression {
1272 parse_path_like(working_set, span, PathLikeKind::Filepath)
1273}
1274
1275pub fn parse_datetime(working_set: &mut StateWorkingSet, span: Span) -> Expression {
1276 trace!("parsing: datetime");
1277
1278 let bytes = working_set.get_span_contents(span);
1279
1280 if bytes.len() < 6
1281 || !bytes[0].is_ascii_digit()
1282 || !bytes[1].is_ascii_digit()
1283 || !bytes[2].is_ascii_digit()
1284 || !bytes[3].is_ascii_digit()
1285 || bytes[4] != b'-'
1286 {
1287 working_set.error(ParseError::Expected("datetime", span));
1288 return garbage(working_set, span);
1289 }
1290
1291 let token = String::from_utf8_lossy(bytes).to_string();
1292
1293 if let Ok(datetime) = chrono::DateTime::parse_from_rfc3339(&token) {
1294 return Expression::new(working_set, Expr::DateTime(datetime), span, Type::Date);
1295 }
1296
1297 let just_date = token.clone() + "T00:00:00+00:00";
1299 if let Ok(datetime) = chrono::DateTime::parse_from_rfc3339(&just_date) {
1300 return Expression::new(working_set, Expr::DateTime(datetime), span, Type::Date);
1301 }
1302
1303 let datetime = token + "+00:00";
1305 if let Ok(datetime) = chrono::DateTime::parse_from_rfc3339(&datetime) {
1306 return Expression::new(working_set, Expr::DateTime(datetime), span, Type::Date);
1307 }
1308
1309 working_set.error(ParseError::Expected("datetime", span));
1310
1311 garbage(working_set, span)
1312}
1313
1314pub fn parse_duration(working_set: &mut StateWorkingSet, span: Span) -> Expression {
1316 trace!("parsing: duration");
1317
1318 let bytes = working_set.get_span_contents(span);
1319
1320 match parse_unit_value(bytes, span, DURATION_UNIT_GROUPS, Type::Duration, |x| x) {
1321 Some(Ok(expr)) => {
1322 let span_id = working_set.add_span(span);
1323 expr.with_span_id(span_id)
1324 }
1325 Some(Err(mk_err_for)) => {
1326 working_set.error(mk_err_for("duration"));
1327 garbage(working_set, span)
1328 }
1329 None => {
1330 working_set.error(ParseError::Expected("duration with valid units", span));
1331 garbage(working_set, span)
1332 }
1333 }
1334}
1335
1336pub fn parse_filesize(working_set: &mut StateWorkingSet, span: Span) -> Expression {
1338 trace!("parsing: filesize");
1339
1340 let bytes = working_set.get_span_contents(span);
1341
1342 if bytes.starts_with(b"0x") {
1344 working_set.error(ParseError::Expected("filesize with valid units", span));
1345 return garbage(working_set, span);
1346 }
1347
1348 match parse_unit_value(bytes, span, FILESIZE_UNIT_GROUPS, Type::Filesize, |x| {
1349 x.to_ascii_uppercase()
1350 }) {
1351 Some(Ok(expr)) => {
1352 let span_id = working_set.add_span(span);
1353 expr.with_span_id(span_id)
1354 }
1355 Some(Err(mk_err_for)) => {
1356 working_set.error(mk_err_for("filesize"));
1357 garbage(working_set, span)
1358 }
1359 None => {
1360 working_set.error(ParseError::Expected("filesize with valid units", span));
1361 garbage(working_set, span)
1362 }
1363 }
1364}
1365
1366type ParseUnitResult<'res> = Result<Expression, Box<dyn Fn(&'res str) -> ParseError>>;
1367type UnitGroup<'unit> = (Unit, &'unit str, Option<(Unit, i64)>);
1368
1369pub fn parse_unit_value<'res>(
1370 bytes: &[u8],
1371 span: Span,
1372 unit_groups: &[UnitGroup],
1373 ty: Type,
1374 transform: fn(String) -> String,
1375) -> Option<ParseUnitResult<'res>> {
1376 if bytes.len() < 2
1377 || !(bytes[0].is_ascii_digit()
1378 || (bytes[0] == b'.' && bytes[1].is_ascii_digit())
1379 || (bytes[0] == b'-' && bytes[1].is_ascii_digit()))
1380 {
1381 return None;
1382 }
1383
1384 let value = transform(str::from_utf8(bytes).ok()?.into());
1386
1387 if let Some((unit, name, convert)) = unit_groups.iter().find(|x| value.ends_with(x.1)) {
1388 let lhs_len = value.len() - name.len();
1389 let lhs = strip_underscores(&value.as_bytes()[..lhs_len]);
1390 let lhs_span = Span::new(span.start, span.start + lhs_len);
1391 let unit_span = Span::new(span.start + lhs_len, span.end);
1392 if lhs.ends_with('$') {
1393 return None;
1396 }
1397
1398 let (decimal_part, number_part) = modf(match lhs.parse::<f64>() {
1399 Ok(it) => it,
1400 Err(_) => {
1401 let mk_err = move |name| {
1402 ParseError::LabeledError(
1403 format!("{name} value must be a number"),
1404 "not a number".into(),
1405 lhs_span,
1406 )
1407 };
1408 return Some(Err(Box::new(mk_err)));
1409 }
1410 });
1411
1412 let mut unit = match convert {
1413 Some(convert_to) => convert_to.0,
1414 None => *unit,
1415 };
1416
1417 let num_float = match convert {
1418 Some(convert_to) => {
1419 (number_part * convert_to.1 as f64) + (decimal_part * convert_to.1 as f64)
1420 }
1421 None => number_part,
1422 };
1423
1424 let factor = match ty {
1427 Type::Filesize => unit_to_byte_factor(&unit),
1428 Type::Duration => unit_to_ns_factor(&unit),
1429 _ => None,
1430 };
1431
1432 let num = match factor {
1433 Some(factor) => {
1434 let num_base = num_float * factor;
1435 if i64::MIN as f64 <= num_base && num_base <= i64::MAX as f64 {
1436 unit = if ty == Type::Filesize {
1437 Unit::Filesize(FilesizeUnit::B)
1438 } else {
1439 Unit::Nanosecond
1440 };
1441 num_base as i64
1442 } else {
1443 num_float as i64
1445 }
1446 }
1447 None => num_float as i64,
1448 };
1449
1450 trace!("-- found {num} {unit:?}");
1451 let value = ValueWithUnit {
1452 expr: Expression::new_unknown(Expr::Int(num), lhs_span, Type::Number),
1453 unit: Spanned {
1454 item: unit,
1455 span: unit_span,
1456 },
1457 };
1458 let expr = Expression::new_unknown(Expr::ValueWithUnit(Box::new(value)), span, ty);
1459
1460 Some(Ok(expr))
1461 } else {
1462 None
1463 }
1464}
1465
1466pub const FILESIZE_UNIT_GROUPS: &[UnitGroup] = &[
1467 (
1468 Unit::Filesize(FilesizeUnit::KB),
1469 "KB",
1470 Some((Unit::Filesize(FilesizeUnit::B), 1000)),
1471 ),
1472 (
1473 Unit::Filesize(FilesizeUnit::MB),
1474 "MB",
1475 Some((Unit::Filesize(FilesizeUnit::KB), 1000)),
1476 ),
1477 (
1478 Unit::Filesize(FilesizeUnit::GB),
1479 "GB",
1480 Some((Unit::Filesize(FilesizeUnit::MB), 1000)),
1481 ),
1482 (
1483 Unit::Filesize(FilesizeUnit::TB),
1484 "TB",
1485 Some((Unit::Filesize(FilesizeUnit::GB), 1000)),
1486 ),
1487 (
1488 Unit::Filesize(FilesizeUnit::PB),
1489 "PB",
1490 Some((Unit::Filesize(FilesizeUnit::TB), 1000)),
1491 ),
1492 (
1493 Unit::Filesize(FilesizeUnit::EB),
1494 "EB",
1495 Some((Unit::Filesize(FilesizeUnit::PB), 1000)),
1496 ),
1497 (
1498 Unit::Filesize(FilesizeUnit::KiB),
1499 "KIB",
1500 Some((Unit::Filesize(FilesizeUnit::B), 1024)),
1501 ),
1502 (
1503 Unit::Filesize(FilesizeUnit::MiB),
1504 "MIB",
1505 Some((Unit::Filesize(FilesizeUnit::KiB), 1024)),
1506 ),
1507 (
1508 Unit::Filesize(FilesizeUnit::GiB),
1509 "GIB",
1510 Some((Unit::Filesize(FilesizeUnit::MiB), 1024)),
1511 ),
1512 (
1513 Unit::Filesize(FilesizeUnit::TiB),
1514 "TIB",
1515 Some((Unit::Filesize(FilesizeUnit::GiB), 1024)),
1516 ),
1517 (
1518 Unit::Filesize(FilesizeUnit::PiB),
1519 "PIB",
1520 Some((Unit::Filesize(FilesizeUnit::TiB), 1024)),
1521 ),
1522 (
1523 Unit::Filesize(FilesizeUnit::EiB),
1524 "EIB",
1525 Some((Unit::Filesize(FilesizeUnit::PiB), 1024)),
1526 ),
1527 (Unit::Filesize(FilesizeUnit::B), "B", None),
1528];
1529
1530pub const DURATION_UNIT_GROUPS: &[UnitGroup] = &[
1531 (Unit::Nanosecond, "ns", None),
1532 (Unit::Microsecond, "us", Some((Unit::Nanosecond, 1000))),
1534 (
1535 Unit::Microsecond,
1537 "\u{00B5}s",
1538 Some((Unit::Nanosecond, 1000)),
1539 ),
1540 (
1541 Unit::Microsecond,
1543 "\u{03BC}s",
1544 Some((Unit::Nanosecond, 1000)),
1545 ),
1546 (Unit::Millisecond, "ms", Some((Unit::Microsecond, 1000))),
1547 (Unit::Second, "sec", Some((Unit::Millisecond, 1000))),
1548 (Unit::Minute, "min", Some((Unit::Second, 60))),
1549 (Unit::Hour, "hr", Some((Unit::Minute, 60))),
1550 (Unit::Day, "day", Some((Unit::Minute, 1440))),
1551 (Unit::Week, "wk", Some((Unit::Day, 7))),
1552];
1553
1554fn unit_to_ns_factor(unit: &Unit) -> Option<f64> {
1555 match unit {
1556 Unit::Nanosecond => Some(1.0),
1557 Unit::Microsecond => Some(1_000.0),
1558 Unit::Millisecond => Some(1_000_000.0),
1559 Unit::Second => Some(1_000_000_000.0),
1560 Unit::Minute => Some(60.0 * 1_000_000_000.0),
1561 Unit::Hour => Some(60.0 * 60.0 * 1_000_000_000.0),
1562 Unit::Day => Some(24.0 * 60.0 * 60.0 * 1_000_000_000.0),
1563 Unit::Week => Some(7.0 * 24.0 * 60.0 * 60.0 * 1_000_000_000.0),
1564 _ => None,
1565 }
1566}
1567
1568fn unit_to_byte_factor(unit: &Unit) -> Option<f64> {
1569 match unit {
1570 Unit::Filesize(FilesizeUnit::B) => Some(1.0),
1571 Unit::Filesize(FilesizeUnit::KB) => Some(1_000.0),
1572 Unit::Filesize(FilesizeUnit::MB) => Some(1_000_000.0),
1573 Unit::Filesize(FilesizeUnit::GB) => Some(1_000_000_000.0),
1574 Unit::Filesize(FilesizeUnit::TB) => Some(1_000_000_000_000.0),
1575 Unit::Filesize(FilesizeUnit::PB) => Some(1_000_000_000_000_000.0),
1576 Unit::Filesize(FilesizeUnit::EB) => Some(1_000_000_000_000_000_000.0),
1577 Unit::Filesize(FilesizeUnit::KiB) => Some(1024.0),
1578 Unit::Filesize(FilesizeUnit::MiB) => Some(1024.0 * 1024.0),
1579 Unit::Filesize(FilesizeUnit::GiB) => Some(1024.0 * 1024.0 * 1024.0),
1580 Unit::Filesize(FilesizeUnit::TiB) => Some(1024.0 * 1024.0 * 1024.0 * 1024.0),
1581 Unit::Filesize(FilesizeUnit::PiB) => Some(1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0),
1582 Unit::Filesize(FilesizeUnit::EiB) => {
1583 Some(1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0)
1584 }
1585 _ => None,
1586 }
1587}
1588
1589fn modf(x: f64) -> (f64, f64) {
1591 let rv2: f64;
1592 let mut u = x.to_bits();
1593 let e = (((u >> 52) & 0x7ff) as i32) - 0x3ff;
1594
1595 if e >= 52 {
1597 rv2 = x;
1598 if e == 0x400 && (u << 12) != 0 {
1599 return (x, rv2);
1601 }
1602 u &= 1 << 63;
1603 return (f64::from_bits(u), rv2);
1604 }
1605
1606 if e < 0 {
1608 u &= 1 << 63;
1609 rv2 = f64::from_bits(u);
1610 return (x, rv2);
1611 }
1612
1613 let mask = ((!0) >> 12) >> e;
1614 if (u & mask) == 0 {
1615 rv2 = x;
1616 u &= 1 << 63;
1617 return (f64::from_bits(u), rv2);
1618 }
1619 u &= !mask;
1620 rv2 = f64::from_bits(u);
1621 (x - rv2, rv2)
1622}
1623
1624pub fn parse_glob_pattern(working_set: &mut StateWorkingSet, span: Span) -> Expression {
1625 parse_path_like(working_set, span, PathLikeKind::Glob)
1626}
1627
1628fn parse_hex_escape(bytes: &[u8], start_idx: usize, span: Span) -> Result<(u8, usize), ParseError> {
1629 let hex_digits = bytes.get(start_idx + 1..start_idx + 3).ok_or_else(|| {
1630 ParseError::InvalidLiteral(
1631 "incomplete hex escape '\\xHH', expected 2 hex digits".into(),
1632 "string".into(),
1633 Span::new(span.start + start_idx, span.end),
1634 )
1635 })?;
1636 if !hex_digits.iter().all(u8::is_ascii_hexdigit) {
1637 return Err(ParseError::InvalidLiteral(
1638 "invalid hex escape '\\xHH', expected exactly 2 hex digits".into(),
1639 "string".into(),
1640 Span::new(span.start + start_idx, span.end),
1641 ));
1642 }
1643 str::from_utf8(hex_digits)
1644 .ok()
1645 .and_then(|s| u8::from_str_radix(s, 0x10).ok())
1646 .map(|byte_val| (byte_val, start_idx + 3))
1647 .ok_or_else(|| {
1648 ParseError::InvalidLiteral(
1649 "invalid hex escape '\\xHH'".into(),
1650 "string".into(),
1651 Span::new(span.start + start_idx, span.end),
1652 )
1653 })
1654}
1655
1656fn parse_unicode_escape(
1657 bytes: &[u8],
1658 start_idx: usize,
1659 span: Span,
1660) -> Result<(char, usize), ParseError> {
1661 let mut slice = &bytes[(start_idx + 1)..];
1662 let mut current_idx = start_idx + 1;
1663
1664 slice = slice.strip_prefix(b"{").ok_or_else(|| {
1670 ParseError::InvalidLiteral(
1671 "invalid unicode escape '\\u{...}', must be 1-6 hex digits, max codepoint 0x10FFFF"
1672 .into(),
1673 "string".into(),
1674 Span::new(span.start + start_idx, span.end),
1675 )
1676 })?;
1677 current_idx += 1;
1678
1679 let end = slice.iter().position(|b| *b == b'}').ok_or_else(|| {
1680 ParseError::InvalidLiteral(
1681 "incomplete unicode escape '\\u{...}', missing closing '}'".into(),
1682 "string".into(),
1683 Span::new(span.start + start_idx, span.end),
1684 )
1685 })?;
1686 let digits = &slice[..end];
1687 current_idx += end; current_idx += 1; let current_idx = current_idx;
1690
1691 let ch = Some(digits)
1692 .filter(|b| (1..=6).contains(&b.len()))
1693 .and_then(|b| str::from_utf8(b).ok())
1694 .and_then(|s| u32::from_str_radix(s, 0x10).ok())
1695 .and_then(char::from_u32)
1696 .ok_or_else(|| {
1697 ParseError::InvalidLiteral(
1698 "invalid unicode escape '\\u{...}', must be 1-6 hex digits, max codepoint 0x10FFFF"
1699 .into(),
1700 "string".into(),
1701 Span::new(span.start + start_idx, span.end),
1702 )
1703 })?;
1704
1705 Ok((ch, current_idx))
1706}
1707
1708pub fn unescape_string(bytes: &[u8], span: Span) -> (Vec<u8>, Option<ParseError>) {
1709 let mut output = Vec::new();
1710 let mut error = None;
1711
1712 let mut idx = 0;
1713
1714 if !bytes.contains(&b'\\') {
1715 return (bytes.to_vec(), None);
1716 }
1717
1718 'us_loop: while idx < bytes.len() {
1719 if bytes[idx] == b'\\' {
1720 idx += 1;
1722
1723 match bytes.get(idx) {
1724 Some(b'"') => {
1725 output.push(b'"');
1726 idx += 1;
1727 }
1728 Some(b'\'') => {
1729 output.push(b'\'');
1730 idx += 1;
1731 }
1732 Some(b'\\') => {
1733 output.push(b'\\');
1734 idx += 1;
1735 }
1736 Some(b'/') => {
1737 output.push(b'/');
1738 idx += 1;
1739 }
1740 Some(b'(') => {
1741 output.push(b'(');
1742 idx += 1;
1743 }
1744 Some(b')') => {
1745 output.push(b')');
1746 idx += 1;
1747 }
1748 Some(b'{') => {
1749 output.push(b'{');
1750 idx += 1;
1751 }
1752 Some(b'}') => {
1753 output.push(b'}');
1754 idx += 1;
1755 }
1756 Some(b'$') => {
1757 output.push(b'$');
1758 idx += 1;
1759 }
1760 Some(b'^') => {
1761 output.push(b'^');
1762 idx += 1;
1763 }
1764 Some(b'#') => {
1765 output.push(b'#');
1766 idx += 1;
1767 }
1768 Some(b'|') => {
1769 output.push(b'|');
1770 idx += 1;
1771 }
1772 Some(b'~') => {
1773 output.push(b'~');
1774 idx += 1;
1775 }
1776 Some(b'a') => {
1777 output.push(0x7);
1778 idx += 1;
1779 }
1780 Some(b'b') => {
1781 output.push(0x8);
1782 idx += 1;
1783 }
1784 Some(b'e') => {
1785 output.push(0x1b);
1786 idx += 1;
1787 }
1788 Some(b'f') => {
1789 output.push(0xc);
1790 idx += 1;
1791 }
1792 Some(b'n') => {
1793 output.push(b'\n');
1794 idx += 1;
1795 }
1796 Some(b'r') => {
1797 output.push(b'\r');
1798 idx += 1;
1799 }
1800 Some(b't') => {
1801 output.push(b'\t');
1802 idx += 1;
1803 }
1804 Some(b'0') => {
1805 output.push(b'\0');
1806 idx += 1;
1807 }
1808 Some(b'x') => {
1809 match parse_hex_escape(bytes, idx, span) {
1811 Ok((byte_val, new_idx)) => {
1812 output.push(byte_val);
1813 idx = new_idx;
1814 }
1815 Err(err) => {
1816 error = error.or(Some(err));
1817 break 'us_loop;
1818 }
1819 }
1820 }
1821 Some(b'u') => {
1822 match parse_unicode_escape(bytes, idx, span) {
1824 Ok((ch, new_idx)) => {
1825 let mut ch_buf = [0u8; 4];
1826 output.extend(ch.encode_utf8(&mut ch_buf).as_bytes());
1827 idx = new_idx;
1828 }
1829 Err(err) => {
1830 error = error.or(Some(err));
1831 break 'us_loop;
1832 }
1833 }
1834 }
1835
1836 Some(other) => {
1837 error = error.or(Some(ParseError::InvalidLiteral(
1838 format!("unrecognized escape sequence '\\{}'", *other as char),
1839 "string".into(),
1840 Span::new(span.start + idx, span.end),
1841 )));
1842 break 'us_loop;
1843 }
1844 None => {
1845 error = error.or(Some(ParseError::InvalidLiteral(
1846 "incomplete escape sequence after '\\'".into(),
1847 "string".into(),
1848 Span::new(span.end.saturating_sub(1), span.end),
1849 )));
1850 break 'us_loop;
1851 }
1852 }
1853 } else {
1854 output.push(bytes[idx]);
1855 idx += 1;
1856 }
1857 }
1858
1859 (output, error)
1860}
1861
1862pub fn unescape_unquote_string(bytes: &[u8], span: Span) -> (String, Option<ParseError>) {
1863 if bytes.starts_with(b"\"") {
1864 let bytes = trim_quotes(bytes);
1866
1867 let (bytes, err) = unescape_string(bytes, span);
1868
1869 if let Ok(token) = String::from_utf8(bytes) {
1870 (token, err)
1871 } else {
1872 (String::new(), Some(ParseError::Expected("string", span)))
1873 }
1874 } else {
1875 let bytes = trim_quotes(bytes);
1876
1877 if let Ok(token) = String::from_utf8(bytes.into()) {
1878 (token, None)
1879 } else {
1880 (String::new(), Some(ParseError::Expected("string", span)))
1881 }
1882 }
1883}
1884
1885fn check_string_no_trailing_tokens(
1886 bytes: &[u8],
1887 span: Span,
1888 opening_quote_pos: usize,
1889 quote: u8,
1890) -> Result<(), ParseError> {
1891 let pos = bytes
1892 .iter()
1893 .rposition(|ch| *ch == quote)
1894 .expect("string begins with quote");
1895 if pos == bytes.len() - 1 {
1896 Ok(())
1897 } else if pos == opening_quote_pos {
1898 Ok(())
1902 } else {
1903 let span = Span::new(span.start + pos + 1, span.end);
1904 Err(ParseError::ExtraTokensAfterClosingDelimiter(span))
1905 }
1906}
1907
1908pub fn parse_string(working_set: &mut StateWorkingSet, span: Span) -> Expression {
1909 trace!("parsing: string");
1910
1911 let bytes = working_set.get_span_contents(span);
1912
1913 if bytes.is_empty() {
1914 working_set.error(ParseError::Expected("String", span));
1915 return Expression::garbage(working_set, span);
1916 }
1917
1918 if is_bare_string_interpolation(bytes) {
1920 return parse_string_interpolation(working_set, span);
1921 }
1922
1923 for quote in [b'\"', b'\''] {
1925 if bytes[0] == quote
1926 && let Err(err) = check_string_no_trailing_tokens(bytes, span, 0, quote)
1927 {
1928 working_set.error(err);
1929 return garbage(working_set, span);
1930 }
1931 }
1932
1933 let (s, err) = unescape_unquote_string(bytes, span);
1934 if let Some(err) = err {
1935 working_set.error(err);
1936 }
1937
1938 Expression::new(working_set, Expr::String(s), span, Type::String)
1939}
1940
1941fn is_quoted(bytes: &[u8]) -> bool {
1942 matches!(bytes, [b'\'', .., b'\''] | [b'"', .., b'"'])
1943}
1944
1945pub fn parse_string_strict(working_set: &mut StateWorkingSet, span: Span) -> Expression {
1946 trace!("parsing: string, with required delimiters");
1947
1948 let bytes = working_set.get_span_contents(span);
1949
1950 {
1952 let bytes = if bytes.starts_with(b"$") {
1953 &bytes[1..]
1954 } else {
1955 bytes
1956 };
1957 if bytes.starts_with(b"\"") && (bytes.len() == 1 || !bytes.ends_with(b"\"")) {
1958 let open = ParseError::opener_span(span, 1);
1959 working_set.error(ParseError::unclosed("\"", open, span));
1960 return garbage(working_set, span);
1961 }
1962 if bytes.starts_with(b"\'") && (bytes.len() == 1 || !bytes.ends_with(b"\'")) {
1963 let open = ParseError::opener_span(span, 1);
1964 working_set.error(ParseError::unclosed("\'", open, span));
1965 return garbage(working_set, span);
1966 }
1967 if bytes.starts_with(b"r#") && (bytes.len() == 1 || !bytes.ends_with(b"#")) {
1968 let open = ParseError::opener_span(span, 2);
1969 working_set.error(ParseError::unclosed("r#", open, span));
1970 return garbage(working_set, span);
1971 }
1972 }
1973
1974 let (bytes, quoted) = if (bytes.starts_with(b"\"") && bytes.ends_with(b"\"") && bytes.len() > 1)
1975 || (bytes.starts_with(b"\'") && bytes.ends_with(b"\'") && bytes.len() > 1)
1976 {
1977 (&bytes[1..(bytes.len() - 1)], true)
1978 } else if (bytes.starts_with(b"$\"") && bytes.ends_with(b"\"") && bytes.len() > 2)
1979 || (bytes.starts_with(b"$\'") && bytes.ends_with(b"\'") && bytes.len() > 2)
1980 {
1981 (&bytes[2..(bytes.len() - 1)], true)
1982 } else {
1983 (bytes, false)
1984 };
1985
1986 if let Ok(token) = String::from_utf8(bytes.into()) {
1987 trace!("-- found {token}");
1988
1989 if quoted {
1990 Expression::new(working_set, Expr::String(token), span, Type::String)
1991 } else if token.contains(' ') {
1992 working_set.error(ParseError::Expected("string", span));
1993
1994 garbage(working_set, span)
1995 } else {
1996 Expression::new(working_set, Expr::String(token), span, Type::String)
1997 }
1998 } else {
1999 working_set.error(ParseError::Expected("string", span));
2000 garbage(working_set, span)
2001 }
2002}