From bbcdeb128cce04cd95714b7bc7af5a23a7e38bd2 Mon Sep 17 00:00:00 2001 From: Laurenz Date: Wed, 29 Jul 2020 18:09:51 +0200 Subject: [PATCH] =?UTF-8?q?Move,=20rename=20and=20switch=20some=20things?= =?UTF-8?q?=20(boring)=20=F0=9F=9A=9A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Problems -> Diagnostics - Position -> Pos - offset_spans -> Offset trait - Size -> Length (and some more size types renamed) - Paper into its own module - scope::Parser -> parsing::CallParser - Create `Decorations` alias - Remove lots of double newlines - Switch from f32 to f64 --- src/bin/main.rs | 1 - src/diagnostic.rs | 91 +++++++++++ src/export/pdf.rs | 35 +++-- src/func.rs | 4 +- src/layout/actions.rs | 23 ++- src/layout/line.rs | 25 ++- src/layout/mod.rs | 25 ++- src/layout/model.rs | 9 +- src/layout/stack.rs | 51 +++---- src/layout/text.rs | 17 +-- src/{size.rs => length.rs} | 285 ++++++++++++++++------------------ src/lib.rs | 35 +++-- src/library/font.rs | 27 ++-- src/library/layout.rs | 27 ++-- src/library/mod.rs | 1 - src/library/page.rs | 21 ++- src/library/spacing.rs | 19 ++- src/paper.rs | 275 +++++++++++++++++++++++++++++++++ src/problem.rs | 94 ------------ src/style.rs | 303 +++---------------------------------- src/syntax/expr.rs | 55 +++---- src/syntax/func/keys.rs | 1 - src/syntax/func/maps.rs | 67 ++++---- src/syntax/func/mod.rs | 8 +- src/syntax/func/values.rs | 43 +++--- src/syntax/mod.rs | 6 +- src/syntax/parsing.rs | 74 ++++----- src/syntax/scope.rs | 22 +-- src/syntax/span.rs | 94 +++++++----- src/syntax/test.rs | 6 +- src/syntax/tokens.rs | 52 +++---- tests/src/typeset.rs | 22 +-- 32 files changed, 891 insertions(+), 927 deletions(-) create mode 100644 src/diagnostic.rs rename src/{size.rs => length.rs} (57%) create mode 100644 src/paper.rs delete mode 100644 src/problem.rs diff --git a/src/bin/main.rs b/src/bin/main.rs index 1b4903972..b191b4377 100644 --- a/src/bin/main.rs +++ b/src/bin/main.rs @@ -8,7 +8,6 @@ use typstc::{Typesetter, DebugErrorProvider}; use typstc::toddle::query::fs::EagerFsProvider; use typstc::export::pdf; - fn main() { if let Err(err) = run() { eprintln!("error: {}", err); diff --git a/src/diagnostic.rs b/src/diagnostic.rs new file mode 100644 index 000000000..a5c5d0b86 --- /dev/null +++ b/src/diagnostic.rs @@ -0,0 +1,91 @@ +//! Diagnostics (errors / warnings) in source code. +//! +//! There are no fatal errors. The document will always compile and yield a +//! layout. However, this is a best effort process and bad things will still +//! generate errors and warnings. + +use serde::Serialize; +use crate::syntax::span::SpanVec; + +/// A list of spanned diagnostics. +pub type Diagnostics = SpanVec; + +/// A diagnostic that arose in parsing or layouting. +#[derive(Debug, Clone, Eq, PartialEq, Serialize)] +pub struct Diagnostic { + /// How severe / important the diagnostic is. + pub level: Level, + /// A message describing the diagnostic. + pub message: String, +} + +/// How severe / important a diagnostic is. +#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize)] +#[serde(rename_all = "camelCase")] +pub enum Level { + Warning, + Error, +} + +impl Diagnostic { + /// Create a new diagnostic from message and level. + pub fn new(message: impl Into, level: Level) -> Self { + Self { message: message.into(), level } + } +} + +/// Construct a diagnostic with `Error` level. +/// +/// ``` +/// # use typstc::error; +/// # use typstc::syntax::span::Span; +/// # use typstc::Feedback; +/// # let span = Span::ZERO; +/// # let mut feedback = Feedback::new(); +/// # let name = ""; +/// // Create formatted error values. +/// let error = error!("expected {}", name); +/// +/// // Create spanned errors. +/// let spanned = error!(span, "there is an error here"); +/// +/// // Create an error and directly add it to existing feedback. +/// error!(@feedback, span, "oh no!"); +/// ``` +#[macro_export] +macro_rules! error { + ($($tts:tt)*) => { + $crate::__impl_diagnostic!($crate::diagnostic::Level::Error; $($tts)*) + }; +} + +/// Construct a diagnostic with `Warning` level. +/// +/// This works exactly like `error!`. See its documentation for more +/// information. +#[macro_export] +macro_rules! warning { + ($($tts:tt)*) => { + $crate::__impl_diagnostic!($crate::diagnostic::Level::Warning; $($tts)*) + }; +} + +/// Backs the `error!` and `warning!` macros. +#[macro_export] +#[doc(hidden)] +macro_rules! __impl_diagnostic { + ($level:expr; @$feedback:expr, $($tts:tt)*) => { + $feedback.diagnostics.push($crate::__impl_diagnostic!($level; $($tts)*)); + }; + + ($level:expr; $fmt:literal $($tts:tt)*) => { + $crate::diagnostic::Diagnostic::new(format!($fmt $($tts)*), $level) + }; + + ($level:expr; $span:expr, $fmt:literal $($tts:tt)*) => { + $crate::syntax::span::Spanned::new( + $crate::__impl_diagnostic!($level; $fmt $($tts)*), + $span, + ) + }; +} diff --git a/src/export/pdf.rs b/src/export/pdf.rs index 5c38084be..42587e5fc 100644 --- a/src/export/pdf.rs +++ b/src/export/pdf.rs @@ -23,8 +23,7 @@ use toddle::tables::{ use crate::GlobalFontLoader; use crate::layout::{MultiLayout, Layout, LayoutAction}; -use crate::size::Size; - +use crate::length::Length; /// Export a layouted list of boxes. The same font loader as used for /// layouting needs to be passed in here since the layout only contains @@ -128,8 +127,8 @@ impl<'a, W: Write> PdfExporter<'a, W> { let rect = Rect::new( 0.0, 0.0, - page.dimensions.x.to_pt(), - page.dimensions.y.to_pt(), + page.dimensions.x.to_pt() as f32, + page.dimensions.y.to_pt() as f32, ); self.writer.write_obj( @@ -167,14 +166,14 @@ impl<'a, W: Write> PdfExporter<'a, W> { LayoutAction::SetFont(id, size) => { active_font = (self.font_remap[id], size.to_pt()); - text.tf(active_font.0 as u32 + 1, size.to_pt()); + text.tf(active_font.0 as u32 + 1, size.to_pt() as f32); } LayoutAction::WriteText(string) => { if let Some(pos) = next_pos.take() { let x = pos.x.to_pt(); - let y = (page.dimensions.y - pos.y - Size::pt(active_font.1)).to_pt(); - text.tm(1.0, 0.0, 0.0, 1.0, x, y); + let y = (page.dimensions.y - pos.y - Length::pt(active_font.1)).to_pt(); + text.tm(1.0, 0.0, 0.0, 1.0, x as f32, y as f32); } text.tj(self.fonts[active_font.0].encode_text(&string)?); @@ -219,8 +218,8 @@ impl<'a, W: Write> PdfExporter<'a, W> { // Extract information from the head and hmtx tables. let head = font.read_table::
()?; - let font_unit_ratio = 1.0 / (head.units_per_em as f32); - let font_unit_to_size = |x| Size::pt(font_unit_ratio * x); + let font_unit_ratio = 1.0 / (head.units_per_em as f64); + let font_unit_to_size = |x| Length::pt(font_unit_ratio * x); let font_unit_to_glyph_unit = |fu| { let size = font_unit_to_size(fu); (1000.0 * size.to_pt()).round() as GlyphUnit @@ -228,10 +227,10 @@ impl<'a, W: Write> PdfExporter<'a, W> { let italic = head.mac_style.contains(MacStyleFlags::ITALIC); let bounding_box = Rect::new( - font_unit_to_glyph_unit(head.x_min as f32), - font_unit_to_glyph_unit(head.y_min as f32), - font_unit_to_glyph_unit(head.x_max as f32), - font_unit_to_glyph_unit(head.y_max as f32), + font_unit_to_glyph_unit(head.x_min as f64), + font_unit_to_glyph_unit(head.y_min as f64), + font_unit_to_glyph_unit(head.x_max as f64), + font_unit_to_glyph_unit(head.y_max as f64), ); // Transform the width into PDF units. @@ -239,7 +238,7 @@ impl<'a, W: Write> PdfExporter<'a, W> { .read_table::()? .metrics .iter() - .map(|m| font_unit_to_glyph_unit(m.advance_width as f32)) + .map(|m| font_unit_to_glyph_unit(m.advance_width as f64)) .collect(); // Write the CID font referencing the font descriptor. @@ -274,12 +273,12 @@ impl<'a, W: Write> PdfExporter<'a, W> { // Write the font descriptor (contains the global information about the font). self.writer.write_obj(id + 2, FontDescriptor::new(base_font, flags, italic_angle) .font_bbox(bounding_box) - .ascent(font_unit_to_glyph_unit(os2.s_typo_ascender as f32)) - .descent(font_unit_to_glyph_unit(os2.s_typo_descender as f32)) + .ascent(font_unit_to_glyph_unit(os2.s_typo_ascender as f64)) + .descent(font_unit_to_glyph_unit(os2.s_typo_descender as f64)) .cap_height(font_unit_to_glyph_unit( - os2.s_cap_height.unwrap_or(os2.s_typo_ascender) as f32, + os2.s_cap_height.unwrap_or(os2.s_typo_ascender) as f64, )) - .stem_v((10.0 + 0.244 * (os2.us_weight_class as f32 - 50.0)) as GlyphUnit) + .stem_v((10.0 + 0.244 * (os2.us_weight_class as f64 - 50.0)) as GlyphUnit) .font_file_2(id + 4) )?; diff --git a/src/func.rs b/src/func.rs index e948d0499..af3cd091d 100644 --- a/src/func.rs +++ b/src/func.rs @@ -52,8 +52,8 @@ pub trait ParseFunc { /// /// parse(header, body, state, f) { /// let body = body!(opt: body, state, f); -/// let hidden = header.args.pos.get::(&mut f.problems) -/// .or_missing(&mut f.problems, header.name.span, "hidden") +/// let hidden = header.args.pos.get::(&mut f.diagnostics) +/// .or_missing(&mut f.diagnostics, header.name.span, "hidden") /// .unwrap_or(false); /// /// HiderFunc { body: if hidden { None } else { body } } diff --git a/src/layout/actions.rs b/src/layout/actions.rs index 8b50edfa3..317cff25c 100644 --- a/src/layout/actions.rs +++ b/src/layout/actions.rs @@ -4,23 +4,22 @@ use std::fmt::{self, Debug, Formatter}; use serde::ser::{Serialize, Serializer, SerializeTuple}; use toddle::query::FontIndex; -use crate::size::{Size, Size2D}; +use crate::length::{Length, Size}; use super::Layout; use self::LayoutAction::*; - /// A layouting action, which is the basic building block layouts are composed /// of. #[derive(Clone, PartialEq)] pub enum LayoutAction { /// Move to an absolute position. - MoveAbsolute(Size2D), + MoveAbsolute(Size), /// Set the font given the index from the font loader and font size. - SetFont(FontIndex, Size), + SetFont(FontIndex, Length), /// Write text at the current position. WriteText(String), /// Visualize a box for debugging purposes. - DebugBox(Size2D), + DebugBox(Size), } impl Serialize for LayoutAction { @@ -81,11 +80,11 @@ impl Debug for LayoutAction { /// position. #[derive(Debug, Clone, PartialEq)] pub struct LayoutActions { - origin: Size2D, + origin: Size, actions: Vec, - active_font: (FontIndex, Size), - next_pos: Option, - next_font: Option<(FontIndex, Size)>, + active_font: (FontIndex, Length), + next_pos: Option, + next_font: Option<(FontIndex, Length)>, } impl LayoutActions { @@ -93,8 +92,8 @@ impl LayoutActions { pub fn new() -> LayoutActions { LayoutActions { actions: vec![], - origin: Size2D::ZERO, - active_font: (FontIndex::MAX, Size::ZERO), + origin: Size::ZERO, + active_font: (FontIndex::MAX, Length::ZERO), next_pos: None, next_font: None, } @@ -126,7 +125,7 @@ impl LayoutActions { /// Add a layout at a position. All move actions inside the layout are /// translated by the position. - pub fn add_layout(&mut self, position: Size2D, layout: Layout) { + pub fn add_layout(&mut self, position: Size, layout: Layout) { self.flush_position(); self.origin = position; diff --git a/src/layout/line.rs b/src/layout/line.rs index 18ace29fe..1bb362042 100644 --- a/src/layout/line.rs +++ b/src/layout/line.rs @@ -11,7 +11,6 @@ use super::stack::{StackLayouter, StackContext}; use super::*; - /// Performs the line layouting. #[derive(Debug)] pub struct LineLayouter { @@ -40,7 +39,7 @@ pub struct LineContext { /// extent of the layout. pub debug: bool, /// The line spacing. - pub line_spacing: Size, + pub line_spacing: Length, } /// A line run is a sequence of boxes with the same alignment that are arranged @@ -49,10 +48,10 @@ pub struct LineContext { #[derive(Debug)] struct LineRun { /// The so-far accumulated layouts in the line. - layouts: Vec<(Size, Layout)>, - /// The width (primary size) and maximal height (secondary size) of the + layouts: Vec<(Length, Layout)>, + /// The width (primary length) and maximal height (secondary length) of the /// line. - size: Size2D, + size: Size, /// The alignment of all layouts in the line. /// /// When a new run is created the alignment is yet to be determined. Once a @@ -61,7 +60,7 @@ struct LineRun { alignment: Option, /// If another line run with different alignment already took up some space /// of the line, this run has less space and how much is stored here. - usable: Option, + usable: Option, /// A possibly cached soft spacing or spacing state. last_spacing: LastSpacing, } @@ -156,7 +155,7 @@ impl LineLayouter { /// /// This specifies how much more fits before a line break needs to be /// issued. - fn usable(&self) -> Size2D { + fn usable(&self) -> Size { // The base is the usable space per stack layouter. let mut usable = self.stack.usable().generalized(self.ctx.axes); @@ -171,7 +170,7 @@ impl LineLayouter { } /// Add spacing along the primary axis to the line. - pub fn add_primary_spacing(&mut self, mut spacing: Size, kind: SpacingKind) { + pub fn add_primary_spacing(&mut self, mut spacing: Length, kind: SpacingKind) { match kind { // A hard space is simply an empty box. SpacingKind::Hard => { @@ -197,7 +196,7 @@ impl LineLayouter { } /// Finish the line and add secondary spacing to the underlying stack. - pub fn add_secondary_spacing(&mut self, spacing: Size, kind: SpacingKind) { + pub fn add_secondary_spacing(&mut self, spacing: Length, kind: SpacingKind) { self.finish_line_if_not_empty(); self.stack.add_spacing(spacing, kind) } @@ -219,7 +218,7 @@ impl LineLayouter { } /// Update the line spacing. - pub fn set_line_spacing(&mut self, line_spacing: Size) { + pub fn set_line_spacing(&mut self, line_spacing: Length) { self.ctx.line_spacing = line_spacing; } @@ -235,7 +234,7 @@ impl LineLayouter { /// Whether the currently set line is empty. pub fn line_is_empty(&self) -> bool { - self.run.size == Size2D::ZERO && self.run.layouts.is_empty() + self.run.size == Size::ZERO && self.run.layouts.is_empty() } /// Finish the last line and compute the final list of boxes. @@ -265,7 +264,7 @@ impl LineLayouter { - layout.dimensions.primary(self.ctx.axes), }; - let pos = Size2D::with_x(x); + let pos = Size::with_x(x); actions.add_layout(pos, layout); } @@ -293,7 +292,7 @@ impl LineRun { fn new() -> LineRun { LineRun { layouts: vec![], - size: Size2D::ZERO, + size: Size::ZERO, alignment: None, usable: None, last_spacing: LastSpacing::Hard, diff --git a/src/layout/mod.rs b/src/layout/mod.rs index 01d402db4..4863d554c 100644 --- a/src/layout/mod.rs +++ b/src/layout/mod.rs @@ -5,7 +5,7 @@ use smallvec::SmallVec; use serde::Serialize; use toddle::query::FontIndex; -use crate::size::{Size, Size2D, SizeBox}; +use crate::length::{Length, Size, Margins}; use self::prelude::*; pub mod line; @@ -27,7 +27,6 @@ pub mod prelude { pub use super::Alignment::{self, *}; } - /// A collection of layouts. pub type MultiLayout = Vec; @@ -35,7 +34,7 @@ pub type MultiLayout = Vec; #[derive(Debug, Clone, PartialEq, Serialize)] pub struct Layout { /// The size of the box. - pub dimensions: Size2D, + pub dimensions: Size, /// How to align this layout in a parent container. #[serde(skip)] pub alignment: LayoutAlignment, @@ -66,9 +65,9 @@ pub type LayoutSpaces = SmallVec<[LayoutSpace; 2]>; #[derive(Debug, Copy, Clone, PartialEq)] pub struct LayoutSpace { /// The maximum size of the box to layout in. - pub dimensions: Size2D, + pub dimensions: Size, /// Padding that should be respected on each side. - pub padding: SizeBox, + pub padding: Margins, /// Whether to expand the dimensions of the resulting layout to the full /// dimensions of this space or to shrink them to fit the content. pub expansion: LayoutExpansion, @@ -77,12 +76,12 @@ pub struct LayoutSpace { impl LayoutSpace { /// The offset from the origin to the start of content, that is, /// `(padding.left, padding.top)`. - pub fn start(&self) -> Size2D { - Size2D::new(self.padding.left, self.padding.top) + pub fn start(&self) -> Size { + Size::new(self.padding.left, self.padding.top) } /// The actually usable area (dimensions minus padding). - pub fn usable(&self) -> Size2D { + pub fn usable(&self) -> Size { self.dimensions.unpadded(self.padding) } @@ -90,7 +89,7 @@ impl LayoutSpace { pub fn usable_space(&self) -> LayoutSpace { LayoutSpace { dimensions: self.usable(), - padding: SizeBox::ZERO, + padding: Margins::ZERO, expansion: LayoutExpansion::new(false, false), } } @@ -369,17 +368,17 @@ enum LastSpacing { /// The last item was hard spacing. Hard, /// The last item was soft spacing with the given width and level. - Soft(Size, u32), + Soft(Length, u32), /// The last item was not spacing. None, } impl LastSpacing { - /// The size of the soft space if this is a soft space or zero otherwise. - fn soft_or_zero(self) -> Size { + /// The length of the soft space if this is a soft space or zero otherwise. + fn soft_or_zero(self) -> Length { match self { LastSpacing::Soft(space, _) => space, - _ => Size::ZERO, + _ => Length::ZERO, } } } diff --git a/src/layout/model.rs b/src/layout/model.rs index 91d21037e..a15598d2d 100644 --- a/src/layout/model.rs +++ b/src/layout/model.rs @@ -10,14 +10,13 @@ use toddle::query::FontStyle; use crate::{Pass, Feedback}; use crate::GlobalFontLoader; use crate::style::{LayoutStyle, PageStyle, TextStyle}; -use crate::size::{Size, Size2D}; +use crate::length::{Length, Size}; use crate::syntax::{Model, SyntaxModel, Node, Decoration}; use crate::syntax::span::{Span, Spanned}; use super::line::{LineLayouter, LineContext}; use super::text::{layout_text, TextContext}; use super::*; - /// Performs the model layouting. #[derive(Debug)] pub struct ModelLayouter<'a> { @@ -36,7 +35,7 @@ pub struct LayoutContext<'a> { /// The style for pages and text. pub style: &'a LayoutStyle, /// The base unpadded dimensions of this container (for relative sizing). - pub base: Size2D, + pub base: Size, /// The spaces to layout in. pub spaces: LayoutSpaces, /// Whether to have repeated spaces or to use only the first and only once. @@ -76,7 +75,7 @@ pub enum Command<'a> { /// Add spacing of given [kind](super::SpacingKind) along the primary or /// secondary axis. The spacing kind defines how the spacing interacts with /// surrounding spacing. - AddSpacing(Size, SpacingKind, GenericAxis), + AddSpacing(Length, SpacingKind, GenericAxis), /// Start a new line. BreakLine, @@ -159,7 +158,7 @@ impl<'a> ModelLayouter<'a> { for Spanned { v: node, span } in &model.nodes { let decorate = |this: &mut ModelLayouter, deco| { - this.feedback.decos.push(Spanned::new(deco, *span)); + this.feedback.decorations.push(Spanned::new(deco, *span)); }; match node { diff --git a/src/layout/stack.rs b/src/layout/stack.rs index 891815e97..20d99fa62 100644 --- a/src/layout/stack.rs +++ b/src/layout/stack.rs @@ -22,10 +22,9 @@ //! sentence in the second box. use smallvec::smallvec; -use crate::size::ValueBox; +use crate::length::Value4; use super::*; - /// Performs the stack layouting. #[derive(Debug)] pub struct StackLayouter { @@ -66,14 +65,14 @@ struct Space { /// The so-far accumulated layouts. layouts: Vec<(LayoutAxes, Layout)>, /// The specialized size of this space. - size: Size2D, + size: Size, /// The specialized remaining space. - usable: Size2D, + usable: Size, /// The specialized extra-needed dimensions to affect the size at all. - extra: Size2D, + extra: Size, /// The rulers of a space dictate which alignments for new boxes are still /// allowed and which require a new space to be started. - rulers: ValueBox, + rulers: Value4, /// The last added spacing if the last added thing was spacing. last_spacing: LastSpacing, } @@ -129,13 +128,13 @@ impl StackLayouter { } /// Add secondary spacing to the stack. - pub fn add_spacing(&mut self, mut spacing: Size, kind: SpacingKind) { + pub fn add_spacing(&mut self, mut spacing: Length, kind: SpacingKind) { match kind { // A hard space is simply an empty box. SpacingKind::Hard => { // Reduce the spacing such that it definitely fits. spacing.min_eq(self.space.usable.secondary(self.ctx.axes)); - let dimensions = Size2D::with_y(spacing); + let dimensions = Size::with_y(spacing); self.update_metrics(dimensions); self.space.layouts.push((self.ctx.axes, Layout { @@ -165,17 +164,17 @@ impl StackLayouter { /// Update the size metrics to reflect that a layout or spacing with the /// given generalized dimensions has been added. - fn update_metrics(&mut self, dimensions: Size2D) { + fn update_metrics(&mut self, dimensions: Size) { let axes = self.ctx.axes; let mut size = self.space.size.generalized(axes); let mut extra = self.space.extra.generalized(axes); - size.x += (dimensions.x - extra.x).max(Size::ZERO); - size.y += (dimensions.y - extra.y).max(Size::ZERO); + size.x += (dimensions.x - extra.x).max(Length::ZERO); + size.y += (dimensions.y - extra.y).max(Length::ZERO); extra.x.max_eq(dimensions.x); - extra.y = (extra.y - dimensions.y).max(Size::ZERO); + extra.y = (extra.y - dimensions.y).max(Length::ZERO); self.space.size = size.specialized(axes); self.space.extra = extra.specialized(axes); @@ -233,7 +232,7 @@ impl StackLayouter { /// Move to the first space that can fit the given dimensions or do nothing /// if no space is capable of that. - pub fn skip_to_fitting_space(&mut self, dimensions: Size2D) { + pub fn skip_to_fitting_space(&mut self, dimensions: Size) { let start = self.next_space(); for (index, space) in self.ctx.spaces[start..].iter().enumerate() { if space.usable().fits(dimensions) { @@ -251,7 +250,7 @@ impl StackLayouter { let mut spaces = smallvec![LayoutSpace { dimensions, - padding: SizeBox::ZERO, + padding: Margins::ZERO, expansion: LayoutExpansion::new(false, false), }]; @@ -263,15 +262,15 @@ impl StackLayouter { } /// The remaining usable size. - pub fn usable(&self) -> Size2D { + pub fn usable(&self) -> Size { self.space.usable - - Size2D::with_y(self.space.last_spacing.soft_or_zero()) + - Size::with_y(self.space.last_spacing.soft_or_zero()) .specialized(self.ctx.axes) } /// Whether the current layout space (not subspace) is empty. pub fn space_is_empty(&self) -> bool { - self.space.size == Size2D::ZERO && self.space.layouts.is_empty() + self.space.size == Size::ZERO && self.space.layouts.is_empty() } /// Whether the current layout space is the last is the followup list. @@ -310,7 +309,7 @@ impl StackLayouter { let start = space.start(); let mut bounds = vec![]; - let mut bound = SizeBox { + let mut bound = Margins { left: start.x, top: start.y, right: start.x + self.space.size.x, @@ -337,7 +336,7 @@ impl StackLayouter { // The `x` field stores the maximal primary extent in one axis-aligned // run, while the `y` fields stores the accumulated secondary extent. - let mut extent = Size2D::ZERO; + let mut extent = Size::ZERO; let mut rotation = Vertical; for (bound, entry) in bounds.iter_mut().zip(&self.space.layouts).rev() { @@ -349,7 +348,7 @@ impl StackLayouter { // is reset for this new axis-aligned run. if rotation != axes.secondary.axis() { extent.y = extent.x; - extent.x = Size::ZERO; + extent.x = Length::ZERO; rotation = axes.secondary.axis(); } @@ -383,11 +382,11 @@ impl StackLayouter { // The space in which this layout is aligned is given by the // distances between the borders of it's bounding box. let usable = - Size2D::new(bound.right - bound.left, bound.bottom - bound.top) + Size::new(bound.right - bound.left, bound.bottom - bound.top) .generalized(axes); let local = usable.anchor(alignment, axes) - size.anchor(alignment, axes); - let pos = Size2D::new(bound.left, bound.top) + local.specialized(axes); + let pos = Size::new(bound.left, bound.top) + local.specialized(axes); actions.add_layout(pos, layout); } @@ -417,15 +416,15 @@ impl StackLayouter { } impl Space { - fn new(index: usize, hard: bool, usable: Size2D) -> Space { + fn new(index: usize, hard: bool, usable: Size) -> Space { Space { index, hard, layouts: vec![], - size: Size2D::ZERO, + size: Size::ZERO, usable, - extra: Size2D::ZERO, - rulers: ValueBox::with_all(Origin), + extra: Size::ZERO, + rulers: Value4::with_all(Origin), last_spacing: LastSpacing::Hard, } } diff --git a/src/layout/text.rs b/src/layout/text.rs index 286ccc68c..cbe40214a 100644 --- a/src/layout/text.rs +++ b/src/layout/text.rs @@ -8,11 +8,10 @@ use toddle::query::{FontQuery, FontIndex}; use toddle::tables::{CharMap, Header, HorizontalMetrics}; use crate::GlobalFontLoader; -use crate::size::{Size, Size2D}; +use crate::length::{Length, Size}; use crate::style::TextStyle; use super::*; - /// Performs the text layouting. #[derive(Debug)] struct TextLayouter<'a> { @@ -21,7 +20,7 @@ struct TextLayouter<'a> { actions: LayoutActions, buffer: String, active_font: FontIndex, - width: Size, + width: Length, } /// The context for text layouting. @@ -54,7 +53,7 @@ impl<'a> TextLayouter<'a> { actions: LayoutActions::new(), buffer: String::new(), active_font: FontIndex::MAX, - width: Size::ZERO, + width: Length::ZERO, } } @@ -77,7 +76,7 @@ impl<'a> TextLayouter<'a> { } Layout { - dimensions: Size2D::new(self.width, self.ctx.style.font_size()), + dimensions: Size::new(self.width, self.ctx.style.font_size()), alignment: self.ctx.alignment, actions: self.actions.into_vec(), } @@ -110,7 +109,7 @@ impl<'a> TextLayouter<'a> { /// Select the best font for a character and return its index along with /// the width of the char in the font. - async fn select_font(&mut self, c: char) -> Option<(FontIndex, Size)> { + async fn select_font(&mut self, c: char) -> Option<(FontIndex, Length)> { let mut loader = self.ctx.loader.borrow_mut(); let mut variant = self.ctx.style.variant; @@ -127,8 +126,8 @@ impl<'a> TextLayouter<'a> { if let Some((font, index)) = loader.get(query).await { // Determine the width of the char. let header = font.read_table::
().ok()?; - let font_unit_ratio = 1.0 / (header.units_per_em as f32); - let font_unit_to_size = |x| Size::pt(font_unit_ratio * x); + let font_unit_ratio = 1.0 / (header.units_per_em as f64); + let font_unit_to_size = |x| Length::pt(font_unit_ratio * x); let glyph = font .read_table::() @@ -139,7 +138,7 @@ impl<'a> TextLayouter<'a> { .read_table::() .ok()? .get(glyph)? - .advance_width as f32; + .advance_width as f64; let char_width = font_unit_to_size(glyph_width) * self.ctx.style.font_size().to_pt(); diff --git a/src/size.rs b/src/length.rs similarity index 57% rename from src/size.rs rename to src/length.rs index e97b92485..8131a6e9c 100644 --- a/src/size.rs +++ b/src/length.rs @@ -8,173 +8,166 @@ use serde::Serialize; use crate::layout::prelude::*; - /// A general spacing type. #[derive(Default, Copy, Clone, PartialEq, PartialOrd, Serialize)] #[serde(transparent)] -pub struct Size { - /// The size in typographic points (1/72 inches). - pub points: f32, +pub struct Length { + /// The length in typographic points (1/72 inches). + pub points: f64, } -impl Size { - /// The zeroed size. - pub const ZERO: Size = Size { points: 0.0 }; +impl Length { + /// The zeroed length. + pub const ZERO: Length = Length { points: 0.0 }; - /// Create a size from an amount of points. - pub fn pt(points: f32) -> Size { Size { points } } + /// Create a length from an amount of points. + pub fn pt(points: f64) -> Length { Length { points } } - /// Create a size from an amount of millimeters. - pub fn mm(mm: f32) -> Size { Size { points: 2.83465 * mm } } + /// Create a length from an amount of millimeters. + pub fn mm(mm: f64) -> Length { Length { points: 2.83465 * mm } } - /// Create a size from an amount of centimeters. - pub fn cm(cm: f32) -> Size { Size { points: 28.3465 * cm } } + /// Create a length from an amount of centimeters. + pub fn cm(cm: f64) -> Length { Length { points: 28.3465 * cm } } - /// Create a size from an amount of inches. - pub fn inches(inches: f32) -> Size { Size { points: 72.0 * inches } } + /// Create a length from an amount of inches. + pub fn inches(inches: f64) -> Length { Length { points: 72.0 * inches } } - /// Convert this size into points. - pub fn to_pt(self) -> f32 { self.points } + /// Convert this length into points. + pub fn to_pt(self) -> f64 { self.points } - /// Convert this size into millimeters. - pub fn to_mm(self) -> f32 { self.points * 0.352778 } + /// Convert this length into millimeters. + pub fn to_mm(self) -> f64 { self.points * 0.352778 } - /// Convert this size into centimeters. - pub fn to_cm(self) -> f32 { self.points * 0.0352778 } + /// Convert this length into centimeters. + pub fn to_cm(self) -> f64 { self.points * 0.0352778 } - /// Convert this size into inches. - pub fn to_inches(self) -> f32 { self.points * 0.0138889 } + /// Convert this length into inches. + pub fn to_inches(self) -> f64 { self.points * 0.0138889 } - /// The maximum of this and the other size. - pub fn max(self, other: Size) -> Size { + /// The maximum of this and the other length. + pub fn max(self, other: Length) -> Length { if self > other { self } else { other } } - /// The minimum of this and the other size. - pub fn min(self, other: Size) -> Size { + /// The minimum of this and the other length. + pub fn min(self, other: Length) -> Length { if self <= other { self } else { other } } - /// Set this size to the maximum of itself and the other size. - pub fn max_eq(&mut self, other: Size) { *self = self.max(other); } + /// Set this length to the maximum of itself and the other length. + pub fn max_eq(&mut self, other: Length) { *self = self.max(other); } - /// Set this size to the minimum of itself and the other size. - pub fn min_eq(&mut self, other: Size) { *self = self.min(other); } + /// Set this length to the minimum of itself and the other length. + pub fn min_eq(&mut self, other: Length) { *self = self.min(other); } /// The anchor position along the given direction for an item with the given - /// alignment in a container with this size. - pub fn anchor(self, alignment: Alignment, direction: Direction) -> Size { + /// alignment in a container with this length. + pub fn anchor(self, alignment: Alignment, direction: Direction) -> Length { match (direction.is_positive(), alignment) { - (true, Origin) | (false, End) => Size::ZERO, + (true, Origin) | (false, End) => Length::ZERO, (_, Center) => self / 2, (true, End) | (false, Origin) => self, } } } -impl Display for Size { +impl Display for Length { fn fmt(&self, f: &mut Formatter) -> fmt::Result { write!(f, "{}pt", self.points) } } -impl Debug for Size { +impl Debug for Length { fn fmt(&self, f: &mut Formatter) -> fmt::Result { Display::fmt(self, f) } } -impl Neg for Size { - type Output = Size; +impl Neg for Length { + type Output = Length; - fn neg(self) -> Size { - Size { points: -self.points } + fn neg(self) -> Length { + Length { points: -self.points } } } -impl Sum for Size { - fn sum(iter: I) -> Size - where I: Iterator { - iter.fold(Size::ZERO, Add::add) +impl Sum for Length { + fn sum(iter: I) -> Length + where I: Iterator { + iter.fold(Length::ZERO, Add::add) } } -/// Either an absolute size or a factor of some entity. +/// Either an absolute length or a factor of some entity. #[derive(Copy, Clone, PartialEq)] #[allow(missing_docs)] -pub enum ScaleSize { - Absolute(Size), - Scaled(f32), +pub enum ScaleLength { + Absolute(Length), + Scaled(f64), } -impl ScaleSize { +impl ScaleLength { /// Use the absolute value or scale the entity. - pub fn scaled(&self, entity: Size) -> Size { + pub fn scaled(&self, entity: Length) -> Length { match self { - ScaleSize::Absolute(s) => *s, - ScaleSize::Scaled(s) => *s * entity, + ScaleLength::Absolute(s) => *s, + ScaleLength::Scaled(s) => *s * entity, } } } -impl Display for ScaleSize { +impl Display for ScaleLength { fn fmt(&self, f: &mut Formatter) -> fmt::Result { match self { - ScaleSize::Absolute(size) => write!(f, "{}", size), - ScaleSize::Scaled(scale) => write!(f, "{}%", scale * 100.0), + ScaleLength::Absolute(length) => write!(f, "{}", length), + ScaleLength::Scaled(scale) => write!(f, "{}%", scale * 100.0), } } } -impl Debug for ScaleSize { +impl Debug for ScaleLength { fn fmt(&self, f: &mut Formatter) -> fmt::Result { Display::fmt(self, f) } } -/// A scale size that is scaled by the font size. -pub type FSize = ScaleSize; - -/// A scale size that is scaled by the size of the padded parent container. -pub type PSize = ScaleSize; - /// A value in two dimensions. #[derive(Default, Copy, Clone, Eq, PartialEq, Serialize)] -pub struct Value2D { +pub struct Value2 { /// The horizontal component. pub x: T, /// The vertical component. pub y: T, } -impl Value2D { +impl Value2 { /// Create a new 2D-value from two values. - pub fn new(x: T, y: T) -> Value2D { Value2D { x, y } } + pub fn new(x: T, y: T) -> Value2 { Value2 { x, y } } /// Create a new 2D-value with `x` set to a value and `y` to default. - pub fn with_x(x: T) -> Value2D where T: Default { - Value2D { x, y: T::default() } + pub fn with_x(x: T) -> Value2 where T: Default { + Value2 { x, y: T::default() } } /// Create a new 2D-value with `y` set to a value and `x` to default. - pub fn with_y(y: T) -> Value2D where T: Default { - Value2D { x: T::default(), y } + pub fn with_y(y: T) -> Value2 where T: Default { + Value2 { x: T::default(), y } } /// Create a new 2D-value with the primary axis set to a value and the other /// one to default. - pub fn with_primary(v: T, axes: LayoutAxes) -> Value2D where T: Default { - Value2D::with_x(v).generalized(axes) + pub fn with_primary(v: T, axes: LayoutAxes) -> Value2 where T: Default { + Value2::with_x(v).generalized(axes) } /// Create a new 2D-value with the secondary axis set to a value and the /// other one to default. - pub fn with_secondary(v: T, axes: LayoutAxes) -> Value2D where T: Default { - Value2D::with_y(v).generalized(axes) + pub fn with_secondary(v: T, axes: LayoutAxes) -> Value2 where T: Default { + Value2::with_y(v).generalized(axes) } /// Create a 2D-value with `x` and `y` set to the same value `s`. - pub fn with_all(s: T) -> Value2D { Value2D { x: s.clone(), y: s } } + pub fn with_all(s: T) -> Value2 { Value2 { x: s.clone(), y: s } } /// Get the specificed component. pub fn get(self, axis: SpecificAxis) -> T { @@ -216,16 +209,16 @@ impl Value2D { /// axes, that is: /// - `x` describes the primary axis instead of the horizontal one. /// - `y` describes the secondary axis instead of the vertical one. - pub fn generalized(self, axes: LayoutAxes) -> Value2D { + pub fn generalized(self, axes: LayoutAxes) -> Value2 { match axes.primary.axis() { Horizontal => self, - Vertical => Value2D { x: self.y, y: self.x }, + Vertical => Value2 { x: self.y, y: self.x }, } } /// Returns the specialized version of this generalized Size2D (inverse to /// `generalized`). - pub fn specialized(self, axes: LayoutAxes) -> Value2D { + pub fn specialized(self, axes: LayoutAxes) -> Value2 { // In fact, generalized is its own inverse. For reasons of clarity // at the call site, we still have this second function. self.generalized(axes) @@ -237,7 +230,7 @@ impl Value2D { } } -impl Debug for Value2D where T: Debug { +impl Debug for Value2 where T: Debug { fn fmt(&self, f: &mut Formatter) -> fmt::Result { f.debug_list() .entry(&self.x) @@ -247,83 +240,61 @@ impl Debug for Value2D where T: Debug { } /// A position or extent in 2-dimensional space. -pub type Size2D = Value2D; +pub type Size = Value2; -impl Size2D { - /// The zeroed 2D-size. - pub const ZERO: Size2D = Size2D { x: Size::ZERO, y: Size::ZERO }; +impl Size { + /// The zeroed 2D-length. + pub const ZERO: Size = Size { x: Length::ZERO, y: Length::ZERO }; - /// Whether the given 2D-size fits into this one, that is, both coordinate + /// Whether the given 2D-length fits into this one, that is, both coordinate /// values are smaller or equal. - pub fn fits(self, other: Size2D) -> bool { + pub fn fits(self, other: Size) -> bool { self.x >= other.x && self.y >= other.y } - /// Return a 2D-size padded by the paddings of the given box. - pub fn padded(self, padding: SizeBox) -> Size2D { - Size2D { + /// Return a 2D-length padded by the paddings of the given box. + pub fn padded(self, padding: Margins) -> Size { + Size { x: self.x + padding.left + padding.right, y: self.y + padding.top + padding.bottom, } } - /// Return a 2D-size reduced by the paddings of the given box. - pub fn unpadded(self, padding: SizeBox) -> Size2D { - Size2D { + /// Return a 2D-length reduced by the paddings of the given box. + pub fn unpadded(self, padding: Margins) -> Size { + Size { x: self.x - padding.left - padding.right, y: self.y - padding.top - padding.bottom, } } /// The anchor position along the given axis for an item with the given - /// alignment in a container with this size. + /// alignment in a container with this length. /// - /// This assumes the size to be generalized such that `x` corresponds to the + /// This assumes the length to be generalized such that `x` corresponds to the /// primary axis. - pub fn anchor(self, alignment: LayoutAlignment, axes: LayoutAxes) -> Size2D { - Size2D { + pub fn anchor(self, alignment: LayoutAlignment, axes: LayoutAxes) -> Size { + Size { x: self.x.anchor(alignment.primary, axes.primary), y: self.y.anchor(alignment.secondary, axes.secondary), } } } -impl Neg for Size2D { - type Output = Size2D; +impl Neg for Size { + type Output = Size; - fn neg(self) -> Size2D { - Size2D { + fn neg(self) -> Size { + Size { x: -self.x, y: -self.y, } } } -/// A value that is stretchable in an interval from a minimal through an optimal -/// to a maximal value. -#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Serialize)] -pub struct StretchValue { - /// The minimum this value can be stretched to. - pub min: T, - /// The optimum for this value. - pub opt: T, - /// The maximum this value can be stretched to. - pub max: T, -} - -impl StretchValue { - /// Create a new stretch size from minimum, optimal and maximum values. - pub fn new(min: T, opt: T, max: T) -> StretchValue { - StretchValue { min, opt, max } - } -} - -/// A size that is stretchable. -pub type StretchSize = StretchValue; - /// A value in four dimensions. #[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Serialize)] -pub struct ValueBox { +pub struct Value4 { /// The left extent. pub left: T, /// The top extent. @@ -334,15 +305,15 @@ pub struct ValueBox { pub bottom: T, } -impl ValueBox { +impl Value4 { /// Create a new box from four sizes. - pub fn new(left: T, top: T, right: T, bottom: T) -> ValueBox { - ValueBox { left, top, right, bottom } + pub fn new(left: T, top: T, right: T, bottom: T) -> Value4 { + Value4 { left, top, right, bottom } } /// Create a box with all four fields set to the same value `s`. - pub fn with_all(value: T) -> ValueBox { - ValueBox { + pub fn with_all(value: T) -> Value4 { + Value4 { left: value.clone(), top: value.clone(), right: value.clone(), @@ -369,7 +340,7 @@ impl ValueBox { /// Set all values to the given value. pub fn set_all(&mut self, value: T) { - *self = ValueBox::with_all(value); + *self = Value4::with_all(value); } /// Set the `left` and `right` values. @@ -385,47 +356,47 @@ impl ValueBox { } } -/// A size in four dimensions. -pub type SizeBox = ValueBox; +/// A length in four dimensions. +pub type Margins = Value4; -impl SizeBox { - /// The zeroed size box. - pub const ZERO: SizeBox = SizeBox { - left: Size::ZERO, - top: Size::ZERO, - right: Size::ZERO, - bottom: Size::ZERO, +impl Margins { + /// The zeroed length box. + pub const ZERO: Margins = Margins { + left: Length::ZERO, + top: Length::ZERO, + right: Length::ZERO, + bottom: Length::ZERO, }; } -impl FromStr for Size { - type Err = ParseSizeError; +impl FromStr for Length { + type Err = ParseLengthError; - fn from_str(src: &str) -> Result { + fn from_str(src: &str) -> Result { let func = match () { - _ if src.ends_with("pt") => Size::pt, - _ if src.ends_with("mm") => Size::mm, - _ if src.ends_with("cm") => Size::cm, - _ if src.ends_with("in") => Size::inches, - _ => return Err(ParseSizeError(())), + _ if src.ends_with("pt") => Length::pt, + _ if src.ends_with("mm") => Length::mm, + _ if src.ends_with("cm") => Length::cm, + _ if src.ends_with("in") => Length::inches, + _ => return Err(ParseLengthError), }; Ok(func(src[..src.len() - 2] - .parse::() - .map_err(|_| ParseSizeError(()))?)) + .parse::() + .map_err(|_| ParseLengthError)?)) } } -/// An error which can be returned when parsing a size. +/// An error which can be returned when parsing a length. #[derive(Debug, Copy, Clone, Eq, PartialEq)] -pub struct ParseSizeError(()); +pub struct ParseLengthError; -impl std::error::Error for ParseSizeError {} +impl std::error::Error for ParseLengthError {} -impl Display for ParseSizeError { +impl Display for ParseLengthError { fn fmt(&self, f: &mut Formatter) -> fmt::Result { - f.write_str("invalid string for size") + f.write_str("invalid string for length") } } @@ -448,7 +419,7 @@ macro_rules! implement_traits { })* $(implement_traits!(@$w i32, $ty $t $o $($rest)*);)* - $(implement_traits!(@$w f32, $ty $t $o $($rest)*);)* + $(implement_traits!(@$w f64, $ty $t $o $($rest)*);)* }; (@front $num:ty, $ty:ident $t:ident $o:ident @@ -458,7 +429,7 @@ macro_rules! implement_traits { impl $tr<$ty> for $num { type Output = $ty; fn $tf($t, $o: $ty) -> $ty { - $ty { $($f: $tr::$tf($t as f32, $o.$f),)* } + $ty { $($f: $tr::$tf($t as f64, $o.$f),)* } } } }; @@ -470,12 +441,12 @@ macro_rules! implement_traits { impl $tr<$num> for $ty { type Output = $ty; fn $tf($t, $o: $num) -> $ty { - $ty { $($f: $tr::$tf($t.$f, $o as f32),)* } + $ty { $($f: $tr::$tf($t.$f, $o as f64),)* } } } impl $at<$num> for $ty { - fn $af(&mut $t, $o: $num) { $($at::$af(&mut $t.$f, $o as f32);)* } + fn $af(&mut $t, $o: $num) { $($at::$af(&mut $t.$f, $o as f64);)* } } }; } @@ -499,5 +470,5 @@ macro_rules! implement_size { }; } -implement_size! { Size(self, other) [points] } -implement_size! { Size2D(self, other) [x, y] } +implement_size! { Length(self, other) [points] } +implement_size! { Size(self, other) [x, y] } diff --git a/src/lib.rs b/src/lib.rs index 8f5bbdd61..77abb6ad2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -27,12 +27,11 @@ use toddle::{Font, OwnedData}; use toddle::query::{FontLoader, SharedFontLoader}; use toddle::query::{FontProvider, FontIndex, FontDescriptor}; -use crate::problem::Problems; +use crate::diagnostic::Diagnostics; use crate::layout::MultiLayout; use crate::style::{LayoutStyle, PageStyle, TextStyle}; -use crate::syntax::{SyntaxModel, Scope, Decoration, ParseState, parse}; -use crate::syntax::span::{Position, SpanVec, offset_spans}; - +use crate::syntax::{Decorations, SyntaxModel, Scope, ParseState, parse}; +use crate::syntax::span::{Offset, Pos}; /// Declare a module and reexport all its contents. macro_rules! pub_use_mod { @@ -45,17 +44,17 @@ macro_rules! pub_use_mod { #[macro_use] mod macros; #[macro_use] -pub mod problem; +pub mod diagnostic; pub mod export; #[macro_use] pub mod func; pub mod layout; pub mod library; -pub mod size; +pub mod length; +pub mod paper; pub mod style; pub mod syntax; - /// Transforms source code into typesetted layouts. /// /// A typesetter can be configured through various methods. @@ -112,7 +111,7 @@ impl Typesetter { /// Parse source code into a syntax tree. pub fn parse(&self, src: &str) -> Pass { - parse(src, Position::ZERO, &self.parse_state) + parse(src, Pos::ZERO, &self.parse_state) } /// Layout a syntax tree and return the produced layout. @@ -176,18 +175,18 @@ impl Pass { /// User feedback data accumulated during a compilation pass. #[derive(Debug, Default, Clone, Eq, PartialEq)] pub struct Feedback { - /// Problems in the source code. - pub problems: Problems, + /// Diagnostics in the source code. + pub diagnostics: Diagnostics, /// Decorations of the source code for semantic syntax highlighting. - pub decos: SpanVec, + pub decorations: Decorations, } impl Feedback { /// Create a new feedback instance without errors and decos. pub fn new() -> Feedback { Feedback { - problems: vec![], - decos: vec![], + diagnostics: vec![], + decorations: vec![], } } @@ -199,15 +198,15 @@ impl Feedback { /// Add other feedback data to this feedback. pub fn extend(&mut self, other: Feedback) { - self.problems.extend(other.problems); - self.decos.extend(other.decos); + self.diagnostics.extend(other.diagnostics); + self.decorations.extend(other.decorations); } /// Add more feedback whose spans are local and need to be offset by an /// `offset` to be correct in this feedback's context. - pub fn extend_offset(&mut self, more: Feedback, offset: Position) { - self.problems.extend(offset_spans(more.problems, offset)); - self.decos.extend(offset_spans(more.decos, offset)); + pub fn extend_offset(&mut self, more: Feedback, offset: Pos) { + self.diagnostics.extend(more.diagnostics.offset(offset)); + self.decorations.extend(more.decorations.offset(offset)); } } diff --git a/src/library/font.rs b/src/library/font.rs index 9f7902f6f..28b791156 100644 --- a/src/library/font.rs +++ b/src/library/font.rs @@ -1,8 +1,7 @@ use toddle::query::{FontWeight, FontStyle}; -use crate::size::FSize; +use crate::length::ScaleLength; use super::*; - function! { /// `font.family`: Set the font family. #[derive(Debug, Clone, PartialEq)] @@ -13,17 +12,17 @@ function! { } parse(header, body, ctx, f) { - let list = header.args.pos.get_all::(&mut f.problems) + let list = header.args.pos.get_all::(&mut f.diagnostics) .map(|s| s.0.to_lowercase()) .collect(); let tuples: Vec<_> = header.args.key - .get_all::(&mut f.problems) + .get_all::(&mut f.diagnostics) .collect(); let classes = tuples.into_iter() .map(|(class, mut tuple)| { - let fallback = tuple.get_all::(&mut f.problems) + let fallback = tuple.get_all::(&mut f.diagnostics) .map(|s| s.0.to_lowercase()) .collect(); (class.to_lowercase(), fallback) @@ -64,8 +63,8 @@ function! { parse(header, body, ctx, f) { FontStyleFunc { body: body!(opt: body, ctx, f), - style: header.args.pos.get::(&mut f.problems) - .or_missing(&mut f.problems, header.name.span, "style"), + style: header.args.pos.get::(&mut f.diagnostics) + .or_missing(&mut f.diagnostics, header.name.span, "style"), } } @@ -84,7 +83,7 @@ function! { parse(header, body, ctx, f) { let body = body!(opt: body, ctx, f); - let weight = header.args.pos.get::>(&mut f.problems) + let weight = header.args.pos.get::>(&mut f.diagnostics) .map(|Spanned { v: (weight, is_clamped), span }| { if is_clamped { warning!( @@ -96,7 +95,7 @@ function! { weight }) - .or_missing(&mut f.problems, header.name.span, "weight"); + .or_missing(&mut f.diagnostics, header.name.span, "weight"); FontWeightFunc { body, weight } } @@ -111,25 +110,25 @@ function! { #[derive(Debug, Clone, PartialEq)] pub struct FontSizeFunc { body: Option, - size: Option, + size: Option, } parse(header, body, ctx, f) { FontSizeFunc { body: body!(opt: body, ctx, f), - size: header.args.pos.get::(&mut f.problems) - .or_missing(&mut f.problems, header.name.span, "size") + size: header.args.pos.get::(&mut f.diagnostics) + .or_missing(&mut f.diagnostics, header.name.span, "size") } } layout(self, ctx, f) { styled(&self.body, ctx, self.size, |t, s| { match s { - FSize::Absolute(size) => { + ScaleLength::Absolute(size) => { t.base_font_size = size; t.font_scale = 1.0; } - FSize::Scaled(scale) => t.font_scale = scale, + ScaleLength::Scaled(scale) => t.font_scale = scale, } }) } diff --git a/src/library/layout.rs b/src/library/layout.rs index 9fac37b7a..2d0e3ac5d 100644 --- a/src/library/layout.rs +++ b/src/library/layout.rs @@ -1,7 +1,6 @@ -use crate::size::PSize; +use crate::length::ScaleLength; use super::*; - function! { /// `align`: Aligns content along the layouting axes. #[derive(Debug, Clone, PartialEq)] @@ -13,14 +12,14 @@ function! { parse(header, body, ctx, f) { AlignFunc { body: body!(opt: body, ctx, f), - map: PosAxisMap::parse::(&mut f.problems, &mut header.args), + map: PosAxisMap::parse::(&mut f.diagnostics, &mut header.args), } } layout(self, ctx, f) { ctx.base = ctx.spaces[0].dimensions; - let map = self.map.dedup(&mut f.problems, ctx.axes, |alignment| { + let map = self.map.dedup(&mut f.diagnostics, ctx.axes, |alignment| { alignment.axis().map(|s| s.to_generic(ctx.axes)) }); @@ -61,14 +60,14 @@ function! { DirectionFunc { name_span: header.name.span, body: body!(opt: body, ctx, f), - map: PosAxisMap::parse::(&mut f.problems, &mut header.args), + map: PosAxisMap::parse::(&mut f.diagnostics, &mut header.args), } } layout(self, ctx, f) { ctx.base = ctx.spaces[0].dimensions; - let map = self.map.dedup(&mut f.problems, ctx.axes, |direction| { + let map = self.map.dedup(&mut f.diagnostics, ctx.axes, |direction| { Some(direction.axis().to_generic(ctx.axes)) }); @@ -103,15 +102,15 @@ function! { #[derive(Debug, Clone, PartialEq)] pub struct BoxFunc { body: SyntaxModel, - extents: AxisMap, + extents: AxisMap, debug: Option, } parse(header, body, ctx, f) { BoxFunc { body: body!(opt: body, ctx, f).unwrap_or(SyntaxModel::new()), - extents: AxisMap::parse::(&mut f.problems, &mut header.args.key), - debug: header.args.key.get::(&mut f.problems, "debug"), + extents: AxisMap::parse::(&mut f.diagnostics, &mut header.args.key), + debug: header.args.key.get::(&mut f.diagnostics, "debug"), } } @@ -123,12 +122,12 @@ function! { ctx.debug = debug; } - let map = self.extents.dedup(&mut f.problems, ctx.axes); + let map = self.extents.dedup(&mut f.diagnostics, ctx.axes); for &axis in &[Horizontal, Vertical] { - if let Some(psize) = map.get(axis) { - let size = psize.scaled(ctx.base.get(axis)); - *ctx.base.get_mut(axis) = size; - *ctx.spaces[0].dimensions.get_mut(axis) = size; + if let Some(scale) = map.get(axis) { + let length = scale.scaled(ctx.base.get(axis)); + *ctx.base.get_mut(axis) = length; + *ctx.spaces[0].dimensions.get_mut(axis) = length; *ctx.spaces[0].expansion.get_mut(axis) = true; } } diff --git a/src/library/mod.rs b/src/library/mod.rs index e1aa4ac20..433a4c73f 100644 --- a/src/library/mod.rs +++ b/src/library/mod.rs @@ -8,7 +8,6 @@ pub_use_mod!(layout); pub_use_mod!(page); pub_use_mod!(spacing); - /// Create a scope with all standard functions. pub fn std() -> Scope { let mut std = Scope::new::(); diff --git a/src/library/page.rs b/src/library/page.rs index 1e782f8f7..dd63a0a74 100644 --- a/src/library/page.rs +++ b/src/library/page.rs @@ -1,23 +1,22 @@ -use crate::size::Size; -use crate::style::{Paper, PaperClass}; +use crate::length::Length; +use crate::paper::{Paper, PaperClass}; use super::*; - function! { /// `page.size`: Set the size of pages. #[derive(Debug, Clone, PartialEq)] pub struct PageSizeFunc { paper: Option, - extents: AxisMap, + extents: AxisMap, flip: bool, } parse(header, body, state, f) { body!(nope: body, f); PageSizeFunc { - paper: header.args.pos.get::(&mut f.problems), - extents: AxisMap::parse::(&mut f.problems, &mut header.args.key), - flip: header.args.key.get::(&mut f.problems, "flip").unwrap_or(false), + paper: header.args.pos.get::(&mut f.diagnostics), + extents: AxisMap::parse::(&mut f.diagnostics, &mut header.args.key), + flip: header.args.key.get::(&mut f.diagnostics, "flip").unwrap_or(false), } } @@ -26,12 +25,12 @@ function! { if let Some(paper) = self.paper { style.class = paper.class; - style.dimensions = paper.dimensions; + style.dimensions = paper.size(); } else { style.class = PaperClass::Custom; } - let map = self.extents.dedup(&mut f.problems, ctx.axes); + let map = self.extents.dedup(&mut f.diagnostics, ctx.axes); map.with(Horizontal, |&width| style.dimensions.x = width); map.with(Vertical, |&height| style.dimensions.y = height); @@ -53,13 +52,13 @@ function! { parse(header, body, state, f) { body!(nope: body, f); PageMarginsFunc { - padding: PaddingMap::parse(&mut f.problems, &mut header.args), + padding: PaddingMap::parse(&mut f.diagnostics, &mut header.args), } } layout(self, ctx, f) { let mut style = ctx.style.page; - self.padding.apply(&mut f.problems, ctx.axes, &mut style.margins); + self.padding.apply(&mut f.diagnostics, ctx.axes, &mut style.margins); vec![SetPageStyle(style)] } } diff --git a/src/library/spacing.rs b/src/library/spacing.rs index adca20afd..5ae25a926 100644 --- a/src/library/spacing.rs +++ b/src/library/spacing.rs @@ -1,10 +1,9 @@ -use crate::size::FSize; +use crate::length::ScaleLength; use crate::layout::SpacingKind; use super::*; use self::ContentKind::*; - function! { /// `line.break`, `n`: Ends the current line. #[derive(Debug, Default, Clone, PartialEq)] @@ -41,7 +40,7 @@ function! { pub struct ContentSpacingFunc { body: Option, content: ContentKind, - spacing: Option, + spacing: Option, } type Meta = ContentKind; @@ -50,9 +49,9 @@ function! { ContentSpacingFunc { body: body!(opt: body, state, f), content: meta, - spacing: header.args.pos.get::(&mut f.problems) - .map(|num| num as f32) - .or_missing(&mut f.problems, header.name.span, "spacing"), + spacing: header.args.pos.get::(&mut f.diagnostics) + .map(|num| num as f64) + .or_missing(&mut f.diagnostics, header.name.span, "spacing"), } } @@ -79,7 +78,7 @@ function! { /// `spacing`, `h`, `v`: Adds spacing along an axis. #[derive(Debug, Clone, PartialEq)] pub struct SpacingFunc { - spacing: Option<(AxisKey, FSize)>, + spacing: Option<(AxisKey, ScaleLength)>, } type Meta = Option; @@ -88,11 +87,11 @@ function! { body!(nope: body, f); SpacingFunc { spacing: if let Some(axis) = meta { - header.args.pos.get::(&mut f.problems) + header.args.pos.get::(&mut f.diagnostics) .map(|s| (AxisKey::Specific(axis), s)) } else { - header.args.key.get_with_key::(&mut f.problems) - }.or_missing(&mut f.problems, header.name.span, "spacing"), + header.args.key.get_with_key::(&mut f.diagnostics) + }.or_missing(&mut f.diagnostics, header.name.span, "spacing"), } } diff --git a/src/paper.rs b/src/paper.rs new file mode 100644 index 000000000..ba2dc212c --- /dev/null +++ b/src/paper.rs @@ -0,0 +1,275 @@ +//! Predefined papers. + +use crate::length::{Length, Size, Value4, ScaleLength}; + +/// Specification of a paper. +#[derive(Debug, Copy, Clone, PartialEq)] +pub struct Paper { + /// The kind of paper, which defines the default margins. + pub class: PaperClass, + /// The width of the paper. + pub width: Length, + /// The height of the paper. + pub height: Length, +} + +impl Paper { + /// The paper with the given name. + pub fn from_name(name: &str) -> Option { + parse_paper(name) + } + + /// The size of the paper. + pub fn size(self) -> Size { + Size::new(self.width, self.height) + } +} + +/// Paper classes define default margins for a class of related papers. +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[allow(missing_docs)] +pub enum PaperClass { + Custom, + Base, + US, + Newspaper, + Book, +} + +impl PaperClass { + /// The default margins for this page class. + pub fn default_margins(self) -> Value4 { + use PaperClass::*; + let values = |l, t, r, b| Value4::new( + ScaleLength::Scaled(l), + ScaleLength::Scaled(t), + ScaleLength::Scaled(r), + ScaleLength::Scaled(b), + ); + + match self { + Custom => values(0.1190, 0.0842, 0.1190, 0.0842), + Base => values(0.1190, 0.0842, 0.1190, 0.0842), + US => values(0.1760, 0.1092, 0.1760, 0.0910), + Newspaper => values(0.0455, 0.0587, 0.0455, 0.0294), + Book => values(0.1200, 0.0852, 0.1500, 0.0965), + } + } +} + +macro_rules! papers { + ($(($var:ident: $class:ident, $width:expr, $height: expr, $($pats:tt)*))*) => { + $(papers!(@$var, stringify!($($pats)*), $class, $width, $height);)* + + fn parse_paper(paper: &str) -> Option { + match paper.to_lowercase().as_str() { + $($($pats)* => Some($var),)* + _ => None, + } + } + }; + + (@$var:ident, $names:expr, $class:ident, $width:expr, $height:expr) => { + #[doc = "Paper with name `"] + #[doc = $names] + #[doc = "`."] + pub const $var: Paper = Paper { + width: Length { points: 2.83465 * $width }, + height: Length { points: 2.83465 * $height }, + class: PaperClass::$class, + }; + }; +} + +// All paper sizes in mm. +papers! { + // ---------------------------------------------------------------------- // + // ISO 216 + + // A Series + (PAPER_A0: Base, 841.0, 1189.0, "a0" | "poster") + (PAPER_A1: Base, 594.0, 841.0, "a1") + (PAPER_A2: Base, 420.0, 594.0, "a2") + (PAPER_A3: Base, 297.0, 420.0, "a3") + (PAPER_A4: Base, 210.0, 297.0, "a4") + (PAPER_A5: Base, 148.0, 210.0, "a5") + (PAPER_A6: Book, 105.0, 148.0, "a6") + (PAPER_A7: Base, 74.0, 105.0, "a7" | "iso-7810-id-2" | "id-2" | "visa" | "flyer") + (PAPER_A8: Base, 52.0, 74.0, "a8") + (PAPER_A9: Base, 37.0, 52.0, "a9") + (PAPER_A10: Base, 26.0, 37.0, "a10") + (PAPER_A11: Base, 18.0, 26.0, "a11") + + // B Series + (PAPER_B1: Base, 707.0, 1000.0, "b1" | "flipchart") + (PAPER_B2: Base, 500.0, 707.0, "b2") + (PAPER_B3: Base, 353.0, 500.0, "b3") + (PAPER_B4: Base, 250.0, 353.0, "b4" | "sheet-music") + (PAPER_B5: Book, 176.0, 250.0, "b5") + (PAPER_B6: Book, 125.0, 176.0, "b6" | "book") + (PAPER_B7: Base, 88.0, 125.0, "b7" | "passport" | "iso-7810-id-3" | "id-3") + (PAPER_B8: Base, 62.0, 88.0, "b8") + + // C Series + (PAPER_C3: Base, 324.0, 458.0, "c3") + (PAPER_C4: Base, 229.0, 324.0, "c4") + (PAPER_C5: Base, 162.0, 229.0, "c5") + (PAPER_C6: Base, 114.0, 162.0, "c6") + (PAPER_C7: Base, 81.0, 114.0, "c7") + (PAPER_C8: Base, 57.0, 81.0, "c8") + + // D Series (DIN extension to ISO) + (PAPER_D3: Base, 272.0, 385.0, "din-d3") + (PAPER_D4: Base, 192.0, 272.0, "din-d4") + (PAPER_D5: Base, 136.0, 192.0, "din-d5" | "dvd") + (PAPER_D6: Base, 96.0, 136.0, "din-d6") + (PAPER_D7: Base, 68.0, 96.0, "din-d7") + (PAPER_D8: Base, 48.0, 68.0, "din-d8") + + // Academically relevant SIS extensions + (PAPER_G5: Base, 169.0, 239.0, "sis-g5") + (PAPER_E5: Base, 115.0, 220.0, "sis-e5") + + // ---------------------------------------------------------------------- // + // Unites States + + // Customary + (PAPER_FOLIO: US, 210.0, 330.0, "folio" | "us-folio" | "us-f4") + (PAPER_LETTER: US, 216.0, 279.0, "letter" | "ansi-a" | + "american-quarto" | "carta") + (PAPER_LEGAL: US, 216.0, 356.0, "legal") + (PAPER_TABLOID: Newspaper, 279.0, 432.0, "tabloid" | "ansi-b") + (PAPER_LEDGER: Base, 432.0, 279.0, "ledger") + (PAPER_JUNIOR_LEGAL: US, 127.0, 203.0, "junior-legal" | "index-card") + (PAPER_HALF_LETTER: Base, 140.0, 216.0, "half-letter") + (PAPER_GOVERNMENT_LETTER: US, 203.0, 267.0, "government-letter") + (PAPER_GOVERNMENT_LEGAL: US, 216.0, 330.0, "government-legal" | "officio") + + // ANSI Extensions + (PAPER_ANSI_C: Base, 432.0, 559.0, "ansi-c") + (PAPER_ANSI_D: Base, 559.0, 864.0, "ansi-d") + (PAPER_ANSI_E: Base, 864.0, 1118.0, "ansi-e") + (PAPER_ENGINEERING_F: Base, 711.0, 1016.0, "engineering-f" | "engineering" | + "navfac" | "aerospace") + + // Architectural Paper + (PAPER_ARCH_A: Base, 229.0, 305.0, "arch-a" | "arch-1") + (PAPER_ARCH_B: Base, 305.0, 457.0, "arch-b" | "arch-2" | "extra-tabloide") + (PAPER_ARCH_C: Base, 457.0, 610.0, "arch-c" | "arch-3") + (PAPER_ARCH_D: Base, 610.0, 914.0, "arch-d" | "arch-4") + (PAPER_ARCH_E1: Base, 762.0, 1067.0, "arch-e1" | "arch-5") + (PAPER_ARCH_E: Base, 914.0, 1219.0, "arch-e" | "arch-6") + + // ---------------------------------------------------------------------- // + // Japan + + // JIS B Series + (PAPER_JIS_B0: Base, 1030.0, 1456.0, "jis-b0" | "jb0") + (PAPER_JIS_B1: Base, 728.0, 1030.0, "jis-b1" | "jb1") + (PAPER_JIS_B2: Base, 515.0, 728.0, "jis-b2" | "jb2") + (PAPER_JIS_B3: Base, 364.0, 515.0, "jis-b3" | "jb3") + (PAPER_JIS_B4: Base, 257.0, 364.0, "jis-b4" | "jb4") + (PAPER_JIS_B5: Base, 182.0, 257.0, "jis-b5" | "jb5") + (PAPER_JIS_B6: Base, 128.0, 182.0, "jis-b6" | "jb6") + (PAPER_JIS_B7: Base, 91.0, 128.0, "jis-b7" | "jb7") + (PAPER_JIS_B8: Base, 64.0, 91.0, "jis-b8" | "jb8") + (PAPER_JIS_B9: Base, 45.0, 64.0, "jis-b9" | "jb9") + (PAPER_JIS_B10: Base, 32.0, 45.0, "jis-b10" | "jb10") + (PAPER_JIS_B11: Base, 22.0, 32.0, "jis-b11" | "jb11") + + // Traditional + (PAPER_SHIROKU_BAN_4: Base, 264.0, 379.0, "shiroku-ban-4") + (PAPER_SHIROKU_BAN_5: Base, 189.0, 262.0, "shiroku-ban-5") + (PAPER_SHIROKU_BAN_6: Base, 127.0, 188.0, "shiroku-ban-6") + (PAPER_KIKU_4: Base, 227.0, 306.0, "kiku-4") + (PAPER_KIKU_5: Base, 151.0, 227.0, "kiku-5") + + // ---------------------------------------------------------------------- // + // China + + // Chinese D Series + (PAPER_SAC_D0: Base, 764.0, 1064.0, "sac-d0" | "cn-d0") + (PAPER_SAC_D1: Base, 532.0, 760.0, "sac-d1" | "cn-d1") + (PAPER_SAC_D2: Base, 380.0, 528.0, "sac-d2" | "cn-d2") + (PAPER_SAC_D3: Base, 264.0, 376.0, "sac-d3" | "cn-d3") + (PAPER_SAC_D4: Base, 188.0, 260.0, "sac-d4" | "cn-d4") + (PAPER_SAC_D5: Base, 130.0, 184.0, "sac-d5" | "cn-d5") + (PAPER_SAC_D6: Base, 92.0, 126.0, "sac-d6" | "cn-d6") + + // ---------------------------------------------------------------------- // + // United Kingdom Imperial (Assortment) + + (PAPER_MONARCH: Base, 184.0, 267.0, "monarch") + (PAPER_QUARTO: Base, 229.0, 279.0, "quarto" | "us-quarto") + (PAPER_UK_QUARTO: Base, 203.0, 254.0, "uk-quarto" | "imperial-quarto") + (PAPER_UK_FOOLSCAP: Base, 343.0, 432.0, "foolscap" | "us-foolscap") + (PAPER_FOOLSCAP: Base, 203.0, 330.0, "imperial-foolscap" | "uk-foolscap") + (PAPER_POTT: Base, 318.0, 381.0, "pott") + (PAPER_CROWN: Base, 318.0, 508.0, "crown") + (PAPER_PINCHED_POST: Base, 375.0, 470.0, "pinched-post") + (PAPER_POST: Base, 394.0, 489.0, "post") + (PAPER_LARGE_POST: Base, 419.0, 533.0, "large-post") + (PAPER_DEMY: Base, 445.0, 572.0, "demy") + (PAPER_ROYAL: Base, 508.0, 635.0, "royal") + (PAPER_DOUBLE_CROWN: Base, 508.0, 762.0, "double-crown" | "theatre") + (PAPER_ELEPHANT: Base, 584.0, 711.0, "elephant") + (PAPER_DOUBLE_ROYAL: Base, 635.0, 1016.0, "double-royal" | "rail") + (PAPER_QUAD_CROWN: Base, 762.0, 1016.0, "quad-crown" | "cinema") + + // ---------------------------------------------------------------------- // + // French Traditional (AFNOR) + + (PAPER_CLOCHE: Base, 300.0, 400.0, "cloche") + (PAPER_POT: Base, 310.0, 400.0, "pot" | "ecolier" | "écolier") + (PAPER_TELLIERE: Base, 340.0, 440.0, "telliere" | "tellière") + (PAPER_COURONNE_ECRITURE: Base, 360.0, 460.0, "couronne-ecriture" | + "couronne" | "couronne-écriture") + (PAPER_COURONNE_EDITION: Base, 370.0, 470.0, "couronne-edition" | + "couronne-édition") + (PAPER_ROBERTO: Base, 390.0, 500.0, "roberto") + (PAPER_ECU: Base, 400.0, 520.0, "ecu" | "écu") + (PAPER_COQUILLE: Base, 440.0, 560.0, "coquille") + (PAPER_CARRE: Base, 450.0, 560.0, "carre" | "carré") + (PAPER_CAVALIER: Base, 460.0, 620.0, "cavalier") + (PAPER_DEMI_RAISIN: Base, 325.0, 500.0, "demi-raisin") + (PAPER_RAISIN: Base, 500.0, 650.0, "raisin" | "dessin") + (PAPER_DOUBLE_RAISIN: Base, 650.0, 1000.0, "double-raisin") + (PAPER_JESUS: Base, 560.0, 760.0, "jesus" | "jésus") + (PAPER_SOLEIL: Base, 600.0, 800.0, "soleil") + (PAPER_COLOMBIER_AFFICHE: Base, 600.0, 800.0, "colombier-affiche" | "affiche") + (PAPER_COLOMBIER_COMMERCIAL: Base, 630.0, 900.0, "colombier-commercial") + (PAPER_PETIT_AIGLE: Base, 700.0, 940.0, "petit-aigle") + (PAPER_GRAND_AIGLE: Base, 750.0, 1060.0, "grand-aigle" | "napoleon") + (PAPER_GRAND_MONDE: Base, 900.0, 1260.0, "grand-monde") + (PAPER_UNIVERS: Base, 1000.0, 1300.0, "univers" | "universe") + + // ---------------------------------------------------------------------- // + // Newspaper + + (PAPER_COMPACT: Newspaper, 280.0, 430.0, "compact") + (PAPER_BERLINER: Newspaper, 315.0, 470.0, "berliner" | "midi") + (PAPER_RHENISH: Newspaper, 350.0, 520.0, "rhenish") + (PAPER_BROADSHEET: Newspaper, 381.0, 578.0, "broadsheet" | "newspaper") + (PAPER_NEW_YORK_TIMES: Newspaper, 305.0, 559.0, "new-york-times" | "times") + + // ---------------------------------------------------------------------- // + // Books + + (PAPER_FOLIO_BOOK: Book, 304.8, 482.6, "book-folio") + (PAPER_QUARTO_BOOK: Book, 241.3, 304.8, "book-quarto") + (PAPER_OCTAVO_BOOK: Book, 152.4, 228.6, "book-octavo") + (PAPER_16_MO_BOOK: Book, 101.6, 171.45, "book-16mo") + (PAPER_32_MO_BOOK: Book, 88.9, 139.7, "book-32mo") + + // ---------------------------------------------------------------------- // + // Various + + (PAPER_ID_1: Base, 85.6, 53.98, "id-card" | "id-1" | "iso-7810-id-1" | + "eu-business-card" | "business-card") + (PAPER_US_BUSINESS_CARD: Base, 88.9, 50.8, "us-business-card") + (PAPER_JP_BUSINESS_CARD: Base, 91.0, 55.0, "jp-business-card") + (PAPER_CN_BUSINESS_CARD: Base, 90.0, 54.0, "cn-business-card") + (PAPER_A4_16_9: Base, 297.0, 167.0625, "presentation-16-9" | "presentation") + (PAPER_A4_4_3: Base, 280.0, 210.0, "presentation-4-3") + (PAPER_POSTCARD: Base, 152.4, 101.6, "postcard") +} diff --git a/src/problem.rs b/src/problem.rs deleted file mode 100644 index 3c2d3635d..000000000 --- a/src/problem.rs +++ /dev/null @@ -1,94 +0,0 @@ -//! Problems (errors / warnings) in _Typst_ documents. -//! -//! There are no fatal errors in _Typst_. The document will always compile and -//! yield a layout. However, this is a best effort process and bad things will -//! still generate errors and warnings. - -use serde::Serialize; -use crate::syntax::span::SpanVec; - - -/// A list of spanned problems. -pub type Problems = SpanVec; - -/// A problem that arose in parsing or layouting. -#[derive(Debug, Clone, Eq, PartialEq, Serialize)] -pub struct Problem { - /// How severe / important the problem is. - pub severity: Severity, - /// A message describing the problem. - pub message: String, -} - -/// How severe / important a problem is. -#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize)] -#[serde(rename_all = "camelCase")] -pub enum Severity { - /// Something in the code is not good. - Warning, - /// Something in the code is wrong! - Error, -} - -impl Problem { - /// Create a new problem from message and severity. - pub fn new(message: impl Into, severity: Severity) -> Self { - Self { message: message.into(), severity } - } -} - -/// Construct a problem with `Error` severity. -/// -/// ``` -/// # use typstc::error; -/// # use typstc::syntax::span::Span; -/// # use typstc::Feedback; -/// # let span = Span::ZERO; -/// # let mut feedback = Feedback::new(); -/// # let name = ""; -/// // Create formatted error values. -/// let error = error!("expected {}", name); -/// -/// // Create spanned errors. -/// let spanned = error!(span, "there is an error here"); -/// -/// // Create an error and directly add it to existing feedback. -/// error!(@feedback, span, "oh no!"); -/// ``` -#[macro_export] -macro_rules! error { - ($($tts:tt)*) => { - $crate::__impl_problem!($crate::problem::Severity::Error; $($tts)*) - }; -} - -/// Construct a problem with `Warning` severity. -/// -/// This works exactly like `error!`. See its documentation for more -/// information. -#[macro_export] -macro_rules! warning { - ($($tts:tt)*) => { - $crate::__impl_problem!($crate::problem::Severity::Warning; $($tts)*) - }; -} - -/// Backs the `error!` and `warning!` macros. -#[macro_export] -#[doc(hidden)] -macro_rules! __impl_problem { - ($severity:expr; @$feedback:expr, $($tts:tt)*) => { - $feedback.problems.push($crate::__impl_problem!($severity; $($tts)*)); - }; - - ($severity:expr; $fmt:literal $($tts:tt)*) => { - $crate::problem::Problem::new(format!($fmt $($tts)*), $severity) - }; - - ($severity:expr; $span:expr, $fmt:literal $($tts:tt)*) => { - $crate::syntax::span::Spanned::new( - $crate::__impl_problem!($severity; $fmt $($tts)*), - $span, - ) - }; -} diff --git a/src/style.rs b/src/style.rs index 075baa5ac..2c74adde4 100644 --- a/src/style.rs +++ b/src/style.rs @@ -2,8 +2,8 @@ use toddle::fallback; use toddle::query::{FallbackTree, FontVariant, FontStyle, FontWeight}; -use crate::size::{Size, Size2D, SizeBox, ValueBox, PSize}; - +use crate::length::{Length, Size, Margins, Value4, ScaleLength}; +use crate::paper::{Paper, PaperClass, PAPER_A4}; /// Defines properties of pages and text. #[derive(Debug, Default, Clone, PartialEq)] @@ -25,35 +25,35 @@ pub struct TextStyle { /// whether the next `*` adds or removes font weight. pub bolder: bool, /// The base font size. - pub base_font_size: Size, + pub base_font_size: Length, /// The font scale to apply on the base font size. - pub font_scale: f32, + pub font_scale: f64, /// The word spacing (as a multiple of the font size). - pub word_spacing_scale: f32, + pub word_spacing_scale: f64, /// The line spacing (as a multiple of the font size). - pub line_spacing_scale: f32, + pub line_spacing_scale: f64, /// The paragraphs spacing (as a multiple of the font size). - pub paragraph_spacing_scale: f32, + pub paragraph_spacing_scale: f64, } impl TextStyle { /// The scaled font size. - pub fn font_size(&self) -> Size { + pub fn font_size(&self) -> Length { self.base_font_size * self.font_scale } /// The absolute word spacing. - pub fn word_spacing(&self) -> Size { + pub fn word_spacing(&self) -> Length { self.word_spacing_scale * self.font_size() } /// The absolute line spacing. - pub fn line_spacing(&self) -> Size { + pub fn line_spacing(&self) -> Length { (self.line_spacing_scale - 1.0) * self.font_size() } /// The absolute paragraph spacing. - pub fn paragraph_spacing(&self) -> Size { + pub fn paragraph_spacing(&self) -> Length { (self.paragraph_spacing_scale - 1.0) * self.font_size() } } @@ -77,7 +77,7 @@ impl Default for TextStyle { weight: FontWeight(400), }, bolder: false, - base_font_size: Size::pt(11.0), + base_font_size: Length::pt(11.0), font_scale: 1.0, word_spacing_scale: 0.25, line_spacing_scale: 1.2, @@ -92,10 +92,10 @@ pub struct PageStyle { /// The class of this page. pub class: PaperClass, /// The width and height of the page. - pub dimensions: Size2D, + pub dimensions: Size, /// The amount of white space on each side. If a side is set to `None`, the /// default for the paper class is used. - pub margins: ValueBox>, + pub margins: Value4>, } impl PageStyle { @@ -103,17 +103,17 @@ impl PageStyle { pub fn new(paper: Paper) -> PageStyle { PageStyle { class: paper.class, - dimensions: paper.dimensions, - margins: ValueBox::with_all(None), + dimensions: paper.size(), + margins: Value4::with_all(None), } } /// The absolute margins. - pub fn margins(&self) -> SizeBox { + pub fn margins(&self) -> Margins { let dims = self.dimensions; let default = self.class.default_margins(); - SizeBox { + Margins { left: self.margins.left.unwrap_or(default.left).scaled(dims.x), top: self.margins.top.unwrap_or(default.top).scaled(dims.y), right: self.margins.right.unwrap_or(default.right).scaled(dims.x), @@ -127,270 +127,3 @@ impl Default for PageStyle { PageStyle::new(PAPER_A4) } } - -/// Details about a type of paper. -#[derive(Debug, Copy, Clone, PartialEq)] -pub struct Paper { - /// The kind of paper, which defines the default margins. - pub class: PaperClass, - /// The size of the paper. - pub dimensions: Size2D, -} - -impl Paper { - /// The paper with the given name. - pub fn from_name(name: &str) -> Option { - parse_paper(name) - } -} - -/// Paper classes define default margins for a class of related papers. -#[derive(Debug, Copy, Clone, Eq, PartialEq)] -#[allow(missing_docs)] -pub enum PaperClass { - Custom, - Base, - US, - Newspaper, - Book, -} - -impl PaperClass { - /// The default margins for this page class. - pub fn default_margins(self) -> ValueBox { - use PaperClass::*; - let values = |l, t, r, b| ValueBox::new( - PSize::Scaled(l), - PSize::Scaled(t), - PSize::Scaled(r), - PSize::Scaled(b), - ); - - match self { - Custom => values(0.1190, 0.0842, 0.1190, 0.0842), - Base => values(0.1190, 0.0842, 0.1190, 0.0842), - US => values(0.1760, 0.1092, 0.1760, 0.0910), - Newspaper => values(0.0455, 0.0587, 0.0455, 0.0294), - Book => values(0.1200, 0.0852, 0.1500, 0.0965), - } - } -} - -macro_rules! papers { - ($(($var:ident: $class:ident, $width:expr, $height: expr, $($pats:tt)*))*) => { - $(papers!(@$var, stringify!($($pats)*), $class, $width, $height);)* - - fn parse_paper(paper: &str) -> Option { - match paper.to_lowercase().as_str() { - $($($pats)* => Some($var),)* - _ => None, - } - } - }; - - (@$var:ident, $names:expr, $class:ident, $width:expr, $height:expr) => { - #[doc = "Paper with name `"] - #[doc = $names] - #[doc = "`."] - pub const $var: Paper = Paper { - dimensions: Size2D { - x: Size { points: 2.83465 * $width }, - y: Size { points: 2.83465 * $height }, - }, - class: PaperClass::$class, - }; - }; -} - -// All paper sizes in mm. -papers! { - // ---------------------------------------------------------------------- // - // ISO 216 - - // A Series - (PAPER_A0: Base, 841.0, 1189.0, "a0" | "poster") - (PAPER_A1: Base, 594.0, 841.0, "a1") - (PAPER_A2: Base, 420.0, 594.0, "a2") - (PAPER_A3: Base, 297.0, 420.0, "a3") - (PAPER_A4: Base, 210.0, 297.0, "a4") - (PAPER_A5: Base, 148.0, 210.0, "a5") - (PAPER_A6: Book, 105.0, 148.0, "a6") - (PAPER_A7: Base, 74.0, 105.0, "a7" | "iso-7810-id-2" | "id-2" | "visa" | "flyer") - (PAPER_A8: Base, 52.0, 74.0, "a8") - (PAPER_A9: Base, 37.0, 52.0, "a9") - (PAPER_A10: Base, 26.0, 37.0, "a10") - (PAPER_A11: Base, 18.0, 26.0, "a11") - - // B Series - (PAPER_B1: Base, 707.0, 1000.0, "b1" | "flipchart") - (PAPER_B2: Base, 500.0, 707.0, "b2") - (PAPER_B3: Base, 353.0, 500.0, "b3") - (PAPER_B4: Base, 250.0, 353.0, "b4" | "sheet-music") - (PAPER_B5: Book, 176.0, 250.0, "b5") - (PAPER_B6: Book, 125.0, 176.0, "b6" | "book") - (PAPER_B7: Base, 88.0, 125.0, "b7" | "passport" | "iso-7810-id-3" | "id-3") - (PAPER_B8: Base, 62.0, 88.0, "b8") - - // C Series - (PAPER_C3: Base, 324.0, 458.0, "c3") - (PAPER_C4: Base, 229.0, 324.0, "c4") - (PAPER_C5: Base, 162.0, 229.0, "c5") - (PAPER_C6: Base, 114.0, 162.0, "c6") - (PAPER_C7: Base, 81.0, 114.0, "c7") - (PAPER_C8: Base, 57.0, 81.0, "c8") - - // D Series (DIN extension to ISO) - (PAPER_D3: Base, 272.0, 385.0, "din-d3") - (PAPER_D4: Base, 192.0, 272.0, "din-d4") - (PAPER_D5: Base, 136.0, 192.0, "din-d5" | "dvd") - (PAPER_D6: Base, 96.0, 136.0, "din-d6") - (PAPER_D7: Base, 68.0, 96.0, "din-d7") - (PAPER_D8: Base, 48.0, 68.0, "din-d8") - - // Academically relevant SIS extensions - (PAPER_G5: Base, 169.0, 239.0, "sis-g5") - (PAPER_E5: Base, 115.0, 220.0, "sis-e5") - - // ---------------------------------------------------------------------- // - // Unites States - - // Customary - (PAPER_FOLIO: US, 210.0, 330.0, "folio" | "us-folio" | "us-f4") - (PAPER_LETTER: US, 216.0, 279.0, "letter" | "ansi-a" | - "american-quarto" | "carta") - (PAPER_LEGAL: US, 216.0, 356.0, "legal") - (PAPER_TABLOID: Newspaper, 279.0, 432.0, "tabloid" | "ansi-b") - (PAPER_LEDGER: Base, 432.0, 279.0, "ledger") - (PAPER_JUNIOR_LEGAL: US, 127.0, 203.0, "junior-legal" | "index-card") - (PAPER_HALF_LETTER: Base, 140.0, 216.0, "half-letter") - (PAPER_GOVERNMENT_LETTER: US, 203.0, 267.0, "government-letter") - (PAPER_GOVERNMENT_LEGAL: US, 216.0, 330.0, "government-legal" | "officio") - - // ANSI Extensions - (PAPER_ANSI_C: Base, 432.0, 559.0, "ansi-c") - (PAPER_ANSI_D: Base, 559.0, 864.0, "ansi-d") - (PAPER_ANSI_E: Base, 864.0, 1118.0, "ansi-e") - (PAPER_ENGINEERING_F: Base, 711.0, 1016.0, "engineering-f" | "engineering" | - "navfac" | "aerospace") - - // Architectural Paper - (PAPER_ARCH_A: Base, 229.0, 305.0, "arch-a" | "arch-1") - (PAPER_ARCH_B: Base, 305.0, 457.0, "arch-b" | "arch-2" | "extra-tabloide") - (PAPER_ARCH_C: Base, 457.0, 610.0, "arch-c" | "arch-3") - (PAPER_ARCH_D: Base, 610.0, 914.0, "arch-d" | "arch-4") - (PAPER_ARCH_E1: Base, 762.0, 1067.0, "arch-e1" | "arch-5") - (PAPER_ARCH_E: Base, 914.0, 1219.0, "arch-e" | "arch-6") - - // ---------------------------------------------------------------------- // - // Japan - - // JIS B Series - (PAPER_JIS_B0: Base, 1030.0, 1456.0, "jis-b0" | "jb0") - (PAPER_JIS_B1: Base, 728.0, 1030.0, "jis-b1" | "jb1") - (PAPER_JIS_B2: Base, 515.0, 728.0, "jis-b2" | "jb2") - (PAPER_JIS_B3: Base, 364.0, 515.0, "jis-b3" | "jb3") - (PAPER_JIS_B4: Base, 257.0, 364.0, "jis-b4" | "jb4") - (PAPER_JIS_B5: Base, 182.0, 257.0, "jis-b5" | "jb5") - (PAPER_JIS_B6: Base, 128.0, 182.0, "jis-b6" | "jb6") - (PAPER_JIS_B7: Base, 91.0, 128.0, "jis-b7" | "jb7") - (PAPER_JIS_B8: Base, 64.0, 91.0, "jis-b8" | "jb8") - (PAPER_JIS_B9: Base, 45.0, 64.0, "jis-b9" | "jb9") - (PAPER_JIS_B10: Base, 32.0, 45.0, "jis-b10" | "jb10") - (PAPER_JIS_B11: Base, 22.0, 32.0, "jis-b11" | "jb11") - - // Traditional - (PAPER_SHIROKU_BAN_4: Base, 264.0, 379.0, "shiroku-ban-4") - (PAPER_SHIROKU_BAN_5: Base, 189.0, 262.0, "shiroku-ban-5") - (PAPER_SHIROKU_BAN_6: Base, 127.0, 188.0, "shiroku-ban-6") - (PAPER_KIKU_4: Base, 227.0, 306.0, "kiku-4") - (PAPER_KIKU_5: Base, 151.0, 227.0, "kiku-5") - - // ---------------------------------------------------------------------- // - // China - - // Chinese D Series - (PAPER_SAC_D0: Base, 764.0, 1064.0, "sac-d0" | "cn-d0") - (PAPER_SAC_D1: Base, 532.0, 760.0, "sac-d1" | "cn-d1") - (PAPER_SAC_D2: Base, 380.0, 528.0, "sac-d2" | "cn-d2") - (PAPER_SAC_D3: Base, 264.0, 376.0, "sac-d3" | "cn-d3") - (PAPER_SAC_D4: Base, 188.0, 260.0, "sac-d4" | "cn-d4") - (PAPER_SAC_D5: Base, 130.0, 184.0, "sac-d5" | "cn-d5") - (PAPER_SAC_D6: Base, 92.0, 126.0, "sac-d6" | "cn-d6") - - // ---------------------------------------------------------------------- // - // United Kingdom Imperial (Assortment) - - (PAPER_MONARCH: Base, 184.0, 267.0, "monarch") - (PAPER_QUARTO: Base, 229.0, 279.0, "quarto" | "us-quarto") - (PAPER_UK_QUARTO: Base, 203.0, 254.0, "uk-quarto" | "imperial-quarto") - (PAPER_UK_FOOLSCAP: Base, 343.0, 432.0, "foolscap" | "us-foolscap") - (PAPER_FOOLSCAP: Base, 203.0, 330.0, "imperial-foolscap" | "uk-foolscap") - (PAPER_POTT: Base, 318.0, 381.0, "pott") - (PAPER_CROWN: Base, 318.0, 508.0, "crown") - (PAPER_PINCHED_POST: Base, 375.0, 470.0, "pinched-post") - (PAPER_POST: Base, 394.0, 489.0, "post") - (PAPER_LARGE_POST: Base, 419.0, 533.0, "large-post") - (PAPER_DEMY: Base, 445.0, 572.0, "demy") - (PAPER_ROYAL: Base, 508.0, 635.0, "royal") - (PAPER_DOUBLE_CROWN: Base, 508.0, 762.0, "double-crown" | "theatre") - (PAPER_ELEPHANT: Base, 584.0, 711.0, "elephant") - (PAPER_DOUBLE_ROYAL: Base, 635.0, 1016.0, "double-royal" | "rail") - (PAPER_QUAD_CROWN: Base, 762.0, 1016.0, "quad-crown" | "cinema") - - // ---------------------------------------------------------------------- // - // French Traditional (AFNOR) - - (PAPER_CLOCHE: Base, 300.0, 400.0, "cloche") - (PAPER_POT: Base, 310.0, 400.0, "pot" | "ecolier" | "écolier") - (PAPER_TELLIERE: Base, 340.0, 440.0, "telliere" | "tellière") - (PAPER_COURONNE_ECRITURE: Base, 360.0, 460.0, "couronne-ecriture" | - "couronne" | "couronne-écriture") - (PAPER_COURONNE_EDITION: Base, 370.0, 470.0, "couronne-edition" | - "couronne-édition") - (PAPER_ROBERTO: Base, 390.0, 500.0, "roberto") - (PAPER_ECU: Base, 400.0, 520.0, "ecu" | "écu") - (PAPER_COQUILLE: Base, 440.0, 560.0, "coquille") - (PAPER_CARRE: Base, 450.0, 560.0, "carre" | "carré") - (PAPER_CAVALIER: Base, 460.0, 620.0, "cavalier") - (PAPER_DEMI_RAISIN: Base, 325.0, 500.0, "demi-raisin") - (PAPER_RAISIN: Base, 500.0, 650.0, "raisin" | "dessin") - (PAPER_DOUBLE_RAISIN: Base, 650.0, 1000.0, "double-raisin") - (PAPER_JESUS: Base, 560.0, 760.0, "jesus" | "jésus") - (PAPER_SOLEIL: Base, 600.0, 800.0, "soleil") - (PAPER_COLOMBIER_AFFICHE: Base, 600.0, 800.0, "colombier-affiche" | "affiche") - (PAPER_COLOMBIER_COMMERCIAL: Base, 630.0, 900.0, "colombier-commercial") - (PAPER_PETIT_AIGLE: Base, 700.0, 940.0, "petit-aigle") - (PAPER_GRAND_AIGLE: Base, 750.0, 1060.0, "grand-aigle" | "napoleon") - (PAPER_GRAND_MONDE: Base, 900.0, 1260.0, "grand-monde") - (PAPER_UNIVERS: Base, 1000.0, 1300.0, "univers" | "universe") - - // ---------------------------------------------------------------------- // - // Newspaper - - (PAPER_COMPACT: Newspaper, 280.0, 430.0, "compact") - (PAPER_BERLINER: Newspaper, 315.0, 470.0, "berliner" | "midi") - (PAPER_RHENISH: Newspaper, 350.0, 520.0, "rhenish") - (PAPER_BROADSHEET: Newspaper, 381.0, 578.0, "broadsheet" | "newspaper") - (PAPER_NEW_YORK_TIMES: Newspaper, 305.0, 559.0, "new-york-times" | "times") - - // ---------------------------------------------------------------------- // - // Books - - (PAPER_FOLIO_BOOK: Book, 304.8, 482.6, "book-folio") - (PAPER_QUARTO_BOOK: Book, 241.3, 304.8, "book-quarto") - (PAPER_OCTAVO_BOOK: Book, 152.4, 228.6, "book-octavo") - (PAPER_16_MO_BOOK: Book, 101.6, 171.45, "book-16mo") - (PAPER_32_MO_BOOK: Book, 88.9, 139.7, "book-32mo") - - // ---------------------------------------------------------------------- // - // Various - - (PAPER_ID_1: Base, 85.6, 53.98, "id-card" | "id-1" | "iso-7810-id-1" | - "eu-business-card" | "business-card") - (PAPER_US_BUSINESS_CARD: Base, 88.9, 50.8, "us-business-card") - (PAPER_JP_BUSINESS_CARD: Base, 91.0, 55.0, "jp-business-card") - (PAPER_CN_BUSINESS_CARD: Base, 90.0, 54.0, "cn-business-card") - (PAPER_A4_16_9: Base, 297.0, 167.0625, "presentation-16-9" | "presentation") - (PAPER_A4_4_3: Base, 280.0, 210.0, "presentation-4-3") - (PAPER_POSTCARD: Base, 152.4, 101.6, "postcard") -} diff --git a/src/syntax/expr.rs b/src/syntax/expr.rs index a1b3fd627..d849366c9 100644 --- a/src/syntax/expr.rs +++ b/src/syntax/expr.rs @@ -6,13 +6,12 @@ use std::ops::Deref; use std::str::FromStr; use std::u8; -use crate::problem::Problems; -use crate::size::Size; +use crate::diagnostic::Diagnostics; +use crate::length::Length; use super::func::{Key, Value}; use super::span::{Span, Spanned}; use super::tokens::is_identifier; - /// An argument or return value. #[derive(Clone, PartialEq)] pub enum Expr { @@ -22,8 +21,8 @@ pub enum Expr { Str(String), /// A number: `1.2, 200%`. Number(f64), - /// A size: `2cm, 5.2in`. - Size(Size), + /// A length: `2cm, 5.2in`. + Length(Length), /// A bool: `true, false`. Bool(bool), /// A color value, including the alpha channel: `#f79143ff`. @@ -32,7 +31,7 @@ pub enum Expr { Tuple(Tuple), /// A named tuple: `cmyk(37.7, 0, 3.9, 1.1)`. NamedTuple(NamedTuple), - /// An object: `{ fit: false, size: 12pt }`. + /// An object: `{ fit: false, width: 12pt }`. Object(Object), /// An operator that negates the contained expression. Neg(Box>), @@ -54,7 +53,7 @@ impl Expr { Ident(_) => "identifier", Str(_) => "string", Number(_) => "number", - Size(_) => "size", + Length(_) => "length", Bool(_) => "bool", Color(_) => "color", Tuple(_) => "tuple", @@ -76,7 +75,7 @@ impl Debug for Expr { Ident(i) => i.fmt(f), Str(s) => s.fmt(f), Number(n) => n.fmt(f), - Size(s) => s.fmt(f), + Length(s) => s.fmt(f), Bool(b) => b.fmt(f), Color(c) => c.fmt(f), Tuple(t) => t.fmt(f), @@ -128,7 +127,7 @@ impl Debug for Ident { /// /// # Example /// ```typst -/// [box: background=#423abaff] +/// [page: background=#423abaff] /// ^^^^^^^^ /// ``` #[derive(Copy, Clone, Eq, PartialEq, Hash)] @@ -256,28 +255,28 @@ impl Tuple { } /// Extract (and remove) the first matching value and remove and generate - /// problems for all previous items that did not match. - pub fn get(&mut self, problems: &mut Problems) -> Option { + /// diagnostics for all previous items that did not match. + pub fn get(&mut self, diagnostics: &mut Diagnostics) -> Option { while !self.items.is_empty() { let expr = self.items.remove(0); let span = expr.span; match V::parse(expr) { Ok(output) => return Some(output), - Err(v) => problems.push(Spanned { v, span }), + Err(v) => diagnostics.push(Spanned { v, span }), } } None } /// Extract and return an iterator over all values that match and generate - /// problems for all items that do not match. - pub fn get_all<'a, V: Value>(&'a mut self, problems: &'a mut Problems) + /// diagnostics for all items that do not match. + pub fn get_all<'a, V: Value>(&'a mut self, diagnostics: &'a mut Diagnostics) -> impl Iterator + 'a { self.items.drain(..).filter_map(move |expr| { let span = expr.span; match V::parse(expr) { Ok(output) => Some(output), - Err(v) => { problems.push(Spanned { v, span }); None } + Err(v) => { diagnostics.push(Spanned { v, span }); None } } }) } @@ -351,13 +350,9 @@ impl Deref for NamedTuple { /// A key-value collection of identifiers and associated expressions. /// -/// The pairs themselves are not spanned, but the combined spans can easily be -/// retrieved by merging the spans of key and value as happening in -/// [`FuncArg::span`](super::func::FuncArg::span). -/// /// # Example /// ```typst -/// { fit: false, size: 12cm, items: (1, 2, 3) } +/// { fit: false, width: 12cm, items: (1, 2, 3) } /// ``` #[derive(Default, Clone, PartialEq)] pub struct Object { @@ -398,9 +393,9 @@ impl Object { /// /// Inserts an error if the value does not match. If the key is not /// contained, no error is inserted. - pub fn get(&mut self, problems: &mut Problems, key: &str) -> Option { + pub fn get(&mut self, diagnostics: &mut Diagnostics, key: &str) -> Option { let index = self.pairs.iter().position(|pair| pair.v.key.v.as_str() == key)?; - self.get_index::(problems, index) + self.get_index::(diagnostics, index) } /// Extract (and remove) a pair with a matching key and value. @@ -409,12 +404,12 @@ impl Object { /// found, no error is inserted. pub fn get_with_key( &mut self, - problems: &mut Problems, + diagnostics: &mut Diagnostics, ) -> Option<(K, V)> { for (index, pair) in self.pairs.iter().enumerate() { let key = Spanned { v: pair.v.key.v.as_str(), span: pair.v.key.span }; if let Some(key) = K::parse(key) { - return self.get_index::(problems, index).map(|value| (key, value)); + return self.get_index::(diagnostics, index).map(|value| (key, value)); } } None @@ -425,7 +420,7 @@ impl Object { /// Inserts errors for values that do not match. pub fn get_all<'a, K: Key, V: Value>( &'a mut self, - problems: &'a mut Problems, + diagnostics: &'a mut Diagnostics, ) -> impl Iterator + 'a { let mut index = 0; std::iter::from_fn(move || { @@ -434,7 +429,7 @@ impl Object { let key = Spanned { v: key.v.as_str(), span: key.span }; Some(if let Some(key) = K::parse(key) { - self.get_index::(problems, index).map(|v| (key, v)) + self.get_index::(diagnostics, index).map(|v| (key, v)) } else { index += 1; None @@ -454,20 +449,20 @@ impl Object { /// ``` pub fn get_all_spanned<'a, K: Key + 'a, V: Value + 'a>( &'a mut self, - problems: &'a mut Problems, + diagnostics: &'a mut Diagnostics, ) -> impl Iterator> + 'a { - self.get_all::, Spanned>(problems) + self.get_all::, Spanned>(diagnostics) .map(|(k, v)| Spanned::new((k.v, v.v), Span::merge(k.span, v.span))) } /// Extract the argument at the given index and insert an error if the value /// does not match. - fn get_index(&mut self, problems: &mut Problems, index: usize) -> Option { + fn get_index(&mut self, diagnostics: &mut Diagnostics, index: usize) -> Option { let expr = self.pairs.remove(index).v.value; let span = expr.span; match V::parse(expr) { Ok(output) => Some(output), - Err(v) => { problems.push(Spanned { v, span }); None } + Err(v) => { diagnostics.push(Spanned { v, span }); None } } } diff --git a/src/syntax/func/keys.rs b/src/syntax/func/keys.rs index b8f142eed..558667ddf 100644 --- a/src/syntax/func/keys.rs +++ b/src/syntax/func/keys.rs @@ -7,7 +7,6 @@ use super::*; use self::AxisKey::*; use self::PaddingKey::*; - /// Key types are used to extract keyword arguments from /// [`Objects`](crate::syntax::expr::Object). They represent the key part of a /// keyword argument. diff --git a/src/syntax/func/maps.rs b/src/syntax/func/maps.rs index 44d8e1aa9..2ac702233 100644 --- a/src/syntax/func/maps.rs +++ b/src/syntax/func/maps.rs @@ -1,18 +1,17 @@ //! Deduplicating maps and keys for argument parsing. -use crate::problem::Problems; +use crate::diagnostic::Diagnostics; use crate::layout::prelude::*; -use crate::size::{PSize, ValueBox}; +use crate::length::{ScaleLength, Value4}; use crate::syntax::span::Spanned; use super::keys::*; use super::values::*; use super::*; - /// A map which deduplicates redundant arguments. /// /// Whenever a duplicate argument is inserted into the map, through the -/// functions `from_iter`, `insert` or `extend` an problems is added to the error +/// functions `from_iter`, `insert` or `extend` an diagnostics is added to the error /// list that needs to be passed to those functions. /// /// All entries need to have span information to enable the error reporting. @@ -28,27 +27,27 @@ impl DedupMap where K: Eq { } /// Create a new map from an iterator of spanned keys and values. - pub fn from_iter(problems: &mut Problems, iter: I) -> DedupMap + pub fn from_iter(diagnostics: &mut Diagnostics, iter: I) -> DedupMap where I: IntoIterator> { let mut map = DedupMap::new(); - map.extend(problems, iter); + map.extend(diagnostics, iter); map } /// Add a spanned key-value pair. - pub fn insert(&mut self, problems: &mut Problems, entry: Spanned<(K, V)>) { + pub fn insert(&mut self, diagnostics: &mut Diagnostics, entry: Spanned<(K, V)>) { if self.map.iter().any(|e| e.v.0 == entry.v.0) { - problems.push(error!(entry.span, "duplicate argument")); + diagnostics.push(error!(entry.span, "duplicate argument")); } else { self.map.push(entry); } } /// Add multiple spanned key-value pairs. - pub fn extend(&mut self, problems: &mut Problems, items: I) + pub fn extend(&mut self, diagnostics: &mut Diagnostics, items: I) where I: IntoIterator> { for item in items.into_iter() { - self.insert(problems, item); + self.insert(diagnostics, item); } } @@ -71,15 +70,15 @@ impl DedupMap where K: Eq { } /// Create a new map where keys and values are mapped to new keys and - /// values. When the mapping introduces new duplicates, problems are + /// values. When the mapping introduces new duplicates, diagnostics are /// generated. - pub fn dedup(&self, problems: &mut Problems, mut f: F) -> DedupMap + pub fn dedup(&self, diagnostics: &mut Diagnostics, mut f: F) -> DedupMap where F: FnMut(&K, &V) -> (K2, V2), K2: Eq { let mut map = DedupMap::new(); for Spanned { v: (key, value), span } in self.map.iter() { let (key, value) = f(key, value); - map.insert(problems, Spanned { v: (key, value), span: *span }); + map.insert(diagnostics, Spanned { v: (key, value), span: *span }); } map @@ -98,21 +97,21 @@ pub struct AxisMap(DedupMap); impl AxisMap { /// Parse an axis map from the object. pub fn parse( - problems: &mut Problems, + diagnostics: &mut Diagnostics, object: &mut Object, ) -> AxisMap where K: Key + Into { let values: Vec<_> = object - .get_all_spanned::(problems) + .get_all_spanned::(diagnostics) .map(|s| s.map(|(k, v)| (k.into(), v))) .collect(); - AxisMap(DedupMap::from_iter(problems, values)) + AxisMap(DedupMap::from_iter(diagnostics, values)) } /// Deduplicate from specific or generic to just specific axes. - pub fn dedup(&self, problems: &mut Problems, axes: LayoutAxes) -> DedupMap + pub fn dedup(&self, diagnostics: &mut Diagnostics, axes: LayoutAxes) -> DedupMap where V: Clone { - self.0.dedup(problems, |key, val| (key.to_specific(axes), val.clone())) + self.0.dedup(diagnostics, |key, val| (key.to_specific(axes), val.clone())) } } @@ -124,23 +123,23 @@ pub struct PosAxisMap(DedupMap); impl PosAxisMap { /// Parse a positional/axis map from the function arguments. pub fn parse( - problems: &mut Problems, + diagnostics: &mut Diagnostics, args: &mut FuncArgs, ) -> PosAxisMap where K: Key + Into { let mut map = DedupMap::new(); for &key in &[PosAxisKey::First, PosAxisKey::Second] { - if let Some(Spanned { v, span }) = args.pos.get::>(problems) { - map.insert(problems, Spanned { v: (key, v), span }) + if let Some(Spanned { v, span }) = args.pos.get::>(diagnostics) { + map.insert(diagnostics, Spanned { v: (key, v), span }) } } let keywords: Vec<_> = args.key - .get_all_spanned::(problems) + .get_all_spanned::(diagnostics) .map(|s| s.map(|(k, v)| (PosAxisKey::Keyword(k.into()), v))) .collect(); - map.extend(problems, keywords); + map.extend(diagnostics, keywords); PosAxisMap(map) } @@ -149,7 +148,7 @@ impl PosAxisMap { /// or specific axes to just generic axes. pub fn dedup( &self, - problems: &mut Problems, + diagnostics: &mut Diagnostics, axes: LayoutAxes, mut f: F, ) -> DedupMap @@ -157,7 +156,7 @@ impl PosAxisMap { F: FnMut(&V) -> Option, V: Clone, { - self.0.dedup(problems, |key, val| { + self.0.dedup(diagnostics, |key, val| { (match key { PosAxisKey::First => f(val).unwrap_or(GenericAxis::Primary), PosAxisKey::Second => f(val).unwrap_or(GenericAxis::Secondary), @@ -171,24 +170,24 @@ impl PosAxisMap { /// A map for storing padding given for a combination of all sides, opposing /// sides or single sides. #[derive(Debug, Clone, PartialEq)] -pub struct PaddingMap(DedupMap, Option>); +pub struct PaddingMap(DedupMap, Option>); impl PaddingMap { /// Parse a padding map from the function arguments. - pub fn parse(problems: &mut Problems, args: &mut FuncArgs) -> PaddingMap { + pub fn parse(diagnostics: &mut Diagnostics, args: &mut FuncArgs) -> PaddingMap { let mut map = DedupMap::new(); - let all = args.pos.get::>>(problems); + let all = args.pos.get::>>(diagnostics); if let Some(Spanned { v, span }) = all { - map.insert(problems, Spanned { v: (PaddingKey::All, v.into()), span }); + map.insert(diagnostics, Spanned { v: (PaddingKey::All, v.into()), span }); } let paddings: Vec<_> = args.key - .get_all_spanned::, Defaultable>(problems) + .get_all_spanned::, Defaultable>(diagnostics) .map(|s| s.map(|(k, v)| (k, v.into()))) .collect(); - map.extend(problems, paddings); + map.extend(diagnostics, paddings); PaddingMap(map) } @@ -196,13 +195,13 @@ impl PaddingMap { /// Apply the specified padding on a value box of optional, scalable sizes. pub fn apply( &self, - problems: &mut Problems, + diagnostics: &mut Diagnostics, axes: LayoutAxes, - padding: &mut ValueBox> + padding: &mut Value4> ) { use PaddingKey::*; - let map = self.0.dedup(problems, |key, &val| { + let map = self.0.dedup(diagnostics, |key, &val| { (match key { All => All, Both(axis) => Both(axis.to_specific(axes)), diff --git a/src/syntax/func/mod.rs b/src/syntax/func/mod.rs index 4228488d9..c26317275 100644 --- a/src/syntax/func/mod.rs +++ b/src/syntax/func/mod.rs @@ -1,7 +1,7 @@ //! Primitives for argument parsing in library functions. use std::iter::FromIterator; -use crate::problem::{Problem, Problems}; +use crate::diagnostic::{Diagnostic, Diagnostics}; use super::expr::{Expr, Ident, Tuple, Object, Pair}; use super::span::{Span, Spanned}; @@ -85,13 +85,13 @@ pub enum FuncArg { pub trait OptionExt: Sized { /// Add an error about a missing argument `arg` with the given span if the /// option is `None`. - fn or_missing(self, problems: &mut Problems, span: Span, arg: &str) -> Self; + fn or_missing(self, diagnostics: &mut Diagnostics, span: Span, arg: &str) -> Self; } impl OptionExt for Option { - fn or_missing(self, problems: &mut Problems, span: Span, arg: &str) -> Self { + fn or_missing(self, diagnostics: &mut Diagnostics, span: Span, arg: &str) -> Self { if self.is_none() { - problems.push(error!(span, "missing argument: {}", arg)); + diagnostics.push(error!(span, "missing argument: {}", arg)); } self } diff --git a/src/syntax/func/values.rs b/src/syntax/func/values.rs index 7a1aa9125..3269f8e94 100644 --- a/src/syntax/func/values.rs +++ b/src/syntax/func/values.rs @@ -4,13 +4,12 @@ use std::fmt::{self, Display, Formatter}; use toddle::query::{FontStyle, FontWeight}; use crate::layout::prelude::*; -use crate::size::{Size, ScaleSize}; -use crate::style::Paper; +use crate::length::{Length, ScaleLength}; +use crate::paper::Paper; use super::*; use self::AlignmentValue::*; - /// Value types are used to extract the values of positional and keyword /// arguments from [`Tuples`](crate::syntax::expr::Tuple) and /// [`Objects`](crate::syntax::expr::Object). They represent the value part of @@ -24,14 +23,14 @@ use self::AlignmentValue::*; /// An implementation for `bool` might look as follows: /// ``` /// # use typstc::error; -/// # use typstc::problem::Problem; +/// # use typstc::diagnostic::Diagnostic; /// # use typstc::syntax::expr::Expr; /// # use typstc::syntax::func::Value; /// # use typstc::syntax::span::Spanned; /// # struct Bool; /* /// impl Value for bool { /// # */ impl Value for Bool { -/// fn parse(expr: Spanned) -> Result { +/// fn parse(expr: Spanned) -> Result { /// match expr.v { /// # /* /// Expr::Bool(b) => Ok(b), @@ -44,11 +43,11 @@ use self::AlignmentValue::*; pub trait Value: Sized { /// Parse an expression into this value or return an error if the expression /// is valid for this value type. - fn parse(expr: Spanned) -> Result; + fn parse(expr: Spanned) -> Result; } impl Value for Spanned { - fn parse(expr: Spanned) -> Result { + fn parse(expr: Spanned) -> Result { let span = expr.span; V::parse(expr).map(|v| Spanned { v, span }) } @@ -58,7 +57,7 @@ impl Value for Spanned { macro_rules! value { ($type:ty, $name:expr, $($p:pat => $r:expr),* $(,)?) => { impl Value for $type { - fn parse(expr: Spanned) -> Result { + fn parse(expr: Spanned) -> Result { #[allow(unreachable_patterns)] match expr.v { $($p => Ok($r)),*, @@ -77,13 +76,13 @@ value!(Ident, "identifier", Expr::Ident(i) => i); value!(String, "string", Expr::Str(s) => s); value!(f64, "number", Expr::Number(n) => n); value!(bool, "bool", Expr::Bool(b) => b); -value!(Size, "size", Expr::Size(s) => s); +value!(Length, "length", Expr::Length(s) => s); value!(Tuple, "tuple", Expr::Tuple(t) => t); value!(Object, "object", Expr::Object(o) => o); -value!(ScaleSize, "number or size", - Expr::Size(size) => ScaleSize::Absolute(size), - Expr::Number(scale) => ScaleSize::Scaled(scale as f32), +value!(ScaleLength, "number or length", + Expr::Length(length) => ScaleLength::Absolute(length), + Expr::Number(scale) => ScaleLength::Scaled(scale as f64), ); /// A value type that matches [`Expr::Ident`] and [`Expr::Str`] and implements @@ -108,20 +107,20 @@ impl From for String { /// # Example /// ``` /// # use typstc::syntax::func::{FuncArgs, Defaultable}; -/// # use typstc::size::Size; +/// # use typstc::length::Length; /// # let mut args = FuncArgs::new(); /// # let mut errors = vec![]; -/// args.key.get::>(&mut errors, "size"); +/// args.key.get::>(&mut errors, "length"); /// ``` /// This will yield. /// ```typst -/// [func: size=default] => None -/// [func: size=2cm] => Some(Size::cm(2.0)) +/// [func: length=default] => None +/// [func: length=2cm] => Some(Length::cm(2.0)) /// ``` pub struct Defaultable(pub Option); impl Value for Defaultable { - fn parse(expr: Spanned) -> Result { + fn parse(expr: Spanned) -> Result { Ok(Defaultable(match expr.v { Expr::Ident(ident) if ident.as_str() == "default" => None, _ => Some(V::parse(expr)?) @@ -136,7 +135,7 @@ impl From> for Option { } impl Value for FontStyle { - fn parse(expr: Spanned) -> Result { + fn parse(expr: Spanned) -> Result { FontStyle::from_name(Ident::parse(expr)?.as_str()) .ok_or_else(|| error!("invalid font style")) } @@ -145,7 +144,7 @@ impl Value for FontStyle { /// The additional boolean specifies whether a number was clamped into the range /// 100 - 900 to make it a valid font weight. impl Value for (FontWeight, bool) { - fn parse(expr: Spanned) -> Result { + fn parse(expr: Spanned) -> Result { match expr.v { Expr::Number(weight) => { let weight = weight.round(); @@ -170,14 +169,14 @@ impl Value for (FontWeight, bool) { } impl Value for Paper { - fn parse(expr: Spanned) -> Result { + fn parse(expr: Spanned) -> Result { Paper::from_name(Ident::parse(expr)?.as_str()) .ok_or_else(|| error!("invalid paper type")) } } impl Value for Direction { - fn parse(expr: Spanned) -> Result { + fn parse(expr: Spanned) -> Result { Ok(match Ident::parse(expr)?.as_str() { "left-to-right" | "ltr" | "LTR" => LeftToRight, "right-to-left" | "rtl" | "RTL" => RightToLeft, @@ -250,7 +249,7 @@ impl AlignmentValue { } impl Value for AlignmentValue { - fn parse(expr: Spanned) -> Result { + fn parse(expr: Spanned) -> Result { Ok(match Ident::parse(expr)?.as_str() { "origin" => Align(Origin), "center" => Align(Center), diff --git a/src/syntax/mod.rs b/src/syntax/mod.rs index f7321c77e..b67d8cd75 100644 --- a/src/syntax/mod.rs +++ b/src/syntax/mod.rs @@ -20,7 +20,6 @@ pub_use_mod!(scope); pub_use_mod!(parsing); pub_use_mod!(tokens); - /// Represents a parsed piece of source that can be layouted and in the future /// also be queried for information used for refactorings, autocomplete, etc. #[async_trait(?Send)] @@ -94,6 +93,9 @@ impl PartialEq for Node { } } +/// A list of spanned decorations. +pub type Decorations = SpanVec; + /// Decorations for semantic syntax highlighting. #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Serialize)] #[serde(rename_all = "camelCase")] @@ -110,7 +112,6 @@ pub enum Decoration { /// ^^^^^^ /// ``` InvalidFuncName, - /// A key of a keyword argument: /// ```typst /// [box: width=5cm] @@ -123,7 +124,6 @@ pub enum Decoration { /// ^^^^ ^^^^^ /// ``` ObjectKey, - /// An italic word. Italic, /// A bold word. diff --git a/src/syntax/parsing.rs b/src/syntax/parsing.rs index a0d9c4e45..a81511251 100644 --- a/src/syntax/parsing.rs +++ b/src/syntax/parsing.rs @@ -4,9 +4,12 @@ use std::str::FromStr; use super::expr::*; use super::func::{FuncCall, FuncHeader, FuncArgs, FuncArg}; -use super::span::{Position, Span, Spanned}; +use super::span::{Pos, Span, Spanned}; use super::*; +/// A function which parses a function call into a model. +pub type CallParser = dyn Fn(FuncCall, &ParseState) -> Pass>; + /// The state which can influence how a string of source code is parsed. /// /// Parsing is pure - when passed in the same state and source code, the output @@ -22,7 +25,7 @@ pub struct ParseState { /// `offset` position. This is used to make spans of a function body relative to /// the start of the function as a whole as opposed to the start of the /// function's body. -pub fn parse(src: &str, offset: Position, state: &ParseState) -> Pass { +pub fn parse(src: &str, offset: Pos, state: &ParseState) -> Pass { let mut model = SyntaxModel::new(); let mut feedback = Feedback::new(); @@ -102,7 +105,7 @@ impl<'s> FuncParser<'s> { state, // Start at column 1 because the opening bracket is also part of // the function, but not part of the `header` string. - tokens: Tokens::new(header, Position::new(0, 1), TokenMode::Header), + tokens: Tokens::new(header, Pos::new(0, 1), TokenMode::Header), peeked: None, body, feedback: Feedback::new(), @@ -127,7 +130,7 @@ impl<'s> FuncParser<'s> { } }; - self.feedback.decos.push(Spanned::new(deco, header.name.span)); + self.feedback.decorations.push(Spanned::new(deco, header.name.span)); (parser, header) } else { // Parse the body with the fallback parser even when the header is @@ -186,7 +189,7 @@ impl<'s> FuncParser<'s> { self.skip_white(); let key = ident; - self.feedback.decos.push( + self.feedback.decorations.push( Spanned::new(Decoration::ArgumentKey, key.span) ); @@ -325,7 +328,7 @@ impl FuncParser<'_> { } Token::ExprNumber(n) => self.eat_span(Expr::Number(n)), - Token::ExprSize(s) => self.eat_span(Expr::Size(s)), + Token::ExprLength(s) => self.eat_span(Expr::Length(s)), Token::ExprBool(b) => self.eat_span(Expr::Bool(b)), Token::ExprHex(s) => { if let Ok(color) = RgbaColor::from_str(s) { @@ -423,7 +426,7 @@ impl FuncParser<'_> { continue; } - self.feedback.decos.push( + self.feedback.decorations.push( Spanned::new(Decoration::ObjectKey, key.span) ); @@ -464,7 +467,7 @@ impl FuncParser<'_> { } } - fn expect_at(&mut self, token: Token<'_>, pos: Position) -> bool { + fn expect_at(&mut self, token: Token<'_>, pos: Pos) -> bool { if self.check(token) { self.eat(); true @@ -485,11 +488,11 @@ impl FuncParser<'_> { } } - fn expected_at(&mut self, thing: &str, pos: Position) { + fn expected_at(&mut self, thing: &str, pos: Pos) { error!(@self.feedback, Span::at(pos), "expected {}", thing); } - fn expected_found_or_at(&mut self, thing: &str, pos: Position) { + fn expected_found_or_at(&mut self, thing: &str, pos: Pos) { if self.eof() { self.expected_at(thing, pos) } else { @@ -544,7 +547,7 @@ impl<'s> FuncParser<'s> { self.peek().is_none() } - fn pos(&self) -> Position { + fn pos(&self) -> Pos { self.peeked.flatten() .map(|s| s.span.start) .unwrap_or_else(|| self.tokens.pos()) @@ -604,13 +607,13 @@ fn unescape_raw(raw: &str) -> Vec { #[cfg(test)] #[allow(non_snake_case)] mod tests { - use crate::size::Size; + use crate::length::Length; use super::super::test::{check, DebugFn}; use super::super::func::Value; use super::*; use Decoration::*; - use Expr::{Number as Num, Size as Sz, Bool}; + use Expr::{Number as Num, Length as Len, Bool}; use Node::{ Space as S, ToggleItalic as Italic, ToggleBolder as Bold, Parbreak, Linebreak, @@ -625,7 +628,7 @@ mod tests { p!($source => [$($model)*], []); }; - ($source:expr => [$($model:tt)*], [$($problems:tt)*] $(, [$($decos:tt)*])? $(,)?) => { + ($source:expr => [$($model:tt)*], [$($diagnostics:tt)*] $(, [$($decos:tt)*])? $(,)?) => { let mut scope = Scope::new::(); scope.add::("f"); scope.add::("n"); @@ -633,25 +636,25 @@ mod tests { scope.add::("val"); let state = ParseState { scope }; - let pass = parse($source, Position::ZERO, &state); + let pass = parse($source, Pos::ZERO, &state); // Test model. let (exp, cmp) = span_vec![$($model)*]; check($source, exp, pass.output.nodes, cmp); - // Test problems. - let (exp, cmp) = span_vec![$($problems)*]; + // Test diagnostics. + let (exp, cmp) = span_vec![$($diagnostics)*]; let exp = exp.into_iter() .map(|s: Spanned<&str>| s.map(|e| e.to_string())) .collect::>(); - let found = pass.feedback.problems.into_iter() + let found = pass.feedback.diagnostics.into_iter() .map(|s| s.map(|e| e.message)) .collect::>(); check($source, exp, found, cmp); // Test decos. $(let (exp, cmp) = span_vec![$($decos)*]; - check($source, exp, pass.feedback.decos, cmp);)? + check($source, exp, pass.feedback.decorations, cmp);)? }; } @@ -664,7 +667,6 @@ mod tests { fn Id(text: &str) -> Expr { Expr::Ident(Ident(text.to_string())) } fn Str(text: &str) -> Expr { Expr::Str(text.to_string()) } - fn Pt(points: f32) -> Expr { Expr::Size(Size::pt(points)) } fn Color(r: u8, g: u8, b: u8, a: u8) -> Expr { Expr::Color(RgbaColor::new(r, g, b, a)) } fn ColorStr(color: &str) -> Expr { Expr::Color(RgbaColor::from_str(color).expect("invalid test color")) } fn ColorHealed() -> Expr { Expr::Color(RgbaColor::new_healed(0, 0, 0, 255)) } @@ -878,8 +880,8 @@ mod tests { pval!("name" => (Id("name"))); pval!("\"hi\"" => (Str("hi"))); pval!("3.14" => (Num(3.14))); - pval!("4.5cm" => (Sz(Size::cm(4.5)))); - pval!("12e1pt" => (Pt(12e1))); + pval!("4.5cm" => (Len(Length::cm(4.5)))); + pval!("12e1pt" => (Len(Length::pt(12e1)))); pval!("#f7a20500" => (ColorStr("f7a20500"))); pval!("\"a\n[]\\\"string\"" => (Str("a\n[]\"string"))); @@ -890,10 +892,10 @@ mod tests { pval!("(hi)" => (Id("hi"))); // Math. - pval!("3.2in + 6pt" => (Add(Sz(Size::inches(3.2)), Sz(Size::pt(6.0))))); + pval!("3.2in + 6pt" => (Add(Len(Length::inches(3.2)), Len(Length::pt(6.0))))); pval!("5 - 0.01" => (Sub(Num(5.0), Num(0.01)))); - pval!("(3mm * 2)" => (Mul(Sz(Size::mm(3.0)), Num(2.0)))); - pval!("12e-3cm/1pt" => (Div(Sz(Size::cm(12e-3)), Sz(Size::pt(1.0))))); + pval!("(3mm * 2)" => (Mul(Len(Length::mm(3.0)), Num(2.0)))); + pval!("12e-3cm/1pt" => (Div(Len(Length::cm(12e-3)), Len(Length::pt(1.0))))); // Unclosed string. p!("[val: \"hello]" => [func!("val": (Str("hello]")), {})], [ @@ -912,26 +914,24 @@ mod tests { fn parse_complex_mathematical_expressions() { // Valid expressions. pval!("(3.2in + 6pt)*(5/2-1)" => (Mul( - Add(Sz(Size::inches(3.2)), Sz(Size::pt(6.0))), + Add(Len(Length::inches(3.2)), Len(Length::pt(6.0))), Sub(Div(Num(5.0), Num(2.0)), Num(1.0)) ))); pval!("(6.3E+2+4* - 3.2pt)/2" => (Div( - Add(Num(6.3e2),Mul(Num(4.0), Neg(Pt(3.2)))), + Add(Num(6.3e2), Mul(Num(4.0), Neg(Len(Length::pt(3.2))))), Num(2.0) ))); // Associativity of multiplication and division. - p!("[val: 3/4*5]" => - [func!("val": (Mul(Div(Num(3.0), Num(4.0)), Num(5.0))), {})] - ); + pval!("3/4*5" => (Mul(Div(Num(3.0), Num(4.0)), Num(5.0)))); // Invalid expressions. - p!("[val: 4pt--]" => [func!("val": (Pt(4.0)))], [ + p!("[val: 4pt--]" => [func!("val": (Len(Length::pt(4.0))))], [ (0:10, 0:11, "dangling minus"), (0:6, 0:10, "missing right summand") ]); p!("[val: 3mm+4pt*]" => - [func!("val": (Add(Sz(Size::mm(3.0)), Pt(4.0))))], + [func!("val": (Add(Len(Length::mm(3.0)), Len(Length::pt(4.0)))))], [(0:10, 0:14, "missing right factor")], ); } @@ -976,7 +976,7 @@ mod tests { // Nested tuples. pval!("css(1pt, rgb(90, 102, 254), \"solid\")" => (named_tuple!( "css", - Pt(1.0), + Len(Length::pt(1.0)), named_tuple!("rgb", Num(90.0), Num(102.0), Num(254.0)), Str("solid"), ))); @@ -1012,7 +1012,7 @@ mod tests { // Missing key. p!("[val: {,}]" => [val()], [(0:7, 0:8, "expected key, found comma")]); - p!("[val: { 12pt }]" => [val()], [(0:8, 0:12, "expected key, found size")]); + p!("[val: { 12pt }]" => [val()], [(0:8, 0:12, "expected key, found length")]); p!("[val: { : }]" => [val()], [(0:8, 0:9, "expected key, found colon")]); // Missing colon. @@ -1053,7 +1053,7 @@ mod tests { Num(1.0), object!( "ab" => tuple!(), - "d" => tuple!(Num(3.0), Pt(14.0)), + "d" => tuple!(Num(3.0), Len(Length::pt(14.0))), ), ), Bool(false), @@ -1085,7 +1085,7 @@ mod tests { #[test] fn parse_multiple_mixed_arguments() { p!("[val: 12pt, key=value]" => - [func!("val": (Pt(12.0)), { "key" => Id("value") })], [], + [func!("val": (Len(Length::pt(12.0))), { "key" => Id("value") })], [], [(0:12, 0:15, ArgumentKey), (0:1, 0:4, ValidFuncName)], ); pval!("a , x=\"b\" , c" => (Id("a"), Id("c")), { "x" => Str("b"), }); @@ -1144,7 +1144,7 @@ mod tests { fn parse_invalid_commas() { // Missing commas. p!("[val: 1pt 1]" => - [func!("val": (Pt(1.0), Num(1.0)), {})], + [func!("val": (Len(Length::pt(1.0)), Num(1.0)), {})], [(0:9, 0:9, "expected comma")], ); p!(r#"[val: _"s"]"# => diff --git a/src/syntax/scope.rs b/src/syntax/scope.rs index 74c64280b..c63508368 100644 --- a/src/syntax/scope.rs +++ b/src/syntax/scope.rs @@ -3,16 +3,14 @@ use std::collections::HashMap; use std::fmt::{self, Debug, Formatter}; -use crate::Pass; use crate::func::ParseFunc; -use super::func::FuncCall; -use super::parsing::ParseState; +use super::parsing::CallParser; use super::Model; /// A map from identifiers to function parsers. pub struct Scope { - parsers: HashMap>, - fallback: Box, + parsers: HashMap>, + fallback: Box, } impl Scope { @@ -22,7 +20,7 @@ impl Scope { where F: ParseFunc + Model + 'static { Scope { parsers: HashMap::new(), - fallback: parser::(()), + fallback: make_parser::(()), } } @@ -43,17 +41,17 @@ impl Scope { where F: ParseFunc + Model + 'static { self.parsers.insert( name.to_string(), - parser::(metadata), + make_parser::(metadata), ); } /// Return the parser with the given name if there is one. - pub fn get_parser(&self, name: &str) -> Option<&Parser> { + pub fn get_parser(&self, name: &str) -> Option<&CallParser> { self.parsers.get(name).map(AsRef::as_ref) } /// Return the fallback parser. - pub fn get_fallback_parser(&self) -> &Parser { + pub fn get_fallback_parser(&self) -> &CallParser { &*self.fallback } } @@ -66,11 +64,7 @@ impl Debug for Scope { } } -/// A function which parses the source of a function into a model type which -/// implements [`Model`]. -type Parser = dyn Fn(FuncCall, &ParseState) -> Pass>; - -fn parser(metadata: ::Meta) -> Box +fn make_parser(metadata: ::Meta) -> Box where F: ParseFunc + Model + 'static { Box::new(move |f, s| { F::parse(f, s, metadata.clone()) diff --git a/src/syntax/span.rs b/src/syntax/span.rs index c8e2cddb2..19562fb1d 100644 --- a/src/syntax/span.rs +++ b/src/syntax/span.rs @@ -4,16 +4,22 @@ use std::fmt::{self, Debug, Formatter}; use std::ops::{Add, Sub}; use serde::Serialize; +/// Span offsetting. +pub trait Offset { + /// Offset all spans contained in `Self` by the given position. + fn offset(self, by: Pos) -> Self; +} + /// A vector of spanned values of type `T`. pub type SpanVec = Vec>; -/// [Offset](Span::offset) all spans in a vector of spanned things by a start -/// position. -pub fn offset_spans( - vec: SpanVec, - start: Position, -) -> impl Iterator> { - vec.into_iter().map(move |s| s.map_span(|span| span.offset(start))) +impl Offset for SpanVec { + fn offset(mut self, by: Pos) -> Self { + for spanned in &mut self { + spanned.span = spanned.span.offset(by); + } + self + } } /// A value with the span it corresponds to in the source code. @@ -53,6 +59,12 @@ impl Spanned { } } +impl Offset for Spanned { + fn offset(self, by: Pos) -> Self { + self.map_span(|span| span.offset(by)) + } +} + impl Debug for Spanned { fn fmt(&self, f: &mut Formatter) -> fmt::Result { self.v.fmt(f)?; @@ -68,20 +80,25 @@ impl Debug for Spanned { #[derive(Copy, Clone, Eq, PartialEq, Hash, Serialize)] pub struct Span { /// The inclusive start position. - pub start: Position, + pub start: Pos, /// The inclusive end position. - pub end: Position, + pub end: Pos, } impl Span { /// The zero span. - pub const ZERO: Span = Span { start: Position::ZERO, end: Position::ZERO }; + pub const ZERO: Span = Span { start: Pos::ZERO, end: Pos::ZERO }; /// Create a new span from start and end positions. - pub fn new(start: Position, end: Position) -> Span { + pub fn new(start: Pos, end: Pos) -> Span { Span { start, end } } + /// Create a span including just a single position. + pub fn at(pos: Pos) -> Span { + Span { start: pos, end: pos } + } + /// Create a new span with the earlier start and later end position. pub fn merge(a: Span, b: Span) -> Span { Span { @@ -90,24 +107,17 @@ impl Span { } } - /// Create a span including just a single position. - pub fn at(pos: Position) -> Span { - Span { start: pos, end: pos } - } - /// Expand a span by merging it with another span. pub fn expand(&mut self, other: Span) { *self = Span::merge(*self, other) } +} - /// Offset a span by a start position. - /// - /// This is, for example, used to translate error spans from function local - /// to global. - pub fn offset(self, start: Position) -> Span { +impl Offset for Span { + fn offset(self, by: Pos) -> Self { Span { - start: start + self.start, - end: start + self.end, + start: self.start.offset(by), + end: self.end.offset(by), } } } @@ -120,34 +130,40 @@ impl Debug for Span { /// Zero-indexed line-column position in source code. #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize)] -pub struct Position { +pub struct Pos { /// The zero-indexed line. pub line: usize, /// The zero-indexed column. pub column: usize, } -impl Position { +impl Pos { /// The line 0, column 0 position. - pub const ZERO: Position = Position { line: 0, column: 0 }; + pub const ZERO: Pos = Pos { line: 0, column: 0 }; /// Create a new position from line and column. - pub fn new(line: usize, column: usize) -> Position { - Position { line, column } + pub fn new(line: usize, column: usize) -> Pos { + Pos { line, column } } } -impl Add for Position { - type Output = Position; +impl Offset for Pos { + fn offset(self, by: Pos) -> Self { + by + self + } +} - fn add(self, rhs: Position) -> Position { +impl Add for Pos { + type Output = Pos; + + fn add(self, rhs: Pos) -> Pos { if rhs.line == 0 { - Position { + Pos { line: self.line, column: self.column + rhs.column } } else { - Position { + Pos { line: self.line + rhs.line, column: rhs.column, } @@ -155,17 +171,17 @@ impl Add for Position { } } -impl Sub for Position { - type Output = Position; +impl Sub for Pos { + type Output = Pos; - fn sub(self, rhs: Position) -> Position { + fn sub(self, rhs: Pos) -> Pos { if self.line == rhs.line { - Position { + Pos { line: 0, column: self.column - rhs.column } } else { - Position { + Pos { line: self.line - rhs.line, column: self.column, } @@ -173,7 +189,7 @@ impl Sub for Position { } } -impl Debug for Position { +impl Debug for Pos { fn fmt(&self, f: &mut Formatter) -> fmt::Result { write!(f, "{}:{}", self.line, self.column) } diff --git a/src/syntax/test.rs b/src/syntax/test.rs index 7b1e08304..639fbb61f 100644 --- a/src/syntax/test.rs +++ b/src/syntax/test.rs @@ -39,11 +39,11 @@ macro_rules! span_vec { macro_rules! span_item { (($sl:tt:$sc:tt, $el:tt:$ec:tt, $v:expr)) => ({ - use $crate::syntax::span::{Position, Span, Spanned}; + use $crate::syntax::span::{Pos, Span, Spanned}; Spanned { span: Span::new( - Position::new($sl, $sc), - Position::new($el, $ec) + Pos::new($sl, $sc), + Pos::new($el, $ec) ), v: $v } diff --git a/src/syntax/tokens.rs b/src/syntax/tokens.rs index 102007080..9bb95c979 100644 --- a/src/syntax/tokens.rs +++ b/src/syntax/tokens.rs @@ -2,8 +2,8 @@ use std::iter::Peekable; use std::str::Chars; use unicode_xid::UnicodeXID; -use crate::size::Size; -use super::span::{Position, Span, Spanned}; +use crate::length::Length; +use super::span::{Pos, Span, Spanned}; use Token::*; use TokenMode::*; @@ -73,8 +73,8 @@ pub enum Token<'s> { }, /// A number in a function header: `3.14`. ExprNumber(f64), - /// A size in a function header: `12pt`. - ExprSize(Size), + /// A length in a function header: `12pt`. + ExprLength(Length), /// A boolean in a function header: `true | false`. ExprBool(bool), /// A hex value in a function header: `#20d82a`. @@ -129,7 +129,7 @@ impl<'s> Token<'s> { ExprIdent(_) => "identifier", ExprStr { .. } => "string", ExprNumber(_) => "number", - ExprSize(_) => "size", + ExprLength(_) => "length", ExprBool(_) => "bool", ExprHex(_) => "hex value", Plus => "plus", @@ -153,7 +153,7 @@ pub struct Tokens<'s> { src: &'s str, mode: TokenMode, iter: Peekable>, - position: Position, + pos: Pos, index: usize, } @@ -172,12 +172,12 @@ impl<'s> Tokens<'s> { /// /// The first token's span starts an the given `offset` position instead of /// the zero position. - pub fn new(src: &'s str, offset: Position, mode: TokenMode) -> Tokens<'s> { + pub fn new(src: &'s str, offset: Pos, mode: TokenMode) -> Tokens<'s> { Tokens { src, mode, iter: src.chars().peekable(), - position: offset, + pos: offset, index: 0, } } @@ -190,8 +190,8 @@ impl<'s> Tokens<'s> { /// The line-colunn position in the source at which the last token ends and /// next token will start. - pub fn pos(&self) -> Position { - self.position + pub fn pos(&self) -> Pos { + self.pos } } @@ -315,14 +315,14 @@ impl<'s> Tokens<'s> { }, true, 0, -2).0) } - fn read_whitespace(&mut self, start: Position) -> Token<'s> { + fn read_whitespace(&mut self, start: Pos) -> Token<'s> { self.read_string_until(|n| !n.is_whitespace(), false, 0, 0); let end = self.pos(); Space(end.line - start.line) } - fn read_function(&mut self, start: Position) -> Token<'s> { + fn read_function(&mut self, start: Pos) -> Token<'s> { let (header, terminated) = self.read_function_part(Header); self.eat(); @@ -354,7 +354,7 @@ impl<'s> Tokens<'s> { self.eat(); match n { - '[' => { self.read_function(Position::ZERO); } + '[' => { self.read_function(Pos::ZERO); } '/' if self.peek() == Some('/') => { self.read_line_comment(); } '/' if self.peek() == Some('*') => { self.read_block_comment(); } '"' if mode == Header => { self.read_string(); } @@ -427,8 +427,8 @@ impl<'s> Tokens<'s> { ExprNumber(num) } else if let Some(num) = parse_percentage(text) { ExprNumber(num / 100.0) - } else if let Ok(size) = text.parse::() { - ExprSize(size) + } else if let Ok(length) = text.parse::() { + ExprLength(length) } else if is_identifier(text) { ExprIdent(text) } else { @@ -476,10 +476,10 @@ impl<'s> Tokens<'s> { self.index += c.len_utf8(); if is_newline_char(c) && !(c == '\r' && self.peek() == Some('\n')) { - self.position.line += 1; - self.position.column = 0; + self.pos.line += 1; + self.pos.column = 0; } else { - self.position.column += 1; + self.pos.column += 1; } Some(c) @@ -543,7 +543,7 @@ mod tests { LeftBrace as LB, RightBrace as RB, ExprIdent as Id, ExprNumber as Num, - ExprSize as Sz, + ExprLength as Len, ExprBool as Bool, ExprHex as Hex, Text as T, @@ -557,7 +557,7 @@ mod tests { macro_rules! t { ($mode:expr, $source:expr => [$($tokens:tt)*]) => { let (exp, spans) = span_vec![$($tokens)*]; - let found = Tokens::new($source, Position::ZERO, $mode).collect::>(); + let found = Tokens::new($source, Pos::ZERO, $mode).collect::>(); check($source, exp, found, spans); } } @@ -640,13 +640,13 @@ mod tests { t!(Header, "__main__" => [Id("__main__")]); t!(Header, ".func.box" => [Id(".func.box")]); t!(Header, "arg, _b, _1" => [Id("arg"), Comma, S(0), Id("_b"), Comma, S(0), Id("_1")]); - t!(Header, "12_pt, 12pt" => [Invalid("12_pt"), Comma, S(0), Sz(Size::pt(12.0))]); - t!(Header, "1e5in" => [Sz(Size::inches(100000.0))]); - t!(Header, "2.3cm" => [Sz(Size::cm(2.3))]); - t!(Header, "12e-3in" => [Sz(Size::inches(12e-3))]); - t!(Header, "6.1cm + 4pt,a=1*2" => [Sz(Size::cm(6.1)), S(0), Plus, S(0), Sz(Size::pt(4.0)), Comma, Id("a"), Equals, Num(1.0), Star, Num(2.0)]); + t!(Header, "12_pt, 12pt" => [Invalid("12_pt"), Comma, S(0), Len(Length::pt(12.0))]); + t!(Header, "1e5in" => [Len(Length::inches(100000.0))]); + t!(Header, "2.3cm" => [Len(Length::cm(2.3))]); + t!(Header, "12e-3in" => [Len(Length::inches(12e-3))]); + t!(Header, "6.1cm + 4pt,a=1*2" => [Len(Length::cm(6.1)), S(0), Plus, S(0), Len(Length::pt(4.0)), Comma, Id("a"), Equals, Num(1.0), Star, Num(2.0)]); t!(Header, "(5 - 1) / 2.1" => [LP, Num(5.0), S(0), Min, S(0), Num(1.0), RP, S(0), Slash, S(0), Num(2.1)]); - t!(Header, "02.4mm" => [Sz(Size::mm(2.4))]); + t!(Header, "02.4mm" => [Len(Length::mm(2.4))]); t!(Header, "2.4.cm" => [Invalid("2.4.cm")]); t!(Header, "(1,2)" => [LP, Num(1.0), Comma, Num(2.0), RP]); t!(Header, "{abc}" => [LB, Id("abc"), RB]); diff --git a/tests/src/typeset.rs b/tests/src/typeset.rs index 8a4cf828e..2126615e6 100644 --- a/tests/src/typeset.rs +++ b/tests/src/typeset.rs @@ -12,13 +12,13 @@ use futures_executor::block_on; use typstc::{Typesetter, DebugErrorProvider}; use typstc::layout::MultiLayout; -use typstc::size::{Size, Size2D, ValueBox}; -use typstc::style::{PageStyle, PaperClass}; +use typstc::length::{Length, Size, Value4}; +use typstc::style::PageStyle; +use typstc::paper::PaperClass; use typstc::export::pdf; use toddle::query::FontIndex; use toddle::query::fs::EagerFsProvider; - type DynResult = Result>; fn main() -> DynResult<()> { @@ -72,8 +72,8 @@ fn test(name: &str, src: &str) -> DynResult<()> { typesetter.set_page_style(PageStyle { class: PaperClass::Custom, - dimensions: Size2D::with_all(Size::pt(250.0)), - margins: ValueBox::with_all(None), + dimensions: Size::with_all(Length::pt(250.0)), + margins: Value4::with_all(None), }); let layouts = compile(&typesetter, src); @@ -123,14 +123,14 @@ fn test(name: &str, src: &str) -> DynResult<()> { fn compile(typesetter: &Typesetter, src: &str) -> MultiLayout { if cfg!(debug_assertions) { let typeset = block_on(typesetter.typeset(src)); - let problems = typeset.feedback.problems; + let diagnostics = typeset.feedback.diagnostics; - if !problems.is_empty() { - for problem in problems { + if !diagnostics.is_empty() { + for diagnostic in diagnostics { println!(" {:?} {:?}: {}", - problem.v.severity, - problem.span, - problem.v.message + diagnostic.v.level, + diagnostic.span, + diagnostic.v.message ); } }