From 85a69c0a4598d95ac784d523514fffa81f922593 Mon Sep 17 00:00:00 2001 From: JT Date: Tue, 26 Oct 2021 10:14:21 +1300 Subject: [PATCH] WIP --- TODO.md | 1 + crates/nu-cli/src/completions.rs | 1 + .../nu-command/src/conversions/into/binary.rs | 32 +++++---- .../src/conversions/into/filesize.rs | 36 +++++----- crates/nu-command/src/conversions/into/int.rs | 32 +++++---- crates/nu-command/src/core_commands/for_.rs | 14 ++-- crates/nu-command/src/filters/length.rs | 2 +- crates/nu-command/src/strings/size.rs | 24 +++---- crates/nu-command/src/strings/split/chars.rs | 6 +- crates/nu-command/src/strings/split/column.rs | 6 +- crates/nu-command/src/strings/split/row.rs | 6 +- crates/nu-engine/src/eval.rs | 35 ++++------ crates/nu-protocol/src/engine/stack.rs | 10 +-- crates/nu-protocol/src/pipeline_data.rs | 66 ++++++++++++++++++- 14 files changed, 151 insertions(+), 120 deletions(-) diff --git a/TODO.md b/TODO.md index f16902de6d..507079c9d7 100644 --- a/TODO.md +++ b/TODO.md @@ -30,6 +30,7 @@ - [x] Handling rows with missing columns during a cell path - [x] finish operator type-checking - [x] Config file loading +- [x] block variable captures - [ ] Input/output types - [ ] Support for `$in` - [ ] ctrl-c support diff --git a/crates/nu-cli/src/completions.rs b/crates/nu-cli/src/completions.rs index 7e3e4f08d1..43f7f92229 100644 --- a/crates/nu-cli/src/completions.rs +++ b/crates/nu-cli/src/completions.rs @@ -77,6 +77,7 @@ impl Completer for NuCompleter { let v: Vec<_> = match result { Ok(pd) => pd + .into_iter() .map(move |x| { let s = x.as_string().expect( "FIXME: better error handling for custom completions", diff --git a/crates/nu-command/src/conversions/into/binary.rs b/crates/nu-command/src/conversions/into/binary.rs index c1f39e26ef..4704b20329 100644 --- a/crates/nu-command/src/conversions/into/binary.rs +++ b/crates/nu-command/src/conversions/into/binary.rs @@ -1,7 +1,7 @@ use nu_protocol::{ ast::Call, engine::{Command, EngineState, Stack}, - Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape, Value, + Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value, }; #[derive(Clone)] @@ -92,23 +92,21 @@ fn into_binary( let head = call.head; // let column_paths: Vec = call.rest(context, 0)?; - Ok(input - .map(move |v| { - action(v, head) - // FIXME: Add back in cell_path support - // if column_paths.is_empty() { - // action(v, head) - // } else { - // let mut ret = v; - // for path in &column_paths { - // ret = - // ret.swap_data_by_cell_path(path, Box::new(move |old| action(old, old.tag())))?; - // } + input.map(move |v| { + action(v, head) + // FIXME: Add back in cell_path support + // if column_paths.is_empty() { + // action(v, head) + // } else { + // let mut ret = v; + // for path in &column_paths { + // ret = + // ret.swap_data_by_cell_path(path, Box::new(move |old| action(old, old.tag())))?; + // } - // Ok(ret) - // } - }) - .into_pipeline_data()) + // Ok(ret) + // } + }) } fn int_to_endian(n: i64) -> Vec { diff --git a/crates/nu-command/src/conversions/into/filesize.rs b/crates/nu-command/src/conversions/into/filesize.rs index 1fa67ef940..68c9746e7f 100644 --- a/crates/nu-command/src/conversions/into/filesize.rs +++ b/crates/nu-command/src/conversions/into/filesize.rs @@ -1,7 +1,7 @@ use nu_protocol::{ ast::Call, engine::{Command, EngineState, Stack}, - Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape, Value, + Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value, }; #[derive(Clone)] @@ -120,26 +120,24 @@ fn into_filesize( let head = call.head; // let call_paths: Vec = args.rest(0)?; - Ok(input - .map(move |v| { - action(v, head) + input.map(move |v| { + action(v, head) - // FIXME: Add back cell_path support - // if column_paths.is_empty() { - // action(&v, v.tag()) - // } else { - // let mut ret = v; - // for path in &column_paths { - // ret = ret.swap_data_by_column_path( - // path, - // Box::new(move |old| action(old, old.tag())), - // )?; - // } + // FIXME: Add back cell_path support + // if column_paths.is_empty() { + // action(&v, v.tag()) + // } else { + // let mut ret = v; + // for path in &column_paths { + // ret = ret.swap_data_by_column_path( + // path, + // Box::new(move |old| action(old, old.tag())), + // )?; + // } - // Ok(ret) - // } - }) - .into_pipeline_data()) + // Ok(ret) + // } + }) } pub fn action(input: Value, span: Span) -> Value { diff --git a/crates/nu-command/src/conversions/into/int.rs b/crates/nu-command/src/conversions/into/int.rs index c8f018fc5f..eb76ef9aa2 100644 --- a/crates/nu-command/src/conversions/into/int.rs +++ b/crates/nu-command/src/conversions/into/int.rs @@ -1,7 +1,7 @@ use nu_protocol::{ ast::Call, engine::{Command, EngineState, Stack}, - Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape, Value, + Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value, }; #[derive(Clone)] @@ -96,23 +96,21 @@ fn into_int( let head = call.head; // let column_paths: Vec = call.rest(context, 0)?; - Ok(input - .map(move |v| { - action(v, head) - // FIXME: Add back cell_path support - // if column_paths.is_empty() { - // action(&v, v.tag()) - // } else { - // let mut ret = v; - // for path in &column_paths { - // ret = ret - // .swap_data_by_column_path(path, Box::new(move |old| action(old, old.tag())))?; - // } + input.map(move |v| { + action(v, head) + // FIXME: Add back cell_path support + // if column_paths.is_empty() { + // action(&v, v.tag()) + // } else { + // let mut ret = v; + // for path in &column_paths { + // ret = ret + // .swap_data_by_column_path(path, Box::new(move |old| action(old, old.tag())))?; + // } - // Ok(ret) - // } - }) - .into_pipeline_data()) + // Ok(ret) + // } + }) } pub fn action(input: Value, span: Span) -> Value { diff --git a/crates/nu-command/src/core_commands/for_.rs b/crates/nu-command/src/core_commands/for_.rs index d85a42617a..f9278de05e 100644 --- a/crates/nu-command/src/core_commands/for_.rs +++ b/crates/nu-command/src/core_commands/for_.rs @@ -60,7 +60,7 @@ impl Command for For { let mut stack = stack.collect_captures(&block.captures); match values { - Value::List { vals, span } => Ok(vals + Value::List { vals, .. } => Ok(vals .into_iter() .map(move |x| { let mut stack = stack.clone(); @@ -68,25 +68,19 @@ impl Command for For { let block = engine_state.get_block(block_id); match eval_block(&engine_state, &mut stack, block, PipelineData::new()) { - Ok(value) => Value::List { - vals: value.collect(), - span, - }, + Ok(pipeline_data) => pipeline_data.into_value(), Err(error) => Value::Error { error }, } }) .into_pipeline_data()), - Value::Range { val, span } => Ok(val + Value::Range { val, .. } => Ok(val .into_range_iter()? .map(move |x| { stack.add_var(var_id, x); let block = engine_state.get_block(block_id); match eval_block(&engine_state, &mut stack, block, PipelineData::new()) { - Ok(value) => Value::List { - vals: value.collect(), - span, - }, + Ok(pipeline_data) => pipeline_data.into_value(), Err(error) => Value::Error { error }, } }) diff --git a/crates/nu-command/src/filters/length.rs b/crates/nu-command/src/filters/length.rs index 0ab8690cf4..724018f851 100644 --- a/crates/nu-command/src/filters/length.rs +++ b/crates/nu-command/src/filters/length.rs @@ -32,7 +32,7 @@ impl Command for Length { } .into_pipeline_data()), _ => Ok(Value::Int { - val: input.count() as i64, + val: input.into_iter().count() as i64, span: call.head, } .into_pipeline_data()), diff --git a/crates/nu-command/src/strings/size.rs b/crates/nu-command/src/strings/size.rs index 251a6b1c7c..96bf047b91 100644 --- a/crates/nu-command/src/strings/size.rs +++ b/crates/nu-command/src/strings/size.rs @@ -4,9 +4,7 @@ use unicode_segmentation::UnicodeSegmentation; use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; -use nu_protocol::{ - Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, Type, Value, -}; +use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, Type, Value}; #[derive(Clone)] pub struct Size; @@ -104,18 +102,16 @@ impl Command for Size { fn size(call: &Call, input: PipelineData) -> Result { let span = call.head; - Ok(input - .map(move |v| match v.as_string() { - Ok(s) => count(&s, span), - Err(_) => Value::Error { - error: ShellError::PipelineMismatch { - expected: Type::String, - expected_span: span, - origin: span, - }, + input.map(move |v| match v.as_string() { + Ok(s) => count(&s, span), + Err(_) => Value::Error { + error: ShellError::PipelineMismatch { + expected: Type::String, + expected_span: span, + origin: span, }, - }) - .into_pipeline_data()) + }, + }) } fn count(contents: &str, span: Span) -> Value { diff --git a/crates/nu-command/src/strings/split/chars.rs b/crates/nu-command/src/strings/split/chars.rs index f553e41b83..163d5bb110 100644 --- a/crates/nu-command/src/strings/split/chars.rs +++ b/crates/nu-command/src/strings/split/chars.rs @@ -1,7 +1,7 @@ use nu_protocol::{ ast::Call, engine::{Command, EngineState, Stack}, - Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, Type, Value, + Example, PipelineData, ShellError, Signature, Span, Type, Value, }; #[derive(Clone)] @@ -54,9 +54,7 @@ fn split_chars( ) -> Result { let span = call.head; - Ok(input - .flat_map(move |x| split_chars_helper(&x, span)) - .into_pipeline_data()) + input.flat_map(move |x| split_chars_helper(&x, span)) } fn split_chars_helper(v: &Value, name: Span) -> Vec { diff --git a/crates/nu-command/src/strings/split/column.rs b/crates/nu-command/src/strings/split/column.rs index 8090c4d237..6dedde4090 100644 --- a/crates/nu-command/src/strings/split/column.rs +++ b/crates/nu-command/src/strings/split/column.rs @@ -2,7 +2,7 @@ use nu_engine::CallExt; use nu_protocol::{ ast::Call, engine::{Command, EngineState, Stack}, - IntoPipelineData, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Type, Value, + PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Type, Value, }; #[derive(Clone)] @@ -54,9 +54,7 @@ fn split_column( let rest: Vec> = call.rest(engine_state, stack, 1)?; let collapse_empty = call.has_flag("collapse-empty"); - Ok(input - .map(move |x| split_column_helper(&x, &separator, &rest, collapse_empty, name_span)) - .into_pipeline_data()) + input.map(move |x| split_column_helper(&x, &separator, &rest, collapse_empty, name_span)) } fn split_column_helper( diff --git a/crates/nu-command/src/strings/split/row.rs b/crates/nu-command/src/strings/split/row.rs index 80bd303945..880672faff 100644 --- a/crates/nu-command/src/strings/split/row.rs +++ b/crates/nu-command/src/strings/split/row.rs @@ -2,7 +2,7 @@ use nu_engine::CallExt; use nu_protocol::{ ast::Call, engine::{Command, EngineState, Stack}, - IntoPipelineData, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Type, Value, + PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Type, Value, }; #[derive(Clone)] @@ -45,9 +45,7 @@ fn split_row( let name_span = call.head; let separator: Spanned = call.req(engine_state, stack, 0)?; - Ok(input - .flat_map(move |x| split_row_helper(&x, &separator, name_span)) - .into_pipeline_data()) + input.flat_map(move |x| split_row_helper(&x, &separator, name_span)) } fn split_row_helper(v: &Value, separator: &Spanned, name: Span) -> Vec { diff --git a/crates/nu-engine/src/eval.rs b/crates/nu-engine/src/eval.rs index fce4519b01..2a568b7901 100644 --- a/crates/nu-engine/src/eval.rs +++ b/crates/nu-engine/src/eval.rs @@ -226,26 +226,20 @@ pub fn eval_expression( Expr::RowCondition(_, expr) => eval_expression(engine_state, stack, expr), Expr::Call(call) => { // FIXME: protect this collect with ctrl-c - Ok(Value::List { - vals: eval_call(engine_state, stack, call, PipelineData::new())?.collect(), - span: expr.span, - }) + Ok(eval_call(engine_state, stack, call, PipelineData::new())?.into_value()) } Expr::ExternalCall(name, span, args) => { // FIXME: protect this collect with ctrl-c - Ok(Value::List { - vals: eval_external( - engine_state, - stack, - name, - span, - args, - PipelineData::new(), - true, - )? - .collect(), - span: expr.span, - }) + Ok(eval_external( + engine_state, + stack, + name, + span, + args, + PipelineData::new(), + true, + )? + .into_value()) } Expr::Operator(_) => Ok(Value::Nothing { span: expr.span }), Expr::BinaryOp(lhs, op, rhs) => { @@ -278,13 +272,8 @@ pub fn eval_expression( Expr::Subexpression(block_id) => { let block = engine_state.get_block(*block_id); - let mut stack = stack.collect_captures(&block.captures); - // FIXME: protect this collect with ctrl-c - Ok(Value::List { - vals: eval_block(engine_state, &mut stack, block, PipelineData::new())?.collect(), - span: expr.span, - }) + Ok(eval_block(engine_state, stack, block, PipelineData::new())?.into_value()) } Expr::Block(block_id) => Ok(Value::Block { val: *block_id, diff --git a/crates/nu-protocol/src/engine/stack.rs b/crates/nu-protocol/src/engine/stack.rs index 4918f98657..3a0e46a1de 100644 --- a/crates/nu-protocol/src/engine/stack.rs +++ b/crates/nu-protocol/src/engine/stack.rs @@ -40,11 +40,11 @@ impl Stack { let mut output = Stack::new(); for capture in captures { - output.vars.insert( - *capture, - self.get_var(*capture) - .expect("internal error: capture of missing variable"), - ); + // Note: this assumes we have calculated captures correctly and that commands + // that take in a var decl will manually set this into scope when running the blocks + if let Ok(value) = self.get_var(*capture) { + output.vars.insert(*capture, value); + } } output diff --git a/crates/nu-protocol/src/pipeline_data.rs b/crates/nu-protocol/src/pipeline_data.rs index d8c1836ee5..db77ad61f7 100644 --- a/crates/nu-protocol/src/pipeline_data.rs +++ b/crates/nu-protocol/src/pipeline_data.rs @@ -38,6 +38,51 @@ impl PipelineData { PipelineData::Value(v) => v.follow_cell_path(cell_path), } } + + /// Simplified mapper to help with simple values also. For full iterator support use `.into_iter()` instead + pub fn map(self, mut f: F) -> Result + where + Self: Sized, + F: FnMut(Value) -> Value + 'static + Send, + { + match self { + PipelineData::Value(Value::List { vals, .. }) => { + Ok(vals.into_iter().map(f).into_pipeline_data()) + } + PipelineData::Stream(stream) => Ok(stream.map(f).into_pipeline_data()), + PipelineData::Value(Value::Range { val, .. }) => { + Ok(val.into_range_iter()?.map(f).into_pipeline_data()) + } + PipelineData::Value(v) => { + let output = f(v); + match output { + Value::Error { error } => Err(error), + v => Ok(v.into_pipeline_data()), + } + } + } + } + + /// Simplified flatmapper. For full iterator support use `.into_iter()` instead + pub fn flat_map(self, mut f: F) -> Result + where + Self: Sized, + U: IntoIterator, + ::IntoIter: 'static + Send, + F: FnMut(Value) -> U + 'static + Send, + { + match self { + PipelineData::Value(Value::List { vals, .. }) => { + Ok(vals.into_iter().map(f).flatten().into_pipeline_data()) + } + PipelineData::Stream(stream) => Ok(stream.map(f).flatten().into_pipeline_data()), + PipelineData::Value(Value::Range { val, .. }) => match val.into_range_iter() { + Ok(iter) => Ok(iter.map(f).flatten().into_pipeline_data()), + Err(error) => Err(error), + }, + PipelineData::Value(v) => Ok(f(v).into_iter().into_pipeline_data()), + } + } } impl Default for PipelineData { @@ -46,11 +91,28 @@ impl Default for PipelineData { } } -impl Iterator for PipelineData { +pub struct PipelineIterator(PipelineData); + +impl IntoIterator for PipelineData { + type Item = Value; + + type IntoIter = PipelineIterator; + + fn into_iter(self) -> Self::IntoIter { + match self { + PipelineData::Value(Value::List { vals, .. }) => PipelineIterator( + PipelineData::Stream(ValueStream(Box::new(vals.into_iter()))), + ), + x => PipelineIterator(x), + } + } +} + +impl Iterator for PipelineIterator { type Item = Value; fn next(&mut self) -> Option { - match self { + match &mut self.0 { PipelineData::Value(Value::Nothing { .. }) => None, PipelineData::Value(v) => { let prev = std::mem::take(v);