Implement apply evaluate

Co-authored-by: rvcas <x@rvcas.dev>
This commit is contained in:
Kasey White 2022-07-06 23:12:26 -04:00 committed by Kasey White
parent 650a789194
commit 0e2214a908
6 changed files with 77 additions and 5 deletions

View File

@ -33,6 +33,13 @@ pub enum UplcCommand {
}, },
/// Format an Untyped Plutus Core program /// Format an Untyped Plutus Core program
Fmt { input: PathBuf }, Fmt { input: PathBuf },
/// Evaluate an Untyped Plutus Core program
Eval {
input: PathBuf,
#[clap(short, long)]
flat: bool,
},
} }
impl Default for Args { impl Default for Args {

View File

@ -1,7 +1,7 @@
use std::fs; use std::fs;
use uplc::{ use uplc::{
ast::{DeBruijn, Name, Program}, ast::{DeBruijn, FakeNamedDeBruijn, Name, NamedDeBruijn, Program, Term},
parser, parser,
}; };
@ -77,6 +77,27 @@ fn main() -> anyhow::Result<()> {
fs::write(&out_name, pretty)?; fs::write(&out_name, pretty)?;
} }
} }
UplcCommand::Eval { input, flat } => {
let program = if flat {
let bytes = std::fs::read(&input)?;
let prog = Program::<FakeNamedDeBruijn>::from_flat(&bytes)?;
prog.into()
} else {
let code = std::fs::read_to_string(&input)?;
let prog = parser::program(&code)?;
Program::<NamedDeBruijn>::try_from(prog)?
};
let term = program.eval()?;
let term: Term<Name> = term.try_into()?;
println!("{}", term.to_pretty());
}
}, },
} }

View File

@ -3,6 +3,7 @@ use std::fmt::Display;
use crate::{ use crate::{
builtins::DefaultFunction, builtins::DefaultFunction,
debruijn::{self, Converter}, debruijn::{self, Converter},
machine::{Costs, ExBudget, Machine},
}; };
/// This represents a program in Untyped Plutus Core. /// This represents a program in Untyped Plutus Core.
@ -400,3 +401,12 @@ impl From<Term<FakeNamedDeBruijn>> for Term<NamedDeBruijn> {
converter.fake_named_debruijn_to_named_debruijn(value) converter.fake_named_debruijn_to_named_debruijn(value)
} }
} }
impl Program<NamedDeBruijn> {
pub fn eval(&self) -> Result<Term<NamedDeBruijn>, crate::machine::Error> {
let mut machine = Machine::new(Costs::default(), ExBudget::default(), 200);
let (term, _, _) = machine.run(&self.term)?;
Ok(term)
}
}

View File

@ -178,10 +178,19 @@ impl Machine {
fn apply_evaluate( fn apply_evaluate(
&mut self, &mut self,
_function: Value, function: Value,
_argument: Value, argument: Value,
) -> Result<Term<NamedDeBruijn>, Error> { ) -> Result<Term<NamedDeBruijn>, 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) { fn push_frame(&mut self, frame: Context) {
@ -262,7 +271,7 @@ pub enum Value {
} }
/// Can be negative /// Can be negative
#[derive(Debug, Clone, PartialEq, Copy)] #[derive(Debug, Clone, PartialEq, Copy, Default)]
pub struct ExBudget { pub struct ExBudget {
mem: i32, mem: i32,
cpu: i32, cpu: i32,
@ -307,6 +316,7 @@ impl TryFrom<u8> for StepKind {
/// There's no entry for Error since we'll be exiting anyway; also, what would /// 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? /// happen if calling 'Error' caused the budget to be exceeded?
#[derive(Default)]
pub struct Costs { pub struct Costs {
startup: ExBudget, startup: ExBudget,
var: ExBudget, var: ExBudget,

View File

@ -16,4 +16,6 @@ pub enum Error {
EvaluationFailure, EvaluationFailure,
#[error("Attempted to instantiate a non-polymorphic term: {0:#?}")] #[error("Attempted to instantiate a non-polymorphic term: {0:#?}")]
NonPolymorphicInstantiation(Value), NonPolymorphicInstantiation(Value),
#[error("Attempted to apply a non-function: {0:#?}")]
NonFunctionalApplication(Value),
} }

View File

@ -41,6 +41,28 @@ impl Program<Name> {
} }
impl Term<Name> { impl Term<Name> {
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::<Vec<_>>()
.join("\n")
}
fn to_doc(&self) -> RcDoc<()> { fn to_doc(&self) -> RcDoc<()> {
match self { match self {
Term::Var(name) => RcDoc::text(&name.text), Term::Var(name) => RcDoc::text(&name.text),