Separate state and constraints 🧶

This commit is contained in:
Laurenz 2020-10-04 19:06:20 +02:00
parent 6672f8f7df
commit 0f7c70fd93
20 changed files with 273 additions and 281 deletions

View File

@ -5,6 +5,7 @@ use criterion::{criterion_group, criterion_main, Criterion};
use fontdock::fs::{FsIndex, FsProvider}; use fontdock::fs::{FsIndex, FsProvider};
use futures_executor::block_on; use futures_executor::block_on;
use typstc::eval::State;
use typstc::font::FontLoader; use typstc::font::FontLoader;
use typstc::parse::parse; use typstc::parse::parse;
use typstc::typeset; use typstc::typeset;
@ -25,10 +26,9 @@ fn typesetting_benchmark(c: &mut Criterion) {
let loader = FontLoader::new(Box::new(provider), descriptors); let loader = FontLoader::new(Box::new(provider), descriptors);
let loader = Rc::new(RefCell::new(loader)); let loader = Rc::new(RefCell::new(loader));
let style = Default::default(); let state = State::default();
let scope = typstc::library::_std();
c.bench_function("typeset-coma", |b| { c.bench_function("typeset-coma", |b| {
b.iter(|| block_on(typeset(COMA, &style, &scope, Rc::clone(&loader)))) b.iter(|| block_on(typeset(COMA, state.clone(), Rc::clone(&loader))))
}); });
} }

View File

