mirror of
https://github.com/nushell/nushell
synced 2024-11-15 09:27:08 +00:00
Co-authored-by: Gavin Foley <gavinmfoley@gmail.com>
This commit is contained in:
parent
0db4d89838
commit
98b9839e3d
2 changed files with 288 additions and 68 deletions
|
@ -1639,42 +1639,52 @@ pub fn parse_string_interpolation(
|
|||
let mut token_start = start;
|
||||
let mut delimiter_stack = vec![];
|
||||
|
||||
let mut consecutive_backslashes: usize = 0;
|
||||
|
||||
let mut b = start;
|
||||
|
||||
while b != end {
|
||||
if contents[b - start] == b'('
|
||||
&& (if double_quote && (b - start) > 0 {
|
||||
contents[b - start - 1] != b'\\'
|
||||
let current_byte = contents[b - start];
|
||||
|
||||
if mode == InterpolationMode::String {
|
||||
let preceding_consecutive_backslashes = consecutive_backslashes;
|
||||
|
||||
let is_backslash = current_byte == b'\\';
|
||||
consecutive_backslashes = if is_backslash {
|
||||
preceding_consecutive_backslashes + 1
|
||||
} else {
|
||||
true
|
||||
})
|
||||
&& mode == InterpolationMode::String
|
||||
{
|
||||
mode = InterpolationMode::Expression;
|
||||
if token_start < b {
|
||||
let span = Span::new(token_start, b);
|
||||
let str_contents = working_set.get_span_contents(span);
|
||||
0
|
||||
};
|
||||
|
||||
let str_contents = if double_quote {
|
||||
let (str_contents, err) = unescape_string(str_contents, span);
|
||||
error = error.or(err);
|
||||
if current_byte == b'(' && (!double_quote || preceding_consecutive_backslashes % 2 == 0)
|
||||
{
|
||||
mode = InterpolationMode::Expression;
|
||||
if token_start < b {
|
||||
let span = Span::new(token_start, b);
|
||||
let str_contents = working_set.get_span_contents(span);
|
||||
|
||||
str_contents
|
||||
} else {
|
||||
str_contents.to_vec()
|
||||
};
|
||||
let str_contents = if double_quote {
|
||||
let (str_contents, err) = unescape_string(str_contents, span);
|
||||
error = error.or(err);
|
||||
|
||||
output.push(Expression {
|
||||
expr: Expr::String(String::from_utf8_lossy(&str_contents).to_string()),
|
||||
span,
|
||||
ty: Type::String,
|
||||
custom_completion: None,
|
||||
});
|
||||
token_start = b;
|
||||
str_contents
|
||||
} else {
|
||||
str_contents.to_vec()
|
||||
};
|
||||
|
||||
output.push(Expression {
|
||||
expr: Expr::String(String::from_utf8_lossy(&str_contents).to_string()),
|
||||
span,
|
||||
ty: Type::String,
|
||||
custom_completion: None,
|
||||
});
|
||||
token_start = b;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if mode == InterpolationMode::Expression {
|
||||
let byte = contents[b - start];
|
||||
let byte = current_byte;
|
||||
if let Some(b'\'') = delimiter_stack.last() {
|
||||
if byte == b'\'' {
|
||||
delimiter_stack.pop();
|
||||
|
|
|
@ -210,48 +210,6 @@ pub fn parse_binary_with_multi_byte_char() {
|
|||
}
|
||||
}
|
||||
|
||||
#[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);
|
||||
if let PipelineElement::Expression(_, expr) = &expressions[0] {
|
||||
assert_eq!(expr.expr, Expr::String("hello nushell".to_string()))
|
||||
} else {
|
||||
panic!("Not an expression")
|
||||
}
|
||||
}
|
||||
|
||||
#[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);
|
||||
if let PipelineElement::Expression(_, expr) = &expressions[0] {
|
||||
assert_eq!(expr.expr, Expr::String("hello nushell".to_string()))
|
||||
} else {
|
||||
panic!("Not an expression")
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn parse_call() {
|
||||
let engine_state = EngineState::new();
|
||||
|
@ -410,6 +368,258 @@ 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);
|
||||
if let PipelineElement::Expression(_, expr) = &expressions[0] {
|
||||
assert_eq!(expr.expr, Expr::String("hello nushell".to_string()))
|
||||
} else {
|
||||
panic!("Not an expression")
|
||||
}
|
||||
}
|
||||
|
||||
#[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);
|
||||
if let PipelineElement::Expression(_, expr) = &expressions[0] {
|
||||
assert_eq!(expr.expr, Expr::String("hello nushell".to_string()))
|
||||
} else {
|
||||
panic!("Not an expression")
|
||||
}
|
||||
}
|
||||
|
||||
mod interpolation {
|
||||
use nu_protocol::Span;
|
||||
|
||||
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);
|
||||
|
||||
if let PipelineElement::Expression(_, expr) = &expressions[0] {
|
||||
let subexprs: Vec<&Expr>;
|
||||
match expr {
|
||||
Expression {
|
||||
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(..)));
|
||||
} else {
|
||||
panic!("Not an expression")
|
||||
}
|
||||
}
|
||||
|
||||
#[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);
|
||||
|
||||
if let PipelineElement::Expression(_, expr) = &expressions[0] {
|
||||
let subexprs: Vec<&Expr>;
|
||||
match expr {
|
||||
Expression {
|
||||
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()));
|
||||
} else {
|
||||
panic!("Not an expression")
|
||||
}
|
||||
}
|
||||
|
||||
#[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);
|
||||
|
||||
if let PipelineElement::Expression(_, expr) = &expressions[0] {
|
||||
let subexprs: Vec<&Expr>;
|
||||
match expr {
|
||||
Expression {
|
||||
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(..)));
|
||||
} else {
|
||||
panic!("Not an expression")
|
||||
}
|
||||
}
|
||||
|
||||
#[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);
|
||||
|
||||
if let PipelineElement::Expression(_, expr) = &expressions[0] {
|
||||
let subexprs: Vec<&Expr>;
|
||||
match expr {
|
||||
Expression {
|
||||
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()));
|
||||
} else {
|
||||
panic!("Not an expression")
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn parse_nested_expressions() {
|
||||
let engine_state = EngineState::new();
|
||||
let mut working_set = StateWorkingSet::new(&engine_state);
|
||||
|
||||
working_set.add_variable(
|
||||
"foo".to_string().into_bytes(),
|
||||
Span::new(0, 0),
|
||||
nu_protocol::Type::CellPath,
|
||||
false,
|
||||
);
|
||||
|
||||
let (_block, err) = parse(
|
||||
&mut working_set,
|
||||
None,
|
||||
br#"
|
||||
$"(($foo))"
|
||||
"#,
|
||||
true,
|
||||
&[],
|
||||
);
|
||||
|
||||
assert!(err.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn parse_path_expression() {
|
||||
let engine_state = EngineState::new();
|
||||
let mut working_set = StateWorkingSet::new(&engine_state);
|
||||
|
||||
working_set.add_variable(
|
||||
"foo".to_string().into_bytes(),
|
||||
Span::new(0, 0),
|
||||
nu_protocol::Type::CellPath,
|
||||
false,
|
||||
);
|
||||
|
||||
let (_block, err) = parse(
|
||||
&mut working_set,
|
||||
None,
|
||||
br#"
|
||||
$"Hello ($foo.bar)"
|
||||
"#,
|
||||
true,
|
||||
&[],
|
||||
);
|
||||
|
||||
assert!(err.is_none());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mod range {
|
||||
use super::*;
|
||||
use nu_protocol::ast::{RangeInclusion, RangeOperator};
|
||||
|
|
Loading…
Reference in a new issue