Unify error types ♾

This commit is contained in:
Laurenz 2019-12-04 20:20:02 +01:00
parent 9fb31defd0
commit f72b1505be
14 changed files with 68 additions and 97 deletions

View File

@ -64,14 +64,14 @@ impl<'a> ArgParser<'a> {
if self.positional_index == self.args.positional.len() { if self.positional_index == self.args.positional.len() {
Ok(()) Ok(())
} else { } else {
pr!("unexpected argument"); error!(unexpected_argument);
} }
} }
/// Covert an option to a result with an error on `None`. /// Covert an option to a result with an error on `None`.
fn expected<T>(val: Option<Spanned<T::Output>>) -> ParseResult<Spanned<T::Output>> fn expected<T>(val: Option<Spanned<T::Output>>) -> ParseResult<Spanned<T::Output>>
where T: Argument<'a> { 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; const ERROR_MESSAGE: &'static str = $err;
fn from_expr(expr: &'a Spanned<Expression>) -> ParseResult<Spanned<Self::Output>> { fn from_expr(expr: &'a Spanned<Expression>) -> ParseResult<Spanned<Self::Output>> {
#[allow(unreachable_patterns)] #[allow(unreachable_code)]
match &expr.val { match &expr.val {
$wanted => Ok(Spanned::new($converted, expr.span)), $wanted => Ok(Spanned::new($converted, expr.span)),
_ => pr!("expected {}", $err), _ => error!("expected {}", $err),
} }
} }
} }
@ -179,7 +179,7 @@ impl AlignmentKey {
Right if horizontal => axes.right(), Right if horizontal => axes.right(),
Top if !horizontal => axes.top(), Top if !horizontal => axes.top(),
Bottom if !horizontal => axes.bottom(), Bottom if !horizontal => axes.bottom(),
_ => lr!( _ => error!(
"invalid alignment `{}` for {} axis", "invalid alignment `{}` for {} axis",
format!("{:?}", self).to_lowercase(), format!("{:?}", self).to_lowercase(),
format!("{:?}", axis).to_lowercase() format!("{:?}", axis).to_lowercase()

View File

@ -111,7 +111,7 @@ macro_rules! function {
macro_rules! parse { macro_rules! parse {
(forbidden: $body:expr) => { (forbidden: $body:expr) => {
if $body.is_some() { if $body.is_some() {
pr!("unexpected body"); error!("unexpected body");
} }
}; };
@ -127,23 +127,16 @@ macro_rules! parse {
if let Some(body) = $body { if let Some(body) = $body {
$crate::syntax::parse(body, $ctx)? $crate::syntax::parse(body, $ctx)?
} else { } else {
pr!("expected body"); error!("expected body");
} }
) )
} }
/// Early-return with a formatted parsing error or yield /// Early-return with a formatted typesetting error or construct an error
/// an error expression without returning when prefixed with `@`. /// expression without returning when prefixed with `@`.
#[macro_export] #[macro_export]
macro_rules! pr { macro_rules! error {
(@$($tts:tt)*) => ($crate::syntax::ParseError::new(format!($($tts)*))); (@unexpected_argument) => (error!(@"unexpected argument"));
($($tts:tt)*) => (return Err(pr!(@$($tts)*));); (@$($tts:tt)*) => ($crate::TypesetError::with_message(format!($($tts)*)));
} ($($tts:tt)*) => (return Err(error!(@$($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)*)););
} }

View File

@ -16,7 +16,7 @@ pub mod prelude {
pub use super::args::*; pub use super::args::*;
pub use super::{Scope, ParseFunc, LayoutFunc, Command, Commands}; pub use super::{Scope, ParseFunc, LayoutFunc, Command, Commands};
pub use crate::syntax::{SyntaxTree, FuncHeader, FuncArgs, Expression, Spanned, Span}; 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::size::{Size, Size2D, SizeBox};
pub use crate::style::{PageStyle, TextStyle}; pub use crate::style::{PageStyle, TextStyle};
pub use crate::layout::{ pub use crate::layout::{
@ -24,8 +24,7 @@ pub mod prelude {
LayoutContext, LayoutSpace, LayoutSpaces, LayoutContext, LayoutSpace, LayoutSpaces,
LayoutAxes, Axis, GenericAxisKind, SpecificAxisKind, LayoutAxes, Axis, GenericAxisKind, SpecificAxisKind,
LayoutAlignment, Alignment, LayoutAlignment, Alignment,
SpacingKind, SpacingKind, LayoutResult,
LayoutError, LayoutResult,
}; };
} }

View File

@ -230,7 +230,8 @@ impl FlexLayouter {
while size.x > self.line.usable { while size.x > self.line.usable {
if self.stack.space_is_last() { 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); self.stack.finish_space(true);

View File

@ -5,8 +5,8 @@ use std::io::{self, Write};
use smallvec::SmallVec; use smallvec::SmallVec;
use toddle::query::{FontClass, SharedFontLoader}; use toddle::query::{FontClass, SharedFontLoader};
use toddle::Error as FontError;
use crate::TypesetResult;
use crate::func::Command; use crate::func::Command;
use crate::size::{Size, Size2D, SizeBox}; use crate::size::{Size, Size2D, SizeBox};
use crate::style::{LayoutStyle, TextStyle}; 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. /// The result type for layouting.
pub type LayoutResult<T> = Result<T, LayoutError>; pub type LayoutResult<T> = TypesetResult<T>;
impl LayoutError {
/// Create a new layout error with a message.
pub fn new<S: Into<String>>(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())),
}

View File

@ -108,7 +108,8 @@ impl StackLayouter {
// Find the first (sub-)space that fits the layout. // Find the first (sub-)space that fits the layout.
while !self.sub.usable.fits(new_size) { while !self.sub.usable.fits(new_size) {
if self.space_is_last() && self.space_is_empty() { 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); self.finish_space(true);

View File

@ -116,6 +116,6 @@ impl<'a, 'p> TextLayouter<'a, 'p> {
self.classes.pop(); self.classes.pop();
} }
lr!("no suitable font for character `{}`", c); error!("no suitable font for character `{}`", c);
} }
} }

View File

@ -111,7 +111,7 @@ impl<'a, 'p> TreeLayouter<'a, 'p> {
SetTextStyle(style) => self.style.text = style, SetTextStyle(style) => self.style.text = style,
SetPageStyle(style) => { SetPageStyle(style) => {
if !self.ctx.top_level { 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; self.style.page = style;

View File

@ -18,13 +18,15 @@ pub extern crate toddle;
use std::cell::RefCell; use std::cell::RefCell;
use smallvec::smallvec; use smallvec::smallvec;
use toddle::query::{FontLoader, FontProvider, SharedFontLoader}; use toddle::query::{FontLoader, FontProvider, SharedFontLoader};
use toddle::Error as FontError;
use crate::func::Scope; use crate::func::Scope;
use crate::layout::{layout_tree, MultiLayout, LayoutContext}; use crate::layout::{layout_tree, MultiLayout, LayoutContext};
use crate::layout::{LayoutAxes, LayoutAlignment, Axis, Alignment}; use crate::layout::{LayoutAxes, LayoutAlignment, Axis, Alignment};
use crate::layout::{LayoutError, LayoutResult, LayoutSpace}; use crate::layout::{LayoutResult, LayoutSpace};
use crate::syntax::{SyntaxTree, parse, ParseContext, ParseError, ParseResult}; use crate::syntax::{parse, SyntaxTree, ParseContext, Span, ParseResult};
use crate::style::{LayoutStyle, PageStyle, TextStyle}; use crate::style::{LayoutStyle, PageStyle, TextStyle};
#[macro_use] #[macro_use]
@ -116,24 +118,31 @@ impl<'p> Typesetter<'p> {
} }
} }
/// The general error type for typesetting. /// The result type for typesetting.
pub enum TypesetError { pub type TypesetResult<T> = Result<T, TypesetError>;
/// An error that occured in the parsing step.
Parse(ParseError), /// The error type for typesetting.
/// An error that occured in the layouting step. pub struct TypesetError {
Layout(LayoutError), message: String,
span: Option<Span>,
}
impl TypesetError {
/// Create a new typesetting error.
pub fn with_message(message: String) -> TypesetError {
TypesetError { message, span: None }
}
} }
error_type! { error_type! {
err: TypesetError, err: TypesetError,
show: f => match err { show: f => {
TypesetError::Parse(e) => write!(f, "{}", e), write!(f, "{}", err.message)?;
TypesetError::Layout(e) => write!(f, "{}", e), if let Some(span) = err.span {
}, write!(f, " at {}", span)?;
source: match err { }
TypesetError::Parse(e) => Some(e), Ok(())
TypesetError::Layout(e) => Some(e), },
}, from: (std::io::Error, TypesetError::with_message(err.to_string())),
from: (ParseError, TypesetError::Parse(err)), from: (FontError, TypesetError::with_message(err.to_string())),
from: (LayoutError, TypesetError::Layout(err)),
} }

View File

@ -19,7 +19,7 @@ function! {
"vertical" => Key::Axis(AxisKey::Vertical), "vertical" => Key::Axis(AxisKey::Vertical),
"primary" => Key::Axis(AxisKey::Primary), "primary" => Key::Axis(AxisKey::Primary),
"secondary" => Key::Axis(AxisKey::Secondary), "secondary" => Key::Axis(AxisKey::Secondary),
_ => pr!("unexpected argument"), _ => error!(unexpected_argument),
}; };
let value = AlignmentKey::parse(arg.val.1.val)?; let value = AlignmentKey::parse(arg.val.1.val)?;

View File

@ -17,7 +17,7 @@ function! {
"height" | "h" => AxisKey::Vertical, "height" | "h" => AxisKey::Vertical,
"primary-size" => AxisKey::Primary, "primary-size" => AxisKey::Primary,
"secondary-size" => AxisKey::Secondary, "secondary-size" => AxisKey::Secondary,
_ => pr!("unexpected argument"), _ => error!(unexpected_argument),
}; };
let size = ArgParser::convert::<ArgSize>(arg.val.1.val)?; let size = ArgParser::convert::<ArgSize>(arg.val.1.val)?;

