From 91d14d2a221f619738e4534c1b4266633ce5789d Mon Sep 17 00:00:00 2001 From: Laurenz Date: Sat, 3 Oct 2020 17:56:56 +0200 Subject: =?UTF-8?q?Evaluate=20expressions=20=F0=9F=A7=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/syntax/ast/expr.rs | 176 ++++++++++++++++++++++++++++++++++++++++++++----- src/syntax/ast/lit.rs | 6 +- 2 files changed, 162 insertions(+), 20 deletions(-) (limited to 'src/syntax') 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(mut a: T, b: T) -> T +where + T: Extend + IntoIterator, +{ + 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), -- cgit v1.2.3