Improve range and internal iteration (#3300)

This commit is contained in:
Jonathan Turner 2021-04-11 13:31:08 +12:00 committed by GitHub
parent a853880e07
commit 2e439ca77f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 271 additions and 298 deletions

View File

@ -8,19 +8,6 @@ macro_rules! return_err {
}; };
} }
#[macro_export]
macro_rules! stream {
($($expr:expr),*) => {{
let mut v = VecDeque::new();
$(
v.push_back($expr);
)*
v
}}
}
#[macro_export] #[macro_export]
macro_rules! trace_out_stream { macro_rules! trace_out_stream {
(target: $target:tt, $desc:tt = $expr:expr) => {{ (target: $target:tt, $desc:tt = $expr:expr) => {{

View File

@ -1,4 +1,5 @@
use crate::prelude::*; use crate::prelude::*;
use bigdecimal::Zero;
use nu_engine::WholeStreamCommand; use nu_engine::WholeStreamCommand;
use nu_errors::ShellError; use nu_errors::ShellError;
use nu_protocol::hir::Operator; use nu_protocol::hir::Operator;
@ -69,11 +70,13 @@ fn echo(args: CommandArgs) -> Result<OutputStream, ShellError> {
} }
struct RangeIterator { struct RangeIterator {
curr: Primitive, curr: UntaggedValue,
end: Primitive, end: UntaggedValue,
tag: Tag, tag: Tag,
is_end_inclusive: bool, is_end_inclusive: bool,
moves_up: bool, moves_up: bool,
one: UntaggedValue,
negative_one: UntaggedValue,
} }
impl RangeIterator { impl RangeIterator {
@ -90,10 +93,12 @@ impl RangeIterator {
RangeIterator { RangeIterator {
moves_up: start <= end, moves_up: start <= end,
curr: start, curr: UntaggedValue::Primitive(start),
end, end: UntaggedValue::Primitive(end),
tag, tag,
is_end_inclusive: matches!(range.to.1, RangeInclusion::Inclusive), is_end_inclusive: matches!(range.to.1, RangeInclusion::Inclusive),
one: UntaggedValue::int(1),
negative_one: UntaggedValue::int(-1),
} }
} }
} }
@ -101,50 +106,46 @@ impl RangeIterator {
impl Iterator for RangeIterator { impl Iterator for RangeIterator {
type Item = Result<ReturnSuccess, ShellError>; type Item = Result<ReturnSuccess, ShellError>;
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
let ordering = if self.end == Primitive::Nothing { use std::cmp::Ordering;
let ordering = if self.end == UntaggedValue::Primitive(Primitive::Nothing) {
Ordering::Less Ordering::Less
} else { } else {
let result = match (&self.curr, &self.end) {
nu_data::base::coerce_compare_primitive(&self.curr, &self.end).map_err(|_| { (
ShellError::labeled_error( UntaggedValue::Primitive(Primitive::Int(x)),
UntaggedValue::Primitive(Primitive::Int(y)),
) => x.cmp(y),
(
UntaggedValue::Primitive(Primitive::Decimal(x)),
UntaggedValue::Primitive(Primitive::Decimal(y)),
) => x.cmp(y),
(
UntaggedValue::Primitive(Primitive::Decimal(x)),
UntaggedValue::Primitive(Primitive::Int(y)),
) => x.cmp(&(BigDecimal::zero() + y)),
(
UntaggedValue::Primitive(Primitive::Int(x)),
UntaggedValue::Primitive(Primitive::Decimal(y)),
) => (BigDecimal::zero() + x).cmp(y),
_ => {
return Some(Err(ShellError::labeled_error(
"Cannot create range", "Cannot create range",
"unsupported range", "unsupported range",
self.tag.span, self.tag.span,
) )))
}); }
if let Err(result) = result {
return Some(Err(result));
} }
let result = result
.expect("Internal error: the error case was already protected, but that failed");
result.compare()
}; };
use std::cmp::Ordering;
if self.moves_up if self.moves_up
&& (ordering == Ordering::Less || self.is_end_inclusive && ordering == Ordering::Equal) && (ordering == Ordering::Less || self.is_end_inclusive && ordering == Ordering::Equal)
{ {
let output = UntaggedValue::Primitive(self.curr.clone()).into_value(self.tag.clone()); let next_value = nu_data::value::compute_values(Operator::Plus, &self.curr, &self.one);
let next_value = nu_data::value::compute_values( let mut next = match next_value {
Operator::Plus, Ok(result) => result,
&UntaggedValue::Primitive(self.curr.clone()),
&UntaggedValue::int(1),
);
self.curr = match next_value {
Ok(result) => match result {
UntaggedValue::Primitive(p) => p,
_ => {
return Some(Err(ShellError::unimplemented(
"Internal error: expected a primitive result from increment",
)));
}
},
Err((left_type, right_type)) => { Err((left_type, right_type)) => {
return Some(Err(ShellError::coerce_error( return Some(Err(ShellError::coerce_error(
left_type.spanned(self.tag.span), left_type.spanned(self.tag.span),
@ -152,28 +153,18 @@ impl Iterator for RangeIterator {
))); )));
} }
}; };
Some(ReturnSuccess::value(output)) std::mem::swap(&mut self.curr, &mut next);
Some(ReturnSuccess::value(next.into_value(self.tag.clone())))
} else if !self.moves_up } else if !self.moves_up
&& (ordering == Ordering::Greater && (ordering == Ordering::Greater
|| self.is_end_inclusive && ordering == Ordering::Equal) || self.is_end_inclusive && ordering == Ordering::Equal)
{ {
let output = UntaggedValue::Primitive(self.curr.clone()).into_value(self.tag.clone()); let next_value =
nu_data::value::compute_values(Operator::Plus, &self.curr, &self.negative_one);
let next_value = nu_data::value::compute_values( let mut next = match next_value {
Operator::Plus, Ok(result) => result,
&UntaggedValue::Primitive(self.curr.clone()),
&UntaggedValue::int(-1),
);
self.curr = match next_value {
Ok(result) => match result {
UntaggedValue::Primitive(p) => p,
_ => {
return Some(Err(ShellError::unimplemented(
"Internal error: expected a primitive result from increment",
)));
}
},
Err((left_type, right_type)) => { Err((left_type, right_type)) => {
return Some(Err(ShellError::coerce_error( return Some(Err(ShellError::coerce_error(
left_type.spanned(self.tag.span), left_type.spanned(self.tag.span),
@ -181,7 +172,9 @@ impl Iterator for RangeIterator {
))); )));
} }
}; };
Some(ReturnSuccess::value(output)) std::mem::swap(&mut self.curr, &mut next);
Some(ReturnSuccess::value(next.into_value(self.tag.clone())))
} else { } else {
None None
} }

View File

@ -8,19 +8,6 @@ macro_rules! return_err {
}; };
} }
#[macro_export]
macro_rules! stream {
($($expr:expr),*) => {{
let mut v = VecDeque::new();
$(
v.push_back($expr);
)*
v
}}
}
pub(crate) use bigdecimal::BigDecimal; pub(crate) use bigdecimal::BigDecimal;
pub(crate) use indexmap::{indexmap, IndexMap}; pub(crate) use indexmap::{indexmap, IndexMap};
pub(crate) use itertools::Itertools; pub(crate) use itertools::Itertools;

View File

@ -33,16 +33,33 @@ pub struct CommandArgs {
pub type RunnableContext = CommandArgs; pub type RunnableContext = CommandArgs;
#[derive(Clone)]
pub struct RunnableContextWithoutInput { pub struct RunnableContextWithoutInput {
pub shell_manager: ShellManager, pub shell_manager: ShellManager,
pub host: Arc<parking_lot::Mutex<Box<dyn Host>>>, pub host: Arc<parking_lot::Mutex<Box<dyn Host>>>,
pub current_errors: Arc<Mutex<Vec<ShellError>>>, pub current_errors: Arc<Mutex<Vec<ShellError>>>,
pub ctrl_c: Arc<AtomicBool>, pub ctrl_c: Arc<AtomicBool>,
pub call_info: UnevaluatedCallInfo,
pub configs: Arc<Mutex<ConfigHolder>>, pub configs: Arc<Mutex<ConfigHolder>>,
pub scope: Scope, pub scope: Scope,
pub name: Tag, pub name: Tag,
} }
impl RunnableContextWithoutInput {
pub fn with_input(self, input: InputStream) -> CommandArgs {
CommandArgs {
shell_manager: self.shell_manager,
host: self.host,
current_errors: self.current_errors,
ctrl_c: self.ctrl_c,
call_info: self.call_info,
configs: self.configs,
scope: self.scope,
input,
}
}
}
#[derive(Getters, Clone)] #[derive(Getters, Clone)]
#[get = "pub"] #[get = "pub"]
pub struct RawCommandArgs { pub struct RawCommandArgs {
@ -108,9 +125,10 @@ impl CommandArgs {
host: self.host, host: self.host,
ctrl_c: self.ctrl_c, ctrl_c: self.ctrl_c,
configs: self.configs, configs: self.configs,
name: self.call_info.name_tag.clone(),
call_info: self.call_info,
current_errors: self.current_errors, current_errors: self.current_errors,
scope: self.scope, scope: self.scope,
name: self.call_info.name_tag,
}; };
(self.input, new_context) (self.input, new_context)

View File

@ -8,8 +8,7 @@ use nu_errors::ShellError;
use nu_protocol::hir::{ExternalRedirection, InternalCommand}; use nu_protocol::hir::{ExternalRedirection, InternalCommand};
use nu_protocol::{CommandAction, ReturnSuccess, UntaggedValue, Value}; use nu_protocol::{CommandAction, ReturnSuccess, UntaggedValue, Value};
use nu_source::{PrettyDebug, Span, Tag}; use nu_source::{PrettyDebug, Span, Tag};
use nu_stream::{InputStream, ToInputStream}; use nu_stream::{InputStream, OutputStream};
use std::sync::Arc;
pub(crate) fn run_internal_command( pub(crate) fn run_internal_command(
command: InternalCommand, command: InternalCommand,
@ -34,52 +33,70 @@ pub(crate) fn run_internal_command(
)? )?
}; };
let head = Arc::new(command.args.head.clone());
let context = context.clone();
let command = Arc::new(command);
Ok(InputStream::from_stream( Ok(InputStream::from_stream(
result InternalIterator {
.map(move |item| { command,
let head = head.clone(); context: context.clone(),
let command = command.clone(); leftovers: vec![],
let context = context.clone(); input: result,
}
.take_while(|x| !x.is_error()),
))
}
struct InternalIterator {
context: EvaluationContext,
command: InternalCommand,
leftovers: Vec<Value>,
input: OutputStream,
}
impl Iterator for InternalIterator {
type Item = Value;
fn next(&mut self) -> Option<Self::Item> {
// let head = Arc::new(command.args.head.clone());
// let context = context.clone();
// let command = Arc::new(command);
if !self.leftovers.is_empty() {
let output = self.leftovers.remove(0);
return Some(output);
}
while let Some(item) = self.input.next() {
match item { match item {
Ok(ReturnSuccess::Action(action)) => match action { Ok(ReturnSuccess::Action(action)) => match action {
CommandAction::ChangePath(path) => { CommandAction::ChangePath(path) => {
context.shell_manager.set_path(path); self.context.shell_manager.set_path(path);
InputStream::empty()
} }
CommandAction::Exit(code) => std::process::exit(code), // TODO: save history.txt CommandAction::Exit(code) => std::process::exit(code), // TODO: save history.txt
CommandAction::Error(err) => { CommandAction::Error(err) => {
context.error(err); self.context.error(err);
InputStream::empty()
} }
CommandAction::AutoConvert(tagged_contents, extension) => { CommandAction::AutoConvert(tagged_contents, extension) => {
let contents_tag = tagged_contents.tag.clone(); let contents_tag = tagged_contents.tag.clone();
let command_name = format!("from {}", extension); let command_name = format!("from {}", extension);
let command = command; if let Some(converter) = self.context.scope.get_command(&command_name) {
if let Some(converter) = context.scope.get_command(&command_name) {
let new_args = RawCommandArgs { let new_args = RawCommandArgs {
host: context.host.clone(), host: self.context.host.clone(),
ctrl_c: context.ctrl_c.clone(), ctrl_c: self.context.ctrl_c.clone(),
configs: context.configs.clone(), configs: self.context.configs.clone(),
current_errors: context.current_errors.clone(), current_errors: self.context.current_errors.clone(),
shell_manager: context.shell_manager.clone(), shell_manager: self.context.shell_manager.clone(),
call_info: UnevaluatedCallInfo { call_info: UnevaluatedCallInfo {
args: nu_protocol::hir::Call { args: nu_protocol::hir::Call {
head: (&*head).clone(), head: self.command.args.head.clone(),
positional: None, positional: None,
named: None, named: None,
span: Span::unknown(), span: Span::unknown(),
external_redirection: ExternalRedirection::Stdout, external_redirection: ExternalRedirection::Stdout,
}, },
name_tag: Tag::unknown_anchor(command.name_span), name_tag: Tag::unknown_anchor(self.command.name_span),
}, },
scope: context.scope.clone(), scope: self.context.scope.clone(),
}; };
let result = let result = converter.run(new_args.with_input(vec![tagged_contents]));
converter.run(new_args.with_input(vec![tagged_contents]));
match result { match result {
Ok(mut result) => { Ok(mut result) => {
@ -94,99 +111,91 @@ pub(crate) fn run_internal_command(
.. ..
})) => { })) => {
for l in list { for l in list {
output.push(Ok(l)); output.push(l);
} }
} }
Ok(ReturnSuccess::Value(Value { Ok(ReturnSuccess::Value(Value { value, .. })) => {
value, .. output.push(value.into_value(contents_tag.clone()));
})) => {
output.push(Ok(
value.into_value(contents_tag.clone())
));
} }
Err(e) => output.push(Err(e)), Err(e) => output.push(
UntaggedValue::Error(e).into_untagged_value(),
),
_ => {} _ => {}
} }
} }
output.into_iter().to_input_stream() let mut output = output.into_iter();
if let Some(x) = output.next() {
self.leftovers = output.collect();
return Some(x);
}
} }
Err(err) => { Err(err) => {
context.error(err); self.context.error(err);
InputStream::empty()
} }
} }
} else { } else {
InputStream::one(tagged_contents) return Some(tagged_contents);
} }
} }
CommandAction::EnterValueShell(value) => { CommandAction::EnterValueShell(value) => {
context self.context
.shell_manager .shell_manager
.insert_at_current(Box::new(ValueShell::new(value))); .insert_at_current(Box::new(ValueShell::new(value)));
InputStream::from_stream(std::iter::empty())
} }
CommandAction::EnterShell(location) => { CommandAction::EnterShell(location) => {
let mode = if context.shell_manager.is_interactive() { let mode = if self.context.shell_manager.is_interactive() {
FilesystemShellMode::Cli FilesystemShellMode::Cli
} else { } else {
FilesystemShellMode::Script FilesystemShellMode::Script
}; };
context.shell_manager.insert_at_current(Box::new( self.context.shell_manager.insert_at_current(Box::new(
match FilesystemShell::with_location(location, mode) { match FilesystemShell::with_location(location, mode) {
Ok(v) => v, Ok(v) => v,
Err(err) => { Err(err) => {
context.error(err.into()); self.context.error(err.into());
return InputStream::empty(); break;
} }
}, },
)); ));
InputStream::from_stream(std::iter::empty())
} }
CommandAction::AddPlugins(path) => { CommandAction::AddPlugins(path) => {
match crate::plugin::build_plugin::scan(vec![std::path::PathBuf::from( match crate::plugin::build_plugin::scan(vec![std::path::PathBuf::from(
path, path,
)]) { )]) {
Ok(plugins) => { Ok(plugins) => {
context.add_commands( self.context.add_commands(
plugins plugins
.into_iter() .into_iter()
.filter(|p| !context.is_command_registered(p.name())) .filter(|p| !self.context.is_command_registered(p.name()))
.collect(), .collect(),
); );
InputStream::empty()
} }
Err(reason) => { Err(reason) => {
context.error(reason); self.context.error(reason);
InputStream::empty()
} }
} }
} }
CommandAction::PreviousShell => { CommandAction::PreviousShell => {
context.shell_manager.prev(); self.context.shell_manager.prev();
InputStream::empty()
} }
CommandAction::NextShell => { CommandAction::NextShell => {
context.shell_manager.next(); self.context.shell_manager.next();
InputStream::empty()
} }
CommandAction::LeaveShell(code) => { CommandAction::LeaveShell(code) => {
context.shell_manager.remove_at_current(); self.context.shell_manager.remove_at_current();
if context.shell_manager.is_empty() { if self.context.shell_manager.is_empty() {
std::process::exit(code); // TODO: save history.txt std::process::exit(code); // TODO: save history.txt
} }
InputStream::empty()
} }
CommandAction::UnloadConfig(cfg_path) => { CommandAction::UnloadConfig(cfg_path) => {
context.unload_config(&cfg_path); self.context.unload_config(&cfg_path);
InputStream::empty()
} }
CommandAction::LoadConfig(cfg_path) => { CommandAction::LoadConfig(cfg_path) => {
if let Err(e) = context.load_config(&cfg_path) { if let Err(e) = self.context.load_config(&cfg_path) {
InputStream::one(UntaggedValue::Error(e).into_untagged_value()) return Some(UntaggedValue::Error(e).into_untagged_value());
} else {
InputStream::empty()
} }
} }
}, },
@ -195,33 +204,31 @@ pub(crate) fn run_internal_command(
value: UntaggedValue::Error(err), value: UntaggedValue::Error(err),
.. ..
})) => { })) => {
context.error(err); self.context.error(err);
InputStream::empty()
} }
Ok(ReturnSuccess::Value(v)) => InputStream::one(v), Ok(ReturnSuccess::Value(v)) => return Some(v),
Ok(ReturnSuccess::DebugValue(v)) => { Ok(ReturnSuccess::DebugValue(v)) => {
let doc = PrettyDebug::pretty_doc(&v); let doc = PrettyDebug::pretty_doc(&v);
let mut buffer = termcolor::Buffer::ansi(); let mut buffer = termcolor::Buffer::ansi();
let _ = doc.render_raw( let _ = doc.render_raw(
context.with_host(|host| host.width() - 5), self.context.with_host(|host| host.width() - 5),
&mut nu_source::TermColored::new(&mut buffer), &mut nu_source::TermColored::new(&mut buffer),
); );
let value = String::from_utf8_lossy(buffer.as_slice()); let value = String::from_utf8_lossy(buffer.as_slice());
InputStream::one(UntaggedValue::string(value).into_untagged_value()) return Some(UntaggedValue::string(value).into_untagged_value());
} }
Err(err) => { Err(err) => {
context.error(err); self.context.error(err);
InputStream::empty()
} }
} }
}) }
.flatten()
.take_while(|x| !x.is_error()), None
)) }
} }

