mirror of
https://github.com/typst/typst
synced 2025-06-28 08:12:53 +08:00
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 serde::Serialize;
|
||||||
use crate::syntax::SpanVec;
|
use crate::syntax::span::SpanVec;
|
||||||
|
|
||||||
|
|
||||||
pub type Errors = SpanVec<Error>;
|
pub type Errors = SpanVec<Error>;
|
||||||
@ -10,14 +10,14 @@ pub struct Error {
|
|||||||
pub severity: Severity,
|
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)]
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Serialize)]
|
||||||
pub enum Severity {
|
pub enum Severity {
|
||||||
Warning,
|
Warning,
|
||||||
Error,
|
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::doc::{Catalog, Page, PageTree, Resource, Text};
|
||||||
use tide::font::{
|
use tide::font::{
|
||||||
CIDFont, CIDFontType, CIDSystemInfo, FontDescriptor, FontFlags, Type0Font,
|
CIDFont, CIDFontType, CIDSystemInfo, FontDescriptor, FontFlags, Type0Font,
|
||||||
CMap, CMapEncoding, FontStream, GlyphUnit, WidthRecord
|
CMap, CMapEncoding, FontStream, GlyphUnit, WidthRecord,
|
||||||
};
|
};
|
||||||
|
|
||||||
use toddle::Error as FontError;
|
use toddle::Error as FontError;
|
||||||
@ -16,7 +16,7 @@ use toddle::font::OwnedFont;
|
|||||||
use toddle::query::{SharedFontLoader, FontIndex};
|
use toddle::query::{SharedFontLoader, FontIndex};
|
||||||
use toddle::tables::{
|
use toddle::tables::{
|
||||||
CharMap, Header, HorizontalMetrics, MacStyleFlags,
|
CharMap, Header, HorizontalMetrics, MacStyleFlags,
|
||||||
Name, NameEntry, Post, OS2
|
Name, NameEntry, Post, OS2,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::layout::{MultiLayout, Layout, LayoutAction};
|
use crate::layout::{MultiLayout, Layout, LayoutAction};
|
||||||
|
@ -1,5 +1,35 @@
|
|||||||
//! Helper types and macros for creating custom functions.
|
//! 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_export]
|
||||||
macro_rules! function {
|
macro_rules! function {
|
||||||
@ -35,15 +65,15 @@ macro_rules! function {
|
|||||||
$decos:ident,
|
$decos:ident,
|
||||||
$metadata:ident
|
$metadata:ident
|
||||||
) $code:block $($r:tt)*) => {
|
) $code:block $($r:tt)*) => {
|
||||||
impl $crate::func::Parse for $name {
|
impl $crate::func::ParseFunc for $name {
|
||||||
type Meta = $meta;
|
type Meta = $meta;
|
||||||
|
|
||||||
fn parse(
|
fn parse(
|
||||||
#[allow(unused)] mut header: FuncHeader,
|
#[allow(unused)] mut header: $crate::syntax::func::FuncHeader,
|
||||||
#[allow(unused)] $body: Option<Spanned<&str>>,
|
#[allow(unused)] $body: Option<$crate::syntax::span::Spanned<&str>>,
|
||||||
#[allow(unused)] $ctx: ParseContext,
|
#[allow(unused)] $ctx: $crate::syntax::ParseContext,
|
||||||
#[allow(unused)] $metadata: Self::Meta,
|
#[allow(unused)] $metadata: Self::Meta,
|
||||||
) -> Parsed<Self> where Self: Sized {
|
) -> $crate::syntax::Parsed<Self> where Self: Sized {
|
||||||
let mut errors = vec![];
|
let mut errors = vec![];
|
||||||
let mut decorations = vec![];
|
let mut decorations = vec![];
|
||||||
#[allow(unused)] let $header = &mut header;
|
#[allow(unused)] let $header = &mut header;
|
||||||
@ -67,8 +97,9 @@ macro_rules! function {
|
|||||||
fn layout<'a, 'b, 'c, 't>(
|
fn layout<'a, 'b, 'c, 't>(
|
||||||
#[allow(unused)] &'a $this,
|
#[allow(unused)] &'a $this,
|
||||||
#[allow(unused)] mut $ctx: $crate::layout::LayoutContext<'b, 'c>,
|
#[allow(unused)] mut $ctx: $crate::layout::LayoutContext<'b, 'c>,
|
||||||
) -> $crate::syntax::DynFuture<'t, $crate::layout::Layouted<$crate::func::Commands<'a>>>
|
) -> $crate::layout::DynFuture<'t, $crate::layout::Layouted<
|
||||||
where
|
$crate::layout::Commands<'a>>
|
||||||
|
> where
|
||||||
'a: 't,
|
'a: 't,
|
||||||
'b: 't,
|
'b: 't,
|
||||||
'c: 't,
|
'c: 't,
|
||||||
@ -96,7 +127,7 @@ macro_rules! body {
|
|||||||
// Since the body span starts at the opening bracket of the body, we
|
// 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
|
// need to add 1 column to find out the start position of body
|
||||||
// content.
|
// 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);
|
let parsed = $crate::syntax::parse(start, body.v, $ctx);
|
||||||
$errors.extend(parsed.errors);
|
$errors.extend(parsed.errors);
|
||||||
$decos.extend(parsed.decorations);
|
$decos.extend(parsed.decorations);
|
||||||
@ -122,7 +153,7 @@ macro_rules! body {
|
|||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! err {
|
macro_rules! err {
|
||||||
(@$severity:ident: $span:expr; $($args:tt)*) => {
|
(@$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)*) => {
|
(@$severity:ident: $($args:tt)*) => {
|
@ -1,10 +1,12 @@
|
|||||||
//! Drawing and cofiguration actions composing layouts.
|
//! Drawing and cofiguration actions composing layouts.
|
||||||
|
|
||||||
|
use std::io::{self, Write};
|
||||||
use std::fmt::{self, Display, Formatter};
|
use std::fmt::{self, Display, Formatter};
|
||||||
use toddle::query::FontIndex;
|
use toddle::query::FontIndex;
|
||||||
|
|
||||||
use super::*;
|
use crate::size::{Size, Size2D};
|
||||||
use LayoutAction::*;
|
use super::{Layout, Serialize};
|
||||||
|
use self::LayoutAction::*;
|
||||||
|
|
||||||
|
|
||||||
/// A layouting action.
|
/// A layouting action.
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
use super::stack::{StackLayouter, StackContext};
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
|
||||||
|
@ -3,40 +3,26 @@
|
|||||||
use std::io::{self, Write};
|
use std::io::{self, Write};
|
||||||
use std::fmt::{self, Display, Formatter};
|
use std::fmt::{self, Display, Formatter};
|
||||||
use smallvec::SmallVec;
|
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::size::{Size, Size2D, SizeBox};
|
||||||
use crate::style::LayoutStyle;
|
use self::{GenericAxis::*, SpecificAxis::*, Direction::*, Alignment::*};
|
||||||
|
|
||||||
mod actions;
|
pub mod line;
|
||||||
mod model;
|
pub mod stack;
|
||||||
mod line;
|
pub mod text;
|
||||||
mod stack;
|
|
||||||
mod text;
|
pub_use_mod!(actions);
|
||||||
|
pub_use_mod!(model);
|
||||||
|
|
||||||
/// Common types for layouting.
|
|
||||||
pub mod prelude {
|
pub mod prelude {
|
||||||
pub use super::*;
|
pub use super::{LayoutSpace, LayoutExpansion, LayoutAxes, LayoutAlignment};
|
||||||
pub use GenericAxis::*;
|
pub use super::GenericAxis::{self, *};
|
||||||
pub use SpecificAxis::*;
|
pub use super::SpecificAxis::{self, *};
|
||||||
pub use Direction::*;
|
pub use super::Direction::{self, *};
|
||||||
pub use Alignment::*;
|
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.
|
/// A collection of layouts.
|
||||||
pub type MultiLayout = Vec<Layout>;
|
pub type MultiLayout = Vec<Layout>;
|
||||||
@ -67,49 +53,32 @@ impl Layout {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The general context for layouting.
|
/// Layout components that can be serialized.
|
||||||
#[derive(Debug, Clone)]
|
pub trait Serialize {
|
||||||
pub struct LayoutContext<'a, 'p> {
|
/// Serialize the data structure into an output writable.
|
||||||
/// The font loader to retrieve fonts from when typesetting text
|
fn serialize<W: Write>(&self, f: &mut W) -> io::Result<()>;
|
||||||
/// 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> {
|
impl Serialize for Layout {
|
||||||
pub output: T,
|
fn serialize<W: Write>(&self, f: &mut W) -> io::Result<()> {
|
||||||
pub errors: Errors,
|
writeln!(f, "{:.4} {:.4}", self.dimensions.x.to_pt(), self.dimensions.y.to_pt())?;
|
||||||
}
|
writeln!(f, "{}", self.actions.len())?;
|
||||||
|
for action in &self.actions {
|
||||||
impl<T> Layouted<T> {
|
action.serialize(f)?;
|
||||||
pub fn map<F, U>(self, f: F) -> Layouted<U> where F: FnOnce(T) -> U {
|
writeln!(f)?;
|
||||||
Layouted {
|
|
||||||
output: f(self.output),
|
|
||||||
errors: self.errors,
|
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn layout(model: &SyntaxModel, ctx: LayoutContext<'_, '_>) -> Layouted<MultiLayout> {
|
impl Serialize for MultiLayout {
|
||||||
let mut layouter = ModelLayouter::new(ctx);
|
fn serialize<W: Write>(&self, f: &mut W) -> io::Result<()> {
|
||||||
layouter.layout_syntax_model(model).await;
|
writeln!(f, "{}", self.len())?;
|
||||||
layouter.finish()
|
for layout in self {
|
||||||
|
layout.serialize(f)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A possibly stack-allocated vector of layout spaces.
|
/// 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 smallvec::smallvec;
|
||||||
|
use toddle::query::SharedFontLoader;
|
||||||
|
|
||||||
use crate::error::Errors;
|
use crate::error::Errors;
|
||||||
use crate::func::Command;
|
use crate::style::{LayoutStyle, PageStyle, TextStyle};
|
||||||
use crate::syntax::{Model, DynFuture, SyntaxModel, Node};
|
use crate::size::{Size, Size2D};
|
||||||
use crate::syntax::{SpanVec, Spanned, Span, offset_spans};
|
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::*;
|
use super::*;
|
||||||
|
|
||||||
|
|
||||||
@ -15,6 +21,76 @@ pub struct ModelLayouter<'a, 'p> {
|
|||||||
errors: Errors,
|
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> {
|
impl<'a, 'p> ModelLayouter<'a, 'p> {
|
||||||
/// Create a new syntax tree layouter.
|
/// Create a new syntax tree layouter.
|
||||||
pub fn new(ctx: LayoutContext<'a, 'p>) -> ModelLayouter<'a, 'p> {
|
pub fn new(ctx: LayoutContext<'a, 'p>) -> ModelLayouter<'a, 'p> {
|
||||||
|
@ -6,12 +6,14 @@ use crate::style::TextStyle;
|
|||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
|
||||||
/// Layouts text into a box.
|
/// Layouts text into boxes.
|
||||||
///
|
struct TextLayouter<'a, 'p> {
|
||||||
/// There is no complex layout involved. The text is simply laid out left-
|
ctx: TextContext<'a, 'p>,
|
||||||
/// to-right using the correct font for each character.
|
text: &'a str,
|
||||||
pub async fn layout_text(text: &str, ctx: TextContext<'_, '_>) -> Layout {
|
actions: LayoutActions,
|
||||||
TextLayouter::new(text, ctx).layout().await
|
buffer: String,
|
||||||
|
active_font: FontIndex,
|
||||||
|
width: Size,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The context for text layouting.
|
/// The context for text layouting.
|
||||||
@ -25,14 +27,12 @@ pub struct TextContext<'a, 'p> {
|
|||||||
pub alignment: LayoutAlignment,
|
pub alignment: LayoutAlignment,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Layouts text into boxes.
|
/// Layouts text into a box.
|
||||||
struct TextLayouter<'a, 'p> {
|
///
|
||||||
ctx: TextContext<'a, 'p>,
|
/// There is no complex layout involved. The text is simply laid out left-
|
||||||
text: &'a str,
|
/// to-right using the correct font for each character.
|
||||||
actions: LayoutActions,
|
pub async fn layout_text(text: &str, ctx: TextContext<'_, '_>) -> Layout {
|
||||||
buffer: String,
|
TextLayouter::new(text, ctx).layout().await
|
||||||
active_font: FontIndex,
|
|
||||||
width: Size,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'p> TextLayouter<'a, 'p> {
|
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 toddle::query::{FontLoader, FontProvider, SharedFontLoader};
|
||||||
|
|
||||||
use crate::func::Scope;
|
use crate::layout::MultiLayout;
|
||||||
use crate::layout::{Layouted, MultiLayout};
|
use crate::layout::prelude::*;
|
||||||
use crate::syntax::{parse, ParseContext, Parsed, SyntaxModel, Position};
|
use crate::layout::{LayoutContext, Layouted, layout};
|
||||||
use crate::style::{LayoutStyle, PageStyle, TextStyle};
|
use crate::style::{LayoutStyle, PageStyle, TextStyle};
|
||||||
|
use crate::syntax::{SyntaxModel, Scope, ParseContext, Parsed, parse};
|
||||||
|
use crate::syntax::span::Position;
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
mod macros;
|
mod macros;
|
||||||
pub mod export;
|
|
||||||
pub mod error;
|
pub mod error;
|
||||||
|
pub mod export;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
pub mod func;
|
pub mod func;
|
||||||
pub mod layout;
|
pub mod layout;
|
||||||
pub mod library;
|
pub mod library;
|
||||||
pub mod syntax;
|
|
||||||
pub mod size;
|
pub mod size;
|
||||||
pub mod style;
|
pub mod style;
|
||||||
|
pub mod syntax;
|
||||||
|
|
||||||
|
|
||||||
/// Transforms source code into typesetted layouts.
|
/// Transforms source code into typesetted layouts.
|
||||||
@ -93,7 +95,6 @@ impl<'p> Typesetter<'p> {
|
|||||||
|
|
||||||
/// Layout a syntax tree and return the produced layout.
|
/// Layout a syntax tree and return the produced layout.
|
||||||
pub async fn layout(&self, model: &SyntaxModel) -> Layouted<MultiLayout> {
|
pub async fn layout(&self, model: &SyntaxModel) -> Layouted<MultiLayout> {
|
||||||
use crate::layout::prelude::*;
|
|
||||||
let margins = self.style.page.margins();
|
let margins = self.style.page.margins();
|
||||||
layout(
|
layout(
|
||||||
&model,
|
&model,
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
use toddle::query::{FontWeight, FontStyle};
|
use toddle::query::{FontWeight, FontStyle};
|
||||||
|
use crate::size::FSize;
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
|
||||||
@ -79,7 +80,7 @@ function! {
|
|||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub struct FontSizeFunc {
|
pub struct FontSizeFunc {
|
||||||
body: Option<SyntaxModel>,
|
body: Option<SyntaxModel>,
|
||||||
size: Option<ScaleSize>,
|
size: Option<FSize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
parse(header, body, ctx, errors, decos) {
|
parse(header, body, ctx, errors, decos) {
|
||||||
@ -93,11 +94,11 @@ function! {
|
|||||||
layout(self, ctx, errors) {
|
layout(self, ctx, errors) {
|
||||||
styled(&self.body, ctx, self.size, |t, s| {
|
styled(&self.body, ctx, self.size, |t, s| {
|
||||||
match s {
|
match s {
|
||||||
ScaleSize::Absolute(size) => {
|
FSize::Absolute(size) => {
|
||||||
t.base_font_size = size;
|
t.base_font_size = size;
|
||||||
t.font_scale = 1.0;
|
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::*;
|
use super::*;
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
//! The standard library.
|
//! The standard library.
|
||||||
|
|
||||||
|
use crate::syntax::Scope;
|
||||||
use crate::func::prelude::*;
|
use crate::func::prelude::*;
|
||||||
|
|
||||||
pub_use_mod!(font);
|
pub_use_mod!(font);
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
|
use crate::size::Size;
|
||||||
use crate::style::{Paper, PaperClass};
|
use crate::style::{Paper, PaperClass};
|
||||||
|
use crate::syntax::func::maps::{AxisMap, PaddingMap};
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
|
use crate::size::FSize;
|
||||||
|
use crate::layout::SpacingKind;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use ContentKind::*;
|
use self::ContentKind::*;
|
||||||
|
|
||||||
|
|
||||||
function! {
|
function! {
|
||||||
|
33
src/size.rs
33
src/size.rs
@ -129,7 +129,7 @@ pub type PSize = ScaleSize;
|
|||||||
|
|
||||||
/// A value in two dimensions.
|
/// A value in two dimensions.
|
||||||
#[derive(Default, Copy, Clone, PartialEq)]
|
#[derive(Default, Copy, Clone, PartialEq)]
|
||||||
pub struct Value2D<T: Copy> {
|
pub struct Value2D<T> {
|
||||||
/// The horizontal component.
|
/// The horizontal component.
|
||||||
pub x: T,
|
pub x: T,
|
||||||
/// The vertical component.
|
/// 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 {
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
write!(f, "[{}, {}]", self.x, self.y)
|
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 {
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
write!(f, "[{:?}, {:?}]", self.x, self.y)
|
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
|
/// A value that is stretchable in an interval from a minimal through an optimal
|
||||||
/// to a maximal value.
|
/// to a maximal value.
|
||||||
pub struct StretchValue<T: Copy> {
|
pub struct StretchValue<T> {
|
||||||
/// The minimum this value can be stretched to.
|
/// The minimum this value can be stretched to.
|
||||||
pub min: T,
|
pub min: T,
|
||||||
/// The optimum for this value.
|
/// The optimum for this value.
|
||||||
@ -302,20 +302,20 @@ pub struct StretchValue<T: Copy> {
|
|||||||
pub max: T,
|
pub max: T,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Copy> StretchValue<T> {
|
impl<T> StretchValue<T> {
|
||||||
/// Create a new stretch size from minimum, optimal and maximum values.
|
/// Create a new stretch size from minimum, optimal and maximum values.
|
||||||
pub fn new(min: T, opt: T, max: T) -> StretchValue<T> {
|
pub fn new(min: T, opt: T, max: T) -> StretchValue<T> {
|
||||||
StretchValue { min, opt, max }
|
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 {
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
write!(f, "({}, {}, {})", self.min, self.opt, self.max)
|
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 {
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
write!(f, "({:?}, {:?}, {:?})", self.min, self.opt, self.max)
|
write!(f, "({:?}, {:?}, {:?})", self.min, self.opt, self.max)
|
||||||
}
|
}
|
||||||
@ -326,7 +326,7 @@ pub type StretchSize = StretchValue<Size>;
|
|||||||
|
|
||||||
/// A value in four dimensions.
|
/// A value in four dimensions.
|
||||||
#[derive(Default, Copy, Clone, PartialEq)]
|
#[derive(Default, Copy, Clone, PartialEq)]
|
||||||
pub struct ValueBox<T: Copy> {
|
pub struct ValueBox<T> {
|
||||||
/// The left extent.
|
/// The left extent.
|
||||||
pub left: T,
|
pub left: T,
|
||||||
/// The top extent.
|
/// The top extent.
|
||||||
@ -337,7 +337,7 @@ pub struct ValueBox<T: Copy> {
|
|||||||
pub bottom: T,
|
pub bottom: T,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Copy> ValueBox<T> {
|
impl<T: Clone> ValueBox<T> {
|
||||||
/// Create a new box from four sizes.
|
/// Create a new box from four sizes.
|
||||||
pub fn new(left: T, top: T, right: T, bottom: T) -> ValueBox<T> {
|
pub fn new(left: T, top: T, right: T, bottom: T) -> ValueBox<T> {
|
||||||
ValueBox { left, top, right, bottom }
|
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`.
|
/// Create a box with all four fields set to the same value `s`.
|
||||||
pub fn with_all(value: T) -> ValueBox<T> {
|
pub fn with_all(value: T) -> 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
|
/// 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.
|
/// Set the `left` and `right` values.
|
||||||
pub fn set_horizontal(&mut self, value: T) {
|
pub fn set_horizontal(&mut self, value: T) {
|
||||||
self.left = value;
|
self.left = value.clone();
|
||||||
self.right = value;
|
self.right = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the `top` and `bottom` values.
|
/// Set the `top` and `bottom` values.
|
||||||
pub fn set_vertical(&mut self, value: T) {
|
pub fn set_vertical(&mut self, value: T) {
|
||||||
self.top = value;
|
self.top = value.clone();
|
||||||
self.bottom = value;
|
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 {
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
write!(f, "[left: {}, top: {}, right: {}, bottom: {}]",
|
write!(f, "[left: {}, top: {}, right: {}, bottom: {}]",
|
||||||
self.left, self.top, self.right, self.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 {
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
write!(f, "[left: {:?}, top: {:?}, right: {:?}, bottom: {:?}]",
|
write!(f, "[left: {:?}, top: {:?}, right: {:?}, bottom: {:?}]",
|
||||||
self.left, self.top, self.right, self.bottom)
|
self.left, self.top, self.right, self.bottom)
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
//! Styles for text and pages.
|
//! Styles for text and pages.
|
||||||
|
|
||||||
use toddle::query::{FontFallbackTree, FontVariant, FontStyle, FontWeight};
|
use toddle::query::{FontFallbackTree, FontVariant, FontStyle, FontWeight};
|
||||||
|
|
||||||
use crate::size::{Size, Size2D, SizeBox, ValueBox, PSize};
|
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.
|
/// An argument or return value.
|
||||||
@ -91,6 +97,13 @@ pub struct Object {
|
|||||||
pub pairs: Vec<Pair>,
|
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 {
|
impl Object {
|
||||||
pub fn new() -> Object {
|
pub fn new() -> Object {
|
||||||
Object { pairs: vec![] }
|
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 {
|
impl Display for Expr {
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
use Expr::*;
|
use Expr::*;
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
use crate::layout::prelude::*;
|
use crate::layout::prelude::*;
|
||||||
|
use super::values::AlignmentValue::{self, *};
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
use AxisKey::*;
|
use self::AxisKey::*;
|
||||||
use PaddingKey::*;
|
use self::PaddingKey::*;
|
||||||
use AlignmentValue::*;
|
|
||||||
|
|
||||||
|
|
||||||
pub trait Key {
|
pub trait Key {
|
||||||
@ -28,7 +29,7 @@ macro_rules! key {
|
|||||||
fn parse(key: Spanned<&str>) -> Option<Self::Output> {
|
fn parse(key: Spanned<&str>) -> Option<Self::Output> {
|
||||||
match key.v {
|
match key.v {
|
||||||
$($($p)|* => Some($r)),*,
|
$($($p)|* => Some($r)),*,
|
||||||
other => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
//! Deduplicating maps and keys for argument parsing.
|
//! Deduplicating maps and keys for argument parsing.
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use crate::error::Errors;
|
||||||
use std::hash::Hash;
|
use crate::layout::prelude::*;
|
||||||
use crate::layout::{LayoutAxes, SpecificAxis, GenericAxis};
|
|
||||||
use crate::size::{PSize, ValueBox};
|
use crate::size::{PSize, ValueBox};
|
||||||
|
use crate::syntax::span::Spanned;
|
||||||
|
use super::keys::*;
|
||||||
|
use super::values::*;
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
|
||||||
@ -179,7 +181,6 @@ impl PaddingMap {
|
|||||||
padding: &mut ValueBox<Option<PSize>>
|
padding: &mut ValueBox<Option<PSize>>
|
||||||
) {
|
) {
|
||||||
use PaddingKey::*;
|
use PaddingKey::*;
|
||||||
use SpecificAxis::*;
|
|
||||||
|
|
||||||
let map = self.0.dedup(errors, |key, &val| {
|
let map = self.0.dedup(errors, |key, &val| {
|
||||||
(match key {
|
(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 mod maps;
|
||||||
pub_use_mod!(keys);
|
pub mod keys;
|
||||||
pub_use_mod!(values);
|
pub mod values;
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
@ -17,22 +19,6 @@ pub struct FuncArgs {
|
|||||||
pub key: Object,
|
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 {
|
impl FuncArgs {
|
||||||
pub fn new() -> FuncArgs {
|
pub fn new() -> FuncArgs {
|
||||||
FuncArgs {
|
FuncArgs {
|
||||||
@ -42,10 +28,10 @@ impl FuncArgs {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Add an argument.
|
/// Add an argument.
|
||||||
pub fn add(&mut self, arg: Arg) {
|
pub fn add(&mut self, arg: FuncArg) {
|
||||||
match arg {
|
match arg {
|
||||||
Arg::Pos(item) => self.add_pos(item),
|
FuncArg::Pos(item) => self.add_pos(item),
|
||||||
Arg::Key(pair) => self.add_key_pair(pair),
|
FuncArg::Key(pair) => self.add_key_pair(pair),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -64,74 +50,27 @@ impl FuncArgs {
|
|||||||
self.key.add_pair(pair);
|
self.key.add_pair(pair);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn into_iter(self) -> impl Iterator<Item=Arg> {
|
pub fn into_iter(self) -> impl Iterator<Item=FuncArg> {
|
||||||
self.pos.items.into_iter().map(|item| Arg::Pos(item))
|
self.pos.items.into_iter().map(|item| FuncArg::Pos(item))
|
||||||
.chain(self.key.pairs.into_iter().map(|pair| Arg::Key(pair)))
|
.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.
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
// fn expect<E: ExpressionKind>(opt: ParseResult<Option<E>>) -> ParseResult<E> {
|
pub enum FuncArg {
|
||||||
// match opt {
|
Pos(Spanned<Expr>),
|
||||||
// Ok(Some(spanned)) => Ok(spanned),
|
Key(Pair),
|
||||||
// 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 {
|
pub trait OptionExt: Sized {
|
||||||
fn or_missing(self, errors: &mut Errors, span: Span, what: &str) -> Self;
|
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 std::marker::PhantomData;
|
||||||
use toddle::query::{FontStyle, FontWeight};
|
use toddle::query::{FontStyle, FontWeight};
|
||||||
|
|
||||||
use crate::layout::prelude::*;
|
use crate::layout::prelude::*;
|
||||||
use crate::size::ScaleSize;
|
use crate::size::{Size, ScaleSize};
|
||||||
use crate::style::Paper;
|
use crate::style::Paper;
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
use AlignmentValue::*;
|
use self::AlignmentValue::*;
|
||||||
|
|
||||||
|
|
||||||
pub trait Value {
|
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 {
|
impl Value for FontStyle {
|
||||||
type Output = Self;
|
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)]
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
||||||
pub enum AlignmentValue {
|
pub enum AlignmentValue {
|
||||||
Align(Alignment),
|
Align(Alignment),
|
||||||
@ -203,7 +204,7 @@ impl Value for AlignmentValue {
|
|||||||
"top" => Top,
|
"top" => Top,
|
||||||
"right" => Right,
|
"right" => Right,
|
||||||
"bottom" => Bottom,
|
"bottom" => Bottom,
|
||||||
other => return Err(err!("invalid alignment"))
|
_ => return Err(err!("invalid alignment"))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,77 +1,25 @@
|
|||||||
//! Tokenization and parsing of source code.
|
//! Tokenization and parsing of source code.
|
||||||
|
|
||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
use std::fmt::{self, Debug, Display, Formatter};
|
use std::fmt::Debug;
|
||||||
use std::future::Future;
|
use async_trait::async_trait;
|
||||||
use std::pin::Pin;
|
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
use crate::error::{Error, Errors};
|
use crate::layout::{LayoutContext, Layouted, Commands, Command};
|
||||||
use crate::func::{Commands, Command};
|
use self::span::{Spanned, SpanVec};
|
||||||
use crate::layout::{Layouted, LayoutContext};
|
|
||||||
use crate::size::Size;
|
|
||||||
|
|
||||||
pub_use_mod!(expr);
|
pub mod expr;
|
||||||
pub_use_mod!(func);
|
pub mod func;
|
||||||
pub_use_mod!(tokens);
|
pub mod span;
|
||||||
|
|
||||||
|
pub_use_mod!(scope);
|
||||||
pub_use_mod!(parsing);
|
pub_use_mod!(parsing);
|
||||||
pub_use_mod!(span);
|
pub_use_mod!(tokens);
|
||||||
|
|
||||||
/// Common syntax types.
|
|
||||||
pub mod prelude {
|
|
||||||
pub use super::*;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#[async_trait::async_trait(?Send)]
|
#[async_trait(?Send)]
|
||||||
pub trait Model: Debug + ModelBounds {
|
pub trait Model: Debug + ModelBounds {
|
||||||
async fn layout<'a>(
|
async fn layout<'a>(&'a self, ctx: LayoutContext<'_, '_>) -> Layouted<Commands<'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())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A tree representation of source code.
|
/// A tree representation of source code.
|
||||||
@ -92,7 +40,7 @@ impl SyntaxModel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait::async_trait(?Send)]
|
#[async_trait(?Send)]
|
||||||
impl Model for SyntaxModel {
|
impl Model for SyntaxModel {
|
||||||
async fn layout<'a>(&'a self, _: LayoutContext<'_, '_>) -> Layouted<Commands<'a>> {
|
async fn layout<'a>(&'a self, _: LayoutContext<'_, '_>) -> Layouted<Commands<'a>> {
|
||||||
Layouted {
|
Layouted {
|
||||||
@ -144,3 +92,44 @@ pub enum Decoration {
|
|||||||
InvalidFuncName,
|
InvalidFuncName,
|
||||||
ArgumentKey,
|
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 super::*;
|
||||||
use Token::*;
|
|
||||||
|
|
||||||
|
|
||||||
/// The context for parsing.
|
/// The context for parsing.
|
||||||
@ -37,13 +41,13 @@ pub fn parse(start: Position, src: &str, ctx: ParseContext) -> Parsed<SyntaxMode
|
|||||||
let span = token.span;
|
let span = token.span;
|
||||||
|
|
||||||
let node = match token.v {
|
let node = match token.v {
|
||||||
Space(newlines) => if newlines >= 2 {
|
Token::Space(newlines) => if newlines >= 2 {
|
||||||
Node::Newline
|
Node::Newline
|
||||||
} else {
|
} else {
|
||||||
Node::Space
|
Node::Space
|
||||||
},
|
},
|
||||||
|
|
||||||
Function { header, body, terminated } => {
|
Token::Function { header, body, terminated } => {
|
||||||
let parsed: Parsed<Node> = FuncParser::new(header, body, ctx).parse();
|
let parsed: Parsed<Node> = FuncParser::new(header, body, ctx).parse();
|
||||||
|
|
||||||
errors.extend(offset_spans(parsed.errors, span.start));
|
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
|
parsed.output
|
||||||
}
|
}
|
||||||
|
|
||||||
Star => Node::ToggleBolder,
|
Token::Star => Node::ToggleBolder,
|
||||||
Underscore => Node::ToggleItalic,
|
Token::Underscore => Node::ToggleItalic,
|
||||||
Backtick => Node::ToggleMonospace,
|
Token::Backtick => Node::ToggleMonospace,
|
||||||
Text(text) => Node::Text(text.to_owned()),
|
Token::Text(text) => Node::Text(text.to_owned()),
|
||||||
|
|
||||||
LineComment(_) | BlockComment(_) => continue,
|
Token::LineComment(_) | Token::BlockComment(_) => continue,
|
||||||
|
|
||||||
other => {
|
other => {
|
||||||
errors.push(err!(span; "unexpected {}", name(other)));
|
errors.push(err!(span; "unexpected {}", other.name()));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -140,7 +144,7 @@ impl<'s> FuncParser<'s> {
|
|||||||
self.skip_whitespace();
|
self.skip_whitespace();
|
||||||
|
|
||||||
let name = match self.eat() {
|
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 }
|
Spanned { v: Ident(ident.to_string()), span }
|
||||||
}
|
}
|
||||||
other => {
|
other => {
|
||||||
@ -151,7 +155,7 @@ impl<'s> FuncParser<'s> {
|
|||||||
|
|
||||||
self.skip_whitespace();
|
self.skip_whitespace();
|
||||||
let args = match self.eat().map(Spanned::value) {
|
let args = match self.eat().map(Spanned::value) {
|
||||||
Some(Colon) => self.parse_func_args(),
|
Some(Token::Colon) => self.parse_func_args(),
|
||||||
Some(_) => {
|
Some(_) => {
|
||||||
self.expected_at("colon", name.span.end);
|
self.expected_at("colon", name.span.end);
|
||||||
FuncArgs::new()
|
FuncArgs::new()
|
||||||
@ -179,38 +183,38 @@ impl<'s> FuncParser<'s> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Parse a positional or keyword argument.
|
/// 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 first = self.peek()?;
|
||||||
let span = first.span;
|
let span = first.span;
|
||||||
|
|
||||||
let arg = if let ExprIdent(ident) = first.v {
|
let arg = if let Token::ExprIdent(ident) = first.v {
|
||||||
self.eat();
|
self.eat();
|
||||||
self.skip_whitespace();
|
self.skip_whitespace();
|
||||||
|
|
||||||
let ident = Ident(ident.to_string());
|
let ident = Ident(ident.to_string());
|
||||||
if let Some(Equals) = self.peekv() {
|
if let Some(Token::Equals) = self.peekv() {
|
||||||
self.eat();
|
self.eat();
|
||||||
self.skip_whitespace();
|
self.skip_whitespace();
|
||||||
|
|
||||||
self.decorations.push(Spanned::new(Decoration::ArgumentKey, span));
|
self.decorations.push(Spanned::new(Decoration::ArgumentKey, span));
|
||||||
|
|
||||||
self.parse_expr().map(|value| {
|
self.parse_expr().map(|value| {
|
||||||
Arg::Key(Pair {
|
FuncArg::Key(Pair {
|
||||||
key: Spanned { v: ident, span },
|
key: Spanned { v: ident, span },
|
||||||
value,
|
value,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
Some(Arg::Pos(Spanned::new(Expr::Ident(ident), span)))
|
Some(FuncArg::Pos(Spanned::new(Expr::Ident(ident), span)))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
self.parse_expr().map(|expr| Arg::Pos(expr))
|
self.parse_expr().map(|expr| FuncArg::Pos(expr))
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(arg) = &arg {
|
if let Some(arg) = &arg {
|
||||||
self.skip_whitespace();
|
self.skip_whitespace();
|
||||||
match self.peekv() {
|
match self.peekv() {
|
||||||
Some(Comma) => { self.eat(); }
|
Some(Token::Comma) => { self.eat(); }
|
||||||
Some(_) => self.expected_at("comma", arg.span().end),
|
Some(_) => self.expected_at("comma", arg.span().end),
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
@ -228,11 +232,11 @@ impl<'s> FuncParser<'s> {
|
|||||||
let spanned = |v| Spanned { v, span: first.span };
|
let spanned = |v| Spanned { v, span: first.span };
|
||||||
|
|
||||||
Some(match first.v {
|
Some(match first.v {
|
||||||
ExprIdent(i) => {
|
Token::ExprIdent(i) => {
|
||||||
self.eat();
|
self.eat();
|
||||||
spanned(Expr::Ident(Ident(i.to_string())))
|
spanned(Expr::Ident(Ident(i.to_string())))
|
||||||
}
|
}
|
||||||
ExprStr { string, terminated } => {
|
Token::ExprStr { string, terminated } => {
|
||||||
if !terminated {
|
if !terminated {
|
||||||
self.expected_at("quote", first.span.end);
|
self.expected_at("quote", first.span.end);
|
||||||
}
|
}
|
||||||
@ -240,12 +244,13 @@ impl<'s> FuncParser<'s> {
|
|||||||
self.eat();
|
self.eat();
|
||||||
spanned(Expr::Str(string.to_string()))
|
spanned(Expr::Str(string.to_string()))
|
||||||
}
|
}
|
||||||
ExprNumber(n) => { self.eat(); spanned(Expr::Number(n)) }
|
Token::ExprNumber(n) => { self.eat(); spanned(Expr::Number(n)) }
|
||||||
ExprSize(s) => { self.eat(); spanned(Expr::Size(s)) }
|
Token::ExprSize(s) => { self.eat(); spanned(Expr::Size(s)) }
|
||||||
ExprBool(b) => { self.eat(); spanned(Expr::Bool(b)) }
|
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,
|
_ => return None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -255,7 +260,7 @@ impl<'s> FuncParser<'s> {
|
|||||||
let start = self.pos();
|
let start = self.pos();
|
||||||
|
|
||||||
// TODO: Do the thing.
|
// TODO: Do the thing.
|
||||||
self.eat_until(|t| t == RightParen, true);
|
self.eat_until(|t| t == Token::RightParen, true);
|
||||||
|
|
||||||
let end = self.pos();
|
let end = self.pos();
|
||||||
let span = Span { start, end };
|
let span = Span { start, end };
|
||||||
@ -268,7 +273,7 @@ impl<'s> FuncParser<'s> {
|
|||||||
let start = self.pos();
|
let start = self.pos();
|
||||||
|
|
||||||
// TODO: Do the thing.
|
// TODO: Do the thing.
|
||||||
self.eat_until(|t| t == RightBrace, true);
|
self.eat_until(|t| t == Token::RightBrace, true);
|
||||||
|
|
||||||
let end = self.pos();
|
let end = self.pos();
|
||||||
let span = Span { start, end };
|
let span = Span { start, end };
|
||||||
@ -278,15 +283,16 @@ impl<'s> FuncParser<'s> {
|
|||||||
|
|
||||||
/// Skip all whitespace/comment tokens.
|
/// Skip all whitespace/comment tokens.
|
||||||
fn skip_whitespace(&mut self) {
|
fn skip_whitespace(&mut self) {
|
||||||
self.eat_until(|t|
|
self.eat_until(|t| !matches!(t,
|
||||||
!matches!(t, Space(_) | LineComment(_) | BlockComment(_)), false)
|
Token::Space(_) | Token::LineComment(_) |
|
||||||
|
Token::BlockComment(_)), false)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add an error about an expected `thing` which was not found, showing
|
/// Add an error about an expected `thing` which was not found, showing
|
||||||
/// what was found instead.
|
/// what was found instead.
|
||||||
fn expected_found(&mut self, thing: &str, found: Spanned<Token>) {
|
fn expected_found(&mut self, thing: &str, found: Spanned<Token>) {
|
||||||
self.errors.push(err!(found.span;
|
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
|
/// 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())
|
.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::collections::HashMap;
|
||||||
use std::fmt::{self, Debug, Formatter};
|
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.
|
/// A map from identifiers to function parsers.
|
||||||
pub struct Scope {
|
pub struct Scope {
|
||||||
@ -73,7 +18,7 @@ impl Scope {
|
|||||||
/// Create a new empty scope with a fallback parser that is invoked when no
|
/// Create a new empty scope with a fallback parser that is invoked when no
|
||||||
/// match is found.
|
/// match is found.
|
||||||
pub fn new<F>() -> Scope
|
pub fn new<F>() -> Scope
|
||||||
where F: Parse<Meta=()> + Model + 'static {
|
where F: ParseFunc<Meta=()> + Model + 'static {
|
||||||
Scope {
|
Scope {
|
||||||
parsers: HashMap::new(),
|
parsers: HashMap::new(),
|
||||||
fallback: parser::<F>(()),
|
fallback: parser::<F>(()),
|
||||||
@ -87,14 +32,14 @@ impl Scope {
|
|||||||
|
|
||||||
/// Associate the given name with a type that is parseable into a function.
|
/// Associate the given name with a type that is parseable into a function.
|
||||||
pub fn add<F>(&mut self, name: &str)
|
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, ());
|
self.add_with_meta::<F>(name, ());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add a parseable type with additional metadata that is given to the
|
/// Add a parseable type with additional metadata that is given to the
|
||||||
/// parser (other than the default of `()`).
|
/// parser (other than the default of `()`).
|
||||||
pub fn add_with_meta<F>(&mut self, name: &str, metadata: <F as Parse>::Meta)
|
pub fn add_with_meta<F>(&mut self, name: &str, metadata: <F as ParseFunc>::Meta)
|
||||||
where F: Parse + Model + 'static {
|
where F: ParseFunc + Model + 'static {
|
||||||
self.parsers.insert(
|
self.parsers.insert(
|
||||||
name.to_owned(),
|
name.to_owned(),
|
||||||
parser::<F>(metadata),
|
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| {
|
Box::new(move |h, b, c| {
|
||||||
F::parse(h, b, c, metadata.clone())
|
F::parse(h, b, c, metadata.clone())
|
||||||
.map(|model| Box::new(model) as Box<dyn Model>)
|
.map(|model| Box::new(model) as Box<dyn Model>)
|
@ -5,93 +5,6 @@ use std::ops::{Add, Sub};
|
|||||||
use serde::Serialize;
|
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.
|
/// A line-column position in source code.
|
||||||
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize)]
|
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize)]
|
||||||
pub struct Position {
|
pub struct Position {
|
||||||
@ -145,13 +58,68 @@ impl Sub for Position {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for Position {
|
/// Describes a slice of source code.
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
#[derive(Copy, Clone, Eq, PartialEq, Hash, Serialize)]
|
||||||
write!(f, "{}:{}", self.line, self.column)
|
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.
|
/// A vector of spanned things.
|
||||||
pub type SpanVec<T> = Vec<Spanned<T>>;
|
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>> {
|
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)))
|
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 std::str::Chars;
|
||||||
use unicode_xid::UnicodeXID;
|
use unicode_xid::UnicodeXID;
|
||||||
|
|
||||||
use super::*;
|
use crate::size::Size;
|
||||||
use Token::*;
|
use super::span::{Position, Span, Spanned};
|
||||||
use TokenizationMode::*;
|
|
||||||
|
use self::Token::*;
|
||||||
|
use self::TokenizationMode::*;
|
||||||
|
|
||||||
|
|
||||||
/// A minimal semantic entity of source code.
|
/// A minimal semantic entity of source code.
|
||||||
@ -68,6 +70,37 @@ pub enum Token<'s> {
|
|||||||
Invalid(&'s str),
|
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.
|
/// An iterator over the tokens of a string of source code.
|
||||||
pub struct Tokens<'s> {
|
pub struct Tokens<'s> {
|
||||||
src: &'s str,
|
src: &'s str,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user