From 69b089845cd0cdeb55f81522089aec19fcc0e89b Mon Sep 17 00:00:00 2001 From: JT <547158+jntrnr@users.noreply.github.com> Date: Sat, 12 Nov 2022 07:21:45 +1300 Subject: [PATCH] Add support for while loops (#7101) --- crates/nu-command/src/core_commands/mod.rs | 2 + crates/nu-command/src/core_commands/while_.rs | 99 +++++++++++++++++++ crates/nu-command/src/default_context.rs | 1 + crates/nu-command/tests/commands/mod.rs | 1 + crates/nu-command/tests/commands/while_.rs | 11 +++ crates/nu-parser/tests/test_parser.rs | 1 - 6 files changed, 114 insertions(+), 1 deletion(-) create mode 100644 crates/nu-command/src/core_commands/while_.rs create mode 100644 crates/nu-command/tests/commands/while_.rs diff --git a/crates/nu-command/src/core_commands/mod.rs b/crates/nu-command/src/core_commands/mod.rs index 6c5d1048b9..b11bc56c8d 100644 --- a/crates/nu-command/src/core_commands/mod.rs +++ b/crates/nu-command/src/core_commands/mod.rs @@ -28,6 +28,7 @@ mod mut_; pub(crate) mod overlay; mod use_; mod version; +mod while_; pub use alias::Alias; pub use ast::Ast; @@ -59,6 +60,7 @@ pub use mut_::Mut; pub use overlay::*; pub use use_::Use; pub use version::Version; +pub use while_::While; #[cfg(feature = "plugin")] mod register; diff --git a/crates/nu-command/src/core_commands/while_.rs b/crates/nu-command/src/core_commands/while_.rs new file mode 100644 index 0000000000..0217f78d3c --- /dev/null +++ b/crates/nu-command/src/core_commands/while_.rs @@ -0,0 +1,99 @@ +use nu_engine::{eval_block, eval_expression, CallExt}; +use nu_protocol::ast::Call; +use nu_protocol::engine::{Block, Command, EngineState, Stack}; +use nu_protocol::{Category, Example, PipelineData, ShellError, Signature, SyntaxShape, Value}; + +#[derive(Clone)] +pub struct While; + +impl Command for While { + fn name(&self) -> &str { + "while" + } + + fn usage(&self) -> &str { + "Conditionally run a block in a loop." + } + + fn signature(&self) -> nu_protocol::Signature { + Signature::build("while") + .required("cond", SyntaxShape::Expression, "condition to check") + .required( + "block", + SyntaxShape::Block, + "block to loop if check succeeds", + ) + .category(Category::Core) + } + + fn extra_usage(&self) -> &str { + r#"This command is a parser keyword. For details, check: + https://www.nushell.sh/book/thinking_in_nu.html"# + } + + fn is_parser_keyword(&self) -> bool { + true + } + + fn run( + &self, + engine_state: &EngineState, + stack: &mut Stack, + call: &Call, + _input: PipelineData, + ) -> Result { + let cond = call.positional_nth(0).expect("checked through parser"); + let block: Block = call.req(engine_state, stack, 1)?; + + loop { + let result = eval_expression(engine_state, stack, cond)?; + match &result { + Value::Bool { val, .. } => { + if *val { + let block = engine_state.get_block(block.block_id); + eval_block( + engine_state, + stack, + block, + PipelineData::new(call.head), + call.redirect_stdout, + call.redirect_stderr, + )? + .into_value(call.head); + } else { + break; + } + } + x => { + return Err(ShellError::CantConvert( + "bool".into(), + x.get_type().to_string(), + result.span()?, + None, + )) + } + } + } + Ok(PipelineData::new(call.head)) + } + + fn examples(&self) -> Vec { + vec![Example { + description: "Loop while a condition is true", + example: "mut x = 0; while $x < 10 { $x = $x + 1 }", + result: None, + }] + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_examples() { + use crate::test_examples; + + test_examples(While {}) + } +} diff --git a/crates/nu-command/src/default_context.rs b/crates/nu-command/src/default_context.rs index 08fcea8525..47ada5d1a4 100644 --- a/crates/nu-command/src/default_context.rs +++ b/crates/nu-command/src/default_context.rs @@ -62,6 +62,7 @@ pub fn create_default_context() -> EngineState { Mut, Use, Version, + While, }; // Charts diff --git a/crates/nu-command/tests/commands/mod.rs b/crates/nu-command/tests/commands/mod.rs index b0aa208d98..0c2d147324 100644 --- a/crates/nu-command/tests/commands/mod.rs +++ b/crates/nu-command/tests/commands/mod.rs @@ -88,6 +88,7 @@ mod use_; mod where_; #[cfg(feature = "which-support")] mod which; +mod while_; mod with_env; mod wrap; mod zip; diff --git a/crates/nu-command/tests/commands/while_.rs b/crates/nu-command/tests/commands/while_.rs new file mode 100644 index 0000000000..3daba56ca3 --- /dev/null +++ b/crates/nu-command/tests/commands/while_.rs @@ -0,0 +1,11 @@ +use nu_test_support::nu; + +#[test] +fn while_sum() { + let actual = nu!( + cwd: ".", + "mut total = 0; mut x = 0; while $x <= 10 { $total = $total + $x; $x = $x + 1 }; $total" + ); + + assert_eq!(actual.out, "55"); +} diff --git a/crates/nu-parser/tests/test_parser.rs b/crates/nu-parser/tests/test_parser.rs index 792b56315e..31acae1b58 100644 --- a/crates/nu-parser/tests/test_parser.rs +++ b/crates/nu-parser/tests/test_parser.rs @@ -968,7 +968,6 @@ mod input_types { working_set.add_decl(Box::new(GroupBy)); working_set.add_decl(Box::new(LsTest)); working_set.add_decl(Box::new(ToCustom)); - working_set.add_decl(Box::new(Let)); working_set.add_decl(Box::new(AggMin)); working_set.add_decl(Box::new(Collect)); working_set.add_decl(Box::new(WithColumn));