Skip to content

Commit

Permalink
Lazy default for Option.getOrElse and Coll.getOrElse
Browse files Browse the repository at this point in the history
  • Loading branch information
SethDusek committed Jan 4, 2025
1 parent ebd50ad commit e805aa2
Show file tree
Hide file tree
Showing 2 changed files with 88 additions and 7 deletions.
47 changes: 43 additions & 4 deletions ergotree-interpreter/src/eval/coll_by_index.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use ergotree_ir::ergo_tree::ErgoTreeVersion;
use ergotree_ir::mir::coll_by_index::ByIndex;
use ergotree_ir::mir::constant::TryExtractInto;
use ergotree_ir::mir::value::CollKind;
Expand Down Expand Up @@ -25,10 +26,14 @@ impl Evaluable for ByIndex {
}?;
match self.default.clone() {
Some(default) => {
let default_v = default.eval(env, ctx)?;
Ok(normalized_input_vals
.get_val(index_v.try_extract_into::<i32>()? as usize)
.unwrap_or(default_v))
let mut default_v = || default.eval(env, ctx);
let val =
normalized_input_vals.get_val(index_v.try_extract_into::<i32>()? as usize);
if ctx.tree_version() >= ErgoTreeVersion::V3 {
val.map(Ok).unwrap_or_else(default_v)
} else {
Ok(val.unwrap_or(default_v()?))
}
}
None => normalized_input_vals
.get_val(index_v.clone().try_extract_into::<i32>()? as usize)
Expand All @@ -47,6 +52,10 @@ impl Evaluable for ByIndex {
#[cfg(test)]
mod tests {
use ergotree_ir::chain::ergo_box::ErgoBox;
use ergotree_ir::mir::bin_op::ArithOp;
use ergotree_ir::mir::bin_op::BinOp;
use ergotree_ir::mir::bin_op::BinOpKind;
use ergotree_ir::mir::constant::Constant;
use ergotree_ir::mir::expr::Expr;
use ergotree_ir::mir::global_vars::GlobalVars;
use ergotree_ir::reference::Ref;
Expand All @@ -55,6 +64,7 @@ mod tests {
use super::*;
use crate::eval::tests::eval_out;
use crate::eval::tests::eval_out_wo_ctx;
use crate::eval::tests::try_eval_out_with_version;
use ergotree_ir::chain::context::Context;

#[test]
Expand All @@ -80,4 +90,33 @@ mod tests {
.into();
assert_eq!(eval_out_wo_ctx::<i64>(&expr), 5);
}

#[test]
fn eval_lazy() {
let expr: Expr = ByIndex::new(
Expr::Const(vec![1i64, 2i64].into()),
Expr::Const(1i32.into()),
Some(Box::new(Expr::BinOp(
BinOp {
kind: BinOpKind::Arith(ArithOp::Divide),
left: Box::new(Constant::from(1i64).into()),
right: Box::new(Constant::from(0i64).into()),
}
.into(),
))),
)
.unwrap()
.into();
let ctx = force_any_val::<Context>();
for tree_version in 0u8..ErgoTreeVersion::V3.into() {
assert!(try_eval_out_with_version::<Value>(&expr, &ctx, tree_version, 3).is_err());
}
for tree_version in ErgoTreeVersion::V3.into()..=ErgoTreeVersion::MAX_SCRIPT_VERSION.into()
{
assert_eq!(
try_eval_out_with_version::<i64>(&expr, &ctx, tree_version, 3).unwrap(),
2i64
);
}
}
}
48 changes: 45 additions & 3 deletions ergotree-interpreter/src/eval/option_get_or_else.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use ergotree_ir::ergo_tree::ErgoTreeVersion;
use ergotree_ir::mir::option_get_or_else::OptionGetOrElse;
use ergotree_ir::mir::value::Value;

Expand All @@ -13,9 +14,12 @@ impl Evaluable for OptionGetOrElse {
ctx: &Context<'ctx>,
) -> Result<Value<'ctx>, EvalError> {
let v = self.input.eval(env, ctx)?;
let default_v = self.default.eval(env, ctx)?;
let mut default_v = || self.default.eval(env, ctx);
match v {
Value::Opt(opt_v) => Ok(opt_v.as_deref().cloned().unwrap_or(default_v)),
Value::Opt(opt_v) if ctx.tree_version() >= ErgoTreeVersion::V3 => {
opt_v.as_deref().cloned().map(Ok).unwrap_or_else(default_v)
}
Value::Opt(opt_v) => Ok(opt_v.as_deref().cloned().unwrap_or(default_v()?)),
_ => Err(EvalError::UnexpectedExpr(format!(
"Don't know how to eval OptM: {0:?}",
self
Expand All @@ -28,13 +32,16 @@ impl Evaluable for OptionGetOrElse {
#[cfg(test)]
mod tests {
use super::OptionGetOrElse;
use crate::eval::tests::eval_out;
use crate::eval::tests::{eval_out, try_eval_out_with_version};
use ergotree_ir::chain::context::Context;
use ergotree_ir::ergo_tree::ErgoTreeVersion;
use ergotree_ir::mir::bin_op::{ArithOp, BinOp, BinOpKind};
use ergotree_ir::mir::constant::Constant;
use ergotree_ir::mir::expr::Expr;
use ergotree_ir::mir::extract_reg_as::ExtractRegisterAs;
use ergotree_ir::mir::get_var::GetVar;
use ergotree_ir::mir::global_vars::GlobalVars;
use ergotree_ir::mir::value::Value;
use ergotree_ir::types::stype::SType;
use sigma_test_util::force_any_val;

Expand Down Expand Up @@ -71,4 +78,39 @@ mod tests {
let v = eval_out::<i64>(&option_get_expr, &ctx);
assert_eq!(v, 1i64);
}
#[test]
fn eval_lazy() {
let get_reg_expr: Expr = ExtractRegisterAs::new(
GlobalVars::SelfBox.into(),
0,
SType::SOption(SType::SLong.into()),
)
.unwrap()
.into();
let divide_by_zero = Expr::BinOp(
BinOp {
kind: BinOpKind::Arith(ArithOp::Divide),
left: Box::new(Constant::from(1i64).into()),
right: Box::new(Constant::from(0i64).into()),
}
.into(),
);
let option_get_expr: Expr = OptionGetOrElse::new(get_reg_expr, divide_by_zero)
.unwrap()
.into();
let ctx = force_any_val::<Context>();
for tree_version in 0..ErgoTreeVersion::V3.into() {
assert!(
try_eval_out_with_version::<Value>(&option_get_expr, &ctx, tree_version, 3)
.is_err()
);
}
for tree_version in ErgoTreeVersion::V3.into()..=ErgoTreeVersion::MAX_SCRIPT_VERSION.into()
{
assert_eq!(
try_eval_out_with_version::<i64>(&option_get_expr, &ctx, tree_version, 3).unwrap(),
ctx.self_box.value.as_i64()
);
}
}
}

0 comments on commit e805aa2

Please sign in to comment.