Tweak pretty-printing

This pretty printing now (mostly) matches https://github.com/input-output-hk/plutus/issues/4751#issuecomment-1538377273; the only concern is whether the PlutusData stuff should be upstreamed to pallas, and whether pallas has a way to print BigInts easier
This commit is contained in:
Pi Lanningham 2023-07-03 20:18:43 -04:00 committed by Lucas
parent 94bf75dd1c
commit ecff82659d
2 changed files with 199 additions and 23 deletions

View File

@ -3,8 +3,10 @@ use crate::{
flat::Binder, flat::Binder,
plutus_data_to_bytes, plutus_data_to_bytes,
}; };
use pallas_codec::utils::KeyValuePairs;
use pallas_primitives::babbage::{PlutusData, Constr};
use pretty::RcDoc; use pretty::RcDoc;
use std::ascii::escape_default; use std::{ascii::escape_default, io::Read};
impl<'a, T> Program<T> impl<'a, T> Program<T>
where where
@ -201,31 +203,31 @@ impl Constant {
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" })),
Constant::ProtoList(r#type, items) => RcDoc::text("list") Constant::ProtoList(r#type, items) => RcDoc::text("(")
.append(RcDoc::line_()) .append("list")
.append(RcDoc::text("<")) .append(RcDoc::space())
.append(r#type.to_doc()) .append(r#type.to_doc())
.append(RcDoc::text(">")) .append(")")
.append(RcDoc::line()) .append(RcDoc::line())
.append(RcDoc::text("[")) .append(RcDoc::text("["))
.append(RcDoc::intersperse( .append(RcDoc::intersperse(
items.iter().map(|c| c.to_doc_list()), items.iter().map(|c| c.to_doc_list()),
RcDoc::text(","), RcDoc::text(", "),
)) ))
.append(RcDoc::text("]")), .append(RcDoc::text("]")),
Constant::ProtoPair(type_left, type_right, left, right) => RcDoc::text("pair") Constant::ProtoPair(type_left, type_right, left, right) => RcDoc::text("(")
.append(RcDoc::line_()) .append("pair")
.append(RcDoc::text("<")) .append(RcDoc::space())
.append(type_left.to_doc()) .append(type_left.to_doc())
.append(RcDoc::text(", ")) .append(RcDoc::space())
.append(type_right.to_doc()) .append(type_right.to_doc())
.append(RcDoc::text(">")) .append(RcDoc::text(")"))
.append(RcDoc::line()) .append(RcDoc::line())
.append(RcDoc::text("[")) .append(RcDoc::text("("))
.append(left.to_doc_list()) .append(left.to_doc_list())
.append(RcDoc::text(",")) .append(RcDoc::text(", "))
.append(right.to_doc_list()) .append(right.to_doc_list())
.append(RcDoc::text("]")), .append(RcDoc::text(")")),
d @ Constant::Data(_) => RcDoc::text("data ").append(d.to_doc_list()), d @ Constant::Data(_) => RcDoc::text("data ").append(d.to_doc_list()),
} }
} }
@ -242,18 +244,66 @@ impl Constant {
Constant::ProtoList(_, items) => RcDoc::text("[") Constant::ProtoList(_, items) => RcDoc::text("[")
.append(RcDoc::intersperse( .append(RcDoc::intersperse(
items.iter().map(|c| c.to_doc_list()), items.iter().map(|c| c.to_doc_list()),
RcDoc::text(","), RcDoc::text(", "),
)) ))
.append(RcDoc::text("]")), .append(RcDoc::text("]")),
Constant::ProtoPair(_, _, left, right) => RcDoc::text("[") Constant::ProtoPair(_, _, left, right) => RcDoc::text("(")
.append((*left).to_doc_list()) .append((*left).to_doc_list())
.append(RcDoc::text(", ")) .append(RcDoc::text(", "))
.append((*right).to_doc_list()) .append((*right).to_doc_list())
.append(RcDoc::text("]")), .append(RcDoc::text(")")),
Constant::Data(data) => RcDoc::text("#").append(RcDoc::text(hex::encode( Constant::Data(data) => RcDoc::text("(")
plutus_data_to_bytes(data).unwrap(), .append(Self::to_doc_list_plutus_data(&data))
))), .append(RcDoc::text(")"))
}
}
// This feels a little awkward here; not sure if it should be upstreamed to pallas
fn to_doc_list_plutus_data(data: &PlutusData) -> RcDoc<()> {
match data {
PlutusData::Constr(Constr{ tag, fields, ..}) => RcDoc::text("Constr")
.append(RcDoc::space())
.append(RcDoc::as_string(tag))
.append(RcDoc::space())
.append(RcDoc::text("["))
.append(RcDoc::intersperse(
fields.iter().map(|f| Self::to_doc_list_plutus_data(f)),
RcDoc::text(", "),
))
.append(RcDoc::text("]")),
PlutusData::Map(kvp) => RcDoc::text("Map")
.append(RcDoc::space())
.append(RcDoc::text("["))
.append(RcDoc::intersperse(
kvp.iter().map(|(key, value)| RcDoc::text("(")
.append(Self::to_doc_list_plutus_data(key))
.append(RcDoc::text(", "))
.append(Self::to_doc_list_plutus_data(value))
.append(RcDoc::text(")")),
),
RcDoc::text(", "),
))
.append(RcDoc::text("]")),
PlutusData::BigInt(bi) => RcDoc::text("I")
.append(RcDoc::space())
.append(match bi {
pallas_primitives::babbage::BigInt::Int(v) => RcDoc::text(v.to_string()),
pallas_primitives::babbage::BigInt::BigUInt(v) => RcDoc::text(v.to_string()),
pallas_primitives::babbage::BigInt::BigNInt(v) => RcDoc::text(v.to_string()),
}),
PlutusData::BoundedBytes(bs) => RcDoc::text("B")
.append(RcDoc::space())
.append(RcDoc::text("#"))
.append(RcDoc::text(hex::encode(bs.to_vec()))),
PlutusData::Array(a) => RcDoc::text("List")
.append(RcDoc::space())
.append(RcDoc::text("["))
.append(RcDoc::intersperse(
a.iter().map(|item| Self::to_doc_list_plutus_data(item)),
RcDoc::text(", "),
))
.append(RcDoc::text("]")),
} }
} }
} }
@ -266,10 +316,11 @@ impl Type {
Type::String => RcDoc::text("string"), Type::String => RcDoc::text("string"),
Type::ByteString => RcDoc::text("bytestring"), Type::ByteString => RcDoc::text("bytestring"),
Type::Unit => RcDoc::text("unit"), Type::Unit => RcDoc::text("unit"),
Type::List(r#type) => RcDoc::text("list") Type::List(r#type) => RcDoc::text("(list")
.append(RcDoc::text("<")) .append(RcDoc::line())
.append(r#type.to_doc()) .append(r#type.to_doc())
.append(RcDoc::text(">")), .append(RcDoc::line_())
.append(")"),
Type::Pair(l, r) => RcDoc::text("pair") Type::Pair(l, r) => RcDoc::text("pair")
.append(RcDoc::text("<")) .append(RcDoc::text("<"))
.append(l.to_doc()) .append(l.to_doc())

View File

@ -0,0 +1,125 @@
use num_bigint::{ToBigInt};
use uplc::{
ast::{DeBruijn, Constant, Type, Term}, PlutusData, Constr,
};
// Examples sourced from https://github.com/input-output-hk/plutus/issues/4751#issuecomment-1538377273
#[test]
fn constant_list_integer() {
let term = Term::<DeBruijn>::Constant(Constant::ProtoList(Type::Integer.into(), vec![
Constant::Integer(0.to_bigint().unwrap()),
Constant::Integer(1.to_bigint().unwrap()),
Constant::Integer(2.to_bigint().unwrap()),
]).into());
assert_eq!(term.to_pretty(), "(con (list integer) [0, 1, 2])");
}
#[test]
fn constant_pair_bool_bytestring() {
let term = Term::<DeBruijn>::Constant(Constant::ProtoPair(
Type::Bool.into(), Type::ByteString.into(),
Constant::Bool(true).into(), Constant::ByteString(vec![0x01, 0x23, 0x45]).into(),
).into());
assert_eq!(term.to_pretty(), "(con (pair bool bytestring) (True, #012345))");
}
#[test]
fn constant_pair_unit_string() {
let term = Term::<DeBruijn>::Constant(Constant::ProtoPair(
Type::Unit.into(), Type::String.into(),
Constant::Unit.into(), Constant::String("hello universe".into()).into(),
).into());
assert_eq!(term.to_pretty(), "(con (pair unit string) ((), \"hello universe\"))")
}
#[test]
fn constant_deeply_nested_list() {
let t0 = Type::Integer;
let t1 = Type::List(t0.clone().into());
let t2 = Type::List(t1.clone().into());
let term = Term::<DeBruijn>::Constant(Constant::ProtoList(t2, vec![
Constant::ProtoList(t1.clone(), vec![
Constant::ProtoList(t0.clone(), vec![
Constant::Integer(-1.to_bigint().unwrap()),
]),
Constant::ProtoList(t0.clone(), vec![])
]),
Constant::ProtoList(t1.clone(), vec![
Constant::ProtoList(t0.clone(), vec![]),
Constant::ProtoList(t0.clone(), vec![
Constant::Integer(2.to_bigint().unwrap()),
Constant::Integer(3.to_bigint().unwrap())
]),
])
]).into());
assert_eq!(term.to_pretty(), "(con (list (list (list integer))) [[[-1], []], [[], [2, 3]]])");
}
#[test]
fn constant_data_constr() {
let term = Term::<DeBruijn>::Constant(Constant::Data(
PlutusData::Constr(Constr::<PlutusData> {
tag: 1,
any_constructor: None,
fields: vec![PlutusData::BigInt(pallas_primitives::alonzo::BigInt::Int(2.into()))]
})
).into());
assert_eq!(term.to_pretty(), "(con data (Constr 1 [I 2]))");
}
#[test]
fn constant_data_map() {
let term = Term::<DeBruijn>::Constant(Constant::Data(
PlutusData::Map(uplc::KeyValuePairs::Def(vec![
(
PlutusData::BigInt(pallas_primitives::alonzo::BigInt::Int(0.into())),
PlutusData::BoundedBytes(vec![0x00].into()),
),
(
PlutusData::BigInt(pallas_primitives::alonzo::BigInt::Int(1.into())),
PlutusData::BoundedBytes(vec![0x0f].into()),
),
]))
).into());
assert_eq!(term.to_pretty(), "(con data (Map [(I 0, B #00), (I 1, B #0f)]))");
}
#[test]
fn constant_data_list() {
let term = Term::<DeBruijn>::Constant(Constant::Data(
PlutusData::Array(vec![
PlutusData::BigInt(pallas_primitives::alonzo::BigInt::Int(0.into())),
PlutusData::BigInt(pallas_primitives::alonzo::BigInt::Int(1.into())),
])
).into());
assert_eq!(term.to_pretty(), "(con data (List [I 0, I 1]))");
}
#[test]
fn constant_data_int() {
let term = Term::<DeBruijn>::Constant(Constant::Data(
PlutusData::BigInt(pallas_primitives::alonzo::BigInt::Int(2.into())),
).into());
assert_eq!(term.to_pretty(), "(con data (I 2))");
// TODO: large integers currently encode as bytestrings, which isn't great
/*
let term = Term::<DeBruijn>::Constant(Constant::Data(
PlutusData::BigInt(pallas_primitives::alonzo::BigInt::BigUInt(vec![2,3,4].into())),
).into());
assert_eq!(term.to_pretty(), "(con data (I 131844))");
let term = Term::<DeBruijn>::Constant(Constant::Data(
PlutusData::BigInt(pallas_primitives::alonzo::BigInt::BigNInt(vec![FF,FD,FC,FC].into())),
).into());
assert_eq!(term.to_pretty(), "(con data (I -131844))");
*/
}
#[test]
fn constant_data_bytes() {
let term = Term::<DeBruijn>::Constant(Constant::Data(
PlutusData::BoundedBytes(vec![0x00, 0x1A].into()),
).into());
assert_eq!(term.to_pretty(), "(con data (B #001a))");
}