diff --git a/src/func/args.rs b/src/func/args.rs index 2e9e80cc1..d1d49b6a7 100644 --- a/src/func/args.rs +++ b/src/func/args.rs @@ -64,14 +64,14 @@ impl<'a> ArgParser<'a> { if self.positional_index == self.args.positional.len() { Ok(()) } else { - pr!("unexpected argument"); + error!(unexpected_argument); } } /// Covert an option to a result with an error on `None`. fn expected(val: Option>) -> ParseResult> where T: Argument<'a> { - val.ok_or_else(|| pr!(@"expected {}", T::ERROR_MESSAGE)) + val.ok_or_else(|| error!(@"expected {}", T::ERROR_MESSAGE)) } } @@ -93,10 +93,10 @@ macro_rules! arg { const ERROR_MESSAGE: &'static str = $err; fn from_expr(expr: &'a Spanned) -> ParseResult> { - #[allow(unreachable_patterns)] + #[allow(unreachable_code)] match &expr.val { $wanted => Ok(Spanned::new($converted, expr.span)), - _ => pr!("expected {}", $err), + _ => error!("expected {}", $err), } } } @@ -179,7 +179,7 @@ impl AlignmentKey { Right if horizontal => axes.right(), Top if !horizontal => axes.top(), Bottom if !horizontal => axes.bottom(), - _ => lr!( + _ => error!( "invalid alignment `{}` for {} axis", format!("{:?}", self).to_lowercase(), format!("{:?}", axis).to_lowercase() diff --git a/src/func/macros.rs b/src/func/macros.rs index 78cf1f56f..0ffdc857c 100644 --- a/src/func/macros.rs +++ b/src/func/macros.rs @@ -111,7 +111,7 @@ macro_rules! function { macro_rules! parse { (forbidden: $body:expr) => { if $body.is_some() { - pr!("unexpected body"); + error!("unexpected body"); } }; @@ -127,23 +127,16 @@ macro_rules! parse { if let Some(body) = $body { $crate::syntax::parse(body, $ctx)? } else { - pr!("expected body"); + error!("expected body"); } ) } -/// Early-return with a formatted parsing error or yield -/// an error expression without returning when prefixed with `@`. +/// Early-return with a formatted typesetting error or construct an error +/// expression without returning when prefixed with `@`. #[macro_export] -macro_rules! pr { - (@$($tts:tt)*) => ($crate::syntax::ParseError::new(format!($($tts)*))); - ($($tts:tt)*) => (return Err(pr!(@$($tts)*));); -} - -/// Early-return with a formatted layouting error or yield -/// an error expression without returning when prefixed with `@`. -#[macro_export] -macro_rules! lr { - (@$($tts:tt)*) => ($crate::layout::LayoutError::new(format!($($tts)*))); - ($($tts:tt)*) => (return Err(lr!(@$($tts)*));); +macro_rules! error { + (@unexpected_argument) => (error!(@"unexpected argument")); + (@$($tts:tt)*) => ($crate::TypesetError::with_message(format!($($tts)*))); + ($($tts:tt)*) => (return Err(error!(@$($tts)*));); } diff --git a/src/func/mod.rs b/src/func/mod.rs index b16eecb81..31e315922 100644 --- a/src/func/mod.rs +++ b/src/func/mod.rs @@ -16,7 +16,7 @@ pub mod prelude { pub use super::args::*; pub use super::{Scope, ParseFunc, LayoutFunc, Command, Commands}; pub use crate::syntax::{SyntaxTree, FuncHeader, FuncArgs, Expression, Spanned, Span}; - pub use crate::syntax::{parse, ParseContext, ParseError, ParseResult}; + pub use crate::syntax::{parse, ParseContext, ParseResult}; pub use crate::size::{Size, Size2D, SizeBox}; pub use crate::style::{PageStyle, TextStyle}; pub use crate::layout::{ @@ -24,8 +24,7 @@ pub mod prelude { LayoutContext, LayoutSpace, LayoutSpaces, LayoutAxes, Axis, GenericAxisKind, SpecificAxisKind, LayoutAlignment, Alignment, - SpacingKind, - LayoutError, LayoutResult, + SpacingKind, LayoutResult, }; } diff --git a/src/layout/flex.rs b/src/layout/flex.rs index 3e8a64e16..46d669512 100644 --- a/src/layout/flex.rs +++ b/src/layout/flex.rs @@ -230,7 +230,8 @@ impl FlexLayouter { while size.x > self.line.usable { if self.stack.space_is_last() { - lr!("box does not fit into line"); + error!("box of size {} does not fit into line of size {}", + size.x, self.line.usable); } self.stack.finish_space(true); diff --git a/src/layout/mod.rs b/src/layout/mod.rs index 4304e46e2..690e91b78 100644 --- a/src/layout/mod.rs +++ b/src/layout/mod.rs @@ -5,8 +5,8 @@ use std::io::{self, Write}; use smallvec::SmallVec; use toddle::query::{FontClass, SharedFontLoader}; -use toddle::Error as FontError; +use crate::TypesetResult; use crate::func::Command; use crate::size::{Size, Size2D, SizeBox}; use crate::style::{LayoutStyle, TextStyle}; @@ -366,22 +366,5 @@ impl Serialize for MultiLayout { } } -/// The error type for layouting. -pub struct LayoutError(String); - /// The result type for layouting. -pub type LayoutResult = Result; - -impl LayoutError { - /// Create a new layout error with a message. - pub fn new>(message: S) -> LayoutError { - LayoutError(message.into()) - } -} - -error_type! { - err: LayoutError, - show: f => f.write_str(&err.0), - from: (std::io::Error, LayoutError::new(err.to_string())), - from: (FontError, LayoutError::new(err.to_string())), -} +pub type LayoutResult = TypesetResult; diff --git a/src/layout/stack.rs b/src/layout/stack.rs index 64823b677..176aa2613 100644 --- a/src/layout/stack.rs +++ b/src/layout/stack.rs @@ -108,7 +108,8 @@ impl StackLayouter { // Find the first (sub-)space that fits the layout. while !self.sub.usable.fits(new_size) { if self.space_is_last() && self.space_is_empty() { - lr!("box does not fit into stack"); + error!("box of size {} does not fit into remaining stack of size {}", + size, self.sub.usable - Size2D::with_y(self.sub.size.y)); } self.finish_space(true); diff --git a/src/layout/text.rs b/src/layout/text.rs index 514bfe92a..8a0e7cec6 100644 --- a/src/layout/text.rs +++ b/src/layout/text.rs @@ -116,6 +116,6 @@ impl<'a, 'p> TextLayouter<'a, 'p> { self.classes.pop(); } - lr!("no suitable font for character `{}`", c); + error!("no suitable font for character `{}`", c); } } diff --git a/src/layout/tree.rs b/src/layout/tree.rs index 5fe3b6fd5..c9d40e936 100644 --- a/src/layout/tree.rs +++ b/src/layout/tree.rs @@ -111,7 +111,7 @@ impl<'a, 'p> TreeLayouter<'a, 'p> { SetTextStyle(style) => self.style.text = style, SetPageStyle(style) => { if !self.ctx.top_level { - lr!("page style cannot only be altered in the top-level context"); + error!("the page style cannot only be altered from a top-level context"); } self.style.page = style; diff --git a/src/lib.rs b/src/lib.rs index 7b6c4012e..3369d01c9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -18,13 +18,15 @@ pub extern crate toddle; use std::cell::RefCell; use smallvec::smallvec; + use toddle::query::{FontLoader, FontProvider, SharedFontLoader}; +use toddle::Error as FontError; use crate::func::Scope; use crate::layout::{layout_tree, MultiLayout, LayoutContext}; use crate::layout::{LayoutAxes, LayoutAlignment, Axis, Alignment}; -use crate::layout::{LayoutError, LayoutResult, LayoutSpace}; -use crate::syntax::{SyntaxTree, parse, ParseContext, ParseError, ParseResult}; +use crate::layout::{LayoutResult, LayoutSpace}; +use crate::syntax::{parse, SyntaxTree, ParseContext, Span, ParseResult}; use crate::style::{LayoutStyle, PageStyle, TextStyle}; #[macro_use] @@ -116,24 +118,31 @@ impl<'p> Typesetter<'p> { } } -/// The general error type for typesetting. -pub enum TypesetError { - /// An error that occured in the parsing step. - Parse(ParseError), - /// An error that occured in the layouting step. - Layout(LayoutError), +/// The result type for typesetting. +pub type TypesetResult = Result; + +/// The error type for typesetting. +pub struct TypesetError { + message: String, + span: Option, +} + +impl TypesetError { + /// Create a new typesetting error. + pub fn with_message(message: String) -> TypesetError { + TypesetError { message, span: None } + } } error_type! { err: TypesetError, - show: f => match err { - TypesetError::Parse(e) => write!(f, "{}", e), - TypesetError::Layout(e) => write!(f, "{}", e), + show: f => { + write!(f, "{}", err.message)?; + if let Some(span) = err.span { + write!(f, " at {}", span)?; + } + Ok(()) }, - source: match err { - TypesetError::Parse(e) => Some(e), - TypesetError::Layout(e) => Some(e), - }, - from: (ParseError, TypesetError::Parse(err)), - from: (LayoutError, TypesetError::Layout(err)), + from: (std::io::Error, TypesetError::with_message(err.to_string())), + from: (FontError, TypesetError::with_message(err.to_string())), } diff --git a/src/library/align.rs b/src/library/align.rs index 14e329e32..417d8f072 100644 --- a/src/library/align.rs +++ b/src/library/align.rs @@ -19,7 +19,7 @@ function! { "vertical" => Key::Axis(AxisKey::Vertical), "primary" => Key::Axis(AxisKey::Primary), "secondary" => Key::Axis(AxisKey::Secondary), - _ => pr!("unexpected argument"), + _ => error!(unexpected_argument), }; let value = AlignmentKey::parse(arg.val.1.val)?; diff --git a/src/library/boxed.rs b/src/library/boxed.rs index d4e394501..a2df45e38 100644 --- a/src/library/boxed.rs +++ b/src/library/boxed.rs @@ -17,7 +17,7 @@ function! { "height" | "h" => AxisKey::Vertical, "primary-size" => AxisKey::Primary, "secondary-size" => AxisKey::Secondary, - _ => pr!("unexpected argument"), + _ => error!(unexpected_argument), }; let size = ArgParser::convert::(arg.val.1.val)?; diff --git a/src/library/mod.rs b/src/library/mod.rs index b09303bba..02af0d830 100644 --- a/src/library/mod.rs +++ b/src/library/mod.rs @@ -128,7 +128,7 @@ function! { "vertical-origin" => AxisAligned(AxisKey::Vertical, Origin), "vertical-end" => AxisAligned(AxisKey::Vertical, End), - _ => pr!("unexpected argument"), + _ => error!(unexpected_argument), }; let size = ArgParser::convert::(arg.val.1.val)?; @@ -200,13 +200,13 @@ function! { "vertical" => AxisKey::Vertical, "primary" => AxisKey::Primary, "secondary" => AxisKey::Secondary, - _ => pr!("unexpected argument"), + _ => error!(unexpected_argument), }; let spacing = SpacingValue::from_expr(arg.val.1.val)?; Spacing { axis, spacing } } else { - pr!("expected axis and expression") + error!("expected axis and expression") } }; @@ -236,7 +236,7 @@ impl SpacingValue { Ok(match expr.val { Expression::Size(s) => SpacingValue::Absolute(*s), Expression::Num(f) => SpacingValue::Relative(*f as f32), - _ => pr!("invalid spacing: expected size or number"), + _ => error!("invalid spacing: expected size or number"), }) } } diff --git a/src/syntax/mod.rs b/src/syntax/mod.rs index 334133171..1b55fb4e8 100644 --- a/src/syntax/mod.rs +++ b/src/syntax/mod.rs @@ -12,7 +12,7 @@ mod span; pub use span::{Span, Spanned}; pub use tokens::{tokenize, Tokens}; -pub use parsing::{parse, ParseContext, ParseError, ParseResult}; +pub use parsing::{parse, ParseContext, ParseResult}; /// A logical unit of the incoming text stream. #[derive(Debug, Copy, Clone, Eq, PartialEq)] diff --git a/src/syntax/parsing.rs b/src/syntax/parsing.rs index b56094e1a..2d76b6cfb 100644 --- a/src/syntax/parsing.rs +++ b/src/syntax/parsing.rs @@ -2,6 +2,7 @@ use unicode_xid::UnicodeXID; +use crate::TypesetResult; use crate::func::{LayoutFunc, Scope}; use crate::size::Size; use super::*; @@ -69,7 +70,7 @@ impl<'s> Parser<'s> { match token.val { // Functions. LeftBracket => self.parse_func()?, - RightBracket => return Err(ParseError::new("unexpected closing bracket")), + RightBracket => error!("unexpected closing bracket"), // Modifiers. Underscore => self.append_consumed(Node::ToggleItalics, token.span), @@ -120,10 +121,10 @@ impl<'s> Parser<'s> { if is_identifier(word) { Ok(Spanned::new(word.to_owned(), span)) } else { - pr!("invalid identifier: '{}'", word); + error!("invalid identifier: `{}`", word); } } - _ => pr!("expected identifier"), + _ => error!("expected identifier"), }?; self.skip_white(); @@ -132,7 +133,7 @@ impl<'s> Parser<'s> { let args = match self.tokens.next().map(Spanned::value) { Some(Token::RightBracket) => FuncArgs::new(), Some(Token::Colon) => self.parse_func_args()?, - _ => pr!("expected arguments or closing bracket"), + _ => error!("expected arguments or closing bracket"), }; let end = self.tokens.string_index(); @@ -158,7 +159,7 @@ impl<'s> Parser<'s> { match self.tokens.next().map(Spanned::value) { Some(Token::Comma) => {}, Some(Token::RightBracket) => break, - _ => pr!("expected comma or closing bracket"), + _ => error!("expected comma or closing bracket"), } } @@ -183,7 +184,7 @@ impl<'s> Parser<'s> { self.skip_white(); let name = token.span_map(|_| name.to_string()); - let next = self.tokens.next().ok_or_else(|| pr!(@"expected value"))?; + let next = self.tokens.next().ok_or_else(|| error!(@"expected expression"))?; let val = Self::parse_expression(next)?; let span = Span::merge(name.span, val.span); @@ -218,8 +219,7 @@ impl<'s> Parser<'s> { Expression::Ident(text.to_owned()) } } - - _ => pr!("expected expression"), + _ => error!("expected expression"), }, token.span)) } @@ -231,7 +231,7 @@ impl<'s> Parser<'s> { .ctx .scope .get_parser(&header.name.val) - .ok_or_else(|| pr!(@"unknown function: '{}'", &header.name.val))?; + .ok_or_else(|| error!(@"unknown function: `{}`", &header.name.val))?; let has_body = self.tokens.peek().map(Spanned::value) == Some(Token::LeftBracket); @@ -243,7 +243,7 @@ impl<'s> Parser<'s> { let start = self.tokens.string_index(); let end = find_closing_bracket(&self.src[start..]) .map(|end| start + end) - .ok_or_else(|| ParseError::new("expected closing bracket"))?; + .ok_or_else(|| error!(@"expected closing bracket"))?; // Parse the body. let body_string = &self.src[start..end]; @@ -299,7 +299,7 @@ impl<'s> Parser<'s> { state = NewlineState::Zero; match token.val { Token::LineComment(_) | Token::BlockComment(_) => self.advance(), - Token::StarSlash => pr!("unexpected end of block comment"), + Token::StarSlash => error!("unexpected end of block comment"), _ => break, } } @@ -431,23 +431,8 @@ fn is_identifier(string: &str) -> bool { true } -/// The error type for parsing. -pub struct ParseError(String); - /// The result type for parsing. -pub type ParseResult = Result; - -impl ParseError { - /// Create a new parse error with a message. - pub fn new>(message: S) -> ParseError { - ParseError(message.into()) - } -} - -error_type! { - err: ParseError, - show: f => f.write_str(&err.0), -} +pub type ParseResult = TypesetResult; #[cfg(test)]