From 0e2214a908b2209edbd307a963b8bdcb4988c603 Mon Sep 17 00:00:00 2001 From: Kasey White Date: Wed, 6 Jul 2022 23:12:26 -0400 Subject: [PATCH] Implement apply evaluate Co-authored-by: rvcas --- crates/cli/src/args.rs | 7 +++++++ crates/cli/src/main.rs | 23 ++++++++++++++++++++++- crates/uplc/src/ast.rs | 10 ++++++++++ crates/uplc/src/machine.rs | 18 ++++++++++++++---- crates/uplc/src/machine/error.rs | 2 ++ crates/uplc/src/pretty.rs | 22 ++++++++++++++++++++++ 6 files changed, 77 insertions(+), 5 deletions(-) diff --git a/crates/cli/src/args.rs b/crates/cli/src/args.rs index 31c4e588..02a850ec 100644 --- a/crates/cli/src/args.rs +++ b/crates/cli/src/args.rs @@ -33,6 +33,13 @@ pub enum UplcCommand { }, /// Format an Untyped Plutus Core program Fmt { input: PathBuf }, + + /// Evaluate an Untyped Plutus Core program + Eval { + input: PathBuf, + #[clap(short, long)] + flat: bool, + }, } impl Default for Args { diff --git a/crates/cli/src/main.rs b/crates/cli/src/main.rs index 98b81c7e..261871ad 100644 --- a/crates/cli/src/main.rs +++ b/crates/cli/src/main.rs @@ -1,7 +1,7 @@ use std::fs; use uplc::{ - ast::{DeBruijn, Name, Program}, + ast::{DeBruijn, FakeNamedDeBruijn, Name, NamedDeBruijn, Program, Term}, parser, }; @@ -77,6 +77,27 @@ fn main() -> anyhow::Result<()> { fs::write(&out_name, pretty)?; } } + UplcCommand::Eval { input, flat } => { + let program = if flat { + let bytes = std::fs::read(&input)?; + + let prog = Program::::from_flat(&bytes)?; + + prog.into() + } else { + let code = std::fs::read_to_string(&input)?; + + let prog = parser::program(&code)?; + + Program::::try_from(prog)? + }; + + let term = program.eval()?; + + let term: Term = term.try_into()?; + + println!("{}", term.to_pretty()); + } }, } diff --git a/crates/uplc/src/ast.rs b/crates/uplc/src/ast.rs index c4422134..fef14a14 100644 --- a/crates/uplc/src/ast.rs +++ b/crates/uplc/src/ast.rs @@ -3,6 +3,7 @@ use std::fmt::Display; use crate::{ builtins::DefaultFunction, debruijn::{self, Converter}, + machine::{Costs, ExBudget, Machine}, }; /// This represents a program in Untyped Plutus Core. @@ -400,3 +401,12 @@ impl From> for Term { converter.fake_named_debruijn_to_named_debruijn(value) } } + +impl Program { + pub fn eval(&self) -> Result, crate::machine::Error> { + let mut machine = Machine::new(Costs::default(), ExBudget::default(), 200); + + let (term, _, _) = machine.run(&self.term)?; + Ok(term) + } +} diff --git a/crates/uplc/src/machine.rs b/crates/uplc/src/machine.rs index bc26a441..2e746df3 100644 --- a/crates/uplc/src/machine.rs +++ b/crates/uplc/src/machine.rs @@ -178,10 +178,19 @@ impl Machine { fn apply_evaluate( &mut self, - _function: Value, - _argument: Value, + function: Value, + argument: Value, ) -> Result, Error> { - todo!() + match function { + Value::Lambda { body, .. } => { + self.env.push(argument); + let term = self.compute(&body)?; + self.env.pop(); + Ok(term) + } + Value::Builtin(_, _) => todo!(), + rest => Err(Error::NonFunctionalApplication(rest)), + } } fn push_frame(&mut self, frame: Context) { @@ -262,7 +271,7 @@ pub enum Value { } /// Can be negative -#[derive(Debug, Clone, PartialEq, Copy)] +#[derive(Debug, Clone, PartialEq, Copy, Default)] pub struct ExBudget { mem: i32, cpu: i32, @@ -307,6 +316,7 @@ impl TryFrom for StepKind { /// There's no entry for Error since we'll be exiting anyway; also, what would /// happen if calling 'Error' caused the budget to be exceeded? +#[derive(Default)] pub struct Costs { startup: ExBudget, var: ExBudget, diff --git a/crates/uplc/src/machine/error.rs b/crates/uplc/src/machine/error.rs index 9ee363a8..25a30175 100644 --- a/crates/uplc/src/machine/error.rs +++ b/crates/uplc/src/machine/error.rs @@ -16,4 +16,6 @@ pub enum Error { EvaluationFailure, #[error("Attempted to instantiate a non-polymorphic term: {0:#?}")] NonPolymorphicInstantiation(Value), + #[error("Attempted to apply a non-function: {0:#?}")] + NonFunctionalApplication(Value), } diff --git a/crates/uplc/src/pretty.rs b/crates/uplc/src/pretty.rs index 90eb08d5..d2e72003 100644 --- a/crates/uplc/src/pretty.rs +++ b/crates/uplc/src/pretty.rs @@ -41,6 +41,28 @@ impl Program { } impl Term { + pub fn to_pretty(&self) -> String { + let mut w = Vec::new(); + + self.to_doc().render(80, &mut w).unwrap(); + + String::from_utf8(w) + .unwrap() + .lines() + // This is a hack to deal with blank newlines + // that end up with a bunch of useless whitespace + // because of the nesting + .map(|l| { + if l.chars().all(|c| c.is_whitespace()) { + "".to_string() + } else { + l.to_string() + } + }) + .collect::>() + .join("\n") + } + fn to_doc(&self) -> RcDoc<()> { match self { Term::Var(name) => RcDoc::text(&name.text),