summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/eval/mod.rs36
-rw-r--r--src/parse/mod.rs21
-rw-r--r--src/syntax/expr.rs25
3 files changed, 82 insertions, 0 deletions
diff --git a/src/eval/mod.rs b/src/eval/mod.rs
index eaa0b08f..27c66095 100644
--- a/src/eval/mod.rs
+++ b/src/eval/mod.rs
@@ -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
+ }
+}
diff --git a/src/parse/mod.rs b/src/parse/mod.rs
index f731cd17..ccf333b9 100644
--- a/src/parse/mod.rs
+++ b/src/parse/mod.rs
@@ -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> {
diff --git a/src/syntax/expr.rs b/src/syntax/expr.rs
index afeac988..5a57cf9a 100644
--- a/src/syntax/expr.rs
+++ b/src/syntax/expr.rs
@@ -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]