feat(cli): rework uplc subcommands

closes #553

* rename flat to encode
* rename unflat to decode
* alias both to their old names
* both only print to stdout
  use can pipe to file
* split cbor and hex flags
* hex flag works for either cbor or flat
* encode takes --to flag
  [name, named-debruijn, debruijn]
* decode takes --from flag
  [name, named-debruijn, debruijn]
This commit is contained in:
rvcas 2023-06-01 23:50:59 -04:00
parent e5d9398625
commit 335cc0c8bc
No known key found for this signature in database
GPG Key ID: C09B64E263F7D68C
8 changed files with 181 additions and 154 deletions

View File

@ -0,0 +1,78 @@
use miette::IntoDiagnostic;
use std::{path::PathBuf, println};
use uplc::ast::{DeBruijn, Name, NamedDeBruijn, Program};
use super::Format;
#[derive(clap::Args)]
/// Decode flat bytes to textual Untyped Plutus Core
pub struct Args {
/// Flat encoded Untyped Plutus Core file
input: PathBuf,
// Format to convert from
#[clap(long, default_value = "debruijn")]
from: Format,
/// Input file contains cbor encoded flat bytes
#[clap(short, long)]
cbor: bool,
/// Input file contents will be hex decoded
#[clap(long)]
hex: bool,
}
pub fn exec(
Args {
input,
from,
cbor,
hex,
}: Args,
) -> miette::Result<()> {
let bytes = if hex {
let hex_bytes = std::fs::read_to_string(&input).into_diagnostic()?;
hex::decode(hex_bytes).into_diagnostic()?
} else {
std::fs::read(&input).into_diagnostic()?
};
let pretty_uplc = match from {
Format::Name => {
let program: Program<Name> = if cbor {
let mut flat_buffer = Vec::new();
Program::from_cbor(&bytes, &mut flat_buffer).into_diagnostic()?
} else {
Program::from_flat(&bytes).into_diagnostic()?
};
program.to_pretty()
}
Format::NamedDebruijn => {
let program: Program<NamedDeBruijn> = if cbor {
let mut flat_buffer = Vec::new();
Program::from_cbor(&bytes, &mut flat_buffer).into_diagnostic()?
} else {
Program::from_flat(&bytes).into_diagnostic()?
};
program.to_pretty()
}
Format::Debruijn => {
let program: Program<DeBruijn> = if cbor {
let mut flat_buffer = Vec::new();
Program::from_cbor(&bytes, &mut flat_buffer).into_diagnostic()?
} else {
Program::from_flat(&bytes).into_diagnostic()?
};
program.to_pretty()
}
};
println!("{pretty_uplc}");
Ok(())
}

View File

@ -0,0 +1,83 @@
use miette::IntoDiagnostic;
use std::{
io::{self, Write},
path::PathBuf,
};
use uplc::{
ast::{DeBruijn, NamedDeBruijn, Program},
flat::Binder,
parser,
};
use super::Format;
/// Encode textual Untyped Plutus Core to flat bytes
#[derive(clap::Args)]
pub struct Args {
/// Textual Untyped Plutus Core file
input: PathBuf,
// Format to convert to
#[clap(long, default_value = "debruijn")]
to: Format,
/// Further encode the flat bytes as cbor bytes
#[clap(short, long)]
cbor: bool,
/// Hex encode the bytes
#[clap(long)]
hex: bool,
}
pub fn exec(
Args {
input,
to,
cbor,
hex,
}: Args,
) -> miette::Result<()> {
let code = std::fs::read_to_string(input).into_diagnostic()?;
let program = parser::program(&code).into_diagnostic()?;
match to {
Format::Name => encode(program, cbor, hex),
Format::NamedDebruijn => {
let program: Program<NamedDeBruijn> = program.try_into().into_diagnostic()?;
encode(program, cbor, hex)
}
Format::Debruijn => {
let program: Program<DeBruijn> = program.try_into().into_diagnostic()?;
encode(program, cbor, hex)
}
}
}
fn encode<'a, T>(program: Program<T>, cbor: bool, hex: bool) -> miette::Result<()>
where
T: Binder<'a> + std::fmt::Debug,
{
let mut stdout = io::stdout();
let bytes = if cbor {
program.to_cbor().into_diagnostic()?
} else {
program.to_flat().into_diagnostic()?
};
if hex {
let bytes_hex = hex::encode(bytes);
print!("{bytes_hex}");
} else {
stdout.write_all(&bytes).into_diagnostic()?;
}
stdout.flush().into_diagnostic()?;
Ok(())
}

