Test (and fix) unwrap_or helpers.

This commit is contained in:
KtorZ 2024-08-01 14:58:16 +02:00 committed by Kasey
parent ea8003af8f
commit 846c16087e
4 changed files with 200 additions and 36 deletions

View File

@ -591,9 +591,7 @@ impl Term<Name> {
Term::unconstr_data() Term::unconstr_data()
.apply(self) .apply(self)
.as_var("__pair__", |pair| { .as_var("__pair__", |pair| {
Term::snd_pair() Term::snd_pair().apply(pair.clone()).choose_list(
.apply(pair.clone())
.choose_list(
Term::less_than_equals_integer() Term::less_than_equals_integer()
.apply(Term::integer(2.into())) .apply(Term::integer(2.into()))
.apply(Term::fst_pair().apply(pair.clone())) .apply(Term::fst_pair().apply(pair.clone()))
@ -613,7 +611,6 @@ impl Term<Name> {
), ),
otherwise.clone(), otherwise.clone(),
) )
.force()
}) })
} }
@ -656,10 +653,20 @@ impl Term<Name> {
tail.as_var("__tail", |tail| { tail.as_var("__tail", |tail| {
let right = Term::head_list().apply(tail.clone()); let right = Term::head_list().apply(tail.clone());
tail.unwrap_tail_or( tail.unwrap_tail_or(
|_| otherwise.clone(), |leftovers| {
&callback(Term::mk_pair_data().apply(left).apply(right)), leftovers
.choose_list(
callback(Term::mk_pair_data().apply(left).apply(right)),
otherwise.clone(),
)
.force()
.delay()
},
otherwise,
) )
}) })
.force()
.delay()
}, },
otherwise, otherwise,
) )
@ -667,9 +674,6 @@ impl Term<Name> {
} }
/// Continue with the tail of a list, if any; or fallback 'otherwise'. /// Continue with the tail of a list, if any; or fallback 'otherwise'.
///
/// Note that the 'otherwise' term as well as the callback's result are expected
/// to be delayed terms.
pub fn unwrap_tail_or<F>(self, callback: F, otherwise: &Term<Name>) -> Term<Name> pub fn unwrap_tail_or<F>(self, callback: F, otherwise: &Term<Name>) -> Term<Name>
where where
F: FnOnce(Term<Name>) -> Term<Name>, F: FnOnce(Term<Name>) -> Term<Name>,
@ -678,3 +682,166 @@ impl Term<Name> {
.choose_list(otherwise.clone(), callback(Term::tail_list().apply(self))) .choose_list(otherwise.clone(), callback(Term::tail_list().apply(self)))
} }
} }
#[cfg(test)]
mod tests {
use crate::{
ast::{Data, Name, NamedDeBruijn, Program, Term},
builder::Constant,
machine::{cost_model::ExBudget, Error},
optimize::interner::CodeGenInterner,
};
fn quick_eval(term: Term<Name>) -> Result<Term<NamedDeBruijn>, Error> {
let version = (1, 0, 0);
let mut program = Program { version, term };
CodeGenInterner::new().program(&mut program);
program
.to_named_debruijn()
.expect("failed to convert program to NamedDeBruijn")
.eval(ExBudget::default())
.result()
}
#[test]
fn unwrap_bool_or_false() {
let result = quick_eval(
Term::data(Data::constr(0, vec![]))
.unwrap_bool_or(|b| b.delay(), &Term::Error.delay())
.force(),
);
assert_eq!(result, Ok(Term::bool(false)));
}
#[test]
fn unwrap_bool_or_true() {
let result = quick_eval(
Term::data(Data::constr(1, vec![]))
.unwrap_bool_or(|b| b.delay(), &Term::Error.delay())
.force(),
);
assert_eq!(result, Ok(Term::bool(true)));
}
#[test]
fn unwrap_bool_or_extra_args() {
let result = quick_eval(
Term::data(Data::constr(1, vec![Data::integer(42.into())]))
.unwrap_bool_or(|b| b.delay(), &Term::Error.delay())
.force(),
);
assert_eq!(result, Err(Error::EvaluationFailure));
}
#[test]
fn unwrap_bool_or_invalid_constr_hi() {
let result = quick_eval(
Term::data(Data::constr(2, vec![]))
.unwrap_bool_or(|b| b.delay(), &Term::Error.delay())
.force(),
);
assert_eq!(result, Err(Error::EvaluationFailure));
}
#[test]
fn unwrap_tail_or_0_elems() {
let result = quick_eval(
Term::list_values(vec![])
.unwrap_tail_or(|p| p.delay(), &Term::Error.delay())
.force(),
);
assert_eq!(result, Err(Error::EvaluationFailure));
}
#[test]
fn unwrap_tail_or_1_elem() {
let result = quick_eval(
Term::list_values(vec![Constant::Data(Data::integer(1.into()))])
.unwrap_tail_or(|p| p.delay(), &Term::Error.delay())
.force(),
);
assert_eq!(result, Ok(Term::list_values(vec![])),);
}
#[test]
fn unwrap_tail_or_2_elems() {
let result = quick_eval(
Term::list_values(vec![
Constant::Data(Data::integer(1.into())),
Constant::Data(Data::integer(2.into())),
])
.unwrap_tail_or(|p| p.delay(), &Term::Error.delay())
.force(),
);
assert_eq!(
result,
Ok(Term::list_values(vec![Constant::Data(Data::integer(
2.into()
))]))
);
}
#[test]
fn unwrap_pair_or() {
let result = quick_eval(
Term::list_values(vec![
Constant::Data(Data::integer(14.into())),
Constant::Data(Data::bytestring(vec![1, 2, 3])),
])
.unwrap_pair_or(|p| p.delay(), &Term::Error.delay())
.force(),
);
assert_eq!(
result,
Ok(Term::pair_values(
Constant::Data(Data::integer(14.into())),
Constant::Data(Data::bytestring(vec![1, 2, 3])),
))
);
}
#[test]
fn unwrap_pair_or_not_enough_args_1() {
let result = quick_eval(
Term::list_values(vec![Constant::Data(Data::integer(1.into()))])
.unwrap_pair_or(|p| p.delay(), &Term::Error.delay())
.force(),
);
assert_eq!(result, Err(Error::EvaluationFailure));
}
#[test]
fn unwrap_pair_or_not_enough_args_0() {
let result = quick_eval(
Term::list_values(vec![])
.unwrap_pair_or(|p| p.delay(), &Term::Error.delay())
.force(),
);
assert_eq!(result, Err(Error::EvaluationFailure));
}
#[test]
fn unwrap_pair_or_too_many_args() {
let result = quick_eval(
Term::list_values(vec![
Constant::Data(Data::integer(1.into())),
Constant::Data(Data::integer(2.into())),
Constant::Data(Data::integer(3.into())),
])
.unwrap_pair_or(|p| p.delay(), &Term::Error.delay())
.force(),
);
assert_eq!(result, Err(Error::EvaluationFailure));
}
}

