feat: start pretty printing

This commit is contained in:
rvcas 2022-06-17 17:40:59 -04:00 committed by Lucas
parent b95c04a9dd
commit 5a6ba40557
7 changed files with 315 additions and 44 deletions

73
Cargo.lock generated
View File

@ -17,6 +17,12 @@ version = "1.0.57"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08f9b8508dccb7687a1d6c4ce66b2b0ecef467c94667de27d8d7fe1f8d2a9cdc" checksum = "08f9b8508dccb7687a1d6c4ce66b2b0ecef467c94667de27d8d7fe1f8d2a9cdc"
[[package]]
name = "arrayvec"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b"
[[package]] [[package]]
name = "atty" name = "atty"
version = "0.2.14" version = "0.2.14"
@ -40,6 +46,12 @@ version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]] [[package]]
name = "clap" name = "clap"
version = "3.1.18" version = "3.1.18"
@ -136,6 +148,15 @@ version = "0.2.126"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836"
[[package]]
name = "log"
version = "0.4.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
dependencies = [
"cfg-if",
]
[[package]] [[package]]
name = "os_str_bytes" name = "os_str_bytes"
version = "6.0.1" version = "6.0.1"
@ -169,6 +190,18 @@ version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f9b0efd3ba03c3a409d44d60425f279ec442bcf0b9e63ff4e410da31c8b0f69f" checksum = "f9b0efd3ba03c3a409d44d60425f279ec442bcf0b9e63ff4e410da31c8b0f69f"
[[package]]
name = "pretty"
version = "0.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "83f3aa1e3ca87d3b124db7461265ac176b40c277f37e503eaa29c9c75c037846"
dependencies = [
"arrayvec",
"log",
"typed-arena",
"unicode-segmentation",
]
[[package]] [[package]]
name = "proc-macro-error" name = "proc-macro-error"
version = "1.0.4" version = "1.0.4"
@ -211,37 +244,12 @@ dependencies = [
"proc-macro2", "proc-macro2",
] ]
[[package]]
name = "rustversion"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2cc38e8fa666e2de3c4aba7edeb5ffc5246c1c2ed0e3d17e560aeeba736b23f"
[[package]] [[package]]
name = "strsim" name = "strsim"
version = "0.10.0" version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]]
name = "strum"
version = "0.24.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e96acfc1b70604b8b2f1ffa4c57e59176c7dbb05d556c71ecd2f5498a1dee7f8"
[[package]]
name = "strum_macros"
version = "0.24.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6878079b17446e4d3eba6192bb0a2950d5b14f0ed8424b852310e5a94345d0ef"
dependencies = [
"heck",
"proc-macro2",
"quote",
"rustversion",
"syn",
]
[[package]] [[package]]
name = "syn" name = "syn"
version = "1.0.95" version = "1.0.95"
@ -288,12 +296,24 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "typed-arena"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0685c84d5d54d1c26f7d3eb96cd41550adb97baed141a761cf335d3d33bcd0ae"
[[package]] [[package]]
name = "unicode-ident" name = "unicode-ident"
version = "1.0.0" version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d22af068fba1eb5edcb4aea19d382b2a3deb4c8f9d475c589b6ada9e0fd493ee" checksum = "d22af068fba1eb5edcb4aea19d382b2a3deb4c8f9d475c589b6ada9e0fd493ee"
[[package]]
name = "unicode-segmentation"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e8820f5d777f6224dc4be3632222971ac30164d4a258d595640799554ebfd99"
[[package]] [[package]]
name = "uplc" name = "uplc"
version = "0.0.2" version = "0.0.2"
@ -301,8 +321,7 @@ dependencies = [
"flat-rs", "flat-rs",
"hex", "hex",
"peg", "peg",
"strum", "pretty",
"strum_macros",
"thiserror", "thiserror",
] ]

View File

