Reorganize modules 🧱

This commit is contained in:
Laurenz 2020-01-24 16:23:57 +01:00
parent 03fddaf3ae
commit 0a087cd28b
26 changed files with 524 additions and 559 deletions

View File

@ -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 }
}
}

View File

@ -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};

View File

@ -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)*) => {

View File

@ -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.

View File

@ -1,3 +1,4 @@
use super::stack::{StackLayouter, StackContext};
use super::*;

View File

@ -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(())
}
}

View File

@ -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> {

View File

@ -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> {

View File

@ -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,

View File

@ -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,
}
})
}

View File

@ -1,4 +1,5 @@
use smallvec::smallvec;
use crate::size::PSize;
use crate::syntax::func::maps::{AxisMap, PosAxisMap};
use super::*;

View File

@ -1,5 +1,6 @@
//! The standard library.
use crate::syntax::Scope;
use crate::func::prelude::*;
pub_use_mod!(font);

View File

@ -1,4 +1,6 @@
use crate::size::Size;
use crate::style::{Paper, PaperClass};
use crate::syntax::func::maps::{AxisMap, PaddingMap};
use super::*;

View File

@ -1,5 +1,8 @@
use crate::size::FSize;
use crate::layout::SpacingKind;
use super::*;
use ContentKind::*;
use self::ContentKind::*;
function! {

View File

@ -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)

View File

@ -1,7 +1,6 @@
//! Styles for text and pages.
use toddle::query::{FontFallbackTree, FontVariant, FontStyle, FontWeight};
use crate::size::{Size, Size2D, SizeBox, ValueBox, PSize};

View File

@ -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::*;

View File

@ -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,
}
}
}

View File

@ -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 {

View File

@ -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;

View File

@ -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"))
})
}
}

View File

@ -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())
}
}

View File

@ -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",
}
}

View File

@ -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>)

View File

@ -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);

View File

@ -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,