From 35c36224058fdad0dc4bc96821fee60e5c41d722 Mon Sep 17 00:00:00 2001 From: JT Date: Thu, 26 Aug 2021 07:29:36 +1200 Subject: [PATCH] Add a few operators. Needs parser work --- Cargo.toml | 3 + crates/nu-cli/src/errors.rs | 9 + crates/nu-engine/src/eval.rs | 12 +- crates/nu-engine/src/value.rs | 302 +++++++++++++++++++++++++++++ crates/nu-parser/src/parser.rs | 30 ++- crates/nu-parser/src/type_check.rs | 211 ++++++++++++++++++-- 6 files changed, 546 insertions(+), 21 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ca3f9ac46a..9bf10f63fb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,6 +5,9 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[workspace] +members = ["crates/nu-cli", "crates/nu-engine", "crates/nu-parser"] + [dependencies] reedline = { git = "https://github.com/jntrnr/reedline", branch = "main" } codespan-reporting = "0.11.1" diff --git a/crates/nu-cli/src/errors.rs b/crates/nu-cli/src/errors.rs index bb96c7ce20..3d1931cd27 100644 --- a/crates/nu-cli/src/errors.rs +++ b/crates/nu-cli/src/errors.rs @@ -273,6 +273,15 @@ pub fn report_shell_error( .with_labels(vec![Label::primary(diag_file_id, diag_range) .with_message(format!("can't convert to {}", s))]) } + ShellError::DivisionByZero(span) => { + let (diag_file_id, diag_range) = convert_span_to_diag(working_set, span)?; + + Diagnostic::error() + .with_message("Division by zero") + .with_labels(vec![ + Label::primary(diag_file_id, diag_range).with_message("division by zero") + ]) + } }; // println!("DIAG"); diff --git a/crates/nu-engine/src/eval.rs b/crates/nu-engine/src/eval.rs index 089cc68af3..aec3786c4c 100644 --- a/crates/nu-engine/src/eval.rs +++ b/crates/nu-engine/src/eval.rs @@ -16,6 +16,7 @@ pub enum ShellError { InternalError(String), VariableNotFound(Span), CantConvert(String, Span), + DivisionByZero(Span), } pub fn eval_operator(op: &Expression) -> Result { @@ -227,7 +228,16 @@ pub fn eval_expression(state: &State, expr: &Expression) -> Result lhs.add(op_span, &rhs), - _ => Ok(Value::Nothing { span: expr.span }), + Operator::Minus => lhs.sub(op_span, &rhs), + Operator::Multiply => lhs.mul(op_span, &rhs), + Operator::Divide => lhs.div(op_span, &rhs), + Operator::LessThan => lhs.lt(op_span, &rhs), + Operator::LessThanOrEqual => lhs.lte(op_span, &rhs), + Operator::GreaterThan => lhs.gt(op_span, &rhs), + Operator::GreaterThanOrEqual => lhs.gte(op_span, &rhs), + Operator::Equal => lhs.eq(op_span, &rhs), + Operator::NotEqual => lhs.ne(op_span, &rhs), + _ => Err(ShellError::Unsupported(op_span)), } } diff --git a/crates/nu-engine/src/value.rs b/crates/nu-engine/src/value.rs index 7029ca6464..c5a200a957 100644 --- a/crates/nu-engine/src/value.rs +++ b/crates/nu-engine/src/value.rs @@ -131,4 +131,306 @@ impl Value { }), } } + pub fn sub(&self, op: Span, rhs: &Value) -> Result { + let span = nu_parser::span(&[self.span(), rhs.span()]); + + match (self, rhs) { + (Value::Int { val: lhs, .. }, Value::Int { val: rhs, .. }) => Ok(Value::Int { + val: lhs - rhs, + span, + }), + (Value::Int { val: lhs, .. }, Value::Float { val: rhs, .. }) => Ok(Value::Float { + val: *lhs as f64 - *rhs, + span, + }), + (Value::Float { val: lhs, .. }, Value::Int { val: rhs, .. }) => Ok(Value::Float { + val: *lhs - *rhs as f64, + span, + }), + (Value::Float { val: lhs, .. }, Value::Float { val: rhs, .. }) => Ok(Value::Float { + val: lhs - rhs, + span, + }), + + _ => Err(ShellError::OperatorMismatch { + op_span: op, + lhs_ty: self.get_type(), + lhs_span: self.span(), + rhs_ty: rhs.get_type(), + rhs_span: rhs.span(), + }), + } + } + pub fn mul(&self, op: Span, rhs: &Value) -> Result { + let span = nu_parser::span(&[self.span(), rhs.span()]); + + match (self, rhs) { + (Value::Int { val: lhs, .. }, Value::Int { val: rhs, .. }) => Ok(Value::Int { + val: lhs * rhs, + span, + }), + (Value::Int { val: lhs, .. }, Value::Float { val: rhs, .. }) => Ok(Value::Float { + val: *lhs as f64 * *rhs, + span, + }), + (Value::Float { val: lhs, .. }, Value::Int { val: rhs, .. }) => Ok(Value::Float { + val: *lhs * *rhs as f64, + span, + }), + (Value::Float { val: lhs, .. }, Value::Float { val: rhs, .. }) => Ok(Value::Float { + val: lhs * rhs, + span, + }), + + _ => Err(ShellError::OperatorMismatch { + op_span: op, + lhs_ty: self.get_type(), + lhs_span: self.span(), + rhs_ty: rhs.get_type(), + rhs_span: rhs.span(), + }), + } + } + pub fn div(&self, op: Span, rhs: &Value) -> Result { + let span = nu_parser::span(&[self.span(), rhs.span()]); + + match (self, rhs) { + (Value::Int { val: lhs, .. }, Value::Int { val: rhs, .. }) => { + if *rhs != 0 { + Ok(Value::Int { + val: lhs / rhs, + span, + }) + } else { + Err(ShellError::DivisionByZero(op)) + } + } + (Value::Int { val: lhs, .. }, Value::Float { val: rhs, .. }) => { + if *rhs != 0.0 { + Ok(Value::Float { + val: *lhs as f64 / *rhs, + span, + }) + } else { + Err(ShellError::DivisionByZero(op)) + } + } + (Value::Float { val: lhs, .. }, Value::Int { val: rhs, .. }) => { + if *rhs != 0 { + Ok(Value::Float { + val: *lhs / *rhs as f64, + span, + }) + } else { + Err(ShellError::DivisionByZero(op)) + } + } + (Value::Float { val: lhs, .. }, Value::Float { val: rhs, .. }) => { + if *rhs != 0.0 { + Ok(Value::Float { + val: lhs / rhs, + span, + }) + } else { + Err(ShellError::DivisionByZero(op)) + } + } + + _ => Err(ShellError::OperatorMismatch { + op_span: op, + lhs_ty: self.get_type(), + lhs_span: self.span(), + rhs_ty: rhs.get_type(), + rhs_span: rhs.span(), + }), + } + } + pub fn lt(&self, op: Span, rhs: &Value) -> Result { + let span = nu_parser::span(&[self.span(), rhs.span()]); + + match (self, rhs) { + (Value::Int { val: lhs, .. }, Value::Int { val: rhs, .. }) => Ok(Value::Bool { + val: lhs < rhs, + span, + }), + (Value::Int { val: lhs, .. }, Value::Float { val: rhs, .. }) => Ok(Value::Bool { + val: (*lhs as f64) < *rhs, + span, + }), + (Value::Float { val: lhs, .. }, Value::Int { val: rhs, .. }) => Ok(Value::Bool { + val: *lhs < *rhs as f64, + span, + }), + (Value::Float { val: lhs, .. }, Value::Float { val: rhs, .. }) => Ok(Value::Bool { + val: lhs < rhs, + span, + }), + _ => Err(ShellError::OperatorMismatch { + op_span: op, + lhs_ty: self.get_type(), + lhs_span: self.span(), + rhs_ty: rhs.get_type(), + rhs_span: rhs.span(), + }), + } + } + pub fn lte(&self, op: Span, rhs: &Value) -> Result { + let span = nu_parser::span(&[self.span(), rhs.span()]); + + match (self, rhs) { + (Value::Int { val: lhs, .. }, Value::Int { val: rhs, .. }) => Ok(Value::Bool { + val: lhs <= rhs, + span, + }), + (Value::Int { val: lhs, .. }, Value::Float { val: rhs, .. }) => Ok(Value::Bool { + val: (*lhs as f64) <= *rhs, + span, + }), + (Value::Float { val: lhs, .. }, Value::Int { val: rhs, .. }) => Ok(Value::Bool { + val: *lhs <= *rhs as f64, + span, + }), + (Value::Float { val: lhs, .. }, Value::Float { val: rhs, .. }) => Ok(Value::Bool { + val: lhs <= rhs, + span, + }), + _ => Err(ShellError::OperatorMismatch { + op_span: op, + lhs_ty: self.get_type(), + lhs_span: self.span(), + rhs_ty: rhs.get_type(), + rhs_span: rhs.span(), + }), + } + } + pub fn gt(&self, op: Span, rhs: &Value) -> Result { + let span = nu_parser::span(&[self.span(), rhs.span()]); + + match (self, rhs) { + (Value::Int { val: lhs, .. }, Value::Int { val: rhs, .. }) => Ok(Value::Bool { + val: lhs > rhs, + span, + }), + (Value::Int { val: lhs, .. }, Value::Float { val: rhs, .. }) => Ok(Value::Bool { + val: (*lhs as f64) > *rhs, + span, + }), + (Value::Float { val: lhs, .. }, Value::Int { val: rhs, .. }) => Ok(Value::Bool { + val: *lhs > *rhs as f64, + span, + }), + (Value::Float { val: lhs, .. }, Value::Float { val: rhs, .. }) => Ok(Value::Bool { + val: lhs > rhs, + span, + }), + _ => Err(ShellError::OperatorMismatch { + op_span: op, + lhs_ty: self.get_type(), + lhs_span: self.span(), + rhs_ty: rhs.get_type(), + rhs_span: rhs.span(), + }), + } + } + pub fn gte(&self, op: Span, rhs: &Value) -> Result { + let span = nu_parser::span(&[self.span(), rhs.span()]); + + match (self, rhs) { + (Value::Int { val: lhs, .. }, Value::Int { val: rhs, .. }) => Ok(Value::Bool { + val: lhs >= rhs, + span, + }), + (Value::Int { val: lhs, .. }, Value::Float { val: rhs, .. }) => Ok(Value::Bool { + val: (*lhs as f64) >= *rhs, + span, + }), + (Value::Float { val: lhs, .. }, Value::Int { val: rhs, .. }) => Ok(Value::Bool { + val: *lhs >= *rhs as f64, + span, + }), + (Value::Float { val: lhs, .. }, Value::Float { val: rhs, .. }) => Ok(Value::Bool { + val: lhs >= rhs, + span, + }), + _ => Err(ShellError::OperatorMismatch { + op_span: op, + lhs_ty: self.get_type(), + lhs_span: self.span(), + rhs_ty: rhs.get_type(), + rhs_span: rhs.span(), + }), + } + } + pub fn eq(&self, op: Span, rhs: &Value) -> Result { + let span = nu_parser::span(&[self.span(), rhs.span()]); + + match (self, rhs) { + (Value::Int { val: lhs, .. }, Value::Int { val: rhs, .. }) => Ok(Value::Bool { + val: lhs == rhs, + span, + }), + (Value::String { val: lhs, .. }, Value::String { val: rhs, .. }) => Ok(Value::Bool { + val: lhs == rhs, + span, + }), + // FIXME: these should consider machine epsilon + (Value::Int { val: lhs, .. }, Value::Float { val: rhs, .. }) => Ok(Value::Bool { + val: (*lhs as f64) == *rhs, + span, + }), + // FIXME: these should consider machine epsilon + (Value::Float { val: lhs, .. }, Value::Int { val: rhs, .. }) => Ok(Value::Bool { + val: *lhs == *rhs as f64, + span, + }), + // FIXME: these should consider machine epsilon + (Value::Float { val: lhs, .. }, Value::Float { val: rhs, .. }) => Ok(Value::Bool { + val: lhs == rhs, + span, + }), + _ => Err(ShellError::OperatorMismatch { + op_span: op, + lhs_ty: self.get_type(), + lhs_span: self.span(), + rhs_ty: rhs.get_type(), + rhs_span: rhs.span(), + }), + } + } + pub fn ne(&self, op: Span, rhs: &Value) -> Result { + let span = nu_parser::span(&[self.span(), rhs.span()]); + + match (self, rhs) { + (Value::Int { val: lhs, .. }, Value::Int { val: rhs, .. }) => Ok(Value::Bool { + val: lhs != rhs, + span, + }), + (Value::String { val: lhs, .. }, Value::String { val: rhs, .. }) => Ok(Value::Bool { + val: lhs != rhs, + span, + }), + // FIXME: these should consider machine epsilon + (Value::Int { val: lhs, .. }, Value::Float { val: rhs, .. }) => Ok(Value::Bool { + val: (*lhs as f64) != *rhs, + span, + }), + // FIXME: these should consider machine epsilon + (Value::Float { val: lhs, .. }, Value::Int { val: rhs, .. }) => Ok(Value::Bool { + val: *lhs != *rhs as f64, + span, + }), + // FIXME: these should consider machine epsilon + (Value::Float { val: lhs, .. }, Value::Float { val: rhs, .. }) => Ok(Value::Bool { + val: lhs != rhs, + span, + }), + _ => Err(ShellError::OperatorMismatch { + op_span: op, + lhs_ty: self.get_type(), + lhs_span: self.span(), + rhs_ty: rhs.get_type(), + rhs_span: rhs.span(), + }), + } + } } diff --git a/crates/nu-parser/src/parser.rs b/crates/nu-parser/src/parser.rs index deab3f13b4..c196981dc7 100644 --- a/crates/nu-parser/src/parser.rs +++ b/crates/nu-parser/src/parser.rs @@ -8,6 +8,7 @@ use crate::{ parser_state::{Type, VarId}, signature::{Flag, PositionalArg}, BlockId, DeclId, Declaration, LiteBlock, ParseError, ParserWorkingSet, Signature, Span, Token, + TokenContents, }; /// The syntactic shapes that values must match to be passed into a command. You can think of this as the type-checking that occurs when you call a function. @@ -1028,7 +1029,7 @@ impl<'a> ParserWorkingSet<'a> { Expression { expr: Expr::Float(x), span, - ty: Type::Int, + ty: Type::Float, }, None, ) @@ -1450,6 +1451,7 @@ impl<'a> ParserWorkingSet<'a> { error = error.or(err); let mut args: Vec = vec![]; + let mut rest: Option = None; let mut parse_mode = ParseMode::ArgMode; for token in &output { @@ -1947,6 +1949,32 @@ impl<'a> ParserWorkingSet<'a> { let (output, err) = lex(source, start, &[], &[]); error = error.or(err); + // Check to see if we have parameters + let params = if matches!( + output.first(), + Some(Token { + contents: TokenContents::Pipe, + .. + }) + ) { + // We've found a parameter list + let mut param_tokens = vec![]; + let mut token_iter = output.iter().skip(1); + for token in &mut token_iter { + if matches!( + token, + Token { + contents: TokenContents::Pipe, + .. + } + ) { + break; + } else { + param_tokens.push(token); + } + } + }; + let (output, err) = lite_parse(&output); error = error.or(err); diff --git a/crates/nu-parser/src/type_check.rs b/crates/nu-parser/src/type_check.rs index ad5dadbe72..9319e3d5f1 100644 --- a/crates/nu-parser/src/type_check.rs +++ b/crates/nu-parser/src/type_check.rs @@ -18,27 +18,11 @@ impl<'a> ParserWorkingSet<'a> { ) -> (Type, Option) { match &op.expr { Expr::Operator(operator) => match operator { - Operator::Equal => (Type::Bool, None), - Operator::Multiply => match (&lhs.ty, &rhs.ty) { - (Type::Int, Type::Int) => (Type::Int, None), - (Type::Unknown, _) => (Type::Unknown, None), - (_, Type::Unknown) => (Type::Unknown, None), - _ => { - *op = Expression::garbage(op.span); - ( - Type::Unknown, - Some(ParseError::UnsupportedOperation( - op.span, - lhs.span, - lhs.ty.clone(), - rhs.span, - rhs.ty.clone(), - )), - ) - } - }, Operator::Plus => match (&lhs.ty, &rhs.ty) { (Type::Int, Type::Int) => (Type::Int, None), + (Type::Float, Type::Int) => (Type::Float, None), + (Type::Int, Type::Float) => (Type::Float, None), + (Type::Float, Type::Float) => (Type::Float, None), (Type::String, Type::String) => (Type::String, None), (Type::Unknown, _) => (Type::Unknown, None), (_, Type::Unknown) => (Type::Unknown, None), @@ -69,6 +53,195 @@ impl<'a> ParserWorkingSet<'a> { ) } }, + Operator::Minus => match (&lhs.ty, &rhs.ty) { + (Type::Int, Type::Int) => (Type::Int, None), + (Type::Float, Type::Int) => (Type::Float, None), + (Type::Int, Type::Float) => (Type::Float, None), + (Type::Float, Type::Float) => (Type::Float, None), + (Type::Unknown, _) => (Type::Unknown, None), + (_, Type::Unknown) => (Type::Unknown, None), + _ => { + *op = Expression::garbage(op.span); + ( + Type::Unknown, + Some(ParseError::UnsupportedOperation( + op.span, + lhs.span, + lhs.ty.clone(), + rhs.span, + rhs.ty.clone(), + )), + ) + } + }, + Operator::Multiply => match (&lhs.ty, &rhs.ty) { + (Type::Int, Type::Int) => (Type::Int, None), + (Type::Float, Type::Int) => (Type::Float, None), + (Type::Int, Type::Float) => (Type::Float, None), + (Type::Float, Type::Float) => (Type::Float, None), + (Type::Unknown, _) => (Type::Unknown, None), + (_, Type::Unknown) => (Type::Unknown, None), + _ => { + *op = Expression::garbage(op.span); + ( + Type::Unknown, + Some(ParseError::UnsupportedOperation( + op.span, + lhs.span, + lhs.ty.clone(), + rhs.span, + rhs.ty.clone(), + )), + ) + } + }, + Operator::Divide => match (&lhs.ty, &rhs.ty) { + (Type::Int, Type::Int) => (Type::Int, None), + (Type::Float, Type::Int) => (Type::Float, None), + (Type::Int, Type::Float) => (Type::Float, None), + (Type::Float, Type::Float) => (Type::Float, None), + (Type::Unknown, _) => (Type::Unknown, None), + (_, Type::Unknown) => (Type::Unknown, None), + _ => { + *op = Expression::garbage(op.span); + ( + Type::Unknown, + Some(ParseError::UnsupportedOperation( + op.span, + lhs.span, + lhs.ty.clone(), + rhs.span, + rhs.ty.clone(), + )), + ) + } + }, + Operator::LessThan => match (&lhs.ty, &rhs.ty) { + (Type::Int, Type::Int) => (Type::Bool, None), + (Type::Float, Type::Int) => (Type::Bool, None), + (Type::Int, Type::Float) => (Type::Bool, None), + (Type::Float, Type::Float) => (Type::Bool, None), + (Type::Unknown, _) => (Type::Bool, None), + (_, Type::Unknown) => (Type::Bool, None), + _ => { + *op = Expression::garbage(op.span); + ( + Type::Unknown, + Some(ParseError::UnsupportedOperation( + op.span, + lhs.span, + lhs.ty.clone(), + rhs.span, + rhs.ty.clone(), + )), + ) + } + }, + Operator::LessThanOrEqual => match (&lhs.ty, &rhs.ty) { + (Type::Int, Type::Int) => (Type::Bool, None), + (Type::Float, Type::Int) => (Type::Bool, None), + (Type::Int, Type::Float) => (Type::Bool, None), + (Type::Float, Type::Float) => (Type::Bool, None), + (Type::Unknown, _) => (Type::Bool, None), + (_, Type::Unknown) => (Type::Bool, None), + _ => { + *op = Expression::garbage(op.span); + ( + Type::Unknown, + Some(ParseError::UnsupportedOperation( + op.span, + lhs.span, + lhs.ty.clone(), + rhs.span, + rhs.ty.clone(), + )), + ) + } + }, + Operator::GreaterThan => match (&lhs.ty, &rhs.ty) { + (Type::Int, Type::Int) => (Type::Bool, None), + (Type::Float, Type::Int) => (Type::Bool, None), + (Type::Int, Type::Float) => (Type::Bool, None), + (Type::Float, Type::Float) => (Type::Bool, None), + (Type::Unknown, _) => (Type::Bool, None), + (_, Type::Unknown) => (Type::Bool, None), + _ => { + *op = Expression::garbage(op.span); + ( + Type::Unknown, + Some(ParseError::UnsupportedOperation( + op.span, + lhs.span, + lhs.ty.clone(), + rhs.span, + rhs.ty.clone(), + )), + ) + } + }, + Operator::GreaterThanOrEqual => match (&lhs.ty, &rhs.ty) { + (Type::Int, Type::Int) => (Type::Bool, None), + (Type::Float, Type::Int) => (Type::Bool, None), + (Type::Int, Type::Float) => (Type::Bool, None), + (Type::Float, Type::Float) => (Type::Bool, None), + (Type::Unknown, _) => (Type::Bool, None), + (_, Type::Unknown) => (Type::Bool, None), + _ => { + *op = Expression::garbage(op.span); + ( + Type::Unknown, + Some(ParseError::UnsupportedOperation( + op.span, + lhs.span, + lhs.ty.clone(), + rhs.span, + rhs.ty.clone(), + )), + ) + } + }, + Operator::Equal => match (&lhs.ty, &rhs.ty) { + (Type::Float, Type::Int) => (Type::Bool, None), + (Type::Int, Type::Float) => (Type::Bool, None), + (x, y) if x == y => (Type::Bool, None), + (Type::Unknown, _) => (Type::Bool, None), + (_, Type::Unknown) => (Type::Bool, None), + _ => { + *op = Expression::garbage(op.span); + ( + Type::Unknown, + Some(ParseError::UnsupportedOperation( + op.span, + lhs.span, + lhs.ty.clone(), + rhs.span, + rhs.ty.clone(), + )), + ) + } + }, + Operator::NotEqual => match (&lhs.ty, &rhs.ty) { + (Type::Int, Type::Int) => (Type::Bool, None), + (Type::Float, Type::Int) => (Type::Bool, None), + (Type::Int, Type::Float) => (Type::Bool, None), + (Type::Float, Type::Float) => (Type::Bool, None), + (Type::Unknown, _) => (Type::Bool, None), + (_, Type::Unknown) => (Type::Bool, None), + _ => { + *op = Expression::garbage(op.span); + ( + Type::Unknown, + Some(ParseError::UnsupportedOperation( + op.span, + lhs.span, + lhs.ty.clone(), + rhs.span, + rhs.ty.clone(), + )), + ) + } + }, + _ => { *op = Expression::garbage(op.span);