From a41d7ab47dda1e30465bdf91fd02bca0e634a38d Mon Sep 17 00:00:00 2001 From: Laurenz Date: Sun, 4 Oct 2020 20:07:01 +0200 Subject: [PATCH] =?UTF-8?q?Expression=20evaluation=20with=20Eval=20trait?= =?UTF-8?q?=20=F0=9F=A7=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cargo.toml | 1 + src/eval/mod.rs | 285 +++++++++++++++++++++++++++++++++++++++++ src/layout/tree.rs | 1 + src/syntax/ast/expr.rs | 199 ---------------------------- src/syntax/ast/lit.rs | 55 ++------ 5 files changed, 295 insertions(+), 246 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d1f7f9463..a9d8011dd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,6 +22,7 @@ opt-level = 2 lto = true [dependencies] +async-trait = "0.1" fontdock = { path = "../fontdock", default-features = false } kurbo = "0.6.3" tide = { path = "../tide" } diff --git a/src/eval/mod.rs b/src/eval/mod.rs index d21fbc7ff..dc35ce714 100644 --- a/src/eval/mod.rs +++ b/src/eval/mod.rs @@ -9,3 +9,288 @@ pub use dict::*; pub use scope::*; pub use state::*; pub use value::*; + +use async_trait::async_trait; + +use crate::layout::LayoutContext; +use crate::syntax::*; + +/// Evaluate an syntactic item into an output value. +/// +/// _Note_: Evaluation is not necessarily pure, it may change the active state. +#[async_trait(?Send)] +pub trait Eval { + /// The output of evaluating the item. + type Output; + + /// Evaluate the item to the output value. + async fn eval(&self, ctx: &mut LayoutContext) -> Self::Output; +} + +#[async_trait(?Send)] +impl Eval for Expr { + type Output = Value; + + async fn eval(&self, ctx: &mut LayoutContext) -> Self::Output { + match self { + Self::Lit(lit) => lit.eval(ctx).await, + Self::Call(call) => call.eval(ctx).await, + Self::Unary(unary) => unary.eval(ctx).await, + Self::Binary(binary) => binary.eval(ctx).await, + } + } +} + +#[async_trait(?Send)] +impl Eval for Lit { + type Output = Value; + + async fn eval(&self, ctx: &mut LayoutContext) -> Self::Output { + match *self { + Lit::Ident(ref v) => Value::Ident(v.clone()), + Lit::Bool(v) => Value::Bool(v), + Lit::Int(v) => Value::Int(v), + Lit::Float(v) => Value::Float(v), + Lit::Length(v) => Value::Length(v.as_raw()), + Lit::Percent(v) => Value::Relative(v / 100.0), + Lit::Color(v) => Value::Color(v), + Lit::Str(ref v) => Value::Str(v.clone()), + Lit::Dict(ref v) => Value::Dict(v.eval(ctx).await), + Lit::Content(ref v) => Value::Content(v.clone()), + } + } +} +#[async_trait(?Send)] +impl Eval for LitDict { + type Output = ValueDict; + + async fn eval(&self, ctx: &mut LayoutContext) -> Self::Output { + let mut dict = ValueDict::new(); + + for entry in &self.0 { + let val = entry.expr.v.eval(ctx).await; + let spanned = val.span_with(entry.expr.span); + if let Some(key) = &entry.key { + dict.insert(&key.v, SpannedEntry::new(key.span, spanned)); + } else { + dict.push(SpannedEntry::value(spanned)); + } + } + + dict + } +} + +#[async_trait(?Send)] +impl Eval for ExprCall { + type Output = Value; + + async fn eval(&self, ctx: &mut LayoutContext) -> Self::Output { + let name = &self.name.v; + let span = self.name.span; + let args = self.args.eval(ctx).await; + + if let Some(func) = ctx.state.scope.func(name) { + ctx.f.decorations.push(Decoration::Resolved.span_with(span)); + (func.clone())(args, ctx).await + } else { + if !name.is_empty() { + error!(@ctx.f, span, "unknown function"); + ctx.f.decorations.push(Decoration::Unresolved.span_with(span)); + } + Value::Dict(args) + } + } +} + +#[async_trait(?Send)] +impl Eval for ExprUnary { + type Output = Value; + + async fn eval(&self, ctx: &mut LayoutContext) -> Self::Output { + use Value::*; + + let value = self.expr.v.eval(ctx).await; + if value == Error { + return Error; + } + + let span = self.op.span.join(self.expr.span); + match self.op.v { + UnOp::Neg => neg(ctx, span, value), + } + } +} + +#[async_trait(?Send)] +impl Eval for ExprBinary { + type Output = Value; + + async fn eval(&self, ctx: &mut LayoutContext) -> Self::Output { + let lhs = self.lhs.v.eval(ctx).await; + let rhs = self.rhs.v.eval(ctx).await; + + if lhs == Value::Error || rhs == Value::Error { + return Value::Error; + } + + let span = self.lhs.span.join(self.rhs.span); + match self.op.v { + BinOp::Add => add(ctx, span, lhs, rhs), + BinOp::Sub => sub(ctx, span, lhs, rhs), + BinOp::Mul => mul(ctx, span, lhs, rhs), + BinOp::Div => div(ctx, span, lhs, rhs), + } + } +} + +/// Compute the negation of a value. +fn neg(ctx: &mut LayoutContext, span: Span, value: Value) -> Value { + use Value::*; + match value { + Int(v) => Int(-v), + Float(v) => Float(-v), + Length(v) => Length(-v), + Relative(v) => Relative(-v), + Linear(v) => Linear(-v), + v => { + error!(@ctx.f, span, "cannot negate {}", v.ty()); + Value::Error + } + } +} + +/// Compute the sum of two values. +fn add(ctx: &mut LayoutContext, span: Span, lhs: Value, rhs: Value) -> Value { + use crate::geom::Linear as Lin; + use Value::*; + match (lhs, rhs) { + // Numbers 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), + (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)), + (Content(a), Content(b)) => Content(concat(a, b)), + (Commands(a), Commands(b)) => Commands(concat(a, b)), + + (a, b) => { + error!(@ctx.f, span, "cannot add {} and {}", a.ty(), b.ty()); + Value::Error + } + } +} + +/// Compute the difference of two values. +fn sub(ctx: &mut LayoutContext, span: Span, lhs: Value, rhs: Value) -> Value { + use crate::geom::Linear as Lin; + use 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), + (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!(@ctx.f, span, "cannot subtract {1} from {0}", a.ty(), b.ty()); + Value::Error + } + } +} + +/// Compute the product of two values. +fn mul(ctx: &mut LayoutContext, span: Span, lhs: Value, rhs: Value) -> Value { + use Value::*; + 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!(@ctx.f, span, "cannot multiply {} with {}", a.ty(), b.ty()); + Value::Error + } + } +} + +/// Compute the quotient of two values. +fn div(ctx: &mut LayoutContext, span: Span, lhs: Value, rhs: Value) -> Value { + use Value::*; + 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!(@ctx.f, span, "cannot divide {} by {}", a.ty(), b.ty()); + Value::Error + } + } +} + +/// Concatenate two collections. +fn concat(mut a: T, b: T) -> T +where + T: Extend + IntoIterator, +{ + a.extend(b); + a +} diff --git a/src/layout/tree.rs b/src/layout/tree.rs index 791bf14bc..4dbf716ef 100644 --- a/src/layout/tree.rs +++ b/src/layout/tree.rs @@ -1,6 +1,7 @@ //! Layouting of syntax trees. use super::*; +use crate::eval::Eval; use crate::shaping; use crate::syntax::{ Decoration, Expr, NodeHeading, NodeRaw, Span, SpanWith, Spanned, SynNode, SynTree, diff --git a/src/syntax/ast/expr.rs b/src/syntax/ast/expr.rs index 718b0568c..4f440d339 100644 --- a/src/syntax/ast/expr.rs +++ b/src/syntax/ast/expr.rs @@ -1,9 +1,6 @@ //! Expressions. use super::*; -use crate::eval::Value; -use crate::layout::LayoutContext; -use crate::DynFuture; /// An expression. #[derive(Debug, Clone, PartialEq)] @@ -18,20 +15,6 @@ pub enum Expr { Binary(ExprBinary), } -impl Expr { - /// Evaluate the expression to a value. - pub fn eval<'a>(&'a self, ctx: &'a mut LayoutContext) -> DynFuture<'a, Value> { - Box::pin(async move { - match self { - Self::Lit(lit) => lit.eval(ctx).await, - Self::Call(call) => call.eval(ctx).await, - Self::Unary(unary) => unary.eval(ctx).await, - Self::Binary(binary) => binary.eval(ctx).await, - } - }) - } -} - /// An invocation of a function: `[foo: ...]`, `foo(...)`. #[derive(Debug, Clone, PartialEq)] pub struct ExprCall { @@ -41,26 +24,6 @@ pub struct ExprCall { pub args: LitDict, } -impl ExprCall { - /// Evaluate the call expression to a value. - pub async fn eval(&self, ctx: &mut LayoutContext) -> Value { - let name = &self.name.v; - let span = self.name.span; - let args = self.args.eval(ctx).await; - - if let Some(func) = ctx.state.scope.func(name) { - ctx.f.decorations.push(Decoration::Resolved.span_with(span)); - (func.clone())(args, ctx).await - } else { - if !name.is_empty() { - error!(@ctx.f, span, "unknown function"); - ctx.f.decorations.push(Decoration::Unresolved.span_with(span)); - } - Value::Dict(args) - } - } -} - /// A unary operation: `-x`. #[derive(Debug, Clone, PartialEq)] pub struct ExprUnary { @@ -70,33 +33,6 @@ pub struct ExprUnary { pub expr: Spanned>, } -impl ExprUnary { - /// Evaluate the expression to a value. - pub async fn eval(&self, ctx: &mut LayoutContext) -> Value { - use Value::*; - - let value = self.expr.v.eval(ctx).await; - if value == Error { - return Error; - } - - let span = self.op.span.join(self.expr.span); - match self.op.v { - UnOp::Neg => match value { - Int(v) => Int(-v), - Float(v) => Float(-v), - Length(v) => Length(-v), - Relative(v) => Relative(-v), - Linear(v) => Linear(-v), - v => { - error!(@ctx.f, span, "cannot negate {}", v.ty()); - Value::Error - } - }, - } - } -} - /// A unary operator. #[derive(Debug, Copy, Clone, Eq, PartialEq)] pub enum UnOp { @@ -115,141 +51,6 @@ pub struct ExprBinary { pub rhs: Spanned>, } -impl ExprBinary { - /// Evaluate the expression to a value. - pub async fn eval(&self, ctx: &mut LayoutContext) -> Value { - use crate::geom::Linear as Lin; - use Value::*; - - let lhs = self.lhs.v.eval(ctx).await; - let rhs = self.rhs.v.eval(ctx).await; - - if lhs == Error || rhs == Error { - return Error; - } - - let span = self.lhs.span.join(self.rhs.span); - match self.op.v { - BinOp::Add => match (lhs, rhs) { - // Numbers 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), - (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)), - (Content(a), Content(b)) => Content(concat(a, b)), - (Commands(a), Commands(b)) => Commands(concat(a, b)), - - (a, b) => { - error!(@ctx.f, span, "cannot add {} and {}", a.ty(), b.ty()); - 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!(@ctx.f, span, "cannot subtract {1} from {0}", a.ty(), b.ty()); - 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!(@ctx.f, span, "cannot multiply {} with {}", a.ty(), b.ty()); - 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!(@ctx.f, span, "cannot divide {} by {}", a.ty(), b.ty()); - 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 414d54900..31566e896 100644 --- a/src/syntax/ast/lit.rs +++ b/src/syntax/ast/lit.rs @@ -2,10 +2,8 @@ use super::*; use crate::color::RgbaColor; -use crate::eval::{DictKey, SpannedEntry, Value, ValueDict}; -use crate::layout::LayoutContext; +use crate::eval::DictKey; use crate::length::Length; -use crate::DynFuture; /// A literal. #[derive(Debug, Clone, PartialEq)] @@ -37,54 +35,10 @@ pub enum Lit { Content(SynTree), } -impl Lit { - /// Evaluate the dictionary literal to a dictionary value. - pub async fn eval(&self, ctx: &mut LayoutContext) -> Value { - match *self { - Lit::Ident(ref v) => Value::Ident(v.clone()), - Lit::Bool(v) => Value::Bool(v), - Lit::Int(v) => Value::Int(v), - Lit::Float(v) => Value::Float(v), - Lit::Length(v) => Value::Length(v.as_raw()), - Lit::Percent(v) => Value::Relative(v / 100.0), - Lit::Color(v) => Value::Color(v), - Lit::Str(ref v) => Value::Str(v.clone()), - Lit::Dict(ref v) => Value::Dict(v.eval(ctx).await), - Lit::Content(ref v) => Value::Content(v.clone()), - } - } -} - /// A dictionary literal: `(false, 12cm, greeting = "hi")`. #[derive(Debug, Clone, PartialEq)] pub struct LitDict(pub Vec); -impl LitDict { - /// Create an empty dict literal. - pub fn new() -> Self { - Self(vec![]) - } - - /// Evaluate the dictionary literal to a dictionary value. - pub fn eval<'a>(&'a self, ctx: &'a mut LayoutContext) -> DynFuture<'a, ValueDict> { - Box::pin(async move { - let mut dict = ValueDict::new(); - - for entry in &self.0 { - let val = entry.expr.v.eval(ctx).await; - let spanned = val.span_with(entry.expr.span); - if let Some(key) = &entry.key { - dict.insert(&key.v, SpannedEntry::new(key.span, spanned)); - } else { - dict.push(SpannedEntry::value(spanned)); - } - } - - dict - }) - } -} - /// An entry in a dictionary literal: `false` or `greeting = "hi"`. #[derive(Debug, Clone, PartialEq)] pub struct LitDictEntry { @@ -93,3 +47,10 @@ pub struct LitDictEntry { /// The value of the entry: `"hi"`. pub expr: Spanned, } + +impl LitDict { + /// Create an empty dict literal. + pub fn new() -> Self { + Self(vec![]) + } +}