Skip to content

Commit cac856d

Browse files
committed
feat: add cli to output for graphviz
1 parent f84b09d commit cac856d

File tree

3 files changed

+100
-0
lines changed

3 files changed

+100
-0
lines changed

simpcli/src/main.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ fn usage(process_name: &str) {
2828
eprintln!("Usage:");
2929
eprintln!(" {} assemble <filename>", process_name);
3030
eprintln!(" {} disassemble <base64>", process_name);
31+
eprintln!(" {} graph <base64>", process_name);
3132
eprintln!(" {} relabel <base64>", process_name);
3233
eprintln!();
3334
eprintln!("For commands which take an optional expression, the default value is \"main\".");
@@ -43,6 +44,7 @@ fn invalid_usage(process_name: &str) -> Result<(), String> {
4344
enum Command {
4445
Assemble,
4546
Disassemble,
47+
Graph,
4648
Relabel,
4749
Help,
4850
}
@@ -53,6 +55,7 @@ impl FromStr for Command {
5355
match s {
5456
"assemble" => Ok(Command::Assemble),
5557
"disassemble" => Ok(Command::Disassemble),
58+
"graphviz" | "dot" | "graph" => Ok(Command::Graph),
5659
"relabel" => Ok(Command::Relabel),
5760
"help" => Ok(Command::Help),
5861
x => Err(format!("unknown command {}", x)),
@@ -65,6 +68,7 @@ impl Command {
6568
match *self {
6669
Command::Assemble => false,
6770
Command::Disassemble => false,
71+
Command::Graph => false,
6872
Command::Relabel => false,
6973
Command::Help => false,
7074
}
@@ -155,6 +159,14 @@ fn main() -> Result<(), String> {
155159
let prog = Forest::<DefaultJet>::from_program(commit);
156160
println!("{}", prog.string_serialize());
157161
}
162+
Command::Graph => {
163+
let v = simplicity::base64::Engine::decode(&STANDARD, first_arg.as_bytes())
164+
.map_err(|e| format!("failed to parse base64: {}", e))?;
165+
let iter = BitIter::from(v.into_iter());
166+
let commit = CommitNode::<DefaultJet>::decode(iter)
167+
.map_err(|e| format!("failed to decode program: {}", e))?;
168+
println!("{}", commit.display_as_dot());
169+
}
158170
Command::Relabel => {
159171
let prog = parse_file(&first_arg)?;
160172
println!("{}", prog.string_serialize());

src/node/display.rs

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,39 @@ where
197197
}
198198
}
199199

200+
pub struct DisplayAsDot<'a, M: Marker>(&'a Node<M>);
201+
202+
impl<'a, M: Marker> From<&'a Node<M>> for DisplayAsDot<'a, M> {
203+
fn from(node: &'a Node<M>) -> Self {
204+
Self(node)
205+
}
206+
}
207+
208+
impl<'a, M: Marker> fmt::Display for DisplayAsDot<'a, M>
209+
where
210+
&'a Node<M>: DagLike,
211+
{
212+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
213+
writeln!(f, "digraph G {{")?;
214+
writeln!(f, "ordering=\"out\";")?;
215+
216+
for data in self.0.post_order_iter::<NoSharing>() {
217+
let node_name = data.node.inner().to_string();
218+
writeln!(f, " node{}[label=\"{}\"];", data.index, node_name)?;
219+
if let Some(left) = data.left_index {
220+
writeln!(f, " node{}->node{};", data.index, left)?;
221+
}
222+
if let Some(right) = data.right_index {
223+
writeln!(f, " node{}->node{};", data.index, right)?;
224+
}
225+
}
226+
227+
writeln!(f, "}}")?;
228+
229+
Ok(())
230+
}
231+
}
232+
200233
#[cfg(test)]
201234
mod tests {
202235
use crate::human_encoding::Forest;
@@ -241,4 +274,54 @@ mod tests {
241274
program.display_expr().to_string()
242275
)
243276
}
277+
278+
#[test]
279+
fn display_as_dot() {
280+
let s = "
281+
oih := take drop iden
282+
input := pair (pair unit unit) unit
283+
output := unit
284+
main := comp input (comp (pair oih (take unit)) output)";
285+
let program = parse_program(s);
286+
let str = program
287+
.display_as_dot()
288+
.to_string()
289+
.replace(" ", "")
290+
.replace("\n", "");
291+
let expected = "
292+
digraph G {
293+
ordering=\"out\";
294+
node0[label=\"unit\"];
295+
node1[label=\"unit\"];
296+
node2[label=\"pair\"];
297+
node2->node0;
298+
node2->node1;
299+
node3[label=\"unit\"];
300+
node4[label=\"pair\"];
301+
node4->node2;
302+
node4->node3;
303+
node5[label=\"iden\"];
304+
node6[label=\"drop\"];
305+
node6->node5;
306+
node7[label=\"take\"];
307+
node7->node6;
308+
node8[label=\"unit\"];
309+
node9[label=\"take\"];
310+
node9->node8;
311+
node10[label=\"pair\"];
312+
node10->node7;
313+
node10->node9;
314+
node11[label=\"unit\"];
315+
node12[label=\"comp\"];
316+
node12->node10;
317+
node12->node11;
318+
node13[label=\"comp\"];
319+
node13->node4;
320+
node13->node12;
321+
}"
322+
.replace(" ", "")
323+
.replace("\n", "");
324+
325+
assert_eq!(str, expected);
326+
}
244327
}

src/node/mod.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@
6666
use crate::dag::{DagLike, MaxSharing, SharingTracker};
6767
use crate::encode;
6868
use crate::jet::Jet;
69+
use crate::node::display::DisplayAsDot;
6970
use crate::{types, BitWriter, Cmr, FailEntropy, HasCmr, Value};
7071

7172
use std::sync::Arc;
@@ -723,6 +724,10 @@ impl<N: Marker> Node<N> {
723724
DisplayExpr::from(self)
724725
}
725726

727+
pub fn display_as_dot(&self) -> DisplayAsDot<'_, N> {
728+
DisplayAsDot::from(self)
729+
}
730+
726731
/// Encode a Simplicity expression to bits without any witness data.
727732
pub fn encode_without_witness<W: io::Write>(&self, prog: W) -> io::Result<usize> {
728733
let mut w = BitWriter::new(prog);

0 commit comments

Comments
 (0)