diff --git a/src/error.rs b/src/error.rs deleted file mode 100644 index 24b1b79cb..000000000 --- a/src/error.rs +++ /dev/null @@ -1,73 +0,0 @@ -//! Errors in source code. -//! -//! There are no fatal errors in _Typst_. The document will always compile and -//! yield a layout. However, this is a best effort process and bad things will -//! still generate errors and warnings. - -use serde::Serialize; -use crate::syntax::span::SpanVec; - - -/// A spanned list of errors. -pub type Errors = SpanVec; - -/// An error that arose in parsing or layouting. -#[derive(Debug, Clone, Eq, PartialEq, Serialize)] -pub struct Error { - /// An error message describing the problem. - pub message: String, - /// How severe / important the error is. - pub severity: Severity, -} - -/// How severe / important an error is. -#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize)] -#[serde(rename_all = "camelCase")] -pub enum Severity { - /// Something in the code is not good. - Warning, - /// Something in the code is wrong! - Error, -} - -impl Error { - /// Create a new error from message and severity. - pub fn new(message: impl Into, severity: Severity) -> Error { - Error { message: message.into(), severity } - } -} - -/// Construct an error with formatted message and optionally severity and / or -/// span. -/// -/// # Examples -/// ``` -/// # use typstc::err; -/// # use typstc::syntax::span::Span; -/// # let span = Span::ZERO; -/// # let value = 0; -/// -/// // With span and default severity `Error`. -/// err!(span; "the wrong {}", value); -/// -/// // With no span and severity `Warning`. -/// err!(@Warning: span; "non-fatal!"); -/// -/// // Without span and default severity. -/// err!("no spans here ..."); -/// ``` -#[macro_export] -macro_rules! err { - (@$severity:ident: $span:expr; $($args:tt)*) => { - $crate::syntax::span::Spanned { v: err!(@$severity: $($args)*), span: $span } - }; - - (@$severity:ident: $($args:tt)*) => { - $crate::error::Error { - message: format!($($args)*), - severity: $crate::error::Severity::$severity, - } - }; - - ($($tts:tt)*) => { err!(@Error: $($tts)*) }; -} diff --git a/src/func.rs b/src/func.rs index 33cc6e838..777e0b26b 100644 --- a/src/func.rs +++ b/src/func.rs @@ -7,7 +7,7 @@ use crate::syntax::span::Spanned; /// Types that are useful for creating your own functions. pub mod prelude { - pub use crate::{function, body, err}; + pub use crate::{function, body, error, warning}; pub use crate::layout::prelude::*; pub use crate::layout::Command::{self, *}; pub use crate::style::{LayoutStyle, PageStyle, TextStyle}; @@ -55,8 +55,8 @@ pub trait ParseFunc { /// /// parse(header, body, ctx, f) { /// let body = body!(opt: body, ctx, f); -/// let hidden = header.args.pos.get::(&mut f.errors) -/// .or_missing(&mut f.errors, header.name.span, "hidden") +/// let hidden = header.args.pos.get::(&mut f.problems) +/// .or_missing(&mut f.problems, header.name.span, "hidden") /// .unwrap_or(false); /// /// HiderFunc { body: if hidden { None } else { body } } @@ -132,7 +132,7 @@ macro_rules! function { let func = $code; for arg in header.args.into_iter() { - feedback.errors.push(err!(arg.span; "unexpected argument")); + error!(@feedback, arg.span, "unexpected argument"); } $crate::Pass::new(func, feedback) @@ -189,7 +189,7 @@ macro_rules! body { (nope: $body:expr, $feedback:expr) => { if let Some(body) = $body { - $feedback.errors.push($crate::err!(body.span; "unexpected body")); + error!(@$feedback, body.span, "unexpected body"); } }; } diff --git a/src/layout/model.rs b/src/layout/model.rs index 2800774a4..3f1c9e7a0 100644 --- a/src/layout/model.rs +++ b/src/layout/model.rs @@ -245,8 +245,10 @@ impl<'a> ModelLayouter<'a> { BreakParagraph => self.layout_paragraph(), BreakPage => { if self.ctx.nested { - self.feedback.errors.push(err!(model_span; - "page break cannot be issued from nested context")); + error!( + @self.feedback, model_span, + "page break cannot be issued from nested context", + ); } else { self.layouter.finish_space(true) } @@ -258,8 +260,10 @@ impl<'a> ModelLayouter<'a> { } SetPageStyle(style) => { if self.ctx.nested { - self.feedback.errors.push(err!(model_span; - "page style cannot be changed from nested context")); + error!( + @self.feedback, model_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 4a92d0960..b140f47da 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -27,7 +27,7 @@ use toddle::{Font, OwnedData}; use toddle::query::{FontLoader, SharedFontLoader}; use toddle::query::{FontProvider, FontIndex, FontDescriptor}; -use crate::error::Error; +use crate::problem::Problems; use crate::layout::MultiLayout; use crate::style::{LayoutStyle, PageStyle, TextStyle}; use crate::syntax::{SyntaxModel, Scope, Decoration, ParseContext, parse}; @@ -43,7 +43,7 @@ macro_rules! pub_use_mod { } #[macro_use] -pub mod error; +pub mod problem; pub mod export; #[macro_use] pub mod func; @@ -175,8 +175,8 @@ impl Pass { /// User feedback data accumulated during a compilation pass. #[derive(Debug, Default, Clone, Eq, PartialEq)] pub struct Feedback { - /// Errors in the source. - pub errors: SpanVec, + /// Problems in the source code. + pub problems: Problems, /// Decorations of the source code for semantic syntax highlighting. pub decos: SpanVec, } @@ -185,7 +185,7 @@ impl Feedback { /// Create a new feedback instance without errors and decos. pub fn new() -> Feedback { Feedback { - errors: vec![], + problems: vec![], decos: vec![], } } @@ -198,14 +198,14 @@ impl Feedback { /// Add other feedback data to this feedback. pub fn extend(&mut self, other: Feedback) { - self.errors.extend(other.errors); + self.problems.extend(other.problems); 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.problems.extend(offset_spans(offset, other.problems)); self.decos.extend(offset_spans(offset, other.decos)); } } diff --git a/src/library/font.rs b/src/library/font.rs index ba778693f..9f7902f6f 100644 --- a/src/library/font.rs +++ b/src/library/font.rs @@ -13,17 +13,17 @@ function! { } parse(header, body, ctx, f) { - let list = header.args.pos.get_all::(&mut f.errors) + let list = header.args.pos.get_all::(&mut f.problems) .map(|s| s.0.to_lowercase()) .collect(); let tuples: Vec<_> = header.args.key - .get_all::(&mut f.errors) + .get_all::(&mut f.problems) .collect(); let classes = tuples.into_iter() .map(|(class, mut tuple)| { - let fallback = tuple.get_all::(&mut f.errors) + let fallback = tuple.get_all::(&mut f.problems) .map(|s| s.0.to_lowercase()) .collect(); (class.to_lowercase(), fallback) @@ -37,7 +37,7 @@ function! { } } - layout(self, ctx, errors) { + layout(self, ctx, f) { styled(&self.body, ctx, Some(()), |s, _| { if !self.list.is_empty() { @@ -64,12 +64,12 @@ function! { parse(header, body, ctx, f) { FontStyleFunc { body: body!(opt: body, ctx, f), - style: header.args.pos.get::(&mut f.errors) - .or_missing(&mut f.errors, header.name.span, "style"), + style: header.args.pos.get::(&mut f.problems) + .or_missing(&mut f.problems, header.name.span, "style"), } } - layout(self, ctx, errors) { + layout(self, ctx, f) { styled(&self.body, ctx, self.style, |t, s| t.variant.style = s) } } @@ -84,22 +84,24 @@ function! { parse(header, body, ctx, f) { let body = body!(opt: body, ctx, f); - let weight = header.args.pos.get::>(&mut f.errors) + let weight = header.args.pos.get::>(&mut f.problems) .map(|Spanned { v: (weight, is_clamped), span }| { if is_clamped { - f.errors.push(err!(@Warning: span; - "weight should be between \ - 100 and 900, clamped to {}", weight.0)); + warning!( + @f, span, + "weight should be between 100 and 900, clamped to {}", + weight.0, + ); } weight }) - .or_missing(&mut f.errors, header.name.span, "weight"); + .or_missing(&mut f.problems, header.name.span, "weight"); FontWeightFunc { body, weight } } - layout(self, ctx, errors) { + layout(self, ctx, f) { styled(&self.body, ctx, self.weight, |t, w| t.variant.weight = w) } } @@ -115,12 +117,12 @@ function! { parse(header, body, ctx, f) { FontSizeFunc { body: body!(opt: body, ctx, f), - size: header.args.pos.get::(&mut f.errors) - .or_missing(&mut f.errors, header.name.span, "size") + size: header.args.pos.get::(&mut f.problems) + .or_missing(&mut f.problems, header.name.span, "size") } } - layout(self, ctx, errors) { + layout(self, ctx, f) { styled(&self.body, ctx, self.size, |t, s| { match s { FSize::Absolute(size) => { diff --git a/src/library/layout.rs b/src/library/layout.rs index da1652b06..9fac37b7a 100644 --- a/src/library/layout.rs +++ b/src/library/layout.rs @@ -13,14 +13,14 @@ function! { parse(header, body, ctx, f) { AlignFunc { body: body!(opt: body, ctx, f), - map: PosAxisMap::parse::(&mut f.errors, &mut header.args), + map: PosAxisMap::parse::(&mut f.problems, &mut header.args), } } layout(self, ctx, f) { ctx.base = ctx.spaces[0].dimensions; - let map = self.map.dedup(&mut f.errors, ctx.axes, |alignment| { + let map = self.map.dedup(&mut f.problems, ctx.axes, |alignment| { alignment.axis().map(|s| s.to_generic(ctx.axes)) }); @@ -29,8 +29,10 @@ function! { if let Some(generic) = alignment.to_generic(ctx.axes, axis) { *ctx.alignment.get_mut(axis) = generic; } else { - f.errors.push(err!(span; - "invalid alignment `{}` for {} axis", alignment, axis)); + error!( + @f, span, + "invalid alignment `{}` for {} axis", alignment, axis, + ); } } } @@ -59,14 +61,14 @@ function! { DirectionFunc { name_span: header.name.span, body: body!(opt: body, ctx, f), - map: PosAxisMap::parse::(&mut f.errors, &mut header.args), + map: PosAxisMap::parse::(&mut f.problems, &mut header.args), } } layout(self, ctx, f) { ctx.base = ctx.spaces[0].dimensions; - let map = self.map.dedup(&mut f.errors, ctx.axes, |direction| { + let map = self.map.dedup(&mut f.problems, ctx.axes, |direction| { Some(direction.axis().to_generic(ctx.axes)) }); @@ -76,9 +78,11 @@ function! { map.with(Secondary, |&dir| axes.secondary = dir); if axes.primary.axis() == axes.secondary.axis() { - f.errors.push(err!(self.name_span; + error!( + @f, self.name_span, "invalid aligned primary and secondary axes: `{}`, `{}`", - ctx.axes.primary, ctx.axes.secondary)); + ctx.axes.primary, ctx.axes.secondary, + ); } else { ctx.axes = axes; } @@ -106,8 +110,8 @@ function! { parse(header, body, ctx, f) { BoxFunc { body: body!(opt: body, ctx, f).unwrap_or(SyntaxModel::new()), - extents: AxisMap::parse::(&mut f.errors, &mut header.args.key), - debug: header.args.key.get::(&mut f.errors, "debug"), + extents: AxisMap::parse::(&mut f.problems, &mut header.args.key), + debug: header.args.key.get::(&mut f.problems, "debug"), } } @@ -119,7 +123,7 @@ function! { ctx.debug = debug; } - let map = self.extents.dedup(&mut f.errors, ctx.axes); + let map = self.extents.dedup(&mut f.problems, ctx.axes); for &axis in &[Horizontal, Vertical] { if let Some(psize) = map.get(axis) { let size = psize.scaled(ctx.base.get(axis)); diff --git a/src/library/mod.rs b/src/library/mod.rs index 08608d05d..c8ca374df 100644 --- a/src/library/mod.rs +++ b/src/library/mod.rs @@ -59,7 +59,7 @@ function! { ValFunc { body: body!(opt: body, ctx, f) } } - layout(self, ctx, errors) { + layout(self, ctx, f) { match &self.body { Some(model) => vec![LayoutSyntaxModel(model)], None => vec![], diff --git a/src/library/page.rs b/src/library/page.rs index 07e430264..4d92ea919 100644 --- a/src/library/page.rs +++ b/src/library/page.rs @@ -15,9 +15,9 @@ function! { parse(header, body, ctx, f) { body!(nope: body, f); PageSizeFunc { - paper: header.args.pos.get::(&mut f.errors), - extents: AxisMap::parse::(&mut f.errors, &mut header.args.key), - flip: header.args.key.get::(&mut f.errors, "flip").unwrap_or(false), + paper: header.args.pos.get::(&mut f.problems), + extents: AxisMap::parse::(&mut f.problems, &mut header.args.key), + flip: header.args.key.get::(&mut f.problems, "flip").unwrap_or(false), } } @@ -31,7 +31,7 @@ function! { style.class = PaperClass::Custom; } - let map = self.extents.dedup(&mut f.errors, ctx.axes); + let map = self.extents.dedup(&mut f.problems, ctx.axes); map.with(Horizontal, |&width| style.dimensions.x = width); map.with(Vertical, |&height| style.dimensions.y = height); @@ -53,13 +53,13 @@ function! { parse(header, body, ctx, f) { body!(nope: body, f); PageMarginsFunc { - padding: PaddingMap::parse(&mut f.errors, &mut header.args), + padding: PaddingMap::parse(&mut f.problems, &mut header.args), } } layout(self, ctx, f) { let mut style = ctx.style.page; - self.padding.apply(&mut f.errors, ctx.axes, &mut style.margins); + self.padding.apply(&mut f.problems, ctx.axes, &mut style.margins); vec![SetPageStyle(style)] } } diff --git a/src/library/spacing.rs b/src/library/spacing.rs index a6db162a8..8d9c46aa7 100644 --- a/src/library/spacing.rs +++ b/src/library/spacing.rs @@ -11,7 +11,7 @@ function! { pub struct LineBreakFunc; parse(default) - layout(self, ctx, errors) { vec![BreakLine] } + layout(self, ctx, f) { vec![BreakLine] } } function! { @@ -22,7 +22,7 @@ function! { pub struct ParBreakFunc; parse(default) - layout(self, ctx, errors) { vec![BreakParagraph] } + layout(self, ctx, f) { vec![BreakParagraph] } } function! { @@ -31,7 +31,7 @@ function! { pub struct PageBreakFunc; parse(default) - layout(self, ctx, errors) { vec![BreakPage] } + layout(self, ctx, f) { vec![BreakPage] } } function! { @@ -50,13 +50,13 @@ function! { ContentSpacingFunc { body: body!(opt: body, ctx, f), content: meta, - spacing: header.args.pos.get::(&mut f.errors) + spacing: header.args.pos.get::(&mut f.problems) .map(|num| num as f32) - .or_missing(&mut f.errors, header.name.span, "spacing"), + .or_missing(&mut f.problems, header.name.span, "spacing"), } } - layout(self, ctx, errors) { + layout(self, ctx, f) { styled(&self.body, ctx, self.spacing, |t, s| match self.content { Word => t.word_spacing_scale = s, Line => t.line_spacing_scale = s, @@ -88,15 +88,15 @@ function! { body!(nope: body, f); SpacingFunc { spacing: if let Some(axis) = meta { - header.args.pos.get::(&mut f.errors) + header.args.pos.get::(&mut f.problems) .map(|s| (AxisKey::Specific(axis), s)) } else { - header.args.key.get_with_key::(&mut f.errors) - }.or_missing(&mut f.errors, header.name.span, "spacing"), + header.args.key.get_with_key::(&mut f.problems) + }.or_missing(&mut f.problems, header.name.span, "spacing"), } } - layout(self, ctx, errors) { + layout(self, ctx, f) { if let Some((axis, spacing)) = self.spacing { let axis = axis.to_generic(ctx.axes); let spacing = spacing.scaled(ctx.style.text.font_size()); diff --git a/src/problem.rs b/src/problem.rs new file mode 100644 index 000000000..3c2d3635d --- /dev/null +++ b/src/problem.rs @@ -0,0 +1,94 @@ +//! Problems (errors / warnings) in _Typst_ documents. +//! +//! There are no fatal errors in _Typst_. The document will always compile and +//! yield a layout. However, this is a best effort process and bad things will +//! still generate errors and warnings. + +use serde::Serialize; +use crate::syntax::span::SpanVec; + + +/// A list of spanned problems. +pub type Problems = SpanVec; + +/// A problem that arose in parsing or layouting. +#[derive(Debug, Clone, Eq, PartialEq, Serialize)] +pub struct Problem { + /// How severe / important the problem is. + pub severity: Severity, + /// A message describing the problem. + pub message: String, +} + +/// How severe / important a problem is. +#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize)] +#[serde(rename_all = "camelCase")] +pub enum Severity { + /// Something in the code is not good. + Warning, + /// Something in the code is wrong! + Error, +} + +impl Problem { + /// Create a new problem from message and severity. + pub fn new(message: impl Into, severity: Severity) -> Self { + Self { message: message.into(), severity } + } +} + +/// Construct a problem with `Error` severity. +/// +/// ``` +/// # use typstc::error; +/// # use typstc::syntax::span::Span; +/// # use typstc::Feedback; +/// # let span = Span::ZERO; +/// # let mut feedback = Feedback::new(); +/// # let name = ""; +/// // Create formatted error values. +/// let error = error!("expected {}", name); +/// +/// // Create spanned errors. +/// let spanned = error!(span, "there is an error here"); +/// +/// // Create an error and directly add it to existing feedback. +/// error!(@feedback, span, "oh no!"); +/// ``` +#[macro_export] +macro_rules! error { + ($($tts:tt)*) => { + $crate::__impl_problem!($crate::problem::Severity::Error; $($tts)*) + }; +} + +/// Construct a problem with `Warning` severity. +/// +/// This works exactly like `error!`. See its documentation for more +/// information. +#[macro_export] +macro_rules! warning { + ($($tts:tt)*) => { + $crate::__impl_problem!($crate::problem::Severity::Warning; $($tts)*) + }; +} + +/// Backs the `error!` and `warning!` macros. +#[macro_export] +#[doc(hidden)] +macro_rules! __impl_problem { + ($severity:expr; @$feedback:expr, $($tts:tt)*) => { + $feedback.problems.push($crate::__impl_problem!($severity; $($tts)*)); + }; + + ($severity:expr; $fmt:literal $($tts:tt)*) => { + $crate::problem::Problem::new(format!($fmt $($tts)*), $severity) + }; + + ($severity:expr; $span:expr, $fmt:literal $($tts:tt)*) => { + $crate::syntax::span::Spanned::new( + $crate::__impl_problem!($severity; $fmt $($tts)*), + $span, + ) + }; +} diff --git a/src/syntax/expr.rs b/src/syntax/expr.rs index 1dc47d03a..a27bdb62f 100644 --- a/src/syntax/expr.rs +++ b/src/syntax/expr.rs @@ -6,7 +6,7 @@ use std::ops::Deref; use std::str::FromStr; use std::u8; -use crate::error::Errors; +use crate::problem::Problems; use crate::size::Size; use super::func::{Key, Value}; use super::span::{Span, Spanned}; @@ -258,28 +258,28 @@ impl Tuple { } /// Extract (and remove) the first matching value and remove and generate - /// errors for all previous items that did not match. - pub fn get(&mut self, errors: &mut Errors) -> Option { + /// problems for all previous items that did not match. + pub fn get(&mut self, problems: &mut Problems) -> Option { while !self.items.is_empty() { let expr = self.items.remove(0); let span = expr.span; match V::parse(expr) { Ok(output) => return Some(output), - Err(err) => errors.push(Spanned { v: err, span }), + Err(v) => problems.push(Spanned { v, span }), } } None } /// Extract and return an iterator over all values that match and generate - /// errors for all items that do not match. - pub fn get_all<'a, V: Value>(&'a mut self, errors: &'a mut Errors) + /// problems for all items that do not match. + pub fn get_all<'a, V: Value>(&'a mut self, problems: &'a mut Problems) -> impl Iterator + 'a { self.items.drain(..).filter_map(move |expr| { let span = expr.span; match V::parse(expr) { Ok(output) => Some(output), - Err(err) => { errors.push(Spanned { v: err, span }); None } + Err(v) => { problems.push(Spanned { v, span }); None } } }) } @@ -400,9 +400,9 @@ impl Object { /// /// Inserts an error if the value does not match. If the key is not /// contained, no error is inserted. - pub fn get(&mut self, errors: &mut Errors, key: &str) -> Option { + pub fn get(&mut self, problems: &mut Problems, key: &str) -> Option { let index = self.pairs.iter().position(|pair| pair.v.key.v.as_str() == key)?; - self.get_index::(errors, index) + self.get_index::(problems, index) } /// Extract (and remove) a pair with a matching key and value. @@ -411,12 +411,12 @@ impl Object { /// found, no error is inserted. pub fn get_with_key( &mut self, - errors: &mut Errors, + problems: &mut Problems, ) -> Option<(K, V)> { for (index, pair) in self.pairs.iter().enumerate() { let key = Spanned { v: pair.v.key.v.as_str(), span: pair.v.key.span }; if let Some(key) = K::parse(key) { - return self.get_index::(errors, index).map(|value| (key, value)); + return self.get_index::(problems, index).map(|value| (key, value)); } } None @@ -427,7 +427,7 @@ impl Object { /// Inserts errors for values that do not match. pub fn get_all<'a, K: Key, V: Value>( &'a mut self, - errors: &'a mut Errors, + problems: &'a mut Problems, ) -> impl Iterator + 'a { let mut index = 0; std::iter::from_fn(move || { @@ -436,7 +436,7 @@ impl Object { let key = Spanned { v: key.v.as_str(), span: key.span }; Some(if let Some(key) = K::parse(key) { - self.get_index::(errors, index).map(|v| (key, v)) + self.get_index::(problems, index).map(|v| (key, v)) } else { index += 1; None @@ -456,20 +456,20 @@ impl Object { /// ``` pub fn get_all_spanned<'a, K: Key + 'a, V: Value + 'a>( &'a mut self, - errors: &'a mut Errors, + problems: &'a mut Problems, ) -> impl Iterator> + 'a { - self.get_all::, Spanned>(errors) + self.get_all::, Spanned>(problems) .map(|(k, v)| Spanned::new((k.v, v.v), Span::merge(k.span, v.span))) } /// Extract the argument at the given index and insert an error if the value /// does not match. - fn get_index(&mut self, errors: &mut Errors, index: usize) -> Option { + fn get_index(&mut self, problems: &mut Problems, index: usize) -> Option { let expr = self.pairs.remove(index).v.value; let span = expr.span; match V::parse(expr) { Ok(output) => Some(output), - Err(err) => { errors.push(Spanned { v: err, span }); None } + Err(v) => { problems.push(Spanned { v, span }); None } } } diff --git a/src/syntax/func/maps.rs b/src/syntax/func/maps.rs index 8143d0a5c..44d8e1aa9 100644 --- a/src/syntax/func/maps.rs +++ b/src/syntax/func/maps.rs @@ -1,6 +1,6 @@ //! Deduplicating maps and keys for argument parsing. -use crate::error::Errors; +use crate::problem::Problems; use crate::layout::prelude::*; use crate::size::{PSize, ValueBox}; use crate::syntax::span::Spanned; @@ -12,7 +12,7 @@ use super::*; /// A map which deduplicates redundant arguments. /// /// Whenever a duplicate argument is inserted into the map, through the -/// functions `from_iter`, `insert` or `extend` an errors is added to the error +/// functions `from_iter`, `insert` or `extend` an problems is added to the error /// list that needs to be passed to those functions. /// /// All entries need to have span information to enable the error reporting. @@ -28,27 +28,27 @@ impl DedupMap where K: Eq { } /// Create a new map from an iterator of spanned keys and values. - pub fn from_iter(errors: &mut Errors, iter: I) -> DedupMap + pub fn from_iter(problems: &mut Problems, iter: I) -> DedupMap where I: IntoIterator> { let mut map = DedupMap::new(); - map.extend(errors, iter); + map.extend(problems, iter); map } /// Add a spanned key-value pair. - pub fn insert(&mut self, errors: &mut Errors, entry: Spanned<(K, V)>) { + pub fn insert(&mut self, problems: &mut Problems, entry: Spanned<(K, V)>) { if self.map.iter().any(|e| e.v.0 == entry.v.0) { - errors.push(err!(entry.span; "duplicate argument")); + problems.push(error!(entry.span, "duplicate argument")); } else { self.map.push(entry); } } /// Add multiple spanned key-value pairs. - pub fn extend(&mut self, errors: &mut Errors, items: I) + pub fn extend(&mut self, problems: &mut Problems, items: I) where I: IntoIterator> { for item in items.into_iter() { - self.insert(errors, item); + self.insert(problems, item); } } @@ -71,15 +71,15 @@ impl DedupMap where K: Eq { } /// Create a new map where keys and values are mapped to new keys and - /// values. When the mapping introduces new duplicates, errors are + /// values. When the mapping introduces new duplicates, problems are /// generated. - pub fn dedup(&self, errors: &mut Errors, mut f: F) -> DedupMap + pub fn dedup(&self, problems: &mut Problems, mut f: F) -> DedupMap where F: FnMut(&K, &V) -> (K2, V2), K2: Eq { let mut map = DedupMap::new(); for Spanned { v: (key, value), span } in self.map.iter() { let (key, value) = f(key, value); - map.insert(errors, Spanned { v: (key, value), span: *span }); + map.insert(problems, Spanned { v: (key, value), span: *span }); } map @@ -98,21 +98,21 @@ pub struct AxisMap(DedupMap); impl AxisMap { /// Parse an axis map from the object. pub fn parse( - errors: &mut Errors, + problems: &mut Problems, object: &mut Object, ) -> AxisMap where K: Key + Into { let values: Vec<_> = object - .get_all_spanned::(errors) + .get_all_spanned::(problems) .map(|s| s.map(|(k, v)| (k.into(), v))) .collect(); - AxisMap(DedupMap::from_iter(errors, values)) + AxisMap(DedupMap::from_iter(problems, values)) } /// Deduplicate from specific or generic to just specific axes. - pub fn dedup(&self, errors: &mut Errors, axes: LayoutAxes) -> DedupMap + pub fn dedup(&self, problems: &mut Problems, axes: LayoutAxes) -> DedupMap where V: Clone { - self.0.dedup(errors, |key, val| (key.to_specific(axes), val.clone())) + self.0.dedup(problems, |key, val| (key.to_specific(axes), val.clone())) } } @@ -124,23 +124,23 @@ pub struct PosAxisMap(DedupMap); impl PosAxisMap { /// Parse a positional/axis map from the function arguments. pub fn parse( - errors: &mut Errors, + problems: &mut Problems, args: &mut FuncArgs, ) -> PosAxisMap where K: Key + Into { let mut map = DedupMap::new(); for &key in &[PosAxisKey::First, PosAxisKey::Second] { - if let Some(Spanned { v, span }) = args.pos.get::>(errors) { - map.insert(errors, Spanned { v: (key, v), span }) + if let Some(Spanned { v, span }) = args.pos.get::>(problems) { + map.insert(problems, Spanned { v: (key, v), span }) } } let keywords: Vec<_> = args.key - .get_all_spanned::(errors) + .get_all_spanned::(problems) .map(|s| s.map(|(k, v)| (PosAxisKey::Keyword(k.into()), v))) .collect(); - map.extend(errors, keywords); + map.extend(problems, keywords); PosAxisMap(map) } @@ -149,7 +149,7 @@ impl PosAxisMap { /// or specific axes to just generic axes. pub fn dedup( &self, - errors: &mut Errors, + problems: &mut Problems, axes: LayoutAxes, mut f: F, ) -> DedupMap @@ -157,7 +157,7 @@ impl PosAxisMap { F: FnMut(&V) -> Option, V: Clone, { - self.0.dedup(errors, |key, val| { + self.0.dedup(problems, |key, val| { (match key { PosAxisKey::First => f(val).unwrap_or(GenericAxis::Primary), PosAxisKey::Second => f(val).unwrap_or(GenericAxis::Secondary), @@ -175,20 +175,20 @@ pub struct PaddingMap(DedupMap, Option>); impl PaddingMap { /// Parse a padding map from the function arguments. - pub fn parse(errors: &mut Errors, args: &mut FuncArgs) -> PaddingMap { + pub fn parse(problems: &mut Problems, args: &mut FuncArgs) -> PaddingMap { let mut map = DedupMap::new(); - let all = args.pos.get::>>(errors); + let all = args.pos.get::>>(problems); if let Some(Spanned { v, span }) = all { - map.insert(errors, Spanned { v: (PaddingKey::All, v.into()), span }); + map.insert(problems, Spanned { v: (PaddingKey::All, v.into()), span }); } let paddings: Vec<_> = args.key - .get_all_spanned::, Defaultable>(errors) + .get_all_spanned::, Defaultable>(problems) .map(|s| s.map(|(k, v)| (k, v.into()))) .collect(); - map.extend(errors, paddings); + map.extend(problems, paddings); PaddingMap(map) } @@ -196,13 +196,13 @@ impl PaddingMap { /// Apply the specified padding on a value box of optional, scalable sizes. pub fn apply( &self, - errors: &mut Errors, + problems: &mut Problems, axes: LayoutAxes, padding: &mut ValueBox> ) { use PaddingKey::*; - let map = self.0.dedup(errors, |key, &val| { + let map = self.0.dedup(problems, |key, &val| { (match key { All => All, Both(axis) => Both(axis.to_specific(axes)), diff --git a/src/syntax/func/mod.rs b/src/syntax/func/mod.rs index fd516208f..d379b4071 100644 --- a/src/syntax/func/mod.rs +++ b/src/syntax/func/mod.rs @@ -1,7 +1,7 @@ //! Primitives for argument parsing in library functions. use std::iter::FromIterator; -use crate::error::{Error, Errors}; +use crate::problem::{Problem, Problems}; use super::expr::{Expr, Ident, Tuple, Object, Pair}; use super::span::{Span, Spanned}; @@ -84,13 +84,13 @@ pub enum FuncArg { pub trait OptionExt: Sized { /// Add an error about a missing argument `arg` with the given span if the /// option is `None`. - fn or_missing(self, errors: &mut Errors, span: Span, arg: &str) -> Self; + fn or_missing(self, problems: &mut Problems, span: Span, arg: &str) -> Self; } impl OptionExt for Option { - fn or_missing(self, errors: &mut Errors, span: Span, arg: &str) -> Self { + fn or_missing(self, problems: &mut Problems, span: Span, arg: &str) -> Self { if self.is_none() { - errors.push(err!(span; "missing argument: {}", arg)); + problems.push(error!(span, "missing argument: {}", arg)); } self } diff --git a/src/syntax/func/values.rs b/src/syntax/func/values.rs index 8e0c24b4f..7a1aa9125 100644 --- a/src/syntax/func/values.rs +++ b/src/syntax/func/values.rs @@ -23,20 +23,20 @@ use self::AlignmentValue::*; /// # Example implementation /// An implementation for `bool` might look as follows: /// ``` -/// # use typstc::err; -/// # use typstc::error::Error; +/// # use typstc::error; +/// # use typstc::problem::Problem; /// # use typstc::syntax::expr::Expr; /// # use typstc::syntax::func::Value; /// # use typstc::syntax::span::Spanned; /// # struct Bool; /* /// impl Value for bool { /// # */ impl Value for Bool { -/// fn parse(expr: Spanned) -> Result { +/// fn parse(expr: Spanned) -> Result { /// match expr.v { /// # /* /// Expr::Bool(b) => Ok(b), /// # */ Expr::Bool(_) => Ok(Bool), -/// other => Err(err!("expected bool, found {}", other.name())), +/// other => Err(error!("expected bool, found {}", other.name())), /// } /// } /// } @@ -44,11 +44,11 @@ use self::AlignmentValue::*; pub trait Value: Sized { /// Parse an expression into this value or return an error if the expression /// is valid for this value type. - fn parse(expr: Spanned) -> Result; + fn parse(expr: Spanned) -> Result; } impl Value for Spanned { - fn parse(expr: Spanned) -> Result { + fn parse(expr: Spanned) -> Result { let span = expr.span; V::parse(expr).map(|v| Spanned { v, span }) } @@ -58,12 +58,13 @@ impl Value for Spanned { macro_rules! value { ($type:ty, $name:expr, $($p:pat => $r:expr),* $(,)?) => { impl Value for $type { - fn parse(expr: Spanned) -> Result { + fn parse(expr: Spanned) -> Result { #[allow(unreachable_patterns)] match expr.v { $($p => Ok($r)),*, - other => Err(err!("expected {}, found {}", - $name, other.name())), + other => Err( + error!("expected {}, found {}", $name, other.name()) + ), } } } @@ -120,7 +121,7 @@ impl From for String { pub struct Defaultable(pub Option); impl Value for Defaultable { - fn parse(expr: Spanned) -> Result { + fn parse(expr: Spanned) -> Result { Ok(Defaultable(match expr.v { Expr::Ident(ident) if ident.as_str() == "default" => None, _ => Some(V::parse(expr)?) @@ -135,16 +136,16 @@ impl From> for Option { } impl Value for FontStyle { - fn parse(expr: Spanned) -> Result { + fn parse(expr: Spanned) -> Result { FontStyle::from_name(Ident::parse(expr)?.as_str()) - .ok_or_else(|| err!("invalid font style")) + .ok_or_else(|| error!("invalid font style")) } } /// The additional boolean specifies whether a number was clamped into the range /// 100 - 900 to make it a valid font weight. impl Value for (FontWeight, bool) { - fn parse(expr: Spanned) -> Result { + fn parse(expr: Spanned) -> Result { match expr.v { Expr::Number(weight) => { let weight = weight.round(); @@ -158,30 +159,31 @@ impl Value for (FontWeight, bool) { } Expr::Ident(id) => { FontWeight::from_name(id.as_str()) - .ok_or_else(|| err!("invalid font weight")) + .ok_or_else(|| error!("invalid font weight")) .map(|weight| (weight, false)) } - other => Err(err!("expected identifier or number, \ - found {}", other.name())), + other => Err( + error!("expected identifier or number, found {}", other.name()) + ), } } } impl Value for Paper { - fn parse(expr: Spanned) -> Result { + fn parse(expr: Spanned) -> Result { Paper::from_name(Ident::parse(expr)?.as_str()) - .ok_or_else(|| err!("invalid paper type")) + .ok_or_else(|| error!("invalid paper type")) } } impl Value for Direction { - fn parse(expr: Spanned) -> Result { + fn parse(expr: Spanned) -> Result { 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")) + _ => return Err(error!("invalid direction")) }) } } @@ -248,7 +250,7 @@ impl AlignmentValue { } impl Value for AlignmentValue { - fn parse(expr: Spanned) -> Result { + fn parse(expr: Spanned) -> Result { Ok(match Ident::parse(expr)?.as_str() { "origin" => Align(Origin), "center" => Align(Center), @@ -257,7 +259,7 @@ impl Value for AlignmentValue { "top" => Top, "right" => Right, "bottom" => Bottom, - _ => return Err(err!("invalid alignment")) + _ => return Err(error!("invalid alignment")) }) } } diff --git a/src/syntax/parsing.rs b/src/syntax/parsing.rs index 9636df210..09405d7fd 100644 --- a/src/syntax/parsing.rs +++ b/src/syntax/parsing.rs @@ -49,8 +49,7 @@ pub fn parse(start: Position, src: &str, ctx: ParseContext) -> Pass feedback.extend_offset(span.start, parsed.feedback); if !terminated { - feedback.errors.push(err!(Span::at(span.end); - "expected closing bracket")); + error!(@feedback, Span::at(span.end), "expected closing bracket"); } parsed.output @@ -62,8 +61,7 @@ pub fn parse(start: Position, src: &str, ctx: ParseContext) -> Pass Token::Raw { raw, terminated } => { if !terminated { - feedback.errors.push(err!(Span::at(span.end); - "expected backtick")); + error!(@feedback, Span::at(span.end), "expected backtick"); } Node::Raw(unescape_raw(raw)) @@ -72,7 +70,7 @@ pub fn parse(start: Position, src: &str, ctx: ParseContext) -> Pass Token::Text(text) => Node::Text(text.to_string()), other => { - feedback.errors.push(err!(span; "unexpected {}", other.name())); + error!(@feedback, span, "unexpected {}", other.name()); continue; } }; @@ -129,7 +127,7 @@ impl<'s> FuncParser<'s> { // The fallback parser was returned. Invalid function. Err(parser) => { - self.feedback.errors.push(err!(header.name.span; "unknown function")); + error!(@self.feedback, header.name.span, "unknown function"); (parser, Decoration::InvalidFuncName) } }; @@ -270,10 +268,10 @@ impl<'s> FuncParser<'s> { let expr = binop(Box::new(o1), Box::new(o2)); return Some(Spanned::new(expr, span)); } else { - self.feedback.errors.push(err!( - Span::merge(next.span, o1.span); + error!( + @self.feedback, Span::merge(next.span, o1.span), "missing right {}", operand_name, - )); + ); } } } @@ -292,7 +290,7 @@ impl<'s> FuncParser<'s> { let span = Span::merge(first.span, factor.span); Some(Spanned::new(Expr::Neg(Box::new(factor)), span)) } else { - self.feedback.errors.push(err!(first.span; "dangling minus")); + error!(@self.feedback, first.span, "dangling minus"); None } } else { @@ -333,7 +331,7 @@ impl<'s> FuncParser<'s> { take!(Expr::Color(color)) } else { // Heal color by assuming black - self.feedback.errors.push(err!(first.span; "invalid color")); + error!(@self.feedback, first.span, "invalid color"); take!(Expr::Color(RgbaColor::new_healed(0, 0, 0, 255))) } }, @@ -517,14 +515,16 @@ 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) { - self.feedback.errors.push(err!(found.span; - "expected {}, found {}", thing, found.v.name())); + error!( + @self.feedback, 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.feedback.errors.push(err!(Span::at(pos); "expected {}", thing)); + error!(@self.feedback, Span::at(pos), "expected {}", thing); } /// Add a expected-found-error if `found` is `Some` and an expected-error @@ -726,7 +726,7 @@ mod tests { p!($source => [$($model)*], []); }; - ($source:expr => [$($model:tt)*], [$($errors:tt)*] $(, [$($decos:tt)*])? $(,)?) => { + ($source:expr => [$($model:tt)*], [$($problems:tt)*] $(, [$($decos:tt)*])? $(,)?) => { let mut scope = Scope::new::(); scope.add::("f"); scope.add::("n"); @@ -740,12 +740,12 @@ mod tests { let (exp, cmp) = spanned![vec $($model)*]; check($source, exp, pass.output.nodes, cmp); - // Test errors - let (exp, cmp) = spanned![vec $($errors)*]; + // Test problems + let (exp, cmp) = spanned![vec $($problems)*]; let exp = exp.into_iter() .map(|s: Spanned<&str>| s.map(|e| e.to_string())) .collect::>(); - let found = pass.feedback.errors.into_iter() + let found = pass.feedback.problems.into_iter() .map(|s| s.map(|e| e.message)) .collect::>(); check($source, exp, found, cmp); diff --git a/src/syntax/span.rs b/src/syntax/span.rs index d19d4c18b..9eb80d921 100644 --- a/src/syntax/span.rs +++ b/src/syntax/span.rs @@ -76,7 +76,7 @@ pub struct Span { } impl Span { - /// A dummy span. + /// The zero span. pub const ZERO: Span = Span { start: Position::ZERO, end: Position::ZERO }; /// Create a new span from start and end positions. diff --git a/src/syntax/test.rs b/src/syntax/test.rs index 47680487b..465f475af 100644 --- a/src/syntax/test.rs +++ b/src/syntax/test.rs @@ -29,7 +29,7 @@ where T: Debug + PartialEq + SpanlessEq { /// spanned![(0:0, 0:5, "hello"), (0:5, 0:3, "world")] /// ``` /// The span information can simply be omitted to create a vector with items -/// that are spanned with dummy zero spans. +/// that are spanned with zero spans. macro_rules! spanned { (item ($sl:tt:$sc:tt, $el:tt:$ec:tt, $v:expr)) => ({ #[allow(unused_imports)] @@ -80,7 +80,7 @@ function! { } } - layout(self, ctx, errors) { vec![] } + layout(self, ctx, f) { vec![] } } /// Compares elements by only looking at values and ignoring spans. diff --git a/tests/src/typeset.rs b/tests/src/typeset.rs index b21f6b3f4..8a4cf828e 100644 --- a/tests/src/typeset.rs +++ b/tests/src/typeset.rs @@ -123,14 +123,14 @@ fn test(name: &str, src: &str) -> DynResult<()> { fn compile(typesetter: &Typesetter, src: &str) -> MultiLayout { if cfg!(debug_assertions) { let typeset = block_on(typesetter.typeset(src)); - let errors = typeset.feedback.errors; + let problems = typeset.feedback.problems; - if !errors.is_empty() { - for error in errors { + if !problems.is_empty() { + for problem in problems { println!(" {:?} {:?}: {}", - error.v.severity, - error.span, - error.v.message + problem.v.severity, + problem.span, + problem.v.message ); } }