Add Range and start Signature support

This commit contains two improvements:

- Support for a Range syntax (and a corresponding Range value)
- Work towards a signature syntax

Implementing the Range syntax resulted in cleaning up how operators in
the core syntax works. There are now two kinds of infix operators

- tight operators (`.` and `..`)
- loose operators

Tight operators may not be interspersed (`$it.left..$it.right` is a
syntax error). Loose operators require whitespace on both sides of the
operator, and can be arbitrarily interspersed. Precedence is left to
right in the core syntax.

Note that delimited syntax (like `( ... )` or `[ ... ]`) is a single
token node in the core syntax. A single token node can be parsed from
beginning to end in a context-free manner.

The rule for `.` is `<token node>.<member>`. The rule for `..` is
`<token node>..<token node>`.

Loose operators all have the same syntactic rule: `<token
node><space><loose op><space><token node>`.

The second aspect of this pull request is the beginning of support for a
signature syntax. Before implementing signatures, a necessary
prerequisite is for the core syntax to support multi-line programs.

That work establishes a few things:

- `;` and newlines are handled in the core grammar, and both count as
  "separators"
- line comments begin with `#` and continue until the end of the line

In this commit, multi-token productions in the core grammar can use
separators interchangably with spaces. However, I think we will
ultimately want a different rule preventing separators from occurring
before an infix operator, so that the end of a line is always
unambiguous. This would avoid gratuitous differences between modules and
repl usage.

We already effectively have this rule, because otherwise `x<newline> |
y` would be a single pipeline, but of course that wouldn't work.
This commit is contained in:
Yehuda Katz
2019-12-04 13:14:52 -08:00
parent 16272b1b20
commit 57af9b5040
64 changed files with 2522 additions and 738 deletions

View File

