From 72a9631b038d1a60e4e4a78e92cd69e6f8ce4316 Mon Sep 17 00:00:00 2001 From: Laurenz Date: Thu, 5 Dec 2019 19:48:37 +0100 Subject: [PATCH] =?UTF-8?q?Move=20arg=20parser=20into=20`FuncArgs`=20and?= =?UTF-8?q?=20create=20(incomplete)=20consistent=20map=20=F0=9F=A7=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/export/pdf.rs | 10 +- src/func/args.rs | 215 ----------------------- src/func/macros.rs | 22 ++- src/func/map.rs | 59 +++++++ src/func/mod.rs | 36 ++-- src/layout/flex.rs | 127 ++------------ src/layout/mod.rs | 34 +++- src/layout/stack.rs | 57 +----- src/layout/tree.rs | 6 +- src/lib.rs | 10 +- src/library/align.rs | 37 ++-- src/library/boxed.rs | 21 ++- src/library/keys.rs | 172 +++++++++++++++++++ src/library/mod.rs | 100 ++++------- src/macros.rs | 24 ++- src/size.rs | 28 ++- src/syntax/mod.rs | 204 +++++++++++++++++++--- src/syntax/parsing.rs | 371 +++++++++++++++++----------------------- src/syntax/span.rs | 14 +- src/syntax/tokens.rs | 2 +- tests/layouting.rs | 4 +- tests/layouts/align.typ | 2 +- 22 files changed, 765 insertions(+), 790 deletions(-) delete mode 100644 src/func/args.rs create mode 100644 src/func/map.rs create mode 100644 src/library/keys.rs diff --git a/src/export/pdf.rs b/src/export/pdf.rs index 8370f4543..8d6393c97 100644 --- a/src/export/pdf.rs +++ b/src/export/pdf.rs @@ -399,16 +399,16 @@ pub enum PdfExportError { } error_type! { - err: PdfExportError, + self: PdfExportError, res: PdfResult, - show: f => match err { + show: f => match self { PdfExportError::Font(err) => write!(f, "font error: {}", err), PdfExportError::Io(err) => write!(f, "io error: {}", err), }, - source: match err { + source: match self { PdfExportError::Font(err) => Some(err), PdfExportError::Io(err) => Some(err), }, - from: (io::Error, PdfExportError::Io(err)), - from: (FontError, PdfExportError::Font(err)), + from: (err: io::Error, PdfExportError::Io(err)), + from: (err: FontError, PdfExportError::Font(err)), } diff --git a/src/func/args.rs b/src/func/args.rs deleted file mode 100644 index d1d49b6a7..000000000 --- a/src/func/args.rs +++ /dev/null @@ -1,215 +0,0 @@ -//! Parsing, storing and deduplication of function arguments. - -use super::prelude::*; -use Expression::*; - -/// Provides a convenient interface to parse the arguments to a function. -pub struct ArgParser<'a> { - args: &'a FuncArgs, - positional_index: usize, -} - -impl<'a> ArgParser<'a> { - pub fn new(args: &'a FuncArgs) -> ArgParser<'a> { - ArgParser { - args, - positional_index: 0, - } - } - - /// Get the next positional argument of the given type. - /// - /// If there are no more arguments or the type is wrong, - /// this will return an error. - pub fn get_pos(&mut self) -> ParseResult> - where T: Argument<'a> { - Self::expected(self.get_pos_opt::()?) - } - - /// Get the next positional argument if there is any. - /// - /// If the argument is of the wrong type, this will return an error. - pub fn get_pos_opt(&mut self) -> ParseResult>> - where T: Argument<'a> { - let arg = self.args.positional - .get(self.positional_index) - .map(T::from_expr) - .transpose(); - - if let Ok(Some(_)) = arg { - self.positional_index += 1; - } - - arg - } - - /// Get a keyword argument with the given key and type. - pub fn get_key(&mut self, key: &str) -> ParseResult> - where T: Argument<'a> { - Self::expected(self.get_key_opt::(key)?) - } - - /// Get a keyword argument with the given key and type if it is present. - pub fn get_key_opt(&mut self, key: &str) -> ParseResult>> - where T: Argument<'a> { - self.args.keyword.iter() - .find(|entry| entry.val.0.val == key) - .map(|entry| T::from_expr(&entry.val.1)) - .transpose() - } - - /// Assert that there are no positional arguments left. Returns an error - /// otherwise. - pub fn done(&self) -> ParseResult<()> { - if self.positional_index == self.args.positional.len() { - Ok(()) - } else { - error!(unexpected_argument); - } - } - - /// Covert an option to a result with an error on `None`. - fn expected(val: Option>) -> ParseResult> - where T: Argument<'a> { - val.ok_or_else(|| error!(@"expected {}", T::ERROR_MESSAGE)) - } -} - -/// A kind of argument. -pub trait Argument<'a> { - type Output; - const ERROR_MESSAGE: &'static str; - - fn from_expr(expr: &'a Spanned) -> ParseResult>; -} - -macro_rules! arg { - ($type:ident, $err:expr, $doc:expr, $output:ty, $wanted:pat => $converted:expr) => ( - #[doc = $doc] - #[doc = " argument for use with the [`ArgParser`]."] - pub struct $type; - impl<'a> Argument<'a> for $type { - type Output = $output; - const ERROR_MESSAGE: &'static str = $err; - - fn from_expr(expr: &'a Spanned) -> ParseResult> { - #[allow(unreachable_code)] - match &expr.val { - $wanted => Ok(Spanned::new($converted, expr.span)), - _ => error!("expected {}", $err), - } - } - } - ); -} - -arg!(ArgExpr, "expression", "A generic expression", &'a Expression, expr => &expr); -arg!(ArgIdent, "identifier", "An identifier (e.g. `horizontal`)", &'a str, Ident(s) => s.as_str()); -arg!(ArgStr, "string", "A string (e.g. `\"Hello\"`)", &'a str, Str(s) => s.as_str()); -arg!(ArgNum, "number", "A number (e.g. `5.4`)", f64, Num(n) => *n); -arg!(ArgSize, "size", "A size (e.g. `12pt`)", crate::size::Size, Size(s) => *s); -arg!(ArgBool, "bool", "A boolean (`true` or `false`)", bool, Bool(b) => *b); - -/// An argument key which identifies a layouting axis. -#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] -pub enum AxisKey { - Primary, - Secondary, - Vertical, - Horizontal, -} - -impl AxisKey { - /// The generic version of this axis key in the given system of axes. - pub fn generic(&self, axes: LayoutAxes) -> GenericAxisKind { - match self { - Primary => GenericAxisKind::Primary, - Secondary => GenericAxisKind::Secondary, - Vertical => axes.vertical(), - Horizontal => axes.horizontal(), - } - } - - /// The specific version of this axis key in the given system of axes. - pub fn specific(&self, axes: LayoutAxes) -> SpecificAxisKind { - match self { - Primary => axes.primary(), - Secondary => axes.secondary(), - Vertical => SpecificAxisKind::Vertical, - Horizontal => SpecificAxisKind::Horizontal, - } - } -} - -/// An argument key which identifies a target alignment. -#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] -pub enum AlignmentKey { - Left, - Top, - Right, - Bottom, - Origin, - Center, - End, -} - -impl AlignmentKey { - /// The generic axis this alignment key corresopnds to in the given system - /// of layouting axes. Falls back to `default` if the alignment is generic. - pub fn axis(&self, axes: LayoutAxes, default: GenericAxisKind) -> GenericAxisKind { - use AlignmentKey::*; - match self { - Origin | Center | End => default, - Left | Right => axes.horizontal(), - Top | Bottom => axes.vertical(), - } - } - - /// The generic version of this alignment in the given system of layouting - /// axes. Returns an error if the alignment is invalid for the given axis. - pub fn generic(&self, axes: LayoutAxes, axis: GenericAxisKind) -> LayoutResult { - use AlignmentKey::*; - - let horizontal = axis == axes.horizontal(); - Ok(match self { - Origin => Alignment::Origin, - Center => Alignment::Center, - End => Alignment::End, - Left if horizontal => axes.left(), - Right if horizontal => axes.right(), - Top if !horizontal => axes.top(), - Bottom if !horizontal => axes.bottom(), - _ => error!( - "invalid alignment `{}` for {} axis", - format!("{:?}", self).to_lowercase(), - format!("{:?}", axis).to_lowercase() - ) - }) - } - - /// The specific version of this alignment in the given system of layouting - /// axes. - pub fn specific(&self, axes: LayoutAxes, axis: SpecificAxisKind) -> AlignmentKey { - use AlignmentKey::*; - match (self, axis) { - (Origin, SpecificAxisKind::Horizontal) => Left, - (End, SpecificAxisKind::Horizontal) => Right, - (Origin, SpecificAxisKind::Vertical) => Top, - (End, SpecificAxisKind::Vertical) => Bottom, - _ => *self, - } - } -} - -/// An argument key which identifies a margin or padding target. -/// -/// A is the axis type used. -#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] -pub enum PaddingKey { - /// All four sides should have the specified padding. - All, - /// Both sides of the given axis should have the specified padding. - Axis(A), - /// Only the given side of the given axis should have the specified padding. - AxisAligned(A, AlignmentKey), -} diff --git a/src/func/macros.rs b/src/func/macros.rs index 0ffdc857c..daae27693 100644 --- a/src/func/macros.rs +++ b/src/func/macros.rs @@ -10,6 +10,13 @@ macro_rules! function { function!(@meta $type | $($rest)*); }; + // Parse a tuple struct. + ($(#[$outer:meta])* pub struct $type:ident($($fields:tt)*); $($rest:tt)*) => { + $(#[$outer])* + pub struct $type($($fields)*); + function!(@meta $type | $($rest)*); + }; + // Parse a struct with fields. ($(#[$outer:meta])* pub struct $type:ident { $($fields:tt)* } $($rest:tt)*) => { $(#[$outer])* @@ -68,14 +75,17 @@ macro_rules! function { type Meta = $meta; fn parse( - header: &FuncHeader, - $body: Option<&str>, + args: FuncArgs, + $body: Option>, $ctx: ParseContext, $metadata: Self::Meta, ) -> ParseResult where Self: Sized { - let mut $args = $crate::func::args::ArgParser::new(&header.args); + #[allow(unused_mut)] + let mut $args = args; let val = $code; - $args.done()?; + if !$args.is_empty() { + error!(unexpected_argument); + } Ok(val) } } @@ -117,7 +127,7 @@ macro_rules! parse { (optional: $body:expr, $ctx:expr) => ( if let Some(body) = $body { - Some($crate::syntax::parse(body, $ctx)?) + Some($crate::syntax::parse(body.v, $ctx)?) } else { None } @@ -125,7 +135,7 @@ macro_rules! parse { (expected: $body:expr, $ctx:expr) => ( if let Some(body) = $body { - $crate::syntax::parse(body, $ctx)? + $crate::syntax::parse(body.v, $ctx)? } else { error!("expected body"); } diff --git a/src/func/map.rs b/src/func/map.rs new file mode 100644 index 000000000..880fe3e61 --- /dev/null +++ b/src/func/map.rs @@ -0,0 +1,59 @@ +//! A deduplicating map. + +use std::collections::HashMap; +use std::hash::Hash; + +use crate::syntax::{Spanned, ParseResult}; + +/// A deduplicating map type useful for storing possibly redundant arguments. +#[derive(Debug, Clone, PartialEq)] +pub struct ConsistentMap where K: Hash + Eq { + map: HashMap, +} + +impl ConsistentMap where K: Hash + Eq { + pub fn new() -> ConsistentMap { + ConsistentMap { map: HashMap::new() } + } + + /// Add a key-value pair. + pub fn add(&mut self, key: K, value: V) -> ParseResult<()> { + self.map.insert(key, value); + // TODO + Ok(()) + } + + /// Add a key-value pair if the value is not `None`. + pub fn add_opt(&mut self, key: K, value: Option) -> ParseResult<()> { + Ok(if let Some(value) = value { + self.add(key, value)?; + }) + } + + /// Add a key-spanned-value pair the value is not `None`. + pub fn add_opt_span(&mut self, key: K, value: Option>) -> ParseResult<()> { + Ok(if let Some(spanned) = value { + self.add(key, spanned.v)?; + }) + } + + /// Call a function with the value if the key is present. + pub fn with(&self, key: K, callback: F) where F: FnOnce(&V) { + if let Some(value) = self.map.get(&key) { + callback(value); + } + } + + /// Create a new consistent map where keys and values are mapped to new + /// keys and values. Returns an error if a new key is duplicate. + pub fn dedup(&self, _f: F) -> ParseResult> + where F: FnOnce(K, V) -> ParseResult<(K2, V2)>, K2: Hash + Eq { + // TODO + Ok(ConsistentMap::new()) + } + + /// Iterate over the (key, value) pairs. + pub fn iter(&self) -> std::collections::hash_map::Iter<'_, K, V> { + self.map.iter() + } +} diff --git a/src/func/mod.rs b/src/func/mod.rs index 31e315922..7ee7d779c 100644 --- a/src/func/mod.rs +++ b/src/func/mod.rs @@ -8,17 +8,12 @@ use self::prelude::*; #[macro_use] pub mod macros; -pub mod args; +pub mod map; /// Useful imports for creating your own functions. pub mod prelude { - pub use Command::*; - pub use super::args::*; - pub use super::{Scope, ParseFunc, LayoutFunc, Command, Commands}; - pub use crate::syntax::{SyntaxTree, FuncHeader, FuncArgs, Expression, Spanned, Span}; - pub use crate::syntax::{parse, ParseContext, ParseResult}; - pub use crate::size::{Size, Size2D, SizeBox}; - pub use crate::style::{PageStyle, TextStyle}; + pub use super::map::ConsistentMap; + pub use crate::func::{Scope, ParseFunc, LayoutFunc, Command, Commands}; pub use crate::layout::{ layout_tree, Layout, MultiLayout, LayoutContext, LayoutSpace, LayoutSpaces, @@ -26,16 +21,25 @@ pub mod prelude { LayoutAlignment, Alignment, SpacingKind, LayoutResult, }; + pub use crate::syntax::{ + parse, ParseContext, ParseResult, + SyntaxTree, FuncCall, FuncArgs, PosArg, KeyArg, + Expression, Ident, ExpressionKind, + Spanned, Span + }; + pub use crate::size::{Size, Size2D, SizeBox, ScaleSize, FSize, PSize}; + pub use crate::style::{LayoutStyle, PageStyle, TextStyle}; + pub use Command::*; } /// Types representing functions that are parsed from source code. pub trait ParseFunc { - type Meta; + type Meta: Clone; /// Parse the header and body into this function given a context. fn parse( - header: &FuncHeader, - body: Option<&str>, + args: FuncArgs, + body: Option>, ctx: ParseContext, metadata: Self::Meta, ) -> ParseResult where Self: Sized; @@ -126,8 +130,8 @@ pub struct Scope { /// A function which parses the source of a function into a function type which /// implements [`LayoutFunc`]. type Parser = dyn Fn( - &FuncHeader, - Option<&str>, + FuncArgs, + Option>, ParseContext ) -> ParseResult>; @@ -153,11 +157,11 @@ impl Scope { /// Add a parseable type with additional metadata that is given to the /// parser (other than the default of `()`). pub fn add_with_metadata(&mut self, name: &str, metadata: T) - where F: ParseFunc + LayoutFunc + 'static, T: 'static { + where F: ParseFunc + LayoutFunc + 'static, T: 'static + Clone { self.parsers.insert( name.to_owned(), - Box::new(|h, b, c| { - F::parse(h, b, c, metadata) + Box::new(move |a, b, c| { + F::parse(a, b, c, metadata.clone()) .map(|f| Box::new(f) as Box) }) ); diff --git a/src/layout/flex.rs b/src/layout/flex.rs index 46d669512..139019682 100644 --- a/src/layout/flex.rs +++ b/src/layout/flex.rs @@ -50,7 +50,7 @@ impl PartialLine { usable, content: vec![], dimensions: Size2D::zero(), - space: LastSpacing::Forbidden, + space: LastSpacing::Hard, } } } @@ -72,7 +72,7 @@ impl FlexLayouter { let stack = StackLayouter::new(StackContext { spaces: ctx.spaces, axes: ctx.axes, - expand: ctx.expand, + alignment: ctx.alignment, }); let usable = stack.primary_usable(); @@ -167,130 +167,27 @@ impl FlexLayouter { } fn finish_line(&mut self) -> LayoutResult { - self.finish_partial_line(); - - if self.axes.primary.needs_expansion() { - self.line.combined_dimensions.x = self.line.usable; - } - - self.stack.add(Layout { - dimensions: self.axes.specialize(self.line.combined_dimensions), - actions: self.line.actions.to_vec(), - debug_render: false, - })?; - - self.stack.add_spacing(self.flex_spacing, SpacingKind::Independent); - - let remaining = self.axes.specialize(Size2D { - x: self.part.usable - - self.part.dimensions.x - - self.part.space.soft_or_zero(), - y: self.line.combined_dimensions.y, - }); - - self.start_line(); - - Ok(remaining) + unimplemented!() } fn start_line(&mut self) { - let usable = self.stack.primary_usable(); - self.line = FlexLine::new(usable); - self.part = PartialLine::new(usable); + unimplemented!() } + #[allow(dead_code)] fn finish_partial_line(&mut self) { - let factor = self.axes.primary.axis.factor(); - let anchor = - self.axes.primary.anchor(self.line.usable) - - self.axes.primary.anchor(self.part.dimensions.x); - - for (offset, layout) in self.part.content.drain(..) { - let pos = self.axes.specialize(Size2D::with_x(anchor + factor * offset)); - self.line.actions.add_layout(pos, layout); - } - - self.line.combined_dimensions.x = match self.axes.primary.alignment { - Alignment::Origin => self.part.dimensions.x, - Alignment::Center => self.part.usable / 2 + self.part.dimensions.x / 2, - Alignment::End => self.part.usable, - }; - - self.line.combined_dimensions.y.max_eq(self.part.dimensions.y); + unimplemented!() } - fn layout_box(&mut self, boxed: Layout) -> LayoutResult<()> { - let size = self.axes.generalize(boxed.dimensions); - let new_dimension = self.part.dimensions.x - + size.x - + self.part.space.soft_or_zero(); - - if new_dimension > self.part.usable { - self.finish_line()?; - - while size.x > self.line.usable { - if self.stack.space_is_last() { - error!("box of size {} does not fit into line of size {}", - size.x, self.line.usable); - } - - self.stack.finish_space(true); - } - } - - if let LastSpacing::Soft(space) = self.part.space { - self.layout_space(space, SpacingKind::Hard); - } - - let offset = self.part.dimensions.x; - self.part.content.push((offset, boxed)); - - self.part.dimensions.x += size.x; - self.part.dimensions.y.max_eq(size.y); - self.part.space = LastSpacing::Allowed; - - Ok(()) + fn layout_box(&mut self, _boxed: Layout) -> LayoutResult<()> { + unimplemented!() } - fn layout_space(&mut self, space: Size, kind: SpacingKind) { - if kind == SpacingKind::Soft { - if self.part.space != LastSpacing::Forbidden { - self.part.space = LastSpacing::Soft(space); - } - } else { - if self.part.dimensions.x + space > self.part.usable { - self.part.dimensions.x = self.part.usable; - } else { - self.part.dimensions.x += space; - } - - if kind == SpacingKind::Hard { - self.part.space = LastSpacing::Forbidden; - } - } + fn layout_space(&mut self, _space: Size, _kind: SpacingKind) { + unimplemented!() } - fn layout_set_axes(&mut self, axes: LayoutAxes) { - if axes.primary != self.axes.primary { - self.finish_partial_line(); - - let extent = self.line.combined_dimensions.x; - let usable = self.line.usable; - - let new_usable = match axes.primary.alignment { - Alignment::Origin if extent == Size::zero() => usable, - Alignment::Center if extent < usable / 2 => usable - 2 * extent, - Alignment::End => usable - extent, - _ => Size::zero(), - }; - - self.part = PartialLine::new(new_usable); - } - - if axes.secondary != self.axes.secondary { - self.stack.set_axes(axes); - } - - self.axes = axes; + fn layout_set_axes(&mut self, _axes: LayoutAxes) { + unimplemented!() } } diff --git a/src/layout/mod.rs b/src/layout/mod.rs index 690e91b78..e31d2c175 100644 --- a/src/layout/mod.rs +++ b/src/layout/mod.rs @@ -10,7 +10,7 @@ use crate::TypesetResult; use crate::func::Command; use crate::size::{Size, Size2D, SizeBox}; use crate::style::{LayoutStyle, TextStyle}; -use crate::syntax::{FuncCall, Node, SyntaxTree}; +use crate::syntax::{Node, SyntaxTree, FuncCall}; mod actions; mod tree; @@ -137,6 +137,19 @@ impl LayoutAxes { self.generalize(size) } + /// Return the specified generic axis. + pub fn get_generic(&self, axis: GenericAxisKind) -> Axis { + match axis { + GenericAxisKind::Primary => self.primary, + GenericAxisKind::Secondary => self.secondary, + } + } + + /// Return the specified specific axis. + pub fn get_specific(&self, axis: SpecificAxisKind) -> Axis { + self.get_generic(axis.generic(*self)) + } + /// Returns the generic axis kind which is the horizontal axis. pub fn horizontal(&self) -> GenericAxisKind { match self.primary.is_horizontal() { @@ -237,6 +250,14 @@ pub enum GenericAxisKind { } impl GenericAxisKind { + /// The specific version of this axis in the given system of axes. + pub fn specific(&self, axes: LayoutAxes) -> SpecificAxisKind { + match self { + GenericAxisKind::Primary => axes.primary(), + GenericAxisKind::Secondary => axes.secondary(), + } + } + /// The other axis. pub fn inv(&self) -> GenericAxisKind { match self { @@ -254,6 +275,14 @@ pub enum SpecificAxisKind { } impl SpecificAxisKind { + /// The generic version of this axis in the given system of axes. + pub fn generic(&self, axes: LayoutAxes) -> GenericAxisKind { + match self { + SpecificAxisKind::Horizontal => axes.horizontal(), + SpecificAxisKind::Vertical => axes.vertical(), + } + } + /// The other axis. pub fn inv(&self) -> SpecificAxisKind { match self { @@ -330,6 +359,7 @@ enum LastSpacing { } impl LastSpacing { + #[allow(dead_code)] fn soft_or_zero(&self) -> Size { match self { LastSpacing::Soft(space, _) => *space, @@ -339,7 +369,7 @@ impl LastSpacing { } /// Layout components that can be serialized. -trait Serialize { +pub trait Serialize { /// Serialize the data structure into an output writable. fn serialize(&self, f: &mut W) -> io::Result<()>; } diff --git a/src/layout/stack.rs b/src/layout/stack.rs index 176aa2613..3f9af3500 100644 --- a/src/layout/stack.rs +++ b/src/layout/stack.rs @@ -244,51 +244,12 @@ impl StackLayouter { self.layouts } - pub fn finish_space(&mut self, hard: bool) { - self.finish_subspace(); - - let space = self.ctx.spaces[self.space.index]; - - self.layouts.push(Layout { - dimensions: match self.ctx.expand { - true => space.dimensions, - false => self.space.combined_dimensions.padded(space.padding), - }, - baseline: None, - alignment: self.ctx.alignment, - actions: actions.to_vec(), - }); - - self.start_space(self.next_space(), hard); + pub fn finish_space(&mut self, _hard: bool) { + unimplemented!() } fn finish_subspace(&mut self) { - let factor = self.ctx.axes.secondary.axis.factor(); - let anchor = - self.ctx.axes.anchor(self.sub.usable) - - self.ctx.axes.anchor(Size2D::with_y(self.sub.dimensions.y)); - - for (offset, layout_anchor, layout) in self.sub.boxes.drain(..) { - let pos = self.sub.origin - + self.ctx.axes.specialize( - anchor + Size2D::new(-layout_anchor, factor * offset) - ); - - self.space.actions.add_layout(pos, layout); - } - - if self.ctx.axes.primary.needs_expansion() { - self.sub.dimensions.x = self.sub.usable.x; - } - - if self.ctx.axes.secondary.needs_expansion() { - self.sub.dimensions.y = self.sub.usable.y; - } - - let space = self.ctx.spaces[self.space.index]; - let origin = self.sub.origin; - let dimensions = self.ctx.axes.specialize(self.sub.dimensions); - self.space.combined_dimensions.max_eq(origin - space.start() + dimensions); + unimplemented!() } /// Start a new space with the given index. @@ -304,17 +265,7 @@ impl StackLayouter { /// The remaining sub fn remaining_subspace(&self) -> (Size2D, Size2D) { - let new_origin = self.sub.origin + match self.ctx.axes.secondary.axis.is_positive() { - true => self.ctx.axes.specialize(Size2D::with_y(self.sub.dimensions.y)), - false => Size2D::zero(), - }; - - let new_usable = self.ctx.axes.specialize(Size2D { - x: self.sub.usable.x, - y: self.sub.usable.y - self.sub.dimensions.y - self.sub.space.soft_or_zero(), - }); - - (new_origin, new_usable) + unimplemented!() } fn next_space(&self) -> usize { diff --git a/src/layout/tree.rs b/src/layout/tree.rs index c9d40e936..4370ceab0 100644 --- a/src/layout/tree.rs +++ b/src/layout/tree.rs @@ -1,5 +1,5 @@ -use super::*; use smallvec::smallvec; +use super::*; pub fn layout_tree(tree: &SyntaxTree, ctx: LayoutContext) -> LayoutResult { let mut layouter = TreeLayouter::new(ctx); @@ -31,7 +31,7 @@ impl<'a, 'p> TreeLayouter<'a, 'p> { fn layout(&mut self, tree: &SyntaxTree) -> LayoutResult<()> { for node in &tree.nodes { - match &node.val { + match &node.v { Node::Text(text) => self.layout_text(text)?, Node::Space => self.layout_space(), @@ -75,7 +75,7 @@ impl<'a, 'p> TreeLayouter<'a, 'p> { fn layout_func(&mut self, func: &FuncCall) -> LayoutResult<()> { let spaces = self.flex.remaining(); - let commands = func.body.val.layout(LayoutContext { + let commands = func.call.layout(LayoutContext { loader: self.ctx.loader, style: &self.style, top_level: false, diff --git a/src/lib.rs b/src/lib.rs index 3369d01c9..e63ec936f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -135,14 +135,14 @@ impl TypesetError { } error_type! { - err: TypesetError, + self: TypesetError, show: f => { - write!(f, "{}", err.message)?; - if let Some(span) = err.span { + write!(f, "{}", self.message)?; + if let Some(span) = self.span { write!(f, " at {}", span)?; } Ok(()) }, - from: (std::io::Error, TypesetError::with_message(err.to_string())), - from: (FontError, TypesetError::with_message(err.to_string())), + from: (err: std::io::Error, TypesetError::with_message(err.to_string())), + from: (err: FontError, TypesetError::with_message(err.to_string())), } diff --git a/src/library/align.rs b/src/library/align.rs index 417d8f072..eea25dfab 100644 --- a/src/library/align.rs +++ b/src/library/align.rs @@ -1,29 +1,25 @@ use crate::func::prelude::*; +use super::keys::*; function! { /// `align`: Aligns content along the layouting axes. #[derive(Debug, PartialEq)] pub struct Align { body: Option, - map: ArgMap, + map: ConsistentMap, } parse(args, body, ctx) { - let mut map = ArgMap::new(); - map.put(Key::First, args.get_pos_opt::()?)?; - map.put(Key::Second, args.get_pos_opt::()?)?; + let mut map = ConsistentMap::new(); + + map.add_opt_span(Key::First, args.get_pos_opt::()?)?; + map.add_opt_span(Key::Second, args.get_pos_opt::()?)?; for arg in args.keys() { - let key = match arg.val.0.val { - "horizontal" => Key::Axis(AxisKey::Horizontal), - "vertical" => Key::Axis(AxisKey::Vertical), - "primary" => Key::Axis(AxisKey::Primary), - "secondary" => Key::Axis(AxisKey::Secondary), - _ => error!(unexpected_argument), - }; + let axis = AxisKey::from_ident(&arg.v.key)?; + let value = AlignmentKey::from_expr(arg.v.value)?; - let value = AlignmentKey::parse(arg.val.1.val)?; - map.add(key, value); + map.add(Key::Axis(axis), value)?; } Align { @@ -34,24 +30,23 @@ function! { layout(self, mut ctx) { let axes = ctx.axes; - let basic = axes.primary.is_horizontal(); - let map = self.map.dedup(|key, val| { + let map = self.map.dedup(|key, alignment| { let axis = match key { - Key::First => val.axis(axes, GenericAxisKind::Primary), - Key::Second => val.axis(axes, GenericAxisKind::Secondary), + Key::First => alignment.axis(axes, GenericAxisKind::Primary), + Key::Second => alignment.axis(axes, GenericAxisKind::Secondary), Key::Axis(AxisKey::Primary) => GenericAxisKind::Primary, Key::Axis(AxisKey::Secondary) => GenericAxisKind::Secondary, Key::Axis(AxisKey::Horizontal) => axes.horizontal(), Key::Axis(AxisKey::Vertical) => axes.vertical(), }; - let alignment = val.generic(axes, axis)?; - Ok((key, alignment)) + let alignment = alignment.generic(axes, axis)?; + Ok((axis, alignment)) })?; - map.with(GenericAxisKind::Primary, |val| ctx.alignment.primary = val); - map.with(GenericAxisKind::Secondary, |val| ctx.alignment.secondary = val); + map.with(GenericAxisKind::Primary, |&val| ctx.alignment.primary = val); + map.with(GenericAxisKind::Secondary, |&val| ctx.alignment.secondary = val); match &self.body { Some(body) => vec![AddMultiple(layout_tree(&body, ctx)?)], diff --git a/src/library/boxed.rs b/src/library/boxed.rs index a2df45e38..ef5ae24e1 100644 --- a/src/library/boxed.rs +++ b/src/library/boxed.rs @@ -1,18 +1,19 @@ use crate::func::prelude::*; +use super::keys::*; function! { /// `box`: Layouts content into a box. #[derive(Debug, PartialEq)] pub struct Boxed { body: SyntaxTree, - map: ArgMap, + map: ConsistentMap, } parse(args, body, ctx) { - let mut map = ArgMap::new(); + let mut map = ConsistentMap::new(); for arg in args.keys() { - let key = match arg.val.0.val { + let key = match arg.v.key.v.0.as_str() { "width" | "w" => AxisKey::Horizontal, "height" | "h" => AxisKey::Vertical, "primary-size" => AxisKey::Primary, @@ -20,8 +21,8 @@ function! { _ => error!(unexpected_argument), }; - let size = ArgParser::convert::(arg.val.1.val)?; - map.add(key, size); + let size = Size::from_expr(arg.v.value)?; + map.add(key, size)?; } Boxed { @@ -31,13 +32,11 @@ function! { } layout(self, mut ctx) { - let map = self.map.dedup(|key, val| { - Ok((key.specific(ctx.axes), val)) - }); + let map = self.map.dedup(|key, val| Ok((key.specific(ctx.axes), val)))?; - let mut dimensions = &mut ctx.spaces[0].dimensions; - map.with(AxisKey::Horizontal, |val| dimensions.x = val); - map.with(AxisKey::Vertical, |val| dimensions.y = val); + let dimensions = &mut ctx.spaces[0].dimensions; + map.with(SpecificAxisKind::Horizontal, |&val| dimensions.x = val); + map.with(SpecificAxisKind::Vertical, |&val| dimensions.y = val); vec![AddMultiple(layout_tree(&self.body, ctx)?)] } diff --git a/src/library/keys.rs b/src/library/keys.rs new file mode 100644 index 000000000..df6580279 --- /dev/null +++ b/src/library/keys.rs @@ -0,0 +1,172 @@ +use crate::func::prelude::*; + +macro_rules! kind { + ($type:ty, $name:expr, $($patterns:tt)*) => { + impl $type { + /// Parse this key from an identifier. + pub fn from_ident(ident: &Spanned) -> ParseResult { + Ok(match ident.v.0.as_str() { + $($patterns)* + _ => error!("expected {}", ::NAME), + }) + } + } + + impl ExpressionKind for $type { + const NAME: &'static str = $name; + + fn from_expr(expr: Spanned) -> ParseResult { + if let Expression::Ident(ident) = expr.v { + Self::from_ident(&Spanned::new(ident, expr.span)) + } else { + error!("expected {}", Self::NAME); + } + } + } + }; +} + +/// An argument key which identifies a layouting axis. +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] +pub enum AxisKey { + Primary, + Secondary, + Vertical, + Horizontal, +} + +impl AxisKey { + /// The generic version of this axis key in the given system of axes. + pub fn generic(&self, axes: LayoutAxes) -> GenericAxisKind { + match self { + AxisKey::Primary => GenericAxisKind::Primary, + AxisKey::Secondary => GenericAxisKind::Secondary, + AxisKey::Vertical => axes.vertical(), + AxisKey::Horizontal => axes.horizontal(), + } + } + + /// The specific version of this axis key in the given system of axes. + pub fn specific(&self, axes: LayoutAxes) -> SpecificAxisKind { + match self { + AxisKey::Primary => axes.primary(), + AxisKey::Secondary => axes.secondary(), + AxisKey::Vertical => SpecificAxisKind::Vertical, + AxisKey::Horizontal => SpecificAxisKind::Horizontal, + } + } +} + +kind!(AxisKey, "axis", + "horizontal" => AxisKey::Horizontal, + "vertical" => AxisKey::Vertical, + "primary" => AxisKey::Primary, + "secondary" => AxisKey::Secondary, +); + +/// An argument key which identifies a target alignment. +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] +pub enum AlignmentKey { + Left, + Top, + Right, + Bottom, + Origin, + Center, + End, +} + +impl AlignmentKey { + /// The generic axis this alignment key corresopnds to in the given system + /// of layouting axes. Falls back to `default` if the alignment is generic. + pub fn axis(&self, axes: LayoutAxes, default: GenericAxisKind) -> GenericAxisKind { + use AlignmentKey::*; + match self { + Origin | Center | End => default, + Left | Right => axes.horizontal(), + Top | Bottom => axes.vertical(), + } + } + + /// The generic version of this alignment in the given system of layouting + /// axes. Returns an error if the alignment is invalid for the given axis. + pub fn generic(&self, axes: LayoutAxes, axis: GenericAxisKind) -> LayoutResult { + use AlignmentKey::*; + + let horizontal = axis == axes.horizontal(); + Ok(match self { + Origin => Alignment::Origin, + Center => Alignment::Center, + End => Alignment::End, + Left if horizontal => axes.left(), + Right if horizontal => axes.right(), + Top if !horizontal => axes.top(), + Bottom if !horizontal => axes.bottom(), + _ => error!( + "invalid alignment `{}` for {} axis", + format!("{:?}", self).to_lowercase(), + format!("{:?}", axis).to_lowercase() + ) + }) + } + + /// The specific version of this alignment in the given system of layouting + /// axes. + pub fn specific(&self, axes: LayoutAxes, axis: SpecificAxisKind) -> AlignmentKey { + use AlignmentKey::*; + use SpecificAxisKind::*; + + let positive = axes.get_specific(axis).is_positive(); + match (self, axis, positive) { + (Origin, Horizontal, true) | (End, Horizontal, false) => Left, + (End, Horizontal, true) | (Origin, Horizontal, false) => Right, + (Origin, Vertical, true) | (End, Vertical, false) => Top, + (End, Vertical, true) | (Origin, Vertical, false) => Bottom, + _ => *self, + } + } +} + +kind!(AlignmentKey, "alignment", + "left" => AlignmentKey::Left, + "top" => AlignmentKey::Top, + "right" => AlignmentKey::Right, + "bottom" => AlignmentKey::Bottom, + "origin" => AlignmentKey::Origin, + "center" => AlignmentKey::Center, + "end" => AlignmentKey::End, +); + +/// An argument key which identifies a margin or padding target. +/// +/// A is the axis type used. +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] +pub enum PaddingKey { + /// All four sides should have the specified padding. + All, + /// Both sides of the given axis should have the specified padding. + Axis(A), + /// Only the given side of the given axis should have the specified padding. + AxisAligned(A, AlignmentKey), +} + +kind!(PaddingKey, "axis or anchor", + "horizontal" => PaddingKey::Axis(AxisKey::Horizontal), + "vertical" => PaddingKey::Axis(AxisKey::Vertical), + "primary" => PaddingKey::Axis(AxisKey::Primary), + "secondary" => PaddingKey::Axis(AxisKey::Secondary), + + "left" => PaddingKey::AxisAligned(AxisKey::Horizontal, AlignmentKey::Left), + "right" => PaddingKey::AxisAligned(AxisKey::Horizontal, AlignmentKey::Right), + "top" => PaddingKey::AxisAligned(AxisKey::Vertical, AlignmentKey::Top), + "bottom" => PaddingKey::AxisAligned(AxisKey::Vertical, AlignmentKey::Bottom), + + "primary-origin" => PaddingKey::AxisAligned(AxisKey::Primary, AlignmentKey::Origin), + "primary-end" => PaddingKey::AxisAligned(AxisKey::Primary, AlignmentKey::End), + "secondary-origin" => PaddingKey::AxisAligned(AxisKey::Secondary, AlignmentKey::Origin), + "secondary-end" => PaddingKey::AxisAligned(AxisKey::Secondary, AlignmentKey::End), + "horizontal-origin" => PaddingKey::AxisAligned(AxisKey::Horizontal, AlignmentKey::Origin), + "horizontal-end" => PaddingKey::AxisAligned(AxisKey::Horizontal, AlignmentKey::End), + "vertical-origin" => PaddingKey::AxisAligned(AxisKey::Vertical, AlignmentKey::Origin), + "vertical-end" => PaddingKey::AxisAligned(AxisKey::Vertical, AlignmentKey::End), +); diff --git a/src/library/mod.rs b/src/library/mod.rs index 02af0d830..f25c6397c 100644 --- a/src/library/mod.rs +++ b/src/library/mod.rs @@ -6,6 +6,9 @@ use toddle::query::FontClass; pub_use_mod!(align); pub_use_mod!(boxed); +mod keys; +use keys::*; + /// Create a scope with all standard functions. pub fn std() -> Scope { let mut std = Scope::new(); @@ -33,7 +36,7 @@ pub fn std() -> Scope { ("italic", FontClass::Italic), ("mono", FontClass::Monospace), ] { - std.add_with_metadata::(name, *class); + std.add_with_metadata::(name, class.clone()); } std @@ -80,8 +83,8 @@ function! { parse(args, body) { parse!(forbidden: body); PageSize { - width: args.get_key_opt::("width")?.map(|a| a.val), - height: args.get_key_opt::("height")?.map(|a| a.val), + width: args.get_key_opt::("width")?.map(|s| s.v), + height: args.get_key_opt::("height")?.map(|s| s.v), } } @@ -97,42 +100,18 @@ function! { /// `page.margins`: Set the margins of pages. #[derive(Debug, PartialEq)] pub struct PageMargins { - map: ArgMap, + map: ConsistentMap, Size>, } parse(args, body) { - use PaddingKey::*; - use AlignmentKey::*; - - let mut map = ArgMap::new(); - map.add_opt(All, args.get_pos_opt::()?); + let mut map = ConsistentMap::new(); + map.add_opt_span(PaddingKey::All, args.get_pos_opt::()?)?; for arg in args.keys() { - let key = match arg.val.0.val { - "horizontal" => Axis(AxisKey::Horizontal), - "vertical" => Axis(AxisKey::Vertical), - "primary" => Axis(AxisKey::Primary), - "secondary" => Axis(AxisKey::Secondary), + let key = PaddingKey::from_ident(&arg.v.key)?; + let size = Size::from_expr(arg.v.value)?; - "left" => AxisAligned(AxisKey::Horizontal, Left), - "right" => AxisAligned(AxisKey::Horizontal, Right), - "top" => AxisAligned(AxisKey::Vertical, Top), - "bottom" => AxisAligned(AxisKey::Vertical, Bottom), - - "primary-origin" => AxisAligned(AxisKey::Primary, Origin), - "primary-end" => AxisAligned(AxisKey::Primary, End), - "secondary-origin" => AxisAligned(AxisKey::Secondary, Origin), - "secondary-end" => AxisAligned(AxisKey::Secondary, End), - "horizontal-origin" => AxisAligned(AxisKey::Horizontal, Origin), - "horizontal-end" => AxisAligned(AxisKey::Horizontal, End), - "vertical-origin" => AxisAligned(AxisKey::Vertical, Origin), - "vertical-end" => AxisAligned(AxisKey::Vertical, End), - - _ => error!(unexpected_argument), - }; - - let size = ArgParser::convert::(arg.val.1.val)?; - map.add(key, size); + map.add(key, size)?; } parse!(forbidden: body); @@ -144,25 +123,25 @@ function! { let axes = ctx.axes; let map = self.map.dedup(|key, val| { - match key { + Ok((match key { All => All, Axis(axis) => Axis(axis.specific(axes)), AxisAligned(axis, alignment) => { let axis = axis.specific(axes); AxisAligned(axis, alignment.specific(axes, axis)) } - } - }); + }, val)) + })?; - let style = ctx.style.page; + let mut style = ctx.style.page; let padding = &mut style.margins; - map.with(All, |val| padding.set_all(val)); - map.with(Axis(AxisKey::Horizontal), |val| padding.set_horizontal(val)); - map.with(Axis(AxisKey::Vertical), |val| padding.set_vertical(val)); + map.with(All, |&val| padding.set_all(val)); + map.with(Axis(SpecificAxisKind::Horizontal), |&val| padding.set_horizontal(val)); + map.with(Axis(SpecificAxisKind::Vertical), |&val| padding.set_vertical(val)); - for (key, val) in map.iter() { - if let AxisAligned(axis, alignment) = key { + for (key, &val) in map.iter() { + if let AxisAligned(_, alignment) = key { match alignment { AlignmentKey::Left => padding.left = val, AlignmentKey::Right => padding.right = val, @@ -182,7 +161,7 @@ function! { #[derive(Debug, PartialEq)] pub struct Spacing { axis: AxisKey, - spacing: SpacingValue, + spacing: FSize, } type Meta = Option; @@ -191,19 +170,14 @@ function! { let spacing = if let Some(axis) = meta { Spacing { axis, - spacing: SpacingValue::from_expr(args.get_pos::()?)?, + spacing: FSize::from_expr(args.get_pos::()?)?, } } else { if let Some(arg) = args.get_key_next() { - let axis = match arg.val.0.val { - "horizontal" => AxisKey::Horizontal, - "vertical" => AxisKey::Vertical, - "primary" => AxisKey::Primary, - "secondary" => AxisKey::Secondary, - _ => error!(unexpected_argument), - }; + let axis = AxisKey::from_ident(&arg.v.key) + .map_err(|_| error!(@unexpected_argument))?; - let spacing = SpacingValue::from_expr(arg.val.1.val)?; + let spacing = FSize::from_expr(arg.v.value)?; Spacing { axis, spacing } } else { error!("expected axis and expression") @@ -217,30 +191,14 @@ function! { layout(self, ctx) { let axis = self.axis.generic(ctx.axes); let spacing = match self.spacing { - SpacingValue::Absolute(s) => s, - SpacingValue::Relative(f) => f * ctx.style.text.font_size, + FSize::Absolute(size) => size, + FSize::Scaled(scale) => scale * ctx.style.text.font_size, }; vec![AddSpacing(spacing, SpacingKind::Hard, axis)] } } -#[derive(Debug, PartialEq)] -enum SpacingValue { - Absolute(Size), - Relative(f32), -} - -impl SpacingValue { - fn from_expr(expr: Spanned<&Expression>) -> ParseResult { - Ok(match expr.val { - Expression::Size(s) => SpacingValue::Absolute(*s), - Expression::Num(f) => SpacingValue::Relative(*f as f32), - _ => error!("invalid spacing: expected size or number"), - }) - } -} - function! { /// Sets text with a different style. #[derive(Debug, PartialEq)] @@ -260,7 +218,7 @@ function! { layout(self, ctx) { let mut style = ctx.style.text.clone(); - style.toggle_class(self.class); + style.toggle_class(self.class.clone()); match &self.body { Some(body) => vec![ diff --git a/src/macros.rs b/src/macros.rs index b6f069b71..ebe1cad7a 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -3,37 +3,33 @@ /// Create trait implementations for an error type. macro_rules! error_type { ( - $var:ident: $err:ident, + $this:ident: $type:ident, $(res: $res:ident,)* show: $f:ident => $show:expr, $(source: $source:expr,)* - $(from: ($from:path, $conv:expr),)* + $(from: ($err:ident: $from:path, $conv:expr),)* ) => { // Possibly create a result type. - $(type $res = std::result::Result;)* + $(type $res = std::result::Result;)* - impl std::fmt::Display for $err { - fn fmt(&self, $f: &mut std::fmt::Formatter) -> std::fmt::Result { - #[allow(unused)] - let $var = self; + impl std::fmt::Display for $type { + fn fmt(&$this, $f: &mut std::fmt::Formatter) -> std::fmt::Result { $show } } - debug_display!($err); + debug_display!($type); - impl std::error::Error for $err { + impl std::error::Error for $type { // The source method is only generated if an implementation was given. - $(fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - #[allow(unused)] - let $var = self; + $(fn source(&$this) -> Option<&(dyn std::error::Error + 'static)> { $source })* } // Create any number of from implementations. - $(impl From<$from> for $err { - fn from($var: $from) -> $err { + $(impl From<$from> for $type { + fn from($err: $from) -> $type { $conv } })* diff --git a/src/size.rs b/src/size.rs index a9c3198cf..d415be3f1 100644 --- a/src/size.rs +++ b/src/size.rs @@ -35,6 +35,19 @@ pub struct SizeBox { pub bottom: Size, } +/// A size or scale. +#[derive(Copy, Clone, PartialEq)] +pub enum ScaleSize { + Absolute(Size), + Scaled(f32), +} + +/// A size that is possibly scaled by the font size. +pub type FSize = ScaleSize; + +/// A size that is possibly scaled by the size of the padded parent container. +pub type PSize = ScaleSize; + impl Size { /// Create a zeroed size. #[inline] @@ -241,7 +254,7 @@ debug_display!(Size); pub struct ParseSizeError; error_type! { - err: ParseSizeError, + self: ParseSizeError, show: f => write!(f, "failed to parse size"), } @@ -469,3 +482,16 @@ impl Display for SizeBox { } debug_display!(SizeBox); + +//------------------------------------------------------------------------------------------------// + +impl Display for ScaleSize { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + match self { + ScaleSize::Absolute(size) => write!(f, "{}", size), + ScaleSize::Scaled(scale) => write!(f, "x{}", scale), + } + } +} + +debug_display!(ScaleSize); diff --git a/src/syntax/mod.rs b/src/syntax/mod.rs index 1b55fb4e8..21088b830 100644 --- a/src/syntax/mod.rs +++ b/src/syntax/mod.rs @@ -1,9 +1,10 @@ //! Tokenization and parsing of source code. use std::fmt::{self, Display, Formatter}; +use unicode_xid::UnicodeXID; use crate::func::LayoutFunc; -use crate::size::Size; +use crate::size::{Size, ScaleSize}; mod tokens; #[macro_use] @@ -88,54 +89,133 @@ pub enum Node { Func(FuncCall), } -/// A function invocation, consisting of header and a dynamically parsed body. +/// An invocation of a function. #[derive(Debug)] pub struct FuncCall { - pub header: Spanned, - pub body: Spanned>, + pub call: Box, } impl PartialEq for FuncCall { fn eq(&self, other: &FuncCall) -> bool { - (self.header == other.header) && (&self.body == &other.body) + &self.call == &other.call } } -/// Contains header information of a function invocation. -#[derive(Debug, Clone, PartialEq)] -pub struct FuncHeader { - pub name: Spanned, - pub args: FuncArgs, -} - /// The arguments passed to a function. #[derive(Debug, Clone, PartialEq)] pub struct FuncArgs { - pub positional: Vec>, - pub keyword: Vec, Spanned)>> + pub pos: Vec>, + pub key: Vec>, } impl FuncArgs { /// Create an empty collection of arguments. - fn new() -> FuncArgs { + pub fn new() -> FuncArgs { FuncArgs { - positional: vec![], - keyword: vec![], + pos: vec![], + key: vec![], } } + + /// Add a positional argument. + pub fn add_pos(&mut self, arg: Spanned) { + self.pos.push(arg); + } + + /// Add a keyword argument. + pub fn add_key(&mut self, arg: Spanned) { + self.key.push(arg); + } + + /// Force-extract the first positional argument. + pub fn get_pos(&mut self) -> ParseResult> { + expect(self.get_pos_opt()) + } + + /// Extract the first positional argument. + pub fn get_pos_opt(&mut self) -> ParseResult>> { + Ok(if !self.pos.is_empty() { + let spanned = self.pos.remove(0); + let span = spanned.span; + Some(Spanned::new(E::from_expr(spanned)?, span)) + } else { + None + }) + } + + /// Iterator over positional arguments. + pub fn pos(&mut self) -> std::vec::IntoIter> { + let vec = std::mem::replace(&mut self.pos, vec![]); + vec.into_iter() + } + + /// Force-extract a keyword argument. + pub fn get_key(&mut self, name: &str) -> ParseResult> { + expect(self.get_key_opt(name)) + } + + /// Extract a keyword argument. + pub fn get_key_opt(&mut self, name: &str) -> ParseResult>> { + Ok(if let Some(index) = self.key.iter().position(|arg| arg.v.key.v.0 == name) { + let Spanned { v, span } = self.key.swap_remove(index); + Some(Spanned::new(E::from_expr(v.value)?, span)) + } else { + None + }) + } + + /// Extract any keyword argument. + pub fn get_key_next(&mut self) -> Option> { + self.key.pop() + } + + /// Iterator over all keyword arguments. + pub fn keys(&mut self) -> std::vec::IntoIter> { + let vec = std::mem::replace(&mut self.key, vec![]); + vec.into_iter() + } + + /// Clear the argument lists. + pub fn clear(&mut self) { + self.pos.clear(); + self.key.clear(); + } + + /// Whether both the positional and keyword argument lists are empty. + pub fn is_empty(&self) -> bool { + self.pos.is_empty() && self.key.is_empty() + } } -/// One argument passed to a function. +fn expect(opt: ParseResult>>) -> ParseResult> { + match opt { + Ok(Some(spanned)) => Ok(spanned), + Ok(None) => error!("expected {}", E::NAME), + Err(e) => Err(e), + } +} + +/// A positional argument passed to a function. +pub type PosArg = Expression; + +/// A keyword argument passed to a function. #[derive(Debug, Clone, PartialEq)] -pub enum FuncArg { - Positional(Spanned), - Keyword(Spanned<(Spanned, Spanned)>), +pub struct KeyArg { + pub key: Spanned, + pub value: Spanned, +} + +/// Either a positional or keyword argument. +#[derive(Debug, Clone, PartialEq)] +pub enum DynArg { + Pos(Spanned), + Key(Spanned), } /// An argument or return value. #[derive(Clone, PartialEq)] pub enum Expression { - Ident(String), + Ident(Ident), Str(String), Num(f64), Size(Size), @@ -146,7 +226,7 @@ impl Display for Expression { fn fmt(&self, f: &mut Formatter) -> fmt::Result { use Expression::*; match self { - Ident(s) => write!(f, "{}", s), + Ident(i) => write!(f, "{}", i), Str(s) => write!(f, "{:?}", s), Num(n) => write!(f, "{}", n), Size(s) => write!(f, "{}", s), @@ -156,3 +236,81 @@ impl Display for Expression { } debug_display!(Expression); + +/// An identifier. +#[derive(Clone, PartialEq)] +pub struct Ident(pub String); + +impl Ident { + fn new(string: String) -> ParseResult { + if is_identifier(&string) { + Ok(Ident(string)) + } else { + error!("invalid identifier: `{}`", string); + } + } +} + +impl Display for Ident { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + write!(f, "{}", self.0) + } +} + +debug_display!(Ident); + +/// Whether this word is a valid unicode identifier. +fn is_identifier(string: &str) -> bool { + let mut chars = string.chars(); + + match chars.next() { + Some('-') => (), + Some(c) if UnicodeXID::is_xid_start(c) => (), + _ => return false, + } + + while let Some(c) = chars.next() { + match c { + '.' | '-' => (), + c if UnicodeXID::is_xid_continue(c) => (), + _ => return false, + } + } + + true +} + +/// Kinds of expressions. +pub trait ExpressionKind: Sized { + const NAME: &'static str; + + /// Create from expression. + fn from_expr(expr: Spanned) -> ParseResult; +} + +macro_rules! kind { + ($type:ty, $name:expr, $($patterns:tt)*) => { + impl ExpressionKind for $type { + const NAME: &'static str = $name; + + fn from_expr(expr: Spanned) -> ParseResult { + #[allow(unreachable_patterns)] + Ok(match expr.v { + $($patterns)*, + _ => error!("expected {}", Self::NAME), + }) + } + } + }; +} + +kind!(Expression, "expression", e => e); +kind!(Ident, "identifier", Expression::Ident(ident) => ident); +kind!(String, "string", Expression::Str(string) => string); +kind!(f64, "number", Expression::Num(num) => num); +kind!(bool, "boolean", Expression::Bool(boolean) => boolean); +kind!(Size, "size", Expression::Size(size) => size); +kind!(ScaleSize, "number or size", + Expression::Size(size) => ScaleSize::Absolute(size), + Expression::Num(scale) => ScaleSize::Scaled(scale as f32) +); diff --git a/src/syntax/parsing.rs b/src/syntax/parsing.rs index 2d76b6cfb..3527e6b11 100644 --- a/src/syntax/parsing.rs +++ b/src/syntax/parsing.rs @@ -1,7 +1,5 @@ //! Parsing of token streams into syntax trees. -use unicode_xid::UnicodeXID; - use crate::TypesetResult; use crate::func::{LayoutFunc, Scope}; use crate::size::Size; @@ -67,7 +65,7 @@ impl<'s> Parser<'s> { use Token::*; if let Some(token) = self.tokens.peek() { - match token.val { + match token.v { // Functions. LeftBracket => self.parse_func()?, RightBracket => error!("unexpected closing bracket"), @@ -80,8 +78,8 @@ impl<'s> Parser<'s> { // Normal text. Text(word) => self.append_consumed(Node::Text(word.to_owned()), token.span), - // The rest is handled elsewhere or should not happen, because `Tokens` does not - // yield these in a body. + // The rest is handled elsewhere or should not happen, because + // the tokenizer does not yield these in a body. Space | Newline | LineComment(_) | BlockComment(_) | Colon | Equals | Comma | Quoted(_) | StarSlash => panic!("parse_body_part: unexpected token: {:?}", token), @@ -95,39 +93,10 @@ impl<'s> Parser<'s> { fn parse_func(&mut self) -> ParseResult<()> { // This should only be called if a left bracket was seen. let token = self.tokens.next().expect("parse_func: expected token"); - assert!(token.val == Token::LeftBracket); + assert!(token.v == Token::LeftBracket); let mut span = token.span; - - let header = self.parse_func_header()?; - let body = self.parse_func_body(&header.val)?; - - span.end = self.tokens.string_index(); - - // Finally this function is parsed to the end. - self.append(Node::Func(FuncCall { header, body }), span); - - Ok(()) - } - - /// Parse a function header. - fn parse_func_header(&mut self) -> ParseResult> { - let start = self.tokens.string_index() - 1; - - self.skip_white(); - - let name = match self.tokens.next() { - Some(Spanned { val: Token::Text(word), span }) => { - if is_identifier(word) { - Ok(Spanned::new(word.to_owned(), span)) - } else { - error!("invalid identifier: `{}`", word); - } - } - _ => error!("expected identifier"), - }?; - - self.skip_white(); + let name = self.parse_func_name()?; // Check for arguments let args = match self.tokens.next().map(Spanned::value) { @@ -136,23 +105,43 @@ impl<'s> Parser<'s> { _ => error!("expected arguments or closing bracket"), }; - let end = self.tokens.string_index(); + let call = self.parse_func_call(name, args)?; + span.end = self.tokens.string_index(); - // Store the header information of the function invocation. - Ok(Spanned::new(FuncHeader { name, args }, Span::new(start, end))) + // Finally this function is parsed to the end. + self.append(Node::Func(FuncCall { call }), span); + + Ok(()) + } + + /// Parse a function header. + fn parse_func_name(&mut self) -> ParseResult> { + self.skip_white(); + + let name = match self.tokens.next() { + Some(Spanned { v: Token::Text(word), span }) => { + let ident = Ident::new(word.to_string())?; + Spanned::new(ident, span) + } + _ => error!("expected identifier"), + }; + + self.skip_white(); + + Ok(name) } /// Parse the arguments to a function. fn parse_func_args(&mut self) -> ParseResult { - let mut positional = Vec::new(); - let mut keyword = Vec::new(); + let mut pos = Vec::new(); + let mut key = Vec::new(); loop { self.skip_white(); match self.parse_func_arg()? { - Some(FuncArg::Positional(arg)) => positional.push(arg), - Some(FuncArg::Keyword(arg)) => keyword.push(arg), + Some(DynArg::Pos(arg)) => pos.push(arg), + Some(DynArg::Key(arg)) => key.push(arg), _ => {}, } @@ -163,17 +152,17 @@ impl<'s> Parser<'s> { } } - Ok(FuncArgs { positional, keyword }) + Ok(FuncArgs { pos, key }) } /// Parse one argument to a function. - fn parse_func_arg(&mut self) -> ParseResult> { + fn parse_func_arg(&mut self) -> ParseResult> { let token = match self.tokens.peek() { Some(token) => token, None => return Ok(None), }; - Ok(match token.val { + Ok(match token.v { Token::Text(name) => { self.advance(); self.skip_white(); @@ -183,55 +172,41 @@ impl<'s> Parser<'s> { self.advance(); self.skip_white(); - let name = token.span_map(|_| name.to_string()); - let next = self.tokens.next().ok_or_else(|| error!(@"expected expression"))?; - let val = Self::parse_expression(next)?; - let span = Span::merge(name.span, val.span); + let name = Ident::new(name.to_string())?; + let key = Spanned::new(name, token.span); - FuncArg::Keyword(Spanned::new((name, val), span)) + let next = self.tokens.next() + .ok_or_else(|| error!(@"expected expression"))?; + let value = Self::parse_expression(next)?; + + let span = Span::merge(key.span, value.span); + let arg = KeyArg { key, value }; + + DynArg::Key(Spanned::new(arg, span)) } - _ => FuncArg::Positional(Self::parse_expression(token)?), + _ => DynArg::Pos(Self::parse_expression(token)?), }) } Token::Quoted(_) => { self.advance(); - Some(FuncArg::Positional(Self::parse_expression(token)?)) + Some(DynArg::Pos(Self::parse_expression(token)?)) } _ => None, }) } - /// Parse an expression. - fn parse_expression(token: Spanned) -> ParseResult> { - Ok(Spanned::new(match token.val { - Token::Quoted(text) => Expression::Str(text.to_owned()), - Token::Text(text) => { - if let Ok(b) = text.parse::() { - Expression::Bool(b) - } else if let Ok(num) = text.parse::() { - Expression::Num(num) - } else if let Ok(size) = text.parse::() { - Expression::Size(size) - } else { - Expression::Ident(text.to_owned()) - } - } - _ => error!("expected expression"), - }, token.span)) - } - - /// Parse the body of a function. - fn parse_func_body(&mut self, header: &FuncHeader) - -> ParseResult>> { + /// Parse a function call. + fn parse_func_call(&mut self, name: Spanned, args: FuncArgs) + -> ParseResult> { // Now we want to parse this function dynamically. let parser = self .ctx .scope - .get_parser(&header.name.val) - .ok_or_else(|| error!(@"unknown function: `{}`", &header.name.val))?; + .get_parser(&name.v.0) + .ok_or_else(|| error!(@"unknown function: `{}`", &name.v))?; let has_body = self.tokens.peek().map(Spanned::value) == Some(Token::LeftBracket); @@ -245,30 +220,50 @@ impl<'s> Parser<'s> { .map(|end| start + end) .ok_or_else(|| error!(@"expected closing bracket"))?; + let span = Span::new(start - 1, end + 1); + // Parse the body. let body_string = &self.src[start..end]; - let body = parser(&header, Some(body_string), self.ctx)?; + let body = parser(args, Some(Spanned::new(body_string, span)), self.ctx)?; // Skip to the end of the function in the token stream. self.tokens.set_string_index(end); // Now the body should be closed. let token = self.tokens.next().expect("parse_func_body: expected token"); - assert!(token.val == Token::RightBracket); + assert!(token.v == Token::RightBracket); - Spanned::new(body, Span::new(start - 1, end + 1)) + body } else { - let body = parser(&header, None, self.ctx)?; - Spanned::new(body, Span::new(0, 0)) + parser(args, None, self.ctx)? }) } + /// Parse an expression. + fn parse_expression(token: Spanned) -> ParseResult> { + Ok(Spanned::new(match token.v { + Token::Quoted(text) => Expression::Str(text.to_owned()), + Token::Text(text) => { + if let Ok(b) = text.parse::() { + Expression::Bool(b) + } else if let Ok(num) = text.parse::() { + Expression::Num(num) + } else if let Ok(size) = text.parse::() { + Expression::Size(size) + } else { + Expression::Ident(Ident::new(text.to_string())?) + } + } + _ => error!("expected expression"), + }, token.span)) + } + /// Parse whitespace (as long as there is any) and skip over comments. fn parse_white(&mut self) -> ParseResult<()> { let mut state = NewlineState::Zero; while let Some(token) = self.tokens.peek() { - match token.val { + match token.v { Token::Space => { self.advance(); match state { @@ -297,7 +292,7 @@ impl<'s> Parser<'s> { } state = NewlineState::Zero; - match token.val { + match token.v { Token::LineComment(_) | Token::BlockComment(_) => self.advance(), Token::StarSlash => error!("unexpected end of block comment"), _ => break, @@ -312,7 +307,7 @@ impl<'s> Parser<'s> { /// Skip over whitespace and comments. fn skip_white(&mut self) { while let Some(token) = self.tokens.peek() { - match token.val { + match token.v { Token::Space | Token::Newline | Token::LineComment(_) | Token::BlockComment(_) => self.advance(), _ => break, @@ -333,7 +328,7 @@ impl<'s> Parser<'s> { /// Append a space, merging with a previous space if there is one. fn append_space(&mut self, span: Span) { match self.tree.nodes.last_mut() { - Some(ref mut node) if node.val == Node::Space => node.span.expand(span), + Some(ref mut node) if node.v == Node::Space => node.span.expand(span), _ => self.append(Node::Space, span), } } @@ -412,102 +407,73 @@ impl<'s> Iterator for PeekableTokens<'s> { } } -/// Whether this word is a valid unicode identifier. -fn is_identifier(string: &str) -> bool { - let mut chars = string.chars(); - - match chars.next() { - Some(c) if c != '.' && !UnicodeXID::is_xid_start(c) => return false, - None => return false, - _ => (), - } - - while let Some(c) = chars.next() { - if c != '.' && !UnicodeXID::is_xid_continue(c) { - return false; - } - } - - true -} - /// The result type for parsing. pub type ParseResult = TypesetResult; #[cfg(test)] +#[allow(non_snake_case)] mod tests { - #![allow(non_snake_case)] - - use super::*; use crate::func::{Commands, Scope}; use crate::layout::{LayoutContext, LayoutResult}; - use funcs::*; + use crate::syntax::*; use Node::{Func as F, Newline as N, Space as S}; - /// Two test functions, one which parses it's body as another syntax tree - /// and another one which does not expect a body. - mod funcs { - use super::*; + function! { + /// A testing function which just parses it's body into a syntax + /// tree. + #[derive(Debug)] + pub struct TreeFn { pub tree: SyntaxTree } - function! { - /// A testing function which just parses it's body into a syntax - /// tree. - #[derive(Debug)] - pub struct TreeFn { pub tree: SyntaxTree } - - parse(args, body, ctx) { - args.clear(); - TreeFn { - tree: parse!(expected: body, ctx) - } - } - - layout() { vec![] } - } - - impl PartialEq for TreeFn { - fn eq(&self, other: &TreeFn) -> bool { - assert_tree_equal(&self.tree, &other.tree); - true + parse(args, body, ctx) { + args.clear(); + TreeFn { + tree: parse!(expected: body, ctx) } } - function! { - /// A testing function without a body. - #[derive(Debug, Default, PartialEq)] - pub struct BodylessFn; + layout() { vec![] } + } - parse(default) - layout() { vec![] } + impl PartialEq for TreeFn { + fn eq(&self, other: &TreeFn) -> bool { + assert_tree_equal(&self.tree, &other.tree); + true } } + function! { + /// A testing function without a body. + #[derive(Debug, Default, PartialEq)] + pub struct BodylessFn(Vec, Vec<(Ident, Expression)>); + + parse(args, body) { + parse!(forbidden: body); + BodylessFn( + args.pos().map(Spanned::value).collect(), + args.keys().map(|arg| (arg.v.key.v, arg.v.value.v)).collect(), + ) + } + + layout() { vec![] } + } + mod args { + use super::*; use super::Expression; pub use Expression::{Num as N, Size as Z, Bool as B}; pub fn S(string: &str) -> Expression { Expression::Str(string.to_owned()) } - pub fn I(string: &str) -> Expression { Expression::Ident(string.to_owned()) } + pub fn I(string: &str) -> Expression { + Expression::Ident(Ident::new(string.to_owned()).unwrap()) + } } /// Asserts that two syntax trees are equal except for all spans inside them. fn assert_tree_equal(a: &SyntaxTree, b: &SyntaxTree) { for (x, y) in a.nodes.iter().zip(&b.nodes) { - let equal = match (x, y) { - (Spanned { val: F(x), .. }, Spanned { val: F(y), .. }) => { - x.header.val.name.val == y.header.val.name.val - && x.header.val.args.positional.iter().map(|span| &span.val) - .eq(y.header.val.args.positional.iter().map(|span| &span.val)) - && x.header.val.args.keyword.iter().map(|s| (&s.val.0.val, &s.val.1.val)) - .eq(y.header.val.args.keyword.iter().map(|s| (&s.val.0.val, &s.val.1.val))) - && &x.body.val == &y.body.val - } - _ => x.val == y.val - }; - - if !equal { - panic!("assert_tree_equal: ({:#?}) != ({:#?})", x.val, y.val); + if x.v != y.v { + panic!("trees are not equal: ({:#?}) != ({:#?})", x.v, y.v); } } } @@ -564,21 +530,15 @@ mod tests { /// Shortcut macro to create a function. macro_rules! func { - (name => $name:expr) => ( - func!(@$name, Box::new(BodylessFn), FuncArgs::new()) + () => ( + FuncCall { call: Box::new(BodylessFn(vec![], vec![])) } ); - (name => $name:expr, body => $tree:expr $(,)*) => ( - func!(@$name, Box::new(TreeFn { tree: $tree }), FuncArgs::new()) + (body: $tree:expr $(,)*) => ( + FuncCall { call: Box::new(TreeFn { tree: $tree }) } + ); + (args: $pos:expr, $key:expr) => ( + FuncCall { call: Box::new(BodylessFn($pos, $key)) } ); - (@$name:expr, $body:expr, $args:expr) => ( - FuncCall { - header: zerospan(FuncHeader { - name: zerospan($name.to_string()), - args: $args, - }), - body: zerospan($body), - } - ) } /// Parse the basic cases. @@ -613,25 +573,21 @@ mod tests { scope.add::("modifier"); scope.add::("func"); - test_scoped(&scope,"[test]", tree! [ F(func! { name => "test" }) ]); - test_scoped(&scope,"[ test]", tree! [ F(func! { name => "test" }) ]); + test_scoped(&scope,"[test]", tree! [ F(func! {}) ]); + test_scoped(&scope,"[ test]", tree! [ F(func! {}) ]); test_scoped(&scope, "This is an [modifier][example] of a function invocation.", tree! [ T("This"), S, T("is"), S, T("an"), S, - F(func! { name => "modifier", body => tree! [ T("example") ] }), S, + F(func! { body: tree! [ T("example") ] }), S, T("of"), S, T("a"), S, T("function"), S, T("invocation.") ]); test_scoped(&scope, "[func][Hello][modifier][Here][end]", tree! [ - F(func! { name => "func", body => tree! [ T("Hello") ] }), - F(func! { name => "modifier", body => tree! [ T("Here") ] }), - F(func! { name => "end" }), + F(func! { body: tree! [ T("Hello") ] }), + F(func! { body: tree! [ T("Here") ] }), + F(func! {}), ]); - test_scoped(&scope, "[func][]", tree! [ F(func! { name => "func", body => tree! [] }) ]); + test_scoped(&scope, "[func][]", tree! [ F(func! { body: tree! [] }) ]); test_scoped(&scope, "[modifier][[func][call]] outside", tree! [ - F(func! { - name => "modifier", - body => tree! [ F(func! { name => "func", body => tree! [ T("call") ] }) ], - }), - S, T("outside") + F(func! { body: tree! [ F(func! { body: tree! [ T("call") ] }) ] }), S, T("outside") ]); } @@ -643,16 +599,14 @@ mod tests { use args::*; fn func( - positional: Vec, - keyword: Vec<(&str, Expression)>, + pos: Vec, + key: Vec<(&str, Expression)>, ) -> SyntaxTree { - let args = FuncArgs { - positional: positional.into_iter().map(zerospan).collect(), - keyword: keyword.into_iter() - .map(|(s, e)| zerospan((zerospan(s.to_string()), zerospan(e)))) - .collect() - }; - tree! [ F(func!(@"align", Box::new(BodylessFn), args)) ] + let key = key.into_iter() + .map(|s| (Ident::new(s.0.to_string()).unwrap(), s.1)) + .collect(); + + tree! [ F(func!(args: pos, key)) ] } let mut scope = Scope::new(); @@ -689,9 +643,9 @@ mod tests { test_scoped(&scope, "Text\n// Comment\n More text", tree! [ T("Text"), S, T("More"), S, T("text") ]); test_scoped(&scope, "[test/*world*/]", - tree! [ F(func! { name => "test" }) ]); + tree! [ F(func! {}) ]); test_scoped(&scope, "[test/*]*/]", - tree! [ F(func! { name => "test" }) ]); + tree! [ F(func! {}) ]); } /// Test if escaped, but unbalanced parens are correctly parsed. @@ -702,21 +656,14 @@ mod tests { scope.add::("code"); test_scoped(&scope, r"My [code][Close \]] end", tree! [ - T("My"), S, F(func! { - name => "code", - body => tree! [ T("Close"), S, T("]") ] - }), S, T("end") + T("My"), S, F(func! { body: tree! [ T("Close"), S, T("]") ] }), S, T("end") ]); test_scoped(&scope, r"My [code][\[ Open] end", tree! [ - T("My"), S, F(func! { - name => "code", - body => tree! [ T("["), S, T("Open") ] - }), S, T("end") + T("My"), S, F(func! { body: tree! [ T("["), S, T("Open") ] }), S, T("end") ]); test_scoped(&scope, r"My [code][Open \] and \[ close]end", tree! [ - T("My"), S, F(func! { - name => "code", - body => tree! [ T("Open"), S, T("]"), S, T("and"), S, T("["), S, T("close") ] + T("My"), S, F(func! { body: + tree! [ T("Open"), S, T("]"), S, T("and"), S, T("["), S, T("close") ] }), T("end") ]); } @@ -729,15 +676,9 @@ mod tests { scope.add::("func"); scope.add::("bold"); - test_scoped(&scope, "[func] ⺐.", tree! [ - F(func! { name => "func" }), - S, T("⺐.") - ]); + test_scoped(&scope, "[func] ⺐.", tree! [ F(func! {}), S, T("⺐.") ]); test_scoped(&scope, "[bold][Hello 🌍!]", tree! [ - F(func! { - name => "bold", - body => tree! [ T("Hello"), S, T("🌍!") ], - }) + F(func! { body: tree! [ T("Hello"), S, T("🌍!") ] }) ]); } @@ -768,14 +709,8 @@ mod tests { assert_eq!(tree[1].span.pair(), (4, 5)); assert_eq!(tree[2].span.pair(), (5, 37)); - let func = if let Node::Func(f) = &tree[2].val { f } else { panic!() }; - assert_eq!(func.header.span.pair(), (5, 24)); - assert_eq!(func.header.val.name.span.pair(), (6, 11)); - assert_eq!(func.header.val.args.positional[0].span.pair(), (13, 16)); - assert_eq!(func.header.val.args.positional[1].span.pair(), (18, 23)); - - let body = &func.body.val.downcast::().unwrap().tree.nodes; - assert_eq!(func.body.span.pair(), (24, 37)); + let func = if let Node::Func(f) = &tree[2].v { f } else { panic!() }; + let body = &func.call.downcast::().unwrap().tree.nodes; assert_eq!(body[0].span.pair(), (0, 4)); assert_eq!(body[1].span.pair(), (4, 5)); assert_eq!(body[2].span.pair(), (5, 6)); @@ -793,7 +728,7 @@ mod tests { test_err("No functions here]", "unexpected closing bracket"); test_err_scoped(&scope, "[hello][world", "expected closing bracket"); test_err("[hello world", "expected arguments or closing bracket"); - test_err("[ no-name][Why?]", "invalid identifier: 'no-name'"); + test_err("[ no^name][Why?]", "invalid identifier: `no^name`"); test_err("Hello */", "unexpected end of block comment"); } } diff --git a/src/syntax/span.rs b/src/syntax/span.rs index aa4942240..9e0184374 100644 --- a/src/syntax/span.rs +++ b/src/syntax/span.rs @@ -5,27 +5,27 @@ use std::fmt::{self, Display, Formatter}; /// Annotates a value with the part of the source code it corresponds to. #[derive(Copy, Clone, Eq, PartialEq)] pub struct Spanned { - pub val: T, + pub v: T, pub span: Span, } impl Spanned { - pub fn new(val: T, span: Span) -> Spanned { - Spanned { val, span } + pub fn new(v: T, span: Span) -> Spanned { + Spanned { v, span } } pub fn value(self) -> T { - self.val + self.v } - pub fn span_map(self, f: F) -> Spanned where F: FnOnce(T) -> U { - Spanned::new(f(self.val), self.span) + pub fn map(self, f: F) -> Spanned where F: FnOnce(T) -> U { + Spanned::new(f(self.v), self.span) } } impl Display for Spanned where T: std::fmt::Debug { fn fmt(&self, f: &mut Formatter) -> fmt::Result { - write!(f, "({:?}:{})", self.val, self.span) + write!(f, "({:?}:{})", self.v, self.span) } } diff --git a/src/syntax/tokens.rs b/src/syntax/tokens.rs index 752a0b295..f5609b599 100644 --- a/src/syntax/tokens.rs +++ b/src/syntax/tokens.rs @@ -354,7 +354,7 @@ mod tests { /// Test if the source code tokenizes to the tokens. fn test(src: &str, tokens: Vec) { assert_eq!(Tokens::new(src) - .map(|token| token.val) + .map(|token| token.v) .collect::>(), tokens); } diff --git a/tests/layouting.rs b/tests/layouting.rs index 72d160e41..845c00340 100644 --- a/tests/layouting.rs +++ b/tests/layouting.rs @@ -3,10 +3,10 @@ use std::io::{BufWriter, Read, Write}; use std::process::Command; use typst::export::pdf::PdfExporter; -use typst::layout::LayoutAction; -use typst::toddle::query::FileSystemFontProvider; +use typst::layout::{LayoutAction, Serialize}; use typst::size::{Size, Size2D, SizeBox}; use typst::style::PageStyle; +use typst::toddle::query::FileSystemFontProvider; use typst::Typesetter; const CACHE_DIR: &str = "tests/cache"; diff --git a/tests/layouts/align.typ b/tests/layouts/align.typ index c3c607337..a71019321 100644 --- a/tests/layouts/align.typ +++ b/tests/layouts/align.typ @@ -3,7 +3,7 @@ A short sentence. [n] [align: right][words.] - A short sentence. [paragraph.break] [align: right][words.] + A short sentence. [par.break] [align: right][words.] [align: bottom] A longer sentence with a few more words.