Reorganize modules 🧱
This commit is contained in:
parent
03fddaf3ae
commit
0a087cd28b
14
src/error.rs
14
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 }
|
||||
}
|
||||
}
|
||||
|
@ -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};
|
||||
|
@ -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)*) => {
|
@ -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.
|
||||
|
@ -1,3 +1,4 @@
|
||||
use super::stack::{StackLayouter, StackContext};
|
||||
use super::*;
|
||||
|
||||
|
||||
|
@ -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;
|
||||
|
||||
pub_use_mod!(actions);
|
||||
pub_use_mod!(model);
|
||||
|
||||
/// Common types for layouting.
|
||||
pub mod prelude {
|
||||
pub use super::*;
|
||||
pub use GenericAxis::*;
|
||||
pub use SpecificAxis::*;
|
||||
pub use Direction::*;
|
||||
pub use Alignment::*;
|
||||
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, *};
|
||||
}
|
||||
|
||||
/// 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 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,
|
||||
/// 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<()>;
|
||||
}
|
||||
|
||||
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,
|
||||
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(())
|
||||
}
|
||||
}
|
||||
|
@ -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> {
|
||||
|
@ -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> {
|
||||
|
13
src/lib.rs
13
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,
|
||||
|
@ -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,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
use smallvec::smallvec;
|
||||
use crate::size::PSize;
|
||||
use crate::syntax::func::maps::{AxisMap, PosAxisMap};
|
||||
use super::*;
|
||||
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
//! The standard library.
|
||||
|
||||
use crate::syntax::Scope;
|
||||
use crate::func::prelude::*;
|
||||
|
||||
pub_use_mod!(font);
|
||||
|
@ -1,4 +1,6 @@
|
||||
use crate::size::Size;
|
||||
use crate::style::{Paper, PaperClass};
|
||||
use crate::syntax::func::maps::{AxisMap, PaddingMap};
|
||||
use super::*;
|
||||
|
||||
|
||||
|
@ -1,5 +1,8 @@
|
||||
use crate::size::FSize;
|
||||
use crate::layout::SpacingKind;
|
||||
|
||||
use super::*;
|
||||
use ContentKind::*;
|
||||
use self::ContentKind::*;
|
||||
|
||||
|
||||
function! {
|
||||
|
33
src/size.rs
33
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)
|
||||
|
@ -1,7 +1,6 @@
|
||||
//! Styles for text and pages.
|
||||
|
||||
use toddle::query::{FontFallbackTree, FontVariant, FontStyle, FontWeight};
|
||||
|
||||
use crate::size::{Size, Size2D, SizeBox, ValueBox, PSize};
|
||||
|
||||
|
||||
|
@ -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::*;
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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()
|
||||
// }
|
||||
}
|
||||
|
||||
// /// 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),
|
||||
// }
|
||||
// }
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum FuncArg {
|
||||
Pos(Spanned<Expr>),
|
||||
Key(Pair),
|
||||
}
|
||||
|
||||
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;
|
||||
|
@ -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"))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -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 mod expr;
|
||||
pub mod func;
|
||||
pub mod span;
|
||||
|
||||
pub_use_mod!(scope);
|
||||
pub_use_mod!(parsing);
|
||||
pub_use_mod!(span);
|
||||
|
||||
/// Common syntax types.
|
||||
pub mod prelude {
|
||||
pub use super::*;
|
||||
}
|
||||
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())
|
||||
}
|
||||
}
|
||||
|
@ -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",
|
||||
}
|
||||
}
|
||||
|
@ -1,67 +1,12 @@
|
||||
//! Dynamic typesetting functions.
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::fmt::{self, Debug, Formatter};
|
||||
|
||||
use self::prelude::*;
|
||||
use crate::func::ParseFunc;
|
||||
use super::func::FuncHeader;
|
||||
use super::parsing::{ParseContext, Parsed};
|
||||
use super::span::Spanned;
|
||||
use super::Model;
|
||||
|
||||
#[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),
|
||||
|
||||
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>)
|
@ -5,93 +5,6 @@ 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,
|
||||
}
|
||||
|
||||
impl<T> Spanned<T> {
|
||||
pub fn new(v: T, span: Span) -> Spanned<T> {
|
||||
Spanned { v, span }
|
||||
}
|
||||
|
||||
pub fn value(self) -> T {
|
||||
self.v
|
||||
}
|
||||
|
||||
pub fn map<V, F>(self, f: F) -> Spanned<V> where F: FnOnce(T) -> V {
|
||||
Spanned { v: f(self.v), span: self.span }
|
||||
}
|
||||
|
||||
pub fn map_span<F>(mut self, f: F) -> Spanned<T> where F: FnOnce(Span) -> Span {
|
||||
self.span = f(self.span);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
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<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, ")")
|
||||
}
|
||||
}
|
||||
|
||||
/// Describes a slice of source code.
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Hash, Serialize)]
|
||||
pub struct Span {
|
||||
pub start: Position,
|
||||
pub end: Position,
|
||||
}
|
||||
|
||||
impl Span {
|
||||
pub const ZERO: Span = Span { start: Position::ZERO, end: Position::ZERO };
|
||||
|
||||
pub fn new(start: Position, end: Position) -> Span {
|
||||
Span { start, end }
|
||||
}
|
||||
|
||||
pub fn merge(a: Span, b: Span) -> Span {
|
||||
Span {
|
||||
start: a.start.min(b.start),
|
||||
end: a.end.max(b.end),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn at(pos: Position) -> Span {
|
||||
Span { start: pos, end: pos }
|
||||
}
|
||||
|
||||
pub fn expand(&mut self, other: Span) {
|
||||
*self = Span::merge(*self, other)
|
||||
}
|
||||
|
||||
pub fn offset(self, start: Position) -> Span {
|
||||
Span {
|
||||
start: start + self.start,
|
||||
end: start + self.end,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
@ -145,13 +58,68 @@ impl Sub for Position {
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Position {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
write!(f, "{}:{}", self.line, self.column)
|
||||
/// Describes a slice of source code.
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Hash, Serialize)]
|
||||
pub struct Span {
|
||||
pub start: Position,
|
||||
pub end: Position,
|
||||
}
|
||||
|
||||
impl Span {
|
||||
pub const ZERO: Span = Span { start: Position::ZERO, end: Position::ZERO };
|
||||
|
||||
pub fn new(start: Position, end: Position) -> Span {
|
||||
Span { start, end }
|
||||
}
|
||||
|
||||
pub fn merge(a: Span, b: Span) -> Span {
|
||||
Span {
|
||||
start: a.start.min(b.start),
|
||||
end: a.end.max(b.end),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn at(pos: Position) -> Span {
|
||||
Span { start: pos, end: pos }
|
||||
}
|
||||
|
||||
pub fn expand(&mut self, other: Span) {
|
||||
*self = Span::merge(*self, other)
|
||||
}
|
||||
|
||||
pub fn offset(self, start: Position) -> Span {
|
||||
Span {
|
||||
start: start + self.start,
|
||||
end: start + self.end,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
debug_display!(Position);
|
||||
/// 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<T> Spanned<T> {
|
||||
pub fn new(v: T, span: Span) -> Spanned<T> {
|
||||
Spanned { v, span }
|
||||
}
|
||||
|
||||
pub fn value(self) -> T {
|
||||
self.v
|
||||
}
|
||||
|
||||
pub fn map<V, F>(self, f: F) -> Spanned<V> where F: FnOnce(T) -> V {
|
||||
Spanned { v: f(self.v), span: self.span }
|
||||
}
|
||||
|
||||
pub fn map_span<F>(mut self, f: F) -> Spanned<T> where F: FnOnce(Span) -> Span {
|
||||
self.span = f(self.span);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// A vector of spanned things.
|
||||
pub type SpanVec<T> = Vec<Spanned<T>>;
|
||||
@ -159,3 +127,34 @@ pub type SpanVec<T> = Vec<Spanned<T>>;
|
||||
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 {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
write!(f, "{}:{}", self.line, self.column)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Span {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
write!(f, "({}, {})", self.start, self.end)
|
||||
}
|
||||
}
|
||||
|
||||
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<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);
|
||||
|
@ -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,
|
||||
|
Loading…
x
Reference in New Issue
Block a user