summaryrefslogtreecommitdiff
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
parentc1dd872b34507a9f45b39a8a6ac70606b642a19d (diff)
Expression evaluation with Eval trait 🧮
-rw-r--r--Cargo.toml1
-rw-r--r--src/eval/mod.rs285
-rw-r--r--src/layout/tree.rs1
-rw-r--r--src/syntax/ast/expr.rs199
-rw-r--r--src/syntax/ast/lit.rs55
5 files changed, 295 insertions, 246 deletions
diff --git a/Cargo.toml b/Cargo.toml
index d1f7f946..a9d8011d 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 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
+}
diff --git a/src/layout/tree.rs b/src/layout/tree.rs
index 791bf14b..4dbf716e 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 718b0568..4f440d33 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<Box<Expr>>,
}
-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<Box<Expr>>,
}
-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<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 414d5490..31566e89 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<LitDictEntry>);
-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<Expr>,
}
+
+impl LitDict {
+ /// Create an empty dict literal.
+ pub fn new() -> Self {
+ Self(vec![])
+ }
+}