From 335fa2d118718b4dba539294a8ef6c96c5bbf09e Mon Sep 17 00:00:00 2001 From: Laurenz Date: Sun, 4 Oct 2020 23:31:35 +0200 Subject: [PATCH] =?UTF-8?q?Small=20improvements=20=F0=9F=8D=AA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/eval/args.rs | 39 ++++++++++++++++++++-- src/eval/convert.rs | 11 ++++--- src/eval/state.rs | 2 +- src/eval/value.rs | 28 ++++++++-------- src/export/pdf.rs | 6 ++-- src/layout/line.rs | 44 +++++++++++++------------ src/layout/mod.rs | 29 ++--------------- src/layout/stack.rs | 73 ++++++++++++++++++++++++++++-------------- src/layout/tree.rs | 4 +-- src/lib.rs | 29 +++++++++-------- src/library/align.rs | 3 +- src/library/boxed.rs | 2 +- src/library/color.rs | 8 ++--- src/library/font.rs | 2 +- src/library/mod.rs | 1 - src/library/page.rs | 2 +- src/library/spacing.rs | 4 +-- src/paper.rs | 2 +- src/parse/tests.rs | 1 + src/syntax/ast/expr.rs | 3 ++ src/syntax/ast/mod.rs | 2 +- 21 files changed, 166 insertions(+), 129 deletions(-) diff --git a/src/eval/args.rs b/src/eval/args.rs index d71cc3741..b89ee61f6 100644 --- a/src/eval/args.rs +++ b/src/eval/args.rs @@ -2,7 +2,9 @@ use std::mem; -use super::*; +use super::{Convert, RefKey, ValueDict}; +use crate::layout::LayoutContext; +use crate::syntax::{SpanWith, Spanned}; /// A wrapper around a dictionary value that simplifies argument parsing in /// functions. @@ -21,14 +23,44 @@ impl Args { { self.0.v.remove(key).and_then(|entry| { let span = entry.value.span; - let (t, diag) = T::convert(entry.value); + let (result, diag) = T::convert(entry.value); if let Some(diag) = diag { ctx.f.diags.push(diag.span_with(span)) } - t.ok() + result.ok() }) } + /// This is the same as [`get`], except that it generates an error about a + /// missing argument with the given `name` if the key does not exist. + /// + /// [`get`]: #method.get + pub fn need<'a, K, T>( + &mut self, + ctx: &mut LayoutContext, + key: K, + name: &str, + ) -> Option + where + K: Into>, + T: Convert, + { + match self.0.v.remove(key) { + Some(entry) => { + let span = entry.value.span; + let (result, diag) = T::convert(entry.value); + if let Some(diag) = diag { + ctx.f.diags.push(diag.span_with(span)) + } + result.ok() + } + None => { + ctx.f.diags.push(error!(self.0.span, "missing argument: {}", name)); + None + } + } + } + /// Retrieve and remove the first matching positional argument. pub fn find(&mut self) -> Option where @@ -104,6 +136,7 @@ impl Args { #[cfg(test)] mod tests { + use super::super::{Dict, SpannedEntry, Value}; use super::*; fn entry(value: Value) -> SpannedEntry { diff --git a/src/eval/convert.rs b/src/eval/convert.rs index a32d59128..4c177c5b6 100644 --- a/src/eval/convert.rs +++ b/src/eval/convert.rs @@ -4,11 +4,12 @@ use std::ops::Deref; use fontdock::{FontStretch, FontStyle, FontWeight}; -use super::*; +use super::{Value, ValueDict, ValueFunc}; use crate::diag::Diag; use crate::geom::Linear; use crate::layout::{Dir, SpecAlign}; use crate::paper::Paper; +use crate::syntax::{Ident, SpanWith, Spanned, SynTree}; /// Types that values can be converted into. pub trait Convert: Sized { @@ -96,9 +97,9 @@ macro_rules! impl_match { } impl_match!(Value, "value", v => v); -impl_match!(Ident, "ident", Value::Ident(v) => v); +impl_match!(Ident, "identifier", Value::Ident(v) => v); impl_match!(bool, "bool", Value::Bool(v) => v); -impl_match!(i64, "int", Value::Int(v) => v); +impl_match!(i64, "integer", Value::Int(v) => v); impl_match!(f64, "float", Value::Int(v) => v as f64, Value::Float(v) => v, @@ -112,9 +113,9 @@ impl_match!(Linear, "linear", ); impl_match!(String, "string", Value::Str(v) => v); impl_match!(SynTree, "tree", Value::Content(v) => v); -impl_match!(ValueDict, "dict", Value::Dict(v) => v); +impl_match!(ValueDict, "dictionary", Value::Dict(v) => v); impl_match!(ValueFunc, "function", Value::Func(v) => v); -impl_match!(StringLike, "ident or string", +impl_match!(StringLike, "identifier or string", Value::Ident(Ident(v)) => StringLike(v), Value::Str(v) => StringLike(v), ); diff --git a/src/eval/state.rs b/src/eval/state.rs index e4e9f7937..3a9f05a40 100644 --- a/src/eval/state.rs +++ b/src/eval/state.rs @@ -1,4 +1,4 @@ -//! State. +//! Evaluation state. use fontdock::{fallback, FallbackTree, FontStretch, FontStyle, FontVariant, FontWeight}; diff --git a/src/eval/value.rs b/src/eval/value.rs index 2d83c8d0b..85cb261c3 100644 --- a/src/eval/value.rs +++ b/src/eval/value.rs @@ -1,4 +1,4 @@ -//! Computational values: Syntactical expressions can be evaluated into these. +//! Computational values. use std::fmt::{self, Debug, Formatter}; use std::ops::Deref; @@ -57,9 +57,9 @@ impl Value { pub fn ty(&self) -> &'static str { match self { Self::None => "none", - Self::Ident(_) => "ident", + Self::Ident(_) => "identifier", Self::Bool(_) => "bool", - Self::Int(_) => "int", + Self::Int(_) => "integer", Self::Float(_) => "float", Self::Relative(_) => "relative", Self::Length(_) => "length", @@ -88,6 +88,9 @@ impl Spanned { /// the value is represented as layoutable content in a reasonable way. pub fn into_commands(self) -> Vec { match self.v { + // Don't print out none values. + Value::None => vec![], + // Pass-through. Value::Commands(commands) => commands, Value::Content(tree) => vec![Command::LayoutSyntaxTree(tree)], @@ -109,9 +112,6 @@ impl Spanned { commands } - // Don't print out none values. - Value::None => vec![], - // Format with debug. val => { let fmt = format!("{:?}", val); @@ -144,6 +144,14 @@ impl Debug for Value { } } +/// A dictionary of values. +/// +/// # Example +/// ```typst +/// (false, 12cm, greeting="hi") +/// ``` +pub type ValueDict = Dict>; + /// An wrapper around a reference-counted executable function value. /// /// The dynamic function object is wrapped in an `Rc` to keep [`Value`] @@ -192,11 +200,3 @@ impl Debug for ValueFunc { f.pad("") } } - -/// A dictionary of values. -/// -/// # Example -/// ```typst -/// (false, 12cm, greeting="hi") -/// ``` -pub type ValueDict = Dict>; diff --git a/src/export/pdf.rs b/src/export/pdf.rs index fb1b3e3b5..ccbc01f15 100644 --- a/src/export/pdf.rs +++ b/src/export/pdf.rs @@ -26,11 +26,11 @@ use crate::length::Length; /// The raw _PDF_ is written into the `target` writable, returning the number of /// bytes written. pub fn export( - layout: &[BoxLayout], + layouts: &[BoxLayout], loader: &FontLoader, target: W, ) -> io::Result { - PdfExporter::new(layout, loader, target)?.write() + PdfExporter::new(layouts, loader, target)?.write() } struct PdfExporter<'a, W: Write> { @@ -42,7 +42,7 @@ struct PdfExporter<'a, W: Write> { /// tree and so on. These offsets are computed in the beginning and stored /// here. offsets: Offsets, - // Font remapping, see below at `remap_fonts`. + // Font remapping, for more information see `remap_fonts`. to_pdf: HashMap, to_layout: Vec, } diff --git a/src/layout/line.rs b/src/layout/line.rs index 9de8348c8..baef9fb01 100644 --- a/src/layout/line.rs +++ b/src/layout/line.rs @@ -12,7 +12,9 @@ use super::*; /// Performs the line layouting. pub struct LineLayouter { + /// The context used for line layouting. ctx: LineContext, + /// The underlying layouter that stacks the finished lines. stack: StackLayouter, /// The in-progress line. run: LineRun, @@ -36,27 +38,6 @@ pub struct LineContext { pub line_spacing: f64, } -/// A sequence of boxes with the same alignment. A real line can consist of -/// multiple runs with different alignments. -struct LineRun { - /// The so-far accumulated items of the run. - layouts: Vec<(f64, BoxLayout)>, - /// The summed width and maximal height of the run. - size: Size, - /// The alignment of all layouts in the line. - /// - /// When a new run is created the alignment is yet to be determined and - /// `None` as such. Once a layout is added, its alignment decides the - /// alignment for the whole run. - align: Option, - /// The amount of space left by another run on the same line or `None` if - /// this is the only run so far. - usable: Option, - /// The spacing state. This influences how new spacing is handled, e.g. hard - /// spacing may override soft spacing. - last_spacing: LastSpacing, -} - impl LineLayouter { /// Create a new line layouter. pub fn new(ctx: LineContext) -> Self { @@ -259,6 +240,27 @@ impl LineLayouter { } } +/// A sequence of boxes with the same alignment. A real line can consist of +/// multiple runs with different alignments. +struct LineRun { + /// The so-far accumulated items of the run. + layouts: Vec<(f64, BoxLayout)>, + /// The summed width and maximal height of the run. + size: Size, + /// The alignment of all layouts in the line. + /// + /// When a new run is created the alignment is yet to be determined and + /// `None` as such. Once a layout is added, its alignment decides the + /// alignment for the whole run. + align: Option, + /// The amount of space left by another run on the same line or `None` if + /// this is the only run so far. + usable: Option, + /// The spacing state. This influences how new spacing is handled, e.g. hard + /// spacing may override soft spacing. + last_spacing: LastSpacing, +} + impl LineRun { fn new() -> Self { Self { diff --git a/src/layout/mod.rs b/src/layout/mod.rs index 75d7b96b2..52f7e0e61 100644 --- a/src/layout/mod.rs +++ b/src/layout/mod.rs @@ -46,7 +46,6 @@ pub async fn layout( }; let layouts = layout_tree(&tree, &mut ctx).await; - Pass::new(layouts, ctx.f) } @@ -177,12 +176,11 @@ pub enum Command { SetTextState(TextState), /// Update the page style. SetPageState(PageState), - - /// Update the alignment for future boxes added to this layouting process. - SetAlignment(LayoutAlign), /// Update the layouting system along which future boxes will be laid /// out. This ends the current line. SetSystem(LayoutSystem), + /// Update the alignment for future boxes added to this layouting process. + SetAlignment(LayoutAlign), } /// Defines how spacing interacts with surrounding spacing. @@ -211,26 +209,3 @@ impl SpacingKind { /// The standard spacing kind used for word spacing. pub const WORD: Self = Self::Soft(1); } - -/// The spacing kind of the most recently inserted item in a layouting process. -/// -/// Since the last inserted item may not be spacing at all, this can be `None`. -#[derive(Debug, Copy, Clone, PartialEq)] -enum LastSpacing { - /// The last item was hard spacing. - Hard, - /// The last item was soft spacing with the given width and level. - Soft(f64, u32), - /// The last item wasn't spacing. - None, -} - -impl LastSpacing { - /// The width of the soft space if this is a soft space or zero otherwise. - fn soft_or_zero(self) -> f64 { - match self { - LastSpacing::Soft(space, _) => space, - _ => 0.0, - } - } -} diff --git a/src/layout/stack.rs b/src/layout/stack.rs index e69dc0715..dadc40b93 100644 --- a/src/layout/stack.rs +++ b/src/layout/stack.rs @@ -23,7 +23,9 @@ use super::*; /// Performs the stack layouting. pub struct StackLayouter { + /// The context used for stack layouting. ctx: StackContext, + /// The finished layouts. layouts: Vec, /// The in-progress space. space: Space, @@ -45,30 +47,6 @@ pub struct StackContext { pub repeat: bool, } -/// A layout space composed of subspaces which can have different systems and -/// alignments. -struct Space { - /// The index of this space in `ctx.spaces`. - index: usize, - /// Whether to include a layout for this space even if it would be empty. - hard: bool, - /// The so-far accumulated layouts. - layouts: Vec<(LayoutSystem, BoxLayout)>, - /// The specialized size of this space. - size: Size, - /// The specialized remaining space. - usable: Size, - /// The specialized extra-needed size to affect the size at all. - extra: Size, - /// Dictate which alignments for new boxes are still allowed and which - /// require a new space to be started. For example, after an `End`-aligned - /// item, no `Start`-aligned one can follow. - rulers: Sides, - /// The spacing state. This influences how new spacing is handled, e.g. hard - /// spacing may override soft spacing. - last_spacing: LastSpacing, -} - impl StackLayouter { /// Create a new stack layouter. pub fn new(ctx: StackContext) -> Self { @@ -387,6 +365,30 @@ impl StackLayouter { } } +/// A layout space composed of subspaces which can have different systems and +/// alignments. +struct Space { + /// The index of this space in `ctx.spaces`. + index: usize, + /// Whether to include a layout for this space even if it would be empty. + hard: bool, + /// The so-far accumulated layouts. + layouts: Vec<(LayoutSystem, BoxLayout)>, + /// The specialized size of this space. + size: Size, + /// The specialized remaining space. + usable: Size, + /// The specialized extra-needed size to affect the size at all. + extra: Size, + /// Dictate which alignments for new boxes are still allowed and which + /// require a new space to be started. For example, after an `End`-aligned + /// item, no `Start`-aligned one can follow. + rulers: Sides, + /// The spacing state. This influences how new spacing is handled, e.g. hard + /// spacing may override soft spacing. + last_spacing: LastSpacing, +} + impl Space { fn new(index: usize, hard: bool, usable: Size) -> Self { Self { @@ -401,3 +403,26 @@ impl Space { } } } + +/// The spacing kind of the most recently inserted item in a layouting process. +/// +/// Since the last inserted item may not be spacing at all, this can be `None`. +#[derive(Debug, Copy, Clone, PartialEq)] +pub(crate) enum LastSpacing { + /// The last item was hard spacing. + Hard, + /// The last item was soft spacing with the given width and level. + Soft(f64, u32), + /// The last item wasn't spacing. + None, +} + +impl LastSpacing { + /// The width of the soft space if this is a soft space or zero otherwise. + fn soft_or_zero(self) -> f64 { + match self { + LastSpacing::Soft(space, _) => space, + _ => 0.0, + } + } +} diff --git a/src/layout/tree.rs b/src/layout/tree.rs index 5468a7cd6..4e15fb12b 100644 --- a/src/layout/tree.rs +++ b/src/layout/tree.rs @@ -3,9 +3,7 @@ use super::*; use crate::eval::Eval; use crate::shaping; -use crate::syntax::{ - Deco, Expr, NodeHeading, NodeRaw, Span, SpanWith, Spanned, SynNode, SynTree, -}; +use crate::syntax::*; use crate::DynFuture; /// Layout a syntax tree in a given context. diff --git a/src/lib.rs b/src/lib.rs index d2bd076d8..ef81a8ae9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,24 +2,25 @@ //! //! # Steps //! - **Parsing:** The parsing step first transforms a plain string into an -//! [iterator of tokens][tokens]. Then, a [parser] constructs a syntax tree -//! from the token stream. The structures describing the tree can be found in -//! the [syntax] module. +//! [iterator of tokens][tokens]. This token stream is [parsed] into a [syntax +//! tree]. The structures describing the tree can be found in the [ast] +//! module. //! - **Layouting:** The next step is to transform the syntax tree into a -//! portable representation of the typesetted document. Types for these can be -//! found in the [layout] module. A finished layout ready for exporting is a -//! [`MultiLayout`] consisting of multiple boxes (or pages). +//! portable representation of the typesetted document. The final output +//! consists of a vector of [`BoxLayouts`] (corresponding to pages), ready for +//! exporting. //! - **Exporting:** The finished layout can then be exported into a supported //! format. Submodules for these formats are located in the [export] module. //! Currently, the only supported output format is [_PDF_]. //! -//! [tokens]: parse/struct.Tokens.html -//! [parser]: parse/fn.parse.html -//! [syntax]: syntax/index.html +//! [tokens]: parsing/struct.Tokens.html +//! [parsed]: parsing/fn.parse.html +//! [syntax tree]: syntax/ast/type.SynTree.html +//! [ast]: syntax/ast/index.html //! [layout]: layout/index.html +//! [`BoxLayouts`]: layout/struct.BoxLayout.html //! [export]: export/index.html //! [_PDF_]: export/pdf/index.html -//! [`MultiLayout`]: layout/type.MultiLayout.html #[macro_use] pub mod diag; @@ -56,7 +57,7 @@ pub async fn typeset( ) -> Pass> { let parsed = parse::parse(src); let layouted = layout::layout(&parsed.output, state, loader).await; - let feedback = Feedback::merge(parsed.feedback, layouted.feedback); + let feedback = Feedback::join(parsed.feedback, layouted.feedback); Pass::new(layouted.output, feedback) } @@ -108,8 +109,8 @@ impl Feedback { Self { diags: vec![], decos: vec![] } } - /// Merged two feedbacks into one. - pub fn merge(mut a: Self, b: Self) -> Self { + /// Merge two feedbacks into one. + pub fn join(mut a: Self, b: Self) -> Self { a.extend(b); a } @@ -120,7 +121,7 @@ impl Feedback { self.decos.extend(more.decos); } - /// Add more feedback whose spans are local and need to be offset by an + /// Add more feedback whose spans are local and need to be translated by an /// `offset` to be correct in this feedback's context. pub fn extend_offset(&mut self, more: Self, offset: Pos) { self.diags.extend(more.diags.offset(offset)); diff --git a/src/library/align.rs b/src/library/align.rs index 674eccebf..eaef4d87d 100644 --- a/src/library/align.rs +++ b/src/library/align.rs @@ -1,4 +1,4 @@ -use super::*; +use crate::prelude::*; /// `align`: Align content along the layouting axes. /// @@ -16,7 +16,6 @@ use super::*; /// There may not be two alignment specifications for the same axis. pub async fn align(mut args: Args, ctx: &mut LayoutContext) -> Value { let body = args.find::(); - let h = args.get::<_, Spanned>(ctx, "horizontal"); let v = args.get::<_, Spanned>(ctx, "vertical"); let pos = args.find_all::>(); diff --git a/src/library/boxed.rs b/src/library/boxed.rs index 81e3a96a8..085176e07 100644 --- a/src/library/boxed.rs +++ b/src/library/boxed.rs @@ -1,5 +1,5 @@ -use super::*; use crate::geom::Linear; +use crate::prelude::*; /// `box`: Layouts its contents into a box. /// diff --git a/src/library/color.rs b/src/library/color.rs index 143ce70a4..a4958c536 100644 --- a/src/library/color.rs +++ b/src/library/color.rs @@ -1,11 +1,11 @@ -use super::*; use crate::color::RgbaColor; +use crate::prelude::*; /// `rgb`: Create an RGB(A) color. pub async fn rgb(mut args: Args, ctx: &mut LayoutContext) -> Value { - let r = args.get::<_, Spanned>(ctx, 0); - let g = args.get::<_, Spanned>(ctx, 1); - let b = args.get::<_, Spanned>(ctx, 2); + let r = args.need::<_, Spanned>(ctx, 0, "red value"); + let g = args.need::<_, Spanned>(ctx, 1, "green value"); + let b = args.need::<_, Spanned>(ctx, 2, "blue value"); let a = args.get::<_, Spanned>(ctx, 3); args.done(ctx); diff --git a/src/library/font.rs b/src/library/font.rs index 0a28beaa4..60380def1 100644 --- a/src/library/font.rs +++ b/src/library/font.rs @@ -1,8 +1,8 @@ use fontdock::{FontStretch, FontStyle, FontWeight}; -use super::*; use crate::eval::StringLike; use crate::geom::Linear; +use crate::prelude::*; /// `font`: Configure the font. /// diff --git a/src/library/mod.rs b/src/library/mod.rs index 66fc5d597..191a3920b 100644 --- a/src/library/mod.rs +++ b/src/library/mod.rs @@ -15,7 +15,6 @@ pub use page::*; pub use spacing::*; use crate::eval::{Scope, ValueFunc}; -use crate::prelude::*; macro_rules! std { ($($name:literal => $func:expr),* $(,)?) => { diff --git a/src/library/page.rs b/src/library/page.rs index db76d2a1b..2bf9f0194 100644 --- a/src/library/page.rs +++ b/src/library/page.rs @@ -1,9 +1,9 @@ use std::mem; -use super::*; use crate::eval::Absolute; use crate::geom::{Linear, Sides}; use crate::paper::{Paper, PaperClass}; +use crate::prelude::*; /// `page`: Configure pages. /// diff --git a/src/library/spacing.rs b/src/library/spacing.rs index b97f4640c..b38f06070 100644 --- a/src/library/spacing.rs +++ b/src/library/spacing.rs @@ -1,6 +1,6 @@ -use super::*; use crate::geom::Linear; use crate::layout::SpacingKind; +use crate::prelude::*; /// `h`: Add horizontal spacing. /// @@ -19,7 +19,7 @@ pub async fn v(args: Args, ctx: &mut LayoutContext) -> Value { } fn spacing(mut args: Args, ctx: &mut LayoutContext, axis: SpecAxis) -> Value { - let spacing = args.get::<_, Linear>(ctx, 0); + let spacing = args.need::<_, Linear>(ctx, 0, "spacing"); args.done(ctx); Value::Commands(if let Some(spacing) = spacing { diff --git a/src/paper.rs b/src/paper.rs index 30e5d8e7f..aff4cdd7c 100644 --- a/src/paper.rs +++ b/src/paper.rs @@ -37,7 +37,7 @@ pub enum PaperClass { } impl PaperClass { - /// The default margin ratios for this page class. + /// The default margins for this page class. pub fn default_margins(self) -> Sides { let f = Linear::rel; let s = |l, r, t, b| Sides::new(f(l), f(r), f(t), f(b)); diff --git a/src/parse/tests.rs b/src/parse/tests.rs index 108b4b294..644b7f160 100644 --- a/src/parse/tests.rs +++ b/src/parse/tests.rs @@ -89,6 +89,7 @@ macro_rules! Call { }}; ($($tts:tt)*) => { Expr::Call(Call![@$($tts)*]) }; } + fn Unary(op: impl Into>, expr: impl Into>) -> Expr { Expr::Unary(ExprUnary { op: op.into(), diff --git a/src/syntax/ast/expr.rs b/src/syntax/ast/expr.rs index 433dcca28..09729f522 100644 --- a/src/syntax/ast/expr.rs +++ b/src/syntax/ast/expr.rs @@ -21,6 +21,9 @@ pub struct ExprCall { /// The name of the function. pub name: Spanned, /// The arguments to the function. + /// + /// In case of a bracketed invocation with a body, the body is _not_ + /// included in the span for the sake of clearer error messages. pub args: Spanned, } diff --git a/src/syntax/ast/mod.rs b/src/syntax/ast/mod.rs index df0fbc233..0d394e54c 100644 --- a/src/syntax/ast/mod.rs +++ b/src/syntax/ast/mod.rs @@ -8,4 +8,4 @@ pub use expr::*; pub use lit::*; pub use tree::*; -use super::*; +use super::{Ident, SpanVec, Spanned};