e2e tests, return

This commit is contained in:
me 2025-12-20 16:25:37 +02:00
parent 805ceb8b15
commit 0abada448b
8 changed files with 127 additions and 13 deletions

View file

@ -102,6 +102,7 @@ pub enum Value {
Ref(Ref),
Closure { env: EnvName, expr: Box<Expr> },
PrimitiveFunc(Name),
Return(Box<Value>),
}
/// A mutable variable.

View file

@ -101,21 +101,29 @@ fn eval_expr(expr_env: &Env, state: &mut State, expr: &ast::Expr) -> Result<ast:
}
ast::Expr::Block(statements) => {
let mut block_env = expr_env.clone();
if let Some(last) = statements.iter().try_fold(None, |_, statement| {
let result = eval_statement(&mut block_env, state, statement)?;
match result {
StatementResult::Result(result) => {
Ok::<Option<ast::Value>, Error>(Some(result))
let mut result = ast::UNIT_VALUE;
for statement in statements {
let r = eval_statement(&mut block_env, state, statement)?;
match r {
StatementResult::Result(ast::Value::Return(r)) => {
result = ast::Value::Return(r);
break;
}
StatementResult::Return(result) => {
Ok::<Option<ast::Value>, Error>(Some(result))
StatementResult::Return(ast::Value::Return(r)) => {
result = ast::Value::Return(r);
break;
}
StatementResult::Result(r) => {
result = r;
}
StatementResult::Return(r) => {
result = ast::Value::Return(Box::new(r));
break;
}
}
})? {
Ok(last)
} else {
Err(Error::LastStatementNotAnExpr(Backtrace::capture()))
}
Ok(result)
}
ast::Expr::Access { expr, field } => match eval_expr(expr_env, state, expr)? {
ast::Value::Record(record) => Ok(state.variables.get(record.get(&field)?).clone()),
@ -148,7 +156,10 @@ fn eval_expr(expr_env: &Env, state: &mut State, expr: &ast::Expr) -> Result<ast:
let evalled = eval_expr(&closure_env, state, e)?;
closure_env.insert(arg.name, evalled);
}
eval_expr(&closure_env, state, &func.body)
match eval_expr(&closure_env, state, &func.body)? {
ast::Value::Return(r) => Ok(*r),
v => Ok(v),
}
}
e => Err(Error::NotAFunction(e, Backtrace::capture())),
},

View file

@ -2,3 +2,69 @@ pub mod ast;
pub mod interpret;
pub mod parser;
pub mod runtime;
pub fn run_main(code: &str) -> Result<ast::Value, String> {
match parser::parse_file(code.into()) {
Err(err) => Err(format!("Error: {err:?}")),
Ok(program) => match interpret::run(program, "main".into(), vec![]) {
Ok(v) => Ok(v),
Err(err) => Err(format!("{}", err)),
},
}
}
#[cfg(test)]
mod tests {
use super::run_main;
#[test]
fn int_108() {
let program = "
let main = fn() {
108
}
";
let result = run_main(program);
insta::assert_debug_snapshot!(result);
}
#[test]
fn let_add_3() {
let program = "
let main = fn() {
let a = 1;
let b = 2;
a + b
}
";
let result = run_main(program);
insta::assert_debug_snapshot!(result);
}
#[test]
fn let_if_4() {
let program = "
let main = fn() {
let a = 1;
let b = if a > 0 { 3 } else { 5 };
a + b
}
";
let result = run_main(program);
insta::assert_debug_snapshot!(result);
}
#[test]
fn bor_onearm_4() {
let program = "
let main = fn() {
let a = 1;
let b = 2 | 3;
if true {
return a + b;
};
return a;
}
";
let result = run_main(program);
insta::assert_debug_snapshot!(result);
}
}

View file

@ -105,7 +105,7 @@ impl Tokens {
if let Some(_end) = self.next_if(end) {
Ok(Some(result))
} else {
// println!("{:#?}", self);
println!("{:#?}", self);
Err(Error::UnexpectedTokenForParser(
format!("between end {start:#?} and {end:#?}"),
self.next(),

View file

@ -0,0 +1,9 @@
---
source: src/lib.rs
expression: result
---
Ok(
Int(
4,
),
)

View file

@ -0,0 +1,9 @@
---
source: src/lib.rs
expression: result
---
Ok(
Int(
108,
),
)

View file

@ -0,0 +1,9 @@
---
source: src/lib.rs
expression: result
---
Ok(
Int(
3,
),
)

View file

@ -0,0 +1,9 @@
---
source: src/lib.rs
expression: result
---
Ok(
Int(
4,
),
)