summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/error.rs14
-rw-r--r--src/export/pdf.rs4
-rw-r--r--src/func.rs (renamed from src/func/macros.rs)49
-rw-r--r--src/layout/actions.rs6
-rw-r--r--src/layout/line.rs1
-rw-r--r--src/layout/mod.rs125
-rw-r--r--src/layout/model.rs82
-rw-r--r--src/layout/text.rs28
-rw-r--r--src/lib.rs13
-rw-r--r--src/library/font.rs7
-rw-r--r--src/library/layout.rs3
-rw-r--r--src/library/mod.rs1
-rw-r--r--src/library/page.rs2
-rw-r--r--src/library/spacing.rs5
-rw-r--r--src/size.rs33
-rw-r--r--src/style.rs1
-rw-r--r--src/syntax/expr.rs22
-rw-r--r--src/syntax/func/keys.rs9
-rw-r--r--src/syntax/func/maps.rs9
-rw-r--r--src/syntax/func/mod.rs113
-rw-r--r--src/syntax/func/values.rs35
-rw-r--r--src/syntax/mod.rs119
-rw-r--r--src/syntax/parsing.rs96
-rw-r--r--src/syntax/scope.rs (renamed from src/func/mod.rs)84
-rw-r--r--src/syntax/span.rs165
-rw-r--r--src/syntax/tokens.rs39
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> {
diff --git a/src/lib.rs b/src/lib.rs
index 3ebe34c2..946e16d2 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -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,