For loops 🔁

(does not support key-value patterns yet)
This commit is contained in:
Laurenz 2021-01-26 21:16:36 +01:00
parent e847082435
commit ed929dd10c
3 changed files with 82 additions and 0 deletions

View File

@ -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
}
}

View File

@ -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> {

View File

@ -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]