feat: 3 new error cases and more generic pretty printing
This commit is contained in:
parent
598c5364fe
commit
f332dfeb38
|
@ -1,4 +1,7 @@
|
||||||
(program
|
(program
|
||||||
1.0.0
|
1.0.0
|
||||||
[ (builtin ifThenElse) (con bool True) (con integer 1) (con string "yo") ]
|
[
|
||||||
|
[ [ (force (builtin ifThenElse)) (con integer 2) ] (con integer 1) ]
|
||||||
|
(con string "yo")
|
||||||
|
]
|
||||||
)
|
)
|
|
@ -32,8 +32,11 @@ pub enum UplcCommand {
|
||||||
out: Option<String>,
|
out: Option<String>,
|
||||||
},
|
},
|
||||||
/// Format an Untyped Plutus Core program
|
/// Format an Untyped Plutus Core program
|
||||||
Fmt { input: PathBuf },
|
Fmt {
|
||||||
|
input: PathBuf,
|
||||||
|
#[clap(short, long)]
|
||||||
|
print: bool,
|
||||||
|
},
|
||||||
/// Evaluate an Untyped Plutus Core program
|
/// Evaluate an Untyped Plutus Core program
|
||||||
Eval {
|
Eval {
|
||||||
input: PathBuf,
|
input: PathBuf,
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use std::fs;
|
use std::{fmt::Write as _, fs};
|
||||||
|
|
||||||
use uplc::{
|
use uplc::{
|
||||||
ast::{DeBruijn, FakeNamedDeBruijn, Name, NamedDeBruijn, Program, Term},
|
ast::{DeBruijn, FakeNamedDeBruijn, Name, NamedDeBruijn, Program, Term},
|
||||||
|
@ -27,7 +27,7 @@ fn main() -> anyhow::Result<()> {
|
||||||
let mut output = String::new();
|
let mut output = String::new();
|
||||||
|
|
||||||
for (i, byte) in bytes.iter().enumerate() {
|
for (i, byte) in bytes.iter().enumerate() {
|
||||||
output.push_str(&format!("{:08b}", byte));
|
let _ = write!(output, "{:08b}", byte);
|
||||||
|
|
||||||
if (i + 1) % 4 == 0 {
|
if (i + 1) % 4 == 0 {
|
||||||
output.push('\n');
|
output.push('\n');
|
||||||
|
@ -47,15 +47,19 @@ fn main() -> anyhow::Result<()> {
|
||||||
fs::write(&out_name, &bytes)?;
|
fs::write(&out_name, &bytes)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
UplcCommand::Fmt { input } => {
|
UplcCommand::Fmt { input, print } => {
|
||||||
let code = std::fs::read_to_string(&input)?;
|
let code = std::fs::read_to_string(&input)?;
|
||||||
|
|
||||||
let program = parser::program(&code)?;
|
let program = parser::program(&code)?;
|
||||||
|
|
||||||
let pretty = program.to_pretty();
|
let pretty = program.to_pretty();
|
||||||
|
|
||||||
|
if print {
|
||||||
|
println!("{}", pretty);
|
||||||
|
} else {
|
||||||
fs::write(&input, pretty)?;
|
fs::write(&input, pretty)?;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
UplcCommand::Unflat { input, print, out } => {
|
UplcCommand::Unflat { input, print, out } => {
|
||||||
let bytes = std::fs::read(&input)?;
|
let bytes = std::fs::read(&input)?;
|
||||||
|
|
||||||
|
@ -105,7 +109,7 @@ fn main() -> anyhow::Result<()> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
println!("Costs - memory: {} & cpu: {}", cost.mem, cost.cpu);
|
println!("\nCosts - memory: {} & cpu: {}", cost.mem, cost.cpu);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -162,7 +162,7 @@ impl PartialEq for NamedDeBruijn {
|
||||||
/// It allows for injecting fake textual names while also using Debruijn for decoding
|
/// It allows for injecting fake textual names while also using Debruijn for decoding
|
||||||
/// without having to loop through twice.
|
/// without having to loop through twice.
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub struct FakeNamedDeBruijn(NamedDeBruijn);
|
pub struct FakeNamedDeBruijn(pub NamedDeBruijn);
|
||||||
|
|
||||||
impl From<DeBruijn> for FakeNamedDeBruijn {
|
impl From<DeBruijn> for FakeNamedDeBruijn {
|
||||||
fn from(d: DeBruijn) -> Self {
|
fn from(d: DeBruijn) -> Self {
|
||||||
|
|
|
@ -18,6 +18,7 @@ const TERM_TAG_WIDTH: u32 = 4;
|
||||||
pub trait Binder<'b>: Encode + Decode<'b> {
|
pub trait Binder<'b>: Encode + Decode<'b> {
|
||||||
fn binder_encode(&self, e: &mut Encoder) -> Result<(), en::Error>;
|
fn binder_encode(&self, e: &mut Encoder) -> Result<(), en::Error>;
|
||||||
fn binder_decode(d: &mut Decoder) -> Result<Self, de::Error>;
|
fn binder_decode(d: &mut Decoder) -> Result<Self, de::Error>;
|
||||||
|
fn text(&self) -> &str;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'b, T> Flat<'b> for Program<T> where T: Binder<'b> + Debug {}
|
impl<'b, T> Flat<'b> for Program<T> where T: Binder<'b> + Debug {}
|
||||||
|
@ -246,6 +247,10 @@ impl<'b> Binder<'b> for Name {
|
||||||
fn binder_decode(d: &mut Decoder) -> Result<Self, de::Error> {
|
fn binder_decode(d: &mut Decoder) -> Result<Self, de::Error> {
|
||||||
Name::decode(d)
|
Name::decode(d)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn text(&self) -> &str {
|
||||||
|
&self.text
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Encode for NamedDeBruijn {
|
impl Encode for NamedDeBruijn {
|
||||||
|
@ -279,6 +284,10 @@ impl<'b> Binder<'b> for NamedDeBruijn {
|
||||||
index: DeBruijn::new(0),
|
index: DeBruijn::new(0),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn text(&self) -> &str {
|
||||||
|
&self.text
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Encode for DeBruijn {
|
impl Encode for DeBruijn {
|
||||||
|
@ -303,6 +312,10 @@ impl<'b> Binder<'b> for DeBruijn {
|
||||||
fn binder_decode(_d: &mut Decoder) -> Result<Self, de::Error> {
|
fn binder_decode(_d: &mut Decoder) -> Result<Self, de::Error> {
|
||||||
Ok(DeBruijn::new(0))
|
Ok(DeBruijn::new(0))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn text(&self) -> &str {
|
||||||
|
"i"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Encode for FakeNamedDeBruijn {
|
impl Encode for FakeNamedDeBruijn {
|
||||||
|
@ -333,6 +346,10 @@ impl<'b> Binder<'b> for FakeNamedDeBruijn {
|
||||||
|
|
||||||
Ok(index.into())
|
Ok(index.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn text(&self) -> &str {
|
||||||
|
&self.0.text
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Encode for DefaultFunction {
|
impl Encode for DefaultFunction {
|
||||||
|
|
|
@ -10,7 +10,7 @@ mod runtime;
|
||||||
use cost_model::{ExBudget, StepKind};
|
use cost_model::{ExBudget, StepKind};
|
||||||
pub use error::Error;
|
pub use error::Error;
|
||||||
|
|
||||||
use self::{cost_model::CostModel, runtime::BuiltinRuntime};
|
use self::{cost_model::CostModel, error::Type, runtime::BuiltinRuntime};
|
||||||
|
|
||||||
pub struct Machine {
|
pub struct Machine {
|
||||||
costs: CostModel,
|
costs: CostModel,
|
||||||
|
@ -198,7 +198,7 @@ impl Machine {
|
||||||
term,
|
term,
|
||||||
mut runtime,
|
mut runtime,
|
||||||
} => {
|
} => {
|
||||||
let force_term = Term::Force(Box::new(dbg!(term)));
|
let force_term = Term::Force(Box::new(term));
|
||||||
|
|
||||||
if runtime.needs_force() {
|
if runtime.needs_force() {
|
||||||
runtime.consume_force();
|
runtime.consume_force();
|
||||||
|
@ -207,7 +207,7 @@ impl Machine {
|
||||||
|
|
||||||
self.return_compute(res)
|
self.return_compute(res)
|
||||||
} else {
|
} else {
|
||||||
todo!()
|
Err(Error::BuiltinTermArgumentExpected(force_term))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
rest => Err(Error::NonPolymorphicInstantiation(rest)),
|
rest => Err(Error::NonPolymorphicInstantiation(rest)),
|
||||||
|
@ -245,7 +245,7 @@ impl Machine {
|
||||||
|
|
||||||
self.return_compute(res)
|
self.return_compute(res)
|
||||||
} else {
|
} else {
|
||||||
todo!()
|
Err(Error::UnexpectedBuiltinTermArgument(t))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
rest => Err(Error::NonFunctionalApplication(rest)),
|
rest => Err(Error::NonFunctionalApplication(rest)),
|
||||||
|
@ -367,7 +367,7 @@ impl Value {
|
||||||
} else {
|
} else {
|
||||||
//TODO
|
//TODO
|
||||||
// std::mem::size_of( i.abs()
|
// std::mem::size_of( i.abs()
|
||||||
todo!()
|
1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Constant::ByteString(b) => (((b.len() - 1) / 8) + 1) as i64,
|
Constant::ByteString(b) => (((b.len() - 1) / 8) + 1) as i64,
|
||||||
|
@ -382,3 +382,21 @@ impl Value {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<&Value> for Type {
|
||||||
|
fn from(value: &Value) -> Self {
|
||||||
|
match value {
|
||||||
|
Value::Con(constant) => match constant {
|
||||||
|
Constant::Integer(_) => Type::Integer,
|
||||||
|
Constant::ByteString(_) => Type::ByteString,
|
||||||
|
Constant::String(_) => Type::String,
|
||||||
|
Constant::Char(_) => todo!(),
|
||||||
|
Constant::Unit => Type::Unit,
|
||||||
|
Constant::Bool(_) => Type::Bool,
|
||||||
|
},
|
||||||
|
Value::Delay(_) => todo!(),
|
||||||
|
Value::Lambda { .. } => todo!(),
|
||||||
|
Value::Builtin { .. } => todo!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
use std::fmt::Display;
|
||||||
|
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
use crate::ast::{NamedDeBruijn, Term};
|
use crate::ast::{NamedDeBruijn, Term};
|
||||||
|
@ -18,4 +20,31 @@ pub enum Error {
|
||||||
NonPolymorphicInstantiation(Value),
|
NonPolymorphicInstantiation(Value),
|
||||||
#[error("Attempted to apply a non-function: {0:#?}")]
|
#[error("Attempted to apply a non-function: {0:#?}")]
|
||||||
NonFunctionalApplication(Value),
|
NonFunctionalApplication(Value),
|
||||||
|
#[error("Type mismatch expected '{0}' got '{1}'")]
|
||||||
|
TypeMismatch(Type, Type),
|
||||||
|
#[error("A builtin received a term argument when something else was expected:\n\n{}\n\nYou probably forgot to wrap the builtin with a force.", .0.to_pretty())]
|
||||||
|
UnexpectedBuiltinTermArgument(Term<NamedDeBruijn>),
|
||||||
|
#[error("A builtin expected a term argument, but something else was received:\n\n{}\n\nYou probably have an extra force wrapped around a builtin", .0.to_pretty())]
|
||||||
|
BuiltinTermArgumentExpected(Term<NamedDeBruijn>),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum Type {
|
||||||
|
Bool,
|
||||||
|
Integer,
|
||||||
|
String,
|
||||||
|
ByteString,
|
||||||
|
Unit,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Type {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Type::Bool => write!(f, "bool"),
|
||||||
|
Type::Integer => write!(f, "integer"),
|
||||||
|
Type::String => write!(f, "string"),
|
||||||
|
Type::ByteString => write!(f, "bytestring"),
|
||||||
|
Type::Unit => write!(f, "unit"),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,9 +2,8 @@ use crate::{ast::Constant, builtins::DefaultFunction};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
cost_model::{BuiltinCosts, ExBudget},
|
cost_model::{BuiltinCosts, ExBudget},
|
||||||
// cost_model::{CostingFun, ExBudget},
|
error::Type,
|
||||||
Error,
|
Error, Value,
|
||||||
Value,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
//#[derive(std::cmp::PartialEq)]
|
//#[derive(std::cmp::PartialEq)]
|
||||||
|
@ -13,12 +12,6 @@ use super::{
|
||||||
// Deferred,
|
// Deferred,
|
||||||
//}
|
//}
|
||||||
|
|
||||||
// pub struct BuiltinRuntimeOptions<T, G> {
|
|
||||||
// immediate_eval: T,
|
|
||||||
// deferred_eval: T,
|
|
||||||
// budget: fn(CostingFun<G>) -> fn(Vec<u32>) -> ExBudget,
|
|
||||||
// }
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct BuiltinRuntime {
|
pub struct BuiltinRuntime {
|
||||||
args: Vec<Value>,
|
args: Vec<Value>,
|
||||||
|
@ -35,16 +28,6 @@ impl BuiltinRuntime {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// fn from_builtin_runtime_options<G>(
|
|
||||||
// eval_mode: EvalMode,
|
|
||||||
// cost: CostingFun<G>,
|
|
||||||
// runtime_options: BuiltinRuntimeOptions<T, G>,
|
|
||||||
// ) -> BuiltinRuntime {
|
|
||||||
// Self {
|
|
||||||
// budget: (runtime_options.budget)(cost),
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
pub fn is_arrow(&self) -> bool {
|
pub fn is_arrow(&self) -> bool {
|
||||||
self.args.len() != self.fun.arity()
|
self.args.len() != self.fun.arity()
|
||||||
}
|
}
|
||||||
|
@ -209,7 +192,7 @@ impl DefaultFunction {
|
||||||
if arg.is_integer() {
|
if arg.is_integer() {
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
todo!("type error")
|
Err(Error::TypeMismatch(Type::Integer, arg.into()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DefaultFunction::SubtractInteger => todo!(),
|
DefaultFunction::SubtractInteger => todo!(),
|
||||||
|
@ -240,16 +223,12 @@ impl DefaultFunction {
|
||||||
DefaultFunction::EncodeUtf8 => todo!(),
|
DefaultFunction::EncodeUtf8 => todo!(),
|
||||||
DefaultFunction::DecodeUtf8 => todo!(),
|
DefaultFunction::DecodeUtf8 => todo!(),
|
||||||
DefaultFunction::IfThenElse => {
|
DefaultFunction::IfThenElse => {
|
||||||
if args.is_empty() {
|
if args.is_empty() && !arg.is_bool() {
|
||||||
if arg.is_bool() {
|
Err(Error::TypeMismatch(Type::Bool, arg.into()))
|
||||||
return Ok(());
|
|
||||||
} else {
|
} else {
|
||||||
todo!("type error")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
}
|
||||||
DefaultFunction::ChooseUnit => todo!(),
|
DefaultFunction::ChooseUnit => todo!(),
|
||||||
DefaultFunction::Trace => todo!(),
|
DefaultFunction::Trace => todo!(),
|
||||||
DefaultFunction::FstPair => todo!(),
|
DefaultFunction::FstPair => todo!(),
|
||||||
|
@ -278,18 +257,19 @@ impl DefaultFunction {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This should be safe because we've already checked
|
||||||
|
// the types of the args as they were pushed. Although
|
||||||
|
// the unreachables look ugly, it's the reality of the situation.
|
||||||
pub fn call(&self, args: &[Value]) -> Result<Value, Error> {
|
pub fn call(&self, args: &[Value]) -> Result<Value, Error> {
|
||||||
match self {
|
match self {
|
||||||
DefaultFunction::AddInteger => {
|
DefaultFunction::AddInteger => {
|
||||||
assert_eq!(args.len(), self.arity());
|
|
||||||
|
|
||||||
let args = (&args[0], &args[1]);
|
let args = (&args[0], &args[1]);
|
||||||
|
|
||||||
match args {
|
match args {
|
||||||
(Value::Con(Constant::Integer(arg1)), Value::Con(Constant::Integer(arg2))) => {
|
(Value::Con(Constant::Integer(arg1)), Value::Con(Constant::Integer(arg2))) => {
|
||||||
Ok(Value::Con(Constant::Integer(arg1 + arg2)))
|
Ok(Value::Con(Constant::Integer(arg1 + arg2)))
|
||||||
}
|
}
|
||||||
_ => todo!("handle error"),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DefaultFunction::SubtractInteger => todo!(),
|
DefaultFunction::SubtractInteger => todo!(),
|
||||||
|
@ -327,7 +307,7 @@ impl DefaultFunction {
|
||||||
Ok(args[2].clone())
|
Ok(args[2].clone())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => todo!("handle error"),
|
_ => unreachable!(),
|
||||||
},
|
},
|
||||||
DefaultFunction::ChooseUnit => todo!(),
|
DefaultFunction::ChooseUnit => todo!(),
|
||||||
DefaultFunction::Trace => todo!(),
|
DefaultFunction::Trace => todo!(),
|
||||||
|
|
|
@ -82,10 +82,10 @@ peg::parser! {
|
||||||
}
|
}
|
||||||
|
|
||||||
rule delay() -> Term<Name>
|
rule delay() -> Term<Name>
|
||||||
= "(" _* "delay" _+ t:term() _* ")" { Term::Delay(Box::new(t)) }
|
= "(" _* "delay" _* t:term() _* ")" { Term::Delay(Box::new(t)) }
|
||||||
|
|
||||||
rule force() -> Term<Name>
|
rule force() -> Term<Name>
|
||||||
= "(" _* "force" _+ t:term() _* ")" { Term::Force(Box::new(t)) }
|
= "(" _* "force" _* t:term() _* ")" { Term::Force(Box::new(t)) }
|
||||||
|
|
||||||
rule error() -> Term<Name>
|
rule error() -> Term<Name>
|
||||||
= "(" _* "error" _* ")" { Term::Error }
|
= "(" _* "error" _* ")" { Term::Error }
|
||||||
|
|
|
@ -1,8 +1,14 @@
|
||||||
use pretty::RcDoc;
|
use pretty::RcDoc;
|
||||||
|
|
||||||
use crate::ast::{Constant, Name, Program, Term};
|
use crate::{
|
||||||
|
ast::{Constant, Program, Term},
|
||||||
|
flat::Binder,
|
||||||
|
};
|
||||||
|
|
||||||
impl Program<Name> {
|
impl<'a, T> Program<T>
|
||||||
|
where
|
||||||
|
T: Binder<'a>,
|
||||||
|
{
|
||||||
pub fn to_pretty(&self) -> String {
|
pub fn to_pretty(&self) -> String {
|
||||||
let mut w = Vec::new();
|
let mut w = Vec::new();
|
||||||
|
|
||||||
|
@ -40,7 +46,10 @@ impl Program<Name> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Term<Name> {
|
impl<'a, T> Term<T>
|
||||||
|
where
|
||||||
|
T: Binder<'a>,
|
||||||
|
{
|
||||||
pub fn to_pretty(&self) -> String {
|
pub fn to_pretty(&self) -> String {
|
||||||
let mut w = Vec::new();
|
let mut w = Vec::new();
|
||||||
|
|
||||||
|
@ -65,7 +74,7 @@ impl Term<Name> {
|
||||||
|
|
||||||
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()),
|
||||||
Term::Delay(term) => RcDoc::text("(")
|
Term::Delay(term) => RcDoc::text("(")
|
||||||
.append(
|
.append(
|
||||||
RcDoc::text("delay")
|
RcDoc::text("delay")
|
||||||
|
@ -82,7 +91,7 @@ impl Term<Name> {
|
||||||
.append(
|
.append(
|
||||||
RcDoc::text("lam")
|
RcDoc::text("lam")
|
||||||
.append(RcDoc::line())
|
.append(RcDoc::line())
|
||||||
.append(RcDoc::text(¶meter_name.text))
|
.append(RcDoc::text(parameter_name.text()))
|
||||||
.append(RcDoc::line())
|
.append(RcDoc::line())
|
||||||
.append(body.to_doc())
|
.append(body.to_doc())
|
||||||
.nest(2),
|
.nest(2),
|
||||||
|
@ -161,7 +170,7 @@ impl Constant {
|
||||||
.append(RcDoc::text("()")),
|
.append(RcDoc::text("()")),
|
||||||
Constant::Bool(b) => RcDoc::text("bool")
|
Constant::Bool(b) => RcDoc::text("bool")
|
||||||
.append(RcDoc::line())
|
.append(RcDoc::line())
|
||||||
.append(RcDoc::text(if *b { "true" } else { "false" })),
|
.append(RcDoc::text(if *b { "True" } else { "False" })),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue