diff options
| author | Laurenz <laurmaedje@gmail.com> | 2020-01-24 16:23:57 +0100 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2020-01-24 16:23:57 +0100 |
| commit | 0a087cd28bbee5fcdffbb9d49b0ba9f413ad7f92 (patch) | |
| tree | fe00c96969ed2ee69e6d3b42de8ff2558f792edd | |
| parent | 03fddaf3aea778057aedd74dbcb27bae971ec22f (diff) | |
Reorganize modules 🧱
| -rw-r--r-- | src/error.rs | 14 | ||||
| -rw-r--r-- | src/export/pdf.rs | 4 | ||||
| -rw-r--r-- | src/func.rs (renamed from src/func/macros.rs) | 49 | ||||
| -rw-r--r-- | src/layout/actions.rs | 6 | ||||
| -rw-r--r-- | src/layout/line.rs | 1 | ||||
| -rw-r--r-- | src/layout/mod.rs | 125 | ||||
| -rw-r--r-- | src/layout/model.rs | 82 | ||||
| -rw-r--r-- | src/layout/text.rs | 28 | ||||
| -rw-r--r-- | src/lib.rs | 13 | ||||
| -rw-r--r-- | src/library/font.rs | 7 | ||||
| -rw-r--r-- | src/library/layout.rs | 3 | ||||
| -rw-r--r-- | src/library/mod.rs | 1 | ||||
| -rw-r--r-- | src/library/page.rs | 2 | ||||
| -rw-r--r-- | src/library/spacing.rs | 5 | ||||
| -rw-r--r-- | src/size.rs | 33 | ||||
| -rw-r--r-- | src/style.rs | 1 | ||||
| -rw-r--r-- | src/syntax/expr.rs | 22 | ||||
| -rw-r--r-- | src/syntax/func/keys.rs | 9 | ||||
| -rw-r--r-- | src/syntax/func/maps.rs | 9 | ||||
| -rw-r--r-- | src/syntax/func/mod.rs | 113 | ||||
| -rw-r--r-- | src/syntax/func/values.rs | 35 | ||||
| -rw-r--r-- | src/syntax/mod.rs | 119 | ||||
| -rw-r--r-- | src/syntax/parsing.rs | 96 | ||||
| -rw-r--r-- | src/syntax/scope.rs (renamed from src/func/mod.rs) | 84 | ||||
| -rw-r--r-- | src/syntax/span.rs | 165 | ||||
| -rw-r--r-- | src/syntax/tokens.rs | 39 |
26 files changed, 515 insertions, 550 deletions
diff --git a/src/error.rs b/src/error.rs index b3597266..cd107741 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,5 +1,5 @@ use serde::Serialize; -use crate::syntax::SpanVec; +use crate::syntax::span::SpanVec; pub type Errors = SpanVec<Error>; @@ -10,14 +10,14 @@ pub struct Error { pub severity: Severity, } -impl Error { - pub fn new(message: impl Into<String>, severity: Severity) -> Error { - Error { message: message.into(), severity } - } -} - #[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Serialize)] pub enum Severity { Warning, Error, } + +impl Error { + pub fn new(message: impl Into<String>, severity: Severity) -> Error { + Error { message: message.into(), severity } + } +} diff --git a/src/export/pdf.rs b/src/export/pdf.rs index c4020e10..421ffb19 100644 --- a/src/export/pdf.rs +++ b/src/export/pdf.rs @@ -8,7 +8,7 @@ use tide::content::Content; use tide::doc::{Catalog, Page, PageTree, Resource, Text}; use tide::font::{ CIDFont, CIDFontType, CIDSystemInfo, FontDescriptor, FontFlags, Type0Font, - CMap, CMapEncoding, FontStream, GlyphUnit, WidthRecord + CMap, CMapEncoding, FontStream, GlyphUnit, WidthRecord, }; use toddle::Error as FontError; @@ -16,7 +16,7 @@ use toddle::font::OwnedFont; use toddle::query::{SharedFontLoader, FontIndex}; use toddle::tables::{ CharMap, Header, HorizontalMetrics, MacStyleFlags, - Name, NameEntry, Post, OS2 + Name, NameEntry, Post, OS2, }; use crate::layout::{MultiLayout, Layout, LayoutAction}; diff --git a/src/func/macros.rs b/src/func.rs index 764f0836..a7cdfe2f 100644 --- a/src/func/macros.rs +++ b/src/func.rs @@ -1,5 +1,35 @@ //! Helper types and macros for creating custom functions. +use crate::syntax::{ParseContext, Parsed}; +use crate::syntax::func::FuncHeader; +use crate::syntax::span::Spanned; + +pub mod prelude { + pub use crate::layout::prelude::*; + pub use crate::layout::{LayoutContext, Commands, layout}; + pub use crate::layout::Command::{self, *}; + pub use crate::style::{LayoutStyle, PageStyle, TextStyle}; + pub use crate::syntax::SyntaxModel; + pub use crate::syntax::expr::*; + pub use crate::syntax::func::*; + pub use crate::syntax::func::keys::*; + pub use crate::syntax::func::values::*; + pub use crate::syntax::span::{Span, Spanned}; +} + + +/// Parse a function from source code. +pub trait ParseFunc { + type Meta: Clone; + + /// Parse the header and body into this function given a context. + fn parse( + header: FuncHeader, + body: Option<Spanned<&str>>, + ctx: ParseContext, + metadata: Self::Meta, + ) -> Parsed<Self> where Self: Sized; +} #[macro_export] macro_rules! function { @@ -35,15 +65,15 @@ macro_rules! function { $decos:ident, $metadata:ident ) $code:block $($r:tt)*) => { - impl $crate::func::Parse for $name { + impl $crate::func::ParseFunc for $name { type Meta = $meta; fn parse( - #[allow(unused)] mut header: FuncHeader, - #[allow(unused)] $body: Option<Spanned<&str>>, - #[allow(unused)] $ctx: ParseContext, + #[allow(unused)] mut header: $crate::syntax::func::FuncHeader, + #[allow(unused)] $body: Option<$crate::syntax::span::Spanned<&str>>, + #[allow(unused)] $ctx: $crate::syntax::ParseContext, #[allow(unused)] $metadata: Self::Meta, - ) -> Parsed<Self> where Self: Sized { + ) -> $crate::syntax::Parsed<Self> where Self: Sized { let mut errors = vec![]; let mut decorations = vec![]; #[allow(unused)] let $header = &mut header; @@ -67,8 +97,9 @@ macro_rules! function { fn layout<'a, 'b, 'c, 't>( #[allow(unused)] &'a $this, #[allow(unused)] mut $ctx: $crate::layout::LayoutContext<'b, 'c>, - ) -> $crate::syntax::DynFuture<'t, $crate::layout::Layouted<$crate::func::Commands<'a>>> - where + ) -> $crate::layout::DynFuture<'t, $crate::layout::Layouted< + $crate::layout::Commands<'a>> + > where 'a: 't, 'b: 't, 'c: 't, @@ -96,7 +127,7 @@ macro_rules! body { // Since the body span starts at the opening bracket of the body, we // need to add 1 column to find out the start position of body // content. - let start = body.span.start + Position::new(0, 1); + let start = body.span.start + $crate::syntax::span::Position::new(0, 1); let parsed = $crate::syntax::parse(start, body.v, $ctx); $errors.extend(parsed.errors); $decos.extend(parsed.decorations); @@ -122,7 +153,7 @@ macro_rules! body { #[macro_export] macro_rules! err { (@$severity:ident: $span:expr; $($args:tt)*) => { - $crate::syntax::Spanned { v: err!(@Error: $($args)*), span: $span } + $crate::syntax::span::Spanned { v: err!(@Error: $($args)*), span: $span } }; (@$severity:ident: $($args:tt)*) => { diff --git a/src/layout/actions.rs b/src/layout/actions.rs index 1bf5d684..710d92b4 100644 --- a/src/layout/actions.rs +++ b/src/layout/actions.rs @@ -1,10 +1,12 @@ //! Drawing and cofiguration actions composing layouts. +use std::io::{self, Write}; use std::fmt::{self, Display, Formatter}; use toddle::query::FontIndex; -use super::*; -use LayoutAction::*; +use crate::size::{Size, Size2D}; +use super::{Layout, Serialize}; +use self::LayoutAction::*; /// A layouting action. diff --git a/src/layout/line.rs b/src/layout/line.rs index f7777ae0..5e0839b1 100644 --- a/src/layout/line.rs +++ b/src/layout/line.rs @@ -1,3 +1,4 @@ +use super::stack::{StackLayouter, StackContext}; use super::*; diff --git a/src/layout/mod.rs b/src/layout/mod.rs index 209874a1..f8074524 100644 --- a/src/layout/mod.rs +++ b/src/layout/mod.rs @@ -3,40 +3,26 @@ use std::io::{self, Write}; use std::fmt::{self, Display, Formatter}; use smallvec::SmallVec; -use toddle::query::{SharedFontLoader, FontIndex}; +use toddle::query::FontIndex; -use crate::error::Errors; -use crate::syntax::{SyntaxModel, SpanVec}; use crate::size::{Size, Size2D, SizeBox}; -use crate::style::LayoutStyle; +use self::{GenericAxis::*, SpecificAxis::*, Direction::*, Alignment::*}; -mod actions; -mod model; -mod line; -mod stack; -mod text; +pub mod line; +pub mod stack; +pub mod text; -/// Common types for layouting. -pub mod prelude { - pub use super::*; - pub use GenericAxis::*; - pub use SpecificAxis::*; - pub use Direction::*; - pub use Alignment::*; -} +pub_use_mod!(actions); +pub_use_mod!(model); -/// Different kinds of layouters (fully re-exported). -pub mod layouters { - pub use super::model::ModelLayouter; - pub use super::line::{LineLayouter, LineContext}; - pub use super::stack::{StackLayouter, StackContext}; - pub use super::text::{layout_text, TextContext}; +pub mod prelude { + pub use super::{LayoutSpace, LayoutExpansion, LayoutAxes, LayoutAlignment}; + pub use super::GenericAxis::{self, *}; + pub use super::SpecificAxis::{self, *}; + pub use super::Direction::{self, *}; + pub use super::Alignment::{self, *}; } -pub use self::actions::{LayoutAction, LayoutActions}; -pub use self::layouters::*; -pub use self::prelude::*; - /// A collection of layouts. pub type MultiLayout = Vec<Layout>; @@ -67,49 +53,32 @@ impl Layout { } } -/// The general context for layouting. -#[derive(Debug, Clone)] -pub struct LayoutContext<'a, 'p> { - /// The font loader to retrieve fonts from when typesetting text - /// using [`layout_text`]. - pub loader: &'a SharedFontLoader<'p>, - /// The style for pages and text. - pub style: &'a LayoutStyle, - /// The base unpadded dimensions of this container (for relative sizing). - pub base: Size2D, - /// The spaces to layout in. - pub spaces: LayoutSpaces, - /// Whether to have repeated spaces or to use only the first and only once. - pub repeat: bool, - /// The initial axes along which content is laid out. - pub axes: LayoutAxes, - /// The alignment of the finished layout. - pub alignment: LayoutAlignment, - /// Whether the layout that is to be created will be nested in a parent - /// container. - pub nested: bool, - /// Whether to debug render a box around the layout. - pub debug: bool, -} - -pub struct Layouted<T> { - pub output: T, - pub errors: Errors, +/// Layout components that can be serialized. +pub trait Serialize { + /// Serialize the data structure into an output writable. + fn serialize<W: Write>(&self, f: &mut W) -> io::Result<()>; } -impl<T> Layouted<T> { - pub fn map<F, U>(self, f: F) -> Layouted<U> where F: FnOnce(T) -> U { - Layouted { - output: f(self.output), - errors: self.errors, +impl Serialize for Layout { + fn serialize<W: Write>(&self, f: &mut W) -> io::Result<()> { + writeln!(f, "{:.4} {:.4}", self.dimensions.x.to_pt(), self.dimensions.y.to_pt())?; + writeln!(f, "{}", self.actions.len())?; + for action in &self.actions { + action.serialize(f)?; + writeln!(f)?; } + Ok(()) } } -pub async fn layout(model: &SyntaxModel, ctx: LayoutContext<'_, '_>) -> Layouted<MultiLayout> { - let mut layouter = ModelLayouter::new(ctx); - layouter.layout_syntax_model(model).await; - layouter.finish() +impl Serialize for MultiLayout { + fn serialize<W: Write>(&self, f: &mut W) -> io::Result<()> { + writeln!(f, "{}", self.len())?; + for layout in self { + layout.serialize(f)?; + } + Ok(()) + } } /// A possibly stack-allocated vector of layout spaces. @@ -405,31 +374,3 @@ impl LastSpacing { } } } - -/// Layout components that can be serialized. -pub trait Serialize { - /// Serialize the data structure into an output writable. - fn serialize<W: Write>(&self, f: &mut W) -> io::Result<()>; -} - -impl Serialize for Layout { - fn serialize<W: Write>(&self, f: &mut W) -> io::Result<()> { - writeln!(f, "{:.4} {:.4}", self.dimensions.x.to_pt(), self.dimensions.y.to_pt())?; - writeln!(f, "{}", self.actions.len())?; - for action in &self.actions { - action.serialize(f)?; - writeln!(f)?; - } - Ok(()) - } -} - -impl Serialize for MultiLayout { - fn serialize<W: Write>(&self, f: &mut W) -> io::Result<()> { - writeln!(f, "{}", self.len())?; - for layout in self { - layout.serialize(f)?; - } - Ok(()) - } -} diff --git a/src/layout/model.rs b/src/layout/model.rs index 13d38083..2e61b453 100644 --- a/src/layout/model.rs +++ b/src/layout/model.rs @@ -1,9 +1,15 @@ +use std::future::Future; +use std::pin::Pin; use smallvec::smallvec; +use toddle::query::SharedFontLoader; use crate::error::Errors; -use crate::func::Command; -use crate::syntax::{Model, DynFuture, SyntaxModel, Node}; -use crate::syntax::{SpanVec, Spanned, Span, offset_spans}; +use crate::style::{LayoutStyle, PageStyle, TextStyle}; +use crate::size::{Size, Size2D}; +use crate::syntax::{Model, SyntaxModel, Node}; +use crate::syntax::span::{Spanned, Span, offset_spans}; +use super::line::{LineLayouter, LineContext}; +use super::text::{layout_text, TextContext}; use super::*; @@ -15,6 +21,76 @@ pub struct ModelLayouter<'a, 'p> { errors: Errors, } +/// The general context for layouting. +#[derive(Debug, Clone)] +pub struct LayoutContext<'a, 'p> { + /// The font loader to retrieve fonts from when typesetting text + /// using [`layout_text`]. + pub loader: &'a SharedFontLoader<'p>, + /// The style for pages and text. + pub style: &'a LayoutStyle, + /// The base unpadded dimensions of this container (for relative sizing). + pub base: Size2D, + /// The spaces to layout in. + pub spaces: LayoutSpaces, + /// Whether to have repeated spaces or to use only the first and only once. + pub repeat: bool, + /// The initial axes along which content is laid out. + pub axes: LayoutAxes, + /// The alignment of the finished layout. + pub alignment: LayoutAlignment, + /// Whether the layout that is to be created will be nested in a parent + /// container. + pub nested: bool, + /// Whether to debug render a box around the layout. + pub debug: bool, +} + +pub struct Layouted<T> { + pub output: T, + pub errors: Errors, +} + +impl<T> Layouted<T> { + pub fn map<F, U>(self, f: F) -> Layouted<U> where F: FnOnce(T) -> U { + Layouted { + output: f(self.output), + errors: self.errors, + } + } +} + +/// A sequence of layouting commands. +pub type Commands<'a> = Vec<Command<'a>>; + +/// Layouting commands from functions to the typesetting engine. +#[derive(Debug)] +pub enum Command<'a> { + LayoutSyntaxModel(&'a SyntaxModel), + + Add(Layout), + AddMultiple(MultiLayout), + AddSpacing(Size, SpacingKind, GenericAxis), + + FinishLine, + FinishSpace, + BreakParagraph, + BreakPage, + + SetTextStyle(TextStyle), + SetPageStyle(PageStyle), + SetAlignment(LayoutAlignment), + SetAxes(LayoutAxes), +} + +pub async fn layout(model: &SyntaxModel, ctx: LayoutContext<'_, '_>) -> Layouted<MultiLayout> { + let mut layouter = ModelLayouter::new(ctx); + layouter.layout_syntax_model(model).await; + layouter.finish() +} + +pub type DynFuture<'a, T> = Pin<Box<dyn Future<Output=T> + 'a>>; + impl<'a, 'p> ModelLayouter<'a, 'p> { /// Create a new syntax tree layouter. pub fn new(ctx: LayoutContext<'a, 'p>) -> ModelLayouter<'a, 'p> { diff --git a/src/layout/text.rs b/src/layout/text.rs index 16ae93da..eb598e2f 100644 --- a/src/layout/text.rs +++ b/src/layout/text.rs @@ -6,12 +6,14 @@ use crate::style::TextStyle; use super::*; -/// Layouts text into a box. -/// -/// There is no complex layout involved. The text is simply laid out left- -/// to-right using the correct font for each character. -pub async fn layout_text(text: &str, ctx: TextContext<'_, '_>) -> Layout { - TextLayouter::new(text, ctx).layout().await +/// Layouts text into boxes. +struct TextLayouter<'a, 'p> { + ctx: TextContext<'a, 'p>, + text: &'a str, + actions: LayoutActions, + buffer: String, + active_font: FontIndex, + width: Size, } /// The context for text layouting. @@ -25,14 +27,12 @@ pub struct TextContext<'a, 'p> { pub alignment: LayoutAlignment, } -/// Layouts text into boxes. -struct TextLayouter<'a, 'p> { - ctx: TextContext<'a, 'p>, - text: &'a str, - actions: LayoutActions, - buffer: String, - active_font: FontIndex, - width: Size, +/// Layouts text into a box. +/// +/// There is no complex layout involved. The text is simply laid out left- +/// to-right using the correct font for each character. +pub async fn layout_text(text: &str, ctx: TextContext<'_, '_>) -> Layout { + TextLayouter::new(text, ctx).layout().await } impl<'a, 'p> TextLayouter<'a, 'p> { @@ -25,22 +25,24 @@ use smallvec::smallvec; use toddle::query::{FontLoader, FontProvider, SharedFontLoader}; -use crate::func::Scope; -use crate::layout::{Layouted, MultiLayout}; -use crate::syntax::{parse, ParseContext, Parsed, SyntaxModel, Position}; +use crate::layout::MultiLayout; +use crate::layout::prelude::*; +use crate::layout::{LayoutContext, Layouted, layout}; use crate::style::{LayoutStyle, PageStyle, TextStyle}; +use crate::syntax::{SyntaxModel, Scope, ParseContext, Parsed, parse}; +use crate::syntax::span::Position; #[macro_use] mod macros; -pub mod export; pub mod error; +pub mod export; #[macro_use] pub mod func; pub mod layout; pub mod library; -pub mod syntax; pub mod size; pub mod style; +pub mod syntax; /// Transforms source code into typesetted layouts. @@ -93,7 +95,6 @@ impl<'p> Typesetter<'p> { /// Layout a syntax tree and return the produced layout. pub async fn layout(&self, model: &SyntaxModel) -> Layouted<MultiLayout> { - use crate::layout::prelude::*; let margins = self.style.page.margins(); layout( &model, diff --git a/src/library/font.rs b/src/library/font.rs index b4a6218a..07707e90 100644 --- a/src/library/font.rs +++ b/src/library/font.rs @@ -1,4 +1,5 @@ use toddle::query::{FontWeight, FontStyle}; +use crate::size::FSize; use super::*; @@ -79,7 +80,7 @@ function! { #[derive(Debug, Clone, PartialEq)] pub struct FontSizeFunc { body: Option<SyntaxModel>, - size: Option<ScaleSize>, + size: Option<FSize>, } parse(header, body, ctx, errors, decos) { @@ -93,11 +94,11 @@ function! { layout(self, ctx, errors) { styled(&self.body, ctx, self.size, |t, s| { match s { - ScaleSize::Absolute(size) => { + FSize::Absolute(size) => { t.base_font_size = size; t.font_scale = 1.0; } - ScaleSize::Scaled(scale) => t.font_scale = scale, + FSize::Scaled(scale) => t.font_scale = scale, } }) } diff --git a/src/library/layout.rs b/src/library/layout.rs index fb8633b1..87e9c357 100644 --- a/src/library/layout.rs +++ b/src/library/layout.rs @@ -1,4 +1,5 @@ -use smallvec::smallvec; +use crate::size::PSize; +use crate::syntax::func::maps::{AxisMap, PosAxisMap}; use super::*; diff --git a/src/library/mod.rs b/src/library/mod.rs index e706642f..b570c48b 100644 --- a/src/library/mod.rs +++ b/src/library/mod.rs @@ -1,5 +1,6 @@ //! The standard library. +use crate::syntax::Scope; use crate::func::prelude::*; pub_use_mod!(font); diff --git a/src/library/page.rs b/src/library/page.rs index 25f81bc1..7e135f59 100644 --- a/src/library/page.rs +++ b/src/library/page.rs @@ -1,4 +1,6 @@ +use crate::size::Size; use crate::style::{Paper, PaperClass}; +use crate::syntax::func::maps::{AxisMap, PaddingMap}; use super::*; diff --git a/src/library/spacing.rs b/src/library/spacing.rs index 6c818292..907d5f9b 100644 --- a/src/library/spacing.rs +++ b/src/library/spacing.rs @@ -1,5 +1,8 @@ +use crate::size::FSize; +use crate::layout::SpacingKind; + use super::*; -use ContentKind::*; +use self::ContentKind::*; function! { diff --git a/src/size.rs b/src/size.rs index a5bc5d7f..9dc74dd6 100644 --- a/src/size.rs +++ b/src/size.rs @@ -129,7 +129,7 @@ pub type PSize = ScaleSize; /// A value in two dimensions. #[derive(Default, Copy, Clone, PartialEq)] -pub struct Value2D<T: Copy> { +pub struct Value2D<T> { /// The horizontal component. pub x: T, /// The vertical component. @@ -226,13 +226,13 @@ impl<T: Copy> Value2D<T> { } } -impl<T: Copy> Display for Value2D<T> where T: Display { +impl<T> Display for Value2D<T> where T: Display { fn fmt(&self, f: &mut Formatter) -> fmt::Result { write!(f, "[{}, {}]", self.x, self.y) } } -impl<T: Copy> Debug for Value2D<T> where T: Debug { +impl<T> Debug for Value2D<T> where T: Debug { fn fmt(&self, f: &mut Formatter) -> fmt::Result { write!(f, "[{:?}, {:?}]", self.x, self.y) } @@ -293,7 +293,7 @@ impl Neg for Size2D { /// A value that is stretchable in an interval from a minimal through an optimal /// to a maximal value. -pub struct StretchValue<T: Copy> { +pub struct StretchValue<T> { /// The minimum this value can be stretched to. pub min: T, /// The optimum for this value. @@ -302,20 +302,20 @@ pub struct StretchValue<T: Copy> { pub max: T, } -impl<T: Copy> StretchValue<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 } } } -impl<T: Copy> Display for StretchValue<T> where T: Display { +impl<T> Display for StretchValue<T> where T: Display { fn fmt(&self, f: &mut Formatter) -> fmt::Result { write!(f, "({}, {}, {})", self.min, self.opt, self.max) } } -impl<T: Copy> Debug for StretchValue<T> where T: Debug { +impl<T> Debug for StretchValue<T> where T: Debug { fn fmt(&self, f: &mut Formatter) -> fmt::Result { write!(f, "({:?}, {:?}, {:?})", self.min, self.opt, self.max) } @@ -326,7 +326,7 @@ pub type StretchSize = StretchValue<Size>; /// A value in four dimensions. #[derive(Default, Copy, Clone, PartialEq)] -pub struct ValueBox<T: Copy> { +pub struct ValueBox<T> { /// The left extent. pub left: T, /// The top extent. @@ -337,7 +337,7 @@ pub struct ValueBox<T: Copy> { pub bottom: T, } -impl<T: Copy> ValueBox<T> { +impl<T: Clone> ValueBox<T> { /// Create a new box from four sizes. pub fn new(left: T, top: T, right: T, bottom: T) -> ValueBox<T> { ValueBox { left, top, right, bottom } @@ -345,7 +345,12 @@ impl<T: Copy> ValueBox<T> { /// Create a box with all four fields set to the same value `s`. pub fn with_all(value: T) -> ValueBox<T> { - ValueBox { left: value, top: value, right: value, bottom: value } + ValueBox { + left: value.clone(), + top: value.clone(), + right: value.clone(), + bottom: value + } } /// Get a mutable reference to the value for the specified direction at the @@ -372,25 +377,25 @@ impl<T: Copy> ValueBox<T> { /// Set the `left` and `right` values. pub fn set_horizontal(&mut self, value: T) { - self.left = value; + self.left = value.clone(); self.right = value; } /// Set the `top` and `bottom` values. pub fn set_vertical(&mut self, value: T) { - self.top = value; + self.top = value.clone(); self.bottom = value; } } -impl<T: Copy> Display for ValueBox<T> where T: Display { +impl<T> Display for ValueBox<T> where T: Display { fn fmt(&self, f: &mut Formatter) -> fmt::Result { write!(f, "[left: {}, top: {}, right: {}, bottom: {}]", self.left, self.top, self.right, self.bottom) } } -impl<T: Copy> Debug for ValueBox<T> where T: Debug { +impl<T> Debug for ValueBox<T> where T: Debug { fn fmt(&self, f: &mut Formatter) -> fmt::Result { write!(f, "[left: {:?}, top: {:?}, right: {:?}, bottom: {:?}]", self.left, self.top, self.right, self.bottom) diff --git a/src/style.rs b/src/style.rs index 430deaca..22ed4d2d 100644 --- a/src/style.rs +++ b/src/style.rs @@ -1,7 +1,6 @@ //! Styles for text and pages. use toddle::query::{FontFallbackTree, FontVariant, FontStyle, FontWeight}; - use crate::size::{Size, Size2D, SizeBox, ValueBox, PSize}; diff --git a/src/syntax/expr.rs b/src/syntax/expr.rs index fe24c655..b4c0dfaa 100644 --- a/src/syntax/expr.rs +++ b/src/syntax/expr.rs @@ -1,4 +1,10 @@ -use super::*; +use std::fmt::{self, Display, Formatter}; + +use crate::error::Errors; +use crate::size::Size; +use super::func::{keys::Key, values::Value}; +use super::span::{Span, Spanned}; +use super::tokens::is_identifier; /// An argument or return value. @@ -91,6 +97,13 @@ pub struct Object { pub pairs: Vec<Pair>, } +/// A key-value pair in an object. +#[derive(Clone, PartialEq)] +pub struct Pair { + pub key: Spanned<Ident>, + pub value: Spanned<Expr>, +} + impl Object { pub fn new() -> Object { Object { pairs: vec![] } @@ -162,13 +175,6 @@ impl Object { } } -/// A key-value pair in an object. -#[derive(Clone, PartialEq)] -pub struct Pair { - pub key: Spanned<Ident>, - pub value: Spanned<Expr>, -} - impl Display for Expr { fn fmt(&self, f: &mut Formatter) -> fmt::Result { use Expr::*; diff --git a/src/syntax/func/keys.rs b/src/syntax/func/keys.rs index dff97bde..116cd4e6 100644 --- a/src/syntax/func/keys.rs +++ b/src/syntax/func/keys.rs @@ -1,9 +1,10 @@ use crate::layout::prelude::*; +use super::values::AlignmentValue::{self, *}; use super::*; -use AxisKey::*; -use PaddingKey::*; -use AlignmentValue::*; +use self::AxisKey::*; +use self::PaddingKey::*; + pub trait Key { @@ -28,7 +29,7 @@ macro_rules! key { fn parse(key: Spanned<&str>) -> Option<Self::Output> { match key.v { $($($p)|* => Some($r)),*, - other => None, + _ => None, } } } diff --git a/src/syntax/func/maps.rs b/src/syntax/func/maps.rs index 452c8ab1..8941024e 100644 --- a/src/syntax/func/maps.rs +++ b/src/syntax/func/maps.rs @@ -1,9 +1,11 @@ //! Deduplicating maps and keys for argument parsing. -use std::collections::HashMap; -use std::hash::Hash; -use crate::layout::{LayoutAxes, SpecificAxis, GenericAxis}; +use crate::error::Errors; +use crate::layout::prelude::*; use crate::size::{PSize, ValueBox}; +use crate::syntax::span::Spanned; +use super::keys::*; +use super::values::*; use super::*; @@ -179,7 +181,6 @@ impl PaddingMap { padding: &mut ValueBox<Option<PSize>> ) { use PaddingKey::*; - use SpecificAxis::*; let map = self.0.dedup(errors, |key, &val| { (match key { diff --git a/src/syntax/func/mod.rs b/src/syntax/func/mod.rs index b6691ab5..e66b4d6a 100644 --- a/src/syntax/func/mod.rs +++ b/src/syntax/func/mod.rs @@ -1,8 +1,10 @@ -use super::*; +use crate::error::{Error, Errors}; +use super::expr::{Expr, Ident, Tuple, Object, Pair}; +use super::span::{Span, Spanned}; -pub_use_mod!(maps); -pub_use_mod!(keys); -pub_use_mod!(values); +pub mod maps; +pub mod keys; +pub mod values; #[derive(Debug, Clone, PartialEq)] @@ -17,22 +19,6 @@ pub struct FuncArgs { pub key: Object, } -#[derive(Debug, Clone, PartialEq)] -pub enum Arg { - Pos(Spanned<Expr>), - Key(Pair), -} - -impl Arg { - /// The span or the value or combined span of key and value. - pub fn span(&self) -> Span { - match self { - Arg::Pos(item) => item.span, - Arg::Key(Pair { key, value }) => Span::merge(key.span, value.span), - } - } -} - impl FuncArgs { pub fn new() -> FuncArgs { FuncArgs { @@ -42,10 +28,10 @@ impl FuncArgs { } /// Add an argument. - pub fn add(&mut self, arg: Arg) { + pub fn add(&mut self, arg: FuncArg) { match arg { - Arg::Pos(item) => self.add_pos(item), - Arg::Key(pair) => self.add_key_pair(pair), + FuncArg::Pos(item) => self.add_pos(item), + FuncArg::Key(pair) => self.add_key_pair(pair), } } @@ -64,74 +50,27 @@ impl FuncArgs { self.key.add_pair(pair); } - pub fn into_iter(self) -> impl Iterator<Item=Arg> { - self.pos.items.into_iter().map(|item| Arg::Pos(item)) - .chain(self.key.pairs.into_iter().map(|pair| Arg::Key(pair))) + pub fn into_iter(self) -> impl Iterator<Item=FuncArg> { + self.pos.items.into_iter().map(|item| FuncArg::Pos(item)) + .chain(self.key.pairs.into_iter().map(|pair| FuncArg::Key(pair))) } +} - // /// Force-extract the first positional argument. - // pub fn get_pos<E: ExpressionKind>(&mut self) -> ParseResult<E> { - // expect(self.get_pos_opt()) - // } - - // /// Extract the first positional argument. - // pub fn get_pos_opt<E: ExpressionKind>(&mut self) -> ParseResult<Option<E>> { - // Ok(if !self.positional.items.is_empty() { - // let spanned = self.positional.items.remove(0); - // Some(E::from_expr(spanned)?) - // } else { - // None - // }) - // } - - // /// Force-extract a keyword argument. - // pub fn get_key<E: ExpressionKind>(&mut self, name: &str) -> ParseResult<E> { - // expect(self.get_key_opt(name)) - // } - - // /// Extract a keyword argument. - // pub fn get_key_opt<E: ExpressionKind>(&mut self, name: &str) -> ParseResult<Option<E>> { - // self.keyword.pairs.iter() - // .position(|p| p.key.v.0 == name) - // .map(|index| { - // let value = self.keyword.pairs.swap_remove(index).value; - // E::from_expr(value) - // }) - // .transpose() - // } - - // /// Iterator over positional arguments. - // pub fn iter_pos(&mut self) -> std::vec::IntoIter<Spanned<Expr>> { - // let tuple = std::mem::replace(&mut self.positional, Tuple::new()); - // tuple.items.into_iter() - // } - - // /// Iterator over all keyword arguments. - // pub fn iter_keys(&mut self) -> std::vec::IntoIter<Pair> { - // let object = std::mem::replace(&mut self.keyword, Object::new()); - // object.pairs.into_iter() - // } - - // /// Clear the argument lists. - // pub fn clear(&mut self) { - // self.positional.items.clear(); - // self.keyword.pairs.clear(); - // } - - // /// Whether both the positional and keyword argument lists are empty. - // pub fn is_empty(&self) -> bool { - // self.positional.items.is_empty() && self.keyword.pairs.is_empty() - // } +#[derive(Debug, Clone, PartialEq)] +pub enum FuncArg { + Pos(Spanned<Expr>), + Key(Pair), } -// /// Extract the option expression kind from the option or return an error. -// fn expect<E: ExpressionKind>(opt: ParseResult<Option<E>>) -> ParseResult<E> { -// match opt { -// Ok(Some(spanned)) => Ok(spanned), -// Ok(None) => error!("expected {}", E::NAME), -// Err(e) => Err(e), -// } -// } +impl FuncArg { + /// The span or the value or combined span of key and value. + pub fn span(&self) -> Span { + match self { + FuncArg::Pos(item) => item.span, + FuncArg::Key(Pair { key, value }) => Span::merge(key.span, value.span), + } + } +} pub trait OptionExt: Sized { fn or_missing(self, errors: &mut Errors, span: Span, what: &str) -> Self; diff --git a/src/syntax/func/values.rs b/src/syntax/func/values.rs index b29b9726..a767aef6 100644 --- a/src/syntax/func/values.rs +++ b/src/syntax/func/values.rs @@ -1,12 +1,13 @@ +use std::fmt::{self, Display, Formatter}; use std::marker::PhantomData; use toddle::query::{FontStyle, FontWeight}; use crate::layout::prelude::*; -use crate::size::ScaleSize; +use crate::size::{Size, ScaleSize}; use crate::style::Paper; use super::*; -use AlignmentValue::*; +use self::AlignmentValue::*; pub trait Value { @@ -76,20 +77,6 @@ impl<T: Value> Value for Defaultable<T> { } } -impl Value for Direction { - type Output = Self; - - fn parse(expr: Spanned<Expr>) -> Result<Self::Output, Error> { - Ok(match Ident::parse(expr)?.as_str() { - "left-to-right" | "ltr" | "LTR" => Direction::LeftToRight, - "right-to-left" | "rtl" | "RTL" => Direction::RightToLeft, - "top-to-bottom" | "ttb" | "TTB" => Direction::TopToBottom, - "bottom-to-top" | "btt" | "BTT" => Direction::BottomToTop, - other => return Err(err!("invalid direction")) - }) - } -} - impl Value for FontStyle { type Output = Self; @@ -134,6 +121,20 @@ impl Value for Paper { } } +impl Value for Direction { + type Output = Self; + + fn parse(expr: Spanned<Expr>) -> Result<Self::Output, Error> { + Ok(match Ident::parse(expr)?.as_str() { + "left-to-right" | "ltr" | "LTR" => LeftToRight, + "right-to-left" | "rtl" | "RTL" => RightToLeft, + "top-to-bottom" | "ttb" | "TTB" => TopToBottom, + "bottom-to-top" | "btt" | "BTT" => BottomToTop, + _ => return Err(err!("invalid direction")) + }) + } +} + #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] pub enum AlignmentValue { Align(Alignment), @@ -203,7 +204,7 @@ impl Value for AlignmentValue { "top" => Top, "right" => Right, "bottom" => Bottom, - other => return Err(err!("invalid alignment")) + _ => return Err(err!("invalid alignment")) }) } } diff --git a/src/syntax/mod.rs b/src/syntax/mod.rs index 356535ae..d5afbca6 100644 --- a/src/syntax/mod.rs +++ b/src/syntax/mod.rs @@ -1,77 +1,25 @@ //! Tokenization and parsing of source code. use std::any::Any; -use std::fmt::{self, Debug, Display, Formatter}; -use std::future::Future; -use std::pin::Pin; +use std::fmt::Debug; +use async_trait::async_trait; use serde::Serialize; -use crate::error::{Error, Errors}; -use crate::func::{Commands, Command}; -use crate::layout::{Layouted, LayoutContext}; -use crate::size::Size; +use crate::layout::{LayoutContext, Layouted, Commands, Command}; +use self::span::{Spanned, SpanVec}; -pub_use_mod!(expr); -pub_use_mod!(func); -pub_use_mod!(tokens); -pub_use_mod!(parsing); -pub_use_mod!(span); +pub mod expr; +pub mod func; +pub mod span; -/// Common syntax types. -pub mod prelude { - pub use super::*; -} +pub_use_mod!(scope); +pub_use_mod!(parsing); +pub_use_mod!(tokens); -#[async_trait::async_trait(?Send)] +#[async_trait(?Send)] pub trait Model: Debug + ModelBounds { - async fn layout<'a>( - &'a self, - ctx: LayoutContext<'_, '_> - ) -> Layouted<Commands<'a>>; -} - -pub type DynFuture<'a, T> = Pin<Box<dyn Future<Output=T> + 'a>>; - -impl dyn Model { - pub fn downcast<T>(&self) -> Option<&T> where T: Model + 'static { - self.as_any().downcast_ref::<T>() - } -} - -impl PartialEq for dyn Model { - fn eq(&self, other: &dyn Model) -> bool { - self.bound_eq(other) - } -} - -impl Clone for Box<dyn Model> { - fn clone(&self) -> Self { - self.bound_clone() - } -} - -pub trait ModelBounds { - fn as_any(&self) -> &dyn Any; - fn bound_eq(&self, other: &dyn Model) -> bool; - fn bound_clone(&self) -> Box<dyn Model>; -} - -impl<T> ModelBounds for T where T: Model + PartialEq + Clone + 'static { - fn as_any(&self) -> &dyn Any { - self - } - - fn bound_eq(&self, other: &dyn Model) -> bool { - match other.as_any().downcast_ref::<Self>() { - Some(other) => self == other, - None => false, - } - } - - fn bound_clone(&self) -> Box<dyn Model> { - Box::new(self.clone()) - } + async fn layout<'a>(&'a self, ctx: LayoutContext<'_, '_>) -> Layouted<Commands<'a>>; } /// A tree representation of source code. @@ -92,7 +40,7 @@ impl SyntaxModel { } } -#[async_trait::async_trait(?Send)] +#[async_trait(?Send)] impl Model for SyntaxModel { async fn layout<'a>(&'a self, _: LayoutContext<'_, '_>) -> Layouted<Commands<'a>> { Layouted { @@ -144,3 +92,44 @@ pub enum Decoration { InvalidFuncName, ArgumentKey, } + +impl dyn Model { + pub fn downcast<T>(&self) -> Option<&T> where T: Model + 'static { + self.as_any().downcast_ref::<T>() + } +} + +impl PartialEq for dyn Model { + fn eq(&self, other: &dyn Model) -> bool { + self.bound_eq(other) + } +} + +impl Clone for Box<dyn Model> { + fn clone(&self) -> Self { + self.bound_clone() + } +} + +pub trait ModelBounds { + fn as_any(&self) -> &dyn Any; + fn bound_eq(&self, other: &dyn Model) -> bool; + fn bound_clone(&self) -> Box<dyn Model>; +} + +impl<T> ModelBounds for T where T: Model + PartialEq + Clone + 'static { + fn as_any(&self) -> &dyn Any { + self + } + + fn bound_eq(&self, other: &dyn Model) -> bool { + match other.as_any().downcast_ref::<Self>() { + Some(other) => self == other, + None => false, + } + } + + fn bound_clone(&self) -> Box<dyn Model> { + Box::new(self.clone()) + } +} diff --git a/src/syntax/parsing.rs b/src/syntax/parsing.rs index e726a2e0..3e3e827f 100644 --- a/src/syntax/parsing.rs +++ b/src/syntax/parsing.rs @@ -1,6 +1,10 @@ -use crate::func::Scope; +use crate::error::Errors; +use super::expr::*; +use super::func::{FuncHeader, FuncArgs, FuncArg}; +use super::scope::Scope; +use super::span::{Position, Span, Spanned, SpanVec, offset_spans}; +use super::tokens::{Token, Tokens, TokenizationMode}; use super::*; -use Token::*; /// The context for parsing. @@ -37,13 +41,13 @@ pub fn parse(start: Position, src: &str, ctx: ParseContext) -> Parsed<SyntaxMode let span = token.span; let node = match token.v { - Space(newlines) => if newlines >= 2 { + Token::Space(newlines) => if newlines >= 2 { Node::Newline } else { Node::Space }, - Function { header, body, terminated } => { + Token::Function { header, body, terminated } => { let parsed: Parsed<Node> = FuncParser::new(header, body, ctx).parse(); errors.extend(offset_spans(parsed.errors, span.start)); @@ -56,15 +60,15 @@ pub fn parse(start: Position, src: &str, ctx: ParseContext) -> Parsed<SyntaxMode parsed.output } - Star => Node::ToggleBolder, - Underscore => Node::ToggleItalic, - Backtick => Node::ToggleMonospace, - Text(text) => Node::Text(text.to_owned()), + Token::Star => Node::ToggleBolder, + Token::Underscore => Node::ToggleItalic, + Token::Backtick => Node::ToggleMonospace, + Token::Text(text) => Node::Text(text.to_owned()), - LineComment(_) | BlockComment(_) => continue, + Token::LineComment(_) | Token::BlockComment(_) => continue, other => { - errors.push(err!(span; "unexpected {}", name(other))); + errors.push(err!(span; "unexpected {}", other.name())); continue; } }; @@ -140,7 +144,7 @@ impl<'s> FuncParser<'s> { self.skip_whitespace(); let name = match self.eat() { - Some(Spanned { v: ExprIdent(ident), span }) => { + Some(Spanned { v: Token::ExprIdent(ident), span }) => { Spanned { v: Ident(ident.to_string()), span } } other => { @@ -151,7 +155,7 @@ impl<'s> FuncParser<'s> { self.skip_whitespace(); let args = match self.eat().map(Spanned::value) { - Some(Colon) => self.parse_func_args(), + Some(Token::Colon) => self.parse_func_args(), Some(_) => { self.expected_at("colon", name.span.end); FuncArgs::new() @@ -179,38 +183,38 @@ impl<'s> FuncParser<'s> { } /// Parse a positional or keyword argument. - fn parse_arg(&mut self) -> Option<Arg> { + fn parse_arg(&mut self) -> Option<FuncArg> { let first = self.peek()?; let span = first.span; - let arg = if let ExprIdent(ident) = first.v { + let arg = if let Token::ExprIdent(ident) = first.v { self.eat(); self.skip_whitespace(); let ident = Ident(ident.to_string()); - if let Some(Equals) = self.peekv() { + if let Some(Token::Equals) = self.peekv() { self.eat(); self.skip_whitespace(); self.decorations.push(Spanned::new(Decoration::ArgumentKey, span)); self.parse_expr().map(|value| { - Arg::Key(Pair { + FuncArg::Key(Pair { key: Spanned { v: ident, span }, value, }) }) } else { - Some(Arg::Pos(Spanned::new(Expr::Ident(ident), span))) + Some(FuncArg::Pos(Spanned::new(Expr::Ident(ident), span))) } } else { - self.parse_expr().map(|expr| Arg::Pos(expr)) + self.parse_expr().map(|expr| FuncArg::Pos(expr)) }; if let Some(arg) = &arg { self.skip_whitespace(); match self.peekv() { - Some(Comma) => { self.eat(); } + Some(Token::Comma) => { self.eat(); } Some(_) => self.expected_at("comma", arg.span().end), _ => {} } @@ -228,11 +232,11 @@ impl<'s> FuncParser<'s> { let spanned = |v| Spanned { v, span: first.span }; Some(match first.v { - ExprIdent(i) => { + Token::ExprIdent(i) => { self.eat(); spanned(Expr::Ident(Ident(i.to_string()))) } - ExprStr { string, terminated } => { + Token::ExprStr { string, terminated } => { if !terminated { self.expected_at("quote", first.span.end); } @@ -240,12 +244,13 @@ impl<'s> FuncParser<'s> { self.eat(); spanned(Expr::Str(string.to_string())) } - ExprNumber(n) => { self.eat(); spanned(Expr::Number(n)) } - ExprSize(s) => { self.eat(); spanned(Expr::Size(s)) } - ExprBool(b) => { self.eat(); spanned(Expr::Bool(b)) } + Token::ExprNumber(n) => { self.eat(); spanned(Expr::Number(n)) } + Token::ExprSize(s) => { self.eat(); spanned(Expr::Size(s)) } + Token::ExprBool(b) => { self.eat(); spanned(Expr::Bool(b)) } + + Token::LeftParen => self.parse_tuple(), + Token::LeftBrace => self.parse_object(), - LeftParen => self.parse_tuple(), - LeftBrace => self.parse_object(), _ => return None, }) } @@ -255,7 +260,7 @@ impl<'s> FuncParser<'s> { let start = self.pos(); // TODO: Do the thing. - self.eat_until(|t| t == RightParen, true); + self.eat_until(|t| t == Token::RightParen, true); let end = self.pos(); let span = Span { start, end }; @@ -268,7 +273,7 @@ impl<'s> FuncParser<'s> { let start = self.pos(); // TODO: Do the thing. - self.eat_until(|t| t == RightBrace, true); + self.eat_until(|t| t == Token::RightBrace, true); let end = self.pos(); let span = Span { start, end }; @@ -278,15 +283,16 @@ impl<'s> FuncParser<'s> { /// Skip all whitespace/comment tokens. fn skip_whitespace(&mut self) { - self.eat_until(|t| - !matches!(t, Space(_) | LineComment(_) | BlockComment(_)), false) + self.eat_until(|t| !matches!(t, + Token::Space(_) | Token::LineComment(_) | + Token::BlockComment(_)), false) } /// Add an error about an expected `thing` which was not found, showing /// what was found instead. fn expected_found(&mut self, thing: &str, found: Spanned<Token>) { self.errors.push(err!(found.span; - "expected {}, found {}", thing, name(found.v))); + "expected {}, found {}", thing, found.v.name())); } /// Add an error about an `thing` which was expected but not found at the @@ -348,31 +354,3 @@ impl<'s> FuncParser<'s> { .unwrap_or_else(|| self.tokens.pos()) } } - -/// The name of a token in an `(un)expected <...>` error. -fn name(token: Token) -> &'static str { - match token { - Space(_) => "space", - LineComment(_) | BlockComment(_) => "comment", - Function { .. } => "function", - LeftParen => "opening paren", - RightParen => "closing paren", - LeftBrace => "opening brace", - RightBrace => "closing brace", - Colon => "colon", - Comma => "comma", - Equals => "equals sign", - ExprIdent(_) => "identifier", - ExprStr { .. } => "string", - ExprNumber(_) => "number", - ExprSize(_) => "size", - ExprBool(_) => "boolean", - Star => "star", - Underscore => "underscore", - Backtick => "backtick", - Text(_) => "invalid identifier", - Invalid("]") => "closing bracket", - Invalid("*/") => "end of block comment", - Invalid(_) => "invalid token", - } -} diff --git a/src/func/mod.rs b/src/syntax/scope.rs index b3721d91..2aae331d 100644 --- a/src/func/mod.rs +++ b/src/syntax/scope.rs @@ -1,67 +1,12 @@ -//! Dynamic typesetting functions. - use std::collections::HashMap; use std::fmt::{self, Debug, Formatter}; -use self::prelude::*; - -#[macro_use] -mod macros; - -/// Useful imports for creating your own functions. -pub mod prelude { - pub use super::{Scope, Parse, Command, Commands}; - pub use crate::error::Error; - pub use crate::layout::prelude::*; - pub use crate::syntax::prelude::*; - pub use crate::size::{Size, Size2D, SizeBox, ValueBox, ScaleSize, FSize, PSize}; - pub use crate::style::{LayoutStyle, PageStyle, TextStyle}; - pub use Command::*; -} - -/// Parse a function from source code. -pub trait Parse { - type Meta: Clone; - - /// Parse the header and body into this function given a context. - fn parse( - header: FuncHeader, - body: Option<Spanned<&str>>, - ctx: ParseContext, - metadata: Self::Meta, - ) -> Parsed<Self> where Self: Sized; -} - -/// A function which parses the source of a function into a model type which -/// implements [`Model`]. -type Parser = dyn Fn( - FuncHeader, - Option<Spanned<&str>>, - ParseContext, -) -> Parsed<Box<dyn Model>>; - -/// A sequence of layouting commands. -pub type Commands<'a> = Vec<Command<'a>>; - -/// Layouting commands from functions to the typesetting engine. -#[derive(Debug)] -pub enum Command<'a> { - LayoutSyntaxModel(&'a SyntaxModel), - - Add(Layout), - AddMultiple(MultiLayout), - AddSpacing(Size, SpacingKind, GenericAxis), +use crate::func::ParseFunc; +use super::func::FuncHeader; +use super::parsing::{ParseContext, Parsed}; +use super::span::Spanned; +use super::Model; - FinishLine, - FinishSpace, - BreakParagraph, - BreakPage, - - SetTextStyle(TextStyle), - SetPageStyle(PageStyle), - SetAlignment(LayoutAlignment), - SetAxes(LayoutAxes), -} /// A map from identifiers to function parsers. pub struct Scope { @@ -73,7 +18,7 @@ impl Scope { /// Create a new empty scope with a fallback parser that is invoked when no /// match is found. pub fn new<F>() -> Scope - where F: Parse<Meta=()> + Model + 'static { + where F: ParseFunc<Meta=()> + Model + 'static { Scope { parsers: HashMap::new(), fallback: parser::<F>(()), @@ -87,14 +32,14 @@ impl Scope { /// Associate the given name with a type that is parseable into a function. pub fn add<F>(&mut self, name: &str) - where F: Parse<Meta=()> + Model + 'static { + where F: ParseFunc<Meta=()> + Model + 'static { self.add_with_meta::<F>(name, ()); } /// Add a parseable type with additional metadata that is given to the /// parser (other than the default of `()`). - pub fn add_with_meta<F>(&mut self, name: &str, metadata: <F as Parse>::Meta) - where F: Parse + Model + 'static { + pub fn add_with_meta<F>(&mut self, name: &str, metadata: <F as ParseFunc>::Meta) + where F: ParseFunc + Model + 'static { self.parsers.insert( name.to_owned(), parser::<F>(metadata), @@ -121,7 +66,16 @@ impl Debug for Scope { } } -fn parser<F>(metadata: <F as Parse>::Meta) -> Box<Parser> where F: Parse + Model + 'static { +/// A function which parses the source of a function into a model type which +/// implements [`Model`]. +type Parser = dyn Fn( + FuncHeader, + Option<Spanned<&str>>, + ParseContext, +) -> Parsed<Box<dyn Model>>; + +fn parser<F>(metadata: <F as ParseFunc>::Meta) -> Box<Parser> +where F: ParseFunc + Model + 'static { Box::new(move |h, b, c| { F::parse(h, b, c, metadata.clone()) .map(|model| Box::new(model) as Box<dyn Model>) diff --git a/src/syntax/span.rs b/src/syntax/span.rs index e049861f..f5bd4caf 100644 --- a/src/syntax/span.rs +++ b/src/syntax/span.rs @@ -5,45 +5,56 @@ use std::ops::{Add, Sub}; use serde::Serialize; -/// Annotates a value with the part of the source code it corresponds to. -#[derive(Copy, Clone, Eq, PartialEq, Hash, Serialize)] -pub struct Spanned<T> { - pub v: T, - pub span: Span, +/// A line-column position in source code. +#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize)] +pub struct Position { + /// The 0-indexed line (inclusive). + pub line: usize, + /// The 0-indexed column (inclusive). + pub column: usize, } -impl<T> Spanned<T> { - pub fn new(v: T, span: Span) -> Spanned<T> { - Spanned { v, span } - } +impl Position { + pub const ZERO: Position = Position { line: 0, column: 0 }; - pub fn value(self) -> T { - self.v + pub fn new(line: usize, column: usize) -> Position { + Position { line, column } } +} - pub fn map<V, F>(self, f: F) -> Spanned<V> where F: FnOnce(T) -> V { - Spanned { v: f(self.v), span: self.span } - } +impl Add for Position { + type Output = Position; - pub fn map_span<F>(mut self, f: F) -> Spanned<T> where F: FnOnce(Span) -> Span { - self.span = f(self.span); - self + fn add(self, rhs: Position) -> Position { + if rhs.line == 0 { + Position { + line: self.line, + column: self.column + rhs.column + } + } else { + Position { + line: self.line + rhs.line, + column: rhs.column, + } + } } } -impl<T> Display for Spanned<T> where T: std::fmt::Display { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - write!(f, "({}, {}, ", self.span.start, self.span.end)?; - self.v.fmt(f)?; - write!(f, ")") - } -} +impl Sub for Position { + type Output = Position; -impl<T> Debug for Spanned<T> where T: std::fmt::Debug { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - write!(f, "({}, {}, ", self.span.start, self.span.end)?; - self.v.fmt(f)?; - write!(f, ")") + fn sub(self, rhs: Position) -> Position { + if self.line == rhs.line { + Position { + line: 0, + column: self.column - rhs.column + } + } else { + Position { + line: self.line - rhs.line, + column: self.column, + } + } } } @@ -84,65 +95,37 @@ impl Span { } } -impl Display for Span { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - write!(f, "({}, {})", self.start, self.end) - } -} - -debug_display!(Span); - -/// A line-column position in source code. -#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize)] -pub struct Position { - /// The 0-indexed line (inclusive). - pub line: usize, - /// The 0-indexed column (inclusive). - pub column: usize, +/// Annotates a value with the part of the source code it corresponds to. +#[derive(Copy, Clone, Eq, PartialEq, Hash, Serialize)] +pub struct Spanned<T> { + pub v: T, + pub span: Span, } -impl Position { - pub const ZERO: Position = Position { line: 0, column: 0 }; +impl<T> Spanned<T> { + pub fn new(v: T, span: Span) -> Spanned<T> { + Spanned { v, span } + } - pub fn new(line: usize, column: usize) -> Position { - Position { line, column } + pub fn value(self) -> T { + self.v } -} -impl Add for Position { - type Output = Position; + pub fn map<V, F>(self, f: F) -> Spanned<V> where F: FnOnce(T) -> V { + Spanned { v: f(self.v), span: self.span } + } - fn add(self, rhs: Position) -> Position { - if rhs.line == 0 { - Position { - line: self.line, - column: self.column + rhs.column - } - } else { - Position { - line: self.line + rhs.line, - column: rhs.column, - } - } + pub fn map_span<F>(mut self, f: F) -> Spanned<T> where F: FnOnce(Span) -> Span { + self.span = f(self.span); + self } } -impl Sub for Position { - type Output = Position; +/// A vector of spanned things. +pub type SpanVec<T> = Vec<Spanned<T>>; - fn sub(self, rhs: Position) -> Position { - if self.line == rhs.line { - Position { - line: 0, - column: self.column - rhs.column - } - } else { - Position { - line: self.line - rhs.line, - column: self.column, - } - } - } +pub fn offset_spans<T>(vec: SpanVec<T>, start: Position) -> impl Iterator<Item=Spanned<T>> { + vec.into_iter().map(move |s| s.map_span(|span| span.offset(start))) } impl Display for Position { @@ -151,11 +134,27 @@ impl Display for Position { } } -debug_display!(Position); +impl Display for Span { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + write!(f, "({}, {})", self.start, self.end) + } +} -/// A vector of spanned things. -pub type SpanVec<T> = Vec<Spanned<T>>; +impl<T> Display for Spanned<T> where T: std::fmt::Display { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + write!(f, "({}, {}, ", self.span.start, self.span.end)?; + self.v.fmt(f)?; + write!(f, ")") + } +} -pub fn offset_spans<T>(vec: SpanVec<T>, start: Position) -> impl Iterator<Item=Spanned<T>> { - vec.into_iter().map(move |s| s.map_span(|span| span.offset(start))) +impl<T> Debug for Spanned<T> where T: std::fmt::Debug { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + write!(f, "({}, {}, ", self.span.start, self.span.end)?; + self.v.fmt(f)?; + write!(f, ")") + } } + +debug_display!(Position); +debug_display!(Span); diff --git a/src/syntax/tokens.rs b/src/syntax/tokens.rs index d0adbf60..0a8e2f17 100644 --- a/src/syntax/tokens.rs +++ b/src/syntax/tokens.rs @@ -2,9 +2,11 @@ use std::iter::Peekable; use std::str::Chars; use unicode_xid::UnicodeXID; -use super::*; -use Token::*; -use TokenizationMode::*; +use crate::size::Size; +use super::span::{Position, Span, Spanned}; + +use self::Token::*; +use self::TokenizationMode::*; /// A minimal semantic entity of source code. @@ -68,6 +70,37 @@ pub enum Token<'s> { Invalid(&'s str), } +impl<'s> Token<'s> { + /// The natural-language name for this token for use in error messages. + pub fn name(self) -> &'static str { + match self { + Space(_) => "space", + LineComment(_) => "line comment", + BlockComment(_) => "block comment", + Function { .. } => "function", + LeftParen => "opening paren", + RightParen => "closing paren", + LeftBrace => "opening brace", + RightBrace => "closing brace", + Colon => "colon", + Comma => "comma", + Equals => "equals sign", + ExprIdent(_) => "identifier", + ExprStr { .. } => "string", + ExprNumber(_) => "number", + ExprSize(_) => "size", + ExprBool(_) => "boolean", + Star => "star", + Underscore => "underscore", + Backtick => "backtick", + Text(_) => "invalid identifier", + Invalid("]") => "closing bracket", + Invalid("*/") => "end of block comment", + Invalid(_) => "invalid token", + } + } +} + /// An iterator over the tokens of a string of source code. pub struct Tokens<'s> { src: &'s str, |
