diff --git a/crates/nu-parser/src/parser.rs b/crates/nu-parser/src/parser.rs index fc2131aad7..9ba7038987 100644 --- a/crates/nu-parser/src/parser.rs +++ b/crates/nu-parser/src/parser.rs @@ -6308,7 +6308,12 @@ fn wrap_expr_with_collect(working_set: &mut StateWorkingSet, expr: &Expression) if let Some(decl_id) = working_set.find_decl(b"collect") { let mut output = vec![]; - let var_id = IN_VARIABLE_ID; + // IN_VARIABLE_ID should get replaced with a unique variable, so that we don't have to + // execute as a closure + let var_id = working_set.add_variable(b"$in".into(), expr.span, Type::Any, false); + let mut expr = expr.clone(); + expr.replace_in_variable(working_set, var_id); + let mut signature = Signature::new(""); signature.required_positional.push(PositionalArg { var_id: Some(var_id), diff --git a/crates/nu-protocol/src/ast/block.rs b/crates/nu-protocol/src/ast/block.rs index 8f62ff99ba..b5ddd84e68 100644 --- a/crates/nu-protocol/src/ast/block.rs +++ b/crates/nu-protocol/src/ast/block.rs @@ -78,6 +78,19 @@ impl Block { Type::Nothing } } + + /// Replace any `$in` variables in the initial element of pipelines within the block + pub fn replace_in_variable( + &mut self, + working_set: &mut StateWorkingSet<'_>, + new_var_id: usize, + ) { + for pipeline in self.pipelines.iter_mut() { + if let Some(element) = pipeline.elements.first_mut() { + element.replace_in_variable(working_set, new_var_id); + } + } + } } impl From for Block diff --git a/crates/nu-protocol/src/ast/expression.rs b/crates/nu-protocol/src/ast/expression.rs index dfd4187a90..5008fedef9 100644 --- a/crates/nu-protocol/src/ast/expression.rs +++ b/crates/nu-protocol/src/ast/expression.rs @@ -6,6 +6,8 @@ use crate::{ use serde::{Deserialize, Serialize}; use std::sync::Arc; +use super::ListItem; + /// Wrapper around [`Expr`] #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct Expression { @@ -106,38 +108,9 @@ impl Expression { left.has_in_variable(working_set) || right.has_in_variable(working_set) } Expr::UnaryNot(expr) => expr.has_in_variable(working_set), - Expr::Block(block_id) => { - let block = working_set.get_block(*block_id); - - if block.captures.contains(&IN_VARIABLE_ID) { - return true; - } - - if let Some(pipeline) = block.pipelines.first() { - match pipeline.elements.first() { - Some(element) => element.has_in_variable(working_set), - None => false, - } - } else { - false - } - } - Expr::Closure(block_id) => { - let block = working_set.get_block(*block_id); - - if block.captures.contains(&IN_VARIABLE_ID) { - return true; - } - - if let Some(pipeline) = block.pipelines.first() { - match pipeline.elements.first() { - Some(element) => element.has_in_variable(working_set), - None => false, - } - } else { - false - } - } + // The $in variable in blocks and closures is local, as they have their own input + Expr::Block(_) => false, + Expr::Closure(_) => false, Expr::Binary(_) => false, Expr::Bool(_) => false, Expr::Call(call) => { @@ -443,6 +416,126 @@ impl Expression { } } + pub fn replace_in_variable(&mut self, working_set: &mut StateWorkingSet, new_var_id: VarId) { + match &mut self.expr { + Expr::Bool(_) => {} + Expr::Int(_) => {} + Expr::Float(_) => {} + Expr::Binary(_) => {} + Expr::Range(_) => {} + Expr::Var(var_id) | Expr::VarDecl(var_id) => { + if *var_id == IN_VARIABLE_ID { + *var_id = new_var_id; + } + } + Expr::Call(call) => { + for arg in call.arguments.iter_mut() { + match arg { + Argument::Positional(expr) + | Argument::Unknown(expr) + | Argument::Named((_, _, Some(expr))) + | Argument::Spread(expr) => { + expr.replace_in_variable(working_set, new_var_id) + } + Argument::Named((_, _, None)) => {} + } + } + for expr in call.parser_info.values_mut() { + expr.replace_in_variable(working_set, new_var_id) + } + } + Expr::ExternalCall(head, args) => { + head.replace_in_variable(working_set, new_var_id); + for arg in args.iter_mut() { + match arg { + ExternalArgument::Regular(expr) | ExternalArgument::Spread(expr) => { + expr.replace_in_variable(working_set, new_var_id) + } + } + } + } + Expr::Operator(_) => {} + // These have their own input + Expr::Block(_) | Expr::Closure(_) => {} + Expr::RowCondition(block_id) | Expr::Subexpression(block_id) => { + let mut block = Block::clone(working_set.get_block(*block_id)); + block.replace_in_variable(working_set, new_var_id); + *working_set.get_block_mut(*block_id) = block; + } + Expr::UnaryNot(expr) => { + expr.replace_in_variable(working_set, new_var_id); + } + Expr::BinaryOp(lhs, op, rhs) => { + for expr in [lhs, op, rhs] { + expr.replace_in_variable(working_set, new_var_id); + } + } + Expr::MatchBlock(match_patterns) => { + for (_, expr) in match_patterns.iter_mut() { + expr.replace_in_variable(working_set, new_var_id); + } + } + Expr::List(items) => { + for item in items.iter_mut() { + match item { + ListItem::Item(expr) | ListItem::Spread(_, expr) => { + expr.replace_in_variable(working_set, new_var_id) + } + } + } + } + Expr::Table(table) => { + for col_expr in table.columns.iter_mut() { + col_expr.replace_in_variable(working_set, new_var_id); + } + for row in table.rows.iter_mut() { + for row_expr in row.iter_mut() { + row_expr.replace_in_variable(working_set, new_var_id); + } + } + } + Expr::Record(items) => { + for item in items.iter_mut() { + match item { + RecordItem::Pair(key, val) => { + key.replace_in_variable(working_set, new_var_id); + val.replace_in_variable(working_set, new_var_id); + } + RecordItem::Spread(_, expr) => { + expr.replace_in_variable(working_set, new_var_id) + } + } + } + } + Expr::Keyword(kw) => kw.expr.replace_in_variable(working_set, new_var_id), + Expr::ValueWithUnit(value_with_unit) => value_with_unit + .expr + .replace_in_variable(working_set, new_var_id), + Expr::DateTime(_) => {} + Expr::Filepath(_, _) => {} + Expr::Directory(_, _) => {} + Expr::GlobPattern(_, _) => {} + Expr::String(_) => {} + Expr::RawString(_) => {} + Expr::CellPath(_) => {} + Expr::FullCellPath(full_cell_path) => { + full_cell_path + .head + .replace_in_variable(working_set, new_var_id); + } + Expr::ImportPattern(_) => {} + Expr::Overlay(_) => {} + Expr::Signature(_) => {} + Expr::StringInterpolation(exprs) | Expr::GlobInterpolation(exprs, _) => { + for expr in exprs.iter_mut() { + expr.replace_in_variable(working_set, new_var_id); + } + } + Expr::Nothing => {} + Expr::Garbage => {} + } + } + pub fn new(working_set: &mut StateWorkingSet, expr: Expr, span: Span, ty: Type) -> Expression { let span_id = working_set.add_span(span); Expression { diff --git a/crates/nu-protocol/src/ast/pipeline.rs b/crates/nu-protocol/src/ast/pipeline.rs index 3f2a216485..7991a6fcf6 100644 --- a/crates/nu-protocol/src/ast/pipeline.rs +++ b/crates/nu-protocol/src/ast/pipeline.rs @@ -62,6 +62,19 @@ impl RedirectionTarget { RedirectionTarget::Pipe { .. } => {} } } + + pub fn replace_in_variable( + &mut self, + working_set: &mut StateWorkingSet<'_>, + new_var_id: usize, + ) { + match self { + RedirectionTarget::File { expr, .. } => { + expr.replace_in_variable(working_set, new_var_id) + } + RedirectionTarget::Pipe { .. } => {} + } + } } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] @@ -75,6 +88,23 @@ pub enum PipelineRedirection { err: RedirectionTarget, }, } +impl PipelineRedirection { + pub fn replace_in_variable( + &mut self, + working_set: &mut StateWorkingSet<'_>, + new_var_id: usize, + ) { + match self { + PipelineRedirection::Single { source: _, target } => { + target.replace_in_variable(working_set, new_var_id) + } + PipelineRedirection::Separate { out, err } => { + out.replace_in_variable(working_set, new_var_id); + err.replace_in_variable(working_set, new_var_id); + } + } + } +} #[derive(Debug, Clone, Serialize, Deserialize)] pub struct PipelineElement { @@ -120,6 +150,17 @@ impl PipelineElement { ) -> (Option, Option) { self.expr.expr.pipe_redirection(working_set) } + + pub fn replace_in_variable( + &mut self, + working_set: &mut StateWorkingSet<'_>, + new_var_id: usize, + ) { + self.expr.replace_in_variable(working_set, new_var_id); + if let Some(redirection) = &mut self.redirection { + redirection.replace_in_variable(working_set, new_var_id); + } + } } #[derive(Debug, Clone, Serialize, Deserialize)]