View File

@ -1,83 +0,0 @@
use miette::IntoDiagnostic;
use std::{fmt::Write, fs, path::PathBuf};
use uplc::{
ast::{DeBruijn, Program},
parser,
};
#[derive(clap::Args)]
/// Encode textual Untyped Plutus Core to flat bytes
pub struct Args {
/// Textual Untyped Plutus Core file
input: PathBuf,
/// Output file name
#[clap(short, long)]
out: Option<String>,
/// Print output instead of saving to file
#[clap(short, long)]
print: bool,
#[clap(short, long)]
cbor_hex: bool,
}
pub fn exec(
Args {
input,
out,
print,
cbor_hex,
}: Args,
) -> miette::Result<()> {
let code = std::fs::read_to_string(&input).into_diagnostic()?;
let program = parser::program(&code).into_diagnostic()?;
let program = Program::<DeBruijn>::try_from(program).into_diagnostic()?;
if !cbor_hex {
let bytes = program.to_flat().into_diagnostic()?;
if print {
let mut output = String::new();
for (i, byte) in bytes.iter().enumerate() {
let _ = write!(output, "{byte:08b}");
if (i + 1) % 4 == 0 {
output.push('\n');
} else {
output.push(' ');
}
}
println!("{output}");
} else {
let out_name = if let Some(out) = out {
out
} else {
format!("{}.flat", input.file_stem().unwrap().to_str().unwrap())
};
fs::write(out_name, &bytes).into_diagnostic()?;
}
} else {
let cbor = program.to_hex().into_diagnostic()?;
if print {
println!("{}", &cbor);
} else {
let out_name = if let Some(out) = out {
out
} else {
format!("{}.cbor", input.file_stem().unwrap().to_str().unwrap())
};
fs::write(out_name, &cbor).into_diagnostic()?;
}
}
Ok(())
}

View File

@ -1,24 +1,33 @@
mod decode;
mod encode;
mod eval; mod eval;
mod flat;
mod fmt; mod fmt;
mod unflat;
use clap::Subcommand; use clap::{Subcommand, ValueEnum};
#[derive(Copy, Clone, ValueEnum)]
pub(super) enum Format {
Name,
NamedDebruijn,
Debruijn,
}
/// Commands for working with untyped Plutus-core /// Commands for working with untyped Plutus-core
#[derive(Subcommand)] #[derive(Subcommand)]
pub enum Cmd { pub enum Cmd {
Fmt(fmt::Args), Fmt(fmt::Args),
Eval(eval::Args), Eval(eval::Args),
Flat(flat::Args), #[clap(alias = "flat")]
Unflat(unflat::Args), Encode(encode::Args),
#[clap(alias = "unflat")]
Decode(decode::Args),
} }
pub fn exec(cmd: Cmd) -> miette::Result<()> { pub fn exec(cmd: Cmd) -> miette::Result<()> {
match cmd { match cmd {
Cmd::Fmt(args) => fmt::exec(args), Cmd::Fmt(args) => fmt::exec(args),
Cmd::Eval(args) => eval::exec(args), Cmd::Eval(args) => eval::exec(args),
Cmd::Flat(args) => flat::exec(args), Cmd::Encode(args) => encode::exec(args),
Cmd::Unflat(args) => unflat::exec(args), Cmd::Decode(args) => decode::exec(args),
} }
} }

View File

