use std::fmt::Display; use crate::{ builtins::DefaultFunction, debruijn::{self, Converter}, machine::{ cost_model::{CostModel, ExBudget}, Machine, }, }; /// This represents a program in Untyped Plutus Core. /// A program contains a version tuple and a term. /// It is generic because Term requires a generic type. #[derive(Debug, Clone, PartialEq)] pub struct Program { pub version: (usize, usize, usize), pub term: Term, } impl Program where T: Clone, { /// We use this to apply the validator to Datum, /// then redeemer, then ScriptContext. If datum is /// even necessary (i.e. minting policy). pub fn apply(&self, program: &Self) -> Self { let applied_term = Term::Apply { function: Box::new(self.term.clone()), argument: Box::new(program.term.clone()), }; Program { version: self.version, term: applied_term, } } } /// This represents a term in Untyped Plutus Core. /// We need a generic type for the different forms that a program may be in. /// Specifically, `Var` and `parameter_name` in `Lambda` can be a `Name`, /// `NamedDebruijn`, or `DeBruijn`. When encoded to flat for on chain usage /// we must encode using the `DeBruijn` form. #[derive(Debug, Clone, PartialEq)] pub enum Term { // tag: 0 Var(T), // tag: 1 Delay(Box>), // tag: 2 Lambda { parameter_name: T, body: Box>, }, // tag: 3 Apply { function: Box>, argument: Box>, }, // tag: 4 Constant(Constant), // tag: 5 Force(Box>), // tag: 6 Error, // tag: 7 Builtin(DefaultFunction), } /// A container for the various constants that are available /// in Untyped Plutus Core. Used in the `Constant` variant of `Term`. #[derive(Debug, Clone, PartialEq)] pub enum Constant { // tag: 0 Integer(isize), // tag: 1 ByteString(Vec), // tag: 2 String(String), // tag: 3 Unit, // tag: 4 Bool(bool), } /// A Name containing it's parsed textual representation /// and a unique id from string interning. The Name's text is /// interned during parsing. #[derive(Debug, Clone)] pub struct Name { pub text: String, pub unique: Unique, } impl PartialEq for Name { fn eq(&self, other: &Self) -> bool { self.unique == other.unique } } /// A unique id used for string interning. #[derive(Debug, Clone, PartialEq, Copy, Eq, Hash)] pub struct Unique(isize); impl Unique { /// Create a new unique id. pub fn new(unique: isize) -> Self { Unique(unique) } /// Increment the available unique id. This is used during /// string interning to get the next available unique id. pub fn increment(&mut self) { self.0 += 1; } } impl From for Unique { fn from(i: isize) -> Self { Unique(i) } } impl From for isize { fn from(d: Unique) -> Self { d.0 } } impl Display for Unique { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.0) } } /// Similar to `Name` but for Debruijn indices. /// `Name` is replaced by `NamedDebruijn` when converting /// program to it's debruijn form. #[derive(Debug, Clone)] pub struct NamedDeBruijn { pub text: String, pub index: DeBruijn, } impl PartialEq for NamedDeBruijn { fn eq(&self, other: &Self) -> bool { self.index == other.index } } /// This is useful for decoding a on chain program into debruijn form. /// It allows for injecting fake textual names while also using Debruijn for decoding /// without having to loop through twice. #[derive(Debug, Clone, PartialEq)] pub struct FakeNamedDeBruijn(pub NamedDeBruijn); impl From for FakeNamedDeBruijn { fn from(d: DeBruijn) -> Self { FakeNamedDeBruijn(d.into()) } } impl From for DeBruijn { fn from(d: FakeNamedDeBruijn) -> Self { d.0.into() } } impl From for NamedDeBruijn { fn from(d: FakeNamedDeBruijn) -> Self { d.0 } } impl From for FakeNamedDeBruijn { fn from(d: NamedDeBruijn) -> Self { FakeNamedDeBruijn(d) } } /// Represents a debruijn index. #[derive(Debug, Clone, PartialEq, Copy)] pub struct DeBruijn(usize); impl DeBruijn { /// Create a new debruijn index. pub fn new(index: usize) -> Self { DeBruijn(index) } pub fn inner(&self) -> usize { self.0 } } impl From for DeBruijn { fn from(i: usize) -> Self { DeBruijn(i) } } impl From for usize { fn from(d: DeBruijn) -> Self { d.0 } } impl Display for DeBruijn { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.0) } } impl From for DeBruijn { fn from(n: NamedDeBruijn) -> Self { n.index } } impl From for NamedDeBruijn { fn from(index: DeBruijn) -> Self { NamedDeBruijn { // Inject fake name. We got `i` from the Plutus code base. text: String::from("i"), index, } } } /// Convert a Parsed `Program` to a `Program` in `NamedDebruijn` form. /// This checks for any Free Uniques in the `Program` and returns an error if found. impl TryFrom> for Program { type Error = debruijn::Error; fn try_from(value: Program) -> Result { Ok(Program:: { version: value.version, term: value.term.try_into()?, }) } } /// Convert a Parsed `Term` to a `Term` in `NamedDebruijn` form. /// This checks for any Free Uniques in the `Term` and returns an error if found. impl TryFrom> for Term { type Error = debruijn::Error; fn try_from(value: Term) -> Result { let mut converter = Converter::new(); let term = converter.name_to_named_debruijn(value)?; Ok(term) } } /// Convert a Parsed `Program` to a `Program` in `Debruijn` form. /// This checks for any Free Uniques in the `Program` and returns an error if found. impl TryFrom> for Program { type Error = debruijn::Error; fn try_from(value: Program) -> Result { Ok(Program:: { version: value.version, term: value.term.try_into()?, }) } } /// Convert a Parsed `Term` to a `Term` in `Debruijn` form. /// This checks for any Free Uniques in the `Program` and returns an error if found. impl TryFrom> for Term { type Error = debruijn::Error; fn try_from(value: Term) -> Result { let mut converter = Converter::new(); let term = converter.name_to_debruijn(value)?; Ok(term) } } impl TryFrom> for Program { type Error = debruijn::Error; fn try_from(value: Program) -> Result { Ok(Program:: { version: value.version, term: value.term.try_into()?, }) } } impl TryFrom> for Term { type Error = debruijn::Error; fn try_from(value: Term) -> Result { let mut converter = Converter::new(); let term = converter.named_debruijn_to_name(value)?; Ok(term) } } impl From> for Program { fn from(value: Program) -> Self { Program:: { version: value.version, term: value.term.into(), } } } impl From> for Term { fn from(value: Term) -> Self { let mut converter = Converter::new(); converter.named_debruijn_to_debruijn(value) } } impl From> for Program { fn from(value: Program) -> Self { Program:: { version: value.version, term: value.term.into(), } } } impl From> for Term { fn from(value: Term) -> Self { let mut converter = Converter::new(); converter.named_debruijn_to_fake_named_debruijn(value) } } impl TryFrom> for Program { type Error = debruijn::Error; fn try_from(value: Program) -> Result { Ok(Program:: { version: value.version, term: value.term.try_into()?, }) } } impl TryFrom> for Term { type Error = debruijn::Error; fn try_from(value: Term) -> Result { let mut converter = Converter::new(); let term = converter.debruijn_to_name(value)?; Ok(term) } } impl From> for Program { fn from(value: Program) -> Self { Program:: { version: value.version, term: value.term.into(), } } } impl From> for Term { fn from(value: Term) -> Self { let mut converter = Converter::new(); converter.debruijn_to_named_debruijn(value) } } impl From> for Program { fn from(value: Program) -> Self { Program:: { version: value.version, term: value.term.into(), } } } impl From> for Term { fn from(value: Term) -> Self { let mut converter = Converter::new(); converter.fake_named_debruijn_to_named_debruijn(value) } } impl Program { pub fn eval( &self, ) -> ( Result, crate::machine::Error>, ExBudget, Vec, ) { let mut machine = Machine::new(CostModel::default(), ExBudget::default(), 200); let term = machine.run(&self.term); (term, machine.ex_budget, machine.logs) } }