Set, show, wrap in code blocks

This commit is contained in:
Laurenz 2022-05-09 00:03:57 +02:00
parent bfaf5447a7
commit 1927cc86da
16 changed files with 372 additions and 202 deletions

View File

@ -3,7 +3,7 @@ use std::fmt::{self, Debug, Formatter, Write};
use std::ops::{Add, AddAssign}; use std::ops::{Add, AddAssign};
use std::sync::Arc; use std::sync::Arc;
use super::{Args, Func, Value}; use super::{ops, Args, Func, Value};
use crate::diag::{At, StrResult, TypResult}; use crate::diag::{At, StrResult, TypResult};
use crate::syntax::Spanned; use crate::syntax::Spanned;
use crate::util::ArcExt; use crate::util::ArcExt;
@ -171,18 +171,14 @@ impl Array {
let mut result = Value::None; let mut result = Value::None;
for (i, value) in self.iter().cloned().enumerate() { for (i, value) in self.iter().cloned().enumerate() {
if i > 0 { if i > 0 {
if i + 1 == len { if i + 1 == len && last.is_some() {
if let Some(last) = last.take() { result = ops::join(result, last.take().unwrap())?;
result = result.join(last)?;
} else {
result = result.join(sep.clone())?;
}
} else { } else {
result = result.join(sep.clone())?; result = ops::join(result, sep.clone())?;
} }
} }
result = result.join(value)?; result = ops::join(result, value)?;
} }
Ok(result) Ok(result)

View File

@ -1,65 +0,0 @@
use super::{ops, EvalResult, Value};
use crate::diag::{At, 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<TypError> for Control {
fn from(error: TypError) -> Self {
Self::Err(error)
}
}
impl From<Control> for TypError {
fn from(control: Control) -> Self {
match control {
Control::Break(_, span) => {
error!(span, "cannot break outside of loop")
}
Control::Continue(_, span) => {
error!(span, "cannot continue outside of loop")
}
Control::Return(_, _, span) => {
error!(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<Value>,
result_span: Span,
) -> EvalResult<Value> {
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,
}
}

View File

@ -2,7 +2,7 @@ use std::fmt::{self, Debug, Formatter};
use std::hash::{Hash, Hasher}; use std::hash::{Hash, Hasher};
use std::sync::Arc; use std::sync::Arc;
use super::{Args, Control, Eval, Scope, Scopes, Value}; use super::{Args, Eval, Flow, Scope, Scopes, Value};
use crate::diag::{StrResult, TypResult}; use crate::diag::{StrResult, TypResult};
use crate::model::{Content, NodeId, StyleMap}; use crate::model::{Content, NodeId, StyleMap};
use crate::syntax::ast::Expr; use crate::syntax::ast::Expr;
@ -210,11 +210,19 @@ impl Closure {
scp.top.def_mut(sink, args.take()); scp.top.def_mut(sink, args.take());
} }
// Backup the old control flow state.
let prev_flow = ctx.flow.take();
// Evaluate the body. // Evaluate the body.
let value = match self.body.eval(ctx, &mut scp) { let mut value = self.body.eval(ctx, &mut scp)?;
Err(Control::Return(value, _, _)) => value,
other => other?, // Handle control flow.
}; match std::mem::replace(&mut ctx.flow, prev_flow) {
Some(Flow::Return(_, Some(explicit))) => value = explicit,
Some(Flow::Return(_, None)) => {}
Some(flow) => return Err(flow.forbidden())?,
None => {}
}
Ok(value) Ok(value)
} }

View File

@ -9,10 +9,8 @@ mod value;
mod args; mod args;
mod capture; mod capture;
mod control;
mod func; mod func;
pub mod methods; pub mod methods;
mod module;
pub mod ops; pub mod ops;
mod raw; mod raw;
mod scope; mod scope;
@ -22,10 +20,8 @@ pub use self::str::*;
pub use args::*; pub use args::*;
pub use array::*; pub use array::*;
pub use capture::*; pub use capture::*;
pub use control::*;
pub use dict::*; pub use dict::*;
pub use func::*; pub use func::*;
pub use module::*;
pub use raw::*; pub use raw::*;
pub use scope::*; pub use scope::*;
pub use value::*; pub use value::*;
@ -35,10 +31,11 @@ use std::collections::BTreeMap;
use parking_lot::{MappedRwLockWriteGuard, RwLockWriteGuard}; use parking_lot::{MappedRwLockWriteGuard, RwLockWriteGuard};
use unicode_segmentation::UnicodeSegmentation; use unicode_segmentation::UnicodeSegmentation;
use crate::diag::{At, StrResult, Trace, Tracepoint, TypResult}; use crate::diag::{At, StrResult, Trace, Tracepoint, TypError, TypResult};
use crate::geom::{Angle, Em, Fraction, Length, Ratio}; use crate::geom::{Angle, Em, Fraction, Length, Ratio};
use crate::library; use crate::library;
use crate::model::{Content, Pattern, Recipe, StyleEntry, StyleMap}; use crate::model::{Content, Pattern, Recipe, StyleEntry, StyleMap};
use crate::source::{SourceId, SourceStore};
use crate::syntax::ast::*; use crate::syntax::ast::*;
use crate::syntax::{Span, Spanned}; use crate::syntax::{Span, Spanned};
use crate::util::EcoString; use crate::util::EcoString;
@ -50,16 +47,60 @@ pub trait Eval {
type Output; type Output;
/// Evaluate the expression to the output value. /// Evaluate the expression to the output value.
fn eval(&self, ctx: &mut Context, scp: &mut Scopes) -> EvalResult<Self::Output>; fn eval(&self, ctx: &mut Context, scp: &mut Scopes) -> TypResult<Self::Output>;
} }
/// The result type for evaluating a syntactic construct. /// An evaluated module, ready for importing or layouting.
pub type EvalResult<T> = Result<T, Control>; #[derive(Debug, Clone)]
pub struct Module {
/// The top-level definitions that were bound in this module.
pub scope: Scope,
/// The module's layoutable contents.
pub content: Content,
/// The source file revisions this module depends on.
pub deps: Vec<(SourceId, usize)>,
}
impl Module {
/// Whether the module is still valid for the given sources.
pub fn valid(&self, sources: &SourceStore) -> bool {
self.deps.iter().all(|&(id, rev)| rev == sources.get(id).rev())
}
}
/// A control flow event that occurred during evaluation.
#[derive(Debug, Clone, PartialEq)]
pub enum Flow {
/// Stop iteration in a loop.
Break(Span),
/// Skip the remainder of the current iteration in a loop.
Continue(Span),
/// Stop execution of a function early, optionally returning an explicit
/// value.
Return(Span, Option<Value>),
}
impl Flow {
/// Return an error stating that this control flow is forbidden.
pub fn forbidden(&self) -> TypError {
match *self {
Self::Break(span) => {
error!(span, "cannot break outside of loop")
}
Self::Continue(span) => {
error!(span, "cannot continue outside of loop")
}
Self::Return(span, _) => {
error!(span, "cannot return outside of function")
}
}
}
}
impl Eval for Markup { impl Eval for Markup {
type Output = Content; type Output = Content;
fn eval(&self, ctx: &mut Context, scp: &mut Scopes) -> EvalResult<Self::Output> { fn eval(&self, ctx: &mut Context, scp: &mut Scopes) -> TypResult<Self::Output> {
eval_markup(ctx, scp, &mut self.nodes()) eval_markup(ctx, scp, &mut self.nodes())
} }
} }
@ -69,17 +110,25 @@ fn eval_markup(
ctx: &mut Context, ctx: &mut Context,
scp: &mut Scopes, scp: &mut Scopes,
nodes: &mut impl Iterator<Item = MarkupNode>, nodes: &mut impl Iterator<Item = MarkupNode>,
) -> EvalResult<Content> { ) -> TypResult<Content> {
let mut seq = Vec::with_capacity(nodes.size_hint().1.unwrap_or_default()); let mut seq = Vec::with_capacity(nodes.size_hint().1.unwrap_or_default());
while let Some(node) = nodes.next() { while let Some(node) = nodes.next() {
seq.push(match node { seq.push(match node {
MarkupNode::Expr(Expr::Set(set)) => { MarkupNode::Expr(Expr::Set(set)) => {
let styles = set.eval(ctx, scp)?; let styles = set.eval(ctx, scp)?;
if ctx.flow.is_some() {
break;
}
eval_markup(ctx, scp, nodes)?.styled_with_map(styles) eval_markup(ctx, scp, nodes)?.styled_with_map(styles)
} }
MarkupNode::Expr(Expr::Show(show)) => { MarkupNode::Expr(Expr::Show(show)) => {
let recipe = show.eval(ctx, scp)?; let recipe = show.eval(ctx, scp)?;
if ctx.flow.is_some() {
break;
}
eval_markup(ctx, scp, nodes)? eval_markup(ctx, scp, nodes)?
.styled_with_entry(StyleEntry::Recipe(recipe).into()) .styled_with_entry(StyleEntry::Recipe(recipe).into())
} }
@ -88,8 +137,13 @@ fn eval_markup(
scp.top.def_mut(wrap.binding().take(), tail); scp.top.def_mut(wrap.binding().take(), tail);
wrap.body().eval(ctx, scp)?.display() wrap.body().eval(ctx, scp)?.display()
} }
_ => node.eval(ctx, scp)?, _ => node.eval(ctx, scp)?,
}); });
if ctx.flow.is_some() {
break;
}
} }
Ok(Content::sequence(seq)) Ok(Content::sequence(seq))
@ -98,7 +152,7 @@ fn eval_markup(
impl Eval for MarkupNode { impl Eval for MarkupNode {
type Output = Content; type Output = Content;
fn eval(&self, ctx: &mut Context, scp: &mut Scopes) -> EvalResult<Self::Output> { fn eval(&self, ctx: &mut Context, scp: &mut Scopes) -> TypResult<Self::Output> {
Ok(match self { Ok(match self {
Self::Space => Content::Space, Self::Space => Content::Space,
Self::Parbreak => Content::Parbreak, Self::Parbreak => Content::Parbreak,
@ -120,7 +174,7 @@ impl Eval for MarkupNode {
impl Eval for StrongNode { impl Eval for StrongNode {
type Output = Content; type Output = Content;
fn eval(&self, ctx: &mut Context, scp: &mut Scopes) -> EvalResult<Self::Output> { fn eval(&self, ctx: &mut Context, scp: &mut Scopes) -> TypResult<Self::Output> {
Ok(Content::show(library::text::StrongNode( Ok(Content::show(library::text::StrongNode(
self.body().eval(ctx, scp)?, self.body().eval(ctx, scp)?,
))) )))
@ -130,7 +184,7 @@ impl Eval for StrongNode {
impl Eval for EmphNode { impl Eval for EmphNode {
type Output = Content; type Output = Content;
fn eval(&self, ctx: &mut Context, scp: &mut Scopes) -> EvalResult<Self::Output> { fn eval(&self, ctx: &mut Context, scp: &mut Scopes) -> TypResult<Self::Output> {
Ok(Content::show(library::text::EmphNode( Ok(Content::show(library::text::EmphNode(
self.body().eval(ctx, scp)?, self.body().eval(ctx, scp)?,
))) )))
@ -140,7 +194,7 @@ impl Eval for EmphNode {
impl Eval for RawNode { impl Eval for RawNode {
type Output = Content; type Output = Content;
fn eval(&self, _: &mut Context, _: &mut Scopes) -> EvalResult<Self::Output> { fn eval(&self, _: &mut Context, _: &mut Scopes) -> TypResult<Self::Output> {
let content = Content::show(library::text::RawNode { let content = Content::show(library::text::RawNode {
text: self.text.clone(), text: self.text.clone(),
block: self.block, block: self.block,
@ -155,7 +209,7 @@ impl Eval for RawNode {
impl Eval for MathNode { impl Eval for MathNode {
type Output = Content; type Output = Content;
fn eval(&self, _: &mut Context, _: &mut Scopes) -> EvalResult<Self::Output> { fn eval(&self, _: &mut Context, _: &mut Scopes) -> TypResult<Self::Output> {
Ok(Content::show(library::math::MathNode { Ok(Content::show(library::math::MathNode {
formula: self.formula.clone(), formula: self.formula.clone(),
display: self.display, display: self.display,
@ -166,7 +220,7 @@ impl Eval for MathNode {
impl Eval for HeadingNode { impl Eval for HeadingNode {
type Output = Content; type Output = Content;
fn eval(&self, ctx: &mut Context, scp: &mut Scopes) -> EvalResult<Self::Output> { fn eval(&self, ctx: &mut Context, scp: &mut Scopes) -> TypResult<Self::Output> {
Ok(Content::show(library::structure::HeadingNode { Ok(Content::show(library::structure::HeadingNode {
body: self.body().eval(ctx, scp)?, body: self.body().eval(ctx, scp)?,
level: self.level(), level: self.level(),
@ -177,7 +231,7 @@ impl Eval for HeadingNode {
impl Eval for ListNode { impl Eval for ListNode {
type Output = Content; type Output = Content;
fn eval(&self, ctx: &mut Context, scp: &mut Scopes) -> EvalResult<Self::Output> { fn eval(&self, ctx: &mut Context, scp: &mut Scopes) -> TypResult<Self::Output> {
Ok(Content::Item(library::structure::ListItem { Ok(Content::Item(library::structure::ListItem {
kind: library::structure::UNORDERED, kind: library::structure::UNORDERED,
number: None, number: None,
@ -189,7 +243,7 @@ impl Eval for ListNode {
impl Eval for EnumNode { impl Eval for EnumNode {
type Output = Content; type Output = Content;
fn eval(&self, ctx: &mut Context, scp: &mut Scopes) -> EvalResult<Self::Output> { fn eval(&self, ctx: &mut Context, scp: &mut Scopes) -> TypResult<Self::Output> {
Ok(Content::Item(library::structure::ListItem { Ok(Content::Item(library::structure::ListItem {
kind: library::structure::ORDERED, kind: library::structure::ORDERED,
number: self.number(), number: self.number(),
@ -201,7 +255,14 @@ impl Eval for EnumNode {
impl Eval for Expr { impl Eval for Expr {
type Output = Value; type Output = Value;
fn eval(&self, ctx: &mut Context, scp: &mut Scopes) -> EvalResult<Self::Output> { fn eval(&self, ctx: &mut Context, scp: &mut Scopes) -> TypResult<Self::Output> {
let forbidden = |name| {
error!(
self.span(),
"{} is only allowed directly in code and content blocks", name
)
};
match self { match self {
Self::Lit(v) => v.eval(ctx, scp), Self::Lit(v) => v.eval(ctx, scp),
Self::Ident(v) => v.eval(ctx, scp), Self::Ident(v) => v.eval(ctx, scp),
@ -217,11 +278,9 @@ impl Eval for Expr {
Self::Unary(v) => v.eval(ctx, scp), Self::Unary(v) => v.eval(ctx, scp),
Self::Binary(v) => v.eval(ctx, scp), Self::Binary(v) => v.eval(ctx, scp),
Self::Let(v) => v.eval(ctx, scp), Self::Let(v) => v.eval(ctx, scp),
Self::Set(_) | Self::Show(_) | Self::Wrap(_) => { Self::Set(_) => Err(forbidden("set")),
Err("set, show and wrap are only allowed directly in markup") Self::Show(_) => Err(forbidden("show")),
.at(self.span()) Self::Wrap(_) => Err(forbidden("wrap")),
.map_err(Into::into)
}
Self::If(v) => v.eval(ctx, scp), Self::If(v) => v.eval(ctx, scp),
Self::While(v) => v.eval(ctx, scp), Self::While(v) => v.eval(ctx, scp),
Self::For(v) => v.eval(ctx, scp), Self::For(v) => v.eval(ctx, scp),
@ -237,7 +296,7 @@ impl Eval for Expr {
impl Eval for Lit { impl Eval for Lit {
type Output = Value; type Output = Value;
fn eval(&self, _: &mut Context, _: &mut Scopes) -> EvalResult<Self::Output> { fn eval(&self, _: &mut Context, _: &mut Scopes) -> TypResult<Self::Output> {
Ok(match self.kind() { Ok(match self.kind() {
LitKind::None => Value::None, LitKind::None => Value::None,
LitKind::Auto => Value::Auto, LitKind::Auto => Value::Auto,
@ -259,7 +318,7 @@ impl Eval for Lit {
impl Eval for Ident { impl Eval for Ident {
type Output = Value; type Output = Value;
fn eval(&self, _: &mut Context, scp: &mut Scopes) -> EvalResult<Self::Output> { fn eval(&self, _: &mut Context, scp: &mut Scopes) -> TypResult<Self::Output> {
match scp.get(self) { match scp.get(self) {
Some(slot) => Ok(slot.read().clone()), Some(slot) => Ok(slot.read().clone()),
None => bail!(self.span(), "unknown variable"), None => bail!(self.span(), "unknown variable"),
@ -270,23 +329,75 @@ impl Eval for Ident {
impl Eval for CodeBlock { impl Eval for CodeBlock {
type Output = Value; type Output = Value;
fn eval(&self, ctx: &mut Context, scp: &mut Scopes) -> EvalResult<Self::Output> { fn eval(&self, ctx: &mut Context, scp: &mut Scopes) -> TypResult<Self::Output> {
scp.enter(); scp.enter();
let output = eval_code(ctx, scp, &mut self.exprs())?;
let mut output = Value::None;
for expr in self.exprs() {
output = join_result(output, expr.eval(ctx, scp), expr.span())?;
}
scp.exit(); scp.exit();
Ok(output) Ok(output)
} }
} }
/// Evaluate a stream of expressions.
fn eval_code(
ctx: &mut Context,
scp: &mut Scopes,
exprs: &mut impl Iterator<Item = Expr>,
) -> TypResult<Value> {
let mut output = Value::None;
while let Some(expr) = exprs.next() {
let span = expr.span();
let value = match expr {
Expr::Set(set) => {
let styles = set.eval(ctx, scp)?;
if ctx.flow.is_some() {
break;
}
let tail = to_content(eval_code(ctx, scp, exprs)?).at(span)?;
Value::Content(tail.styled_with_map(styles))
}
Expr::Show(show) => {
let recipe = show.eval(ctx, scp)?;
let entry = StyleEntry::Recipe(recipe).into();
if ctx.flow.is_some() {
break;
}
let tail = to_content(eval_code(ctx, scp, exprs)?).at(span)?;
Value::Content(tail.styled_with_entry(entry))
}
Expr::Wrap(wrap) => {
let tail = to_content(eval_code(ctx, scp, exprs)?).at(span)?;
scp.top.def_mut(wrap.binding().take(), Value::Content(tail));
wrap.body().eval(ctx, scp)?
}
_ => expr.eval(ctx, scp)?,
};
output = ops::join(output, value).at(span)?;
if ctx.flow.is_some() {
break;
}
}
Ok(output)
}
/// Extract content from a value.
fn to_content(value: Value) -> StrResult<Content> {
let ty = value.type_name();
value
.cast()
.map_err(|_| format!("expected remaining block to yield content, found {ty}"))
}
impl Eval for ContentBlock { impl Eval for ContentBlock {
type Output = Content; type Output = Content;
fn eval(&self, ctx: &mut Context, scp: &mut Scopes) -> EvalResult<Self::Output> { fn eval(&self, ctx: &mut Context, scp: &mut Scopes) -> TypResult<Self::Output> {
scp.enter(); scp.enter();
let content = self.body().eval(ctx, scp)?; let content = self.body().eval(ctx, scp)?;
scp.exit(); scp.exit();
@ -297,7 +408,7 @@ impl Eval for ContentBlock {
impl Eval for GroupExpr { impl Eval for GroupExpr {
type Output = Value; type Output = Value;
fn eval(&self, ctx: &mut Context, scp: &mut Scopes) -> EvalResult<Self::Output> { fn eval(&self, ctx: &mut Context, scp: &mut Scopes) -> TypResult<Self::Output> {
self.expr().eval(ctx, scp) self.expr().eval(ctx, scp)
} }
} }
@ -305,7 +416,7 @@ impl Eval for GroupExpr {
impl Eval for ArrayExpr { impl Eval for ArrayExpr {
type Output = Array; type Output = Array;
fn eval(&self, ctx: &mut Context, scp: &mut Scopes) -> EvalResult<Self::Output> { fn eval(&self, ctx: &mut Context, scp: &mut Scopes) -> TypResult<Self::Output> {
let items = self.items(); let items = self.items();
let mut vec = Vec::with_capacity(items.size_hint().0); let mut vec = Vec::with_capacity(items.size_hint().0);
@ -327,7 +438,7 @@ impl Eval for ArrayExpr {
impl Eval for DictExpr { impl Eval for DictExpr {
type Output = Dict; type Output = Dict;
fn eval(&self, ctx: &mut Context, scp: &mut Scopes) -> EvalResult<Self::Output> { fn eval(&self, ctx: &mut Context, scp: &mut Scopes) -> TypResult<Self::Output> {
let mut map = BTreeMap::new(); let mut map = BTreeMap::new();
for item in self.items() { for item in self.items() {
@ -357,7 +468,7 @@ impl Eval for DictExpr {
impl Eval for UnaryExpr { impl Eval for UnaryExpr {
type Output = Value; type Output = Value;
fn eval(&self, ctx: &mut Context, scp: &mut Scopes) -> EvalResult<Self::Output> { fn eval(&self, ctx: &mut Context, scp: &mut Scopes) -> TypResult<Self::Output> {
let value = self.expr().eval(ctx, scp)?; let value = self.expr().eval(ctx, scp)?;
let result = match self.op() { let result = match self.op() {
UnOp::Pos => ops::pos(value), UnOp::Pos => ops::pos(value),
@ -371,7 +482,7 @@ impl Eval for UnaryExpr {
impl Eval for BinaryExpr { impl Eval for BinaryExpr {
type Output = Value; type Output = Value;
fn eval(&self, ctx: &mut Context, scp: &mut Scopes) -> EvalResult<Self::Output> { fn eval(&self, ctx: &mut Context, scp: &mut Scopes) -> TypResult<Self::Output> {
match self.op() { match self.op() {
BinOp::Add => self.apply(ctx, scp, ops::add), BinOp::Add => self.apply(ctx, scp, ops::add),
BinOp::Sub => self.apply(ctx, scp, ops::sub), BinOp::Sub => self.apply(ctx, scp, ops::sub),
@ -403,7 +514,7 @@ impl BinaryExpr {
ctx: &mut Context, ctx: &mut Context,
scp: &mut Scopes, scp: &mut Scopes,
op: fn(Value, Value) -> StrResult<Value>, op: fn(Value, Value) -> StrResult<Value>,
) -> EvalResult<Value> { ) -> TypResult<Value> {
let lhs = self.lhs().eval(ctx, scp)?; let lhs = self.lhs().eval(ctx, scp)?;
// Short-circuit boolean operations. // Short-circuit boolean operations.
@ -423,7 +534,7 @@ impl BinaryExpr {
ctx: &mut Context, ctx: &mut Context,
scp: &mut Scopes, scp: &mut Scopes,
op: fn(Value, Value) -> StrResult<Value>, op: fn(Value, Value) -> StrResult<Value>,
) -> EvalResult<Value> { ) -> TypResult<Value> {
let rhs = self.rhs().eval(ctx, scp)?; let rhs = self.rhs().eval(ctx, scp)?;
let lhs = self.lhs(); let lhs = self.lhs();
let mut location = lhs.access(ctx, scp)?; let mut location = lhs.access(ctx, scp)?;
@ -436,7 +547,7 @@ impl BinaryExpr {
impl Eval for FieldAccess { impl Eval for FieldAccess {
type Output = Value; type Output = Value;
fn eval(&self, ctx: &mut Context, scp: &mut Scopes) -> EvalResult<Self::Output> { fn eval(&self, ctx: &mut Context, scp: &mut Scopes) -> TypResult<Self::Output> {
let object = self.object().eval(ctx, scp)?; let object = self.object().eval(ctx, scp)?;
let span = self.field().span(); let span = self.field().span();
let field = self.field().take(); let field = self.field().take();
@ -462,7 +573,7 @@ impl Eval for FieldAccess {
impl Eval for FuncCall { impl Eval for FuncCall {
type Output = Value; type Output = Value;
fn eval(&self, ctx: &mut Context, scp: &mut Scopes) -> EvalResult<Self::Output> { fn eval(&self, ctx: &mut Context, scp: &mut Scopes) -> TypResult<Self::Output> {
let callee = self.callee().eval(ctx, scp)?; let callee = self.callee().eval(ctx, scp)?;
let args = self.args().eval(ctx, scp)?; let args = self.args().eval(ctx, scp)?;
@ -486,7 +597,7 @@ impl Eval for FuncCall {
impl Eval for MethodCall { impl Eval for MethodCall {
type Output = Value; type Output = Value;
fn eval(&self, ctx: &mut Context, scp: &mut Scopes) -> EvalResult<Self::Output> { fn eval(&self, ctx: &mut Context, scp: &mut Scopes) -> TypResult<Self::Output> {
let span = self.span(); let span = self.span();
let method = self.method(); let method = self.method();
let point = || Tracepoint::Call(Some(method.to_string())); let point = || Tracepoint::Call(Some(method.to_string()));
@ -507,7 +618,7 @@ impl Eval for MethodCall {
impl Eval for CallArgs { impl Eval for CallArgs {
type Output = Args; type Output = Args;
fn eval(&self, ctx: &mut Context, scp: &mut Scopes) -> EvalResult<Self::Output> { fn eval(&self, ctx: &mut Context, scp: &mut Scopes) -> TypResult<Self::Output> {
let mut items = Vec::new(); let mut items = Vec::new();
for arg in self.items() { for arg in self.items() {
@ -559,7 +670,7 @@ impl Eval for CallArgs {
impl Eval for ClosureExpr { impl Eval for ClosureExpr {
type Output = Value; type Output = Value;
fn eval(&self, ctx: &mut Context, scp: &mut Scopes) -> EvalResult<Self::Output> { fn eval(&self, ctx: &mut Context, scp: &mut Scopes) -> TypResult<Self::Output> {
// The closure's name is defined by its let binding if there's one. // The closure's name is defined by its let binding if there's one.
let name = self.name().map(Ident::take); let name = self.name().map(Ident::take);
@ -606,7 +717,7 @@ impl Eval for ClosureExpr {
impl Eval for LetExpr { impl Eval for LetExpr {
type Output = Value; type Output = Value;
fn eval(&self, ctx: &mut Context, scp: &mut Scopes) -> EvalResult<Self::Output> { fn eval(&self, ctx: &mut Context, scp: &mut Scopes) -> TypResult<Self::Output> {
let value = match self.init() { let value = match self.init() {
Some(expr) => expr.eval(ctx, scp)?, Some(expr) => expr.eval(ctx, scp)?,
None => Value::None, None => Value::None,
@ -619,7 +730,7 @@ impl Eval for LetExpr {
impl Eval for SetExpr { impl Eval for SetExpr {
type Output = StyleMap; type Output = StyleMap;
fn eval(&self, ctx: &mut Context, scp: &mut Scopes) -> EvalResult<Self::Output> { fn eval(&self, ctx: &mut Context, scp: &mut Scopes) -> TypResult<Self::Output> {
let target = self.target(); let target = self.target();
let target = target.eval(ctx, scp)?.cast::<Func>().at(target.span())?; let target = target.eval(ctx, scp)?.cast::<Func>().at(target.span())?;
let args = self.args().eval(ctx, scp)?; let args = self.args().eval(ctx, scp)?;
@ -630,7 +741,7 @@ impl Eval for SetExpr {
impl Eval for ShowExpr { impl Eval for ShowExpr {
type Output = Recipe; type Output = Recipe;
fn eval(&self, ctx: &mut Context, scp: &mut Scopes) -> EvalResult<Self::Output> { fn eval(&self, ctx: &mut Context, scp: &mut Scopes) -> TypResult<Self::Output> {
// Evaluate the target function. // Evaluate the target function.
let pattern = self.pattern(); let pattern = self.pattern();
let pattern = pattern.eval(ctx, scp)?.cast::<Pattern>().at(pattern.span())?; let pattern = pattern.eval(ctx, scp)?.cast::<Pattern>().at(pattern.span())?;
@ -666,7 +777,7 @@ impl Eval for ShowExpr {
impl Eval for IfExpr { impl Eval for IfExpr {
type Output = Value; type Output = Value;
fn eval(&self, ctx: &mut Context, scp: &mut Scopes) -> EvalResult<Self::Output> { fn eval(&self, ctx: &mut Context, scp: &mut Scopes) -> TypResult<Self::Output> {
let condition = self.condition(); let condition = self.condition();
if condition.eval(ctx, scp)?.cast::<bool>().at(condition.span())? { if condition.eval(ctx, scp)?.cast::<bool>().at(condition.span())? {
self.if_body().eval(ctx, scp) self.if_body().eval(ctx, scp)
@ -681,19 +792,23 @@ impl Eval for IfExpr {
impl Eval for WhileExpr { impl Eval for WhileExpr {
type Output = Value; type Output = Value;
fn eval(&self, ctx: &mut Context, scp: &mut Scopes) -> EvalResult<Self::Output> { fn eval(&self, ctx: &mut Context, scp: &mut Scopes) -> TypResult<Self::Output> {
let mut output = Value::None; let mut output = Value::None;
let condition = self.condition(); let condition = self.condition();
while condition.eval(ctx, scp)?.cast::<bool>().at(condition.span())? { while condition.eval(ctx, scp)?.cast::<bool>().at(condition.span())? {
let body = self.body(); let body = self.body();
match join_result(output, body.eval(ctx, scp), body.span()) { let value = body.eval(ctx, scp)?;
Err(Control::Break(value, _)) => { output = ops::join(output, value).at(body.span())?;
output = value;
match ctx.flow {
Some(Flow::Break(_)) => {
ctx.flow = None;
break; break;
} }
Err(Control::Continue(value, _)) => output = value, Some(Flow::Continue(_)) => ctx.flow = None,
other => output = other?, Some(Flow::Return(..)) => break,
None => {}
} }
} }
@ -704,7 +819,7 @@ impl Eval for WhileExpr {
impl Eval for ForExpr { impl Eval for ForExpr {
type Output = Value; type Output = Value;
fn eval(&self, ctx: &mut Context, scp: &mut Scopes) -> EvalResult<Self::Output> { fn eval(&self, ctx: &mut Context, scp: &mut Scopes) -> TypResult<Self::Output> {
macro_rules! iter { macro_rules! iter {
(for ($($binding:ident => $value:ident),*) in $iter:expr) => {{ (for ($($binding:ident => $value:ident),*) in $iter:expr) => {{
let mut output = Value::None; let mut output = Value::None;
@ -715,13 +830,17 @@ impl Eval for ForExpr {
$(scp.top.def_mut(&$binding, $value);)* $(scp.top.def_mut(&$binding, $value);)*
let body = self.body(); let body = self.body();
match join_result(output, body.eval(ctx, scp), body.span()) { let value = body.eval(ctx, scp)?;
Err(Control::Break(value, _)) => { output = ops::join(output, value).at(body.span())?;
output = value;
match ctx.flow {
Some(Flow::Break(_)) => {
ctx.flow = None;
break; break;
} }
Err(Control::Continue(value, _)) => output = value, Some(Flow::Continue(_)) => ctx.flow = None,
other => output = other?, Some(Flow::Return(..)) => break,
None => {}
} }
} }
@ -773,7 +892,7 @@ impl Eval for ForExpr {
impl Eval for ImportExpr { impl Eval for ImportExpr {
type Output = Value; type Output = Value;
fn eval(&self, ctx: &mut Context, scp: &mut Scopes) -> EvalResult<Self::Output> { fn eval(&self, ctx: &mut Context, scp: &mut Scopes) -> TypResult<Self::Output> {
let span = self.path().span(); let span = self.path().span();
let path = self.path().eval(ctx, scp)?.cast::<EcoString>().at(span)?; let path = self.path().eval(ctx, scp)?.cast::<EcoString>().at(span)?;
let module = import(ctx, &path, span)?; let module = import(ctx, &path, span)?;
@ -802,7 +921,7 @@ impl Eval for ImportExpr {
impl Eval for IncludeExpr { impl Eval for IncludeExpr {
type Output = Content; type Output = Content;
fn eval(&self, ctx: &mut Context, scp: &mut Scopes) -> EvalResult<Self::Output> { fn eval(&self, ctx: &mut Context, scp: &mut Scopes) -> TypResult<Self::Output> {
let span = self.path().span(); let span = self.path().span();
let path = self.path().eval(ctx, scp)?.cast::<EcoString>().at(span)?; let path = self.path().eval(ctx, scp)?.cast::<EcoString>().at(span)?;
let module = import(ctx, &path, span)?; let module = import(ctx, &path, span)?;
@ -833,30 +952,34 @@ fn import(ctx: &mut Context, path: &str, span: Span) -> TypResult<Module> {
impl Eval for BreakExpr { impl Eval for BreakExpr {
type Output = Value; type Output = Value;
fn eval(&self, _: &mut Context, _: &mut Scopes) -> EvalResult<Self::Output> { fn eval(&self, ctx: &mut Context, _: &mut Scopes) -> TypResult<Self::Output> {
Err(Control::Break(Value::default(), self.span())) if ctx.flow.is_none() {
ctx.flow = Some(Flow::Break(self.span()));
}
Ok(Value::None)
} }
} }
impl Eval for ContinueExpr { impl Eval for ContinueExpr {
type Output = Value; type Output = Value;
fn eval(&self, _: &mut Context, _: &mut Scopes) -> EvalResult<Self::Output> { fn eval(&self, ctx: &mut Context, _: &mut Scopes) -> TypResult<Self::Output> {
Err(Control::Continue(Value::default(), self.span())) if ctx.flow.is_none() {
ctx.flow = Some(Flow::Continue(self.span()));
}
Ok(Value::None)
} }
} }
impl Eval for ReturnExpr { impl Eval for ReturnExpr {
type Output = Value; type Output = Value;
fn eval(&self, ctx: &mut Context, scp: &mut Scopes) -> EvalResult<Self::Output> { fn eval(&self, ctx: &mut Context, scp: &mut Scopes) -> TypResult<Self::Output> {
let value = self.body().map(|body| body.eval(ctx, scp)).transpose()?; let value = self.body().map(|body| body.eval(ctx, scp)).transpose()?;
let explicit = value.is_some(); if ctx.flow.is_none() {
Err(Control::Return( ctx.flow = Some(Flow::Return(self.span(), value));
value.unwrap_or_default(), }
explicit, Ok(Value::None)
self.span(),
))
} }
} }
@ -867,7 +990,7 @@ pub trait Access {
&self, &self,
ctx: &mut Context, ctx: &mut Context,
scp: &'a mut Scopes, scp: &'a mut Scopes,
) -> EvalResult<Location<'a>>; ) -> TypResult<Location<'a>>;
} }
impl Access for Expr { impl Access for Expr {
@ -875,7 +998,7 @@ impl Access for Expr {
&self, &self,
ctx: &mut Context, ctx: &mut Context,
scp: &'a mut Scopes, scp: &'a mut Scopes,
) -> EvalResult<Location<'a>> { ) -> TypResult<Location<'a>> {
match self { match self {
Expr::Ident(ident) => ident.access(ctx, scp), Expr::Ident(ident) => ident.access(ctx, scp),
Expr::FuncCall(call) => call.access(ctx, scp), Expr::FuncCall(call) => call.access(ctx, scp),
@ -889,7 +1012,7 @@ impl Access for Ident {
&self, &self,
_: &mut Context, _: &mut Context,
scp: &'a mut Scopes, scp: &'a mut Scopes,
) -> EvalResult<Location<'a>> { ) -> TypResult<Location<'a>> {
match scp.get(self) { match scp.get(self) {
Some(slot) => match slot.try_write() { Some(slot) => match slot.try_write() {
Some(guard) => Ok(RwLockWriteGuard::map(guard, |v| v)), Some(guard) => Ok(RwLockWriteGuard::map(guard, |v| v)),
@ -905,7 +1028,7 @@ impl Access for FuncCall {
&self, &self,
ctx: &mut Context, ctx: &mut Context,
scp: &'a mut Scopes, scp: &'a mut Scopes,
) -> EvalResult<Location<'a>> { ) -> TypResult<Location<'a>> {
let args = self.args().eval(ctx, scp)?; let args = self.args().eval(ctx, scp)?;
let guard = self.callee().access(ctx, scp)?; let guard = self.callee().access(ctx, scp)?;
try_map(guard, |value| { try_map(guard, |value| {
@ -928,9 +1051,9 @@ impl Access for FuncCall {
type Location<'a> = MappedRwLockWriteGuard<'a, Value>; type Location<'a> = MappedRwLockWriteGuard<'a, Value>;
/// Map a reader-writer lock with a function. /// Map a reader-writer lock with a function.
fn try_map<F>(location: Location, f: F) -> EvalResult<Location> fn try_map<F>(location: Location, f: F) -> TypResult<Location>
where where
F: FnOnce(&mut Value) -> EvalResult<&mut Value>, F: FnOnce(&mut Value) -> TypResult<&mut Value>,
{ {
let mut error = None; let mut error = None;
MappedRwLockWriteGuard::try_map(location, |value| match f(value) { MappedRwLockWriteGuard::try_map(location, |value| match f(value) {

View File

@ -1,21 +0,0 @@
use super::Scope;
use crate::model::Content;
use crate::source::{SourceId, SourceStore};
/// An evaluated module, ready for importing or layouting.
#[derive(Debug, Clone)]
pub struct Module {
/// The top-level definitions that were bound in this module.
pub scope: Scope,
/// The module's layoutable contents.
pub content: Content,
/// The source file revisions this module depends on.
pub deps: Vec<(SourceId, usize)>,
}
impl Module {
/// Whether the module is still valid for the given sources.
pub fn valid(&self, sources: &SourceStore) -> bool {
self.deps.iter().all(|&(id, rev)| rev == sources.get(id).rev())
}
}

View File

@ -102,11 +102,6 @@ impl Value {
T::cast(self) T::cast(self)
} }
/// Join the value with another value.
pub fn join(self, rhs: Self) -> StrResult<Self> {
ops::join(self, rhs)
}
/// Return the debug representation of the value. /// Return the debug representation of the value.
pub fn repr(&self) -> EcoString { pub fn repr(&self) -> EcoString {
format_eco!("{:?}", self) format_eco!("{:?}", self)

View File

@ -58,7 +58,7 @@ use std::path::PathBuf;
use std::sync::Arc; use std::sync::Arc;
use crate::diag::TypResult; use crate::diag::TypResult;
use crate::eval::{Eval, Module, Scope, Scopes}; use crate::eval::{Eval, Flow, Module, Scope, Scopes};
use crate::font::FontStore; use crate::font::FontStore;
use crate::frame::Frame; use crate::frame::Frame;
use crate::image::ImageStore; use crate::image::ImageStore;
@ -88,6 +88,8 @@ pub struct Context {
route: Vec<SourceId>, route: Vec<SourceId>,
/// The dependencies of the current evaluation process. /// The dependencies of the current evaluation process.
deps: Vec<(SourceId, usize)>, deps: Vec<(SourceId, usize)>,
/// A control flow event that is currently happening.
flow: Option<Flow>,
} }
impl Context { impl Context {
@ -145,10 +147,16 @@ impl Context {
let content = ast.eval(self, &mut scp); let content = ast.eval(self, &mut scp);
self.route.pop().unwrap(); self.route.pop().unwrap();
let deps = std::mem::replace(&mut self.deps, prev); let deps = std::mem::replace(&mut self.deps, prev);
let flow = self.flow.take();
// Assemble the module. // Assemble the module.
let module = Module { scope: scp.top, content: content?, deps }; let module = Module { scope: scp.top, content: content?, deps };
// Handle unhandled flow.
if let Some(flow) = flow {
return Err(flow.forbidden());
}
// Save the evaluated module. // Save the evaluated module.
self.modules.insert(id, module.clone()); self.modules.insert(id, module.clone());
@ -213,6 +221,7 @@ impl ContextBuilder {
cache: HashMap::new(), cache: HashMap::new(),
route: vec![], route: vec![],
deps: vec![], deps: vec![],
flow: None,
} }
} }
} }

View File

@ -246,7 +246,10 @@ fn markup_node(p: &mut Parser, at_start: &mut bool) {
| NodeKind::While | NodeKind::While
| NodeKind::For | NodeKind::For
| NodeKind::Import | NodeKind::Import
| NodeKind::Include => markup_expr(p), | NodeKind::Include
| NodeKind::Break
| NodeKind::Continue
| NodeKind::Return => markup_expr(p),
// Code and content block. // Code and content block.
NodeKind::LeftBrace => code_block(p), NodeKind::LeftBrace => code_block(p),
@ -965,7 +968,7 @@ fn continue_expr(p: &mut Parser) -> ParseResult {
fn return_expr(p: &mut Parser) -> ParseResult { fn return_expr(p: &mut Parser) -> ParseResult {
p.perform(NodeKind::ReturnExpr, |p| { p.perform(NodeKind::ReturnExpr, |p| {
p.assert(NodeKind::Return); p.assert(NodeKind::Return);
if !p.eof() { if !p.at(NodeKind::Comma) && !p.eof() {
expr(p)?; expr(p)?;
} }
Ok(()) Ok(())

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 22 KiB

View File

@ -66,13 +66,28 @@
--- ---
// Test break outside of loop. // Test break outside of loop.
#let f() = { #let f() = {
// Error: 3-8 cannot break outside of loop // Error: 3-8 cannot break outside of loop
break break
} }
#f() #for i in range(1) {
f()
}
---
// Test break in function call.
#let identity(x) = x
#let out = for i in range(5) {
"A"
identity({
"B"
break
})
"C"
}
#test(out, "AB")
--- ---
// Test continue outside of loop. // Test continue outside of loop.
@ -81,5 +96,41 @@
#let x = { continue } #let x = { continue }
--- ---
// Error: 1-10 unexpected keyword `continue` // Error: 1-10 cannot continue outside of loop
#continue #continue
---
// Ref: true
// Should output `Hello World 🌎`.
#for _ in range(10) {
[Hello ]
[World {
[🌎]
break
}]
}
---
// Ref: true
// Should output `Some` in red, `Some` in blue and `Last` in green.
// Everything should be in smallcaps.
#for color in (red, blue, green, yellow) [
#set text("Roboto")
#wrap body in text(fill: color, body)
#smallcaps(if color != green [
Some
] else [
Last
#break
])
]
---
// Ref: true
// Test break in set rule.
// Should output `Hi` in blue.
#for i in range(10) {
[Hello]
set text(blue, ..break)
[Not happening]
}

View File

@ -35,10 +35,7 @@
#let f(text, caption: none) = { #let f(text, caption: none) = {
text text
if caption == none { if caption == none [\.#return]
[\.]
return
}
[, ] [, ]
emph(caption) emph(caption)
[\.] [\.]
@ -55,3 +52,33 @@
// Error: 3-9 cannot return outside of function // Error: 3-9 cannot return outside of function
return return
} }
---
// Test that the expression is evaluated to the end.
#let y = 1
#let identity(x, ..rest) = x
#let f(x) = {
identity(
..return,
x + 1,
y = 2,
)
"nope"
}
#test(f(1), 2)
#test(y, 2)
---
// Test value return from content.
#let x = 3
#let f() = [
Hello 😀
{ x = 1 }
#return "nope"
{ x = 2 }
World
]
#test(f(), "nope")
#test(x, 1)

View File

@ -27,5 +27,15 @@ Hello *{x}*
#text(fill: forest, x) #text(fill: forest, x)
--- ---
// Error: 2-10 set, show and wrap are only allowed directly in markup // Test that scoping works as expected.
{set f(x)} {
if true {
set text(blue)
[Blue ]
}
[Not blue]
}
---
// Error: 11-25 set is only allowed directly in code and content blocks
{ let x = set text(blue) }

View File

@ -48,6 +48,33 @@ Some more text.
= Task 2 = Task 2
Another text. Another text.
---
// Test set and show in code blocks.
#show node: heading as {
set text(red)
show "ding" as [🛎]
node.body
}
= Heading
---
// Test that scoping works as expected.
{
let world = [ World ]
show c: "W" as strong(c)
world
{
set text(blue)
wrap it in {
show "o" as "Ø"
it
}
world
}
world
}
--- ---
// Error: 18-22 expected content, found integer // Error: 18-22 expected content, found integer
#show heading as 1234 #show heading as 1234
@ -67,5 +94,5 @@ Another text.
#show red as [] #show red as []
--- ---
// Error: 2-16 set, show and wrap are only allowed directly in markup // Error: 7-27 show is only allowed directly in code and content blocks
{show list as a} { 1 + show heading as none }

View File

@ -25,5 +25,12 @@ A [_B #wrap c in [*#c*]; C_] D
Forest Forest
--- ---
// Error: 6-17 set, show and wrap are only allowed directly in markup {
{1 + wrap x in y} // Error: 3-24 expected remaining block to yield content, found integer
wrap body in 2 * body
2
}
---
// Error: 4-18 wrap is only allowed directly in code and content blocks
{ (wrap body in 2) * body }