mirror of
https://github.com/typst/typst
synced 2025-05-14 04:56:26 +08:00
For loops 🔁
(does not support key-value patterns yet)
This commit is contained in:
parent
e847082435
commit
ed929dd10c
@ -182,6 +182,7 @@ impl Eval for Spanned<&Expr> {
|
||||
Expr::Binary(v) => v.with_span(self.span).eval(ctx),
|
||||
Expr::Let(v) => v.with_span(self.span).eval(ctx),
|
||||
Expr::If(v) => v.with_span(self.span).eval(ctx),
|
||||
Expr::For(v) => v.with_span(self.span).eval(ctx),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -372,3 +373,38 @@ impl Eval for Spanned<&ExprIf> {
|
||||
Value::Error
|
||||
}
|
||||
}
|
||||
|
||||
impl Eval for Spanned<&ExprFor> {
|
||||
type Output = Value;
|
||||
|
||||
fn eval(self, ctx: &mut EvalContext) -> Self::Output {
|
||||
let iter = self.v.iter.eval(ctx);
|
||||
if let Value::Array(array) = iter {
|
||||
let mut output = match self.v.body.v {
|
||||
Expr::Template(_) => Value::Template(vec![]),
|
||||
_ => Value::None,
|
||||
};
|
||||
|
||||
for value in array {
|
||||
ctx.scopes.define(self.v.pat.v.as_str(), value);
|
||||
let value = self.v.body.eval(ctx);
|
||||
|
||||
if let Value::Template(prev) = &mut output {
|
||||
if let Value::Template(new) = value {
|
||||
prev.extend(new);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
} else if iter != Value::Error {
|
||||
ctx.diag(error!(
|
||||
self.v.iter.span,
|
||||
"expected array, found {}",
|
||||
iter.type_name(),
|
||||
));
|
||||
}
|
||||
|
||||
Value::Error
|
||||
}
|
||||
}
|
||||
|
@ -319,6 +319,7 @@ fn primary(p: &mut Parser) -> Option<Expr> {
|
||||
// Keywords.
|
||||
Some(Token::Let) => return expr_let(p),
|
||||
Some(Token::If) => return expr_if(p),
|
||||
Some(Token::For) => return expr_for(p),
|
||||
|
||||
// No value.
|
||||
_ => {
|
||||
@ -417,7 +418,27 @@ fn expr_if(p: &mut Parser) -> Option<Expr> {
|
||||
expr_if
|
||||
}
|
||||
|
||||
/// Parse a for expression.
|
||||
fn expr_for(p: &mut Parser) -> Option<Expr> {
|
||||
p.assert(Token::For);
|
||||
|
||||
let mut expr_for = None;
|
||||
if let Some(pat) = p.span_if(ident) {
|
||||
if p.expect(Token::In) {
|
||||
if let Some(iter) = p.span_if(expr) {
|
||||
if let Some(body) = p.span_if(body) {
|
||||
expr_for = Some(Expr::For(ExprFor {
|
||||
pat,
|
||||
iter: Box::new(iter),
|
||||
body: Box::new(body),
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
expr_for
|
||||
}
|
||||
|
||||
/// Parse an identifier.
|
||||
fn ident(p: &mut Parser) -> Option<Ident> {
|
||||
|
@ -48,6 +48,8 @@ pub enum Expr {
|
||||
Let(ExprLet),
|
||||
/// An if expression: `if x { y } else { z }`.
|
||||
If(ExprIf),
|
||||
/// A for expression: `for x in y { z }`.
|
||||
For(ExprFor),
|
||||
}
|
||||
|
||||
impl Pretty for Expr {
|
||||
@ -81,6 +83,7 @@ impl Pretty for Expr {
|
||||
Self::Call(v) => v.pretty(p),
|
||||
Self::Let(v) => v.pretty(p),
|
||||
Self::If(v) => v.pretty(p),
|
||||
Self::For(v) => v.pretty(p),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -519,6 +522,27 @@ impl Pretty for ExprIf {
|
||||
}
|
||||
}
|
||||
|
||||
/// A for expression: `for x in y { z }`.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct ExprFor {
|
||||
/// The pattern to assign to.
|
||||
pub pat: Spanned<Ident>,
|
||||
/// The expression to iterate over.
|
||||
pub iter: SpanBox<Expr>,
|
||||
/// The expression to evaluate for each iteration.
|
||||
pub body: SpanBox<Expr>,
|
||||
}
|
||||
|
||||
impl Pretty for ExprFor {
|
||||
fn pretty(&self, p: &mut Printer) {
|
||||
p.push_str("#for ");
|
||||
p.push_str(&self.pat.v);
|
||||
p.push_str(" #in ");
|
||||
self.iter.v.pretty(p);
|
||||
p.push_str(" ");
|
||||
self.body.v.pretty(p);
|
||||
}
|
||||
}
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::super::tests::test_pretty;
|
||||
@ -560,6 +584,7 @@ mod tests {
|
||||
// Control flow.
|
||||
test_pretty("#let x = 1+2", "#let x = 1 + 2");
|
||||
test_pretty("#if x [y] #else [z]", "#if x [y] #else [z]");
|
||||
test_pretty("#for x #in y {z}", "#for x #in y {z}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
Loading…
x
Reference in New Issue
Block a user