mirror of
https://github.com/typst/typst
synced 2025-05-14 04:56:26 +08:00
Expression evaluation with Eval trait 🧮
This commit is contained in:
parent
c1dd872b34
commit
a41d7ab47d
@ -22,6 +22,7 @@ opt-level = 2
|
|||||||
lto = true
|
lto = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
async-trait = "0.1"
|
||||||
fontdock = { path = "../fontdock", default-features = false }
|
fontdock = { path = "../fontdock", default-features = false }
|
||||||
kurbo = "0.6.3"
|
kurbo = "0.6.3"
|
||||||
tide = { path = "../tide" }
|
tide = { path = "../tide" }
|
||||||
|
285
src/eval/mod.rs
285
src/eval/mod.rs
@ -9,3 +9,288 @@ pub use dict::*;
|
|||||||
pub use scope::*;
|
pub use scope::*;
|
||||||
pub use state::*;
|
pub use state::*;
|
||||||
pub use value::*;
|
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
|
||||||
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
//! Layouting of syntax trees.
|
//! Layouting of syntax trees.
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use crate::eval::Eval;
|
||||||
use crate::shaping;
|
use crate::shaping;
|
||||||
use crate::syntax::{
|
use crate::syntax::{
|
||||||
Decoration, Expr, NodeHeading, NodeRaw, Span, SpanWith, Spanned, SynNode, SynTree,
|
Decoration, Expr, NodeHeading, NodeRaw, Span, SpanWith, Spanned, SynNode, SynTree,
|
||||||
|
@ -1,9 +1,6 @@
|
|||||||
//! Expressions.
|
//! Expressions.
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::eval::Value;
|
|
||||||
use crate::layout::LayoutContext;
|
|
||||||
use crate::DynFuture;
|
|
||||||
|
|
||||||
/// An expression.
|
/// An expression.
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
@ -18,20 +15,6 @@ pub enum Expr {
|
|||||||
Binary(ExprBinary),
|
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(...)`.
|
/// An invocation of a function: `[foo: ...]`, `foo(...)`.
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub struct ExprCall {
|
pub struct ExprCall {
|
||||||
@ -41,26 +24,6 @@ pub struct ExprCall {
|
|||||||
pub args: LitDict,
|
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`.
|
/// A unary operation: `-x`.
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub struct ExprUnary {
|
pub struct ExprUnary {
|
||||||
@ -70,33 +33,6 @@ pub struct ExprUnary {
|
|||||||
pub expr: Spanned<Box<Expr>>,
|
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.
|
/// A unary operator.
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||||
pub enum UnOp {
|
pub enum UnOp {
|
||||||
@ -115,141 +51,6 @@ pub struct ExprBinary {
|
|||||||
pub rhs: Spanned<Box<Expr>>,
|
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.
|
/// A binary operator.
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||||
pub enum BinOp {
|
pub enum BinOp {
|
||||||
|
@ -2,10 +2,8 @@
|
|||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::color::RgbaColor;
|
use crate::color::RgbaColor;
|
||||||
use crate::eval::{DictKey, SpannedEntry, Value, ValueDict};
|
use crate::eval::DictKey;
|
||||||
use crate::layout::LayoutContext;
|
|
||||||
use crate::length::Length;
|
use crate::length::Length;
|
||||||
use crate::DynFuture;
|
|
||||||
|
|
||||||
/// A literal.
|
/// A literal.
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
@ -37,54 +35,10 @@ pub enum Lit {
|
|||||||
Content(SynTree),
|
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")`.
|
/// A dictionary literal: `(false, 12cm, greeting = "hi")`.
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub struct LitDict(pub Vec<LitDictEntry>);
|
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"`.
|
/// An entry in a dictionary literal: `false` or `greeting = "hi"`.
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub struct LitDictEntry {
|
pub struct LitDictEntry {
|
||||||
@ -93,3 +47,10 @@ pub struct LitDictEntry {
|
|||||||
/// The value of the entry: `"hi"`.
|
/// The value of the entry: `"hi"`.
|
||||||
pub expr: Spanned<Expr>,
|
pub expr: Spanned<Expr>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl LitDict {
|
||||||
|
/// Create an empty dict literal.
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self(vec![])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user