decompile.rs 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. /* This file is part of DarkFi (https://dark.fi)
  2. *
  3. * Copyright (C) 2020-2026 Dyne.org foundation
  4. *
  5. * This program is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU Affero General Public License as
  7. * published by the Free Software Foundation, either version 3 of the
  8. * License, or (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU Affero General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU Affero General Public License
  16. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  17. */
  18. use super::{Op, SExprCode};
  19. /// Renders an expr op tree back into a human-readable string. Used by the
  20. /// netdebug backend so debug clients can see what an expr does. The output
  21. /// is informational: it recompiles to an equivalent expr when restricted to
  22. /// the compiler-expressible subset, but ops without source syntax render in
  23. /// prefix form and u32 constants are tagged to keep their u32-ness visible.
  24. pub fn decompile(code: &SExprCode) -> String {
  25. let stmts: Vec<String> = code.iter().map(render_op).collect();
  26. stmts.join("; ")
  27. }
  28. fn render_op(op: &Op) -> String {
  29. match op {
  30. Op::Null => "null".to_string(),
  31. Op::Add((lhs, rhs)) => format!("({} + {})", render_op(lhs), render_op(rhs)),
  32. Op::Sub((lhs, rhs)) => format!("({} - {})", render_op(lhs), render_op(rhs)),
  33. Op::Mul((lhs, rhs)) => format!("({} * {})", render_op(lhs), render_op(rhs)),
  34. Op::Div((lhs, rhs)) => format!("({} / {})", render_op(lhs), render_op(rhs)),
  35. Op::ConstBool(val) => format!("{val}"),
  36. Op::ConstUint32(val) => format!("u32({val})"),
  37. Op::ConstFloat32(val) => format!("{val}"),
  38. Op::ConstStr(val) => format!("{val:?}"),
  39. Op::LoadVar(var) => var.clone(),
  40. Op::StoreVar((var, val)) => format!("{var} = {}", render_op(val)),
  41. Op::Min((lhs, rhs)) => format!("min({}, {})", render_op(lhs), render_op(rhs)),
  42. Op::Max((lhs, rhs)) => format!("max({}, {})", render_op(lhs), render_op(rhs)),
  43. Op::IsEqual((lhs, rhs)) => format!("({} == {})", render_op(lhs), render_op(rhs)),
  44. Op::LessThan((lhs, rhs)) => format!("({} < {})", render_op(lhs), render_op(rhs)),
  45. Op::Float32ToUint32(val) => format!("as_u32({})", render_op(val)),
  46. Op::IfElse((cond, if_val, else_val)) => {
  47. format!(
  48. "if {} {{ {} }} else {{ {} }}",
  49. render_op(cond),
  50. decompile(if_val),
  51. decompile(else_val)
  52. )
  53. }
  54. Op::NativeFn(_) => "<native_fn>".to_string(),
  55. }
  56. }
  57. #[cfg(test)]
  58. mod tests {
  59. use super::{
  60. super::{Compiler, NativeFnCallback, SExprVal},
  61. *,
  62. };
  63. #[test]
  64. fn render_null() {
  65. assert_eq!(decompile(&vec![Op::Null]), "null");
  66. }
  67. #[test]
  68. fn render_arith() {
  69. let code = vec![Op::Add((
  70. Box::new(Op::ConstUint32(5)),
  71. Box::new(Op::Div((
  72. Box::new(Op::LoadVar("sw".to_string())),
  73. Box::new(Op::ConstUint32(2)),
  74. ))),
  75. ))];
  76. assert_eq!(decompile(&code), "(u32(5) + (sw / u32(2)))");
  77. }
  78. #[test]
  79. fn render_consts() {
  80. assert_eq!(decompile(&vec![Op::ConstBool(true)]), "true");
  81. assert_eq!(decompile(&vec![Op::ConstBool(false)]), "false");
  82. assert_eq!(decompile(&vec![Op::ConstUint32(110)]), "u32(110)");
  83. assert_eq!(decompile(&vec![Op::ConstFloat32(2.5)]), "2.5");
  84. assert_eq!(decompile(&vec![Op::ConstFloat32(200.)]), "200");
  85. assert_eq!(decompile(&vec![Op::ConstStr("hello".to_string())]), "\"hello\"");
  86. }
  87. #[test]
  88. fn render_vars() {
  89. assert_eq!(decompile(&vec![Op::LoadVar("w".to_string())]), "w");
  90. let code = vec![Op::StoreVar((
  91. "r".to_string(),
  92. Box::new(Op::Div((Box::new(Op::ConstFloat32(10.)), Box::new(Op::ConstFloat32(4.))))),
  93. ))];
  94. assert_eq!(decompile(&code), "r = (10 / 4)");
  95. }
  96. #[test]
  97. fn render_ops_without_source_syntax() {
  98. let lhs = Box::new(Op::LoadVar("a".to_string()));
  99. let rhs = Box::new(Op::LoadVar("b".to_string()));
  100. assert_eq!(decompile(&vec![Op::Min((lhs.clone(), rhs.clone()))]), "min(a, b)");
  101. assert_eq!(decompile(&vec![Op::Max((lhs.clone(), rhs.clone()))]), "max(a, b)");
  102. assert_eq!(decompile(&vec![Op::IsEqual((lhs.clone(), rhs.clone()))]), "(a == b)");
  103. assert_eq!(decompile(&vec![Op::LessThan((lhs, rhs))]), "(a < b)");
  104. assert_eq!(
  105. decompile(&vec![Op::Float32ToUint32(Box::new(Op::ConstFloat32(1.6)))]),
  106. "as_u32(1.6)"
  107. );
  108. }
  109. #[test]
  110. fn render_if_else() {
  111. let code = vec![Op::IfElse((
  112. Box::new(Op::LessThan((
  113. Box::new(Op::LoadVar("h".to_string())),
  114. Box::new(Op::ConstFloat32(4.)),
  115. ))),
  116. vec![Op::Sub((Box::new(Op::LoadVar("h".to_string())), Box::new(Op::ConstFloat32(1.))))],
  117. vec![Op::Add((
  118. Box::new(Op::Mul((
  119. Box::new(Op::ConstFloat32(2.)),
  120. Box::new(Op::LoadVar("h".to_string())),
  121. ))),
  122. Box::new(Op::ConstFloat32(5.)),
  123. ))],
  124. ))];
  125. assert_eq!(decompile(&code), "if (h < 4) { (h - 1) } else { ((2 * h) + 5) }");
  126. }
  127. #[test]
  128. fn render_native_fn() {
  129. let code = vec![Op::NativeFn(NativeFnCallback(|_| Ok(SExprVal::Null)))];
  130. assert_eq!(decompile(&code), "<native_fn>");
  131. }
  132. #[test]
  133. fn render_stmts() {
  134. let code = vec![
  135. Op::StoreVar(("r".to_string(), Box::new(Op::ConstFloat32(1.)))),
  136. Op::Add((Box::new(Op::LoadVar("r".to_string())), Box::new(Op::ConstFloat32(1.)))),
  137. ];
  138. assert_eq!(decompile(&code), "r = 1; (r + 1)");
  139. }
  140. #[test]
  141. fn decompile_compiled() {
  142. let cc = Compiler::new();
  143. let code = cc.compile("h/2 - 200").unwrap();
  144. assert_eq!(decompile(&code), "((h / 2) - 200)");
  145. }
  146. #[test]
  147. fn roundtrip() {
  148. let cc = Compiler::new();
  149. let fixtures = [
  150. "h/2 - 200",
  151. "(x + h/2 + (y + 7)/5) - 200",
  152. "h - 1",
  153. "r = 10 / 4",
  154. "r = 10 / 4;\n\ns = if h < 4 {\n h - 1\n} else {\n 2 * h + 5\n};\n\nr + 1",
  155. ];
  156. for src in fixtures {
  157. let once = cc.compile(src).unwrap();
  158. let src2 = decompile(&once);
  159. let twice = cc.compile(&src2).unwrap();
  160. assert_eq!(once, twice, "fixture: {src} rendered as: {src2}");
  161. }
  162. }
  163. }