Scheduled maintenance 🔨

- 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
This commit is contained in:
Laurenz 2021-03-19 17:57:31 +01:00
parent ca3df70e2a
commit 264a7dedd4
49 changed files with 663 additions and 653 deletions

View File

@ -1,11 +1,10 @@
use criterion::{criterion_group, criterion_main, Criterion}; use criterion::{criterion_group, criterion_main, Criterion};
use fontdock::fs::FsIndex; use fontdock::fs::FsIndex;
use typst::env::{Env, ResourceLoader}; use typst::env::{Env, FsIndexExt, ResourceLoader};
use typst::eval::eval; use typst::eval::eval;
use typst::exec::{exec, State}; use typst::exec::{exec, State};
use typst::export::pdf; use typst::export::pdf;
use typst::font::FsIndexExt;
use typst::layout::layout; use typst::layout::layout;
use typst::library; use typst::library;
use typst::parse::parse; use typst::parse::parse;

View File

@ -7,11 +7,13 @@ use std::fs;
use std::io::Cursor; use std::io::Cursor;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use fontdock::fs::FsSource; use fontdock::{ContainsChar, FaceFromVec, FaceId, FontSource};
use image::io::Reader as ImageReader; use image::io::Reader as ImageReader;
use image::{DynamicImage, GenericImageView, ImageFormat}; 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). /// Encapsulates all environment dependencies (fonts, resources).
#[derive(Debug)] #[derive(Debug)]
@ -25,13 +27,84 @@ pub struct Env {
impl Env { impl Env {
/// Create an empty environment for testing purposes. /// Create an empty environment for testing purposes.
pub fn blank() -> Self { pub fn blank() -> Self {
struct BlankSource;
impl FontSource for BlankSource {
type Face = FaceBuf;
fn load(&self, _: FaceId) -> Option<Self::Face> {
None
}
}
Self { Self {
fonts: FontLoader::new(Box::new(FsSource::new(vec![])), vec![]), fonts: FontLoader::new(Box::new(BlankSource), vec![]),
resources: ResourceLoader::new(), resources: ResourceLoader::new(),
} }
} }
} }
/// A font loader that is backed by a dynamic source.
pub type FontLoader = fontdock::FontLoader<Box<dyn FontSource<Face = FaceBuf>>>;
/// 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<u8>, i: u32) -> Option<Self> {
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. /// Loads resource from the file system.
pub struct ResourceLoader { pub struct ResourceLoader {
paths: HashMap<PathBuf, ResourceId>, paths: HashMap<PathBuf, ResourceId>,

View File

@ -1,7 +1,8 @@
use std::rc::Rc; use std::rc::Rc;
use super::*; use super::{Scope, Scopes, Value};
use crate::syntax::visit::*; use crate::syntax::visit::{visit_expr, Visit};
use crate::syntax::{Expr, Ident};
/// A visitor that captures variable slots. /// A visitor that captures variable slots.
#[derive(Debug)] #[derive(Debug)]

View File

@ -13,9 +13,9 @@ pub use value::*;
use std::collections::HashMap; use std::collections::HashMap;
use std::rc::Rc; use std::rc::Rc;
use super::*;
use crate::color::Color; 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::geom::{Angle, Length, Relative};
use crate::syntax::visit::Visit; use crate::syntax::visit::Visit;
use crate::syntax::*; use crate::syntax::*;
@ -143,16 +143,16 @@ impl Eval for Lit {
} }
} }
impl Eval for ExprArray { impl Eval for ArrayExpr {
type Output = ValueArray; type Output = ArrayValue;
fn eval(&self, ctx: &mut EvalContext) -> Self::Output { fn eval(&self, ctx: &mut EvalContext) -> Self::Output {
self.items.iter().map(|expr| expr.eval(ctx)).collect() self.items.iter().map(|expr| expr.eval(ctx)).collect()
} }
} }
impl Eval for ExprDict { impl Eval for DictExpr {
type Output = ValueDict; type Output = DictValue;
fn eval(&self, ctx: &mut EvalContext) -> Self::Output { fn eval(&self, ctx: &mut EvalContext) -> Self::Output {
self.items self.items
@ -162,7 +162,7 @@ impl Eval for ExprDict {
} }
} }
impl Eval for ExprTemplate { impl Eval for TemplateExpr {
type Output = TemplateNode; type Output = TemplateNode;
fn eval(&self, ctx: &mut EvalContext) -> Self::Output { 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; type Output = Value;
fn eval(&self, ctx: &mut EvalContext) -> Self::Output { 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; type Output = Value;
fn eval(&self, ctx: &mut EvalContext) -> Self::Output { 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; type Output = Value;
fn eval(&self, ctx: &mut EvalContext) -> Self::Output { 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; type Output = Value;
fn eval(&self, ctx: &mut EvalContext) -> Self::Output { 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. /// Apply a basic binary operation.
fn apply<F>(&self, ctx: &mut EvalContext, op: F) -> Value fn apply<F>(&self, ctx: &mut EvalContext, op: F) -> Value
where where
@ -335,7 +335,7 @@ impl ExprBinary {
} }
} }
impl Eval for ExprCall { impl Eval for CallExpr {
type Output = Value; type Output = Value;
fn eval(&self, ctx: &mut EvalContext) -> Self::Output { fn eval(&self, ctx: &mut EvalContext) -> Self::Output {
@ -361,25 +361,25 @@ impl Eval for ExprCall {
} }
} }
impl Eval for ExprArgs { impl Eval for CallArgs {
type Output = ValueArgs; type Output = FuncArgs;
fn eval(&self, ctx: &mut EvalContext) -> Self::Output { fn eval(&self, ctx: &mut EvalContext) -> Self::Output {
let items = self.items.iter().map(|arg| arg.eval(ctx)).collect(); 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 { impl Eval for CallArg {
type Output = ValueArg; type Output = FuncArg;
fn eval(&self, ctx: &mut EvalContext) -> Self::Output { fn eval(&self, ctx: &mut EvalContext) -> Self::Output {
match self { match self {
Self::Pos(expr) => ValueArg { Self::Pos(expr) => FuncArg {
name: None, name: None,
value: Spanned::new(expr.eval(ctx), expr.span()), 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)), name: Some(Spanned::new(name.string.clone(), name.span)),
value: Spanned::new(expr.eval(ctx), expr.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; type Output = Value;
fn eval(&self, ctx: &mut EvalContext) -> Self::Output { 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()); 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 // Don't leak the scopes from the call site. Instead, we use the
// scope of captured variables we collected earlier. // scope of captured variables we collected earlier.
let prev = std::mem::take(&mut ctx.scopes); 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; type Output = Value;
fn eval(&self, ctx: &mut EvalContext) -> Self::Output { 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; type Output = Value;
fn eval(&self, ctx: &mut EvalContext) -> Self::Output { 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; type Output = Value;
fn eval(&self, ctx: &mut EvalContext) -> Self::Output { 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; type Output = Value;
fn eval(&self, ctx: &mut EvalContext) -> Self::Output { fn eval(&self, ctx: &mut EvalContext) -> Self::Output {

View File

@ -1,4 +1,5 @@
use super::*; use super::{ArrayValue, DictValue, TemplateNode, Value};
use crate::syntax::Span;
use Value::*; use Value::*;
/// Apply the plus operator to a 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. /// 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)) a.len() == b.len() && a.iter().zip(b).all(|(x, y)| value_eq(x, y))
} }
/// Compute whether two dictionaries are equal. /// 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.len() == b.len()
&& a.iter().all(|(k, x)| b.get(k).map_or(false, |y| value_eq(x, y))) && a.iter().all(|(k, x)| b.get(k).map_or(false, |y| value_eq(x, y)))
} }

View File

@ -4,11 +4,12 @@ use std::fmt::{self, Debug, Display, Formatter};
use std::ops::Deref; use std::ops::Deref;
use std::rc::Rc; use std::rc::Rc;
use super::*; use super::{EvalContext, ExprMap};
use crate::color::Color; use crate::color::Color;
use crate::diag::DiagSet;
use crate::exec::ExecContext; use crate::exec::ExecContext;
use crate::geom::{Angle, Length, Linear, Relative}; use crate::geom::{Angle, Length, Linear, Relative};
use crate::syntax::Tree; use crate::syntax::{Span, Spanned, Tree};
/// A computational value. /// A computational value.
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
@ -34,17 +35,15 @@ pub enum Value {
/// A string: `"string"`. /// A string: `"string"`.
Str(String), Str(String),
/// An array value: `(1, "hi", 12cm)`. /// An array value: `(1, "hi", 12cm)`.
Array(ValueArray), Array(ArrayValue),
/// A dictionary value: `(color: #f79143, pattern: dashed)`. /// A dictionary value: `(color: #f79143, pattern: dashed)`.
Dict(ValueDict), Dict(DictValue),
/// A template value: `[*Hi* there]`. /// A template value: `[*Hi* there]`.
Template(ValueTemplate), Template(TemplateValue),
/// An executable function. /// An executable function.
Func(ValueFunc), Func(FuncValue),
/// Arguments to a function.
Args(ValueArgs),
/// Any object. /// Any object.
Any(ValueAny), Any(AnyValue),
/// The result of invalid operations. /// The result of invalid operations.
Error, Error,
} }
@ -71,11 +70,10 @@ impl Value {
Self::Linear(_) => Linear::TYPE_NAME, Self::Linear(_) => Linear::TYPE_NAME,
Self::Color(_) => Color::TYPE_NAME, Self::Color(_) => Color::TYPE_NAME,
Self::Str(_) => String::TYPE_NAME, Self::Str(_) => String::TYPE_NAME,
Self::Array(_) => ValueArray::TYPE_NAME, Self::Array(_) => ArrayValue::TYPE_NAME,
Self::Dict(_) => ValueDict::TYPE_NAME, Self::Dict(_) => DictValue::TYPE_NAME,
Self::Template(_) => ValueTemplate::TYPE_NAME, Self::Template(_) => TemplateValue::TYPE_NAME,
Self::Func(_) => ValueFunc::TYPE_NAME, Self::Func(_) => FuncValue::TYPE_NAME,
Self::Args(_) => ValueArgs::TYPE_NAME,
Self::Any(v) => v.type_name(), Self::Any(v) => v.type_name(),
Self::Error => "error", Self::Error => "error",
} }
@ -97,13 +95,13 @@ impl Default for Value {
} }
/// An array value: `(1, "hi", 12cm)`. /// An array value: `(1, "hi", 12cm)`.
pub type ValueArray = Vec<Value>; pub type ArrayValue = Vec<Value>;
/// A dictionary value: `(color: #f79143, pattern: dashed)`. /// A dictionary value: `(color: #f79143, pattern: dashed)`.
pub type ValueDict = BTreeMap<String, Value>; pub type DictValue = BTreeMap<String, Value>;
/// A template value: `[*Hi* there]`. /// A template value: `[*Hi* there]`.
pub type ValueTemplate = Vec<TemplateNode>; pub type TemplateValue = Vec<TemplateNode>;
/// One chunk of a template. /// One chunk of a template.
/// ///
@ -171,16 +169,16 @@ impl Debug for TemplateFunc {
/// A wrapper around a reference-counted executable function. /// A wrapper around a reference-counted executable function.
#[derive(Clone)] #[derive(Clone)]
pub struct ValueFunc { pub struct FuncValue {
name: Option<String>, name: Option<String>,
f: Rc<dyn Fn(&mut EvalContext, &mut ValueArgs) -> Value>, f: Rc<dyn Fn(&mut EvalContext, &mut FuncArgs) -> Value>,
} }
impl ValueFunc { impl FuncValue {
/// Create a new function value from a rust function or closure. /// Create a new function value from a rust function or closure.
pub fn new<F>(name: Option<String>, f: F) -> Self pub fn new<F>(name: Option<String>, f: F) -> Self
where where
F: Fn(&mut EvalContext, &mut ValueArgs) -> Value + 'static, F: Fn(&mut EvalContext, &mut FuncArgs) -> Value + 'static,
{ {
Self { name, f: Rc::new(f) } 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 { fn eq(&self, _: &Self) -> bool {
// TODO: Figure out what we want here. // TODO: Figure out what we want here.
false false
} }
} }
impl Deref for ValueFunc { impl Deref for FuncValue {
type Target = dyn Fn(&mut EvalContext, &mut ValueArgs) -> Value; type Target = dyn Fn(&mut EvalContext, &mut FuncArgs) -> Value;
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
self.f.as_ref() self.f.as_ref()
} }
} }
impl Debug for ValueFunc { impl Debug for FuncValue {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.debug_struct("ValueFunc").field("name", &self.name).finish() f.debug_struct("ValueFunc").field("name", &self.name).finish()
} }
@ -214,14 +212,14 @@ impl Debug for ValueFunc {
/// Evaluated arguments to a function. /// Evaluated arguments to a function.
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub struct ValueArgs { pub struct FuncArgs {
/// The span of the whole argument list. /// The span of the whole argument list.
pub span: Span, pub span: Span,
/// The arguments. /// The arguments.
pub items: Vec<ValueArg>, pub items: Vec<FuncArg>,
} }
impl ValueArgs { impl FuncArgs {
/// Find and remove the first convertible positional argument. /// Find and remove the first convertible positional argument.
pub fn find<T>(&mut self, ctx: &mut EvalContext) -> Option<T> pub fn find<T>(&mut self, ctx: &mut EvalContext) -> Option<T>
where where
@ -345,14 +343,14 @@ impl ValueArgs {
/// An argument to a function call: `12` or `draw: false`. /// An argument to a function call: `12` or `draw: false`.
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub struct ValueArg { pub struct FuncArg {
/// The name of the argument (`None` for positional arguments). /// The name of the argument (`None` for positional arguments).
pub name: Option<Spanned<String>>, pub name: Option<Spanned<String>>,
/// The value of the argument. /// The value of the argument.
pub value: Spanned<Value>, pub value: Spanned<Value>,
} }
impl ValueArg { impl FuncArg {
/// The source code location. /// The source code location.
pub fn span(&self) -> Span { pub fn span(&self) -> Span {
match &self.name { match &self.name {
@ -363,9 +361,9 @@ impl ValueArg {
} }
/// A wrapper around a dynamic value. /// A wrapper around a dynamic value.
pub struct ValueAny(Box<dyn Bounds>); pub struct AnyValue(Box<dyn Bounds>);
impl ValueAny { impl AnyValue {
/// Create a new instance from any value that satisifies the required bounds. /// Create a new instance from any value that satisifies the required bounds.
pub fn new<T>(any: T) -> Self pub fn new<T>(any: T) -> Self
where where
@ -399,25 +397,25 @@ impl ValueAny {
} }
} }
impl Clone for ValueAny { impl Clone for AnyValue {
fn clone(&self) -> Self { fn clone(&self) -> Self {
Self(self.0.dyn_clone()) Self(self.0.dyn_clone())
} }
} }
impl PartialEq for ValueAny { impl PartialEq for AnyValue {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
self.0.dyn_eq(other) self.0.dyn_eq(other)
} }
} }
impl Debug for ValueAny { impl Debug for AnyValue {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.debug_tuple("ValueAny").field(&self.0).finish() f.debug_tuple("ValueAny").field(&self.0).finish()
} }
} }
impl Display for ValueAny { impl Display for AnyValue {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
Display::fmt(&self.0, f) Display::fmt(&self.0, f)
} }
@ -426,7 +424,7 @@ impl Display for ValueAny {
trait Bounds: Debug + Display + 'static { trait Bounds: Debug + Display + 'static {
fn as_any(&self) -> &dyn Any; fn as_any(&self) -> &dyn Any;
fn into_any(self: Box<Self>) -> Box<dyn Any>; fn into_any(self: Box<Self>) -> Box<dyn Any>;
fn dyn_eq(&self, other: &ValueAny) -> bool; fn dyn_eq(&self, other: &AnyValue) -> bool;
fn dyn_clone(&self) -> Box<dyn Bounds>; fn dyn_clone(&self) -> Box<dyn Bounds>;
fn dyn_type_name(&self) -> &'static str; fn dyn_type_name(&self) -> &'static str;
} }
@ -443,7 +441,7 @@ where
self self
} }
fn dyn_eq(&self, other: &ValueAny) -> bool { fn dyn_eq(&self, other: &AnyValue) -> bool {
if let Some(other) = other.downcast_ref::<Self>() { if let Some(other) = other.downcast_ref::<Self>() {
self == other self == other
} else { } else {
@ -584,15 +582,14 @@ primitive! {
} }
primitive! { Color: "color", Value::Color } primitive! { Color: "color", Value::Color }
primitive! { String: "string", Value::Str } primitive! { String: "string", Value::Str }
primitive! { ValueArray: "array", Value::Array } primitive! { ArrayValue: "array", Value::Array }
primitive! { ValueDict: "dictionary", Value::Dict } primitive! { DictValue: "dictionary", Value::Dict }
primitive! { primitive! {
ValueTemplate: "template", TemplateValue: "template",
Value::Template, Value::Template,
Value::Str(v) => vec![TemplateNode::Str(v)], Value::Str(v) => vec![TemplateNode::Str(v)],
} }
primitive! { ValueFunc: "function", Value::Func } primitive! { FuncValue: "function", Value::Func }
primitive! { ValueArgs: "arguments", Value::Args }
impl From<usize> for Value { impl From<usize> for Value {
fn from(v: usize) -> Self { fn from(v: usize) -> Self {
@ -606,8 +603,8 @@ impl From<&str> for Value {
} }
} }
impl From<ValueAny> for Value { impl From<AnyValue> for Value {
fn from(v: ValueAny) -> Self { fn from(v: AnyValue) -> Self {
Self::Any(v) Self::Any(v)
} }
} }

View File

@ -3,13 +3,16 @@ use std::rc::Rc;
use fontdock::FontStyle; use fontdock::FontStyle;
use super::*; use super::{Exec, State};
use crate::diag::{Diag, DiagSet}; use crate::diag::{Diag, DiagSet, Pass};
use crate::env::Env;
use crate::eval::TemplateValue;
use crate::geom::{Dir, Gen, Linear, Sides, Size}; use crate::geom::{Dir, Gen, Linear, Sides, Size};
use crate::layout::{ 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::parse::is_newline;
use crate::syntax::{Span, Spanned};
/// The context for execution. /// The context for execution.
#[derive(Debug)] #[derive(Debug)]
@ -26,9 +29,9 @@ pub struct ExecContext<'a> {
page: Option<PageInfo>, page: Option<PageInfo>,
/// The content of the active stack. This may be the top-level stack for the /// 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). /// page or a lower one created by [`exec`](Self::exec).
stack: NodeStack, stack: StackNode,
/// The content of the active paragraph. /// The content of the active paragraph.
par: NodePar, par: ParNode,
} }
impl<'a> ExecContext<'a> { impl<'a> ExecContext<'a> {
@ -39,8 +42,8 @@ impl<'a> ExecContext<'a> {
diags: DiagSet::new(), diags: DiagSet::new(),
tree: Tree { runs: vec![] }, tree: Tree { runs: vec![] },
page: Some(PageInfo::new(&state, true)), page: Some(PageInfo::new(&state, true)),
stack: NodeStack::new(&state), stack: StackNode::new(&state),
par: NodePar::new(&state), par: ParNode::new(&state),
state, state,
} }
} }
@ -78,7 +81,7 @@ impl<'a> ExecContext<'a> {
/// Push a layout node into the active paragraph. /// Push a layout node into the active paragraph.
/// ///
/// Spacing nodes will be handled according to their /// Spacing nodes will be handled according to their
/// [`softness`](NodeSpacing::softness). /// [`softness`](SpacingNode::softness).
pub fn push(&mut self, node: impl Into<Node>) { pub fn push(&mut self, node: impl Into<Node>) {
push(&mut self.par.children, node.into()); push(&mut self.par.children, node.into());
} }
@ -86,7 +89,7 @@ impl<'a> ExecContext<'a> {
/// Push a word space into the active paragraph. /// Push a word space into the active paragraph.
pub fn push_space(&mut self) { pub fn push_space(&mut self) {
let em = self.state.font.font_size(); let em = self.state.font.font_size();
self.push(NodeSpacing { self.push(SpacingNode {
amount: self.state.par.word_spacing.resolve(em), amount: self.state.par.word_spacing.resolve(em),
softness: 1, softness: 1,
}); });
@ -111,7 +114,7 @@ impl<'a> ExecContext<'a> {
/// Apply a forced line break. /// Apply a forced line break.
pub fn push_linebreak(&mut self) { pub fn push_linebreak(&mut self) {
let em = self.state.font.font_size(); let em = self.state.font.font_size();
self.push_into_stack(NodeSpacing { self.push_into_stack(SpacingNode {
amount: self.state.par.leading.resolve(em), amount: self.state.par.leading.resolve(em),
softness: 2, softness: 2,
}); });
@ -120,7 +123,7 @@ impl<'a> ExecContext<'a> {
/// Apply a forced paragraph break. /// Apply a forced paragraph break.
pub fn push_parbreak(&mut self) { pub fn push_parbreak(&mut self) {
let em = self.state.font.font_size(); let em = self.state.font.font_size();
self.push_into_stack(NodeSpacing { self.push_into_stack(SpacingNode {
amount: self.state.par.spacing.resolve(em), amount: self.state.par.spacing.resolve(em),
softness: 1, softness: 1,
}); });
@ -134,10 +137,10 @@ impl<'a> ExecContext<'a> {
} }
/// Execute a template and return the result as a stack node. /// 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 page = self.page.take();
let stack = mem::replace(&mut self.stack, NodeStack::new(&self.state)); let stack = mem::replace(&mut self.stack, StackNode::new(&self.state));
let par = mem::replace(&mut self.par, NodePar::new(&self.state)); let par = mem::replace(&mut self.par, ParNode::new(&self.state));
template.exec(self); template.exec(self);
let result = self.finish_stack(); 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 /// Construct a text node from the given string based on the active text
/// state. /// 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; let mut variant = self.state.font.variant;
if self.state.font.strong { if self.state.font.strong {
@ -166,7 +169,7 @@ impl<'a> ExecContext<'a> {
} }
} }
NodeText { TextNode {
text, text,
dir: self.state.dirs.cross, dir: self.state.dirs.cross,
aligns: self.state.aligns, aligns: self.state.aligns,
@ -180,7 +183,7 @@ impl<'a> ExecContext<'a> {
/// Finish the active paragraph. /// Finish the active paragraph.
fn finish_par(&mut self) { 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); trim(&mut par.children);
if !par.children.is_empty() { if !par.children.is_empty() {
@ -189,10 +192,10 @@ impl<'a> ExecContext<'a> {
} }
/// Finish the active stack. /// Finish the active stack.
fn finish_stack(&mut self) -> NodeStack { fn finish_stack(&mut self) -> StackNode {
self.finish_par(); 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); trim(&mut stack.children);
stack stack
@ -205,9 +208,9 @@ impl<'a> ExecContext<'a> {
let stack = self.finish_stack(); let stack = self.finish_stack();
if !stack.children.is_empty() || (keep && info.hard) { if !stack.children.is_empty() || (keep && info.hard) {
self.tree.runs.push(NodePages { self.tree.runs.push(PageRun {
size: info.size, size: info.size,
child: NodePad { child: PadNode {
padding: info.padding, padding: info.padding,
child: stack.into(), child: stack.into(),
} }
@ -274,7 +277,7 @@ impl PageInfo {
} }
} }
impl NodeStack { impl StackNode {
fn new(state: &State) -> Self { fn new(state: &State) -> Self {
Self { Self {
dirs: state.dirs, dirs: state.dirs,
@ -284,7 +287,7 @@ impl NodeStack {
} }
} }
impl NodePar { impl ParNode {
fn new(state: &State) -> Self { fn new(state: &State) -> Self {
let em = state.font.font_size(); let em = state.font.font_size();
Self { Self {

View File

@ -10,8 +10,8 @@ use std::rc::Rc;
use crate::diag::Pass; use crate::diag::Pass;
use crate::env::Env; use crate::env::Env;
use crate::eval::{ExprMap, TemplateFunc, TemplateNode, Value, ValueTemplate}; use crate::eval::{ExprMap, TemplateFunc, TemplateNode, TemplateValue, Value};
use crate::layout::{self, NodeFixed, NodeSpacing, NodeStack}; use crate::layout::{self, FixedNode, SpacingNode, StackNode};
use crate::pretty::pretty; use crate::pretty::pretty;
use crate::syntax::*; 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) { fn exec_with_map(&self, ctx: &mut ExecContext, map: &ExprMap) {
let prev = ctx.state.clone(); let prev = ctx.state.clone();
let upscale = 1.5 - 0.1 * self.level as f64; 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) { fn exec(&self, ctx: &mut ExecContext) {
let prev = Rc::clone(&ctx.state.font.families); let prev = Rc::clone(&ctx.state.font.families);
ctx.set_monospace(); ctx.set_monospace();
@ -103,7 +103,7 @@ impl Exec for NodeRaw {
let mut newline = false; let mut newline = false;
for line in &self.lines { for line in &self.lines {
if newline { if newline {
children.push(layout::Node::Spacing(NodeSpacing { children.push(layout::Node::Spacing(SpacingNode {
amount: leading, amount: leading,
softness: 2, 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 // This is wrapped in a fixed node to make sure the stack fits to its
// content instead of filling the available area. // content instead of filling the available area.
ctx.push(NodeFixed { ctx.push(FixedNode {
width: None, width: None,
height: None, height: None,
child: NodeStack { child: StackNode {
dirs: ctx.state.dirs, dirs: ctx.state.dirs,
aligns: ctx.state.aligns, aligns: ctx.state.aligns,
children, children,
@ -159,7 +159,7 @@ impl Exec for Value {
} }
} }
impl Exec for ValueTemplate { impl Exec for TemplateValue {
fn exec(&self, ctx: &mut ExecContext) { fn exec(&self, ctx: &mut ExecContext) {
for node in self { for node in self {
node.exec(ctx); node.exec(ctx);

View File

@ -3,8 +3,8 @@ use std::rc::Rc;
use fontdock::{fallback, FallbackTree, FontStretch, FontStyle, FontVariant, FontWeight}; use fontdock::{fallback, FallbackTree, FontStretch, FontStyle, FontVariant, FontWeight};
use crate::geom::*; use crate::geom::*;
use crate::layout::VerticalFontMetric;
use crate::paper::{Paper, PaperClass, PAPER_A4}; use crate::paper::{Paper, PaperClass, PAPER_A4};
use crate::shaping::VerticalFontMetric;
/// The evaluation state. /// The evaluation state.
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]

View File

@ -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<Box<dyn FontSource<Face = FaceBuf>>>;
/// 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<u8>, i: u32) -> Option<Self> {
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)
}
}

View File

@ -1,5 +1,3 @@
use std::f64::consts::PI;
use super::*; use super::*;
/// An angle. /// An angle.

View File

@ -26,6 +26,7 @@ pub use sides::*;
pub use size::*; pub use size::*;
pub use spec::*; pub use spec::*;
use std::f64::consts::PI;
use std::fmt::{self, Debug, Display, Formatter}; use std::fmt::{self, Debug, Display, Formatter};
use std::iter::Sum; use std::iter::Sum;
use std::ops::*; use std::ops::*;

View File

@ -1,16 +1,16 @@
use super::*; 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)] #[derive(Debug, Clone, PartialEq)]
pub struct NodeBackground { pub struct BackgroundNode {
/// The background fill. /// The background fill.
pub fill: Fill, pub fill: Fill,
/// The child node to be filled. /// The child node to be filled.
pub child: Node, pub child: Node,
} }
impl Layout for NodeBackground { impl Layout for BackgroundNode {
fn layout(&self, ctx: &mut LayoutContext, areas: &Areas) -> Layouted { fn layout(&self, ctx: &mut LayoutContext, areas: &Areas) -> Fragment {
let mut layouted = self.child.layout(ctx, areas); let mut layouted = self.child.layout(ctx, areas);
for frame in layouted.frames_mut() { for frame in layouted.frames_mut() {
@ -25,8 +25,8 @@ impl Layout for NodeBackground {
} }
} }
impl From<NodeBackground> for NodeAny { impl From<BackgroundNode> for AnyNode {
fn from(background: NodeBackground) -> Self { fn from(background: BackgroundNode) -> Self {
Self::new(background) Self::new(background)
} }
} }

View File

@ -1,9 +1,8 @@
use super::*; use super::*;
use crate::geom::Linear;
/// A node that can fix its child's width and height. /// A node that can fix its child's width and height.
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub struct NodeFixed { pub struct FixedNode {
/// The fixed width, if any. /// The fixed width, if any.
pub width: Option<Linear>, pub width: Option<Linear>,
/// The fixed height, if any. /// The fixed height, if any.
@ -12,8 +11,8 @@ pub struct NodeFixed {
pub child: Node, pub child: Node,
} }
impl Layout for NodeFixed { impl Layout for FixedNode {
fn layout(&self, ctx: &mut LayoutContext, areas: &Areas) -> Layouted { fn layout(&self, ctx: &mut LayoutContext, areas: &Areas) -> Fragment {
let Areas { current, full, .. } = areas; let Areas { current, full, .. } = areas;
let size = Size::new( let size = Size::new(
self.width.map(|w| w.resolve(full.width)).unwrap_or(current.width), self.width.map(|w| w.resolve(full.width)).unwrap_or(current.width),
@ -31,8 +30,8 @@ impl Layout for NodeFixed {
} }
} }
impl From<NodeFixed> for NodeAny { impl From<FixedNode> for AnyNode {
fn from(fixed: NodeFixed) -> Self { fn from(fixed: FixedNode) -> Self {
Self::new(fixed) Self::new(fixed)
} }
} }

82
src/layout/frame.rs Normal file
View File

@ -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<Fill> 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,
}

View File

@ -2,27 +2,29 @@
mod background; mod background;
mod fixed; mod fixed;
mod frame;
mod node; mod node;
mod pad; mod pad;
mod par; mod par;
mod shaping;
mod spacing; mod spacing;
mod stack; mod stack;
mod text; mod text;
use crate::color::Color;
use crate::env::{Env, ResourceId};
use crate::geom::*;
use crate::shaping::Shaped;
pub use background::*; pub use background::*;
pub use fixed::*; pub use fixed::*;
pub use frame::*;
pub use node::*; pub use node::*;
pub use pad::*; pub use pad::*;
pub use par::*; pub use par::*;
pub use shaping::*;
pub use spacing::*; pub use spacing::*;
pub use stack::*; pub use stack::*;
pub use text::*; pub use text::*;
use crate::env::Env;
use crate::geom::*;
/// Layout a tree into a collection of frames. /// Layout a tree into a collection of frames.
pub fn layout(env: &mut Env, tree: &Tree) -> Vec<Frame> { pub fn layout(env: &mut Env, tree: &Tree) -> Vec<Frame> {
tree.layout(&mut LayoutContext { env }) tree.layout(&mut LayoutContext { env })
@ -32,7 +34,7 @@ pub fn layout(env: &mut Env, tree: &Tree) -> Vec<Frame> {
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub struct Tree { pub struct Tree {
/// Runs of pages with the same properties. /// Runs of pages with the same properties.
pub runs: Vec<NodePages>, pub runs: Vec<PageRun>,
} }
impl Tree { impl Tree {
@ -44,15 +46,15 @@ impl Tree {
/// A run of pages that all have the same properties. /// A run of pages that all have the same properties.
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub struct NodePages { pub struct PageRun {
/// The size of each page. /// The size of each page.
pub size: Size, pub size: Size,
/// The layout node that produces the actual pages (typically a /// The layout node that produces the actual pages (typically a
/// [`NodeStack`]). /// [`StackNode`]).
pub child: Node, pub child: Node,
} }
impl NodePages { impl PageRun {
/// Layout the page run. /// Layout the page run.
pub fn layout(&self, ctx: &mut LayoutContext) -> Vec<Frame> { pub fn layout(&self, ctx: &mut LayoutContext) -> Vec<Frame> {
let areas = Areas::repeat(self.size, Spec::uniform(Expand::Fill)); let areas = Areas::repeat(self.size, Spec::uniform(Expand::Fill));
@ -64,7 +66,7 @@ impl NodePages {
/// Layout a node. /// Layout a node.
pub trait Layout { pub trait Layout {
/// Layout the node into the given areas. /// 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. /// The context for layouting.
@ -74,7 +76,7 @@ pub struct LayoutContext<'a> {
pub env: &'a mut Env, pub env: &'a mut Env,
} }
/// A collection of areas to layout into. /// A sequence of areas to layout into.
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub struct Areas { pub struct Areas {
/// The remaining size of the current area. /// The remaining size of the current area.
@ -155,7 +157,7 @@ impl Expand {
/// The result of layouting a node. /// The result of layouting a node.
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub enum Layouted { pub enum Fragment {
/// Spacing that should be added to the parent. /// Spacing that should be added to the parent.
Spacing(Length), Spacing(Length),
/// A layout that should be added to and aligned in the parent. /// A layout that should be added to and aligned in the parent.
@ -164,7 +166,7 @@ pub enum Layouted {
Frames(Vec<Frame>, LayoutAligns), Frames(Vec<Frame>, LayoutAligns),
} }
impl Layouted { impl Fragment {
/// Return a reference to all frames contained in this variant (zero, one or /// Return a reference to all frames contained in this variant (zero, one or
/// arbitrarily many). /// arbitrarily many).
pub fn frames(&self) -> &[Frame] { 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<Fill> 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,
}

View File

@ -7,15 +7,15 @@ use super::*;
#[derive(Clone, PartialEq)] #[derive(Clone, PartialEq)]
pub enum Node { pub enum Node {
/// A text node. /// A text node.
Text(NodeText), Text(TextNode),
/// A spacing node. /// A spacing node.
Spacing(NodeSpacing), Spacing(SpacingNode),
/// A dynamic node that can implement custom layouting behaviour. /// A dynamic node that can implement custom layouting behaviour.
Any(NodeAny), Any(AnyNode),
} }
impl Layout for Node { impl Layout for Node {
fn layout(&self, ctx: &mut LayoutContext, areas: &Areas) -> Layouted { fn layout(&self, ctx: &mut LayoutContext, areas: &Areas) -> Fragment {
match self { match self {
Self::Spacing(spacing) => spacing.layout(ctx, areas), Self::Spacing(spacing) => spacing.layout(ctx, areas),
Self::Text(text) => text.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. /// A wrapper around a dynamic layouting node.
pub struct NodeAny(Box<dyn Bounds>); pub struct AnyNode(Box<dyn Bounds>);
impl NodeAny { impl AnyNode {
/// Create a new instance from any node that satisifies the required bounds. /// Create a new instance from any node that satisifies the required bounds.
pub fn new<T>(any: T) -> Self pub fn new<T>(any: T) -> Self
where where
@ -47,25 +47,25 @@ impl NodeAny {
} }
} }
impl Layout for NodeAny { impl Layout for AnyNode {
fn layout(&self, ctx: &mut LayoutContext, areas: &Areas) -> Layouted { fn layout(&self, ctx: &mut LayoutContext, areas: &Areas) -> Fragment {
self.0.layout(ctx, areas) self.0.layout(ctx, areas)
} }
} }
impl Clone for NodeAny { impl Clone for AnyNode {
fn clone(&self) -> Self { fn clone(&self) -> Self {
Self(self.0.dyn_clone()) Self(self.0.dyn_clone())
} }
} }
impl PartialEq for NodeAny { impl PartialEq for AnyNode {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
self.0.dyn_eq(other.0.as_ref()) self.0.dyn_eq(other.0.as_ref())
} }
} }
impl Debug for NodeAny { impl Debug for AnyNode {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
self.0.fmt(f) self.0.fmt(f)
} }
@ -73,7 +73,7 @@ impl Debug for NodeAny {
impl<T> From<T> for Node impl<T> From<T> for Node
where where
T: Into<NodeAny>, T: Into<AnyNode>,
{ {
fn from(t: T) -> Self { fn from(t: T) -> Self {
Self::Any(t.into()) Self::Any(t.into())

View File

@ -1,17 +1,16 @@
use super::*; use super::*;
use crate::geom::Linear;
/// A node that adds padding to its child. /// A node that adds padding to its child.
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub struct NodePad { pub struct PadNode {
/// The amount of padding. /// The amount of padding.
pub padding: Sides<Linear>, pub padding: Sides<Linear>,
/// The child node whose sides to pad. /// The child node whose sides to pad.
pub child: Node, pub child: Node,
} }
impl Layout for NodePad { impl Layout for PadNode {
fn layout(&self, ctx: &mut LayoutContext, areas: &Areas) -> Layouted { fn layout(&self, ctx: &mut LayoutContext, areas: &Areas) -> Fragment {
let areas = shrink(areas, self.padding); let areas = shrink(areas, self.padding);
let mut layouted = self.child.layout(ctx, &areas); let mut layouted = self.child.layout(ctx, &areas);
@ -23,8 +22,8 @@ impl Layout for NodePad {
} }
} }
impl From<NodePad> for NodeAny { impl From<PadNode> for AnyNode {
fn from(pad: NodePad) -> Self { fn from(pad: PadNode) -> Self {
Self::new(pad) Self::new(pad)
} }
} }

View File

@ -2,7 +2,7 @@ use super::*;
/// A node that arranges its children into a paragraph. /// A node that arranges its children into a paragraph.
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub struct NodePar { pub struct ParNode {
/// The `main` and `cross` directions of this paragraph. /// The `main` and `cross` directions of this paragraph.
/// ///
/// The children are placed in lines along the `cross` direction. The lines /// The children are placed in lines along the `cross` direction. The lines
@ -16,28 +16,28 @@ pub struct NodePar {
pub children: Vec<Node>, pub children: Vec<Node>,
} }
impl Layout for NodePar { impl Layout for ParNode {
fn layout(&self, ctx: &mut LayoutContext, areas: &Areas) -> Layouted { fn layout(&self, ctx: &mut LayoutContext, areas: &Areas) -> Fragment {
let mut layouter = ParLayouter::new(self, areas.clone()); let mut layouter = ParLayouter::new(self, areas.clone());
for child in &self.children { for child in &self.children {
match child.layout(ctx, &layouter.areas) { match child.layout(ctx, &layouter.areas) {
Layouted::Spacing(spacing) => layouter.push_spacing(spacing), Fragment::Spacing(spacing) => layouter.push_spacing(spacing),
Layouted::Frame(frame, aligns) => { Fragment::Frame(frame, aligns) => {
layouter.push_frame(frame, aligns.cross) layouter.push_frame(frame, aligns.cross)
} }
Layouted::Frames(frames, aligns) => { Fragment::Frames(frames, aligns) => {
for frame in frames { for frame in frames {
layouter.push_frame(frame, aligns.cross); layouter.push_frame(frame, aligns.cross);
} }
} }
} }
} }
Layouted::Frames(layouter.finish(), self.aligns) Fragment::Frames(layouter.finish(), self.aligns)
} }
} }
impl From<NodePar> for NodeAny { impl From<ParNode> for AnyNode {
fn from(par: NodePar) -> Self { fn from(par: ParNode) -> Self {
Self::new(par) Self::new(par)
} }
} }
@ -57,7 +57,7 @@ struct ParLayouter {
} }
impl ParLayouter { impl ParLayouter {
fn new(par: &NodePar, areas: Areas) -> Self { fn new(par: &ParNode, areas: Areas) -> Self {
Self { Self {
main: par.dirs.main.axis(), main: par.dirs.main.axis(),
cross: par.dirs.cross.axis(), cross: par.dirs.cross.axis(),

View File

@ -9,7 +9,7 @@ use std::fmt::{self, Debug, Display, Formatter};
use fontdock::{FaceId, FaceQuery, FallbackTree, FontVariant}; use fontdock::{FaceId, FaceQuery, FallbackTree, FontVariant};
use ttf_parser::{Face, GlyphId}; use ttf_parser::{Face, GlyphId};
use crate::font::FontLoader; use crate::env::FontLoader;
use crate::geom::{Dir, Length, Point, Size}; use crate::geom::{Dir, Length, Point, Size};
use crate::layout::{Element, Frame}; use crate::layout::{Element, Frame};

View File

@ -2,9 +2,9 @@ use std::fmt::{self, Debug, Formatter};
use super::*; use super::*;
/// A spacing node. /// A node that adds spacing to its parent.
#[derive(Copy, Clone, PartialEq)] #[derive(Copy, Clone, PartialEq)]
pub struct NodeSpacing { pub struct SpacingNode {
/// The amount of spacing to insert. /// The amount of spacing to insert.
pub amount: Length, pub amount: Length,
/// Defines how spacing interacts with surrounding spacing. /// Defines how spacing interacts with surrounding spacing.
@ -16,20 +16,20 @@ pub struct NodeSpacing {
pub softness: u8, pub softness: u8,
} }
impl Layout for NodeSpacing { impl Layout for SpacingNode {
fn layout(&self, _: &mut LayoutContext, _: &Areas) -> Layouted { fn layout(&self, _: &mut LayoutContext, _: &Areas) -> Fragment {
Layouted::Spacing(self.amount) Fragment::Spacing(self.amount)
} }
} }
impl Debug for NodeSpacing { impl Debug for SpacingNode {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "Spacing({}, {})", self.amount, self.softness) write!(f, "Spacing({}, {})", self.amount, self.softness)
} }
} }
impl From<NodeSpacing> for Node { impl From<SpacingNode> for Node {
fn from(spacing: NodeSpacing) -> Self { fn from(spacing: SpacingNode) -> Self {
Self::Spacing(spacing) Self::Spacing(spacing)
} }
} }

View File

@ -2,7 +2,7 @@ use super::*;
/// A node that stacks its children. /// A node that stacks its children.
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub struct NodeStack { pub struct StackNode {
/// The `main` and `cross` directions of this stack. /// The `main` and `cross` directions of this stack.
/// ///
/// The children are stacked along the `main` direction. The `cross` /// The children are stacked along the `main` direction. The `cross`
@ -14,26 +14,26 @@ pub struct NodeStack {
pub children: Vec<Node>, pub children: Vec<Node>,
} }
impl Layout for NodeStack { impl Layout for StackNode {
fn layout(&self, ctx: &mut LayoutContext, areas: &Areas) -> Layouted { fn layout(&self, ctx: &mut LayoutContext, areas: &Areas) -> Fragment {
let mut layouter = StackLayouter::new(self, areas.clone()); let mut layouter = StackLayouter::new(self, areas.clone());
for child in &self.children { for child in &self.children {
match child.layout(ctx, &layouter.areas) { match child.layout(ctx, &layouter.areas) {
Layouted::Spacing(spacing) => layouter.push_spacing(spacing), Fragment::Spacing(spacing) => layouter.push_spacing(spacing),
Layouted::Frame(frame, aligns) => layouter.push_frame(frame, aligns), Fragment::Frame(frame, aligns) => layouter.push_frame(frame, aligns),
Layouted::Frames(frames, aligns) => { Fragment::Frames(frames, aligns) => {
for frame in frames { for frame in frames {
layouter.push_frame(frame, aligns); layouter.push_frame(frame, aligns);
} }
} }
} }
} }
Layouted::Frames(layouter.finish(), self.aligns) Fragment::Frames(layouter.finish(), self.aligns)
} }
} }
impl From<NodeStack> for NodeAny { impl From<StackNode> for AnyNode {
fn from(stack: NodeStack) -> Self { fn from(stack: StackNode) -> Self {
Self::new(stack) Self::new(stack)
} }
} }
@ -49,7 +49,7 @@ struct StackLayouter {
} }
impl StackLayouter { impl StackLayouter {
fn new(stack: &NodeStack, areas: Areas) -> Self { fn new(stack: &StackNode, areas: Areas) -> Self {
Self { Self {
main: stack.dirs.main.axis(), main: stack.dirs.main.axis(),
dirs: stack.dirs, dirs: stack.dirs,

View File

@ -4,11 +4,10 @@ use std::rc::Rc;
use fontdock::{FallbackTree, FontVariant}; use fontdock::{FallbackTree, FontVariant};
use super::*; use super::*;
use crate::shaping::{shape, VerticalFontMetric};
/// A text node. /// A consecutive, styled run of text.
#[derive(Clone, PartialEq)] #[derive(Clone, PartialEq)]
pub struct NodeText { pub struct TextNode {
/// The text. /// The text.
pub text: String, pub text: String,
/// The text direction. /// The text direction.
@ -27,9 +26,9 @@ pub struct NodeText {
pub bottom_edge: VerticalFontMetric, pub bottom_edge: VerticalFontMetric,
} }
impl Layout for NodeText { impl Layout for TextNode {
fn layout(&self, ctx: &mut LayoutContext, _: &Areas) -> Layouted { fn layout(&self, ctx: &mut LayoutContext, _: &Areas) -> Fragment {
Layouted::Frame( Fragment::Frame(
shape( shape(
&self.text, &self.text,
self.dir, 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 { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "Text({})", self.text) write!(f, "Text({})", self.text)
} }
} }
impl From<NodeText> for Node { impl From<TextNode> for Node {
fn from(text: NodeText) -> Self { fn from(text: TextNode) -> Self {
Self::Text(text) Self::Text(text)
} }
} }

View File

@ -9,12 +9,13 @@
//! computes the value of each expression in document and stores them in a map //! computes the value of each expression in document and stores them in a map
//! from expression-pointers to values. //! from expression-pointers to values.
//! - **Execution:** Now, we can [execute] the parsed and evaluated "script". //! - **Execution:** Now, we can [execute] the parsed and evaluated "script".
//! This produces a [layout tree], a high-level, fully styled representation. //! This produces a [layout tree], a high-level, fully styled representation
//! The nodes of this tree are self-contained and order-independent and thus //! of the document. The nodes of this tree are self-contained and
//! much better suited for layouting than the syntax tree. //! 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 //! - **Layouting:** Next, the tree is [layouted] into a portable version of the
//! typeset document. The output of this is a vector of [`Frame`]s //! typeset document. The output of this is a collection of [`Frame`]s (one
//! (corresponding to pages), ready for exporting. //! per page), ready for exporting.
//! - **Exporting:** The finished layout can be exported into a supported //! - **Exporting:** The finished layout can be exported into a supported
//! format. Submodules for these formats are located in the [export] module. //! format. Submodules for these formats are located in the [export] module.
//! Currently, the only supported output format is [_PDF_]. //! Currently, the only supported output format is [_PDF_].
@ -36,15 +37,12 @@ pub mod color;
pub mod env; pub mod env;
pub mod exec; pub mod exec;
pub mod export; pub mod export;
pub mod font;
pub mod geom; pub mod geom;
pub mod layout; pub mod layout;
pub mod library; pub mod library;
pub mod paper; pub mod paper;
pub mod parse; pub mod parse;
pub mod prelude;
pub mod pretty; pub mod pretty;
pub mod shaping;
pub mod syntax; pub mod syntax;
use crate::diag::Pass; use crate::diag::Pass;

View File

@ -26,12 +26,12 @@ use super::*;
/// - `top` /// - `top`
/// - `bottom` /// - `bottom`
/// - `center` /// - `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 first = args.find(ctx);
let second = args.find(ctx); let second = args.find(ctx);
let hor = args.get(ctx, "horizontal"); let hor = args.get(ctx, "horizontal");
let ver = args.get(ctx, "vertical"); let ver = args.get(ctx, "vertical");
let body = args.find::<ValueTemplate>(ctx); let body = args.find::<TemplateValue>(ctx);
Value::template("align", move |ctx| { Value::template("align", move |ctx| {
let snapshot = ctx.state.clone(); let snapshot = ctx.state.clone();

View File

@ -10,7 +10,7 @@ use super::*;
/// ///
/// # Return value /// # Return value
/// The string representation of the 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::<Value>(ctx, "value") { match args.require::<Value>(ctx, "value") {
Some(value) => pretty(&value).into(), Some(value) => pretty(&value).into(),
None => Value::Error, None => Value::Error,
@ -27,7 +27,7 @@ pub fn repr(ctx: &mut EvalContext, args: &mut ValueArgs) -> Value {
/// ///
/// # Return value /// # Return value
/// The color with the given components. /// 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 r = args.require(ctx, "red component");
let g = args.require(ctx, "green component"); let g = args.require(ctx, "green component");
let b = args.require(ctx, "blue component"); let b = args.require(ctx, "blue component");
@ -57,7 +57,7 @@ pub fn rgb(ctx: &mut EvalContext, args: &mut ValueArgs) -> Value {
/// ///
/// # Return value /// # Return value
/// The name of the value's type as a string. /// 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::<Value>(ctx, "value") { match args.require::<Value>(ctx, "value") {
Some(value) => value.type_name().into(), Some(value) => value.type_name().into(),
None => Value::Error, None => Value::Error,

View File

@ -1,7 +1,6 @@
use fontdock::{FontStretch, FontStyle, FontWeight}; use fontdock::{FontStretch, FontStyle, FontWeight};
use super::*; use super::*;
use crate::shaping::VerticalFontMetric;
/// `font`: Configure the font. /// `font`: Configure the font.
/// ///
@ -55,7 +54,7 @@ use crate::shaping::VerticalFontMetric;
/// - `x-height` /// - `x-height`
/// - `baseline` /// - `baseline`
/// - `descender` /// - `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::<Linear>(ctx); let size = args.find::<Linear>(ctx);
let list: Vec<_> = args.filter::<FontFamily>(ctx).map(|f| f.to_string()).collect(); let list: Vec<_> = args.filter::<FontFamily>(ctx).map(|f| f.to_string()).collect();
let style = args.get(ctx, "style"); 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 serif = args.get(ctx, "serif");
let sans_serif = args.get(ctx, "sans-serif"); let sans_serif = args.get(ctx, "sans-serif");
let monospace = args.get(ctx, "monospace"); let monospace = args.get(ctx, "monospace");
let body = args.find::<ValueTemplate>(ctx); let body = args.find::<TemplateValue>(ctx);
Value::template("font", move |ctx| { Value::template("font", move |ctx| {
let snapshot = ctx.state.clone(); let snapshot = ctx.state.clone();

View File

@ -2,7 +2,9 @@ use ::image::GenericImageView;
use super::*; use super::*;
use crate::env::{ImageResource, ResourceId}; use crate::env::{ImageResource, ResourceId};
use crate::layout::*; use crate::layout::{
AnyNode, Areas, Element, Fragment, Frame, Image, Layout, LayoutContext,
};
/// `image`: Insert an image. /// `image`: Insert an image.
/// ///
@ -13,7 +15,7 @@ use crate::layout::*;
/// ///
/// # Return value /// # Return value
/// A template that inserts an image. /// 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::<Spanned<String>>(ctx, "path to image file"); let path = args.require::<Spanned<String>>(ctx, "path to image file");
let width = args.get(ctx, "width"); let width = args.get(ctx, "width");
let height = args.get(ctx, "height"); let height = args.get(ctx, "height");
@ -53,7 +55,7 @@ struct NodeImage {
} }
impl Layout for 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 Areas { current, full, .. } = areas;
let pixel_width = self.dimensions.0 as f64; let pixel_width = self.dimensions.0 as f64;
@ -84,11 +86,11 @@ impl Layout for NodeImage {
let mut frame = Frame::new(size); let mut frame = Frame::new(size);
frame.push(Point::ZERO, Element::Image(Image { res: self.res, size })); frame.push(Point::ZERO, Element::Image(Image { res: self.res, size }));
Layouted::Frame(frame, self.aligns) Fragment::Frame(frame, self.aligns)
} }
} }
impl From<NodeImage> for NodeAny { impl From<NodeImage> for AnyNode {
fn from(image: NodeImage) -> Self { fn from(image: NodeImage) -> Self {
Self::new(image) Self::new(image)
} }

View File

@ -27,68 +27,72 @@ use std::fmt::{self, Display, Formatter};
use fontdock::{FontStyle, FontWeight}; use fontdock::{FontStyle, FontWeight};
use crate::eval::{Scope, ValueAny, ValueFunc}; use crate::eval::{AnyValue, FuncValue, Scope};
use crate::layout::*; use crate::eval::{EvalContext, FuncArgs, TemplateValue, Value};
use crate::prelude::*; use crate::exec::{Exec, ExecContext};
use crate::shaping::VerticalFontMetric; use crate::geom::*;
use crate::layout::VerticalFontMetric;
use crate::syntax::Spanned;
/// Construct a scope containing all standard library definitions. /// Construct a scope containing all standard library definitions.
pub fn new() -> Scope { pub fn new() -> Scope {
let mut std = Scope::new(); let mut std = Scope::new();
macro_rules! set {
(func: $name:expr, $func:expr) => { macro_rules! func {
std.def_const($name, ValueFunc::new(Some($name.into()), $func)) ($name:expr, $func:expr) => {
}; std.def_const($name, FuncValue::new(Some($name.into()), $func))
(any: $var:expr, $any:expr) => {
std.def_const($var, ValueAny::new($any))
}; };
} }
// Functions. macro_rules! constant {
set!(func: "align", align); ($var:expr, $any:expr) => {
set!(func: "font", font); std.def_const($var, AnyValue::new($any))
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);
// Constants. func!("align", align);
set!(any: "left", AlignValue::Left); func!("font", font);
set!(any: "center", AlignValue::Center); func!("h", h);
set!(any: "right", AlignValue::Right); func!("image", image);
set!(any: "top", AlignValue::Top); func!("pad", pad);
set!(any: "bottom", AlignValue::Bottom); func!("page", page);
set!(any: "ltr", Dir::LTR); func!("pagebreak", pagebreak);
set!(any: "rtl", Dir::RTL); func!("paragraph", par);
set!(any: "ttb", Dir::TTB); func!("rect", rect);
set!(any: "btt", Dir::BTT); func!("repr", repr);
set!(any: "serif", FontFamily::Serif); func!("rgb", rgb);
set!(any: "sans-serif", FontFamily::SansSerif); func!("type", type_);
set!(any: "monospace", FontFamily::Monospace); func!("v", v);
set!(any: "normal", FontStyle::Normal);
set!(any: "italic", FontStyle::Italic); constant!("left", AlignValue::Left);
set!(any: "oblique", FontStyle::Oblique); constant!("center", AlignValue::Center);
set!(any: "thin", FontWeight::THIN); constant!("right", AlignValue::Right);
set!(any: "extralight", FontWeight::EXTRALIGHT); constant!("top", AlignValue::Top);
set!(any: "light", FontWeight::LIGHT); constant!("bottom", AlignValue::Bottom);
set!(any: "regular", FontWeight::REGULAR); constant!("ltr", Dir::LTR);
set!(any: "medium", FontWeight::MEDIUM); constant!("rtl", Dir::RTL);
set!(any: "semibold", FontWeight::SEMIBOLD); constant!("ttb", Dir::TTB);
set!(any: "bold", FontWeight::BOLD); constant!("btt", Dir::BTT);
set!(any: "extrabold", FontWeight::EXTRABOLD); constant!("serif", FontFamily::Serif);
set!(any: "black", FontWeight::BLACK); constant!("sans-serif", FontFamily::SansSerif);
set!(any: "ascender", VerticalFontMetric::Ascender); constant!("monospace", FontFamily::Monospace);
set!(any: "cap-height", VerticalFontMetric::CapHeight); constant!("normal", FontStyle::Normal);
set!(any: "x-height", VerticalFontMetric::XHeight); constant!("italic", FontStyle::Italic);
set!(any: "baseline", VerticalFontMetric::Baseline); constant!("oblique", FontStyle::Oblique);
set!(any: "descender", VerticalFontMetric::Descender); 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 std
} }

View File

@ -1,4 +1,5 @@
use super::*; use super::*;
use crate::layout::PadNode;
/// `pad`: Pad content at the sides. /// `pad`: Pad content at the sides.
/// ///
@ -14,13 +15,13 @@ use super::*;
/// ///
/// # Return value /// # Return value
/// A template that pads the body at the sides. /// 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 all = args.find(ctx);
let left = args.get(ctx, "left"); let left = args.get(ctx, "left");
let top = args.get(ctx, "top"); let top = args.get(ctx, "top");
let right = args.get(ctx, "right"); let right = args.get(ctx, "right");
let bottom = args.get(ctx, "bottom"); let bottom = args.get(ctx, "bottom");
let body = args.require::<ValueTemplate>(ctx, "body").unwrap_or_default(); let body = args.require::<TemplateValue>(ctx, "body").unwrap_or_default();
let padding = Sides::new( let padding = Sides::new(
left.or(all).unwrap_or_default(), 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| { Value::template("pad", move |ctx| {
let snapshot = ctx.state.clone(); let snapshot = ctx.state.clone();
let child = ctx.exec(&body).into(); let child = ctx.exec(&body).into();
ctx.push(NodePad { padding, child }); ctx.push(PadNode { padding, child });
ctx.state = snapshot; ctx.state = snapshot;
}) })
} }

View File

@ -30,7 +30,7 @@ use crate::paper::{Paper, PaperClass};
/// - `rtl` (right to left) /// - `rtl` (right to left)
/// - `ttb` (top to bottom) /// - `ttb` (top to bottom)
/// - `btt` (bottom to top) /// - `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::<Spanned<String>>(ctx).and_then(|name| { let paper = args.find::<Spanned<String>>(ctx).and_then(|name| {
Paper::from_name(&name.v).or_else(|| { Paper::from_name(&name.v).or_else(|| {
ctx.diag(error!(name.span, "invalid paper name")); 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 flip = args.get(ctx, "flip");
let main = args.get(ctx, "main-dir"); let main = args.get(ctx, "main-dir");
let cross = args.get(ctx, "cross-dir"); let cross = args.get(ctx, "cross-dir");
let body = args.find::<ValueTemplate>(ctx); let body = args.find::<TemplateValue>(ctx);
let span = args.span; let span = args.span;
Value::template("page", move |ctx| { Value::template("page", move |ctx| {
@ -110,7 +110,7 @@ pub fn page(ctx: &mut EvalContext, args: &mut ValueArgs) -> Value {
/// ///
/// # Return value /// # Return value
/// A template that starts a new page. /// 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; let span = args.span;
Value::template("pagebreak", move |ctx| { Value::template("pagebreak", move |ctx| {
ctx.finish_page(true, true, span); ctx.finish_page(true, true, span);

View File

@ -2,6 +2,9 @@ use super::*;
/// `paragraph`: Configure paragraphs. /// `paragraph`: Configure paragraphs.
/// ///
/// # Positional parameters
/// - Body: optional, of type `template`.
///
/// # Named parameters /// # Named parameters
/// - Paragraph spacing: `spacing`, of type `linear` relative to current font size. /// - Paragraph spacing: `spacing`, of type `linear` relative to current font size.
/// - Line leading: `leading`, 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 /// # Return value
/// A template that configures paragraph properties. The effect is scoped to the /// A template that configures paragraph properties. The effect is scoped to the
/// body if present. /// 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 spacing = args.get(ctx, "spacing");
let leading = args.get(ctx, "leading"); let leading = args.get(ctx, "leading");
let word_spacing = args.get(ctx, "word-spacing"); let word_spacing = args.get(ctx, "word-spacing");
let body = args.find::<ValueTemplate>(ctx); let body = args.find::<TemplateValue>(ctx);
Value::template("paragraph", move |ctx| { Value::template("paragraph", move |ctx| {
let snapshot = ctx.state.clone(); let snapshot = ctx.state.clone();

View File

@ -1,4 +1,5 @@
use super::*; use super::*;
use crate::layout::{BackgroundNode, Fill, FixedNode};
/// `rect`: Create a rectangular box. /// `rect`: Create a rectangular box.
/// ///
@ -21,13 +22,13 @@ use super::*;
/// - `rtl` (right to left) /// - `rtl` (right to left)
/// - `ttb` (top to bottom) /// - `ttb` (top to bottom)
/// - `btt` (bottom to top) /// - `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 width = args.get(ctx, "width");
let height = args.get(ctx, "height"); let height = args.get(ctx, "height");
let main = args.get(ctx, "main-dir"); let main = args.get(ctx, "main-dir");
let cross = args.get(ctx, "cross-dir"); let cross = args.get(ctx, "cross-dir");
let fill = args.get(ctx, "fill"); let fill = args.get(ctx, "fill");
let body = args.find::<ValueTemplate>(ctx).unwrap_or_default(); let body = args.find::<TemplateValue>(ctx).unwrap_or_default();
Value::template("box", move |ctx| { Value::template("box", move |ctx| {
let snapshot = ctx.state.clone(); 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)); ctx.set_dirs(Gen::new(main, cross));
let child = ctx.exec(&body).into(); let child = ctx.exec(&body).into();
let fixed = NodeFixed { width, height, child }; let fixed = FixedNode { width, height, child };
if let Some(color) = fill { if let Some(color) = fill {
ctx.push(NodeBackground { ctx.push(BackgroundNode {
fill: Fill::Color(color), fill: Fill::Color(color),
child: fixed.into(), child: fixed.into(),
}); });

View File

@ -1,4 +1,5 @@
use super::*; use super::*;
use crate::layout::SpacingNode;
/// `h`: Add horizontal spacing. /// `h`: Add horizontal spacing.
/// ///
@ -7,7 +8,7 @@ use super::*;
/// ///
/// # Return value /// # Return value
/// A template that adds horizontal spacing. /// 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) spacing(ctx, args, SpecAxis::Horizontal)
} }
@ -18,17 +19,17 @@ pub fn h(ctx: &mut EvalContext, args: &mut ValueArgs) -> Value {
/// ///
/// # Return value /// # Return value
/// A template that adds vertical spacing. /// 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) spacing(ctx, args, SpecAxis::Vertical)
} }
/// Apply spacing along a specific axis. /// 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<Linear> = args.require(ctx, "spacing"); let spacing: Option<Linear> = args.require(ctx, "spacing");
Value::template("spacing", move |ctx| { Value::template("spacing", move |ctx| {
if let Some(linear) = spacing { if let Some(linear) = spacing {
let amount = linear.resolve(ctx.state.font.font_size()); 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() { if axis == ctx.state.dirs.main.axis() {
ctx.push_into_stack(spacing); ctx.push_into_stack(spacing);
} else { } else {

View File

@ -5,10 +5,9 @@ use anyhow::{anyhow, bail, Context};
use fontdock::fs::FsIndex; use fontdock::fs::FsIndex;
use typst::diag::Pass; use typst::diag::Pass;
use typst::env::{Env, ResourceLoader}; use typst::env::{Env, FsIndexExt, ResourceLoader};
use typst::exec::State; use typst::exec::State;
use typst::export::pdf; use typst::export::pdf;
use typst::font::FsIndexExt;
use typst::library; use typst::library;
use typst::parse::LineMap; use typst::parse::LineMap;
use typst::typeset; use typst::typeset;

View File

@ -136,11 +136,11 @@ fn heading(p: &mut Parser) -> Node {
contents.extend(node(p, &mut false)); contents.extend(node(p, &mut false));
} }
Node::Heading(NodeHeading { level, contents }) Node::Heading(HeadingNode { level, contents })
} }
/// Handle a raw block. /// 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()); let raw = resolve::resolve_raw(token.text, token.backticks, p.start());
if !token.terminated { if !token.terminated {
p.diag(error!(p.peek_span().end, "expected backtick(s)")); 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. /// 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 span = p.peek_span();
let text = if let Some(c) = resolve::resolve_hex(token.sequence) { let text = if let Some(c) = resolve::resolve_hex(token.sequence) {
c.to_string() c.to_string()
@ -184,7 +184,7 @@ fn expr_with(p: &mut Parser, atomic: bool, min_prec: usize) -> Option<Expr> {
Some(op) => { Some(op) => {
let prec = op.precedence(); let prec = op.precedence();
let expr = Box::new(expr_with(p, atomic, prec)?); 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)?, None => primary(p, atomic)?,
}; };
@ -225,7 +225,7 @@ fn expr_with(p: &mut Parser, atomic: bool, min_prec: usize) -> Option<Expr> {
}; };
let span = lhs.span().join(rhs.span()); 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) Some(lhs)
@ -248,7 +248,7 @@ fn primary(p: &mut Parser, atomic: bool) -> Option<Expr> {
// Arrow means this is a closure's lone parameter. // Arrow means this is a closure's lone parameter.
Some(if !atomic && p.eat_if(Token::Arrow) { Some(if !atomic && p.eat_if(Token::Arrow) {
let body = expr(p)?; let body = expr(p)?;
Expr::Closure(ExprClosure { Expr::Closure(ClosureExpr {
span: id.span.join(body.span()), span: id.span.join(body.span()),
name: None, name: None,
params: Rc::new(vec![id]), params: Rc::new(vec![id]),
@ -306,7 +306,7 @@ fn literal(p: &mut Parser) -> Option<Expr> {
/// - Dictionary literal /// - Dictionary literal
/// - Parenthesized expression /// - Parenthesized expression
/// - Parameter list of closure expression /// - Parameter list of closure expression
pub fn parenthesized(p: &mut Parser) -> Option<Expr> { fn parenthesized(p: &mut Parser) -> Option<Expr> {
p.start_group(Group::Paren, TokenMode::Code); p.start_group(Group::Paren, TokenMode::Code);
let colon = p.eat_if(Token::Colon); let colon = p.eat_if(Token::Colon);
let (items, has_comma) = collection(p); let (items, has_comma) = collection(p);
@ -321,7 +321,7 @@ pub fn parenthesized(p: &mut Parser) -> Option<Expr> {
if p.eat_if(Token::Arrow) { if p.eat_if(Token::Arrow) {
let params = params(p, items); let params = params(p, items);
let body = expr(p)?; let body = expr(p)?;
return Some(Expr::Closure(ExprClosure { return Some(Expr::Closure(ClosureExpr {
span: span.join(body.span()), span: span.join(body.span()),
name: None, name: None,
params: Rc::new(params), params: Rc::new(params),
@ -332,21 +332,21 @@ pub fn parenthesized(p: &mut Parser) -> Option<Expr> {
// Find out which kind of collection this is. // Find out which kind of collection this is.
Some(match items.as_slice() { Some(match items.as_slice() {
[] => array(p, items, span), [] => array(p, items, span),
[ExprArg::Pos(_)] if !has_comma => match items.into_iter().next() { [CallArg::Pos(_)] if !has_comma => match items.into_iter().next() {
Some(ExprArg::Pos(expr)) => { Some(CallArg::Pos(expr)) => {
Expr::Group(ExprGroup { span, expr: Box::new(expr) }) Expr::Group(GroupExpr { span, expr: Box::new(expr) })
} }
_ => unreachable!(), _ => unreachable!(),
}, },
[ExprArg::Pos(_), ..] => array(p, items, span), [CallArg::Pos(_), ..] => array(p, items, span),
[ExprArg::Named(_), ..] => dict(p, items, span), [CallArg::Named(_), ..] => dict(p, items, span),
}) })
} }
/// Parse a collection. /// Parse a collection.
/// ///
/// Returns whether the literal contained any commas. /// Returns whether the literal contained any commas.
fn collection(p: &mut Parser) -> (Vec<ExprArg>, bool) { fn collection(p: &mut Parser) -> (Vec<CallArg>, bool) {
let mut items = vec![]; let mut items = vec![];
let mut has_comma = false; let mut has_comma = false;
let mut missing_coma = None; let mut missing_coma = None;
@ -376,52 +376,52 @@ fn collection(p: &mut Parser) -> (Vec<ExprArg>, bool) {
} }
/// Parse an expression or a named pair. /// Parse an expression or a named pair.
fn item(p: &mut Parser) -> Option<ExprArg> { fn item(p: &mut Parser) -> Option<CallArg> {
let first = expr(p)?; let first = expr(p)?;
if p.eat_if(Token::Colon) { if p.eat_if(Token::Colon) {
if let Expr::Ident(name) = first { if let Expr::Ident(name) = first {
Some(ExprArg::Named(Named { name, expr: expr(p)? })) Some(CallArg::Named(Named { name, expr: expr(p)? }))
} else { } else {
p.diag(error!(first.span(), "expected identifier")); p.diag(error!(first.span(), "expected identifier"));
expr(p); expr(p);
None None
} }
} else { } else {
Some(ExprArg::Pos(first)) Some(CallArg::Pos(first))
} }
} }
/// Convert a collection into an array, producing errors for named items. /// Convert a collection into an array, producing errors for named items.
fn array(p: &mut Parser, items: Vec<ExprArg>, span: Span) -> Expr { fn array(p: &mut Parser, items: Vec<CallArg>, span: Span) -> Expr {
let items = items.into_iter().filter_map(|item| match item { let items = items.into_iter().filter_map(|item| match item {
ExprArg::Pos(expr) => Some(expr), CallArg::Pos(expr) => Some(expr),
ExprArg::Named(_) => { CallArg::Named(_) => {
p.diag(error!(item.span(), "expected expression, found named pair")); p.diag(error!(item.span(), "expected expression, found named pair"));
None 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. /// Convert a collection into a dictionary, producing errors for expressions.
fn dict(p: &mut Parser, items: Vec<ExprArg>, span: Span) -> Expr { fn dict(p: &mut Parser, items: Vec<CallArg>, span: Span) -> Expr {
let items = items.into_iter().filter_map(|item| match item { let items = items.into_iter().filter_map(|item| match item {
ExprArg::Named(named) => Some(named), CallArg::Named(named) => Some(named),
ExprArg::Pos(_) => { CallArg::Pos(_) => {
p.diag(error!(item.span(), "expected named pair, found expression")); p.diag(error!(item.span(), "expected named pair, found expression"));
None 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 /// Convert a collection into a parameter list, producing errors for anything
/// other than identifiers. /// other than identifiers.
fn params(p: &mut Parser, items: Vec<ExprArg>) -> Vec<Ident> { fn params(p: &mut Parser, items: Vec<CallArg>) -> Vec<Ident> {
let items = items.into_iter().filter_map(|item| match item { 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")); p.diag(error!(item.span(), "expected identifier"));
None None
@ -435,7 +435,7 @@ fn template(p: &mut Parser) -> Expr {
p.start_group(Group::Bracket, TokenMode::Markup); p.start_group(Group::Bracket, TokenMode::Markup);
let tree = Rc::new(tree(p)); let tree = Rc::new(tree(p));
let span = p.end_group(); let span = p.end_group();
Expr::Template(ExprTemplate { span, tree }) Expr::Template(TemplateExpr { span, tree })
} }
/// Parse a block expression: `{...}`. /// Parse a block expression: `{...}`.
@ -454,7 +454,7 @@ fn block(p: &mut Parser, scoping: bool) -> Expr {
p.skip_white(); p.skip_white();
} }
let span = p.end_group(); let span = p.end_group();
Expr::Block(ExprBlock { span, exprs, scoping }) Expr::Block(BlockExpr { span, exprs, scoping })
} }
/// Parse a function call. /// Parse a function call.
@ -466,7 +466,7 @@ fn call(p: &mut Parser, callee: Expr) -> Expr {
p.end_group(); p.end_group();
args args
} }
_ => ExprArgs { _ => CallArgs {
span: Span::at(callee.span().end), span: Span::at(callee.span().end),
items: vec![], items: vec![],
}, },
@ -474,10 +474,10 @@ fn call(p: &mut Parser, callee: Expr) -> Expr {
if p.peek_direct() == Some(Token::LeftBracket) { if p.peek_direct() == Some(Token::LeftBracket) {
let body = template(p); 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), span: p.span(callee.span().start),
callee: Box::new(callee), callee: Box::new(callee),
args, args,
@ -485,10 +485,10 @@ fn call(p: &mut Parser, callee: Expr) -> Expr {
} }
/// Parse the arguments to a function call. /// Parse the arguments to a function call.
fn args(p: &mut Parser) -> ExprArgs { fn args(p: &mut Parser) -> CallArgs {
let start = p.start(); let start = p.start();
let items = collection(p).0; let items = collection(p).0;
ExprArgs { span: p.span(start), items } CallArgs { span: p.span(start), items }
} }
/// Parse a let expression. /// Parse a let expression.
@ -518,7 +518,7 @@ fn expr_let(p: &mut Parser) -> Option<Expr> {
// Rewrite into a closure expression if it's a function definition. // Rewrite into a closure expression if it's a function definition.
if let Some(params) = parameters { if let Some(params) = parameters {
let body = init?; let body = init?;
init = Some(Expr::Closure(ExprClosure { init = Some(Expr::Closure(ClosureExpr {
span: binding.span.join(body.span()), span: binding.span.join(body.span()),
name: Some(binding.clone()), name: Some(binding.clone()),
params: Rc::new(params), params: Rc::new(params),
@ -526,7 +526,7 @@ fn expr_let(p: &mut Parser) -> Option<Expr> {
})); }));
} }
expr_let = Some(Expr::Let(ExprLet { expr_let = Some(Expr::Let(LetExpr {
span: p.span(start), span: p.span(start),
binding, binding,
init: init.map(Box::new), init: init.map(Box::new),
@ -555,7 +555,7 @@ fn expr_if(p: &mut Parser) -> Option<Expr> {
else_body = body(p); else_body = body(p);
} }
expr_if = Some(Expr::If(ExprIf { expr_if = Some(Expr::If(IfExpr {
span: p.span(start), span: p.span(start),
condition: Box::new(condition), condition: Box::new(condition),
if_body: Box::new(if_body), if_body: Box::new(if_body),
@ -575,7 +575,7 @@ fn expr_while(p: &mut Parser) -> Option<Expr> {
let mut expr_while = None; let mut expr_while = None;
if let Some(condition) = expr(p) { if let Some(condition) = expr(p) {
if let Some(body) = body(p) { if let Some(body) = body(p) {
expr_while = Some(Expr::While(ExprWhile { expr_while = Some(Expr::While(WhileExpr {
span: p.span(start), span: p.span(start),
condition: Box::new(condition), condition: Box::new(condition),
body: Box::new(body), body: Box::new(body),
@ -596,7 +596,7 @@ fn expr_for(p: &mut Parser) -> Option<Expr> {
if p.expect(Token::In) { if p.expect(Token::In) {
if let Some(iter) = expr(p) { if let Some(iter) = expr(p) {
if let Some(body) = body(p) { if let Some(body) = body(p) {
expr_for = Some(Expr::For(ExprFor { expr_for = Some(Expr::For(ForExpr {
span: p.span(start), span: p.span(start),
pattern, pattern,
iter: Box::new(iter), iter: Box::new(iter),

View File

@ -1,5 +1,5 @@
use super::{is_newline, Scanner}; use super::{is_newline, Scanner};
use crate::syntax::{Ident, NodeRaw, Pos}; use crate::syntax::{Ident, Pos, RawNode};
/// Resolve all escape sequences in a string. /// Resolve all escape sequences in a string.
pub fn resolve_string(string: &str) -> String { pub fn resolve_string(string: &str) -> String {
@ -47,17 +47,17 @@ pub fn resolve_hex(sequence: &str) -> Option<char> {
} }
/// Resolve the language tag and trims the raw text. /// 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 { if backticks > 1 {
let (tag, inner) = split_at_lang_tag(text); let (tag, inner) = split_at_lang_tag(text);
let (lines, had_newline) = trim_and_split_raw(inner); let (lines, had_newline) = trim_and_split_raw(inner);
NodeRaw { RawNode {
lang: Ident::new(tag, start .. start + tag.len()), lang: Ident::new(tag, start .. start + tag.len()),
lines, lines,
block: had_newline, block: had_newline,
} }
} else { } else {
NodeRaw { RawNode {
lang: None, lang: None,
lines: split_lines(text), lines: split_lines(text),
block: false, block: false,
@ -105,7 +105,7 @@ fn trim_and_split_raw(mut raw: &str) -> (Vec<String>, bool) {
/// Split a string into a vector of lines /// Split a string into a vector of lines
/// (respecting Unicode, Unix, Mac and Windows line breaks). /// (respecting Unicode, Unix, Mac and Windows line breaks).
pub fn split_lines(text: &str) -> Vec<String> { fn split_lines(text: &str) -> Vec<String> {
let mut s = Scanner::new(text); let mut s = Scanner::new(text);
let mut line = String::new(); let mut line = String::new();
let mut lines = Vec::new(); let mut lines = Vec::new();
@ -174,7 +174,7 @@ mod tests {
lines: &[&str], lines: &[&str],
block: bool, 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)), lang: lang.and_then(|id| Ident::new(id, 0)),
lines: lines.iter().map(ToString::to_string).collect(), lines: lines.iter().map(ToString::to_string).collect(),
block, block,

View File

@ -232,7 +232,7 @@ impl<'s> Tokens<'s> {
// Special case for empty inline block. // Special case for empty inline block.
if backticks == 2 { 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(); let start = self.s.index();
@ -249,7 +249,7 @@ impl<'s> Tokens<'s> {
let terminated = found == backticks; let terminated = found == backticks;
let end = self.s.index() - if terminated { found } else { 0 }; let end = self.s.index() - if terminated { found } else { 0 };
Token::Raw(TokenRaw { Token::Raw(RawToken {
text: self.s.get(start .. end), text: self.s.get(start .. end),
backticks, backticks,
terminated, terminated,
@ -286,7 +286,7 @@ impl<'s> Tokens<'s> {
(true, true) => 2, (true, true) => 2,
}; };
Token::Math(TokenMath { Token::Math(MathToken {
formula: self.s.get(start .. end), formula: self.s.get(start .. end),
display, display,
terminated, terminated,
@ -309,7 +309,7 @@ impl<'s> Tokens<'s> {
'u' if self.s.peek_nth(1) == Some('{') => { 'u' if self.s.peek_nth(1) == Some('{') => {
self.s.eat_assert('u'); self.s.eat_assert('u');
self.s.eat_assert('{'); self.s.eat_assert('{');
Token::UnicodeEscape(TokenUnicodeEscape { Token::UnicodeEscape(UnicodeEscapeToken {
// Allow more than `ascii_hexdigit` for better error recovery. // Allow more than `ascii_hexdigit` for better error recovery.
sequence: self.s.eat_while(|c| c.is_ascii_alphanumeric()), sequence: self.s.eat_while(|c| c.is_ascii_alphanumeric()),
terminated: self.s.eat_if('}'), terminated: self.s.eat_if('}'),
@ -391,7 +391,7 @@ impl<'s> Tokens<'s> {
fn string(&mut self) -> Token<'s> { fn string(&mut self) -> Token<'s> {
let mut escaped = false; let mut escaped = false;
Token::Str(TokenStr { Token::Str(StrToken {
string: self.s.eat_until(|c| { string: self.s.eat_until(|c| {
if c == '"' && !escaped { if c == '"' && !escaped {
true true
@ -470,19 +470,19 @@ mod tests {
use TokenMode::{Code, Markup}; use TokenMode::{Code, Markup};
const fn Raw(text: &str, backticks: usize, terminated: bool) -> Token { 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 { 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 { 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 { 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> { const fn Color(r: u8, g: u8, b: u8, a: u8) -> Token<'static> {

View File

@ -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};

View File

@ -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>) { fn pretty_with_map(&self, p: &mut Printer, map: Option<&ExprMap>) {
for _ in 0 ..= self.level { for _ in 0 ..= self.level {
p.push('='); p.push('=');
@ -147,7 +147,7 @@ impl PrettyWithMap for NodeHeading {
} }
} }
impl Pretty for NodeRaw { impl Pretty for RawNode {
fn pretty(&self, p: &mut Printer) { fn pretty(&self, p: &mut Printer) {
// Find out how many backticks we need. // Find out how many backticks we need.
let mut backticks = 1; 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) { fn pretty(&self, p: &mut Printer) {
p.push('('); p.push('(');
p.join(&self.items, ", ", |item, p| item.pretty(p)); 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) { fn pretty(&self, p: &mut Printer) {
p.push('('); p.push('(');
if self.items.is_empty() { 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) { fn pretty(&self, p: &mut Printer) {
p.push('['); p.push('[');
self.tree.pretty_with_map(p, None); 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) { fn pretty(&self, p: &mut Printer) {
p.push('('); p.push('(');
self.expr.pretty(p); 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) { fn pretty(&self, p: &mut Printer) {
p.push('{'); p.push('{');
if self.exprs.len() > 1 { 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) { fn pretty(&self, p: &mut Printer) {
self.op.pretty(p); self.op.pretty(p);
if self.op == UnOp::Not { 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) { fn pretty(&self, p: &mut Printer) {
self.lhs.pretty(p); self.lhs.pretty(p);
p.push(' '); p.push(' ');
@ -343,11 +343,11 @@ impl Pretty for BinOp {
} }
} }
impl Pretty for ExprCall { impl Pretty for CallExpr {
fn pretty(&self, p: &mut Printer) { fn pretty(&self, p: &mut Printer) {
self.callee.pretty(p); self.callee.pretty(p);
let mut write_args = |items: &[ExprArg]| { let mut write_args = |items: &[CallArg]| {
p.push('('); p.push('(');
p.join(items, ", ", |item, p| item.pretty(p)); p.join(items, ", ", |item, p| item.pretty(p));
p.push(')'); p.push(')');
@ -357,7 +357,7 @@ impl Pretty for ExprCall {
// This can be moved behind the arguments. // This can be moved behind the arguments.
// //
// Example: Transforms "#v(a, [b])" => "#v(a)[b]". // Example: Transforms "#v(a, [b])" => "#v(a)[b]".
[head @ .., ExprArg::Pos(Expr::Template(template))] => { [head @ .., CallArg::Pos(Expr::Template(template))] => {
if !head.is_empty() { if !head.is_empty() {
write_args(head); 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) { fn pretty(&self, p: &mut Printer) {
p.join(&self.items, ", ", |item, p| item.pretty(p)); p.join(&self.items, ", ", |item, p| item.pretty(p));
} }
} }
impl Pretty for ExprArg { impl Pretty for CallArg {
fn pretty(&self, p: &mut Printer) { fn pretty(&self, p: &mut Printer) {
match self { match self {
Self::Pos(expr) => expr.pretty(p), 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) { fn pretty(&self, p: &mut Printer) {
p.push('('); p.push('(');
p.join(self.params.iter(), ", ", |item, p| item.pretty(p)); 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) { fn pretty(&self, p: &mut Printer) {
p.push_str("let "); p.push_str("let ");
self.binding.pretty(p); 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) { fn pretty(&self, p: &mut Printer) {
p.push_str("if "); p.push_str("if ");
self.condition.pretty(p); 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) { fn pretty(&self, p: &mut Printer) {
p.push_str("while "); p.push_str("while ");
self.condition.pretty(p); 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) { fn pretty(&self, p: &mut Printer) {
p.push_str("for "); p.push_str("for ");
self.pattern.pretty(p); self.pattern.pretty(p);
@ -475,14 +475,13 @@ impl Pretty for Value {
Value::Dict(v) => v.pretty(p), Value::Dict(v) => v.pretty(p),
Value::Template(v) => v.pretty(p), Value::Template(v) => v.pretty(p),
Value::Func(v) => v.pretty(p), Value::Func(v) => v.pretty(p),
Value::Args(v) => v.pretty(p),
Value::Any(v) => v.pretty(p), Value::Any(v) => v.pretty(p),
Value::Error => p.push_str("<error>"), Value::Error => p.push_str("<error>"),
} }
} }
} }
impl Pretty for ValueArray { impl Pretty for ArrayValue {
fn pretty(&self, p: &mut Printer) { fn pretty(&self, p: &mut Printer) {
p.push('('); p.push('(');
p.join(self, ", ", |item, p| item.pretty(p)); 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) { fn pretty(&self, p: &mut Printer) {
p.push('('); p.push('(');
if self.is_empty() { 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) { fn pretty(&self, p: &mut Printer) {
p.push('['); p.push('[');
for part in self { 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) { fn pretty(&self, p: &mut Printer) {
p.push_str("<function"); p.push_str("<function");
if let Some(name) = self.name() { if let Some(name) = self.name() {
@ -548,7 +547,7 @@ impl Pretty for ValueFunc {
} }
} }
impl Pretty for ValueArgs { impl Pretty for FuncArgs {
fn pretty(&self, p: &mut Printer) { fn pretty(&self, p: &mut Printer) {
p.push('('); p.push('(');
p.join(&self.items, ", ", |item, p| item.pretty(p)); p.join(&self.items, ", ", |item, p| item.pretty(p));
@ -556,7 +555,7 @@ impl Pretty for ValueArgs {
} }
} }
impl Pretty for ValueArg { impl Pretty for FuncArg {
fn pretty(&self, p: &mut Printer) { fn pretty(&self, p: &mut Printer) {
if let Some(name) = &self.name { if let Some(name) = &self.name {
p.push_str(&name.v); p.push_str(&name.v);
@ -613,7 +612,7 @@ pretty_display! {
Linear, Linear,
RgbaColor, RgbaColor,
Color, Color,
ValueAny, AnyValue,
} }
#[cfg(test)] #[cfg(test)]
@ -622,9 +621,7 @@ mod tests {
use std::rc::Rc; use std::rc::Rc;
use super::*; use super::*;
use crate::color::RgbaColor;
use crate::env::Env; use crate::env::Env;
use crate::eval::eval;
use crate::parse::parse; use crate::parse::parse;
#[track_caller] #[track_caller]
@ -796,34 +793,37 @@ mod tests {
); );
// Function. // Function.
test_value(ValueFunc::new(None, |_, _| Value::None), "<function>"); test_value(FuncValue::new(None, |_, _| Value::None), "<function>");
test_value( test_value(
ValueFunc::new(Some("nil".into()), |_, _| Value::None), FuncValue::new(Some("nil".into()), |_, _| Value::None),
"<function nil>", "<function nil>",
); );
// 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. // Any.
test_value(ValueAny::new(1), "1"); test_value(AnyValue::new(1), "1");
// Error. // Error.
test_value(Value::Error, "<error>"); test_value(Value::Error, "<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)",
);
}
} }

View File

@ -7,36 +7,36 @@ use crate::geom::{AngularUnit, LengthUnit};
/// An expression. /// An expression.
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub enum Expr { pub enum Expr {
/// A literal. /// A literal, like `11pt` or `"hi"`.
Lit(Lit), Lit(Lit),
/// An identifier: `left`. /// An identifier: `left`.
Ident(Ident), Ident(Ident),
/// An array expression: `(1, "hi", 12cm)`. /// An array expression: `(1, "hi", 12cm)`.
Array(ExprArray), Array(ArrayExpr),
/// A dictionary expression: `(color: #f79143, pattern: dashed)`. /// A dictionary expression: `(color: #f79143, pattern: dashed)`.
Dict(ExprDict), Dict(DictExpr),
/// A template expression: `[*Hi* there!]`. /// A template expression: `[*Hi* there!]`.
Template(ExprTemplate), Template(TemplateExpr),
/// A grouped expression: `(1 + 2)`. /// A grouped expression: `(1 + 2)`.
Group(ExprGroup), Group(GroupExpr),
/// A block expression: `{ let x = 1; x + 2 }`. /// A block expression: `{ let x = 1; x + 2 }`.
Block(ExprBlock), Block(BlockExpr),
/// A unary operation: `-x`. /// A unary operation: `-x`.
Unary(ExprUnary), Unary(UnaryExpr),
/// A binary operation: `a + b`. /// A binary operation: `a + b`.
Binary(ExprBinary), Binary(BinaryExpr),
/// An invocation of a function: `f(x, y)`. /// An invocation of a function: `f(x, y)`.
Call(ExprCall), Call(CallExpr),
/// A closure expression: `(x, y) => z`. /// A closure expression: `(x, y) => z`.
Closure(ExprClosure), Closure(ClosureExpr),
/// A let expression: `let x = 1`. /// A let expression: `let x = 1`.
Let(ExprLet), Let(LetExpr),
/// An if expression: `if x { y } else { z }`. /// An if-else expression: `if x { y } else { z }`.
If(ExprIf), If(IfExpr),
/// A while expression: `while x { y }`. /// A while loop expression: `while x { y }`.
While(ExprWhile), While(WhileExpr),
/// A for expression: `for x in y { z }`. /// A for loop expression: `for x in y { z }`.
For(ExprFor), For(ForExpr),
} }
impl Expr { impl Expr {
@ -74,7 +74,7 @@ impl Expr {
} }
} }
/// A literal. /// A literal, like `11pt` or `"hi"`.
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub struct Lit { pub struct Lit {
/// The source code location. /// The source code location.
@ -111,7 +111,7 @@ pub enum LitKind {
/// An array expression: `(1, "hi", 12cm)`. /// An array expression: `(1, "hi", 12cm)`.
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub struct ExprArray { pub struct ArrayExpr {
/// The source code location. /// The source code location.
pub span: Span, pub span: Span,
/// The entries of the array. /// The entries of the array.
@ -120,7 +120,7 @@ pub struct ExprArray {
/// A dictionary expression: `(color: #f79143, pattern: dashed)`. /// A dictionary expression: `(color: #f79143, pattern: dashed)`.
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub struct ExprDict { pub struct DictExpr {
/// The source code location. /// The source code location.
pub span: Span, pub span: Span,
/// The named dictionary entries. /// The named dictionary entries.
@ -145,7 +145,7 @@ impl Named {
/// A template expression: `[*Hi* there!]`. /// A template expression: `[*Hi* there!]`.
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub struct ExprTemplate { pub struct TemplateExpr {
/// The source code location. /// The source code location.
pub span: Span, pub span: Span,
/// The contents of the template. /// The contents of the template.
@ -154,7 +154,7 @@ pub struct ExprTemplate {
/// A grouped expression: `(1 + 2)`. /// A grouped expression: `(1 + 2)`.
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub struct ExprGroup { pub struct GroupExpr {
/// The source code location. /// The source code location.
pub span: Span, pub span: Span,
/// The wrapped expression. /// The wrapped expression.
@ -163,7 +163,7 @@ pub struct ExprGroup {
/// A block expression: `{ let x = 1; x + 2 }`. /// A block expression: `{ let x = 1; x + 2 }`.
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub struct ExprBlock { pub struct BlockExpr {
/// The source code location. /// The source code location.
pub span: Span, pub span: Span,
/// The list of expressions contained in the block. /// The list of expressions contained in the block.
@ -174,7 +174,7 @@ pub struct ExprBlock {
/// A unary operation: `-x`. /// A unary operation: `-x`.
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub struct ExprUnary { pub struct UnaryExpr {
/// The source code location. /// The source code location.
pub span: Span, pub span: Span,
/// The operator: `-`. /// The operator: `-`.
@ -225,7 +225,7 @@ impl UnOp {
/// A binary operation: `a + b`. /// A binary operation: `a + b`.
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub struct ExprBinary { pub struct BinaryExpr {
/// The source code location. /// The source code location.
pub span: Span, pub span: Span,
/// The left-hand side of the operation: `a`. /// The left-hand side of the operation: `a`.
@ -374,13 +374,13 @@ pub enum Associativity {
/// An invocation of a function: `foo(...)`. /// An invocation of a function: `foo(...)`.
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub struct ExprCall { pub struct CallExpr {
/// The source code location. /// The source code location.
pub span: Span, pub span: Span,
/// The callee of the function. /// The callee of the function.
pub callee: Box<Expr>, pub callee: Box<Expr>,
/// The arguments to the function. /// The arguments to the function.
pub args: ExprArgs, pub args: CallArgs,
} }
/// The arguments to a function: `12, draw: false`. /// 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_ /// In case of a bracketed invocation with a body, the body is _not_
/// included in the span for the sake of clearer error messages. /// included in the span for the sake of clearer error messages.
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub struct ExprArgs { pub struct CallArgs {
/// The source code location. /// The source code location.
pub span: Span, pub span: Span,
/// The positional and named arguments. /// The positional and named arguments.
pub items: Vec<ExprArg>, pub items: Vec<CallArg>,
} }
/// An argument to a function call: `12` or `draw: false`. /// An argument to a function call: `12` or `draw: false`.
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub enum ExprArg { pub enum CallArg {
/// A positional argument. /// A positional argument.
Pos(Expr), Pos(Expr),
/// A named argument. /// A named argument.
Named(Named), Named(Named),
} }
impl ExprArg { impl CallArg {
/// The source code location. /// The source code location.
pub fn span(&self) -> Span { pub fn span(&self) -> Span {
match self { match self {
@ -416,7 +416,7 @@ impl ExprArg {
/// A closure expression: `(x, y) => z`. /// A closure expression: `(x, y) => z`.
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub struct ExprClosure { pub struct ClosureExpr {
/// The source code location. /// The source code location.
pub span: Span, pub span: Span,
/// The name of the closure. /// The name of the closure.
@ -431,7 +431,7 @@ pub struct ExprClosure {
/// A let expression: `let x = 1`. /// A let expression: `let x = 1`.
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub struct ExprLet { pub struct LetExpr {
/// The source code location. /// The source code location.
pub span: Span, pub span: Span,
/// The binding to assign to. /// The binding to assign to.
@ -440,9 +440,9 @@ pub struct ExprLet {
pub init: Option<Box<Expr>>, pub init: Option<Box<Expr>>,
} }
/// An if expression: `if x { y } else { z }`. /// An if-else expression: `if x { y } else { z }`.
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub struct ExprIf { pub struct IfExpr {
/// The source code location. /// The source code location.
pub span: Span, pub span: Span,
/// The condition which selects the body to evaluate. /// The condition which selects the body to evaluate.
@ -453,9 +453,9 @@ pub struct ExprIf {
pub else_body: Option<Box<Expr>>, pub else_body: Option<Box<Expr>>,
} }
/// A while expression: `while x { y }`. /// A while loop expression: `while x { y }`.
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub struct ExprWhile { pub struct WhileExpr {
/// The source code location. /// The source code location.
pub span: Span, pub span: Span,
/// The condition which selects whether to evaluate the body. /// The condition which selects whether to evaluate the body.
@ -464,9 +464,9 @@ pub struct ExprWhile {
pub body: Box<Expr>, pub body: Box<Expr>,
} }
/// A for expression: `for x in y { z }`. /// A for loop expression: `for x in y { z }`.
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub struct ExprFor { pub struct ForExpr {
/// The source code location. /// The source code location.
pub span: Span, pub span: Span,
/// The pattern to assign to. /// The pattern to assign to.

View File

@ -4,7 +4,7 @@ use unicode_xid::UnicodeXID;
use super::Span; 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: /// In addition to what is specified in the [Unicode Standard][uax31], we allow:
/// - `_` as a starting character, /// - `_` as a starting character,

View File

@ -14,4 +14,6 @@ pub use span::*;
pub use token::*; pub use token::*;
/// The abstract syntax tree. /// The abstract syntax tree.
///
/// This type can represent a full parsed document.
pub type Tree = Vec<Node>; pub type Tree = Vec<Node>;

View File

@ -16,23 +16,23 @@ pub enum Node {
/// Plain text. /// Plain text.
Text(String), Text(String),
/// A section heading. /// A section heading.
Heading(NodeHeading), Heading(HeadingNode),
/// An optionally syntax-highlighted raw block. /// An optionally syntax-highlighted raw block.
Raw(NodeRaw), Raw(RawNode),
/// An expression. /// An expression.
Expr(Expr), Expr(Expr),
} }
/// A section heading: `= Introduction`. /// A section heading: `= Introduction`.
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub struct NodeHeading { pub struct HeadingNode {
/// The section depth (numer of equals signs minus 1). /// The section depth (numer of equals signs minus 1).
pub level: usize, pub level: usize,
/// The contents of the heading. /// The contents of the heading.
pub contents: Tree, 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 /// Raw blocks start with 1 or 3+ backticks and end with the same number of
/// backticks. /// backticks.
@ -96,7 +96,7 @@ pub struct NodeHeading {
/// Note that with these rules you can always force leading or trailing /// Note that with these rules you can always force leading or trailing
/// whitespace simply by adding more spaces. /// whitespace simply by adding more spaces.
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub struct NodeRaw { pub struct RawNode {
/// An optional identifier specifying the language to syntax-highlight in. /// An optional identifier specifying the language to syntax-highlight in.
pub lang: Option<Ident>, pub lang: Option<Ident>,
/// The lines of raw text, determined as the raw string between the /// The lines of raw text, determined as the raw string between the

View File

@ -32,7 +32,7 @@ impl<T> Spanned<T> {
Spanned { v: &self.v, span: self.span } 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<F, U>(self, f: F) -> Spanned<U> pub fn map<F, U>(self, f: F) -> Spanned<U>
where where
F: FnOnce(T) -> U, F: FnOnce(T) -> U,

View File

@ -99,13 +99,13 @@ pub enum Token<'s> {
Text(&'s str), Text(&'s str),
/// An arbitrary number of backticks followed by inner contents, terminated /// An arbitrary number of backticks followed by inner contents, terminated
/// with the same number of backticks: `` `...` ``. /// with the same number of backticks: `` `...` ``.
Raw(TokenRaw<'s>), Raw(RawToken<'s>),
/// One or two dollar signs followed by inner contents, terminated with the /// One or two dollar signs followed by inner contents, terminated with the
/// same number of dollar signs. /// same number of dollar signs.
Math(TokenMath<'s>), Math(MathToken<'s>),
/// A slash and the letter "u" followed by a hexadecimal unicode entity /// A slash and the letter "u" followed by a hexadecimal unicode entity
/// enclosed in curly braces: `\u{1F5FA}`. /// enclosed in curly braces: `\u{1F5FA}`.
UnicodeEscape(TokenUnicodeEscape<'s>), UnicodeEscape(UnicodeEscapeToken<'s>),
/// An identifier: `center`. /// An identifier: `center`.
Ident(&'s str), Ident(&'s str),
/// A boolean: `true`, `false`. /// A boolean: `true`, `false`.
@ -126,7 +126,7 @@ pub enum Token<'s> {
/// A color value: `#20d82a`. /// A color value: `#20d82a`.
Color(RgbaColor), Color(RgbaColor),
/// A quoted string: `"..."`. /// A quoted string: `"..."`.
Str(TokenStr<'s>), Str(StrToken<'s>),
/// Two slashes followed by inner contents, terminated with a newline: /// Two slashes followed by inner contents, terminated with a newline:
/// `//<str>\n`. /// `//<str>\n`.
LineComment(&'s str), LineComment(&'s str),
@ -139,9 +139,9 @@ pub enum Token<'s> {
Invalid(&'s str), Invalid(&'s str),
} }
/// A quoted string: `"..."`. /// A quoted string token: `"..."`.
#[derive(Debug, Copy, Clone, PartialEq)] #[derive(Debug, Copy, Clone, PartialEq)]
pub struct TokenStr<'s> { pub struct StrToken<'s> {
/// The string inside the quotes. /// The string inside the quotes.
/// ///
/// _Note_: If the string contains escape sequences these are not yet /// _Note_: If the string contains escape sequences these are not yet
@ -152,9 +152,9 @@ pub struct TokenStr<'s> {
pub terminated: bool, pub terminated: bool,
} }
/// A raw block: `` `...` ``. /// A raw block token: `` `...` ``.
#[derive(Debug, Copy, Clone, PartialEq)] #[derive(Debug, Copy, Clone, PartialEq)]
pub struct TokenRaw<'s> { pub struct RawToken<'s> {
/// The raw text between the backticks. /// The raw text between the backticks.
pub text: &'s str, pub text: &'s str,
/// The number of opening backticks. /// The number of opening backticks.
@ -163,9 +163,9 @@ pub struct TokenRaw<'s> {
pub terminated: bool, 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)] #[derive(Debug, Copy, Clone, PartialEq)]
pub struct TokenMath<'s> { pub struct MathToken<'s> {
/// The formula between the dollars. /// The formula between the dollars.
pub formula: &'s str, pub formula: &'s str,
/// Whether the formula is display-level, that is, it is surrounded by /// Whether the formula is display-level, that is, it is surrounded by
@ -175,9 +175,9 @@ pub struct TokenMath<'s> {
pub terminated: bool, pub terminated: bool,
} }
/// A unicode escape sequence: `\u{1F5FA}`. /// A unicode escape sequence token: `\u{1F5FA}`.
#[derive(Debug, Copy, Clone, PartialEq)] #[derive(Debug, Copy, Clone, PartialEq)]
pub struct TokenUnicodeEscape<'s> { pub struct UnicodeEscapeToken<'s> {
/// The escape sequence between the braces. /// The escape sequence between the braces.
pub sequence: &'s str, pub sequence: &'s str,
/// Whether the closing brace was present. /// Whether the closing brace was present.

View File

@ -82,29 +82,29 @@ visit! {
} }
} }
fn visit_array(v, node: &ExprArray) { fn visit_array(v, node: &ArrayExpr) {
for expr in &node.items { for expr in &node.items {
v.visit_expr(&expr); v.visit_expr(&expr);
} }
} }
fn visit_dict(v, node: &ExprDict) { fn visit_dict(v, node: &DictExpr) {
for named in &node.items { for named in &node.items {
v.visit_expr(&named.expr); v.visit_expr(&named.expr);
} }
} }
fn visit_template(v, node: &ExprTemplate) { fn visit_template(v, node: &TemplateExpr) {
v.visit_enter(); v.visit_enter();
v.visit_tree(&node.tree); v.visit_tree(&node.tree);
v.visit_exit(); v.visit_exit();
} }
fn visit_group(v, node: &ExprGroup) { fn visit_group(v, node: &GroupExpr) {
v.visit_expr(&node.expr); v.visit_expr(&node.expr);
} }
fn visit_block(v, node: &ExprBlock) { fn visit_block(v, node: &BlockExpr) {
if node.scoping { if node.scoping {
v.visit_enter(); 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.lhs);
v.visit_expr(&node.rhs); v.visit_expr(&node.rhs);
} }
fn visit_unary(v, node: &ExprUnary) { fn visit_unary(v, node: &UnaryExpr) {
v.visit_expr(&node.expr); v.visit_expr(&node.expr);
} }
fn visit_call(v, node: &ExprCall) { fn visit_call(v, node: &CallExpr) {
v.visit_expr(&node.callee); v.visit_expr(&node.callee);
v.visit_args(&node.args); v.visit_args(&node.args);
} }
fn visit_closure(v, node: &ExprClosure) { fn visit_closure(v, node: &ClosureExpr) {
for param in node.params.iter() { for param in node.params.iter() {
v.visit_binding(param); v.visit_binding(param);
} }
v.visit_expr(&node.body); v.visit_expr(&node.body);
} }
fn visit_args(v, node: &ExprArgs) { fn visit_args(v, node: &CallArgs) {
for arg in &node.items { for arg in &node.items {
v.visit_arg(arg); v.visit_arg(arg);
} }
} }
fn visit_arg(v, node: &ExprArg) { fn visit_arg(v, node: &CallArg) {
match node { match node {
ExprArg::Pos(expr) => v.visit_expr(&expr), CallArg::Pos(expr) => v.visit_expr(&expr),
ExprArg::Named(named) => v.visit_expr(&named.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); v.visit_binding(&node.binding);
if let Some(init) = &node.init { if let Some(init) = &node.init {
v.visit_expr(&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.condition);
v.visit_expr(&node.if_body); v.visit_expr(&node.if_body);
if let Some(body) = &node.else_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.condition);
v.visit_expr(&node.body); v.visit_expr(&node.body);
} }
fn visit_for(v, node: &ExprFor) { fn visit_for(v, node: &ForExpr) {
match &node.pattern { match &node.pattern {
ForPattern::Value(value) => v.visit_binding(value), ForPattern::Value(value) => v.visit_binding(value),
ForPattern::KeyValue(key, value) => { ForPattern::KeyValue(key, value) => {

View File

@ -3,29 +3,35 @@
## Directory structure ## Directory structure
Top level directory structure: Top level directory structure:
- `typ`: Input files. - `typ`: Input files.
- `res`: Resource files used by tests.
- `ref`: Reference images which the output is compared with to determine whether - `ref`: Reference images which the output is compared with to determine whether
a test passed or failed. a test passed or failed.
- `png`: PNG files produced by tests. - `png`: PNG files produced by tests.
- `pdf`: PDF files produced by tests. - `pdf`: PDF files produced by tests.
- `res`: Resource files used by tests.
## Running the tests ## Running the tests
Running the integration tests (the tests in this directory).
```bash ```bash
# Run all tests
cargo test
# Run unit tests
cargo test --lib
# Run integration tests (the tests in this directory)
cargo test --test typeset 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 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 ## 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 ```bash
# One image # One image
oxipng -o max path/to/image.png oxipng -o max path/to/image.png

View File

@ -15,16 +15,15 @@ use ttf_parser::OutlineBuilder;
use walkdir::WalkDir; use walkdir::WalkDir;
use typst::diag::{Diag, DiagSet, Level, Pass}; use typst::diag::{Diag, DiagSet, Level, Pass};
use typst::env::{Env, ImageResource, ResourceLoader}; use typst::env::{Env, FsIndexExt, ImageResource, ResourceLoader};
use typst::eval::{EvalContext, Scope, Value, ValueArgs, ValueFunc}; use typst::eval::{EvalContext, FuncArgs, FuncValue, Scope, Value};
use typst::exec::State; use typst::exec::State;
use typst::export::pdf; use typst::export::pdf;
use typst::font::FsIndexExt;
use typst::geom::{Length, Point, Sides, Size}; 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::library;
use typst::parse::{LineMap, Scanner}; use typst::parse::{LineMap, Scanner};
use typst::shaping::Shaped; use typst::pretty::pretty;
use typst::syntax::{Location, Pos}; use typst::syntax::{Location, Pos};
use typst::typeset; use typst::typeset;
@ -313,13 +312,18 @@ struct Panic {
} }
fn register_helpers(scope: &mut Scope, panics: Rc<RefCell<Vec<Panic>>>) { fn register_helpers(scope: &mut Scope, panics: Rc<RefCell<Vec<Panic>>>) {
pub fn args(_: &mut EvalContext, args: &mut ValueArgs) -> Value { pub fn args(_: &mut EvalContext, args: &mut FuncArgs) -> Value {
let value = args.clone().into(); let repr = pretty(args);
args.items.clear(); 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::<Value>(ctx, "left-hand side"); let lhs = args.require::<Value>(ctx, "left-hand side");
let rhs = args.require::<Value>(ctx, "right-hand side"); let rhs = args.require::<Value>(ctx, "right-hand side");
if lhs != rhs { if lhs != rhs {
@ -331,8 +335,8 @@ fn register_helpers(scope: &mut Scope, panics: Rc<RefCell<Vec<Panic>>>) {
}; };
scope.def_const("error", Value::Error); scope.def_const("error", Value::Error);
scope.def_const("args", ValueFunc::new(Some("args".into()), args)); scope.def_const("args", FuncValue::new(Some("args".into()), args));
scope.def_const("test", ValueFunc::new(Some("test".into()), test)); scope.def_const("test", FuncValue::new(Some("test".into()), test));
} }
fn print_diag(diag: &Diag, map: &LineMap, lines: u32) { fn print_diag(diag: &Diag, map: &LineMap, lines: u32) {