simplicity/node/
display.rs1use std::fmt;
2use std::sync::OnceLock;
3
4use crate::dag::{Dag, DagLike, InternalSharing, MaxSharing, NoSharing};
5use crate::encode;
6use crate::node::{Inner, Marker, Node};
7use crate::BitWriter;
8
9pub struct Display<'n, M: Marker> {
12 node: &'n Node<M>,
13 #[cfg_attr(not(feature = "base64"), allow(dead_code))]
14 prog_bytes: OnceLock<Vec<u8>>,
15 wit_bytes: OnceLock<Vec<u8>>,
16}
17
18impl<'n, M: Marker> From<&'n Node<M>> for Display<'n, M> {
19 fn from(node: &'n Node<M>) -> Self {
20 Self {
27 node,
28 prog_bytes: OnceLock::new(),
29 wit_bytes: OnceLock::new(),
30 }
31 }
32}
33
34impl<'n, M: Marker> Display<'n, M> {
35 #[cfg(feature = "base64")]
37 pub fn program(&self) -> impl fmt::Display + '_ {
38 use crate::base64::display::Base64Display;
39 use crate::base64::engine::general_purpose;
40
41 let prog_bytes = self
42 .prog_bytes
43 .get_or_init(|| self.node.to_vec_without_witness());
44 Base64Display::new(prog_bytes, &general_purpose::STANDARD)
45 }
46}
47
48impl<'n, M: Marker<Witness = crate::Value>> Display<'n, M> {
49 pub fn witness(&self) -> impl fmt::Display + '_ {
51 use crate::hex::DisplayHex;
52
53 let wit_bytes = self.wit_bytes.get_or_init(|| {
54 let mut wit_v = vec![];
55 let mut witness = BitWriter::new(&mut wit_v);
56 let sharing_iter = self.node.post_order_iter::<MaxSharing<M>>();
57
58 encode::encode_witness(sharing_iter.into_witnesses(), &mut witness)
59 .expect("Vec::write is infallible");
60 witness.flush_all().expect("Vec::write is infallible");
61
62 wit_v
63 });
64
65 wit_bytes.as_hex()
66 }
67}
68
69pub struct DisplayExpr<'a, M: Marker>(&'a Node<M>);
100
101impl<'a, M: Marker> From<&'a Node<M>> for DisplayExpr<'a, M> {
102 fn from(node: &'a Node<M>) -> Self {
103 Self(node)
104 }
105}
106
107impl<'a, M: Marker> fmt::Display for DisplayExpr<'a, M>
108where
109 &'a Node<M>: DagLike,
110{
111 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
112 for data in self.0.verbose_pre_order_iter::<NoSharing>(None) {
113 match data.n_children_yielded {
114 1 => match data.node.inner() {
115 Inner::Comp(..) => f.write_str("; ")?,
116 Inner::Pair(..) => f.write_str(" & ")?,
117 Inner::Case(..) => f.write_str(") (")?,
118 x => debug_assert!(matches!(x.as_dag(), Dag::Unary(..))),
119 },
120 2 => {
121 debug_assert!(matches!(data.node.inner().as_dag(), Dag::Binary(..)));
122 if data.parent.is_some() {
123 f.write_str(")")?;
124 } else {
125 }
127 }
128 n => {
129 debug_assert!(n == 0, "Combinators are nullary, unary or binary");
130 match data.node.inner() {
131 Inner::Iden
132 if matches!(
133 data.parent.map(Node::inner),
134 Some(Inner::Take(..) | Inner::Drop(..))
135 ) =>
136 {
137 f.write_str("H")?
138 }
139 Inner::Take(child) if is_take_drop_iden(child) => f.write_str("O")?,
140 Inner::Drop(child) if is_take_drop_iden(child) => f.write_str("I")?,
141 Inner::Iden => f.write_str("iden")?,
142 Inner::Take(..) => f.write_str("take ")?,
143 Inner::Drop(..) => f.write_str("drop ")?,
144 Inner::Unit
145 if matches!(
146 data.parent.map(Node::inner),
147 Some(Inner::InjL(..) | Inner::InjR(..))
148 ) => {} Inner::InjL(child) if matches!(child.inner(), Inner::Unit) => {
150 f.write_str("false")?
151 }
152 Inner::InjR(child) if matches!(child.inner(), Inner::Unit) => {
153 f.write_str("true")?
154 }
155 Inner::Unit => f.write_str("unit")?,
156 Inner::InjL(..) => f.write_str("injl ")?,
157 Inner::InjR(..) => f.write_str("injr ")?,
158 Inner::Comp(..) | Inner::Pair(..) => {} Inner::Case(..) => f.write_str("case ")?,
160 Inner::AssertL(..) => f.write_str("assertl ")?,
161 Inner::AssertR(..) => f.write_str("assertr ")?,
162 Inner::Disconnect(..) => f.write_str("disconnect ")?,
163 Inner::Witness(..) => f.write_str("witness ")?,
164 Inner::Fail(..) => f.write_str("fail")?,
165 Inner::Jet(jet) => write!(f, "jet_{jet} ")?,
166 Inner::Word(value) => write!(f, "const {value} ")?,
167 }
168
169 match data.node.inner().as_dag() {
170 Dag::Binary(..) if data.parent.is_some() => f.write_str("(")?,
171 _ => {} }
173 }
174 };
175 }
176
177 Ok(())
178 }
179}
180
181fn is_take_drop_iden<M: Marker>(node: &Node<M>) -> bool {
182 for node in node.pre_order_iter::<InternalSharing>() {
183 match node.inner() {
184 Inner::Take(..) | Inner::Drop(..) | Inner::Iden => {}
185 _ => return false,
186 }
187 }
188 true
189}
190
191impl<'a, M: Marker> fmt::Debug for DisplayExpr<'a, M>
192where
193 &'a Node<M>: DagLike,
194{
195 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
196 fmt::Display::fmt(self, f)
197 }
198}
199
200#[cfg(test)]
201mod tests {
202 use crate::human_encoding::Forest;
203 use crate::jet::Core;
204 use crate::types;
205 use crate::RedeemNode;
206 use std::collections::HashMap;
207 use std::sync::Arc;
208
209 fn parse_program(s: &str) -> Arc<RedeemNode<Core>> {
210 types::Context::with_context(|ctx| {
211 let empty_witness = HashMap::new();
212 Forest::<Core>::parse(s)
213 .unwrap()
214 .to_witness_node(&ctx, &empty_witness)
215 .unwrap()
216 .finalize_unpruned()
217 .unwrap()
218 })
219 }
220
221 #[test]
222 fn display_boolean() {
223 let s = "
224 false := injl unit
225 true := injr unit
226 main := comp pair false true unit";
227 let program = parse_program(s);
228 assert_eq!("(false & true); unit", program.display_expr().to_string())
229 }
230
231 #[test]
232 fn display_oih() {
233 let s = "
234 oih := take drop iden
235 input := pair (pair unit unit) unit
236 output := unit
237 main := comp input (comp (pair oih (take unit)) output)";
238 let program = parse_program(s);
239 assert_eq!(
240 "((unit & unit) & unit); ((OIH & take unit); unit)",
241 program.display_expr().to_string()
242 )
243 }
244}