View File

@ -1,12 +1,9 @@
use super::{ExBudget, Value};
use crate::ast::{NamedDeBruijn, Term, Type};
use num_bigint::BigInt;
use std::string::FromUtf8Error; use std::string::FromUtf8Error;
use num_bigint::BigInt; #[derive(Debug, Clone, PartialEq, thiserror::Error, miette::Diagnostic)]
use crate::ast::{NamedDeBruijn, Term, Type};
use super::{ExBudget, Value};
#[derive(Debug, Clone, thiserror::Error, miette::Diagnostic)]
pub enum Error { pub enum Error {
#[error("Over budget mem: {} & cpu: {}", .0.mem, .0.cpu)] #[error("Over budget mem: {} & cpu: {}", .0.mem, .0.cpu)]
OutOfExError(ExBudget), OutOfExError(ExBudget),

View File

@ -54,7 +54,7 @@ impl From<&Language> for BuiltinSemantics {
} }
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug, PartialEq)]
pub struct BuiltinRuntime { pub struct BuiltinRuntime {
pub(super) args: Vec<Value>, pub(super) args: Vec<Value>,
fun: DefaultFunction, fun: DefaultFunction,

View File

@ -12,7 +12,7 @@ use super::{runtime::BuiltinRuntime, Error};
pub(super) type Env = Rc<Vec<Value>>; pub(super) type Env = Rc<Vec<Value>>;
#[derive(Clone, Debug)] #[derive(Clone, Debug, PartialEq)]
pub enum Value { pub enum Value {
Con(Rc<Constant>), Con(Rc<Constant>),
Delay(Rc<Term<NamedDeBruijn>>, Env), Delay(Rc<Term<NamedDeBruijn>>, Env),