@ -6,7 +6,7 @@ use clap::{Parser, Subcommand};
#[derive(Parser)] #[derive(Parser)]
#[clap(author, version, about, long_about = None)] #[clap(author, version, about, long_about = None)]
#[clap(propagate_version = true)] #[clap(propagate_version = true)]
pub enum Cli { pub enum Args {
/// A subcommand for working with Untyped Plutus Core /// A subcommand for working with Untyped Plutus Core
#[clap(subcommand)] #[clap(subcommand)]
Uplc(UplcCommand), Uplc(UplcCommand),
@ -28,10 +28,12 @@ pub enum UplcCommand {
input: PathBuf, input: PathBuf,
#[clap(short, long)] #[clap(short, long)]
print: bool, print: bool,
#[clap(short, long)]
out: Option<String>,
}, },
} }
impl Default for Cli { impl Default for Args {
fn default() -> Self { fn default() -> Self {
Self::parse() Self::parse()
} }

View File

@ -5,13 +5,15 @@ use uplc::{
parser, parser,
}; };
use aiken::{Cli, UplcCommand}; mod args;
use args::{Args, UplcCommand};
fn main() -> anyhow::Result<()> { fn main() -> anyhow::Result<()> {
let args = Cli::default(); let args = Args::default();
match args { match args {
Cli::Uplc(uplc) => match uplc { Args::Uplc(uplc) => match uplc {
UplcCommand::Flat { input, print, out } => { UplcCommand::Flat { input, print, out } => {
let code = std::fs::read_to_string(&input)?; let code = std::fs::read_to_string(&input)?;
@ -22,17 +24,19 @@ fn main() -> anyhow::Result<()> {
let bytes = program.to_flat()?; let bytes = program.to_flat()?;
if print { if print {
let mut output = String::new();
for (i, byte) in bytes.iter().enumerate() { for (i, byte) in bytes.iter().enumerate() {
print!("{:08b}", byte); output.push_str(&format!("{:08b}", byte));
if (i + 1) % 4 == 0 { if (i + 1) % 4 == 0 {
println!(); output.push('\n');
} else { } else {
print!(" "); output.push(' ');
} }
} }
println!(); println!("{}", output);
} else { } else {
let out_name = if let Some(out) = out { let out_name = if let Some(out) = out {
out out
@ -43,15 +47,25 @@ fn main() -> anyhow::Result<()> {
fs::write(&out_name, &bytes)?; fs::write(&out_name, &bytes)?;
} }
} }
UplcCommand::Unflat { input, print } => { UplcCommand::Unflat { input, print, out } => {
let bytes = std::fs::read(&input)?; let bytes = std::fs::read(&input)?;
let program = Program::<DeBruijn>::from_flat(&bytes)?; let program = Program::<DeBruijn>::from_flat(&bytes)?;
let program: Program<Name> = program.try_into()?; let program: Program<Name> = program.try_into()?;
let pretty = program.to_pretty();
if print { if print {
println!("{:#?}", program); println!("{}", pretty);
} else {
let out_name = if let Some(out) = out {
out
} else {
format!("{}.uplc", input.file_stem().unwrap().to_str().unwrap())
};
fs::write(&out_name, pretty)?;
} }
} }
}, },

View File

@ -16,6 +16,5 @@ exclude = ["test_data/*"]
flat-rs = { path = "../flat", version = "0.0.2" } flat-rs = { path = "../flat", version = "0.0.2" }
hex = "0.4.3" hex = "0.4.3"
peg = "0.8.0" peg = "0.8.0"
strum = "0.24.0" pretty = "0.11.3"
strum_macros = "0.24.0"
thiserror = "1.0.31" thiserror = "1.0.31"

View File

@ -1,11 +1,11 @@
use std::{fmt::Display, str::FromStr};
use flat_rs::de; use flat_rs::de;
use strum_macros::EnumString;
/// All the possible builtin functions in Untyped Plutus Core. /// All the possible builtin functions in Untyped Plutus Core.
#[repr(u8)] #[repr(u8)]
#[allow(non_camel_case_types)] #[allow(non_camel_case_types)]
#[derive(Debug, Clone, EnumString, PartialEq, Copy)] #[derive(Debug, Clone, PartialEq, Copy)]
#[strum(serialize_all = "camelCase")]
pub enum DefaultFunction { pub enum DefaultFunction {
// Integer functions // Integer functions
AddInteger = 0, AddInteger = 0,
@ -28,7 +28,6 @@ pub enum DefaultFunction {
LessThanByteString = 16, LessThanByteString = 16,
LessThanEqualsByteString = 17, LessThanEqualsByteString = 17,
// Cryptography and hash functions // Cryptography and hash functions
#[strum(serialize = "sha2_256")]
Sha2_256 = 18, Sha2_256 = 18,
Sha3_256 = 19, Sha3_256 = 19,
Blake2b_256 = 20, Blake2b_256 = 20,
@ -195,3 +194,132 @@ impl TryFrom<u8> for DefaultFunction {
} }
} }
} }
impl FromStr for DefaultFunction {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
use DefaultFunction::*;
match s {
"addInteger" => Ok(AddInteger),
"subtractInteger" => Ok(SubtractInteger),
"multiplyInteger" => Ok(MultiplyInteger),
"divideInteger" => Ok(DivideInteger),
"quotientInteger" => Ok(QuotientInteger),
"remainderInteger" => Ok(RemainderInteger),
"modInteger" => Ok(ModInteger),
"equalsInteger" => Ok(EqualsInteger),
"lessThanInteger" => Ok(LessThanInteger),
"lessThanEqualsInteger" => Ok(LessThanEqualsInteger),
"appendByteString" => Ok(AppendByteString),
"consByteString" => Ok(ConsByteString),
"sliceByteString" => Ok(SliceByteString),
"lengthOfByteString" => Ok(LengthOfByteString),
"indexByteString" => Ok(IndexByteString),
"equalsByteString" => Ok(EqualsByteString),
"lessThanByteString" => Ok(LessThanByteString),
"lessThanEqualsByteString" => Ok(LessThanEqualsByteString),
"sha2_256" => Ok(Sha2_256),
"sha3_256" => Ok(Sha3_256),
"blake2b_256" => Ok(Blake2b_256),
"verifySignature" => Ok(VerifySignature),
"verifyEcdsaSecp256k1Signature" => Ok(VerifyEcdsaSecp256k1Signature),
"verifySchnorrSecp256k1Signature" => Ok(VerifySchnorrSecp256k1Signature),
"appendString" => Ok(AppendString),
"equalsString" => Ok(EqualsString),
"encodeUtf8" => Ok(EncodeUtf8),
"decodeUtf8" => Ok(DecodeUtf8),
"ifThenElse" => Ok(IfThenElse),
"chooseUnit" => Ok(ChooseUnit),
"trace" => Ok(Trace),
"fstPair" => Ok(FstPair),
"sndPair" => Ok(SndPair),
"chooseList" => Ok(ChooseList),
"mkCons" => Ok(MkCons),
"headList" => Ok(HeadList),
"tailList" => Ok(TailList),
"nullList" => Ok(NullList),
"chooseData" => Ok(ChooseData),
"constrData" => Ok(ConstrData),
"mapData" => Ok(MapData),
"listData" => Ok(ListData),
"iData" => Ok(IData),
"bData" => Ok(BData),
"unConstrData" => Ok(UnConstrData),
"unMapData" => Ok(UnMapData),
"unListData" => Ok(UnListData),
"unIData" => Ok(UnIData),
"unBData" => Ok(UnBData),
"equalsData" => Ok(EqualsData),
"serialiseData" => Ok(SerialiseData),
"mkPairData" => Ok(MkPairData),
"mkNilData" => Ok(MkNilData),
"mkNilPairData" => Ok(MkNilPairData),
rest => Err(format!("Default Function not found - {}", rest)),
}
}
}
impl Display for DefaultFunction {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
use DefaultFunction::*;
match self {
AddInteger => write!(f, "addInteger"),
SubtractInteger => write!(f, "subtractInteger"),
MultiplyInteger => write!(f, "multiplyInteger"),
DivideInteger => write!(f, "divideInteger"),
QuotientInteger => write!(f, "quotientInteger"),
RemainderInteger => write!(f, "remainderInteger"),
ModInteger => write!(f, "modInteger"),
EqualsInteger => write!(f, "equalsInteger"),
LessThanInteger => write!(f, "lessThanInteger"),
LessThanEqualsInteger => write!(f, "lessThanEqualsInteger"),
AppendByteString => write!(f, "appendByteString"),
ConsByteString => write!(f, "consByteString"),
SliceByteString => write!(f, "sliceByteString"),
LengthOfByteString => write!(f, "lengthOfByteString"),
IndexByteString => write!(f, "indexByteString"),
EqualsByteString => write!(f, "equalsByteString"),
LessThanByteString => write!(f, "lessThanByteString"),
LessThanEqualsByteString => write!(f, "lessThanEqualsByteString"),
Sha2_256 => write!(f, "sha2_256"),
Sha3_256 => write!(f, "sha3_256"),
Blake2b_256 => write!(f, "blake2b_256"),
VerifySignature => write!(f, "verifySignature"),
VerifyEcdsaSecp256k1Signature => write!(f, "verifyEcdsaSecp256k1Signature"),
VerifySchnorrSecp256k1Signature => write!(f, "verifySchnorrSecp256k1Signature"),
AppendString => write!(f, "appendString"),
EqualsString => write!(f, "equalsString"),
EncodeUtf8 => write!(f, "encodeUtf8"),
DecodeUtf8 => write!(f, "decodeUtf8"),
IfThenElse => write!(f, "ifThenElse"),
ChooseUnit => write!(f, "chooseUnit"),
Trace => write!(f, "trace"),
FstPair => write!(f, "fstPair"),
SndPair => write!(f, "sndPair"),
ChooseList => write!(f, "chooseList"),
MkCons => write!(f, "mkCons"),
HeadList => write!(f, "headList"),
TailList => write!(f, "tailList"),
NullList => write!(f, "nullList"),
ChooseData => write!(f, "chooseData"),
ConstrData => write!(f, "constrData"),
MapData => write!(f, "mapData"),
ListData => write!(f, "listData"),
IData => write!(f, "iData"),
BData => write!(f, "bData"),
UnConstrData => write!(f, "unConstrData"),
UnMapData => write!(f, "unMapData"),
UnListData => write!(f, "unListData"),
UnIData => write!(f, "unIData"),
UnBData => write!(f, "unBData"),
EqualsData => write!(f, "equalsData"),
SerialiseData => write!(f, "serialiseData"),
MkPairData => write!(f, "mkPairData"),
MkNilData => write!(f, "mkNilData"),
MkNilPairData => write!(f, "mkNilPairData"),
}
}
}

View File

@ -3,6 +3,7 @@ pub mod builtins;
mod debruijn; mod debruijn;
mod flat; mod flat;
pub mod parser; pub mod parser;
mod pretty;
#[cfg(test)] #[cfg(test)]
mod test; mod test;

108
crates/uplc/src/pretty.rs Normal file
View File

@ -0,0 +1,108 @@
use pretty::RcDoc;
use crate::ast::{Constant, Name, Program, Term};
impl Program<Name> {
pub fn to_pretty(&self) -> String {
let mut w = Vec::new();
self.to_doc().render(20, &mut w).unwrap();
String::from_utf8(w).unwrap()
}
fn to_doc(&self) -> RcDoc<()> {
let version = format!("{}.{}.{}", self.version.0, self.version.1, self.version.2);
RcDoc::text("(")
.append(RcDoc::text("program"))
.append(RcDoc::line())
.append(RcDoc::text(version))
.append(RcDoc::line())
.append(self.term.to_doc())
.append(RcDoc::line_())
.append(RcDoc::text(")"))
}
}
impl Term<Name> {
fn to_doc(&self) -> RcDoc<()> {
match self {
Term::Var(name) => RcDoc::text(format!("{}_{}", name.text, name.unique)),
Term::Delay(term) => RcDoc::text("(")
.append(RcDoc::line_())
.append(RcDoc::text("delay"))
.append(RcDoc::line())
.append(term.to_doc())
.append(RcDoc::line_())
.append(RcDoc::text(")")),
Term::Lambda {
parameter_name,
body,
} => RcDoc::text("(")
.append(RcDoc::line_())
.append(RcDoc::text(format!(
"{}_{}",
parameter_name.text, parameter_name.unique
)))
.append(RcDoc::line())
.append(body.to_doc())
.append(RcDoc::line_())
.append(RcDoc::text(")")),
Term::Apply { function, argument } => RcDoc::text("[")
.append(RcDoc::line_())
.append(function.to_doc())
.append(RcDoc::line())
.append(argument.to_doc())
.append(RcDoc::line_())
.append(RcDoc::text("]")),
Term::Constant(constant) => RcDoc::text("(")
.append(RcDoc::text("con"))
.append(RcDoc::text(" "))
.append(constant.to_doc())
.append(RcDoc::text(")")),
Term::Force(term) => RcDoc::text("(")
.append(RcDoc::line_())
.append(RcDoc::text("force"))
.append(RcDoc::line())
.append(term.to_doc())
.append(RcDoc::line_())
.append(RcDoc::text(")")),
Term::Error => RcDoc::text("(")
.append(RcDoc::line_())
.append(RcDoc::text("error"))
.append(RcDoc::line_())
.append(RcDoc::text(")")),
Term::Builtin(builtin) => RcDoc::text("(")
.append(RcDoc::line_())
.append(RcDoc::text("builtin"))
.append(RcDoc::line())
.append(RcDoc::text(builtin.to_string()))
.append(RcDoc::line_())
.append(RcDoc::text(")")),
}
}
}
impl Constant {
fn to_doc(&self) -> RcDoc<()> {
match self {
Constant::Integer(i) => RcDoc::text("integer")
.append(RcDoc::space())
.append(RcDoc::as_string(i)),
Constant::ByteString(bs) => RcDoc::text("bytestring")
.append(RcDoc::space())
.append(RcDoc::text(hex::encode(bs))),
Constant::String(s) => RcDoc::text("string")
.append(RcDoc::space())
.append(RcDoc::text(s)),
Constant::Char(c) => unimplemented!("char: {}", c),
Constant::Unit => RcDoc::text("unit")
.append(RcDoc::space())
.append(RcDoc::text("()")),
Constant::Bool(b) => RcDoc::text("bool")
.append(RcDoc::space())
.append(RcDoc::text(if *b { "true" } else { "false" })),
}
}
}