Implement apply evaluate
Co-authored-by: rvcas <x@rvcas.dev>
This commit is contained in:
parent
650a789194
commit
0e2214a908
|
@ -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 {
|
||||||
|
|
|
@ -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());
|
||||||
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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),
|
||||||
}
|
}
|
||||||
|
|
|
@ -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),
|
||||||
|
|
Loading…
Reference in New Issue