From 5a6ba405571c5ef14d2fb00b49f3191d396892a8 Mon Sep 17 00:00:00 2001 From: rvcas Date: Fri, 17 Jun 2022 17:40:59 -0400 Subject: [PATCH] feat: start pretty printing --- Cargo.lock | 73 ++++++++++------ crates/cli/src/{lib.rs => args.rs} | 6 +- crates/cli/src/main.rs | 32 +++++-- crates/uplc/Cargo.toml | 3 +- crates/uplc/src/builtins.rs | 136 ++++++++++++++++++++++++++++- crates/uplc/src/lib.rs | 1 + crates/uplc/src/pretty.rs | 108 +++++++++++++++++++++++ 7 files changed, 315 insertions(+), 44 deletions(-) rename crates/cli/src/{lib.rs => args.rs} (89%) create mode 100644 crates/uplc/src/pretty.rs diff --git a/Cargo.lock b/Cargo.lock index fa034cf9..d376b8a4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,6 +17,12 @@ version = "1.0.57" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08f9b8508dccb7687a1d6c4ce66b2b0ecef467c94667de27d8d7fe1f8d2a9cdc" +[[package]] +name = "arrayvec" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" + [[package]] name = "atty" version = "0.2.14" @@ -40,6 +46,12 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + [[package]] name = "clap" version = "3.1.18" @@ -136,6 +148,15 @@ version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" 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]] name = "os_str_bytes" version = "6.0.1" @@ -169,6 +190,18 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" 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]] name = "proc-macro-error" version = "1.0.4" @@ -211,37 +244,12 @@ dependencies = [ "proc-macro2", ] -[[package]] -name = "rustversion" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2cc38e8fa666e2de3c4aba7edeb5ffc5246c1c2ed0e3d17e560aeeba736b23f" - [[package]] name = "strsim" version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" 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]] name = "syn" version = "1.0.95" @@ -288,12 +296,24 @@ dependencies = [ "syn", ] +[[package]] +name = "typed-arena" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0685c84d5d54d1c26f7d3eb96cd41550adb97baed141a761cf335d3d33bcd0ae" + [[package]] name = "unicode-ident" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d22af068fba1eb5edcb4aea19d382b2a3deb4c8f9d475c589b6ada9e0fd493ee" +[[package]] +name = "unicode-segmentation" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e8820f5d777f6224dc4be3632222971ac30164d4a258d595640799554ebfd99" + [[package]] name = "uplc" version = "0.0.2" @@ -301,8 +321,7 @@ dependencies = [ "flat-rs", "hex", "peg", - "strum", - "strum_macros", + "pretty", "thiserror", ] diff --git a/crates/cli/src/lib.rs b/crates/cli/src/args.rs similarity index 89% rename from crates/cli/src/lib.rs rename to crates/cli/src/args.rs index abe25f89..bf149545 100644 --- a/crates/cli/src/lib.rs +++ b/crates/cli/src/args.rs @@ -6,7 +6,7 @@ use clap::{Parser, Subcommand}; #[derive(Parser)] #[clap(author, version, about, long_about = None)] #[clap(propagate_version = true)] -pub enum Cli { +pub enum Args { /// A subcommand for working with Untyped Plutus Core #[clap(subcommand)] Uplc(UplcCommand), @@ -28,10 +28,12 @@ pub enum UplcCommand { input: PathBuf, #[clap(short, long)] print: bool, + #[clap(short, long)] + out: Option, }, } -impl Default for Cli { +impl Default for Args { fn default() -> Self { Self::parse() } diff --git a/crates/cli/src/main.rs b/crates/cli/src/main.rs index 6db068b0..c383e00f 100644 --- a/crates/cli/src/main.rs +++ b/crates/cli/src/main.rs @@ -5,13 +5,15 @@ use uplc::{ parser, }; -use aiken::{Cli, UplcCommand}; +mod args; + +use args::{Args, UplcCommand}; fn main() -> anyhow::Result<()> { - let args = Cli::default(); + let args = Args::default(); match args { - Cli::Uplc(uplc) => match uplc { + Args::Uplc(uplc) => match uplc { UplcCommand::Flat { input, print, out } => { let code = std::fs::read_to_string(&input)?; @@ -22,17 +24,19 @@ fn main() -> anyhow::Result<()> { let bytes = program.to_flat()?; if print { + let mut output = String::new(); + for (i, byte) in bytes.iter().enumerate() { - print!("{:08b}", byte); + output.push_str(&format!("{:08b}", byte)); if (i + 1) % 4 == 0 { - println!(); + output.push('\n'); } else { - print!(" "); + output.push(' '); } } - println!(); + println!("{}", output); } else { let out_name = if let Some(out) = out { out @@ -43,15 +47,25 @@ fn main() -> anyhow::Result<()> { fs::write(&out_name, &bytes)?; } } - UplcCommand::Unflat { input, print } => { + UplcCommand::Unflat { input, print, out } => { let bytes = std::fs::read(&input)?; let program = Program::::from_flat(&bytes)?; let program: Program = program.try_into()?; + let pretty = program.to_pretty(); + 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)?; } } }, diff --git a/crates/uplc/Cargo.toml b/crates/uplc/Cargo.toml index 1992b2df..acf37425 100644 --- a/crates/uplc/Cargo.toml +++ b/crates/uplc/Cargo.toml @@ -16,6 +16,5 @@ exclude = ["test_data/*"] flat-rs = { path = "../flat", version = "0.0.2" } hex = "0.4.3" peg = "0.8.0" -strum = "0.24.0" -strum_macros = "0.24.0" +pretty = "0.11.3" thiserror = "1.0.31" diff --git a/crates/uplc/src/builtins.rs b/crates/uplc/src/builtins.rs index 16c6cc56..4f545c52 100644 --- a/crates/uplc/src/builtins.rs +++ b/crates/uplc/src/builtins.rs @@ -1,11 +1,11 @@ +use std::{fmt::Display, str::FromStr}; + use flat_rs::de; -use strum_macros::EnumString; /// All the possible builtin functions in Untyped Plutus Core. #[repr(u8)] #[allow(non_camel_case_types)] -#[derive(Debug, Clone, EnumString, PartialEq, Copy)] -#[strum(serialize_all = "camelCase")] +#[derive(Debug, Clone, PartialEq, Copy)] pub enum DefaultFunction { // Integer functions AddInteger = 0, @@ -28,7 +28,6 @@ pub enum DefaultFunction { LessThanByteString = 16, LessThanEqualsByteString = 17, // Cryptography and hash functions - #[strum(serialize = "sha2_256")] Sha2_256 = 18, Sha3_256 = 19, Blake2b_256 = 20, @@ -195,3 +194,132 @@ impl TryFrom for DefaultFunction { } } } + +impl FromStr for DefaultFunction { + type Err = String; + + fn from_str(s: &str) -> Result { + 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"), + } + } +} diff --git a/crates/uplc/src/lib.rs b/crates/uplc/src/lib.rs index 5dc3c5d4..fd3b4c31 100644 --- a/crates/uplc/src/lib.rs +++ b/crates/uplc/src/lib.rs @@ -3,6 +3,7 @@ pub mod builtins; mod debruijn; mod flat; pub mod parser; +mod pretty; #[cfg(test)] mod test; diff --git a/crates/uplc/src/pretty.rs b/crates/uplc/src/pretty.rs new file mode 100644 index 00000000..ebad5969 --- /dev/null +++ b/crates/uplc/src/pretty.rs @@ -0,0 +1,108 @@ +use pretty::RcDoc; + +use crate::ast::{Constant, Name, Program, Term}; + +impl Program { + 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 { + 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" })), + } + } +}