mirror of
https://github.com/nushell/nushell.git
synced 2025-08-09 02:55:07 +02:00
fix parsing of bare word string interpolations that start with a sub expression (#15735)
- fixes #15731 # Description Existing bare word string interpolation only works if the string doesn't start with a subxpression. ```nushell echo fork(2) # => fork2 echo (2)fork # => Error: nu::parser::unclosed_delimiter # => # => × Unclosed delimiter. # => ╭─[entry #25:1:13] # => 1 │ echo (2)fork # => ╰──── ``` This PR lifts that restriction. ```nushell echo fork(2) # => fork2 echo (2)fork # => 2fork ``` This was first brought to my attention on discord with the following command failing to parse. ```nushell docker run -u (id -u):(id -g) ``` It now works. # User-Facing Changes # Tests + Formatting No existing test broke or required tweaking. Additional tests covering this case was added. - 🟢 toolkit fmt - 🟢 toolkit clippy - 🟢 toolkit test - 🟢 toolkit test stdlib # After Submitting --------- Co-authored-by: Bahex <17417311+Bahex@users.noreply.github.com>
This commit is contained in:
@ -1576,6 +1576,78 @@ mod string {
|
||||
assert!(matches!(subexprs[3], &Expr::FullCellPath(..)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn parse_string_interpolation_bare_starting_subexpr() {
|
||||
let engine_state = EngineState::new();
|
||||
let mut working_set = StateWorkingSet::new(&engine_state);
|
||||
|
||||
let block = parse(
|
||||
&mut working_set,
|
||||
None,
|
||||
b"\"\" ++ (1 + 3)foo(7 - 5)bar",
|
||||
true,
|
||||
);
|
||||
|
||||
assert!(working_set.parse_errors.is_empty());
|
||||
|
||||
assert_eq!(block.len(), 1);
|
||||
let pipeline = &block.pipelines[0];
|
||||
assert_eq!(pipeline.len(), 1);
|
||||
let element = &pipeline.elements[0];
|
||||
assert!(element.redirection.is_none());
|
||||
|
||||
let subexprs: Vec<&Expr> = match &element.expr.expr {
|
||||
Expr::BinaryOp(_, _, rhs) => match &rhs.expr {
|
||||
Expr::StringInterpolation(expressions) => {
|
||||
expressions.iter().map(|e| &e.expr).collect()
|
||||
}
|
||||
_ => panic!("Expected an `Expr::StringInterpolation`"),
|
||||
},
|
||||
_ => panic!("Expected an `Expr::BinaryOp`"),
|
||||
};
|
||||
|
||||
assert_eq!(subexprs.len(), 4);
|
||||
|
||||
assert!(matches!(subexprs[0], &Expr::FullCellPath(..)));
|
||||
assert_eq!(subexprs[1], &Expr::String("foo".to_string()));
|
||||
assert!(matches!(subexprs[2], &Expr::FullCellPath(..)));
|
||||
assert_eq!(subexprs[3], &Expr::String("bar".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn parse_string_interpolation_bare_starting_subexpr_external_arg() {
|
||||
let engine_state = EngineState::new();
|
||||
let mut working_set = StateWorkingSet::new(&engine_state);
|
||||
|
||||
let block = parse(&mut working_set, None, b"^echo ($nu.home-path)/path", true);
|
||||
|
||||
assert!(working_set.parse_errors.is_empty());
|
||||
|
||||
assert_eq!(block.len(), 1);
|
||||
let pipeline = &block.pipelines[0];
|
||||
assert_eq!(pipeline.len(), 1);
|
||||
let element = &pipeline.elements[0];
|
||||
assert!(element.redirection.is_none());
|
||||
|
||||
let subexprs: Vec<&Expr> = match &element.expr.expr {
|
||||
Expr::ExternalCall(_, args) => match &args[0] {
|
||||
ExternalArgument::Regular(expression) => match &expression.expr {
|
||||
Expr::StringInterpolation(expressions) => {
|
||||
expressions.iter().map(|e| &e.expr).collect()
|
||||
}
|
||||
_ => panic!("Expected an `ExternalArgument::Regular`"),
|
||||
},
|
||||
_ => panic!("Expected an `Expr::StringInterpolation`"),
|
||||
},
|
||||
_ => panic!("Expected an `Expr::BinaryOp`"),
|
||||
};
|
||||
|
||||
assert_eq!(subexprs.len(), 2);
|
||||
|
||||
assert!(matches!(subexprs[0], &Expr::FullCellPath(..)));
|
||||
assert_eq!(subexprs[1], &Expr::String("/path".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn parse_nested_expressions() {
|
||||
let engine_state = EngineState::new();
|
||||
|
Reference in New Issue
Block a user