summaryrefslogtreecommitdiff
path: root/src/syntax
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2020-10-03 17:56:56 +0200
committerLaurenz <laurmaedje@gmail.com>2020-10-03 17:56:56 +0200
commit91d14d2a221f619738e4534c1b4266633ce5789d (patch)
treec10b288b9473153fede116204cc861c89dad71a1 /src/syntax
parent95bae5725cf6495644e2593f8492f1cd0e5bd3c1 (diff)
Evaluate expressions 🧮
Diffstat (limited to 'src/syntax')
-rw-r--r--src/syntax/ast/expr.rs176
-rw-r--r--src/syntax/ast/lit.rs6
2 files changed, 162 insertions, 20 deletions
diff --git a/src/syntax/ast/expr.rs b/src/syntax/ast/expr.rs
index c07c6216..4f067145 100644
--- a/src/syntax/ast/expr.rs
+++ b/src/syntax/ast/expr.rs
@@ -3,7 +3,7 @@
use crate::eval::Value;
use crate::layout::LayoutContext;
use crate::syntax::{Decoration, Ident, Lit, LitDict, SpanWith, Spanned};
-use crate::Feedback;
+use crate::{DynFuture, Feedback};
/// An expression.
#[derive(Debug, Clone, PartialEq)]
@@ -20,13 +20,19 @@ pub enum Expr {
impl Expr {
/// Evaluate the expression to a value.
- pub async fn eval(&self, ctx: &LayoutContext<'_>, f: &mut Feedback) -> Value {
- match self {
- Self::Lit(lit) => lit.eval(ctx, f).await,
- Self::Unary(unary) => unary.eval(ctx, f).await,
- Self::Binary(binary) => binary.eval(ctx, f).await,
- Self::Call(call) => call.eval(ctx, f).await,
- }
+ pub fn eval<'a>(
+ &'a self,
+ ctx: &'a LayoutContext<'a>,
+ f: &'a mut Feedback,
+ ) -> DynFuture<'a, Value> {
+ Box::pin(async move {
+ match self {
+ Self::Lit(lit) => lit.eval(ctx, f).await,
+ Self::Unary(unary) => unary.eval(ctx, f).await,
+ Self::Binary(binary) => binary.eval(ctx, f).await,
+ Self::Call(call) => call.eval(ctx, f).await,
+ }
+ })
}
}
@@ -41,9 +47,27 @@ pub struct ExprUnary {
impl ExprUnary {
/// Evaluate the expression to a value.
- pub async fn eval(&self, _: &LayoutContext<'_>, _: &mut Feedback) -> Value {
+ pub async fn eval(&self, ctx: &LayoutContext<'_>, f: &mut Feedback) -> Value {
+ use Value::*;
+
+ let value = self.expr.v.eval(ctx, f).await;
+ if value == Error {
+ return Error;
+ }
+
+ let span = self.op.span.join(self.expr.span);
match self.op.v {
- UnOp::Neg => todo!("eval neg"),
+ UnOp::Neg => match value {
+ Int(x) => Int(-x),
+ Float(x) => Float(-x),
+ Length(x) => Length(-x),
+ Relative(x) => Relative(-x),
+ Linear(x) => Linear(-x),
+ v => {
+ error!(@f, span, "cannot negate {}", v.name());
+ Value::Error
+ }
+ },
}
}
}
@@ -68,16 +92,138 @@ pub struct ExprBinary {
impl ExprBinary {
/// Evaluate the expression to a value.
- pub async fn eval(&self, _: &LayoutContext<'_>, _: &mut Feedback) -> Value {
+ pub async fn eval(&self, ctx: &LayoutContext<'_>, f: &mut Feedback) -> Value {
+ use crate::geom::Linear as Lin;
+ use Value::*;
+
+ let lhs = self.lhs.v.eval(ctx, f).await;
+ let rhs = self.rhs.v.eval(ctx, f).await;
+
+ if lhs == Error || rhs == Error {
+ return Error;
+ }
+
+ let span = self.lhs.span.join(self.rhs.span);
match self.op.v {
- BinOp::Add => todo!("eval add"),
- BinOp::Sub => todo!("eval sub"),
- BinOp::Mul => todo!("eval mul"),
- BinOp::Div => todo!("eval div"),
+ BinOp::Add => match (lhs, rhs) {
+ // Numbers to themselves.
+ (Int(a), Int(b)) => Int(a + b),
+ (Int(i), Float(f)) | (Float(f), Int(i)) => Float(i as f64 + f),
+ (Float(a), Float(b)) => Float(a + b),
+
+ // Lengths, relatives and linears to themselves.
+ (Length(a), Length(b)) => Length(a + b),
+ (Length(a), Relative(b)) => Linear(Lin::abs(a) + Lin::rel(b)),
+ (Length(a), Linear(b)) => Linear(Lin::abs(a) + b),
+
+ (Relative(a), Length(b)) => Linear(Lin::rel(a) + Lin::abs(b)),
+ (Relative(a), Relative(b)) => Relative(a + b),
+ (Relative(a), Linear(b)) => Linear(Lin::rel(a) + b),
+
+ (Linear(a), Length(b)) => Linear(a + Lin::abs(b)),
+ (Linear(a), Relative(b)) => Linear(a + Lin::rel(b)),
+ (Linear(a), Linear(b)) => Linear(a + b),
+
+ // Complex data types to themselves.
+ (Str(a), Str(b)) => Str(a + &b),
+ (Dict(a), Dict(b)) => Dict(concat(a, b)),
+ (Tree(a), Tree(b)) => Tree(concat(a, b)),
+ (Commands(a), Commands(b)) => Commands(concat(a, b)),
+
+ (a, b) => {
+ error!(@f, span, "cannot add {} and {}", a.name(), b.name());
+ Value::Error
+ }
+ },
+
+ BinOp::Sub => 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),
+ (Float(a), Float(b)) => Float(a - b),
+
+ // Lengths, relatives and linears from themselves.
+ (Length(a), Length(b)) => Length(a - b),
+ (Length(a), Relative(b)) => Linear(Lin::abs(a) - Lin::rel(b)),
+ (Length(a), Linear(b)) => Linear(Lin::abs(a) - b),
+ (Relative(a), Length(b)) => Linear(Lin::rel(a) - Lin::abs(b)),
+ (Relative(a), Relative(b)) => Relative(a - b),
+ (Relative(a), Linear(b)) => Linear(Lin::rel(a) - b),
+ (Linear(a), Length(b)) => Linear(a - Lin::abs(b)),
+ (Linear(a), Relative(b)) => Linear(a - Lin::rel(b)),
+ (Linear(a), Linear(b)) => Linear(a - b),
+
+ (a, b) => {
+ error!(@f, span, "cannot subtract {1} from {0}", a.name(), b.name());
+ Value::Error
+ }
+ },
+
+ BinOp::Mul => match (lhs, rhs) {
+ // Numbers with 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),
+ (Float(a), Float(b)) => Float(a * b),
+
+ // Lengths, relatives and linears with numbers.
+ (Length(a), Int(b)) => Length(a * b as f64),
+ (Length(a), Float(b)) => Length(a * b),
+ (Int(a), Length(b)) => Length(a as f64 * b),
+ (Float(a), Length(b)) => Length(a * b),
+ (Relative(a), Int(b)) => Relative(a * b as f64),
+ (Relative(a), Float(b)) => Relative(a * b),
+ (Int(a), Relative(b)) => Relative(a as f64 * b),
+ (Float(a), Relative(b)) => Relative(a * b),
+ (Linear(a), Int(b)) => Linear(a * b as f64),
+ (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(a.max(0) as usize)),
+ (Str(a), Int(b)) => Str(a.repeat(b.max(0) as usize)),
+
+ (a, b) => {
+ error!(@f, span, "cannot multiply {} with {}", a.name(), b.name());
+ Value::Error
+ }
+ },
+
+ BinOp::Div => match (lhs, rhs) {
+ // Numbers by themselves.
+ (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),
+ (Float(a), Float(b)) => Float(a / b),
+
+ // Lengths by numbers.
+ (Length(a), Int(b)) => Length(a / b as f64),
+ (Length(a), Float(b)) => Length(a / b),
+ (Relative(a), Int(b)) => Relative(a / b as f64),
+ (Relative(a), Float(b)) => Relative(a / b),
+ (Linear(a), Int(b)) => Linear(a / b as f64),
+ (Linear(a), Float(b)) => Linear(a / b),
+
+ (a, b) => {
+ error!(@f, span, "cannot divide {} by {}", a.name(), b.name());
+ Value::Error
+ }
+ },
}
}
}
+/// Concatenate two collections.
+fn concat<T, A>(mut a: T, b: T) -> T
+where
+ T: Extend<A> + IntoIterator<Item = A>,
+{
+ a.extend(b);
+ a
+}
+
/// A binary operator.
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum BinOp {
diff --git a/src/syntax/ast/lit.rs b/src/syntax/ast/lit.rs
index ba7e7e4f..c08dc8cd 100644
--- a/src/syntax/ast/lit.rs
+++ b/src/syntax/ast/lit.rs
@@ -39,11 +39,7 @@ pub enum Lit {
impl Lit {
/// Evaluate the dictionary literal to a dictionary value.
- pub async fn eval<'a>(
- &'a self,
- ctx: &'a LayoutContext<'a>,
- f: &'a mut Feedback,
- ) -> Value {
+ pub async fn eval(&self, ctx: &LayoutContext<'_>, f: &mut Feedback) -> Value {
match *self {
Lit::Ident(ref i) => Value::Ident(i.clone()),
Lit::Bool(b) => Value::Bool(b),