View File

@ -128,7 +128,7 @@ function! {
"vertical-origin" => AxisAligned(AxisKey::Vertical, Origin), "vertical-origin" => AxisAligned(AxisKey::Vertical, Origin),
"vertical-end" => AxisAligned(AxisKey::Vertical, End), "vertical-end" => AxisAligned(AxisKey::Vertical, End),
_ => pr!("unexpected argument"), _ => error!(unexpected_argument),
}; };
let size = ArgParser::convert::<ArgSize>(arg.val.1.val)?; let size = ArgParser::convert::<ArgSize>(arg.val.1.val)?;
@ -200,13 +200,13 @@ function! {
"vertical" => AxisKey::Vertical, "vertical" => AxisKey::Vertical,
"primary" => AxisKey::Primary, "primary" => AxisKey::Primary,
"secondary" => AxisKey::Secondary, "secondary" => AxisKey::Secondary,
_ => pr!("unexpected argument"), _ => error!(unexpected_argument),
}; };
let spacing = SpacingValue::from_expr(arg.val.1.val)?; let spacing = SpacingValue::from_expr(arg.val.1.val)?;
Spacing { axis, spacing } Spacing { axis, spacing }
} else { } else {
pr!("expected axis and expression") error!("expected axis and expression")
} }
}; };
@ -236,7 +236,7 @@ impl SpacingValue {
Ok(match expr.val { Ok(match expr.val {
Expression::Size(s) => SpacingValue::Absolute(*s), Expression::Size(s) => SpacingValue::Absolute(*s),
Expression::Num(f) => SpacingValue::Relative(*f as f32), Expression::Num(f) => SpacingValue::Relative(*f as f32),
_ => pr!("invalid spacing: expected size or number"), _ => error!("invalid spacing: expected size or number"),
}) })
} }
} }

