summaryrefslogtreecommitdiff
path: root/src/eval/ops.rs
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2021-01-22 17:16:42 +0100
committerLaurenz <laurmaedje@gmail.com>2021-01-22 17:16:42 +0100
commitac788f2082711161ec8208eede04d9a2bae02241 (patch)
treeb139e41d327af906163c0b177d402b855c04507e /src/eval/ops.rs
parent0de4f3ed7bb20a94fd58f93b0793d3b5a8e13972 (diff)
Many more expressions 🥗
Boolean, equality, comparison and assignment expression parsing and evaluation.
Diffstat (limited to 'src/eval/ops.rs')
-rw-r--r--src/eval/ops.rs177
1 files changed, 103 insertions, 74 deletions
diff --git a/src/eval/ops.rs b/src/eval/ops.rs
index 0a273da5..939445f0 100644
--- a/src/eval/ops.rs
+++ b/src/eval/ops.rs
@@ -1,22 +1,21 @@
use super::*;
+use Value::*;
-/// Apply plus operator to a value.
-pub fn pos(ctx: &mut EvalContext, span: Span, value: Value) -> Value {
- if value.is_numeric() {
- value
- } else {
- ctx.diag(error!(
- span,
- "cannot apply plus operator to {}",
- value.type_name()
- ));
- Value::Error
+/// Apply the plus operator to a value.
+pub fn pos(value: Value) -> Value {
+ match value {
+ Int(v) => Int(v),
+ Float(v) => Float(v),
+ Length(v) => Length(v),
+ Angle(v) => Angle(v),
+ Relative(v) => Relative(v),
+ Linear(v) => Linear(v),
+ _ => Error,
}
}
/// Compute the negation of a value.
-pub fn neg(ctx: &mut EvalContext, span: Span, value: Value) -> Value {
- use Value::*;
+pub fn neg(value: Value) -> Value {
match value {
Int(v) => Int(-v),
Float(v) => Float(-v),
@@ -24,18 +23,13 @@ pub fn neg(ctx: &mut EvalContext, span: Span, value: Value) -> Value {
Angle(v) => Angle(-v),
Relative(v) => Relative(-v),
Linear(v) => Linear(-v),
- v => {
- ctx.diag(error!(span, "cannot negate {}", v.type_name()));
- Value::Error
- }
+ _ => Error,
}
}
/// Compute the sum of two values.
-pub fn add(ctx: &mut EvalContext, span: Span, lhs: Value, rhs: Value) -> Value {
- use Value::*;
+pub fn add(lhs: Value, rhs: Value) -> Value {
match (lhs, rhs) {
- // Numeric types to themselves.
(Int(a), Int(b)) => Int(a + b),
(Int(a), Float(b)) => Float(a as f64 + b),
(Float(a), Int(b)) => Float(a + b as f64),
@@ -50,30 +44,17 @@ pub fn add(ctx: &mut EvalContext, span: Span, lhs: Value, rhs: Value) -> Value {
(Linear(a), Length(b)) => Linear(a + b),
(Linear(a), Relative(b)) => Linear(a + b),
(Linear(a), Linear(b)) => Linear(a + b),
-
- // Complex data types to themselves.
(Str(a), Str(b)) => Str(a + &b),
(Array(a), Array(b)) => Array(concat(a, b)),
(Dict(a), Dict(b)) => Dict(concat(a, b)),
(Template(a), Template(b)) => Template(concat(a, b)),
-
- (a, b) => {
- ctx.diag(error!(
- span,
- "cannot add {} and {}",
- a.type_name(),
- b.type_name()
- ));
- Value::Error
- }
+ _ => Error,
}
}
/// Compute the difference of two values.
-pub fn sub(ctx: &mut EvalContext, span: Span, lhs: Value, rhs: Value) -> Value {
- use Value::*;
+pub fn sub(lhs: Value, rhs: Value) -> Value {
match (lhs, rhs) {
- // Numbers from themselves.
(Int(a), Int(b)) => Int(a - b),
(Int(a), Float(b)) => Float(a as f64 - b),
(Float(a), Int(b)) => Float(a - b as f64),
@@ -88,24 +69,13 @@ pub fn sub(ctx: &mut EvalContext, span: Span, lhs: Value, rhs: Value) -> Value {
(Linear(a), Length(b)) => Linear(a - b),
(Linear(a), Relative(b)) => Linear(a - b),
(Linear(a), Linear(b)) => Linear(a - b),
-
- (a, b) => {
- ctx.diag(error!(
- span,
- "cannot subtract {1} from {0}",
- a.type_name(),
- b.type_name()
- ));
- Value::Error
- }
+ _ => Error,
}
}
/// Compute the product of two values.
-pub fn mul(ctx: &mut EvalContext, span: Span, lhs: Value, rhs: Value) -> Value {
- use Value::*;
+pub fn mul(lhs: Value, rhs: Value) -> Value {
match (lhs, rhs) {
- // Numeric types with numbers.
(Int(a), Int(b)) => Int(a * b),
(Int(a), Float(b)) => Float(a as f64 * b),
(Float(a), Int(b)) => Float(a * b as f64),
@@ -126,28 +96,13 @@ pub fn mul(ctx: &mut EvalContext, span: Span, lhs: Value, rhs: Value) -> Value {
(Linear(a), Float(b)) => Linear(a * b),
(Int(a), Linear(b)) => Linear(a as f64 * b),
(Float(a), Linear(b)) => Linear(a * b),
-
- // Integers with strings.
- (Int(a), Str(b)) => Str(b.repeat(0.max(a) as usize)),
- (Str(a), Int(b)) => Str(a.repeat(0.max(b) as usize)),
-
- (a, b) => {
- ctx.diag(error!(
- span,
- "cannot multiply {} with {}",
- a.type_name(),
- b.type_name()
- ));
- Value::Error
- }
+ _ => Error,
}
}
/// Compute the quotient of two values.
-pub fn div(ctx: &mut EvalContext, span: Span, lhs: Value, rhs: Value) -> Value {
- use Value::*;
+pub fn div(lhs: Value, rhs: Value) -> Value {
match (lhs, rhs) {
- // Numeric types by numbers.
(Int(a), Int(b)) => Float(a as f64 / b as f64),
(Int(a), Float(b)) => Float(a as f64 / b),
(Float(a), Int(b)) => Float(a / b as f64),
@@ -160,19 +115,93 @@ pub fn div(ctx: &mut EvalContext, span: Span, lhs: Value, rhs: Value) -> Value {
(Relative(a), Float(b)) => Relative(a / b),
(Linear(a), Int(b)) => Linear(a / b as f64),
(Linear(a), Float(b)) => Linear(a / b),
+ _ => Error,
+ }
+}
- (a, b) => {
- ctx.diag(error!(
- span,
- "cannot divide {} by {}",
- a.type_name(),
- b.type_name()
- ));
- Value::Error
- }
+/// Compute the logical "not" of a value.
+pub fn not(value: Value) -> Value {
+ match value {
+ Bool(b) => Bool(!b),
+ _ => Error,
}
}
+/// Compute the logical "and" of two values.
+pub fn and(lhs: Value, rhs: Value) -> Value {
+ match (lhs, rhs) {
+ (Bool(a), Bool(b)) => Bool(a && b),
+ _ => Error,
+ }
+}
+
+/// Compute the logical "or" of two values.
+pub fn or(lhs: Value, rhs: Value) -> Value {
+ match (lhs, rhs) {
+ (Bool(a), Bool(b)) => Bool(a || b),
+ _ => Error,
+ }
+}
+
+/// Compute whether two values are equal.
+pub fn eq(lhs: Value, rhs: Value) -> Value {
+ Bool(value_eq(&lhs, &rhs))
+}
+
+/// Compute whether two values are equal.
+pub fn neq(lhs: Value, rhs: Value) -> Value {
+ Bool(!value_eq(&lhs, &rhs))
+}
+
+/// Recursively compute whether two values are equal.
+fn value_eq(lhs: &Value, rhs: &Value) -> bool {
+ match (lhs, rhs) {
+ (&Int(a), &Float(b)) => a as f64 == b,
+ (&Float(a), &Int(b)) => a == b as f64,
+ (&Length(a), &Linear(b)) => a == b.abs && b.rel.is_zero(),
+ (&Relative(a), &Linear(b)) => a == b.rel && b.abs.is_zero(),
+ (&Linear(a), &Length(b)) => a.abs == b && a.rel.is_zero(),
+ (&Linear(a), &Relative(b)) => a.rel == b && a.abs.is_zero(),
+ (Array(a), Array(b)) => array_eq(a, b),
+ (Dict(a), Dict(b)) => dict_eq(a, b),
+ (Template(a), Template(b)) => Span::without_cmp(|| a == b),
+ (a, b) => a == b,
+ }
+}
+
+/// Compute whether two arrays are equal.
+fn array_eq(a: &ValueArray, b: &ValueArray) -> bool {
+ a.len() == b.len() && a.iter().zip(b).all(|(x, y)| value_eq(x, y))
+}
+
+/// Compute whether two dictionaries are equal.
+fn dict_eq(a: &ValueDict, b: &ValueDict) -> bool {
+ a.len() == b.len()
+ && a.iter().all(|(k, x)| b.get(k).map_or(false, |y| value_eq(x, y)))
+}
+
+macro_rules! comparison {
+ ($name:ident, $op:tt) => {
+ /// Compute how a value compares with another value.
+ pub fn $name(lhs: Value, rhs: Value) -> Value {
+ match (lhs, rhs) {
+ (Int(a), Int(b)) => Bool(a $op b),
+ (Int(a), Float(b)) => Bool((a as f64) $op b),
+ (Float(a), Int(b)) => Bool(a $op b as f64),
+ (Float(a), Float(b)) => Bool(a $op b),
+ (Angle(a), Angle(b)) => Bool(a $op b),
+ (Length(a), Length(b)) => Bool(a $op b),
+ _ => Error,
+ }
+ }
+ };
+}
+
+comparison!(lt, <);
+comparison!(leq, <=);
+comparison!(gt, >);
+comparison!(geq, >=);
+
/// Concatenate two collections.
fn concat<T, A>(mut a: T, b: T) -> T
where