mirror of
https://github.com/typst/typst
synced 2025-05-13 12:36:23 +08:00
Set, show, wrap in code blocks
This commit is contained in:
parent
bfaf5447a7
commit
1927cc86da
@ -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)
|
||||||
|
@ -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,
|
|
||||||
}
|
|
||||||
}
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
285
src/eval/mod.rs
285
src/eval/mod.rs
@ -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) {
|
||||||
|
@ -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())
|
|
||||||
}
|
|
||||||
}
|
|
@ -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)
|
||||||
|
11
src/lib.rs
11
src/lib.rs
@ -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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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(())
|
||||||
|
BIN
tests/ref/code/break-continue.png
Normal file
BIN
tests/ref/code/break-continue.png
Normal file
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 |
@ -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]
|
||||||
|
}
|
||||||
|
@ -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)
|
||||||
|
@ -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) }
|
||||||
|
@ -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 }
|
||||||
|
@ -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 }
|
||||||
|
Loading…
x
Reference in New Issue
Block a user