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 fontdock::fs::FsIndex;
use typst::env::{Env, ResourceLoader};
use typst::env::{Env, FsIndexExt, ResourceLoader};
use typst::eval::eval;
use typst::exec::{exec, State};
use typst::export::pdf;
use typst::font::FsIndexExt;
use typst::layout::layout;
use typst::library;
use typst::parse::parse;

View File

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

View File

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

View File

@ -13,9 +13,9 @@ pub use value::*;
use std::collections::HashMap;
use std::rc::Rc;
use super::*;
use crate::color::Color;
use crate::diag::{Diag, DiagSet};
use crate::diag::{Diag, DiagSet, Pass};
use crate::env::Env;
use crate::geom::{Angle, Length, Relative};
use crate::syntax::visit::Visit;
use crate::syntax::*;
@ -143,16 +143,16 @@ impl Eval for Lit {
}
}
impl Eval for ExprArray {
type Output = ValueArray;
impl Eval for ArrayExpr {
type Output = ArrayValue;
fn eval(&self, ctx: &mut EvalContext) -> Self::Output {
self.items.iter().map(|expr| expr.eval(ctx)).collect()
}
}
impl Eval for ExprDict {
type Output = ValueDict;
impl Eval for DictExpr {
type Output = DictValue;
fn eval(&self, ctx: &mut EvalContext) -> Self::Output {
self.items
@ -162,7 +162,7 @@ impl Eval for ExprDict {
}
}
impl Eval for ExprTemplate {
impl Eval for TemplateExpr {
type Output = TemplateNode;
fn eval(&self, ctx: &mut EvalContext) -> Self::Output {
@ -172,7 +172,7 @@ impl Eval for ExprTemplate {
}
}
impl Eval for ExprGroup {
impl Eval for GroupExpr {
type Output = Value;
fn eval(&self, ctx: &mut EvalContext) -> Self::Output {
@ -180,7 +180,7 @@ impl Eval for ExprGroup {
}
}
impl Eval for ExprBlock {
impl Eval for BlockExpr {
type Output = Value;
fn eval(&self, ctx: &mut EvalContext) -> Self::Output {
@ -201,7 +201,7 @@ impl Eval for ExprBlock {
}
}
impl Eval for ExprUnary {
impl Eval for UnaryExpr {
type Output = Value;
fn eval(&self, ctx: &mut EvalContext) -> Self::Output {
@ -230,7 +230,7 @@ impl Eval for ExprUnary {
}
}
impl Eval for ExprBinary {
impl Eval for BinaryExpr {
type Output = Value;
fn eval(&self, ctx: &mut EvalContext) -> Self::Output {
@ -256,7 +256,7 @@ impl Eval for ExprBinary {
}
}
impl ExprBinary {
impl BinaryExpr {
/// Apply a basic binary operation.
fn apply<F>(&self, ctx: &mut EvalContext, op: F) -> Value
where
@ -335,7 +335,7 @@ impl ExprBinary {
}
}
impl Eval for ExprCall {
impl Eval for CallExpr {
type Output = Value;
fn eval(&self, ctx: &mut EvalContext) -> Self::Output {
@ -361,25 +361,25 @@ impl Eval for ExprCall {
}
}
impl Eval for ExprArgs {
type Output = ValueArgs;
impl Eval for CallArgs {
type Output = FuncArgs;
fn eval(&self, ctx: &mut EvalContext) -> Self::Output {
let items = self.items.iter().map(|arg| arg.eval(ctx)).collect();
ValueArgs { span: self.span, items }
FuncArgs { span: self.span, items }
}
}
impl Eval for ExprArg {
type Output = ValueArg;
impl Eval for CallArg {
type Output = FuncArg;
fn eval(&self, ctx: &mut EvalContext) -> Self::Output {
match self {
Self::Pos(expr) => ValueArg {
Self::Pos(expr) => FuncArg {
name: None,
value: Spanned::new(expr.eval(ctx), expr.span()),
},
Self::Named(Named { name, expr }) => ValueArg {
Self::Named(Named { name, expr }) => FuncArg {
name: Some(Spanned::new(name.string.clone(), name.span)),
value: Spanned::new(expr.eval(ctx), expr.span()),
},
@ -387,7 +387,7 @@ impl Eval for ExprArg {
}
}
impl Eval for ExprClosure {
impl Eval for ClosureExpr {
type Output = Value;
fn eval(&self, ctx: &mut EvalContext) -> Self::Output {
@ -402,7 +402,7 @@ impl Eval for ExprClosure {
};
let name = self.name.as_ref().map(|id| id.to_string());
Value::Func(ValueFunc::new(name, move |ctx, args| {
Value::Func(FuncValue::new(name, move |ctx, args| {
// Don't leak the scopes from the call site. Instead, we use the
// scope of captured variables we collected earlier.
let prev = std::mem::take(&mut ctx.scopes);
@ -422,7 +422,7 @@ impl Eval for ExprClosure {
}
}
impl Eval for ExprLet {
impl Eval for LetExpr {
type Output = Value;
fn eval(&self, ctx: &mut EvalContext) -> Self::Output {
@ -435,7 +435,7 @@ impl Eval for ExprLet {
}
}
impl Eval for ExprIf {
impl Eval for IfExpr {
type Output = Value;
fn eval(&self, ctx: &mut EvalContext) -> Self::Output {
@ -461,7 +461,7 @@ impl Eval for ExprIf {
}
}
impl Eval for ExprWhile {
impl Eval for WhileExpr {
type Output = Value;
fn eval(&self, ctx: &mut EvalContext) -> Self::Output {
@ -493,7 +493,7 @@ impl Eval for ExprWhile {
}
}
impl Eval for ExprFor {
impl Eval for ForExpr {
type Output = Value;
fn eval(&self, ctx: &mut EvalContext) -> Self::Output {

View File

@ -1,4 +1,5 @@
use super::*;
use super::{ArrayValue, DictValue, TemplateNode, Value};
use crate::syntax::Span;
use Value::*;
/// Apply the plus operator to a value.
@ -189,12 +190,12 @@ fn value_eq(lhs: &Value, rhs: &Value) -> bool {
}
/// Compute whether two arrays are equal.
fn array_eq(a: &ValueArray, b: &ValueArray) -> bool {
fn array_eq(a: &ArrayValue, b: &ArrayValue) -> bool {
a.len() == b.len() && a.iter().zip(b).all(|(x, y)| value_eq(x, y))
}
/// Compute whether two dictionaries are equal.
fn dict_eq(a: &ValueDict, b: &ValueDict) -> bool {
fn dict_eq(a: &DictValue, b: &DictValue) -> bool {
a.len() == b.len()
&& a.iter().all(|(k, x)| b.get(k).map_or(false, |y| value_eq(x, y)))
}

View File

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

View File

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

View File

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

View File

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

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::*;
/// An angle.

View File

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

View File

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

View File

@ -1,9 +1,8 @@
use super::*;
use crate::geom::Linear;
/// A node that can fix its child's width and height.
#[derive(Debug, Clone, PartialEq)]
pub struct NodeFixed {
pub struct FixedNode {
/// The fixed width, if any.
pub width: Option<Linear>,
/// The fixed height, if any.
@ -12,8 +11,8 @@ pub struct NodeFixed {
pub child: Node,
}
impl Layout for NodeFixed {
fn layout(&self, ctx: &mut LayoutContext, areas: &Areas) -> Layouted {
impl Layout for FixedNode {
fn layout(&self, ctx: &mut LayoutContext, areas: &Areas) -> Fragment {
let Areas { current, full, .. } = areas;
let size = Size::new(
self.width.map(|w| w.resolve(full.width)).unwrap_or(current.width),
@ -31,8 +30,8 @@ impl Layout for NodeFixed {
}
}
impl From<NodeFixed> for NodeAny {
fn from(fixed: NodeFixed) -> Self {
impl From<FixedNode> for AnyNode {
fn from(fixed: FixedNode) -> Self {
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 fixed;
mod frame;
mod node;
mod pad;
mod par;
mod shaping;
mod spacing;
mod stack;
mod text;
use crate::color::Color;
use crate::env::{Env, ResourceId};
use crate::geom::*;
use crate::shaping::Shaped;
pub use background::*;
pub use fixed::*;
pub use frame::*;
pub use node::*;
pub use pad::*;
pub use par::*;
pub use shaping::*;
pub use spacing::*;
pub use stack::*;
pub use text::*;
use crate::env::Env;
use crate::geom::*;
/// Layout a tree into a collection of frames.
pub fn layout(env: &mut Env, tree: &Tree) -> Vec<Frame> {
tree.layout(&mut LayoutContext { env })
@ -32,7 +34,7 @@ pub fn layout(env: &mut Env, tree: &Tree) -> Vec<Frame> {
#[derive(Debug, Clone, PartialEq)]
pub struct Tree {
/// Runs of pages with the same properties.
pub runs: Vec<NodePages>,
pub runs: Vec<PageRun>,
}
impl Tree {
@ -44,15 +46,15 @@ impl Tree {
/// A run of pages that all have the same properties.
#[derive(Debug, Clone, PartialEq)]
pub struct NodePages {
pub struct PageRun {
/// The size of each page.
pub size: Size,
/// The layout node that produces the actual pages (typically a
/// [`NodeStack`]).
/// [`StackNode`]).
pub child: Node,
}
impl NodePages {
impl PageRun {
/// Layout the page run.
pub fn layout(&self, ctx: &mut LayoutContext) -> Vec<Frame> {
let areas = Areas::repeat(self.size, Spec::uniform(Expand::Fill));
@ -64,7 +66,7 @@ impl NodePages {
/// Layout a node.
pub trait Layout {
/// Layout the node into the given areas.
fn layout(&self, ctx: &mut LayoutContext, areas: &Areas) -> Layouted;
fn layout(&self, ctx: &mut LayoutContext, areas: &Areas) -> Fragment;
}
/// The context for layouting.
@ -74,7 +76,7 @@ pub struct LayoutContext<'a> {
pub env: &'a mut Env,
}
/// A collection of areas to layout into.
/// A sequence of areas to layout into.
#[derive(Debug, Clone, PartialEq)]
pub struct Areas {
/// The remaining size of the current area.
@ -155,7 +157,7 @@ impl Expand {
/// The result of layouting a node.
#[derive(Debug, Clone, PartialEq)]
pub enum Layouted {
pub enum Fragment {
/// Spacing that should be added to the parent.
Spacing(Length),
/// A layout that should be added to and aligned in the parent.
@ -164,7 +166,7 @@ pub enum Layouted {
Frames(Vec<Frame>, LayoutAligns),
}
impl Layouted {
impl Fragment {
/// Return a reference to all frames contained in this variant (zero, one or
/// arbitrarily many).
pub fn frames(&self) -> &[Frame] {
@ -193,81 +195,3 @@ impl Layouted {
}
}
}
/// A finished layout with elements at fixed positions.
#[derive(Debug, Clone, PartialEq)]
pub struct Frame {
/// The size of the frame.
pub size: Size,
/// The elements composing this layout.
pub elements: Vec<(Point, Element)>,
}
impl Frame {
/// Create a new, empty frame.
pub fn new(size: Size) -> Self {
Self { size, elements: vec![] }
}
/// Add an element at a position.
pub fn push(&mut self, pos: Point, element: Element) {
self.elements.push((pos, element));
}
/// Add all elements of another frame, placing them relative to the given
/// position.
pub fn push_frame(&mut self, pos: Point, subframe: Self) {
for (subpos, element) in subframe.elements {
self.push(pos + subpos, element);
}
}
}
/// The building block frames are composed of.
#[derive(Debug, Clone, PartialEq)]
pub enum Element {
/// Shaped text.
Text(Shaped),
/// An image.
Image(Image),
/// Some shape that could hold another frame.
Geometry(Geometry),
}
/// A shape with some kind of fill.
#[derive(Debug, Clone, PartialEq)]
pub struct Geometry {
/// The shape to draw.
pub shape: Shape,
/// How the shape looks on the inside.
//
// TODO: This could be made into a Vec<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)]
pub enum Node {
/// A text node.
Text(NodeText),
Text(TextNode),
/// A spacing node.
Spacing(NodeSpacing),
Spacing(SpacingNode),
/// A dynamic node that can implement custom layouting behaviour.
Any(NodeAny),
Any(AnyNode),
}
impl Layout for Node {
fn layout(&self, ctx: &mut LayoutContext, areas: &Areas) -> Layouted {
fn layout(&self, ctx: &mut LayoutContext, areas: &Areas) -> Fragment {
match self {
Self::Spacing(spacing) => spacing.layout(ctx, areas),
Self::Text(text) => text.layout(ctx, areas),
@ -35,9 +35,9 @@ impl Debug for Node {
}
/// A wrapper around a dynamic layouting node.
pub struct NodeAny(Box<dyn Bounds>);
pub struct AnyNode(Box<dyn Bounds>);
impl NodeAny {
impl AnyNode {
/// Create a new instance from any node that satisifies the required bounds.
pub fn new<T>(any: T) -> Self
where
@ -47,25 +47,25 @@ impl NodeAny {
}
}
impl Layout for NodeAny {
fn layout(&self, ctx: &mut LayoutContext, areas: &Areas) -> Layouted {
impl Layout for AnyNode {
fn layout(&self, ctx: &mut LayoutContext, areas: &Areas) -> Fragment {
self.0.layout(ctx, areas)
}
}
impl Clone for NodeAny {
impl Clone for AnyNode {
fn clone(&self) -> Self {
Self(self.0.dyn_clone())
}
}
impl PartialEq for NodeAny {
impl PartialEq for AnyNode {
fn eq(&self, other: &Self) -> bool {
self.0.dyn_eq(other.0.as_ref())
}
}
impl Debug for NodeAny {
impl Debug for AnyNode {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
self.0.fmt(f)
}
@ -73,7 +73,7 @@ impl Debug for NodeAny {
impl<T> From<T> for Node
where
T: Into<NodeAny>,
T: Into<AnyNode>,
{
fn from(t: T) -> Self {
Self::Any(t.into())

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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>) {
for _ in 0 ..= self.level {
p.push('=');
@ -147,7 +147,7 @@ impl PrettyWithMap for NodeHeading {
}
}
impl Pretty for NodeRaw {
impl Pretty for RawNode {
fn pretty(&self, p: &mut Printer) {
// Find out how many backticks we need.
let mut backticks = 1;
@ -250,7 +250,7 @@ impl Pretty for LitKind {
}
}
impl Pretty for ExprArray {
impl Pretty for ArrayExpr {
fn pretty(&self, p: &mut Printer) {
p.push('(');
p.join(&self.items, ", ", |item, p| item.pretty(p));
@ -261,7 +261,7 @@ impl Pretty for ExprArray {
}
}
impl Pretty for ExprDict {
impl Pretty for DictExpr {
fn pretty(&self, p: &mut Printer) {
p.push('(');
if self.items.is_empty() {
@ -281,7 +281,7 @@ impl Pretty for Named {
}
}
impl Pretty for ExprTemplate {
impl Pretty for TemplateExpr {
fn pretty(&self, p: &mut Printer) {
p.push('[');
self.tree.pretty_with_map(p, None);
@ -289,7 +289,7 @@ impl Pretty for ExprTemplate {
}
}
impl Pretty for ExprGroup {
impl Pretty for GroupExpr {
fn pretty(&self, p: &mut Printer) {
p.push('(');
self.expr.pretty(p);
@ -297,7 +297,7 @@ impl Pretty for ExprGroup {
}
}
impl Pretty for ExprBlock {
impl Pretty for BlockExpr {
fn pretty(&self, p: &mut Printer) {
p.push('{');
if self.exprs.len() > 1 {
@ -311,7 +311,7 @@ impl Pretty for ExprBlock {
}
}
impl Pretty for ExprUnary {
impl Pretty for UnaryExpr {
fn pretty(&self, p: &mut Printer) {
self.op.pretty(p);
if self.op == UnOp::Not {
@ -327,7 +327,7 @@ impl Pretty for UnOp {
}
}
impl Pretty for ExprBinary {
impl Pretty for BinaryExpr {
fn pretty(&self, p: &mut Printer) {
self.lhs.pretty(p);
p.push(' ');
@ -343,11 +343,11 @@ impl Pretty for BinOp {
}
}
impl Pretty for ExprCall {
impl Pretty for CallExpr {
fn pretty(&self, p: &mut Printer) {
self.callee.pretty(p);
let mut write_args = |items: &[ExprArg]| {
let mut write_args = |items: &[CallArg]| {
p.push('(');
p.join(items, ", ", |item, p| item.pretty(p));
p.push(')');
@ -357,7 +357,7 @@ impl Pretty for ExprCall {
// This can be moved behind the arguments.
//
// Example: Transforms "#v(a, [b])" => "#v(a)[b]".
[head @ .., ExprArg::Pos(Expr::Template(template))] => {
[head @ .., CallArg::Pos(Expr::Template(template))] => {
if !head.is_empty() {
write_args(head);
}
@ -369,13 +369,13 @@ impl Pretty for ExprCall {
}
}
impl Pretty for ExprArgs {
impl Pretty for CallArgs {
fn pretty(&self, p: &mut Printer) {
p.join(&self.items, ", ", |item, p| item.pretty(p));
}
}
impl Pretty for ExprArg {
impl Pretty for CallArg {
fn pretty(&self, p: &mut Printer) {
match self {
Self::Pos(expr) => expr.pretty(p),
@ -384,7 +384,7 @@ impl Pretty for ExprArg {
}
}
impl Pretty for ExprClosure {
impl Pretty for ClosureExpr {
fn pretty(&self, p: &mut Printer) {
p.push('(');
p.join(self.params.iter(), ", ", |item, p| item.pretty(p));
@ -393,7 +393,7 @@ impl Pretty for ExprClosure {
}
}
impl Pretty for ExprLet {
impl Pretty for LetExpr {
fn pretty(&self, p: &mut Printer) {
p.push_str("let ");
self.binding.pretty(p);
@ -404,7 +404,7 @@ impl Pretty for ExprLet {
}
}
impl Pretty for ExprIf {
impl Pretty for IfExpr {
fn pretty(&self, p: &mut Printer) {
p.push_str("if ");
self.condition.pretty(p);
@ -418,7 +418,7 @@ impl Pretty for ExprIf {
}
}
impl Pretty for ExprWhile {
impl Pretty for WhileExpr {
fn pretty(&self, p: &mut Printer) {
p.push_str("while ");
self.condition.pretty(p);
@ -427,7 +427,7 @@ impl Pretty for ExprWhile {
}
}
impl Pretty for ExprFor {
impl Pretty for ForExpr {
fn pretty(&self, p: &mut Printer) {
p.push_str("for ");
self.pattern.pretty(p);
@ -475,14 +475,13 @@ impl Pretty for Value {
Value::Dict(v) => v.pretty(p),
Value::Template(v) => v.pretty(p),
Value::Func(v) => v.pretty(p),
Value::Args(v) => v.pretty(p),
Value::Any(v) => v.pretty(p),
Value::Error => p.push_str("<error>"),
}
}
}
impl Pretty for ValueArray {
impl Pretty for ArrayValue {
fn pretty(&self, p: &mut Printer) {
p.push('(');
p.join(self, ", ", |item, p| item.pretty(p));
@ -493,7 +492,7 @@ impl Pretty for ValueArray {
}
}
impl Pretty for ValueDict {
impl Pretty for DictValue {
fn pretty(&self, p: &mut Printer) {
p.push('(');
if self.is_empty() {
@ -509,7 +508,7 @@ impl Pretty for ValueDict {
}
}
impl Pretty for ValueTemplate {
impl Pretty for TemplateValue {
fn pretty(&self, p: &mut Printer) {
p.push('[');
for part in self {
@ -537,7 +536,7 @@ impl Pretty for TemplateFunc {
}
}
impl Pretty for ValueFunc {
impl Pretty for FuncValue {
fn pretty(&self, p: &mut Printer) {
p.push_str("<function");
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) {
p.push('(');
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) {
if let Some(name) = &self.name {
p.push_str(&name.v);
@ -613,7 +612,7 @@ pretty_display! {
Linear,
RgbaColor,
Color,
ValueAny,
AnyValue,
}
#[cfg(test)]
@ -622,9 +621,7 @@ mod tests {
use std::rc::Rc;
use super::*;
use crate::color::RgbaColor;
use crate::env::Env;
use crate::eval::eval;
use crate::parse::parse;
#[track_caller]
@ -796,34 +793,37 @@ mod tests {
);
// Function.
test_value(ValueFunc::new(None, |_, _| Value::None), "<function>");
test_value(FuncValue::new(None, |_, _| Value::None), "<function>");
test_value(
ValueFunc::new(Some("nil".into()), |_, _| Value::None),
FuncValue::new(Some("nil".into()), |_, _| Value::None),
"<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.
test_value(ValueAny::new(1), "1");
test_value(AnyValue::new(1), "1");
// 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.
#[derive(Debug, Clone, PartialEq)]
pub enum Expr {
/// A literal.
/// A literal, like `11pt` or `"hi"`.
Lit(Lit),
/// An identifier: `left`.
Ident(Ident),
/// An array expression: `(1, "hi", 12cm)`.
Array(ExprArray),
Array(ArrayExpr),
/// A dictionary expression: `(color: #f79143, pattern: dashed)`.
Dict(ExprDict),
Dict(DictExpr),
/// A template expression: `[*Hi* there!]`.
Template(ExprTemplate),
Template(TemplateExpr),
/// A grouped expression: `(1 + 2)`.
Group(ExprGroup),
Group(GroupExpr),
/// A block expression: `{ let x = 1; x + 2 }`.
Block(ExprBlock),
Block(BlockExpr),
/// A unary operation: `-x`.
Unary(ExprUnary),
Unary(UnaryExpr),
/// A binary operation: `a + b`.
Binary(ExprBinary),
Binary(BinaryExpr),
/// An invocation of a function: `f(x, y)`.
Call(ExprCall),
Call(CallExpr),
/// A closure expression: `(x, y) => z`.
Closure(ExprClosure),
Closure(ClosureExpr),
/// A let expression: `let x = 1`.
Let(ExprLet),
/// An if expression: `if x { y } else { z }`.
If(ExprIf),
/// A while expression: `while x { y }`.
While(ExprWhile),
/// A for expression: `for x in y { z }`.
For(ExprFor),
Let(LetExpr),
/// An if-else expression: `if x { y } else { z }`.
If(IfExpr),
/// A while loop expression: `while x { y }`.
While(WhileExpr),
/// A for loop expression: `for x in y { z }`.
For(ForExpr),
}
impl Expr {
@ -74,7 +74,7 @@ impl Expr {
}
}
/// A literal.
/// A literal, like `11pt` or `"hi"`.
#[derive(Debug, Clone, PartialEq)]
pub struct Lit {
/// The source code location.
@ -111,7 +111,7 @@ pub enum LitKind {
/// An array expression: `(1, "hi", 12cm)`.
#[derive(Debug, Clone, PartialEq)]
pub struct ExprArray {
pub struct ArrayExpr {
/// The source code location.
pub span: Span,
/// The entries of the array.
@ -120,7 +120,7 @@ pub struct ExprArray {
/// A dictionary expression: `(color: #f79143, pattern: dashed)`.
#[derive(Debug, Clone, PartialEq)]
pub struct ExprDict {
pub struct DictExpr {
/// The source code location.
pub span: Span,
/// The named dictionary entries.
@ -145,7 +145,7 @@ impl Named {
/// A template expression: `[*Hi* there!]`.
#[derive(Debug, Clone, PartialEq)]
pub struct ExprTemplate {
pub struct TemplateExpr {
/// The source code location.
pub span: Span,
/// The contents of the template.
@ -154,7 +154,7 @@ pub struct ExprTemplate {
/// A grouped expression: `(1 + 2)`.
#[derive(Debug, Clone, PartialEq)]
pub struct ExprGroup {
pub struct GroupExpr {
/// The source code location.
pub span: Span,
/// The wrapped expression.
@ -163,7 +163,7 @@ pub struct ExprGroup {
/// A block expression: `{ let x = 1; x + 2 }`.
#[derive(Debug, Clone, PartialEq)]
pub struct ExprBlock {
pub struct BlockExpr {
/// The source code location.
pub span: Span,
/// The list of expressions contained in the block.
@ -174,7 +174,7 @@ pub struct ExprBlock {
/// A unary operation: `-x`.
#[derive(Debug, Clone, PartialEq)]
pub struct ExprUnary {
pub struct UnaryExpr {
/// The source code location.
pub span: Span,
/// The operator: `-`.
@ -225,7 +225,7 @@ impl UnOp {
/// A binary operation: `a + b`.
#[derive(Debug, Clone, PartialEq)]
pub struct ExprBinary {
pub struct BinaryExpr {
/// The source code location.
pub span: Span,
/// The left-hand side of the operation: `a`.
@ -374,13 +374,13 @@ pub enum Associativity {
/// An invocation of a function: `foo(...)`.
#[derive(Debug, Clone, PartialEq)]
pub struct ExprCall {
pub struct CallExpr {
/// The source code location.
pub span: Span,
/// The callee of the function.
pub callee: Box<Expr>,
/// The arguments to the function.
pub args: ExprArgs,
pub args: CallArgs,
}
/// The arguments to a function: `12, draw: false`.
@ -388,23 +388,23 @@ pub struct ExprCall {
/// In case of a bracketed invocation with a body, the body is _not_
/// included in the span for the sake of clearer error messages.
#[derive(Debug, Clone, PartialEq)]
pub struct ExprArgs {
pub struct CallArgs {
/// The source code location.
pub span: Span,
/// The positional and named arguments.
pub items: Vec<ExprArg>,
pub items: Vec<CallArg>,
}
/// An argument to a function call: `12` or `draw: false`.
#[derive(Debug, Clone, PartialEq)]
pub enum ExprArg {
pub enum CallArg {
/// A positional argument.
Pos(Expr),
/// A named argument.
Named(Named),
}
impl ExprArg {
impl CallArg {
/// The source code location.
pub fn span(&self) -> Span {
match self {
@ -416,7 +416,7 @@ impl ExprArg {
/// A closure expression: `(x, y) => z`.
#[derive(Debug, Clone, PartialEq)]
pub struct ExprClosure {
pub struct ClosureExpr {
/// The source code location.
pub span: Span,
/// The name of the closure.
@ -431,7 +431,7 @@ pub struct ExprClosure {
/// A let expression: `let x = 1`.
#[derive(Debug, Clone, PartialEq)]
pub struct ExprLet {
pub struct LetExpr {
/// The source code location.
pub span: Span,
/// The binding to assign to.
@ -440,9 +440,9 @@ pub struct ExprLet {
pub init: Option<Box<Expr>>,
}
/// An if expression: `if x { y } else { z }`.
/// An if-else expression: `if x { y } else { z }`.
#[derive(Debug, Clone, PartialEq)]
pub struct ExprIf {
pub struct IfExpr {
/// The source code location.
pub span: Span,
/// The condition which selects the body to evaluate.
@ -453,9 +453,9 @@ pub struct ExprIf {
pub else_body: Option<Box<Expr>>,
}
/// A while expression: `while x { y }`.
/// A while loop expression: `while x { y }`.
#[derive(Debug, Clone, PartialEq)]
pub struct ExprWhile {
pub struct WhileExpr {
/// The source code location.
pub span: Span,
/// The condition which selects whether to evaluate the body.
@ -464,9 +464,9 @@ pub struct ExprWhile {
pub body: Box<Expr>,
}
/// A for expression: `for x in y { z }`.
/// A for loop expression: `for x in y { z }`.
#[derive(Debug, Clone, PartialEq)]
pub struct ExprFor {
pub struct ForExpr {
/// The source code location.
pub span: Span,
/// The pattern to assign to.

View File

@ -4,7 +4,7 @@ use unicode_xid::UnicodeXID;
use super::Span;
/// An Unicode identifier with a few extra permissible characters.
/// An unicode identifier with a few extra permissible characters.
///
/// In addition to what is specified in the [Unicode Standard][uax31], we allow:
/// - `_` as a starting character,

View File

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

View File

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

View File

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

View File

@ -82,29 +82,29 @@ visit! {
}
}
fn visit_array(v, node: &ExprArray) {
fn visit_array(v, node: &ArrayExpr) {
for expr in &node.items {
v.visit_expr(&expr);
}
}
fn visit_dict(v, node: &ExprDict) {
fn visit_dict(v, node: &DictExpr) {
for named in &node.items {
v.visit_expr(&named.expr);
}
}
fn visit_template(v, node: &ExprTemplate) {
fn visit_template(v, node: &TemplateExpr) {
v.visit_enter();
v.visit_tree(&node.tree);
v.visit_exit();
}
fn visit_group(v, node: &ExprGroup) {
fn visit_group(v, node: &GroupExpr) {
v.visit_expr(&node.expr);
}
fn visit_block(v, node: &ExprBlock) {
fn visit_block(v, node: &BlockExpr) {
if node.scoping {
v.visit_enter();
}
@ -116,48 +116,48 @@ visit! {
}
}
fn visit_binary(v, node: &ExprBinary) {
fn visit_binary(v, node: &BinaryExpr) {
v.visit_expr(&node.lhs);
v.visit_expr(&node.rhs);
}
fn visit_unary(v, node: &ExprUnary) {
fn visit_unary(v, node: &UnaryExpr) {
v.visit_expr(&node.expr);
}
fn visit_call(v, node: &ExprCall) {
fn visit_call(v, node: &CallExpr) {
v.visit_expr(&node.callee);
v.visit_args(&node.args);
}
fn visit_closure(v, node: &ExprClosure) {
fn visit_closure(v, node: &ClosureExpr) {
for param in node.params.iter() {
v.visit_binding(param);
}
v.visit_expr(&node.body);
}
fn visit_args(v, node: &ExprArgs) {
fn visit_args(v, node: &CallArgs) {
for arg in &node.items {
v.visit_arg(arg);
}
}
fn visit_arg(v, node: &ExprArg) {
fn visit_arg(v, node: &CallArg) {
match node {
ExprArg::Pos(expr) => v.visit_expr(&expr),
ExprArg::Named(named) => v.visit_expr(&named.expr),
CallArg::Pos(expr) => v.visit_expr(&expr),
CallArg::Named(named) => v.visit_expr(&named.expr),
}
}
fn visit_let(v, node: &ExprLet) {
fn visit_let(v, node: &LetExpr) {
v.visit_binding(&node.binding);
if let Some(init) = &node.init {
v.visit_expr(&init);
}
}
fn visit_if(v, node: &ExprIf) {
fn visit_if(v, node: &IfExpr) {
v.visit_expr(&node.condition);
v.visit_expr(&node.if_body);
if let Some(body) = &node.else_body {
@ -165,12 +165,12 @@ visit! {
}
}
fn visit_while(v, node: &ExprWhile) {
fn visit_while(v, node: &WhileExpr) {
v.visit_expr(&node.condition);
v.visit_expr(&node.body);
}
fn visit_for(v, node: &ExprFor) {
fn visit_for(v, node: &ForExpr) {
match &node.pattern {
ForPattern::Value(value) => v.visit_binding(value),
ForPattern::KeyValue(key, value) => {

View File

@ -3,29 +3,35 @@
## Directory structure
Top level directory structure:
- `typ`: Input files.
- `res`: Resource files used by tests.
- `ref`: Reference images which the output is compared with to determine whether
a test passed or failed.
- `png`: PNG files produced by tests.
- `pdf`: PDF files produced by tests.
- `res`: Resource files used by tests.
## Running the tests
Running the integration tests (the tests in this directory).
```bash
# Run all tests
cargo test
# Run unit tests
cargo test --lib
# Run integration tests (the tests in this directory)
cargo test --test typeset
```
# Run all tests whose names contain the word `filter`
Running all tests whose names contain the word `filter`.
```bash
cargo test --test typeset filter
```
To make the integration tests go faster they don't generate PDFs by default.
Pass the `--pdf` flag to generate those. Mind that PDFs are not tested
automatically at the moment, so you should always check the output manually when
making changes.
```bash
cargo test --test typeset -- --pdf
```
## Creating new tests
To keep things small, please optimize reference images before committing them:
To keep things small, please optimize reference images before committing them.
When you use the approve buttom from the Test Helper (see the `tools` folder)
this happens automatically if you have `oxipng` installed.
```bash
# One image
oxipng -o max path/to/image.png

View File

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