Merge pull request #127 from nushell/add_missing_operators

Add the remaining missing operators
This commit is contained in:
JT 2021-10-12 09:42:58 +13:00 committed by GitHub
commit db62bce6aa
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 257 additions and 19 deletions

View file

@ -28,11 +28,11 @@
- [x] Error shortcircuit (stopping on first error). Revised: errors emit first, but can be seen by commands.
- [x] Value serialization
- [x] Handling rows with missing columns during a cell path
- [x] finish operator type-checking
- [ ] Input/output types
- [ ] Support for `$in`
- [ ] ctrl-c support
- [ ] operator overflow
- [ ] finish operator type-checking
- [ ] Overlays (replacement for `autoenv`)
## Maybe:

View file

@ -192,7 +192,12 @@ pub fn eval_expression(
Operator::NotEqual => lhs.ne(op_span, &rhs),
Operator::In => lhs.r#in(op_span, &rhs),
Operator::NotIn => lhs.not_in(op_span, &rhs),
x => Err(ShellError::UnsupportedOperator(x, op_span)),
Operator::Contains => lhs.contains(op_span, &rhs),
Operator::NotContains => lhs.not_contains(op_span, &rhs),
Operator::Modulo => lhs.modulo(op_span, &rhs),
Operator::And => lhs.and(op_span, &rhs),
Operator::Or => lhs.or(op_span, &rhs),
Operator::Pow => lhs.pow(op_span, &rhs),
}
}
Expr::Subexpression(block_id) => {

View file

@ -86,7 +86,7 @@ pub fn math_result_type(
)
}
},
Operator::Multiply => match (&lhs.ty, &rhs.ty) {
Operator::Multiply | Operator::Pow => 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),
@ -108,7 +108,7 @@ pub fn math_result_type(
)
}
},
Operator::Divide => match (&lhs.ty, &rhs.ty) {
Operator::Divide | Operator::Modulo => 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),
@ -130,6 +130,25 @@ pub fn math_result_type(
)
}
},
Operator::And | Operator::Or => match (&lhs.ty, &rhs.ty) {
(Type::Bool, Type::Bool) => (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::LessThan => match (&lhs.ty, &rhs.ty) {
(Type::Int, Type::Int) => (Type::Bool, None),
(Type::Float, Type::Int) => (Type::Bool, None),
@ -273,6 +292,42 @@ pub fn math_result_type(
)
}
},
Operator::Contains => match (&lhs.ty, &rhs.ty) {
(Type::String, Type::String) => (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::NotContains => match (&lhs.ty, &rhs.ty) {
(Type::String, Type::String) => (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::In => match (&lhs.ty, &rhs.ty) {
(t, Type::List(u)) if type_compatible(t, u) => (Type::Bool, None),
(Type::Int | Type::Float, Type::Range) => (Type::Bool, None),
@ -317,21 +372,6 @@ pub fn math_result_type(
)
}
},
_ => {
*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);

View file

@ -872,6 +872,164 @@ impl Value {
}),
}
}
pub fn contains(&self, op: Span, rhs: &Value) -> Result<Value, ShellError> {
let span = span(&[self.span()?, rhs.span()?]);
match (self, rhs) {
(Value::String { val: lhs, .. }, Value::String { val: rhs, .. }) => Ok(Value::Bool {
val: lhs.contains(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 not_contains(&self, op: Span, rhs: &Value) -> Result<Value, ShellError> {
let span = span(&[self.span()?, rhs.span()?]);
match (self, rhs) {
(Value::String { val: lhs, .. }, Value::String { val: rhs, .. }) => Ok(Value::Bool {
val: !lhs.contains(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 modulo(&self, op: Span, rhs: &Value) -> Result<Value, ShellError> {
let span = 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 and(&self, op: Span, rhs: &Value) -> Result<Value, ShellError> {
let span = span(&[self.span()?, rhs.span()?]);
match (self, rhs) {
(Value::Bool { val: lhs, .. }, Value::Bool { 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 or(&self, op: Span, rhs: &Value) -> Result<Value, ShellError> {
let span = span(&[self.span()?, rhs.span()?]);
match (self, rhs) {
(Value::Bool { val: lhs, .. }, Value::Bool { 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 pow(&self, op: Span, rhs: &Value) -> Result<Value, ShellError> {
let span = span(&[self.span()?, rhs.span()?]);
match (self, rhs) {
(Value::Int { val: lhs, .. }, Value::Int { val: rhs, .. }) => Ok(Value::Int {
val: lhs.pow(*rhs as u32),
span,
}),
(Value::Int { val: lhs, .. }, Value::Float { val: rhs, .. }) => Ok(Value::Float {
val: (*lhs as f64).powf(*rhs),
span,
}),
(Value::Float { val: lhs, .. }, Value::Int { val: rhs, .. }) => Ok(Value::Float {
val: lhs.powf(*rhs as f64),
span,
}),
(Value::Float { val: lhs, .. }, Value::Float { val: rhs, .. }) => Ok(Value::Float {
val: lhs.powf(*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()?,
}),
}
}
}
/// Format a duration in nanoseconds into a string

View file

@ -77,6 +77,41 @@ fn broken_math() -> TestResult {
fail_test("3 + ", "incomplete")
}
#[test]
fn modulo1() -> TestResult {
run_test("5 mod 2", "1")
}
#[test]
fn modulo2() -> TestResult {
run_test("5.25 mod 2", "1.25")
}
#[test]
fn and() -> TestResult {
run_test("$true && $false", "false")
}
#[test]
fn or() -> TestResult {
run_test("$true || $false", "true")
}
#[test]
fn pow() -> TestResult {
run_test("3 ** 3", "27")
}
#[test]
fn contains() -> TestResult {
run_test("'testme' =~ 'test'", "true")
}
#[test]
fn not_contains() -> TestResult {
run_test("'testme' !~ 'test'", "false")
}
#[test]
fn if_test1() -> TestResult {
run_test("if $true { 10 } else { 20 } ", "10")