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::hash::{Hash, Hasher};
use std::sync::Arc; 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::diag::{At, TypResult};
use crate::syntax::ast::Expr; use crate::syntax::ast::Expr;
use crate::syntax::{Span, Spanned}; use crate::syntax::{Span, Spanned};
@ -138,7 +138,11 @@ impl Closure {
} }
// Evaluate the body. // 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) Ok(value)
} }

View File

@ -633,7 +633,12 @@ impl Eval for WhileExpr {
let condition = self.condition(); let condition = self.condition();
while condition.eval(ctx, scp)?.cast::<bool>().at(condition.span())? { while condition.eval(ctx, scp)?.cast::<bool>().at(condition.span())? {
let body = self.body(); 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())?; output = ops::join(output, value).at(body.span())?;
} }
@ -654,7 +659,14 @@ impl Eval for ForExpr {
for ($($value),*) in $iter { for ($($value),*) in $iter {
$(scp.top.def_mut(&$binding, $value);)* $(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) output = ops::join(output, value)
.at(self.body().span())?; .at(self.body().span())?;
} }

View File

@ -2,12 +2,54 @@
// Ref: false // Ref: false
--- ---
// Test break.
#let error = false
#let var = 0
#for i in range(10) { #for i in range(10) {
var += i
if i > 5 { 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 break
} }
} #f()
---
// Test continue outside of loop.
// Error: 12-20 cannot continue outside of loop
#let x = { continue }
--- ---
// Error: 1-10 unexpected keyword `continue` // Error: 1-10 unexpected keyword `continue`

View File

@ -3,8 +3,15 @@
--- ---
#let f(x) = { #let f(x) = {
// Error: 3-15 return is not yet implemented
return x + 1 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
}