@ -7,6 +7,7 @@ use std::rc::Rc;
use fontdock::fs::{FsIndex, FsProvider}; use fontdock::fs::{FsIndex, FsProvider};
use futures_executor::block_on; use futures_executor::block_on;
use typstc::eval::State;
use typstc::export::pdf; use typstc::export::pdf;
use typstc::font::FontLoader; use typstc::font::FontLoader;
use typstc::parse::LineMap; use typstc::parse::LineMap;
@ -41,12 +42,11 @@ fn main() {
let loader = FontLoader::new(Box::new(provider), descriptors); let loader = FontLoader::new(Box::new(provider), descriptors);
let loader = Rc::new(RefCell::new(loader)); let loader = Rc::new(RefCell::new(loader));
let style = Default::default(); let state = State::default();
let scope = typstc::library::_std();
let Pass { let Pass {
output: layouts, output: layouts,
feedback: Feedback { mut diagnostics, .. }, feedback: Feedback { mut diagnostics, .. },
} = block_on(typeset(&src, &style, &scope, Rc::clone(&loader))); } = block_on(typeset(&src, state, Rc::clone(&loader)));
if !diagnostics.is_empty() { if !diagnostics.is_empty() {
diagnostics.sort(); diagnostics.sort();

View File

@ -2,8 +2,10 @@
mod dict; mod dict;
mod scope; mod scope;
mod state;
mod value; mod value;
pub use dict::*; pub use dict::*;
pub use scope::*; pub use scope::*;
pub use state::*;
pub use value::*; pub use value::*;

View File

@ -6,6 +6,7 @@ use std::fmt::{self, Debug, Formatter};
use super::value::FuncValue; use super::value::FuncValue;
/// A map from identifiers to functions. /// A map from identifiers to functions.
#[derive(Clone, PartialEq)]
pub struct Scope { pub struct Scope {
functions: HashMap<String, FuncValue>, functions: HashMap<String, FuncValue>,
} }

View File

@ -1,23 +1,43 @@
//! Styles for text and pages. //! State.
use fontdock::{fallback, FallbackTree, FontStretch, FontStyle, FontVariant, FontWeight}; use fontdock::{fallback, FallbackTree, FontStretch, FontStyle, FontVariant, FontWeight};
use super::Scope;
use crate::geom::{Insets, Linear, Sides, Size}; use crate::geom::{Insets, Linear, Sides, Size};
use crate::layout::{Dir, GenAlign, LayoutAlign, LayoutSystem};
use crate::length::Length; use crate::length::Length;
use crate::paper::{Paper, PaperClass, PAPER_A4}; use crate::paper::{Paper, PaperClass, PAPER_A4};
/// Defines properties of pages and text. /// The active evaluation state.
#[derive(Debug, Default, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub struct LayoutStyle { pub struct State {
/// The style for text. /// The scope that contains function definitions.
pub text: TextStyle, pub scope: Scope,
/// The style for pages. /// The text state.
pub page: PageStyle, pub text: TextState,
/// The page state.
pub page: PageState,
/// The active layouting system.
pub sys: LayoutSystem,
/// The active alignments.
pub align: LayoutAlign,
}
impl Default for State {
fn default() -> Self {
Self {
scope: crate::library::_std(),
text: TextState::default(),
page: PageState::default(),
sys: LayoutSystem::new(Dir::LTR, Dir::TTB),
align: LayoutAlign::new(GenAlign::Start, GenAlign::Start),
}
}
} }
/// Defines which fonts to use and how to space text. /// Defines which fonts to use and how to space text.
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub struct TextStyle { pub struct TextState {
/// A tree of font family names and generic class names. /// A tree of font family names and generic class names.
pub fallback: FallbackTree, pub fallback: FallbackTree,
/// The selected font variant. /// The selected font variant.
@ -38,7 +58,7 @@ pub struct TextStyle {
pub par_spacing: Linear, pub par_spacing: Linear,
} }
impl TextStyle { impl TextState {
/// The absolute font size. /// The absolute font size.
pub fn font_size(&self) -> f64 { pub fn font_size(&self) -> f64 {
self.font_size.eval() self.font_size.eval()
@ -60,7 +80,7 @@ impl TextStyle {
} }
} }
impl Default for TextStyle { impl Default for TextState {
fn default() -> Self { fn default() -> Self {
Self { Self {
fallback: fallback! { fallback: fallback! {
@ -120,7 +140,7 @@ impl FontSize {
/// Defines the size and margins of a page. /// Defines the size and margins of a page.
#[derive(Debug, Copy, Clone, PartialEq)] #[derive(Debug, Copy, Clone, PartialEq)]
pub struct PageStyle { pub struct PageState {
/// The class of this page. /// The class of this page.
pub class: PaperClass, pub class: PaperClass,
/// The width and height of the page. /// The width and height of the page.
@ -130,7 +150,7 @@ pub struct PageStyle {
pub margins: Sides<Option<Linear>>, pub margins: Sides<Option<Linear>>,
} }
impl PageStyle { impl PageState {
/// The default page style for the given paper. /// The default page style for the given paper.
pub fn new(paper: Paper) -> Self { pub fn new(paper: Paper) -> Self {
Self { Self {
@ -153,7 +173,7 @@ impl PageStyle {
} }
} }
impl Default for PageStyle { impl Default for PageState {
fn default() -> Self { fn default() -> Self {
Self::new(PAPER_A4) Self::new(PAPER_A4)
} }

View File

@ -12,7 +12,7 @@ use crate::geom::Linear;
use crate::layout::{Command, Commands, Dir, LayoutContext, SpecAlign}; use crate::layout::{Command, Commands, Dir, LayoutContext, SpecAlign};
use crate::paper::Paper; use crate::paper::Paper;
use crate::syntax::{Ident, Span, SpanWith, Spanned, SynNode, SynTree}; use crate::syntax::{Ident, Span, SpanWith, Spanned, SynNode, SynTree};
use crate::{DynFuture, Feedback, Pass}; use crate::{DynFuture, Feedback};
/// A computational value. /// A computational value.
#[derive(Clone, PartialEq)] #[derive(Clone, PartialEq)]
@ -149,13 +149,13 @@ impl Debug for Value {
pub struct FuncValue(pub Rc<FuncType>); pub struct FuncValue(pub Rc<FuncType>);
/// The signature of executable functions. /// The signature of executable functions.
type FuncType = dyn Fn(Span, DictValue, LayoutContext<'_>) -> DynFuture<Pass<Value>>; type FuncType = dyn Fn(DictValue, &mut LayoutContext) -> DynFuture<Value>;
impl FuncValue { impl FuncValue {
/// Create a new function value from a rust function or closure. /// Create a new function value from a rust function or closure.
pub fn new<F: 'static>(f: F) -> Self pub fn new<F: 'static>(f: F) -> Self
where where
F: Fn(Span, DictValue, LayoutContext<'_>) -> DynFuture<Pass<Value>>, F: Fn(DictValue, &mut LayoutContext) -> DynFuture<Value>,
{ {
Self(Rc::new(f)) Self(Rc::new(f))
} }

View File

@ -15,36 +15,40 @@ pub use tree::*;
use crate::geom::{Insets, Point, Rect, RectExt, Sides, Size, SizeExt}; use crate::geom::{Insets, Point, Rect, RectExt, Sides, Size, SizeExt};
use crate::eval::Scope; use crate::eval::{PageState, State, TextState};
use crate::font::SharedFontLoader; use crate::font::SharedFontLoader;
use crate::style::{LayoutStyle, PageStyle, TextStyle};
use crate::syntax::SynTree; use crate::syntax::SynTree;
use crate::Pass; use crate::{Feedback, Pass};
/// Layout a syntax tree and return the produced layout. /// Layout a syntax tree and return the produced layout.
pub async fn layout( pub async fn layout(
tree: &SynTree, tree: &SynTree,
style: &LayoutStyle, state: State,
scope: &Scope,
loader: SharedFontLoader, loader: SharedFontLoader,
) -> Pass<MultiLayout> { ) -> Pass<MultiLayout> {
let space = LayoutSpace { let space = LayoutSpace {
size: style.page.size, size: state.page.size,
insets: style.page.insets(), insets: state.page.insets(),
expansion: LayoutExpansion::new(true, true), expansion: LayoutExpansion::new(true, true),
}; };
tree::layout_tree(&tree, LayoutContext {
loader, let constraints = LayoutConstraints {
scope, root: true,
style,
base: space.usable(), base: space.usable(),
spaces: vec![space], spaces: vec![space],
repeat: true, repeat: true,
sys: LayoutSystem::new(Dir::LTR, Dir::TTB), };
align: LayoutAlign::new(GenAlign::Start, GenAlign::Start),
root: true, let mut ctx = LayoutContext {
}) loader,
.await state,
constraints,
f: Feedback::new(),
};
let layouts = layout_tree(&tree, &mut ctx).await;
Pass::new(layouts, ctx.f)
} }
/// A collection of layouts. /// A collection of layouts.
@ -63,13 +67,22 @@ pub struct BoxLayout {
/// The context for layouting. /// The context for layouting.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct LayoutContext<'a> { pub struct LayoutContext {
/// The font loader to query fonts from when typesetting text. /// The font loader to query fonts from when typesetting text.
pub loader: SharedFontLoader, pub loader: SharedFontLoader,
/// The function scope. /// The active state.
pub scope: &'a Scope, pub state: State,
/// The style for pages and text. /// The active constraints.
pub style: &'a LayoutStyle, pub constraints: LayoutConstraints,
/// The accumulated feedback.
pub f: Feedback,
}
/// The constraints for layouting a single node.
#[derive(Debug, Clone)]
pub struct LayoutConstraints {
/// Whether this layouting process is the root page-building process.
pub root: bool,
/// The unpadded size of this container (the base 100% for relative sizes). /// The unpadded size of this container (the base 100% for relative sizes).
pub base: Size, pub base: Size,
/// The spaces to layout into. /// The spaces to layout into.
@ -77,14 +90,6 @@ pub struct LayoutContext<'a> {
/// Whether to spill over into copies of the last space or finish layouting /// Whether to spill over into copies of the last space or finish layouting
/// when the last space is used up. /// when the last space is used up.
pub repeat: bool, pub repeat: bool,
/// The system into which content is laid out.
pub sys: LayoutSystem,
/// The alignment of the _resulting_ layout. This does not effect the line
/// layouting itself, but rather how the finished layout will be positioned
/// in a parent layout.
pub align: LayoutAlign,
/// Whether this layouting process is the root page-building process.
pub root: bool,
} }
/// A collection of layout spaces. /// A collection of layout spaces.
@ -156,9 +161,9 @@ pub enum Command {
BreakPage, BreakPage,
/// Update the text style. /// Update the text style.
SetTextStyle(TextStyle), SetTextState(TextState),
/// Update the page style. /// Update the page style.
SetPageStyle(PageStyle), SetPageState(PageState),
/// Update the alignment for future boxes added to this layouting process. /// Update the alignment for future boxes added to this layouting process.
SetAlignment(LayoutAlign), SetAlignment(LayoutAlign),

View File

@ -1,48 +1,45 @@
//! Layouting of syntax trees. //! Layouting of syntax trees.
use std::rc::Rc;
use super::*; use super::*;
use crate::shaping; use crate::shaping;
use crate::style::LayoutStyle;
use crate::syntax::{ use crate::syntax::{
Decoration, Expr, NodeHeading, NodeRaw, Span, SpanWith, Spanned, SynNode, SynTree, Decoration, Expr, NodeHeading, NodeRaw, Span, SpanWith, Spanned, SynNode, SynTree,
}; };
use crate::{DynFuture, Feedback, Pass}; use crate::DynFuture;
/// Layout a syntax tree in a given context. /// Layout a syntax tree in a given context.
pub async fn layout_tree(tree: &SynTree, ctx: LayoutContext<'_>) -> Pass<MultiLayout> { pub async fn layout_tree(tree: &SynTree, ctx: &mut LayoutContext) -> MultiLayout {
let mut layouter = TreeLayouter::new(ctx); let mut layouter = TreeLayouter::new(ctx);
layouter.layout_tree(tree).await; layouter.layout_tree(tree).await;
layouter.finish() layouter.finish()
} }
/// Performs the tree layouting. /// Layouts trees.
struct TreeLayouter<'a> { struct TreeLayouter<'a> {
ctx: LayoutContext<'a>, ctx: &'a mut LayoutContext,
constraints: LayoutConstraints,
layouter: LineLayouter, layouter: LineLayouter,
style: LayoutStyle,
feedback: Feedback,
} }
impl<'a> TreeLayouter<'a> { impl<'a> TreeLayouter<'a> {
fn new(ctx: LayoutContext<'a>) -> Self { fn new(ctx: &'a mut LayoutContext) -> Self {
let layouter = LineLayouter::new(LineContext {
spaces: ctx.constraints.spaces.clone(),
sys: ctx.state.sys,
align: ctx.state.align,
repeat: ctx.constraints.repeat,
line_spacing: ctx.state.text.line_spacing(),
});
Self { Self {
layouter: LineLayouter::new(LineContext { layouter,
spaces: ctx.spaces.clone(), constraints: ctx.constraints.clone(),
sys: ctx.sys,
align: ctx.align,
repeat: ctx.repeat,
line_spacing: ctx.style.text.line_spacing(),
}),
style: ctx.style.clone(),
ctx, ctx,
feedback: Feedback::new(),
} }
} }
fn finish(self) -> Pass<MultiLayout> { fn finish(self) -> MultiLayout {
Pass::new(self.layouter.finish(), self.feedback) self.layouter.finish()
} }
fn layout_tree<'t>(&'t mut self, tree: &'t SynTree) -> DynFuture<'t, ()> { fn layout_tree<'t>(&'t mut self, tree: &'t SynTree) -> DynFuture<'t, ()> {
@ -55,16 +52,16 @@ impl<'a> TreeLayouter<'a> {
async fn layout_node(&mut self, node: &Spanned<SynNode>) { async fn layout_node(&mut self, node: &Spanned<SynNode>) {
let decorate = |this: &mut Self, deco: Decoration| { let decorate = |this: &mut Self, deco: Decoration| {
this.feedback.decorations.push(deco.span_with(node.span)); this.ctx.f.decorations.push(deco.span_with(node.span));
}; };
match &node.v { match &node.v {
SynNode::Space => self.layout_space(), SynNode::Space => self.layout_space(),
SynNode::Text(text) => { SynNode::Text(text) => {
if self.style.text.emph { if self.ctx.state.text.emph {
decorate(self, Decoration::Emph); decorate(self, Decoration::Emph);
} }
if self.style.text.strong { if self.ctx.state.text.strong {
decorate(self, Decoration::Strong); decorate(self, Decoration::Strong);
} }
self.layout_text(text).await; self.layout_text(text).await;
@ -73,11 +70,11 @@ impl<'a> TreeLayouter<'a> {
SynNode::Linebreak => self.layouter.finish_line(), SynNode::Linebreak => self.layouter.finish_line(),
SynNode::Parbreak => self.layout_parbreak(), SynNode::Parbreak => self.layout_parbreak(),
SynNode::Emph => { SynNode::Emph => {
self.style.text.emph = !self.style.text.emph; self.ctx.state.text.emph ^= true;
decorate(self, Decoration::Emph); decorate(self, Decoration::Emph);
} }
SynNode::Strong => { SynNode::Strong => {
self.style.text.strong = !self.style.text.strong; self.ctx.state.text.strong ^= true;
decorate(self, Decoration::Strong); decorate(self, Decoration::Strong);
} }
@ -92,12 +89,12 @@ impl<'a> TreeLayouter<'a> {
fn layout_space(&mut self) { fn layout_space(&mut self) {
self.layouter self.layouter
.add_primary_spacing(self.style.text.word_spacing(), SpacingKind::WORD); .add_primary_spacing(self.ctx.state.text.word_spacing(), SpacingKind::WORD);
} }
fn layout_parbreak(&mut self) { fn layout_parbreak(&mut self) {
self.layouter.add_secondary_spacing( self.layouter.add_secondary_spacing(
self.style.text.paragraph_spacing(), self.ctx.state.text.paragraph_spacing(),
SpacingKind::PARAGRAPH, SpacingKind::PARAGRAPH,
); );
} }
@ -106,9 +103,9 @@ impl<'a> TreeLayouter<'a> {
self.layouter.add( self.layouter.add(
shaping::shape( shaping::shape(
text, text,
self.ctx.sys.primary, self.ctx.state.sys.primary,
self.ctx.align, self.ctx.state.align,
&self.style.text, &self.ctx.state.text,
&mut self.ctx.loader.borrow_mut(), &mut self.ctx.loader.borrow_mut(),
) )
.await, .await,
@ -116,17 +113,17 @@ impl<'a> TreeLayouter<'a> {
} }
async fn layout_heading(&mut self, heading: &NodeHeading) { async fn layout_heading(&mut self, heading: &NodeHeading) {
let style = self.style.text.clone(); let style = self.ctx.state.text.clone();
let factor = 1.5 - 0.1 * heading.level.v as f64; let factor = 1.5 - 0.1 * heading.level.v as f64;
self.style.text.font_size.scale *= factor; self.ctx.state.text.font_size.scale *= factor;
self.style.text.strong = true; self.ctx.state.text.strong = true;
self.layout_parbreak(); self.layout_parbreak();
self.layout_tree(&heading.contents).await; self.layout_tree(&heading.contents).await;
self.layout_parbreak(); self.layout_parbreak();
self.style.text = style; self.ctx.state.text = style;
} }
async fn layout_raw(&mut self, raw: &NodeRaw) { async fn layout_raw(&mut self, raw: &NodeRaw) {
@ -135,9 +132,9 @@ impl<'a> TreeLayouter<'a> {
} }
// TODO: Make this more efficient. // TODO: Make this more efficient.
let fallback = self.style.text.fallback.clone(); let fallback = self.ctx.state.text.fallback.clone();
self.style.text.fallback.list.insert(0, "monospace".to_string()); self.ctx.state.text.fallback.list.insert(0, "monospace".to_string());
self.style.text.fallback.flatten(); self.ctx.state.text.fallback.flatten();
let mut first = true; let mut first = true;
for line in &raw.lines { for line in &raw.lines {
@ -148,7 +145,7 @@ impl<'a> TreeLayouter<'a> {
self.layout_text(line).await; self.layout_text(line).await;
} }
self.style.text.fallback = fallback; self.ctx.state.text.fallback = fallback;
if !raw.inline { if !raw.inline {
self.layout_parbreak(); self.layout_parbreak();
@ -156,15 +153,15 @@ impl<'a> TreeLayouter<'a> {
} }
async fn layout_expr(&mut self, expr: Spanned<&Expr>) { async fn layout_expr(&mut self, expr: Spanned<&Expr>) {
let ctx = LayoutContext { self.ctx.constraints = LayoutConstraints {
style: &self.style,
spaces: self.layouter.remaining(),
root: false, root: false,
loader: Rc::clone(&self.ctx.loader), base: self.constraints.base,
..self.ctx spaces: self.layouter.remaining(),
repeat: self.constraints.repeat,
}; };
let val = expr.v.eval(&ctx, &mut self.feedback).await; let val = expr.v.eval(self.ctx).await;
let commands = val.span_with(expr.span).into_commands(); let commands = val.span_with(expr.span).into_commands();
for command in commands { for command in commands {
@ -186,23 +183,23 @@ impl<'a> TreeLayouter<'a> {
BreakLine => self.layouter.finish_line(), BreakLine => self.layouter.finish_line(),
BreakPage => { BreakPage => {
if self.ctx.root { if self.constraints.root {
self.layouter.finish_space(true) self.layouter.finish_space(true)
} else { } else {
error!( error!(
@self.feedback, span, @self.ctx.f, span,
"page break cannot only be issued from root context", "page break cannot only be issued from root context",
); );
} }
} }
SetTextStyle(style) => { SetTextState(style) => {
self.layouter.set_line_spacing(style.line_spacing()); self.layouter.set_line_spacing(style.line_spacing());
self.style.text = style; self.ctx.state.text = style;
} }
SetPageStyle(style) => { SetPageState(style) => {
if self.ctx.root { if self.constraints.root {
self.style.page = style; self.ctx.state.page = style;
// The line layouter has no idea of page styles and thus we // The line layouter has no idea of page styles and thus we
// need to recompute the layouting space resulting of the // need to recompute the layouting space resulting of the
@ -212,20 +209,20 @@ impl<'a> TreeLayouter<'a> {
insets: style.insets(), insets: style.insets(),
expansion: LayoutExpansion::new(true, true), expansion: LayoutExpansion::new(true, true),
}; };
self.ctx.base = space.usable(); self.constraints.base = space.usable();
self.layouter.set_spaces(vec![space], true); self.layouter.set_spaces(vec![space], true);
} else { } else {
error!( error!(
@self.feedback, span, @self.ctx.f, span,
"page style cannot only be changed from root context", "page style cannot only be changed from root context",
); );
} }
} }
SetAlignment(align) => self.ctx.align = align, SetAlignment(align) => self.ctx.state.align = align,
SetSystem(sys) => { SetSystem(sys) => {
self.layouter.set_sys(sys); self.layouter.set_sys(sys);
self.ctx.sys = sys; self.ctx.state.sys = sys;
} }
} }
} }

View File

@ -36,7 +36,6 @@ pub mod paper;
pub mod parse; pub mod parse;
pub mod prelude; pub mod prelude;
pub mod shaping; pub mod shaping;
pub mod style;
pub mod syntax; pub mod syntax;
use std::fmt::Debug; use std::fmt::Debug;
@ -44,21 +43,19 @@ use std::future::Future;
use std::pin::Pin; use std::pin::Pin;
use crate::diagnostic::Diagnostic; use crate::diagnostic::Diagnostic;
use crate::eval::{Scope, Value}; use crate::eval::{State, Value};
use crate::font::SharedFontLoader; use crate::font::SharedFontLoader;
use crate::layout::{Commands, MultiLayout}; use crate::layout::{Commands, MultiLayout};
use crate::style::LayoutStyle;
use crate::syntax::{Decoration, Offset, Pos, SpanVec}; use crate::syntax::{Decoration, Offset, Pos, SpanVec};
/// Process source code directly into a collection of layouts. /// Process source code directly into a collection of layouts.
pub async fn typeset( pub async fn typeset(
src: &str, src: &str,
style: &LayoutStyle, state: State,
scope: &Scope,
loader: SharedFontLoader, loader: SharedFontLoader,
) -> Pass<MultiLayout> { ) -> Pass<MultiLayout> {
let parsed = parse::parse(src); let parsed = parse::parse(src);
let layouted = layout::layout(&parsed.output, style, scope, loader).await; let layouted = layout::layout(&parsed.output, state, loader).await;
let feedback = Feedback::merge(parsed.feedback, layouted.feedback); let feedback = Feedback::merge(parsed.feedback, layouted.feedback);
Pass::new(layouted.output, feedback) Pass::new(layouted.output, feedback)
} }

View File

@ -14,19 +14,17 @@ use super::*;
/// - `vertical`: Any of `top`, `bottom` or `center`. /// - `vertical`: Any of `top`, `bottom` or `center`.
/// ///
/// There may not be two alignment specifications for the same axis. /// There may not be two alignment specifications for the same axis.
pub async fn align(_: Span, mut args: DictValue, ctx: LayoutContext<'_>) -> Pass<Value> { pub async fn align(mut args: DictValue, ctx: &mut LayoutContext) -> Value {
let mut f = Feedback::new();
let content = args.take::<SynTree>(); let content = args.take::<SynTree>();
let h = args.take_key::<Spanned<SpecAlign>>("horizontal", &mut f); let h = args.take_key::<Spanned<SpecAlign>>("horizontal", &mut ctx.f);
let v = args.take_key::<Spanned<SpecAlign>>("vertical", &mut f); let v = args.take_key::<Spanned<SpecAlign>>("vertical", &mut ctx.f);
let all = args let all = args
.take_all_num_vals::<Spanned<SpecAlign>>() .take_all_num_vals::<Spanned<SpecAlign>>()
.map(|align| (align.v.axis(), align)) .map(|align| (align.v.axis(), align))
.chain(h.into_iter().map(|align| (Some(SpecAxis::Horizontal), align))) .chain(h.into_iter().map(|align| (Some(SpecAxis::Horizontal), align)))
.chain(v.into_iter().map(|align| (Some(SpecAxis::Vertical), align))); .chain(v.into_iter().map(|align| (Some(SpecAxis::Vertical), align)));
let mut aligns = ctx.align; let mut aligns = ctx.state.align;
let mut had = [false; 2]; let mut had = [false; 2];
let mut deferred_center = false; let mut deferred_center = false;
@ -37,19 +35,19 @@ pub async fn align(_: Span, mut args: DictValue, ctx: LayoutContext<'_>) -> Pass
if let Some(axis) = axis { if let Some(axis) = axis {
if align.v.axis().map_or(false, |a| a != axis) { if align.v.axis().map_or(false, |a| a != axis) {
error!( error!(
@f, align.span, @ctx.f, align.span,
"invalid alignment {} for {} axis", align.v, axis, "invalid alignment {} for {} axis", align.v, axis,
); );
} else if had[axis as usize] { } else if had[axis as usize] {
error!(@f, align.span, "duplicate alignment for {} axis", axis); error!(@ctx.f, align.span, "duplicate alignment for {} axis", axis);
} else { } else {
let gen_align = align.v.to_gen(ctx.sys); let gen_align = align.v.to_gen(ctx.state.sys);
*aligns.get_mut(axis.to_gen(ctx.sys)) = gen_align; *aligns.get_mut(axis.to_gen(ctx.state.sys)) = gen_align;
had[axis as usize] = true; had[axis as usize] = true;
} }
} else { } else {
if had == [true, true] { if had == [true, true] {
error!(@f, align.span, "duplicate alignment"); error!(@ctx.f, align.span, "duplicate alignment");
} else if deferred_center { } else if deferred_center {
// We have two unflushed centers, meaning we know that both axes // We have two unflushed centers, meaning we know that both axes
// are to be centered. // are to be centered.
@ -69,7 +67,7 @@ pub async fn align(_: Span, mut args: DictValue, ctx: LayoutContext<'_>) -> Pass
SpecAxis::Vertical SpecAxis::Vertical
}; };
*aligns.get_mut(axis.to_gen(ctx.sys)) = GenAlign::Center; *aligns.get_mut(axis.to_gen(ctx.state.sys)) = GenAlign::Center;
had[axis as usize] = true; had[axis as usize] = true;
deferred_center = false; deferred_center = false;
@ -82,15 +80,13 @@ pub async fn align(_: Span, mut args: DictValue, ctx: LayoutContext<'_>) -> Pass
aligns.primary = GenAlign::Center; aligns.primary = GenAlign::Center;
} }
let commands = match content { args.unexpected(&mut ctx.f);
Value::Commands(match content {
Some(tree) => vec![ Some(tree) => vec![
SetAlignment(aligns), SetAlignment(aligns),
LayoutSyntaxTree(tree), LayoutSyntaxTree(tree),
SetAlignment(ctx.align), SetAlignment(ctx.state.align),
], ],
None => vec![SetAlignment(aligns)], None => vec![SetAlignment(aligns)],
}; })
args.unexpected(&mut f);
Pass::commands(commands, f)
} }

View File

@ -6,37 +6,32 @@ use crate::geom::Linear;
/// # Keyword arguments /// # Keyword arguments
/// - `width`: The width of the box (length or relative to parent's width). /// - `width`: The width of the box (length or relative to parent's width).
/// - `height`: The height of the box (length or relative to parent's height). /// - `height`: The height of the box (length or relative to parent's height).
pub async fn boxed( pub async fn boxed(mut args: DictValue, ctx: &mut LayoutContext) -> Value {
_: Span,
mut args: DictValue,
mut ctx: LayoutContext<'_>,
) -> Pass<Value> {
let mut f = Feedback::new();
let content = args.take::<SynTree>().unwrap_or_default(); let content = args.take::<SynTree>().unwrap_or_default();
ctx.base = ctx.spaces[0].size; let constraints = &mut ctx.constraints;
ctx.spaces.truncate(1); constraints.base = constraints.spaces[0].size;
ctx.repeat = false; constraints.spaces.truncate(1);
constraints.repeat = false;
if let Some(width) = args.take_key::<Linear>("width", &mut f) { if let Some(width) = args.take_key::<Linear>("width", &mut ctx.f) {
let abs = width.eval(ctx.base.width); let abs = width.eval(constraints.base.width);
ctx.base.width = abs; constraints.base.width = abs;
ctx.spaces[0].size.width = abs; constraints.spaces[0].size.width = abs;
ctx.spaces[0].expansion.horizontal = true; constraints.spaces[0].expansion.horizontal = true;
} }
if let Some(height) = args.take_key::<Linear>("height", &mut f) { if let Some(height) = args.take_key::<Linear>("height", &mut ctx.f) {
let abs = height.eval(ctx.base.height); let abs = height.eval(constraints.base.height);
ctx.base.height = abs; constraints.base.height = abs;
ctx.spaces[0].size.height = abs; constraints.spaces[0].size.height = abs;
ctx.spaces[0].expansion.vertical = true; constraints.spaces[0].expansion.vertical = true;
} }
args.unexpected(&mut ctx.f);
let layouted = layout_tree(&content, ctx).await; let layouted = layout_tree(&content, ctx).await;
let layout = layouted.output.into_iter().next().unwrap(); let layout = layouted.into_iter().next().unwrap();
f.extend(layouted.feedback);
args.unexpected(&mut f); Value::Commands(vec![Add(layout)])
Pass::commands(vec![Add(layout)], f)
} }

View File

@ -2,12 +2,12 @@ use super::*;
use crate::color::RgbaColor; use crate::color::RgbaColor;
/// `rgb`: Create an RGB(A) color. /// `rgb`: Create an RGB(A) color.
pub async fn rgb(span: Span, mut args: DictValue, _: LayoutContext<'_>) -> Pass<Value> { pub async fn rgb(mut args: DictValue, ctx: &mut LayoutContext) -> Value {
let mut f = Feedback::new(); let mut f = Feedback::new();
let r = args.expect::<Spanned<i64>>("red value", span, &mut f); let r = args.expect::<Spanned<i64>>("red value", Span::ZERO, &mut f);
let g = args.expect::<Spanned<i64>>("green value", span, &mut f); let g = args.expect::<Spanned<i64>>("green value", Span::ZERO, &mut f);
let b = args.expect::<Spanned<i64>>("blue value", span, &mut f); let b = args.expect::<Spanned<i64>>("blue value", Span::ZERO, &mut f);
let a = args.take::<Spanned<i64>>(); let a = args.take::<Spanned<i64>>();
let mut clamp = |component: Option<Spanned<i64>>, default| { let mut clamp = |component: Option<Spanned<i64>>, default| {
@ -19,8 +19,11 @@ pub async fn rgb(span: Span, mut args: DictValue, _: LayoutContext<'_>) -> Pass<
}) })
}; };
let color = RgbaColor::new(clamp(r, 0), clamp(g, 0), clamp(b, 0), clamp(a, 255)); args.unexpected(&mut ctx.f);
Value::Color(RgbaColor::new(
args.unexpected(&mut f); clamp(r, 0),
Pass::new(Value::Color(color), f) clamp(g, 0),
clamp(b, 0),
clamp(a, 255),
))
} }

View File

@ -49,9 +49,8 @@ use crate::geom::Linear;
/// ```typst /// ```typst
/// [font: "My Serif", serif] /// [font: "My Serif", serif]
/// ``` /// ```
pub async fn font(_: Span, mut args: DictValue, ctx: LayoutContext<'_>) -> Pass<Value> { pub async fn font(mut args: DictValue, ctx: &mut LayoutContext) -> Value {
let mut f = Feedback::new(); let mut text = ctx.state.text.clone();
let mut text = ctx.style.text.clone();
let mut updated_fallback = false; let mut updated_fallback = false;
let content = args.take::<SynTree>(); let content = args.take::<SynTree>();
@ -75,15 +74,15 @@ pub async fn font(_: Span, mut args: DictValue, ctx: LayoutContext<'_>) -> Pass<
updated_fallback = true; updated_fallback = true;
} }
if let Some(style) = args.take_key::<FontStyle>("style", &mut f) { if let Some(style) = args.take_key::<FontStyle>("style", &mut ctx.f) {
text.variant.style = style; text.variant.style = style;
} }
if let Some(weight) = args.take_key::<FontWeight>("weight", &mut f) { if let Some(weight) = args.take_key::<FontWeight>("weight", &mut ctx.f) {
text.variant.weight = weight; text.variant.weight = weight;
} }
if let Some(stretch) = args.take_key::<FontStretch>("stretch", &mut f) { if let Some(stretch) = args.take_key::<FontStretch>("stretch", &mut ctx.f) {
text.variant.stretch = stretch; text.variant.stretch = stretch;
} }
@ -101,15 +100,13 @@ pub async fn font(_: Span, mut args: DictValue, ctx: LayoutContext<'_>) -> Pass<
text.fallback.flatten(); text.fallback.flatten();
} }
let commands = match content { args.unexpected(&mut ctx.f);
Value::Commands(match content {
Some(tree) => vec![ Some(tree) => vec![
SetTextStyle(text), SetTextState(text),
LayoutSyntaxTree(tree), LayoutSyntaxTree(tree),
SetTextStyle(ctx.style.text.clone()), SetTextState(ctx.state.text.clone()),
], ],
None => vec![SetTextStyle(text)], None => vec![SetTextState(text)],
}; })
args.unexpected(&mut f);
Pass::commands(commands, f)
} }

View File

@ -30,7 +30,7 @@ macro_rules! std {
macro_rules! wrap { macro_rules! wrap {
($func:expr) => { ($func:expr) => {
FuncValue::new(|name, args, ctx| Box::pin($func(name, args, ctx))) FuncValue::new(|args, ctx| Box::pin($func(args, ctx)))
}; };
} }

View File

@ -19,56 +19,54 @@ use crate::paper::{Paper, PaperClass};
/// - `top`: The top margin (length or relative to height). /// - `top`: The top margin (length or relative to height).
/// - `bottom`: The bottom margin (length or relative to height). /// - `bottom`: The bottom margin (length or relative to height).
/// - `flip`: Flips custom or paper-defined width and height (boolean). /// - `flip`: Flips custom or paper-defined width and height (boolean).
pub async fn page(_: Span, mut args: DictValue, ctx: LayoutContext<'_>) -> Pass<Value> { pub async fn page(mut args: DictValue, ctx: &mut LayoutContext) -> Value {
let mut f = Feedback::new(); let mut page = ctx.state.page.clone();
let mut style = ctx.style.page;
if let Some(paper) = args.take::<Paper>() { if let Some(paper) = args.take::<Paper>() {
style.class = paper.class; page.class = paper.class;
style.size = paper.size(); page.size = paper.size();
} }
if let Some(Abs(width)) = args.take_key::<Abs>("width", &mut f) { if let Some(Abs(width)) = args.take_key::<Abs>("width", &mut ctx.f) {
style.class = PaperClass::Custom; page.class = PaperClass::Custom;
style.size.width = width; page.size.width = width;
} }
if let Some(Abs(height)) = args.take_key::<Abs>("height", &mut f) { if let Some(Abs(height)) = args.take_key::<Abs>("height", &mut ctx.f) {
style.class = PaperClass::Custom; page.class = PaperClass::Custom;
style.size.height = height; page.size.height = height;
} }
if let Some(margins) = args.take_key::<Linear>("margins", &mut f) { if let Some(margins) = args.take_key::<Linear>("margins", &mut ctx.f) {
style.margins = Sides::uniform(Some(margins)); page.margins = Sides::uniform(Some(margins));
} }
if let Some(left) = args.take_key::<Linear>("left", &mut f) { if let Some(left) = args.take_key::<Linear>("left", &mut ctx.f) {
style.margins.left = Some(left); page.margins.left = Some(left);
} }
if let Some(top) = args.take_key::<Linear>("top", &mut f) { if let Some(top) = args.take_key::<Linear>("top", &mut ctx.f) {
style.margins.top = Some(top); page.margins.top = Some(top);
} }
if let Some(right) = args.take_key::<Linear>("right", &mut f) { if let Some(right) = args.take_key::<Linear>("right", &mut ctx.f) {
style.margins.right = Some(right); page.margins.right = Some(right);
} }
if let Some(bottom) = args.take_key::<Linear>("bottom", &mut f) { if let Some(bottom) = args.take_key::<Linear>("bottom", &mut ctx.f) {
style.margins.bottom = Some(bottom); page.margins.bottom = Some(bottom);
} }
if args.take_key::<bool>("flip", &mut f).unwrap_or(false) { if args.take_key::<bool>("flip", &mut ctx.f).unwrap_or(false) {
mem::swap(&mut style.size.width, &mut style.size.height); mem::swap(&mut page.size.width, &mut page.size.height);
} }
args.unexpected(&mut f); args.unexpected(&mut ctx.f);
Pass::commands(vec![SetPageStyle(style)], f) Value::Commands(vec![SetPageState(page)])
} }
/// `pagebreak`: Ends the current page. /// `pagebreak`: Ends the current page.
pub async fn pagebreak(_: Span, args: DictValue, _: LayoutContext<'_>) -> Pass<Value> { pub async fn pagebreak(args: DictValue, ctx: &mut LayoutContext) -> Value {
let mut f = Feedback::new(); args.unexpected(&mut ctx.f);
args.unexpected(&mut f); Value::Commands(vec![BreakPage])
Pass::commands(vec![BreakPage], f)
} }

View File

@ -6,35 +6,26 @@ use crate::layout::SpacingKind;
/// ///
/// # Positional arguments /// # Positional arguments
/// - The spacing (length or relative to font size). /// - The spacing (length or relative to font size).
pub async fn h(name: Span, args: DictValue, ctx: LayoutContext<'_>) -> Pass<Value> { pub async fn h(args: DictValue, ctx: &mut LayoutContext) -> Value {
spacing(name, args, ctx, SpecAxis::Horizontal) spacing(args, ctx, SpecAxis::Horizontal)
} }
/// `v`: Add vertical spacing. /// `v`: Add vertical spacing.
/// ///
/// # Positional arguments /// # Positional arguments
/// - The spacing (length or relative to font size). /// - The spacing (length or relative to font size).
pub async fn v(name: Span, args: DictValue, ctx: LayoutContext<'_>) -> Pass<Value> { pub async fn v(args: DictValue, ctx: &mut LayoutContext) -> Value {
spacing(name, args, ctx, SpecAxis::Vertical) spacing(args, ctx, SpecAxis::Vertical)
} }
fn spacing( fn spacing(mut args: DictValue, ctx: &mut LayoutContext, axis: SpecAxis) -> Value {
name: Span, let spacing = args.expect::<Linear>("spacing", Span::ZERO, &mut ctx.f);
mut args: DictValue, args.unexpected(&mut ctx.f);
ctx: LayoutContext<'_>, Value::Commands(if let Some(spacing) = spacing {
axis: SpecAxis, let axis = axis.to_gen(ctx.state.sys);
) -> Pass<Value> { let spacing = spacing.eval(ctx.state.text.font_size());
let mut f = Feedback::new();
let spacing = args.expect::<Linear>("spacing", name, &mut f);
let commands = if let Some(spacing) = spacing {
let axis = axis.to_gen(ctx.sys);
let spacing = spacing.eval(ctx.style.text.font_size());
vec![AddSpacing(spacing, SpacingKind::Hard, axis)] vec![AddSpacing(spacing, SpacingKind::Hard, axis)]
} else { } else {
vec![] vec![]
}; })
args.unexpected(&mut f);
Pass::commands(commands, f)
} }

View File

@ -7,20 +7,20 @@
use fontdock::{FaceId, FaceQuery, FallbackTree, FontStyle, FontVariant}; use fontdock::{FaceId, FaceQuery, FallbackTree, FontStyle, FontVariant};
use ttf_parser::GlyphId; use ttf_parser::GlyphId;
use crate::eval::TextState;
use crate::font::FontLoader; use crate::font::FontLoader;
use crate::geom::{Point, Size}; use crate::geom::{Point, Size};
use crate::layout::{BoxLayout, Dir, LayoutAlign, LayoutElement, LayoutElements, Shaped}; use crate::layout::{BoxLayout, Dir, LayoutAlign, LayoutElement, LayoutElements, Shaped};
use crate::style::TextStyle;
/// Shape text into a box. /// Shape text into a box.
pub async fn shape( pub async fn shape(
text: &str, text: &str,
dir: Dir, dir: Dir,
align: LayoutAlign, align: LayoutAlign,
style: &TextStyle, state: &TextState,
loader: &mut FontLoader, loader: &mut FontLoader,
) -> BoxLayout { ) -> BoxLayout {
Shaper::new(text, dir, align, style, loader).shape().await Shaper::new(text, dir, align, state, loader).shape().await
} }
/// Performs super-basic text shaping. /// Performs super-basic text shaping.
@ -40,16 +40,16 @@ impl<'a> Shaper<'a> {
text: &'a str, text: &'a str,
dir: Dir, dir: Dir,
align: LayoutAlign, align: LayoutAlign,
style: &'a TextStyle, state: &'a TextState,
loader: &'a mut FontLoader, loader: &'a mut FontLoader,
) -> Self { ) -> Self {
let mut variant = style.variant; let mut variant = state.variant;
if style.strong { if state.strong {
variant.weight = variant.weight.thicken(300); variant.weight = variant.weight.thicken(300);
} }
if style.emph { if state.emph {
variant.style = match variant.style { variant.style = match variant.style {
FontStyle::Normal => FontStyle::Italic, FontStyle::Normal => FontStyle::Italic,
FontStyle::Italic => FontStyle::Normal, FontStyle::Italic => FontStyle::Normal,
@ -61,11 +61,11 @@ impl<'a> Shaper<'a> {
text, text,
dir, dir,
variant, variant,
fallback: &style.fallback, fallback: &state.fallback,
loader, loader,
shaped: Shaped::new(FaceId::MAX, style.font_size()), shaped: Shaped::new(FaceId::MAX, state.font_size()),
layout: BoxLayout { layout: BoxLayout {
size: Size::new(0.0, style.font_size()), size: Size::new(0.0, state.font_size()),
align, align,
elements: LayoutElements::new(), elements: LayoutElements::new(),
}, },

View File

@ -3,7 +3,7 @@
use crate::eval::Value; use crate::eval::Value;
use crate::layout::LayoutContext; use crate::layout::LayoutContext;
use crate::syntax::{Decoration, Ident, Lit, LitDict, SpanWith, Spanned}; use crate::syntax::{Decoration, Ident, Lit, LitDict, SpanWith, Spanned};
use crate::{DynFuture, Feedback}; use crate::DynFuture;
/// An expression. /// An expression.
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
@ -20,17 +20,13 @@ pub enum Expr {
impl Expr { impl Expr {
/// Evaluate the expression to a value. /// Evaluate the expression to a value.
pub fn eval<'a>( pub fn eval<'a>(&'a self, ctx: &'a mut LayoutContext) -> DynFuture<'a, Value> {
&'a self,
ctx: &'a LayoutContext<'a>,
f: &'a mut Feedback,
) -> DynFuture<'a, Value> {
Box::pin(async move { Box::pin(async move {
match self { match self {
Self::Lit(lit) => lit.eval(ctx, f).await, Self::Lit(lit) => lit.eval(ctx).await,
Self::Unary(unary) => unary.eval(ctx, f).await, Self::Unary(unary) => unary.eval(ctx).await,
Self::Binary(binary) => binary.eval(ctx, f).await, Self::Binary(binary) => binary.eval(ctx).await,
Self::Call(call) => call.eval(ctx, f).await, Self::Call(call) => call.eval(ctx).await,
} }
}) })
} }
@ -47,10 +43,10 @@ pub struct ExprUnary {
impl ExprUnary { impl ExprUnary {
/// Evaluate the expression to a value. /// Evaluate the expression to a value.
pub async fn eval(&self, ctx: &LayoutContext<'_>, f: &mut Feedback) -> Value { pub async fn eval(&self, ctx: &mut LayoutContext) -> Value {
use Value::*; use Value::*;
let value = self.expr.v.eval(ctx, f).await; let value = self.expr.v.eval(ctx).await;
if value == Error { if value == Error {
return Error; return Error;
} }
@ -64,7 +60,7 @@ impl ExprUnary {
Relative(x) => Relative(-x), Relative(x) => Relative(-x),
Linear(x) => Linear(-x), Linear(x) => Linear(-x),
v => { v => {
error!(@f, span, "cannot negate {}", v.name()); error!(@ctx.f, span, "cannot negate {}", v.name());
Value::Error Value::Error
} }
}, },
@ -92,12 +88,12 @@ pub struct ExprBinary {
impl ExprBinary { impl ExprBinary {
/// Evaluate the expression to a value. /// Evaluate the expression to a value.
pub async fn eval(&self, ctx: &LayoutContext<'_>, f: &mut Feedback) -> Value { pub async fn eval(&self, ctx: &mut LayoutContext) -> Value {
use crate::geom::Linear as Lin; use crate::geom::Linear as Lin;
use Value::*; use Value::*;
let lhs = self.lhs.v.eval(ctx, f).await; let lhs = self.lhs.v.eval(ctx).await;
let rhs = self.rhs.v.eval(ctx, f).await; let rhs = self.rhs.v.eval(ctx).await;
if lhs == Error || rhs == Error { if lhs == Error || rhs == Error {
return Error; return Error;
@ -131,7 +127,7 @@ impl ExprBinary {
(Commands(a), Commands(b)) => Commands(concat(a, b)), (Commands(a), Commands(b)) => Commands(concat(a, b)),
(a, b) => { (a, b) => {
error!(@f, span, "cannot add {} and {}", a.name(), b.name()); error!(@ctx.f, span, "cannot add {} and {}", a.name(), b.name());
Value::Error Value::Error
} }
}, },
@ -155,7 +151,7 @@ impl ExprBinary {
(Linear(a), Linear(b)) => Linear(a - b), (Linear(a), Linear(b)) => Linear(a - b),
(a, b) => { (a, b) => {
error!(@f, span, "cannot subtract {1} from {0}", a.name(), b.name()); error!(@ctx.f, span, "cannot subtract {1} from {0}", a.name(), b.name());
Value::Error Value::Error
} }
}, },
@ -186,7 +182,7 @@ impl ExprBinary {
(Str(a), Int(b)) => Str(a.repeat(b.max(0) as usize)), (Str(a), Int(b)) => Str(a.repeat(b.max(0) as usize)),
(a, b) => { (a, b) => {
error!(@f, span, "cannot multiply {} with {}", a.name(), b.name()); error!(@ctx.f, span, "cannot multiply {} with {}", a.name(), b.name());
Value::Error Value::Error
} }
}, },
@ -207,7 +203,7 @@ impl ExprBinary {
(Linear(a), Float(b)) => Linear(a / b), (Linear(a), Float(b)) => Linear(a / b),
(a, b) => { (a, b) => {
error!(@f, span, "cannot divide {} by {}", a.name(), b.name()); error!(@ctx.f, span, "cannot divide {} by {}", a.name(), b.name());
Value::Error Value::Error
} }
}, },
@ -248,20 +244,18 @@ pub struct ExprCall {
impl ExprCall { impl ExprCall {
/// Evaluate the call expression to a value. /// Evaluate the call expression to a value.
pub async fn eval(&self, ctx: &LayoutContext<'_>, f: &mut Feedback) -> Value { pub async fn eval(&self, ctx: &mut LayoutContext) -> Value {
let name = &self.name.v; let name = &self.name.v;
let span = self.name.span; let span = self.name.span;
let args = self.args.eval(ctx, f).await; let args = self.args.eval(ctx).await;
if let Some(func) = ctx.scope.func(name) { if let Some(func) = ctx.state.scope.func(name) {
let pass = func(span, args, ctx.clone()).await; ctx.f.decorations.push(Decoration::Resolved.span_with(span));
f.extend(pass.feedback); (func.clone())(args, ctx).await
f.decorations.push(Decoration::Resolved.span_with(span));
pass.output
} else { } else {
if !name.is_empty() { if !name.is_empty() {
error!(@f, span, "unknown function"); error!(@ctx.f, span, "unknown function");
f.decorations.push(Decoration::Unresolved.span_with(span)); ctx.f.decorations.push(Decoration::Unresolved.span_with(span));
} }
Value::Dict(args) Value::Dict(args)
} }

View File

@ -5,7 +5,7 @@ use crate::eval::{DictKey, DictValue, SpannedEntry, Value};
use crate::layout::LayoutContext; use crate::layout::LayoutContext;
use crate::length::Length; use crate::length::Length;
use crate::syntax::{Expr, Ident, SpanWith, Spanned, SynTree}; use crate::syntax::{Expr, Ident, SpanWith, Spanned, SynTree};
use crate::{DynFuture, Feedback}; use crate::DynFuture;
/// A literal. /// A literal.
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
@ -39,7 +39,7 @@ pub enum Lit {
impl Lit { impl Lit {
/// Evaluate the dictionary literal to a dictionary value. /// Evaluate the dictionary literal to a dictionary value.
pub async fn eval(&self, ctx: &LayoutContext<'_>, f: &mut Feedback) -> Value { pub async fn eval(&self, ctx: &mut LayoutContext) -> Value {
match *self { match *self {
Lit::Ident(ref i) => Value::Ident(i.clone()), Lit::Ident(ref i) => Value::Ident(i.clone()),
Lit::Bool(b) => Value::Bool(b), Lit::Bool(b) => Value::Bool(b),
@ -49,7 +49,7 @@ impl Lit {
Lit::Percent(p) => Value::Relative(p / 100.0), Lit::Percent(p) => Value::Relative(p / 100.0),
Lit::Color(c) => Value::Color(c), Lit::Color(c) => Value::Color(c),
Lit::Str(ref s) => Value::Str(s.clone()), Lit::Str(ref s) => Value::Str(s.clone()),
Lit::Dict(ref d) => Value::Dict(d.eval(ctx, f).await), Lit::Dict(ref d) => Value::Dict(d.eval(ctx).await),
Lit::Content(ref c) => Value::Tree(c.clone()), Lit::Content(ref c) => Value::Tree(c.clone()),
} }
} }
@ -66,16 +66,12 @@ impl LitDict {
} }
/// Evaluate the dictionary literal to a dictionary value. /// Evaluate the dictionary literal to a dictionary value.
pub fn eval<'a>( pub fn eval<'a>(&'a self, ctx: &'a mut LayoutContext) -> DynFuture<'a, DictValue> {
&'a self,
ctx: &'a LayoutContext<'a>,
f: &'a mut Feedback,
) -> DynFuture<'a, DictValue> {
Box::pin(async move { Box::pin(async move {
let mut dict = DictValue::new(); let mut dict = DictValue::new();
for entry in &self.0 { for entry in &self.0 {
let val = entry.expr.v.eval(ctx, f).await; let val = entry.expr.v.eval(ctx).await;
let spanned = val.span_with(entry.expr.span); let spanned = val.span_with(entry.expr.span);
if let Some(key) = &entry.key { if let Some(key) = &entry.key {
dict.insert(&key.v, SpannedEntry::new(key.span, spanned)); dict.insert(&key.v, SpannedEntry::new(key.span, spanned));

View File

@ -11,6 +11,7 @@ use futures_executor::block_on;
use raqote::{DrawTarget, PathBuilder, SolidSource, Source, Transform, Vector}; use raqote::{DrawTarget, PathBuilder, SolidSource, Source, Transform, Vector};
use ttf_parser::OutlineBuilder; use ttf_parser::OutlineBuilder;
use typstc::eval::State;
use typstc::export::pdf; use typstc::export::pdf;
use typstc::font::{FontLoader, SharedFontLoader}; use typstc::font::{FontLoader, SharedFontLoader};
use typstc::geom::{Point, Vec2}; use typstc::geom::{Point, Vec2};
@ -69,12 +70,11 @@ fn main() {
fn test(name: &str, src: &str, src_path: &Path, loader: &SharedFontLoader) { fn test(name: &str, src: &str, src_path: &Path, loader: &SharedFontLoader) {
println!("Testing {}.", name); println!("Testing {}.", name);
let style = Default::default(); let state = State::default();
let scope = typstc::library::_std();
let Pass { let Pass {
output: layouts, output: layouts,
feedback: Feedback { mut diagnostics, .. }, feedback: Feedback { mut diagnostics, .. },
} = block_on(typeset(&src, &style, &scope, Rc::clone(loader))); } = block_on(typeset(&src, state, Rc::clone(loader)));
if !diagnostics.is_empty() { if !diagnostics.is_empty() {
diagnostics.sort(); diagnostics.sort();