diff options
| author | Laurenz <laurmaedje@gmail.com> | 2020-10-04 20:07:01 +0200 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2020-10-04 20:07:01 +0200 |
| commit | a41d7ab47dda1e30465bdf91fd02bca0e634a38d (patch) | |
| tree | 981f115f6cd87a4906bee058f7c80e1a200c2770 /src/eval | |
| parent | c1dd872b34507a9f45b39a8a6ac70606b642a19d (diff) | |
Expression evaluation with Eval trait 🧮
Diffstat (limited to 'src/eval')
| -rw-r--r-- | src/eval/mod.rs | 285 |
1 files changed, 285 insertions, 0 deletions
diff --git a/src/eval/mod.rs b/src/eval/mod.rs index d21fbc7f..dc35ce71 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<T, A>(mut a: T, b: T) -> T +where + T: Extend<A> + IntoIterator<Item = A>, +{ + a.extend(b); + a +} |
