Make loops and functions react to control flow

This commit is contained in:
Martin Haug 2022-02-28 13:41:15 +01:00
parent d007788db8
commit 8e0f5993f1
4 changed files with 72 additions and 7 deletions

View File

@ -2,7 +2,7 @@ use std::fmt::{self, Debug, Formatter, Write};
use std::hash::{Hash, Hasher};
use std::sync::Arc;
use super::{Cast, Eval, Scope, Scopes, Value};
use super::{Cast, Control, Eval, Scope, Scopes, Value};
use crate::diag::{At, TypResult};
use crate::syntax::ast::Expr;
use crate::syntax::{Span, Spanned};
@ -138,7 +138,11 @@ impl Closure {
}
// Evaluate the body.
let value = self.body.eval(ctx, &mut scp)?;
let value = match self.body.eval(ctx, &mut scp) {
Ok(value) => value,
Err(Control::Return(value, _)) => return Ok(value.unwrap_or_default()),
other => other?,
};
Ok(value)
}

View File

@ -633,7 +633,12 @@ impl Eval for WhileExpr {
let condition = self.condition();
while condition.eval(ctx, scp)?.cast::<bool>().at(condition.span())? {
let body = self.body();
let value = body.eval(ctx, scp)?;
let value = match body.eval(ctx, scp) {
Ok(value) => value,
Err(Control::Break(_)) => break,
Err(Control::Continue(_)) => continue,
other => other?,
};
output = ops::join(output, value).at(body.span())?;
}
@ -654,7 +659,14 @@ impl Eval for ForExpr {
for ($($value),*) in $iter {
$(scp.top.def_mut(&$binding, $value);)*
let value = self.body().eval(ctx, scp)?;
let body = self.body();
let value = match body.eval(ctx, scp) {
Ok(value) => value,
Err(Control::Break(_)) => break,
Err(Control::Continue(_)) => continue,
other => other?,
};
output = ops::join(output, value)
.at(self.body().span())?;
}

View File

@ -2,13 +2,55 @@
// Ref: false
---
// Test break.
#let error = false
#let var = 0
#for i in range(10) {
var += i
if i > 5 {
// Error: 5-10 break is not yet implemented
break
error = true
}
}
#test(error, false)
#test(var, 21)
---
// Test continue.
#let x = 0
#let i = 0
#while x < 8 {
i += 1
if mod(i, 3) == 0 {
continue
}
x += i
}
// If continue did not work, this would equal 10.
#test(x, 12)
---
// Test break outside of loop.
#let f() = {
// Error: 3-8 cannot break outside of loop
break
}
#f()
---
// Test continue outside of loop.
// Error: 12-20 cannot continue outside of loop
#let x = { continue }
---
// Error: 1-10 unexpected keyword `continue`
#continue

View File

@ -3,8 +3,15 @@
---
#let f(x) = {
// Error: 3-15 return is not yet implemented
return x + 1
}
#f(1)
#test(f(1), 2)
---
// Test return outside of function.
#for x in range(5) {
// Error: 3-9 cannot return outside of function
return
}