summaryrefslogtreecommitdiff
path: root/src/eval
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2020-10-04 20:07:01 +0200
committerLaurenz <laurmaedje@gmail.com>2020-10-04 20:07:01 +0200
commita41d7ab47dda1e30465bdf91fd02bca0e634a38d (patch)
tree981f115f6cd87a4906bee058f7c80e1a200c2770 /src/eval
parentc1dd872b34507a9f45b39a8a6ac70606b642a19d (diff)
Expression evaluation with Eval trait 🧮
Diffstat (limited to 'src/eval')
-rw-r--r--src/eval/mod.rs285
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
+}