diff --git a/crates/nu-cli/src/completions/variable_completions.rs b/crates/nu-cli/src/completions/variable_completions.rs index 9f47197998..2eb5b43817 100644 --- a/crates/nu-cli/src/completions/variable_completions.rs +++ b/crates/nu-cli/src/completions/variable_completions.rs @@ -179,11 +179,7 @@ impl Completer for VariableCompletion { let mut removed_overlays = vec![]; // Working set scope vars for scope_frame in working_set.delta.scope.iter().rev() { - for overlay_frame in scope_frame - .active_overlays(&mut removed_overlays) - .iter() - .rev() - { + for overlay_frame in scope_frame.active_overlays(&mut removed_overlays).rev() { for v in &overlay_frame.vars { if options.match_algorithm.matches_u8_insensitive( options.case_sensitive, @@ -204,12 +200,7 @@ impl Completer for VariableCompletion { // Permanent state vars // for scope in &self.engine_state.scope { - for overlay_frame in self - .engine_state - .active_overlays(&removed_overlays) - .iter() - .rev() - { + for overlay_frame in self.engine_state.active_overlays(&removed_overlays).rev() { for v in &overlay_frame.vars { if options.match_algorithm.matches_u8_insensitive( options.case_sensitive, diff --git a/crates/nu-parser/src/parse_keywords.rs b/crates/nu-parser/src/parse_keywords.rs index cfb2597bb5..2704e3f075 100644 --- a/crates/nu-parser/src/parse_keywords.rs +++ b/crates/nu-parser/src/parse_keywords.rs @@ -232,6 +232,17 @@ pub fn parse_for(working_set: &mut StateWorkingSet, spans: &[Span]) -> Expressio let decl = working_set.get_decl(decl_id); let sig = decl.signature(); + let starting_error_count = working_set.parse_errors.len(); + check_call(working_set, call_span, &sig, &call); + if starting_error_count != working_set.parse_errors.len() || call.has_flag("help") { + return Expression { + expr: Expr::Call(call), + span: call_span, + ty: output, + custom_completion: None, + }; + } + // Let's get our block and make sure it has the right signature if let Some(arg) = call.positional_nth(2) { match arg { @@ -245,23 +256,12 @@ pub fn parse_for(working_set: &mut StateWorkingSet, spans: &[Span]) -> Expressio } => { let block = working_set.get_block_mut(*block_id); - block.signature = Box::new(sig.clone()); + block.signature = Box::new(sig); } _ => {} } } - let starting_error_count = working_set.parse_errors.len(); - check_call(working_set, call_span, &sig, &call); - if starting_error_count != working_set.parse_errors.len() || call.has_flag("help") { - return Expression { - expr: Expr::Call(call), - span: call_span, - ty: output, - custom_completion: None, - }; - } - (call, call_span) } }; @@ -1403,85 +1403,87 @@ pub fn parse_module_block( let mut module = Module::from_span(module_name.to_vec(), span); - let block: Block = output - .block - .iter() - .map(|pipeline| { - if pipeline.commands.len() == 1 { - match &pipeline.commands[0] { - LiteElement::Command(_, command) => { - let name = working_set.get_span_contents(command.parts[0]); + let mut block = Block::new_with_capacity(output.block.len()); - match name { - b"def" | b"def-env" => { - parse_def( - working_set, - command, - None, // using commands named as the module locally is OK - ) - } - b"extern" => parse_extern(working_set, command, None), - b"alias" => { - parse_alias( - working_set, - command, - None, // using aliases named as the module locally is OK - ) - } - b"use" => { - let (pipeline, _) = parse_use(working_set, &command.parts); + for pipeline in output.block.iter() { + if pipeline.commands.len() == 1 { + match &pipeline.commands[0] { + LiteElement::Command(_, command) => { + let name = working_set.get_span_contents(command.parts[0]); - pipeline - } - b"export" => { - let (pipe, exportables) = - parse_export_in_module(working_set, command, module_name); + match name { + b"def" | b"def-env" => { + block.pipelines.push(parse_def( + working_set, + command, + None, // using commands named as the module locally is OK + )) + } + b"extern" => block + .pipelines + .push(parse_extern(working_set, command, None)), + b"alias" => { + block.pipelines.push(parse_alias( + working_set, + command, + None, // using aliases named as the module locally is OK + )) + } + b"use" => { + let (pipeline, _) = parse_use(working_set, &command.parts); - for exportable in exportables { - match exportable { - Exportable::Decl { name, id } => { - if &name == b"main" { - module.main = Some(id); - } else { - module.add_decl(name, id); - } + block.pipelines.push(pipeline) + } + b"export" => { + let (pipe, exportables) = + parse_export_in_module(working_set, command, module_name); + + for exportable in exportables { + match exportable { + Exportable::Decl { name, id } => { + if &name == b"main" { + module.main = Some(id); + } else { + module.add_decl(name, id); } } } - - pipe } - b"export-env" => { - let (pipe, maybe_env_block) = - parse_export_env(working_set, &command.parts); - if let Some(block_id) = maybe_env_block { - module.add_env_block(block_id); - } + block.pipelines.push(pipe) + } + b"export-env" => { + let (pipe, maybe_env_block) = + parse_export_env(working_set, &command.parts); - pipe + if let Some(block_id) = maybe_env_block { + module.add_env_block(block_id); } - _ => { - working_set.error(ParseError::ExpectedKeyword( - "def or export keyword".into(), - command.parts[0], - )); - garbage_pipeline(&command.parts) - } + block.pipelines.push(pipe) + } + _ => { + working_set.error(ParseError::ExpectedKeyword( + "def or export keyword".into(), + command.parts[0], + )); + + block.pipelines.push(garbage_pipeline(&command.parts)) } } - LiteElement::Redirection(_, _, command) => garbage_pipeline(&command.parts), - LiteElement::SeparateRedirection { - out: (_, command), .. - } => garbage_pipeline(&command.parts), } - } else { - working_set.error(ParseError::Expected("not a pipeline".into(), span)); - garbage_pipeline(&[span]) + LiteElement::Redirection(_, _, command) => { + block.pipelines.push(garbage_pipeline(&command.parts)) + } + LiteElement::SeparateRedirection { + out: (_, command), .. + } => block.pipelines.push(garbage_pipeline(&command.parts)), } - }) - .into(); + } else { + working_set.error(ParseError::Expected("not a pipeline".into(), span)); + block.pipelines.push(garbage_pipeline(&[span])) + } + } working_set.exit_scope(); diff --git a/crates/nu-parser/src/parser.rs b/crates/nu-parser/src/parser.rs index 698064207a..f533e8f8d4 100644 --- a/crates/nu-parser/src/parser.rs +++ b/crates/nu-parser/src/parser.rs @@ -2145,7 +2145,12 @@ pub fn parse_datetime(working_set: &mut StateWorkingSet, span: Span) -> Expressi let bytes = working_set.get_span_contents(span); - if bytes.is_empty() || !bytes[0].is_ascii_digit() { + if bytes.len() < 5 + || !bytes[0].is_ascii_digit() + || !bytes[1].is_ascii_digit() + || !bytes[2].is_ascii_digit() + || !bytes[3].is_ascii_digit() + { working_set.error(ParseError::Expected("datetime".into(), span)); return garbage(span); } @@ -2450,6 +2455,10 @@ pub fn unescape_string(bytes: &[u8], span: Span) -> (Vec, Option let mut idx = 0; + if !bytes.contains(&b'\\') { + return (bytes.to_vec(), None); + } + 'us_loop: while idx < bytes.len() { if bytes[idx] == b'\\' { // We're in an escape @@ -4523,7 +4532,6 @@ pub fn parse_value( } } working_set.error(ParseError::Expected("any shape".into(), span)); - garbage(span) } } @@ -5217,132 +5225,127 @@ pub fn parse_block( } } - let block: Block = lite_block - .block - .iter() - .enumerate() - .map(|(idx, pipeline)| { - if pipeline.commands.len() > 1 { - let mut output = pipeline - .commands - .iter() - .map(|command| match command { - LiteElement::Command(span, command) => { - trace!("parsing: pipeline element: command"); - let expr = - parse_expression(working_set, &command.parts, is_subexpression); - working_set.type_scope.add_type(expr.ty.clone()); + let mut block = Block::new_with_capacity(lite_block.block.len()); - PipelineElement::Expression(*span, expr) - } - LiteElement::Redirection(span, redirection, command) => { - trace!("parsing: pipeline element: redirection"); - let expr = parse_string(working_set, command.parts[0]); + for (idx, pipeline) in lite_block.block.iter().enumerate() { + if pipeline.commands.len() > 1 { + let mut output = pipeline + .commands + .iter() + .map(|command| match command { + LiteElement::Command(span, command) => { + trace!("parsing: pipeline element: command"); + let expr = parse_expression(working_set, &command.parts, is_subexpression); + working_set.type_scope.add_type(expr.ty.clone()); - working_set.type_scope.add_type(expr.ty.clone()); + PipelineElement::Expression(*span, expr) + } + LiteElement::Redirection(span, redirection, command) => { + trace!("parsing: pipeline element: redirection"); + let expr = parse_string(working_set, command.parts[0]); - PipelineElement::Redirection(*span, redirection.clone(), expr) - } - LiteElement::SeparateRedirection { - out: (out_span, out_command), - err: (err_span, err_command), - } => { - trace!("parsing: pipeline element: separate redirection"); - let out_expr = parse_string(working_set, out_command.parts[0]); + working_set.type_scope.add_type(expr.ty.clone()); - working_set.type_scope.add_type(out_expr.ty.clone()); + PipelineElement::Redirection(*span, redirection.clone(), expr) + } + LiteElement::SeparateRedirection { + out: (out_span, out_command), + err: (err_span, err_command), + } => { + trace!("parsing: pipeline element: separate redirection"); + let out_expr = parse_string(working_set, out_command.parts[0]); - let err_expr = parse_string(working_set, err_command.parts[0]); + working_set.type_scope.add_type(out_expr.ty.clone()); - working_set.type_scope.add_type(err_expr.ty.clone()); + let err_expr = parse_string(working_set, err_command.parts[0]); - PipelineElement::SeparateRedirection { - out: (*out_span, out_expr), - err: (*err_span, err_expr), - } - } - }) - .collect::>(); + working_set.type_scope.add_type(err_expr.ty.clone()); - if is_subexpression { - for element in output.iter_mut().skip(1) { - if element.has_in_variable(working_set) { - *element = wrap_element_with_collect(working_set, element); + PipelineElement::SeparateRedirection { + out: (*out_span, out_expr), + err: (*err_span, err_expr), } } - } else { - for element in output.iter_mut() { - if element.has_in_variable(working_set) { - *element = wrap_element_with_collect(working_set, element); - } + }) + .collect::>(); + + if is_subexpression { + for element in output.iter_mut().skip(1) { + if element.has_in_variable(working_set) { + *element = wrap_element_with_collect(working_set, element); } } - - Pipeline { elements: output } } else { - match &pipeline.commands[0] { - LiteElement::Command(_, command) - | LiteElement::Redirection(_, _, command) - | LiteElement::SeparateRedirection { - out: (_, command), .. - } => { - let mut pipeline = - parse_builtin_commands(working_set, command, is_subexpression); + for element in output.iter_mut() { + if element.has_in_variable(working_set) { + *element = wrap_element_with_collect(working_set, element); + } + } + } - if idx == 0 { - if let Some(let_decl_id) = working_set.find_decl(b"let", &Type::Any) { - if let Some(let_env_decl_id) = - working_set.find_decl(b"let-env", &Type::Any) - { - for element in pipeline.elements.iter_mut() { - if let PipelineElement::Expression( - _, - Expression { - expr: Expr::Call(call), - .. - }, - ) = element + block.pipelines.push(Pipeline { elements: output }) + } else { + match &pipeline.commands[0] { + LiteElement::Command(_, command) + | LiteElement::Redirection(_, _, command) + | LiteElement::SeparateRedirection { + out: (_, command), .. + } => { + let mut pipeline = + parse_builtin_commands(working_set, command, is_subexpression); + + if idx == 0 { + if let Some(let_decl_id) = working_set.find_decl(b"let", &Type::Any) { + if let Some(let_env_decl_id) = + working_set.find_decl(b"let-env", &Type::Any) + { + for element in pipeline.elements.iter_mut() { + if let PipelineElement::Expression( + _, + Expression { + expr: Expr::Call(call), + .. + }, + ) = element + { + if call.decl_id == let_decl_id + || call.decl_id == let_env_decl_id { - if call.decl_id == let_decl_id - || call.decl_id == let_env_decl_id + // Do an expansion + if let Some(Expression { + expr: Expr::Keyword(_, _, expr), + .. + }) = call.positional_iter_mut().nth(1) { - // Do an expansion - if let Some(Expression { - expr: Expr::Keyword(_, _, expr), - .. - }) = call.positional_iter_mut().nth(1) - { - if expr.has_in_variable(working_set) { - *expr = Box::new(wrap_expr_with_collect( - working_set, - expr, - )); - } + if expr.has_in_variable(working_set) { + *expr = Box::new(wrap_expr_with_collect( + working_set, + expr, + )); } - continue; - } else if element.has_in_variable(working_set) - && !is_subexpression - { - *element = - wrap_element_with_collect(working_set, element); } + continue; } else if element.has_in_variable(working_set) && !is_subexpression { *element = wrap_element_with_collect(working_set, element); } + } else if element.has_in_variable(working_set) + && !is_subexpression + { + *element = wrap_element_with_collect(working_set, element); } } } } - - pipeline } + + block.pipelines.push(pipeline) } } - }) - .into(); + } + } if scoped { working_set.exit_scope(); @@ -5663,6 +5666,7 @@ pub fn discover_captures_in_expr( )?; results }; + seen_blocks.insert(*block_id, results.clone()); for (var_id, span) in results.into_iter() { if !seen.contains(&var_id) { diff --git a/crates/nu-protocol/src/ast/block.rs b/crates/nu-protocol/src/ast/block.rs index 0ec405c30c..55ad6de7aa 100644 --- a/crates/nu-protocol/src/ast/block.rs +++ b/crates/nu-protocol/src/ast/block.rs @@ -55,6 +55,17 @@ impl Block { recursive: None, } } + + pub fn new_with_capacity(capacity: usize) -> Self { + Self { + signature: Box::new(Signature::new("")), + pipelines: Vec::with_capacity(capacity), + captures: vec![], + redirect_env: false, + span: None, + recursive: None, + } + } } impl From for Block diff --git a/crates/nu-protocol/src/engine/engine_state.rs b/crates/nu-protocol/src/engine/engine_state.rs index dfed7272cf..2464ba7f12 100644 --- a/crates/nu-protocol/src/engine/engine_state.rs +++ b/crates/nu-protocol/src/engine/engine_state.rs @@ -323,27 +323,39 @@ impl EngineState { .any(|(overlay_name, _)| name == overlay_name) } - pub fn active_overlay_ids(&self, removed_overlays: &[Vec]) -> Vec { + pub fn active_overlay_ids<'a, 'b>( + &'b self, + removed_overlays: &'a [Vec], + ) -> impl DoubleEndedIterator + 'a + where + 'b: 'a, + { self.scope .active_overlays .iter() .filter(|id| !removed_overlays.contains(self.get_overlay_name(**id))) - .copied() - .collect() } - pub fn active_overlays(&self, removed_overlays: &[Vec]) -> Vec<&OverlayFrame> { + pub fn active_overlays<'a, 'b>( + &'b self, + removed_overlays: &'a [Vec], + ) -> impl DoubleEndedIterator + 'a + where + 'b: 'a, + { self.active_overlay_ids(removed_overlays) - .iter() .map(|id| self.get_overlay(*id)) - .collect() } - pub fn active_overlay_names(&self, removed_overlays: &[Vec]) -> Vec<&Vec> { + pub fn active_overlay_names<'a, 'b>( + &'b self, + removed_overlays: &'a [Vec], + ) -> impl DoubleEndedIterator> + 'a + where + 'b: 'a, + { self.active_overlay_ids(removed_overlays) - .iter() .map(|id| self.get_overlay_name(*id)) - .collect() } /// Translate overlay IDs from other to IDs in self @@ -587,7 +599,7 @@ impl EngineState { pub fn find_decl(&self, name: &[u8], removed_overlays: &[Vec]) -> Option { let mut visibility: Visibility = Visibility::new(); - for overlay_frame in self.active_overlays(removed_overlays).iter().rev() { + for overlay_frame in self.active_overlays(removed_overlays).rev() { visibility.append(&overlay_frame.visibility); if let Some(decl_id) = overlay_frame.get_decl(name, &Type::Any) { @@ -603,7 +615,7 @@ impl EngineState { pub fn find_decl_name(&self, decl_id: DeclId, removed_overlays: &[Vec]) -> Option<&[u8]> { let mut visibility: Visibility = Visibility::new(); - for overlay_frame in self.active_overlays(removed_overlays).iter().rev() { + for overlay_frame in self.active_overlays(removed_overlays).rev() { visibility.append(&overlay_frame.visibility); if visibility.is_decl_id_visible(&decl_id) { @@ -640,7 +652,7 @@ impl EngineState { } pub fn find_module(&self, name: &[u8], removed_overlays: &[Vec]) -> Option { - for overlay_frame in self.active_overlays(removed_overlays).iter().rev() { + for overlay_frame in self.active_overlays(removed_overlays).rev() { if let Some(module_id) = overlay_frame.modules.get(name) { return Some(*module_id); } @@ -654,7 +666,7 @@ impl EngineState { decl_name: &[u8], removed_overlays: &[Vec], ) -> Option<&[u8]> { - for overlay_frame in self.active_overlays(removed_overlays).iter().rev() { + for overlay_frame in self.active_overlays(removed_overlays).rev() { for (module_name, module_id) in overlay_frame.modules.iter() { let module = self.get_module(*module_id); if module.has_decl(decl_name) { @@ -680,7 +692,7 @@ impl EngineState { ) -> Vec<(Vec, Option)> { let mut output = vec![]; - for overlay_frame in self.active_overlays(&[]).iter().rev() { + for overlay_frame in self.active_overlays(&[]).rev() { for decl in &overlay_frame.decls { if overlay_frame.visibility.is_decl_id_visible(decl.1) && predicate(&decl.0 .0) { let command = self.get_decl(*decl.1); @@ -693,7 +705,7 @@ impl EngineState { } pub fn find_constant(&self, var_id: VarId, removed_overlays: &[Vec]) -> Option<&Value> { - for overlay_frame in self.active_overlays(removed_overlays).iter().rev() { + for overlay_frame in self.active_overlays(removed_overlays).rev() { if let Some(val) = overlay_frame.constants.get(&var_id) { return Some(val); } @@ -1236,7 +1248,7 @@ impl<'a> StateWorkingSet<'a> { for scope_frame in self.delta.scope.iter_mut().rev() { for overlay_id in scope_frame .active_overlay_ids(&mut removed_overlays) - .iter_mut() + .iter() .rev() { let overlay_frame = scope_frame.get_overlay_mut(*overlay_id); @@ -1258,7 +1270,6 @@ impl<'a> StateWorkingSet<'a> { for overlay_frame in self .permanent_state .active_overlays(&removed_overlays) - .iter() .rev() { visibility.append(&overlay_frame.visibility); @@ -1417,11 +1428,7 @@ impl<'a> StateWorkingSet<'a> { return Some(*decl_id); } - for overlay_frame in scope_frame - .active_overlays(&mut removed_overlays) - .iter() - .rev() - { + for overlay_frame in scope_frame.active_overlays(&mut removed_overlays).rev() { if let Some(decl_id) = overlay_frame.predecls.get(name) { return Some(*decl_id); } @@ -1444,11 +1451,7 @@ impl<'a> StateWorkingSet<'a> { } // check overlay in delta - for overlay_frame in scope_frame - .active_overlays(&mut removed_overlays) - .iter() - .rev() - { + for overlay_frame in scope_frame.active_overlays(&mut removed_overlays).rev() { visibility.append(&overlay_frame.visibility); if let Some(decl_id) = overlay_frame.predecls.get(name) { @@ -1469,7 +1472,6 @@ impl<'a> StateWorkingSet<'a> { for overlay_frame in self .permanent_state .active_overlays(&removed_overlays) - .iter() .rev() { visibility.append(&overlay_frame.visibility); @@ -1488,11 +1490,7 @@ impl<'a> StateWorkingSet<'a> { let mut removed_overlays = vec![]; for scope_frame in self.delta.scope.iter().rev() { - for overlay_frame in scope_frame - .active_overlays(&mut removed_overlays) - .iter() - .rev() - { + for overlay_frame in scope_frame.active_overlays(&mut removed_overlays).rev() { if let Some(module_id) = overlay_frame.modules.get(name) { return Some(*module_id); } @@ -1502,7 +1500,6 @@ impl<'a> StateWorkingSet<'a> { for overlay_frame in self .permanent_state .active_overlays(&removed_overlays) - .iter() .rev() { if let Some(module_id) = overlay_frame.modules.get(name) { @@ -1517,11 +1514,7 @@ impl<'a> StateWorkingSet<'a> { let mut removed_overlays = vec![]; for scope_frame in self.delta.scope.iter().rev() { - for overlay_frame in scope_frame - .active_overlays(&mut removed_overlays) - .iter() - .rev() - { + for overlay_frame in scope_frame.active_overlays(&mut removed_overlays).rev() { for decl in &overlay_frame.decls { if decl.0 .0.starts_with(name) { return true; @@ -1533,7 +1526,6 @@ impl<'a> StateWorkingSet<'a> { for overlay_frame in self .permanent_state .active_overlays(&removed_overlays) - .iter() .rev() { for decl in &overlay_frame.decls { @@ -1555,11 +1547,7 @@ impl<'a> StateWorkingSet<'a> { let mut removed_overlays = vec![]; for scope_frame in self.delta.scope.iter().rev() { - for overlay_frame in scope_frame - .active_overlays(&mut removed_overlays) - .iter() - .rev() - { + for overlay_frame in scope_frame.active_overlays(&mut removed_overlays).rev() { if let Some(var_id) = overlay_frame.vars.get(name) { return Some(*var_id); } @@ -1569,7 +1557,6 @@ impl<'a> StateWorkingSet<'a> { for overlay_frame in self .permanent_state .active_overlays(&removed_overlays) - .iter() .rev() { if let Some(var_id) = overlay_frame.vars.get(name) { @@ -1584,11 +1571,7 @@ impl<'a> StateWorkingSet<'a> { let mut removed_overlays = vec![]; for scope_frame in self.delta.scope.iter().rev().take(1) { - for overlay_frame in scope_frame - .active_overlays(&mut removed_overlays) - .iter() - .rev() - { + for overlay_frame in scope_frame.active_overlays(&mut removed_overlays).rev() { if let Some(var_id) = overlay_frame.vars.get(name) { return Some(*var_id); } @@ -1662,11 +1645,7 @@ impl<'a> StateWorkingSet<'a> { let mut removed_overlays = vec![]; for scope_frame in self.delta.scope.iter().rev() { - for overlay_frame in scope_frame - .active_overlays(&mut removed_overlays) - .iter() - .rev() - { + for overlay_frame in scope_frame.active_overlays(&mut removed_overlays).rev() { if let Some(val) = overlay_frame.constants.get(&var_id) { return Some(val); } @@ -1835,7 +1814,6 @@ impl<'a> StateWorkingSet<'a> { for scope_frame in self.delta.scope.iter().rev() { if let Some(last_overlay) = scope_frame .active_overlays(&mut removed_overlays) - .iter() .rev() .last() { diff --git a/crates/nu-protocol/src/engine/overlay.rs b/crates/nu-protocol/src/engine/overlay.rs index c8825cc6d6..6cd5cb6e6d 100644 --- a/crates/nu-protocol/src/engine/overlay.rs +++ b/crates/nu-protocol/src/engine/overlay.rs @@ -115,11 +115,16 @@ impl ScopeFrame { .collect() } - pub fn active_overlays(&self, removed_overlays: &mut Vec>) -> Vec<&OverlayFrame> { + pub fn active_overlays<'a, 'b>( + &'b self, + removed_overlays: &'a mut Vec>, + ) -> impl DoubleEndedIterator + 'a + where + 'b: 'a, + { self.active_overlay_ids(removed_overlays) - .iter() - .map(|id| self.get_overlay(*id)) - .collect() + .into_iter() + .map(|id| self.get_overlay(id)) } pub fn active_overlay_names(&self, removed_overlays: &mut Vec>) -> Vec<&Vec> {