Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 106 additions & 0 deletions crates/uplc/src/machine/cek.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use bumpalo::{collections::Vec as BumpVec, Bump};

use crate::{
binder::Eval,
constant::Constant,
machine::{context::Context, env::Env, state::MachineState},
term::Term,
};
Expand All @@ -15,6 +16,9 @@ use super::{
CostModel, ExBudget, MachineError,
};

pub const CONS_BRANCH: usize = 0;
pub const NILS_BRANCH: usize = 1;

pub struct Machine<'a> {
pub(super) arena: &'a Bump,
ex_budget: ExBudget,
Expand Down Expand Up @@ -248,6 +252,108 @@ impl<'a> Machine<'a> {
Err(MachineError::MissingCaseBranch(branches, value))
}
}
Value::Con(constant) => match constant {
Constant::Integer(scrutinee) => match usize::try_from(*scrutinee) {
Ok(scrutinee_usize) => branches
.get(scrutinee_usize)
.map(|branch| MachineState::compute(self.arena, context, env, branch))
.ok_or(MachineError::CekCaseBuiltinError(branches, value, format!("out of bounds for the given number of branches: {:?}", branches.len()))),
Err(_) => Err(MachineError::CekCaseBuiltinError(branches, value, format!("out of bounds for the given number of branches: {:?}", branches.len())))
},
Constant::Boolean(scrutinee) => {
if branches.len() > 2 {
return Err(MachineError::CekCaseBuiltinError(branches, value, "caseing on bool requires exactly one branch or two branches".to_string()));
}
branches
.get(*scrutinee as usize)
.map(|branch| MachineState::compute(self.arena, context, env, branch))
.ok_or(MachineError::CekCaseBuiltinError(branches, value, format!("out of bounds for the given number of branches: {:?}", branches.len())))
}
Constant::Unit => {
if branches.len() > 1 {
return Err(MachineError::CekCaseBuiltinError(branches, value, "caseing on unit only allows exactly one branch".to_string()));
}
branches
.get(CONS_BRANCH)
.map(|branch| MachineState::compute(self.arena, context, env, branch))
.ok_or(MachineError::CekCaseBuiltinError(branches, value, format!("out of bounds for the given number of branches: {:?}", branches.len())))
}
// Caseing on pairs expects a single branch that takes two arguments for each values of the pair.
Constant::ProtoPair(_, _, left_constant, right_constant) => {
if branches.len() > 1 {
return Err(MachineError::CekCaseBuiltinError(branches, value, "caseing on pair requires exactly one branch".to_string()));
}
branches
.get(CONS_BRANCH)
.map(|branch| {
let right_value: &Value<'_, V> =
Value::con(self.arena, right_constant);
let right_frame: &Context<'_, V> = Context::frame_await_fun_value(
self.arena,
right_value,
context,
);
let left_value = Value::con(self.arena, left_constant);
let left_frame = Context::frame_await_fun_value(
self.arena,
left_value,
right_frame,
);
MachineState::compute(self.arena, left_frame, env, branch)
})
.ok_or(MachineError::CekCaseBuiltinError(branches, value, format!("out of bounds for the given number of branches: {:?}", branches.len())))
}
// When matching (case-ing) on a builtin list, exactly one or two branches are allowed:
// - With a single branch, it is assumed the list is non-empty; the branch receives the head and tail as arguments.
// If the list is actually empty, script evaluation will fail.
// - With two branches, the nils branch is selected for the empty list (receiving no arguments),
// and the cons branch is selected for a non-empty list (receiving the head and tail as arguments).
//
// Note: In the Haskell implementation, when a list contains only a single element,
// the tail argument passed to the branch is an empty list.
Constant::ProtoList(list_type, list) => {
if branches.len() > 2 {
return Err(MachineError::CekCaseBuiltinError(branches, value, "casing on list requires exactly one branch or two branches".to_string()));
}

match list.split_first() {
None => {
branches
.get(NILS_BRANCH)
.map(|branch| {
let frame = self.transfer_arg_stack(&[], context);
MachineState::compute(self.arena, frame, env, branch)
})
.ok_or(MachineError::CekCaseBuiltinError(branches, value, format!("out of bounds for the given number of branches: {:?}", branches.len())))
}
Some((head, tail)) => {
branches
.get(CONS_BRANCH)
.map(|branch| {
let tail_value = if tail.is_empty() {
let empty_list_const =
Constant::proto_list(self.arena, list_type, &[]);
Value::con(self.arena, empty_list_const)
} else {
Value::con(self.arena, tail.last().unwrap())
};

let tail_frame = Context::frame_await_fun_value(
self.arena, tail_value, context,
);
let head_value = Value::con(self.arena, head);
let head_frame = Context::frame_await_fun_value(
self.arena, head_value, tail_frame,
);

MachineState::compute(self.arena, head_frame, env, branch)
})
.ok_or(MachineError::CekCaseBuiltinError(branches, value, format!("out of bounds for the given number of branches: {:?}", branches.len())))
}
}
}
_ => Err(MachineError::CekCaseBuiltinError(branches, value, format!("cannot case on constant of type {constant:?}"))),
},
v => Err(MachineError::NonConstrScrutinized(v)),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Has this error changed in the haskell version?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've updated some of the errors to be more in line with the Haskell implementation.

},
Context::NoFrame => {
Expand Down
6 changes: 0 additions & 6 deletions crates/uplc/src/machine/cost_model/costing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -356,12 +356,6 @@ pub struct ConstantOrLinear {
pub slope: i64,
}

#[derive(Debug, PartialEq)]
pub struct ConstantOrTwoArguments {
pub constant: i64,
pub model: Box<TwoArguments>,
}

#[derive(Debug, PartialEq)]
pub struct QuadraticFunction {
coeff_0: i64,
Expand Down
6 changes: 4 additions & 2 deletions crates/uplc/src/machine/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,12 @@ where
NonPolymorphicInstantiation(&'a Value<'a, V>),
#[error("Builtin term argument expected")]
BuiltinTermArgumentExpected(&'a Term<'a, V>),
#[error("Non-constructor scrutinized")]
#[error("Non-constructor/Non-builtin scrutinized")]
NonConstrScrutinized(&'a Value<'a, V>),
#[error("Non-integer index")]
#[error("Missing case branch")]
MissingCaseBranch(&'a [&'a Term<'a, V>], &'a Value<'a, V>),
#[error("Cek case builtin error: {2}")]
CekCaseBuiltinError(&'a [&'a Term<'a, V>], &'a Value<'a, V>, String),
#[error(transparent)]
Runtime(RuntimeError<'a>),
#[error("Max constr tag exceeded")]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
-- case on false
(program 1.1.0
(case (con bool False) (con integer 0) (con integer 1))
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
({cpu: 48100
| mem: 400})
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
(program 1.1.0 (con integer 0))
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
-- case on true
(program 1.1.0
(case (con bool True) (con integer 0) (con integer 1))
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
({cpu: 48100
| mem: 400})
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
(program 1.1.0 (con integer 1))
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
-- case on false, one branch
(program 1.1.0
(case (con bool False) (con integer 0))
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
({cpu: 48100
| mem: 400})
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
(program 1.1.0 (con integer 0))
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
-- case on true, one branch. Fails
(program 1.1.0
(case (con bool True) (con integer 0))
)
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
evaluation failure
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
evaluation failure
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
-- case on False with 3+ branches fails
(program 1.1.0
(case (con bool False) (con integer 0) (con integer 1) (con integer 2))
)
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
evaluation failure
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
evaluation failure
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
-- case on True with 3+ branches fails
(program 1.1.0
(case (con bool True) (con integer 0) (con integer 1) (con integer 2))
)
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
evaluation failure
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
evaluation failure
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
-- case on boolean with no branches fails
(program 1.1.0
(case (con bool False))
)
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
evaluation failure
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
evaluation failure
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
-- case on integer with no cases fails
(program 1.1.0
(case (con integer 0))
)
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
evaluation failure
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
evaluation failure
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
-- case on integer second branch
(program 1.1.0
(case (con integer 1) (con integer 42) (con integer 43) (con integer 443))
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
({cpu: 48100
| mem: 400})
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
(program 1.1.0 (con integer 43))
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
-- case on integer fails when given integer is bigger than number of branches given
(program 1.1.0
(case (con integer 3) (con integer 42) (con integer 43) (con integer 44))
)
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
evaluation failure
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
evaluation failure
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
-- case on negative integer fails
(program 1.1.0
(case (con integer -1) (con integer 42) (con integer 43) (con integer 44))
)
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
evaluation failure
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
evaluation failure
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
-- case on non-empty list with two branches
(program 1.1.0
(case (con (list integer) [1, 2, 3, 4]) (lam x (lam xs x)) (con integer -1))
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
({cpu: 80100
| mem: 600})
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
(program 1.1.0 (con integer 1))
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
-- case on empty list with two branches
(program 1.1.0
(case (con (list integer) []) (lam x (lam xs x)) (con integer -1))
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
({cpu: 48100
| mem: 400})
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
(program 1.1.0 (con integer -1))
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
-- case on non-empty list with one branch
(program 1.1.0
(case (con (list integer) [1, 2, 3, 4]) (lam x (lam xs x)))
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
({cpu: 80100
| mem: 600})
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
(program 1.1.0 (con integer 1))
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
-- case on empty list with one branch
(program 1.1.0
(case (con (list integer) []) (lam x (lam xs x)))
)
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
evaluation failure
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
evaluation failure
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
-- case on non-empty list with one incorrectly typed branch
(program 1.1.0
(case (con (list integer) [1, 2, 3, 4]) (con integer -1))
)
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
evaluation failure
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
evaluation failure
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
-- case on non-empty list with two incorrectly typed branch
(program 1.1.0
(case (con (list integer) [1, 2, 3, 4]) (con integer -1) (con integer -1))
)
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
evaluation failure
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
evaluation failure
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
-- case on empty list with two incorrectly typed branch
(program 1.1.0
(case (con (list integer) [1, 2, 3, 4]) (con integer -1) (lam x (lam xs x)))
)
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
evaluation failure
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
evaluation failure
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
(program 1.1.0
(case (con (pair integer bool) (42, False)) (lam l (lam r l)))
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
({cpu: 80100
| mem: 600})
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
(program 1.1.0 (con integer 42))
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
(program 1.1.0
(case (con (pair integer bool) (42, False)) (lam l (lam r r)))
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
({cpu: 80100
| mem: 600})
Loading
Loading