summaryrefslogtreecommitdiff
path: root/src/eval
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2020-10-07 17:07:44 +0200
committerLaurenz <laurmaedje@gmail.com>2020-10-07 17:07:44 +0200
commit537545e7f8351d7677c396456e46568f5a5e2a7a (patch)
treef4c7614293246db06c7fa7496458da01b15c3b84 /src/eval
parentca1256c924f3672feb76dbc2bc2e309eb4fc4cf5 (diff)
Evaluation and node-based layouting 🚀
Diffstat (limited to 'src/eval')
-rw-r--r--src/eval/args.rs9
-rw-r--r--src/eval/mod.rs376
-rw-r--r--src/eval/state.rs23
-rw-r--r--src/eval/value.rs71
4 files changed, 383 insertions, 96 deletions
diff --git a/src/eval/args.rs b/src/eval/args.rs
index d11deac6..04f83b50 100644
--- a/src/eval/args.rs
+++ b/src/eval/args.rs
@@ -2,8 +2,7 @@
use std::mem;
-use super::{Convert, RefKey, ValueDict};
-use crate::layout::LayoutContext;
+use super::{Convert, EvalContext, RefKey, ValueDict};
use crate::syntax::{SpanWith, Spanned};
/// A wrapper around a dictionary value that simplifies argument parsing in
@@ -16,7 +15,7 @@ impl Args {
///
/// Generates an error if the key exists, but the value can't be converted
/// into the type `T`.
- pub fn get<'a, K, T>(&mut self, ctx: &mut LayoutContext, key: K) -> Option<T>
+ pub fn get<'a, K, T>(&mut self, ctx: &mut EvalContext, key: K) -> Option<T>
where
K: Into<RefKey<'a>>,
T: Convert,
@@ -37,7 +36,7 @@ impl Args {
/// [`get`]: #method.get
pub fn need<'a, K, T>(
&mut self,
- ctx: &mut LayoutContext,
+ ctx: &mut EvalContext,
key: K,
name: &str,
) -> Option<T>
@@ -126,7 +125,7 @@ impl Args {
}
/// Generated _unexpected argument_ errors for all remaining entries.
- pub fn done(&self, ctx: &mut LayoutContext) {
+ pub fn done(&self, ctx: &mut EvalContext) {
for entry in self.0.v.values() {
let span = entry.key_span.join(entry.value.span);
ctx.diag(error!(span, "unexpected argument"));
diff --git a/src/eval/mod.rs b/src/eval/mod.rs
index 6f882ab8..101d26d9 100644
--- a/src/eval/mod.rs
+++ b/src/eval/mod.rs
@@ -14,42 +14,355 @@ pub use scope::*;
pub use state::*;
pub use value::*;
-use async_trait::async_trait;
+use std::any::Any;
+use std::mem;
+use std::rc::Rc;
-use crate::layout::LayoutContext;
+use async_trait::async_trait;
+use fontdock::FontStyle;
+
+use crate::diag::Diag;
+use crate::geom::Size;
+use crate::layout::nodes::{
+ Document, LayoutNode, Pad, Pages, Par, Softness, Spacing, Stack, Text,
+};
+use crate::layout::{Gen2, Spec2, Switch};
use crate::syntax::*;
+use crate::{Feedback, Pass};
-/// Evaluate an syntactic item into an output value.
+/// Evaluate a syntax tree into a document.
+///
+/// The given `state` the base state that may be updated over the course of
+/// evaluation.
+pub fn eval(tree: &SynTree, state: State) -> Pass<Document> {
+ let mut ctx = EvalContext::new(state);
+
+ ctx.start_page_group(false);
+ tree.eval(&mut ctx);
+ ctx.end_page_group();
+
+ ctx.finish()
+}
+
+/// The context for evaluation.
+#[derive(Debug)]
+pub struct EvalContext {
+ /// The active evaluation state.
+ pub state: State,
+ /// The accumualted feedback.
+ f: Feedback,
+ /// The finished page runs.
+ runs: Vec<Pages>,
+ /// The stack of logical groups (paragraphs and such).
+ ///
+ /// Each entry contains metadata about the group and nodes that are at the
+ /// same level as the group, which will return to `inner` once the group is
+ /// finished.
+ groups: Vec<(Box<dyn Any>, Vec<LayoutNode>)>,
+ /// The nodes in the current innermost group
+ /// (whose metadata is in `groups.last()`).
+ inner: Vec<LayoutNode>,
+}
+
+impl EvalContext {
+ /// Create a new evaluation context with a base state.
+ pub fn new(state: State) -> Self {
+ Self {
+ state,
+ groups: vec![],
+ inner: vec![],
+ runs: vec![],
+ f: Feedback::new(),
+ }
+ }
+
+ /// Finish evaluation and return the created document.
+ pub fn finish(self) -> Pass<Document> {
+ assert!(self.groups.is_empty(), "unpoped group");
+ Pass::new(Document { runs: self.runs }, self.f)
+ }
+
+ /// Add a diagnostic to the feedback.
+ pub fn diag(&mut self, diag: Spanned<Diag>) {
+ self.f.diags.push(diag);
+ }
+
+ /// Add a decoration to the feedback.
+ pub fn deco(&mut self, deco: Spanned<Deco>) {
+ self.f.decos.push(deco);
+ }
+
+ /// Push a layout node to the active group.
+ ///
+ /// Spacing nodes will be handled according to their [`Softness`].
+ ///
+ /// [`Softness`]: ../layout/nodes/enum.Softness.html
+ pub fn push(&mut self, node: impl Into<LayoutNode>) {
+ let node = node.into();
+
+ if let LayoutNode::Spacing(this) = node {
+ if this.softness == Softness::Soft && self.inner.is_empty() {
+ return;
+ }
+
+ if let Some(&LayoutNode::Spacing(other)) = self.inner.last() {
+ if this.softness > other.softness {
+ self.inner.pop();
+ } else if this.softness == Softness::Soft {
+ return;
+ }
+ }
+ }
+
+ self.inner.push(node);
+ }
+
+ /// Start a layouting group.
+ ///
+ /// All further calls to [`push`] will collect nodes for this group.
+ /// The given metadata will be returned alongside the collected nodes
+ /// in a matching call to [`end_group`].
+ ///
+ /// [`push`]: #method.push
+ /// [`end_group`]: #method.end_group
+ pub fn start_group<T: 'static>(&mut self, meta: T) {
+ self.groups.push((Box::new(meta), mem::take(&mut self.inner)));
+ }
+
+ /// End a layouting group started with [`start_group`].
+ ///
+ /// This returns the stored metadata and the collected nodes.
+ ///
+ /// [`start_group`]: #method.start_group
+ pub fn end_group<T: 'static>(&mut self) -> (T, Vec<LayoutNode>) {
+ let (any, outer) = self.groups.pop().expect("no pushed group");
+ let group = *any.downcast::<T>().expect("bad group type");
+ (group, mem::replace(&mut self.inner, outer))
+ }
+
+ /// Start a page run group based on the active page state.
+ ///
+ /// If `hard` is false, empty page runs will be omitted from the output.
+ ///
+ /// This also starts an inner paragraph.
+ pub fn start_page_group(&mut self, hard: bool) {
+ let size = self.state.page.size;
+ let margins = self.state.page.margins();
+ let dirs = self.state.dirs;
+ let aligns = self.state.aligns;
+ self.start_group((size, margins, dirs, aligns, hard));
+ self.start_par_group();
+ }
+
+ /// End a page run group and push it to its parent group.
+ ///
+ /// This also ends an inner paragraph.
+ pub fn end_page_group(&mut self) {
+ self.end_par_group();
+ let ((size, padding, dirs, aligns, hard), children) = self.end_group();
+ let hard: bool = hard;
+ if hard || !children.is_empty() {
+ self.runs.push(Pages {
+ size,
+ child: LayoutNode::dynamic(Pad {
+ padding,
+ child: LayoutNode::dynamic(Stack {
+ dirs,
+ children,
+ aligns,
+ expand: Spec2::new(true, true),
+ }),
+ }),
+ })
+ }
+ }
+
+ /// Start a paragraph group based on the active text state.
+ pub fn start_par_group(&mut self) {
+ let dirs = self.state.dirs;
+ let line_spacing = self.state.text.line_spacing();
+ let aligns = self.state.aligns;
+ self.start_group((dirs, line_spacing, aligns));
+ }
+
+ /// End a paragraph group and push it to its parent group if its not empty.
+ pub fn end_par_group(&mut self) {
+ let ((dirs, line_spacing, aligns), children) = self.end_group();
+ if !children.is_empty() {
+ // FIXME: This is a hack and should be superseded by constraints
+ // having min and max size.
+ let expand_cross = self.groups.len() <= 1;
+ self.push(Par {
+ dirs,
+ line_spacing,
+ children,
+ aligns,
+ expand: Gen2::new(false, expand_cross).switch(dirs),
+ });
+ }
+ }
+
+ /// Construct a text node from the given string based on the active text
+ /// state.
+ pub fn make_text_node(&self, text: String) -> Text {
+ let mut variant = self.state.text.variant;
+
+ if self.state.text.strong {
+ variant.weight = variant.weight.thicken(300);
+ }
+
+ if self.state.text.emph {
+ variant.style = match variant.style {
+ FontStyle::Normal => FontStyle::Italic,
+ FontStyle::Italic => FontStyle::Normal,
+ FontStyle::Oblique => FontStyle::Normal,
+ }
+ }
+
+ Text {
+ text,
+ dir: self.state.dirs.cross,
+ size: self.state.text.font_size(),
+ fallback: Rc::clone(&self.state.text.fallback),
+ variant,
+ aligns: self.state.aligns,
+ }
+ }
+}
+
+/// Evaluate an item.
///
/// _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;
+ fn eval(&self, ctx: &mut EvalContext) -> Self::Output;
+}
+
+impl Eval for SynTree {
+ type Output = ();
+
+ fn eval(&self, ctx: &mut EvalContext) -> Self::Output {
+ for node in self {
+ node.v.eval(ctx);
+ }
+ }
+}
+
+impl Eval for SynNode {
+ type Output = ();
+
+ fn eval(&self, ctx: &mut EvalContext) -> Self::Output {
+ match self {
+ SynNode::Space => {
+ ctx.push(Spacing {
+ amount: ctx.state.text.word_spacing(),
+ softness: Softness::Soft,
+ });
+ }
+
+ SynNode::Text(text) => {
+ let node = ctx.make_text_node(text.clone());
+ ctx.push(node);
+ }
+
+ SynNode::Linebreak => {
+ ctx.end_par_group();
+ ctx.start_par_group();
+ }
+
+ SynNode::Parbreak => {
+ ctx.end_par_group();
+ ctx.push(Spacing {
+ amount: ctx.state.text.par_spacing(),
+ softness: Softness::Soft,
+ });
+ ctx.start_par_group();
+ }
+
+ SynNode::Emph => {
+ ctx.state.text.emph ^= true;
+ }
+
+ SynNode::Strong => {
+ ctx.state.text.strong ^= true;
+ }
+
+ SynNode::Heading(heading) => {
+ heading.eval(ctx);
+ }
+
+ SynNode::Raw(raw) => {
+ raw.eval(ctx);
+ }
+
+ SynNode::Expr(expr) => {
+ let value = expr.eval(ctx);
+ value.eval(ctx);
+ }
+ }
+ }
+}
+
+impl Eval for NodeHeading {
+ type Output = ();
+
+ fn eval(&self, ctx: &mut EvalContext) -> Self::Output {
+ let prev = ctx.state.clone();
+ let upscale = 1.5 - 0.1 * self.level.v as f64;
+ ctx.state.text.font_size.scale *= upscale;
+ ctx.state.text.strong = true;
+
+ self.contents.eval(ctx);
+
+ ctx.state = prev;
+ }
+}
+
+impl Eval for NodeRaw {
+ type Output = ();
+
+ fn eval(&self, ctx: &mut EvalContext) -> Self::Output {
+ let prev = Rc::clone(&ctx.state.text.fallback);
+ let fallback = Rc::make_mut(&mut ctx.state.text.fallback);
+ fallback.list.insert(0, "monospace".to_string());
+ fallback.flatten();
+
+ let mut children = vec![];
+ for line in &self.lines {
+ children.push(LayoutNode::Text(ctx.make_text_node(line.clone())));
+ }
+
+ ctx.push(Stack {
+ dirs: ctx.state.dirs,
+ children,
+ aligns: ctx.state.aligns,
+ expand: Spec2::new(false, false),
+ });
+
+ ctx.state.text.fallback = prev;
+ }
}
-#[async_trait(?Send)]
impl Eval for Expr {
type Output = Value;
- async fn eval(&self, ctx: &mut LayoutContext) -> Self::Output {
+ fn eval(&self, ctx: &mut EvalContext) -> 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,
+ Self::Lit(lit) => lit.eval(ctx),
+ Self::Call(call) => call.eval(ctx),
+ Self::Unary(unary) => unary.eval(ctx),
+ Self::Binary(binary) => binary.eval(ctx),
}
}
}
-#[async_trait(?Send)]
impl Eval for Lit {
type Output = Value;
- async fn eval(&self, ctx: &mut LayoutContext) -> Self::Output {
+ fn eval(&self, ctx: &mut EvalContext) -> Self::Output {
match *self {
Lit::Ident(ref v) => Value::Ident(v.clone()),
Lit::Bool(v) => Value::Bool(v),
@@ -59,20 +372,19 @@ impl Eval for Lit {
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::Dict(ref v) => Value::Dict(v.eval(ctx)),
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 {
+ fn eval(&self, ctx: &mut EvalContext) -> Self::Output {
let mut dict = ValueDict::new();
for entry in &self.0 {
- let val = entry.expr.v.eval(ctx).await;
+ let val = entry.expr.v.eval(ctx);
let spanned = val.span_with(entry.expr.span);
if let Some(key) = &entry.key {
dict.insert(&key.v, SpannedEntry::new(key.span, spanned));
@@ -85,19 +397,18 @@ impl Eval for LitDict {
}
}
-#[async_trait(?Send)]
impl Eval for ExprCall {
type Output = Value;
- async fn eval(&self, ctx: &mut LayoutContext) -> Self::Output {
+ fn eval(&self, ctx: &mut EvalContext) -> Self::Output {
let name = &self.name.v;
let span = self.name.span;
- let dict = self.args.v.eval(ctx).await;
+ let dict = self.args.v.eval(ctx);
if let Some(func) = ctx.state.scope.get(name) {
let args = Args(dict.span_with(self.args.span));
ctx.f.decos.push(Deco::Resolved.span_with(span));
- (func.clone())(args, ctx).await
+ (func.clone())(args, ctx)
} else {
if !name.is_empty() {
ctx.diag(error!(span, "unknown function"));
@@ -108,14 +419,13 @@ impl Eval for ExprCall {
}
}
-#[async_trait(?Send)]
impl Eval for ExprUnary {
type Output = Value;
- async fn eval(&self, ctx: &mut LayoutContext) -> Self::Output {
+ fn eval(&self, ctx: &mut EvalContext) -> Self::Output {
use Value::*;
- let value = self.expr.v.eval(ctx).await;
+ let value = self.expr.v.eval(ctx);
if value == Error {
return Error;
}
@@ -127,13 +437,12 @@ impl Eval for ExprUnary {
}
}
-#[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;
+ fn eval(&self, ctx: &mut EvalContext) -> Self::Output {
+ let lhs = self.lhs.v.eval(ctx);
+ let rhs = self.rhs.v.eval(ctx);
if lhs == Value::Error || rhs == Value::Error {
return Value::Error;
@@ -150,7 +459,7 @@ impl Eval for ExprBinary {
}
/// Compute the negation of a value.
-fn neg(ctx: &mut LayoutContext, span: Span, value: Value) -> Value {
+fn neg(ctx: &mut EvalContext, span: Span, value: Value) -> Value {
use Value::*;
match value {
Int(v) => Int(-v),
@@ -166,7 +475,7 @@ fn neg(ctx: &mut LayoutContext, span: Span, value: Value) -> Value {
}
/// Compute the sum of two values.
-fn add(ctx: &mut LayoutContext, span: Span, lhs: Value, rhs: Value) -> Value {
+fn add(ctx: &mut EvalContext, span: Span, lhs: Value, rhs: Value) -> Value {
use crate::geom::Linear as Lin;
use Value::*;
match (lhs, rhs) {
@@ -193,7 +502,6 @@ fn add(ctx: &mut LayoutContext, span: Span, lhs: Value, rhs: Value) -> Value {
(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) => {
ctx.diag(error!(span, "cannot add {} and {}", a.ty(), b.ty()));
@@ -203,7 +511,7 @@ fn add(ctx: &mut LayoutContext, span: Span, lhs: Value, rhs: Value) -> Value {
}
/// Compute the difference of two values.
-fn sub(ctx: &mut LayoutContext, span: Span, lhs: Value, rhs: Value) -> Value {
+fn sub(ctx: &mut EvalContext, span: Span, lhs: Value, rhs: Value) -> Value {
use crate::geom::Linear as Lin;
use Value::*;
match (lhs, rhs) {
@@ -232,7 +540,7 @@ fn sub(ctx: &mut LayoutContext, span: Span, lhs: Value, rhs: Value) -> Value {
}
/// Compute the product of two values.
-fn mul(ctx: &mut LayoutContext, span: Span, lhs: Value, rhs: Value) -> Value {
+fn mul(ctx: &mut EvalContext, span: Span, lhs: Value, rhs: Value) -> Value {
use Value::*;
match (lhs, rhs) {
// Numbers with themselves.
@@ -267,7 +575,7 @@ fn mul(ctx: &mut LayoutContext, span: Span, lhs: Value, rhs: Value) -> Value {
}
/// Compute the quotient of two values.
-fn div(ctx: &mut LayoutContext, span: Span, lhs: Value, rhs: Value) -> Value {
+fn div(ctx: &mut EvalContext, span: Span, lhs: Value, rhs: Value) -> Value {
use Value::*;
match (lhs, rhs) {
// Numbers by themselves.
diff --git a/src/eval/state.rs b/src/eval/state.rs
index 295a106c..5861ada1 100644
--- a/src/eval/state.rs
+++ b/src/eval/state.rs
@@ -1,5 +1,7 @@
//! Evaluation state.
+use std::rc::Rc;
+
use fontdock::{fallback, FallbackTree, FontStretch, FontStyle, FontVariant, FontWeight};
use super::Scope;
@@ -39,7 +41,7 @@ impl Default for State {
#[derive(Debug, Clone, PartialEq)]
pub struct TextState {
/// A tree of font family names and generic class names.
- pub fallback: FallbackTree,
+ pub fallback: Rc<FallbackTree>,
/// The selected font variant.
pub variant: FontVariant,
/// Whether the strong toggle is active or inactive. This determines
@@ -83,7 +85,7 @@ impl TextState {
impl Default for TextState {
fn default() -> Self {
Self {
- fallback: fallback! {
+ fallback: Rc::new(fallback! {
list: ["sans-serif"],
classes: {
"serif" => ["source serif pro", "noto serif"],
@@ -95,7 +97,7 @@ impl Default for TextState {
"source sans pro", "noto sans", "segoe ui emoji",
"noto emoji", "latin modern math",
],
- },
+ }),
variant: FontVariant {
style: FontStyle::Normal,
weight: FontWeight::REGULAR,
@@ -160,15 +162,14 @@ impl PageState {
}
}
- /// The absolute insets.
- pub fn insets(&self) -> Insets {
- let Size { width, height } = self.size;
+ /// The margins.
+ pub fn margins(&self) -> Sides<Linear> {
let default = self.class.default_margins();
- Insets {
- x0: -self.margins.left.unwrap_or(default.left).eval(width),
- y0: -self.margins.top.unwrap_or(default.top).eval(height),
- x1: -self.margins.right.unwrap_or(default.right).eval(width),
- y1: -self.margins.bottom.unwrap_or(default.bottom).eval(height),
+ Sides {
+ left: self.margins.left.unwrap_or(default.left),
+ top: self.margins.top.unwrap_or(default.top),
+ right: self.margins.right.unwrap_or(default.right),
+ bottom: self.margins.bottom.unwrap_or(default.bottom),
}
}
}
diff --git a/src/eval/value.rs b/src/eval/value.rs
index 85cb261c..37fd7ead 100644
--- a/src/eval/value.rs
+++ b/src/eval/value.rs
@@ -4,10 +4,9 @@ use std::fmt::{self, Debug, Formatter};
use std::ops::Deref;
use std::rc::Rc;
-use super::{Args, Dict, SpannedEntry};
+use super::{Args, Dict, Eval, EvalContext, SpannedEntry};
use crate::color::RgbaColor;
use crate::geom::Linear;
-use crate::layout::{Command, LayoutContext};
use crate::syntax::{Ident, Span, SpanWith, Spanned, SynNode, SynTree};
use crate::DynFuture;
@@ -45,8 +44,6 @@ pub enum Value {
Content(SynTree),
/// An executable function.
Func(ValueFunc),
- /// Layouting commands.
- Commands(Vec<Command>),
/// The result of invalid operations.
Error,
}
@@ -69,59 +66,42 @@ impl Value {
Self::Dict(_) => "dict",
Self::Content(_) => "content",
Self::Func(_) => "function",
- Self::Commands(_) => "commands",
Self::Error => "error",
}
}
}
-impl Default for Value {
- fn default() -> Self {
- Value::None
- }
-}
+impl Eval for Value {
+ type Output = ();
-impl Spanned<Value> {
- /// Transform this value into something layoutable.
- ///
- /// If this is already a command-value, it is simply unwrapped, otherwise
- /// the value is represented as layoutable content in a reasonable way.
- pub fn into_commands(self) -> Vec<Command> {
- match self.v {
+ /// Evaluate everything contained in this value.
+ fn eval(&self, ctx: &mut EvalContext) -> Self::Output {
+ match self {
// Don't print out none values.
- Value::None => vec![],
+ Value::None => {}
- // Pass-through.
- Value::Commands(commands) => commands,
- Value::Content(tree) => vec![Command::LayoutSyntaxTree(tree)],
+ // Pass through.
+ Value::Content(tree) => tree.eval(ctx),
- // Forward to each entry, separated with spaces.
+ // Forward to each dictionary entry.
Value::Dict(dict) => {
- let mut commands = vec![];
- let mut end = None;
- for entry in dict.into_values() {
- if let Some(last_end) = end {
- let span = Span::new(last_end, entry.key_span.start);
- let tree = vec![SynNode::Space.span_with(span)];
- commands.push(Command::LayoutSyntaxTree(tree));
- }
-
- end = Some(entry.value.span.end);
- commands.extend(entry.value.into_commands());
+ for entry in dict.values() {
+ entry.value.v.eval(ctx);
}
- commands
}
// Format with debug.
- val => {
- let fmt = format!("{:?}", val);
- let tree = vec![SynNode::Text(fmt).span_with(self.span)];
- vec![Command::LayoutSyntaxTree(tree)]
- }
+ val => ctx.push(ctx.make_text_node(format!("{:?}", val))),
}
}
}
+impl Default for Value {
+ fn default() -> Self {
+ Value::None
+ }
+}
+
impl Debug for Value {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self {
@@ -138,7 +118,6 @@ impl Debug for Value {
Self::Dict(v) => v.fmt(f),
Self::Content(v) => v.fmt(f),
Self::Func(v) => v.fmt(f),
- Self::Commands(v) => v.fmt(f),
Self::Error => f.pad("<error>"),
}
}
@@ -157,9 +136,9 @@ pub type ValueDict = Dict<SpannedEntry<Value>>;
/// The dynamic function object is wrapped in an `Rc` to keep [`Value`]
/// clonable.
///
-/// _Note_: This is needed because the compiler can't `derive(PartialEq)`
-/// for `Value` when directly putting the boxed function in there,
-/// see the [Rust Issue].
+/// _Note_: This is needed because the compiler can't `derive(PartialEq)` for
+/// [`Value`] when directly putting the boxed function in there, see the
+/// [Rust Issue].
///
/// [`Value`]: enum.Value.html
/// [Rust Issue]: https://github.com/rust-lang/rust/issues/31740
@@ -167,13 +146,13 @@ pub type ValueDict = Dict<SpannedEntry<Value>>;
pub struct ValueFunc(pub Rc<Func>);
/// The signature of executable functions.
-pub type Func = dyn Fn(Args, &mut LayoutContext) -> DynFuture<Value>;
+type Func = dyn Fn(Args, &mut EvalContext) -> Value;
impl ValueFunc {
/// Create a new function value from a rust function or closure.
- pub fn new<F: 'static>(f: F) -> Self
+ pub fn new<F>(f: F) -> Self
where
- F: Fn(Args, &mut LayoutContext) -> DynFuture<Value>,
+ F: Fn(Args, &mut EvalContext) -> Value + 'static,
{
Self(Rc::new(f))
}