diff --git a/src/diag.rs b/src/diag.rs index 329e9a8a9..9d1ec1603 100644 --- a/src/diag.rs +++ b/src/diag.rs @@ -7,7 +7,7 @@ use crate::syntax::{Span, Spanned}; /// Early-return with a vec-boxed [`Error`]. macro_rules! bail { ($span:expr, $message:expr $(,)?) => { - return Err($crate::diag::Error::boxed($span, $message)) + return Err($crate::diag::Error::boxed($span, $message).into()) }; ($span:expr, $fmt:expr, $($arg:expr),+ $(,)?) => { @@ -16,7 +16,10 @@ macro_rules! bail { } /// The result type for typesetting and all its subpasses. -pub type TypResult = Result>>; +pub type TypResult = Result; + +/// The error type for typesetting and all its subpasses. +pub type TypError = Box>; /// A result type with a string error message. pub type StrResult = Result; diff --git a/src/eval/control.rs b/src/eval/control.rs new file mode 100644 index 000000000..b310bfb84 --- /dev/null +++ b/src/eval/control.rs @@ -0,0 +1,63 @@ +use super::{ops, EvalResult, Value}; +use crate::diag::{At, Error, TypError}; +use crate::syntax::Span; + +/// A control flow event that occurred during evaluation. +#[derive(Clone, Debug, PartialEq)] +pub enum Control { + /// Stop iteration in a loop. + Break(Value, Span), + /// Skip the remainder of the current iteration in a loop. + Continue(Value, Span), + /// Stop execution of a function early, returning a value. The bool + /// indicates whether this was an explicit return with value. + Return(Value, bool, Span), + /// Stop the execution because an error occurred. + Err(TypError), +} + +impl From for Control { + fn from(error: TypError) -> Self { + Self::Err(error) + } +} + +impl From for TypError { + fn from(control: Control) -> Self { + match control { + Control::Break(_, span) => Error::boxed(span, "cannot break outside of loop"), + Control::Continue(_, span) => { + Error::boxed(span, "cannot continue outside of loop") + } + Control::Return(_, _, span) => { + Error::boxed(span, "cannot return outside of function") + } + Control::Err(e) => e, + } + } +} + +/// Join a value with an evaluated result. +pub(super) fn join_result( + prev: Value, + result: EvalResult, + result_span: Span, +) -> EvalResult { + match result { + Ok(value) => Ok(ops::join(prev, value).at(result_span)?), + Err(Control::Break(value, span)) => Err(Control::Break( + ops::join(prev, value).at(result_span)?, + span, + )), + Err(Control::Continue(value, span)) => Err(Control::Continue( + ops::join(prev, value).at(result_span)?, + span, + )), + Err(Control::Return(value, false, span)) => Err(Control::Return( + ops::join(prev, value).at(result_span)?, + false, + span, + )), + other => other, + } +} diff --git a/src/eval/func.rs b/src/eval/func.rs index cd54a140b..451dcbbb7 100644 --- a/src/eval/func.rs +++ b/src/eval/func.rs @@ -2,7 +2,7 @@ use std::fmt::{self, Debug, Formatter, Write}; use std::hash::{Hash, Hasher}; use std::sync::Arc; -use super::{Cast, Eval, Scope, Scopes, Value}; +use super::{Cast, Control, Eval, Scope, Scopes, Value}; use crate::diag::{At, TypResult}; use crate::syntax::ast::Expr; use crate::syntax::{Span, Spanned}; @@ -138,7 +138,10 @@ impl Closure { } // Evaluate the body. - let value = self.body.eval(ctx, &mut scp)?; + let value = match self.body.eval(ctx, &mut scp) { + Err(Control::Return(value, _, _)) => value, + other => other?, + }; Ok(value) } diff --git a/src/eval/mod.rs b/src/eval/mod.rs index bc6c8fc8a..380e9e9d1 100644 --- a/src/eval/mod.rs +++ b/src/eval/mod.rs @@ -11,6 +11,7 @@ mod styles; mod capture; mod class; mod collapse; +mod control; mod func; mod layout; mod module; @@ -23,6 +24,7 @@ pub use array::*; pub use capture::*; pub use class::*; pub use collapse::*; +pub use control::*; pub use dict::*; pub use func::*; pub use layout::*; @@ -49,13 +51,16 @@ pub trait Eval { type Output; /// Evaluate the expression to the output value. - fn eval(&self, ctx: &mut Context, scp: &mut Scopes) -> TypResult; + fn eval(&self, ctx: &mut Context, scp: &mut Scopes) -> EvalResult; } +/// The result type for evaluating a syntactic construct. +pub type EvalResult = Result; + impl Eval for Markup { type Output = Template; - fn eval(&self, ctx: &mut Context, scp: &mut Scopes) -> TypResult { + fn eval(&self, ctx: &mut Context, scp: &mut Scopes) -> EvalResult { eval_markup(ctx, scp, &mut self.nodes()) } } @@ -65,7 +70,7 @@ fn eval_markup( ctx: &mut Context, scp: &mut Scopes, nodes: &mut impl Iterator, -) -> TypResult