Rename errors to problems and make error! macro more ergonomic 🧼

Also adds a `warning!` macro.
This commit is contained in:
Laurenz 2020-07-26 17:28:43 +02:00
parent 0e8c2cad6e
commit e2ef4f64e7
18 changed files with 266 additions and 233 deletions

View File

@ -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<Error>;
/// 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<String>, 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)*) };
}

View File

@ -7,7 +7,7 @@ use crate::syntax::span::Spanned;
/// Types that are useful for creating your own functions. /// Types that are useful for creating your own functions.
pub mod prelude { 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::prelude::*;
pub use crate::layout::Command::{self, *}; pub use crate::layout::Command::{self, *};
pub use crate::style::{LayoutStyle, PageStyle, TextStyle}; pub use crate::style::{LayoutStyle, PageStyle, TextStyle};
@ -55,8 +55,8 @@ pub trait ParseFunc {
/// ///
/// parse(header, body, ctx, f) { /// parse(header, body, ctx, f) {
/// let body = body!(opt: body, ctx, f); /// let body = body!(opt: body, ctx, f);
/// let hidden = header.args.pos.get::<bool>(&mut f.errors) /// let hidden = header.args.pos.get::<bool>(&mut f.problems)
/// .or_missing(&mut f.errors, header.name.span, "hidden") /// .or_missing(&mut f.problems, header.name.span, "hidden")
/// .unwrap_or(false); /// .unwrap_or(false);
/// ///
/// HiderFunc { body: if hidden { None } else { body } } /// HiderFunc { body: if hidden { None } else { body } }
@ -132,7 +132,7 @@ macro_rules! function {
let func = $code; let func = $code;
for arg in header.args.into_iter() { 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) $crate::Pass::new(func, feedback)
@ -189,7 +189,7 @@ macro_rules! body {
(nope: $body:expr, $feedback:expr) => { (nope: $body:expr, $feedback:expr) => {
if let Some(body) = $body { if let Some(body) = $body {
$feedback.errors.push($crate::err!(body.span; "unexpected body")); error!(@$feedback, body.span, "unexpected body");
} }
}; };
} }

View File

@ -245,8 +245,10 @@ impl<'a> ModelLayouter<'a> {
BreakParagraph => self.layout_paragraph(), BreakParagraph => self.layout_paragraph(),
BreakPage => { BreakPage => {
if self.ctx.nested { if self.ctx.nested {
self.feedback.errors.push(err!(model_span; error!(
"page break cannot be issued from nested context")); @self.feedback, model_span,
"page break cannot be issued from nested context",
);
} else { } else {
self.layouter.finish_space(true) self.layouter.finish_space(true)
} }
@ -258,8 +260,10 @@ impl<'a> ModelLayouter<'a> {
} }
SetPageStyle(style) => { SetPageStyle(style) => {
if self.ctx.nested { if self.ctx.nested {
self.feedback.errors.push(err!(model_span; error!(
"page style cannot be changed from nested context")); @self.feedback, model_span,
"page style cannot be changed from nested context",
);
} else { } else {
self.style.page = style; self.style.page = style;

View File

@ -27,7 +27,7 @@ use toddle::{Font, OwnedData};
use toddle::query::{FontLoader, SharedFontLoader}; use toddle::query::{FontLoader, SharedFontLoader};
use toddle::query::{FontProvider, FontIndex, FontDescriptor}; use toddle::query::{FontProvider, FontIndex, FontDescriptor};
use crate::error::Error; use crate::problem::Problems;
use crate::layout::MultiLayout; use crate::layout::MultiLayout;
use crate::style::{LayoutStyle, PageStyle, TextStyle}; use crate::style::{LayoutStyle, PageStyle, TextStyle};
use crate::syntax::{SyntaxModel, Scope, Decoration, ParseContext, parse}; use crate::syntax::{SyntaxModel, Scope, Decoration, ParseContext, parse};
@ -43,7 +43,7 @@ macro_rules! pub_use_mod {
} }
#[macro_use] #[macro_use]
pub mod error; pub mod problem;
pub mod export; pub mod export;
#[macro_use] #[macro_use]
pub mod func; pub mod func;
@ -175,8 +175,8 @@ impl<T> Pass<T> {
/// User feedback data accumulated during a compilation pass. /// User feedback data accumulated during a compilation pass.
#[derive(Debug, Default, Clone, Eq, PartialEq)] #[derive(Debug, Default, Clone, Eq, PartialEq)]
pub struct Feedback { pub struct Feedback {
/// Errors in the source. /// Problems in the source code.
pub errors: SpanVec<Error>, pub problems: Problems,
/// Decorations of the source code for semantic syntax highlighting. /// Decorations of the source code for semantic syntax highlighting.
pub decos: SpanVec<Decoration>, pub decos: SpanVec<Decoration>,
} }
@ -185,7 +185,7 @@ impl Feedback {
/// Create a new feedback instance without errors and decos. /// Create a new feedback instance without errors and decos.
pub fn new() -> Feedback { pub fn new() -> Feedback {
Feedback { Feedback {
errors: vec![], problems: vec![],
decos: vec![], decos: vec![],
} }
} }
@ -198,14 +198,14 @@ impl Feedback {
/// Add other feedback data to this feedback. /// Add other feedback data to this feedback.
pub fn extend(&mut self, other: Feedback) { pub fn extend(&mut self, other: Feedback) {
self.errors.extend(other.errors); self.problems.extend(other.problems);
self.decos.extend(other.decos); self.decos.extend(other.decos);
} }
/// Add more feedback whose spans are local and need to be offset by an /// Add more feedback whose spans are local and need to be offset by an
/// `offset` to be correct for this feedbacks context. /// `offset` to be correct for this feedbacks context.
pub fn extend_offset(&mut self, offset: Position, other: Feedback) { 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)); self.decos.extend(offset_spans(offset, other.decos));
} }
} }

View File

@ -13,17 +13,17 @@ function! {
} }
parse(header, body, ctx, f) { parse(header, body, ctx, f) {
let list = header.args.pos.get_all::<StringLike>(&mut f.errors) let list = header.args.pos.get_all::<StringLike>(&mut f.problems)
.map(|s| s.0.to_lowercase()) .map(|s| s.0.to_lowercase())
.collect(); .collect();
let tuples: Vec<_> = header.args.key let tuples: Vec<_> = header.args.key
.get_all::<String, Tuple>(&mut f.errors) .get_all::<String, Tuple>(&mut f.problems)
.collect(); .collect();
let classes = tuples.into_iter() let classes = tuples.into_iter()
.map(|(class, mut tuple)| { .map(|(class, mut tuple)| {
let fallback = tuple.get_all::<StringLike>(&mut f.errors) let fallback = tuple.get_all::<StringLike>(&mut f.problems)
.map(|s| s.0.to_lowercase()) .map(|s| s.0.to_lowercase())
.collect(); .collect();
(class.to_lowercase(), fallback) (class.to_lowercase(), fallback)
@ -37,7 +37,7 @@ function! {
} }
} }
layout(self, ctx, errors) { layout(self, ctx, f) {
styled(&self.body, ctx, Some(()), styled(&self.body, ctx, Some(()),
|s, _| { |s, _| {
if !self.list.is_empty() { if !self.list.is_empty() {
@ -64,12 +64,12 @@ function! {
parse(header, body, ctx, f) { parse(header, body, ctx, f) {
FontStyleFunc { FontStyleFunc {
body: body!(opt: body, ctx, f), body: body!(opt: body, ctx, f),
style: header.args.pos.get::<FontStyle>(&mut f.errors) style: header.args.pos.get::<FontStyle>(&mut f.problems)
.or_missing(&mut f.errors, header.name.span, "style"), .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) styled(&self.body, ctx, self.style, |t, s| t.variant.style = s)
} }
} }
@ -84,22 +84,24 @@ function! {
parse(header, body, ctx, f) { parse(header, body, ctx, f) {
let body = body!(opt: body, ctx, f); let body = body!(opt: body, ctx, f);
let weight = header.args.pos.get::<Spanned<(FontWeight, bool)>>(&mut f.errors) let weight = header.args.pos.get::<Spanned<(FontWeight, bool)>>(&mut f.problems)
.map(|Spanned { v: (weight, is_clamped), span }| { .map(|Spanned { v: (weight, is_clamped), span }| {
if is_clamped { if is_clamped {
f.errors.push(err!(@Warning: span; warning!(
"weight should be between \ @f, span,
100 and 900, clamped to {}", weight.0)); "weight should be between 100 and 900, clamped to {}",
weight.0,
);
} }
weight weight
}) })
.or_missing(&mut f.errors, header.name.span, "weight"); .or_missing(&mut f.problems, header.name.span, "weight");
FontWeightFunc { body, weight } FontWeightFunc { body, weight }
} }
layout(self, ctx, errors) { layout(self, ctx, f) {
styled(&self.body, ctx, self.weight, |t, w| t.variant.weight = w) styled(&self.body, ctx, self.weight, |t, w| t.variant.weight = w)
} }
} }
@ -115,12 +117,12 @@ function! {
parse(header, body, ctx, f) { parse(header, body, ctx, f) {
FontSizeFunc { FontSizeFunc {
body: body!(opt: body, ctx, f), body: body!(opt: body, ctx, f),
size: header.args.pos.get::<FSize>(&mut f.errors) size: header.args.pos.get::<FSize>(&mut f.problems)
.or_missing(&mut f.errors, header.name.span, "size") .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| { styled(&self.body, ctx, self.size, |t, s| {
match s { match s {
FSize::Absolute(size) => { FSize::Absolute(size) => {

View File

@ -13,14 +13,14 @@ function! {
parse(header, body, ctx, f) { parse(header, body, ctx, f) {
AlignFunc { AlignFunc {
body: body!(opt: body, ctx, f), body: body!(opt: body, ctx, f),
map: PosAxisMap::parse::<AxisKey>(&mut f.errors, &mut header.args), map: PosAxisMap::parse::<AxisKey>(&mut f.problems, &mut header.args),
} }
} }
layout(self, ctx, f) { layout(self, ctx, f) {
ctx.base = ctx.spaces[0].dimensions; 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)) alignment.axis().map(|s| s.to_generic(ctx.axes))
}); });
@ -29,8 +29,10 @@ 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 {
f.errors.push(err!(span; error!(
"invalid alignment `{}` for {} axis", alignment, axis)); @f, span,
"invalid alignment `{}` for {} axis", alignment, axis,
);
} }
} }
} }
@ -59,14 +61,14 @@ function! {
DirectionFunc { DirectionFunc {
name_span: header.name.span, name_span: header.name.span,
body: body!(opt: body, ctx, f), body: body!(opt: body, ctx, f),
map: PosAxisMap::parse::<AxisKey>(&mut f.errors, &mut header.args), map: PosAxisMap::parse::<AxisKey>(&mut f.problems, &mut header.args),
} }
} }
layout(self, ctx, f) { layout(self, ctx, f) {
ctx.base = ctx.spaces[0].dimensions; 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)) Some(direction.axis().to_generic(ctx.axes))
}); });
@ -76,9 +78,11 @@ 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() {
f.errors.push(err!(self.name_span; error!(
@f, 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 {
ctx.axes = axes; ctx.axes = axes;
} }
@ -106,8 +110,8 @@ function! {
parse(header, body, ctx, f) { parse(header, body, ctx, f) {
BoxFunc { BoxFunc {
body: body!(opt: body, ctx, f).unwrap_or(SyntaxModel::new()), body: body!(opt: body, ctx, f).unwrap_or(SyntaxModel::new()),
extents: AxisMap::parse::<ExtentKey>(&mut f.errors, &mut header.args.key), extents: AxisMap::parse::<ExtentKey>(&mut f.problems, &mut header.args.key),
debug: header.args.key.get::<bool>(&mut f.errors, "debug"), debug: header.args.key.get::<bool>(&mut f.problems, "debug"),
} }
} }
@ -119,7 +123,7 @@ function! {
ctx.debug = debug; 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] { 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));

View File

@ -59,7 +59,7 @@ function! {
ValFunc { body: body!(opt: body, ctx, f) } ValFunc { body: body!(opt: body, ctx, f) }
} }
layout(self, ctx, errors) { layout(self, ctx, f) {
match &self.body { match &self.body {
Some(model) => vec![LayoutSyntaxModel(model)], Some(model) => vec![LayoutSyntaxModel(model)],
None => vec![], None => vec![],

View File

@ -15,9 +15,9 @@ function! {
parse(header, body, ctx, f) { parse(header, body, ctx, f) {
body!(nope: body, f); body!(nope: body, f);
PageSizeFunc { PageSizeFunc {
paper: header.args.pos.get::<Paper>(&mut f.errors), paper: header.args.pos.get::<Paper>(&mut f.problems),
extents: AxisMap::parse::<ExtentKey>(&mut f.errors, &mut header.args.key), extents: AxisMap::parse::<ExtentKey>(&mut f.problems, &mut header.args.key),
flip: header.args.key.get::<bool>(&mut f.errors, "flip").unwrap_or(false), flip: header.args.key.get::<bool>(&mut f.problems, "flip").unwrap_or(false),
} }
} }
@ -31,7 +31,7 @@ function! {
style.class = PaperClass::Custom; 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(Horizontal, |&width| style.dimensions.x = width);
map.with(Vertical, |&height| style.dimensions.y = height); map.with(Vertical, |&height| style.dimensions.y = height);
@ -53,13 +53,13 @@ function! {
parse(header, body, ctx, f) { parse(header, body, ctx, f) {
body!(nope: body, f); body!(nope: body, f);
PageMarginsFunc { PageMarginsFunc {
padding: PaddingMap::parse(&mut f.errors, &mut header.args), padding: PaddingMap::parse(&mut f.problems, &mut header.args),
} }
} }
layout(self, ctx, f) { layout(self, ctx, f) {
let mut style = ctx.style.page; 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)] vec![SetPageStyle(style)]
} }
} }

View File

@ -11,7 +11,7 @@ function! {
pub struct LineBreakFunc; pub struct LineBreakFunc;
parse(default) parse(default)
layout(self, ctx, errors) { vec![BreakLine] } layout(self, ctx, f) { vec![BreakLine] }
} }
function! { function! {
@ -22,7 +22,7 @@ function! {
pub struct ParBreakFunc; pub struct ParBreakFunc;
parse(default) parse(default)
layout(self, ctx, errors) { vec![BreakParagraph] } layout(self, ctx, f) { vec![BreakParagraph] }
} }
function! { function! {
@ -31,7 +31,7 @@ function! {
pub struct PageBreakFunc; pub struct PageBreakFunc;
parse(default) parse(default)
layout(self, ctx, errors) { vec![BreakPage] } layout(self, ctx, f) { vec![BreakPage] }
} }
function! { function! {
@ -50,13 +50,13 @@ function! {
ContentSpacingFunc { ContentSpacingFunc {
body: body!(opt: body, ctx, f), body: body!(opt: body, ctx, f),
content: meta, content: meta,
spacing: header.args.pos.get::<f64>(&mut f.errors) spacing: header.args.pos.get::<f64>(&mut f.problems)
.map(|num| num as f32) .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 { styled(&self.body, ctx, self.spacing, |t, s| match self.content {
Word => t.word_spacing_scale = s, Word => t.word_spacing_scale = s,
Line => t.line_spacing_scale = s, Line => t.line_spacing_scale = s,
@ -88,15 +88,15 @@ function! {
body!(nope: body, f); body!(nope: body, f);
SpacingFunc { SpacingFunc {
spacing: if let Some(axis) = meta { spacing: if let Some(axis) = meta {
header.args.pos.get::<FSize>(&mut f.errors) header.args.pos.get::<FSize>(&mut f.problems)
.map(|s| (AxisKey::Specific(axis), s)) .map(|s| (AxisKey::Specific(axis), s))
} else { } else {
header.args.key.get_with_key::<AxisKey, FSize>(&mut f.errors) header.args.key.get_with_key::<AxisKey, FSize>(&mut f.problems)
}.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) {
if let Some((axis, spacing)) = self.spacing { if let Some((axis, spacing)) = self.spacing {
let axis = axis.to_generic(ctx.axes); let axis = axis.to_generic(ctx.axes);
let spacing = spacing.scaled(ctx.style.text.font_size()); let spacing = spacing.scaled(ctx.style.text.font_size());

94
src/problem.rs Normal file
View File

@ -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<Problem>;
/// 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<String>, 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,
)
};
}

View File

@ -6,7 +6,7 @@ use std::ops::Deref;
use std::str::FromStr; use std::str::FromStr;
use std::u8; use std::u8;
use crate::error::Errors; use crate::problem::Problems;
use crate::size::Size; use crate::size::Size;
use super::func::{Key, Value}; use super::func::{Key, Value};
use super::span::{Span, Spanned}; use super::span::{Span, Spanned};
@ -258,28 +258,28 @@ impl Tuple {
} }
/// Extract (and remove) the first matching value and remove and generate /// Extract (and remove) the first matching value and remove and generate
/// errors for all previous items that did not match. /// problems for all previous items that did not match.
pub fn get<V: Value>(&mut self, errors: &mut Errors) -> Option<V> { pub fn get<V: Value>(&mut self, problems: &mut Problems) -> Option<V> {
while !self.items.is_empty() { while !self.items.is_empty() {
let expr = self.items.remove(0); let expr = self.items.remove(0);
let span = expr.span; let span = expr.span;
match V::parse(expr) { match V::parse(expr) {
Ok(output) => return Some(output), Ok(output) => return Some(output),
Err(err) => errors.push(Spanned { v: err, span }), Err(v) => problems.push(Spanned { v, span }),
} }
} }
None None
} }
/// Extract and return an iterator over all values that match and generate /// Extract and return an iterator over all values that match and generate
/// errors for all items that do not match. /// problems for all items that do not match.
pub fn get_all<'a, V: Value>(&'a mut self, errors: &'a mut Errors) pub fn get_all<'a, V: Value>(&'a mut self, problems: &'a mut Problems)
-> impl Iterator<Item=V> + 'a { -> impl Iterator<Item=V> + 'a {
self.items.drain(..).filter_map(move |expr| { self.items.drain(..).filter_map(move |expr| {
let span = expr.span; let span = expr.span;
match V::parse(expr) { match V::parse(expr) {
Ok(output) => Some(output), 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 /// Inserts an error if the value does not match. If the key is not
/// contained, no error is inserted. /// contained, no error is inserted.
pub fn get<V: Value>(&mut self, errors: &mut Errors, key: &str) -> Option<V> { pub fn get<V: Value>(&mut self, problems: &mut Problems, key: &str) -> Option<V> {
let index = self.pairs.iter().position(|pair| pair.v.key.v.as_str() == key)?; let index = self.pairs.iter().position(|pair| pair.v.key.v.as_str() == key)?;
self.get_index::<V>(errors, index) self.get_index::<V>(problems, index)
} }
/// Extract (and remove) a pair with a matching key and value. /// Extract (and remove) a pair with a matching key and value.
@ -411,12 +411,12 @@ impl Object {
/// found, no error is inserted. /// found, no error is inserted.
pub fn get_with_key<K: Key, V: Value>( pub fn get_with_key<K: Key, V: Value>(
&mut self, &mut self,
errors: &mut Errors, problems: &mut Problems,
) -> Option<(K, V)> { ) -> Option<(K, V)> {
for (index, pair) in self.pairs.iter().enumerate() { for (index, pair) in self.pairs.iter().enumerate() {
let key = Spanned { v: pair.v.key.v.as_str(), span: pair.v.key.span }; let key = Spanned { v: pair.v.key.v.as_str(), span: pair.v.key.span };
if let Some(key) = K::parse(key) { if let Some(key) = K::parse(key) {
return self.get_index::<V>(errors, index).map(|value| (key, value)); return self.get_index::<V>(problems, index).map(|value| (key, value));
} }
} }
None None
@ -427,7 +427,7 @@ impl Object {
/// Inserts errors for values that do not match. /// Inserts errors for values that do not match.
pub fn get_all<'a, K: Key, V: Value>( pub fn get_all<'a, K: Key, V: Value>(
&'a mut self, &'a mut self,
errors: &'a mut Errors, problems: &'a mut Problems,
) -> impl Iterator<Item=(K, V)> + 'a { ) -> impl Iterator<Item=(K, V)> + 'a {
let mut index = 0; let mut index = 0;
std::iter::from_fn(move || { std::iter::from_fn(move || {
@ -436,7 +436,7 @@ impl Object {
let key = Spanned { v: key.v.as_str(), span: key.span }; let key = Spanned { v: key.v.as_str(), span: key.span };
Some(if let Some(key) = K::parse(key) { Some(if let Some(key) = K::parse(key) {
self.get_index::<V>(errors, index).map(|v| (key, v)) self.get_index::<V>(problems, index).map(|v| (key, v))
} else { } else {
index += 1; index += 1;
None None
@ -456,20 +456,20 @@ impl Object {
/// ``` /// ```
pub fn get_all_spanned<'a, K: Key + 'a, V: Value + 'a>( pub fn get_all_spanned<'a, K: Key + 'a, V: Value + 'a>(
&'a mut self, &'a mut self,
errors: &'a mut Errors, problems: &'a mut Problems,
) -> impl Iterator<Item=Spanned<(K, V)>> + 'a { ) -> impl Iterator<Item=Spanned<(K, V)>> + 'a {
self.get_all::<Spanned<K>, Spanned<V>>(errors) self.get_all::<Spanned<K>, Spanned<V>>(problems)
.map(|(k, v)| Spanned::new((k.v, v.v), Span::merge(k.span, v.span))) .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 /// Extract the argument at the given index and insert an error if the value
/// does not match. /// does not match.
fn get_index<V: Value>(&mut self, errors: &mut Errors, index: usize) -> Option<V> { fn get_index<V: Value>(&mut self, problems: &mut Problems, index: usize) -> Option<V> {
let expr = self.pairs.remove(index).v.value; let expr = self.pairs.remove(index).v.value;
let span = expr.span; let span = expr.span;
match V::parse(expr) { match V::parse(expr) {
Ok(output) => Some(output), Ok(output) => Some(output),
Err(err) => { errors.push(Spanned { v: err, span }); None } Err(v) => { problems.push(Spanned { v, span }); None }
} }
} }

View File

@ -1,6 +1,6 @@
//! Deduplicating maps and keys for argument parsing. //! Deduplicating maps and keys for argument parsing.
use crate::error::Errors; use crate::problem::Problems;
use crate::layout::prelude::*; use crate::layout::prelude::*;
use crate::size::{PSize, ValueBox}; use crate::size::{PSize, ValueBox};
use crate::syntax::span::Spanned; use crate::syntax::span::Spanned;
@ -12,7 +12,7 @@ use super::*;
/// A map which deduplicates redundant arguments. /// A map which deduplicates redundant arguments.
/// ///
/// Whenever a duplicate argument is inserted into the map, through the /// 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. /// list that needs to be passed to those functions.
/// ///
/// All entries need to have span information to enable the error reporting. /// All entries need to have span information to enable the error reporting.
@ -28,27 +28,27 @@ impl<K, V> DedupMap<K, V> where K: Eq {
} }
/// Create a new map from an iterator of spanned keys and values. /// Create a new map from an iterator of spanned keys and values.
pub fn from_iter<I>(errors: &mut Errors, iter: I) -> DedupMap<K, V> pub fn from_iter<I>(problems: &mut Problems, iter: I) -> DedupMap<K, V>
where I: IntoIterator<Item=Spanned<(K, V)>> { where I: IntoIterator<Item=Spanned<(K, V)>> {
let mut map = DedupMap::new(); let mut map = DedupMap::new();
map.extend(errors, iter); map.extend(problems, iter);
map map
} }
/// Add a spanned key-value pair. /// 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) { 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 { } else {
self.map.push(entry); self.map.push(entry);
} }
} }
/// Add multiple spanned key-value pairs. /// Add multiple spanned key-value pairs.
pub fn extend<I>(&mut self, errors: &mut Errors, items: I) pub fn extend<I>(&mut self, problems: &mut Problems, items: I)
where I: IntoIterator<Item=Spanned<(K, V)>> { where I: IntoIterator<Item=Spanned<(K, V)>> {
for item in items.into_iter() { for item in items.into_iter() {
self.insert(errors, item); self.insert(problems, item);
} }
} }
@ -71,15 +71,15 @@ impl<K, V> DedupMap<K, V> where K: Eq {
} }
/// Create a new map where keys and values are mapped to new keys and /// 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. /// generated.
pub fn dedup<F, K2, V2>(&self, errors: &mut Errors, mut f: F) -> DedupMap<K2, V2> pub fn dedup<F, K2, V2>(&self, problems: &mut Problems, mut f: F) -> DedupMap<K2, V2>
where F: FnMut(&K, &V) -> (K2, V2), K2: Eq { where F: FnMut(&K, &V) -> (K2, V2), K2: Eq {
let mut map = DedupMap::new(); let mut map = DedupMap::new();
for Spanned { v: (key, value), span } in self.map.iter() { for Spanned { v: (key, value), span } in self.map.iter() {
let (key, value) = f(key, value); 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 map
@ -98,21 +98,21 @@ pub struct AxisMap<V>(DedupMap<AxisKey, V>);
impl<V: Value> AxisMap<V> { impl<V: Value> AxisMap<V> {
/// Parse an axis map from the object. /// Parse an axis map from the object.
pub fn parse<K>( pub fn parse<K>(
errors: &mut Errors, problems: &mut Problems,
object: &mut Object, object: &mut Object,
) -> AxisMap<V> where K: Key + Into<AxisKey> { ) -> AxisMap<V> where K: Key + Into<AxisKey> {
let values: Vec<_> = object let values: Vec<_> = object
.get_all_spanned::<K, V>(errors) .get_all_spanned::<K, V>(problems)
.map(|s| s.map(|(k, v)| (k.into(), v))) .map(|s| s.map(|(k, v)| (k.into(), v)))
.collect(); .collect();
AxisMap(DedupMap::from_iter(errors, values)) AxisMap(DedupMap::from_iter(problems, values))
} }
/// Deduplicate from specific or generic to just specific axes. /// Deduplicate from specific or generic to just specific axes.
pub fn dedup(&self, errors: &mut Errors, axes: LayoutAxes) -> DedupMap<SpecificAxis, V> pub fn dedup(&self, problems: &mut Problems, axes: LayoutAxes) -> DedupMap<SpecificAxis, V>
where V: Clone { 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<V>(DedupMap<PosAxisKey, V>);
impl<V: Value> PosAxisMap<V> { impl<V: Value> PosAxisMap<V> {
/// Parse a positional/axis map from the function arguments. /// Parse a positional/axis map from the function arguments.
pub fn parse<K>( pub fn parse<K>(
errors: &mut Errors, problems: &mut Problems,
args: &mut FuncArgs, args: &mut FuncArgs,
) -> PosAxisMap<V> where K: Key + Into<AxisKey> { ) -> PosAxisMap<V> where K: Key + Into<AxisKey> {
let mut map = DedupMap::new(); let mut map = DedupMap::new();
for &key in &[PosAxisKey::First, PosAxisKey::Second] { for &key in &[PosAxisKey::First, PosAxisKey::Second] {
if let Some(Spanned { v, span }) = args.pos.get::<Spanned<V>>(errors) { if let Some(Spanned { v, span }) = args.pos.get::<Spanned<V>>(problems) {
map.insert(errors, Spanned { v: (key, v), span }) map.insert(problems, Spanned { v: (key, v), span })
} }
} }
let keywords: Vec<_> = args.key let keywords: Vec<_> = args.key
.get_all_spanned::<K, V>(errors) .get_all_spanned::<K, V>(problems)
.map(|s| s.map(|(k, v)| (PosAxisKey::Keyword(k.into()), v))) .map(|s| s.map(|(k, v)| (PosAxisKey::Keyword(k.into()), v)))
.collect(); .collect();
map.extend(errors, keywords); map.extend(problems, keywords);
PosAxisMap(map) PosAxisMap(map)
} }
@ -149,7 +149,7 @@ impl<V: Value> PosAxisMap<V> {
/// or specific axes to just generic axes. /// or specific axes to just generic axes.
pub fn dedup<F>( pub fn dedup<F>(
&self, &self,
errors: &mut Errors, problems: &mut Problems,
axes: LayoutAxes, axes: LayoutAxes,
mut f: F, mut f: F,
) -> DedupMap<GenericAxis, V> ) -> DedupMap<GenericAxis, V>
@ -157,7 +157,7 @@ impl<V: Value> PosAxisMap<V> {
F: FnMut(&V) -> Option<GenericAxis>, F: FnMut(&V) -> Option<GenericAxis>,
V: Clone, V: Clone,
{ {
self.0.dedup(errors, |key, val| { self.0.dedup(problems, |key, val| {
(match key { (match key {
PosAxisKey::First => f(val).unwrap_or(GenericAxis::Primary), PosAxisKey::First => f(val).unwrap_or(GenericAxis::Primary),
PosAxisKey::Second => f(val).unwrap_or(GenericAxis::Secondary), PosAxisKey::Second => f(val).unwrap_or(GenericAxis::Secondary),
@ -175,20 +175,20 @@ pub struct PaddingMap(DedupMap<PaddingKey<AxisKey>, Option<PSize>>);
impl PaddingMap { impl PaddingMap {
/// Parse a padding map from the function arguments. /// 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 mut map = DedupMap::new();
let all = args.pos.get::<Spanned<Defaultable<PSize>>>(errors); let all = args.pos.get::<Spanned<Defaultable<PSize>>>(problems);
if let Some(Spanned { v, span }) = all { 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 let paddings: Vec<_> = args.key
.get_all_spanned::<PaddingKey<AxisKey>, Defaultable<PSize>>(errors) .get_all_spanned::<PaddingKey<AxisKey>, Defaultable<PSize>>(problems)
.map(|s| s.map(|(k, v)| (k, v.into()))) .map(|s| s.map(|(k, v)| (k, v.into())))
.collect(); .collect();
map.extend(errors, paddings); map.extend(problems, paddings);
PaddingMap(map) PaddingMap(map)
} }
@ -196,13 +196,13 @@ impl PaddingMap {
/// Apply the specified padding on a value box of optional, scalable sizes. /// Apply the specified padding on a value box of optional, scalable sizes.
pub fn apply( pub fn apply(
&self, &self,
errors: &mut Errors, problems: &mut Problems,
axes: LayoutAxes, axes: LayoutAxes,
padding: &mut ValueBox<Option<PSize>> padding: &mut ValueBox<Option<PSize>>
) { ) {
use PaddingKey::*; use PaddingKey::*;
let map = self.0.dedup(errors, |key, &val| { let map = self.0.dedup(problems, |key, &val| {
(match key { (match key {
All => All, All => All,
Both(axis) => Both(axis.to_specific(axes)), Both(axis) => Both(axis.to_specific(axes)),

View File

@ -1,7 +1,7 @@
//! Primitives for argument parsing in library functions. //! Primitives for argument parsing in library functions.
use std::iter::FromIterator; use std::iter::FromIterator;
use crate::error::{Error, Errors}; use crate::problem::{Problem, Problems};
use super::expr::{Expr, Ident, Tuple, Object, Pair}; use super::expr::{Expr, Ident, Tuple, Object, Pair};
use super::span::{Span, Spanned}; use super::span::{Span, Spanned};
@ -84,13 +84,13 @@ pub enum FuncArg {
pub trait OptionExt: Sized { pub trait OptionExt: Sized {
/// Add an error about a missing argument `arg` with the given span if the /// Add an error about a missing argument `arg` with the given span if the
/// option is `None`. /// 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<T> OptionExt for Option<T> { impl<T> OptionExt for Option<T> {
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() { if self.is_none() {
errors.push(err!(span; "missing argument: {}", arg)); problems.push(error!(span, "missing argument: {}", arg));
} }
self self
} }

View File

@ -23,20 +23,20 @@ use self::AlignmentValue::*;
/// # Example implementation /// # Example implementation
/// An implementation for `bool` might look as follows: /// An implementation for `bool` might look as follows:
/// ``` /// ```
/// # use typstc::err; /// # use typstc::error;
/// # use typstc::error::Error; /// # use typstc::problem::Problem;
/// # use typstc::syntax::expr::Expr; /// # use typstc::syntax::expr::Expr;
/// # use typstc::syntax::func::Value; /// # use typstc::syntax::func::Value;
/// # use typstc::syntax::span::Spanned; /// # use typstc::syntax::span::Spanned;
/// # struct Bool; /* /// # struct Bool; /*
/// impl Value for bool { /// impl Value for bool {
/// # */ impl Value for Bool { /// # */ impl Value for Bool {
/// fn parse(expr: Spanned<Expr>) -> Result<Self, Error> { /// fn parse(expr: Spanned<Expr>) -> Result<Self, Problem> {
/// match expr.v { /// match expr.v {
/// # /* /// # /*
/// Expr::Bool(b) => Ok(b), /// Expr::Bool(b) => Ok(b),
/// # */ Expr::Bool(_) => Ok(Bool), /// # */ 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 { pub trait Value: Sized {
/// Parse an expression into this value or return an error if the expression /// Parse an expression into this value or return an error if the expression
/// is valid for this value type. /// is valid for this value type.
fn parse(expr: Spanned<Expr>) -> Result<Self, Error>; fn parse(expr: Spanned<Expr>) -> Result<Self, Problem>;
} }
impl<V: Value> Value for Spanned<V> { impl<V: Value> Value for Spanned<V> {
fn parse(expr: Spanned<Expr>) -> Result<Self, Error> { fn parse(expr: Spanned<Expr>) -> Result<Self, Problem> {
let span = expr.span; let span = expr.span;
V::parse(expr).map(|v| Spanned { v, span }) V::parse(expr).map(|v| Spanned { v, span })
} }
@ -58,12 +58,13 @@ impl<V: Value> Value for Spanned<V> {
macro_rules! value { macro_rules! value {
($type:ty, $name:expr, $($p:pat => $r:expr),* $(,)?) => { ($type:ty, $name:expr, $($p:pat => $r:expr),* $(,)?) => {
impl Value for $type { impl Value for $type {
fn parse(expr: Spanned<Expr>) -> Result<Self, Error> { fn parse(expr: Spanned<Expr>) -> Result<Self, Problem> {
#[allow(unreachable_patterns)] #[allow(unreachable_patterns)]
match expr.v { match expr.v {
$($p => Ok($r)),*, $($p => Ok($r)),*,
other => Err(err!("expected {}, found {}", other => Err(
$name, other.name())), error!("expected {}, found {}", $name, other.name())
),
} }
} }
} }
@ -120,7 +121,7 @@ impl From<StringLike> for String {
pub struct Defaultable<V>(pub Option<V>); pub struct Defaultable<V>(pub Option<V>);
impl<V: Value> Value for Defaultable<V> { impl<V: Value> Value for Defaultable<V> {
fn parse(expr: Spanned<Expr>) -> Result<Self, Error> { fn parse(expr: Spanned<Expr>) -> Result<Self, Problem> {
Ok(Defaultable(match expr.v { Ok(Defaultable(match expr.v {
Expr::Ident(ident) if ident.as_str() == "default" => None, Expr::Ident(ident) if ident.as_str() == "default" => None,
_ => Some(V::parse(expr)?) _ => Some(V::parse(expr)?)
@ -135,16 +136,16 @@ impl<V> From<Defaultable<V>> for Option<V> {
} }
impl Value for FontStyle { impl Value for FontStyle {
fn parse(expr: Spanned<Expr>) -> Result<Self, Error> { fn parse(expr: Spanned<Expr>) -> Result<Self, Problem> {
FontStyle::from_name(Ident::parse(expr)?.as_str()) 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 /// The additional boolean specifies whether a number was clamped into the range
/// 100 - 900 to make it a valid font weight. /// 100 - 900 to make it a valid font weight.
impl Value for (FontWeight, bool) { impl Value for (FontWeight, bool) {
fn parse(expr: Spanned<Expr>) -> Result<Self, Error> { fn parse(expr: Spanned<Expr>) -> Result<Self, Problem> {
match expr.v { match expr.v {
Expr::Number(weight) => { Expr::Number(weight) => {
let weight = weight.round(); let weight = weight.round();
@ -158,30 +159,31 @@ impl Value for (FontWeight, bool) {
} }
Expr::Ident(id) => { Expr::Ident(id) => {
FontWeight::from_name(id.as_str()) FontWeight::from_name(id.as_str())
.ok_or_else(|| err!("invalid font weight")) .ok_or_else(|| error!("invalid font weight"))
.map(|weight| (weight, false)) .map(|weight| (weight, false))
} }
other => Err(err!("expected identifier or number, \ other => Err(
found {}", other.name())), error!("expected identifier or number, found {}", other.name())
),
} }
} }
} }
impl Value for Paper { impl Value for Paper {
fn parse(expr: Spanned<Expr>) -> Result<Self, Error> { fn parse(expr: Spanned<Expr>) -> Result<Self, Problem> {
Paper::from_name(Ident::parse(expr)?.as_str()) 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 { impl Value for Direction {
fn parse(expr: Spanned<Expr>) -> Result<Self, Error> { fn parse(expr: Spanned<Expr>) -> Result<Self, Problem> {
Ok(match Ident::parse(expr)?.as_str() { Ok(match Ident::parse(expr)?.as_str() {
"left-to-right" | "ltr" | "LTR" => LeftToRight, "left-to-right" | "ltr" | "LTR" => LeftToRight,
"right-to-left" | "rtl" | "RTL" => RightToLeft, "right-to-left" | "rtl" | "RTL" => RightToLeft,
"top-to-bottom" | "ttb" | "TTB" => TopToBottom, "top-to-bottom" | "ttb" | "TTB" => TopToBottom,
"bottom-to-top" | "btt" | "BTT" => BottomToTop, "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 { impl Value for AlignmentValue {
fn parse(expr: Spanned<Expr>) -> Result<Self, Error> { fn parse(expr: Spanned<Expr>) -> Result<Self, Problem> {
Ok(match Ident::parse(expr)?.as_str() { Ok(match Ident::parse(expr)?.as_str() {
"origin" => Align(Origin), "origin" => Align(Origin),
"center" => Align(Center), "center" => Align(Center),
@ -257,7 +259,7 @@ impl Value for AlignmentValue {
"top" => Top, "top" => Top,
"right" => Right, "right" => Right,
"bottom" => Bottom, "bottom" => Bottom,
_ => return Err(err!("invalid alignment")) _ => return Err(error!("invalid alignment"))
}) })
} }
} }

View File

@ -49,8 +49,7 @@ pub fn parse(start: Position, src: &str, ctx: ParseContext) -> Pass<SyntaxModel>
feedback.extend_offset(span.start, parsed.feedback); feedback.extend_offset(span.start, parsed.feedback);
if !terminated { if !terminated {
feedback.errors.push(err!(Span::at(span.end); error!(@feedback, Span::at(span.end), "expected closing bracket");
"expected closing bracket"));
} }
parsed.output parsed.output
@ -62,8 +61,7 @@ pub fn parse(start: Position, src: &str, ctx: ParseContext) -> Pass<SyntaxModel>
Token::Raw { raw, terminated } => { Token::Raw { raw, terminated } => {
if !terminated { if !terminated {
feedback.errors.push(err!(Span::at(span.end); error!(@feedback, Span::at(span.end), "expected backtick");
"expected backtick"));
} }
Node::Raw(unescape_raw(raw)) Node::Raw(unescape_raw(raw))
@ -72,7 +70,7 @@ pub fn parse(start: Position, src: &str, ctx: ParseContext) -> Pass<SyntaxModel>
Token::Text(text) => Node::Text(text.to_string()), Token::Text(text) => Node::Text(text.to_string()),
other => { other => {
feedback.errors.push(err!(span; "unexpected {}", other.name())); error!(@feedback, span, "unexpected {}", other.name());
continue; continue;
} }
}; };
@ -129,7 +127,7 @@ impl<'s> FuncParser<'s> {
// The fallback parser was returned. Invalid function. // The fallback parser was returned. Invalid function.
Err(parser) => { Err(parser) => {
self.feedback.errors.push(err!(header.name.span; "unknown function")); error!(@self.feedback, header.name.span, "unknown function");
(parser, Decoration::InvalidFuncName) (parser, Decoration::InvalidFuncName)
} }
}; };
@ -270,10 +268,10 @@ impl<'s> FuncParser<'s> {
let expr = binop(Box::new(o1), Box::new(o2)); let expr = binop(Box::new(o1), Box::new(o2));
return Some(Spanned::new(expr, span)); return Some(Spanned::new(expr, span));
} else { } else {
self.feedback.errors.push(err!( error!(
Span::merge(next.span, o1.span); @self.feedback, Span::merge(next.span, o1.span),
"missing right {}", operand_name, "missing right {}", operand_name,
)); );
} }
} }
} }
@ -292,7 +290,7 @@ impl<'s> FuncParser<'s> {
let span = Span::merge(first.span, factor.span); let span = Span::merge(first.span, factor.span);
Some(Spanned::new(Expr::Neg(Box::new(factor)), span)) Some(Spanned::new(Expr::Neg(Box::new(factor)), span))
} else { } else {
self.feedback.errors.push(err!(first.span; "dangling minus")); error!(@self.feedback, first.span, "dangling minus");
None None
} }
} else { } else {
@ -333,7 +331,7 @@ impl<'s> FuncParser<'s> {
take!(Expr::Color(color)) take!(Expr::Color(color))
} else { } else {
// Heal color by assuming black // 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))) 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 /// 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.feedback.errors.push(err!(found.span; error!(
"expected {}, found {}", thing, found.v.name())); @self.feedback, found.span,
"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.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 /// Add a expected-found-error if `found` is `Some` and an expected-error
@ -726,7 +726,7 @@ mod tests {
p!($source => [$($model)*], []); p!($source => [$($model)*], []);
}; };
($source:expr => [$($model:tt)*], [$($errors:tt)*] $(, [$($decos:tt)*])? $(,)?) => { ($source:expr => [$($model:tt)*], [$($problems:tt)*] $(, [$($decos:tt)*])? $(,)?) => {
let mut scope = Scope::new::<DebugFn>(); let mut scope = Scope::new::<DebugFn>();
scope.add::<DebugFn>("f"); scope.add::<DebugFn>("f");
scope.add::<DebugFn>("n"); scope.add::<DebugFn>("n");
@ -740,12 +740,12 @@ mod tests {
let (exp, cmp) = spanned![vec $($model)*]; let (exp, cmp) = spanned![vec $($model)*];
check($source, exp, pass.output.nodes, cmp); check($source, exp, pass.output.nodes, cmp);
// Test errors // Test problems
let (exp, cmp) = spanned![vec $($errors)*]; let (exp, cmp) = spanned![vec $($problems)*];
let exp = exp.into_iter() let exp = exp.into_iter()
.map(|s: Spanned<&str>| s.map(|e| e.to_string())) .map(|s: Spanned<&str>| s.map(|e| e.to_string()))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let found = pass.feedback.errors.into_iter() let found = pass.feedback.problems.into_iter()
.map(|s| s.map(|e| e.message)) .map(|s| s.map(|e| e.message))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
check($source, exp, found, cmp); check($source, exp, found, cmp);

View File

@ -76,7 +76,7 @@ pub struct Span {
} }
impl Span { impl Span {
/// A dummy span. /// The zero span.
pub const ZERO: Span = Span { start: Position::ZERO, end: Position::ZERO }; pub const ZERO: Span = Span { start: Position::ZERO, end: Position::ZERO };
/// Create a new span from start and end positions. /// Create a new span from start and end positions.

View File

@ -29,7 +29,7 @@ where T: Debug + PartialEq + SpanlessEq {
/// spanned![(0:0, 0:5, "hello"), (0:5, 0:3, "world")] /// spanned![(0:0, 0:5, "hello"), (0:5, 0:3, "world")]
/// ``` /// ```
/// The span information can simply be omitted to create a vector with items /// 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 { macro_rules! spanned {
(item ($sl:tt:$sc:tt, $el:tt:$ec:tt, $v:expr)) => ({ (item ($sl:tt:$sc:tt, $el:tt:$ec:tt, $v:expr)) => ({
#[allow(unused_imports)] #[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. /// Compares elements by only looking at values and ignoring spans.

View File

@ -123,14 +123,14 @@ fn test(name: &str, src: &str) -> DynResult<()> {
fn compile(typesetter: &Typesetter, src: &str) -> MultiLayout { fn compile(typesetter: &Typesetter, src: &str) -> MultiLayout {
if cfg!(debug_assertions) { if cfg!(debug_assertions) {
let typeset = block_on(typesetter.typeset(src)); let typeset = block_on(typesetter.typeset(src));
let errors = typeset.feedback.errors; let problems = typeset.feedback.problems;
if !errors.is_empty() { if !problems.is_empty() {
for error in errors { for problem in problems {
println!(" {:?} {:?}: {}", println!(" {:?} {:?}: {}",
error.v.severity, problem.v.severity,
error.span, problem.span,
error.v.message problem.v.message
); );
} }
} }