summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/bin/main.rs2
-rw-r--r--src/func.rs53
-rw-r--r--src/layout/mod.rs3
-rw-r--r--src/layout/model.rs32
-rw-r--r--src/lib.rs81
-rw-r--r--src/library/font.rs32
-rw-r--r--src/library/layout.rs42
-rw-r--r--src/library/mod.rs4
-rw-r--r--src/library/page.rs24
-rw-r--r--src/library/spacing.rs18
-rw-r--r--src/syntax/mod.rs18
-rw-r--r--src/syntax/parsing.rs78
-rw-r--r--src/syntax/scope.rs5
-rw-r--r--src/syntax/span.rs5
-rw-r--r--src/syntax/test.rs4
-rw-r--r--tests/src/typeset.rs66
16 files changed, 236 insertions, 231 deletions
diff --git a/src/bin/main.rs b/src/bin/main.rs
index 92a83dff..1b490397 100644
--- a/src/bin/main.rs
+++ b/src/bin/main.rs
@@ -42,7 +42,7 @@ fn run() -> Result<(), Box<dyn Error>> {
let provider = DebugErrorProvider::new(fs);
let typesetter = Typesetter::new((Box::new(provider), entries));
- let layouts = block_on(typesetter.typeset(&src));
+ let layouts = block_on(typesetter.typeset(&src)).output;
let writer = BufWriter::new(File::create(&dest)?);
pdf::export(&layouts, typesetter.loader(), writer)?;
diff --git a/src/func.rs b/src/func.rs
index 590d9ed3..215de60f 100644
--- a/src/func.rs
+++ b/src/func.rs
@@ -1,6 +1,7 @@
//! Trait and prelude for custom functions.
-use crate::syntax::{ParseContext, Parsed};
+use crate::Pass;
+use crate::syntax::ParseContext;
use crate::syntax::func::FuncHeader;
use crate::syntax::span::Spanned;
@@ -36,7 +37,7 @@ pub trait ParseFunc {
body: Option<Spanned<&str>>,
ctx: ParseContext,
metadata: Self::Meta,
- ) -> Parsed<Self> where Self: Sized;
+ ) -> Pass<Self> where Self: Sized;
}
/// Allows to implement a function type concisely.
@@ -103,17 +104,16 @@ macro_rules! function {
// Parse trait.
(@parse($($a:tt)*) parse(default) $($r:tt)*) => {
- function!(@parse($($a)*) parse(_h, _b, _c, _e, _d, _m) {Default::default() } $($r)*);
+ function!(@parse($($a)*) parse(_h, _b, _c, _f, _m) {Default::default() } $($r)*);
};
- (@parse($($a:tt)*) parse($h:ident, $b:ident, $c:ident, $e:ident, $d:ident) $($r:tt)* ) => {
- function!(@parse($($a)*) parse($h, $b, $c, $e, $d, _metadata) $($r)*);
+ (@parse($($a:tt)*) parse($h:ident, $b:ident, $c:ident, $f:ident) $($r:tt)* ) => {
+ function!(@parse($($a)*) parse($h, $b, $c, $f, _metadata) $($r)*);
};
(@parse($name:ident, $meta:ty) parse(
$header:ident,
$body:ident,
$ctx:ident,
- $errors:ident,
- $decos:ident,
+ $feedback:ident,
$metadata:ident
) $code:block $($r:tt)*) => {
impl $crate::func::ParseFunc for $name {
@@ -124,42 +124,40 @@ macro_rules! function {
#[allow(unused)] $body: Option<$crate::syntax::span::Spanned<&str>>,
#[allow(unused)] $ctx: $crate::syntax::ParseContext,
#[allow(unused)] $metadata: Self::Meta,
- ) -> $crate::syntax::Parsed<Self> where Self: Sized {
- let mut errors = vec![];
- let mut decorations = vec![];
+ ) -> $crate::Pass<Self> where Self: Sized {
+ let mut feedback = $crate::Feedback::new();
#[allow(unused)] let $header = &mut header;
- #[allow(unused)] let $errors = &mut errors;
- #[allow(unused)] let $decos = &mut decorations;
- let output = $code;
+ #[allow(unused)] let $feedback = &mut feedback;
+
+ let func = $code;
for arg in header.args.into_iter() {
- errors.push(err!(arg.span(); "unexpected argument"));
+ feedback.errors.push(err!(arg.span(); "unexpected argument"));
}
- $crate::syntax::Parsed { output, errors, decorations }
+ $crate::Pass::new(func, feedback)
}
}
function!(@layout($name) $($r)*);
};
- (@layout($name:ident) layout($this:ident, $ctx:ident, $errors:ident) $code:block) => {
+ (@layout($name:ident) layout($this:ident, $ctx:ident, $feedback:ident) $code:block) => {
impl $crate::syntax::Model for $name {
fn layout<'a, 'b, 't>(
#[allow(unused)] &'a $this,
#[allow(unused)] mut $ctx: $crate::layout::LayoutContext<'b>,
- ) -> $crate::layout::DynFuture<'t, $crate::layout::Layouted<
- $crate::layout::Commands<'a>>
- > where
+ ) -> $crate::layout::DynFuture<'t, $crate::Pass<$crate::layout::Commands<'a>>>
+ where
'a: 't,
'b: 't,
Self: 't,
{
Box::pin(async move {
- let mut errors = vec![];
- #[allow(unused)] let $errors = &mut errors;
- let output = $code;
- $crate::layout::Layouted { output, errors }
+ let mut feedback = $crate::Feedback::new();
+ #[allow(unused)] let $feedback = &mut feedback;
+ let commands = $code;
+ $crate::Pass::new(commands, feedback)
})
}
}
@@ -179,22 +177,21 @@ macro_rules! function {
/// from parsing.
#[macro_export]
macro_rules! body {
- (opt: $body:expr, $ctx:expr, $errors:expr, $decos:expr) => ({
+ (opt: $body:expr, $ctx:expr, $feedback:expr) => ({
$body.map(|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 + $crate::syntax::span::Position::new(0, 1);
let parsed = $crate::syntax::parse(start, body.v, $ctx);
- $errors.extend(parsed.errors);
- $decos.extend(parsed.decorations);
+ $feedback.extend(parsed.feedback);
parsed.output
})
});
- (nope: $body:expr, $errors:expr) => {
+ (nope: $body:expr, $feedback:expr) => {
if let Some(body) = $body {
- $errors.push($crate::err!(body.span; "unexpected body"));
+ $feedback.errors.push($crate::err!(body.span; "unexpected body"));
}
};
}
diff --git a/src/layout/mod.rs b/src/layout/mod.rs
index 8c120c6b..b29d87e3 100644
--- a/src/layout/mod.rs
+++ b/src/layout/mod.rs
@@ -18,8 +18,7 @@ pub_use_mod!(model);
/// Basic types used across the layouting engine.
pub mod prelude {
pub use super::{
- LayoutContext, layout, LayoutSpace,
- Layouted, Commands,
+ LayoutContext, layout, LayoutSpace, Commands,
LayoutAxes, LayoutAlignment, LayoutExpansion
};
pub use super::GenericAxis::{self, *};
diff --git a/src/layout/model.rs b/src/layout/model.rs
index d23968d8..343205ec 100644
--- a/src/layout/model.rs
+++ b/src/layout/model.rs
@@ -6,12 +6,12 @@ use std::future::Future;
use std::pin::Pin;
use smallvec::smallvec;
+use crate::{Pass, Feedback};
use crate::GlobalFontLoader;
-use crate::error::Errors;
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 crate::syntax::span::{Span, Spanned};
use super::line::{LineLayouter, LineContext};
use super::text::{layout_text, TextContext};
use super::*;
@@ -23,7 +23,7 @@ pub struct ModelLayouter<'a> {
ctx: LayoutContext<'a>,
layouter: LineLayouter,
style: LayoutStyle,
- errors: Errors,
+ feedback: Feedback,
}
/// The context for layouting.
@@ -51,15 +51,6 @@ pub struct LayoutContext<'a> {
pub debug: bool,
}
-/// The result of layouting: Some layouted things and a list of errors.
-#[derive(Debug, Clone, Eq, PartialEq)]
-pub struct Layouted<T> {
- /// The result of the layouting process.
- pub output: T,
- /// Errors that arose in the process of layouting.
- pub errors: Errors,
-}
-
/// A sequence of layouting commands.
pub type Commands<'a> = Vec<Command<'a>>;
@@ -107,7 +98,7 @@ pub enum Command<'a> {
}
/// Layout a syntax model into a list of boxes.
-pub async fn layout(model: &SyntaxModel, ctx: LayoutContext<'_>) -> Layouted<MultiLayout> {
+pub async fn layout(model: &SyntaxModel, ctx: LayoutContext<'_>) -> Pass<MultiLayout> {
let mut layouter = ModelLayouter::new(ctx);
layouter.layout_syntax_model(model).await;
layouter.finish()
@@ -132,7 +123,7 @@ impl<'a> ModelLayouter<'a> {
}),
style: ctx.style.clone(),
ctx,
- errors: vec![],
+ feedback: Feedback::new(),
}
}
@@ -151,7 +142,7 @@ impl<'a> ModelLayouter<'a> {
}).await;
// Add the errors generated by the model to the error list.
- self.errors.extend(offset_spans(layouted.errors, model.span.start));
+ self.feedback.extend_offset(model.span.start, layouted.feedback);
for command in layouted.output {
self.execute_command(command, model.span).await;
@@ -195,11 +186,8 @@ impl<'a> ModelLayouter<'a> {
}) }
/// Compute the finished list of boxes.
- pub fn finish(self) -> Layouted<MultiLayout> {
- Layouted {
- output: self.layouter.finish(),
- errors: self.errors,
- }
+ pub fn finish(self) -> Pass<MultiLayout> {
+ Pass::new(self.layouter.finish(), self.feedback)
}
/// Execute a command issued by a model. When the command is errorful, the
@@ -225,7 +213,7 @@ impl<'a> ModelLayouter<'a> {
BreakParagraph => self.layout_paragraph(),
BreakPage => {
if self.ctx.nested {
- self.errors.push(err!(span;
+ self.feedback.errors.push(err!(span;
"page break cannot be issued from nested context"));
} else {
self.layouter.finish_space(true)
@@ -238,7 +226,7 @@ impl<'a> ModelLayouter<'a> {
}
SetPageStyle(style) => {
if self.ctx.nested {
- self.errors.push(err!(span;
+ self.feedback.errors.push(err!(span;
"page style cannot be changed from nested context"));
} else {
self.style.page = style;
diff --git a/src/lib.rs b/src/lib.rs
index 2d6fa021..b74837fe 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -26,10 +26,11 @@ use smallvec::smallvec;
use toddle::{Font, OwnedData};
use toddle::query::{FontLoader, FontProvider, SharedFontLoader, FontDescriptor};
-use crate::layout::{Layouted, MultiLayout};
+use crate::error::Error;
+use crate::layout::MultiLayout;
use crate::style::{LayoutStyle, PageStyle, TextStyle};
-use crate::syntax::{SyntaxModel, Scope, ParseContext, Parsed, parse};
-use crate::syntax::span::Position;
+use crate::syntax::{SyntaxModel, Scope, Decoration, ParseContext, parse};
+use crate::syntax::span::{Position, SpanVec, offset_spans};
/// Declare a module and reexport all its contents.
@@ -100,12 +101,12 @@ impl Typesetter {
}
/// Parse source code into a syntax tree.
- pub fn parse(&self, src: &str) -> Parsed<SyntaxModel> {
+ pub fn parse(&self, src: &str) -> Pass<SyntaxModel> {
parse(Position::ZERO, src, ParseContext { scope: &self.scope })
}
/// 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) -> Pass<MultiLayout> {
use crate::layout::prelude::*;
let margins = self.style.page.margins();
@@ -130,9 +131,73 @@ impl Typesetter {
}
/// Process source code directly into a collection of layouts.
- pub async fn typeset(&self, src: &str) -> MultiLayout {
- let tree = self.parse(src).output;
- self.layout(&tree).await.output
+ pub async fn typeset(&self, src: &str) -> Pass<MultiLayout> {
+ let parsed = self.parse(src);
+ let layouted = self.layout(&parsed.output).await;
+ let feedback = Feedback::merge(parsed.feedback, layouted.feedback);
+ Pass::new(layouted.output, feedback)
+ }
+}
+
+/// The result of some pass: Some output `T` and feedback data.
+#[derive(Debug, Clone, Eq, PartialEq)]
+pub struct Pass<T> {
+ /// The output of this compilation pass.
+ pub output: T,
+ /// User feedback data accumulated in this pass.
+ pub feedback: Feedback,
+}
+
+impl<T> Pass<T> {
+ /// Create a new pass from output and feedback data.
+ pub fn new(output: T, feedback: Feedback) -> Pass<T> {
+ Pass { output, feedback }
+ }
+
+ /// Map the output type and keep the feedback data.
+ pub fn map<F, U>(self, f: F) -> Pass<U> where F: FnOnce(T) -> U {
+ Pass {
+ output: f(self.output),
+ feedback: self.feedback,
+ }
+ }
+}
+
+/// User feedback data accumulated during a compilation pass.
+#[derive(Debug, Default, Clone, Eq, PartialEq)]
+pub struct Feedback {
+ /// Errors in the source.
+ pub errors: SpanVec<Error>,
+ /// Decorations of the source code for semantic syntax highlighting.
+ pub decos: SpanVec<Decoration>,
+}
+
+impl Feedback {
+ /// Create a new feedback instance without errors and decos.
+ pub fn new() -> Feedback {
+ Feedback {
+ errors: vec![],
+ decos: vec![],
+ }
+ }
+
+ /// Merged two feedbacks into one.
+ pub fn merge(mut a: Feedback, b: Feedback) -> Feedback {
+ a.extend(b);
+ a
+ }
+
+ /// Add other feedback data to this feedback.
+ pub fn extend(&mut self, other: Feedback) {
+ self.errors.extend(other.errors);
+ self.decos.extend(other.decos);
+ }
+
+ /// Add more feedback whose spans are local and need to be offset by an
+ /// `offset` to be correct for this feedbacks context.
+ pub fn extend_offset(&mut self, offset: Position, other: Feedback) {
+ self.errors.extend(offset_spans(offset, other.errors));
+ self.decos.extend(offset_spans(offset, other.decos));
}
}
diff --git a/src/library/font.rs b/src/library/font.rs
index 422a68f9..9c69e1dd 100644
--- a/src/library/font.rs
+++ b/src/library/font.rs
@@ -11,10 +11,10 @@ function! {
list: Vec<String>,
}
- parse(header, body, ctx, errors, decos) {
+ parse(header, body, ctx, f) {
FontFamilyFunc {
- body: body!(opt: body, ctx, errors, decos),
- list: header.args.pos.get_all::<StringLike>(errors)
+ body: body!(opt: body, ctx, f),
+ list: header.args.pos.get_all::<StringLike>(&mut f.errors)
.map(|s| s.0.to_lowercase())
.collect(),
}
@@ -37,11 +37,11 @@ function! {
style: Option<FontStyle>,
}
- parse(header, body, ctx, errors, decos) {
+ parse(header, body, ctx, f) {
FontStyleFunc {
- body: body!(opt: body, ctx, errors, decos),
- style: header.args.pos.get::<FontStyle>(errors)
- .or_missing(errors, header.name.span, "style"),
+ body: body!(opt: body, ctx, f),
+ style: header.args.pos.get::<FontStyle>(&mut f.errors)
+ .or_missing(&mut f.errors, header.name.span, "style"),
}
}
@@ -58,19 +58,19 @@ function! {
weight: Option<FontWeight>,
}
- parse(header, body, ctx, errors, decos) {
- let body = body!(opt: body, ctx, errors, decos);
- let weight = header.args.pos.get::<Spanned<(FontWeight, bool)>>(errors)
+ parse(header, body, ctx, f) {
+ let body = body!(opt: body, ctx, f);
+ let weight = header.args.pos.get::<Spanned<(FontWeight, bool)>>(&mut f.errors)
.map(|Spanned { v: (weight, is_clamped), span }| {
if is_clamped {
- errors.push(err!(@Warning: span;
+ f.errors.push(err!(@Warning: span;
"weight should be between \
100 and 900, clamped to {}", weight.0));
}
weight
})
- .or_missing(errors, header.name.span, "weight");
+ .or_missing(&mut f.errors, header.name.span, "weight");
FontWeightFunc { body, weight }
}
@@ -88,11 +88,11 @@ function! {
size: Option<FSize>,
}
- parse(header, body, ctx, errors, decos) {
+ parse(header, body, ctx, f) {
FontSizeFunc {
- body: body!(opt: body, ctx, errors, decos),
- size: header.args.pos.get::<FSize>(errors)
- .or_missing(errors, header.name.span, "size")
+ body: body!(opt: body, ctx, f),
+ size: header.args.pos.get::<FSize>(&mut f.errors)
+ .or_missing(&mut f.errors, header.name.span, "size")
}
}
diff --git a/src/library/layout.rs b/src/library/layout.rs
index 591ea2c0..da1652b0 100644
--- a/src/library/layout.rs
+++ b/src/library/layout.rs
@@ -10,17 +10,17 @@ function! {
map: PosAxisMap<AlignmentValue>,
}
- parse(header, body, ctx, errors, decos) {
+ parse(header, body, ctx, f) {
AlignFunc {
- body: body!(opt: body, ctx, errors, decos),
- map: PosAxisMap::parse::<AxisKey>(errors, &mut header.args),
+ body: body!(opt: body, ctx, f),
+ map: PosAxisMap::parse::<AxisKey>(&mut f.errors, &mut header.args),
}
}
- layout(self, ctx, errors) {
+ layout(self, ctx, f) {
ctx.base = ctx.spaces[0].dimensions;
- let map = self.map.dedup(errors, ctx.axes, |alignment| {
+ let map = self.map.dedup(&mut f.errors, ctx.axes, |alignment| {
alignment.axis().map(|s| s.to_generic(ctx.axes))
});
@@ -29,7 +29,7 @@ function! {
if let Some(generic) = alignment.to_generic(ctx.axes, axis) {
*ctx.alignment.get_mut(axis) = generic;
} else {
- errors.push(err!(span;
+ f.errors.push(err!(span;
"invalid alignment `{}` for {} axis", alignment, axis));
}
}
@@ -38,7 +38,7 @@ function! {
match &self.body {
Some(body) => {
let layouted = layout(body, ctx).await;
- errors.extend(layouted.errors);
+ f.extend(layouted.feedback);
vec![AddMultiple(layouted.output)]
}
None => vec![SetAlignment(ctx.alignment)],
@@ -55,18 +55,18 @@ function! {
map: PosAxisMap<Direction>,
}
- parse(header, body, ctx, errors, decos) {
+ parse(header, body, ctx, f) {
DirectionFunc {
name_span: header.name.span,
- body: body!(opt: body, ctx, errors, decos),
- map: PosAxisMap::parse::<AxisKey>(errors, &mut header.args),
+ body: body!(opt: body, ctx, f),
+ map: PosAxisMap::parse::<AxisKey>(&mut f.errors, &mut header.args),
}
}
- layout(self, ctx, errors) {
+ layout(self, ctx, f) {
ctx.base = ctx.spaces[0].dimensions;
- let map = self.map.dedup(errors, ctx.axes, |direction| {
+ let map = self.map.dedup(&mut f.errors, ctx.axes, |direction| {
Some(direction.axis().to_generic(ctx.axes))
});
@@ -76,7 +76,7 @@ function! {
map.with(Secondary, |&dir| axes.secondary = dir);
if axes.primary.axis() == axes.secondary.axis() {
- errors.push(err!(self.name_span;
+ f.errors.push(err!(self.name_span;
"invalid aligned primary and secondary axes: `{}`, `{}`",
ctx.axes.primary, ctx.axes.secondary));
} else {
@@ -86,7 +86,7 @@ function! {
match &self.body {
Some(body) => {
let layouted = layout(body, ctx).await;
- errors.extend(layouted.errors);
+ f.extend(layouted.feedback);
vec![AddMultiple(layouted.output)]
}
None => vec![SetAxes(ctx.axes)],
@@ -103,15 +103,15 @@ function! {
debug: Option<bool>,
}
- parse(header, body, ctx, errors, decos) {
+ parse(header, body, ctx, f) {
BoxFunc {
- body: body!(opt: body, ctx, errors, decos).unwrap_or(SyntaxModel::new()),
- extents: AxisMap::parse::<ExtentKey>(errors, &mut header.args.key),
- debug: header.args.key.get::<bool>(errors, "debug"),
+ body: body!(opt: body, ctx, f).unwrap_or(SyntaxModel::new()),
+ extents: AxisMap::parse::<ExtentKey>(&mut f.errors, &mut header.args.key),
+ debug: header.args.key.get::<bool>(&mut f.errors, "debug"),
}
}
- layout(self, ctx, errors) {
+ layout(self, ctx, f) {
ctx.repeat = false;
ctx.spaces.truncate(1);
@@ -119,7 +119,7 @@ function! {
ctx.debug = debug;
}
- let map = self.extents.dedup(errors, ctx.axes);
+ let map = self.extents.dedup(&mut f.errors, ctx.axes);
for &axis in &[Horizontal, Vertical] {
if let Some(psize) = map.get(axis) {
let size = psize.scaled(ctx.base.get(axis));
@@ -131,7 +131,7 @@ function! {
let layouted = layout(&self.body, ctx).await;
let layout = layouted.output.into_iter().next().unwrap();
- errors.extend(layouted.errors);
+ f.extend(layouted.feedback);
vec![Add(layout)]
}
diff --git a/src/library/mod.rs b/src/library/mod.rs
index f6666174..08dffdd7 100644
--- a/src/library/mod.rs
+++ b/src/library/mod.rs
@@ -53,8 +53,8 @@ function! {
body: Option<SyntaxModel>,
}
- parse(header, body, ctx, errors, decos) {
- ValFunc { body: body!(opt: body, ctx, errors, decos) }
+ parse(header, body, ctx, f) {
+ ValFunc { body: body!(opt: body, ctx, f) }
}
layout(self, ctx, errors) {
diff --git a/src/library/page.rs b/src/library/page.rs
index 084b3446..07e43026 100644
--- a/src/library/page.rs
+++ b/src/library/page.rs
@@ -12,16 +12,16 @@ function! {
flip: bool,
}
- parse(header, body, ctx, errors, decos) {
- body!(nope: body, errors);
+ parse(header, body, ctx, f) {
+ body!(nope: body, f);
PageSizeFunc {
- paper: header.args.pos.get::<Paper>(errors),
- extents: AxisMap::parse::<ExtentKey>(errors, &mut header.args.key),
- flip: header.args.key.get::<bool>(errors, "flip").unwrap_or(false),
+ paper: header.args.pos.get::<Paper>(&mut f.errors),
+ extents: AxisMap::parse::<ExtentKey>(&mut f.errors, &mut header.args.key),
+ flip: header.args.key.get::<bool>(&mut f.errors, "flip").unwrap_or(false),
}
}
- layout(self, ctx, errors) {
+ layout(self, ctx, f) {
let mut style = ctx.style.page;
if let Some(paper) = self.paper {
@@ -31,7 +31,7 @@ function! {
style.class = PaperClass::Custom;
}
- let map = self.extents.dedup(errors, ctx.axes);
+ let map = self.extents.dedup(&mut f.errors, ctx.axes);
map.with(Horizontal, |&width| style.dimensions.x = width);
map.with(Vertical, |&height| style.dimensions.y = height);
@@ -50,16 +50,16 @@ function! {
padding: PaddingMap,
}
- parse(header, body, ctx, errors, decos) {
- body!(nope: body, errors);
+ parse(header, body, ctx, f) {
+ body!(nope: body, f);
PageMarginsFunc {
- padding: PaddingMap::parse(errors, &mut header.args),
+ padding: PaddingMap::parse(&mut f.errors, &mut header.args),
}
}
- layout(self, ctx, errors) {
+ layout(self, ctx, f) {
let mut style = ctx.style.page;
- self.padding.apply(errors, ctx.axes, &mut style.margins);
+ self.padding.apply(&mut f.errors, ctx.axes, &mut style.margins);
vec![SetPageStyle(style)]
}
}
diff --git a/src/library/spacing.rs b/src/library/spacing.rs
index d77285c5..a6db162a 100644
--- a/src/library/spacing.rs
+++ b/src/library/spacing.rs
@@ -46,13 +46,13 @@ function! {
type Meta = ContentKind;
- parse(header, body, ctx, errors, decos, meta) {
+ parse(header, body, ctx, f, meta) {
ContentSpacingFunc {
- body: body!(opt: body, ctx, errors, decos),
+ body: body!(opt: body, ctx, f),
content: meta,
- spacing: header.args.pos.get::<f64>(errors)
+ spacing: header.args.pos.get::<f64>(&mut f.errors)
.map(|num| num as f32)
- .or_missing(errors, header.name.span, "spacing"),
+ .or_missing(&mut f.errors, header.name.span, "spacing"),
}
}
@@ -84,15 +84,15 @@ function! {
type Meta = Option<SpecificAxis>;
- parse(header, body, ctx, errors, decos, meta) {
- body!(nope: body, errors);
+ parse(header, body, ctx, f, meta) {
+ body!(nope: body, f);
SpacingFunc {
spacing: if let Some(axis) = meta {
- header.args.pos.get::<FSize>(errors)
+ header.args.pos.get::<FSize>(&mut f.errors)
.map(|s| (AxisKey::Specific(axis), s))
} else {
- header.args.key.get_with_key::<AxisKey, FSize>(errors)
- }.or_missing(errors, header.name.span, "spacing"),
+ header.args.key.get_with_key::<AxisKey, FSize>(&mut f.errors)
+ }.or_missing(&mut f.errors, header.name.span, "spacing"),
}
}
diff --git a/src/syntax/mod.rs b/src/syntax/mod.rs
index cfa4c2e5..8596a618 100644
--- a/src/syntax/mod.rs
+++ b/src/syntax/mod.rs
@@ -5,7 +5,8 @@ use std::fmt::Debug;
use async_trait::async_trait;
use serde::Serialize;
-use crate::layout::{LayoutContext, Layouted, Commands, Command};
+use crate::{Pass, Feedback};
+use crate::layout::{LayoutContext, Commands, Command};
use self::span::{Spanned, SpanVec};
pub mod expr;
@@ -25,10 +26,7 @@ mod test;
pub trait Model: Debug + ModelBounds {
/// Layout the model into a sequence of commands processed by a
/// [`ModelLayouter`](crate::layout::ModelLayouter).
- async fn layout<'a>(
- &'a self,
- ctx: LayoutContext<'_>,
- ) -> Layouted<Commands<'a>>;
+ async fn layout<'a>(&'a self, ctx: LayoutContext<'_>) -> Pass<Commands<'a>>;
}
/// A tree representation of source code.
@@ -52,14 +50,8 @@ impl SyntaxModel {
#[async_trait(?Send)]
impl Model for SyntaxModel {
- async fn layout<'a>(
- &'a self,
- _: LayoutContext<'_>,
- ) -> Layouted<Commands<'a>> {
- Layouted {
- output: vec![Command::LayoutSyntaxModel(self)],
- errors: vec![],
- }
+ async fn layout<'a>(&'a self, _: LayoutContext<'_>) -> Pass<Commands<'a>> {
+ Pass::new(vec![Command::LayoutSyntaxModel(self)], Feedback::new())
}
}
diff --git a/src/syntax/parsing.rs b/src/syntax/parsing.rs
index 03866c2c..1526a5cb 100644
--- a/src/syntax/parsing.rs
+++ b/src/syntax/parsing.rs
@@ -1,10 +1,10 @@
//! Parsing of source code into syntax models.
-use crate::error::Errors;
+use crate::{Pass, Feedback};
use super::expr::*;
use super::func::{FuncHeader, FuncArgs, FuncArg};
use super::scope::Scope;
-use super::span::{Position, Span, Spanned, SpanVec, offset_spans};
+use super::span::{Position, Span, Spanned};
use super::tokens::{Token, Tokens, TokenizationMode};
use super::*;
@@ -16,36 +16,12 @@ pub struct ParseContext<'a> {
pub scope: &'a Scope,
}
-/// The result of parsing: Some parsed thing, errors and decorations for syntax
-/// highlighting.
-#[derive(Debug, Clone, Eq, PartialEq)]
-pub struct Parsed<T> {
- /// The result of the parsing process.
- pub output: T,
- /// Errors that arose in the parsing process.
- pub errors: Errors,
- /// Decorations for semantic syntax highlighting.
- pub decorations: SpanVec<Decoration>,
-}
-
-impl<T> Parsed<T> {
- /// Map the output type and keep errors and decorations.
- pub fn map<F, U>(self, f: F) -> Parsed<U> where F: FnOnce(T) -> U {
- Parsed {
- output: f(self.output),
- errors: self.errors,
- decorations: self.decorations,
- }
- }
-}
-
/// Parse source code into a syntax model.
///
/// All errors and decorations are offset by the `start` position.
-pub fn parse(start: Position, src: &str, ctx: ParseContext) -> Parsed<SyntaxModel> {
+pub fn parse(start: Position, src: &str, ctx: ParseContext) -> Pass<SyntaxModel> {
let mut model = SyntaxModel::new();
- let mut errors = Vec::new();
- let mut decorations = Vec::new();
+ let mut feedback = Feedback::new();
// We always start in body mode. The header tokenization mode is only used
// in the `FuncParser`.
@@ -64,16 +40,12 @@ pub fn parse(start: Position, src: &str, ctx: ParseContext) -> Parsed<SyntaxMode
},
Token::Function { header, body, terminated } => {
- let parsed: Parsed<Node> = FuncParser::new(header, body, ctx).parse();
-
- // Collect the errors and decorations from the function parsing,
- // but offset their spans by the start of the function since
- // they are function-local.
- errors.extend(offset_spans(parsed.errors, span.start));
- decorations.extend(offset_spans(parsed.decorations, span.start));
+ let parsed = FuncParser::new(header, body, ctx).parse();
+ feedback.extend_offset(span.start, parsed.feedback);
if !terminated {
- errors.push(err!(Span::at(span.end); "expected closing bracket"));
+ feedback.errors.push(err!(Span::at(span.end);
+ "expected closing bracket"));
}
parsed.output
@@ -87,7 +59,7 @@ pub fn parse(start: Position, src: &str, ctx: ParseContext) -> Parsed<SyntaxMode
Token::LineComment(_) | Token::BlockComment(_) => continue,
other => {
- errors.push(err!(span; "unexpected {}", other.name()));
+ feedback.errors.push(err!(span; "unexpected {}", other.name()));
continue;
}
};
@@ -95,14 +67,13 @@ pub fn parse(start: Position, src: &str, ctx: ParseContext) -> Parsed<SyntaxMode
model.add(Spanned { v: node, span: token.span });
}
- Parsed { output: model, errors, decorations }
+ Pass::new(model, feedback)
}
/// Performs the function parsing.
struct FuncParser<'s> {
ctx: ParseContext<'s>,
- errors: Errors,
- decorations: SpanVec<Decoration>,
+ feedback: Feedback,
/// ```typst
/// [tokens][body]
@@ -129,8 +100,7 @@ impl<'s> FuncParser<'s> {
) -> FuncParser<'s> {
FuncParser {
ctx,
- errors: vec![],
- decorations: vec![],
+ feedback: Feedback::new(),
tokens: Tokens::new(Position::new(0, 1), header, TokenizationMode::Header),
peeked: None,
body,
@@ -138,7 +108,7 @@ impl<'s> FuncParser<'s> {
}
/// Do the parsing.
- fn parse(mut self) -> Parsed<Node> {
+ fn parse(mut self) -> Pass<Node> {
let parsed = if let Some(header) = self.parse_func_header() {
let name = header.name.v.as_str();
let (parser, deco) = match self.ctx.scope.get_parser(name) {
@@ -147,12 +117,12 @@ impl<'s> FuncParser<'s> {
// The fallback parser was returned. Invalid function.
Err(parser) => {
- self.errors.push(err!(header.name.span; "unknown function"));
+ self.feedback.errors.push(err!(header.name.span; "unknown function"));
(parser, Decoration::InvalidFuncName)
}
};
- self.decorations.push(Spanned::new(deco, header.name.span));
+ self.feedback.decos.push(Spanned::new(deco, header.name.span));
parser(header, self.body, self.ctx)
} else {
@@ -166,14 +136,9 @@ impl<'s> FuncParser<'s> {
self.ctx.scope.get_fallback_parser()(default, self.body, self.ctx)
};
- self.errors.extend(parsed.errors);
- self.decorations.extend(parsed.decorations);
+ self.feedback.extend(parsed.feedback);
- Parsed {
- output: Node::Model(parsed.output),
- errors: self.errors,
- decorations: self.decorations,
- }
+ Pass::new(Node::Model(parsed.output), self.feedback)
}
/// Parse the header tokens.
@@ -235,7 +200,7 @@ impl<'s> FuncParser<'s> {
self.eat();
self.skip_whitespace();
- self.decorations.push(Spanned::new(Decoration::ArgumentKey, span));
+ self.feedback.decos.push(Spanned::new(Decoration::ArgumentKey, span));
self.parse_expr().map(|value| {
FuncArg::Key(Pair {
@@ -332,14 +297,14 @@ impl<'s> FuncParser<'s> {
/// 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;
+ self.feedback.errors.push(err!(found.span;
"expected {}, found {}", thing, found.v.name()));
}
/// Add an error about an `thing` which was expected but not found at the
/// given position.
fn expected_at(&mut self, thing: &str, pos: Position) {
- self.errors.push(err!(Span::at(pos); "expected {}", thing));
+ self.feedback.errors.push(err!(Span::at(pos); "expected {}", thing));
}
/// Add a expected-found-error if `found` is `Some` and an expected-error
@@ -434,7 +399,8 @@ mod tests {
macro_rules! e {
($s:expr => [$(($sl:tt:$sc:tt, $el:tt:$ec:tt, $e:expr)),* $(,)?]) => {
let ctx = ParseContext { scope: &scope() };
- let errors = parse(Position::ZERO, $s, ctx).errors
+ let errors = parse(Position::ZERO, $s, ctx).feedback
+ .errors
.into_iter()
.map(|s| s.map(|e| e.message))
.collect::<Vec<_>>();
diff --git a/src/syntax/scope.rs b/src/syntax/scope.rs
index 551d0684..f7b20b9f 100644
--- a/src/syntax/scope.rs
+++ b/src/syntax/scope.rs
@@ -3,9 +3,10 @@
use std::collections::HashMap;
use std::fmt::{self, Debug, Formatter};
+use crate::Pass;
use crate::func::ParseFunc;
use super::func::FuncHeader;
-use super::parsing::{ParseContext, Parsed};
+use super::parsing::ParseContext;
use super::span::Spanned;
use super::Model;
@@ -75,7 +76,7 @@ type Parser = dyn Fn(
FuncHeader,
Option<Spanned<&str>>,
ParseContext,
-) -> Parsed<Box<dyn Model>>;
+) -> Pass<Box<dyn Model>>;
fn parser<F>(metadata: <F as ParseFunc>::Meta) -> Box<Parser>
where F: ParseFunc + Model + 'static {
diff --git a/src/syntax/span.rs b/src/syntax/span.rs
index 8973ef89..3c55daa1 100644
--- a/src/syntax/span.rs
+++ b/src/syntax/span.rs
@@ -160,6 +160,7 @@ pub type SpanVec<T> = Vec<Spanned<T>>;
/// [Offset](Span::offset) all spans in a vector of spanned things by a start
/// position.
-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)))
+pub fn offset_spans<T>(start: Position, vec: SpanVec<T>) -> impl Iterator<Item=Spanned<T>> {
+ vec.into_iter()
+ .map(move |s| s.map_span(|span| span.offset(start)))
}
diff --git a/src/syntax/test.rs b/src/syntax/test.rs
index e37e8cf5..df354768 100644
--- a/src/syntax/test.rs
+++ b/src/syntax/test.rs
@@ -12,13 +12,13 @@ function! {
pub body: Option<SyntaxModel>,
}
- parse(header, body, ctx, errors, decos) {
+ parse(header, body, ctx, f) {
let cloned = header.clone();
header.args.pos.items.clear();
header.args.key.pairs.clear();
DebugFn {
header: cloned,
- body: body!(opt: body, ctx, errors, decos),
+ body: body!(opt: body, ctx, f),
}
}
diff --git a/tests/src/typeset.rs b/tests/src/typeset.rs
index 61889e95..efeb5aee 100644
--- a/tests/src/typeset.rs
+++ b/tests/src/typeset.rs
@@ -5,6 +5,7 @@ use std::fs::{File, create_dir_all, read_dir, read_to_string};
use std::io::{BufWriter, Write};
use std::panic;
use std::process::Command;
+use std::time::{Instant, Duration};
use futures_executor::block_on;
@@ -55,8 +56,6 @@ fn main() -> DynResult<()> {
}).ok();
}
- println!();
-
Ok(())
}
@@ -121,43 +120,40 @@ fn test(name: &str, src: &str) -> DynResult<()> {
/// Compile the source code with the typesetter.
fn compile(typesetter: &Typesetter, src: &str) -> MultiLayout {
- #![allow(unused_variables)]
- use std::time::Instant;
-
- // Warmup.
- #[cfg(not(debug_assertions))]
- let warmup = {
- let warmup_start = Instant::now();
- block_on(typesetter.typeset(&src));
- Instant::now() - warmup_start
- };
-
- let start = Instant::now();
- let parsed = typesetter.parse(&src);
- let parse = Instant::now() - start;
-
- if !parsed.errors.is_empty() {
- println!("parse errors: {:#?}", parsed.errors);
- }
-
- let start_layout = Instant::now();
- let layouted = block_on(typesetter.layout(&parsed.output));
- let layout = Instant::now() - start_layout;
- let total = Instant::now() - start;
-
- if !layouted.errors.is_empty() {
- println!("layout errors: {:#?}", layouted.errors);
- }
+ if cfg!(debug_assertions) {
+ let typeset = block_on(typesetter.typeset(src));
+ let errors = typeset.feedback.errors;
+
+ if !errors.is_empty() {
+ for error in errors {
+ println!(" {:?} {:?}: {}",
+ error.v.severity,
+ error.span,
+ error.v.message
+ );
+ }
+ }
- #[cfg(not(debug_assertions))] {
- println!(" - cold start: {:?}", warmup);
- println!(" - warmed up: {:?}", total);
+ typeset.output
+ } else {
+ fn measure<T>(f: impl FnOnce() -> T) -> (T, Duration) {
+ let start = Instant::now();
+ let output = f();
+ let duration = Instant::now() - start;
+ (output, duration)
+ };
+
+ let (_, cold) = measure(|| block_on(typesetter.typeset(src)));
+ let (model, parse) = measure(|| typesetter.parse(src).output);
+ let (layouts, layout) = measure(|| block_on(typesetter.layout(&model)).output);
+
+ println!(" - cold start: {:?}", cold);
+ println!(" - warmed up: {:?}", parse + layout);
println!(" - parsing: {:?}", parse);
println!(" - layouting: {:?}", layout);
- println!();
- }
- layouted.output
+ layouts
+ }
}
/// Command line options.