mirror of
https://github.com/typst/typst
synced 2025-05-14 04:56:26 +08:00
Merge Parsed
and Layouted
types into Pass
with Feedback
🌝🎢🌚
This commit is contained in:
parent
5c11aa7223
commit
e63ce52ae0
@ -42,7 +42,7 @@ fn run() -> Result<(), Box<dyn Error>> {
|
|||||||
let provider = DebugErrorProvider::new(fs);
|
let provider = DebugErrorProvider::new(fs);
|
||||||
let typesetter = Typesetter::new((Box::new(provider), entries));
|
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)?);
|
let writer = BufWriter::new(File::create(&dest)?);
|
||||||
pdf::export(&layouts, typesetter.loader(), writer)?;
|
pdf::export(&layouts, typesetter.loader(), writer)?;
|
||||||
|
53
src/func.rs
53
src/func.rs
@ -1,6 +1,7 @@
|
|||||||
//! Trait and prelude for custom functions.
|
//! 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::func::FuncHeader;
|
||||||
use crate::syntax::span::Spanned;
|
use crate::syntax::span::Spanned;
|
||||||
|
|
||||||
@ -36,7 +37,7 @@ pub trait ParseFunc {
|
|||||||
body: Option<Spanned<&str>>,
|
body: Option<Spanned<&str>>,
|
||||||
ctx: ParseContext,
|
ctx: ParseContext,
|
||||||
metadata: Self::Meta,
|
metadata: Self::Meta,
|
||||||
) -> Parsed<Self> where Self: Sized;
|
) -> Pass<Self> where Self: Sized;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Allows to implement a function type concisely.
|
/// Allows to implement a function type concisely.
|
||||||
@ -103,17 +104,16 @@ macro_rules! function {
|
|||||||
|
|
||||||
// Parse trait.
|
// Parse trait.
|
||||||
(@parse($($a:tt)*) parse(default) $($r:tt)*) => {
|
(@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)* ) => {
|
(@parse($($a:tt)*) parse($h:ident, $b:ident, $c:ident, $f:ident) $($r:tt)* ) => {
|
||||||
function!(@parse($($a)*) parse($h, $b, $c, $e, $d, _metadata) $($r)*);
|
function!(@parse($($a)*) parse($h, $b, $c, $f, _metadata) $($r)*);
|
||||||
};
|
};
|
||||||
(@parse($name:ident, $meta:ty) parse(
|
(@parse($name:ident, $meta:ty) parse(
|
||||||
$header:ident,
|
$header:ident,
|
||||||
$body:ident,
|
$body:ident,
|
||||||
$ctx:ident,
|
$ctx:ident,
|
||||||
$errors:ident,
|
$feedback:ident,
|
||||||
$decos:ident,
|
|
||||||
$metadata:ident
|
$metadata:ident
|
||||||
) $code:block $($r:tt)*) => {
|
) $code:block $($r:tt)*) => {
|
||||||
impl $crate::func::ParseFunc for $name {
|
impl $crate::func::ParseFunc for $name {
|
||||||
@ -124,42 +124,40 @@ macro_rules! function {
|
|||||||
#[allow(unused)] $body: Option<$crate::syntax::span::Spanned<&str>>,
|
#[allow(unused)] $body: Option<$crate::syntax::span::Spanned<&str>>,
|
||||||
#[allow(unused)] $ctx: $crate::syntax::ParseContext,
|
#[allow(unused)] $ctx: $crate::syntax::ParseContext,
|
||||||
#[allow(unused)] $metadata: Self::Meta,
|
#[allow(unused)] $metadata: Self::Meta,
|
||||||
) -> $crate::syntax::Parsed<Self> where Self: Sized {
|
) -> $crate::Pass<Self> where Self: Sized {
|
||||||
let mut errors = vec![];
|
let mut feedback = $crate::Feedback::new();
|
||||||
let mut decorations = vec![];
|
|
||||||
#[allow(unused)] let $header = &mut header;
|
#[allow(unused)] let $header = &mut header;
|
||||||
#[allow(unused)] let $errors = &mut errors;
|
#[allow(unused)] let $feedback = &mut feedback;
|
||||||
#[allow(unused)] let $decos = &mut decorations;
|
|
||||||
let output = $code;
|
let func = $code;
|
||||||
|
|
||||||
for arg in header.args.into_iter() {
|
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)*);
|
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 {
|
impl $crate::syntax::Model for $name {
|
||||||
fn layout<'a, 'b, 't>(
|
fn layout<'a, 'b, 't>(
|
||||||
#[allow(unused)] &'a $this,
|
#[allow(unused)] &'a $this,
|
||||||
#[allow(unused)] mut $ctx: $crate::layout::LayoutContext<'b>,
|
#[allow(unused)] mut $ctx: $crate::layout::LayoutContext<'b>,
|
||||||
) -> $crate::layout::DynFuture<'t, $crate::layout::Layouted<
|
) -> $crate::layout::DynFuture<'t, $crate::Pass<$crate::layout::Commands<'a>>>
|
||||||
$crate::layout::Commands<'a>>
|
where
|
||||||
> where
|
|
||||||
'a: 't,
|
'a: 't,
|
||||||
'b: 't,
|
'b: 't,
|
||||||
Self: 't,
|
Self: 't,
|
||||||
{
|
{
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
let mut errors = vec![];
|
let mut feedback = $crate::Feedback::new();
|
||||||
#[allow(unused)] let $errors = &mut errors;
|
#[allow(unused)] let $feedback = &mut feedback;
|
||||||
let output = $code;
|
let commands = $code;
|
||||||
$crate::layout::Layouted { output, errors }
|
$crate::Pass::new(commands, feedback)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -179,22 +177,21 @@ macro_rules! function {
|
|||||||
/// from parsing.
|
/// from parsing.
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! body {
|
macro_rules! body {
|
||||||
(opt: $body:expr, $ctx:expr, $errors:expr, $decos:expr) => ({
|
(opt: $body:expr, $ctx:expr, $feedback:expr) => ({
|
||||||
$body.map(|body| {
|
$body.map(|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 + $crate::syntax::span::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);
|
$feedback.extend(parsed.feedback);
|
||||||
$decos.extend(parsed.decorations);
|
|
||||||
parsed.output
|
parsed.output
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
(nope: $body:expr, $errors:expr) => {
|
(nope: $body:expr, $feedback:expr) => {
|
||||||
if let Some(body) = $body {
|
if let Some(body) = $body {
|
||||||
$errors.push($crate::err!(body.span; "unexpected body"));
|
$feedback.errors.push($crate::err!(body.span; "unexpected body"));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -18,8 +18,7 @@ pub_use_mod!(model);
|
|||||||
/// Basic types used across the layouting engine.
|
/// Basic types used across the layouting engine.
|
||||||
pub mod prelude {
|
pub mod prelude {
|
||||||
pub use super::{
|
pub use super::{
|
||||||
LayoutContext, layout, LayoutSpace,
|
LayoutContext, layout, LayoutSpace, Commands,
|
||||||
Layouted, Commands,
|
|
||||||
LayoutAxes, LayoutAlignment, LayoutExpansion
|
LayoutAxes, LayoutAlignment, LayoutExpansion
|
||||||
};
|
};
|
||||||
pub use super::GenericAxis::{self, *};
|
pub use super::GenericAxis::{self, *};
|
||||||
|
@ -6,12 +6,12 @@ use std::future::Future;
|
|||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
use smallvec::smallvec;
|
use smallvec::smallvec;
|
||||||
|
|
||||||
|
use crate::{Pass, Feedback};
|
||||||
use crate::GlobalFontLoader;
|
use crate::GlobalFontLoader;
|
||||||
use crate::error::Errors;
|
|
||||||
use crate::style::{LayoutStyle, PageStyle, TextStyle};
|
use crate::style::{LayoutStyle, PageStyle, TextStyle};
|
||||||
use crate::size::{Size, Size2D};
|
use crate::size::{Size, Size2D};
|
||||||
use crate::syntax::{Model, SyntaxModel, Node};
|
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::line::{LineLayouter, LineContext};
|
||||||
use super::text::{layout_text, TextContext};
|
use super::text::{layout_text, TextContext};
|
||||||
use super::*;
|
use super::*;
|
||||||
@ -23,7 +23,7 @@ pub struct ModelLayouter<'a> {
|
|||||||
ctx: LayoutContext<'a>,
|
ctx: LayoutContext<'a>,
|
||||||
layouter: LineLayouter,
|
layouter: LineLayouter,
|
||||||
style: LayoutStyle,
|
style: LayoutStyle,
|
||||||
errors: Errors,
|
feedback: Feedback,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The context for layouting.
|
/// The context for layouting.
|
||||||
@ -51,15 +51,6 @@ pub struct LayoutContext<'a> {
|
|||||||
pub debug: bool,
|
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.
|
/// A sequence of layouting commands.
|
||||||
pub type Commands<'a> = Vec<Command<'a>>;
|
pub type Commands<'a> = Vec<Command<'a>>;
|
||||||
|
|
||||||
@ -107,7 +98,7 @@ pub enum Command<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Layout a syntax model into a list of boxes.
|
/// 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);
|
let mut layouter = ModelLayouter::new(ctx);
|
||||||
layouter.layout_syntax_model(model).await;
|
layouter.layout_syntax_model(model).await;
|
||||||
layouter.finish()
|
layouter.finish()
|
||||||
@ -132,7 +123,7 @@ impl<'a> ModelLayouter<'a> {
|
|||||||
}),
|
}),
|
||||||
style: ctx.style.clone(),
|
style: ctx.style.clone(),
|
||||||
ctx,
|
ctx,
|
||||||
errors: vec![],
|
feedback: Feedback::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -151,7 +142,7 @@ impl<'a> ModelLayouter<'a> {
|
|||||||
}).await;
|
}).await;
|
||||||
|
|
||||||
// Add the errors generated by the model to the error list.
|
// 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 {
|
for command in layouted.output {
|
||||||
self.execute_command(command, model.span).await;
|
self.execute_command(command, model.span).await;
|
||||||
@ -195,11 +186,8 @@ impl<'a> ModelLayouter<'a> {
|
|||||||
}) }
|
}) }
|
||||||
|
|
||||||
/// Compute the finished list of boxes.
|
/// Compute the finished list of boxes.
|
||||||
pub fn finish(self) -> Layouted<MultiLayout> {
|
pub fn finish(self) -> Pass<MultiLayout> {
|
||||||
Layouted {
|
Pass::new(self.layouter.finish(), self.feedback)
|
||||||
output: self.layouter.finish(),
|
|
||||||
errors: self.errors,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Execute a command issued by a model. When the command is errorful, the
|
/// 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(),
|
BreakParagraph => self.layout_paragraph(),
|
||||||
BreakPage => {
|
BreakPage => {
|
||||||
if self.ctx.nested {
|
if self.ctx.nested {
|
||||||
self.errors.push(err!(span;
|
self.feedback.errors.push(err!(span;
|
||||||
"page break cannot be issued from nested context"));
|
"page break cannot be issued from nested context"));
|
||||||
} else {
|
} else {
|
||||||
self.layouter.finish_space(true)
|
self.layouter.finish_space(true)
|
||||||
@ -238,7 +226,7 @@ impl<'a> ModelLayouter<'a> {
|
|||||||
}
|
}
|
||||||
SetPageStyle(style) => {
|
SetPageStyle(style) => {
|
||||||
if self.ctx.nested {
|
if self.ctx.nested {
|
||||||
self.errors.push(err!(span;
|
self.feedback.errors.push(err!(span;
|
||||||
"page style cannot be changed from nested context"));
|
"page style cannot be changed from nested context"));
|
||||||
} else {
|
} else {
|
||||||
self.style.page = style;
|
self.style.page = style;
|
||||||
|
81
src/lib.rs
81
src/lib.rs
@ -26,10 +26,11 @@ use smallvec::smallvec;
|
|||||||
use toddle::{Font, OwnedData};
|
use toddle::{Font, OwnedData};
|
||||||
use toddle::query::{FontLoader, FontProvider, SharedFontLoader, FontDescriptor};
|
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::style::{LayoutStyle, PageStyle, TextStyle};
|
||||||
use crate::syntax::{SyntaxModel, Scope, ParseContext, Parsed, parse};
|
use crate::syntax::{SyntaxModel, Scope, Decoration, ParseContext, parse};
|
||||||
use crate::syntax::span::Position;
|
use crate::syntax::span::{Position, SpanVec, offset_spans};
|
||||||
|
|
||||||
|
|
||||||
/// Declare a module and reexport all its contents.
|
/// Declare a module and reexport all its contents.
|
||||||
@ -100,12 +101,12 @@ impl Typesetter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Parse source code into a syntax tree.
|
/// 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 })
|
parse(Position::ZERO, src, ParseContext { scope: &self.scope })
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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) -> Pass<MultiLayout> {
|
||||||
use crate::layout::prelude::*;
|
use crate::layout::prelude::*;
|
||||||
|
|
||||||
let margins = self.style.page.margins();
|
let margins = self.style.page.margins();
|
||||||
@ -130,9 +131,73 @@ impl Typesetter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Process source code directly into a collection of layouts.
|
/// Process source code directly into a collection of layouts.
|
||||||
pub async fn typeset(&self, src: &str) -> MultiLayout {
|
pub async fn typeset(&self, src: &str) -> Pass<MultiLayout> {
|
||||||
let tree = self.parse(src).output;
|
let parsed = self.parse(src);
|
||||||
self.layout(&tree).await.output
|
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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,10 +11,10 @@ function! {
|
|||||||
list: Vec<String>,
|
list: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
parse(header, body, ctx, errors, decos) {
|
parse(header, body, ctx, f) {
|
||||||
FontFamilyFunc {
|
FontFamilyFunc {
|
||||||
body: body!(opt: body, ctx, errors, decos),
|
body: body!(opt: body, ctx, f),
|
||||||
list: header.args.pos.get_all::<StringLike>(errors)
|
list: header.args.pos.get_all::<StringLike>(&mut f.errors)
|
||||||
.map(|s| s.0.to_lowercase())
|
.map(|s| s.0.to_lowercase())
|
||||||
.collect(),
|
.collect(),
|
||||||
}
|
}
|
||||||
@ -37,11 +37,11 @@ function! {
|
|||||||
style: Option<FontStyle>,
|
style: Option<FontStyle>,
|
||||||
}
|
}
|
||||||
|
|
||||||
parse(header, body, ctx, errors, decos) {
|
parse(header, body, ctx, f) {
|
||||||
FontStyleFunc {
|
FontStyleFunc {
|
||||||
body: body!(opt: body, ctx, errors, decos),
|
body: body!(opt: body, ctx, f),
|
||||||
style: header.args.pos.get::<FontStyle>(errors)
|
style: header.args.pos.get::<FontStyle>(&mut f.errors)
|
||||||
.or_missing(errors, header.name.span, "style"),
|
.or_missing(&mut f.errors, header.name.span, "style"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -58,19 +58,19 @@ function! {
|
|||||||
weight: Option<FontWeight>,
|
weight: Option<FontWeight>,
|
||||||
}
|
}
|
||||||
|
|
||||||
parse(header, body, ctx, errors, decos) {
|
parse(header, body, ctx, f) {
|
||||||
let body = body!(opt: body, ctx, errors, decos);
|
let body = body!(opt: body, ctx, f);
|
||||||
let weight = header.args.pos.get::<Spanned<(FontWeight, bool)>>(errors)
|
let weight = header.args.pos.get::<Spanned<(FontWeight, bool)>>(&mut f.errors)
|
||||||
.map(|Spanned { v: (weight, is_clamped), span }| {
|
.map(|Spanned { v: (weight, is_clamped), span }| {
|
||||||
if is_clamped {
|
if is_clamped {
|
||||||
errors.push(err!(@Warning: span;
|
f.errors.push(err!(@Warning: span;
|
||||||
"weight should be between \
|
"weight should be between \
|
||||||
100 and 900, clamped to {}", weight.0));
|
100 and 900, clamped to {}", weight.0));
|
||||||
}
|
}
|
||||||
|
|
||||||
weight
|
weight
|
||||||
})
|
})
|
||||||
.or_missing(errors, header.name.span, "weight");
|
.or_missing(&mut f.errors, header.name.span, "weight");
|
||||||
|
|
||||||
FontWeightFunc { body, weight }
|
FontWeightFunc { body, weight }
|
||||||
}
|
}
|
||||||
@ -88,11 +88,11 @@ function! {
|
|||||||
size: Option<FSize>,
|
size: Option<FSize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
parse(header, body, ctx, errors, decos) {
|
parse(header, body, ctx, f) {
|
||||||
FontSizeFunc {
|
FontSizeFunc {
|
||||||
body: body!(opt: body, ctx, errors, decos),
|
body: body!(opt: body, ctx, f),
|
||||||
size: header.args.pos.get::<FSize>(errors)
|
size: header.args.pos.get::<FSize>(&mut f.errors)
|
||||||
.or_missing(errors, header.name.span, "size")
|
.or_missing(&mut f.errors, header.name.span, "size")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,17 +10,17 @@ function! {
|
|||||||
map: PosAxisMap<AlignmentValue>,
|
map: PosAxisMap<AlignmentValue>,
|
||||||
}
|
}
|
||||||
|
|
||||||
parse(header, body, ctx, errors, decos) {
|
parse(header, body, ctx, f) {
|
||||||
AlignFunc {
|
AlignFunc {
|
||||||
body: body!(opt: body, ctx, errors, decos),
|
body: body!(opt: body, ctx, f),
|
||||||
map: PosAxisMap::parse::<AxisKey>(errors, &mut header.args),
|
map: PosAxisMap::parse::<AxisKey>(&mut f.errors, &mut header.args),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
layout(self, ctx, errors) {
|
layout(self, ctx, f) {
|
||||||
ctx.base = ctx.spaces[0].dimensions;
|
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))
|
alignment.axis().map(|s| s.to_generic(ctx.axes))
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -29,7 +29,7 @@ function! {
|
|||||||
if let Some(generic) = alignment.to_generic(ctx.axes, axis) {
|
if let Some(generic) = alignment.to_generic(ctx.axes, axis) {
|
||||||
*ctx.alignment.get_mut(axis) = generic;
|
*ctx.alignment.get_mut(axis) = generic;
|
||||||
} else {
|
} else {
|
||||||
errors.push(err!(span;
|
f.errors.push(err!(span;
|
||||||
"invalid alignment `{}` for {} axis", alignment, axis));
|
"invalid alignment `{}` for {} axis", alignment, axis));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -38,7 +38,7 @@ function! {
|
|||||||
match &self.body {
|
match &self.body {
|
||||||
Some(body) => {
|
Some(body) => {
|
||||||
let layouted = layout(body, ctx).await;
|
let layouted = layout(body, ctx).await;
|
||||||
errors.extend(layouted.errors);
|
f.extend(layouted.feedback);
|
||||||
vec![AddMultiple(layouted.output)]
|
vec![AddMultiple(layouted.output)]
|
||||||
}
|
}
|
||||||
None => vec![SetAlignment(ctx.alignment)],
|
None => vec![SetAlignment(ctx.alignment)],
|
||||||
@ -55,18 +55,18 @@ function! {
|
|||||||
map: PosAxisMap<Direction>,
|
map: PosAxisMap<Direction>,
|
||||||
}
|
}
|
||||||
|
|
||||||
parse(header, body, ctx, errors, decos) {
|
parse(header, body, ctx, f) {
|
||||||
DirectionFunc {
|
DirectionFunc {
|
||||||
name_span: header.name.span,
|
name_span: header.name.span,
|
||||||
body: body!(opt: body, ctx, errors, decos),
|
body: body!(opt: body, ctx, f),
|
||||||
map: PosAxisMap::parse::<AxisKey>(errors, &mut header.args),
|
map: PosAxisMap::parse::<AxisKey>(&mut f.errors, &mut header.args),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
layout(self, ctx, errors) {
|
layout(self, ctx, f) {
|
||||||
ctx.base = ctx.spaces[0].dimensions;
|
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))
|
Some(direction.axis().to_generic(ctx.axes))
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -76,7 +76,7 @@ function! {
|
|||||||
map.with(Secondary, |&dir| axes.secondary = dir);
|
map.with(Secondary, |&dir| axes.secondary = dir);
|
||||||
|
|
||||||
if axes.primary.axis() == axes.secondary.axis() {
|
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: `{}`, `{}`",
|
"invalid aligned primary and secondary axes: `{}`, `{}`",
|
||||||
ctx.axes.primary, ctx.axes.secondary));
|
ctx.axes.primary, ctx.axes.secondary));
|
||||||
} else {
|
} else {
|
||||||
@ -86,7 +86,7 @@ function! {
|
|||||||
match &self.body {
|
match &self.body {
|
||||||
Some(body) => {
|
Some(body) => {
|
||||||
let layouted = layout(body, ctx).await;
|
let layouted = layout(body, ctx).await;
|
||||||
errors.extend(layouted.errors);
|
f.extend(layouted.feedback);
|
||||||
vec![AddMultiple(layouted.output)]
|
vec![AddMultiple(layouted.output)]
|
||||||
}
|
}
|
||||||
None => vec![SetAxes(ctx.axes)],
|
None => vec![SetAxes(ctx.axes)],
|
||||||
@ -103,15 +103,15 @@ function! {
|
|||||||
debug: Option<bool>,
|
debug: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
parse(header, body, ctx, errors, decos) {
|
parse(header, body, ctx, f) {
|
||||||
BoxFunc {
|
BoxFunc {
|
||||||
body: body!(opt: body, ctx, errors, decos).unwrap_or(SyntaxModel::new()),
|
body: body!(opt: body, ctx, f).unwrap_or(SyntaxModel::new()),
|
||||||
extents: AxisMap::parse::<ExtentKey>(errors, &mut header.args.key),
|
extents: AxisMap::parse::<ExtentKey>(&mut f.errors, &mut header.args.key),
|
||||||
debug: header.args.key.get::<bool>(errors, "debug"),
|
debug: header.args.key.get::<bool>(&mut f.errors, "debug"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
layout(self, ctx, errors) {
|
layout(self, ctx, f) {
|
||||||
ctx.repeat = false;
|
ctx.repeat = false;
|
||||||
ctx.spaces.truncate(1);
|
ctx.spaces.truncate(1);
|
||||||
|
|
||||||
@ -119,7 +119,7 @@ function! {
|
|||||||
ctx.debug = debug;
|
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] {
|
for &axis in &[Horizontal, Vertical] {
|
||||||
if let Some(psize) = map.get(axis) {
|
if let Some(psize) = map.get(axis) {
|
||||||
let size = psize.scaled(ctx.base.get(axis));
|
let size = psize.scaled(ctx.base.get(axis));
|
||||||
@ -131,7 +131,7 @@ function! {
|
|||||||
|
|
||||||
let layouted = layout(&self.body, ctx).await;
|
let layouted = layout(&self.body, ctx).await;
|
||||||
let layout = layouted.output.into_iter().next().unwrap();
|
let layout = layouted.output.into_iter().next().unwrap();
|
||||||
errors.extend(layouted.errors);
|
f.extend(layouted.feedback);
|
||||||
|
|
||||||
vec![Add(layout)]
|
vec![Add(layout)]
|
||||||
}
|
}
|
||||||
|
@ -53,8 +53,8 @@ function! {
|
|||||||
body: Option<SyntaxModel>,
|
body: Option<SyntaxModel>,
|
||||||
}
|
}
|
||||||
|
|
||||||
parse(header, body, ctx, errors, decos) {
|
parse(header, body, ctx, f) {
|
||||||
ValFunc { body: body!(opt: body, ctx, errors, decos) }
|
ValFunc { body: body!(opt: body, ctx, f) }
|
||||||
}
|
}
|
||||||
|
|
||||||
layout(self, ctx, errors) {
|
layout(self, ctx, errors) {
|
||||||
|
@ -12,16 +12,16 @@ function! {
|
|||||||
flip: bool,
|
flip: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
parse(header, body, ctx, errors, decos) {
|
parse(header, body, ctx, f) {
|
||||||
body!(nope: body, errors);
|
body!(nope: body, f);
|
||||||
PageSizeFunc {
|
PageSizeFunc {
|
||||||
paper: header.args.pos.get::<Paper>(errors),
|
paper: header.args.pos.get::<Paper>(&mut f.errors),
|
||||||
extents: AxisMap::parse::<ExtentKey>(errors, &mut header.args.key),
|
extents: AxisMap::parse::<ExtentKey>(&mut f.errors, &mut header.args.key),
|
||||||
flip: header.args.key.get::<bool>(errors, "flip").unwrap_or(false),
|
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;
|
let mut style = ctx.style.page;
|
||||||
|
|
||||||
if let Some(paper) = self.paper {
|
if let Some(paper) = self.paper {
|
||||||
@ -31,7 +31,7 @@ function! {
|
|||||||
style.class = PaperClass::Custom;
|
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(Horizontal, |&width| style.dimensions.x = width);
|
||||||
map.with(Vertical, |&height| style.dimensions.y = height);
|
map.with(Vertical, |&height| style.dimensions.y = height);
|
||||||
|
|
||||||
@ -50,16 +50,16 @@ function! {
|
|||||||
padding: PaddingMap,
|
padding: PaddingMap,
|
||||||
}
|
}
|
||||||
|
|
||||||
parse(header, body, ctx, errors, decos) {
|
parse(header, body, ctx, f) {
|
||||||
body!(nope: body, errors);
|
body!(nope: body, f);
|
||||||
PageMarginsFunc {
|
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;
|
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)]
|
vec![SetPageStyle(style)]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -46,13 +46,13 @@ function! {
|
|||||||
|
|
||||||
type Meta = ContentKind;
|
type Meta = ContentKind;
|
||||||
|
|
||||||
parse(header, body, ctx, errors, decos, meta) {
|
parse(header, body, ctx, f, meta) {
|
||||||
ContentSpacingFunc {
|
ContentSpacingFunc {
|
||||||
body: body!(opt: body, ctx, errors, decos),
|
body: body!(opt: body, ctx, f),
|
||||||
content: meta,
|
content: meta,
|
||||||
spacing: header.args.pos.get::<f64>(errors)
|
spacing: header.args.pos.get::<f64>(&mut f.errors)
|
||||||
.map(|num| num as f32)
|
.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>;
|
type Meta = Option<SpecificAxis>;
|
||||||
|
|
||||||
parse(header, body, ctx, errors, decos, meta) {
|
parse(header, body, ctx, f, meta) {
|
||||||
body!(nope: body, errors);
|
body!(nope: body, f);
|
||||||
SpacingFunc {
|
SpacingFunc {
|
||||||
spacing: if let Some(axis) = meta {
|
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))
|
.map(|s| (AxisKey::Specific(axis), s))
|
||||||
} else {
|
} else {
|
||||||
header.args.key.get_with_key::<AxisKey, FSize>(errors)
|
header.args.key.get_with_key::<AxisKey, FSize>(&mut f.errors)
|
||||||
}.or_missing(errors, header.name.span, "spacing"),
|
}.or_missing(&mut f.errors, header.name.span, "spacing"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,7 +5,8 @@ use std::fmt::Debug;
|
|||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use serde::Serialize;
|
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};
|
use self::span::{Spanned, SpanVec};
|
||||||
|
|
||||||
pub mod expr;
|
pub mod expr;
|
||||||
@ -25,10 +26,7 @@ mod test;
|
|||||||
pub trait Model: Debug + ModelBounds {
|
pub trait Model: Debug + ModelBounds {
|
||||||
/// Layout the model into a sequence of commands processed by a
|
/// Layout the model into a sequence of commands processed by a
|
||||||
/// [`ModelLayouter`](crate::layout::ModelLayouter).
|
/// [`ModelLayouter`](crate::layout::ModelLayouter).
|
||||||
async fn layout<'a>(
|
async fn layout<'a>(&'a self, ctx: LayoutContext<'_>) -> Pass<Commands<'a>>;
|
||||||
&'a self,
|
|
||||||
ctx: LayoutContext<'_>,
|
|
||||||
) -> Layouted<Commands<'a>>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A tree representation of source code.
|
/// A tree representation of source code.
|
||||||
@ -52,14 +50,8 @@ impl SyntaxModel {
|
|||||||
|
|
||||||
#[async_trait(?Send)]
|
#[async_trait(?Send)]
|
||||||
impl Model for SyntaxModel {
|
impl Model for SyntaxModel {
|
||||||
async fn layout<'a>(
|
async fn layout<'a>(&'a self, _: LayoutContext<'_>) -> Pass<Commands<'a>> {
|
||||||
&'a self,
|
Pass::new(vec![Command::LayoutSyntaxModel(self)], Feedback::new())
|
||||||
_: LayoutContext<'_>,
|
|
||||||
) -> Layouted<Commands<'a>> {
|
|
||||||
Layouted {
|
|
||||||
output: vec![Command::LayoutSyntaxModel(self)],
|
|
||||||
errors: vec![],
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
//! Parsing of source code into syntax models.
|
//! Parsing of source code into syntax models.
|
||||||
|
|
||||||
use crate::error::Errors;
|
use crate::{Pass, Feedback};
|
||||||
use super::expr::*;
|
use super::expr::*;
|
||||||
use super::func::{FuncHeader, FuncArgs, FuncArg};
|
use super::func::{FuncHeader, FuncArgs, FuncArg};
|
||||||
use super::scope::Scope;
|
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::tokens::{Token, Tokens, TokenizationMode};
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
@ -16,36 +16,12 @@ pub struct ParseContext<'a> {
|
|||||||
pub scope: &'a Scope,
|
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.
|
/// Parse source code into a syntax model.
|
||||||
///
|
///
|
||||||
/// All errors and decorations are offset by the `start` position.
|
/// 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 model = SyntaxModel::new();
|
||||||
let mut errors = Vec::new();
|
let mut feedback = Feedback::new();
|
||||||
let mut decorations = Vec::new();
|
|
||||||
|
|
||||||
// We always start in body mode. The header tokenization mode is only used
|
// We always start in body mode. The header tokenization mode is only used
|
||||||
// in the `FuncParser`.
|
// in the `FuncParser`.
|
||||||
@ -64,16 +40,12 @@ pub fn parse(start: Position, src: &str, ctx: ParseContext) -> Parsed<SyntaxMode
|
|||||||
},
|
},
|
||||||
|
|
||||||
Token::Function { header, body, terminated } => {
|
Token::Function { header, body, terminated } => {
|
||||||
let parsed: Parsed<Node> = FuncParser::new(header, body, ctx).parse();
|
let parsed = FuncParser::new(header, body, ctx).parse();
|
||||||
|
feedback.extend_offset(span.start, parsed.feedback);
|
||||||
// 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));
|
|
||||||
|
|
||||||
if !terminated {
|
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
|
parsed.output
|
||||||
@ -87,7 +59,7 @@ pub fn parse(start: Position, src: &str, ctx: ParseContext) -> Parsed<SyntaxMode
|
|||||||
Token::LineComment(_) | Token::BlockComment(_) => continue,
|
Token::LineComment(_) | Token::BlockComment(_) => continue,
|
||||||
|
|
||||||
other => {
|
other => {
|
||||||
errors.push(err!(span; "unexpected {}", other.name()));
|
feedback.errors.push(err!(span; "unexpected {}", other.name()));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -95,14 +67,13 @@ pub fn parse(start: Position, src: &str, ctx: ParseContext) -> Parsed<SyntaxMode
|
|||||||
model.add(Spanned { v: node, span: token.span });
|
model.add(Spanned { v: node, span: token.span });
|
||||||
}
|
}
|
||||||
|
|
||||||
Parsed { output: model, errors, decorations }
|
Pass::new(model, feedback)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Performs the function parsing.
|
/// Performs the function parsing.
|
||||||
struct FuncParser<'s> {
|
struct FuncParser<'s> {
|
||||||
ctx: ParseContext<'s>,
|
ctx: ParseContext<'s>,
|
||||||
errors: Errors,
|
feedback: Feedback,
|
||||||
decorations: SpanVec<Decoration>,
|
|
||||||
|
|
||||||
/// ```typst
|
/// ```typst
|
||||||
/// [tokens][body]
|
/// [tokens][body]
|
||||||
@ -129,8 +100,7 @@ impl<'s> FuncParser<'s> {
|
|||||||
) -> FuncParser<'s> {
|
) -> FuncParser<'s> {
|
||||||
FuncParser {
|
FuncParser {
|
||||||
ctx,
|
ctx,
|
||||||
errors: vec![],
|
feedback: Feedback::new(),
|
||||||
decorations: vec![],
|
|
||||||
tokens: Tokens::new(Position::new(0, 1), header, TokenizationMode::Header),
|
tokens: Tokens::new(Position::new(0, 1), header, TokenizationMode::Header),
|
||||||
peeked: None,
|
peeked: None,
|
||||||
body,
|
body,
|
||||||
@ -138,7 +108,7 @@ impl<'s> FuncParser<'s> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Do the parsing.
|
/// 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 parsed = if let Some(header) = self.parse_func_header() {
|
||||||
let name = header.name.v.as_str();
|
let name = header.name.v.as_str();
|
||||||
let (parser, deco) = match self.ctx.scope.get_parser(name) {
|
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.
|
// The fallback parser was returned. Invalid function.
|
||||||
Err(parser) => {
|
Err(parser) => {
|
||||||
self.errors.push(err!(header.name.span; "unknown function"));
|
self.feedback.errors.push(err!(header.name.span; "unknown function"));
|
||||||
(parser, Decoration::InvalidFuncName)
|
(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)
|
parser(header, self.body, self.ctx)
|
||||||
} else {
|
} else {
|
||||||
@ -166,14 +136,9 @@ impl<'s> FuncParser<'s> {
|
|||||||
self.ctx.scope.get_fallback_parser()(default, self.body, self.ctx)
|
self.ctx.scope.get_fallback_parser()(default, self.body, self.ctx)
|
||||||
};
|
};
|
||||||
|
|
||||||
self.errors.extend(parsed.errors);
|
self.feedback.extend(parsed.feedback);
|
||||||
self.decorations.extend(parsed.decorations);
|
|
||||||
|
|
||||||
Parsed {
|
Pass::new(Node::Model(parsed.output), self.feedback)
|
||||||
output: Node::Model(parsed.output),
|
|
||||||
errors: self.errors,
|
|
||||||
decorations: self.decorations,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse the header tokens.
|
/// Parse the header tokens.
|
||||||
@ -235,7 +200,7 @@ impl<'s> FuncParser<'s> {
|
|||||||
self.eat();
|
self.eat();
|
||||||
self.skip_whitespace();
|
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| {
|
self.parse_expr().map(|value| {
|
||||||
FuncArg::Key(Pair {
|
FuncArg::Key(Pair {
|
||||||
@ -332,14 +297,14 @@ impl<'s> FuncParser<'s> {
|
|||||||
/// 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.feedback.errors.push(err!(found.span;
|
||||||
"expected {}, found {}", thing, found.v.name()));
|
"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
|
||||||
/// given position.
|
/// given position.
|
||||||
fn expected_at(&mut self, thing: &str, pos: 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
|
/// Add a expected-found-error if `found` is `Some` and an expected-error
|
||||||
@ -434,7 +399,8 @@ mod tests {
|
|||||||
macro_rules! e {
|
macro_rules! e {
|
||||||
($s:expr => [$(($sl:tt:$sc:tt, $el:tt:$ec:tt, $e:expr)),* $(,)?]) => {
|
($s:expr => [$(($sl:tt:$sc:tt, $el:tt:$ec:tt, $e:expr)),* $(,)?]) => {
|
||||||
let ctx = ParseContext { scope: &scope() };
|
let ctx = ParseContext { scope: &scope() };
|
||||||
let errors = parse(Position::ZERO, $s, ctx).errors
|
let errors = parse(Position::ZERO, $s, ctx).feedback
|
||||||
|
.errors
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|s| s.map(|e| e.message))
|
.map(|s| s.map(|e| e.message))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
@ -3,9 +3,10 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fmt::{self, Debug, Formatter};
|
use std::fmt::{self, Debug, Formatter};
|
||||||
|
|
||||||
|
use crate::Pass;
|
||||||
use crate::func::ParseFunc;
|
use crate::func::ParseFunc;
|
||||||
use super::func::FuncHeader;
|
use super::func::FuncHeader;
|
||||||
use super::parsing::{ParseContext, Parsed};
|
use super::parsing::ParseContext;
|
||||||
use super::span::Spanned;
|
use super::span::Spanned;
|
||||||
use super::Model;
|
use super::Model;
|
||||||
|
|
||||||
@ -75,7 +76,7 @@ type Parser = dyn Fn(
|
|||||||
FuncHeader,
|
FuncHeader,
|
||||||
Option<Spanned<&str>>,
|
Option<Spanned<&str>>,
|
||||||
ParseContext,
|
ParseContext,
|
||||||
) -> Parsed<Box<dyn Model>>;
|
) -> Pass<Box<dyn Model>>;
|
||||||
|
|
||||||
fn parser<F>(metadata: <F as ParseFunc>::Meta) -> Box<Parser>
|
fn parser<F>(metadata: <F as ParseFunc>::Meta) -> Box<Parser>
|
||||||
where F: ParseFunc + Model + 'static {
|
where F: ParseFunc + Model + 'static {
|
||||||
|
@ -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
|
/// [Offset](Span::offset) all spans in a vector of spanned things by a start
|
||||||
/// position.
|
/// position.
|
||||||
pub fn offset_spans<T>(vec: SpanVec<T>, start: Position) -> impl Iterator<Item=Spanned<T>> {
|
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)))
|
vec.into_iter()
|
||||||
|
.map(move |s| s.map_span(|span| span.offset(start)))
|
||||||
}
|
}
|
||||||
|
@ -12,13 +12,13 @@ function! {
|
|||||||
pub body: Option<SyntaxModel>,
|
pub body: Option<SyntaxModel>,
|
||||||
}
|
}
|
||||||
|
|
||||||
parse(header, body, ctx, errors, decos) {
|
parse(header, body, ctx, f) {
|
||||||
let cloned = header.clone();
|
let cloned = header.clone();
|
||||||
header.args.pos.items.clear();
|
header.args.pos.items.clear();
|
||||||
header.args.key.pairs.clear();
|
header.args.key.pairs.clear();
|
||||||
DebugFn {
|
DebugFn {
|
||||||
header: cloned,
|
header: cloned,
|
||||||
body: body!(opt: body, ctx, errors, decos),
|
body: body!(opt: body, ctx, f),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ use std::fs::{File, create_dir_all, read_dir, read_to_string};
|
|||||||
use std::io::{BufWriter, Write};
|
use std::io::{BufWriter, Write};
|
||||||
use std::panic;
|
use std::panic;
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
|
use std::time::{Instant, Duration};
|
||||||
|
|
||||||
use futures_executor::block_on;
|
use futures_executor::block_on;
|
||||||
|
|
||||||
@ -55,8 +56,6 @@ fn main() -> DynResult<()> {
|
|||||||
}).ok();
|
}).ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
println!();
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -121,43 +120,40 @@ fn test(name: &str, src: &str) -> DynResult<()> {
|
|||||||
|
|
||||||
/// Compile the source code with the typesetter.
|
/// Compile the source code with the typesetter.
|
||||||
fn compile(typesetter: &Typesetter, src: &str) -> MultiLayout {
|
fn compile(typesetter: &Typesetter, src: &str) -> MultiLayout {
|
||||||
#![allow(unused_variables)]
|
if cfg!(debug_assertions) {
|
||||||
use std::time::Instant;
|
let typeset = block_on(typesetter.typeset(src));
|
||||||
|
let errors = typeset.feedback.errors;
|
||||||
|
|
||||||
// Warmup.
|
if !errors.is_empty() {
|
||||||
#[cfg(not(debug_assertions))]
|
for error in errors {
|
||||||
let warmup = {
|
println!(" {:?} {:?}: {}",
|
||||||
let warmup_start = Instant::now();
|
error.v.severity,
|
||||||
block_on(typesetter.typeset(&src));
|
error.span,
|
||||||
Instant::now() - warmup_start
|
error.v.message
|
||||||
};
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let start = Instant::now();
|
typeset.output
|
||||||
let parsed = typesetter.parse(&src);
|
} else {
|
||||||
let parse = Instant::now() - start;
|
fn measure<T>(f: impl FnOnce() -> T) -> (T, Duration) {
|
||||||
|
let start = Instant::now();
|
||||||
|
let output = f();
|
||||||
|
let duration = Instant::now() - start;
|
||||||
|
(output, duration)
|
||||||
|
};
|
||||||
|
|
||||||
if !parsed.errors.is_empty() {
|
let (_, cold) = measure(|| block_on(typesetter.typeset(src)));
|
||||||
println!("parse errors: {:#?}", parsed.errors);
|
let (model, parse) = measure(|| typesetter.parse(src).output);
|
||||||
}
|
let (layouts, layout) = measure(|| block_on(typesetter.layout(&model)).output);
|
||||||
|
|
||||||
let start_layout = Instant::now();
|
println!(" - cold start: {:?}", cold);
|
||||||
let layouted = block_on(typesetter.layout(&parsed.output));
|
println!(" - warmed up: {:?}", parse + layout);
|
||||||
let layout = Instant::now() - start_layout;
|
|
||||||
let total = Instant::now() - start;
|
|
||||||
|
|
||||||
if !layouted.errors.is_empty() {
|
|
||||||
println!("layout errors: {:#?}", layouted.errors);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(debug_assertions))] {
|
|
||||||
println!(" - cold start: {:?}", warmup);
|
|
||||||
println!(" - warmed up: {:?}", total);
|
|
||||||
println!(" - parsing: {:?}", parse);
|
println!(" - parsing: {:?}", parse);
|
||||||
println!(" - layouting: {:?}", layout);
|
println!(" - layouting: {:?}", layout);
|
||||||
println!();
|
|
||||||
}
|
|
||||||
|
|
||||||
layouted.output
|
layouts
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Command line options.
|
/// Command line options.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user