From d55409d8c5d0299995b85247e058a17f3a24f368 Mon Sep 17 00:00:00 2001 From: rvcas Date: Sat, 9 Jul 2022 21:46:01 -0400 Subject: [PATCH] feat: basic builtin execution Co-authored-by: Kasey White --- add_integers.uplc | 4 + crates/uplc/src/machine.rs | 70 +++++-- crates/uplc/src/machine/runtime.rs | 297 ++++++++++++++++++++++++++--- 3 files changed, 329 insertions(+), 42 deletions(-) create mode 100644 add_integers.uplc diff --git a/add_integers.uplc b/add_integers.uplc new file mode 100644 index 00000000..5854dd64 --- /dev/null +++ b/add_integers.uplc @@ -0,0 +1,4 @@ +(program + 1.0.0 + [ (builtin addInteger) (con integer 2) (con integer 3) ] +) diff --git a/crates/uplc/src/machine.rs b/crates/uplc/src/machine.rs index 92fd6d11..ce1bd594 100644 --- a/crates/uplc/src/machine.rs +++ b/crates/uplc/src/machine.rs @@ -10,6 +10,8 @@ mod runtime; use cost_model::{ExBudget, MachineCosts, StepKind}; pub use error::Error; +use self::runtime::BuiltinRuntime; + pub struct Machine { costs: MachineCosts, ex_budget: ExBudget, @@ -89,9 +91,16 @@ impl Machine { self.compute(body) } Term::Error => Err(Error::EvaluationFailure), - Term::Builtin(_bn) => { + Term::Builtin(fun) => { self.step_and_maybe_spend(StepKind::Builtin)?; - todo!() + + let runtime: BuiltinRuntime = (*fun).into(); + + self.return_compute(Value::Builtin { + fun: *fun, + term: term.clone(), + runtime, + }) } } } @@ -135,7 +144,7 @@ impl Machine { fn discharge_value(&mut self, value: Value) -> Term { match value { Value::Con(x) => Term::Constant(x), - Value::Builtin(_, t) => t, + Value::Builtin { term, .. } => term, Value::Delay(body) => self.discharge_value_env(Term::Delay(Box::new(body))), Value::Lambda { parameter_name, @@ -187,7 +196,7 @@ impl Machine { fn force_evaluate(&mut self, value: Value) -> Result, Error> { match value { Value::Delay(body) => self.compute(&body), - Value::Builtin(_, _) => todo!(), + Value::Builtin { .. } => todo!(), rest => Err(Error::NonPolymorphicInstantiation(rest)), } } @@ -204,11 +213,45 @@ impl Machine { self.env.pop(); Ok(term) } - Value::Builtin(_, _) => todo!(), + Value::Builtin { + fun, + term, + mut runtime, + } => { + let arg_term = self.discharge_value(argument.clone()); + + let t = Term::::Apply { + function: Box::new(term), + argument: Box::new(arg_term), + }; + + if runtime.is_arrow() { + runtime.push(argument)?; + } + + let res = self.eval_builtin_app(fun, t, runtime)?; + + self.return_compute(res) + } rest => Err(Error::NonFunctionalApplication(rest)), } } + fn eval_builtin_app( + &mut self, + fun: DefaultFunction, + term: Term, + runtime: BuiltinRuntime, + ) -> Result { + if runtime.is_ready() { + self.spend_budget(ExBudget::default())?; + + runtime.call() + } else { + Ok(Value::Builtin { fun, term, runtime }) + } + } + fn push_frame(&mut self, frame: Context) { self.frames.push(frame); } @@ -280,10 +323,15 @@ pub enum Value { parameter_name: NamedDeBruijn, body: Term, }, - Builtin( - DefaultFunction, - Term, - // Need to figure out run time stuff - // BuiltinRuntime (CekValue uni fun) - ), + Builtin { + fun: DefaultFunction, + term: Term, + runtime: BuiltinRuntime, + }, +} + +impl Value { + pub fn is_integer(&self) -> bool { + matches!(self, Value::Con(Constant::Integer(_))) + } } diff --git a/crates/uplc/src/machine/runtime.rs b/crates/uplc/src/machine/runtime.rs index e69c6063..e2add823 100644 --- a/crates/uplc/src/machine/runtime.rs +++ b/crates/uplc/src/machine/runtime.rs @@ -1,44 +1,279 @@ -use super::cost_model::{CostingFun, ExBudget}; +use crate::{ast::Constant, builtins::DefaultFunction}; -enum RuntimeScheme { - RuntimeSchemeResult, - RuntimeSchemeArrow(usize), - RuntimeSchemeAll(usize), +use super::{ + // cost_model::{CostingFun, ExBudget}, + Error, + Value, +}; + +//#[derive(std::cmp::PartialEq)] +//pub enum EvalMode { +// Immediate, +// Deferred, +//} + +// pub struct BuiltinRuntimeOptions { +// immediate_eval: T, +// deferred_eval: T, +// budget: fn(CostingFun) -> fn(Vec) -> ExBudget, +// } + +#[derive(Clone, Debug)] +pub struct BuiltinRuntime { + meaning: BuiltinMeaning, + // budget: fn(Vec) -> ExBudget, } -#[derive(std::cmp::PartialEq)] -enum EvalMode { - Immediate, - Deferred, +#[derive(Clone, Debug)] +struct BuiltinMeaning { + args: Vec, + fun: DefaultFunction, } -struct BuiltinRuntimeOptions { - runtime_scheme: Vec, - immediate_eval: T, - deferred_eval: T, - budget: fn(CostingFun) -> fn(Vec) -> ExBudget, +impl BuiltinMeaning { + pub fn new(fun: DefaultFunction) -> BuiltinMeaning { + Self { args: vec![], fun } + } } -struct BuiltinRuntime { - runtime_scheme: Vec, - runtime_denotation: T, - budget: fn(Vec) -> ExBudget, +impl BuiltinRuntime { + // fn from_builtin_runtime_options( + // eval_mode: EvalMode, + // cost: CostingFun, + // runtime_options: BuiltinRuntimeOptions, + // ) -> BuiltinRuntime { + // Self { + // budget: (runtime_options.budget)(cost), + // } + // } + + pub fn is_arrow(&self) -> bool { + self.meaning.args.len() != self.meaning.fun.arity() + } + + pub fn is_ready(&self) -> bool { + self.meaning.args.len() == self.meaning.fun.arity() + } + + pub fn call(&self) -> Result { + self.meaning.fun.call(&self.meaning.args) + } + + pub fn push(&mut self, arg: Value) -> Result<(), Error> { + self.meaning.fun.check_type(&arg)?; + + self.meaning.args.push(arg); + + Ok(()) + } } -impl BuiltinRuntime { - fn from_builtin_runtime_options( - eval_mode: EvalMode, - cost: CostingFun, - runtime_options: BuiltinRuntimeOptions, - ) -> BuiltinRuntime { +impl From for BuiltinMeaning { + fn from(fun: DefaultFunction) -> Self { + BuiltinMeaning::new(fun) + } +} + +impl From for BuiltinRuntime { + fn from(fun: DefaultFunction) -> Self { BuiltinRuntime { - runtime_scheme: runtime_options.runtime_scheme, - runtime_denotation: if eval_mode == EvalMode::Immediate { - runtime_options.immediate_eval - } else { - runtime_options.deferred_eval - }, - budget: (runtime_options.budget)(cost), + meaning: fun.into(), + } + } +} + +impl DefaultFunction { + pub fn arity(&self) -> usize { + match self { + DefaultFunction::AddInteger => 2, + DefaultFunction::SubtractInteger => todo!(), + DefaultFunction::MultiplyInteger => todo!(), + DefaultFunction::DivideInteger => todo!(), + DefaultFunction::QuotientInteger => todo!(), + DefaultFunction::RemainderInteger => todo!(), + DefaultFunction::ModInteger => todo!(), + DefaultFunction::EqualsInteger => todo!(), + DefaultFunction::LessThanInteger => todo!(), + DefaultFunction::LessThanEqualsInteger => todo!(), + DefaultFunction::AppendByteString => todo!(), + DefaultFunction::ConsByteString => todo!(), + DefaultFunction::SliceByteString => todo!(), + DefaultFunction::LengthOfByteString => todo!(), + DefaultFunction::IndexByteString => todo!(), + DefaultFunction::EqualsByteString => todo!(), + DefaultFunction::LessThanByteString => todo!(), + DefaultFunction::LessThanEqualsByteString => todo!(), + DefaultFunction::Sha2_256 => todo!(), + DefaultFunction::Sha3_256 => todo!(), + DefaultFunction::Blake2b_256 => todo!(), + DefaultFunction::VerifySignature => todo!(), + DefaultFunction::VerifyEcdsaSecp256k1Signature => todo!(), + DefaultFunction::VerifySchnorrSecp256k1Signature => todo!(), + DefaultFunction::AppendString => todo!(), + DefaultFunction::EqualsString => todo!(), + DefaultFunction::EncodeUtf8 => todo!(), + DefaultFunction::DecodeUtf8 => todo!(), + DefaultFunction::IfThenElse => todo!(), + DefaultFunction::ChooseUnit => todo!(), + DefaultFunction::Trace => todo!(), + DefaultFunction::FstPair => todo!(), + DefaultFunction::SndPair => todo!(), + DefaultFunction::ChooseList => todo!(), + DefaultFunction::MkCons => todo!(), + DefaultFunction::HeadList => todo!(), + DefaultFunction::TailList => todo!(), + DefaultFunction::NullList => todo!(), + DefaultFunction::ChooseData => todo!(), + DefaultFunction::ConstrData => todo!(), + DefaultFunction::MapData => todo!(), + DefaultFunction::ListData => todo!(), + DefaultFunction::IData => todo!(), + DefaultFunction::BData => todo!(), + DefaultFunction::UnConstrData => todo!(), + DefaultFunction::UnMapData => todo!(), + DefaultFunction::UnListData => todo!(), + DefaultFunction::UnIData => todo!(), + DefaultFunction::UnBData => todo!(), + DefaultFunction::EqualsData => todo!(), + DefaultFunction::SerialiseData => todo!(), + DefaultFunction::MkPairData => todo!(), + DefaultFunction::MkNilData => todo!(), + DefaultFunction::MkNilPairData => todo!(), + } + } + + pub fn check_type(&self, arg: &Value) -> Result<(), Error> { + match self { + DefaultFunction::AddInteger => { + if arg.is_integer() { + Ok(()) + } else { + todo!("type error") + } + } + DefaultFunction::SubtractInteger => todo!(), + DefaultFunction::MultiplyInteger => todo!(), + DefaultFunction::DivideInteger => todo!(), + DefaultFunction::QuotientInteger => todo!(), + DefaultFunction::RemainderInteger => todo!(), + DefaultFunction::ModInteger => todo!(), + DefaultFunction::EqualsInteger => todo!(), + DefaultFunction::LessThanInteger => todo!(), + DefaultFunction::LessThanEqualsInteger => todo!(), + DefaultFunction::AppendByteString => todo!(), + DefaultFunction::ConsByteString => todo!(), + DefaultFunction::SliceByteString => todo!(), + DefaultFunction::LengthOfByteString => todo!(), + DefaultFunction::IndexByteString => todo!(), + DefaultFunction::EqualsByteString => todo!(), + DefaultFunction::LessThanByteString => todo!(), + DefaultFunction::LessThanEqualsByteString => todo!(), + DefaultFunction::Sha2_256 => todo!(), + DefaultFunction::Sha3_256 => todo!(), + DefaultFunction::Blake2b_256 => todo!(), + DefaultFunction::VerifySignature => todo!(), + DefaultFunction::VerifyEcdsaSecp256k1Signature => todo!(), + DefaultFunction::VerifySchnorrSecp256k1Signature => todo!(), + DefaultFunction::AppendString => todo!(), + DefaultFunction::EqualsString => todo!(), + DefaultFunction::EncodeUtf8 => todo!(), + DefaultFunction::DecodeUtf8 => todo!(), + DefaultFunction::IfThenElse => todo!(), + DefaultFunction::ChooseUnit => todo!(), + DefaultFunction::Trace => todo!(), + DefaultFunction::FstPair => todo!(), + DefaultFunction::SndPair => todo!(), + DefaultFunction::ChooseList => todo!(), + DefaultFunction::MkCons => todo!(), + DefaultFunction::HeadList => todo!(), + DefaultFunction::TailList => todo!(), + DefaultFunction::NullList => todo!(), + DefaultFunction::ChooseData => todo!(), + DefaultFunction::ConstrData => todo!(), + DefaultFunction::MapData => todo!(), + DefaultFunction::ListData => todo!(), + DefaultFunction::IData => todo!(), + DefaultFunction::BData => todo!(), + DefaultFunction::UnConstrData => todo!(), + DefaultFunction::UnMapData => todo!(), + DefaultFunction::UnListData => todo!(), + DefaultFunction::UnIData => todo!(), + DefaultFunction::UnBData => todo!(), + DefaultFunction::EqualsData => todo!(), + DefaultFunction::SerialiseData => todo!(), + DefaultFunction::MkPairData => todo!(), + DefaultFunction::MkNilData => todo!(), + DefaultFunction::MkNilPairData => todo!(), + } + } + + pub fn call(&self, args: &[Value]) -> Result { + match self { + DefaultFunction::AddInteger => { + assert_eq!(args.len(), self.arity()); + + let args = (&args[0], &args[1]); + + match args { + (Value::Con(Constant::Integer(arg1)), Value::Con(Constant::Integer(arg2))) => { + Ok(Value::Con(Constant::Integer(arg1 + arg2))) + } + _ => todo!("handle error"), + } + } + DefaultFunction::SubtractInteger => todo!(), + DefaultFunction::MultiplyInteger => todo!(), + DefaultFunction::DivideInteger => todo!(), + DefaultFunction::QuotientInteger => todo!(), + DefaultFunction::RemainderInteger => todo!(), + DefaultFunction::ModInteger => todo!(), + DefaultFunction::EqualsInteger => todo!(), + DefaultFunction::LessThanInteger => todo!(), + DefaultFunction::LessThanEqualsInteger => todo!(), + DefaultFunction::AppendByteString => todo!(), + DefaultFunction::ConsByteString => todo!(), + DefaultFunction::SliceByteString => todo!(), + DefaultFunction::LengthOfByteString => todo!(), + DefaultFunction::IndexByteString => todo!(), + DefaultFunction::EqualsByteString => todo!(), + DefaultFunction::LessThanByteString => todo!(), + DefaultFunction::LessThanEqualsByteString => todo!(), + DefaultFunction::Sha2_256 => todo!(), + DefaultFunction::Sha3_256 => todo!(), + DefaultFunction::Blake2b_256 => todo!(), + DefaultFunction::VerifySignature => todo!(), + DefaultFunction::VerifyEcdsaSecp256k1Signature => todo!(), + DefaultFunction::VerifySchnorrSecp256k1Signature => todo!(), + DefaultFunction::AppendString => todo!(), + DefaultFunction::EqualsString => todo!(), + DefaultFunction::EncodeUtf8 => todo!(), + DefaultFunction::DecodeUtf8 => todo!(), + DefaultFunction::IfThenElse => todo!(), + DefaultFunction::ChooseUnit => todo!(), + DefaultFunction::Trace => todo!(), + DefaultFunction::FstPair => todo!(), + DefaultFunction::SndPair => todo!(), + DefaultFunction::ChooseList => todo!(), + DefaultFunction::MkCons => todo!(), + DefaultFunction::HeadList => todo!(), + DefaultFunction::TailList => todo!(), + DefaultFunction::NullList => todo!(), + DefaultFunction::ChooseData => todo!(), + DefaultFunction::ConstrData => todo!(), + DefaultFunction::MapData => todo!(), + DefaultFunction::ListData => todo!(), + DefaultFunction::IData => todo!(), + DefaultFunction::BData => todo!(), + DefaultFunction::UnConstrData => todo!(), + DefaultFunction::UnMapData => todo!(), + DefaultFunction::UnListData => todo!(), + DefaultFunction::UnIData => todo!(), + DefaultFunction::UnBData => todo!(), + DefaultFunction::EqualsData => todo!(), + DefaultFunction::SerialiseData => todo!(), + DefaultFunction::MkPairData => todo!(), + DefaultFunction::MkNilData => todo!(), + DefaultFunction::MkNilPairData => todo!(), } } }