View File

@ -778,13 +778,10 @@ impl Shell for FilesystemShell {
} }
}; };
let mut stream = VecDeque::new(); Ok(OutputStream::one(ReturnSuccess::value(
stream.push_back(ReturnSuccess::value(
UntaggedValue::Primitive(Primitive::String(p.to_string_lossy().to_string())) UntaggedValue::Primitive(Primitive::String(p.to_string_lossy().to_string()))
.into_value(&args.call_info.name_tag), .into_value(&args.call_info.name_tag),
)); )))
Ok(stream.into())
} }
fn set_path(&mut self, path: String) { fn set_path(&mut self, path: String) {

View File

@ -178,9 +178,7 @@ impl Shell for ValueShell {
)); ));
} }
let mut stream = VecDeque::new(); Ok(OutputStream::one(ReturnSuccess::change_cwd(path)))
stream.push_back(ReturnSuccess::change_cwd(path));
Ok(stream.into())
} }
fn cp(&self, _args: CopyArgs, name: Tag, _path: &str) -> Result<OutputStream, ShellError> { fn cp(&self, _args: CopyArgs, name: Tag, _path: &str) -> Result<OutputStream, ShellError> {
@ -220,11 +218,9 @@ impl Shell for ValueShell {
} }
fn pwd(&self, args: EvaluatedWholeStreamCommandArgs) -> Result<OutputStream, ShellError> { fn pwd(&self, args: EvaluatedWholeStreamCommandArgs) -> Result<OutputStream, ShellError> {
let mut stream = VecDeque::new(); Ok(OutputStream::one(
stream.push_back(ReturnSuccess::value(
UntaggedValue::string(self.path()).into_value(&args.call_info.name_tag), UntaggedValue::string(self.path()).into_value(&args.call_info.name_tag),
)); ))
Ok(stream.into())
} }
fn set_path(&mut self, path: String) { fn set_path(&mut self, path: String) {

View File

@ -27,9 +27,10 @@ impl InputStream {
} }
pub fn one(item: impl Into<Value>) -> InputStream { pub fn one(item: impl Into<Value>) -> InputStream {
let mut v: VecDeque<Value> = VecDeque::new(); InputStream {
v.push_back(item.into()); values: Box::new(std::iter::once(item.into())),
v.into() empty: false,
}
} }
pub fn into_vec(self) -> Vec<Value> { pub fn into_vec(self) -> Vec<Value> {

View File

@ -8,19 +8,6 @@ macro_rules! return_err {
}; };
} }
#[macro_export]
macro_rules! stream {
($($expr:expr),*) => {{
let mut v = VecDeque::new();
$(
v.push_back($expr);
)*
v
}}
}
#[macro_export] #[macro_export]
macro_rules! trace_out_stream { macro_rules! trace_out_stream {
(target: $target:tt, $desc:tt = $expr:expr) => {{ (target: $target:tt, $desc:tt = $expr:expr) => {{