Add =~ and !~ operators on strings

`left =~ right` return true if left contains right, using Rust's
`String::contains`. `!~` is the negated version.

A new `apply_operator` function is added which decouples evaluation from
`Value::compare`. This returns a `Value` and opens the door to
implementing `+` for example, though it wouldn't be useful immediately.

The `operator!` macro had to be changed slightly as it would choke on
`~` in arguments.
This commit is contained in:
Belhorma Bendebiche 2019-11-21 12:18:00 -05:00 committed by Yehuda Katz
parent e4226def16
commit 8f9dd6516e
13 changed files with 30 additions and 91 deletions

View File

@ -1,51 +0,0 @@
use nu_source::ShellAnnotation;
use pretty::{Render, RenderAnnotated};
use std::io;
use termcolor::WriteColor;
pub struct TermColored<'a, W> {
color_stack: Vec<ShellAnnotation>,
upstream: &'a mut W,
}
impl<'a, W> TermColored<'a, W> {
pub fn new(upstream: &'a mut W) -> TermColored<'a, W> {
TermColored {
color_stack: Vec::new(),
upstream,
}
}
}
impl<'a, W> Render for TermColored<'a, W>
where
W: io::Write,
{
type Error = io::Error;
fn write_str(&mut self, s: &str) -> io::Result<usize> {
self.upstream.write(s.as_bytes())
}
fn write_str_all(&mut self, s: &str) -> io::Result<()> {
self.upstream.write_all(s.as_bytes())
}
}
impl<'a, W> RenderAnnotated<ShellAnnotation> for TermColored<'a, W>
where
W: WriteColor,
{
fn push_annotation(&mut self, ann: &ShellAnnotation) -> Result<(), Self::Error> {
self.color_stack.push(*ann);
self.upstream.set_color(&(*ann).into())
}
fn pop_annotation(&mut self) -> Result<(), Self::Error> {
self.color_stack.pop();
match self.color_stack.last() {
Some(previous) => self.upstream.set_color(&(*previous).into()),
None => self.upstream.reset(),
}
}
}

View File

@ -13,13 +13,7 @@ use crate::parse::parser::Number;
use crate::parse::unit::Unit;
use derive_new::new;
use getset::Getters;
#[cfg(not(coloring_in_tokens))]
use nu_errors::ShellError;
#[cfg(not(coloring_in_tokens))]
use nu_protocol::{EvaluatedArgs, Scope};
use nu_protocol::{PathMember, ShellTypeName};
#[cfg(not(coloring_in_tokens))]
use nu_source::Text;
use nu_source::{
b, DebugDocBuilder, HasSpan, PrettyDebug, PrettyDebugWithSource, Span, Spanned, SpannedItem,
};

View File

@ -1,5 +1,4 @@
pub mod commands;
pub mod debug;
pub mod hir;
pub mod parse;
pub mod parse_command;

View File

@ -11,5 +11,6 @@ pub use self::meta::{
pub use self::pretty::{
b, DebugDoc, DebugDocBuilder, PrettyDebug, PrettyDebugWithSource, ShellAnnotation,
};
pub use self::term_colored::TermColored;
pub use self::text::Text;
pub use self::tracable::{nom_input, NomSpan, TracableContext};

View File

@ -1,5 +1,5 @@
use crate::parser::hir;
use derive_new::new;
use nu_parser::hir;
#[derive(new, Debug, Eq, PartialEq)]
pub(crate) struct Command {

View File

@ -1,9 +1,12 @@
use super::ClassifiedInputStream;
use crate::data::value;
use crate::prelude::*;
use bytes::{BufMut, BytesMut};
use futures::stream::StreamExt;
use futures_codec::{Decoder, Encoder, Framed};
use log::trace;
use nu_errors::ShellError;
use nu_protocol::Value;
use std::io::{Error, ErrorKind};
use subprocess::Exec;
@ -105,7 +108,7 @@ impl Command {
let input_strings = inputs
.iter()
.map(|i| {
i.as_string().map_err(|_| {
i.as_string().map(|s| s.to_string()).map_err(|_| {
let arg = self.args.iter().find(|arg| arg.arg.contains("$it"));
if let Some(arg) = arg {
ShellError::labeled_error(
@ -206,9 +209,8 @@ impl Command {
let stdout = popen.stdout.take().unwrap();
let file = futures::io::AllowStdIo::new(stdout);
let stream = Framed::new(file, LinesCodec {});
let stream = stream.map(move |line| {
UntaggedValue::string(line.unwrap()).into_value(&name_tag)
});
let stream =
stream.map(move |line| value::string(line.unwrap()).into_value(&name_tag));
Ok(ClassifiedInputStream::from_input_stream(
stream.boxed() as BoxStream<'static, Value>
))

View File

@ -1,7 +1,10 @@
use crate::parser::hir;
use crate::data::value;
use crate::prelude::*;
use derive_new::new;
use log::{log_enabled, trace};
use nu_errors::ShellError;
use nu_parser::hir;
use nu_protocol::{CommandAction, Primitive, ReturnSuccess, UntaggedValue, Value};
use super::ClassifiedInputStream;
@ -77,7 +80,7 @@ impl Command {
} => {
context.shell_manager.insert_at_current(Box::new(
HelpShell::for_command(
UntaggedValue::string(cmd).into_value(tag),
value::string(cmd).into_value(tag),
&context.registry(),
).unwrap(),
));
@ -126,12 +129,12 @@ impl Command {
doc.render_raw(
context.with_host(|host| host.width() - 5),
&mut crate::parser::debug::TermColored::new(&mut buffer),
&mut nu_source::TermColored::new(&mut buffer),
).unwrap();
let value = String::from_utf8_lossy(buffer.as_slice());
yield Ok(UntaggedValue::string(value).into_untagged_value())
yield Ok(value::string(value).into_untagged_value())
}
Err(err) => {

View File

@ -1,5 +1,5 @@
use crate::parser::{hir, TokenNode};
use crate::prelude::*;
use nu_parser::{hir, TokenNode};
mod dynamic;
mod external;
@ -11,7 +11,6 @@ pub(crate) use dynamic::Command as DynamicCommand;
#[allow(unused_imports)]
pub(crate) use external::{Command as ExternalCommand, ExternalArg, ExternalArgs, StreamNext};
pub(crate) use internal::Command as InternalCommand;
pub(crate) use pipeline::Pipeline as ClassifiedPipeline;
pub(crate) struct ClassifiedInputStream {
pub(crate) objects: InputStream,
@ -21,7 +20,7 @@ pub(crate) struct ClassifiedInputStream {
impl ClassifiedInputStream {
pub(crate) fn new() -> ClassifiedInputStream {
ClassifiedInputStream {
objects: vec![UntaggedValue::nothing().into_untagged_value()].into(),
objects: vec![crate::data::value::nothing().into_untagged_value()].into(),
stdin: None,
}
}

View File

@ -1,8 +1,8 @@
use crate::commands::WholeStreamCommand;
use crate::context::CommandRegistry;
use crate::prelude::*;
use nu_protocol::{Signature, SyntaxShape};
use nu_errors::ShellError;
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape};
use nu_source::Tagged;
#[derive(Deserialize)]

View File

@ -150,10 +150,10 @@ impl CompareValues {
}
pub(crate) fn coerce_compare(
left: &Value,
right: &Value,
left: &UntaggedValue,
right: &UntaggedValue,
) -> Result<CompareValues, (&'static str, &'static str)> {
match (&left.value, &right.value) {
match (left, right) {
(UntaggedValue::Primitive(left), UntaggedValue::Primitive(right)) => {
coerce_compare_primitive(left, right)
}

View File

@ -98,8 +98,8 @@ pub fn nothing() -> UntaggedValue {
pub fn compare_values(
operator: &Operator,
left: &Value,
right: &Value,
left: &UntaggedValue,
right: &UntaggedValue,
) -> Result<bool, (&'static str, &'static str)> {
match operator {
_ => {

View File

@ -1,13 +1,7 @@
use crate::context::CommandRegistry;
use crate::data::base::Block;
use crate::data::value;
use crate::errors::ArgumentError;
use crate::evaluate::operator::apply_operator;
use crate::parser::hir::path::{ColumnPath, UnspannedPathMember};
use crate::parser::{
hir::{self, Expression, RawExpression},
CommandRegistry,
};
use crate::prelude::*;
use crate::TaggedDictBuilder;
use log::trace;

View File

@ -1,6 +1,6 @@
use crate::data::base::{Primitive, UntaggedValue, Value};
use crate::parser::Operator;
use crate::traits::ShellTypeName;
use crate::data::value;
use nu_parser::Operator;
use nu_protocol::{Primitive, ShellTypeName, UntaggedValue, Value};
use std::ops::Not;
pub fn apply_operator(
@ -14,12 +14,10 @@ pub fn apply_operator(
| Operator::LessThan
| Operator::GreaterThan
| Operator::LessThanOrEqual
| Operator::GreaterThanOrEqual => left.compare(op, right).map(UntaggedValue::boolean),
Operator::Dot => Ok(UntaggedValue::boolean(false)),
Operator::Contains => contains(left, right).map(UntaggedValue::boolean),
Operator::NotContains => contains(left, right)
.map(Not::not)
.map(UntaggedValue::boolean),
| Operator::GreaterThanOrEqual => left.compare(op, right).map(value::boolean),
Operator::Dot => Ok(value::boolean(false)),
Operator::Contains => contains(left, right).map(value::boolean),
Operator::NotContains => contains(left, right).map(Not::not).map(value::boolean),
}
}