From 264a7dedd42e27cd9e604037640cf0594b2ec46b Mon Sep 17 00:00:00 2001 From: Laurenz Date: Fri, 19 Mar 2021 17:57:31 +0100 Subject: [PATCH] =?UTF-8?q?Scheduled=20maintenance=20=F0=9F=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - New naming scheme - TextNode instead of NodeText - CallExpr instead of ExprCall - ... - Less glob imports - Removes Value::Args variant - Removes prelude - Renames Layouted to Fragment - Moves font into env - Moves shaping into layout - Moves frame into separate module --- bench/src/bench.rs | 3 +- src/env.rs | 79 ++++++++++++++++++++++++- src/eval/capture.rs | 5 +- src/eval/mod.rs | 52 ++++++++--------- src/eval/ops.rs | 7 ++- src/eval/value.rs | 87 ++++++++++++++-------------- src/exec/context.rs | 49 ++++++++-------- src/exec/mod.rs | 16 +++--- src/exec/state.rs | 2 +- src/font.rs | 68 ---------------------- src/geom/angle.rs | 2 - src/geom/mod.rs | 1 + src/layout/background.rs | 12 ++-- src/layout/fixed.rs | 11 ++-- src/layout/frame.rs | 82 ++++++++++++++++++++++++++ src/layout/mod.rs | 106 +++++----------------------------- src/layout/node.rs | 24 ++++---- src/layout/pad.rs | 11 ++-- src/layout/par.rs | 20 +++---- src/{ => layout}/shaping.rs | 2 +- src/layout/spacing.rs | 16 +++--- src/layout/stack.rs | 20 +++---- src/layout/text.rs | 17 +++--- src/lib.rs | 14 ++--- src/library/align.rs | 4 +- src/library/base.rs | 6 +- src/library/font.rs | 5 +- src/library/image.rs | 12 ++-- src/library/mod.rs | 112 +++++++++++++++++++----------------- src/library/pad.rs | 9 ++- src/library/page.rs | 6 +- src/library/par.rs | 7 ++- src/library/shapes.rs | 9 +-- src/library/spacing.rs | 9 +-- src/main.rs | 3 +- src/parse/mod.rs | 78 ++++++++++++------------- src/parse/resolve.rs | 12 ++-- src/parse/tokens.rs | 18 +++--- src/prelude.rs | 16 ------ src/pretty.rs | 100 ++++++++++++++++---------------- src/syntax/expr.rs | 78 ++++++++++++------------- src/syntax/ident.rs | 2 +- src/syntax/mod.rs | 2 + src/syntax/node.rs | 10 ++-- src/syntax/span.rs | 2 +- src/syntax/token.rs | 24 ++++---- src/syntax/visit.rs | 34 +++++------ tests/README.md | 26 +++++---- tests/typeset.rs | 26 +++++---- 49 files changed, 663 insertions(+), 653 deletions(-) delete mode 100644 src/font.rs create mode 100644 src/layout/frame.rs rename src/{ => layout}/shaping.rs (99%) delete mode 100644 src/prelude.rs diff --git a/bench/src/bench.rs b/bench/src/bench.rs index cf7527693..afb827b81 100644 --- a/bench/src/bench.rs +++ b/bench/src/bench.rs @@ -1,11 +1,10 @@ use criterion::{criterion_group, criterion_main, Criterion}; use fontdock::fs::FsIndex; -use typst::env::{Env, ResourceLoader}; +use typst::env::{Env, FsIndexExt, ResourceLoader}; use typst::eval::eval; use typst::exec::{exec, State}; use typst::export::pdf; -use typst::font::FsIndexExt; use typst::layout::layout; use typst::library; use typst::parse::parse; diff --git a/src/env.rs b/src/env.rs index 66cc5e191..3db71e087 100644 --- a/src/env.rs +++ b/src/env.rs @@ -7,11 +7,13 @@ use std::fs; use std::io::Cursor; use std::path::{Path, PathBuf}; -use fontdock::fs::FsSource; +use fontdock::{ContainsChar, FaceFromVec, FaceId, FontSource}; use image::io::Reader as ImageReader; use image::{DynamicImage, GenericImageView, ImageFormat}; +use ttf_parser::Face; -use crate::font::FontLoader; +#[cfg(feature = "fs")] +use fontdock::fs::{FsIndex, FsSource}; /// Encapsulates all environment dependencies (fonts, resources). #[derive(Debug)] @@ -25,13 +27,84 @@ pub struct Env { impl Env { /// Create an empty environment for testing purposes. pub fn blank() -> Self { + struct BlankSource; + + impl FontSource for BlankSource { + type Face = FaceBuf; + + fn load(&self, _: FaceId) -> Option { + None + } + } + Self { - fonts: FontLoader::new(Box::new(FsSource::new(vec![])), vec![]), + fonts: FontLoader::new(Box::new(BlankSource), vec![]), resources: ResourceLoader::new(), } } } +/// A font loader that is backed by a dynamic source. +pub type FontLoader = fontdock::FontLoader>>; + +/// An owned font face. +pub struct FaceBuf { + data: Box<[u8]>, + face: Face<'static>, +} + +impl FaceBuf { + /// Get a reference to the underlying face. + pub fn get(&self) -> &Face<'_> { + // We can't implement Deref because that would leak the internal 'static + // lifetime. + &self.face + } + + /// The raw face data. + pub fn data(&self) -> &[u8] { + &self.data + } +} + +impl FaceFromVec for FaceBuf { + fn from_vec(vec: Vec, i: u32) -> Option { + let data = vec.into_boxed_slice(); + + // SAFETY: The slices's location is stable in memory since we don't + // touch it and it can't be touched from outside this type. + let slice: &'static [u8] = + unsafe { std::slice::from_raw_parts(data.as_ptr(), data.len()) }; + + Some(Self { + data, + face: Face::from_slice(slice, i).ok()?, + }) + } +} + +impl ContainsChar for FaceBuf { + fn contains_char(&self, c: char) -> bool { + self.get().glyph_index(c).is_some() + } +} + +/// Simplify font loader construction from an [`FsIndex`]. +#[cfg(feature = "fs")] +pub trait FsIndexExt { + /// Create a font loader backed by a boxed [`FsSource`] which serves all + /// indexed font faces. + fn into_dynamic_loader(self) -> FontLoader; +} + +#[cfg(feature = "fs")] +impl FsIndexExt for FsIndex { + fn into_dynamic_loader(self) -> FontLoader { + let (files, descriptors) = self.into_vecs(); + FontLoader::new(Box::new(FsSource::new(files)), descriptors) + } +} + /// Loads resource from the file system. pub struct ResourceLoader { paths: HashMap, diff --git a/src/eval/capture.rs b/src/eval/capture.rs index 182468f7e..c0354cd30 100644 --- a/src/eval/capture.rs +++ b/src/eval/capture.rs @@ -1,7 +1,8 @@ use std::rc::Rc; -use super::*; -use crate::syntax::visit::*; +use super::{Scope, Scopes, Value}; +use crate::syntax::visit::{visit_expr, Visit}; +use crate::syntax::{Expr, Ident}; /// A visitor that captures variable slots. #[derive(Debug)] diff --git a/src/eval/mod.rs b/src/eval/mod.rs index 524ef43a8..6d8edf794 100644 --- a/src/eval/mod.rs +++ b/src/eval/mod.rs @@ -13,9 +13,9 @@ pub use value::*; use std::collections::HashMap; use std::rc::Rc; -use super::*; use crate::color::Color; -use crate::diag::{Diag, DiagSet}; +use crate::diag::{Diag, DiagSet, Pass}; +use crate::env::Env; use crate::geom::{Angle, Length, Relative}; use crate::syntax::visit::Visit; use crate::syntax::*; @@ -143,16 +143,16 @@ impl Eval for Lit { } } -impl Eval for ExprArray { - type Output = ValueArray; +impl Eval for ArrayExpr { + type Output = ArrayValue; fn eval(&self, ctx: &mut EvalContext) -> Self::Output { self.items.iter().map(|expr| expr.eval(ctx)).collect() } } -impl Eval for ExprDict { - type Output = ValueDict; +impl Eval for DictExpr { + type Output = DictValue; fn eval(&self, ctx: &mut EvalContext) -> Self::Output { self.items @@ -162,7 +162,7 @@ impl Eval for ExprDict { } } -impl Eval for ExprTemplate { +impl Eval for TemplateExpr { type Output = TemplateNode; fn eval(&self, ctx: &mut EvalContext) -> Self::Output { @@ -172,7 +172,7 @@ impl Eval for ExprTemplate { } } -impl Eval for ExprGroup { +impl Eval for GroupExpr { type Output = Value; fn eval(&self, ctx: &mut EvalContext) -> Self::Output { @@ -180,7 +180,7 @@ impl Eval for ExprGroup { } } -impl Eval for ExprBlock { +impl Eval for BlockExpr { type Output = Value; fn eval(&self, ctx: &mut EvalContext) -> Self::Output { @@ -201,7 +201,7 @@ impl Eval for ExprBlock { } } -impl Eval for ExprUnary { +impl Eval for UnaryExpr { type Output = Value; fn eval(&self, ctx: &mut EvalContext) -> Self::Output { @@ -230,7 +230,7 @@ impl Eval for ExprUnary { } } -impl Eval for ExprBinary { +impl Eval for BinaryExpr { type Output = Value; fn eval(&self, ctx: &mut EvalContext) -> Self::Output { @@ -256,7 +256,7 @@ impl Eval for ExprBinary { } } -impl ExprBinary { +impl BinaryExpr { /// Apply a basic binary operation. fn apply(&self, ctx: &mut EvalContext, op: F) -> Value where @@ -335,7 +335,7 @@ impl ExprBinary { } } -impl Eval for ExprCall { +impl Eval for CallExpr { type Output = Value; fn eval(&self, ctx: &mut EvalContext) -> Self::Output { @@ -361,25 +361,25 @@ impl Eval for ExprCall { } } -impl Eval for ExprArgs { - type Output = ValueArgs; +impl Eval for CallArgs { + type Output = FuncArgs; fn eval(&self, ctx: &mut EvalContext) -> Self::Output { let items = self.items.iter().map(|arg| arg.eval(ctx)).collect(); - ValueArgs { span: self.span, items } + FuncArgs { span: self.span, items } } } -impl Eval for ExprArg { - type Output = ValueArg; +impl Eval for CallArg { + type Output = FuncArg; fn eval(&self, ctx: &mut EvalContext) -> Self::Output { match self { - Self::Pos(expr) => ValueArg { + Self::Pos(expr) => FuncArg { name: None, value: Spanned::new(expr.eval(ctx), expr.span()), }, - Self::Named(Named { name, expr }) => ValueArg { + Self::Named(Named { name, expr }) => FuncArg { name: Some(Spanned::new(name.string.clone(), name.span)), value: Spanned::new(expr.eval(ctx), expr.span()), }, @@ -387,7 +387,7 @@ impl Eval for ExprArg { } } -impl Eval for ExprClosure { +impl Eval for ClosureExpr { type Output = Value; fn eval(&self, ctx: &mut EvalContext) -> Self::Output { @@ -402,7 +402,7 @@ impl Eval for ExprClosure { }; let name = self.name.as_ref().map(|id| id.to_string()); - Value::Func(ValueFunc::new(name, move |ctx, args| { + Value::Func(FuncValue::new(name, move |ctx, args| { // Don't leak the scopes from the call site. Instead, we use the // scope of captured variables we collected earlier. let prev = std::mem::take(&mut ctx.scopes); @@ -422,7 +422,7 @@ impl Eval for ExprClosure { } } -impl Eval for ExprLet { +impl Eval for LetExpr { type Output = Value; fn eval(&self, ctx: &mut EvalContext) -> Self::Output { @@ -435,7 +435,7 @@ impl Eval for ExprLet { } } -impl Eval for ExprIf { +impl Eval for IfExpr { type Output = Value; fn eval(&self, ctx: &mut EvalContext) -> Self::Output { @@ -461,7 +461,7 @@ impl Eval for ExprIf { } } -impl Eval for ExprWhile { +impl Eval for WhileExpr { type Output = Value; fn eval(&self, ctx: &mut EvalContext) -> Self::Output { @@ -493,7 +493,7 @@ impl Eval for ExprWhile { } } -impl Eval for ExprFor { +impl Eval for ForExpr { type Output = Value; fn eval(&self, ctx: &mut EvalContext) -> Self::Output { diff --git a/src/eval/ops.rs b/src/eval/ops.rs index c4adf5873..bef4dd58f 100644 --- a/src/eval/ops.rs +++ b/src/eval/ops.rs @@ -1,4 +1,5 @@ -use super::*; +use super::{ArrayValue, DictValue, TemplateNode, Value}; +use crate::syntax::Span; use Value::*; /// Apply the plus operator to a value. @@ -189,12 +190,12 @@ fn value_eq(lhs: &Value, rhs: &Value) -> bool { } /// Compute whether two arrays are equal. -fn array_eq(a: &ValueArray, b: &ValueArray) -> bool { +fn array_eq(a: &ArrayValue, b: &ArrayValue) -> bool { a.len() == b.len() && a.iter().zip(b).all(|(x, y)| value_eq(x, y)) } /// Compute whether two dictionaries are equal. -fn dict_eq(a: &ValueDict, b: &ValueDict) -> bool { +fn dict_eq(a: &DictValue, b: &DictValue) -> bool { a.len() == b.len() && a.iter().all(|(k, x)| b.get(k).map_or(false, |y| value_eq(x, y))) } diff --git a/src/eval/value.rs b/src/eval/value.rs index 206369177..485829f39 100644 --- a/src/eval/value.rs +++ b/src/eval/value.rs @@ -4,11 +4,12 @@ use std::fmt::{self, Debug, Display, Formatter}; use std::ops::Deref; use std::rc::Rc; -use super::*; +use super::{EvalContext, ExprMap}; use crate::color::Color; +use crate::diag::DiagSet; use crate::exec::ExecContext; use crate::geom::{Angle, Length, Linear, Relative}; -use crate::syntax::Tree; +use crate::syntax::{Span, Spanned, Tree}; /// A computational value. #[derive(Debug, Clone, PartialEq)] @@ -34,17 +35,15 @@ pub enum Value { /// A string: `"string"`. Str(String), /// An array value: `(1, "hi", 12cm)`. - Array(ValueArray), + Array(ArrayValue), /// A dictionary value: `(color: #f79143, pattern: dashed)`. - Dict(ValueDict), + Dict(DictValue), /// A template value: `[*Hi* there]`. - Template(ValueTemplate), + Template(TemplateValue), /// An executable function. - Func(ValueFunc), - /// Arguments to a function. - Args(ValueArgs), + Func(FuncValue), /// Any object. - Any(ValueAny), + Any(AnyValue), /// The result of invalid operations. Error, } @@ -71,11 +70,10 @@ impl Value { Self::Linear(_) => Linear::TYPE_NAME, Self::Color(_) => Color::TYPE_NAME, Self::Str(_) => String::TYPE_NAME, - Self::Array(_) => ValueArray::TYPE_NAME, - Self::Dict(_) => ValueDict::TYPE_NAME, - Self::Template(_) => ValueTemplate::TYPE_NAME, - Self::Func(_) => ValueFunc::TYPE_NAME, - Self::Args(_) => ValueArgs::TYPE_NAME, + Self::Array(_) => ArrayValue::TYPE_NAME, + Self::Dict(_) => DictValue::TYPE_NAME, + Self::Template(_) => TemplateValue::TYPE_NAME, + Self::Func(_) => FuncValue::TYPE_NAME, Self::Any(v) => v.type_name(), Self::Error => "error", } @@ -97,13 +95,13 @@ impl Default for Value { } /// An array value: `(1, "hi", 12cm)`. -pub type ValueArray = Vec; +pub type ArrayValue = Vec; /// A dictionary value: `(color: #f79143, pattern: dashed)`. -pub type ValueDict = BTreeMap; +pub type DictValue = BTreeMap; /// A template value: `[*Hi* there]`. -pub type ValueTemplate = Vec; +pub type TemplateValue = Vec; /// One chunk of a template. /// @@ -171,16 +169,16 @@ impl Debug for TemplateFunc { /// A wrapper around a reference-counted executable function. #[derive(Clone)] -pub struct ValueFunc { +pub struct FuncValue { name: Option, - f: Rc Value>, + f: Rc Value>, } -impl ValueFunc { +impl FuncValue { /// Create a new function value from a rust function or closure. pub fn new(name: Option, f: F) -> Self where - F: Fn(&mut EvalContext, &mut ValueArgs) -> Value + 'static, + F: Fn(&mut EvalContext, &mut FuncArgs) -> Value + 'static, { Self { name, f: Rc::new(f) } } @@ -191,22 +189,22 @@ impl ValueFunc { } } -impl PartialEq for ValueFunc { +impl PartialEq for FuncValue { fn eq(&self, _: &Self) -> bool { // TODO: Figure out what we want here. false } } -impl Deref for ValueFunc { - type Target = dyn Fn(&mut EvalContext, &mut ValueArgs) -> Value; +impl Deref for FuncValue { + type Target = dyn Fn(&mut EvalContext, &mut FuncArgs) -> Value; fn deref(&self) -> &Self::Target { self.f.as_ref() } } -impl Debug for ValueFunc { +impl Debug for FuncValue { fn fmt(&self, f: &mut Formatter) -> fmt::Result { f.debug_struct("ValueFunc").field("name", &self.name).finish() } @@ -214,14 +212,14 @@ impl Debug for ValueFunc { /// Evaluated arguments to a function. #[derive(Debug, Clone, PartialEq)] -pub struct ValueArgs { +pub struct FuncArgs { /// The span of the whole argument list. pub span: Span, /// The arguments. - pub items: Vec, + pub items: Vec, } -impl ValueArgs { +impl FuncArgs { /// Find and remove the first convertible positional argument. pub fn find(&mut self, ctx: &mut EvalContext) -> Option where @@ -345,14 +343,14 @@ impl ValueArgs { /// An argument to a function call: `12` or `draw: false`. #[derive(Debug, Clone, PartialEq)] -pub struct ValueArg { +pub struct FuncArg { /// The name of the argument (`None` for positional arguments). pub name: Option>, /// The value of the argument. pub value: Spanned, } -impl ValueArg { +impl FuncArg { /// The source code location. pub fn span(&self) -> Span { match &self.name { @@ -363,9 +361,9 @@ impl ValueArg { } /// A wrapper around a dynamic value. -pub struct ValueAny(Box); +pub struct AnyValue(Box); -impl ValueAny { +impl AnyValue { /// Create a new instance from any value that satisifies the required bounds. pub fn new(any: T) -> Self where @@ -399,25 +397,25 @@ impl ValueAny { } } -impl Clone for ValueAny { +impl Clone for AnyValue { fn clone(&self) -> Self { Self(self.0.dyn_clone()) } } -impl PartialEq for ValueAny { +impl PartialEq for AnyValue { fn eq(&self, other: &Self) -> bool { self.0.dyn_eq(other) } } -impl Debug for ValueAny { +impl Debug for AnyValue { fn fmt(&self, f: &mut Formatter) -> fmt::Result { f.debug_tuple("ValueAny").field(&self.0).finish() } } -impl Display for ValueAny { +impl Display for AnyValue { fn fmt(&self, f: &mut Formatter) -> fmt::Result { Display::fmt(&self.0, f) } @@ -426,7 +424,7 @@ impl Display for ValueAny { trait Bounds: Debug + Display + 'static { fn as_any(&self) -> &dyn Any; fn into_any(self: Box) -> Box; - fn dyn_eq(&self, other: &ValueAny) -> bool; + fn dyn_eq(&self, other: &AnyValue) -> bool; fn dyn_clone(&self) -> Box; fn dyn_type_name(&self) -> &'static str; } @@ -443,7 +441,7 @@ where self } - fn dyn_eq(&self, other: &ValueAny) -> bool { + fn dyn_eq(&self, other: &AnyValue) -> bool { if let Some(other) = other.downcast_ref::() { self == other } else { @@ -584,15 +582,14 @@ primitive! { } primitive! { Color: "color", Value::Color } primitive! { String: "string", Value::Str } -primitive! { ValueArray: "array", Value::Array } -primitive! { ValueDict: "dictionary", Value::Dict } +primitive! { ArrayValue: "array", Value::Array } +primitive! { DictValue: "dictionary", Value::Dict } primitive! { - ValueTemplate: "template", + TemplateValue: "template", Value::Template, Value::Str(v) => vec![TemplateNode::Str(v)], } -primitive! { ValueFunc: "function", Value::Func } -primitive! { ValueArgs: "arguments", Value::Args } +primitive! { FuncValue: "function", Value::Func } impl From for Value { fn from(v: usize) -> Self { @@ -606,8 +603,8 @@ impl From<&str> for Value { } } -impl From for Value { - fn from(v: ValueAny) -> Self { +impl From for Value { + fn from(v: AnyValue) -> Self { Self::Any(v) } } diff --git a/src/exec/context.rs b/src/exec/context.rs index 7fd72fe3b..311619cc6 100644 --- a/src/exec/context.rs +++ b/src/exec/context.rs @@ -3,13 +3,16 @@ use std::rc::Rc; use fontdock::FontStyle; -use super::*; -use crate::diag::{Diag, DiagSet}; +use super::{Exec, State}; +use crate::diag::{Diag, DiagSet, Pass}; +use crate::env::Env; +use crate::eval::TemplateValue; use crate::geom::{Dir, Gen, Linear, Sides, Size}; use crate::layout::{ - Node, NodePad, NodePages, NodePar, NodeSpacing, NodeStack, NodeText, Tree, + Node, PadNode, PageRun, ParNode, SpacingNode, StackNode, TextNode, Tree, }; use crate::parse::is_newline; +use crate::syntax::{Span, Spanned}; /// The context for execution. #[derive(Debug)] @@ -26,9 +29,9 @@ pub struct ExecContext<'a> { page: Option, /// The content of the active stack. This may be the top-level stack for the /// page or a lower one created by [`exec`](Self::exec). - stack: NodeStack, + stack: StackNode, /// The content of the active paragraph. - par: NodePar, + par: ParNode, } impl<'a> ExecContext<'a> { @@ -39,8 +42,8 @@ impl<'a> ExecContext<'a> { diags: DiagSet::new(), tree: Tree { runs: vec![] }, page: Some(PageInfo::new(&state, true)), - stack: NodeStack::new(&state), - par: NodePar::new(&state), + stack: StackNode::new(&state), + par: ParNode::new(&state), state, } } @@ -78,7 +81,7 @@ impl<'a> ExecContext<'a> { /// Push a layout node into the active paragraph. /// /// Spacing nodes will be handled according to their - /// [`softness`](NodeSpacing::softness). + /// [`softness`](SpacingNode::softness). pub fn push(&mut self, node: impl Into) { push(&mut self.par.children, node.into()); } @@ -86,7 +89,7 @@ impl<'a> ExecContext<'a> { /// Push a word space into the active paragraph. pub fn push_space(&mut self) { let em = self.state.font.font_size(); - self.push(NodeSpacing { + self.push(SpacingNode { amount: self.state.par.word_spacing.resolve(em), softness: 1, }); @@ -111,7 +114,7 @@ impl<'a> ExecContext<'a> { /// Apply a forced line break. pub fn push_linebreak(&mut self) { let em = self.state.font.font_size(); - self.push_into_stack(NodeSpacing { + self.push_into_stack(SpacingNode { amount: self.state.par.leading.resolve(em), softness: 2, }); @@ -120,7 +123,7 @@ impl<'a> ExecContext<'a> { /// Apply a forced paragraph break. pub fn push_parbreak(&mut self) { let em = self.state.font.font_size(); - self.push_into_stack(NodeSpacing { + self.push_into_stack(SpacingNode { amount: self.state.par.spacing.resolve(em), softness: 1, }); @@ -134,10 +137,10 @@ impl<'a> ExecContext<'a> { } /// Execute a template and return the result as a stack node. - pub fn exec(&mut self, template: &ValueTemplate) -> NodeStack { + pub fn exec(&mut self, template: &TemplateValue) -> StackNode { let page = self.page.take(); - let stack = mem::replace(&mut self.stack, NodeStack::new(&self.state)); - let par = mem::replace(&mut self.par, NodePar::new(&self.state)); + let stack = mem::replace(&mut self.stack, StackNode::new(&self.state)); + let par = mem::replace(&mut self.par, ParNode::new(&self.state)); template.exec(self); let result = self.finish_stack(); @@ -151,7 +154,7 @@ impl<'a> ExecContext<'a> { /// Construct a text node from the given string based on the active text /// state. - pub fn make_text_node(&self, text: String) -> NodeText { + pub fn make_text_node(&self, text: String) -> TextNode { let mut variant = self.state.font.variant; if self.state.font.strong { @@ -166,7 +169,7 @@ impl<'a> ExecContext<'a> { } } - NodeText { + TextNode { text, dir: self.state.dirs.cross, aligns: self.state.aligns, @@ -180,7 +183,7 @@ impl<'a> ExecContext<'a> { /// Finish the active paragraph. fn finish_par(&mut self) { - let mut par = mem::replace(&mut self.par, NodePar::new(&self.state)); + let mut par = mem::replace(&mut self.par, ParNode::new(&self.state)); trim(&mut par.children); if !par.children.is_empty() { @@ -189,10 +192,10 @@ impl<'a> ExecContext<'a> { } /// Finish the active stack. - fn finish_stack(&mut self) -> NodeStack { + fn finish_stack(&mut self) -> StackNode { self.finish_par(); - let mut stack = mem::replace(&mut self.stack, NodeStack::new(&self.state)); + let mut stack = mem::replace(&mut self.stack, StackNode::new(&self.state)); trim(&mut stack.children); stack @@ -205,9 +208,9 @@ impl<'a> ExecContext<'a> { let stack = self.finish_stack(); if !stack.children.is_empty() || (keep && info.hard) { - self.tree.runs.push(NodePages { + self.tree.runs.push(PageRun { size: info.size, - child: NodePad { + child: PadNode { padding: info.padding, child: stack.into(), } @@ -274,7 +277,7 @@ impl PageInfo { } } -impl NodeStack { +impl StackNode { fn new(state: &State) -> Self { Self { dirs: state.dirs, @@ -284,7 +287,7 @@ impl NodeStack { } } -impl NodePar { +impl ParNode { fn new(state: &State) -> Self { let em = state.font.font_size(); Self { diff --git a/src/exec/mod.rs b/src/exec/mod.rs index 35ffa2b6c..90e5a2255 100644 --- a/src/exec/mod.rs +++ b/src/exec/mod.rs @@ -10,8 +10,8 @@ use std::rc::Rc; use crate::diag::Pass; use crate::env::Env; -use crate::eval::{ExprMap, TemplateFunc, TemplateNode, Value, ValueTemplate}; -use crate::layout::{self, NodeFixed, NodeSpacing, NodeStack}; +use crate::eval::{ExprMap, TemplateFunc, TemplateNode, TemplateValue, Value}; +use crate::layout::{self, FixedNode, SpacingNode, StackNode}; use crate::pretty::pretty; use crate::syntax::*; @@ -77,7 +77,7 @@ impl ExecWithMap for Node { } } -impl ExecWithMap for NodeHeading { +impl ExecWithMap for HeadingNode { fn exec_with_map(&self, ctx: &mut ExecContext, map: &ExprMap) { let prev = ctx.state.clone(); let upscale = 1.5 - 0.1 * self.level as f64; @@ -91,7 +91,7 @@ impl ExecWithMap for NodeHeading { } } -impl Exec for NodeRaw { +impl Exec for RawNode { fn exec(&self, ctx: &mut ExecContext) { let prev = Rc::clone(&ctx.state.font.families); ctx.set_monospace(); @@ -103,7 +103,7 @@ impl Exec for NodeRaw { let mut newline = false; for line in &self.lines { if newline { - children.push(layout::Node::Spacing(NodeSpacing { + children.push(layout::Node::Spacing(SpacingNode { amount: leading, softness: 2, })); @@ -119,10 +119,10 @@ impl Exec for NodeRaw { // This is wrapped in a fixed node to make sure the stack fits to its // content instead of filling the available area. - ctx.push(NodeFixed { + ctx.push(FixedNode { width: None, height: None, - child: NodeStack { + child: StackNode { dirs: ctx.state.dirs, aligns: ctx.state.aligns, children, @@ -159,7 +159,7 @@ impl Exec for Value { } } -impl Exec for ValueTemplate { +impl Exec for TemplateValue { fn exec(&self, ctx: &mut ExecContext) { for node in self { node.exec(ctx); diff --git a/src/exec/state.rs b/src/exec/state.rs index aa2dde1c5..6775f394b 100644 --- a/src/exec/state.rs +++ b/src/exec/state.rs @@ -3,8 +3,8 @@ use std::rc::Rc; use fontdock::{fallback, FallbackTree, FontStretch, FontStyle, FontVariant, FontWeight}; use crate::geom::*; +use crate::layout::VerticalFontMetric; use crate::paper::{Paper, PaperClass, PAPER_A4}; -use crate::shaping::VerticalFontMetric; /// The evaluation state. #[derive(Debug, Clone, PartialEq)] diff --git a/src/font.rs b/src/font.rs deleted file mode 100644 index e57183316..000000000 --- a/src/font.rs +++ /dev/null @@ -1,68 +0,0 @@ -//! Font handling. - -use fontdock::{ContainsChar, FaceFromVec, FontSource}; -use ttf_parser::Face; - -#[cfg(feature = "fs")] -use fontdock::fs::{FsIndex, FsSource}; - -/// A font loader that is backed by a dynamic source. -pub type FontLoader = fontdock::FontLoader>>; - -/// An owned font face. -pub struct FaceBuf { - data: Box<[u8]>, - face: Face<'static>, -} - -impl FaceBuf { - /// Get a reference to the underlying face. - pub fn get(&self) -> &Face<'_> { - // We can't implement Deref because that would leak the internal 'static - // lifetime. - &self.face - } - - /// The raw face data. - pub fn data(&self) -> &[u8] { - &self.data - } -} - -impl FaceFromVec for FaceBuf { - fn from_vec(vec: Vec, i: u32) -> Option { - let data = vec.into_boxed_slice(); - - // SAFETY: The slices's location is stable in memory since we don't - // touch it and it can't be touched from outside this type. - let slice: &'static [u8] = - unsafe { std::slice::from_raw_parts(data.as_ptr(), data.len()) }; - - Some(Self { - data, - face: Face::from_slice(slice, i).ok()?, - }) - } -} - -impl ContainsChar for FaceBuf { - fn contains_char(&self, c: char) -> bool { - self.get().glyph_index(c).is_some() - } -} - -/// Simplify font loader construction from an [`FsIndex`]. -#[cfg(feature = "fs")] -pub trait FsIndexExt { - /// Create a font loader backed by a boxed [`FsSource`] which serves all - /// indexed font faces. - fn into_dynamic_loader(self) -> FontLoader; -} - -#[cfg(feature = "fs")] -impl FsIndexExt for FsIndex { - fn into_dynamic_loader(self) -> FontLoader { - let (files, descriptors) = self.into_vecs(); - FontLoader::new(Box::new(FsSource::new(files)), descriptors) - } -} diff --git a/src/geom/angle.rs b/src/geom/angle.rs index 2392efa58..a0e2877a2 100644 --- a/src/geom/angle.rs +++ b/src/geom/angle.rs @@ -1,5 +1,3 @@ -use std::f64::consts::PI; - use super::*; /// An angle. diff --git a/src/geom/mod.rs b/src/geom/mod.rs index e601cb1f8..5d9068348 100644 --- a/src/geom/mod.rs +++ b/src/geom/mod.rs @@ -26,6 +26,7 @@ pub use sides::*; pub use size::*; pub use spec::*; +use std::f64::consts::PI; use std::fmt::{self, Debug, Display, Formatter}; use std::iter::Sum; use std::ops::*; diff --git a/src/layout/background.rs b/src/layout/background.rs index b331ba3c2..bb155073f 100644 --- a/src/layout/background.rs +++ b/src/layout/background.rs @@ -1,16 +1,16 @@ use super::*; -/// A node that places a rectangular filled background behind another node. +/// A node that places a rectangular filled background behind its child. #[derive(Debug, Clone, PartialEq)] -pub struct NodeBackground { +pub struct BackgroundNode { /// The background fill. pub fill: Fill, /// The child node to be filled. pub child: Node, } -impl Layout for NodeBackground { - fn layout(&self, ctx: &mut LayoutContext, areas: &Areas) -> Layouted { +impl Layout for BackgroundNode { + fn layout(&self, ctx: &mut LayoutContext, areas: &Areas) -> Fragment { let mut layouted = self.child.layout(ctx, areas); for frame in layouted.frames_mut() { @@ -25,8 +25,8 @@ impl Layout for NodeBackground { } } -impl From for NodeAny { - fn from(background: NodeBackground) -> Self { +impl From for AnyNode { + fn from(background: BackgroundNode) -> Self { Self::new(background) } } diff --git a/src/layout/fixed.rs b/src/layout/fixed.rs index a9554a071..e3365668a 100644 --- a/src/layout/fixed.rs +++ b/src/layout/fixed.rs @@ -1,9 +1,8 @@ use super::*; -use crate::geom::Linear; /// A node that can fix its child's width and height. #[derive(Debug, Clone, PartialEq)] -pub struct NodeFixed { +pub struct FixedNode { /// The fixed width, if any. pub width: Option, /// The fixed height, if any. @@ -12,8 +11,8 @@ pub struct NodeFixed { pub child: Node, } -impl Layout for NodeFixed { - fn layout(&self, ctx: &mut LayoutContext, areas: &Areas) -> Layouted { +impl Layout for FixedNode { + fn layout(&self, ctx: &mut LayoutContext, areas: &Areas) -> Fragment { let Areas { current, full, .. } = areas; let size = Size::new( self.width.map(|w| w.resolve(full.width)).unwrap_or(current.width), @@ -31,8 +30,8 @@ impl Layout for NodeFixed { } } -impl From for NodeAny { - fn from(fixed: NodeFixed) -> Self { +impl From for AnyNode { + fn from(fixed: FixedNode) -> Self { Self::new(fixed) } } diff --git a/src/layout/frame.rs b/src/layout/frame.rs new file mode 100644 index 000000000..c85d75392 --- /dev/null +++ b/src/layout/frame.rs @@ -0,0 +1,82 @@ +use super::Shaped; +use crate::color::Color; +use crate::env::ResourceId; +use crate::geom::{Point, Size}; + +/// A finished layout with elements at fixed positions. +#[derive(Debug, Clone, PartialEq)] +pub struct Frame { + /// The size of the frame. + pub size: Size, + /// The elements composing this layout. + pub elements: Vec<(Point, Element)>, +} + +impl Frame { + /// Create a new, empty frame. + pub fn new(size: Size) -> Self { + Self { size, elements: vec![] } + } + + /// Add an element at a position. + pub fn push(&mut self, pos: Point, element: Element) { + self.elements.push((pos, element)); + } + + /// Add all elements of another frame, placing them relative to the given + /// position. + pub fn push_frame(&mut self, pos: Point, subframe: Self) { + for (subpos, element) in subframe.elements { + self.push(pos + subpos, element); + } + } +} + +/// The building block frames are composed of. +#[derive(Debug, Clone, PartialEq)] +pub enum Element { + /// Shaped text. + Text(Shaped), + /// A geometric shape. + Geometry(Geometry), + /// A raster image. + Image(Image), +} + +/// A shape with some kind of fill. +#[derive(Debug, Clone, PartialEq)] +pub struct Geometry { + /// The shape to draw. + pub shape: Shape, + /// How the shape looks on the inside. + // + // TODO: This could be made into a Vec or something such that + // the user can compose multiple fills with alpha values less + // than one to achieve cool effects. + pub fill: Fill, +} + +/// Some shape. +#[derive(Debug, Clone, PartialEq)] +pub enum Shape { + /// A rectangle. + Rect(Size), +} + +/// The kind of graphic fill to be applied to a [`Shape`]. +#[derive(Debug, Copy, Clone, PartialEq)] +pub enum Fill { + /// The fill is a color. + Color(Color), + /// The fill is an image. + Image(Image), +} + +/// An image element. +#[derive(Debug, Copy, Clone, PartialEq)] +pub struct Image { + /// The image resource. + pub res: ResourceId, + /// The size of the image in the document. + pub size: Size, +} diff --git a/src/layout/mod.rs b/src/layout/mod.rs index b5cfb1b06..ae4ab89d3 100644 --- a/src/layout/mod.rs +++ b/src/layout/mod.rs @@ -2,27 +2,29 @@ mod background; mod fixed; +mod frame; mod node; mod pad; mod par; +mod shaping; mod spacing; mod stack; mod text; -use crate::color::Color; -use crate::env::{Env, ResourceId}; -use crate::geom::*; -use crate::shaping::Shaped; - pub use background::*; pub use fixed::*; +pub use frame::*; pub use node::*; pub use pad::*; pub use par::*; +pub use shaping::*; pub use spacing::*; pub use stack::*; pub use text::*; +use crate::env::Env; +use crate::geom::*; + /// Layout a tree into a collection of frames. pub fn layout(env: &mut Env, tree: &Tree) -> Vec { tree.layout(&mut LayoutContext { env }) @@ -32,7 +34,7 @@ pub fn layout(env: &mut Env, tree: &Tree) -> Vec { #[derive(Debug, Clone, PartialEq)] pub struct Tree { /// Runs of pages with the same properties. - pub runs: Vec, + pub runs: Vec, } impl Tree { @@ -44,15 +46,15 @@ impl Tree { /// A run of pages that all have the same properties. #[derive(Debug, Clone, PartialEq)] -pub struct NodePages { +pub struct PageRun { /// The size of each page. pub size: Size, /// The layout node that produces the actual pages (typically a - /// [`NodeStack`]). + /// [`StackNode`]). pub child: Node, } -impl NodePages { +impl PageRun { /// Layout the page run. pub fn layout(&self, ctx: &mut LayoutContext) -> Vec { let areas = Areas::repeat(self.size, Spec::uniform(Expand::Fill)); @@ -64,7 +66,7 @@ impl NodePages { /// Layout a node. pub trait Layout { /// Layout the node into the given areas. - fn layout(&self, ctx: &mut LayoutContext, areas: &Areas) -> Layouted; + fn layout(&self, ctx: &mut LayoutContext, areas: &Areas) -> Fragment; } /// The context for layouting. @@ -74,7 +76,7 @@ pub struct LayoutContext<'a> { pub env: &'a mut Env, } -/// A collection of areas to layout into. +/// A sequence of areas to layout into. #[derive(Debug, Clone, PartialEq)] pub struct Areas { /// The remaining size of the current area. @@ -155,7 +157,7 @@ impl Expand { /// The result of layouting a node. #[derive(Debug, Clone, PartialEq)] -pub enum Layouted { +pub enum Fragment { /// Spacing that should be added to the parent. Spacing(Length), /// A layout that should be added to and aligned in the parent. @@ -164,7 +166,7 @@ pub enum Layouted { Frames(Vec, LayoutAligns), } -impl Layouted { +impl Fragment { /// Return a reference to all frames contained in this variant (zero, one or /// arbitrarily many). pub fn frames(&self) -> &[Frame] { @@ -193,81 +195,3 @@ impl Layouted { } } } - -/// A finished layout with elements at fixed positions. -#[derive(Debug, Clone, PartialEq)] -pub struct Frame { - /// The size of the frame. - pub size: Size, - /// The elements composing this layout. - pub elements: Vec<(Point, Element)>, -} - -impl Frame { - /// Create a new, empty frame. - pub fn new(size: Size) -> Self { - Self { size, elements: vec![] } - } - - /// Add an element at a position. - pub fn push(&mut self, pos: Point, element: Element) { - self.elements.push((pos, element)); - } - - /// Add all elements of another frame, placing them relative to the given - /// position. - pub fn push_frame(&mut self, pos: Point, subframe: Self) { - for (subpos, element) in subframe.elements { - self.push(pos + subpos, element); - } - } -} - -/// The building block frames are composed of. -#[derive(Debug, Clone, PartialEq)] -pub enum Element { - /// Shaped text. - Text(Shaped), - /// An image. - Image(Image), - /// Some shape that could hold another frame. - Geometry(Geometry), -} - -/// A shape with some kind of fill. -#[derive(Debug, Clone, PartialEq)] -pub struct Geometry { - /// The shape to draw. - pub shape: Shape, - /// How the shape looks on the inside. - // - // TODO: This could be made into a Vec or something such that - // the user can compose multiple fills with alpha values less - // than one to achieve cool effects. - pub fill: Fill, -} - -/// Some shape. -#[derive(Debug, Clone, PartialEq)] -pub enum Shape { - /// A rectangle. - Rect(Size), -} - -/// The kind of graphic fill to be applied to a [`Shape`]. -#[derive(Debug, Copy, Clone, PartialEq)] -pub enum Fill { - /// The fill is a color. - Color(Color), - /// The fill is an image. - Image(Image), -} - -/// An image element. -#[derive(Debug, Copy, Clone, PartialEq)] -pub struct Image { - /// The image resource. - pub res: ResourceId, - /// The size of the image in the document. - pub size: Size, -} diff --git a/src/layout/node.rs b/src/layout/node.rs index fa7fe010f..443a96ae7 100644 --- a/src/layout/node.rs +++ b/src/layout/node.rs @@ -7,15 +7,15 @@ use super::*; #[derive(Clone, PartialEq)] pub enum Node { /// A text node. - Text(NodeText), + Text(TextNode), /// A spacing node. - Spacing(NodeSpacing), + Spacing(SpacingNode), /// A dynamic node that can implement custom layouting behaviour. - Any(NodeAny), + Any(AnyNode), } impl Layout for Node { - fn layout(&self, ctx: &mut LayoutContext, areas: &Areas) -> Layouted { + fn layout(&self, ctx: &mut LayoutContext, areas: &Areas) -> Fragment { match self { Self::Spacing(spacing) => spacing.layout(ctx, areas), Self::Text(text) => text.layout(ctx, areas), @@ -35,9 +35,9 @@ impl Debug for Node { } /// A wrapper around a dynamic layouting node. -pub struct NodeAny(Box); +pub struct AnyNode(Box); -impl NodeAny { +impl AnyNode { /// Create a new instance from any node that satisifies the required bounds. pub fn new(any: T) -> Self where @@ -47,25 +47,25 @@ impl NodeAny { } } -impl Layout for NodeAny { - fn layout(&self, ctx: &mut LayoutContext, areas: &Areas) -> Layouted { +impl Layout for AnyNode { + fn layout(&self, ctx: &mut LayoutContext, areas: &Areas) -> Fragment { self.0.layout(ctx, areas) } } -impl Clone for NodeAny { +impl Clone for AnyNode { fn clone(&self) -> Self { Self(self.0.dyn_clone()) } } -impl PartialEq for NodeAny { +impl PartialEq for AnyNode { fn eq(&self, other: &Self) -> bool { self.0.dyn_eq(other.0.as_ref()) } } -impl Debug for NodeAny { +impl Debug for AnyNode { fn fmt(&self, f: &mut Formatter) -> fmt::Result { self.0.fmt(f) } @@ -73,7 +73,7 @@ impl Debug for NodeAny { impl From for Node where - T: Into, + T: Into, { fn from(t: T) -> Self { Self::Any(t.into()) diff --git a/src/layout/pad.rs b/src/layout/pad.rs index 88ef88284..33ce217d9 100644 --- a/src/layout/pad.rs +++ b/src/layout/pad.rs @@ -1,17 +1,16 @@ use super::*; -use crate::geom::Linear; /// A node that adds padding to its child. #[derive(Debug, Clone, PartialEq)] -pub struct NodePad { +pub struct PadNode { /// The amount of padding. pub padding: Sides, /// The child node whose sides to pad. pub child: Node, } -impl Layout for NodePad { - fn layout(&self, ctx: &mut LayoutContext, areas: &Areas) -> Layouted { +impl Layout for PadNode { + fn layout(&self, ctx: &mut LayoutContext, areas: &Areas) -> Fragment { let areas = shrink(areas, self.padding); let mut layouted = self.child.layout(ctx, &areas); @@ -23,8 +22,8 @@ impl Layout for NodePad { } } -impl From for NodeAny { - fn from(pad: NodePad) -> Self { +impl From for AnyNode { + fn from(pad: PadNode) -> Self { Self::new(pad) } } diff --git a/src/layout/par.rs b/src/layout/par.rs index 8e8f5aac5..e9fda015a 100644 --- a/src/layout/par.rs +++ b/src/layout/par.rs @@ -2,7 +2,7 @@ use super::*; /// A node that arranges its children into a paragraph. #[derive(Debug, Clone, PartialEq)] -pub struct NodePar { +pub struct ParNode { /// The `main` and `cross` directions of this paragraph. /// /// The children are placed in lines along the `cross` direction. The lines @@ -16,28 +16,28 @@ pub struct NodePar { pub children: Vec, } -impl Layout for NodePar { - fn layout(&self, ctx: &mut LayoutContext, areas: &Areas) -> Layouted { +impl Layout for ParNode { + fn layout(&self, ctx: &mut LayoutContext, areas: &Areas) -> Fragment { let mut layouter = ParLayouter::new(self, areas.clone()); for child in &self.children { match child.layout(ctx, &layouter.areas) { - Layouted::Spacing(spacing) => layouter.push_spacing(spacing), - Layouted::Frame(frame, aligns) => { + Fragment::Spacing(spacing) => layouter.push_spacing(spacing), + Fragment::Frame(frame, aligns) => { layouter.push_frame(frame, aligns.cross) } - Layouted::Frames(frames, aligns) => { + Fragment::Frames(frames, aligns) => { for frame in frames { layouter.push_frame(frame, aligns.cross); } } } } - Layouted::Frames(layouter.finish(), self.aligns) + Fragment::Frames(layouter.finish(), self.aligns) } } -impl From for NodeAny { - fn from(par: NodePar) -> Self { +impl From for AnyNode { + fn from(par: ParNode) -> Self { Self::new(par) } } @@ -57,7 +57,7 @@ struct ParLayouter { } impl ParLayouter { - fn new(par: &NodePar, areas: Areas) -> Self { + fn new(par: &ParNode, areas: Areas) -> Self { Self { main: par.dirs.main.axis(), cross: par.dirs.cross.axis(), diff --git a/src/shaping.rs b/src/layout/shaping.rs similarity index 99% rename from src/shaping.rs rename to src/layout/shaping.rs index 41119639f..141fa10e9 100644 --- a/src/shaping.rs +++ b/src/layout/shaping.rs @@ -9,7 +9,7 @@ use std::fmt::{self, Debug, Display, Formatter}; use fontdock::{FaceId, FaceQuery, FallbackTree, FontVariant}; use ttf_parser::{Face, GlyphId}; -use crate::font::FontLoader; +use crate::env::FontLoader; use crate::geom::{Dir, Length, Point, Size}; use crate::layout::{Element, Frame}; diff --git a/src/layout/spacing.rs b/src/layout/spacing.rs index 2bcb7ac10..361b03ee3 100644 --- a/src/layout/spacing.rs +++ b/src/layout/spacing.rs @@ -2,9 +2,9 @@ use std::fmt::{self, Debug, Formatter}; use super::*; -/// A spacing node. +/// A node that adds spacing to its parent. #[derive(Copy, Clone, PartialEq)] -pub struct NodeSpacing { +pub struct SpacingNode { /// The amount of spacing to insert. pub amount: Length, /// Defines how spacing interacts with surrounding spacing. @@ -16,20 +16,20 @@ pub struct NodeSpacing { pub softness: u8, } -impl Layout for NodeSpacing { - fn layout(&self, _: &mut LayoutContext, _: &Areas) -> Layouted { - Layouted::Spacing(self.amount) +impl Layout for SpacingNode { + fn layout(&self, _: &mut LayoutContext, _: &Areas) -> Fragment { + Fragment::Spacing(self.amount) } } -impl Debug for NodeSpacing { +impl Debug for SpacingNode { fn fmt(&self, f: &mut Formatter) -> fmt::Result { write!(f, "Spacing({}, {})", self.amount, self.softness) } } -impl From for Node { - fn from(spacing: NodeSpacing) -> Self { +impl From for Node { + fn from(spacing: SpacingNode) -> Self { Self::Spacing(spacing) } } diff --git a/src/layout/stack.rs b/src/layout/stack.rs index 8748c5c72..32eba6765 100644 --- a/src/layout/stack.rs +++ b/src/layout/stack.rs @@ -2,7 +2,7 @@ use super::*; /// A node that stacks its children. #[derive(Debug, Clone, PartialEq)] -pub struct NodeStack { +pub struct StackNode { /// The `main` and `cross` directions of this stack. /// /// The children are stacked along the `main` direction. The `cross` @@ -14,26 +14,26 @@ pub struct NodeStack { pub children: Vec, } -impl Layout for NodeStack { - fn layout(&self, ctx: &mut LayoutContext, areas: &Areas) -> Layouted { +impl Layout for StackNode { + fn layout(&self, ctx: &mut LayoutContext, areas: &Areas) -> Fragment { let mut layouter = StackLayouter::new(self, areas.clone()); for child in &self.children { match child.layout(ctx, &layouter.areas) { - Layouted::Spacing(spacing) => layouter.push_spacing(spacing), - Layouted::Frame(frame, aligns) => layouter.push_frame(frame, aligns), - Layouted::Frames(frames, aligns) => { + Fragment::Spacing(spacing) => layouter.push_spacing(spacing), + Fragment::Frame(frame, aligns) => layouter.push_frame(frame, aligns), + Fragment::Frames(frames, aligns) => { for frame in frames { layouter.push_frame(frame, aligns); } } } } - Layouted::Frames(layouter.finish(), self.aligns) + Fragment::Frames(layouter.finish(), self.aligns) } } -impl From for NodeAny { - fn from(stack: NodeStack) -> Self { +impl From for AnyNode { + fn from(stack: StackNode) -> Self { Self::new(stack) } } @@ -49,7 +49,7 @@ struct StackLayouter { } impl StackLayouter { - fn new(stack: &NodeStack, areas: Areas) -> Self { + fn new(stack: &StackNode, areas: Areas) -> Self { Self { main: stack.dirs.main.axis(), dirs: stack.dirs, diff --git a/src/layout/text.rs b/src/layout/text.rs index 7b4eb08ed..7faefa0d5 100644 --- a/src/layout/text.rs +++ b/src/layout/text.rs @@ -4,11 +4,10 @@ use std::rc::Rc; use fontdock::{FallbackTree, FontVariant}; use super::*; -use crate::shaping::{shape, VerticalFontMetric}; -/// A text node. +/// A consecutive, styled run of text. #[derive(Clone, PartialEq)] -pub struct NodeText { +pub struct TextNode { /// The text. pub text: String, /// The text direction. @@ -27,9 +26,9 @@ pub struct NodeText { pub bottom_edge: VerticalFontMetric, } -impl Layout for NodeText { - fn layout(&self, ctx: &mut LayoutContext, _: &Areas) -> Layouted { - Layouted::Frame( +impl Layout for TextNode { + fn layout(&self, ctx: &mut LayoutContext, _: &Areas) -> Fragment { + Fragment::Frame( shape( &self.text, self.dir, @@ -45,14 +44,14 @@ impl Layout for NodeText { } } -impl Debug for NodeText { +impl Debug for TextNode { fn fmt(&self, f: &mut Formatter) -> fmt::Result { write!(f, "Text({})", self.text) } } -impl From for Node { - fn from(text: NodeText) -> Self { +impl From for Node { + fn from(text: TextNode) -> Self { Self::Text(text) } } diff --git a/src/lib.rs b/src/lib.rs index d4a73ed4e..d2e47c62b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,12 +9,13 @@ //! computes the value of each expression in document and stores them in a map //! from expression-pointers to values. //! - **Execution:** Now, we can [execute] the parsed and evaluated "script". -//! This produces a [layout tree], a high-level, fully styled representation. -//! The nodes of this tree are self-contained and order-independent and thus -//! much better suited for layouting than the syntax tree. +//! This produces a [layout tree], a high-level, fully styled representation +//! of the document. The nodes of this tree are self-contained and +//! order-independent and thus much better suited for layouting than the +//! syntax tree. //! - **Layouting:** Next, the tree is [layouted] into a portable version of the -//! typeset document. The output of this is a vector of [`Frame`]s -//! (corresponding to pages), ready for exporting. +//! typeset document. The output of this is a collection of [`Frame`]s (one +//! per page), ready for exporting. //! - **Exporting:** The finished layout can be exported into a supported //! format. Submodules for these formats are located in the [export] module. //! Currently, the only supported output format is [_PDF_]. @@ -36,15 +37,12 @@ pub mod color; pub mod env; pub mod exec; pub mod export; -pub mod font; pub mod geom; pub mod layout; pub mod library; pub mod paper; pub mod parse; -pub mod prelude; pub mod pretty; -pub mod shaping; pub mod syntax; use crate::diag::Pass; diff --git a/src/library/align.rs b/src/library/align.rs index a516ff4cc..93c6db0dc 100644 --- a/src/library/align.rs +++ b/src/library/align.rs @@ -26,12 +26,12 @@ use super::*; /// - `top` /// - `bottom` /// - `center` -pub fn align(ctx: &mut EvalContext, args: &mut ValueArgs) -> Value { +pub fn align(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value { let first = args.find(ctx); let second = args.find(ctx); let hor = args.get(ctx, "horizontal"); let ver = args.get(ctx, "vertical"); - let body = args.find::(ctx); + let body = args.find::(ctx); Value::template("align", move |ctx| { let snapshot = ctx.state.clone(); diff --git a/src/library/base.rs b/src/library/base.rs index 4d36b878f..22adb1f46 100644 --- a/src/library/base.rs +++ b/src/library/base.rs @@ -10,7 +10,7 @@ use super::*; /// /// # Return value /// The string representation of the value. -pub fn repr(ctx: &mut EvalContext, args: &mut ValueArgs) -> Value { +pub fn repr(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value { match args.require::(ctx, "value") { Some(value) => pretty(&value).into(), None => Value::Error, @@ -27,7 +27,7 @@ pub fn repr(ctx: &mut EvalContext, args: &mut ValueArgs) -> Value { /// /// # Return value /// The color with the given components. -pub fn rgb(ctx: &mut EvalContext, args: &mut ValueArgs) -> Value { +pub fn rgb(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value { let r = args.require(ctx, "red component"); let g = args.require(ctx, "green component"); let b = args.require(ctx, "blue component"); @@ -57,7 +57,7 @@ pub fn rgb(ctx: &mut EvalContext, args: &mut ValueArgs) -> Value { /// /// # Return value /// The name of the value's type as a string. -pub fn type_(ctx: &mut EvalContext, args: &mut ValueArgs) -> Value { +pub fn type_(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value { match args.require::(ctx, "value") { Some(value) => value.type_name().into(), None => Value::Error, diff --git a/src/library/font.rs b/src/library/font.rs index ecc15d960..00fd0e81c 100644 --- a/src/library/font.rs +++ b/src/library/font.rs @@ -1,7 +1,6 @@ use fontdock::{FontStretch, FontStyle, FontWeight}; use super::*; -use crate::shaping::VerticalFontMetric; /// `font`: Configure the font. /// @@ -55,7 +54,7 @@ use crate::shaping::VerticalFontMetric; /// - `x-height` /// - `baseline` /// - `descender` -pub fn font(ctx: &mut EvalContext, args: &mut ValueArgs) -> Value { +pub fn font(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value { let size = args.find::(ctx); let list: Vec<_> = args.filter::(ctx).map(|f| f.to_string()).collect(); let style = args.get(ctx, "style"); @@ -66,7 +65,7 @@ pub fn font(ctx: &mut EvalContext, args: &mut ValueArgs) -> Value { let serif = args.get(ctx, "serif"); let sans_serif = args.get(ctx, "sans-serif"); let monospace = args.get(ctx, "monospace"); - let body = args.find::(ctx); + let body = args.find::(ctx); Value::template("font", move |ctx| { let snapshot = ctx.state.clone(); diff --git a/src/library/image.rs b/src/library/image.rs index c3200e939..8cb094630 100644 --- a/src/library/image.rs +++ b/src/library/image.rs @@ -2,7 +2,9 @@ use ::image::GenericImageView; use super::*; use crate::env::{ImageResource, ResourceId}; -use crate::layout::*; +use crate::layout::{ + AnyNode, Areas, Element, Fragment, Frame, Image, Layout, LayoutContext, +}; /// `image`: Insert an image. /// @@ -13,7 +15,7 @@ use crate::layout::*; /// /// # Return value /// A template that inserts an image. -pub fn image(ctx: &mut EvalContext, args: &mut ValueArgs) -> Value { +pub fn image(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value { let path = args.require::>(ctx, "path to image file"); let width = args.get(ctx, "width"); let height = args.get(ctx, "height"); @@ -53,7 +55,7 @@ struct NodeImage { } impl Layout for NodeImage { - fn layout(&self, _: &mut LayoutContext, areas: &Areas) -> Layouted { + fn layout(&self, _: &mut LayoutContext, areas: &Areas) -> Fragment { let Areas { current, full, .. } = areas; let pixel_width = self.dimensions.0 as f64; @@ -84,11 +86,11 @@ impl Layout for NodeImage { let mut frame = Frame::new(size); frame.push(Point::ZERO, Element::Image(Image { res: self.res, size })); - Layouted::Frame(frame, self.aligns) + Fragment::Frame(frame, self.aligns) } } -impl From for NodeAny { +impl From for AnyNode { fn from(image: NodeImage) -> Self { Self::new(image) } diff --git a/src/library/mod.rs b/src/library/mod.rs index e813a138d..d0920cf1c 100644 --- a/src/library/mod.rs +++ b/src/library/mod.rs @@ -27,68 +27,72 @@ use std::fmt::{self, Display, Formatter}; use fontdock::{FontStyle, FontWeight}; -use crate::eval::{Scope, ValueAny, ValueFunc}; -use crate::layout::*; -use crate::prelude::*; -use crate::shaping::VerticalFontMetric; +use crate::eval::{AnyValue, FuncValue, Scope}; +use crate::eval::{EvalContext, FuncArgs, TemplateValue, Value}; +use crate::exec::{Exec, ExecContext}; +use crate::geom::*; +use crate::layout::VerticalFontMetric; +use crate::syntax::Spanned; /// Construct a scope containing all standard library definitions. pub fn new() -> Scope { let mut std = Scope::new(); - macro_rules! set { - (func: $name:expr, $func:expr) => { - std.def_const($name, ValueFunc::new(Some($name.into()), $func)) - }; - (any: $var:expr, $any:expr) => { - std.def_const($var, ValueAny::new($any)) + + macro_rules! func { + ($name:expr, $func:expr) => { + std.def_const($name, FuncValue::new(Some($name.into()), $func)) }; } - // Functions. - set!(func: "align", align); - set!(func: "font", font); - set!(func: "h", h); - set!(func: "image", image); - set!(func: "pad", pad); - set!(func: "page", page); - set!(func: "pagebreak", pagebreak); - set!(func: "paragraph", paragraph); - set!(func: "rect", rect); - set!(func: "repr", repr); - set!(func: "rgb", rgb); - set!(func: "type", type_); - set!(func: "v", v); + macro_rules! constant { + ($var:expr, $any:expr) => { + std.def_const($var, AnyValue::new($any)) + }; + } - // Constants. - set!(any: "left", AlignValue::Left); - set!(any: "center", AlignValue::Center); - set!(any: "right", AlignValue::Right); - set!(any: "top", AlignValue::Top); - set!(any: "bottom", AlignValue::Bottom); - set!(any: "ltr", Dir::LTR); - set!(any: "rtl", Dir::RTL); - set!(any: "ttb", Dir::TTB); - set!(any: "btt", Dir::BTT); - set!(any: "serif", FontFamily::Serif); - set!(any: "sans-serif", FontFamily::SansSerif); - set!(any: "monospace", FontFamily::Monospace); - set!(any: "normal", FontStyle::Normal); - set!(any: "italic", FontStyle::Italic); - set!(any: "oblique", FontStyle::Oblique); - set!(any: "thin", FontWeight::THIN); - set!(any: "extralight", FontWeight::EXTRALIGHT); - set!(any: "light", FontWeight::LIGHT); - set!(any: "regular", FontWeight::REGULAR); - set!(any: "medium", FontWeight::MEDIUM); - set!(any: "semibold", FontWeight::SEMIBOLD); - set!(any: "bold", FontWeight::BOLD); - set!(any: "extrabold", FontWeight::EXTRABOLD); - set!(any: "black", FontWeight::BLACK); - set!(any: "ascender", VerticalFontMetric::Ascender); - set!(any: "cap-height", VerticalFontMetric::CapHeight); - set!(any: "x-height", VerticalFontMetric::XHeight); - set!(any: "baseline", VerticalFontMetric::Baseline); - set!(any: "descender", VerticalFontMetric::Descender); + func!("align", align); + func!("font", font); + func!("h", h); + func!("image", image); + func!("pad", pad); + func!("page", page); + func!("pagebreak", pagebreak); + func!("paragraph", par); + func!("rect", rect); + func!("repr", repr); + func!("rgb", rgb); + func!("type", type_); + func!("v", v); + + constant!("left", AlignValue::Left); + constant!("center", AlignValue::Center); + constant!("right", AlignValue::Right); + constant!("top", AlignValue::Top); + constant!("bottom", AlignValue::Bottom); + constant!("ltr", Dir::LTR); + constant!("rtl", Dir::RTL); + constant!("ttb", Dir::TTB); + constant!("btt", Dir::BTT); + constant!("serif", FontFamily::Serif); + constant!("sans-serif", FontFamily::SansSerif); + constant!("monospace", FontFamily::Monospace); + constant!("normal", FontStyle::Normal); + constant!("italic", FontStyle::Italic); + constant!("oblique", FontStyle::Oblique); + constant!("thin", FontWeight::THIN); + constant!("extralight", FontWeight::EXTRALIGHT); + constant!("light", FontWeight::LIGHT); + constant!("regular", FontWeight::REGULAR); + constant!("medium", FontWeight::MEDIUM); + constant!("semibold", FontWeight::SEMIBOLD); + constant!("bold", FontWeight::BOLD); + constant!("extrabold", FontWeight::EXTRABOLD); + constant!("black", FontWeight::BLACK); + constant!("ascender", VerticalFontMetric::Ascender); + constant!("cap-height", VerticalFontMetric::CapHeight); + constant!("x-height", VerticalFontMetric::XHeight); + constant!("baseline", VerticalFontMetric::Baseline); + constant!("descender", VerticalFontMetric::Descender); std } diff --git a/src/library/pad.rs b/src/library/pad.rs index 5d59f2b36..9f05f7ce3 100644 --- a/src/library/pad.rs +++ b/src/library/pad.rs @@ -1,4 +1,5 @@ use super::*; +use crate::layout::PadNode; /// `pad`: Pad content at the sides. /// @@ -14,13 +15,13 @@ use super::*; /// /// # Return value /// A template that pads the body at the sides. -pub fn pad(ctx: &mut EvalContext, args: &mut ValueArgs) -> Value { +pub fn pad(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value { let all = args.find(ctx); let left = args.get(ctx, "left"); let top = args.get(ctx, "top"); let right = args.get(ctx, "right"); let bottom = args.get(ctx, "bottom"); - let body = args.require::(ctx, "body").unwrap_or_default(); + let body = args.require::(ctx, "body").unwrap_or_default(); let padding = Sides::new( left.or(all).unwrap_or_default(), @@ -31,10 +32,8 @@ pub fn pad(ctx: &mut EvalContext, args: &mut ValueArgs) -> Value { Value::template("pad", move |ctx| { let snapshot = ctx.state.clone(); - let child = ctx.exec(&body).into(); - ctx.push(NodePad { padding, child }); - + ctx.push(PadNode { padding, child }); ctx.state = snapshot; }) } diff --git a/src/library/page.rs b/src/library/page.rs index f7d76eafb..067258f5b 100644 --- a/src/library/page.rs +++ b/src/library/page.rs @@ -30,7 +30,7 @@ use crate::paper::{Paper, PaperClass}; /// - `rtl` (right to left) /// - `ttb` (top to bottom) /// - `btt` (bottom to top) -pub fn page(ctx: &mut EvalContext, args: &mut ValueArgs) -> Value { +pub fn page(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value { let paper = args.find::>(ctx).and_then(|name| { Paper::from_name(&name.v).or_else(|| { ctx.diag(error!(name.span, "invalid paper name")); @@ -48,7 +48,7 @@ pub fn page(ctx: &mut EvalContext, args: &mut ValueArgs) -> Value { let flip = args.get(ctx, "flip"); let main = args.get(ctx, "main-dir"); let cross = args.get(ctx, "cross-dir"); - let body = args.find::(ctx); + let body = args.find::(ctx); let span = args.span; Value::template("page", move |ctx| { @@ -110,7 +110,7 @@ pub fn page(ctx: &mut EvalContext, args: &mut ValueArgs) -> Value { /// /// # Return value /// A template that starts a new page. -pub fn pagebreak(_: &mut EvalContext, args: &mut ValueArgs) -> Value { +pub fn pagebreak(_: &mut EvalContext, args: &mut FuncArgs) -> Value { let span = args.span; Value::template("pagebreak", move |ctx| { ctx.finish_page(true, true, span); diff --git a/src/library/par.rs b/src/library/par.rs index 8242bfdcf..a7db46de6 100644 --- a/src/library/par.rs +++ b/src/library/par.rs @@ -2,6 +2,9 @@ use super::*; /// `paragraph`: Configure paragraphs. /// +/// # Positional parameters +/// - Body: optional, of type `template`. +/// /// # Named parameters /// - Paragraph spacing: `spacing`, of type `linear` relative to current font size. /// - Line leading: `leading`, of type `linear` relative to current font size. @@ -10,11 +13,11 @@ use super::*; /// # Return value /// A template that configures paragraph properties. The effect is scoped to the /// body if present. -pub fn paragraph(ctx: &mut EvalContext, args: &mut ValueArgs) -> Value { +pub fn par(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value { let spacing = args.get(ctx, "spacing"); let leading = args.get(ctx, "leading"); let word_spacing = args.get(ctx, "word-spacing"); - let body = args.find::(ctx); + let body = args.find::(ctx); Value::template("paragraph", move |ctx| { let snapshot = ctx.state.clone(); diff --git a/src/library/shapes.rs b/src/library/shapes.rs index 48bc7ebd1..211a4f2e9 100644 --- a/src/library/shapes.rs +++ b/src/library/shapes.rs @@ -1,4 +1,5 @@ use super::*; +use crate::layout::{BackgroundNode, Fill, FixedNode}; /// `rect`: Create a rectangular box. /// @@ -21,13 +22,13 @@ use super::*; /// - `rtl` (right to left) /// - `ttb` (top to bottom) /// - `btt` (bottom to top) -pub fn rect(ctx: &mut EvalContext, args: &mut ValueArgs) -> Value { +pub fn rect(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value { let width = args.get(ctx, "width"); let height = args.get(ctx, "height"); let main = args.get(ctx, "main-dir"); let cross = args.get(ctx, "cross-dir"); let fill = args.get(ctx, "fill"); - let body = args.find::(ctx).unwrap_or_default(); + let body = args.find::(ctx).unwrap_or_default(); Value::template("box", move |ctx| { let snapshot = ctx.state.clone(); @@ -35,9 +36,9 @@ pub fn rect(ctx: &mut EvalContext, args: &mut ValueArgs) -> Value { ctx.set_dirs(Gen::new(main, cross)); let child = ctx.exec(&body).into(); - let fixed = NodeFixed { width, height, child }; + let fixed = FixedNode { width, height, child }; if let Some(color) = fill { - ctx.push(NodeBackground { + ctx.push(BackgroundNode { fill: Fill::Color(color), child: fixed.into(), }); diff --git a/src/library/spacing.rs b/src/library/spacing.rs index 4965a2202..fee802fac 100644 --- a/src/library/spacing.rs +++ b/src/library/spacing.rs @@ -1,4 +1,5 @@ use super::*; +use crate::layout::SpacingNode; /// `h`: Add horizontal spacing. /// @@ -7,7 +8,7 @@ use super::*; /// /// # Return value /// A template that adds horizontal spacing. -pub fn h(ctx: &mut EvalContext, args: &mut ValueArgs) -> Value { +pub fn h(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value { spacing(ctx, args, SpecAxis::Horizontal) } @@ -18,17 +19,17 @@ pub fn h(ctx: &mut EvalContext, args: &mut ValueArgs) -> Value { /// /// # Return value /// A template that adds vertical spacing. -pub fn v(ctx: &mut EvalContext, args: &mut ValueArgs) -> Value { +pub fn v(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value { spacing(ctx, args, SpecAxis::Vertical) } /// Apply spacing along a specific axis. -fn spacing(ctx: &mut EvalContext, args: &mut ValueArgs, axis: SpecAxis) -> Value { +fn spacing(ctx: &mut EvalContext, args: &mut FuncArgs, axis: SpecAxis) -> Value { let spacing: Option = args.require(ctx, "spacing"); Value::template("spacing", move |ctx| { if let Some(linear) = spacing { let amount = linear.resolve(ctx.state.font.font_size()); - let spacing = NodeSpacing { amount, softness: 0 }; + let spacing = SpacingNode { amount, softness: 0 }; if axis == ctx.state.dirs.main.axis() { ctx.push_into_stack(spacing); } else { diff --git a/src/main.rs b/src/main.rs index ae655253c..8b56512df 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,10 +5,9 @@ use anyhow::{anyhow, bail, Context}; use fontdock::fs::FsIndex; use typst::diag::Pass; -use typst::env::{Env, ResourceLoader}; +use typst::env::{Env, FsIndexExt, ResourceLoader}; use typst::exec::State; use typst::export::pdf; -use typst::font::FsIndexExt; use typst::library; use typst::parse::LineMap; use typst::typeset; diff --git a/src/parse/mod.rs b/src/parse/mod.rs index f23f19785..ceb8a206e 100644 --- a/src/parse/mod.rs +++ b/src/parse/mod.rs @@ -136,11 +136,11 @@ fn heading(p: &mut Parser) -> Node { contents.extend(node(p, &mut false)); } - Node::Heading(NodeHeading { level, contents }) + Node::Heading(HeadingNode { level, contents }) } /// Handle a raw block. -fn raw(p: &mut Parser, token: TokenRaw) -> Node { +fn raw(p: &mut Parser, token: RawToken) -> Node { let raw = resolve::resolve_raw(token.text, token.backticks, p.start()); if !token.terminated { p.diag(error!(p.peek_span().end, "expected backtick(s)")); @@ -149,7 +149,7 @@ fn raw(p: &mut Parser, token: TokenRaw) -> Node { } /// Handle a unicode escape sequence. -fn unicode_escape(p: &mut Parser, token: TokenUnicodeEscape) -> String { +fn unicode_escape(p: &mut Parser, token: UnicodeEscapeToken) -> String { let span = p.peek_span(); let text = if let Some(c) = resolve::resolve_hex(token.sequence) { c.to_string() @@ -184,7 +184,7 @@ fn expr_with(p: &mut Parser, atomic: bool, min_prec: usize) -> Option { Some(op) => { let prec = op.precedence(); let expr = Box::new(expr_with(p, atomic, prec)?); - Expr::Unary(ExprUnary { span: p.span(start), op, expr }) + Expr::Unary(UnaryExpr { span: p.span(start), op, expr }) } None => primary(p, atomic)?, }; @@ -225,7 +225,7 @@ fn expr_with(p: &mut Parser, atomic: bool, min_prec: usize) -> Option { }; let span = lhs.span().join(rhs.span()); - lhs = Expr::Binary(ExprBinary { span, lhs: Box::new(lhs), op, rhs }); + lhs = Expr::Binary(BinaryExpr { span, lhs: Box::new(lhs), op, rhs }); } Some(lhs) @@ -248,7 +248,7 @@ fn primary(p: &mut Parser, atomic: bool) -> Option { // Arrow means this is a closure's lone parameter. Some(if !atomic && p.eat_if(Token::Arrow) { let body = expr(p)?; - Expr::Closure(ExprClosure { + Expr::Closure(ClosureExpr { span: id.span.join(body.span()), name: None, params: Rc::new(vec![id]), @@ -306,7 +306,7 @@ fn literal(p: &mut Parser) -> Option { /// - Dictionary literal /// - Parenthesized expression /// - Parameter list of closure expression -pub fn parenthesized(p: &mut Parser) -> Option { +fn parenthesized(p: &mut Parser) -> Option { p.start_group(Group::Paren, TokenMode::Code); let colon = p.eat_if(Token::Colon); let (items, has_comma) = collection(p); @@ -321,7 +321,7 @@ pub fn parenthesized(p: &mut Parser) -> Option { if p.eat_if(Token::Arrow) { let params = params(p, items); let body = expr(p)?; - return Some(Expr::Closure(ExprClosure { + return Some(Expr::Closure(ClosureExpr { span: span.join(body.span()), name: None, params: Rc::new(params), @@ -332,21 +332,21 @@ pub fn parenthesized(p: &mut Parser) -> Option { // Find out which kind of collection this is. Some(match items.as_slice() { [] => array(p, items, span), - [ExprArg::Pos(_)] if !has_comma => match items.into_iter().next() { - Some(ExprArg::Pos(expr)) => { - Expr::Group(ExprGroup { span, expr: Box::new(expr) }) + [CallArg::Pos(_)] if !has_comma => match items.into_iter().next() { + Some(CallArg::Pos(expr)) => { + Expr::Group(GroupExpr { span, expr: Box::new(expr) }) } _ => unreachable!(), }, - [ExprArg::Pos(_), ..] => array(p, items, span), - [ExprArg::Named(_), ..] => dict(p, items, span), + [CallArg::Pos(_), ..] => array(p, items, span), + [CallArg::Named(_), ..] => dict(p, items, span), }) } /// Parse a collection. /// /// Returns whether the literal contained any commas. -fn collection(p: &mut Parser) -> (Vec, bool) { +fn collection(p: &mut Parser) -> (Vec, bool) { let mut items = vec![]; let mut has_comma = false; let mut missing_coma = None; @@ -376,52 +376,52 @@ fn collection(p: &mut Parser) -> (Vec, bool) { } /// Parse an expression or a named pair. -fn item(p: &mut Parser) -> Option { +fn item(p: &mut Parser) -> Option { let first = expr(p)?; if p.eat_if(Token::Colon) { if let Expr::Ident(name) = first { - Some(ExprArg::Named(Named { name, expr: expr(p)? })) + Some(CallArg::Named(Named { name, expr: expr(p)? })) } else { p.diag(error!(first.span(), "expected identifier")); expr(p); None } } else { - Some(ExprArg::Pos(first)) + Some(CallArg::Pos(first)) } } /// Convert a collection into an array, producing errors for named items. -fn array(p: &mut Parser, items: Vec, span: Span) -> Expr { +fn array(p: &mut Parser, items: Vec, span: Span) -> Expr { let items = items.into_iter().filter_map(|item| match item { - ExprArg::Pos(expr) => Some(expr), - ExprArg::Named(_) => { + CallArg::Pos(expr) => Some(expr), + CallArg::Named(_) => { p.diag(error!(item.span(), "expected expression, found named pair")); None } }); - Expr::Array(ExprArray { span, items: items.collect() }) + Expr::Array(ArrayExpr { span, items: items.collect() }) } /// Convert a collection into a dictionary, producing errors for expressions. -fn dict(p: &mut Parser, items: Vec, span: Span) -> Expr { +fn dict(p: &mut Parser, items: Vec, span: Span) -> Expr { let items = items.into_iter().filter_map(|item| match item { - ExprArg::Named(named) => Some(named), - ExprArg::Pos(_) => { + CallArg::Named(named) => Some(named), + CallArg::Pos(_) => { p.diag(error!(item.span(), "expected named pair, found expression")); None } }); - Expr::Dict(ExprDict { span, items: items.collect() }) + Expr::Dict(DictExpr { span, items: items.collect() }) } /// Convert a collection into a parameter list, producing errors for anything /// other than identifiers. -fn params(p: &mut Parser, items: Vec) -> Vec { +fn params(p: &mut Parser, items: Vec) -> Vec { let items = items.into_iter().filter_map(|item| match item { - ExprArg::Pos(Expr::Ident(id)) => Some(id), + CallArg::Pos(Expr::Ident(id)) => Some(id), _ => { p.diag(error!(item.span(), "expected identifier")); None @@ -435,7 +435,7 @@ fn template(p: &mut Parser) -> Expr { p.start_group(Group::Bracket, TokenMode::Markup); let tree = Rc::new(tree(p)); let span = p.end_group(); - Expr::Template(ExprTemplate { span, tree }) + Expr::Template(TemplateExpr { span, tree }) } /// Parse a block expression: `{...}`. @@ -454,7 +454,7 @@ fn block(p: &mut Parser, scoping: bool) -> Expr { p.skip_white(); } let span = p.end_group(); - Expr::Block(ExprBlock { span, exprs, scoping }) + Expr::Block(BlockExpr { span, exprs, scoping }) } /// Parse a function call. @@ -466,7 +466,7 @@ fn call(p: &mut Parser, callee: Expr) -> Expr { p.end_group(); args } - _ => ExprArgs { + _ => CallArgs { span: Span::at(callee.span().end), items: vec![], }, @@ -474,10 +474,10 @@ fn call(p: &mut Parser, callee: Expr) -> Expr { if p.peek_direct() == Some(Token::LeftBracket) { let body = template(p); - args.items.push(ExprArg::Pos(body)); + args.items.push(CallArg::Pos(body)); } - Expr::Call(ExprCall { + Expr::Call(CallExpr { span: p.span(callee.span().start), callee: Box::new(callee), args, @@ -485,10 +485,10 @@ fn call(p: &mut Parser, callee: Expr) -> Expr { } /// Parse the arguments to a function call. -fn args(p: &mut Parser) -> ExprArgs { +fn args(p: &mut Parser) -> CallArgs { let start = p.start(); let items = collection(p).0; - ExprArgs { span: p.span(start), items } + CallArgs { span: p.span(start), items } } /// Parse a let expression. @@ -518,7 +518,7 @@ fn expr_let(p: &mut Parser) -> Option { // Rewrite into a closure expression if it's a function definition. if let Some(params) = parameters { let body = init?; - init = Some(Expr::Closure(ExprClosure { + init = Some(Expr::Closure(ClosureExpr { span: binding.span.join(body.span()), name: Some(binding.clone()), params: Rc::new(params), @@ -526,7 +526,7 @@ fn expr_let(p: &mut Parser) -> Option { })); } - expr_let = Some(Expr::Let(ExprLet { + expr_let = Some(Expr::Let(LetExpr { span: p.span(start), binding, init: init.map(Box::new), @@ -555,7 +555,7 @@ fn expr_if(p: &mut Parser) -> Option { else_body = body(p); } - expr_if = Some(Expr::If(ExprIf { + expr_if = Some(Expr::If(IfExpr { span: p.span(start), condition: Box::new(condition), if_body: Box::new(if_body), @@ -575,7 +575,7 @@ fn expr_while(p: &mut Parser) -> Option { let mut expr_while = None; if let Some(condition) = expr(p) { if let Some(body) = body(p) { - expr_while = Some(Expr::While(ExprWhile { + expr_while = Some(Expr::While(WhileExpr { span: p.span(start), condition: Box::new(condition), body: Box::new(body), @@ -596,7 +596,7 @@ fn expr_for(p: &mut Parser) -> Option { if p.expect(Token::In) { if let Some(iter) = expr(p) { if let Some(body) = body(p) { - expr_for = Some(Expr::For(ExprFor { + expr_for = Some(Expr::For(ForExpr { span: p.span(start), pattern, iter: Box::new(iter), diff --git a/src/parse/resolve.rs b/src/parse/resolve.rs index 88e11784c..1f33198a7 100644 --- a/src/parse/resolve.rs +++ b/src/parse/resolve.rs @@ -1,5 +1,5 @@ use super::{is_newline, Scanner}; -use crate::syntax::{Ident, NodeRaw, Pos}; +use crate::syntax::{Ident, Pos, RawNode}; /// Resolve all escape sequences in a string. pub fn resolve_string(string: &str) -> String { @@ -47,17 +47,17 @@ pub fn resolve_hex(sequence: &str) -> Option { } /// Resolve the language tag and trims the raw text. -pub fn resolve_raw(text: &str, backticks: usize, start: Pos) -> NodeRaw { +pub fn resolve_raw(text: &str, backticks: usize, start: Pos) -> RawNode { if backticks > 1 { let (tag, inner) = split_at_lang_tag(text); let (lines, had_newline) = trim_and_split_raw(inner); - NodeRaw { + RawNode { lang: Ident::new(tag, start .. start + tag.len()), lines, block: had_newline, } } else { - NodeRaw { + RawNode { lang: None, lines: split_lines(text), block: false, @@ -105,7 +105,7 @@ fn trim_and_split_raw(mut raw: &str) -> (Vec, bool) { /// Split a string into a vector of lines /// (respecting Unicode, Unix, Mac and Windows line breaks). -pub fn split_lines(text: &str) -> Vec { +fn split_lines(text: &str) -> Vec { let mut s = Scanner::new(text); let mut line = String::new(); let mut lines = Vec::new(); @@ -174,7 +174,7 @@ mod tests { lines: &[&str], block: bool, ) { - Span::without_cmp(|| assert_eq!(resolve_raw(raw, backticks, Pos(0)), NodeRaw { + Span::without_cmp(|| assert_eq!(resolve_raw(raw, backticks, Pos(0)), RawNode { lang: lang.and_then(|id| Ident::new(id, 0)), lines: lines.iter().map(ToString::to_string).collect(), block, diff --git a/src/parse/tokens.rs b/src/parse/tokens.rs index a1457c22c..a57db93b1 100644 --- a/src/parse/tokens.rs +++ b/src/parse/tokens.rs @@ -232,7 +232,7 @@ impl<'s> Tokens<'s> { // Special case for empty inline block. if backticks == 2 { - return Token::Raw(TokenRaw { text: "", backticks: 1, terminated: true }); + return Token::Raw(RawToken { text: "", backticks: 1, terminated: true }); } let start = self.s.index(); @@ -249,7 +249,7 @@ impl<'s> Tokens<'s> { let terminated = found == backticks; let end = self.s.index() - if terminated { found } else { 0 }; - Token::Raw(TokenRaw { + Token::Raw(RawToken { text: self.s.get(start .. end), backticks, terminated, @@ -286,7 +286,7 @@ impl<'s> Tokens<'s> { (true, true) => 2, }; - Token::Math(TokenMath { + Token::Math(MathToken { formula: self.s.get(start .. end), display, terminated, @@ -309,7 +309,7 @@ impl<'s> Tokens<'s> { 'u' if self.s.peek_nth(1) == Some('{') => { self.s.eat_assert('u'); self.s.eat_assert('{'); - Token::UnicodeEscape(TokenUnicodeEscape { + Token::UnicodeEscape(UnicodeEscapeToken { // Allow more than `ascii_hexdigit` for better error recovery. sequence: self.s.eat_while(|c| c.is_ascii_alphanumeric()), terminated: self.s.eat_if('}'), @@ -391,7 +391,7 @@ impl<'s> Tokens<'s> { fn string(&mut self) -> Token<'s> { let mut escaped = false; - Token::Str(TokenStr { + Token::Str(StrToken { string: self.s.eat_until(|c| { if c == '"' && !escaped { true @@ -470,19 +470,19 @@ mod tests { use TokenMode::{Code, Markup}; const fn Raw(text: &str, backticks: usize, terminated: bool) -> Token { - Token::Raw(TokenRaw { text, backticks, terminated }) + Token::Raw(RawToken { text, backticks, terminated }) } const fn Math(formula: &str, display: bool, terminated: bool) -> Token { - Token::Math(TokenMath { formula, display, terminated }) + Token::Math(MathToken { formula, display, terminated }) } const fn UnicodeEscape(sequence: &str, terminated: bool) -> Token { - Token::UnicodeEscape(TokenUnicodeEscape { sequence, terminated }) + Token::UnicodeEscape(UnicodeEscapeToken { sequence, terminated }) } const fn Str(string: &str, terminated: bool) -> Token { - Token::Str(TokenStr { string, terminated }) + Token::Str(StrToken { string, terminated }) } const fn Color(r: u8, g: u8, b: u8, a: u8) -> Token<'static> { diff --git a/src/prelude.rs b/src/prelude.rs deleted file mode 100644 index 33542ad34..000000000 --- a/src/prelude.rs +++ /dev/null @@ -1,16 +0,0 @@ -//! A prelude for building custom functions. - -pub use crate::diag::{Diag, Pass}; -#[doc(no_inline)] -pub use crate::eval::{ - CastResult, Eval, EvalContext, TemplateFunc, TemplateNode, Value, ValueAny, - ValueArgs, ValueArray, ValueDict, ValueTemplate, -}; -#[doc(no_inline)] -pub use crate::exec::{Exec, ExecContext}; -pub use crate::geom::*; -#[doc(no_inline)] -pub use crate::layout::Node; -#[doc(no_inline)] -pub use crate::syntax::{Span, Spanned}; -pub use crate::{error, typify, warning}; diff --git a/src/pretty.rs b/src/pretty.rs index 963157c02..1db54d10a 100644 --- a/src/pretty.rs +++ b/src/pretty.rs @@ -138,7 +138,7 @@ impl PrettyWithMap for Node { } } -impl PrettyWithMap for NodeHeading { +impl PrettyWithMap for HeadingNode { fn pretty_with_map(&self, p: &mut Printer, map: Option<&ExprMap>) { for _ in 0 ..= self.level { p.push('='); @@ -147,7 +147,7 @@ impl PrettyWithMap for NodeHeading { } } -impl Pretty for NodeRaw { +impl Pretty for RawNode { fn pretty(&self, p: &mut Printer) { // Find out how many backticks we need. let mut backticks = 1; @@ -250,7 +250,7 @@ impl Pretty for LitKind { } } -impl Pretty for ExprArray { +impl Pretty for ArrayExpr { fn pretty(&self, p: &mut Printer) { p.push('('); p.join(&self.items, ", ", |item, p| item.pretty(p)); @@ -261,7 +261,7 @@ impl Pretty for ExprArray { } } -impl Pretty for ExprDict { +impl Pretty for DictExpr { fn pretty(&self, p: &mut Printer) { p.push('('); if self.items.is_empty() { @@ -281,7 +281,7 @@ impl Pretty for Named { } } -impl Pretty for ExprTemplate { +impl Pretty for TemplateExpr { fn pretty(&self, p: &mut Printer) { p.push('['); self.tree.pretty_with_map(p, None); @@ -289,7 +289,7 @@ impl Pretty for ExprTemplate { } } -impl Pretty for ExprGroup { +impl Pretty for GroupExpr { fn pretty(&self, p: &mut Printer) { p.push('('); self.expr.pretty(p); @@ -297,7 +297,7 @@ impl Pretty for ExprGroup { } } -impl Pretty for ExprBlock { +impl Pretty for BlockExpr { fn pretty(&self, p: &mut Printer) { p.push('{'); if self.exprs.len() > 1 { @@ -311,7 +311,7 @@ impl Pretty for ExprBlock { } } -impl Pretty for ExprUnary { +impl Pretty for UnaryExpr { fn pretty(&self, p: &mut Printer) { self.op.pretty(p); if self.op == UnOp::Not { @@ -327,7 +327,7 @@ impl Pretty for UnOp { } } -impl Pretty for ExprBinary { +impl Pretty for BinaryExpr { fn pretty(&self, p: &mut Printer) { self.lhs.pretty(p); p.push(' '); @@ -343,11 +343,11 @@ impl Pretty for BinOp { } } -impl Pretty for ExprCall { +impl Pretty for CallExpr { fn pretty(&self, p: &mut Printer) { self.callee.pretty(p); - let mut write_args = |items: &[ExprArg]| { + let mut write_args = |items: &[CallArg]| { p.push('('); p.join(items, ", ", |item, p| item.pretty(p)); p.push(')'); @@ -357,7 +357,7 @@ impl Pretty for ExprCall { // This can be moved behind the arguments. // // Example: Transforms "#v(a, [b])" => "#v(a)[b]". - [head @ .., ExprArg::Pos(Expr::Template(template))] => { + [head @ .., CallArg::Pos(Expr::Template(template))] => { if !head.is_empty() { write_args(head); } @@ -369,13 +369,13 @@ impl Pretty for ExprCall { } } -impl Pretty for ExprArgs { +impl Pretty for CallArgs { fn pretty(&self, p: &mut Printer) { p.join(&self.items, ", ", |item, p| item.pretty(p)); } } -impl Pretty for ExprArg { +impl Pretty for CallArg { fn pretty(&self, p: &mut Printer) { match self { Self::Pos(expr) => expr.pretty(p), @@ -384,7 +384,7 @@ impl Pretty for ExprArg { } } -impl Pretty for ExprClosure { +impl Pretty for ClosureExpr { fn pretty(&self, p: &mut Printer) { p.push('('); p.join(self.params.iter(), ", ", |item, p| item.pretty(p)); @@ -393,7 +393,7 @@ impl Pretty for ExprClosure { } } -impl Pretty for ExprLet { +impl Pretty for LetExpr { fn pretty(&self, p: &mut Printer) { p.push_str("let "); self.binding.pretty(p); @@ -404,7 +404,7 @@ impl Pretty for ExprLet { } } -impl Pretty for ExprIf { +impl Pretty for IfExpr { fn pretty(&self, p: &mut Printer) { p.push_str("if "); self.condition.pretty(p); @@ -418,7 +418,7 @@ impl Pretty for ExprIf { } } -impl Pretty for ExprWhile { +impl Pretty for WhileExpr { fn pretty(&self, p: &mut Printer) { p.push_str("while "); self.condition.pretty(p); @@ -427,7 +427,7 @@ impl Pretty for ExprWhile { } } -impl Pretty for ExprFor { +impl Pretty for ForExpr { fn pretty(&self, p: &mut Printer) { p.push_str("for "); self.pattern.pretty(p); @@ -475,14 +475,13 @@ impl Pretty for Value { Value::Dict(v) => v.pretty(p), Value::Template(v) => v.pretty(p), Value::Func(v) => v.pretty(p), - Value::Args(v) => v.pretty(p), Value::Any(v) => v.pretty(p), Value::Error => p.push_str(""), } } } -impl Pretty for ValueArray { +impl Pretty for ArrayValue { fn pretty(&self, p: &mut Printer) { p.push('('); p.join(self, ", ", |item, p| item.pretty(p)); @@ -493,7 +492,7 @@ impl Pretty for ValueArray { } } -impl Pretty for ValueDict { +impl Pretty for DictValue { fn pretty(&self, p: &mut Printer) { p.push('('); if self.is_empty() { @@ -509,7 +508,7 @@ impl Pretty for ValueDict { } } -impl Pretty for ValueTemplate { +impl Pretty for TemplateValue { fn pretty(&self, p: &mut Printer) { p.push('['); for part in self { @@ -537,7 +536,7 @@ impl Pretty for TemplateFunc { } } -impl Pretty for ValueFunc { +impl Pretty for FuncValue { fn pretty(&self, p: &mut Printer) { p.push_str(""); + test_value(FuncValue::new(None, |_, _| Value::None), ""); test_value( - ValueFunc::new(Some("nil".into()), |_, _| Value::None), + FuncValue::new(Some("nil".into()), |_, _| Value::None), "", ); - // Arguments. - test_value( - ValueArgs { - span: Span::ZERO, - items: vec![ - ValueArg { - name: Some(Spanned::zero("a".into())), - value: Spanned::zero(Value::Int(1)), - }, - ValueArg { - name: None, - value: Spanned::zero(Value::Int(2)), - }, - ], - }, - "(a: 1, 2)", - ); - // Any. - test_value(ValueAny::new(1), "1"); + test_value(AnyValue::new(1), "1"); // Error. test_value(Value::Error, ""); } + + #[test] + fn test_pretty_print_args() { + // Arguments. + assert_eq!( + pretty(&FuncArgs { + span: Span::ZERO, + items: vec![ + FuncArg { + name: Some(Spanned::zero("a".into())), + value: Spanned::zero(Value::Int(1)), + }, + FuncArg { + name: None, + value: Spanned::zero(Value::Int(2)), + }, + ], + }), + "(a: 1, 2)", + ); + } } diff --git a/src/syntax/expr.rs b/src/syntax/expr.rs index 5d10349b4..2c631991f 100644 --- a/src/syntax/expr.rs +++ b/src/syntax/expr.rs @@ -7,36 +7,36 @@ use crate::geom::{AngularUnit, LengthUnit}; /// An expression. #[derive(Debug, Clone, PartialEq)] pub enum Expr { - /// A literal. + /// A literal, like `11pt` or `"hi"`. Lit(Lit), /// An identifier: `left`. Ident(Ident), /// An array expression: `(1, "hi", 12cm)`. - Array(ExprArray), + Array(ArrayExpr), /// A dictionary expression: `(color: #f79143, pattern: dashed)`. - Dict(ExprDict), + Dict(DictExpr), /// A template expression: `[*Hi* there!]`. - Template(ExprTemplate), + Template(TemplateExpr), /// A grouped expression: `(1 + 2)`. - Group(ExprGroup), + Group(GroupExpr), /// A block expression: `{ let x = 1; x + 2 }`. - Block(ExprBlock), + Block(BlockExpr), /// A unary operation: `-x`. - Unary(ExprUnary), + Unary(UnaryExpr), /// A binary operation: `a + b`. - Binary(ExprBinary), + Binary(BinaryExpr), /// An invocation of a function: `f(x, y)`. - Call(ExprCall), + Call(CallExpr), /// A closure expression: `(x, y) => z`. - Closure(ExprClosure), + Closure(ClosureExpr), /// A let expression: `let x = 1`. - Let(ExprLet), - /// An if expression: `if x { y } else { z }`. - If(ExprIf), - /// A while expression: `while x { y }`. - While(ExprWhile), - /// A for expression: `for x in y { z }`. - For(ExprFor), + Let(LetExpr), + /// An if-else expression: `if x { y } else { z }`. + If(IfExpr), + /// A while loop expression: `while x { y }`. + While(WhileExpr), + /// A for loop expression: `for x in y { z }`. + For(ForExpr), } impl Expr { @@ -74,7 +74,7 @@ impl Expr { } } -/// A literal. +/// A literal, like `11pt` or `"hi"`. #[derive(Debug, Clone, PartialEq)] pub struct Lit { /// The source code location. @@ -111,7 +111,7 @@ pub enum LitKind { /// An array expression: `(1, "hi", 12cm)`. #[derive(Debug, Clone, PartialEq)] -pub struct ExprArray { +pub struct ArrayExpr { /// The source code location. pub span: Span, /// The entries of the array. @@ -120,7 +120,7 @@ pub struct ExprArray { /// A dictionary expression: `(color: #f79143, pattern: dashed)`. #[derive(Debug, Clone, PartialEq)] -pub struct ExprDict { +pub struct DictExpr { /// The source code location. pub span: Span, /// The named dictionary entries. @@ -145,7 +145,7 @@ impl Named { /// A template expression: `[*Hi* there!]`. #[derive(Debug, Clone, PartialEq)] -pub struct ExprTemplate { +pub struct TemplateExpr { /// The source code location. pub span: Span, /// The contents of the template. @@ -154,7 +154,7 @@ pub struct ExprTemplate { /// A grouped expression: `(1 + 2)`. #[derive(Debug, Clone, PartialEq)] -pub struct ExprGroup { +pub struct GroupExpr { /// The source code location. pub span: Span, /// The wrapped expression. @@ -163,7 +163,7 @@ pub struct ExprGroup { /// A block expression: `{ let x = 1; x + 2 }`. #[derive(Debug, Clone, PartialEq)] -pub struct ExprBlock { +pub struct BlockExpr { /// The source code location. pub span: Span, /// The list of expressions contained in the block. @@ -174,7 +174,7 @@ pub struct ExprBlock { /// A unary operation: `-x`. #[derive(Debug, Clone, PartialEq)] -pub struct ExprUnary { +pub struct UnaryExpr { /// The source code location. pub span: Span, /// The operator: `-`. @@ -225,7 +225,7 @@ impl UnOp { /// A binary operation: `a + b`. #[derive(Debug, Clone, PartialEq)] -pub struct ExprBinary { +pub struct BinaryExpr { /// The source code location. pub span: Span, /// The left-hand side of the operation: `a`. @@ -374,13 +374,13 @@ pub enum Associativity { /// An invocation of a function: `foo(...)`. #[derive(Debug, Clone, PartialEq)] -pub struct ExprCall { +pub struct CallExpr { /// The source code location. pub span: Span, /// The callee of the function. pub callee: Box, /// The arguments to the function. - pub args: ExprArgs, + pub args: CallArgs, } /// The arguments to a function: `12, draw: false`. @@ -388,23 +388,23 @@ pub struct ExprCall { /// In case of a bracketed invocation with a body, the body is _not_ /// included in the span for the sake of clearer error messages. #[derive(Debug, Clone, PartialEq)] -pub struct ExprArgs { +pub struct CallArgs { /// The source code location. pub span: Span, /// The positional and named arguments. - pub items: Vec, + pub items: Vec, } /// An argument to a function call: `12` or `draw: false`. #[derive(Debug, Clone, PartialEq)] -pub enum ExprArg { +pub enum CallArg { /// A positional argument. Pos(Expr), /// A named argument. Named(Named), } -impl ExprArg { +impl CallArg { /// The source code location. pub fn span(&self) -> Span { match self { @@ -416,7 +416,7 @@ impl ExprArg { /// A closure expression: `(x, y) => z`. #[derive(Debug, Clone, PartialEq)] -pub struct ExprClosure { +pub struct ClosureExpr { /// The source code location. pub span: Span, /// The name of the closure. @@ -431,7 +431,7 @@ pub struct ExprClosure { /// A let expression: `let x = 1`. #[derive(Debug, Clone, PartialEq)] -pub struct ExprLet { +pub struct LetExpr { /// The source code location. pub span: Span, /// The binding to assign to. @@ -440,9 +440,9 @@ pub struct ExprLet { pub init: Option>, } -/// An if expression: `if x { y } else { z }`. +/// An if-else expression: `if x { y } else { z }`. #[derive(Debug, Clone, PartialEq)] -pub struct ExprIf { +pub struct IfExpr { /// The source code location. pub span: Span, /// The condition which selects the body to evaluate. @@ -453,9 +453,9 @@ pub struct ExprIf { pub else_body: Option>, } -/// A while expression: `while x { y }`. +/// A while loop expression: `while x { y }`. #[derive(Debug, Clone, PartialEq)] -pub struct ExprWhile { +pub struct WhileExpr { /// The source code location. pub span: Span, /// The condition which selects whether to evaluate the body. @@ -464,9 +464,9 @@ pub struct ExprWhile { pub body: Box, } -/// A for expression: `for x in y { z }`. +/// A for loop expression: `for x in y { z }`. #[derive(Debug, Clone, PartialEq)] -pub struct ExprFor { +pub struct ForExpr { /// The source code location. pub span: Span, /// The pattern to assign to. diff --git a/src/syntax/ident.rs b/src/syntax/ident.rs index 26c46b988..a46ad2325 100644 --- a/src/syntax/ident.rs +++ b/src/syntax/ident.rs @@ -4,7 +4,7 @@ use unicode_xid::UnicodeXID; use super::Span; -/// An Unicode identifier with a few extra permissible characters. +/// An unicode identifier with a few extra permissible characters. /// /// In addition to what is specified in the [Unicode Standard][uax31], we allow: /// - `_` as a starting character, diff --git a/src/syntax/mod.rs b/src/syntax/mod.rs index 09c445b0b..9426090cf 100644 --- a/src/syntax/mod.rs +++ b/src/syntax/mod.rs @@ -14,4 +14,6 @@ pub use span::*; pub use token::*; /// The abstract syntax tree. +/// +/// This type can represent a full parsed document. pub type Tree = Vec; diff --git a/src/syntax/node.rs b/src/syntax/node.rs index 19fdfa50e..c94ee5b01 100644 --- a/src/syntax/node.rs +++ b/src/syntax/node.rs @@ -16,23 +16,23 @@ pub enum Node { /// Plain text. Text(String), /// A section heading. - Heading(NodeHeading), + Heading(HeadingNode), /// An optionally syntax-highlighted raw block. - Raw(NodeRaw), + Raw(RawNode), /// An expression. Expr(Expr), } /// A section heading: `= Introduction`. #[derive(Debug, Clone, PartialEq)] -pub struct NodeHeading { +pub struct HeadingNode { /// The section depth (numer of equals signs minus 1). pub level: usize, /// The contents of the heading. pub contents: Tree, } -/// A raw block with optional syntax highlighting: `` `raw` ``. +/// A raw block with optional syntax highlighting: `` `...` ``. /// /// Raw blocks start with 1 or 3+ backticks and end with the same number of /// backticks. @@ -96,7 +96,7 @@ pub struct NodeHeading { /// Note that with these rules you can always force leading or trailing /// whitespace simply by adding more spaces. #[derive(Debug, Clone, PartialEq)] -pub struct NodeRaw { +pub struct RawNode { /// An optional identifier specifying the language to syntax-highlight in. pub lang: Option, /// The lines of raw text, determined as the raw string between the diff --git a/src/syntax/span.rs b/src/syntax/span.rs index d939ed285..d3683c1a0 100644 --- a/src/syntax/span.rs +++ b/src/syntax/span.rs @@ -32,7 +32,7 @@ impl Spanned { Spanned { v: &self.v, span: self.span } } - /// Map the value using a function while keeping the span. + /// Map the value using a function keeping the span. pub fn map(self, f: F) -> Spanned where F: FnOnce(T) -> U, diff --git a/src/syntax/token.rs b/src/syntax/token.rs index e57620af4..832c923dc 100644 --- a/src/syntax/token.rs +++ b/src/syntax/token.rs @@ -99,13 +99,13 @@ pub enum Token<'s> { Text(&'s str), /// An arbitrary number of backticks followed by inner contents, terminated /// with the same number of backticks: `` `...` ``. - Raw(TokenRaw<'s>), + Raw(RawToken<'s>), /// One or two dollar signs followed by inner contents, terminated with the /// same number of dollar signs. - Math(TokenMath<'s>), + Math(MathToken<'s>), /// A slash and the letter "u" followed by a hexadecimal unicode entity /// enclosed in curly braces: `\u{1F5FA}`. - UnicodeEscape(TokenUnicodeEscape<'s>), + UnicodeEscape(UnicodeEscapeToken<'s>), /// An identifier: `center`. Ident(&'s str), /// A boolean: `true`, `false`. @@ -126,7 +126,7 @@ pub enum Token<'s> { /// A color value: `#20d82a`. Color(RgbaColor), /// A quoted string: `"..."`. - Str(TokenStr<'s>), + Str(StrToken<'s>), /// Two slashes followed by inner contents, terminated with a newline: /// `//\n`. LineComment(&'s str), @@ -139,9 +139,9 @@ pub enum Token<'s> { Invalid(&'s str), } -/// A quoted string: `"..."`. +/// A quoted string token: `"..."`. #[derive(Debug, Copy, Clone, PartialEq)] -pub struct TokenStr<'s> { +pub struct StrToken<'s> { /// The string inside the quotes. /// /// _Note_: If the string contains escape sequences these are not yet @@ -152,9 +152,9 @@ pub struct TokenStr<'s> { pub terminated: bool, } -/// A raw block: `` `...` ``. +/// A raw block token: `` `...` ``. #[derive(Debug, Copy, Clone, PartialEq)] -pub struct TokenRaw<'s> { +pub struct RawToken<'s> { /// The raw text between the backticks. pub text: &'s str, /// The number of opening backticks. @@ -163,9 +163,9 @@ pub struct TokenRaw<'s> { pub terminated: bool, } -/// A math formula: `$2pi + x$`, `$$f'(x) = x^2$$`. +/// A math formula token: `$2pi + x$` or `$[f'(x) = x^2]$`. #[derive(Debug, Copy, Clone, PartialEq)] -pub struct TokenMath<'s> { +pub struct MathToken<'s> { /// The formula between the dollars. pub formula: &'s str, /// Whether the formula is display-level, that is, it is surrounded by @@ -175,9 +175,9 @@ pub struct TokenMath<'s> { pub terminated: bool, } -/// A unicode escape sequence: `\u{1F5FA}`. +/// A unicode escape sequence token: `\u{1F5FA}`. #[derive(Debug, Copy, Clone, PartialEq)] -pub struct TokenUnicodeEscape<'s> { +pub struct UnicodeEscapeToken<'s> { /// The escape sequence between the braces. pub sequence: &'s str, /// Whether the closing brace was present. diff --git a/src/syntax/visit.rs b/src/syntax/visit.rs index 15613233a..04546c5db 100644 --- a/src/syntax/visit.rs +++ b/src/syntax/visit.rs @@ -82,29 +82,29 @@ visit! { } } - fn visit_array(v, node: &ExprArray) { + fn visit_array(v, node: &ArrayExpr) { for expr in &node.items { v.visit_expr(&expr); } } - fn visit_dict(v, node: &ExprDict) { + fn visit_dict(v, node: &DictExpr) { for named in &node.items { v.visit_expr(&named.expr); } } - fn visit_template(v, node: &ExprTemplate) { + fn visit_template(v, node: &TemplateExpr) { v.visit_enter(); v.visit_tree(&node.tree); v.visit_exit(); } - fn visit_group(v, node: &ExprGroup) { + fn visit_group(v, node: &GroupExpr) { v.visit_expr(&node.expr); } - fn visit_block(v, node: &ExprBlock) { + fn visit_block(v, node: &BlockExpr) { if node.scoping { v.visit_enter(); } @@ -116,48 +116,48 @@ visit! { } } - fn visit_binary(v, node: &ExprBinary) { + fn visit_binary(v, node: &BinaryExpr) { v.visit_expr(&node.lhs); v.visit_expr(&node.rhs); } - fn visit_unary(v, node: &ExprUnary) { + fn visit_unary(v, node: &UnaryExpr) { v.visit_expr(&node.expr); } - fn visit_call(v, node: &ExprCall) { + fn visit_call(v, node: &CallExpr) { v.visit_expr(&node.callee); v.visit_args(&node.args); } - fn visit_closure(v, node: &ExprClosure) { + fn visit_closure(v, node: &ClosureExpr) { for param in node.params.iter() { v.visit_binding(param); } v.visit_expr(&node.body); } - fn visit_args(v, node: &ExprArgs) { + fn visit_args(v, node: &CallArgs) { for arg in &node.items { v.visit_arg(arg); } } - fn visit_arg(v, node: &ExprArg) { + fn visit_arg(v, node: &CallArg) { match node { - ExprArg::Pos(expr) => v.visit_expr(&expr), - ExprArg::Named(named) => v.visit_expr(&named.expr), + CallArg::Pos(expr) => v.visit_expr(&expr), + CallArg::Named(named) => v.visit_expr(&named.expr), } } - fn visit_let(v, node: &ExprLet) { + fn visit_let(v, node: &LetExpr) { v.visit_binding(&node.binding); if let Some(init) = &node.init { v.visit_expr(&init); } } - fn visit_if(v, node: &ExprIf) { + fn visit_if(v, node: &IfExpr) { v.visit_expr(&node.condition); v.visit_expr(&node.if_body); if let Some(body) = &node.else_body { @@ -165,12 +165,12 @@ visit! { } } - fn visit_while(v, node: &ExprWhile) { + fn visit_while(v, node: &WhileExpr) { v.visit_expr(&node.condition); v.visit_expr(&node.body); } - fn visit_for(v, node: &ExprFor) { + fn visit_for(v, node: &ForExpr) { match &node.pattern { ForPattern::Value(value) => v.visit_binding(value), ForPattern::KeyValue(key, value) => { diff --git a/tests/README.md b/tests/README.md index fb755f1bf..57d47fe25 100644 --- a/tests/README.md +++ b/tests/README.md @@ -3,29 +3,35 @@ ## Directory structure Top level directory structure: - `typ`: Input files. +- `res`: Resource files used by tests. - `ref`: Reference images which the output is compared with to determine whether a test passed or failed. - `png`: PNG files produced by tests. - `pdf`: PDF files produced by tests. -- `res`: Resource files used by tests. ## Running the tests +Running the integration tests (the tests in this directory). ```bash -# Run all tests -cargo test - -# Run unit tests -cargo test --lib - -# Run integration tests (the tests in this directory) cargo test --test typeset +``` -# Run all tests whose names contain the word `filter` +Running all tests whose names contain the word `filter`. +```bash cargo test --test typeset filter ``` +To make the integration tests go faster they don't generate PDFs by default. +Pass the `--pdf` flag to generate those. Mind that PDFs are not tested +automatically at the moment, so you should always check the output manually when +making changes. +```bash +cargo test --test typeset -- --pdf +``` + ## Creating new tests -To keep things small, please optimize reference images before committing them: +To keep things small, please optimize reference images before committing them. +When you use the approve buttom from the Test Helper (see the `tools` folder) +this happens automatically if you have `oxipng` installed. ```bash # One image oxipng -o max path/to/image.png diff --git a/tests/typeset.rs b/tests/typeset.rs index 0cd146f45..7e7efc677 100644 --- a/tests/typeset.rs +++ b/tests/typeset.rs @@ -15,16 +15,15 @@ use ttf_parser::OutlineBuilder; use walkdir::WalkDir; use typst::diag::{Diag, DiagSet, Level, Pass}; -use typst::env::{Env, ImageResource, ResourceLoader}; -use typst::eval::{EvalContext, Scope, Value, ValueArgs, ValueFunc}; +use typst::env::{Env, FsIndexExt, ImageResource, ResourceLoader}; +use typst::eval::{EvalContext, FuncArgs, FuncValue, Scope, Value}; use typst::exec::State; use typst::export::pdf; -use typst::font::FsIndexExt; use typst::geom::{Length, Point, Sides, Size}; -use typst::layout::{Element, Fill, Frame, Geometry, Image, Shape}; +use typst::layout::{Element, Fill, Frame, Geometry, Image, Shape, Shaped}; use typst::library; use typst::parse::{LineMap, Scanner}; -use typst::shaping::Shaped; +use typst::pretty::pretty; use typst::syntax::{Location, Pos}; use typst::typeset; @@ -313,13 +312,18 @@ struct Panic { } fn register_helpers(scope: &mut Scope, panics: Rc>>) { - pub fn args(_: &mut EvalContext, args: &mut ValueArgs) -> Value { - let value = args.clone().into(); + pub fn args(_: &mut EvalContext, args: &mut FuncArgs) -> Value { + let repr = pretty(args); args.items.clear(); - value + Value::template("args", move |ctx| { + let snapshot = ctx.state.clone(); + ctx.set_monospace(); + ctx.push_text(&repr); + ctx.state = snapshot; + }) } - let test = move |ctx: &mut EvalContext, args: &mut ValueArgs| -> Value { + let test = move |ctx: &mut EvalContext, args: &mut FuncArgs| -> Value { let lhs = args.require::(ctx, "left-hand side"); let rhs = args.require::(ctx, "right-hand side"); if lhs != rhs { @@ -331,8 +335,8 @@ fn register_helpers(scope: &mut Scope, panics: Rc>>) { }; scope.def_const("error", Value::Error); - scope.def_const("args", ValueFunc::new(Some("args".into()), args)); - scope.def_const("test", ValueFunc::new(Some("test".into()), test)); + scope.def_const("args", FuncValue::new(Some("args".into()), args)); + scope.def_const("test", FuncValue::new(Some("test".into()), test)); } fn print_diag(diag: &Diag, map: &LineMap, lines: u32) {