mirror of
https://github.com/nushell/nushell
synced 2024-11-10 15:14:14 +00:00
Co-authored-by: Gavin Foley <gavinmfoley@gmail.com>
This commit is contained in:
parent
5ea245badf
commit
d4798d6ee1
2 changed files with 257 additions and 105 deletions
|
@ -1604,87 +1604,104 @@ pub fn parse_string_interpolation(
|
||||||
|
|
||||||
let mut b = start;
|
let mut b = start;
|
||||||
|
|
||||||
|
let mut consecutive_backslashes: usize = 0;
|
||||||
|
|
||||||
while b != end {
|
while b != end {
|
||||||
if contents[b - start] == b'('
|
let current_byte = contents[b - start];
|
||||||
&& (if double_quote && (b - start) > 0 {
|
|
||||||
contents[b - start - 1] != b'\\'
|
|
||||||
} else {
|
|
||||||
true
|
|
||||||
})
|
|
||||||
&& mode == InterpolationMode::String
|
|
||||||
{
|
|
||||||
mode = InterpolationMode::Expression;
|
|
||||||
if token_start < b {
|
|
||||||
let span = Span {
|
|
||||||
start: token_start,
|
|
||||||
end: b,
|
|
||||||
};
|
|
||||||
let str_contents = working_set.get_span_contents(span);
|
|
||||||
|
|
||||||
let str_contents = if double_quote {
|
match mode {
|
||||||
let (str_contents, err) = unescape_string(str_contents, span);
|
InterpolationMode::String => {
|
||||||
error = error.or(err);
|
let preceding_consecutive_backslashes = consecutive_backslashes;
|
||||||
|
|
||||||
str_contents
|
let is_backslash = current_byte == b'\\';
|
||||||
|
consecutive_backslashes = if is_backslash {
|
||||||
|
preceding_consecutive_backslashes + 1
|
||||||
} else {
|
} else {
|
||||||
str_contents.to_vec()
|
0
|
||||||
};
|
};
|
||||||
|
|
||||||
output.push(Expression {
|
if current_byte == b'('
|
||||||
expr: Expr::String(String::from_utf8_lossy(&str_contents).to_string()),
|
&& (!double_quote || preceding_consecutive_backslashes % 2 == 0)
|
||||||
span,
|
{
|
||||||
ty: Type::String,
|
mode = InterpolationMode::Expression;
|
||||||
custom_completion: None,
|
|
||||||
});
|
|
||||||
token_start = b;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if mode == InterpolationMode::Expression {
|
|
||||||
let byte = contents[b - start];
|
|
||||||
if let Some(b'\'') = delimiter_stack.last() {
|
|
||||||
if byte == b'\'' {
|
|
||||||
delimiter_stack.pop();
|
|
||||||
}
|
|
||||||
} else if let Some(b'"') = delimiter_stack.last() {
|
|
||||||
if byte == b'"' {
|
|
||||||
delimiter_stack.pop();
|
|
||||||
}
|
|
||||||
} else if let Some(b'`') = delimiter_stack.last() {
|
|
||||||
if byte == b'`' {
|
|
||||||
delimiter_stack.pop();
|
|
||||||
}
|
|
||||||
} else if byte == b'\'' {
|
|
||||||
delimiter_stack.push(b'\'')
|
|
||||||
} else if byte == b'"' {
|
|
||||||
delimiter_stack.push(b'"');
|
|
||||||
} else if byte == b'`' {
|
|
||||||
delimiter_stack.push(b'`')
|
|
||||||
} else if byte == b'(' {
|
|
||||||
delimiter_stack.push(b')');
|
|
||||||
} else if byte == b')' {
|
|
||||||
if let Some(b')') = delimiter_stack.last() {
|
|
||||||
delimiter_stack.pop();
|
|
||||||
}
|
|
||||||
if delimiter_stack.is_empty() {
|
|
||||||
mode = InterpolationMode::String;
|
|
||||||
|
|
||||||
if token_start < b {
|
if token_start < b {
|
||||||
let span = Span {
|
let span = Span {
|
||||||
start: token_start,
|
start: token_start,
|
||||||
end: b + 1,
|
end: b,
|
||||||
|
};
|
||||||
|
let str_contents = working_set.get_span_contents(span);
|
||||||
|
|
||||||
|
let str_contents = if double_quote {
|
||||||
|
let (str_contents, err) = unescape_string(str_contents, span);
|
||||||
|
error = error.or(err);
|
||||||
|
|
||||||
|
str_contents
|
||||||
|
} else {
|
||||||
|
str_contents.to_vec()
|
||||||
};
|
};
|
||||||
|
|
||||||
let (expr, err) =
|
output.push(Expression {
|
||||||
parse_full_cell_path(working_set, None, span, expand_aliases_denylist);
|
expr: Expr::String(String::from_utf8_lossy(&str_contents).to_string()),
|
||||||
error = error.or(err);
|
span,
|
||||||
output.push(expr);
|
ty: Type::String,
|
||||||
|
custom_completion: None,
|
||||||
|
});
|
||||||
|
token_start = b;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
InterpolationMode::Expression => {
|
||||||
|
let byte = current_byte;
|
||||||
|
if let Some(b'\'') = delimiter_stack.last() {
|
||||||
|
if byte == b'\'' {
|
||||||
|
delimiter_stack.pop();
|
||||||
|
}
|
||||||
|
} else if let Some(b'"') = delimiter_stack.last() {
|
||||||
|
if byte == b'"' {
|
||||||
|
delimiter_stack.pop();
|
||||||
|
}
|
||||||
|
} else if let Some(b'`') = delimiter_stack.last() {
|
||||||
|
if byte == b'`' {
|
||||||
|
delimiter_stack.pop();
|
||||||
|
}
|
||||||
|
} else if byte == b'\'' {
|
||||||
|
delimiter_stack.push(b'\'')
|
||||||
|
} else if byte == b'"' {
|
||||||
|
delimiter_stack.push(b'"');
|
||||||
|
} else if byte == b'`' {
|
||||||
|
delimiter_stack.push(b'`')
|
||||||
|
} else if byte == b'(' {
|
||||||
|
delimiter_stack.push(b')');
|
||||||
|
} else if byte == b')' {
|
||||||
|
if let Some(b')') = delimiter_stack.last() {
|
||||||
|
delimiter_stack.pop();
|
||||||
|
}
|
||||||
|
if delimiter_stack.is_empty() {
|
||||||
|
mode = InterpolationMode::String;
|
||||||
|
|
||||||
token_start = b + 1;
|
if token_start < b {
|
||||||
continue;
|
let span = Span {
|
||||||
|
start: token_start,
|
||||||
|
end: b + 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
let (expr, err) = parse_full_cell_path(
|
||||||
|
working_set,
|
||||||
|
None,
|
||||||
|
span,
|
||||||
|
expand_aliases_denylist,
|
||||||
|
);
|
||||||
|
error = error.or(err);
|
||||||
|
output.push(expr);
|
||||||
|
}
|
||||||
|
|
||||||
|
token_start = b + 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
b += 1;
|
b += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -175,46 +175,6 @@ pub fn parse_binary_with_multi_byte_char() {
|
||||||
assert!(!matches!(&expressions[0].expr, Expr::Binary(_)))
|
assert!(!matches!(&expressions[0].expr, Expr::Binary(_)))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
pub fn parse_string() {
|
|
||||||
let engine_state = EngineState::new();
|
|
||||||
let mut working_set = StateWorkingSet::new(&engine_state);
|
|
||||||
|
|
||||||
let (block, err) = parse(&mut working_set, None, b"\"hello nushell\"", true, &[]);
|
|
||||||
|
|
||||||
assert!(err.is_none());
|
|
||||||
assert!(block.len() == 1);
|
|
||||||
let expressions = &block[0];
|
|
||||||
assert!(expressions.len() == 1);
|
|
||||||
assert_eq!(
|
|
||||||
expressions[0].expr,
|
|
||||||
Expr::String("hello nushell".to_string())
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
pub fn parse_escaped_string() {
|
|
||||||
let engine_state = EngineState::new();
|
|
||||||
let mut working_set = StateWorkingSet::new(&engine_state);
|
|
||||||
|
|
||||||
let (block, err) = parse(
|
|
||||||
&mut working_set,
|
|
||||||
None,
|
|
||||||
b"\"hello \\u006e\\u0075\\u0073hell\"",
|
|
||||||
true,
|
|
||||||
&[],
|
|
||||||
);
|
|
||||||
|
|
||||||
assert!(err.is_none());
|
|
||||||
assert!(block.len() == 1);
|
|
||||||
let expressions = &block[0];
|
|
||||||
assert!(expressions.len() == 1);
|
|
||||||
assert_eq!(
|
|
||||||
expressions[0].expr,
|
|
||||||
Expr::String("hello nushell".to_string())
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
pub fn parse_call() {
|
pub fn parse_call() {
|
||||||
let engine_state = EngineState::new();
|
let engine_state = EngineState::new();
|
||||||
|
@ -364,6 +324,181 @@ fn test_nothing_comparisson_neq() {
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mod string {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
pub fn parse_string() {
|
||||||
|
let engine_state = EngineState::new();
|
||||||
|
let mut working_set = StateWorkingSet::new(&engine_state);
|
||||||
|
|
||||||
|
let (block, err) = parse(&mut working_set, None, b"\"hello nushell\"", true, &[]);
|
||||||
|
|
||||||
|
assert!(err.is_none());
|
||||||
|
assert!(block.len() == 1);
|
||||||
|
let expressions = &block[0];
|
||||||
|
assert!(expressions.len() == 1);
|
||||||
|
assert_eq!(
|
||||||
|
expressions[0].expr,
|
||||||
|
Expr::String("hello nushell".to_string())
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
pub fn parse_escaped_string() {
|
||||||
|
let engine_state = EngineState::new();
|
||||||
|
let mut working_set = StateWorkingSet::new(&engine_state);
|
||||||
|
|
||||||
|
let (block, err) = parse(
|
||||||
|
&mut working_set,
|
||||||
|
None,
|
||||||
|
b"\"hello \\u006e\\u0075\\u0073hell\"",
|
||||||
|
true,
|
||||||
|
&[],
|
||||||
|
);
|
||||||
|
|
||||||
|
assert!(err.is_none());
|
||||||
|
assert!(block.len() == 1);
|
||||||
|
let expressions = &block[0];
|
||||||
|
assert!(expressions.len() == 1);
|
||||||
|
assert_eq!(
|
||||||
|
expressions[0].expr,
|
||||||
|
Expr::String("hello nushell".to_string())
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
mod interpolation {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
pub fn parse_string_interpolation() {
|
||||||
|
let engine_state = EngineState::new();
|
||||||
|
let mut working_set = StateWorkingSet::new(&engine_state);
|
||||||
|
|
||||||
|
let (block, err) = parse(&mut working_set, None, b"$\"hello (39 + 3)\"", true, &[]);
|
||||||
|
|
||||||
|
assert!(err.is_none());
|
||||||
|
assert!(block.len() == 1);
|
||||||
|
|
||||||
|
let expressions = &block[0];
|
||||||
|
assert!(expressions.len() == 1);
|
||||||
|
|
||||||
|
let expr = &expressions[0].expr;
|
||||||
|
|
||||||
|
let subexprs: Vec<&Expr>;
|
||||||
|
match expr {
|
||||||
|
Expr::StringInterpolation(expressions) => {
|
||||||
|
subexprs = expressions.iter().map(|e| &e.expr).collect();
|
||||||
|
}
|
||||||
|
_ => panic!("Expected an `Expr::StringInterpolation`"),
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_eq!(subexprs.len(), 2);
|
||||||
|
|
||||||
|
assert_eq!(subexprs[0], &Expr::String("hello ".to_string()));
|
||||||
|
|
||||||
|
assert!(matches!(subexprs[1], &Expr::FullCellPath(..)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
pub fn parse_string_interpolation_escaped_parenthesis() {
|
||||||
|
let engine_state = EngineState::new();
|
||||||
|
let mut working_set = StateWorkingSet::new(&engine_state);
|
||||||
|
|
||||||
|
let (block, err) = parse(&mut working_set, None, b"$\"hello \\(39 + 3)\"", true, &[]);
|
||||||
|
|
||||||
|
assert!(err.is_none());
|
||||||
|
|
||||||
|
assert!(block.len() == 1);
|
||||||
|
let expressions = &block[0];
|
||||||
|
|
||||||
|
assert!(expressions.len() == 1);
|
||||||
|
let expr = &expressions[0].expr;
|
||||||
|
|
||||||
|
let subexprs: Vec<&Expr>;
|
||||||
|
match expr {
|
||||||
|
Expr::StringInterpolation(expressions) => {
|
||||||
|
subexprs = expressions.iter().map(|e| &e.expr).collect();
|
||||||
|
}
|
||||||
|
_ => panic!("Expected an `Expr::StringInterpolation`"),
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_eq!(subexprs.len(), 1);
|
||||||
|
|
||||||
|
assert_eq!(subexprs[0], &Expr::String("hello (39 + 3)".to_string()));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
pub fn parse_string_interpolation_escaped_backslash_before_parenthesis() {
|
||||||
|
let engine_state = EngineState::new();
|
||||||
|
let mut working_set = StateWorkingSet::new(&engine_state);
|
||||||
|
|
||||||
|
let (block, err) = parse(
|
||||||
|
&mut working_set,
|
||||||
|
None,
|
||||||
|
b"$\"hello \\\\(39 + 3)\"",
|
||||||
|
true,
|
||||||
|
&[],
|
||||||
|
);
|
||||||
|
|
||||||
|
assert!(err.is_none());
|
||||||
|
|
||||||
|
assert!(block.len() == 1);
|
||||||
|
let expressions = &block[0];
|
||||||
|
|
||||||
|
assert!(expressions.len() == 1);
|
||||||
|
let expr = &expressions[0].expr;
|
||||||
|
|
||||||
|
let subexprs: Vec<&Expr>;
|
||||||
|
match expr {
|
||||||
|
Expr::StringInterpolation(expressions) => {
|
||||||
|
subexprs = expressions.iter().map(|e| &e.expr).collect();
|
||||||
|
}
|
||||||
|
_ => panic!("Expected an `Expr::StringInterpolation`"),
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_eq!(subexprs.len(), 2);
|
||||||
|
|
||||||
|
assert_eq!(subexprs[0], &Expr::String("hello \\".to_string()));
|
||||||
|
|
||||||
|
assert!(matches!(subexprs[1], &Expr::FullCellPath(..)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
pub fn parse_string_interpolation_backslash_count_reset_by_expression() {
|
||||||
|
let engine_state = EngineState::new();
|
||||||
|
let mut working_set = StateWorkingSet::new(&engine_state);
|
||||||
|
|
||||||
|
let (block, err) = parse(
|
||||||
|
&mut working_set,
|
||||||
|
None,
|
||||||
|
b"$\"\\(1 + 3)\\(7 - 5)\"",
|
||||||
|
true,
|
||||||
|
&[],
|
||||||
|
);
|
||||||
|
|
||||||
|
assert!(err.is_none());
|
||||||
|
|
||||||
|
assert!(block.len() == 1);
|
||||||
|
let expressions = &block[0];
|
||||||
|
|
||||||
|
assert!(expressions.len() == 1);
|
||||||
|
let expr = &expressions[0].expr;
|
||||||
|
|
||||||
|
let subexprs: Vec<&Expr>;
|
||||||
|
match expr {
|
||||||
|
Expr::StringInterpolation(expressions) => {
|
||||||
|
subexprs = expressions.iter().map(|e| &e.expr).collect();
|
||||||
|
}
|
||||||
|
_ => panic!("Expected an `Expr::StringInterpolation`"),
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_eq!(subexprs.len(), 1);
|
||||||
|
assert_eq!(subexprs[0], &Expr::String("(1 + 3)(7 - 5)".to_string()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
mod range {
|
mod range {
|
||||||
use super::*;
|
use super::*;
|
||||||
use nu_protocol::ast::{RangeInclusion, RangeOperator};
|
use nu_protocol::ast::{RangeInclusion, RangeOperator};
|
||||||
|
|
Loading…
Reference in a new issue