Move, rename and switch some things (boring) 🚚

- 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
This commit is contained in:
Laurenz 2020-07-29 18:09:51 +02:00
parent f34ba3dcda
commit bbcdeb128c
32 changed files with 891 additions and 927 deletions

View File

@ -8,7 +8,6 @@ use typstc::{Typesetter, DebugErrorProvider};
use typstc::toddle::query::fs::EagerFsProvider; use typstc::toddle::query::fs::EagerFsProvider;
use typstc::export::pdf; use typstc::export::pdf;
fn main() { fn main() {
if let Err(err) = run() { if let Err(err) = run() {
eprintln!("error: {}", err); eprintln!("error: {}", err);

91
src/diagnostic.rs Normal file
View File

@ -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<Diagnostic>;
/// 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<String>, 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,
)
};
}

View File

@ -23,8 +23,7 @@ use toddle::tables::{
use crate::GlobalFontLoader; use crate::GlobalFontLoader;
use crate::layout::{MultiLayout, Layout, LayoutAction}; 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 /// 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 /// 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( let rect = Rect::new(
0.0, 0.0,
0.0, 0.0,
page.dimensions.x.to_pt(), page.dimensions.x.to_pt() as f32,
page.dimensions.y.to_pt(), page.dimensions.y.to_pt() as f32,
); );
self.writer.write_obj( self.writer.write_obj(
@ -167,14 +166,14 @@ impl<'a, W: Write> PdfExporter<'a, W> {
LayoutAction::SetFont(id, size) => { LayoutAction::SetFont(id, size) => {
active_font = (self.font_remap[id], size.to_pt()); 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) => { LayoutAction::WriteText(string) => {
if let Some(pos) = next_pos.take() { if let Some(pos) = next_pos.take() {
let x = pos.x.to_pt(); let x = pos.x.to_pt();
let y = (page.dimensions.y - pos.y - Size::pt(active_font.1)).to_pt(); 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, y); 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)?); 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. // Extract information from the head and hmtx tables.
let head = font.read_table::<Header>()?; let head = font.read_table::<Header>()?;
let font_unit_ratio = 1.0 / (head.units_per_em as f32); let font_unit_ratio = 1.0 / (head.units_per_em as f64);
let font_unit_to_size = |x| Size::pt(font_unit_ratio * x); let font_unit_to_size = |x| Length::pt(font_unit_ratio * x);
let font_unit_to_glyph_unit = |fu| { let font_unit_to_glyph_unit = |fu| {
let size = font_unit_to_size(fu); let size = font_unit_to_size(fu);
(1000.0 * size.to_pt()).round() as GlyphUnit (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 italic = head.mac_style.contains(MacStyleFlags::ITALIC);
let bounding_box = Rect::new( let bounding_box = Rect::new(
font_unit_to_glyph_unit(head.x_min as f32), font_unit_to_glyph_unit(head.x_min as f64),
font_unit_to_glyph_unit(head.y_min as f32), font_unit_to_glyph_unit(head.y_min as f64),
font_unit_to_glyph_unit(head.x_max as f32), font_unit_to_glyph_unit(head.x_max as f64),
font_unit_to_glyph_unit(head.y_max as f32), font_unit_to_glyph_unit(head.y_max as f64),
); );
// Transform the width into PDF units. // Transform the width into PDF units.
@ -239,7 +238,7 @@ impl<'a, W: Write> PdfExporter<'a, W> {
.read_table::<HorizontalMetrics>()? .read_table::<HorizontalMetrics>()?
.metrics .metrics
.iter() .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(); .collect();
// Write the CID font referencing the font descriptor. // 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). // Write the font descriptor (contains the global information about the font).
self.writer.write_obj(id + 2, FontDescriptor::new(base_font, flags, italic_angle) self.writer.write_obj(id + 2, FontDescriptor::new(base_font, flags, italic_angle)
.font_bbox(bounding_box) .font_bbox(bounding_box)
.ascent(font_unit_to_glyph_unit(os2.s_typo_ascender 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 f32)) .descent(font_unit_to_glyph_unit(os2.s_typo_descender as f64))
.cap_height(font_unit_to_glyph_unit( .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) .font_file_2(id + 4)
)?; )?;

View File

@ -52,8 +52,8 @@ pub trait ParseFunc {
/// ///
/// parse(header, body, state, f) { /// parse(header, body, state, f) {
/// let body = body!(opt: body, state, f); /// let body = body!(opt: body, state, f);
/// let hidden = header.args.pos.get::<bool>(&mut f.problems) /// let hidden = header.args.pos.get::<bool>(&mut f.diagnostics)
/// .or_missing(&mut f.problems, header.name.span, "hidden") /// .or_missing(&mut f.diagnostics, header.name.span, "hidden")
/// .unwrap_or(false); /// .unwrap_or(false);
/// ///
/// HiderFunc { body: if hidden { None } else { body } } /// HiderFunc { body: if hidden { None } else { body } }

View File

@ -4,23 +4,22 @@ use std::fmt::{self, Debug, Formatter};
use serde::ser::{Serialize, Serializer, SerializeTuple}; use serde::ser::{Serialize, Serializer, SerializeTuple};
use toddle::query::FontIndex; use toddle::query::FontIndex;
use crate::size::{Size, Size2D}; use crate::length::{Length, Size};
use super::Layout; use super::Layout;
use self::LayoutAction::*; use self::LayoutAction::*;
/// A layouting action, which is the basic building block layouts are composed /// A layouting action, which is the basic building block layouts are composed
/// of. /// of.
#[derive(Clone, PartialEq)] #[derive(Clone, PartialEq)]
pub enum LayoutAction { pub enum LayoutAction {
/// Move to an absolute position. /// Move to an absolute position.
MoveAbsolute(Size2D), MoveAbsolute(Size),
/// Set the font given the index from the font loader and font 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. /// Write text at the current position.
WriteText(String), WriteText(String),
/// Visualize a box for debugging purposes. /// Visualize a box for debugging purposes.
DebugBox(Size2D), DebugBox(Size),
} }
impl Serialize for LayoutAction { impl Serialize for LayoutAction {
@ -81,11 +80,11 @@ impl Debug for LayoutAction {
/// position. /// position.
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub struct LayoutActions { pub struct LayoutActions {
origin: Size2D, origin: Size,
actions: Vec<LayoutAction>, actions: Vec<LayoutAction>,
active_font: (FontIndex, Size), active_font: (FontIndex, Length),
next_pos: Option<Size2D>, next_pos: Option<Size>,
next_font: Option<(FontIndex, Size)>, next_font: Option<(FontIndex, Length)>,
} }
impl LayoutActions { impl LayoutActions {
@ -93,8 +92,8 @@ impl LayoutActions {
pub fn new() -> LayoutActions { pub fn new() -> LayoutActions {
LayoutActions { LayoutActions {
actions: vec![], actions: vec![],
origin: Size2D::ZERO, origin: Size::ZERO,
active_font: (FontIndex::MAX, Size::ZERO), active_font: (FontIndex::MAX, Length::ZERO),
next_pos: None, next_pos: None,
next_font: None, next_font: None,
} }
@ -126,7 +125,7 @@ impl LayoutActions {
/// Add a layout at a position. All move actions inside the layout are /// Add a layout at a position. All move actions inside the layout are
/// translated by the position. /// 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.flush_position();
self.origin = position; self.origin = position;

View File

@ -11,7 +11,6 @@
use super::stack::{StackLayouter, StackContext}; use super::stack::{StackLayouter, StackContext};
use super::*; use super::*;
/// Performs the line layouting. /// Performs the line layouting.
#[derive(Debug)] #[derive(Debug)]
pub struct LineLayouter { pub struct LineLayouter {
@ -40,7 +39,7 @@ pub struct LineContext {
/// extent of the layout. /// extent of the layout.
pub debug: bool, pub debug: bool,
/// The line spacing. /// 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 /// A line run is a sequence of boxes with the same alignment that are arranged
@ -49,10 +48,10 @@ pub struct LineContext {
#[derive(Debug)] #[derive(Debug)]
struct LineRun { struct LineRun {
/// The so-far accumulated layouts in the line. /// The so-far accumulated layouts in the line.
layouts: Vec<(Size, Layout)>, layouts: Vec<(Length, Layout)>,
/// The width (primary size) and maximal height (secondary size) of the /// The width (primary length) and maximal height (secondary length) of the
/// line. /// line.
size: Size2D, size: Size,
/// The alignment of all layouts in the line. /// The alignment of all layouts in the line.
/// ///
/// When a new run is created the alignment is yet to be determined. Once a /// When a new run is created the alignment is yet to be determined. Once a
@ -61,7 +60,7 @@ struct LineRun {
alignment: Option<LayoutAlignment>, alignment: Option<LayoutAlignment>,
/// If another line run with different alignment already took up some space /// 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. /// of the line, this run has less space and how much is stored here.
usable: Option<Size>, usable: Option<Length>,
/// A possibly cached soft spacing or spacing state. /// A possibly cached soft spacing or spacing state.
last_spacing: LastSpacing, last_spacing: LastSpacing,
} }
@ -156,7 +155,7 @@ impl LineLayouter {
/// ///
/// This specifies how much more fits before a line break needs to be /// This specifies how much more fits before a line break needs to be
/// issued. /// issued.
fn usable(&self) -> Size2D { fn usable(&self) -> Size {
// The base is the usable space per stack layouter. // The base is the usable space per stack layouter.
let mut usable = self.stack.usable().generalized(self.ctx.axes); 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. /// 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 { match kind {
// A hard space is simply an empty box. // A hard space is simply an empty box.
SpacingKind::Hard => { SpacingKind::Hard => {
@ -197,7 +196,7 @@ impl LineLayouter {
} }
/// Finish the line and add secondary spacing to the underlying stack. /// 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.finish_line_if_not_empty();
self.stack.add_spacing(spacing, kind) self.stack.add_spacing(spacing, kind)
} }
@ -219,7 +218,7 @@ impl LineLayouter {
} }
/// Update the line spacing. /// 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; self.ctx.line_spacing = line_spacing;
} }
@ -235,7 +234,7 @@ impl LineLayouter {
/// Whether the currently set line is empty. /// Whether the currently set line is empty.
pub fn line_is_empty(&self) -> bool { 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. /// Finish the last line and compute the final list of boxes.
@ -265,7 +264,7 @@ impl LineLayouter {
- layout.dimensions.primary(self.ctx.axes), - layout.dimensions.primary(self.ctx.axes),
}; };
let pos = Size2D::with_x(x); let pos = Size::with_x(x);
actions.add_layout(pos, layout); actions.add_layout(pos, layout);
} }
@ -293,7 +292,7 @@ impl LineRun {
fn new() -> LineRun { fn new() -> LineRun {
LineRun { LineRun {
layouts: vec![], layouts: vec![],
size: Size2D::ZERO, size: Size::ZERO,
alignment: None, alignment: None,
usable: None, usable: None,
last_spacing: LastSpacing::Hard, last_spacing: LastSpacing::Hard,

View File

@ -5,7 +5,7 @@ use smallvec::SmallVec;
use serde::Serialize; use serde::Serialize;
use toddle::query::FontIndex; use toddle::query::FontIndex;
use crate::size::{Size, Size2D, SizeBox}; use crate::length::{Length, Size, Margins};
use self::prelude::*; use self::prelude::*;
pub mod line; pub mod line;
@ -27,7 +27,6 @@ pub mod prelude {
pub use super::Alignment::{self, *}; pub use super::Alignment::{self, *};
} }
/// A collection of layouts. /// A collection of layouts.
pub type MultiLayout = Vec<Layout>; pub type MultiLayout = Vec<Layout>;
@ -35,7 +34,7 @@ pub type MultiLayout = Vec<Layout>;
#[derive(Debug, Clone, PartialEq, Serialize)] #[derive(Debug, Clone, PartialEq, Serialize)]
pub struct Layout { pub struct Layout {
/// The size of the box. /// The size of the box.
pub dimensions: Size2D, pub dimensions: Size,
/// How to align this layout in a parent container. /// How to align this layout in a parent container.
#[serde(skip)] #[serde(skip)]
pub alignment: LayoutAlignment, pub alignment: LayoutAlignment,
@ -66,9 +65,9 @@ pub type LayoutSpaces = SmallVec<[LayoutSpace; 2]>;
#[derive(Debug, Copy, Clone, PartialEq)] #[derive(Debug, Copy, Clone, PartialEq)]
pub struct LayoutSpace { pub struct LayoutSpace {
/// The maximum size of the box to layout in. /// The maximum size of the box to layout in.
pub dimensions: Size2D, pub dimensions: Size,
/// Padding that should be respected on each side. /// 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 /// Whether to expand the dimensions of the resulting layout to the full
/// dimensions of this space or to shrink them to fit the content. /// dimensions of this space or to shrink them to fit the content.
pub expansion: LayoutExpansion, pub expansion: LayoutExpansion,
@ -77,12 +76,12 @@ pub struct LayoutSpace {
impl LayoutSpace { impl LayoutSpace {
/// The offset from the origin to the start of content, that is, /// The offset from the origin to the start of content, that is,
/// `(padding.left, padding.top)`. /// `(padding.left, padding.top)`.
pub fn start(&self) -> Size2D { pub fn start(&self) -> Size {
Size2D::new(self.padding.left, self.padding.top) Size::new(self.padding.left, self.padding.top)
} }
/// The actually usable area (dimensions minus padding). /// The actually usable area (dimensions minus padding).
pub fn usable(&self) -> Size2D { pub fn usable(&self) -> Size {
self.dimensions.unpadded(self.padding) self.dimensions.unpadded(self.padding)
} }
@ -90,7 +89,7 @@ impl LayoutSpace {
pub fn usable_space(&self) -> LayoutSpace { pub fn usable_space(&self) -> LayoutSpace {
LayoutSpace { LayoutSpace {
dimensions: self.usable(), dimensions: self.usable(),
padding: SizeBox::ZERO, padding: Margins::ZERO,
expansion: LayoutExpansion::new(false, false), expansion: LayoutExpansion::new(false, false),
} }
} }
@ -369,17 +368,17 @@ enum LastSpacing {
/// The last item was hard spacing. /// The last item was hard spacing.
Hard, Hard,
/// The last item was soft spacing with the given width and level. /// The last item was soft spacing with the given width and level.
Soft(Size, u32), Soft(Length, u32),
/// The last item was not spacing. /// The last item was not spacing.
None, None,
} }
impl LastSpacing { impl LastSpacing {
/// The size of the soft space if this is a soft space or zero otherwise. /// The length of the soft space if this is a soft space or zero otherwise.
fn soft_or_zero(self) -> Size { fn soft_or_zero(self) -> Length {
match self { match self {
LastSpacing::Soft(space, _) => space, LastSpacing::Soft(space, _) => space,
_ => Size::ZERO, _ => Length::ZERO,
} }
} }
} }

View File

@ -10,14 +10,13 @@ use toddle::query::FontStyle;
use crate::{Pass, Feedback}; use crate::{Pass, Feedback};
use crate::GlobalFontLoader; use crate::GlobalFontLoader;
use crate::style::{LayoutStyle, PageStyle, TextStyle}; 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::{Model, SyntaxModel, Node, Decoration};
use crate::syntax::span::{Span, Spanned}; use crate::syntax::span::{Span, Spanned};
use super::line::{LineLayouter, LineContext}; use super::line::{LineLayouter, LineContext};
use super::text::{layout_text, TextContext}; use super::text::{layout_text, TextContext};
use super::*; use super::*;
/// Performs the model layouting. /// Performs the model layouting.
#[derive(Debug)] #[derive(Debug)]
pub struct ModelLayouter<'a> { pub struct ModelLayouter<'a> {
@ -36,7 +35,7 @@ pub struct LayoutContext<'a> {
/// The style for pages and text. /// The style for pages and text.
pub style: &'a LayoutStyle, pub style: &'a LayoutStyle,
/// The base unpadded dimensions of this container (for relative sizing). /// The base unpadded dimensions of this container (for relative sizing).
pub base: Size2D, pub base: Size,
/// The spaces to layout in. /// The spaces to layout in.
pub spaces: LayoutSpaces, pub spaces: LayoutSpaces,
/// Whether to have repeated spaces or to use only the first and only once. /// 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 /// Add spacing of given [kind](super::SpacingKind) along the primary or
/// secondary axis. The spacing kind defines how the spacing interacts with /// secondary axis. The spacing kind defines how the spacing interacts with
/// surrounding spacing. /// surrounding spacing.
AddSpacing(Size, SpacingKind, GenericAxis), AddSpacing(Length, SpacingKind, GenericAxis),
/// Start a new line. /// Start a new line.
BreakLine, BreakLine,
@ -159,7 +158,7 @@ impl<'a> ModelLayouter<'a> {
for Spanned { v: node, span } in &model.nodes { for Spanned { v: node, span } in &model.nodes {
let decorate = |this: &mut ModelLayouter, deco| { let decorate = |this: &mut ModelLayouter, deco| {
this.feedback.decos.push(Spanned::new(deco, *span)); this.feedback.decorations.push(Spanned::new(deco, *span));
}; };
match node { match node {

View File

@ -22,10 +22,9 @@
//! sentence in the second box. //! sentence in the second box.
use smallvec::smallvec; use smallvec::smallvec;
use crate::size::ValueBox; use crate::length::Value4;
use super::*; use super::*;
/// Performs the stack layouting. /// Performs the stack layouting.
#[derive(Debug)] #[derive(Debug)]
pub struct StackLayouter { pub struct StackLayouter {
@ -66,14 +65,14 @@ struct Space {
/// The so-far accumulated layouts. /// The so-far accumulated layouts.
layouts: Vec<(LayoutAxes, Layout)>, layouts: Vec<(LayoutAxes, Layout)>,
/// The specialized size of this space. /// The specialized size of this space.
size: Size2D, size: Size,
/// The specialized remaining space. /// The specialized remaining space.
usable: Size2D, usable: Size,
/// The specialized extra-needed dimensions to affect the size at all. /// 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 /// The rulers of a space dictate which alignments for new boxes are still
/// allowed and which require a new space to be started. /// allowed and which require a new space to be started.
rulers: ValueBox<Alignment>, rulers: Value4<Alignment>,
/// The last added spacing if the last added thing was spacing. /// The last added spacing if the last added thing was spacing.
last_spacing: LastSpacing, last_spacing: LastSpacing,
} }
@ -129,13 +128,13 @@ impl StackLayouter {
} }
/// Add secondary spacing to the stack. /// 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 { match kind {
// A hard space is simply an empty box. // A hard space is simply an empty box.
SpacingKind::Hard => { SpacingKind::Hard => {
// Reduce the spacing such that it definitely fits. // Reduce the spacing such that it definitely fits.
spacing.min_eq(self.space.usable.secondary(self.ctx.axes)); 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.update_metrics(dimensions);
self.space.layouts.push((self.ctx.axes, Layout { 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 /// Update the size metrics to reflect that a layout or spacing with the
/// given generalized dimensions has been added. /// 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 axes = self.ctx.axes;
let mut size = self.space.size.generalized(axes); let mut size = self.space.size.generalized(axes);
let mut extra = self.space.extra.generalized(axes); let mut extra = self.space.extra.generalized(axes);
size.x += (dimensions.x - extra.x).max(Size::ZERO); size.x += (dimensions.x - extra.x).max(Length::ZERO);
size.y += (dimensions.y - extra.y).max(Size::ZERO); size.y += (dimensions.y - extra.y).max(Length::ZERO);
extra.x.max_eq(dimensions.x); 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.size = size.specialized(axes);
self.space.extra = extra.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 /// Move to the first space that can fit the given dimensions or do nothing
/// if no space is capable of that. /// 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(); let start = self.next_space();
for (index, space) in self.ctx.spaces[start..].iter().enumerate() { for (index, space) in self.ctx.spaces[start..].iter().enumerate() {
if space.usable().fits(dimensions) { if space.usable().fits(dimensions) {
@ -251,7 +250,7 @@ impl StackLayouter {
let mut spaces = smallvec![LayoutSpace { let mut spaces = smallvec![LayoutSpace {
dimensions, dimensions,
padding: SizeBox::ZERO, padding: Margins::ZERO,
expansion: LayoutExpansion::new(false, false), expansion: LayoutExpansion::new(false, false),
}]; }];
@ -263,15 +262,15 @@ impl StackLayouter {
} }
/// The remaining usable size. /// The remaining usable size.
pub fn usable(&self) -> Size2D { pub fn usable(&self) -> Size {
self.space.usable 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) .specialized(self.ctx.axes)
} }
/// Whether the current layout space (not subspace) is empty. /// Whether the current layout space (not subspace) is empty.
pub fn space_is_empty(&self) -> bool { 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. /// Whether the current layout space is the last is the followup list.
@ -310,7 +309,7 @@ impl StackLayouter {
let start = space.start(); let start = space.start();
let mut bounds = vec![]; let mut bounds = vec![];
let mut bound = SizeBox { let mut bound = Margins {
left: start.x, left: start.x,
top: start.y, top: start.y,
right: start.x + self.space.size.x, 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 // The `x` field stores the maximal primary extent in one axis-aligned
// run, while the `y` fields stores the accumulated secondary extent. // run, while the `y` fields stores the accumulated secondary extent.
let mut extent = Size2D::ZERO; let mut extent = Size::ZERO;
let mut rotation = Vertical; let mut rotation = Vertical;
for (bound, entry) in bounds.iter_mut().zip(&self.space.layouts).rev() { 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. // is reset for this new axis-aligned run.
if rotation != axes.secondary.axis() { if rotation != axes.secondary.axis() {
extent.y = extent.x; extent.y = extent.x;
extent.x = Size::ZERO; extent.x = Length::ZERO;
rotation = axes.secondary.axis(); rotation = axes.secondary.axis();
} }
@ -383,11 +382,11 @@ impl StackLayouter {
// The space in which this layout is aligned is given by the // The space in which this layout is aligned is given by the
// distances between the borders of it's bounding box. // distances between the borders of it's bounding box.
let usable = let usable =
Size2D::new(bound.right - bound.left, bound.bottom - bound.top) Size::new(bound.right - bound.left, bound.bottom - bound.top)
.generalized(axes); .generalized(axes);
let local = usable.anchor(alignment, axes) - size.anchor(alignment, 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); actions.add_layout(pos, layout);
} }
@ -417,15 +416,15 @@ impl StackLayouter {
} }
impl Space { impl Space {
fn new(index: usize, hard: bool, usable: Size2D) -> Space { fn new(index: usize, hard: bool, usable: Size) -> Space {
Space { Space {
index, index,
hard, hard,
layouts: vec![], layouts: vec![],
size: Size2D::ZERO, size: Size::ZERO,
usable, usable,
extra: Size2D::ZERO, extra: Size::ZERO,
rulers: ValueBox::with_all(Origin), rulers: Value4::with_all(Origin),
last_spacing: LastSpacing::Hard, last_spacing: LastSpacing::Hard,
} }
} }

View File

@ -8,11 +8,10 @@ use toddle::query::{FontQuery, FontIndex};
use toddle::tables::{CharMap, Header, HorizontalMetrics}; use toddle::tables::{CharMap, Header, HorizontalMetrics};
use crate::GlobalFontLoader; use crate::GlobalFontLoader;
use crate::size::{Size, Size2D}; use crate::length::{Length, Size};
use crate::style::TextStyle; use crate::style::TextStyle;
use super::*; use super::*;
/// Performs the text layouting. /// Performs the text layouting.
#[derive(Debug)] #[derive(Debug)]
struct TextLayouter<'a> { struct TextLayouter<'a> {
@ -21,7 +20,7 @@ struct TextLayouter<'a> {
actions: LayoutActions, actions: LayoutActions,
buffer: String, buffer: String,
active_font: FontIndex, active_font: FontIndex,
width: Size, width: Length,
} }
/// The context for text layouting. /// The context for text layouting.
@ -54,7 +53,7 @@ impl<'a> TextLayouter<'a> {
actions: LayoutActions::new(), actions: LayoutActions::new(),
buffer: String::new(), buffer: String::new(),
active_font: FontIndex::MAX, active_font: FontIndex::MAX,
width: Size::ZERO, width: Length::ZERO,
} }
} }
@ -77,7 +76,7 @@ impl<'a> TextLayouter<'a> {
} }
Layout { 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, alignment: self.ctx.alignment,
actions: self.actions.into_vec(), 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 /// Select the best font for a character and return its index along with
/// the width of the char in the font. /// 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 loader = self.ctx.loader.borrow_mut();
let mut variant = self.ctx.style.variant; let mut variant = self.ctx.style.variant;
@ -127,8 +126,8 @@ impl<'a> TextLayouter<'a> {
if let Some((font, index)) = loader.get(query).await { if let Some((font, index)) = loader.get(query).await {
// Determine the width of the char. // Determine the width of the char.
let header = font.read_table::<Header>().ok()?; let header = font.read_table::<Header>().ok()?;
let font_unit_ratio = 1.0 / (header.units_per_em as f32); let font_unit_ratio = 1.0 / (header.units_per_em as f64);
let font_unit_to_size = |x| Size::pt(font_unit_ratio * x); let font_unit_to_size = |x| Length::pt(font_unit_ratio * x);
let glyph = font let glyph = font
.read_table::<CharMap>() .read_table::<CharMap>()
@ -139,7 +138,7 @@ impl<'a> TextLayouter<'a> {
.read_table::<HorizontalMetrics>() .read_table::<HorizontalMetrics>()
.ok()? .ok()?
.get(glyph)? .get(glyph)?
.advance_width as f32; .advance_width as f64;
let char_width = font_unit_to_size(glyph_width) let char_width = font_unit_to_size(glyph_width)
* self.ctx.style.font_size().to_pt(); * self.ctx.style.font_size().to_pt();

View File

@ -8,173 +8,166 @@ use serde::Serialize;
use crate::layout::prelude::*; use crate::layout::prelude::*;
/// A general spacing type. /// A general spacing type.
#[derive(Default, Copy, Clone, PartialEq, PartialOrd, Serialize)] #[derive(Default, Copy, Clone, PartialEq, PartialOrd, Serialize)]
#[serde(transparent)] #[serde(transparent)]
pub struct Size { pub struct Length {
/// The size in typographic points (1/72 inches). /// The length in typographic points (1/72 inches).
pub points: f32, pub points: f64,
} }
impl Size { impl Length {
/// The zeroed size. /// The zeroed length.
pub const ZERO: Size = Size { points: 0.0 }; pub const ZERO: Length = Length { points: 0.0 };
/// Create a size from an amount of points. /// Create a length from an amount of points.
pub fn pt(points: f32) -> Size { Size { points } } pub fn pt(points: f64) -> Length { Length { points } }
/// Create a size from an amount of millimeters. /// Create a length from an amount of millimeters.
pub fn mm(mm: f32) -> Size { Size { points: 2.83465 * mm } } pub fn mm(mm: f64) -> Length { Length { points: 2.83465 * mm } }
/// Create a size from an amount of centimeters. /// Create a length from an amount of centimeters.
pub fn cm(cm: f32) -> Size { Size { points: 28.3465 * cm } } pub fn cm(cm: f64) -> Length { Length { points: 28.3465 * cm } }
/// Create a size from an amount of inches. /// Create a length from an amount of inches.
pub fn inches(inches: f32) -> Size { Size { points: 72.0 * inches } } pub fn inches(inches: f64) -> Length { Length { points: 72.0 * inches } }
/// Convert this size into points. /// Convert this length into points.
pub fn to_pt(self) -> f32 { self.points } pub fn to_pt(self) -> f64 { self.points }
/// Convert this size into millimeters. /// Convert this length into millimeters.
pub fn to_mm(self) -> f32 { self.points * 0.352778 } pub fn to_mm(self) -> f64 { self.points * 0.352778 }
/// Convert this size into centimeters. /// Convert this length into centimeters.
pub fn to_cm(self) -> f32 { self.points * 0.0352778 } pub fn to_cm(self) -> f64 { self.points * 0.0352778 }
/// Convert this size into inches. /// Convert this length into inches.
pub fn to_inches(self) -> f32 { self.points * 0.0138889 } pub fn to_inches(self) -> f64 { self.points * 0.0138889 }
/// The maximum of this and the other size. /// The maximum of this and the other length.
pub fn max(self, other: Size) -> Size { pub fn max(self, other: Length) -> Length {
if self > other { self } else { other } if self > other { self } else { other }
} }
/// The minimum of this and the other size. /// The minimum of this and the other length.
pub fn min(self, other: Size) -> Size { pub fn min(self, other: Length) -> Length {
if self <= other { self } else { other } if self <= other { self } else { other }
} }
/// Set this size to the maximum of itself and the other size. /// Set this length to the maximum of itself and the other length.
pub fn max_eq(&mut self, other: Size) { *self = self.max(other); } pub fn max_eq(&mut self, other: Length) { *self = self.max(other); }
/// Set this size to the minimum of itself and the other size. /// Set this length to the minimum of itself and the other length.
pub fn min_eq(&mut self, other: Size) { *self = self.min(other); } 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 /// The anchor position along the given direction for an item with the given
/// alignment in a container with this size. /// alignment in a container with this length.
pub fn anchor(self, alignment: Alignment, direction: Direction) -> Size { pub fn anchor(self, alignment: Alignment, direction: Direction) -> Length {
match (direction.is_positive(), alignment) { match (direction.is_positive(), alignment) {
(true, Origin) | (false, End) => Size::ZERO, (true, Origin) | (false, End) => Length::ZERO,
(_, Center) => self / 2, (_, Center) => self / 2,
(true, End) | (false, Origin) => self, (true, End) | (false, Origin) => self,
} }
} }
} }
impl Display for Size { impl Display for Length {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "{}pt", self.points) write!(f, "{}pt", self.points)
} }
} }
impl Debug for Size { impl Debug for Length {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
Display::fmt(self, f) Display::fmt(self, f)
} }
} }
impl Neg for Size { impl Neg for Length {
type Output = Size; type Output = Length;
fn neg(self) -> Size { fn neg(self) -> Length {
Size { points: -self.points } Length { points: -self.points }
} }
} }
impl Sum for Size { impl Sum for Length {
fn sum<I>(iter: I) -> Size fn sum<I>(iter: I) -> Length
where I: Iterator<Item = Size> { where I: Iterator<Item = Length> {
iter.fold(Size::ZERO, Add::add) 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)] #[derive(Copy, Clone, PartialEq)]
#[allow(missing_docs)] #[allow(missing_docs)]
pub enum ScaleSize { pub enum ScaleLength {
Absolute(Size), Absolute(Length),
Scaled(f32), Scaled(f64),
} }
impl ScaleSize { impl ScaleLength {
/// Use the absolute value or scale the entity. /// Use the absolute value or scale the entity.
pub fn scaled(&self, entity: Size) -> Size { pub fn scaled(&self, entity: Length) -> Length {
match self { match self {
ScaleSize::Absolute(s) => *s, ScaleLength::Absolute(s) => *s,
ScaleSize::Scaled(s) => *s * entity, ScaleLength::Scaled(s) => *s * entity,
} }
} }
} }
impl Display for ScaleSize { impl Display for ScaleLength {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self { match self {
ScaleSize::Absolute(size) => write!(f, "{}", size), ScaleLength::Absolute(length) => write!(f, "{}", length),
ScaleSize::Scaled(scale) => write!(f, "{}%", scale * 100.0), ScaleLength::Scaled(scale) => write!(f, "{}%", scale * 100.0),
} }
} }
} }
impl Debug for ScaleSize { impl Debug for ScaleLength {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
Display::fmt(self, f) 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. /// A value in two dimensions.
#[derive(Default, Copy, Clone, Eq, PartialEq, Serialize)] #[derive(Default, Copy, Clone, Eq, PartialEq, Serialize)]
pub struct Value2D<T> { pub struct Value2<T> {
/// The horizontal component. /// The horizontal component.
pub x: T, pub x: T,
/// The vertical component. /// The vertical component.
pub y: T, pub y: T,
} }
impl<T: Clone> Value2D<T> { impl<T: Clone> Value2<T> {
/// Create a new 2D-value from two values. /// Create a new 2D-value from two values.
pub fn new(x: T, y: T) -> Value2D<T> { Value2D { x, y } } pub fn new(x: T, y: T) -> Value2<T> { Value2 { x, y } }
/// Create a new 2D-value with `x` set to a value and `y` to default. /// Create a new 2D-value with `x` set to a value and `y` to default.
pub fn with_x(x: T) -> Value2D<T> where T: Default { pub fn with_x(x: T) -> Value2<T> where T: Default {
Value2D { x, y: T::default() } Value2 { x, y: T::default() }
} }
/// Create a new 2D-value with `y` set to a value and `x` to default. /// Create a new 2D-value with `y` set to a value and `x` to default.
pub fn with_y(y: T) -> Value2D<T> where T: Default { pub fn with_y(y: T) -> Value2<T> where T: Default {
Value2D { x: T::default(), y } Value2 { x: T::default(), y }
} }
/// Create a new 2D-value with the primary axis set to a value and the other /// Create a new 2D-value with the primary axis set to a value and the other
/// one to default. /// one to default.
pub fn with_primary(v: T, axes: LayoutAxes) -> Value2D<T> where T: Default { pub fn with_primary(v: T, axes: LayoutAxes) -> Value2<T> where T: Default {
Value2D::with_x(v).generalized(axes) Value2::with_x(v).generalized(axes)
} }
/// Create a new 2D-value with the secondary axis set to a value and the /// Create a new 2D-value with the secondary axis set to a value and the
/// other one to default. /// other one to default.
pub fn with_secondary(v: T, axes: LayoutAxes) -> Value2D<T> where T: Default { pub fn with_secondary(v: T, axes: LayoutAxes) -> Value2<T> where T: Default {
Value2D::with_y(v).generalized(axes) Value2::with_y(v).generalized(axes)
} }
/// Create a 2D-value with `x` and `y` set to the same value `s`. /// Create a 2D-value with `x` and `y` set to the same value `s`.
pub fn with_all(s: T) -> Value2D<T> { Value2D { x: s.clone(), y: s } } pub fn with_all(s: T) -> Value2<T> { Value2 { x: s.clone(), y: s } }
/// Get the specificed component. /// Get the specificed component.
pub fn get(self, axis: SpecificAxis) -> T { pub fn get(self, axis: SpecificAxis) -> T {
@ -216,16 +209,16 @@ impl<T: Clone> Value2D<T> {
/// axes, that is: /// axes, that is:
/// - `x` describes the primary axis instead of the horizontal one. /// - `x` describes the primary axis instead of the horizontal one.
/// - `y` describes the secondary axis instead of the vertical one. /// - `y` describes the secondary axis instead of the vertical one.
pub fn generalized(self, axes: LayoutAxes) -> Value2D<T> { pub fn generalized(self, axes: LayoutAxes) -> Value2<T> {
match axes.primary.axis() { match axes.primary.axis() {
Horizontal => self, 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 /// Returns the specialized version of this generalized Size2D (inverse to
/// `generalized`). /// `generalized`).
pub fn specialized(self, axes: LayoutAxes) -> Value2D<T> { pub fn specialized(self, axes: LayoutAxes) -> Value2<T> {
// In fact, generalized is its own inverse. For reasons of clarity // In fact, generalized is its own inverse. For reasons of clarity
// at the call site, we still have this second function. // at the call site, we still have this second function.
self.generalized(axes) self.generalized(axes)
@ -237,7 +230,7 @@ impl<T: Clone> Value2D<T> {
} }
} }
impl<T> Debug for Value2D<T> where T: Debug { impl<T> Debug for Value2<T> where T: Debug {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.debug_list() f.debug_list()
.entry(&self.x) .entry(&self.x)
@ -247,83 +240,61 @@ impl<T> Debug for Value2D<T> where T: Debug {
} }
/// A position or extent in 2-dimensional space. /// A position or extent in 2-dimensional space.
pub type Size2D = Value2D<Size>; pub type Size = Value2<Length>;
impl Size2D { impl Size {
/// The zeroed 2D-size. /// The zeroed 2D-length.
pub const ZERO: Size2D = Size2D { x: Size::ZERO, y: Size::ZERO }; 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. /// 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 self.x >= other.x && self.y >= other.y
} }
/// Return a 2D-size padded by the paddings of the given box. /// Return a 2D-length padded by the paddings of the given box.
pub fn padded(self, padding: SizeBox) -> Size2D { pub fn padded(self, padding: Margins) -> Size {
Size2D { Size {
x: self.x + padding.left + padding.right, x: self.x + padding.left + padding.right,
y: self.y + padding.top + padding.bottom, y: self.y + padding.top + padding.bottom,
} }
} }
/// Return a 2D-size reduced by the paddings of the given box. /// Return a 2D-length reduced by the paddings of the given box.
pub fn unpadded(self, padding: SizeBox) -> Size2D { pub fn unpadded(self, padding: Margins) -> Size {
Size2D { Size {
x: self.x - padding.left - padding.right, x: self.x - padding.left - padding.right,
y: self.y - padding.top - padding.bottom, y: self.y - padding.top - padding.bottom,
} }
} }
/// The anchor position along the given axis for an item with the given /// 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. /// primary axis.
pub fn anchor(self, alignment: LayoutAlignment, axes: LayoutAxes) -> Size2D { pub fn anchor(self, alignment: LayoutAlignment, axes: LayoutAxes) -> Size {
Size2D { Size {
x: self.x.anchor(alignment.primary, axes.primary), x: self.x.anchor(alignment.primary, axes.primary),
y: self.y.anchor(alignment.secondary, axes.secondary), y: self.y.anchor(alignment.secondary, axes.secondary),
} }
} }
} }
impl Neg for Size2D { impl Neg for Size {
type Output = Size2D; type Output = Size;
fn neg(self) -> Size2D { fn neg(self) -> Size {
Size2D { Size {
x: -self.x, x: -self.x,
y: -self.y, 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<T> {
/// 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<T> StretchValue<T> {
/// Create a new stretch size from minimum, optimal and maximum values.
pub fn new(min: T, opt: T, max: T) -> StretchValue<T> {
StretchValue { min, opt, max }
}
}
/// A size that is stretchable.
pub type StretchSize = StretchValue<Size>;
/// A value in four dimensions. /// A value in four dimensions.
#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Serialize)] #[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Serialize)]
pub struct ValueBox<T> { pub struct Value4<T> {
/// The left extent. /// The left extent.
pub left: T, pub left: T,
/// The top extent. /// The top extent.
@ -334,15 +305,15 @@ pub struct ValueBox<T> {
pub bottom: T, pub bottom: T,
} }
impl<T: Clone> ValueBox<T> { impl<T: Clone> Value4<T> {
/// Create a new box from four sizes. /// Create a new box from four sizes.
pub fn new(left: T, top: T, right: T, bottom: T) -> ValueBox<T> { pub fn new(left: T, top: T, right: T, bottom: T) -> Value4<T> {
ValueBox { left, top, right, bottom } Value4 { left, top, right, bottom }
} }
/// Create a box with all four fields set to the same value `s`. /// Create a box with all four fields set to the same value `s`.
pub fn with_all(value: T) -> ValueBox<T> { pub fn with_all(value: T) -> Value4<T> {
ValueBox { Value4 {
left: value.clone(), left: value.clone(),
top: value.clone(), top: value.clone(),
right: value.clone(), right: value.clone(),
@ -369,7 +340,7 @@ impl<T: Clone> ValueBox<T> {
/// Set all values to the given value. /// Set all values to the given value.
pub fn set_all(&mut self, value: T) { pub fn set_all(&mut self, value: T) {
*self = ValueBox::with_all(value); *self = Value4::with_all(value);
} }
/// Set the `left` and `right` values. /// Set the `left` and `right` values.
@ -385,47 +356,47 @@ impl<T: Clone> ValueBox<T> {
} }
} }
/// A size in four dimensions. /// A length in four dimensions.
pub type SizeBox = ValueBox<Size>; pub type Margins = Value4<Length>;
impl SizeBox { impl Margins {
/// The zeroed size box. /// The zeroed length box.
pub const ZERO: SizeBox = SizeBox { pub const ZERO: Margins = Margins {
left: Size::ZERO, left: Length::ZERO,
top: Size::ZERO, top: Length::ZERO,
right: Size::ZERO, right: Length::ZERO,
bottom: Size::ZERO, bottom: Length::ZERO,
}; };
} }
impl FromStr for Size { impl FromStr for Length {
type Err = ParseSizeError; type Err = ParseLengthError;
fn from_str(src: &str) -> Result<Size, ParseSizeError> { fn from_str(src: &str) -> Result<Length, ParseLengthError> {
let func = match () { let func = match () {
_ if src.ends_with("pt") => Size::pt, _ if src.ends_with("pt") => Length::pt,
_ if src.ends_with("mm") => Size::mm, _ if src.ends_with("mm") => Length::mm,
_ if src.ends_with("cm") => Size::cm, _ if src.ends_with("cm") => Length::cm,
_ if src.ends_with("in") => Size::inches, _ if src.ends_with("in") => Length::inches,
_ => return Err(ParseSizeError(())), _ => return Err(ParseLengthError),
}; };
Ok(func(src[..src.len() - 2] Ok(func(src[..src.len() - 2]
.parse::<f32>() .parse::<f64>()
.map_err(|_| ParseSizeError(()))?)) .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)] #[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 { 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 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 (@front $num:ty, $ty:ident $t:ident $o:ident
@ -458,7 +429,7 @@ macro_rules! implement_traits {
impl $tr<$ty> for $num { impl $tr<$ty> for $num {
type Output = $ty; type Output = $ty;
fn $tf($t, $o: $ty) -> $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 { impl $tr<$num> for $ty {
type Output = $ty; type Output = $ty;
fn $tf($t, $o: $num) -> $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 { 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! { Length(self, other) [points] }
implement_size! { Size2D(self, other) [x, y] } implement_size! { Size(self, other) [x, y] }

View File

@ -27,12 +27,11 @@ use toddle::{Font, OwnedData};
use toddle::query::{FontLoader, SharedFontLoader}; use toddle::query::{FontLoader, SharedFontLoader};
use toddle::query::{FontProvider, FontIndex, FontDescriptor}; use toddle::query::{FontProvider, FontIndex, FontDescriptor};
use crate::problem::Problems; use crate::diagnostic::Diagnostics;
use crate::layout::MultiLayout; use crate::layout::MultiLayout;
use crate::style::{LayoutStyle, PageStyle, TextStyle}; use crate::style::{LayoutStyle, PageStyle, TextStyle};
use crate::syntax::{SyntaxModel, Scope, Decoration, ParseState, parse}; use crate::syntax::{Decorations, SyntaxModel, Scope, ParseState, parse};
use crate::syntax::span::{Position, SpanVec, offset_spans}; use crate::syntax::span::{Offset, Pos};
/// Declare a module and reexport all its contents. /// Declare a module and reexport all its contents.
macro_rules! pub_use_mod { macro_rules! pub_use_mod {
@ -45,17 +44,17 @@ macro_rules! pub_use_mod {
#[macro_use] #[macro_use]
mod macros; mod macros;
#[macro_use] #[macro_use]
pub mod problem; pub mod diagnostic;
pub mod export; pub mod export;
#[macro_use] #[macro_use]
pub mod func; pub mod func;
pub mod layout; pub mod layout;
pub mod library; pub mod library;
pub mod size; pub mod length;
pub mod paper;
pub mod style; pub mod style;
pub mod syntax; pub mod syntax;
/// Transforms source code into typesetted layouts. /// Transforms source code into typesetted layouts.
/// ///
/// A typesetter can be configured through various methods. /// A typesetter can be configured through various methods.
@ -112,7 +111,7 @@ impl Typesetter {
/// Parse source code into a syntax tree. /// Parse source code into a syntax tree.
pub fn parse(&self, src: &str) -> Pass<SyntaxModel> { pub fn parse(&self, src: &str) -> Pass<SyntaxModel> {
parse(src, Position::ZERO, &self.parse_state) parse(src, Pos::ZERO, &self.parse_state)
} }
/// Layout a syntax tree and return the produced layout. /// Layout a syntax tree and return the produced layout.
@ -176,18 +175,18 @@ impl<T> Pass<T> {
/// User feedback data accumulated during a compilation pass. /// User feedback data accumulated during a compilation pass.
#[derive(Debug, Default, Clone, Eq, PartialEq)] #[derive(Debug, Default, Clone, Eq, PartialEq)]
pub struct Feedback { pub struct Feedback {
/// Problems in the source code. /// Diagnostics in the source code.
pub problems: Problems, pub diagnostics: Diagnostics,
/// Decorations of the source code for semantic syntax highlighting. /// Decorations of the source code for semantic syntax highlighting.
pub decos: SpanVec<Decoration>, pub decorations: Decorations,
} }
impl Feedback { impl Feedback {
/// Create a new feedback instance without errors and decos. /// Create a new feedback instance without errors and decos.
pub fn new() -> Feedback { pub fn new() -> Feedback {
Feedback { Feedback {
problems: vec![], diagnostics: vec![],
decos: vec![], decorations: vec![],
} }
} }
@ -199,15 +198,15 @@ impl Feedback {
/// Add other feedback data to this feedback. /// Add other feedback data to this feedback.
pub fn extend(&mut self, other: Feedback) { pub fn extend(&mut self, other: Feedback) {
self.problems.extend(other.problems); self.diagnostics.extend(other.diagnostics);
self.decos.extend(other.decos); self.decorations.extend(other.decorations);
} }
/// Add more feedback whose spans are local and need to be offset by an /// Add more feedback whose spans are local and need to be offset by an
/// `offset` to be correct in this feedback's context. /// `offset` to be correct in this feedback's context.
pub fn extend_offset(&mut self, more: Feedback, offset: Position) { pub fn extend_offset(&mut self, more: Feedback, offset: Pos) {
self.problems.extend(offset_spans(more.problems, offset)); self.diagnostics.extend(more.diagnostics.offset(offset));
self.decos.extend(offset_spans(more.decos, offset)); self.decorations.extend(more.decorations.offset(offset));
} }
} }

View File

@ -1,8 +1,7 @@
use toddle::query::{FontWeight, FontStyle}; use toddle::query::{FontWeight, FontStyle};
use crate::size::FSize; use crate::length::ScaleLength;
use super::*; use super::*;
function! { function! {
/// `font.family`: Set the font family. /// `font.family`: Set the font family.
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
@ -13,17 +12,17 @@ function! {
} }
parse(header, body, ctx, f) { parse(header, body, ctx, f) {
let list = header.args.pos.get_all::<StringLike>(&mut f.problems) let list = header.args.pos.get_all::<StringLike>(&mut f.diagnostics)
.map(|s| s.0.to_lowercase()) .map(|s| s.0.to_lowercase())
.collect(); .collect();
let tuples: Vec<_> = header.args.key let tuples: Vec<_> = header.args.key
.get_all::<String, Tuple>(&mut f.problems) .get_all::<String, Tuple>(&mut f.diagnostics)
.collect(); .collect();
let classes = tuples.into_iter() let classes = tuples.into_iter()
.map(|(class, mut tuple)| { .map(|(class, mut tuple)| {
let fallback = tuple.get_all::<StringLike>(&mut f.problems) let fallback = tuple.get_all::<StringLike>(&mut f.diagnostics)
.map(|s| s.0.to_lowercase()) .map(|s| s.0.to_lowercase())
.collect(); .collect();
(class.to_lowercase(), fallback) (class.to_lowercase(), fallback)
@ -64,8 +63,8 @@ function! {
parse(header, body, ctx, f) { parse(header, body, ctx, f) {
FontStyleFunc { FontStyleFunc {
body: body!(opt: body, ctx, f), body: body!(opt: body, ctx, f),
style: header.args.pos.get::<FontStyle>(&mut f.problems) style: header.args.pos.get::<FontStyle>(&mut f.diagnostics)
.or_missing(&mut f.problems, header.name.span, "style"), .or_missing(&mut f.diagnostics, header.name.span, "style"),
} }
} }
@ -84,7 +83,7 @@ function! {
parse(header, body, ctx, f) { parse(header, body, ctx, f) {
let body = body!(opt: body, ctx, f); let body = body!(opt: body, ctx, f);
let weight = header.args.pos.get::<Spanned<(FontWeight, bool)>>(&mut f.problems) let weight = header.args.pos.get::<Spanned<(FontWeight, bool)>>(&mut f.diagnostics)
.map(|Spanned { v: (weight, is_clamped), span }| { .map(|Spanned { v: (weight, is_clamped), span }| {
if is_clamped { if is_clamped {
warning!( warning!(
@ -96,7 +95,7 @@ function! {
weight weight
}) })
.or_missing(&mut f.problems, header.name.span, "weight"); .or_missing(&mut f.diagnostics, header.name.span, "weight");
FontWeightFunc { body, weight } FontWeightFunc { body, weight }
} }
@ -111,25 +110,25 @@ function! {
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub struct FontSizeFunc { pub struct FontSizeFunc {
body: Option<SyntaxModel>, body: Option<SyntaxModel>,
size: Option<FSize>, size: Option<ScaleLength>,
} }
parse(header, body, ctx, f) { parse(header, body, ctx, f) {
FontSizeFunc { FontSizeFunc {
body: body!(opt: body, ctx, f), body: body!(opt: body, ctx, f),
size: header.args.pos.get::<FSize>(&mut f.problems) size: header.args.pos.get::<ScaleLength>(&mut f.diagnostics)
.or_missing(&mut f.problems, header.name.span, "size") .or_missing(&mut f.diagnostics, header.name.span, "size")
} }
} }
layout(self, ctx, f) { layout(self, ctx, f) {
styled(&self.body, ctx, self.size, |t, s| { styled(&self.body, ctx, self.size, |t, s| {
match s { match s {
FSize::Absolute(size) => { ScaleLength::Absolute(size) => {
t.base_font_size = size; t.base_font_size = size;
t.font_scale = 1.0; t.font_scale = 1.0;
} }
FSize::Scaled(scale) => t.font_scale = scale, ScaleLength::Scaled(scale) => t.font_scale = scale,
} }
}) })
} }

View File

@ -1,7 +1,6 @@
use crate::size::PSize; use crate::length::ScaleLength;
use super::*; use super::*;
function! { function! {
/// `align`: Aligns content along the layouting axes. /// `align`: Aligns content along the layouting axes.
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
@ -13,14 +12,14 @@ function! {
parse(header, body, ctx, f) { parse(header, body, ctx, f) {
AlignFunc { AlignFunc {
body: body!(opt: body, ctx, f), body: body!(opt: body, ctx, f),
map: PosAxisMap::parse::<AxisKey>(&mut f.problems, &mut header.args), map: PosAxisMap::parse::<AxisKey>(&mut f.diagnostics, &mut header.args),
} }
} }
layout(self, ctx, f) { layout(self, ctx, f) {
ctx.base = ctx.spaces[0].dimensions; 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)) alignment.axis().map(|s| s.to_generic(ctx.axes))
}); });
@ -61,14 +60,14 @@ function! {
DirectionFunc { DirectionFunc {
name_span: header.name.span, name_span: header.name.span,
body: body!(opt: body, ctx, f), body: body!(opt: body, ctx, f),
map: PosAxisMap::parse::<AxisKey>(&mut f.problems, &mut header.args), map: PosAxisMap::parse::<AxisKey>(&mut f.diagnostics, &mut header.args),
} }
} }
layout(self, ctx, f) { layout(self, ctx, f) {
ctx.base = ctx.spaces[0].dimensions; 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)) Some(direction.axis().to_generic(ctx.axes))
}); });
@ -103,15 +102,15 @@ function! {
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub struct BoxFunc { pub struct BoxFunc {
body: SyntaxModel, body: SyntaxModel,
extents: AxisMap<PSize>, extents: AxisMap<ScaleLength>,
debug: Option<bool>, debug: Option<bool>,
} }
parse(header, body, ctx, f) { parse(header, body, ctx, f) {
BoxFunc { BoxFunc {
body: body!(opt: body, ctx, f).unwrap_or(SyntaxModel::new()), body: body!(opt: body, ctx, f).unwrap_or(SyntaxModel::new()),
extents: AxisMap::parse::<ExtentKey>(&mut f.problems, &mut header.args.key), extents: AxisMap::parse::<ExtentKey>(&mut f.diagnostics, &mut header.args.key),
debug: header.args.key.get::<bool>(&mut f.problems, "debug"), debug: header.args.key.get::<bool>(&mut f.diagnostics, "debug"),
} }
} }
@ -123,12 +122,12 @@ function! {
ctx.debug = debug; 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] { for &axis in &[Horizontal, Vertical] {
if let Some(psize) = map.get(axis) { if let Some(scale) = map.get(axis) {
let size = psize.scaled(ctx.base.get(axis)); let length = scale.scaled(ctx.base.get(axis));
*ctx.base.get_mut(axis) = size; *ctx.base.get_mut(axis) = length;
*ctx.spaces[0].dimensions.get_mut(axis) = size; *ctx.spaces[0].dimensions.get_mut(axis) = length;
*ctx.spaces[0].expansion.get_mut(axis) = true; *ctx.spaces[0].expansion.get_mut(axis) = true;
} }
} }

View File

@ -8,7 +8,6 @@ pub_use_mod!(layout);
pub_use_mod!(page); pub_use_mod!(page);
pub_use_mod!(spacing); pub_use_mod!(spacing);
/// Create a scope with all standard functions. /// Create a scope with all standard functions.
pub fn std() -> Scope { pub fn std() -> Scope {
let mut std = Scope::new::<ValFunc>(); let mut std = Scope::new::<ValFunc>();

View File

@ -1,23 +1,22 @@
use crate::size::Size; use crate::length::Length;
use crate::style::{Paper, PaperClass}; use crate::paper::{Paper, PaperClass};
use super::*; use super::*;
function! { function! {
/// `page.size`: Set the size of pages. /// `page.size`: Set the size of pages.
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub struct PageSizeFunc { pub struct PageSizeFunc {
paper: Option<Paper>, paper: Option<Paper>,
extents: AxisMap<Size>, extents: AxisMap<Length>,
flip: bool, flip: bool,
} }
parse(header, body, state, f) { parse(header, body, state, f) {
body!(nope: body, f); body!(nope: body, f);
PageSizeFunc { PageSizeFunc {
paper: header.args.pos.get::<Paper>(&mut f.problems), paper: header.args.pos.get::<Paper>(&mut f.diagnostics),
extents: AxisMap::parse::<ExtentKey>(&mut f.problems, &mut header.args.key), extents: AxisMap::parse::<ExtentKey>(&mut f.diagnostics, &mut header.args.key),
flip: header.args.key.get::<bool>(&mut f.problems, "flip").unwrap_or(false), flip: header.args.key.get::<bool>(&mut f.diagnostics, "flip").unwrap_or(false),
} }
} }
@ -26,12 +25,12 @@ function! {
if let Some(paper) = self.paper { if let Some(paper) = self.paper {
style.class = paper.class; style.class = paper.class;
style.dimensions = paper.dimensions; style.dimensions = paper.size();
} else { } else {
style.class = PaperClass::Custom; 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(Horizontal, |&width| style.dimensions.x = width);
map.with(Vertical, |&height| style.dimensions.y = height); map.with(Vertical, |&height| style.dimensions.y = height);
@ -53,13 +52,13 @@ function! {
parse(header, body, state, f) { parse(header, body, state, f) {
body!(nope: body, f); body!(nope: body, f);
PageMarginsFunc { PageMarginsFunc {
padding: PaddingMap::parse(&mut f.problems, &mut header.args), padding: PaddingMap::parse(&mut f.diagnostics, &mut header.args),
} }
} }
layout(self, ctx, f) { layout(self, ctx, f) {
let mut style = ctx.style.page; 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)] vec![SetPageStyle(style)]
} }
} }

View File

@ -1,10 +1,9 @@
use crate::size::FSize; use crate::length::ScaleLength;
use crate::layout::SpacingKind; use crate::layout::SpacingKind;
use super::*; use super::*;
use self::ContentKind::*; use self::ContentKind::*;
function! { function! {
/// `line.break`, `n`: Ends the current line. /// `line.break`, `n`: Ends the current line.
#[derive(Debug, Default, Clone, PartialEq)] #[derive(Debug, Default, Clone, PartialEq)]
@ -41,7 +40,7 @@ function! {
pub struct ContentSpacingFunc { pub struct ContentSpacingFunc {
body: Option<SyntaxModel>, body: Option<SyntaxModel>,
content: ContentKind, content: ContentKind,
spacing: Option<f32>, spacing: Option<f64>,
} }
type Meta = ContentKind; type Meta = ContentKind;
@ -50,9 +49,9 @@ function! {
ContentSpacingFunc { ContentSpacingFunc {
body: body!(opt: body, state, f), body: body!(opt: body, state, f),
content: meta, content: meta,
spacing: header.args.pos.get::<f64>(&mut f.problems) spacing: header.args.pos.get::<f64>(&mut f.diagnostics)
.map(|num| num as f32) .map(|num| num as f64)
.or_missing(&mut f.problems, header.name.span, "spacing"), .or_missing(&mut f.diagnostics, header.name.span, "spacing"),
} }
} }
@ -79,7 +78,7 @@ function! {
/// `spacing`, `h`, `v`: Adds spacing along an axis. /// `spacing`, `h`, `v`: Adds spacing along an axis.
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub struct SpacingFunc { pub struct SpacingFunc {
spacing: Option<(AxisKey, FSize)>, spacing: Option<(AxisKey, ScaleLength)>,
} }
type Meta = Option<SpecificAxis>; type Meta = Option<SpecificAxis>;
@ -88,11 +87,11 @@ function! {
body!(nope: body, f); body!(nope: body, f);
SpacingFunc { SpacingFunc {
spacing: if let Some(axis) = meta { spacing: if let Some(axis) = meta {
header.args.pos.get::<FSize>(&mut f.problems) header.args.pos.get::<ScaleLength>(&mut f.diagnostics)
.map(|s| (AxisKey::Specific(axis), s)) .map(|s| (AxisKey::Specific(axis), s))
} else { } else {
header.args.key.get_with_key::<AxisKey, FSize>(&mut f.problems) header.args.key.get_with_key::<AxisKey, ScaleLength>(&mut f.diagnostics)
}.or_missing(&mut f.problems, header.name.span, "spacing"), }.or_missing(&mut f.diagnostics, header.name.span, "spacing"),
} }
} }

275
src/paper.rs Normal file
View File

@ -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<Paper> {
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<ScaleLength> {
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<Paper> {
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")
}

View File

@ -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<Problem>;
/// 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<String>, 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,
)
};
}

View File

@ -2,8 +2,8 @@
use toddle::fallback; use toddle::fallback;
use toddle::query::{FallbackTree, FontVariant, FontStyle, FontWeight}; 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. /// Defines properties of pages and text.
#[derive(Debug, Default, Clone, PartialEq)] #[derive(Debug, Default, Clone, PartialEq)]
@ -25,35 +25,35 @@ pub struct TextStyle {
/// whether the next `*` adds or removes font weight. /// whether the next `*` adds or removes font weight.
pub bolder: bool, pub bolder: bool,
/// The base font size. /// The base font size.
pub base_font_size: Size, pub base_font_size: Length,
/// The font scale to apply on the base font size. /// 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). /// 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). /// 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). /// The paragraphs spacing (as a multiple of the font size).
pub paragraph_spacing_scale: f32, pub paragraph_spacing_scale: f64,
} }
impl TextStyle { impl TextStyle {
/// The scaled font size. /// The scaled font size.
pub fn font_size(&self) -> Size { pub fn font_size(&self) -> Length {
self.base_font_size * self.font_scale self.base_font_size * self.font_scale
} }
/// The absolute word spacing. /// The absolute word spacing.
pub fn word_spacing(&self) -> Size { pub fn word_spacing(&self) -> Length {
self.word_spacing_scale * self.font_size() self.word_spacing_scale * self.font_size()
} }
/// The absolute line spacing. /// 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() (self.line_spacing_scale - 1.0) * self.font_size()
} }
/// The absolute paragraph spacing. /// 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() (self.paragraph_spacing_scale - 1.0) * self.font_size()
} }
} }
@ -77,7 +77,7 @@ impl Default for TextStyle {
weight: FontWeight(400), weight: FontWeight(400),
}, },
bolder: false, bolder: false,
base_font_size: Size::pt(11.0), base_font_size: Length::pt(11.0),
font_scale: 1.0, font_scale: 1.0,
word_spacing_scale: 0.25, word_spacing_scale: 0.25,
line_spacing_scale: 1.2, line_spacing_scale: 1.2,
@ -92,10 +92,10 @@ pub struct PageStyle {
/// The class of this page. /// The class of this page.
pub class: PaperClass, pub class: PaperClass,
/// The width and height of the page. /// 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 /// The amount of white space on each side. If a side is set to `None`, the
/// default for the paper class is used. /// default for the paper class is used.
pub margins: ValueBox<Option<PSize>>, pub margins: Value4<Option<ScaleLength>>,
} }
impl PageStyle { impl PageStyle {
@ -103,17 +103,17 @@ impl PageStyle {
pub fn new(paper: Paper) -> PageStyle { pub fn new(paper: Paper) -> PageStyle {
PageStyle { PageStyle {
class: paper.class, class: paper.class,
dimensions: paper.dimensions, dimensions: paper.size(),
margins: ValueBox::with_all(None), margins: Value4::with_all(None),
} }
} }
/// The absolute margins. /// The absolute margins.
pub fn margins(&self) -> SizeBox { pub fn margins(&self) -> Margins {
let dims = self.dimensions; let dims = self.dimensions;
let default = self.class.default_margins(); let default = self.class.default_margins();
SizeBox { Margins {
left: self.margins.left.unwrap_or(default.left).scaled(dims.x), left: self.margins.left.unwrap_or(default.left).scaled(dims.x),
top: self.margins.top.unwrap_or(default.top).scaled(dims.y), top: self.margins.top.unwrap_or(default.top).scaled(dims.y),
right: self.margins.right.unwrap_or(default.right).scaled(dims.x), right: self.margins.right.unwrap_or(default.right).scaled(dims.x),
@ -127,270 +127,3 @@ impl Default for PageStyle {
PageStyle::new(PAPER_A4) 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<Paper> {
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<PSize> {
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<Paper> {
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")
}

View File

@ -6,13 +6,12 @@ use std::ops::Deref;
use std::str::FromStr; use std::str::FromStr;
use std::u8; use std::u8;
use crate::problem::Problems; use crate::diagnostic::Diagnostics;
use crate::size::Size; use crate::length::Length;
use super::func::{Key, Value}; use super::func::{Key, Value};
use super::span::{Span, Spanned}; use super::span::{Span, Spanned};
use super::tokens::is_identifier; use super::tokens::is_identifier;
/// An argument or return value. /// An argument or return value.
#[derive(Clone, PartialEq)] #[derive(Clone, PartialEq)]
pub enum Expr { pub enum Expr {
@ -22,8 +21,8 @@ pub enum Expr {
Str(String), Str(String),
/// A number: `1.2, 200%`. /// A number: `1.2, 200%`.
Number(f64), Number(f64),
/// A size: `2cm, 5.2in`. /// A length: `2cm, 5.2in`.
Size(Size), Length(Length),
/// A bool: `true, false`. /// A bool: `true, false`.
Bool(bool), Bool(bool),
/// A color value, including the alpha channel: `#f79143ff`. /// A color value, including the alpha channel: `#f79143ff`.
@ -32,7 +31,7 @@ pub enum Expr {
Tuple(Tuple), Tuple(Tuple),
/// A named tuple: `cmyk(37.7, 0, 3.9, 1.1)`. /// A named tuple: `cmyk(37.7, 0, 3.9, 1.1)`.
NamedTuple(NamedTuple), NamedTuple(NamedTuple),
/// An object: `{ fit: false, size: 12pt }`. /// An object: `{ fit: false, width: 12pt }`.
Object(Object), Object(Object),
/// An operator that negates the contained expression. /// An operator that negates the contained expression.
Neg(Box<Spanned<Expr>>), Neg(Box<Spanned<Expr>>),
@ -54,7 +53,7 @@ impl Expr {
Ident(_) => "identifier", Ident(_) => "identifier",
Str(_) => "string", Str(_) => "string",
Number(_) => "number", Number(_) => "number",
Size(_) => "size", Length(_) => "length",
Bool(_) => "bool", Bool(_) => "bool",
Color(_) => "color", Color(_) => "color",
Tuple(_) => "tuple", Tuple(_) => "tuple",
@ -76,7 +75,7 @@ impl Debug for Expr {
Ident(i) => i.fmt(f), Ident(i) => i.fmt(f),
Str(s) => s.fmt(f), Str(s) => s.fmt(f),
Number(n) => n.fmt(f), Number(n) => n.fmt(f),
Size(s) => s.fmt(f), Length(s) => s.fmt(f),
Bool(b) => b.fmt(f), Bool(b) => b.fmt(f),
Color(c) => c.fmt(f), Color(c) => c.fmt(f),
Tuple(t) => t.fmt(f), Tuple(t) => t.fmt(f),
@ -128,7 +127,7 @@ impl Debug for Ident {
/// ///
/// # Example /// # Example
/// ```typst /// ```typst
/// [box: background=#423abaff] /// [page: background=#423abaff]
/// ^^^^^^^^ /// ^^^^^^^^
/// ``` /// ```
#[derive(Copy, Clone, Eq, PartialEq, Hash)] #[derive(Copy, Clone, Eq, PartialEq, Hash)]
@ -256,28 +255,28 @@ impl Tuple {
} }
/// Extract (and remove) the first matching value and remove and generate /// Extract (and remove) the first matching value and remove and generate
/// problems for all previous items that did not match. /// diagnostics for all previous items that did not match.
pub fn get<V: Value>(&mut self, problems: &mut Problems) -> Option<V> { pub fn get<V: Value>(&mut self, diagnostics: &mut Diagnostics) -> Option<V> {
while !self.items.is_empty() { while !self.items.is_empty() {
let expr = self.items.remove(0); let expr = self.items.remove(0);
let span = expr.span; let span = expr.span;
match V::parse(expr) { match V::parse(expr) {
Ok(output) => return Some(output), Ok(output) => return Some(output),
Err(v) => problems.push(Spanned { v, span }), Err(v) => diagnostics.push(Spanned { v, span }),
} }
} }
None None
} }
/// Extract and return an iterator over all values that match and generate /// Extract and return an iterator over all values that match and generate
/// problems for all items that do not match. /// diagnostics for all items that do not match.
pub fn get_all<'a, V: Value>(&'a mut self, problems: &'a mut Problems) pub fn get_all<'a, V: Value>(&'a mut self, diagnostics: &'a mut Diagnostics)
-> impl Iterator<Item=V> + 'a { -> impl Iterator<Item=V> + 'a {
self.items.drain(..).filter_map(move |expr| { self.items.drain(..).filter_map(move |expr| {
let span = expr.span; let span = expr.span;
match V::parse(expr) { match V::parse(expr) {
Ok(output) => Some(output), 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. /// 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 /// # Example
/// ```typst /// ```typst
/// { fit: false, size: 12cm, items: (1, 2, 3) } /// { fit: false, width: 12cm, items: (1, 2, 3) }
/// ``` /// ```
#[derive(Default, Clone, PartialEq)] #[derive(Default, Clone, PartialEq)]
pub struct Object { pub struct Object {
@ -398,9 +393,9 @@ impl Object {
/// ///
/// Inserts an error if the value does not match. If the key is not /// Inserts an error if the value does not match. If the key is not
/// contained, no error is inserted. /// contained, no error is inserted.
pub fn get<V: Value>(&mut self, problems: &mut Problems, key: &str) -> Option<V> { pub fn get<V: Value>(&mut self, diagnostics: &mut Diagnostics, key: &str) -> Option<V> {
let index = self.pairs.iter().position(|pair| pair.v.key.v.as_str() == key)?; let index = self.pairs.iter().position(|pair| pair.v.key.v.as_str() == key)?;
self.get_index::<V>(problems, index) self.get_index::<V>(diagnostics, index)
} }
/// Extract (and remove) a pair with a matching key and value. /// Extract (and remove) a pair with a matching key and value.
@ -409,12 +404,12 @@ impl Object {
/// found, no error is inserted. /// found, no error is inserted.
pub fn get_with_key<K: Key, V: Value>( pub fn get_with_key<K: Key, V: Value>(
&mut self, &mut self,
problems: &mut Problems, diagnostics: &mut Diagnostics,
) -> Option<(K, V)> { ) -> Option<(K, V)> {
for (index, pair) in self.pairs.iter().enumerate() { for (index, pair) in self.pairs.iter().enumerate() {
let key = Spanned { v: pair.v.key.v.as_str(), span: pair.v.key.span }; let key = Spanned { v: pair.v.key.v.as_str(), span: pair.v.key.span };
if let Some(key) = K::parse(key) { if let Some(key) = K::parse(key) {
return self.get_index::<V>(problems, index).map(|value| (key, value)); return self.get_index::<V>(diagnostics, index).map(|value| (key, value));
} }
} }
None None
@ -425,7 +420,7 @@ impl Object {
/// Inserts errors for values that do not match. /// Inserts errors for values that do not match.
pub fn get_all<'a, K: Key, V: Value>( pub fn get_all<'a, K: Key, V: Value>(
&'a mut self, &'a mut self,
problems: &'a mut Problems, diagnostics: &'a mut Diagnostics,
) -> impl Iterator<Item=(K, V)> + 'a { ) -> impl Iterator<Item=(K, V)> + 'a {
let mut index = 0; let mut index = 0;
std::iter::from_fn(move || { std::iter::from_fn(move || {
@ -434,7 +429,7 @@ impl Object {
let key = Spanned { v: key.v.as_str(), span: key.span }; let key = Spanned { v: key.v.as_str(), span: key.span };
Some(if let Some(key) = K::parse(key) { Some(if let Some(key) = K::parse(key) {
self.get_index::<V>(problems, index).map(|v| (key, v)) self.get_index::<V>(diagnostics, index).map(|v| (key, v))
} else { } else {
index += 1; index += 1;
None None
@ -454,20 +449,20 @@ impl Object {
/// ``` /// ```
pub fn get_all_spanned<'a, K: Key + 'a, V: Value + 'a>( pub fn get_all_spanned<'a, K: Key + 'a, V: Value + 'a>(
&'a mut self, &'a mut self,
problems: &'a mut Problems, diagnostics: &'a mut Diagnostics,
) -> impl Iterator<Item=Spanned<(K, V)>> + 'a { ) -> impl Iterator<Item=Spanned<(K, V)>> + 'a {
self.get_all::<Spanned<K>, Spanned<V>>(problems) self.get_all::<Spanned<K>, Spanned<V>>(diagnostics)
.map(|(k, v)| Spanned::new((k.v, v.v), Span::merge(k.span, v.span))) .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 /// Extract the argument at the given index and insert an error if the value
/// does not match. /// does not match.
fn get_index<V: Value>(&mut self, problems: &mut Problems, index: usize) -> Option<V> { fn get_index<V: Value>(&mut self, diagnostics: &mut Diagnostics, index: usize) -> Option<V> {
let expr = self.pairs.remove(index).v.value; let expr = self.pairs.remove(index).v.value;
let span = expr.span; let span = expr.span;
match V::parse(expr) { match V::parse(expr) {
Ok(output) => Some(output), Ok(output) => Some(output),
Err(v) => { problems.push(Spanned { v, span }); None } Err(v) => { diagnostics.push(Spanned { v, span }); None }
} }
} }

View File

@ -7,7 +7,6 @@ use super::*;
use self::AxisKey::*; use self::AxisKey::*;
use self::PaddingKey::*; use self::PaddingKey::*;
/// Key types are used to extract keyword arguments from /// Key types are used to extract keyword arguments from
/// [`Objects`](crate::syntax::expr::Object). They represent the key part of a /// [`Objects`](crate::syntax::expr::Object). They represent the key part of a
/// keyword argument. /// keyword argument.

View File

@ -1,18 +1,17 @@
//! Deduplicating maps and keys for argument parsing. //! Deduplicating maps and keys for argument parsing.
use crate::problem::Problems; use crate::diagnostic::Diagnostics;
use crate::layout::prelude::*; use crate::layout::prelude::*;
use crate::size::{PSize, ValueBox}; use crate::length::{ScaleLength, Value4};
use crate::syntax::span::Spanned; use crate::syntax::span::Spanned;
use super::keys::*; use super::keys::*;
use super::values::*; use super::values::*;
use super::*; use super::*;
/// A map which deduplicates redundant arguments. /// A map which deduplicates redundant arguments.
/// ///
/// Whenever a duplicate argument is inserted into the map, through the /// 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. /// list that needs to be passed to those functions.
/// ///
/// All entries need to have span information to enable the error reporting. /// All entries need to have span information to enable the error reporting.
@ -28,27 +27,27 @@ impl<K, V> DedupMap<K, V> where K: Eq {
} }
/// Create a new map from an iterator of spanned keys and values. /// Create a new map from an iterator of spanned keys and values.
pub fn from_iter<I>(problems: &mut Problems, iter: I) -> DedupMap<K, V> pub fn from_iter<I>(diagnostics: &mut Diagnostics, iter: I) -> DedupMap<K, V>
where I: IntoIterator<Item=Spanned<(K, V)>> { where I: IntoIterator<Item=Spanned<(K, V)>> {
let mut map = DedupMap::new(); let mut map = DedupMap::new();
map.extend(problems, iter); map.extend(diagnostics, iter);
map map
} }
/// Add a spanned key-value pair. /// 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) { 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 { } else {
self.map.push(entry); self.map.push(entry);
} }
} }
/// Add multiple spanned key-value pairs. /// Add multiple spanned key-value pairs.
pub fn extend<I>(&mut self, problems: &mut Problems, items: I) pub fn extend<I>(&mut self, diagnostics: &mut Diagnostics, items: I)
where I: IntoIterator<Item=Spanned<(K, V)>> { where I: IntoIterator<Item=Spanned<(K, V)>> {
for item in items.into_iter() { for item in items.into_iter() {
self.insert(problems, item); self.insert(diagnostics, item);
} }
} }
@ -71,15 +70,15 @@ impl<K, V> DedupMap<K, V> where K: Eq {
} }
/// Create a new map where keys and values are mapped to new keys and /// 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. /// generated.
pub fn dedup<F, K2, V2>(&self, problems: &mut Problems, mut f: F) -> DedupMap<K2, V2> pub fn dedup<F, K2, V2>(&self, diagnostics: &mut Diagnostics, mut f: F) -> DedupMap<K2, V2>
where F: FnMut(&K, &V) -> (K2, V2), K2: Eq { where F: FnMut(&K, &V) -> (K2, V2), K2: Eq {
let mut map = DedupMap::new(); let mut map = DedupMap::new();
for Spanned { v: (key, value), span } in self.map.iter() { for Spanned { v: (key, value), span } in self.map.iter() {
let (key, value) = f(key, value); 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 map
@ -98,21 +97,21 @@ pub struct AxisMap<V>(DedupMap<AxisKey, V>);
impl<V: Value> AxisMap<V> { impl<V: Value> AxisMap<V> {
/// Parse an axis map from the object. /// Parse an axis map from the object.
pub fn parse<K>( pub fn parse<K>(
problems: &mut Problems, diagnostics: &mut Diagnostics,
object: &mut Object, object: &mut Object,
) -> AxisMap<V> where K: Key + Into<AxisKey> { ) -> AxisMap<V> where K: Key + Into<AxisKey> {
let values: Vec<_> = object let values: Vec<_> = object
.get_all_spanned::<K, V>(problems) .get_all_spanned::<K, V>(diagnostics)
.map(|s| s.map(|(k, v)| (k.into(), v))) .map(|s| s.map(|(k, v)| (k.into(), v)))
.collect(); .collect();
AxisMap(DedupMap::from_iter(problems, values)) AxisMap(DedupMap::from_iter(diagnostics, values))
} }
/// Deduplicate from specific or generic to just specific axes. /// Deduplicate from specific or generic to just specific axes.
pub fn dedup(&self, problems: &mut Problems, axes: LayoutAxes) -> DedupMap<SpecificAxis, V> pub fn dedup(&self, diagnostics: &mut Diagnostics, axes: LayoutAxes) -> DedupMap<SpecificAxis, V>
where V: Clone { 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<V>(DedupMap<PosAxisKey, V>);
impl<V: Value> PosAxisMap<V> { impl<V: Value> PosAxisMap<V> {
/// Parse a positional/axis map from the function arguments. /// Parse a positional/axis map from the function arguments.
pub fn parse<K>( pub fn parse<K>(
problems: &mut Problems, diagnostics: &mut Diagnostics,
args: &mut FuncArgs, args: &mut FuncArgs,
) -> PosAxisMap<V> where K: Key + Into<AxisKey> { ) -> PosAxisMap<V> where K: Key + Into<AxisKey> {
let mut map = DedupMap::new(); let mut map = DedupMap::new();
for &key in &[PosAxisKey::First, PosAxisKey::Second] { for &key in &[PosAxisKey::First, PosAxisKey::Second] {
if let Some(Spanned { v, span }) = args.pos.get::<Spanned<V>>(problems) { if let Some(Spanned { v, span }) = args.pos.get::<Spanned<V>>(diagnostics) {
map.insert(problems, Spanned { v: (key, v), span }) map.insert(diagnostics, Spanned { v: (key, v), span })
} }
} }
let keywords: Vec<_> = args.key let keywords: Vec<_> = args.key
.get_all_spanned::<K, V>(problems) .get_all_spanned::<K, V>(diagnostics)
.map(|s| s.map(|(k, v)| (PosAxisKey::Keyword(k.into()), v))) .map(|s| s.map(|(k, v)| (PosAxisKey::Keyword(k.into()), v)))
.collect(); .collect();
map.extend(problems, keywords); map.extend(diagnostics, keywords);
PosAxisMap(map) PosAxisMap(map)
} }
@ -149,7 +148,7 @@ impl<V: Value> PosAxisMap<V> {
/// or specific axes to just generic axes. /// or specific axes to just generic axes.
pub fn dedup<F>( pub fn dedup<F>(
&self, &self,
problems: &mut Problems, diagnostics: &mut Diagnostics,
axes: LayoutAxes, axes: LayoutAxes,
mut f: F, mut f: F,
) -> DedupMap<GenericAxis, V> ) -> DedupMap<GenericAxis, V>
@ -157,7 +156,7 @@ impl<V: Value> PosAxisMap<V> {
F: FnMut(&V) -> Option<GenericAxis>, F: FnMut(&V) -> Option<GenericAxis>,
V: Clone, V: Clone,
{ {
self.0.dedup(problems, |key, val| { self.0.dedup(diagnostics, |key, val| {
(match key { (match key {
PosAxisKey::First => f(val).unwrap_or(GenericAxis::Primary), PosAxisKey::First => f(val).unwrap_or(GenericAxis::Primary),
PosAxisKey::Second => f(val).unwrap_or(GenericAxis::Secondary), PosAxisKey::Second => f(val).unwrap_or(GenericAxis::Secondary),
@ -171,24 +170,24 @@ impl<V: Value> PosAxisMap<V> {
/// A map for storing padding given for a combination of all sides, opposing /// A map for storing padding given for a combination of all sides, opposing
/// sides or single sides. /// sides or single sides.
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub struct PaddingMap(DedupMap<PaddingKey<AxisKey>, Option<PSize>>); pub struct PaddingMap(DedupMap<PaddingKey<AxisKey>, Option<ScaleLength>>);
impl PaddingMap { impl PaddingMap {
/// Parse a padding map from the function arguments. /// 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 mut map = DedupMap::new();
let all = args.pos.get::<Spanned<Defaultable<PSize>>>(problems); let all = args.pos.get::<Spanned<Defaultable<ScaleLength>>>(diagnostics);
if let Some(Spanned { v, span }) = all { 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 let paddings: Vec<_> = args.key
.get_all_spanned::<PaddingKey<AxisKey>, Defaultable<PSize>>(problems) .get_all_spanned::<PaddingKey<AxisKey>, Defaultable<ScaleLength>>(diagnostics)
.map(|s| s.map(|(k, v)| (k, v.into()))) .map(|s| s.map(|(k, v)| (k, v.into())))
.collect(); .collect();
map.extend(problems, paddings); map.extend(diagnostics, paddings);
PaddingMap(map) PaddingMap(map)
} }
@ -196,13 +195,13 @@ impl PaddingMap {
/// Apply the specified padding on a value box of optional, scalable sizes. /// Apply the specified padding on a value box of optional, scalable sizes.
pub fn apply( pub fn apply(
&self, &self,
problems: &mut Problems, diagnostics: &mut Diagnostics,
axes: LayoutAxes, axes: LayoutAxes,
padding: &mut ValueBox<Option<PSize>> padding: &mut Value4<Option<ScaleLength>>
) { ) {
use PaddingKey::*; use PaddingKey::*;
let map = self.0.dedup(problems, |key, &val| { let map = self.0.dedup(diagnostics, |key, &val| {
(match key { (match key {
All => All, All => All,
Both(axis) => Both(axis.to_specific(axes)), Both(axis) => Both(axis.to_specific(axes)),

View File

@ -1,7 +1,7 @@
//! Primitives for argument parsing in library functions. //! Primitives for argument parsing in library functions.
use std::iter::FromIterator; use std::iter::FromIterator;
use crate::problem::{Problem, Problems}; use crate::diagnostic::{Diagnostic, Diagnostics};
use super::expr::{Expr, Ident, Tuple, Object, Pair}; use super::expr::{Expr, Ident, Tuple, Object, Pair};
use super::span::{Span, Spanned}; use super::span::{Span, Spanned};
@ -85,13 +85,13 @@ pub enum FuncArg {
pub trait OptionExt: Sized { pub trait OptionExt: Sized {
/// Add an error about a missing argument `arg` with the given span if the /// Add an error about a missing argument `arg` with the given span if the
/// option is `None`. /// 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<T> OptionExt for Option<T> { impl<T> OptionExt for Option<T> {
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() { if self.is_none() {
problems.push(error!(span, "missing argument: {}", arg)); diagnostics.push(error!(span, "missing argument: {}", arg));
} }
self self
} }

View File

@ -4,13 +4,12 @@ use std::fmt::{self, Display, Formatter};
use toddle::query::{FontStyle, FontWeight}; use toddle::query::{FontStyle, FontWeight};
use crate::layout::prelude::*; use crate::layout::prelude::*;
use crate::size::{Size, ScaleSize}; use crate::length::{Length, ScaleLength};
use crate::style::Paper; use crate::paper::Paper;
use super::*; use super::*;
use self::AlignmentValue::*; use self::AlignmentValue::*;
/// Value types are used to extract the values of positional and keyword /// Value types are used to extract the values of positional and keyword
/// arguments from [`Tuples`](crate::syntax::expr::Tuple) and /// arguments from [`Tuples`](crate::syntax::expr::Tuple) and
/// [`Objects`](crate::syntax::expr::Object). They represent the value part of /// [`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: /// An implementation for `bool` might look as follows:
/// ``` /// ```
/// # use typstc::error; /// # use typstc::error;
/// # use typstc::problem::Problem; /// # use typstc::diagnostic::Diagnostic;
/// # use typstc::syntax::expr::Expr; /// # use typstc::syntax::expr::Expr;
/// # use typstc::syntax::func::Value; /// # use typstc::syntax::func::Value;
/// # use typstc::syntax::span::Spanned; /// # use typstc::syntax::span::Spanned;
/// # struct Bool; /* /// # struct Bool; /*
/// impl Value for bool { /// impl Value for bool {
/// # */ impl Value for Bool { /// # */ impl Value for Bool {
/// fn parse(expr: Spanned<Expr>) -> Result<Self, Problem> { /// fn parse(expr: Spanned<Expr>) -> Result<Self, Diagnostic> {
/// match expr.v { /// match expr.v {
/// # /* /// # /*
/// Expr::Bool(b) => Ok(b), /// Expr::Bool(b) => Ok(b),
@ -44,11 +43,11 @@ use self::AlignmentValue::*;
pub trait Value: Sized { pub trait Value: Sized {
/// Parse an expression into this value or return an error if the expression /// Parse an expression into this value or return an error if the expression
/// is valid for this value type. /// is valid for this value type.
fn parse(expr: Spanned<Expr>) -> Result<Self, Problem>; fn parse(expr: Spanned<Expr>) -> Result<Self, Diagnostic>;
} }
impl<V: Value> Value for Spanned<V> { impl<V: Value> Value for Spanned<V> {
fn parse(expr: Spanned<Expr>) -> Result<Self, Problem> { fn parse(expr: Spanned<Expr>) -> Result<Self, Diagnostic> {
let span = expr.span; let span = expr.span;
V::parse(expr).map(|v| Spanned { v, span }) V::parse(expr).map(|v| Spanned { v, span })
} }
@ -58,7 +57,7 @@ impl<V: Value> Value for Spanned<V> {
macro_rules! value { macro_rules! value {
($type:ty, $name:expr, $($p:pat => $r:expr),* $(,)?) => { ($type:ty, $name:expr, $($p:pat => $r:expr),* $(,)?) => {
impl Value for $type { impl Value for $type {
fn parse(expr: Spanned<Expr>) -> Result<Self, Problem> { fn parse(expr: Spanned<Expr>) -> Result<Self, Diagnostic> {
#[allow(unreachable_patterns)] #[allow(unreachable_patterns)]
match expr.v { match expr.v {
$($p => Ok($r)),*, $($p => Ok($r)),*,
@ -77,13 +76,13 @@ value!(Ident, "identifier", Expr::Ident(i) => i);
value!(String, "string", Expr::Str(s) => s); value!(String, "string", Expr::Str(s) => s);
value!(f64, "number", Expr::Number(n) => n); value!(f64, "number", Expr::Number(n) => n);
value!(bool, "bool", Expr::Bool(b) => b); 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!(Tuple, "tuple", Expr::Tuple(t) => t);
value!(Object, "object", Expr::Object(o) => o); value!(Object, "object", Expr::Object(o) => o);
value!(ScaleSize, "number or size", value!(ScaleLength, "number or length",
Expr::Size(size) => ScaleSize::Absolute(size), Expr::Length(length) => ScaleLength::Absolute(length),
Expr::Number(scale) => ScaleSize::Scaled(scale as f32), Expr::Number(scale) => ScaleLength::Scaled(scale as f64),
); );
/// A value type that matches [`Expr::Ident`] and [`Expr::Str`] and implements /// A value type that matches [`Expr::Ident`] and [`Expr::Str`] and implements
@ -108,20 +107,20 @@ impl From<StringLike> for String {
/// # Example /// # Example
/// ``` /// ```
/// # use typstc::syntax::func::{FuncArgs, Defaultable}; /// # use typstc::syntax::func::{FuncArgs, Defaultable};
/// # use typstc::size::Size; /// # use typstc::length::Length;
/// # let mut args = FuncArgs::new(); /// # let mut args = FuncArgs::new();
/// # let mut errors = vec![]; /// # let mut errors = vec![];
/// args.key.get::<Defaultable<Size>>(&mut errors, "size"); /// args.key.get::<Defaultable<Length>>(&mut errors, "length");
/// ``` /// ```
/// This will yield. /// This will yield.
/// ```typst /// ```typst
/// [func: size=default] => None /// [func: length=default] => None
/// [func: size=2cm] => Some(Size::cm(2.0)) /// [func: length=2cm] => Some(Length::cm(2.0))
/// ``` /// ```
pub struct Defaultable<V>(pub Option<V>); pub struct Defaultable<V>(pub Option<V>);
impl<V: Value> Value for Defaultable<V> { impl<V: Value> Value for Defaultable<V> {
fn parse(expr: Spanned<Expr>) -> Result<Self, Problem> { fn parse(expr: Spanned<Expr>) -> Result<Self, Diagnostic> {
Ok(Defaultable(match expr.v { Ok(Defaultable(match expr.v {
Expr::Ident(ident) if ident.as_str() == "default" => None, Expr::Ident(ident) if ident.as_str() == "default" => None,
_ => Some(V::parse(expr)?) _ => Some(V::parse(expr)?)
@ -136,7 +135,7 @@ impl<V> From<Defaultable<V>> for Option<V> {
} }
impl Value for FontStyle { impl Value for FontStyle {
fn parse(expr: Spanned<Expr>) -> Result<Self, Problem> { fn parse(expr: Spanned<Expr>) -> Result<Self, Diagnostic> {
FontStyle::from_name(Ident::parse(expr)?.as_str()) FontStyle::from_name(Ident::parse(expr)?.as_str())
.ok_or_else(|| error!("invalid font style")) .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 /// The additional boolean specifies whether a number was clamped into the range
/// 100 - 900 to make it a valid font weight. /// 100 - 900 to make it a valid font weight.
impl Value for (FontWeight, bool) { impl Value for (FontWeight, bool) {
fn parse(expr: Spanned<Expr>) -> Result<Self, Problem> { fn parse(expr: Spanned<Expr>) -> Result<Self, Diagnostic> {
match expr.v { match expr.v {
Expr::Number(weight) => { Expr::Number(weight) => {
let weight = weight.round(); let weight = weight.round();
@ -170,14 +169,14 @@ impl Value for (FontWeight, bool) {
} }
impl Value for Paper { impl Value for Paper {
fn parse(expr: Spanned<Expr>) -> Result<Self, Problem> { fn parse(expr: Spanned<Expr>) -> Result<Self, Diagnostic> {
Paper::from_name(Ident::parse(expr)?.as_str()) Paper::from_name(Ident::parse(expr)?.as_str())
.ok_or_else(|| error!("invalid paper type")) .ok_or_else(|| error!("invalid paper type"))
} }
} }
impl Value for Direction { impl Value for Direction {
fn parse(expr: Spanned<Expr>) -> Result<Self, Problem> { fn parse(expr: Spanned<Expr>) -> Result<Self, Diagnostic> {
Ok(match Ident::parse(expr)?.as_str() { Ok(match Ident::parse(expr)?.as_str() {
"left-to-right" | "ltr" | "LTR" => LeftToRight, "left-to-right" | "ltr" | "LTR" => LeftToRight,
"right-to-left" | "rtl" | "RTL" => RightToLeft, "right-to-left" | "rtl" | "RTL" => RightToLeft,
@ -250,7 +249,7 @@ impl AlignmentValue {
} }
impl Value for AlignmentValue { impl Value for AlignmentValue {
fn parse(expr: Spanned<Expr>) -> Result<Self, Problem> { fn parse(expr: Spanned<Expr>) -> Result<Self, Diagnostic> {
Ok(match Ident::parse(expr)?.as_str() { Ok(match Ident::parse(expr)?.as_str() {
"origin" => Align(Origin), "origin" => Align(Origin),
"center" => Align(Center), "center" => Align(Center),

View File

@ -20,7 +20,6 @@ pub_use_mod!(scope);
pub_use_mod!(parsing); pub_use_mod!(parsing);
pub_use_mod!(tokens); pub_use_mod!(tokens);
/// Represents a parsed piece of source that can be layouted and in the future /// Represents a parsed piece of source that can be layouted and in the future
/// also be queried for information used for refactorings, autocomplete, etc. /// also be queried for information used for refactorings, autocomplete, etc.
#[async_trait(?Send)] #[async_trait(?Send)]
@ -94,6 +93,9 @@ impl PartialEq for Node {
} }
} }
/// A list of spanned decorations.
pub type Decorations = SpanVec<Decoration>;
/// Decorations for semantic syntax highlighting. /// Decorations for semantic syntax highlighting.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Serialize)] #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Serialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
@ -110,7 +112,6 @@ pub enum Decoration {
/// ^^^^^^ /// ^^^^^^
/// ``` /// ```
InvalidFuncName, InvalidFuncName,
/// A key of a keyword argument: /// A key of a keyword argument:
/// ```typst /// ```typst
/// [box: width=5cm] /// [box: width=5cm]
@ -123,7 +124,6 @@ pub enum Decoration {
/// ^^^^ ^^^^^ /// ^^^^ ^^^^^
/// ``` /// ```
ObjectKey, ObjectKey,
/// An italic word. /// An italic word.
Italic, Italic,
/// A bold word. /// A bold word.

View File

@ -4,9 +4,12 @@ use std::str::FromStr;
use super::expr::*; use super::expr::*;
use super::func::{FuncCall, FuncHeader, FuncArgs, FuncArg}; use super::func::{FuncCall, FuncHeader, FuncArgs, FuncArg};
use super::span::{Position, Span, Spanned}; use super::span::{Pos, Span, Spanned};
use super::*; use super::*;
/// A function which parses a function call into a model.
pub type CallParser = dyn Fn(FuncCall, &ParseState) -> Pass<Box<dyn Model>>;
/// The state which can influence how a string of source code is parsed. /// 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 /// 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 /// `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 /// the start of the function as a whole as opposed to the start of the
/// function's body. /// function's body.
pub fn parse(src: &str, offset: Position, state: &ParseState) -> Pass<SyntaxModel> { pub fn parse(src: &str, offset: Pos, state: &ParseState) -> Pass<SyntaxModel> {
let mut model = SyntaxModel::new(); let mut model = SyntaxModel::new();
let mut feedback = Feedback::new(); let mut feedback = Feedback::new();
@ -102,7 +105,7 @@ impl<'s> FuncParser<'s> {
state, state,
// Start at column 1 because the opening bracket is also part of // Start at column 1 because the opening bracket is also part of
// the function, but not part of the `header` string. // 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, peeked: None,
body, body,
feedback: Feedback::new(), 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) (parser, header)
} else { } else {
// Parse the body with the fallback parser even when the header is // Parse the body with the fallback parser even when the header is
@ -186,7 +189,7 @@ impl<'s> FuncParser<'s> {
self.skip_white(); self.skip_white();
let key = ident; let key = ident;
self.feedback.decos.push( self.feedback.decorations.push(
Spanned::new(Decoration::ArgumentKey, key.span) Spanned::new(Decoration::ArgumentKey, key.span)
); );
@ -325,7 +328,7 @@ impl FuncParser<'_> {
} }
Token::ExprNumber(n) => self.eat_span(Expr::Number(n)), 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::ExprBool(b) => self.eat_span(Expr::Bool(b)),
Token::ExprHex(s) => { Token::ExprHex(s) => {
if let Ok(color) = RgbaColor::from_str(s) { if let Ok(color) = RgbaColor::from_str(s) {
@ -423,7 +426,7 @@ impl FuncParser<'_> {
continue; continue;
} }
self.feedback.decos.push( self.feedback.decorations.push(
Spanned::new(Decoration::ObjectKey, key.span) 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) { if self.check(token) {
self.eat(); self.eat();
true 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); 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() { if self.eof() {
self.expected_at(thing, pos) self.expected_at(thing, pos)
} else { } else {
@ -544,7 +547,7 @@ impl<'s> FuncParser<'s> {
self.peek().is_none() self.peek().is_none()
} }
fn pos(&self) -> Position { fn pos(&self) -> Pos {
self.peeked.flatten() self.peeked.flatten()
.map(|s| s.span.start) .map(|s| s.span.start)
.unwrap_or_else(|| self.tokens.pos()) .unwrap_or_else(|| self.tokens.pos())
@ -604,13 +607,13 @@ fn unescape_raw(raw: &str) -> Vec<String> {
#[cfg(test)] #[cfg(test)]
#[allow(non_snake_case)] #[allow(non_snake_case)]
mod tests { mod tests {
use crate::size::Size; use crate::length::Length;
use super::super::test::{check, DebugFn}; use super::super::test::{check, DebugFn};
use super::super::func::Value; use super::super::func::Value;
use super::*; use super::*;
use Decoration::*; use Decoration::*;
use Expr::{Number as Num, Size as Sz, Bool}; use Expr::{Number as Num, Length as Len, Bool};
use Node::{ use Node::{
Space as S, ToggleItalic as Italic, ToggleBolder as Bold, Space as S, ToggleItalic as Italic, ToggleBolder as Bold,
Parbreak, Linebreak, Parbreak, Linebreak,
@ -625,7 +628,7 @@ mod tests {
p!($source => [$($model)*], []); p!($source => [$($model)*], []);
}; };
($source:expr => [$($model:tt)*], [$($problems:tt)*] $(, [$($decos:tt)*])? $(,)?) => { ($source:expr => [$($model:tt)*], [$($diagnostics:tt)*] $(, [$($decos:tt)*])? $(,)?) => {
let mut scope = Scope::new::<DebugFn>(); let mut scope = Scope::new::<DebugFn>();
scope.add::<DebugFn>("f"); scope.add::<DebugFn>("f");
scope.add::<DebugFn>("n"); scope.add::<DebugFn>("n");
@ -633,25 +636,25 @@ mod tests {
scope.add::<DebugFn>("val"); scope.add::<DebugFn>("val");
let state = ParseState { scope }; let state = ParseState { scope };
let pass = parse($source, Position::ZERO, &state); let pass = parse($source, Pos::ZERO, &state);
// Test model. // Test model.
let (exp, cmp) = span_vec![$($model)*]; let (exp, cmp) = span_vec![$($model)*];
check($source, exp, pass.output.nodes, cmp); check($source, exp, pass.output.nodes, cmp);
// Test problems. // Test diagnostics.
let (exp, cmp) = span_vec![$($problems)*]; let (exp, cmp) = span_vec![$($diagnostics)*];
let exp = exp.into_iter() let exp = exp.into_iter()
.map(|s: Spanned<&str>| s.map(|e| e.to_string())) .map(|s: Spanned<&str>| s.map(|e| e.to_string()))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let found = pass.feedback.problems.into_iter() let found = pass.feedback.diagnostics.into_iter()
.map(|s| s.map(|e| e.message)) .map(|s| s.map(|e| e.message))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
check($source, exp, found, cmp); check($source, exp, found, cmp);
// Test decos. // Test decos.
$(let (exp, cmp) = span_vec![$($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 Id(text: &str) -> Expr { Expr::Ident(Ident(text.to_string())) }
fn Str(text: &str) -> Expr { Expr::Str(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 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 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)) } fn ColorHealed() -> Expr { Expr::Color(RgbaColor::new_healed(0, 0, 0, 255)) }
@ -878,8 +880,8 @@ mod tests {
pval!("name" => (Id("name"))); pval!("name" => (Id("name")));
pval!("\"hi\"" => (Str("hi"))); pval!("\"hi\"" => (Str("hi")));
pval!("3.14" => (Num(3.14))); pval!("3.14" => (Num(3.14)));
pval!("4.5cm" => (Sz(Size::cm(4.5)))); pval!("4.5cm" => (Len(Length::cm(4.5))));
pval!("12e1pt" => (Pt(12e1))); pval!("12e1pt" => (Len(Length::pt(12e1))));
pval!("#f7a20500" => (ColorStr("f7a20500"))); pval!("#f7a20500" => (ColorStr("f7a20500")));
pval!("\"a\n[]\\\"string\"" => (Str("a\n[]\"string"))); pval!("\"a\n[]\\\"string\"" => (Str("a\n[]\"string")));
@ -890,10 +892,10 @@ mod tests {
pval!("(hi)" => (Id("hi"))); pval!("(hi)" => (Id("hi")));
// Math. // 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!("5 - 0.01" => (Sub(Num(5.0), Num(0.01))));
pval!("(3mm * 2)" => (Mul(Sz(Size::mm(3.0)), Num(2.0)))); pval!("(3mm * 2)" => (Mul(Len(Length::mm(3.0)), Num(2.0))));
pval!("12e-3cm/1pt" => (Div(Sz(Size::cm(12e-3)), Sz(Size::pt(1.0))))); pval!("12e-3cm/1pt" => (Div(Len(Length::cm(12e-3)), Len(Length::pt(1.0)))));
// Unclosed string. // Unclosed string.
p!("[val: \"hello]" => [func!("val": (Str("hello]")), {})], [ p!("[val: \"hello]" => [func!("val": (Str("hello]")), {})], [
@ -912,26 +914,24 @@ mod tests {
fn parse_complex_mathematical_expressions() { fn parse_complex_mathematical_expressions() {
// Valid expressions. // Valid expressions.
pval!("(3.2in + 6pt)*(5/2-1)" => (Mul( 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)) Sub(Div(Num(5.0), Num(2.0)), Num(1.0))
))); )));
pval!("(6.3E+2+4* - 3.2pt)/2" => (Div( 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) Num(2.0)
))); )));
// Associativity of multiplication and division. // Associativity of multiplication and division.
p!("[val: 3/4*5]" => pval!("3/4*5" => (Mul(Div(Num(3.0), Num(4.0)), Num(5.0))));
[func!("val": (Mul(Div(Num(3.0), Num(4.0)), Num(5.0))), {})]
);
// Invalid expressions. // 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:10, 0:11, "dangling minus"),
(0:6, 0:10, "missing right summand") (0:6, 0:10, "missing right summand")
]); ]);
p!("[val: 3mm+4pt*]" => 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")], [(0:10, 0:14, "missing right factor")],
); );
} }
@ -976,7 +976,7 @@ mod tests {
// Nested tuples. // Nested tuples.
pval!("css(1pt, rgb(90, 102, 254), \"solid\")" => (named_tuple!( pval!("css(1pt, rgb(90, 102, 254), \"solid\")" => (named_tuple!(
"css", "css",
Pt(1.0), Len(Length::pt(1.0)),
named_tuple!("rgb", Num(90.0), Num(102.0), Num(254.0)), named_tuple!("rgb", Num(90.0), Num(102.0), Num(254.0)),
Str("solid"), Str("solid"),
))); )));
@ -1012,7 +1012,7 @@ mod tests {
// Missing key. // Missing key.
p!("[val: {,}]" => [val()], [(0:7, 0:8, "expected key, found comma")]); 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")]); p!("[val: { : }]" => [val()], [(0:8, 0:9, "expected key, found colon")]);
// Missing colon. // Missing colon.
@ -1053,7 +1053,7 @@ mod tests {
Num(1.0), Num(1.0),
object!( object!(
"ab" => tuple!(), "ab" => tuple!(),
"d" => tuple!(Num(3.0), Pt(14.0)), "d" => tuple!(Num(3.0), Len(Length::pt(14.0))),
), ),
), ),
Bool(false), Bool(false),
@ -1085,7 +1085,7 @@ mod tests {
#[test] #[test]
fn parse_multiple_mixed_arguments() { fn parse_multiple_mixed_arguments() {
p!("[val: 12pt, key=value]" => 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)], [(0:12, 0:15, ArgumentKey), (0:1, 0:4, ValidFuncName)],
); );
pval!("a , x=\"b\" , c" => (Id("a"), Id("c")), { "x" => Str("b"), }); pval!("a , x=\"b\" , c" => (Id("a"), Id("c")), { "x" => Str("b"), });
@ -1144,7 +1144,7 @@ mod tests {
fn parse_invalid_commas() { fn parse_invalid_commas() {
// Missing commas. // Missing commas.
p!("[val: 1pt 1]" => 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")], [(0:9, 0:9, "expected comma")],
); );
p!(r#"[val: _"s"]"# => p!(r#"[val: _"s"]"# =>

View File

@ -3,16 +3,14 @@
use std::collections::HashMap; use std::collections::HashMap;
use std::fmt::{self, Debug, Formatter}; use std::fmt::{self, Debug, Formatter};
use crate::Pass;
use crate::func::ParseFunc; use crate::func::ParseFunc;
use super::func::FuncCall; use super::parsing::CallParser;
use super::parsing::ParseState;
use super::Model; use super::Model;
/// A map from identifiers to function parsers. /// A map from identifiers to function parsers.
pub struct Scope { pub struct Scope {
parsers: HashMap<String, Box<Parser>>, parsers: HashMap<String, Box<CallParser>>,
fallback: Box<Parser>, fallback: Box<CallParser>,
} }
impl Scope { impl Scope {
@ -22,7 +20,7 @@ impl Scope {
where F: ParseFunc<Meta=()> + Model + 'static { where F: ParseFunc<Meta=()> + Model + 'static {
Scope { Scope {
parsers: HashMap::new(), parsers: HashMap::new(),
fallback: parser::<F>(()), fallback: make_parser::<F>(()),
} }
} }
@ -43,17 +41,17 @@ impl Scope {
where F: ParseFunc + Model + 'static { where F: ParseFunc + Model + 'static {
self.parsers.insert( self.parsers.insert(
name.to_string(), name.to_string(),
parser::<F>(metadata), make_parser::<F>(metadata),
); );
} }
/// Return the parser with the given name if there is one. /// 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) self.parsers.get(name).map(AsRef::as_ref)
} }
/// Return the fallback parser. /// Return the fallback parser.
pub fn get_fallback_parser(&self) -> &Parser { pub fn get_fallback_parser(&self) -> &CallParser {
&*self.fallback &*self.fallback
} }
} }
@ -66,11 +64,7 @@ impl Debug for Scope {
} }
} }
/// A function which parses the source of a function into a model type which fn make_parser<F>(metadata: <F as ParseFunc>::Meta) -> Box<CallParser>
/// implements [`Model`].
type Parser = dyn Fn(FuncCall, &ParseState) -> Pass<Box<dyn Model>>;
fn parser<F>(metadata: <F as ParseFunc>::Meta) -> Box<Parser>
where F: ParseFunc + Model + 'static { where F: ParseFunc + Model + 'static {
Box::new(move |f, s| { Box::new(move |f, s| {
F::parse(f, s, metadata.clone()) F::parse(f, s, metadata.clone())

View File

@ -4,16 +4,22 @@ use std::fmt::{self, Debug, Formatter};
use std::ops::{Add, Sub}; use std::ops::{Add, Sub};
use serde::Serialize; 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`. /// A vector of spanned values of type `T`.
pub type SpanVec<T> = Vec<Spanned<T>>; pub type SpanVec<T> = Vec<Spanned<T>>;
/// [Offset](Span::offset) all spans in a vector of spanned things by a start impl<T> Offset for SpanVec<T> {
/// position. fn offset(mut self, by: Pos) -> Self {
pub fn offset_spans<T>( for spanned in &mut self {
vec: SpanVec<T>, spanned.span = spanned.span.offset(by);
start: Position, }
) -> impl Iterator<Item=Spanned<T>> { self
vec.into_iter().map(move |s| s.map_span(|span| span.offset(start))) }
} }
/// A value with the span it corresponds to in the source code. /// A value with the span it corresponds to in the source code.
@ -53,6 +59,12 @@ impl<T> Spanned<T> {
} }
} }
impl<T> Offset for Spanned<T> {
fn offset(self, by: Pos) -> Self {
self.map_span(|span| span.offset(by))
}
}
impl<T: Debug> Debug for Spanned<T> { impl<T: Debug> Debug for Spanned<T> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
self.v.fmt(f)?; self.v.fmt(f)?;
@ -68,20 +80,25 @@ impl<T: Debug> Debug for Spanned<T> {
#[derive(Copy, Clone, Eq, PartialEq, Hash, Serialize)] #[derive(Copy, Clone, Eq, PartialEq, Hash, Serialize)]
pub struct Span { pub struct Span {
/// The inclusive start position. /// The inclusive start position.
pub start: Position, pub start: Pos,
/// The inclusive end position. /// The inclusive end position.
pub end: Position, pub end: Pos,
} }
impl Span { impl Span {
/// The zero 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. /// 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 } 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. /// Create a new span with the earlier start and later end position.
pub fn merge(a: Span, b: Span) -> Span { pub fn merge(a: Span, b: Span) -> 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. /// Expand a span by merging it with another span.
pub fn expand(&mut self, other: Span) { pub fn expand(&mut self, other: Span) {
*self = Span::merge(*self, other) *self = Span::merge(*self, other)
} }
}
/// Offset a span by a start position. impl Offset for Span {
/// fn offset(self, by: Pos) -> Self {
/// This is, for example, used to translate error spans from function local
/// to global.
pub fn offset(self, start: Position) -> Span {
Span { Span {
start: start + self.start, start: self.start.offset(by),
end: start + self.end, end: self.end.offset(by),
} }
} }
} }
@ -120,34 +130,40 @@ impl Debug for Span {
/// Zero-indexed line-column position in source code. /// Zero-indexed line-column position in source code.
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize)] #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize)]
pub struct Position { pub struct Pos {
/// The zero-indexed line. /// The zero-indexed line.
pub line: usize, pub line: usize,
/// The zero-indexed column. /// The zero-indexed column.
pub column: usize, pub column: usize,
} }
impl Position { impl Pos {
/// The line 0, column 0 position. /// 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. /// Create a new position from line and column.
pub fn new(line: usize, column: usize) -> Position { pub fn new(line: usize, column: usize) -> Pos {
Position { line, column } Pos { line, column }
} }
} }
impl Add for Position { impl Offset for Pos {
type Output = Position; 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 { if rhs.line == 0 {
Position { Pos {
line: self.line, line: self.line,
column: self.column + rhs.column column: self.column + rhs.column
} }
} else { } else {
Position { Pos {
line: self.line + rhs.line, line: self.line + rhs.line,
column: rhs.column, column: rhs.column,
} }
@ -155,17 +171,17 @@ impl Add for Position {
} }
} }
impl Sub for Position { impl Sub for Pos {
type Output = Position; type Output = Pos;
fn sub(self, rhs: Position) -> Position { fn sub(self, rhs: Pos) -> Pos {
if self.line == rhs.line { if self.line == rhs.line {
Position { Pos {
line: 0, line: 0,
column: self.column - rhs.column column: self.column - rhs.column
} }
} else { } else {
Position { Pos {
line: self.line - rhs.line, line: self.line - rhs.line,
column: self.column, 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 { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "{}:{}", self.line, self.column) write!(f, "{}:{}", self.line, self.column)
} }

View File

@ -39,11 +39,11 @@ macro_rules! span_vec {
macro_rules! span_item { macro_rules! span_item {
(($sl:tt:$sc:tt, $el:tt:$ec:tt, $v:expr)) => ({ (($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 { Spanned {
span: Span::new( span: Span::new(
Position::new($sl, $sc), Pos::new($sl, $sc),
Position::new($el, $ec) Pos::new($el, $ec)
), ),
v: $v v: $v
} }

View File

@ -2,8 +2,8 @@ use std::iter::Peekable;
use std::str::Chars; use std::str::Chars;
use unicode_xid::UnicodeXID; use unicode_xid::UnicodeXID;
use crate::size::Size; use crate::length::Length;
use super::span::{Position, Span, Spanned}; use super::span::{Pos, Span, Spanned};
use Token::*; use Token::*;
use TokenMode::*; use TokenMode::*;
@ -73,8 +73,8 @@ pub enum Token<'s> {
}, },
/// A number in a function header: `3.14`. /// A number in a function header: `3.14`.
ExprNumber(f64), ExprNumber(f64),
/// A size in a function header: `12pt`. /// A length in a function header: `12pt`.
ExprSize(Size), ExprLength(Length),
/// A boolean in a function header: `true | false`. /// A boolean in a function header: `true | false`.
ExprBool(bool), ExprBool(bool),
/// A hex value in a function header: `#20d82a`. /// A hex value in a function header: `#20d82a`.
@ -129,7 +129,7 @@ impl<'s> Token<'s> {
ExprIdent(_) => "identifier", ExprIdent(_) => "identifier",
ExprStr { .. } => "string", ExprStr { .. } => "string",
ExprNumber(_) => "number", ExprNumber(_) => "number",
ExprSize(_) => "size", ExprLength(_) => "length",
ExprBool(_) => "bool", ExprBool(_) => "bool",
ExprHex(_) => "hex value", ExprHex(_) => "hex value",
Plus => "plus", Plus => "plus",
@ -153,7 +153,7 @@ pub struct Tokens<'s> {
src: &'s str, src: &'s str,
mode: TokenMode, mode: TokenMode,
iter: Peekable<Chars<'s>>, iter: Peekable<Chars<'s>>,
position: Position, pos: Pos,
index: usize, index: usize,
} }
@ -172,12 +172,12 @@ impl<'s> Tokens<'s> {
/// ///
/// The first token's span starts an the given `offset` position instead of /// The first token's span starts an the given `offset` position instead of
/// the zero position. /// 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 { Tokens {
src, src,
mode, mode,
iter: src.chars().peekable(), iter: src.chars().peekable(),
position: offset, pos: offset,
index: 0, index: 0,
} }
} }
@ -190,8 +190,8 @@ impl<'s> Tokens<'s> {
/// The line-colunn position in the source at which the last token ends and /// The line-colunn position in the source at which the last token ends and
/// next token will start. /// next token will start.
pub fn pos(&self) -> Position { pub fn pos(&self) -> Pos {
self.position self.pos
} }
} }
@ -315,14 +315,14 @@ impl<'s> Tokens<'s> {
}, true, 0, -2).0) }, 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); self.read_string_until(|n| !n.is_whitespace(), false, 0, 0);
let end = self.pos(); let end = self.pos();
Space(end.line - start.line) 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); let (header, terminated) = self.read_function_part(Header);
self.eat(); self.eat();
@ -354,7 +354,7 @@ impl<'s> Tokens<'s> {
self.eat(); self.eat();
match n { 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_line_comment(); }
'/' if self.peek() == Some('*') => { self.read_block_comment(); } '/' if self.peek() == Some('*') => { self.read_block_comment(); }
'"' if mode == Header => { self.read_string(); } '"' if mode == Header => { self.read_string(); }
@ -427,8 +427,8 @@ impl<'s> Tokens<'s> {
ExprNumber(num) ExprNumber(num)
} else if let Some(num) = parse_percentage(text) { } else if let Some(num) = parse_percentage(text) {
ExprNumber(num / 100.0) ExprNumber(num / 100.0)
} else if let Ok(size) = text.parse::<Size>() { } else if let Ok(length) = text.parse::<Length>() {
ExprSize(size) ExprLength(length)
} else if is_identifier(text) { } else if is_identifier(text) {
ExprIdent(text) ExprIdent(text)
} else { } else {
@ -476,10 +476,10 @@ impl<'s> Tokens<'s> {
self.index += c.len_utf8(); self.index += c.len_utf8();
if is_newline_char(c) && !(c == '\r' && self.peek() == Some('\n')) { if is_newline_char(c) && !(c == '\r' && self.peek() == Some('\n')) {
self.position.line += 1; self.pos.line += 1;
self.position.column = 0; self.pos.column = 0;
} else { } else {
self.position.column += 1; self.pos.column += 1;
} }
Some(c) Some(c)
@ -543,7 +543,7 @@ mod tests {
LeftBrace as LB, RightBrace as RB, LeftBrace as LB, RightBrace as RB,
ExprIdent as Id, ExprIdent as Id,
ExprNumber as Num, ExprNumber as Num,
ExprSize as Sz, ExprLength as Len,
ExprBool as Bool, ExprBool as Bool,
ExprHex as Hex, ExprHex as Hex,
Text as T, Text as T,
@ -557,7 +557,7 @@ mod tests {
macro_rules! t { macro_rules! t {
($mode:expr, $source:expr => [$($tokens:tt)*]) => { ($mode:expr, $source:expr => [$($tokens:tt)*]) => {
let (exp, spans) = span_vec![$($tokens)*]; let (exp, spans) = span_vec![$($tokens)*];
let found = Tokens::new($source, Position::ZERO, $mode).collect::<Vec<_>>(); let found = Tokens::new($source, Pos::ZERO, $mode).collect::<Vec<_>>();
check($source, exp, found, spans); check($source, exp, found, spans);
} }
} }
@ -640,13 +640,13 @@ mod tests {
t!(Header, "__main__" => [Id("__main__")]); t!(Header, "__main__" => [Id("__main__")]);
t!(Header, ".func.box" => [Id(".func.box")]); 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, "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, "12_pt, 12pt" => [Invalid("12_pt"), Comma, S(0), Len(Length::pt(12.0))]);
t!(Header, "1e5in" => [Sz(Size::inches(100000.0))]); t!(Header, "1e5in" => [Len(Length::inches(100000.0))]);
t!(Header, "2.3cm" => [Sz(Size::cm(2.3))]); t!(Header, "2.3cm" => [Len(Length::cm(2.3))]);
t!(Header, "12e-3in" => [Sz(Size::inches(12e-3))]); t!(Header, "12e-3in" => [Len(Length::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, "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, "(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, "2.4.cm" => [Invalid("2.4.cm")]);
t!(Header, "(1,2)" => [LP, Num(1.0), Comma, Num(2.0), RP]); t!(Header, "(1,2)" => [LP, Num(1.0), Comma, Num(2.0), RP]);
t!(Header, "{abc}" => [LB, Id("abc"), RB]); t!(Header, "{abc}" => [LB, Id("abc"), RB]);

View File

@ -12,13 +12,13 @@ use futures_executor::block_on;
use typstc::{Typesetter, DebugErrorProvider}; use typstc::{Typesetter, DebugErrorProvider};
use typstc::layout::MultiLayout; use typstc::layout::MultiLayout;
use typstc::size::{Size, Size2D, ValueBox}; use typstc::length::{Length, Size, Value4};
use typstc::style::{PageStyle, PaperClass}; use typstc::style::PageStyle;
use typstc::paper::PaperClass;
use typstc::export::pdf; use typstc::export::pdf;
use toddle::query::FontIndex; use toddle::query::FontIndex;
use toddle::query::fs::EagerFsProvider; use toddle::query::fs::EagerFsProvider;
type DynResult<T> = Result<T, Box<dyn Error>>; type DynResult<T> = Result<T, Box<dyn Error>>;
fn main() -> DynResult<()> { fn main() -> DynResult<()> {
@ -72,8 +72,8 @@ fn test(name: &str, src: &str) -> DynResult<()> {
typesetter.set_page_style(PageStyle { typesetter.set_page_style(PageStyle {
class: PaperClass::Custom, class: PaperClass::Custom,
dimensions: Size2D::with_all(Size::pt(250.0)), dimensions: Size::with_all(Length::pt(250.0)),
margins: ValueBox::with_all(None), margins: Value4::with_all(None),
}); });
let layouts = compile(&typesetter, src); let layouts = compile(&typesetter, src);
@ -123,14 +123,14 @@ fn test(name: &str, src: &str) -> DynResult<()> {
fn compile(typesetter: &Typesetter, src: &str) -> MultiLayout { fn compile(typesetter: &Typesetter, src: &str) -> MultiLayout {
if cfg!(debug_assertions) { if cfg!(debug_assertions) {
let typeset = block_on(typesetter.typeset(src)); let typeset = block_on(typesetter.typeset(src));
let problems = typeset.feedback.problems; let diagnostics = typeset.feedback.diagnostics;
if !problems.is_empty() { if !diagnostics.is_empty() {
for problem in problems { for diagnostic in diagnostics {
println!(" {:?} {:?}: {}", println!(" {:?} {:?}: {}",
problem.v.severity, diagnostic.v.level,
problem.span, diagnostic.span,
problem.v.message diagnostic.v.message
); );
} }
} }