@ -1,61 +0,0 @@
use miette::IntoDiagnostic;
use std::{fs, path::PathBuf};
use uplc::ast::{DeBruijn, Name, Program};
#[derive(clap::Args)]
/// Decode flat bytes to textual Untyped Plutus Core
pub struct Args {
/// Flat encoded Untyped Plutus Core file
input: PathBuf,
/// Output file name
#[clap(short, long)]
out: Option<String>,
/// Print output instead of saving to file
#[clap(short, long)]
print: bool,
#[clap(short, long)]
cbor_hex: bool,
}
pub fn exec(
Args {
input,
out,
print,
cbor_hex,
}: Args,
) -> miette::Result<()> {
let program = if cbor_hex {
let cbor = std::fs::read_to_string(&input).into_diagnostic()?;
let mut cbor_buffer = Vec::new();
let mut flat_buffer = Vec::new();
Program::<DeBruijn>::from_hex(cbor.trim(), &mut cbor_buffer, &mut flat_buffer)
.into_diagnostic()?
} else {
let bytes = std::fs::read(&input).into_diagnostic()?;
Program::<DeBruijn>::from_flat(&bytes).into_diagnostic()?
};
let program: Program<Name> = program.try_into().into_diagnostic()?;
let pretty = program.to_pretty();
if print {
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).into_diagnostic()?;
}
Ok(())
}

View File

@ -2,7 +2,7 @@ pub mod ast;
pub mod builder; pub mod builder;
pub mod builtins; pub mod builtins;
mod debruijn; mod debruijn;
mod flat; pub mod flat;
pub mod machine; pub mod machine;
pub mod optimize; pub mod optimize;
pub mod parser; pub mod parser;

View File

@ -1,3 +1,4 @@
artifacts/
build/ build/
*.sk *.sk
*.addr *.addr

View File

@ -20,8 +20,8 @@
"$ref": "#/definitions/hello_world~1Redeemer" "$ref": "#/definitions/hello_world~1Redeemer"
} }
}, },
"compiledCode": "58dd0100003232323232323232222533300632323232533300a002100114a06464660026eb0cc010c014cc010c014019200048040dd7198021802804240006002002444a66601e00429404c8c94ccc038cdc78010018a5113330050050010033012003375c602000466e3cdd71980098010022400091010d48656c6c6f2c20576f726c64210022323330010014800000c888cccc030cdc3802001008119980200219b8000348008c0480040048c024dd50008a4c2c6002002444a66600e004293099802980098040011998018019804801000ab9a5736aae7955cfaba15745", "compiledCode": "5901ec01000032323232323232323232322223232533300a3232533300c002100114a066646002002444a66602400429404c8c94ccc040cdc78010018a5113330050050010033015003375c60260046eb0cc01cc024cc01cc024011200048040dd71980398048012400066e3cdd7198031804001240009110d48656c6c6f2c20576f726c642100149858c8014c94ccc028cdc3a400000226464a66602060240042930a99806a49334c6973742f5475706c652f436f6e73747220636f6e7461696e73206d6f7265206974656d73207468616e2065787065637465640016375c6020002601000a2a660169212b436f6e73747220696e64657820646964206e6f74206d6174636820616e7920747970652076617269616e7400163008004320033253330093370e900000089919299980798088010a4c2a66018921334c6973742f5475706c652f436f6e73747220636f6e7461696e73206d6f7265206974656d73207468616e2065787065637465640016375c601e002600e0062a660149212b436f6e73747220696e64657820646964206e6f74206d6174636820616e7920747970652076617269616e740016300700233001001480008888cccc01ccdc38008018061199980280299b8000448008c0380040080088c018dd5000918021baa0015734ae7155ceaab9e5573eae855d11",
"hash": "46872294cadbacb2c3214086c0129ede75cf9f767e95a449f996685f" "hash": "f3f821d122b041244de074b9554c7dbcc62f34f62426344c0d0b4c86"
} }
], ],
"definitions": { "definitions": {