@ -1,6 +1,7 @@
use crate::commands::WholeStreamCommand;
use crate::prelude::*;
use nu_errors::ShellError;
use nu_macros::signature;
use nu_protocol::{Signature, SyntaxShape};
pub struct CD;
@ -11,11 +12,17 @@ impl WholeStreamCommand for CD {
}
fn signature(&self) -> Signature {
Signature::build("cd").optional(
"directory",
SyntaxShape::Path,
"the directory to change to",
)
signature! {
def cd {
"the directory to change to"
directory(optional Path) - "the directory to change to"
}
}
// Signature::build("cd").optional(
// "directory",
// SyntaxShape::Path,
// "the directory to change to",
// )
}
fn usage(&self) -> &str {

View File

@ -378,14 +378,7 @@ pub trait WholeStreamCommand: Send + Sync {
fn name(&self) -> &str;
fn signature(&self) -> Signature {
Signature {
name: self.name().to_string(),
usage: self.usage().to_string(),
positional: vec![],
rest_positional: None,
named: indexmap::IndexMap::new(),
is_filter: true,
}
Signature::new(self.name()).desc(self.usage()).filter()
}
fn usage(&self) -> &str;
@ -405,14 +398,7 @@ pub trait PerItemCommand: Send + Sync {
fn name(&self) -> &str;
fn signature(&self) -> Signature {
Signature {
name: self.name().to_string(),
usage: self.usage().to_string(),
positional: vec![],
rest_positional: None,
named: indexmap::IndexMap::new(),
is_filter: true,
}
Signature::new(self.name()).desc(self.usage()).filter()
}
fn usage(&self) -> &str;

View File

@ -1,5 +1,6 @@
use crate::commands::WholeStreamCommand;
use crate::context::CommandRegistry;
use crate::deserializer::NumericRange;
use crate::prelude::*;
use nu_errors::ShellError;
use nu_protocol::{Signature, SyntaxShape};
@ -7,7 +8,7 @@ use nu_source::Tagged;
#[derive(Deserialize)]
struct RangeArgs {
area: Tagged<String>,
area: Tagged<NumericRange>,
}
pub struct Range;
@ -20,7 +21,7 @@ impl WholeStreamCommand for Range {
fn signature(&self) -> Signature {
Signature::build("range").required(
"rows ",
SyntaxShape::Any,
SyntaxShape::Range,
"range of rows to return: Eg) 4..7 (=> from 4 to 7)",
)
}
@ -39,48 +40,14 @@ impl WholeStreamCommand for Range {
}
fn range(
RangeArgs { area: rows }: RangeArgs,
RunnableContext { input, name, .. }: RunnableContext,
RangeArgs { area }: RangeArgs,
RunnableContext { input, name: _, .. }: RunnableContext,
) -> Result<OutputStream, ShellError> {
match rows.item.find('.') {
Some(value) => {
let (first, last) = rows.item.split_at(value);
let first = match first.parse::<u64>() {
Ok(postion) => postion,
Err(_) => {
if first == "" {
0
} else {
return Err(ShellError::labeled_error(
"no correct start of range",
"'from' needs to be an Integer or empty",
name,
));
}
}
};
let last = match last.trim_start_matches('.').parse::<u64>() {
Ok(postion) => postion,
Err(_) => {
if last == ".." {
std::u64::MAX - 1
} else {
return Err(ShellError::labeled_error(
"no correct end of range",
"'to' needs to be an Integer or empty",
name,
));
}
}
};
Ok(OutputStream::from_input(
input.values.skip(first).take(last - first + 1),
))
}
None => Err(ShellError::labeled_error(
"No correct formatted range found",
"format: <from>..<to>",
name,
)),
}
let range = area.item;
let (from, _) = range.from;
let (to, _) = range.to;
return Ok(OutputStream::from_input(
input.values.skip(*from).take(*to - *from + 1),
));
}

View File

@ -73,7 +73,7 @@ pub fn value_to_bson_value(v: &Value) -> Result<Bson, ShellError> {
.map(|x| value_to_bson_value(x))
.collect::<Result<_, _>>()?,
),
UntaggedValue::Block(_) => Bson::Null,
UntaggedValue::Block(_) | UntaggedValue::Primitive(Primitive::Range(_)) => Bson::Null,
UntaggedValue::Error(e) => return Err(e.clone()),
UntaggedValue::Primitive(Primitive::Binary(b)) => {
Bson::Binary(BinarySubtype::Generic, b.clone())

View File

@ -76,7 +76,9 @@ pub fn value_to_json_value(v: &Value) -> Result<serde_json::Value, ShellError> {
UntaggedValue::Table(l) => serde_json::Value::Array(json_list(l)?),
UntaggedValue::Error(e) => return Err(e.clone()),
UntaggedValue::Block(_) => serde_json::Value::Null,
UntaggedValue::Block(_) | UntaggedValue::Primitive(Primitive::Range(_)) => {
serde_json::Value::Null
}
UntaggedValue::Primitive(Primitive::Binary(b)) => serde_json::Value::Array(
b.iter()
.map(|x| {

View File

@ -100,9 +100,10 @@ fn nu_value_to_sqlite_string(v: Value) -> String {
Primitive::Date(d) => format!("'{}'", d),
Primitive::Path(p) => format!("'{}'", p.display().to_string().replace("'", "''")),
Primitive::Binary(u) => format!("x'{}'", encode(u)),
Primitive::BeginningOfStream | Primitive::EndOfStream | Primitive::ColumnPath(_) => {
"NULL".into()
}
Primitive::BeginningOfStream
| Primitive::EndOfStream
| Primitive::ColumnPath(_)
| Primitive::Range(_) => "NULL".into(),
},
_ => "NULL".into(),
}

View File

@ -69,6 +69,7 @@ pub fn value_to_toml_value(v: &Value) -> Result<toml::Value, ShellError> {
UntaggedValue::Table(l) => toml::Value::Array(collect_values(l)?),
UntaggedValue::Error(e) => return Err(e.clone()),
UntaggedValue::Block(_) => toml::Value::String("<Block>".to_string()),
UntaggedValue::Primitive(Primitive::Range(_)) => toml::Value::String("<Range>".to_string()),
UntaggedValue::Primitive(Primitive::Binary(b)) => {
toml::Value::Array(b.iter().map(|x| toml::Value::Integer(*x as i64)).collect())
}

View File

@ -85,7 +85,9 @@ pub fn value_to_yaml_value(v: &Value) -> Result<serde_yaml::Value, ShellError> {
serde_yaml::Value::Sequence(out)
}
UntaggedValue::Error(e) => return Err(e.clone()),
UntaggedValue::Block(_) => serde_yaml::Value::Null,
UntaggedValue::Block(_) | UntaggedValue::Primitive(Primitive::Range(_)) => {
serde_yaml::Value::Null
}
UntaggedValue::Primitive(Primitive::Binary(b)) => serde_yaml::Value::Sequence(
b.iter()
.map(|x| serde_yaml::Value::Number(serde_yaml::Number::from(*x)))

View File

@ -7,7 +7,7 @@ use chrono::{DateTime, Utc};
use derive_new::new;
use log::trace;
use nu_errors::ShellError;
use nu_parser::{hir, Operator};
use nu_parser::{hir, CompareOperator};
use nu_protocol::{
Evaluate, EvaluateTrait, Primitive, Scope, ShellTypeName, SpannedTypeName, TaggedDictBuilder,
UntaggedValue, Value,
@ -23,7 +23,7 @@ use std::time::SystemTime;
#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Clone, new, Serialize)]
pub struct Operation {
pub(crate) left: Value,
pub(crate) operator: Operator,
pub(crate) operator: CompareOperator,
pub(crate) right: Value,
}

View File

@ -1,207 +1,24 @@
use crate::prelude::*;
use chrono::{DateTime, Utc};
use chrono_humanize::Humanize;
use derive_new::new;
use indexmap::IndexMap;
use nu_errors::ShellError;
use nu_protocol::RangeInclusion;
use nu_protocol::{
format_primitive, ColumnPath, Dictionary, Evaluate, Primitive, ShellTypeName,
TaggedDictBuilder, UntaggedValue, Value,
};
use nu_source::{b, DebugDoc, PrettyDebug};
use nu_source::{b, PrettyDebug};
use std::collections::BTreeMap;
use std::fmt::Debug;
use std::hash::Hash;
use std::io::Write;
use std::path::PathBuf;
/**
This file describes the structural types of the nushell system.
Its primary purpose today is to identify "equivalent" values for the purpose
of merging rows into a single table or identify rows in a table that have the
same shape for reflection.
It also serves as the primary vehicle for pretty-printing.
*/
#[allow(unused)]
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub enum TypeShape {
Nothing,
Int,
Decimal,
Bytesize,
String,
Line,
ColumnPath,
Pattern,
Boolean,
Date,
Duration,
Path,
Binary,
Row(BTreeMap<Column, TypeShape>),
Table(Vec<TypeShape>),
// TODO: Block arguments
Block,
// TODO: Error type
Error,
// Stream markers (used as bookend markers rather than actual values)
BeginningOfStream,
EndOfStream,
}
impl TypeShape {
pub fn from_primitive(primitive: &Primitive) -> TypeShape {
match primitive {
Primitive::Nothing => TypeShape::Nothing,
Primitive::Int(_) => TypeShape::Int,
Primitive::Decimal(_) => TypeShape::Decimal,
Primitive::Bytes(_) => TypeShape::Bytesize,
Primitive::String(_) => TypeShape::String,
Primitive::Line(_) => TypeShape::Line,
Primitive::ColumnPath(_) => TypeShape::ColumnPath,
Primitive::Pattern(_) => TypeShape::Pattern,
Primitive::Boolean(_) => TypeShape::Boolean,
Primitive::Date(_) => TypeShape::Date,
Primitive::Duration(_) => TypeShape::Duration,
Primitive::Path(_) => TypeShape::Path,
Primitive::Binary(_) => TypeShape::Binary,
Primitive::BeginningOfStream => TypeShape::BeginningOfStream,
Primitive::EndOfStream => TypeShape::EndOfStream,
}
}
pub fn from_dictionary(dictionary: &Dictionary) -> TypeShape {
let mut map = BTreeMap::new();
for (key, value) in dictionary.entries.iter() {
let column = Column::String(key.clone());
map.insert(column, TypeShape::from_value(value));
}
TypeShape::Row(map)
}
pub fn from_table<'a>(table: impl IntoIterator<Item = &'a Value>) -> TypeShape {
let mut vec = vec![];
for item in table.into_iter() {
vec.push(TypeShape::from_value(item))
}
TypeShape::Table(vec)
}
pub fn from_value<'a>(value: impl Into<&'a UntaggedValue>) -> TypeShape {
match value.into() {
UntaggedValue::Primitive(p) => TypeShape::from_primitive(p),
UntaggedValue::Row(row) => TypeShape::from_dictionary(row),
UntaggedValue::Table(table) => TypeShape::from_table(table.iter()),
UntaggedValue::Error(_) => TypeShape::Error,
UntaggedValue::Block(_) => TypeShape::Block,
}
}
}
impl PrettyDebug for TypeShape {
fn pretty(&self) -> DebugDocBuilder {
match self {
TypeShape::Nothing => ty("nothing"),
TypeShape::Int => ty("integer"),
TypeShape::Decimal => ty("decimal"),
TypeShape::Bytesize => ty("bytesize"),
TypeShape::String => ty("string"),
TypeShape::Line => ty("line"),
TypeShape::ColumnPath => ty("column-path"),
TypeShape::Pattern => ty("pattern"),
TypeShape::Boolean => ty("boolean"),
TypeShape::Date => ty("date"),
TypeShape::Duration => ty("duration"),
TypeShape::Path => ty("path"),
TypeShape::Binary => ty("binary"),
TypeShape::Error => b::error("error"),
TypeShape::BeginningOfStream => b::keyword("beginning-of-stream"),
TypeShape::EndOfStream => b::keyword("end-of-stream"),
TypeShape::Row(row) => (b::kind("row")
+ b::space()
+ b::intersperse(
row.iter().map(|(key, ty)| {
(b::key(match key {
Column::String(string) => string.clone(),
Column::Value => "<value>".to_string(),
}) + b::delimit("(", ty.pretty(), ")").into_kind())
.nest()
}),
b::space(),
)
.nest())
.nest(),
TypeShape::Table(table) => {
let mut group: Group<DebugDoc, Vec<(usize, usize)>> = Group::new();
for (i, item) in table.iter().enumerate() {
group.add(item.to_doc(), i);
}
(b::kind("table") + b::space() + b::keyword("of")).group()
+ b::space()
+ (if group.len() == 1 {
let (doc, _) = group.into_iter().nth(0).unwrap();
DebugDocBuilder::from_doc(doc)
} else {
b::intersperse(
group.into_iter().map(|(doc, rows)| {
(b::intersperse(
rows.iter().map(|(from, to)| {
if from == to {
b::description(from)
} else {
(b::description(from)
+ b::space()
+ b::keyword("to")
+ b::space()
+ b::description(to))
.group()
}
}),
b::description(", "),
) + b::description(":")
+ b::space()
+ DebugDocBuilder::from_doc(doc))
.nest()
}),
b::space(),
)
})
}
TypeShape::Block => ty("block"),
}
}
}
#[derive(Debug, new)]
struct DebugEntry<'a> {
key: &'a Column,
value: &'a TypeShape,
}
impl<'a> PrettyDebug for DebugEntry<'a> {
fn pretty(&self) -> DebugDocBuilder {
(b::key(match self.key {
Column::String(string) => string.clone(),
Column::Value => "<value>".to_owned(),
}) + b::delimit("(", self.value.pretty(), ")").into_kind())
}
}
fn ty(name: impl std::fmt::Display) -> DebugDocBuilder {
b::kind(format!("{}", name))
pub struct InlineRange {
from: (InlineShape, RangeInclusion),
to: (InlineShape, RangeInclusion),
}
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
@ -209,6 +26,7 @@ pub enum InlineShape {
Nothing,
Int(BigInt),
Decimal(BigDecimal),
Range(Box<InlineRange>),
Bytesize(u64),
String(String),
Line(String),
@ -243,6 +61,15 @@ impl InlineShape {
match primitive {
Primitive::Nothing => InlineShape::Nothing,
Primitive::Int(int) => InlineShape::Int(int.clone()),
Primitive::Range(range) => {
let (left, left_inclusion) = &range.from;
let (right, right_inclusion) = &range.to;
InlineShape::Range(Box::new(InlineRange {
from: (InlineShape::from_primitive(left), *left_inclusion),
to: (InlineShape::from_primitive(right), *right_inclusion),
}))
}
Primitive::Decimal(decimal) => InlineShape::Decimal(decimal.clone()),
Primitive::Bytes(bytesize) => InlineShape::Bytesize(*bytesize),
Primitive::String(string) => InlineShape::String(string.clone()),
@ -314,6 +141,17 @@ impl PrettyDebug for FormatInlineShape {
InlineShape::Nothing => b::blank(),
InlineShape::Int(int) => b::primitive(format!("{}", int)),
InlineShape::Decimal(decimal) => b::primitive(format!("{}", decimal)),
InlineShape::Range(range) => {
let (left, left_inclusion) = &range.from;
let (right, right_inclusion) = &range.to;
let op = match (left_inclusion, right_inclusion) {
(RangeInclusion::Inclusive, RangeInclusion::Exclusive) => "..",
_ => unimplemented!("No syntax for ranges that aren't inclusive on the left and exclusive on the right")
};
left.clone().format().pretty() + b::operator(op) + right.clone().format().pretty()
}
InlineShape::Bytesize(bytesize) => {
let byte = byte_unit::Byte::from_bytes(*bytesize as u128);
@ -411,51 +249,6 @@ impl GroupedValue for Vec<(usize, usize)> {
}
}
#[derive(Debug)]
pub struct Group<K: Debug + Eq + Hash, V: GroupedValue> {
values: indexmap::IndexMap<K, V>,
}
impl<K, G> Group<K, G>
where
K: Debug + Eq + Hash,
G: GroupedValue,
{
pub fn new() -> Group<K, G> {
Group {
values: indexmap::IndexMap::default(),
}
}
pub fn len(&self) -> usize {
self.values.len()
}
pub fn into_iter(self) -> impl Iterator<Item = (K, G)> {
self.values.into_iter()
}
pub fn add(&mut self, key: impl Into<K>, value: impl Into<G::Item>) {
let key = key.into();
let value = value.into();
let group = self.values.get_mut(&key);
match group {
None => {
self.values.insert(key, {
let mut group = G::new();
group.merge(value);
group
});
}
Some(group) => {
group.merge(value);
}
}
}
}
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub enum Column {
String(String),

View File

@ -1,10 +1,10 @@
use crate::data::base::coerce_compare;
use crate::data::base::shape::{Column, InlineShape, TypeShape};
use crate::data::base::shape::{Column, InlineShape};
use crate::data::primitive::style_primitive;
use chrono::DateTime;
use nu_errors::ShellError;
use nu_parser::Operator;
use nu_protocol::{Primitive, UntaggedValue};
use nu_parser::CompareOperator;
use nu_protocol::{Primitive, Type, UntaggedValue};
use nu_source::{DebugDocBuilder, PrettyDebug, Tagged};
pub fn date_from_str(s: Tagged<&str>) -> Result<UntaggedValue, ShellError> {
@ -22,7 +22,7 @@ pub fn date_from_str(s: Tagged<&str>) -> Result<UntaggedValue, ShellError> {
}
pub fn compare_values(
operator: Operator,
operator: &CompareOperator,
left: &UntaggedValue,
right: &UntaggedValue,
) -> Result<bool, (&'static str, &'static str)> {
@ -34,16 +34,15 @@ pub fn compare_values(
use std::cmp::Ordering;
let result = match (operator, ordering) {
(Operator::Equal, Ordering::Equal) => true,
(Operator::NotEqual, Ordering::Less) | (Operator::NotEqual, Ordering::Greater) => {
true
}
(Operator::LessThan, Ordering::Less) => true,
(Operator::GreaterThan, Ordering::Greater) => true,
(Operator::GreaterThanOrEqual, Ordering::Greater)
| (Operator::GreaterThanOrEqual, Ordering::Equal) => true,
(Operator::LessThanOrEqual, Ordering::Less)
| (Operator::LessThanOrEqual, Ordering::Equal) => true,
(CompareOperator::Equal, Ordering::Equal) => true,
(CompareOperator::NotEqual, Ordering::Less)
| (CompareOperator::NotEqual, Ordering::Greater) => true,
(CompareOperator::LessThan, Ordering::Less) => true,
(CompareOperator::GreaterThan, Ordering::Greater) => true,
(CompareOperator::GreaterThanOrEqual, Ordering::Greater)
| (CompareOperator::GreaterThanOrEqual, Ordering::Equal) => true,
(CompareOperator::LessThanOrEqual, Ordering::Less)
| (CompareOperator::LessThanOrEqual, Ordering::Equal) => true,
_ => false,
};
@ -53,7 +52,7 @@ pub fn compare_values(
}
pub fn format_type<'a>(value: impl Into<&'a UntaggedValue>, width: usize) -> String {
TypeShape::from_value(value.into()).colored_string(width)
Type::from_value(value.into()).colored_string(width)
}
pub fn format_leaf<'a>(value: impl Into<&'a UntaggedValue>) -> DebugDocBuilder {

View File

@ -1,11 +1,20 @@
use log::trace;
use nu_errors::{CoerceInto, ShellError};
use nu_protocol::{CallInfo, ColumnPath, Evaluate, Primitive, ShellTypeName, UntaggedValue, Value};
use nu_source::{HasSpan, SpannedItem, Tagged, TaggedItem};
use nu_protocol::{
CallInfo, ColumnPath, Evaluate, Primitive, RangeInclusion, ShellTypeName, UntaggedValue, Value,
};
use nu_source::{HasSpan, Spanned, SpannedItem, Tagged, TaggedItem};
use nu_value_ext::ValueExt;
use serde::de;
use serde::{Deserialize, Serialize};
use std::path::PathBuf;
#[derive(Copy, Clone, Deserialize, Serialize)]
pub struct NumericRange {
pub from: (Spanned<u64>, RangeInclusion),
pub to: (Spanned<u64>, RangeInclusion),
}
#[derive(Debug)]
pub struct DeserializerItem<'de> {
key_struct_field: Option<(String, &'de str)>,
@ -406,6 +415,25 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut ConfigDeserializer<'de> {
value: UntaggedValue::Primitive(Primitive::String(string)),
..
} => visit::<Tagged<String>, _>(string.tagged(tag), name, fields, visitor),
Value {
value: UntaggedValue::Primitive(Primitive::Range(range)),
..
} => {
let (left, left_inclusion) = range.from;
let (right, right_inclusion) = range.to;
let left_span = left.span;
let right_span = right.span;
let left = left.as_u64(left_span)?;
let right = right.as_u64(right_span)?;
let numeric_range = NumericRange {
from: (left.spanned(left_span), left_inclusion),
to: (right.spanned(right_span), right_inclusion),
};
visit::<Tagged<NumericRange>, _>(numeric_range.tagged(tag), name, fields, visitor)
}
other => Err(ShellError::type_error(
name,

View File

@ -6,8 +6,8 @@ use log::trace;
use nu_errors::{ArgumentError, ShellError};
use nu_parser::hir::{self, Expression, RawExpression};
use nu_protocol::{
ColumnPath, Evaluate, Primitive, Scope, TaggedDictBuilder, UnspannedPathMember, UntaggedValue,
Value,
ColumnPath, Evaluate, Primitive, RangeInclusion, Scope, TaggedDictBuilder, UnspannedPathMember,
UntaggedValue, Value,
};
use nu_source::Text;
@ -40,7 +40,7 @@ pub(crate) fn evaluate_baseline_expr(
trace!("left={:?} right={:?}", left.value, right.value);
match apply_operator(**binary.op(), &left, &right) {
match apply_operator(&**binary.op(), &left, &right) {
Ok(result) => Ok(result.into_value(tag)),
Err((left_type, right_type)) => Err(ShellError::coerce_error(
left_type.spanned(binary.left().span),
@ -48,6 +48,26 @@ pub(crate) fn evaluate_baseline_expr(
)),
}
}
RawExpression::Range(range) => {
let left = range.left();
let right = range.right();
let left = evaluate_baseline_expr(left, registry, scope, source)?;
let right = evaluate_baseline_expr(right, registry, scope, source)?;
let left_span = left.tag.span;
let right_span = right.tag.span;
let left = (
left.as_primitive()?.spanned(left_span),
RangeInclusion::Inclusive,
);
let right = (
right.as_primitive()?.spanned(right_span),
RangeInclusion::Exclusive,
);
Ok(UntaggedValue::range(left, right).into_value(tag))
}
RawExpression::List(list) => {
let mut exprs = vec![];

View File

@ -1,25 +1,24 @@
use crate::data::value;
use nu_parser::Operator;
use nu_parser::CompareOperator;
use nu_protocol::{Primitive, ShellTypeName, UntaggedValue, Value};
use std::ops::Not;
pub fn apply_operator(
op: Operator,
op: &CompareOperator,
left: &Value,
right: &Value,
) -> Result<UntaggedValue, (&'static str, &'static str)> {
match op {
Operator::Equal
| Operator::NotEqual
| Operator::LessThan
| Operator::GreaterThan
| Operator::LessThanOrEqual
| Operator::GreaterThanOrEqual => {
match *op {
CompareOperator::Equal
| CompareOperator::NotEqual
| CompareOperator::LessThan
| CompareOperator::GreaterThan
| CompareOperator::LessThanOrEqual
| CompareOperator::GreaterThanOrEqual => {
value::compare_values(op, left, right).map(UntaggedValue::boolean)
}
Operator::Dot => Ok(UntaggedValue::boolean(false)),
Operator::Contains => contains(left, right).map(UntaggedValue::boolean),
Operator::NotContains => contains(left, right)
CompareOperator::Contains => contains(left, right).map(UntaggedValue::boolean),
CompareOperator::NotContains => contains(left, right)
.map(Not::not)
.map(UntaggedValue::boolean),
}

View File

@ -144,7 +144,8 @@ fn paint_flat_shape(flat_shape: &Spanned<FlatShape>, line: &str) -> String {
FlatShape::CloseDelimiter(_) => Color::White.normal(),
FlatShape::ItVariable => Color::Purple.bold(),
FlatShape::Variable => Color::Purple.normal(),
FlatShape::Operator => Color::Yellow.normal(),
FlatShape::CompareOperator => Color::Yellow.normal(),
FlatShape::DotDot => Color::Yellow.bold(),
FlatShape::Dot => Color::White.normal(),
FlatShape::InternalCommand => Color::Cyan.bold(),
FlatShape::ExternalCommand => Color::Cyan.normal(),
@ -160,7 +161,8 @@ fn paint_flat_shape(flat_shape: &Spanned<FlatShape>, line: &str) -> String {
FlatShape::ShorthandFlag => Color::Black.bold(),
FlatShape::Int => Color::Purple.bold(),
FlatShape::Decimal => Color::Purple.bold(),
FlatShape::Whitespace => Color::White.normal(),
FlatShape::Whitespace | FlatShape::Separator => Color::White.normal(),
FlatShape::Comment => Color::Black.bold(),
FlatShape::Error => Color::Red.bold(),
FlatShape::Size { number, unit } => {
let number = number.slice(line);