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::Binary(v) => v.with_span(self.span).eval(ctx),
|
||||||
Expr::Let(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::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
|
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.
|
// Keywords.
|
||||||
Some(Token::Let) => return expr_let(p),
|
Some(Token::Let) => return expr_let(p),
|
||||||
Some(Token::If) => return expr_if(p),
|
Some(Token::If) => return expr_if(p),
|
||||||
|
Some(Token::For) => return expr_for(p),
|
||||||
|
|
||||||
// No value.
|
// No value.
|
||||||
_ => {
|
_ => {
|
||||||
@ -417,7 +418,27 @@ fn expr_if(p: &mut Parser) -> Option<Expr> {
|
|||||||
expr_if
|
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.
|
/// Parse an identifier.
|
||||||
fn ident(p: &mut Parser) -> Option<Ident> {
|
fn ident(p: &mut Parser) -> Option<Ident> {
|
||||||
|
@ -48,6 +48,8 @@ pub enum Expr {
|
|||||||
Let(ExprLet),
|
Let(ExprLet),
|
||||||
/// An if expression: `if x { y } else { z }`.
|
/// An if expression: `if x { y } else { z }`.
|
||||||
If(ExprIf),
|
If(ExprIf),
|
||||||
|
/// A for expression: `for x in y { z }`.
|
||||||
|
For(ExprFor),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Pretty for Expr {
|
impl Pretty for Expr {
|
||||||
@ -81,6 +83,7 @@ impl Pretty for Expr {
|
|||||||
Self::Call(v) => v.pretty(p),
|
Self::Call(v) => v.pretty(p),
|
||||||
Self::Let(v) => v.pretty(p),
|
Self::Let(v) => v.pretty(p),
|
||||||
Self::If(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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::super::tests::test_pretty;
|
use super::super::tests::test_pretty;
|
||||||
@ -560,6 +584,7 @@ mod tests {
|
|||||||
// Control flow.
|
// Control flow.
|
||||||
test_pretty("#let x = 1+2", "#let x = 1 + 2");
|
test_pretty("#let x = 1+2", "#let x = 1 + 2");
|
||||||
test_pretty("#if x [y] #else [z]", "#if x [y] #else [z]");
|
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]
|
#[test]
|
||||||
|
Loading…
x
Reference in New Issue
Block a user