View File

@ -12,7 +12,7 @@ mod span;
pub use span::{Span, Spanned}; pub use span::{Span, Spanned};
pub use tokens::{tokenize, Tokens}; 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. /// A logical unit of the incoming text stream.
#[derive(Debug, Copy, Clone, Eq, PartialEq)] #[derive(Debug, Copy, Clone, Eq, PartialEq)]

View File

@ -2,6 +2,7 @@
use unicode_xid::UnicodeXID; use unicode_xid::UnicodeXID;
use crate::TypesetResult;
use crate::func::{LayoutFunc, Scope}; use crate::func::{LayoutFunc, Scope};
use crate::size::Size; use crate::size::Size;
use super::*; use super::*;
@ -69,7 +70,7 @@ impl<'s> Parser<'s> {
match token.val { match token.val {
// Functions. // Functions.
LeftBracket => self.parse_func()?, LeftBracket => self.parse_func()?,
RightBracket => return Err(ParseError::new("unexpected closing bracket")), RightBracket => error!("unexpected closing bracket"),
// Modifiers. // Modifiers.
Underscore => self.append_consumed(Node::ToggleItalics, token.span), Underscore => self.append_consumed(Node::ToggleItalics, token.span),
@ -120,10 +121,10 @@ impl<'s> Parser<'s> {
if is_identifier(word) { if is_identifier(word) {
Ok(Spanned::new(word.to_owned(), span)) Ok(Spanned::new(word.to_owned(), span))
} else { } else {
pr!("invalid identifier: '{}'", word); error!("invalid identifier: `{}`", word);
} }
} }
_ => pr!("expected identifier"), _ => error!("expected identifier"),
}?; }?;
self.skip_white(); self.skip_white();
@ -132,7 +133,7 @@ impl<'s> Parser<'s> {
let args = match self.tokens.next().map(Spanned::value) { let args = match self.tokens.next().map(Spanned::value) {
Some(Token::RightBracket) => FuncArgs::new(), Some(Token::RightBracket) => FuncArgs::new(),
Some(Token::Colon) => self.parse_func_args()?, 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(); let end = self.tokens.string_index();
@ -158,7 +159,7 @@ impl<'s> Parser<'s> {
match self.tokens.next().map(Spanned::value) { match self.tokens.next().map(Spanned::value) {
Some(Token::Comma) => {}, Some(Token::Comma) => {},
Some(Token::RightBracket) => break, 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(); self.skip_white();
let name = token.span_map(|_| name.to_string()); 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 val = Self::parse_expression(next)?;
let span = Span::merge(name.span, val.span); let span = Span::merge(name.span, val.span);
@ -218,8 +219,7 @@ impl<'s> Parser<'s> {
Expression::Ident(text.to_owned()) Expression::Ident(text.to_owned())
} }
} }
_ => error!("expected expression"),
_ => pr!("expected expression"),
}, token.span)) }, token.span))
} }
@ -231,7 +231,7 @@ impl<'s> Parser<'s> {
.ctx .ctx
.scope .scope
.get_parser(&header.name.val) .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); 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 start = self.tokens.string_index();
let end = find_closing_bracket(&self.src[start..]) let end = find_closing_bracket(&self.src[start..])
.map(|end| start + end) .map(|end| start + end)
.ok_or_else(|| ParseError::new("expected closing bracket"))?; .ok_or_else(|| error!(@"expected closing bracket"))?;
// Parse the body. // Parse the body.
let body_string = &self.src[start..end]; let body_string = &self.src[start..end];
@ -299,7 +299,7 @@ impl<'s> Parser<'s> {
state = NewlineState::Zero; state = NewlineState::Zero;
match token.val { match token.val {
Token::LineComment(_) | Token::BlockComment(_) => self.advance(), Token::LineComment(_) | Token::BlockComment(_) => self.advance(),
Token::StarSlash => pr!("unexpected end of block comment"), Token::StarSlash => error!("unexpected end of block comment"),
_ => break, _ => break,
} }
} }
@ -431,23 +431,8 @@ fn is_identifier(string: &str) -> bool {
true true
} }
/// The error type for parsing.
pub struct ParseError(String);
/// The result type for parsing. /// The result type for parsing.
pub type ParseResult<T> = Result<T, ParseError>; pub type ParseResult<T> = TypesetResult<T>;
impl ParseError {
/// Create a new parse error with a message.
pub fn new<S: Into<String>>(message: S) -> ParseError {
ParseError(message.into())
}
}
error_type! {
err: ParseError,
show: f => f.write_str(&err.0),
}
#[cfg(test)] #[cfg(test)]