From 9a8e0376c26cf864bf1397e1209379b12da82f40 Mon Sep 17 00:00:00 2001 From: art yerkes Date: Thu, 23 Jul 2026 12:38:50 -0700 Subject: [PATCH 01/38] trying out making the classic compiler functions generic --- Cargo.lock | 70 ++ Cargo.toml | 1 + src/classic/clvm/sexp.rs | 174 ++-- src/classic/clvm_tools/cmds.rs | 2 +- src/classic/clvm_tools/debug.rs | 80 +- src/classic/clvm_tools/pattern_match.rs | 61 +- .../clvm_tools/stages/stage_2/abstraction.rs | 115 +++ .../clvm_tools/stages/stage_2/compile.rs | 832 ++++++++++-------- .../clvm_tools/stages/stage_2/defaults.rs | 29 +- .../clvm_tools/stages/stage_2/helpers.rs | 64 +- .../clvm_tools/stages/stage_2/inline.rs | 174 ++-- src/classic/clvm_tools/stages/stage_2/mod.rs | 1 + .../clvm_tools/stages/stage_2/module.rs | 702 +++++++++------ .../clvm_tools/stages/stage_2/operators.rs | 46 +- .../clvm_tools/stages/stage_2/optimize.rs | 597 +++++++------ .../clvm_tools/stages/stage_2/reader.rs | 139 ++- src/compiler/dialect.rs | 2 +- src/compiler/optimize/mod.rs | 5 +- 18 files changed, 1872 insertions(+), 1222 deletions(-) create mode 100644 src/classic/clvm_tools/stages/stage_2/abstraction.rs diff --git a/Cargo.lock b/Cargo.lock index e7a166390..977951a4c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -214,6 +214,7 @@ dependencies = [ "getrandom 0.2.15", "hashlink", "hex", + "html_parser", "indoc", "js-sys", "lazy_static", @@ -361,6 +362,12 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3e16a80c1dda2cf52fa07106427d3d798b6331dca8155fcb8c39f7fc78f6dd2" +[[package]] +name = "doc-comment" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "780955b8b195a21ab8e4ac6b60dd1dbdcec1dc6c51c0617964b08c81785e12c9" + [[package]] name = "ecdsa" version = "0.16.8" @@ -573,6 +580,21 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "html_parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f56db07b6612644f6f7719f8ef944f75fff9d6378fdf3d316fd32194184abd" +dependencies = [ + "doc-comment", + "pest", + "pest_derive", + "serde", + "serde_derive", + "serde_json", + "thiserror", +] + [[package]] name = "hybrid-array" version = "0.4.12" @@ -845,6 +867,48 @@ dependencies = [ "base64ct", ] +[[package]] +name = "pest" +version = "2.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7df728be843c7070fab6ab7c328c4e9e9d78e23bf749c0669c86ee7ebfa050a2" +dependencies = [ + "memchr", + "ucd-trie", +] + +[[package]] +name = "pest_derive" +version = "2.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e2dd6fc3b26b3462ee188aac870f5a41d398f1cd5e2408d16531bd71c9591fd" +dependencies = [ + "pest", + "pest_generator", +] + +[[package]] +name = "pest_generator" +version = "2.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a7a9205cfb6f596a9e8b689c0a15f9ceb7a1aafae7aaf788150ac65b29975b6" +dependencies = [ + "pest", + "pest_meta", + "proc-macro2 1.0.95", + "quote 1.0.40", + "syn 2.0.104", +] + +[[package]] +name = "pest_meta" +version = "2.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85abd351c0de1e8384fc791a0737111a350394937e92b956b743dac12429f57c" +dependencies = [ + "pest", +] + [[package]] name = "pkcs8" version = "0.10.2" @@ -1348,6 +1412,12 @@ version = "1.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40ce102ab67701b8526c123c1bab5cbe42d7040ccfd0f64af1a385808d2f43de" +[[package]] +name = "ucd-trie" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" + [[package]] name = "unicode-ident" version = "1.0.11" diff --git a/Cargo.toml b/Cargo.toml index ab0394b91..ef26bb1c9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,6 +39,7 @@ tempfile = "3.25.0" unicode-segmentation = "1.13.3" yaml-rust2 = "0.11.0" subprocess = { version = "1.1.0", optional = true } +html_parser = "0.7.0" [dependencies.pyo3] version = "0.29.0" diff --git a/src/classic/clvm/sexp.rs b/src/classic/clvm/sexp.rs index a20a33bc9..3997c99e5 100644 --- a/src/classic/clvm/sexp.rs +++ b/src/classic/clvm/sexp.rs @@ -6,9 +6,11 @@ use chia_bls::PublicKey; use clvm_rs::allocator::{Allocator, NodePtr, SExp}; use clvm_rs::error::EvalErr; +use crate::classic::clvm_tools::stages::stage_2::abstraction::{ASExp, ClassicAllocator, ClError}; use crate::classic::clvm::__type_compatibility__::{Bytes, BytesFromType, Stream}; use crate::classic::clvm::serialize::sexp_to_stream; use crate::util::{u8_from_number, Number}; +use crate::compiler::srcloc::Srcloc; #[derive(Debug)] pub enum CastableType { @@ -344,21 +346,38 @@ pub fn non_nil(allocator: &Allocator, sexp: NodePtr) -> bool { } } -pub fn first(allocator: &Allocator, sexp: NodePtr) -> Result { - match allocator.sexp(sexp) { - SExp::Pair(f, _) => Ok(f), - _ => Err(EvalErr::InternalError( - sexp, - "first of non-cons".to_string(), - )), +pub fn first(allocator: &A, sexp: &A::NodePtr) -> Result { + let exported = allocator.export(sexp); + if let ASExp::Pair(f, _) = allocator.sexp(sexp) { + return Ok(f); } + + Err( + ClError( + allocator.loc(sexp), + EvalErr::InternalError( + exported, + "first of non-cons".to_string(), + ) + ) + ) } -pub fn rest(allocator: &Allocator, sexp: NodePtr) -> Result { - match allocator.sexp(sexp) { - SExp::Pair(_, r) => Ok(r), - _ => Err(EvalErr::InternalError(sexp, "rest of non-cons".to_string())), +pub fn rest(allocator: &A, sexp: &A::NodePtr) -> Result { + let exported = allocator.export(sexp); + if let ASExp::Pair(_, r) = allocator.sexp(sexp) { + return Ok(r); } + + Err( + ClError( + allocator.loc(sexp), + EvalErr::InternalError( + exported, + "rest of non-cons".to_string(), + ) + ) + ) } pub fn atom(allocator: &Allocator, sexp: NodePtr) -> Result, EvalErr> { @@ -372,19 +391,26 @@ pub fn atom(allocator: &Allocator, sexp: NodePtr) -> Result, EvalErr> { } } -pub fn proper_list(allocator: &Allocator, sexp: NodePtr, store: bool) -> Option> { +pub fn proper_list( + allocator: &A, + sexp: &A::NodePtr, + store: bool +) -> Option> +where + A::NodePtr: Clone +{ let mut args = vec![]; - let mut args_sexp = sexp; + let mut args_sexp = sexp.clone(); loop { - match allocator.sexp(args_sexp) { - SExp::Atom => { - if !non_nil(allocator, args_sexp) { + match allocator.sexp(&args_sexp) { + ASExp::Atom => { + if allocator.is_nil(&args_sexp) { return Some(args); } else { return None; } } - SExp::Pair(f, r) => { + ASExp::Pair(f, r) => { if store { args.push(f); } @@ -394,21 +420,27 @@ pub fn proper_list(allocator: &Allocator, sexp: NodePtr, store: bool) -> Option< } } -pub fn enlist(allocator: &mut Allocator, vec: &[NodePtr]) -> Result { - let mut built = NodePtr::NIL; +pub fn enlist( + allocator: &mut A, vec: &[A::NodePtr] +) -> Result +where + A::NodePtr: Clone +{ + let mut built = allocator.import(Srcloc::start("*nil*"), NodePtr::NIL)?; for i_reverse in 0..vec.len() { let i = vec.len() - i_reverse - 1; - built = allocator.new_pair(vec[i], built)?; + let loc = allocator.loc(&vec[i]); + built = allocator.new_pair(loc, &vec[i], &built)?; } Ok(built) } -pub fn map_m( - allocator: &mut Allocator, +pub fn map_m( + allocator: &mut A, iter: &mut impl Iterator, - f: &dyn Fn(&mut Allocator, T) -> Result, -) -> Result, EvalErr> { + f: &dyn Fn(&mut A, T) -> Result, +) -> Result, ClError> { let mut result = Vec::new(); loop { match iter.next() { @@ -427,9 +459,9 @@ pub fn map_m( } } -pub fn fold_m( - allocator: &mut Allocator, - f: &dyn Fn(&mut Allocator, A, B) -> Result, +pub fn fold_m( + allocator: &mut CA, + f: &dyn Fn(&mut CA, A, B) -> Result, start_: A, iter: &mut impl Iterator, ) -> Result { @@ -451,21 +483,24 @@ pub fn fold_m( } } -pub fn equal_to(allocator: &mut Allocator, first_: NodePtr, second_: NodePtr) -> bool { - let mut first = first_; - let mut second = second_; +pub fn equal_to(allocator: &mut A, first_: &A::NodePtr, second_: &A::NodePtr) -> bool +where + A::NodePtr: Clone +{ + let mut first = first_.clone(); + let mut second = second_.clone(); loop { - if first == second { + if allocator.node_equal(&first, &second) { return true; } - match (allocator.sexp(first), allocator.sexp(second)) { - (SExp::Atom, SExp::Atom) => { + match (allocator.sexp(&first), allocator.sexp(&second)) { + (ASExp::Atom, ASExp::Atom) => { // two atoms in scope, both are used - return allocator.atom(first) == allocator.atom(second); + return allocator.atom(&first) == allocator.atom(&second); } - (SExp::Pair(ff, fr), SExp::Pair(rf, rr)) => { - if !equal_to(allocator, ff, rf) { + (ASExp::Pair(ff, fr), ASExp::Pair(rf, rr)) => { + if !equal_to(allocator, &ff, &rf) { return false; } first = fr; @@ -478,19 +513,26 @@ pub fn equal_to(allocator: &mut Allocator, first_: NodePtr, second_: NodePtr) -> } } -pub fn flatten(allocator: &mut Allocator, tree_: NodePtr, res: &mut Vec) { - let mut tree = tree_; +pub fn flatten( + allocator: &mut A, + tree_: &A::NodePtr, + res: &mut Vec +) +where + A::NodePtr: Clone +{ + let mut tree = tree_.clone(); loop { - match allocator.sexp(tree) { - SExp::Atom => { - if non_nil(allocator, tree) { - res.push(tree); + match allocator.sexp(&tree) { + ASExp::Atom => { + if !allocator.is_nil(&tree) { + res.push(tree.clone()); } return; } - SExp::Pair(l, r) => { - flatten(allocator, l, res); + ASExp::Pair(l, r) => { + flatten(allocator, &l, res); tree = r; } } @@ -501,10 +543,10 @@ pub fn flatten(allocator: &mut Allocator, tree_: NodePtr, res: &mut Vec // the classic chialisp code. pub fn nonempty_last(nil: NodePtr, lst: &[X]) -> Result where - X: Copy, + X: Clone, { lst.last() - .copied() + .cloned() .ok_or_else(|| EvalErr::InternalError(nil, "alist is empty and shouldn't be".to_string())) } @@ -533,57 +575,57 @@ pub enum ThisNode { Here, } -pub trait SelectNode { - fn select_nodes(&self, allocator: &mut Allocator, n: NodePtr) -> Result; +pub trait SelectNode { + fn select_nodes(&self, allocator: &mut A, n: A::NodePtr) -> Result; } -impl SelectNode for ThisNode { - fn select_nodes(&self, _allocator: &mut Allocator, n: NodePtr) -> Result { +impl SelectNode for ThisNode { + fn select_nodes(&self, _allocator: &mut A, n: A::NodePtr) -> Result { Ok(n) } } -impl SelectNode<()> for () { - fn select_nodes(&self, _allocator: &mut Allocator, _n: NodePtr) -> Result<(), EvalErr> { +impl SelectNode<(), A> for () { + fn select_nodes(&self, _allocator: &mut A, _n: A::NodePtr) -> Result<(), ClError> { Ok(()) } } -impl SelectNode> for First +impl SelectNode, A> for First where - R: SelectNode + Clone, + R: SelectNode + Clone, { - fn select_nodes(&self, allocator: &mut Allocator, n: NodePtr) -> Result, EvalErr> { + fn select_nodes(&self, allocator: &mut A, n: A::NodePtr) -> Result, ClError> { let First::Here(f) = &self; let NodeSel::Cons(first, ()) = NodeSel::Cons(f.clone(), ()).select_nodes(allocator, n)?; Ok(First::Here(first)) } } -impl SelectNode> for Rest +impl SelectNode, A> for Rest where - R: SelectNode + Clone, + R: SelectNode + Clone, { - fn select_nodes(&self, allocator: &mut Allocator, n: NodePtr) -> Result, EvalErr> { + fn select_nodes(&self, allocator: &mut A, n: A::NodePtr) -> Result, ClError> { let Rest::Here(f) = &self; let NodeSel::Cons((), rest) = NodeSel::Cons((), f.clone()).select_nodes(allocator, n)?; Ok(Rest::Here(rest)) } } -impl SelectNode> for NodeSel +impl SelectNode, A> for NodeSel where - R: SelectNode, - S: SelectNode, + R: SelectNode, + S: SelectNode, { fn select_nodes( &self, - allocator: &mut Allocator, - n: NodePtr, - ) -> Result, EvalErr> { + allocator: &mut A, + n: A::NodePtr, + ) -> Result, ClError> { let NodeSel::Cons(my_left, my_right) = &self; - let l = first(allocator, n)?; - let r = rest(allocator, n)?; + let l = first(allocator, &n)?; + let r = rest(allocator, &n)?; let first = my_left.select_nodes(allocator, l)?; let rest = my_right.select_nodes(allocator, r)?; Ok(NodeSel::Cons(first, rest)) diff --git a/src/classic/clvm_tools/cmds.rs b/src/classic/clvm_tools/cmds.rs index c1a92abc1..8fddb6214 100644 --- a/src/classic/clvm_tools/cmds.rs +++ b/src/classic/clvm_tools/cmds.rs @@ -835,7 +835,7 @@ fn fix_log( for (i, entry) in log_result.to_vec().iter().enumerate() { update_map.get(entry).and_then(|v| *v).map(|v| { - proper_list(allocator, *entry, true).map(|list| { + proper_list(allocator, entry, true).map(|list| { let mut updated = list.to_vec(); updated.push(v); log_result[i] = enlist(allocator, &updated).unwrap(); diff --git a/src/classic/clvm_tools/debug.rs b/src/classic/clvm_tools/debug.rs index 9671a05fa..eb13f69af 100644 --- a/src/classic/clvm_tools/debug.rs +++ b/src/classic/clvm_tools/debug.rs @@ -8,9 +8,9 @@ use crate::classic::clvm::__type_compatibility__::{Bytes, BytesFromType, Stream} use crate::classic::clvm::serialize::sexp_to_stream; use crate::classic::clvm::sexp::{enlist, proper_list, rest, First, SelectNode, ThisNode}; -use crate::classic::clvm_tools::binutils::disassemble; use crate::classic::clvm_tools::sha256tree::sha256tree; use crate::classic::clvm_tools::stages::stage_0::TRunProgram; +use crate::classic::clvm_tools::stages::stage_2::abstraction::{ClassicAllocator, ClError}; use crate::compiler::comptypes::{CompileErr, CompilerOpts}; use crate::compiler::frontend::frontend; @@ -49,15 +49,29 @@ use crate::compiler::usecheck::check_parameters_used_compileform; /// Contains additional info beside the compiled form for chialisp functions/ /// These can be passed on and used by debuggers and such. -#[derive(Clone)] -pub struct FunctionExtraInfo { +pub struct FunctionExtraInfo +where + A::NodePtr: Clone +{ /// The form of the original arguments from the source code. - pub args: NodePtr, + pub args: A::NodePtr, /// Whether this function requires the constants and functions of the program /// as an additional hidden parameter. pub has_constants_tree: bool, } +impl Clone for FunctionExtraInfo +where + A::NodePtr: Clone +{ + fn clone(&self) -> Self { + FunctionExtraInfo { + args: self.args.clone(), + has_constants_tree: self.has_constants_tree + } + } +} + // // The function below is broken as of 2021/06/22. // /* // export function dump_invocation( @@ -92,23 +106,33 @@ pub struct FunctionExtraInfo { // // @todo Implement here if original python code is fixed. // } // */ -pub fn build_symbol_dump( - allocator: &mut Allocator, - constants_lookup: &HashMap, NodePtr>, - extra_function_data: &HashMap, FunctionExtraInfo>, +pub fn build_symbol_dump( + allocator: &mut A, + constants_lookup: &HashMap, A::NodePtr>, + extra_function_data: &HashMap, FunctionExtraInfo>, run_program: Rc, extra_info: bool, -) -> Result { - let mut map_result: Vec = Vec::new(); +) -> Result +where + A::NodePtr: Clone +{ + let mut map_result: Vec = Vec::new(); for (k, v) in constants_lookup.iter() { - let run_result = run_program.run_program(allocator, *v, NodePtr::NIL, None)?; + let v_export = allocator.export(v); + let vloc = allocator.loc(v); + let run_result = run_program.run_program( + allocator.allocator(), + v_export, + NodePtr::NIL, + None + ).map_err(|e| allocator.map_err(vloc.clone(), e))?; - let sha256 = sha256tree(allocator, run_result.1).hex(); - let sha_atom = allocator.new_atom(sha256.as_bytes())?; - let name_atom = allocator.new_atom(&k.clone())?; + let sha256 = sha256tree(allocator.allocator(), run_result.1).hex(); + let sha_atom = allocator.new_atom(vloc.clone(), sha256.as_bytes())?; + let name_atom = allocator.new_atom(vloc.clone(), &k.clone())?; - map_result.push(allocator.new_pair(sha_atom, name_atom)?); + map_result.push(allocator.new_pair(vloc.clone(), &sha_atom, &name_atom)?); if !extra_info { continue; @@ -123,16 +147,16 @@ pub fn build_symbol_dump( left_env_atom.append(&mut sha256.as_bytes().to_vec()); left_env_atom.append(&mut "_left_env".as_bytes().to_vec()); - let args_name_atom = allocator.new_atom(&args_atom)?; - let left_env_name_atom = allocator.new_atom(&left_env_atom)?; + let args_name_atom = allocator.new_atom(vloc.clone(), &args_atom)?; + let left_env_name_atom = allocator.new_atom(vloc.clone(), &left_env_atom)?; - let serialized_args = disassemble(allocator, extra.args, Some(0)); - let serialized_args_atom = allocator.new_atom(serialized_args.as_bytes())?; + let serialized_args = allocator.disassemble(&extra.args, Some(0)); + let serialized_args_atom = allocator.new_atom(vloc.clone(), serialized_args.as_bytes())?; - let left_env_value = allocator.new_atom(&[extra.has_constants_tree as u8])?; + let left_env_value = allocator.new_atom(vloc.clone(), &[extra.has_constants_tree as u8])?; - map_result.push(allocator.new_pair(args_name_atom, serialized_args_atom)?); - map_result.push(allocator.new_pair(left_env_name_atom, left_env_value)?); + map_result.push(allocator.new_pair(vloc.clone(), &args_name_atom, &serialized_args_atom)?); + map_result.push(allocator.new_pair(vloc.clone(), &left_env_name_atom, &left_env_value)?); } } @@ -152,7 +176,7 @@ fn text_trace( let mut env = env_; match symbol { Some(sym) => { - env = rest(allocator, env).unwrap_or(NodePtr::NIL); + env = rest(allocator, &env).unwrap_or(NodePtr::NIL); let symbol_atom = allocator.new_atom(sym.as_bytes()).unwrap(); let symbol_list = allocator.new_pair(symbol_atom, env).unwrap(); symbol_val = disassemble_f(allocator, symbol_list); @@ -219,7 +243,7 @@ fn display_trace( display_fun: &DisplayTraceFun, ) { for item in trace { - let item_vec = proper_list(allocator, *item, true).unwrap(); + let item_vec = proper_list(allocator, item, true).unwrap(); let form = item_vec[0]; let env = item_vec[1]; let not_exn = item_vec.len() > 2; @@ -294,11 +318,9 @@ pub fn trace_pre_eval( if recognized.is_none() && symbol_table.is_some() { Ok(None) } else { - m! { - log_entry <- enlist(allocator, &[sexp, args]); - let _ = append_log(allocator, log_entry); - Ok(Some(log_entry)) - } + let log_entry = enlist(allocator, &[sexp, args])?; + let _ = append_log(allocator, log_entry); + Ok(Some(log_entry)) } } diff --git a/src/classic/clvm_tools/pattern_match.rs b/src/classic/clvm_tools/pattern_match.rs index a096ce8ef..757bd35b5 100644 --- a/src/classic/clvm_tools/pattern_match.rs +++ b/src/classic/clvm_tools/pattern_match.rs @@ -1,17 +1,21 @@ use crate::classic::clvm::__type_compatibility__::{Bytes, BytesFromType}; use crate::classic::clvm::sexp::equal_to; -use clvm_rs::allocator::{Allocator, NodePtr, SExp}; +use crate::classic::clvm_tools::stages::stage_2::abstraction::{ASExp, BufCarrier, ClassicAllocator}; + use std::collections::HashMap; pub const ATOM_MATCH: [u8; 1] = *b"$"; pub const SEXP_MATCH: [u8; 1] = *b":"; -pub fn unify_bindings( - allocator: &mut Allocator, - bindings: HashMap, +pub fn unify_bindings( + allocator: &mut A, + bindings: HashMap, new_key: &[u8], - new_value: NodePtr, -) -> Option> { + new_value: &A::NodePtr, +) -> Option> +where + A::NodePtr: Clone +{ /* * Try to add a new binding to the list, rejecting it if it conflicts * with an existing binding. @@ -19,25 +23,28 @@ pub fn unify_bindings( let new_key_str = Bytes::new(Some(BytesFromType::Raw(new_key.to_vec()))).decode(); match bindings.get(&new_key_str) { Some(binding) => { - if !equal_to(allocator, *binding, new_value) { + if !equal_to(allocator, binding, &new_value) { return None; } Some(bindings) } _ => { let mut new_bindings = bindings.clone(); - new_bindings.insert(new_key_str, new_value); + new_bindings.insert(new_key_str, new_value.clone()); Some(new_bindings) } } } -pub fn match_sexp( - allocator: &mut Allocator, - pattern: NodePtr, - sexp: NodePtr, - known_bindings: HashMap, -) -> Option> { +pub fn match_sexp( + allocator: &mut A, + pattern: &A::NodePtr, + sexp: &A::NodePtr, + known_bindings: HashMap, +) -> Option> +where + A::NodePtr: Clone +{ /* * Determine if sexp matches the pattern, with the given known bindings already applied. * Returns None if no match, or a (possibly empty) dictionary of bindings if there is a match @@ -51,7 +58,7 @@ pub fn match_sexp( */ match (allocator.sexp(pattern), allocator.sexp(sexp)) { - (SExp::Atom, SExp::Atom) => { + (ASExp::Atom, ASExp::Atom) => { // Two nodes in scope, both used. if allocator.atom(pattern) == allocator.atom(sexp) { Some(known_bindings) @@ -59,15 +66,15 @@ pub fn match_sexp( None } } - (SExp::Pair(pleft, pright), _) => match (allocator.sexp(pleft), allocator.sexp(pright)) { - (SExp::Atom, SExp::Atom) => { - let left_atom = allocator.atom(pleft); - let right_atom = allocator.atom(pright); + (ASExp::Pair(pleft, pright), _) => match (allocator.sexp(&pleft), allocator.sexp(&pright)) { + (ASExp::Atom, ASExp::Atom) => { + let left_atom = allocator.atom(&pleft); + let right_atom = allocator.atom(&pright); // This is a false positive due to Allocator lifetime. #[allow(clippy::unnecessary_to_owned)] match allocator.sexp(sexp) { - SExp::Atom => { + ASExp::Atom => { // Expression is ($ . $), sexp is '$', result: no capture. // Avoid double borrow. let sexp_atom = allocator.atom(sexp); @@ -103,7 +110,7 @@ pub fn match_sexp( None } - SExp::Pair(sleft, sright) => { + ASExp::Pair(sleft, sright) => { if left_atom.as_ref() == SEXP_MATCH && right_atom.as_ref() != SEXP_MATCH { return unify_bindings( allocator, @@ -114,18 +121,18 @@ pub fn match_sexp( ); } - match_sexp(allocator, pleft, sleft, known_bindings).and_then( - |new_bindings| match_sexp(allocator, pright, sright, new_bindings), + match_sexp(allocator, &pleft, &sleft, known_bindings).and_then( + |new_bindings| match_sexp(allocator, &pright, &sright, new_bindings), ) } } } _ => match allocator.sexp(sexp) { - SExp::Atom => None, - SExp::Pair(sleft, sright) => match_sexp(allocator, pleft, sleft, known_bindings) - .and_then(|new_bindings| match_sexp(allocator, pright, sright, new_bindings)), + ASExp::Atom => None, + ASExp::Pair(sleft, sright) => match_sexp(allocator, &pleft, &sleft, known_bindings) + .and_then(|new_bindings| match_sexp(allocator, &pright, &sright, new_bindings)), }, }, - (SExp::Atom, _) => None, + (ASExp::Atom, _) => None, } } diff --git a/src/classic/clvm_tools/stages/stage_2/abstraction.rs b/src/classic/clvm_tools/stages/stage_2/abstraction.rs new file mode 100644 index 000000000..83bc0b0ea --- /dev/null +++ b/src/classic/clvm_tools/stages/stage_2/abstraction.rs @@ -0,0 +1,115 @@ +use std::ops::Index; + +use clvm_rs::allocator::{Allocator, NodePtr, SExp}; +use clvm_rs::error::EvalErr; + +use crate::classic::clvm_tools::binutils::disassemble; +use crate::compiler::srcloc::Srcloc; + +pub enum ASExp { + Pair(T, T), + Atom +} + +#[derive(Debug)] +pub struct ClError(pub Srcloc, pub EvalErr); + +impl std::fmt::Display for ClError { + fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { + write!(formatter, "{}", self.1) + } +} + +impl From for EvalErr { + fn from(err: ClError) -> Self { + err.1 + } +} + +pub trait BufCarrier<'a> { + fn as_ref(&'a self) -> &'a [u8]; +} + +#[derive(PartialEq, Eq)] +pub struct BufHolder<'a>(clvmr::Atom<'a>); + +impl<'a> Index for BufHolder<'a> { + type Output = u8; + fn index(&self, idx: usize) -> &Self::Output { + &self.as_ref()[idx] + } +} + +impl<'a> BufCarrier<'a> for BufHolder<'a> { + fn as_ref(&'a self) -> &'a [u8] { + self.0.as_ref() + } +} + +pub trait ClassicAllocator { + type NodePtr; + fn loc(&self, node: &Self::NodePtr) -> Srcloc; + fn sexp(&self, node: &Self::NodePtr) -> ASExp; + fn atom<'a>(&'a self, node: &Self::NodePtr) -> BufHolder<'a>; + fn is_nil(&self, node: &Self::NodePtr) -> bool; + fn disassemble(&self, node: &Self::NodePtr, version: Option) -> String; + fn allocator(&mut self) -> &mut Allocator; + fn node_equal(&self, a: &Self::NodePtr, b: &Self::NodePtr) -> bool; + fn map_err(&self, loc: Srcloc, err: EvalErr) -> ClError; + fn new_atom(&mut self, loc: Srcloc, value: &[u8]) -> Result; + fn new_pair(&mut self, loc: Srcloc, a: &Self::NodePtr, b: &Self::NodePtr) -> Result; + fn import(&mut self, loc: Srcloc, node: NodePtr) -> Result; + fn export(&self, node: &Self::NodePtr) -> NodePtr; +} + +thread_local! { + pub static DEFAULT_SRCLOC: Srcloc = Srcloc::start("*clvm*"); +} + +impl ClassicAllocator for Allocator { + type NodePtr = clvmr::NodePtr; + + fn loc(&self, _node: &Self::NodePtr) -> Srcloc { + DEFAULT_SRCLOC.with(|s| s.clone()) + } + fn sexp(&self, node: &Self::NodePtr) -> ASExp { + match clvmr::Allocator::sexp(self, *node) { + SExp::Pair(a, b) => { + ASExp::Pair(a, b) + } + SExp::Atom => ASExp::Atom + } + } + fn atom<'a>(&'a self, node: &Self::NodePtr) -> BufHolder<'a> { + BufHolder(self.atom(*node)) + } + fn is_nil(&self, node: &Self::NodePtr) -> bool { + *node == NodePtr::NIL + } + fn disassemble(&self, node: &Self::NodePtr, version: Option) -> String { + disassemble(self, *node, version) + } + fn allocator(&mut self) -> &mut Allocator { + self + } + fn node_equal(&self, a: &Self::NodePtr, b: &Self::NodePtr) -> bool { + *a == *b + } + fn map_err(&self, loc: Srcloc, err: EvalErr) -> ClError { + ClError(loc, err) + } + fn new_atom(&mut self, loc: Srcloc, value: &[u8]) -> Result { + let new_atom = clvmr::Allocator::new_atom(self, value).map_err(|e| self.map_err(loc.clone(), e))?; + self.import(loc, new_atom) + } + fn new_pair(&mut self, loc: Srcloc, a: &Self::NodePtr, b: &Self::NodePtr) -> Result { + let new_pair = clvmr::Allocator::new_pair(self, *a, *b).map_err(|e| self.map_err(loc.clone(), e))?; + self.import(loc, new_pair) + } + fn import(&mut self, _loc: Srcloc, node: NodePtr) -> Result { + Ok(node) + } + fn export(&self, node: &Self::NodePtr) -> NodePtr { + *node + } +} diff --git a/src/classic/clvm_tools/stages/stage_2/compile.rs b/src/classic/clvm_tools/stages/stage_2/compile.rs index bad7b8182..236197ea7 100644 --- a/src/classic/clvm_tools/stages/stage_2/compile.rs +++ b/src/classic/clvm_tools/stages/stage_2/compile.rs @@ -3,19 +3,21 @@ use std::rc::Rc; use clvm_rs::allocator::{Allocator, NodePtr, SExp}; use clvm_rs::error::EvalErr; -use clvm_rs::reduction::{Reduction, Response}; +use clvm_rs::reduction::{Reduction}; use crate::classic::clvm::__type_compatibility__::{Bytes, BytesFromType}; -use crate::classic::clvm::sexp::{enlist, first, map_m, non_nil, proper_list, rest}; +use crate::classic::clvm::sexp::{enlist, first, map_m, proper_list, rest}; use crate::classic::clvm::OPERATORS_LATEST_VERSION; use crate::classic::clvm::{keyword_from_atom, keyword_to_atom}; -use crate::classic::clvm_tools::binutils::{assemble, disassemble}; +use crate::classic::clvm_tools::binutils::{assemble}; use crate::classic::clvm_tools::node_path::NodePath; use crate::classic::clvm_tools::stages::stage_0::TRunProgram; +use crate::classic::clvm_tools::stages::stage_2::abstraction::{ASExp, BufCarrier, ClassicAllocator, ClError}; use crate::classic::clvm_tools::stages::stage_2::defaults::default_macro_lookup; use crate::classic::clvm_tools::stages::stage_2::helpers::{brun, evaluate, quote}; use crate::classic::clvm_tools::stages::stage_2::module::compile_mod; +use crate::compiler::srcloc::Srcloc; const DIAG_OUTPUT: bool = false; @@ -35,20 +37,23 @@ lazy_static! { }; } -struct Closure<'a> { +struct Closure<'a, A: ClassicAllocator> { name: String, #[allow(clippy::type_complexity)] to_run: &'a dyn Fn( - &mut Allocator, - NodePtr, - NodePtr, - NodePtr, + &mut A, + &A::NodePtr, + &A::NodePtr, + &A::NodePtr, Rc, usize, - ) -> Result, + ) -> Result, } -fn compile_bindings<'a>() -> HashMap, Closure<'a>> { +fn compile_bindings<'a, A: ClassicAllocator>() -> HashMap, Closure<'a, A>> +where + A::NodePtr: Clone +{ let mut bindings = HashMap::new(); let bindings_source = vec![ Closure { @@ -87,145 +92,184 @@ fn unquote_atom() -> Vec { "unquote".as_bytes().to_vec() } -fn com_qq( - allocator: &mut Allocator, +fn com_qq( + allocator: &mut A, ident: String, - macro_lookup: NodePtr, - symbol_table: NodePtr, + macro_lookup: &A::NodePtr, + symbol_table: &A::NodePtr, runner: Rc, - sexp: NodePtr, -) -> Result { + sexp: &A::NodePtr, +) -> Result +where + A::NodePtr: Clone +{ if DIAG_OUTPUT { - println!("com_qq {} {}", ident, disassemble(allocator, sexp, None)); + println!("com_qq {} {}", ident, allocator.disassemble(sexp, None)); } - do_com_prog(allocator, 110, sexp, macro_lookup, symbol_table, runner).map(|x| x.1) + do_com_prog(allocator, 110, sexp, macro_lookup, symbol_table, runner).map(|x| x) } -pub fn compile_qq( - allocator: &mut Allocator, - args: NodePtr, - macro_lookup: NodePtr, - symbol_table: NodePtr, +pub fn compile_qq( + allocator: &mut A, + args: &A::NodePtr, + macro_lookup: &A::NodePtr, + symbol_table: &A::NodePtr, runner: Rc, level: usize, -) -> Result { +) -> Result +where + A::NodePtr: Clone +{ /* * (qq ATOM) => (q . ATOM) * (qq (unquote X)) => X * (qq (a . B)) => (c (qq a) (qq B)) */ - let sexp = match first(allocator, args) { - Err(e) => { - return Err(e); - } - Ok(x) => x, - }; + let sexp = first(allocator, args)?; + let loc = allocator.loc(&sexp); + let nil_import = allocator.import(loc.clone(), NodePtr::NIL)?; - match allocator.sexp(sexp) { - SExp::Atom => { + match allocator.sexp(&sexp) { + ASExp::Atom => { // (qq ATOM) => (q . ATOM) - quote(allocator, sexp) + quote(allocator, &sexp) } - SExp::Pair(op, sexp_rest) => { - if let SExp::Atom = allocator.sexp(op) { + ASExp::Pair(op, sexp_rest) => { + if let ASExp::Atom = allocator.sexp(&op) { // opbuf => op - let op_atom = allocator.atom(op); + let op_atom = allocator.atom(&op); + let op_loc = allocator.loc(&op); if op_atom.as_ref() == qq_atom() { - return m! { - cons_atom <- allocator.new_atom(&[4]); - subexp <- - compile_qq(allocator, sexp_rest, macro_lookup, symbol_table, runner.clone(), level+1); - quoted_null <- quote(allocator, NodePtr::NIL); - consed <- enlist(allocator, &[cons_atom, subexp, quoted_null]); - run_list <- enlist(allocator, &[cons_atom, op, consed]); - com_qq(allocator, "qq sexp pair".to_string(), macro_lookup, symbol_table, runner, run_list) - }; + let op_loc = allocator.loc(&op); + let cons_atom = allocator.new_atom(op_loc, &[4])?; + let subexp = compile_qq(allocator, &sexp_rest, macro_lookup, symbol_table, runner.clone(), level+1)?; + let quoted_null = quote(allocator, &nil_import)?; + let consed = enlist(allocator, &[cons_atom.clone(), subexp, quoted_null])?; + let run_list = enlist(allocator, &[cons_atom, op, consed])?; + return com_qq( + allocator, + "qq sexp pair".to_string(), + macro_lookup, + symbol_table, + runner, + &run_list + ); } else if op_atom.as_ref() == unquote_atom() { // opbuf if level == 1 { // (qq (unquote X)) => X - return m! { - sexp_rf <- first(allocator, sexp_rest); - com_qq(allocator, "level 1".to_string(), macro_lookup, symbol_table, runner, sexp_rf) - }; + let sexp_rf = first(allocator, &sexp_rest)?; + return com_qq( + allocator, + "level 1".to_string(), + macro_lookup, + symbol_table, + runner, + &sexp_rf + ); } - return m! { - // (qq (a . B)) => (c (qq a) (qq B)) - cons_atom <- allocator.new_atom(&[4]); - subexp <- - compile_qq(allocator, sexp_rest, macro_lookup, symbol_table, runner.clone(), level-1); - quoted_null <- quote(allocator, NodePtr::NIL); - consed_subexp <- enlist(allocator, &[cons_atom, subexp, quoted_null]); - run_list <- enlist(allocator, &[cons_atom, op, consed_subexp]); - com_qq(allocator, "qq pair general".to_string(), macro_lookup, symbol_table, runner, run_list) - }; + + // (qq (a . B)) => (c (qq a) (qq B)) + let cons_atom = allocator.new_atom(op_loc, &[4])?; + let subexp = compile_qq(allocator, &sexp_rest, macro_lookup, symbol_table, runner.clone(), level-1)?; + let quoted_null = quote(allocator, &nil_import)?; + let consed_subexp = enlist(allocator, &[cons_atom.clone(), subexp, quoted_null])?; + let run_list = enlist(allocator, &[cons_atom, op, consed_subexp])?; + + return com_qq( + allocator, + "qq pair general".to_string(), + macro_lookup, + symbol_table, + runner, + &run_list + ); } } // (qq (a . B)) => (c (qq a) (qq B)) - m! { - cons_atom <- allocator.new_atom(&[4]); - qq <- allocator.new_atom(&qq_atom()); - qq_l <- enlist(allocator, &[qq, op]); - qq_r <- enlist(allocator, &[qq, sexp_rest]); - compiled_l <- com_qq(allocator, "A".to_string(), macro_lookup, symbol_table, runner.clone(), qq_l); - compiled_r <- com_qq(allocator, "B".to_string(), macro_lookup, symbol_table, runner, qq_r); - enlist(allocator, &[cons_atom, compiled_l, compiled_r]) - } + let cons_atom = allocator.new_atom(loc.clone(), &[4])?; + let qq = allocator.new_atom(loc, &qq_atom())?; + let qq_l = enlist(allocator, &[qq.clone(), op])?; + let qq_r = enlist(allocator, &[qq, sexp_rest])?; + let compiled_l = com_qq(allocator, "A".to_string(), macro_lookup, symbol_table, runner.clone(), &qq_l)?; + let compiled_r = com_qq(allocator, "B".to_string(), macro_lookup, symbol_table, runner, &qq_r)?; + enlist(allocator, &[cons_atom, compiled_l, compiled_r]) } } } -pub fn compile_macros( - allocator: &mut Allocator, - _args: NodePtr, - macro_lookup: NodePtr, - _symbol_table: NodePtr, +pub fn compile_macros( + allocator: &mut A, + _args: &A::NodePtr, + macro_lookup: &A::NodePtr, + _symbol_table: &A::NodePtr, _run_program: Rc, _level: usize, -) -> Result { +) -> Result +where + A::NodePtr: Clone +{ quote(allocator, macro_lookup) } -pub fn compile_symbols( - allocator: &mut Allocator, - _args: NodePtr, - _macro_lookup: NodePtr, - symbol_table: NodePtr, +pub fn compile_symbols( + allocator: &mut A, + _args: &A::NodePtr, + _macro_lookup: &A::NodePtr, + symbol_table: &A::NodePtr, _run_program: Rc, _level: usize, -) -> Result { +) -> Result +where + A::NodePtr: Clone +{ quote(allocator, symbol_table) } // # Transform "quote" to "q" everywhere. Note that quote will not be compiled if behind qq. // # Overrides symbol table defns. -fn lower_quote_(allocator: &mut Allocator, prog: NodePtr) -> Result { - if !non_nil(allocator, prog) { - return Ok(prog); +fn lower_quote_(allocator: &mut A, prog: &A::NodePtr) -> Result +where + A::NodePtr: Clone +{ + let loc = allocator.loc(prog); + let exported = allocator.export(prog); + + if allocator.is_nil(prog) { + return Ok(prog.clone()); } - if let Some(qlist) = proper_list(allocator, prog, true) { + if let Some(qlist) = proper_list(allocator, &prog, true) { if qlist.is_empty() { - return Ok(prog); + return Ok(prog.clone()); } // quote_node was Atom(q) - let quote_node = qlist[0]; - if let SExp::Atom = allocator.sexp(quote_node) { - let quote_atom = allocator.atom(quote_node); + let quote_node = &qlist[0]; + if let ASExp::Atom = allocator.sexp("e_node) { + let quote_atom = allocator.atom("e_node); if quote_atom.as_ref() == b"quote" { if qlist.len() != 2 { // quoted list should be 2: "(quote arg)" - return Err(EvalErr::InternalError(prog, format!("Compilation error while compiling [{}]. quote takes exactly one argument.", disassemble(allocator, prog, None)))); + return Err( + ClError( + loc, + EvalErr::InternalError( + exported, + format!( + "Compilation error while compiling [{}]. quote takes exactly one argument.", + allocator.disassemble(&prog, None) + ) + ) + ) + ); } // Note: quote should have exactly one arg, so the length of - return m! { - lowered <- lower_quote(allocator, qlist[1]); - quote(allocator, lowered) - }; + let lowered = lower_quote(allocator, &qlist[1])?; + return quote(allocator, &lowered); } } } @@ -235,26 +279,27 @@ fn lower_quote_(allocator: &mut Allocator, prog: NodePtr) -> Result Result { +pub fn lower_quote(allocator: &mut A, prog: &A::NodePtr) -> Result +where + A::NodePtr: Clone +{ let res = lower_quote_(allocator, prog); if DIAG_OUTPUT { res.as_ref() .map(|x| { println!( "LOWER_QUOTE {} TO {}", - disassemble(allocator, prog, None), - disassemble(allocator, *x, None) + allocator.disassemble(prog, None), + allocator.disassemble(x, None) ); }) .unwrap_or_else(|_| ()) @@ -262,97 +307,110 @@ pub fn lower_quote(allocator: &mut Allocator, prog: NodePtr) -> Result Response { - m! { - com_atom <- allocator.new_atom("com".as_bytes()); - post_prog <- brun(allocator, macro_code, prog_rest); - - quoted_macros <- quote(allocator, macro_lookup); - quoted_symbols <- quote(allocator, symbol_table); - to_eval <- enlist( - allocator, - &[ - com_atom, - post_prog, - quoted_macros, - quoted_symbols - ] - ); - top_path <- allocator.new_atom(NodePath::new(None).as_path().data()); - evaluate( - allocator, - to_eval, - top_path - ).map(|x| { - if DIAG_OUTPUT { - print!( - "TRY_EXPAND_MACRO {} WITH {} GIVES {} MACROS {} SYMBOLS {}", - disassemble(allocator, macro_code, None), - disassemble(allocator, prog_rest, None), - disassemble(allocator, x, None), - disassemble(allocator, macro_lookup, None), - disassemble(allocator, symbol_table, None) - ); - } - Reduction(1, x) - }) - } +fn try_expand_macro_for_atom_( + allocator: &mut A, + macro_code: &A::NodePtr, + prog_rest: &A::NodePtr, + macro_lookup: &A::NodePtr, + symbol_table: &A::NodePtr, +) -> Result +where + A::NodePtr: Clone +{ + let loc = allocator.loc(macro_code); + let com_atom = allocator.new_atom(loc.clone(), "com".as_bytes())?; + let exported_macro = allocator.export(macro_code); + let exported_prog = allocator.export(prog_rest); + let post_prog = brun( + allocator.allocator(), + exported_macro, + exported_prog, + )?; + let imported_post = allocator.import(loc.clone(), post_prog)?; + let quoted_macros = quote(allocator, macro_lookup)?; + let quoted_symbols = quote(allocator, symbol_table)?; + let to_eval = enlist( + allocator, + &[ + com_atom, + imported_post, + quoted_macros, + quoted_symbols + ] + )?; + let top_path = allocator.new_atom(loc, NodePath::new(None).as_path().data())?; + evaluate( + allocator, + &to_eval, + &top_path + ).map(|x| { + if DIAG_OUTPUT { + print!( + "TRY_EXPAND_MACRO {} WITH {} GIVES {} MACROS {} SYMBOLS {}", + allocator.disassemble(¯o_code, None), + allocator.disassemble(&prog_rest, None), + allocator.disassemble(&x, None), + allocator.disassemble(¯o_lookup, None), + allocator.disassemble(&symbol_table, None) + ); + } + x + }) } -pub fn try_expand_macro_for_atom( - allocator: &mut Allocator, - macro_code: NodePtr, - prog_rest: NodePtr, - macro_lookup: NodePtr, - symbol_table: NodePtr, -) -> Response { - m! { - res <- try_expand_macro_for_atom_( - allocator, - macro_code, - prog_rest, - macro_lookup, - symbol_table - ); - Ok(res) - } +pub fn try_expand_macro_for_atom( + allocator: &mut A, + macro_code: &A::NodePtr, + prog_rest: &A::NodePtr, + macro_lookup: &A::NodePtr, + symbol_table: &A::NodePtr, +) -> Result +where + A::NodePtr: Clone +{ + try_expand_macro_for_atom_( + allocator, + macro_code, + prog_rest, + macro_lookup, + symbol_table + ) } -fn get_macro_program( - allocator: &Allocator, +fn get_macro_program( + allocator: &mut A, operator: &[u8], - macro_lookup: NodePtr, -) -> Result, EvalErr> { - if let Some(mlist) = proper_list(allocator, macro_lookup, true) { + macro_lookup: &A::NodePtr, +) -> Result, ClError> +where + A::NodePtr: Clone +{ + if let Some(mlist) = proper_list(allocator, ¯o_lookup, true) { for macro_pair in mlist { - match proper_list(allocator, macro_pair, true) { + match proper_list(allocator, ¯o_pair, true) { None => {} Some(mp_list) => { if mp_list.is_empty() { continue; } let value = if mp_list.len() > 1 { - mp_list[1] + mp_list[1].clone() } else { - NodePtr::NIL + let loc = allocator.loc(macro_lookup); + let imported_nil = allocator.import(loc, NodePtr::NIL)?; + imported_nil }; - match allocator.sexp(mp_list[0]) { - SExp::Atom => { + match allocator.sexp(&mp_list[0]) { + ASExp::Atom => { // was macro_name, but it's singular and probably // not useful to rename. - let atom = allocator.atom(mp_list[0]); + let atom = allocator.atom(&mp_list[0]); if atom.as_ref() == operator { return Ok(Some(value)); } } - SExp::Pair(_, _) => { + ASExp::Pair(_, _) => { continue; } } @@ -364,40 +422,49 @@ fn get_macro_program( Ok(None) } -fn transform_program_atom( - allocator: &mut Allocator, - prog: NodePtr, +fn transform_program_atom( + allocator: &mut A, + prog: &A::NodePtr, a: &[u8], - symbol_table: NodePtr, -) -> Response { + symbol_table: &A::NodePtr, +) -> Result +where + A::NodePtr: Clone +{ + let loc = allocator.loc(prog); if a == b"@" { return allocator - .new_atom(NodePath::new(None).as_path().data()) - .map(|x| Reduction(1, x)); + .new_atom(loc, NodePath::new(None).as_path().data()) } - match proper_list(allocator, symbol_table, true) { + + match proper_list(allocator, &symbol_table, true) { None => {} Some(symlist) => { + let nil_import = allocator.import(loc.clone(), NodePtr::NIL)?; for sym in symlist { - match proper_list(allocator, sym, true) { + match proper_list(allocator, &sym, true) { None => {} Some(v) => { if v.is_empty() { continue; } - let value = if v.len() > 1 { v[1] } else { NodePtr::NIL }; + let value = if v.len() > 1 { + v[1].clone() + } else { + nil_import.clone() + }; - match allocator.sexp(v[0]) { - SExp::Atom => { + match allocator.sexp(&v[0]) { + ASExp::Atom => { // v[0] is close by, and probably not useful to // rename here. - let atom = allocator.atom(v[0]); + let atom = allocator.atom(&v[0]); if atom.as_ref() == a { - return Ok(Reduction(1, value)); + return Ok(value); } } - SExp::Pair(_, _) => {} + ASExp::Pair(_, _) => {} } } } @@ -405,85 +472,88 @@ fn transform_program_atom( } } - quote(allocator, prog).map(|x| Reduction(1, x)) + quote(allocator, &prog) } -fn compile_operator_atom( - allocator: &mut Allocator, - prog: NodePtr, +fn compile_operator_atom( + allocator: &mut A, + prog: &A::NodePtr, avec: &[u8], - macro_lookup: NodePtr, - symbol_table: NodePtr, + macro_lookup: &A::NodePtr, + symbol_table: &A::NodePtr, run_program: Rc, -) -> Result, EvalErr> { +) -> Result, ClError> +where + A::NodePtr: Clone +{ let compile_bindings = compile_bindings(); if *avec == vec![1] { - return Ok(Some(prog)); + return Ok(Some(prog.clone())); } if let Some(f) = compile_bindings.get(avec) { - return m! { - prog_rest <- rest(allocator, prog); - post_prog <- - (f.to_run)( - allocator, - prog_rest, - macro_lookup, - symbol_table, - run_program.clone(), - 1 - ); - quoted_post_prog <- quote(allocator, post_prog); - top_atom <- - allocator.new_atom(NodePath::new(None).as_path().data()); - - let _ = if DIAG_OUTPUT { - print!("COMPILE_BINDINGS {}", disassemble(allocator, quoted_post_prog, None)); - }; - evaluate(allocator, quoted_post_prog, top_atom).map(Some) + let prog_rest = rest(allocator, &prog)?; + let post_prog = (f.to_run)( + allocator, + &prog_rest, + ¯o_lookup, + &symbol_table, + run_program.clone(), + 1 + )?; + let quoted_post_prog = quote(allocator, &post_prog)?; + let loc = allocator.loc(prog); + let top_atom = allocator.new_atom(loc, NodePath::new(None).as_path().data())?; + let _ = if DIAG_OUTPUT { + print!("COMPILE_BINDINGS {}", allocator.disassemble("ed_post_prog, None)); }; + return evaluate(allocator, "ed_post_prog, &top_atom).map(Some); } Ok(None) } -enum SymbolResult { - Direct(NodePtr), - Matched(NodePtr, NodePtr), +enum SymbolResult { + Direct(A::NodePtr), + Matched(A::NodePtr, A::NodePtr), } -fn find_symbol_match( - allocator: &Allocator, +fn find_symbol_match( + allocator: &mut A, opname: &[u8], - r: NodePtr, - symbol_table: NodePtr, -) -> Result, EvalErr> { - if let Some(symlist) = proper_list(allocator, symbol_table, true) { + r: &A::NodePtr, + symbol_table: &A::NodePtr, +) -> Result>, ClError> +where + A::NodePtr: Clone +{ + if let Some(symlist) = proper_list(allocator, &symbol_table, true) { for sym in symlist { - if let Some(symdef) = proper_list(allocator, sym, true) { + if let Some(symdef) = proper_list(allocator, &sym, true) { if symdef.is_empty() { continue; } - match allocator.sexp(symdef[0]) { - SExp::Atom => { - let symbol = symdef[0]; + match allocator.sexp(&symdef[0]) { + ASExp::Atom => { + let symbol = symdef[0].clone(); let value = if symdef.len() == 1 { - NodePtr::NIL + let loc = allocator.loc(r); + allocator.import(loc, NodePtr::NIL)? } else { - symdef[1] + symdef[1].clone() }; - let symbuf = allocator.atom(symdef[0]); + let symbuf = allocator.atom(&symdef[0]); if b"*" == symbuf.as_ref() { - return Ok(Some(SymbolResult::Direct(r))); + return Ok(Some(SymbolResult::Direct(r.clone()))); } else if opname == symbuf.as_ref() { return Ok(Some(SymbolResult::Matched(symbol, value))); } } - SExp::Pair(_, _) => {} + ASExp::Pair(_, _) => {} } } } @@ -493,42 +563,52 @@ fn find_symbol_match( } #[allow(clippy::too_many_arguments)] -fn compile_application( - allocator: &mut Allocator, - prog: NodePtr, - operator: NodePtr, +fn compile_application( + allocator: &mut A, + prog: &A::NodePtr, + operator: &A::NodePtr, opbuf: &[u8], - rest: NodePtr, - macro_lookup: NodePtr, - symbol_table: NodePtr, + rest: &A::NodePtr, + macro_lookup: &A::NodePtr, + symbol_table: &A::NodePtr, run_program: Rc, -) -> Result { - let mut compiled_args = vec![operator]; - - let error_result = Err(EvalErr::InternalError( - prog, - format!( - "can't compile {}, unknown operator", - disassemble(allocator, prog, None) - ), - )); +) -> Result +where + A::NodePtr: Clone +{ + let mut compiled_args = vec![operator.clone()]; + + let loc = allocator.loc(prog); + let exported_prog = allocator.export(prog); + let error_result = Err( + ClError( + loc, + EvalErr::InternalError( + exported_prog, + format!( + "can't compile {}, unknown operator", + allocator.disassemble(&prog, None) + ), + ) + ) + ); if *opbuf == vec![1] || *opbuf == vec![b'q'] { - return allocator.new_pair(operator, rest); + let rest_loc = allocator.loc(&rest); + return allocator.new_pair(rest_loc, operator, rest); } - match proper_list(allocator, rest, true) { + match proper_list(allocator, &rest, true) { Some(prog_args) => { let mut new_args = map_m(allocator, &mut prog_args.iter(), &|allocator, arg| { do_com_prog( allocator, 544, - *arg, - macro_lookup, - symbol_table, + arg, + ¯o_lookup, + &symbol_table, run_program.clone(), ) - .map(|x| x.1) })?; compiled_args.append(&mut new_args); @@ -540,41 +620,34 @@ fn compile_application( find_symbol_match( allocator, opbuf, - r, + &r, symbol_table ).and_then(|x| match x { Some(SymbolResult::Direct(v)) => { Ok(v) }, Some(SymbolResult::Matched(_symbol,value)) => { - match proper_list(allocator, rest, true) { + match proper_list(allocator, &rest, true) { Some(proglist) => { - m! { - apply_atom <- allocator.new_atom(&[2]); - list_atom <- allocator.new_atom("list".as_bytes()); - cons_atom <- allocator.new_atom(&[4]); - com_atom <- allocator.new_atom("com".as_bytes()); - opt_atom <- allocator.new_atom("opt".as_bytes()); - top_atom <- allocator.new_atom(NodePath::new(None).as_path().data()); - left_atom <- allocator.new_atom(NodePath::new(None).first().as_path().data()); - - enlisted <- enlist(allocator, &proglist); - list_application <- allocator.new_pair(list_atom, enlisted); - - quoted_list <- quote(allocator, list_application); - quoted_macros <- quote(allocator, macro_lookup); - quoted_symbols <- quote(allocator, symbol_table); - compiled <- enlist(allocator, &[com_atom, quoted_list, quoted_macros, quoted_symbols]); - to_run <- enlist(allocator, &[opt_atom, compiled]); - new_args <- evaluate(allocator, to_run, top_atom); - - cons_enlisted <- enlist(allocator, &[cons_atom, left_atom, new_args]); - - result <- enlist( - allocator, - &[apply_atom, value, cons_enlisted] - ); - - Ok(result) - } + let loc = allocator.loc(&value); + let apply_atom = allocator.new_atom(loc.clone(), &[2])?; + let list_atom = allocator.new_atom(loc.clone(), "list".as_bytes())?; + let cons_atom = allocator.new_atom(loc.clone(), &[4])?; + let com_atom = allocator.new_atom(loc.clone(), "com".as_bytes())?; + let opt_atom = allocator.new_atom(loc.clone(), "opt".as_bytes())?; + let top_atom = allocator.new_atom(loc.clone(), NodePath::new(None).as_path().data())?; + let left_atom = allocator.new_atom(loc.clone(), NodePath::new(None).first().as_path().data())?; + let enlisted = enlist(allocator, &proglist)?; + let list_application = allocator.new_pair(loc, &list_atom, &enlisted)?; + let quoted_list = quote(allocator, &list_application)?; + let quoted_macros = quote(allocator, ¯o_lookup)?; + let quoted_symbols = quote(allocator, &symbol_table)?; + let compiled = enlist(allocator, &[com_atom, quoted_list, quoted_macros, quoted_symbols])?; + let to_run = enlist(allocator, &[opt_atom, compiled])?; + let new_args = evaluate(allocator, &to_run, &top_atom)?; + let cons_enlisted = enlist(allocator, &[cons_atom, left_atom, new_args])?; + enlist( + allocator, + &[apply_atom, value, cons_enlisted] + ) }, None => { error_result } } @@ -587,21 +660,24 @@ fn compile_application( } } -pub fn do_com_prog( - allocator: &mut Allocator, +pub fn do_com_prog( + allocator: &mut A, from: usize, - prog: NodePtr, - macro_lookup: NodePtr, - symbol_table: NodePtr, + prog: &A::NodePtr, + macro_lookup: &A::NodePtr, + symbol_table: &A::NodePtr, run_program: Rc, -) -> Response { +) -> Result +where + A::NodePtr: Clone +{ if DIAG_OUTPUT { println!( "START COMPILE {}: {} MACRO {} SYMBOLS {}", from, - disassemble(allocator, prog, None), - disassemble(allocator, macro_lookup, None), - disassemble(allocator, symbol_table, None), + allocator.disassemble(&prog, None), + allocator.disassemble(¯o_lookup, None), + allocator.disassemble(&symbol_table, None), ); } do_com_prog_(allocator, prog, macro_lookup, symbol_table, run_program).inspect(|x| { @@ -609,22 +685,25 @@ pub fn do_com_prog( println!( "DO_COM_PROG {}: {} MACRO {} SYMBOLS {} RESULT {}", from, - disassemble(allocator, prog, None), - disassemble(allocator, macro_lookup, None), - disassemble(allocator, symbol_table, None), - disassemble(allocator, x.1, None) + allocator.disassemble(&prog, None), + allocator.disassemble(¯o_lookup, None), + allocator.disassemble(&symbol_table, None), + allocator.disassemble(&x, None) ); } }) } -fn do_com_prog_( - allocator: &mut Allocator, - prog_: NodePtr, - macro_lookup: NodePtr, - symbol_table: NodePtr, +fn do_com_prog_( + allocator: &mut A, + prog_: &A::NodePtr, + macro_lookup: &A::NodePtr, + symbol_table: &A::NodePtr, run_program: Rc, -) -> Response { +) -> Result +where + A::NodePtr: Clone +{ /* * Turn the given program `prog` into a clvm program using * the macros to do transformation. @@ -638,43 +717,43 @@ fn do_com_prog_( // lower "quote" to "q" m! { - prog <- lower_quote(allocator, prog_); + prog <- lower_quote(allocator, &prog_); // quote atoms - match allocator.sexp(prog) { - SExp::Atom => { + match allocator.sexp(&prog) { + ASExp::Atom => { // Note: can't co-borrow with allocator below. - let prog_atom = allocator.atom(prog); + let prog_atom = allocator.atom(&prog); transform_program_atom( allocator, - prog, + &prog, // This is a false positive due to Allocator lifetime. #[allow(clippy::unnecessary_to_owned)] &prog_atom.as_ref().to_vec(), symbol_table ) }, - SExp::Pair(operator,prog_rest) => { - match allocator.sexp(operator) { - SExp::Atom => { + ASExp::Pair(operator,prog_rest) => { + match allocator.sexp(&operator) { + ASExp::Atom => { // Note: can't co-borrow with allocator below. - let op_atom = allocator.atom(operator); + let op_atom = allocator.atom(&operator); let op_buf = op_atom.as_ref().to_vec(); - get_macro_program(allocator, op_atom.as_ref(), macro_lookup). + get_macro_program(allocator, &op_buf, ¯o_lookup). and_then(|x| match x { Some(value) => { try_expand_macro_for_atom( allocator, - value, - prog_rest, - macro_lookup, + &value, + &prog_rest, + ¯o_lookup, symbol_table ) }, None => { compile_operator_atom( allocator, - prog, + &prog, &op_buf, macro_lookup, symbol_table, @@ -682,40 +761,35 @@ fn do_com_prog_( ).and_then(|x| x.map(Ok).unwrap_or_else(|| m! { compile_application( allocator, - prog, - operator, + &prog, + &operator, &op_buf, - prog_rest, + &prog_rest, macro_lookup, symbol_table, run_program.clone() ) - })).map(|x| Reduction(1, x)) + })) } }) }, _ => { // (com ((OP) . RIGHT)) => (a (com (q OP)) 1) - m! { - com_atom <- allocator.new_atom("com".as_bytes()); - quoted_op <- quote(allocator, operator); - quoted_macro_lookup <- - quote(allocator, macro_lookup); - quoted_symbol_table <- - quote(allocator, symbol_table); - top_atom <- allocator.new_atom(NodePath::new(None).as_path().data()); - eval_list <- enlist(allocator, &[ - com_atom, - quoted_op, - quoted_macro_lookup, - quoted_symbol_table - ]); - - evaluate( - allocator, eval_list, top_atom - ).and_then(|x| enlist(allocator, &[x])). - map(|x| Reduction(1, x)) - } + let loc = allocator.loc(&operator); + let com_atom = allocator.new_atom(loc.clone(), "com".as_bytes())?; + let quoted_op = quote(allocator, &operator)?; + let quoted_macro_lookup = quote(allocator, macro_lookup)?; + let quoted_symbol_table = quote(allocator, symbol_table)?; + let top_atom = allocator.new_atom(loc, NodePath::new(None).as_path().data())?; + let eval_list = enlist(allocator, &[ + com_atom, + quoted_op, + quoted_macro_lookup, + quoted_symbol_table + ])?; + evaluate( + allocator, &eval_list, &top_atom + ).and_then(|x| enlist(allocator, &[x])) } } } @@ -723,27 +797,32 @@ fn do_com_prog_( } } -pub fn do_com_prog_for_dialect( +pub fn do_com_prog_for_dialect( runner: Rc, - allocator: &mut Allocator, - sexp: NodePtr, -) -> Response { + allocator: &mut A, + sexp: &A::NodePtr, +) -> Result +where + A::NodePtr: Clone +{ match allocator.sexp(sexp) { - SExp::Pair(prog, extras) => { - let mut symbol_table = NodePtr::NIL; + ASExp::Pair(prog, extras) => { + let loc = allocator.loc(sexp); + let imported_nil = allocator.import(loc, NodePtr::NIL)?; + let mut symbol_table = imported_nil; let macro_lookup; let mut elist = Vec::new(); - if let Some(elist_vec) = proper_list(allocator, extras, true) { + if let Some(elist_vec) = proper_list(allocator, &extras, true) { elist = elist_vec.to_vec(); } if elist.is_empty() { macro_lookup = default_macro_lookup(allocator, runner.clone()); } else { - macro_lookup = elist[0]; + macro_lookup = elist[0].clone(); if elist.len() > 1 { - symbol_table = elist[1]; + symbol_table = elist[1].clone(); } } @@ -754,9 +833,9 @@ pub fn do_com_prog_for_dialect( do_com_prog( allocator, 773, - prog, - macro_lookup, - symbol_table, + &prog, + ¯o_lookup, + &symbol_table, runner.clone(), ) //.map(|x| { @@ -770,10 +849,18 @@ pub fn do_com_prog_for_dialect( // x //}) } - _ => Err(EvalErr::InternalError( - sexp, - "Program is not a pair in do_com_prog".to_string(), - )), + _ => { + let exported = allocator.export(&sexp); + Err( + ClError( + allocator.loc(sexp), + EvalErr::InternalError( + exported, + "Program is not a pair in do_com_prog".to_string(), + ) + ) + ) + } } } @@ -804,20 +891,25 @@ pub fn get_compile_filename( )) } -pub fn get_search_paths( +pub fn get_search_paths( runner: Rc, - allocator: &mut Allocator, -) -> Result, EvalErr> { - let search_paths_prog = assemble(allocator, "(_get_include_paths)")?; - let search_path_result = - runner.run_program(allocator, search_paths_prog, NodePtr::NIL, None)?; - + loc: Srcloc, + allocator: &mut A, +) -> Result, ClError> +where + A::NodePtr: Clone +{ + let search_paths_result = ((|| { + let search_paths_prog = assemble(allocator.allocator(), "(_get_include_paths)")?; + runner.run_program(allocator.allocator(), search_paths_prog, NodePtr::NIL, None) + })()).map_err(|e| ClError(loc.clone(), e))?; let mut res = Vec::new(); - if let Some(l) = proper_list(allocator, search_path_result.1, true) { - for elt in l.iter().copied() { - if let SExp::Atom = allocator.sexp(elt) { + let search_paths_result_import = allocator.import(loc, search_paths_result.1)?; + if let Some(l) = proper_list(allocator, &search_paths_result_import, true) { + for elt in l.iter().cloned() { + if let ASExp::Atom = allocator.sexp(&elt) { // Only elt in scope. - let atom = allocator.atom(elt); + let atom = allocator.atom(&elt); res.push(Bytes::new(Some(BytesFromType::Raw(atom.as_ref().to_vec()))).decode()); } } diff --git a/src/classic/clvm_tools/stages/stage_2/defaults.rs b/src/classic/clvm_tools/stages/stage_2/defaults.rs index 96a395066..1f60462ce 100644 --- a/src/classic/clvm_tools/stages/stage_2/defaults.rs +++ b/src/classic/clvm_tools/stages/stage_2/defaults.rs @@ -1,9 +1,11 @@ use std::rc::Rc; -use clvm_rs::allocator::{Allocator, NodePtr}; +use clvm_rs::allocator::{NodePtr}; use crate::classic::clvm_tools::binutils::assemble; use crate::classic::clvm_tools::stages::stage_0::TRunProgram; +use crate::classic::clvm_tools::stages::stage_2::abstraction::{ClassicAllocator}; +use crate::compiler::srcloc::Srcloc; /* "function" is used in front of a constant uncompiled @@ -100,25 +102,30 @@ fn default_macros_src() -> Vec<&'static str> { ] } -fn build_default_macro_lookup( - allocator: &mut Allocator, +fn build_default_macro_lookup( + allocator: &mut A, eval_f: Rc, macros_src: &[String], -) -> NodePtr { - let run = assemble(allocator, "(a (com 2 3) 1)").unwrap(); - let mut default_macro_lookup: NodePtr = NodePtr::NIL; +) -> A::NodePtr { + let run = assemble(allocator.allocator(), "(a (com 2 3) 1)").unwrap(); + let macro_loc = Srcloc::start("*macros*"); + let imported_nil = allocator.import(macro_loc.clone(), NodePtr::NIL).unwrap(); + let mut default_macro_lookup = imported_nil; for macro_src in macros_src { - let macro_sexp = assemble(allocator, macro_src).unwrap(); + let macro_sexp = assemble(allocator.allocator(), macro_src).unwrap(); + let imported_macro_sexp = allocator.import(macro_loc.clone(), macro_sexp).unwrap(); let env = allocator - .new_pair(macro_sexp, default_macro_lookup) + .new_pair(macro_loc.clone(), &imported_macro_sexp, &default_macro_lookup) .unwrap(); - let new_macro = eval_f.run_program(allocator, run, env, None).unwrap().1; - default_macro_lookup = allocator.new_pair(new_macro, default_macro_lookup).unwrap(); + let exported_env = allocator.export(&env); + let new_macro = eval_f.run_program(allocator.allocator(), run, exported_env, None).unwrap().1; + let imported_new_macro = allocator.import(macro_loc.clone(), new_macro).unwrap(); + default_macro_lookup = allocator.new_pair(macro_loc.clone(), &imported_new_macro, &default_macro_lookup).unwrap(); } default_macro_lookup } -pub fn default_macro_lookup(allocator: &mut Allocator, runner: Rc) -> NodePtr { +pub fn default_macro_lookup(allocator: &mut A, runner: Rc) -> A::NodePtr { let macro_srcs: Vec = default_macros_src().iter().map(|s| s.to_string()).collect(); build_default_macro_lookup(allocator, runner.clone(), ¯o_srcs) } diff --git a/src/classic/clvm_tools/stages/stage_2/helpers.rs b/src/classic/clvm_tools/stages/stage_2/helpers.rs index 79b4638e0..0e58e57a2 100644 --- a/src/classic/clvm_tools/stages/stage_2/helpers.rs +++ b/src/classic/clvm_tools/stages/stage_2/helpers.rs @@ -1,8 +1,8 @@ use clvm_rs::allocator::{Allocator, NodePtr}; -use clvm_rs::error::EvalErr; use crate::classic::clvm::sexp::enlist; use crate::classic::clvm_tools::node_path::NodePath; +use crate::classic::clvm_tools::stages::stage_2::abstraction::{ClassicAllocator, ClError}; lazy_static! { pub static ref QUOTE_ATOM: Vec = vec![1]; @@ -10,30 +10,35 @@ lazy_static! { pub static ref COM_ATOM: Vec = vec![b'c', b'o', b'm']; } -pub fn quote(allocator: &mut Allocator, sexp: NodePtr) -> Result { +pub fn quote(allocator: &mut A, sexp: &A::NodePtr) -> Result { allocator - .new_atom("E_ATOM) - .and_then(|q| allocator.new_pair(q, sexp)) + .new_atom(allocator.loc(&sexp), "E_ATOM) + .and_then(|q| allocator.new_pair(allocator.loc(&sexp), &q, &sexp)) } // In original python code, the name of this function is `eval`, // but since the name `eval` cannot be used in typescript context, change the name to `evaluate`. -pub fn evaluate( - allocator: &mut Allocator, - prog: NodePtr, - args: NodePtr, -) -> Result { - m! { - a <- allocator.new_atom(&APPLY_ATOM); - enlist(allocator, &[a, prog, args]) - } +pub fn evaluate( + allocator: &mut A, + prog: &A::NodePtr, + args: &A::NodePtr, +) -> Result +where + A::NodePtr: Clone +{ + let loc = allocator.loc(prog); + let a = allocator.new_atom(loc, &APPLY_ATOM)?; + enlist(allocator, &[a, prog.clone(), args.clone()]) } -pub fn run( - allocator: &mut Allocator, - prog: NodePtr, - macro_lookup: NodePtr, -) -> Result { +pub fn run( + allocator: &mut A, + prog: &A::NodePtr, + macro_lookup: &A::NodePtr, +) -> Result +where + A::NodePtr: Clone +{ /* * PROG => (e (com (q . PROG) (mac)) ARGS) * @@ -41,19 +46,16 @@ pub fn run( * function. */ let args = NodePath::new(None).as_path(); - m! { - mac <- quote(allocator, macro_lookup); - com_sexp <- allocator.new_atom(&COM_ATOM); - arg_sexp <- allocator.new_atom(args.data()); - to_eval <- enlist(allocator, &[com_sexp, prog, mac]); - evaluate(allocator, to_eval, arg_sexp) - } + let loc = allocator.loc(prog); + let mac = quote(allocator, ¯o_lookup)?; + let com_sexp = allocator.new_atom(loc.clone(), &COM_ATOM)?; + let arg_sexp = allocator.new_atom(loc, args.data())?; + let to_eval = enlist(allocator, &[com_sexp, prog.clone(), mac])?; + evaluate(allocator, &to_eval, &arg_sexp) } -pub fn brun(allocator: &mut Allocator, prog: NodePtr, args: NodePtr) -> Result { - m! { - quoted_prog <- quote(allocator, prog); - quoted_args <- quote(allocator, args); - evaluate(allocator, quoted_prog, quoted_args) - } +pub fn brun(allocator: &mut Allocator, prog: NodePtr, args: NodePtr) -> Result { + let quoted_prog = quote(allocator, &prog)?; + let quoted_args = quote(allocator, &args)?; + evaluate(allocator, "ed_prog, "ed_args) } diff --git a/src/classic/clvm_tools/stages/stage_2/inline.rs b/src/classic/clvm_tools/stages/stage_2/inline.rs index dca663fd6..6247ab73d 100644 --- a/src/classic/clvm_tools/stages/stage_2/inline.rs +++ b/src/classic/clvm_tools/stages/stage_2/inline.rs @@ -1,27 +1,30 @@ use crate::classic::clvm::__type_compatibility__::{bi_one, bi_zero}; use crate::classic::clvm::sexp::{enlist, proper_list}; use crate::compiler::gensym::gensym; +use crate::classic::clvm_tools::stages::stage_2::abstraction::{ASExp, BufCarrier, ClassicAllocator, ClError}; + use crate::util::Number; -use clvm_rs::allocator::{Allocator, NodePtr, SExp}; -use clvm_rs::error::EvalErr; use num_bigint::ToBigInt; use std::collections::HashMap; // If this is an at capture of the form // (@ name substructure) // then return name and substructure. -pub fn is_at_capture( - allocator: &Allocator, - tree_first: NodePtr, - tree_rest: NodePtr, -) -> Option<(NodePtr, NodePtr)> { - if let (SExp::Atom, Some(spec)) = ( +pub fn is_at_capture( + allocator: &A, + tree_first: &A::NodePtr, + tree_rest: &A::NodePtr, +) -> Option<(A::NodePtr, A::NodePtr)> +where + A::NodePtr: Clone +{ + if let (ASExp::Atom, Some(spec)) = ( allocator.sexp(tree_first), - proper_list(allocator, tree_rest, true), + proper_list(allocator, &tree_rest, true), ) { let first_atom = allocator.atom(tree_first); if first_atom.as_ref() == b"@" && spec.len() == 2 { - return Some((spec[0], spec[1])); + return Some((spec[0].clone(), spec[1].clone())); } } @@ -29,22 +32,30 @@ pub fn is_at_capture( } // (unquote X) -fn wrap_in_unquote(allocator: &mut Allocator, code: NodePtr) -> Result { - let unquote_atom = allocator.new_atom("unquote".as_bytes())?; - enlist(allocator, &[unquote_atom, code]) +fn wrap_in_unquote(allocator: &mut A, code: &A::NodePtr) -> Result +where + A::NodePtr: Clone +{ + let loc = allocator.loc(code); + let unquote_atom = allocator.new_atom(loc, "unquote".as_bytes())?; + enlist(allocator, &[unquote_atom, code.clone()]) } // (__chia__enlist X) -fn wrap_in_compile_time_list(allocator: &mut Allocator, code: NodePtr) -> Result { - let chia_enlist_atom = allocator.new_atom("__chia__enlist".as_bytes())?; - enlist(allocator, &[chia_enlist_atom, code]) +fn wrap_in_compile_time_list(allocator: &mut A, code: &A::NodePtr) -> Result +where + A::NodePtr: Clone +{ + let loc = allocator.loc(code); + let chia_enlist_atom = allocator.new_atom(loc, "__chia__enlist".as_bytes())?; + enlist(allocator, &[chia_enlist_atom, code.clone()]) } // Create the sequence of individual tree moves that will translate to // (f ...) and (r ...) wrapping to select the given path from a larger structure. -fn create_path_selection_plan(path: Number, operators: &mut Vec) -> Result<(), EvalErr> { +fn create_path_selection_plan(path: Number, operators: &mut Vec) { if path <= bi_one() { - Ok(()) + return; } else { operators.push(path.clone() % 2_u32.to_bigint().unwrap() == bi_one()); create_path_selection_plan(path / 2_u32.to_bigint().unwrap(), operators) @@ -52,17 +63,21 @@ fn create_path_selection_plan(path: Number, operators: &mut Vec) -> Result } // Given a path and code to be wrapped, generate a lookup by path into that code. -fn wrap_path_selection( - allocator: &mut Allocator, +fn wrap_path_selection( + allocator: &mut A, path: Number, - wrapped: NodePtr, -) -> Result { + wrapped: &A::NodePtr, +) -> Result +where + A::NodePtr: Clone +{ let mut operator_stack = Vec::new(); - let mut tail = wrapped; - create_path_selection_plan(path, &mut operator_stack)?; + let mut tail = wrapped.clone(); + create_path_selection_plan(path, &mut operator_stack); for o in operator_stack.iter() { let head_op = if *o { vec![6] } else { vec![5] }; - let head_atom = allocator.new_atom(&head_op)?; + let loc = allocator.loc(wrapped); + let head_atom = allocator.new_atom(loc, &head_op)?; tail = enlist(allocator, &[head_atom, tail])?; } Ok(tail) @@ -77,71 +92,75 @@ fn wrap_path_selection( // as the classic macro system handles destructuring on the source text rather // than the argument values, so we must eliminate all deep references past the // top of the argument list. -fn formulate_path_selections_for_destructuring_arg( - allocator: &mut Allocator, - arg_sexp: NodePtr, +fn formulate_path_selections_for_destructuring_arg( + allocator: &mut A, + arg_sexp: &A::NodePtr, arg_path: Number, arg_depth: Number, - referenced_from: Option, - selections: &mut HashMap, NodePtr>, -) -> Result { - match allocator.sexp(arg_sexp) { - SExp::Pair(a, b) => { + referenced_from: Option, + selections: &mut HashMap, A::NodePtr>, +) -> Result +where + A::NodePtr: Clone +{ + let loc = allocator.loc(arg_sexp); + match allocator.sexp(&arg_sexp) { + ASExp::Pair(a, b) => { let next_depth = arg_depth.clone() * 2_u32.to_bigint().unwrap(); - if let Some((capture, substructure)) = is_at_capture(allocator, a, b) { - if let SExp::Atom = allocator.sexp(capture) { + if let Some((capture, substructure)) = is_at_capture(allocator, &a, &b) { + if let ASExp::Atom = allocator.sexp(&capture) { let (new_arg_path, new_arg_depth, tail) = if let Some(prev_ref) = referenced_from { (arg_path, arg_depth, prev_ref) } else { - let capture_code = wrap_in_unquote(allocator, capture)?; + let capture_code = wrap_in_unquote(allocator, &capture)?; let qtail = - wrap_path_selection(allocator, arg_path + arg_depth, capture_code)?; + wrap_path_selection(allocator, arg_path + arg_depth, &capture_code)?; (bi_zero(), bi_one(), qtail) }; // Was cbuf from capture. - let capture_atom = allocator.atom(capture); - selections.insert(capture_atom.as_ref().to_vec(), tail); + let capture_atom = allocator.atom(&capture); + selections.insert(capture_atom.as_ref().to_vec(), tail.clone()); return formulate_path_selections_for_destructuring_arg( allocator, - substructure, + &substructure, new_arg_path, new_arg_depth, Some(tail), selections, ) - .map(|_| arg_sexp); + .map(|_| arg_sexp.clone()); } } if referenced_from.is_some() { let f = formulate_path_selections_for_destructuring_arg( allocator, - a, + &a, arg_path.clone(), next_depth.clone(), - referenced_from, + referenced_from.clone(), selections, )?; let r = formulate_path_selections_for_destructuring_arg( allocator, - b, + &b, arg_depth + arg_path, next_depth, referenced_from, selections, )?; - allocator.new_pair(f, r) + allocator.new_pair(loc, &f, &r) } else { let ref_name = gensym("destructuring_capture".as_bytes().to_vec()); - let at_atom = allocator.new_atom("@".as_bytes())?; - let name_atom = allocator.new_atom(&ref_name)?; - let new_arg_list = enlist(allocator, &[at_atom, name_atom, arg_sexp])?; + let at_atom = allocator.new_atom(loc.clone(), "@".as_bytes())?; + let name_atom = allocator.new_atom(loc, &ref_name)?; + let new_arg_list = enlist(allocator, &[at_atom, name_atom, arg_sexp.clone()])?; formulate_path_selections_for_destructuring_arg( allocator, - new_arg_list, + &new_arg_list, bi_zero(), bi_one(), None, @@ -149,18 +168,18 @@ fn formulate_path_selections_for_destructuring_arg( ) } } - SExp::Atom => { + ASExp::Atom => { // Note: can't co-borrow with allocator below. let buf_atom = allocator.atom(arg_sexp); let buf = buf_atom.as_ref().to_vec(); if !buf.is_empty() { if let Some(capture) = referenced_from { - let tail = wrap_path_selection(allocator, arg_path + arg_depth, capture)?; + let tail = wrap_path_selection(allocator, arg_path + arg_depth, &capture)?; selections.insert(buf, tail); - return Ok(arg_sexp); + return Ok(arg_sexp.clone()); } } - Ok(arg_sexp) + Ok(arg_sexp.clone()) } } } @@ -221,22 +240,25 @@ fn formulate_path_selections_for_destructuring_arg( // environment but that destructures just its first argument as a list, // so i adapted list into __chia__enlist. // When so wrapped, the user may then destructure the capture argument. -pub fn formulate_path_selections_for_destructuring( - allocator: &mut Allocator, - args_sexp: NodePtr, - selections: &mut HashMap, NodePtr>, -) -> Result { - if let SExp::Pair(a, b) = allocator.sexp(args_sexp) { - if let Some((capture, substructure)) = is_at_capture(allocator, a, b) { - if let SExp::Atom = allocator.sexp(capture) { - let quoted_arg_list = wrap_in_unquote(allocator, capture)?; - let tail = wrap_in_compile_time_list(allocator, quoted_arg_list)?; +pub fn formulate_path_selections_for_destructuring( + allocator: &mut A, + args_sexp: &A::NodePtr, + selections: &mut HashMap, A::NodePtr>, +) -> Result +where + A::NodePtr: Clone +{ + if let ASExp::Pair(a, b) = allocator.sexp(args_sexp) { + if let Some((capture, substructure)) = is_at_capture(allocator, &a, &b) { + if let ASExp::Atom = allocator.sexp(&capture) { + let quoted_arg_list = wrap_in_unquote(allocator, &capture)?; + let tail = wrap_in_compile_time_list(allocator, "ed_arg_list)?; // Was: cbuf from capture. - let buf_atom = allocator.atom(capture); - selections.insert(buf_atom.as_ref().to_vec(), tail); + let buf_atom = allocator.atom(&capture); + selections.insert(buf_atom.as_ref().to_vec(), tail.clone()); let newsub = formulate_path_selections_for_destructuring_arg( allocator, - substructure, + &substructure, bi_zero(), bi_one(), Some(tail), @@ -247,29 +269,33 @@ pub fn formulate_path_selections_for_destructuring( } let f = formulate_path_selections_for_destructuring_arg( allocator, - a, + &a, bi_zero(), bi_one(), None, selections, )?; - let r = formulate_path_selections_for_destructuring(allocator, b, selections)?; - allocator.new_pair(f, r) + let r = formulate_path_selections_for_destructuring(allocator, &b, selections)?; + let loc = allocator.loc(&b); + allocator.new_pair(loc, &f, &r) } else { - Ok(args_sexp) + Ok(args_sexp.clone()) } } // If true, these arguments represent a destructuring of some kind. // In the case of inlines in classic chialisp, we must adjust how arguments // are passed down to the macro body that gets created for the inline function. -pub fn is_inline_destructure(allocator: &mut Allocator, args_sexp: NodePtr) -> bool { - if let SExp::Pair(a, b) = allocator.sexp(args_sexp) { - if let SExp::Pair(_, _) = allocator.sexp(a) { +pub fn is_inline_destructure( + allocator: &mut A, + args_sexp: &A::NodePtr +) -> bool { + if let ASExp::Pair(a, b) = allocator.sexp(args_sexp) { + if let ASExp::Pair(_, _) = allocator.sexp(&a) { return true; } - return is_inline_destructure(allocator, b); + return is_inline_destructure(allocator, &b); } false diff --git a/src/classic/clvm_tools/stages/stage_2/mod.rs b/src/classic/clvm_tools/stages/stage_2/mod.rs index 6e24f1bc8..2ba6e230f 100644 --- a/src/classic/clvm_tools/stages/stage_2/mod.rs +++ b/src/classic/clvm_tools/stages/stage_2/mod.rs @@ -1,3 +1,4 @@ +pub mod abstraction; pub mod compile; pub mod defaults; pub mod helpers; diff --git a/src/classic/clvm_tools/stages/stage_2/module.rs b/src/classic/clvm_tools/stages/stage_2/module.rs index bb57a4b99..442751143 100644 --- a/src/classic/clvm_tools/stages/stage_2/module.rs +++ b/src/classic/clvm_tools/stages/stage_2/module.rs @@ -2,46 +2,67 @@ use std::collections::HashMap; use std::collections::HashSet; use std::rc::Rc; -use clvm_rs::allocator::{Allocator, NodePtr, SExp}; +use clvm_rs::allocator::{NodePtr}; use clvm_rs::error::EvalErr; use crate::classic::clvm::__type_compatibility__::{Bytes, BytesFromType}; use crate::classic::clvm::sexp::{ - enlist, first, flatten, fold_m, map_m, non_nil, nonempty_last, proper_list, rest, First, + enlist, first, flatten, fold_m, map_m, nonempty_last, proper_list, rest, First, NodeSel, Rest, SelectNode, ThisNode, }; -use crate::classic::clvm_tools::binutils::disassemble; use crate::classic::clvm_tools::debug::{build_symbol_dump, FunctionExtraInfo}; use crate::classic::clvm_tools::node_path::NodePath; use crate::classic::clvm_tools::stages::assemble; use crate::classic::clvm_tools::stages::stage_0::TRunProgram; +use crate::classic::clvm_tools::stages::stage_2::abstraction::{ASExp, BufCarrier, ClassicAllocator, ClError}; use crate::classic::clvm_tools::stages::stage_2::helpers::{evaluate, quote}; use crate::classic::clvm_tools::stages::stage_2::inline::{ formulate_path_selections_for_destructuring, is_at_capture, is_inline_destructure, }; use crate::classic::clvm_tools::stages::stage_2::optimize::optimize_sexp; use crate::classic::clvm_tools::stages::stage_2::reader::process_embed_file; +use crate::compiler::srcloc::Srcloc; lazy_static! { pub static ref MAIN_NAME: String = "".to_string(); } -struct CollectionResult { - pub functions: HashMap, NodePtr>, - pub constants: HashMap, NodePtr>, - pub macros: Vec<(Vec, NodePtr)>, +struct CollectionResult { + pub functions: HashMap, A::NodePtr>, + pub constants: HashMap, A::NodePtr>, + pub macros: Vec<(Vec, A::NodePtr)>, } -#[derive(Default)] -struct CompileOutput { - pub functions: HashMap, NodePtr>, - pub symbols_extra_info: HashMap, FunctionExtraInfo>, +struct CompileOutput +where + A::NodePtr: Clone +{ + pub functions: HashMap, A::NodePtr>, + pub symbols_extra_info: HashMap, FunctionExtraInfo>, } -impl CompileOutput { - pub fn add_definitions(&mut self, other: &CompileOutput) { +impl Default for CompileOutput +where + A::NodePtr: Clone +{ + fn default() -> Self { + CompileOutput { + functions: HashMap::default(), + symbols_extra_info: HashMap::default(), + } + } +} + +impl CompileOutput +where + A::NodePtr: Clone +{ + pub fn add_definitions(&mut self, other: &CompileOutput) + where + A::NodePtr: Clone + { for (n, v) in other.functions.iter() { - self.functions.insert(n.to_vec(), *v); + self.functions.insert(n.to_vec(), v.clone()); } for (n, v) in other.symbols_extra_info.iter() { self.symbols_extra_info.insert(n.to_vec(), v.clone()); @@ -50,56 +71,63 @@ impl CompileOutput { } // export type TBuildTree = Bytes | Tuple | []; -fn build_tree(allocator: &mut Allocator, items: &[Vec]) -> Result { +fn build_tree( + allocator: &mut A, + items: &[Vec] +) -> Result +where + A::NodePtr: Clone +{ if items.is_empty() { - Ok(NodePtr::NIL) + let imported_nil = allocator.import(Srcloc::start("*nil*"), NodePtr::NIL)?; + Ok(imported_nil) } else if items.len() == 1 { - allocator.new_atom(&items[0]) + allocator.new_atom(Srcloc::start("*ident*"), &items[0]) } else { - m! { - let half_size = items.len() >> 1; - left <- build_tree(allocator, &items[..half_size]); - right <- build_tree(allocator, &items[half_size..]); - allocator.new_pair(left, right) - } + let half_size = items.len() >> 1; + let left = build_tree(allocator, &items[..half_size])?; + let right = build_tree(allocator, &items[half_size..])?; + let loc = allocator.loc(&left); + allocator.new_pair(loc, &left, &right) } } // export type TBuildTreeProgram = SExp | [Bytes, TBuildTree, TBuildTree] | [Tuple]; -fn build_tree_program(allocator: &mut Allocator, items: &[NodePtr]) -> Result { +fn build_tree_program(allocator: &mut A, items: &[A::NodePtr]) -> Result +where + A::NodePtr: Clone +{ // This function takes a Python list of items and turns it into a program that // a binary tree of the items, suitable for casting to an s-expression. let size = items.len(); if size == 0 { - m! { - list_of_nil <- enlist(allocator, &[NodePtr::NIL]); - quote(allocator, list_of_nil) - } + let imported_nil = allocator.import(Srcloc::start("*nil*"), NodePtr::NIL)?; + let list_of_nil = enlist(allocator, &[imported_nil])?; + quote(allocator, &list_of_nil) } else if size == 1 { - Ok(items[0]) + Ok(items[0].clone()) } else { - m! { - let half_size = items.len() >> 1; - left <- - build_tree_program(allocator, &items[..half_size]); - right <- - build_tree_program(allocator, &items[half_size..]); - - cons_atom <- allocator.new_atom(&[4_u8]); - enlist(allocator, &[cons_atom, left, right]) - } + let half_size = items.len() >> 1; + let left = build_tree_program(allocator, &items[..half_size])?; + let right = build_tree_program(allocator, &items[half_size..])?; + let loc = allocator.loc(&left); + let cons_atom = allocator.new_atom(loc, &[4_u8])?; + enlist(allocator, &[cons_atom, left, right]) } } /** * @return Used constants name array in `hex string` format. */ -fn build_used_constants_names( - allocator: &mut Allocator, - functions: &HashMap, NodePtr>, - constants: &HashMap, NodePtr>, - macros: &[(Vec, NodePtr)], -) -> Result>, EvalErr> { +fn build_used_constants_names( + allocator: &mut A, + functions: &HashMap, A::NodePtr>, + constants: &HashMap, A::NodePtr>, + macros: &[(Vec, A::NodePtr)], +) -> Result>, ClError> +where + A::NodePtr: Clone +{ /* Do a naĂ¯ve pruning of unused symbols. It may be too big, but it shouldn't be too small. Return a list of all atoms used that are also the names of @@ -109,7 +137,7 @@ fn build_used_constants_names( for nv in macros { let (name, value) = nv; - macro_as_dict.insert(name.to_vec(), *value); + macro_as_dict.insert(name.to_vec(), value.clone()); } let mut possible_symbols = HashSet::new(); @@ -137,17 +165,17 @@ fn build_used_constants_names( .flat_map(|v| { v.map(|v| { let mut res = Vec::new(); - flatten(allocator, *v, &mut res); + flatten(allocator, v, &mut res); res }) .unwrap_or_default() }) - .collect::>(); + .collect::>(); let matching_names = matching_names_1.iter().filter_map(|v| { // Only v usefully in scope. - if let SExp::Atom = allocator.sexp(*v) { - let atom = allocator.atom(*v); + if let ASExp::Atom = allocator.sexp(v) { + let atom = allocator.atom(v); Some(atom.as_ref().to_vec()) } else { None @@ -176,56 +204,67 @@ fn build_used_constants_names( } #[allow(clippy::too_many_arguments)] -fn parse_include( - allocator: &mut Allocator, - name: NodePtr, +fn parse_include( + allocator: &mut A, + name: &A::NodePtr, namespace: &mut HashSet>, - functions: &mut HashMap, NodePtr>, - constants: &mut HashMap, NodePtr>, - delayed_constants: &mut HashMap, NodePtr>, - macros: &mut Vec<(Vec, NodePtr)>, + functions: &mut HashMap, A::NodePtr>, + constants: &mut HashMap, A::NodePtr>, + delayed_constants: &mut HashMap, A::NodePtr>, + macros: &mut Vec<(Vec, A::NodePtr)>, run_program: Rc, -) -> Result<(), EvalErr> { - m! { - prog <- assemble( - allocator, - "(_read (_full_path_for_name 1))" - ); - assembled_sexp <- run_program.run_program( - allocator, - prog, - name, - None - ); - match proper_list(allocator, assembled_sexp.1, true) { - None => { Err(EvalErr::InternalError(name, "include returned malformed result".to_string())) }, - Some(assembled) => { - for sexp in assembled { - parse_mod_sexp( - allocator, - sexp, - namespace, - functions, - constants, - delayed_constants, - macros, - run_program.clone() - )?; - }; - Ok(()) - } - } +) -> Result<(), ClError> +where + A::NodePtr: Clone +{ + let loc = allocator.loc(name); + let prog = assemble( + allocator.allocator(), + "(_read (_full_path_for_name 1))" + ).map_err(|e| ClError(loc.clone(), e))?; + let name_export = allocator.export(name); + let assembled_sexp = run_program.run_program( + allocator.allocator(), + prog, + name_export, + None + ).map_err(|e| ClError(loc.clone(), e))?; + let assembled_sexp_import = allocator.import(loc.clone(), assembled_sexp.1)?; + if let Some(assembled) = proper_list(allocator, &assembled_sexp_import, true) { + for sexp in assembled { + parse_mod_sexp( + allocator, + &sexp, + namespace, + functions, + constants, + delayed_constants, + macros, + run_program.clone() + )?; + }; + return Ok(()); } + + Err( + ClError( + loc, + EvalErr::InternalError(name_export, "include returned malformed result".to_string()) + ) + ) } -fn unquote_args( - allocator: &mut Allocator, - code: NodePtr, +fn unquote_args( + allocator: &mut A, + code: &A::NodePtr, args: &[Vec], - matches: &HashMap, NodePtr>, -) -> Result { + matches: &HashMap, A::NodePtr>, +) -> Result +where + A::NodePtr: Clone +{ match allocator.sexp(code) { - SExp::Atom => { + ASExp::Atom => { // Only code in scope. let code_atom = allocator.atom(code); let matching_args = args @@ -237,59 +276,63 @@ fn unquote_args( if let Some(argval) = matches.get(&matching_args[0]) { // New case: if we've been given an alternate way of computing // the argument, use it here. - return Ok(*argval); + return Ok(argval.clone()); } - let unquote_atom = allocator.new_atom("unquote".as_bytes())?; - return enlist(allocator, &[unquote_atom, code]); + let loc = allocator.loc(code); + let unquote_atom = allocator.new_atom(loc, "unquote".as_bytes())?; + return enlist(allocator, &[unquote_atom, code.clone()]); } - Ok(code) + Ok(code.clone()) } - SExp::Pair(c1, c2) => { - m! { - unquoted_c2 <- unquote_args(allocator, c2, args, matches); - unquoted_c1 <- unquote_args(allocator, c1, args, matches); - allocator.new_pair(unquoted_c1, unquoted_c2) - } + ASExp::Pair(c1, c2) => { + let unquoted_c2 = unquote_args(allocator, &c2, args, matches)?; + let unquoted_c1 = unquote_args(allocator, &c1, args, matches)?; + let loc = allocator.loc(&c1); + allocator.new_pair(loc, &unquoted_c1, &unquoted_c2) } } } -fn defun_inline_to_macro( - allocator: &mut Allocator, - declaration_sexp: NodePtr, -) -> Result { +fn defun_inline_to_macro( + allocator: &mut A, + declaration_sexp: &A::NodePtr, +) -> Result +where + A::NodePtr: Clone +{ let Rest::Here(NodeSel::Cons(name, NodeSel::Cons(arg_spec, First::Here(code)))) = Rest::Here(NodeSel::Cons( ThisNode::Here, NodeSel::Cons(ThisNode::Here, First::Here(ThisNode::Here)), )) - .select_nodes(allocator, declaration_sexp)?; - let defmacro_atom = allocator.new_atom("defmacro".as_bytes())?; + .select_nodes(allocator, declaration_sexp.clone())?; + let loc = allocator.loc(declaration_sexp); + let defmacro_atom = allocator.new_atom(loc, "defmacro".as_bytes())?; let mut destructure_matches = HashMap::new(); - let use_args = if is_inline_destructure(allocator, arg_spec) { + let use_args = if is_inline_destructure(allocator, &arg_spec) { // Given an attempt to destructure via the argument list, we need // to ensure that the inline function receives arguments that are // relative to the _values_ given rather than the code given to // generate the arguments. These overlap when the argument list is // a single level proper list, but not otherwise. - formulate_path_selections_for_destructuring(allocator, arg_spec, &mut destructure_matches)? + formulate_path_selections_for_destructuring(allocator, &arg_spec, &mut destructure_matches)? } else { arg_spec }; - let mut r_vec = vec![defmacro_atom, name, use_args]; + let mut r_vec = vec![defmacro_atom, name, use_args.clone()]; let mut arg_atom_list = Vec::new(); - flatten(allocator, use_args, &mut arg_atom_list); + flatten(allocator, &use_args, &mut arg_atom_list); let arg_name_list = arg_atom_list .iter() .filter_map(|x| { - if let SExp::Atom = allocator.sexp(*x) { + if let ASExp::Atom = allocator.sexp(x) { // only x usefully in scope. - Some(allocator.atom(*x)) + Some(allocator.atom(x)) } else { None } @@ -298,9 +341,10 @@ fn defun_inline_to_macro( .map(|v| v.as_ref().to_vec()) .collect::>>(); - let unquoted_code = unquote_args(allocator, code, &arg_name_list, &destructure_matches)?; + let unquoted_code = unquote_args(allocator, &code, &arg_name_list, &destructure_matches)?; - let qq_atom = allocator.new_atom("qq".as_bytes())?; + let loc = allocator.loc(&unquoted_code); + let qq_atom = allocator.new_atom(loc, "qq".as_bytes())?; let qq_list = enlist(allocator, &[qq_atom, unquoted_code])?; r_vec.push(qq_list); let res = enlist(allocator, &r_vec)?; @@ -308,35 +352,38 @@ fn defun_inline_to_macro( } #[allow(clippy::too_many_arguments)] -fn parse_mod_sexp( - allocator: &mut Allocator, - declaration_sexp: NodePtr, +fn parse_mod_sexp( + allocator: &mut A, + declaration_sexp: &A::NodePtr, namespace: &mut HashSet>, - functions: &mut HashMap, NodePtr>, - constants: &mut HashMap, NodePtr>, + functions: &mut HashMap, A::NodePtr>, + constants: &mut HashMap, A::NodePtr>, // Delayed constants are new: they represent constant values // but we need the whole module to evaluate them since they // may call local functions (such as sha256tree). - delayed_constants: &mut HashMap, NodePtr>, - macros: &mut Vec<(Vec, NodePtr)>, + delayed_constants: &mut HashMap, A::NodePtr>, + macros: &mut Vec<(Vec, A::NodePtr)>, run_program: Rc, -) -> Result<(), EvalErr> { +) -> Result<(), ClError> +where + A::NodePtr: Clone +{ let NodeSel::Cons(op_node, First::Here(name_node)) = NodeSel::Cons(ThisNode::Here, First::Here(ThisNode::Here)) - .select_nodes(allocator, declaration_sexp)?; + .select_nodes(allocator, declaration_sexp.clone())?; - let op = match allocator.sexp(op_node) { + let op = match allocator.sexp(&op_node) { // op_node in use. - SExp::Atom => { - let atom = allocator.atom(op_node); + ASExp::Atom => { + let atom = allocator.atom(&op_node); atom.as_ref().to_vec() } _ => Vec::new(), }; - let name = match allocator.sexp(name_node) { + let name = match allocator.sexp(&name_node) { // name_node in use. - SExp::Atom => { - let atom = allocator.atom(name_node); + ASExp::Atom => { + let atom = allocator.atom(&name_node); atom.as_ref().to_vec() } _ => Vec::new(), @@ -345,7 +392,7 @@ fn parse_mod_sexp( if op == "include".as_bytes() { parse_include( allocator, - name_node, + &name_node, namespace, functions, constants, @@ -359,22 +406,29 @@ fn parse_mod_sexp( constants.insert(name, constant); Ok(()) } else if namespace.contains(&name) { - Err(EvalErr::InternalError( - declaration_sexp, - format!( - "symbol \"{}\" redefined", - Bytes::new(Some(BytesFromType::Raw(name))).decode() - ), - )) + let loc = allocator.loc(declaration_sexp); + let dec_export = allocator.export(declaration_sexp); + Err( + ClError( + loc, + EvalErr::InternalError( + dec_export, + format!( + "symbol \"{}\" redefined", + Bytes::new(Some(BytesFromType::Raw(name))).decode() + ), + ) + ) + ) } else { namespace.insert(name.to_vec()); if op == "defmacro".as_bytes() { - macros.push((name.to_vec(), declaration_sexp)); + macros.push((name.to_vec(), declaration_sexp.clone())); Ok(()) } else if op == "defun".as_bytes() { let Rest::Here(Rest::Here(declaration_sexp_rr)) = - Rest::Here(Rest::Here(ThisNode::Here)).select_nodes(allocator, declaration_sexp)?; + Rest::Here(Rest::Here(ThisNode::Here)).select_nodes(allocator, declaration_sexp.clone())?; functions.insert(name, declaration_sexp_rr); Ok(()) } else if op == "defun-inline".as_bytes() { @@ -384,33 +438,43 @@ fn parse_mod_sexp( } else if op == "defconstant".as_bytes() { let Rest::Here(Rest::Here(First::Here(frr_of_declaration))) = Rest::Here(Rest::Here(First::Here(ThisNode::Here))) - .select_nodes(allocator, declaration_sexp)?; - let quoted_decl = quote(allocator, frr_of_declaration)?; + .select_nodes(allocator, declaration_sexp.clone())?; + let quoted_decl = quote(allocator, &frr_of_declaration)?; constants.insert(name, quoted_decl); Ok(()) } else if op == "defconst".as_bytes() { // Use a new type-based match language. let Rest::Here(Rest::Here(First::Here(definition))) = Rest::Here(Rest::Here(First::Here(ThisNode::Here))) - .select_nodes(allocator, declaration_sexp)?; + .select_nodes(allocator, declaration_sexp.clone())?; delayed_constants.insert(name, definition); Ok(()) } else { - Err(EvalErr::InternalError( - declaration_sexp, - "expected defun, defmacro, defconst, compile-file or defconstant".to_string(), - )) + let loc = allocator.loc(declaration_sexp); + let export_sexp = allocator.export(declaration_sexp); + Err( + ClError( + loc, + EvalErr::InternalError( + export_sexp, + "expected defun, defmacro, defconst, compile-file or defconstant".to_string(), + ) + ) + ) } } } -fn compile_mod_stage_1( - allocator: &mut Allocator, - args: NodePtr, - macro_lookup: NodePtr, +fn compile_mod_stage_1( + allocator: &mut A, + args: &A::NodePtr, + macro_lookup: &A::NodePtr, run_program: Rc, produce_extra_info: bool, -) -> Result { +) -> Result, ClError> +where + A::NodePtr: Clone +{ // stage 1: collect up names of globals (functions, constants, macros) m! { let mut functions = HashMap::new(); @@ -420,19 +484,34 @@ fn compile_mod_stage_1( let mut namespace = HashSet::new(); // eslint-disable-next-line no-constant-condition - match proper_list(allocator, args, true) { - None => { Err(EvalErr::InternalError(args, "miscompiled mod is not a proper list\n".to_string())) }, + let loc = allocator.loc(args); + match proper_list(allocator, &args, true) { + None => { + let export_args = allocator.export(args); + Err( + ClError( + loc, + EvalErr::InternalError(export_args, "miscompiled mod is not a proper list\n".to_string()) + ) + ) + } Some(alist) => { if alist.is_empty() { - return Err(EvalErr::InternalError(args, "miscompiled mod is 0 size\n".to_string())); + let export_args = allocator.export(args); + return Err( + ClError( + loc, + EvalErr::InternalError(export_args, "miscompiled mod is 0 size\n".to_string()) + ) + ); } - let main_local_arguments = alist[0]; + let main_local_arguments = alist[0].clone(); for arg in alist.iter().take(alist.len()-1).skip(1) { parse_mod_sexp( allocator, - *arg, + arg, &mut namespace, &mut functions, &mut constants, @@ -462,14 +541,15 @@ fn compile_mod_stage_1( let mut processed = false; // copy so we can modify delayed_constants. - let delayed_constant_defs: Vec<(Vec, NodePtr)> = - delayed_constants.iter().map(|(k,v)| (k.clone(), *v)).collect(); + let delayed_constant_defs: Vec<(Vec, A::NodePtr)> = + delayed_constants.iter().map(|(k,v)| (k.clone(), v.clone())).collect(); + let nil_import = allocator.import(Srcloc::start("*nil*"), NodePtr::NIL)?; for (name, delayed_body) in delayed_constant_defs.iter() { let main_list = enlist( allocator, - &[NodePtr::NIL, *delayed_body] + &[nil_import.clone(), delayed_body.clone()] )?; result_collection.functions.insert( @@ -502,34 +582,48 @@ fn compile_mod_stage_1( produce_extra_info )?; + let compiled_export = allocator.export(&compiled); + let loc = allocator.loc(&compiled); let compilation_result = run_program.run_program( - allocator, - compiled, + allocator.allocator(), + compiled_export, NodePtr::NIL, None - )?; + ).map_err(|e| { + ClError(loc.clone(), e) + })?; let result = run_program.run_program( - allocator, + allocator.allocator(), compilation_result.1, NodePtr::NIL, None - )?; + ).map_err(|e| { + ClError(loc.clone(), e) + })?; + let result_imp = allocator.import(loc, result.1)?; delayed_constants.remove(name); result_collection.constants.insert( - name.to_vec(), quote(allocator, result.1)? + name.to_vec(), quote(allocator, &result_imp)? ); } if !processed { - return Err(EvalErr::InternalError(NodePtr::NIL, "got stuck untangling defconst dependencies".to_string())); + return Err( + ClError( + loc, + EvalErr::InternalError(NodePtr::NIL, "got stuck untangling defconst dependencies".to_string()) + ) + ); } } - let uncompiled_main = nonempty_last(NodePtr::NIL, &alist)?; + let uncompiled_main = nonempty_last(NodePtr::NIL, &alist).map_err(|e| { + ClError(loc, e) + })?; let main_list = enlist( allocator, @@ -548,41 +642,44 @@ fn compile_mod_stage_1( // export type TSymbolTable = Array<[SExp, Bytes]>; -fn symbol_table_for_tree( - allocator: &mut Allocator, - tree: NodePtr, +fn symbol_table_for_tree( + allocator: &mut A, + tree: &A::NodePtr, root_node: &NodePath, -) -> Result)>, EvalErr> { - if !non_nil(allocator, tree) { +) -> Result)>, ClError> +where + A::NodePtr: Clone +{ + if allocator.is_nil(tree) { return Ok(Vec::new()); } match allocator.sexp(tree) { - SExp::Atom => Ok(vec![(tree, root_node.as_path().data().to_vec())]), - SExp::Pair(_, _) => { + ASExp::Atom => Ok(vec![(tree.clone(), root_node.as_path().data().to_vec())]), + ASExp::Pair(_, _) => { let left_bytes = NodePath::new(None).first(); let right_bytes = NodePath::new(None).rest(); let NodeSel::Cons(tree_first, tree_rest) = - NodeSel::Cons(ThisNode::Here, ThisNode::Here).select_nodes(allocator, tree)?; + NodeSel::Cons(ThisNode::Here, ThisNode::Here).select_nodes(allocator, tree.clone())?; // Allow haskell-like @ capture for destructuring. // If we encounter a form like (@ name substructure) then // treat it as though name captures the current path but // we also continue evaluating at the current position. let mut result_fin = Vec::new(); - if let Some((capture, destructure)) = is_at_capture(allocator, tree_first, tree_rest) { + if let Some((capture, destructure)) = is_at_capture(allocator, &tree_first, &tree_rest) { // Push the given name here. result_fin.push((capture, root_node.as_path().data().to_vec())); - let mut substructure = symbol_table_for_tree(allocator, destructure, root_node)?; + let mut substructure = symbol_table_for_tree(allocator, &destructure, root_node)?; result_fin.append(&mut substructure); } else { let mut left = - symbol_table_for_tree(allocator, tree_first, &root_node.add(left_bytes))?; + symbol_table_for_tree(allocator, &tree_first, &root_node.add(left_bytes))?; let mut right = - symbol_table_for_tree(allocator, tree_rest, &root_node.add(right_bytes))?; + symbol_table_for_tree(allocator, &tree_rest, &root_node.add(right_bytes))?; result_fin.append(&mut left); result_fin.append(&mut right); @@ -593,80 +690,87 @@ fn symbol_table_for_tree( } } -fn build_macro_lookup_program( - allocator: &mut Allocator, - macro_lookup: NodePtr, - macros: &[(Vec, NodePtr)], +fn build_macro_lookup_program( + allocator: &mut A, + macro_lookup: &A::NodePtr, + macros: &[(Vec, A::NodePtr)], run_program: Rc, -) -> Result { - m! { - com_atom <- allocator.new_atom("com".as_bytes()); - cons_atom <- allocator.new_atom(&[4]); - opt_atom <- allocator.new_atom("opt".as_bytes()); - - let runner = || run_program.clone(); - macro_lookup_program <- quote(allocator, macro_lookup); - result_program <- fold_m( - allocator, - &|allocator, macro_lookup_program, macro_def: &(Vec, NodePtr)| m! { - cons_list <- - enlist( - allocator, - &[cons_atom, macro_def.1, macro_lookup_program] - ); - quoted_to_compile <- quote(allocator, cons_list); - compile_form <- - enlist( - allocator, - &[com_atom, quoted_to_compile, macro_lookup_program] - ); - opt_form <- enlist(allocator, &[opt_atom, compile_form]); - top_atom <- allocator.new_atom(NodePath::new(None).as_path().data()); - macro_evaluated <- evaluate(allocator, opt_form, top_atom); - optimize_sexp(allocator, macro_evaluated, runner()) - }, - macro_lookup_program, - &mut macros.iter() - ); - Ok(result_program) - } +) -> Result +where + A::NodePtr: Clone +{ + let loc = allocator.loc(macro_lookup); + let com_atom = allocator.new_atom(loc.clone(), "com".as_bytes())?; + let cons_atom = allocator.new_atom(loc.clone(), &[4])?; + let opt_atom = allocator.new_atom(loc.clone(), "opt".as_bytes())?; + + let runner = || run_program.clone(); + let macro_lookup_program = quote(allocator, ¯o_lookup)?; + fold_m( + allocator, + &|allocator, macro_lookup_program, macro_def: &(Vec, A::NodePtr)| { + let cons_list = + enlist( + allocator, + &[cons_atom.clone(), macro_def.1.clone(), macro_lookup_program.clone()] + )?; + let quoted_to_compile = quote(allocator, &cons_list)?; + let compile_form = + enlist( + allocator, + &[com_atom.clone(), quoted_to_compile, macro_lookup_program] + )?; + let opt_form = enlist(allocator, &[opt_atom.clone(), compile_form])?; + let loc = allocator.loc(&opt_form); + let top_atom = allocator.new_atom(loc, NodePath::new(None).as_path().data())?; + let macro_evaluated = evaluate(allocator, &opt_form, &top_atom)?; + optimize_sexp(allocator, ¯o_evaluated, runner()) + }, + macro_lookup_program, + &mut macros.iter() + ) } #[allow(clippy::too_many_arguments)] -fn add_one_function( - allocator: &mut Allocator, +fn add_one_function( + allocator: &mut A, args_root_node: &NodePath, - macro_lookup_program: NodePtr, - constants_symbol_table: &[(NodePtr, Vec)], + macro_lookup_program: &A::NodePtr, + constants_symbol_table: &[(A::NodePtr, Vec)], name: &[u8], - lambda_expression: NodePtr, + lambda_expression: &A::NodePtr, has_constants_tree: bool, -) -> Result { - let mut compile: CompileOutput = Default::default(); - let com_atom = allocator.new_atom("com".as_bytes())?; - let opt_atom = allocator.new_atom("opt".as_bytes())?; - - let function_args = first(allocator, lambda_expression)?; - let local_symbol_table = symbol_table_for_tree(allocator, function_args, args_root_node)?; +) -> Result, ClError> +where + A::NodePtr: Clone, +{ + let mut compile: CompileOutput = Default::default(); + let loc = allocator.loc(lambda_expression); + let com_atom = allocator.new_atom(loc.clone(), "com".as_bytes())?; + let opt_atom = allocator.new_atom(loc.clone(), "opt".as_bytes())?; + + let function_args = first(allocator, &lambda_expression)?; + let local_symbol_table = symbol_table_for_tree(allocator, &function_args, args_root_node)?; let mut all_symbols = local_symbol_table; all_symbols.append(&mut constants_symbol_table.to_owned()); - let lambda_form_content = rest(allocator, lambda_expression)?; - let lambda_body = first(allocator, lambda_form_content)?; - let quoted_lambda_expr = quote(allocator, lambda_body)?; + let lambda_form_content = rest(allocator, &lambda_expression)?; + let lambda_body = first(allocator, &lambda_form_content)?; + let quoted_lambda_expr = quote(allocator, &lambda_body)?; let all_symbols_list_sexp = map_m(allocator, &mut all_symbols.iter(), &|allocator, pair| { - let path_atom = allocator.new_atom(&pair.1)?; - enlist(allocator, &[pair.0, path_atom]) + let loc = allocator.loc(&lambda_body); + let path_atom = allocator.new_atom(loc, &pair.1)?; + enlist(allocator, &[pair.0.clone(), path_atom]) })?; let all_symbols_list = enlist(allocator, &all_symbols_list_sexp)?; - let quoted_symbols = quote(allocator, all_symbols_list)?; + let quoted_symbols = quote(allocator, &all_symbols_list)?; let com_list = enlist( allocator, &[ com_atom, quoted_lambda_expr, - macro_lookup_program, + macro_lookup_program.clone(), quoted_symbols, ], )?; @@ -684,15 +788,18 @@ fn add_one_function( Ok(compile) } -fn compile_functions( - allocator: &mut Allocator, - functions: &HashMap, NodePtr>, - macro_lookup_program: NodePtr, - constants_symbol_table: &[(NodePtr, Vec)], +fn compile_functions( + allocator: &mut A, + functions: &HashMap, A::NodePtr>, + macro_lookup_program: &A::NodePtr, + constants_symbol_table: &[(A::NodePtr, Vec)], args_root_node: &NodePath, has_constants_tree: bool, -) -> Result { - let mut compiled: CompileOutput = Default::default(); +) -> Result, ClError> +where + A::NodePtr: Clone +{ + let mut compiled: CompileOutput = Default::default(); for (name, exp) in functions.iter() { compiled.add_definitions(&add_one_function( @@ -701,7 +808,7 @@ fn compile_functions( macro_lookup_program, constants_symbol_table, name, - *exp, + exp, has_constants_tree, )?); } @@ -711,29 +818,36 @@ fn compile_functions( // Add an entry for main's arguments, named __chia__main_arguments in the // symbols, to the symbol list, placing it at the front for simplicity. -fn add_main_args( - allocator: &mut Allocator, - args: NodePtr, - symbols: NodePtr, -) -> Result { - let entry_name = allocator.new_atom("__chia__main_arguments".as_bytes())?; - let entry_value_string = disassemble(allocator, args, None); - let entry_value = allocator.new_atom(entry_value_string.as_bytes())?; - let entry_cons = allocator.new_pair(entry_name, entry_value)?; - allocator.new_pair(entry_cons, symbols) +fn add_main_args( + allocator: &mut A, + args: &A::NodePtr, + symbols: &A::NodePtr, +) -> Result { + let entry_value_loc = allocator.loc(args); + let entry_name = allocator.new_atom(entry_value_loc.clone(), "__chia__main_arguments".as_bytes())?; + let entry_value_string = allocator.disassemble(args, None); + let entry_value = allocator.new_atom(entry_value_loc, entry_value_string.as_bytes())?; + let entry_cons_loc = allocator.loc(args); + let entry_cons = allocator.new_pair(entry_cons_loc, &entry_name, &entry_value)?; + let sym_loc = allocator.loc(symbols); + allocator.new_pair(sym_loc, &entry_cons, symbols) } -fn finish_compile_from_collection( - allocator: &mut Allocator, - args: NodePtr, - macro_lookup: NodePtr, +fn finish_compile_from_collection( + allocator: &mut A, + args: &A::NodePtr, + macro_lookup: &A::NodePtr, run_program: Rc, - cr: &CollectionResult, + cr: &CollectionResult, produce_extra_info: bool, -) -> Result { - let a_atom = allocator.new_atom(&[2])?; - let cons_atom = allocator.new_atom(&[4])?; - let opt_atom = allocator.new_atom("opt".as_bytes())?; +) -> Result +where + A::NodePtr: Clone +{ + let loc = allocator.loc(macro_lookup); + let a_atom = allocator.new_atom(loc.clone(), &[2])?; + let cons_atom = allocator.new_atom(loc.clone(), &[4])?; + let opt_atom = allocator.new_atom(loc.clone(), "opt".as_bytes())?; // move macros into the macro lookup let macro_lookup_program = @@ -756,18 +870,18 @@ fn finish_compile_from_collection( }; let constants_symbol_table = - symbol_table_for_tree(allocator, constants_tree, &constants_root_node)?; + symbol_table_for_tree(allocator, &constants_tree, &constants_root_node)?; let compiled = compile_functions( allocator, &cr.functions, - macro_lookup_program, + ¯o_lookup_program, &constants_symbol_table, &args_root_node, has_constants_tree, )?; - let main_path = compiled.functions[MAIN_NAME.as_bytes()]; + let main_path = compiled.functions[MAIN_NAME.as_bytes()].clone(); if has_constants_tree { let mut all_constants_lookup = HashMap::new(); @@ -778,17 +892,18 @@ fn finish_compile_from_collection( } for (k, v) in cr.constants.iter() { - all_constants_lookup.insert(k.to_vec(), *v); + all_constants_lookup.insert(k.to_vec(), v.clone()); } let all_constants_list = all_constants_names .iter() .filter_map(|name| all_constants_lookup.get(name)) - .copied() - .collect::>(); + .cloned() + .collect::>(); let all_constants_tree_program = build_tree_program(allocator, &all_constants_list)?; - let top_atom = allocator.new_atom(NodePath::new(None).as_path().data())?; + let loc = allocator.loc(macro_lookup); + let top_atom = allocator.new_atom(loc.clone(), NodePath::new(None).as_path().data())?; let arg_tree = enlist( allocator, &[cons_atom, all_constants_tree_program, top_atom], @@ -796,7 +911,7 @@ fn finish_compile_from_collection( let apply_list = enlist(allocator, &[a_atom, main_path, arg_tree])?; - let quoted_apply_list = quote(allocator, apply_list)?; + let quoted_apply_list = quote(allocator, &apply_list)?; let opt_list = enlist(allocator, &[opt_atom, quoted_apply_list])?; let symbols_no_main = build_symbol_dump( allocator, @@ -807,49 +922,60 @@ fn finish_compile_from_collection( )?; let first_of_args = first(allocator, args)?; let symbols = if produce_extra_info { - add_main_args(allocator, first_of_args, symbols_no_main)? + add_main_args(allocator, &first_of_args, &symbols_no_main)? } else { symbols_no_main }; let to_run = assemble( - allocator, + allocator.allocator(), if produce_extra_info { "(_set_symbol_table (c (c (q . \"source_file\") (_get_source_file)) 1))" } else { "(_set_symbol_table 1)" }, - )?; + ).map_err(|e| ClError(loc.clone(), e))?; - run_program.run_program(allocator, to_run, symbols, None)?; + let exported_symbols = allocator.export(&symbols); + run_program.run_program( + allocator.allocator(), + to_run, + exported_symbols, + None + ).map_err(|e| ClError(loc, e))?; Ok(opt_list) } else { - let top_atom = allocator.new_atom(NodePath::new(None).as_path().data())?; + let top_atom = allocator.new_atom(loc, NodePath::new(None).as_path().data())?; let apply_list = enlist(allocator, &[a_atom, main_path, top_atom])?; - let quoted_apply_list = quote(allocator, apply_list)?; + let quoted_apply_list = quote(allocator, &apply_list)?; enlist(allocator, &[opt_atom, quoted_apply_list]) } } -pub fn compile_mod( - allocator: &mut Allocator, - args: NodePtr, - macro_lookup: NodePtr, - _symbol_table: NodePtr, +pub fn compile_mod( + allocator: &mut A, + args: &A::NodePtr, + macro_lookup: &A::NodePtr, + _symbol_table: &A::NodePtr, run_program: Rc, _level: usize, -) -> Result { +) -> Result +where + A::NodePtr: Clone +{ // Deal with the "mod" keyword. - let produce_extra_info_prog = assemble(allocator, "(_symbols_extra_info)")?; + let loc = allocator.loc(macro_lookup); + let produce_extra_info_prog = assemble(allocator.allocator(), "(_symbols_extra_info)").map_err(|e| ClError(loc.clone(), e))?; let produce_extra_info_null = NodePtr::NIL; let extra_info_res = run_program.run_program( - allocator, + allocator.allocator(), produce_extra_info_prog, produce_extra_info_null, None, - )?; - let produce_extra_info = non_nil(allocator, extra_info_res.1); + ).map_err(|e| ClError(loc.clone(), e))?; + let imported_extra_info = allocator.import(loc, extra_info_res.1)?; + let produce_extra_info = !allocator.is_nil(&imported_extra_info); let cr = compile_mod_stage_1( allocator, diff --git a/src/classic/clvm_tools/stages/stage_2/operators.rs b/src/classic/clvm_tools/stages/stage_2/operators.rs index 420caa1e8..351d0832b 100644 --- a/src/classic/clvm_tools/stages/stage_2/operators.rs +++ b/src/classic/clvm_tools/stages/stage_2/operators.rs @@ -25,6 +25,7 @@ use crate::classic::clvm_tools::sha256tree::TreeHash; use crate::classic::clvm_tools::stages::stage_0::{ DefaultProgramRunner, OriginalDialect, RunProgramOption, TRunProgram, }; +use crate::classic::clvm_tools::stages::stage_2::abstraction::{ClassicAllocator, ClError}; use crate::classic::clvm_tools::stages::stage_2::compile::do_com_prog_for_dialect; use crate::classic::clvm_tools::stages::stage_2::optimize::do_optimize; @@ -66,11 +67,14 @@ pub struct CompilerOperatorsInternal { /// is given. If the file can't be found in any search path, use the expression /// the user gave to cause the file to be searched for in the error result. /// They're searched in order so repetition doesn't do anything. (suggested Q+A) -pub fn full_path_for_filename( - parent_sexp: NodePtr, +pub fn full_path_for_filename( + allocator: &A, + parent_sexp: &A::NodePtr, filename: &str, search_paths: &[String], -) -> Result { +) -> Result { + let loc = allocator.loc(parent_sexp); + let exported = allocator.export(parent_sexp); if filename.starts_with("*") { return Ok(filename.to_string()); }; @@ -85,18 +89,28 @@ pub fn full_path_for_filename( .map(|x| x.to_owned()) .map(Ok) .unwrap_or_else(|| { - Err(EvalErr::InternalError( - parent_sexp, - format!("could not compute absolute path for the combination of search path {path} and file name {filename} during text conversion from path_buf") - )) + Err( + ClError( + loc, + EvalErr::InternalError( + exported, + format!("could not compute absolute path for the combination of search path {path} and file name {filename} during text conversion from path_buf") + ) + ) + ) }); } } - Err(EvalErr::InternalError( - parent_sexp, - "can't open file".to_string(), - )) + Err( + ClError( + loc, + EvalErr::InternalError( + exported, + "can't open file".to_string(), + ) + ) + ) } pub struct CompilerOperators { @@ -327,7 +341,7 @@ impl CompilerOperatorsInternal { return convert_filename(allocator, &filename); } - let full_name = full_path_for_filename(sexp, &filename, &self.search_paths)?; + let full_name = full_path_for_filename(allocator, &sexp, &filename, &self.search_paths)?; return convert_filename(allocator, &full_name); } } @@ -347,7 +361,7 @@ impl CompilerOperatorsInternal { table: NodePtr, ) -> Result { if let Some(symtable) = - proper_list(allocator, table, true).and_then(|t| proper_list(allocator, t[0], true)) + proper_list(allocator, &table, true).and_then(|t| proper_list(allocator, &t[0], true)) { for kv in symtable.iter() { if let SExp::Pair(hash, name) = allocator.sexp(*kv) { @@ -440,9 +454,11 @@ impl Dialect for CompilerOperatorsInternal { } else if opbuf == b"_write" { self.write(allocator, sexp) } else if opbuf == b"com" { - do_com_prog_for_dialect(self.get_runner(), allocator, sexp) + let result = do_com_prog_for_dialect(self.get_runner(), allocator, &sexp)?; + Ok(Reduction(1, result)) } else if opbuf == b"opt" { - do_optimize(self.get_runner(), allocator, &self.opt_memo, sexp) + let result = do_optimize(self.get_runner(), allocator, &self.opt_memo, &sexp)?; + Ok(Reduction(1, result)) } else if opbuf == b"_set_symbol_table" { self.set_symbol_table(allocator, sexp) } else if opbuf == b"_get_compile_filename" { diff --git a/src/classic/clvm_tools/stages/stage_2/optimize.rs b/src/classic/clvm_tools/stages/stage_2/optimize.rs index b9b054795..a0f8b665d 100644 --- a/src/classic/clvm_tools/stages/stage_2/optimize.rs +++ b/src/classic/clvm_tools/stages/stage_2/optimize.rs @@ -6,21 +6,21 @@ use std::rc::Rc; use clvm_rs::error::EvalErr; use num_bigint::ToBigInt; -use clvm_rs::allocator::{Allocator, NodePtr, SExp}; +use clvm_rs::allocator::{NodePtr}; use clvm_rs::cost::Cost; -use clvm_rs::reduction::{Reduction, Response}; use crate::classic::clvm::__type_compatibility__::{bi_one, bi_zero}; use crate::classic::clvm::sexp::{ - atom, enlist, equal_to, first, fold_m, map_m, non_nil, proper_list, + enlist, equal_to, first, fold_m, map_m, proper_list, }; -use crate::classic::clvm_tools::binutils::disassemble; use crate::classic::clvm_tools::node_path::NodePath; use crate::classic::clvm_tools::pattern_match::match_sexp; use crate::classic::clvm_tools::stages::assemble; use crate::classic::clvm_tools::stages::stage_0::TRunProgram; +use crate::classic::clvm_tools::stages::stage_2::abstraction::{ASExp, BufCarrier, ClassicAllocator, ClError}; use crate::classic::clvm_tools::stages::stage_2::helpers::quote; use crate::classic::clvm_tools::stages::stage_2::operators::AllocatorRefOrTreeHash; +use crate::compiler::srcloc::Srcloc; use crate::util::{number_from_u8, u8_from_number}; @@ -30,43 +30,43 @@ pub struct DoOptProg {} const DEBUG_OPTIMIZATIONS: bool = false; const DIAG_OPTIMIZATIONS: bool = false; -pub fn seems_constant_tail(allocator: &mut Allocator, sexp_: NodePtr) -> bool { +fn seems_constant_tail(allocator: &mut A, sexp_: A::NodePtr) -> bool { let mut sexp = sexp_; loop { - match allocator.sexp(sexp) { - SExp::Pair(l, r) => { - if !seems_constant(allocator, l) { + match allocator.sexp(&sexp) { + ASExp::Pair(l, r) => { + if !seems_constant_tail(allocator, l) { return false; } sexp = r; } - SExp::Atom => { - return sexp == NodePtr::NIL; + ASExp::Atom => { + return allocator.is_nil(&sexp); } } } } -pub fn seems_constant(allocator: &mut Allocator, sexp: NodePtr) -> bool { - match allocator.sexp(sexp) { - SExp::Atom => { - return sexp == NodePtr::NIL; +fn seems_constant(allocator: &mut A, sexp: &A::NodePtr) -> bool { + match allocator.sexp(&sexp) { + ASExp::Atom => { + return allocator.is_nil(&sexp); } - SExp::Pair(operator, r) => { - match allocator.sexp(operator) { - SExp::Atom => { + ASExp::Pair(operator, r) => { + match allocator.sexp(&operator) { + ASExp::Atom => { // Was buf of operator. - let atom = allocator.atom(operator); + let atom = allocator.atom(&operator); if atom.as_ref().len() == 1 && atom.as_ref()[0] == 1 { return true; } else if atom.as_ref().len() == 1 && atom.as_ref()[0] == 8 { return false; } } - SExp::Pair(_, _) => { - if !seems_constant(allocator, operator) { + ASExp::Pair(_, _) => { + if !seems_constant(allocator, &operator) { return false; } } @@ -80,65 +80,66 @@ pub fn seems_constant(allocator: &mut Allocator, sexp: NodePtr) -> bool { true } -pub fn constant_optimizer( - allocator: &mut Allocator, +pub fn constant_optimizer( + allocator: &mut A, _memo: &RefCell>, - r: NodePtr, + r: &A::NodePtr, _max_cost: Cost, runner: Rc, -) -> Result { +) -> Result +where + A::NodePtr: Clone +{ /* * If the expression does not depend upon @ anywhere, * it's a constant. So we can simply evaluate it and * return the quoted result. */ - if let SExp::Pair(first, _) = allocator.sexp(r) { + if let ASExp::Pair(first, _) = allocator.sexp(&r) { // first relevant in scope. - if let SExp::Atom = allocator.sexp(first) { - let buf = allocator.atom(first); + if let ASExp::Atom = allocator.sexp(&first) { + let buf = allocator.atom(&first); if buf.as_ref().len() == 1 && buf.as_ref()[0] == 1 { // Short circuit already quoted expression. - return Ok(r); + return Ok(r.clone()); } } } - let sc_r = seems_constant(allocator, r); - let nn_r = non_nil(allocator, r); + let sc_r = seems_constant(allocator, &r); + let nn_r = !allocator.is_nil(&r); if DIAG_OPTIMIZATIONS { println!( "COPT {} SC_R {} NN_R {}", - disassemble(allocator, r, None), + allocator.disassemble(&r, None), sc_r, nn_r ); } if sc_r && nn_r { - return m! { - res <- runner.run_program( - allocator, - r, - NodePtr::NIL, - None + let r_export = allocator.export(&r); + let res = runner.run_program( + allocator.allocator(), + r_export, + NodePtr::NIL, + None + ).map_err(|e| ClError(allocator.loc(&r), e))?; + let r1 = allocator.import(allocator.loc(&r), res.1)?; + let _ = if DIAG_OPTIMIZATIONS { + println!( + "CONSTANT_OPTIMIZER {} TO {}", + allocator.disassemble(&r, None), + allocator.disassemble(&r1, None) ); - let r1 = res.1; - let _ = if DIAG_OPTIMIZATIONS { - println!( - "CONSTANT_OPTIMIZER {} TO {}", - disassemble(allocator, r, None), - disassemble(allocator, r1, None) - ); - }; - quoted <- quote(allocator, r1); - Ok(quoted) }; + return quote(allocator, &r1); } - Ok(r) + Ok(r.clone()) } -pub fn is_args_call(allocator: &Allocator, r: NodePtr) -> bool { - if let SExp::Atom = allocator.sexp(r) { +pub fn is_args_call(allocator: &A, r: &A::NodePtr) -> bool { + if let ASExp::Atom = allocator.sexp(r) { // Only r in scope. let buf = allocator.atom(r); buf.as_ref().len() == 1 && buf.as_ref()[0] == 1 @@ -147,16 +148,20 @@ pub fn is_args_call(allocator: &Allocator, r: NodePtr) -> bool { } } -pub fn cons_q_a_optimizer_pattern(allocator: &mut Allocator) -> NodePtr { - assemble(allocator, "(a (q . (: . sexp)) (: . args))").unwrap() +pub fn cons_q_a_optimizer_pattern(allocator: &mut A) -> A::NodePtr { + let assembled = assemble(allocator.allocator(), "(a (q . (: . sexp)) (: . args))").unwrap(); + allocator.import(Srcloc::start("*cons_q_a_optimizer_pattern*"), assembled).unwrap() } -pub fn cons_q_a_optimizer( - allocator: &mut Allocator, +pub fn cons_q_a_optimizer( + allocator: &mut A, _memo: &RefCell>, - r: NodePtr, + r: &A::NodePtr, _eval_f: Rc, -) -> Result { +) -> Result +where + A::NodePtr: Clone +{ let cons_q_a_optimizer_pattern = cons_q_a_optimizer_pattern(allocator); /* @@ -164,120 +169,133 @@ pub fn cons_q_a_optimizer( * (a (q . SEXP) @) => SEXP */ - let matched = match_sexp(allocator, cons_q_a_optimizer_pattern, r, HashMap::new()); + let matched = match_sexp(allocator, &cons_q_a_optimizer_pattern, &r, HashMap::new()); match ( - matched.as_ref().and_then(|t1| t1.get("args").copied()), - matched.as_ref().and_then(|t1| t1.get("sexp").copied()), + matched.as_ref().and_then(|t1| t1.get("args").cloned()), + matched.as_ref().and_then(|t1| t1.get("sexp").cloned()), ) { (Some(args), Some(sexp)) => { - if is_args_call(allocator, args) { + if is_args_call(allocator, &args) { Ok(sexp) } else { - Ok(r) + Ok(r.clone()) } } - _ => Ok(r), + _ => Ok(r.clone()), } } -fn cons_pattern(allocator: &mut Allocator) -> NodePtr { - assemble(allocator, "(c (: . first) (: . rest)))").unwrap() +fn cons_pattern(allocator: &mut A) -> A::NodePtr { + let assembled = assemble(allocator.allocator(), "(c (: . first) (: . rest)))").unwrap(); + allocator.import(Srcloc::start("*cons_pattern*"), assembled).unwrap() } -fn cons_f(allocator: &mut Allocator, args: NodePtr) -> Result { - m! { - let cons_pattern = cons_pattern(allocator); - if let Some(first) = match_sexp(allocator, cons_pattern, args, HashMap::new()).and_then(|t| t.get("first").copied()) { - Ok(first) - } else { - m! { - first_atom <- allocator.new_atom(&[5]); - tail <- allocator.new_pair(args, NodePtr::NIL); - allocator.new_pair(first_atom, tail) - } - } +fn cons_f(allocator: &mut A, args: &A::NodePtr) -> Result +where + A::NodePtr: Clone +{ + let cons_pattern = cons_pattern(allocator); + let pair_loc = allocator.loc(&args); + if let Some(first) = match_sexp(allocator, &cons_pattern, &args, HashMap::new()).and_then(|t| t.get("first").cloned()) { + Ok(first) + } else { + let first_loc = allocator.loc(&args); + let first_atom = allocator.new_atom(first_loc.clone(), &[5])?; + let nil = allocator.import(first_loc, NodePtr::NIL)?; + let tail = allocator.new_pair(pair_loc.clone(), args, &nil)?; + allocator.new_pair(pair_loc, &first_atom, &tail) } } -fn cons_r(allocator: &mut Allocator, args: NodePtr) -> Result { - m! { - let cons_pattern = cons_pattern(allocator); - if let Some(rest) = match_sexp(allocator, cons_pattern, args, HashMap::new()).and_then(|t| t.get("rest").copied()) { - Ok(rest) - } else { - m! { - rest_atom <- allocator.new_atom(&[6]); - tail <- allocator.new_pair(args, NodePtr::NIL); - allocator.new_pair(rest_atom, tail) - } - } +fn cons_r(allocator: &mut A, args: &A::NodePtr) -> Result +where + A::NodePtr: Clone +{ + let cons_pattern = cons_pattern(allocator); + let pair_loc = allocator.loc(&args); + if let Some(rest) = match_sexp(allocator, &cons_pattern, &args, HashMap::new()).and_then(|t| t.get("rest").cloned()) { + Ok(rest) + } else { + let rest_loc = allocator.loc(&args); + let rest_atom = allocator.new_atom(rest_loc.clone(), &[6])?; + let nil = allocator.import(rest_loc, NodePtr::NIL)?; + let tail = allocator.new_pair(pair_loc.clone(), args, &nil)?; + allocator.new_pair(pair_loc, &rest_atom, &tail) } } -fn path_from_args( - allocator: &mut Allocator, - sexp: NodePtr, - new_args: NodePtr, -) -> Result { - match allocator.sexp(sexp) { - SExp::Atom => { +fn path_from_args( + allocator: &mut A, + sexp: &A::NodePtr, + new_args: &A::NodePtr, +) -> Result +where + A::NodePtr: Clone +{ + match allocator.sexp(&sexp) { + ASExp::Atom => { // Only sexp in scope. let atom = allocator.atom(sexp); let v = number_from_u8(atom.as_ref()); if v <= bi_one() { - Ok(new_args) + Ok(new_args.clone()) } else { - let sexp = allocator.new_atom(&u8_from_number(v.clone() >> 1).to_vec())?; + let loc = allocator.loc(&sexp); + let sexp = allocator.new_atom(loc, &u8_from_number(v.clone() >> 1).to_vec())?; if (v & 1_u32.to_bigint().unwrap()) != bi_zero() { - let cons_r_res = cons_r(allocator, new_args)?; - path_from_args(allocator, sexp, cons_r_res) + let cons_r_res = cons_r(allocator, &new_args)?; + path_from_args(allocator, &sexp, &cons_r_res) } else { - let cons_f_res = cons_f(allocator, new_args)?; - path_from_args(allocator, sexp, cons_f_res) + let cons_f_res = cons_f(allocator, &new_args)?; + path_from_args(allocator, &sexp, &cons_f_res) } } } - _ => Ok(new_args), + _ => Ok(new_args.clone()), } } -pub fn sub_args( - allocator: &mut Allocator, - sexp: NodePtr, - new_args: NodePtr, -) -> Result { +pub fn sub_args( + allocator: &mut A, + sexp: &A::NodePtr, + new_args: &A::NodePtr, +) -> Result +where + A::NodePtr: Clone +{ match allocator.sexp(sexp) { - SExp::Atom => path_from_args(allocator, sexp, new_args), - SExp::Pair(first_pre, rest) => { + ASExp::Atom => path_from_args(allocator, &sexp, &new_args), + ASExp::Pair(first_pre, rest) => { let first; - match allocator.sexp(first_pre) { - SExp::Pair(_, _) => { - first = sub_args(allocator, first_pre, new_args)?; + match allocator.sexp(&first_pre) { + ASExp::Pair(_, _) => { + first = sub_args(allocator, &first_pre, &new_args)?; } - SExp::Atom => { + ASExp::Atom => { // Atom is a reflection of first_pre. - let atom = allocator.atom(first_pre); + let atom = allocator.atom(&first_pre); if atom.as_ref().len() == 1 && atom.as_ref()[0] == 1 { - return Ok(sexp); + return Ok(sexp.clone()); } else { first = first_pre; } } } - match proper_list(allocator, rest, true) { - Some(tail_args) => m! { - res <- map_m( + match proper_list(allocator, &rest, true) { + Some(tail_args) => { + let res = map_m( allocator, &mut tail_args.iter(), &|allocator, elt| { - sub_args(allocator, *elt, new_args) + Ok(sub_args(allocator, elt, new_args)?) } - ); - tail_list <- enlist(allocator, &res); - allocator.new_pair(first, tail_list) + )?; + let tail_list = enlist(allocator, &res)?; + let first_loc = allocator.loc(&first); + allocator.new_pair(first_loc, &first, &tail_list) }, None => path_from_args(allocator, sexp, new_args), } @@ -285,16 +303,22 @@ pub fn sub_args( } } -fn var_change_optimizer_cons_eval_pattern(allocator: &mut Allocator) -> NodePtr { - assemble(allocator, "(a (q . (: . sexp)) (: . args))").unwrap() +fn var_change_optimizer_cons_eval_pattern( + allocator: &mut A +) -> A::NodePtr { + let a = assemble(allocator.allocator(), "(a (q . (: . sexp)) (: . args))").unwrap(); + allocator.import(Srcloc::start("*var_change_optimizer_cons_eval_pattern*"), a).unwrap() } -pub fn var_change_optimizer_cons_eval( - allocator: &mut Allocator, +pub fn var_change_optimizer_cons_eval( + allocator: &mut A, memo: &RefCell>, - r: NodePtr, + r: &A::NodePtr, eval_f: Rc, -) -> Result { +) -> Result +where + A::NodePtr: Clone +{ /* * This applies the transform * (a (q . (op SEXP1...)) (ARGS)) => (q . RET_VAL) where ARGS != @ @@ -307,59 +331,60 @@ pub fn var_change_optimizer_cons_eval( */ let pattern = var_change_optimizer_cons_eval_pattern(allocator); - match match_sexp(allocator, pattern, r, HashMap::new()).as_ref() { - None => Ok(r), + let export_r = allocator.export(r); + match match_sexp(allocator, &pattern, &r, HashMap::new()).as_ref() { + None => Ok(r.clone()), Some(t1) => { let original_args = t1.get("args").ok_or_else(|| { - EvalErr::InternalError(r, "bad pattern match on args".to_string()) + ClError(allocator.loc(&r), EvalErr::InternalError(export_r, "bad pattern match on args".to_string())) })?; if DIAG_OPTIMIZATIONS { println!( "XXX ORIGINAL_ARGS {}", - disassemble(allocator, *original_args, None) + allocator.disassemble(original_args, None) ); }; let original_call = t1.get("sexp").ok_or_else(|| { - EvalErr::InternalError(r, "bad pattern match on sexp".to_string()) + ClError(allocator.loc(&r), EvalErr::InternalError(export_r, "bad pattern match on sexp".to_string())) })?; if DIAG_OPTIMIZATIONS { println!( "XXX ORIGINAL_CALL {}", - disassemble(allocator, *original_call, None) + allocator.disassemble(original_call, None) ); }; - let new_eval_sexp_args = sub_args(allocator, *original_call, *original_args)?; + let new_eval_sexp_args = sub_args(allocator, original_call, original_args)?; if DIAG_OPTIMIZATIONS { println!( "XXX new_eval_sexp_args {} ORIG {}", - disassemble(allocator, new_eval_sexp_args, None), - disassemble(allocator, *original_args, None) + allocator.disassemble(&new_eval_sexp_args, None), + allocator.disassemble(original_args, None) ); }; // Do not iterate into a quoted value as if it were a list - if seems_constant(allocator, new_eval_sexp_args) { + if seems_constant(allocator, &new_eval_sexp_args) { if DIAG_OPTIMIZATIONS { println!("XXX seems_constant"); } - optimize_sexp_(allocator, memo, new_eval_sexp_args, eval_f) + optimize_sexp_(allocator, memo, &new_eval_sexp_args, eval_f) } else { if DIAG_OPTIMIZATIONS { println!("XXX does not seems_constant"); }; - proper_list(allocator, new_eval_sexp_args, true) + proper_list(allocator, &new_eval_sexp_args, true) .map(|new_operands| { let mut opt_operands = Vec::new(); for item in new_operands.iter() { opt_operands.push(optimize_sexp_( allocator, memo, - *item, + item, eval_f.clone(), )?); } @@ -371,14 +396,14 @@ pub fn var_change_optimizer_cons_eval( println!( "XXX opt_operands {} {}", acc, - disassemble(allocator, val, None) + allocator.disassemble(&val, None) ); } - let increment = match allocator.sexp(val) { - SExp::Pair(val_first, _) => match allocator.sexp(val_first) { - SExp::Atom => { + let increment = match allocator.sexp(&val) { + ASExp::Pair(val_first, _) => match allocator.sexp(&val_first) { + ASExp::Atom => { // Atom reflects val_first. - let vf_buf = allocator.atom(val_first); + let vf_buf = allocator.atom(&val_first); (vf_buf.as_ref().len() != 1 || vf_buf.as_ref()[0] != 1) as i32 } @@ -387,10 +412,10 @@ pub fn var_change_optimizer_cons_eval( _ => 0, }; - Ok::<_, EvalErr>(acc + increment) + Ok::<_, ClError>(acc + increment) }, 0, - &mut opt_operands.iter().copied(), + &mut opt_operands.iter().cloned(), )?; if DIAG_OPTIMIZATIONS { @@ -400,40 +425,43 @@ pub fn var_change_optimizer_cons_eval( if non_constant_count < 1 { enlist(allocator, &opt_operands) } else { - Ok(r) + Ok(r.clone()) } }) - .unwrap_or(Ok(r)) + .unwrap_or(Ok(r.clone())) } } } } -pub fn children_optimizer( - allocator: &mut Allocator, +pub fn children_optimizer( + allocator: &mut A, memo: &RefCell>, - r: NodePtr, + r: &A::NodePtr, eval_f: Rc, -) -> Result { +) -> Result +where + A::NodePtr: Clone +{ // Recursively apply optimizations to all non-quoted child nodes. match proper_list(allocator, r, true) { - None => Ok(r), + None => Ok(r.clone()), Some(list) => { if list.is_empty() { - return Ok(r); + return Ok(r.clone()); } - if let SExp::Atom = allocator.sexp(list[0]) { - let atom = allocator.atom(list[0]); + if let ASExp::Atom = allocator.sexp(&list[0]) { + let atom = allocator.atom(&list[0]); if atom.as_ref().to_vec() == vec![1] { - return Ok(r); + return Ok(r.clone()); } } let mut optimized = Vec::new(); let mut different = false; for item in list.iter() { - let res = optimize_sexp_(allocator, memo, *item, eval_f.clone())?; - if different || !equal_to(allocator, *item, res) { + let res = optimize_sexp_(allocator, memo, item, eval_f.clone())?; + if different || !equal_to(allocator, item, &res) { different = true; } optimized.push(res); @@ -445,26 +473,37 @@ pub fn children_optimizer( // If we didn't produce any different children, skip producing // a new list and return r. Take advantage of using a consistent // allocator to help the cache. - Ok(r) + Ok(r.clone()) } } } } -fn cons_optimizer_pattern_first(allocator: &mut Allocator) -> NodePtr { - assemble(allocator, "(f (c (: . first) (: . rest)))").unwrap() +fn cons_optimizer_pattern_first( + allocator: &mut A +) -> A::NodePtr +{ + let a = assemble(allocator.allocator(), "(f (c (: . first) (: . rest)))").unwrap(); + allocator.import(Srcloc::start("*cons_optimizer_pattern_first*"), a).unwrap() } -fn cons_optimizer_pattern_rest(allocator: &mut Allocator) -> NodePtr { - assemble(allocator, "(r (c (: . first) (: . rest)))").unwrap() +fn cons_optimizer_pattern_rest( + allocator: &mut A +) -> A::NodePtr +{ + let a = assemble(allocator.allocator(), "(r (c (: . first) (: . rest)))").unwrap(); + allocator.import(Srcloc::start("*cons_optimizer_pattern_rest*"), a).unwrap() } -fn cons_optimizer( - allocator: &mut Allocator, +fn cons_optimizer( + allocator: &mut A, _memo: &RefCell>, - r: NodePtr, + r: &A::NodePtr, _eval_f: Rc, -) -> Result { +) -> Result +where + A::NodePtr: Clone +{ /* * This applies the transform * (f (c A B)) => A @@ -476,18 +515,18 @@ fn cons_optimizer( m! { let t1 = match_sexp( - allocator, cons_optimizer_pattern_first, r, HashMap::new() + allocator, &cons_optimizer_pattern_first, r, HashMap::new() ); - match t1.and_then(|t| t.get("first").copied()) { + match t1.and_then(|t| t.get("first").cloned()) { Some(first) => Ok(first), _ => { m! { let t2 = match_sexp( - allocator, cons_optimizer_pattern_rest, r, HashMap::new() + allocator, &cons_optimizer_pattern_rest, &r, HashMap::new() ); - match t2.and_then(|t| t.get("rest").copied()) { + match t2.and_then(|t| t.get("rest").cloned()) { Some(rest) => Ok(rest), - _ => Ok(r) + _ => Ok(r.clone()) } } } @@ -495,20 +534,25 @@ fn cons_optimizer( } } -fn first_atom_pattern(allocator: &mut Allocator) -> NodePtr { - assemble(allocator, "(f ($ . atom))").unwrap() +fn first_atom_pattern(allocator: &mut A) -> A::NodePtr { + let a = assemble(allocator.allocator(), "(f ($ . atom))").unwrap(); + allocator.import(Srcloc::start("*first_atom_pattern*"), a).unwrap() } -fn rest_atom_pattern(allocator: &mut Allocator) -> NodePtr { - assemble(allocator, "(r ($ . atom))").unwrap() +fn rest_atom_pattern(allocator: &mut A) -> A::NodePtr { + let a = assemble(allocator.allocator(), "(r ($ . atom))").unwrap(); + allocator.import(Srcloc::start("*first_atom_pattern*"), a).unwrap() } -fn path_optimizer( - allocator: &mut Allocator, +fn path_optimizer( + allocator: &mut A, _memo: &RefCell>, - r: NodePtr, + r: &A::NodePtr, _eval_f: Rc, -) -> Result { +) -> Result +where + A::NodePtr: Clone +{ let first_atom_pattern = first_atom_pattern(allocator); let rest_atom_pattern = rest_atom_pattern(allocator); @@ -519,93 +563,115 @@ fn path_optimizer( * (r N) => B */ - let first_match = match_sexp(allocator, first_atom_pattern, r, HashMap::new()); - let rest_match = match_sexp(allocator, rest_atom_pattern, r, HashMap::new()); + let first_match = match_sexp(allocator, &first_atom_pattern, &r, HashMap::new()); + let rest_match = match_sexp(allocator, &rest_atom_pattern, &r, HashMap::new()); match (first_match, rest_match) { (Some(first), _) => { match first .get("atom") - .and_then(|a| atom(allocator, *a).ok()) - .map(|atom| number_from_u8(&atom)) + .filter(|a| matches!(allocator.sexp(a), ASExp::Atom)) + .map(|a| (allocator.loc(a), allocator.atom(a))) + .map(|(loc, atom)| (loc, number_from_u8(atom.as_ref()))) { - Some(atom) => { + Some((loc, atom)) => { let node = NodePath::new(Some(atom)).add(NodePath::new(None).first()); - allocator.new_atom(node.as_path().data()) + allocator.new_atom(loc, node.as_path().data()) } - _ => Ok(r), + _ => Ok(r.clone()), } } (_, Some(rest)) => { match rest .get("atom") - .and_then(|a| atom(allocator, *a).ok()) - .map(|atom| number_from_u8(&atom)) + .filter(|a| matches!(allocator.sexp(a), ASExp::Atom)) + .map(|a| (allocator.loc(a), allocator.atom(a))) + .map(|(loc, atom)| (loc, number_from_u8(atom.as_ref()))) { - Some(atom) => { + Some((loc, atom)) => { let node = NodePath::new(Some(atom)).add(NodePath::new(None).rest()); - allocator.new_atom(node.as_path().data()) + allocator.new_atom(loc, node.as_path().data()) } - _ => Ok(r), + _ => Ok(r.clone()), } } - _ => Ok(r), + _ => Ok(r.clone()), } } -fn quote_pattern_1(allocator: &mut Allocator) -> NodePtr { - assemble(allocator, "(q . 0)").unwrap() +fn quote_pattern_1(allocator: &mut A) -> A::NodePtr { + let a = assemble(allocator.allocator(), "(q . 0)").unwrap(); + allocator.import(Srcloc::start("*quote_pattern_1*"), a).unwrap() } -fn quote_null_optimizer( - allocator: &mut Allocator, +fn quote_null_optimizer( + allocator: &mut A, _memo: &RefCell>, - r: NodePtr, + r: &A::NodePtr, _eval_f: Rc, -) -> Result { +) -> Result +where + A::NodePtr: Clone +{ let quote_pattern_1 = quote_pattern_1(allocator); // This applies the transform `(q . 0)` => `0` - let t1 = match_sexp(allocator, quote_pattern_1, r, HashMap::new()); - Ok(t1.map(|_| NodePtr::NIL).unwrap_or_else(|| r)) + let t1 = match_sexp(allocator, "e_pattern_1, &r, HashMap::new()); + let loc = allocator.loc(r); + let imported_nil = allocator.import(loc, NodePtr::NIL)?; + Ok(t1.map(|_| imported_nil).unwrap_or_else(|| r.clone())) } -fn apply_null_pattern_1(allocator: &mut Allocator) -> NodePtr { - assemble(allocator, "(a 0 . (: . rest))").unwrap() +fn apply_null_pattern_1( + allocator: &mut A +) -> A::NodePtr { + let a = assemble(allocator.allocator(), "(a 0 . (: . rest))").unwrap(); + allocator.import(Srcloc::start("*apply_null_pattern_1*"), a).unwrap() } -fn apply_null_optimizer( - allocator: &mut Allocator, +fn apply_null_optimizer( + allocator: &mut A, _memo: &RefCell>, - r: NodePtr, + r: &A::NodePtr, _eval_f: Rc, -) -> Result { +) -> Result +where + A::NodePtr: Clone +{ let apply_null_pattern_1 = apply_null_pattern_1(allocator); // This applies the transform `(a 0 ARGS)` => `0` - let t1 = match_sexp(allocator, apply_null_pattern_1, r, HashMap::new()); - Ok(t1.map(|_| NodePtr::NIL).unwrap_or_else(|| r)) + let t1 = match_sexp(allocator, &apply_null_pattern_1, r, HashMap::new()); + let loc = allocator.loc(r); + let imported_nil = allocator.import(loc, NodePtr::NIL)?; + Ok(t1.map(|_| imported_nil).unwrap_or_else(|| r.clone())) } -struct OptimizerRunner<'a> { +struct OptimizerRunner<'a, A: ClassicAllocator> +where + A::NodePtr: Clone +{ pub name: String, #[allow(clippy::type_complexity)] to_run: &'a dyn Fn( - &mut Allocator, + &mut A, &RefCell>, - NodePtr, + &A::NodePtr, Rc, - ) -> Result, + ) -> Result, } -impl<'a> OptimizerRunner<'a> { +impl<'a, A: ClassicAllocator> OptimizerRunner<'a, A> +where + A::NodePtr: Clone +{ pub fn invoke( &self, - allocator: &mut Allocator, + allocator: &mut A, memo: &RefCell>, - r: NodePtr, + r: &A::NodePtr, eval_f: Rc, - ) -> Result { + ) -> Result { (self.to_run)(allocator, memo, r, eval_f) } @@ -613,11 +679,11 @@ impl<'a> OptimizerRunner<'a> { pub fn new( name: &str, to_run: &'a dyn Fn( - &mut Allocator, + &mut A, &RefCell>, - NodePtr, + &A::NodePtr, Rc, - ) -> Result, + ) -> Result, ) -> Self { OptimizerRunner { name: name.to_string(), @@ -626,31 +692,37 @@ impl<'a> OptimizerRunner<'a> { } } -pub fn optimize_sexp_( - allocator: &mut Allocator, +pub fn optimize_sexp_( + allocator: &mut A, memo: &RefCell>, - r_: NodePtr, + r_: &A::NodePtr, eval_f: Rc, -) -> Result { +) -> Result +where + A::NodePtr: Clone +{ // First compare the NodePtr to see if we've cached this exact one. // Note that this scoping is here to prevent the borrowed mutable ref from // preventing us from using memo downstream when we've done one optimize // pass and need to cache the result. + let exported_r = allocator.export(&r_); { let memo_ref: Ref> = memo.borrow(); let memo: &HashMap = &memo_ref; - if let Some(res) = memo.get(&AllocatorRefOrTreeHash::new_from_nodeptr(r_)) { - return Ok(*res); + if let Some(res) = memo.get(&AllocatorRefOrTreeHash::new_from_nodeptr(exported_r)) { + let imported_cached = allocator.import(allocator.loc(r_), *res)?; + return Ok(imported_cached); } } // Fall back to treehash comparison since we didn't get an exact pointer hit. - let footprint = AllocatorRefOrTreeHash::new_from_sexp(allocator, r_); + let footprint = AllocatorRefOrTreeHash::new_from_sexp(allocator.allocator(), exported_r); { let memo_ref: Ref> = memo.borrow(); let memo: &HashMap = &memo_ref; if let Some(res) = memo.get(&footprint) { - return Ok(*res); + let imported_cached = allocator.import(allocator.loc(r_), *res)?; + return Ok(imported_cached); } } @@ -658,7 +730,7 @@ pub fn optimize_sexp_( * Optimize an s-expression R written for clvm to R_opt where * (a R args) == (a R_opt args) for ANY args. */ - let optimizers: Vec = vec![ + let optimizers: Vec> = vec![ OptimizerRunner::new("cons_optimizer", &cons_optimizer), OptimizerRunner::new("constant_optimizer", &|allocator, memo, r, eval_f| { constant_optimizer(allocator, memo, r, 0, eval_f.clone()) @@ -674,25 +746,26 @@ pub fn optimize_sexp_( OptimizerRunner::new("apply_null_optimizer", &apply_null_optimizer), ]; - let mut r = r_; + let mut r = r_.clone(); loop { - let start_r = r; + let start_r = r.clone(); + let start_r_export = allocator.export(&r); let mut name = "".to_string(); - match allocator.sexp(r) { - SExp::Atom => { - return Ok(r); + match allocator.sexp(&r) { + ASExp::Atom => { + return Ok(r.clone()); } - SExp::Pair(_, _) => { + ASExp::Pair(_, _) => { for opt in optimizers.iter() { name.clone_from(&opt.name); - match opt.invoke(allocator, memo, r, eval_f.clone()) { + match opt.invoke(allocator, memo, &r, eval_f.clone()) { Err(e) => { return Err(e); } Ok(res) => { - if !equal_to(allocator, r, res) { + if !equal_to(allocator, &r, &res) { r = res; break; } @@ -700,12 +773,13 @@ pub fn optimize_sexp_( } } - if equal_to(allocator, start_r, r) { + if equal_to(allocator, &start_r, &r) { memo.replace_with(|mr| { let mut work = HashMap::new(); swap(&mut work, mr); - work.insert(footprint.clone(), start_r); - work.insert(AllocatorRefOrTreeHash::new_from_nodeptr(r), start_r); + work.insert(footprint.clone(), start_r_export); + let r_export = allocator.export(&r); + work.insert(AllocatorRefOrTreeHash::new_from_nodeptr(r_export), start_r_export); work }); @@ -716,8 +790,8 @@ pub fn optimize_sexp_( println!( "OPT-{:?}[{}] => {}", name, - disassemble(allocator, start_r, None), - disassemble(allocator, r, None) + allocator.disassemble(&start_r, None), + allocator.disassemble(&r, None) ); } } @@ -725,34 +799,39 @@ pub fn optimize_sexp_( } } -pub fn optimize_sexp( - allocator: &mut Allocator, - r: NodePtr, +pub fn optimize_sexp( + allocator: &mut A, + r: &A::NodePtr, eval_f: Rc, -) -> Result { +) -> Result +where + A::NodePtr: Clone +{ let optimized = RefCell::new(HashMap::new()); if DIAG_OPTIMIZATIONS { - println!("START OPTIMIZE {}", disassemble(allocator, r, None)); + println!("START OPTIMIZE {}", allocator.disassemble(&r, None)); } optimize_sexp_(allocator, &optimized, r, eval_f).inspect(|x| { if DIAG_OPTIMIZATIONS { println!( "OPTIMIZE_SEXP {} GIVING {}", - disassemble(allocator, r, None), - disassemble(allocator, *x, None) + allocator.disassemble(&r, None), + allocator.disassemble(x, None) ); } }) } -pub fn do_optimize( +pub fn do_optimize( runner: Rc, - allocator: &mut Allocator, + allocator: &mut A, memo: &RefCell>, - r: NodePtr, -) -> Response { - let r_first = first(allocator, r)?; - optimize_sexp_(allocator, memo, r_first, runner.clone()) - .map(|optimized| Reduction(1, optimized)) + r: &A::NodePtr, +) -> Result +where + A::NodePtr: Clone +{ + let r_first = first(allocator, &r)?; + optimize_sexp_(allocator, memo, &r_first, runner.clone()) } diff --git a/src/classic/clvm_tools/stages/stage_2/reader.rs b/src/classic/clvm_tools/stages/stage_2/reader.rs index 9eb439266..a49c352ae 100644 --- a/src/classic/clvm_tools/stages/stage_2/reader.rs +++ b/src/classic/clvm_tools/stages/stage_2/reader.rs @@ -2,7 +2,7 @@ use std::fs; use std::rc::Rc; use clvm_rs::error::EvalErr; -use clvmr::allocator::{Allocator, NodePtr, SExp}; +use clvmr::allocator::{NodePtr}; use crate::classic::clvm::__type_compatibility__::{Bytes, Stream, UnvalidatedBytesFromType}; use crate::classic::clvm::serialize::{sexp_from_stream, SimpleCreateCLVMObject}; @@ -12,6 +12,7 @@ use crate::classic::clvm_tools::stages::stage_0::TRunProgram; use crate::classic::clvm_tools::stages::stage_2::compile::get_search_paths; use crate::classic::clvm_tools::stages::stage_2::helpers::quote; use crate::classic::clvm_tools::stages::stage_2::operators::full_path_for_filename; +use crate::classic::clvm_tools::stages::stage_2::abstraction::{ASExp, BufCarrier, ClassicAllocator, ClError}; use crate::compiler::sexp::decode_string; @@ -25,21 +26,30 @@ pub struct PresentFile { /// Given u8 data from a hex file, build an sexp from it. /// This is used for the compile-file and embed-file feature. -pub fn convert_hex_to_sexp( - allocator: &mut Allocator, +pub fn convert_hex_to_sexp( + allocator: &mut A, + parent_sexp: &A::NodePtr, file_data: &[u8], -) -> Result { +) -> Result +where + A::NodePtr: Clone +{ + let loc = allocator.loc(parent_sexp); let content_bytes = Bytes::new_validated(Some(UnvalidatedBytesFromType::Hex(decode_string( file_data, )))) - .map_err(|e| EvalErr::InternalError(NodePtr::NIL, e.to_string()))?; + .map_err(|e| { + ClError(loc.clone(), EvalErr::InternalError(NodePtr::NIL, e.to_string())) + })?; let mut reader_stream = Stream::new(Some(content_bytes)); - Ok(sexp_from_stream( - allocator, + let incoming_data = sexp_from_stream( + allocator.allocator(), &mut reader_stream, Box::new(SimpleCreateCLVMObject {}), - )? - .1) + ).map_err(|e| { + ClError(loc.clone(), e) + })?; + allocator.import(loc, incoming_data.1) } /// Given a runner (which in the case of classic, contains the search paths as @@ -47,18 +57,23 @@ pub fn convert_hex_to_sexp( /// time runner), try to find a file to embed given its name. Try to report an /// error nicely by using the form the user gave (parent_sexp) in the error /// report. -pub fn read_file( +pub fn read_file( runner: Rc, - allocator: &mut Allocator, - parent_sexp: NodePtr, + allocator: &mut A, + parent_sexp: &A::NodePtr, filename: &str, -) -> Result { - let search_paths = get_search_paths(runner, allocator)?; - let full_path = full_path_for_filename(parent_sexp, filename, &search_paths)?; +) -> Result +where + A::NodePtr: Clone +{ + let loc = allocator.loc(parent_sexp); + let search_paths = get_search_paths(runner, loc.clone(), allocator)?; + let full_path = full_path_for_filename(allocator, parent_sexp, filename, &search_paths)?; + let export = allocator.export(parent_sexp); fs::read(full_path.clone()) .map_err(|x| { - EvalErr::InternalError(parent_sexp, format!("error reading {full_path}: {x:?}")) + ClError(loc, EvalErr::InternalError(export, format!("error reading {full_path}: {x:?}"))) }) .map(|data| PresentFile { data, @@ -72,11 +87,14 @@ pub fn read_file( /// or (compile-file constant-name filename) /// Return the resulting constant name and a quoted expression suitable for use /// as a constant or an error if the file wasn't found. -pub fn process_embed_file( - allocator: &mut Allocator, +pub fn process_embed_file( + allocator: &mut A, runner: Rc, - declaration_sexp: NodePtr, -) -> Result<(Vec, NodePtr), EvalErr> { + declaration_sexp: &A::NodePtr, +) -> Result<(Vec, A::NodePtr), ClError> +where + A::NodePtr: Clone +{ // Include the file's contents in the constant pool. // The user can specify the format to read: // @@ -84,24 +102,33 @@ pub fn process_embed_file( // hex // sexp let rest_of_decl = rest(allocator, declaration_sexp)?; - if let Some(l) = proper_list(allocator, rest_of_decl, true) { + let loc = allocator.loc(declaration_sexp); + let export = allocator.export(declaration_sexp); + if let Some(l) = proper_list(allocator, &rest_of_decl, true) { if l.len() != 3 { - return Err(EvalErr::InternalError( - declaration_sexp, - "must have a type and a name".to_string(), - )); + let loc = allocator.loc(declaration_sexp); + let dec_export = allocator.export(declaration_sexp); + return Err( + ClError( + loc, + EvalErr::InternalError( + dec_export, + "must have a type and a name".to_string(), + ) + ) + ); } - if let (SExp::Atom, SExp::Atom, SExp::Atom) = ( - allocator.sexp(l[0]), - allocator.sexp(l[1]), - allocator.sexp(l[2]), + if let (ASExp::Atom, ASExp::Atom, ASExp::Atom) = ( + allocator.sexp(&l[0]), + allocator.sexp(&l[1]), + allocator.sexp(&l[2]), ) { // Note: we don't want to keep borrowing here because we // need the mutable borrow below. - let name_atom = allocator.atom(l[0]); - let kind_atom = allocator.atom(l[1]); - let filename_atom = allocator.atom(l[2]); + let name_atom = allocator.atom(&l[0]); + let kind_atom = allocator.atom(&l[1]); + let filename_atom = allocator.atom(&l[2]); let name_buf = name_atom.as_ref().to_vec(); let kind_buf = kind_atom.as_ref().to_vec(); let filename_buf = filename_atom.as_ref().to_vec(); @@ -112,7 +139,7 @@ pub fn process_embed_file( declaration_sexp, &decode_string(&filename_buf), )?; - allocator.new_atom(&file.data)? + allocator.new_atom(loc, &file.data)? } else if kind_buf == b"hex" { let file = read_file( runner, @@ -120,7 +147,7 @@ pub fn process_embed_file( declaration_sexp, &decode_string(&filename_buf), )?; - convert_hex_to_sexp(allocator, &file.data)? + convert_hex_to_sexp(allocator, declaration_sexp, &file.data)? } else if kind_buf == b"sexp" { let file = read_file( runner, @@ -128,25 +155,41 @@ pub fn process_embed_file( declaration_sexp, &decode_string(&filename_buf), )?; - assemble(allocator, &decode_string(&file.data))? + let assembled = assemble(allocator.allocator(), &decode_string(&file.data)).map_err(|e| ClError(loc.clone(), e))?; + allocator.import(loc, assembled)? } else { - return Err(EvalErr::InternalError( - declaration_sexp, - "no such embed kind".to_string(), - )); + return Err( + ClError( + loc, + EvalErr::InternalError( + export, + "no such embed kind".to_string(), + ) + ) + ); }; - Ok((name_buf.to_vec(), quote(allocator, file_data)?)) + Ok((name_buf.to_vec(), quote(allocator, &file_data)?)) } else { - Err(EvalErr::InternalError( - declaration_sexp, - "malformed embed-file".to_string(), - )) + Err( + ClError( + loc, + EvalErr::InternalError( + export, + "malformed embed-file".to_string(), + ) + ) + ) } } else { - Err(EvalErr::InternalError( - declaration_sexp, - "must be a proper list".to_string(), - )) + Err( + ClError( + loc, + EvalErr::InternalError( + export, + "must be a proper list".to_string(), + ) + ) + ) } } diff --git a/src/compiler/dialect.rs b/src/compiler/dialect.rs index dc9ee5ac5..06436bff2 100644 --- a/src/compiler/dialect.rs +++ b/src/compiler/dialect.rs @@ -228,7 +228,7 @@ fn include_dialect(allocator: &Allocator, e: &[NodePtr]) -> Option AcceptedDialect { let mut result = AcceptedDialect::default(); - if let Some(l) = proper_list(allocator, sexp, true) { + if let Some(l) = proper_list(allocator, &sexp, true) { if l.len() == 2 { if let Some(dialect) = include_dialect(allocator, &l) { return dialect; diff --git a/src/compiler/optimize/mod.rs b/src/compiler/optimize/mod.rs index 893b1226a..742851773 100644 --- a/src/compiler/optimize/mod.rs +++ b/src/compiler/optimize/mod.rs @@ -22,6 +22,7 @@ use crate::classic::clvm::__type_compatibility__::bi_one; use crate::classic::clvm::__type_compatibility__::bi_zero; use crate::classic::clvm_tools::stages::stage_0::TRunProgram; +use crate::classic::clvm_tools::stages::stage_2::abstraction::ClError; use crate::classic::clvm_tools::stages::stage_2::optimize::optimize_sexp; use crate::compiler::clvm::{convert_from_clvm_rs, convert_to_clvm_rs, run}; @@ -707,12 +708,12 @@ pub fn run_optimizer( RunFailure::RunExn(s, e) => CompileErr(s, format!("exception {e}\n")), })?; - let optimized = optimize_sexp(allocator, to_clvm_rs.1, runner) + let optimized = optimize_sexp(allocator, &to_clvm_rs.1, runner) .map_err(|e| { CompileErr( to_clvm_rs.0.clone(), match e { - EvalErr::InternalError(_, e) => e.to_string(), + ClError(l, EvalErr::InternalError(_, e)) => format!("{l}: {e}"), _ => e.to_string(), }, ) From 030c2db1d4b950bcb1aab74d4ccb9f2ecc3df88e Mon Sep 17 00:00:00 2001 From: art yerkes Date: Thu, 23 Jul 2026 13:25:53 -0700 Subject: [PATCH 02/38] tests compile --- .../clvm_tools/stages/stage_2/optimize.rs | 2 +- src/tests/classic/optimize.rs | 12 +++---- src/tests/classic/smoke.rs | 11 +++--- src/tests/classic/stage_2.rs | 34 +++++++++---------- 4 files changed, 30 insertions(+), 29 deletions(-) diff --git a/src/classic/clvm_tools/stages/stage_2/optimize.rs b/src/classic/clvm_tools/stages/stage_2/optimize.rs index a0f8b665d..c1e54418b 100644 --- a/src/classic/clvm_tools/stages/stage_2/optimize.rs +++ b/src/classic/clvm_tools/stages/stage_2/optimize.rs @@ -49,7 +49,7 @@ fn seems_constant_tail(allocator: &mut A, sexp_: A::NodePtr } } -fn seems_constant(allocator: &mut A, sexp: &A::NodePtr) -> bool { +pub fn seems_constant(allocator: &mut A, sexp: &A::NodePtr) -> bool { match allocator.sexp(&sexp) { ASExp::Atom => { return allocator.is_nil(&sexp); diff --git a/src/tests/classic/optimize.rs b/src/tests/classic/optimize.rs index f8e2e5517..3395bc29d 100644 --- a/src/tests/classic/optimize.rs +++ b/src/tests/classic/optimize.rs @@ -18,7 +18,7 @@ fn test_cons_q_a(src: String) -> String { let input_ir = read_ir(&src, 0).unwrap(); let assembled = assemble_from_ir(&mut allocator, Rc::new(input_ir)).unwrap(); let runner = run_program_for_search_paths("*test*", &vec![".".to_string()], false, 0); - let optimized = cons_q_a_optimizer(&mut allocator, &memo, assembled, runner.clone()).unwrap(); + let optimized = cons_q_a_optimizer(&mut allocator, &memo, &assembled, runner.clone()).unwrap(); disassemble(&mut allocator, optimized, Some(0)) } @@ -28,7 +28,7 @@ fn test_children_optimizer(src: String) -> String { let input_ir = read_ir(&src, 0).unwrap(); let assembled = assemble_from_ir(&mut allocator, Rc::new(input_ir)).unwrap(); let runner = run_program_for_search_paths("*test*", &vec![".".to_string()], false, 0); - let optimized = children_optimizer(&mut allocator, &memo, assembled, runner.clone()).unwrap(); + let optimized = children_optimizer(&mut allocator, &memo, &assembled, runner.clone()).unwrap(); disassemble(&mut allocator, optimized, Some(0)) } @@ -39,7 +39,7 @@ fn test_constant_optimizer(src: String) -> String { let assembled = assemble_from_ir(&mut allocator, Rc::new(input_ir)).unwrap(); let runner = run_program_for_search_paths("*test*", &vec![".".to_string()], false, 0); let optimized = - constant_optimizer(&mut allocator, &memo, assembled, 0, runner.clone()).unwrap(); + constant_optimizer(&mut allocator, &memo, &assembled, 0, runner.clone()).unwrap(); disassemble(&mut allocator, optimized, Some(0)) } @@ -48,7 +48,7 @@ fn test_optimizer(src: String) -> String { let input_ir = read_ir(&src, 0).unwrap(); let assembled = assemble_from_ir(&mut allocator, Rc::new(input_ir)).unwrap(); let runner = run_program_for_search_paths("*test*", &vec![".".to_string()], false, 0); - let optimized = optimize_sexp(&mut allocator, assembled, runner.clone()).unwrap(); + let optimized = optimize_sexp(&mut allocator, &assembled, runner.clone()).unwrap(); disassemble(&mut allocator, optimized, Some(0)) } @@ -58,7 +58,7 @@ fn test_sub_args(src: String) -> String { let assembled = assemble_from_ir(&mut allocator, Rc::new(input_ir)).unwrap(); match allocator.sexp(assembled) { SExp::Pair(a, b) => { - let optimized = sub_args(&mut allocator, a, b).unwrap(); + let optimized = sub_args(&mut allocator, &a, &b).unwrap(); disassemble(&mut allocator, optimized, Some(0)) } _ => { @@ -133,7 +133,7 @@ fn seems_constant_quote_test() { let src = "(q . 15)".to_string(); let input_ir = read_ir(&src, 0).unwrap(); let assembled = assemble_from_ir(&mut allocator, Rc::new(input_ir)).unwrap(); - assert_eq!(seems_constant(&mut allocator, assembled), true); + assert_eq!(seems_constant(&mut allocator, &assembled), true); } #[test] diff --git a/src/tests/classic/smoke.rs b/src/tests/classic/smoke.rs index 386cb3856..9c9c6f6c3 100644 --- a/src/tests/classic/smoke.rs +++ b/src/tests/classic/smoke.rs @@ -25,6 +25,7 @@ use crate::classic::clvm_tools::node_path::NodePath; use crate::classic::clvm_tools::pattern_match::match_sexp; use crate::classic::clvm_tools::stages; use crate::classic::clvm_tools::stages::stage_0::{DefaultProgramRunner, TRunProgram}; +use crate::classic::clvm_tools::stages::stage_2::abstraction::ClError; use crate::classic::clvm_tools::stages::stage_2::operators::run_program_for_search_paths; use crate::classic::clvm_tools::stages::stage_2::optimize::sub_args; use crate::classic::platform::argparse::{ @@ -699,10 +700,10 @@ fn test_check_simple_arg_path_0() { assert_eq!(cp.as_path().raw(), &[4]); } -fn assert_node_find_error(r: Result) { +fn assert_node_find_error(r: Result) { assert!(r.is_err()); } -fn assert_node_not_error(r: Result) -> X { +fn assert_node_not_error(r: Result) -> X { r.unwrap() } @@ -806,7 +807,7 @@ fn test_pattern_match_dollar_for_dollar() { let pattern = assemble(&mut allocator, "($ . $)").expect("should assemble"); let target_expr = assemble(&mut allocator, "$").expect("should assemble"); let empty_map = HashMap::new(); - let matched = match_sexp(&mut allocator, pattern, target_expr, empty_map.clone()); + let matched = match_sexp(&mut allocator, &pattern, &target_expr, empty_map.clone()); // Returns empty map. assert_eq!(Some(empty_map), matched); } @@ -817,7 +818,7 @@ fn test_pattern_match_colon_for_colon() { let pattern = assemble(&mut allocator, "(: . :)").expect("should assemble"); let target_expr = assemble(&mut allocator, ":").expect("should assemble"); let empty_map = HashMap::new(); - let matched = match_sexp(&mut allocator, pattern, target_expr, empty_map.clone()); + let matched = match_sexp(&mut allocator, &pattern, &target_expr, empty_map.clone()); // Returns empty map. assert_eq!(Some(empty_map), matched); } @@ -827,7 +828,7 @@ fn test_sub_args() { let mut allocator = Allocator::new(); let expr_sexp = assemble(&mut allocator, "(body 2 5)").expect("should assemble"); let new_args = assemble(&mut allocator, "(test1 test2)").expect("should assemble"); - let result = sub_args(&mut allocator, expr_sexp, new_args).expect("should run"); + let result = sub_args(&mut allocator, &expr_sexp, &new_args).expect("should run"); assert_eq!( disassemble(&mut allocator, result, None), "(\"body\" (f (\"test1\" \"test2\")) (f (r (\"test1\" \"test2\"))))" diff --git a/src/tests/classic/stage_2.rs b/src/tests/classic/stage_2.rs index 671346c4a..5c7e109c2 100644 --- a/src/tests/classic/stage_2.rs +++ b/src/tests/classic/stage_2.rs @@ -38,13 +38,13 @@ fn test_expand_macro( let symbols_source = assemble_from_ir(allocator, Rc::new(symbols_ir)).unwrap(); let exp_res = try_expand_macro_for_atom( allocator, - macro_source, - prog_source, - macros_source, - symbols_source, + ¯o_source, + &prog_source, + ¯os_source, + &symbols_source, ) .unwrap(); - disassemble(allocator, exp_res.1, Some(0)) + disassemble(allocator, exp_res, Some(0)) } fn test_inner_expansion( @@ -73,8 +73,8 @@ fn test_do_com_prog( let macro_lookup = assemble_from_ir(allocator, Rc::new(macro_ir)).unwrap(); let sym_ir = read_ir(&symbol_table_src, 0).unwrap(); let symbol_table = assemble_from_ir(allocator, Rc::new(sym_ir)).unwrap(); - let result = do_com_prog(allocator, 849, program, macro_lookup, symbol_table, runner).unwrap(); - disassemble(allocator, result.1, Some(0)) + let result = do_com_prog(allocator, 849, &program, ¯o_lookup, &symbol_table, runner).unwrap(); + disassemble(allocator, result, Some(0)) } #[test] @@ -150,7 +150,7 @@ fn test_compile_assert_2() { fn test_stage_2_quote() { let mut allocator = Allocator::new(); let assembled = assemble(&mut allocator, "(1 2 3)").unwrap(); - let quoted = quote(&mut allocator, assembled).unwrap(); + let quoted = quote(&mut allocator, &assembled).unwrap(); assert_eq!(disassemble(&mut allocator, quoted, Some(0)), "(q 1 2 3)"); } @@ -159,7 +159,7 @@ fn test_stage_2_evaluate() { let mut allocator = Allocator::new(); let prog = assemble(&mut allocator, "(q 16 2 3)").unwrap(); let args = assemble(&mut allocator, "(q 9 15)").unwrap(); - let to_eval = evaluate(&mut allocator, prog, args).unwrap(); + let to_eval = evaluate(&mut allocator, &prog, &args).unwrap(); assert_eq!( disassemble(&mut allocator, to_eval, Some(0)), "(a (q 16 2 3) (q 9 15))" @@ -171,7 +171,7 @@ fn test_stage_2_run() { let mut allocator = Allocator::new(); let prog = assemble(&mut allocator, "(q 16 2 3)").unwrap(); let macro_lookup_throw = assemble(&mut allocator, "(q 9)").unwrap(); - let to_eval = run(&mut allocator, prog, macro_lookup_throw).unwrap(); + let to_eval = run(&mut allocator, &prog, ¯o_lookup_throw).unwrap(); assert_eq!( disassemble(&mut allocator, to_eval, Some(0)), "(a (\"com\" (q 16 2 3) (q 1 9)) 1)" @@ -188,7 +188,7 @@ fn test_present_file_smoke_not_exists() { let res = read_file( runner, &mut allocator, - sexp_triggering_read, + &sexp_triggering_read, "test-embed-not-exist.clsp", ); assert!(res.is_err()); @@ -201,7 +201,7 @@ fn test_present_file_smoke_exists() { run_program_for_search_paths("*test*", &vec!["resources/tests".to_string()], false, 0); let sexp_triggering_read = assemble(&mut allocator, "(embed-file test-file sexp embed.sexp)") .expect("should assemble"); - let res = read_file(runner, &mut allocator, sexp_triggering_read, "embed.sexp") + let res = read_file(runner, &mut allocator, &sexp_triggering_read, "embed.sexp") .expect("should exist"); assert_eq!(decode_string(&res.data), "(23 24 25)"); } @@ -215,7 +215,7 @@ fn test_process_embed_file_as_sexp() { .expect("should assemble"); let want_exp = assemble(&mut allocator, "(q 23 24 25)").expect("should assemble"); let (name, content) = - process_embed_file(&mut allocator, runner, declaration_sexp).expect("should work"); + process_embed_file(&mut allocator, runner, &declaration_sexp).expect("should work"); assert_eq!( disassemble(&mut allocator, want_exp, Some(0)), disassemble(&mut allocator, content, Some(0)) @@ -240,7 +240,7 @@ fn test_process_embed_file_as_sexp_in_an_unexpected_location() { let res = read_file( runner, &mut allocator, - sexp_triggering_read, + &sexp_triggering_read, "fact.clvm.hex", ); assert!(res.is_err()); @@ -261,7 +261,7 @@ fn test_process_embed_file_as_sexp_in_an_expected_location() { let res = read_file( runner, &mut allocator, - sexp_triggering_read, + &sexp_triggering_read, "fact.clvm.hex", ) .expect("should exist"); @@ -281,8 +281,8 @@ fn test_process_embed_file_as_hex() { ) .expect("should assemble"); let (name, content) = - process_embed_file(&mut allocator, runner, declaration_sexp).expect("should work"); - let matching_part_of_decl = rest(&mut allocator, content).expect("should be quoted"); + process_embed_file(&mut allocator, runner, &declaration_sexp).expect("should work"); + let matching_part_of_decl = rest(&mut allocator, &content).expect("should be quoted"); let mut outstream = Stream::new(None); call_tool( &mut outstream, From d8f45044aebbfc85eac4a437fcf4a0e365a9ee91 Mon Sep 17 00:00:00 2001 From: art yerkes Date: Fri, 24 Jul 2026 14:48:15 -0700 Subject: [PATCH 03/38] working tests --- .../clvm_tools/stages/stage_2/optimize.rs | 26 ++++++++++++------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/src/classic/clvm_tools/stages/stage_2/optimize.rs b/src/classic/clvm_tools/stages/stage_2/optimize.rs index c1e54418b..6f1dc3274 100644 --- a/src/classic/clvm_tools/stages/stage_2/optimize.rs +++ b/src/classic/clvm_tools/stages/stage_2/optimize.rs @@ -30,13 +30,16 @@ pub struct DoOptProg {} const DEBUG_OPTIMIZATIONS: bool = false; const DIAG_OPTIMIZATIONS: bool = false; -fn seems_constant_tail(allocator: &mut A, sexp_: A::NodePtr) -> bool { - let mut sexp = sexp_; +fn seems_constant_tail(allocator: &mut A, sexp_: &A::NodePtr) -> bool +where + A::NodePtr: Clone +{ + let mut sexp = sexp_.clone(); loop { match allocator.sexp(&sexp) { ASExp::Pair(l, r) => { - if !seems_constant_tail(allocator, l) { + if !seems_constant(allocator, &l) { return false; } @@ -49,7 +52,10 @@ fn seems_constant_tail(allocator: &mut A, sexp_: A::NodePtr } } -pub fn seems_constant(allocator: &mut A, sexp: &A::NodePtr) -> bool { +pub fn seems_constant(allocator: &mut A, sexp: &A::NodePtr) -> bool +where + A::NodePtr: Clone +{ match allocator.sexp(&sexp) { ASExp::Atom => { return allocator.is_nil(&sexp); @@ -66,13 +72,13 @@ pub fn seems_constant(allocator: &mut A, sexp: &A::NodePtr) } } ASExp::Pair(_, _) => { - if !seems_constant(allocator, &operator) { + if !seems_constant_tail(allocator, &operator) { return false; } } } - if !seems_constant_tail(allocator, r) { + if !seems_constant_tail(allocator, &r) { return false; } } @@ -110,10 +116,10 @@ where let nn_r = !allocator.is_nil(&r); if DIAG_OPTIMIZATIONS { println!( - "COPT {} SC_R {} NN_R {}", - allocator.disassemble(&r, None), + "COPT SC_R {} NN_R {} {}", sc_r, - nn_r + nn_r, + allocator.disassemble(&r, None), ); } if sc_r && nn_r { @@ -705,7 +711,7 @@ where // Note that this scoping is here to prevent the borrowed mutable ref from // preventing us from using memo downstream when we've done one optimize // pass and need to cache the result. - let exported_r = allocator.export(&r_); + let exported_r = allocator.export(r_); { let memo_ref: Ref> = memo.borrow(); let memo: &HashMap = &memo_ref; From 2061fa0cb39aee9b6942f75eb103b0ecfd63e744 Mon Sep 17 00:00:00 2001 From: art yerkes Date: Fri, 24 Jul 2026 14:51:39 -0700 Subject: [PATCH 04/38] fmt --- src/classic/clvm/sexp.rs | 60 ++--- src/classic/clvm_tools/debug.rs | 41 ++-- src/classic/clvm_tools/pattern_match.rs | 117 ++++----- .../clvm_tools/stages/stage_2/abstraction.rs | 28 ++- .../clvm_tools/stages/stage_2/compile.rs | 223 ++++++++++-------- .../clvm_tools/stages/stage_2/defaults.rs | 28 ++- .../clvm_tools/stages/stage_2/helpers.rs | 11 +- .../clvm_tools/stages/stage_2/inline.rs | 47 ++-- .../clvm_tools/stages/stage_2/module.rs | 183 +++++++------- .../clvm_tools/stages/stage_2/operators.rs | 18 +- .../clvm_tools/stages/stage_2/optimize.rs | 146 ++++++------ .../clvm_tools/stages/stage_2/reader.rs | 82 +++---- src/tests/classic/stage_2.rs | 10 +- 13 files changed, 533 insertions(+), 461 deletions(-) diff --git a/src/classic/clvm/sexp.rs b/src/classic/clvm/sexp.rs index 3997c99e5..e12f38d43 100644 --- a/src/classic/clvm/sexp.rs +++ b/src/classic/clvm/sexp.rs @@ -6,11 +6,11 @@ use chia_bls::PublicKey; use clvm_rs::allocator::{Allocator, NodePtr, SExp}; use clvm_rs::error::EvalErr; -use crate::classic::clvm_tools::stages::stage_2::abstraction::{ASExp, ClassicAllocator, ClError}; use crate::classic::clvm::__type_compatibility__::{Bytes, BytesFromType, Stream}; use crate::classic::clvm::serialize::sexp_to_stream; -use crate::util::{u8_from_number, Number}; +use crate::classic::clvm_tools::stages::stage_2::abstraction::{ASExp, ClError, ClassicAllocator}; use crate::compiler::srcloc::Srcloc; +use crate::util::{u8_from_number, Number}; #[derive(Debug)] pub enum CastableType { @@ -352,15 +352,10 @@ pub fn first(allocator: &A, sexp: &A::NodePtr) -> Result(allocator: &A, sexp: &A::NodePtr) -> Result { @@ -369,15 +364,10 @@ pub fn rest(allocator: &A, sexp: &A::NodePtr) -> Result Result, EvalErr> { @@ -394,10 +384,10 @@ pub fn atom(allocator: &Allocator, sexp: NodePtr) -> Result, EvalErr> { pub fn proper_list( allocator: &A, sexp: &A::NodePtr, - store: bool + store: bool, ) -> Option> where - A::NodePtr: Clone + A::NodePtr: Clone, { let mut args = vec![]; let mut args_sexp = sexp.clone(); @@ -421,10 +411,11 @@ where } pub fn enlist( - allocator: &mut A, vec: &[A::NodePtr] + allocator: &mut A, + vec: &[A::NodePtr], ) -> Result where - A::NodePtr: Clone + A::NodePtr: Clone, { let mut built = allocator.import(Srcloc::start("*nil*"), NodePtr::NIL)?; @@ -483,9 +474,13 @@ pub fn fold_m( } } -pub fn equal_to(allocator: &mut A, first_: &A::NodePtr, second_: &A::NodePtr) -> bool +pub fn equal_to( + allocator: &mut A, + first_: &A::NodePtr, + second_: &A::NodePtr, +) -> bool where - A::NodePtr: Clone + A::NodePtr: Clone, { let mut first = first_.clone(); let mut second = second_.clone(); @@ -516,10 +511,9 @@ where pub fn flatten( allocator: &mut A, tree_: &A::NodePtr, - res: &mut Vec -) -where - A::NodePtr: Clone + res: &mut Vec, +) where + A::NodePtr: Clone, { let mut tree = tree_.clone(); @@ -618,11 +612,7 @@ where R: SelectNode, S: SelectNode, { - fn select_nodes( - &self, - allocator: &mut A, - n: A::NodePtr, - ) -> Result, ClError> { + fn select_nodes(&self, allocator: &mut A, n: A::NodePtr) -> Result, ClError> { let NodeSel::Cons(my_left, my_right) = &self; let l = first(allocator, &n)?; let r = rest(allocator, &n)?; diff --git a/src/classic/clvm_tools/debug.rs b/src/classic/clvm_tools/debug.rs index eb13f69af..ec99df3dd 100644 --- a/src/classic/clvm_tools/debug.rs +++ b/src/classic/clvm_tools/debug.rs @@ -10,7 +10,7 @@ use crate::classic::clvm::sexp::{enlist, proper_list, rest, First, SelectNode, T use crate::classic::clvm_tools::sha256tree::sha256tree; use crate::classic::clvm_tools::stages::stage_0::TRunProgram; -use crate::classic::clvm_tools::stages::stage_2::abstraction::{ClassicAllocator, ClError}; +use crate::classic::clvm_tools::stages::stage_2::abstraction::{ClError, ClassicAllocator}; use crate::compiler::comptypes::{CompileErr, CompilerOpts}; use crate::compiler::frontend::frontend; @@ -51,7 +51,7 @@ use crate::compiler::usecheck::check_parameters_used_compileform; /// These can be passed on and used by debuggers and such. pub struct FunctionExtraInfo where - A::NodePtr: Clone + A::NodePtr: Clone, { /// The form of the original arguments from the source code. pub args: A::NodePtr, @@ -62,12 +62,12 @@ where impl Clone for FunctionExtraInfo where - A::NodePtr: Clone + A::NodePtr: Clone, { fn clone(&self) -> Self { FunctionExtraInfo { args: self.args.clone(), - has_constants_tree: self.has_constants_tree + has_constants_tree: self.has_constants_tree, } } } @@ -114,19 +114,16 @@ pub fn build_symbol_dump( extra_info: bool, ) -> Result where - A::NodePtr: Clone + A::NodePtr: Clone, { let mut map_result: Vec = Vec::new(); for (k, v) in constants_lookup.iter() { let v_export = allocator.export(v); let vloc = allocator.loc(v); - let run_result = run_program.run_program( - allocator.allocator(), - v_export, - NodePtr::NIL, - None - ).map_err(|e| allocator.map_err(vloc.clone(), e))?; + let run_result = run_program + .run_program(allocator.allocator(), v_export, NodePtr::NIL, None) + .map_err(|e| allocator.map_err(vloc.clone(), e))?; let sha256 = sha256tree(allocator.allocator(), run_result.1).hex(); let sha_atom = allocator.new_atom(vloc.clone(), sha256.as_bytes())?; @@ -151,12 +148,22 @@ where let left_env_name_atom = allocator.new_atom(vloc.clone(), &left_env_atom)?; let serialized_args = allocator.disassemble(&extra.args, Some(0)); - let serialized_args_atom = allocator.new_atom(vloc.clone(), serialized_args.as_bytes())?; - - let left_env_value = allocator.new_atom(vloc.clone(), &[extra.has_constants_tree as u8])?; - - map_result.push(allocator.new_pair(vloc.clone(), &args_name_atom, &serialized_args_atom)?); - map_result.push(allocator.new_pair(vloc.clone(), &left_env_name_atom, &left_env_value)?); + let serialized_args_atom = + allocator.new_atom(vloc.clone(), serialized_args.as_bytes())?; + + let left_env_value = + allocator.new_atom(vloc.clone(), &[extra.has_constants_tree as u8])?; + + map_result.push(allocator.new_pair( + vloc.clone(), + &args_name_atom, + &serialized_args_atom, + )?); + map_result.push(allocator.new_pair( + vloc.clone(), + &left_env_name_atom, + &left_env_value, + )?); } } diff --git a/src/classic/clvm_tools/pattern_match.rs b/src/classic/clvm_tools/pattern_match.rs index 757bd35b5..734fd0354 100644 --- a/src/classic/clvm_tools/pattern_match.rs +++ b/src/classic/clvm_tools/pattern_match.rs @@ -1,6 +1,8 @@ use crate::classic::clvm::__type_compatibility__::{Bytes, BytesFromType}; use crate::classic::clvm::sexp::equal_to; -use crate::classic::clvm_tools::stages::stage_2::abstraction::{ASExp, BufCarrier, ClassicAllocator}; +use crate::classic::clvm_tools::stages::stage_2::abstraction::{ + ASExp, BufCarrier, ClassicAllocator, +}; use std::collections::HashMap; @@ -14,7 +16,7 @@ pub fn unify_bindings( new_value: &A::NodePtr, ) -> Option> where - A::NodePtr: Clone + A::NodePtr: Clone, { /* * Try to add a new binding to the list, rejecting it if it conflicts @@ -43,7 +45,7 @@ pub fn match_sexp( known_bindings: HashMap, ) -> Option> where - A::NodePtr: Clone + A::NodePtr: Clone, { /* * Determine if sexp matches the pattern, with the given known bindings already applied. @@ -66,73 +68,82 @@ where None } } - (ASExp::Pair(pleft, pright), _) => match (allocator.sexp(&pleft), allocator.sexp(&pright)) { - (ASExp::Atom, ASExp::Atom) => { - let left_atom = allocator.atom(&pleft); - let right_atom = allocator.atom(&pright); + (ASExp::Pair(pleft, pright), _) => { + match (allocator.sexp(&pleft), allocator.sexp(&pright)) { + (ASExp::Atom, ASExp::Atom) => { + let left_atom = allocator.atom(&pleft); + let right_atom = allocator.atom(&pright); - // This is a false positive due to Allocator lifetime. - #[allow(clippy::unnecessary_to_owned)] - match allocator.sexp(sexp) { - ASExp::Atom => { - // Expression is ($ . $), sexp is '$', result: no capture. - // Avoid double borrow. - let sexp_atom = allocator.atom(sexp); - if left_atom.as_ref() == ATOM_MATCH { - if right_atom.as_ref() == ATOM_MATCH { - if sexp_atom.as_ref() == ATOM_MATCH { + // This is a false positive due to Allocator lifetime. + #[allow(clippy::unnecessary_to_owned)] + match allocator.sexp(sexp) { + ASExp::Atom => { + // Expression is ($ . $), sexp is '$', result: no capture. + // Avoid double borrow. + let sexp_atom = allocator.atom(sexp); + if left_atom.as_ref() == ATOM_MATCH { + if right_atom.as_ref() == ATOM_MATCH { + if sexp_atom.as_ref() == ATOM_MATCH { + return Some(HashMap::new()); + } + return None; + } + + return unify_bindings( + allocator, + known_bindings, + &right_atom.as_ref().to_vec(), + sexp, + ); + } + if left_atom.as_ref() == SEXP_MATCH { + if right_atom.as_ref() == SEXP_MATCH + && sexp_atom.as_ref() == SEXP_MATCH + { return Some(HashMap::new()); } - return None; + + return unify_bindings( + allocator, + known_bindings, + // pat_right_bytes + &right_atom.as_ref().to_vec(), + sexp, + ); } - return unify_bindings( - allocator, - known_bindings, - &right_atom.as_ref().to_vec(), - sexp, - ); + None } - if left_atom.as_ref() == SEXP_MATCH { - if right_atom.as_ref() == SEXP_MATCH && sexp_atom.as_ref() == SEXP_MATCH + ASExp::Pair(sleft, sright) => { + if left_atom.as_ref() == SEXP_MATCH && right_atom.as_ref() != SEXP_MATCH { - return Some(HashMap::new()); + return unify_bindings( + allocator, + known_bindings, + // pat_right_bytes + &right_atom.as_ref().to_vec(), + sexp, + ); } - return unify_bindings( - allocator, - known_bindings, - // pat_right_bytes - &right_atom.as_ref().to_vec(), - sexp, - ); + match_sexp(allocator, &pleft, &sleft, known_bindings).and_then( + |new_bindings| { + match_sexp(allocator, &pright, &sright, new_bindings) + }, + ) } - - None } + } + _ => match allocator.sexp(sexp) { + ASExp::Atom => None, ASExp::Pair(sleft, sright) => { - if left_atom.as_ref() == SEXP_MATCH && right_atom.as_ref() != SEXP_MATCH { - return unify_bindings( - allocator, - known_bindings, - // pat_right_bytes - &right_atom.as_ref().to_vec(), - sexp, - ); - } - match_sexp(allocator, &pleft, &sleft, known_bindings).and_then( |new_bindings| match_sexp(allocator, &pright, &sright, new_bindings), ) } - } + }, } - _ => match allocator.sexp(sexp) { - ASExp::Atom => None, - ASExp::Pair(sleft, sright) => match_sexp(allocator, &pleft, &sleft, known_bindings) - .and_then(|new_bindings| match_sexp(allocator, &pright, &sright, new_bindings)), - }, - }, + } (ASExp::Atom, _) => None, } } diff --git a/src/classic/clvm_tools/stages/stage_2/abstraction.rs b/src/classic/clvm_tools/stages/stage_2/abstraction.rs index 83bc0b0ea..0cc15eae2 100644 --- a/src/classic/clvm_tools/stages/stage_2/abstraction.rs +++ b/src/classic/clvm_tools/stages/stage_2/abstraction.rs @@ -8,7 +8,7 @@ use crate::compiler::srcloc::Srcloc; pub enum ASExp { Pair(T, T), - Atom + Atom, } #[derive(Debug)] @@ -57,7 +57,12 @@ pub trait ClassicAllocator { fn node_equal(&self, a: &Self::NodePtr, b: &Self::NodePtr) -> bool; fn map_err(&self, loc: Srcloc, err: EvalErr) -> ClError; fn new_atom(&mut self, loc: Srcloc, value: &[u8]) -> Result; - fn new_pair(&mut self, loc: Srcloc, a: &Self::NodePtr, b: &Self::NodePtr) -> Result; + fn new_pair( + &mut self, + loc: Srcloc, + a: &Self::NodePtr, + b: &Self::NodePtr, + ) -> Result; fn import(&mut self, loc: Srcloc, node: NodePtr) -> Result; fn export(&self, node: &Self::NodePtr) -> NodePtr; } @@ -74,10 +79,8 @@ impl ClassicAllocator for Allocator { } fn sexp(&self, node: &Self::NodePtr) -> ASExp { match clvmr::Allocator::sexp(self, *node) { - SExp::Pair(a, b) => { - ASExp::Pair(a, b) - } - SExp::Atom => ASExp::Atom + SExp::Pair(a, b) => ASExp::Pair(a, b), + SExp::Atom => ASExp::Atom, } } fn atom<'a>(&'a self, node: &Self::NodePtr) -> BufHolder<'a> { @@ -99,11 +102,18 @@ impl ClassicAllocator for Allocator { ClError(loc, err) } fn new_atom(&mut self, loc: Srcloc, value: &[u8]) -> Result { - let new_atom = clvmr::Allocator::new_atom(self, value).map_err(|e| self.map_err(loc.clone(), e))?; + let new_atom = + clvmr::Allocator::new_atom(self, value).map_err(|e| self.map_err(loc.clone(), e))?; self.import(loc, new_atom) } - fn new_pair(&mut self, loc: Srcloc, a: &Self::NodePtr, b: &Self::NodePtr) -> Result { - let new_pair = clvmr::Allocator::new_pair(self, *a, *b).map_err(|e| self.map_err(loc.clone(), e))?; + fn new_pair( + &mut self, + loc: Srcloc, + a: &Self::NodePtr, + b: &Self::NodePtr, + ) -> Result { + let new_pair = + clvmr::Allocator::new_pair(self, *a, *b).map_err(|e| self.map_err(loc.clone(), e))?; self.import(loc, new_pair) } fn import(&mut self, _loc: Srcloc, node: NodePtr) -> Result { diff --git a/src/classic/clvm_tools/stages/stage_2/compile.rs b/src/classic/clvm_tools/stages/stage_2/compile.rs index 236197ea7..47dbf615f 100644 --- a/src/classic/clvm_tools/stages/stage_2/compile.rs +++ b/src/classic/clvm_tools/stages/stage_2/compile.rs @@ -3,17 +3,19 @@ use std::rc::Rc; use clvm_rs::allocator::{Allocator, NodePtr, SExp}; use clvm_rs::error::EvalErr; -use clvm_rs::reduction::{Reduction}; +use clvm_rs::reduction::Reduction; use crate::classic::clvm::__type_compatibility__::{Bytes, BytesFromType}; use crate::classic::clvm::sexp::{enlist, first, map_m, proper_list, rest}; use crate::classic::clvm::OPERATORS_LATEST_VERSION; use crate::classic::clvm::{keyword_from_atom, keyword_to_atom}; -use crate::classic::clvm_tools::binutils::{assemble}; +use crate::classic::clvm_tools::binutils::assemble; use crate::classic::clvm_tools::node_path::NodePath; use crate::classic::clvm_tools::stages::stage_0::TRunProgram; -use crate::classic::clvm_tools::stages::stage_2::abstraction::{ASExp, BufCarrier, ClassicAllocator, ClError}; +use crate::classic::clvm_tools::stages::stage_2::abstraction::{ + ASExp, BufCarrier, ClError, ClassicAllocator, +}; use crate::classic::clvm_tools::stages::stage_2::defaults::default_macro_lookup; use crate::classic::clvm_tools::stages::stage_2::helpers::{brun, evaluate, quote}; use crate::classic::clvm_tools::stages::stage_2::module::compile_mod; @@ -52,7 +54,7 @@ struct Closure<'a, A: ClassicAllocator> { fn compile_bindings<'a, A: ClassicAllocator>() -> HashMap, Closure<'a, A>> where - A::NodePtr: Clone + A::NodePtr: Clone, { let mut bindings = HashMap::new(); let bindings_source = vec![ @@ -101,7 +103,7 @@ fn com_qq( sexp: &A::NodePtr, ) -> Result where - A::NodePtr: Clone + A::NodePtr: Clone, { if DIAG_OUTPUT { println!("com_qq {} {}", ident, allocator.disassemble(sexp, None)); @@ -118,7 +120,7 @@ pub fn compile_qq( level: usize, ) -> Result where - A::NodePtr: Clone + A::NodePtr: Clone, { /* * (qq ATOM) => (q . ATOM) @@ -143,7 +145,14 @@ where if op_atom.as_ref() == qq_atom() { let op_loc = allocator.loc(&op); let cons_atom = allocator.new_atom(op_loc, &[4])?; - let subexp = compile_qq(allocator, &sexp_rest, macro_lookup, symbol_table, runner.clone(), level+1)?; + let subexp = compile_qq( + allocator, + &sexp_rest, + macro_lookup, + symbol_table, + runner.clone(), + level + 1, + )?; let quoted_null = quote(allocator, &nil_import)?; let consed = enlist(allocator, &[cons_atom.clone(), subexp, quoted_null])?; let run_list = enlist(allocator, &[cons_atom, op, consed])?; @@ -153,7 +162,7 @@ where macro_lookup, symbol_table, runner, - &run_list + &run_list, ); } else if op_atom.as_ref() == unquote_atom() { // opbuf @@ -166,15 +175,23 @@ where macro_lookup, symbol_table, runner, - &sexp_rf + &sexp_rf, ); } // (qq (a . B)) => (c (qq a) (qq B)) let cons_atom = allocator.new_atom(op_loc, &[4])?; - let subexp = compile_qq(allocator, &sexp_rest, macro_lookup, symbol_table, runner.clone(), level-1)?; + let subexp = compile_qq( + allocator, + &sexp_rest, + macro_lookup, + symbol_table, + runner.clone(), + level - 1, + )?; let quoted_null = quote(allocator, &nil_import)?; - let consed_subexp = enlist(allocator, &[cons_atom.clone(), subexp, quoted_null])?; + let consed_subexp = + enlist(allocator, &[cons_atom.clone(), subexp, quoted_null])?; let run_list = enlist(allocator, &[cons_atom, op, consed_subexp])?; return com_qq( @@ -183,7 +200,7 @@ where macro_lookup, symbol_table, runner, - &run_list + &run_list, ); } } @@ -193,8 +210,22 @@ where let qq = allocator.new_atom(loc, &qq_atom())?; let qq_l = enlist(allocator, &[qq.clone(), op])?; let qq_r = enlist(allocator, &[qq, sexp_rest])?; - let compiled_l = com_qq(allocator, "A".to_string(), macro_lookup, symbol_table, runner.clone(), &qq_l)?; - let compiled_r = com_qq(allocator, "B".to_string(), macro_lookup, symbol_table, runner, &qq_r)?; + let compiled_l = com_qq( + allocator, + "A".to_string(), + macro_lookup, + symbol_table, + runner.clone(), + &qq_l, + )?; + let compiled_r = com_qq( + allocator, + "B".to_string(), + macro_lookup, + symbol_table, + runner, + &qq_r, + )?; enlist(allocator, &[cons_atom, compiled_l, compiled_r]) } } @@ -209,7 +240,7 @@ pub fn compile_macros( _level: usize, ) -> Result where - A::NodePtr: Clone + A::NodePtr: Clone, { quote(allocator, macro_lookup) } @@ -223,16 +254,19 @@ pub fn compile_symbols( _level: usize, ) -> Result where - A::NodePtr: Clone + A::NodePtr: Clone, { quote(allocator, symbol_table) } // # Transform "quote" to "q" everywhere. Note that quote will not be compiled if behind qq. // # Overrides symbol table defns. -fn lower_quote_(allocator: &mut A, prog: &A::NodePtr) -> Result +fn lower_quote_( + allocator: &mut A, + prog: &A::NodePtr, +) -> Result where - A::NodePtr: Clone + A::NodePtr: Clone, { let loc = allocator.loc(prog); let exported = allocator.export(prog); @@ -288,9 +322,12 @@ where Ok(prog.clone()) } -pub fn lower_quote(allocator: &mut A, prog: &A::NodePtr) -> Result +pub fn lower_quote( + allocator: &mut A, + prog: &A::NodePtr, +) -> Result where - A::NodePtr: Clone + A::NodePtr: Clone, { let res = lower_quote_(allocator, prog); if DIAG_OUTPUT { @@ -315,35 +352,22 @@ fn try_expand_macro_for_atom_( symbol_table: &A::NodePtr, ) -> Result where - A::NodePtr: Clone + A::NodePtr: Clone, { let loc = allocator.loc(macro_code); let com_atom = allocator.new_atom(loc.clone(), "com".as_bytes())?; let exported_macro = allocator.export(macro_code); let exported_prog = allocator.export(prog_rest); - let post_prog = brun( - allocator.allocator(), - exported_macro, - exported_prog, - )?; + let post_prog = brun(allocator.allocator(), exported_macro, exported_prog)?; let imported_post = allocator.import(loc.clone(), post_prog)?; let quoted_macros = quote(allocator, macro_lookup)?; let quoted_symbols = quote(allocator, symbol_table)?; let to_eval = enlist( allocator, - &[ - com_atom, - imported_post, - quoted_macros, - quoted_symbols - ] + &[com_atom, imported_post, quoted_macros, quoted_symbols], )?; let top_path = allocator.new_atom(loc, NodePath::new(None).as_path().data())?; - evaluate( - allocator, - &to_eval, - &top_path - ).map(|x| { + evaluate(allocator, &to_eval, &top_path).map(|x| { if DIAG_OUTPUT { print!( "TRY_EXPAND_MACRO {} WITH {} GIVES {} MACROS {} SYMBOLS {}", @@ -366,15 +390,9 @@ pub fn try_expand_macro_for_atom( symbol_table: &A::NodePtr, ) -> Result where - A::NodePtr: Clone + A::NodePtr: Clone, { - try_expand_macro_for_atom_( - allocator, - macro_code, - prog_rest, - macro_lookup, - symbol_table - ) + try_expand_macro_for_atom_(allocator, macro_code, prog_rest, macro_lookup, symbol_table) } fn get_macro_program( @@ -383,7 +401,7 @@ fn get_macro_program( macro_lookup: &A::NodePtr, ) -> Result, ClError> where - A::NodePtr: Clone + A::NodePtr: Clone, { if let Some(mlist) = proper_list(allocator, ¯o_lookup, true) { for macro_pair in mlist { @@ -429,12 +447,11 @@ fn transform_program_atom( symbol_table: &A::NodePtr, ) -> Result where - A::NodePtr: Clone + A::NodePtr: Clone, { let loc = allocator.loc(prog); if a == b"@" { - return allocator - .new_atom(loc, NodePath::new(None).as_path().data()) + return allocator.new_atom(loc, NodePath::new(None).as_path().data()); } match proper_list(allocator, &symbol_table, true) { @@ -484,7 +501,7 @@ fn compile_operator_atom( run_program: Rc, ) -> Result, ClError> where - A::NodePtr: Clone + A::NodePtr: Clone, { let compile_bindings = compile_bindings(); @@ -500,13 +517,16 @@ where ¯o_lookup, &symbol_table, run_program.clone(), - 1 + 1, )?; let quoted_post_prog = quote(allocator, &post_prog)?; let loc = allocator.loc(prog); let top_atom = allocator.new_atom(loc, NodePath::new(None).as_path().data())?; let _ = if DIAG_OUTPUT { - print!("COMPILE_BINDINGS {}", allocator.disassemble("ed_post_prog, None)); + print!( + "COMPILE_BINDINGS {}", + allocator.disassemble("ed_post_prog, None) + ); }; return evaluate(allocator, "ed_post_prog, &top_atom).map(Some); } @@ -526,7 +546,7 @@ fn find_symbol_match( symbol_table: &A::NodePtr, ) -> Result>, ClError> where - A::NodePtr: Clone + A::NodePtr: Clone, { if let Some(symlist) = proper_list(allocator, &symbol_table, true) { for sym in symlist { @@ -574,24 +594,22 @@ fn compile_application( run_program: Rc, ) -> Result where - A::NodePtr: Clone + A::NodePtr: Clone, { let mut compiled_args = vec![operator.clone()]; let loc = allocator.loc(prog); let exported_prog = allocator.export(prog); - let error_result = Err( - ClError( - loc, - EvalErr::InternalError( - exported_prog, - format!( - "can't compile {}, unknown operator", - allocator.disassemble(&prog, None) - ), - ) - ) - ); + let error_result = Err(ClError( + loc, + EvalErr::InternalError( + exported_prog, + format!( + "can't compile {}, unknown operator", + allocator.disassemble(&prog, None) + ), + ), + )); if *opbuf == vec![1] || *opbuf == vec![b'q'] { let rest_loc = allocator.loc(&rest); @@ -617,42 +635,44 @@ where if PASS_THROUGH_OPERATORS.contains(opbuf) || (!opbuf.is_empty() && opbuf[0] == b'_') { Ok(r) } else { - find_symbol_match( - allocator, - opbuf, - &r, - symbol_table - ).and_then(|x| match x { - Some(SymbolResult::Direct(v)) => { Ok(v) }, - Some(SymbolResult::Matched(_symbol,value)) => { + find_symbol_match(allocator, opbuf, &r, symbol_table).and_then(|x| match x { + Some(SymbolResult::Direct(v)) => Ok(v), + Some(SymbolResult::Matched(_symbol, value)) => { match proper_list(allocator, &rest, true) { Some(proglist) => { let loc = allocator.loc(&value); let apply_atom = allocator.new_atom(loc.clone(), &[2])?; - let list_atom = allocator.new_atom(loc.clone(), "list".as_bytes())?; + let list_atom = + allocator.new_atom(loc.clone(), "list".as_bytes())?; let cons_atom = allocator.new_atom(loc.clone(), &[4])?; let com_atom = allocator.new_atom(loc.clone(), "com".as_bytes())?; let opt_atom = allocator.new_atom(loc.clone(), "opt".as_bytes())?; - let top_atom = allocator.new_atom(loc.clone(), NodePath::new(None).as_path().data())?; - let left_atom = allocator.new_atom(loc.clone(), NodePath::new(None).first().as_path().data())?; + let top_atom = allocator + .new_atom(loc.clone(), NodePath::new(None).as_path().data())?; + let left_atom = allocator.new_atom( + loc.clone(), + NodePath::new(None).first().as_path().data(), + )?; let enlisted = enlist(allocator, &proglist)?; - let list_application = allocator.new_pair(loc, &list_atom, &enlisted)?; + let list_application = + allocator.new_pair(loc, &list_atom, &enlisted)?; let quoted_list = quote(allocator, &list_application)?; let quoted_macros = quote(allocator, ¯o_lookup)?; let quoted_symbols = quote(allocator, &symbol_table)?; - let compiled = enlist(allocator, &[com_atom, quoted_list, quoted_macros, quoted_symbols])?; + let compiled = enlist( + allocator, + &[com_atom, quoted_list, quoted_macros, quoted_symbols], + )?; let to_run = enlist(allocator, &[opt_atom, compiled])?; let new_args = evaluate(allocator, &to_run, &top_atom)?; - let cons_enlisted = enlist(allocator, &[cons_atom, left_atom, new_args])?; - enlist( - allocator, - &[apply_atom, value, cons_enlisted] - ) - }, - None => { error_result } + let cons_enlisted = + enlist(allocator, &[cons_atom, left_atom, new_args])?; + enlist(allocator, &[apply_atom, value, cons_enlisted]) + } + None => error_result, } - }, - None => { error_result } + } + None => error_result, }) } } @@ -669,7 +689,7 @@ pub fn do_com_prog( run_program: Rc, ) -> Result where - A::NodePtr: Clone + A::NodePtr: Clone, { if DIAG_OUTPUT { println!( @@ -702,7 +722,7 @@ fn do_com_prog_( run_program: Rc, ) -> Result where - A::NodePtr: Clone + A::NodePtr: Clone, { /* * Turn the given program `prog` into a clvm program using @@ -803,7 +823,7 @@ pub fn do_com_prog_for_dialect( sexp: &A::NodePtr, ) -> Result where - A::NodePtr: Clone + A::NodePtr: Clone, { match allocator.sexp(sexp) { ASExp::Pair(prog, extras) => { @@ -851,15 +871,13 @@ where } _ => { let exported = allocator.export(&sexp); - Err( - ClError( - allocator.loc(sexp), - EvalErr::InternalError( - exported, - "Program is not a pair in do_com_prog".to_string(), - ) - ) - ) + Err(ClError( + allocator.loc(sexp), + EvalErr::InternalError( + exported, + "Program is not a pair in do_com_prog".to_string(), + ), + )) } } } @@ -897,12 +915,13 @@ pub fn get_search_paths( allocator: &mut A, ) -> Result, ClError> where - A::NodePtr: Clone + A::NodePtr: Clone, { let search_paths_result = ((|| { let search_paths_prog = assemble(allocator.allocator(), "(_get_include_paths)")?; runner.run_program(allocator.allocator(), search_paths_prog, NodePtr::NIL, None) - })()).map_err(|e| ClError(loc.clone(), e))?; + })()) + .map_err(|e| ClError(loc.clone(), e))?; let mut res = Vec::new(); let search_paths_result_import = allocator.import(loc, search_paths_result.1)?; if let Some(l) = proper_list(allocator, &search_paths_result_import, true) { diff --git a/src/classic/clvm_tools/stages/stage_2/defaults.rs b/src/classic/clvm_tools/stages/stage_2/defaults.rs index 1f60462ce..c908fdedb 100644 --- a/src/classic/clvm_tools/stages/stage_2/defaults.rs +++ b/src/classic/clvm_tools/stages/stage_2/defaults.rs @@ -1,10 +1,10 @@ use std::rc::Rc; -use clvm_rs::allocator::{NodePtr}; +use clvm_rs::allocator::NodePtr; use crate::classic::clvm_tools::binutils::assemble; use crate::classic::clvm_tools::stages::stage_0::TRunProgram; -use crate::classic::clvm_tools::stages::stage_2::abstraction::{ClassicAllocator}; +use crate::classic::clvm_tools::stages::stage_2::abstraction::ClassicAllocator; use crate::compiler::srcloc::Srcloc; /* @@ -115,17 +115,33 @@ fn build_default_macro_lookup( let macro_sexp = assemble(allocator.allocator(), macro_src).unwrap(); let imported_macro_sexp = allocator.import(macro_loc.clone(), macro_sexp).unwrap(); let env = allocator - .new_pair(macro_loc.clone(), &imported_macro_sexp, &default_macro_lookup) + .new_pair( + macro_loc.clone(), + &imported_macro_sexp, + &default_macro_lookup, + ) .unwrap(); let exported_env = allocator.export(&env); - let new_macro = eval_f.run_program(allocator.allocator(), run, exported_env, None).unwrap().1; + let new_macro = eval_f + .run_program(allocator.allocator(), run, exported_env, None) + .unwrap() + .1; let imported_new_macro = allocator.import(macro_loc.clone(), new_macro).unwrap(); - default_macro_lookup = allocator.new_pair(macro_loc.clone(), &imported_new_macro, &default_macro_lookup).unwrap(); + default_macro_lookup = allocator + .new_pair( + macro_loc.clone(), + &imported_new_macro, + &default_macro_lookup, + ) + .unwrap(); } default_macro_lookup } -pub fn default_macro_lookup(allocator: &mut A, runner: Rc) -> A::NodePtr { +pub fn default_macro_lookup( + allocator: &mut A, + runner: Rc, +) -> A::NodePtr { let macro_srcs: Vec = default_macros_src().iter().map(|s| s.to_string()).collect(); build_default_macro_lookup(allocator, runner.clone(), ¯o_srcs) } diff --git a/src/classic/clvm_tools/stages/stage_2/helpers.rs b/src/classic/clvm_tools/stages/stage_2/helpers.rs index 0e58e57a2..2003b2937 100644 --- a/src/classic/clvm_tools/stages/stage_2/helpers.rs +++ b/src/classic/clvm_tools/stages/stage_2/helpers.rs @@ -2,7 +2,7 @@ use clvm_rs::allocator::{Allocator, NodePtr}; use crate::classic::clvm::sexp::enlist; use crate::classic::clvm_tools::node_path::NodePath; -use crate::classic::clvm_tools::stages::stage_2::abstraction::{ClassicAllocator, ClError}; +use crate::classic::clvm_tools::stages::stage_2::abstraction::{ClError, ClassicAllocator}; lazy_static! { pub static ref QUOTE_ATOM: Vec = vec![1]; @@ -10,7 +10,10 @@ lazy_static! { pub static ref COM_ATOM: Vec = vec![b'c', b'o', b'm']; } -pub fn quote(allocator: &mut A, sexp: &A::NodePtr) -> Result { +pub fn quote( + allocator: &mut A, + sexp: &A::NodePtr, +) -> Result { allocator .new_atom(allocator.loc(&sexp), "E_ATOM) .and_then(|q| allocator.new_pair(allocator.loc(&sexp), &q, &sexp)) @@ -24,7 +27,7 @@ pub fn evaluate( args: &A::NodePtr, ) -> Result where - A::NodePtr: Clone + A::NodePtr: Clone, { let loc = allocator.loc(prog); let a = allocator.new_atom(loc, &APPLY_ATOM)?; @@ -37,7 +40,7 @@ pub fn run( macro_lookup: &A::NodePtr, ) -> Result where - A::NodePtr: Clone + A::NodePtr: Clone, { /* * PROG => (e (com (q . PROG) (mac)) ARGS) diff --git a/src/classic/clvm_tools/stages/stage_2/inline.rs b/src/classic/clvm_tools/stages/stage_2/inline.rs index 6247ab73d..59c74471f 100644 --- a/src/classic/clvm_tools/stages/stage_2/inline.rs +++ b/src/classic/clvm_tools/stages/stage_2/inline.rs @@ -1,7 +1,9 @@ use crate::classic::clvm::__type_compatibility__::{bi_one, bi_zero}; use crate::classic::clvm::sexp::{enlist, proper_list}; +use crate::classic::clvm_tools::stages::stage_2::abstraction::{ + ASExp, BufCarrier, ClError, ClassicAllocator, +}; use crate::compiler::gensym::gensym; -use crate::classic::clvm_tools::stages::stage_2::abstraction::{ASExp, BufCarrier, ClassicAllocator, ClError}; use crate::util::Number; use num_bigint::ToBigInt; @@ -16,7 +18,7 @@ pub fn is_at_capture( tree_rest: &A::NodePtr, ) -> Option<(A::NodePtr, A::NodePtr)> where - A::NodePtr: Clone + A::NodePtr: Clone, { if let (ASExp::Atom, Some(spec)) = ( allocator.sexp(tree_first), @@ -32,9 +34,12 @@ where } // (unquote X) -fn wrap_in_unquote(allocator: &mut A, code: &A::NodePtr) -> Result +fn wrap_in_unquote( + allocator: &mut A, + code: &A::NodePtr, +) -> Result where - A::NodePtr: Clone + A::NodePtr: Clone, { let loc = allocator.loc(code); let unquote_atom = allocator.new_atom(loc, "unquote".as_bytes())?; @@ -42,9 +47,12 @@ where } // (__chia__enlist X) -fn wrap_in_compile_time_list(allocator: &mut A, code: &A::NodePtr) -> Result +fn wrap_in_compile_time_list( + allocator: &mut A, + code: &A::NodePtr, +) -> Result where - A::NodePtr: Clone + A::NodePtr: Clone, { let loc = allocator.loc(code); let chia_enlist_atom = allocator.new_atom(loc, "__chia__enlist".as_bytes())?; @@ -69,7 +77,7 @@ fn wrap_path_selection( wrapped: &A::NodePtr, ) -> Result where - A::NodePtr: Clone + A::NodePtr: Clone, { let mut operator_stack = Vec::new(); let mut tail = wrapped.clone(); @@ -101,7 +109,7 @@ fn formulate_path_selections_for_destructuring_arg( selections: &mut HashMap, A::NodePtr>, ) -> Result where - A::NodePtr: Clone + A::NodePtr: Clone, { let loc = allocator.loc(arg_sexp); match allocator.sexp(&arg_sexp) { @@ -109,15 +117,16 @@ where let next_depth = arg_depth.clone() * 2_u32.to_bigint().unwrap(); if let Some((capture, substructure)) = is_at_capture(allocator, &a, &b) { if let ASExp::Atom = allocator.sexp(&capture) { - let (new_arg_path, new_arg_depth, tail) = - if let Some(prev_ref) = referenced_from { - (arg_path, arg_depth, prev_ref) - } else { - let capture_code = wrap_in_unquote(allocator, &capture)?; - let qtail = - wrap_path_selection(allocator, arg_path + arg_depth, &capture_code)?; - (bi_zero(), bi_one(), qtail) - }; + let (new_arg_path, new_arg_depth, tail) = if let Some(prev_ref) = + referenced_from + { + (arg_path, arg_depth, prev_ref) + } else { + let capture_code = wrap_in_unquote(allocator, &capture)?; + let qtail = + wrap_path_selection(allocator, arg_path + arg_depth, &capture_code)?; + (bi_zero(), bi_one(), qtail) + }; // Was cbuf from capture. let capture_atom = allocator.atom(&capture); @@ -246,7 +255,7 @@ pub fn formulate_path_selections_for_destructuring( selections: &mut HashMap, A::NodePtr>, ) -> Result where - A::NodePtr: Clone + A::NodePtr: Clone, { if let ASExp::Pair(a, b) = allocator.sexp(args_sexp) { if let Some((capture, substructure)) = is_at_capture(allocator, &a, &b) { @@ -288,7 +297,7 @@ where // are passed down to the macro body that gets created for the inline function. pub fn is_inline_destructure( allocator: &mut A, - args_sexp: &A::NodePtr + args_sexp: &A::NodePtr, ) -> bool { if let ASExp::Pair(a, b) = allocator.sexp(args_sexp) { if let ASExp::Pair(_, _) = allocator.sexp(&a) { diff --git a/src/classic/clvm_tools/stages/stage_2/module.rs b/src/classic/clvm_tools/stages/stage_2/module.rs index 442751143..0790676aa 100644 --- a/src/classic/clvm_tools/stages/stage_2/module.rs +++ b/src/classic/clvm_tools/stages/stage_2/module.rs @@ -2,19 +2,21 @@ use std::collections::HashMap; use std::collections::HashSet; use std::rc::Rc; -use clvm_rs::allocator::{NodePtr}; +use clvm_rs::allocator::NodePtr; use clvm_rs::error::EvalErr; use crate::classic::clvm::__type_compatibility__::{Bytes, BytesFromType}; use crate::classic::clvm::sexp::{ - enlist, first, flatten, fold_m, map_m, nonempty_last, proper_list, rest, First, - NodeSel, Rest, SelectNode, ThisNode, + enlist, first, flatten, fold_m, map_m, nonempty_last, proper_list, rest, First, NodeSel, Rest, + SelectNode, ThisNode, }; use crate::classic::clvm_tools::debug::{build_symbol_dump, FunctionExtraInfo}; use crate::classic::clvm_tools::node_path::NodePath; use crate::classic::clvm_tools::stages::assemble; use crate::classic::clvm_tools::stages::stage_0::TRunProgram; -use crate::classic::clvm_tools::stages::stage_2::abstraction::{ASExp, BufCarrier, ClassicAllocator, ClError}; +use crate::classic::clvm_tools::stages::stage_2::abstraction::{ + ASExp, BufCarrier, ClError, ClassicAllocator, +}; use crate::classic::clvm_tools::stages::stage_2::helpers::{evaluate, quote}; use crate::classic::clvm_tools::stages::stage_2::inline::{ formulate_path_selections_for_destructuring, is_at_capture, is_inline_destructure, @@ -35,7 +37,7 @@ struct CollectionResult { struct CompileOutput where - A::NodePtr: Clone + A::NodePtr: Clone, { pub functions: HashMap, A::NodePtr>, pub symbols_extra_info: HashMap, FunctionExtraInfo>, @@ -43,7 +45,7 @@ where impl Default for CompileOutput where - A::NodePtr: Clone + A::NodePtr: Clone, { fn default() -> Self { CompileOutput { @@ -55,11 +57,11 @@ where impl CompileOutput where - A::NodePtr: Clone + A::NodePtr: Clone, { pub fn add_definitions(&mut self, other: &CompileOutput) where - A::NodePtr: Clone + A::NodePtr: Clone, { for (n, v) in other.functions.iter() { self.functions.insert(n.to_vec(), v.clone()); @@ -73,10 +75,10 @@ where // export type TBuildTree = Bytes | Tuple | []; fn build_tree( allocator: &mut A, - items: &[Vec] + items: &[Vec], ) -> Result where - A::NodePtr: Clone + A::NodePtr: Clone, { if items.is_empty() { let imported_nil = allocator.import(Srcloc::start("*nil*"), NodePtr::NIL)?; @@ -93,9 +95,12 @@ where } // export type TBuildTreeProgram = SExp | [Bytes, TBuildTree, TBuildTree] | [Tuple]; -fn build_tree_program(allocator: &mut A, items: &[A::NodePtr]) -> Result +fn build_tree_program( + allocator: &mut A, + items: &[A::NodePtr], +) -> Result where - A::NodePtr: Clone + A::NodePtr: Clone, { // This function takes a Python list of items and turns it into a program that // a binary tree of the items, suitable for casting to an s-expression. @@ -126,7 +131,7 @@ fn build_used_constants_names( macros: &[(Vec, A::NodePtr)], ) -> Result>, ClError> where - A::NodePtr: Clone + A::NodePtr: Clone, { /* Do a naĂ¯ve pruning of unused symbols. It may be too big, but it shouldn't @@ -215,20 +220,15 @@ fn parse_include( run_program: Rc, ) -> Result<(), ClError> where - A::NodePtr: Clone + A::NodePtr: Clone, { let loc = allocator.loc(name); - let prog = assemble( - allocator.allocator(), - "(_read (_full_path_for_name 1))" - ).map_err(|e| ClError(loc.clone(), e))?; + let prog = assemble(allocator.allocator(), "(_read (_full_path_for_name 1))") + .map_err(|e| ClError(loc.clone(), e))?; let name_export = allocator.export(name); - let assembled_sexp = run_program.run_program( - allocator.allocator(), - prog, - name_export, - None - ).map_err(|e| ClError(loc.clone(), e))?; + let assembled_sexp = run_program + .run_program(allocator.allocator(), prog, name_export, None) + .map_err(|e| ClError(loc.clone(), e))?; let assembled_sexp_import = allocator.import(loc.clone(), assembled_sexp.1)?; if let Some(assembled) = proper_list(allocator, &assembled_sexp_import, true) { for sexp in assembled { @@ -240,18 +240,16 @@ where constants, delayed_constants, macros, - run_program.clone() + run_program.clone(), )?; - }; + } return Ok(()); } - Err( - ClError( - loc, - EvalErr::InternalError(name_export, "include returned malformed result".to_string()) - ) - ) + Err(ClError( + loc, + EvalErr::InternalError(name_export, "include returned malformed result".to_string()), + )) } fn unquote_args( @@ -261,7 +259,7 @@ fn unquote_args( matches: &HashMap, A::NodePtr>, ) -> Result where - A::NodePtr: Clone + A::NodePtr: Clone, { match allocator.sexp(code) { ASExp::Atom => { @@ -300,7 +298,7 @@ fn defun_inline_to_macro( declaration_sexp: &A::NodePtr, ) -> Result where - A::NodePtr: Clone + A::NodePtr: Clone, { let Rest::Here(NodeSel::Cons(name, NodeSel::Cons(arg_spec, First::Here(code)))) = Rest::Here(NodeSel::Cons( @@ -366,7 +364,7 @@ fn parse_mod_sexp( run_program: Rc, ) -> Result<(), ClError> where - A::NodePtr: Clone + A::NodePtr: Clone, { let NodeSel::Cons(op_node, First::Here(name_node)) = NodeSel::Cons(ThisNode::Here, First::Here(ThisNode::Here)) @@ -408,18 +406,16 @@ where } else if namespace.contains(&name) { let loc = allocator.loc(declaration_sexp); let dec_export = allocator.export(declaration_sexp); - Err( - ClError( - loc, - EvalErr::InternalError( - dec_export, - format!( - "symbol \"{}\" redefined", - Bytes::new(Some(BytesFromType::Raw(name))).decode() - ), - ) - ) - ) + Err(ClError( + loc, + EvalErr::InternalError( + dec_export, + format!( + "symbol \"{}\" redefined", + Bytes::new(Some(BytesFromType::Raw(name))).decode() + ), + ), + )) } else { namespace.insert(name.to_vec()); @@ -428,7 +424,8 @@ where Ok(()) } else if op == "defun".as_bytes() { let Rest::Here(Rest::Here(declaration_sexp_rr)) = - Rest::Here(Rest::Here(ThisNode::Here)).select_nodes(allocator, declaration_sexp.clone())?; + Rest::Here(Rest::Here(ThisNode::Here)) + .select_nodes(allocator, declaration_sexp.clone())?; functions.insert(name, declaration_sexp_rr); Ok(()) } else if op == "defun-inline".as_bytes() { @@ -452,15 +449,13 @@ where } else { let loc = allocator.loc(declaration_sexp); let export_sexp = allocator.export(declaration_sexp); - Err( - ClError( - loc, - EvalErr::InternalError( - export_sexp, - "expected defun, defmacro, defconst, compile-file or defconstant".to_string(), - ) - ) - ) + Err(ClError( + loc, + EvalErr::InternalError( + export_sexp, + "expected defun, defmacro, defconst, compile-file or defconstant".to_string(), + ), + )) } } } @@ -473,7 +468,7 @@ fn compile_mod_stage_1( produce_extra_info: bool, ) -> Result, ClError> where - A::NodePtr: Clone + A::NodePtr: Clone, { // stage 1: collect up names of globals (functions, constants, macros) m! { @@ -648,7 +643,7 @@ fn symbol_table_for_tree( root_node: &NodePath, ) -> Result)>, ClError> where - A::NodePtr: Clone + A::NodePtr: Clone, { if allocator.is_nil(tree) { return Ok(Vec::new()); @@ -661,14 +656,16 @@ where let right_bytes = NodePath::new(None).rest(); let NodeSel::Cons(tree_first, tree_rest) = - NodeSel::Cons(ThisNode::Here, ThisNode::Here).select_nodes(allocator, tree.clone())?; + NodeSel::Cons(ThisNode::Here, ThisNode::Here) + .select_nodes(allocator, tree.clone())?; // Allow haskell-like @ capture for destructuring. // If we encounter a form like (@ name substructure) then // treat it as though name captures the current path but // we also continue evaluating at the current position. let mut result_fin = Vec::new(); - if let Some((capture, destructure)) = is_at_capture(allocator, &tree_first, &tree_rest) { + if let Some((capture, destructure)) = is_at_capture(allocator, &tree_first, &tree_rest) + { // Push the given name here. result_fin.push((capture, root_node.as_path().data().to_vec())); @@ -697,7 +694,7 @@ fn build_macro_lookup_program( run_program: Rc, ) -> Result where - A::NodePtr: Clone + A::NodePtr: Clone, { let loc = allocator.loc(macro_lookup); let com_atom = allocator.new_atom(loc.clone(), "com".as_bytes())?; @@ -709,17 +706,19 @@ where fold_m( allocator, &|allocator, macro_lookup_program, macro_def: &(Vec, A::NodePtr)| { - let cons_list = - enlist( - allocator, - &[cons_atom.clone(), macro_def.1.clone(), macro_lookup_program.clone()] - )?; + let cons_list = enlist( + allocator, + &[ + cons_atom.clone(), + macro_def.1.clone(), + macro_lookup_program.clone(), + ], + )?; let quoted_to_compile = quote(allocator, &cons_list)?; - let compile_form = - enlist( - allocator, - &[com_atom.clone(), quoted_to_compile, macro_lookup_program] - )?; + let compile_form = enlist( + allocator, + &[com_atom.clone(), quoted_to_compile, macro_lookup_program], + )?; let opt_form = enlist(allocator, &[opt_atom.clone(), compile_form])?; let loc = allocator.loc(&opt_form); let top_atom = allocator.new_atom(loc, NodePath::new(None).as_path().data())?; @@ -727,7 +726,7 @@ where optimize_sexp(allocator, ¯o_evaluated, runner()) }, macro_lookup_program, - &mut macros.iter() + &mut macros.iter(), ) } @@ -797,7 +796,7 @@ fn compile_functions( has_constants_tree: bool, ) -> Result, ClError> where - A::NodePtr: Clone + A::NodePtr: Clone, { let mut compiled: CompileOutput = Default::default(); @@ -824,7 +823,8 @@ fn add_main_args( symbols: &A::NodePtr, ) -> Result { let entry_value_loc = allocator.loc(args); - let entry_name = allocator.new_atom(entry_value_loc.clone(), "__chia__main_arguments".as_bytes())?; + let entry_name = + allocator.new_atom(entry_value_loc.clone(), "__chia__main_arguments".as_bytes())?; let entry_value_string = allocator.disassemble(args, None); let entry_value = allocator.new_atom(entry_value_loc, entry_value_string.as_bytes())?; let entry_cons_loc = allocator.loc(args); @@ -842,7 +842,7 @@ fn finish_compile_from_collection( produce_extra_info: bool, ) -> Result where - A::NodePtr: Clone + A::NodePtr: Clone, { let loc = allocator.loc(macro_lookup); let a_atom = allocator.new_atom(loc.clone(), &[2])?; @@ -934,15 +934,13 @@ where } else { "(_set_symbol_table 1)" }, - ).map_err(|e| ClError(loc.clone(), e))?; + ) + .map_err(|e| ClError(loc.clone(), e))?; let exported_symbols = allocator.export(&symbols); - run_program.run_program( - allocator.allocator(), - to_run, - exported_symbols, - None - ).map_err(|e| ClError(loc, e))?; + run_program + .run_program(allocator.allocator(), to_run, exported_symbols, None) + .map_err(|e| ClError(loc, e))?; Ok(opt_list) } else { @@ -962,18 +960,21 @@ pub fn compile_mod( _level: usize, ) -> Result where - A::NodePtr: Clone + A::NodePtr: Clone, { // Deal with the "mod" keyword. let loc = allocator.loc(macro_lookup); - let produce_extra_info_prog = assemble(allocator.allocator(), "(_symbols_extra_info)").map_err(|e| ClError(loc.clone(), e))?; + let produce_extra_info_prog = assemble(allocator.allocator(), "(_symbols_extra_info)") + .map_err(|e| ClError(loc.clone(), e))?; let produce_extra_info_null = NodePtr::NIL; - let extra_info_res = run_program.run_program( - allocator.allocator(), - produce_extra_info_prog, - produce_extra_info_null, - None, - ).map_err(|e| ClError(loc.clone(), e))?; + let extra_info_res = run_program + .run_program( + allocator.allocator(), + produce_extra_info_prog, + produce_extra_info_null, + None, + ) + .map_err(|e| ClError(loc.clone(), e))?; let imported_extra_info = allocator.import(loc, extra_info_res.1)?; let produce_extra_info = !allocator.is_nil(&imported_extra_info); diff --git a/src/classic/clvm_tools/stages/stage_2/operators.rs b/src/classic/clvm_tools/stages/stage_2/operators.rs index 351d0832b..fe47c99e4 100644 --- a/src/classic/clvm_tools/stages/stage_2/operators.rs +++ b/src/classic/clvm_tools/stages/stage_2/operators.rs @@ -25,7 +25,7 @@ use crate::classic::clvm_tools::sha256tree::TreeHash; use crate::classic::clvm_tools::stages::stage_0::{ DefaultProgramRunner, OriginalDialect, RunProgramOption, TRunProgram, }; -use crate::classic::clvm_tools::stages::stage_2::abstraction::{ClassicAllocator, ClError}; +use crate::classic::clvm_tools::stages::stage_2::abstraction::{ClError, ClassicAllocator}; use crate::classic::clvm_tools::stages::stage_2::compile::do_com_prog_for_dialect; use crate::classic::clvm_tools::stages::stage_2::optimize::do_optimize; @@ -102,15 +102,10 @@ pub fn full_path_for_filename( } } - Err( - ClError( - loc, - EvalErr::InternalError( - exported, - "can't open file".to_string(), - ) - ) - ) + Err(ClError( + loc, + EvalErr::InternalError(exported, "can't open file".to_string()), + )) } pub struct CompilerOperators { @@ -341,7 +336,8 @@ impl CompilerOperatorsInternal { return convert_filename(allocator, &filename); } - let full_name = full_path_for_filename(allocator, &sexp, &filename, &self.search_paths)?; + let full_name = + full_path_for_filename(allocator, &sexp, &filename, &self.search_paths)?; return convert_filename(allocator, &full_name); } } diff --git a/src/classic/clvm_tools/stages/stage_2/optimize.rs b/src/classic/clvm_tools/stages/stage_2/optimize.rs index 6f1dc3274..46b2a5c69 100644 --- a/src/classic/clvm_tools/stages/stage_2/optimize.rs +++ b/src/classic/clvm_tools/stages/stage_2/optimize.rs @@ -6,18 +6,18 @@ use std::rc::Rc; use clvm_rs::error::EvalErr; use num_bigint::ToBigInt; -use clvm_rs::allocator::{NodePtr}; +use clvm_rs::allocator::NodePtr; use clvm_rs::cost::Cost; use crate::classic::clvm::__type_compatibility__::{bi_one, bi_zero}; -use crate::classic::clvm::sexp::{ - enlist, equal_to, first, fold_m, map_m, proper_list, -}; +use crate::classic::clvm::sexp::{enlist, equal_to, first, fold_m, map_m, proper_list}; use crate::classic::clvm_tools::node_path::NodePath; use crate::classic::clvm_tools::pattern_match::match_sexp; use crate::classic::clvm_tools::stages::assemble; use crate::classic::clvm_tools::stages::stage_0::TRunProgram; -use crate::classic::clvm_tools::stages::stage_2::abstraction::{ASExp, BufCarrier, ClassicAllocator, ClError}; +use crate::classic::clvm_tools::stages::stage_2::abstraction::{ + ASExp, BufCarrier, ClError, ClassicAllocator, +}; use crate::classic::clvm_tools::stages::stage_2::helpers::quote; use crate::classic::clvm_tools::stages::stage_2::operators::AllocatorRefOrTreeHash; use crate::compiler::srcloc::Srcloc; @@ -32,7 +32,7 @@ const DIAG_OPTIMIZATIONS: bool = false; fn seems_constant_tail(allocator: &mut A, sexp_: &A::NodePtr) -> bool where - A::NodePtr: Clone + A::NodePtr: Clone, { let mut sexp = sexp_.clone(); @@ -54,7 +54,7 @@ where pub fn seems_constant(allocator: &mut A, sexp: &A::NodePtr) -> bool where - A::NodePtr: Clone + A::NodePtr: Clone, { match allocator.sexp(&sexp) { ASExp::Atom => { @@ -94,7 +94,7 @@ pub fn constant_optimizer( runner: Rc, ) -> Result where - A::NodePtr: Clone + A::NodePtr: Clone, { /* * If the expression does not depend upon @ anywhere, @@ -124,12 +124,9 @@ where } if sc_r && nn_r { let r_export = allocator.export(&r); - let res = runner.run_program( - allocator.allocator(), - r_export, - NodePtr::NIL, - None - ).map_err(|e| ClError(allocator.loc(&r), e))?; + let res = runner + .run_program(allocator.allocator(), r_export, NodePtr::NIL, None) + .map_err(|e| ClError(allocator.loc(&r), e))?; let r1 = allocator.import(allocator.loc(&r), res.1)?; let _ = if DIAG_OPTIMIZATIONS { println!( @@ -156,7 +153,9 @@ pub fn is_args_call(allocator: &A, r: &A::NodePtr) -> bool pub fn cons_q_a_optimizer_pattern(allocator: &mut A) -> A::NodePtr { let assembled = assemble(allocator.allocator(), "(a (q . (: . sexp)) (: . args))").unwrap(); - allocator.import(Srcloc::start("*cons_q_a_optimizer_pattern*"), assembled).unwrap() + allocator + .import(Srcloc::start("*cons_q_a_optimizer_pattern*"), assembled) + .unwrap() } pub fn cons_q_a_optimizer( @@ -166,7 +165,7 @@ pub fn cons_q_a_optimizer( _eval_f: Rc, ) -> Result where - A::NodePtr: Clone + A::NodePtr: Clone, { let cons_q_a_optimizer_pattern = cons_q_a_optimizer_pattern(allocator); @@ -194,16 +193,20 @@ where fn cons_pattern(allocator: &mut A) -> A::NodePtr { let assembled = assemble(allocator.allocator(), "(c (: . first) (: . rest)))").unwrap(); - allocator.import(Srcloc::start("*cons_pattern*"), assembled).unwrap() + allocator + .import(Srcloc::start("*cons_pattern*"), assembled) + .unwrap() } fn cons_f(allocator: &mut A, args: &A::NodePtr) -> Result where - A::NodePtr: Clone + A::NodePtr: Clone, { let cons_pattern = cons_pattern(allocator); let pair_loc = allocator.loc(&args); - if let Some(first) = match_sexp(allocator, &cons_pattern, &args, HashMap::new()).and_then(|t| t.get("first").cloned()) { + if let Some(first) = match_sexp(allocator, &cons_pattern, &args, HashMap::new()) + .and_then(|t| t.get("first").cloned()) + { Ok(first) } else { let first_loc = allocator.loc(&args); @@ -216,11 +219,13 @@ where fn cons_r(allocator: &mut A, args: &A::NodePtr) -> Result where - A::NodePtr: Clone + A::NodePtr: Clone, { let cons_pattern = cons_pattern(allocator); let pair_loc = allocator.loc(&args); - if let Some(rest) = match_sexp(allocator, &cons_pattern, &args, HashMap::new()).and_then(|t| t.get("rest").cloned()) { + if let Some(rest) = match_sexp(allocator, &cons_pattern, &args, HashMap::new()) + .and_then(|t| t.get("rest").cloned()) + { Ok(rest) } else { let rest_loc = allocator.loc(&args); @@ -237,7 +242,7 @@ fn path_from_args( new_args: &A::NodePtr, ) -> Result where - A::NodePtr: Clone + A::NodePtr: Clone, { match allocator.sexp(&sexp) { ASExp::Atom => { @@ -268,7 +273,7 @@ pub fn sub_args( new_args: &A::NodePtr, ) -> Result where - A::NodePtr: Clone + A::NodePtr: Clone, { match allocator.sexp(sexp) { ASExp::Atom => path_from_args(allocator, &sexp, &new_args), @@ -292,28 +297,24 @@ where match proper_list(allocator, &rest, true) { Some(tail_args) => { - let res = map_m( - allocator, - &mut tail_args.iter(), - &|allocator, elt| { - Ok(sub_args(allocator, elt, new_args)?) - } - )?; + let res = map_m(allocator, &mut tail_args.iter(), &|allocator, elt| { + Ok(sub_args(allocator, elt, new_args)?) + })?; let tail_list = enlist(allocator, &res)?; let first_loc = allocator.loc(&first); allocator.new_pair(first_loc, &first, &tail_list) - }, + } None => path_from_args(allocator, sexp, new_args), } } } } -fn var_change_optimizer_cons_eval_pattern( - allocator: &mut A -) -> A::NodePtr { +fn var_change_optimizer_cons_eval_pattern(allocator: &mut A) -> A::NodePtr { let a = assemble(allocator.allocator(), "(a (q . (: . sexp)) (: . args))").unwrap(); - allocator.import(Srcloc::start("*var_change_optimizer_cons_eval_pattern*"), a).unwrap() + allocator + .import(Srcloc::start("*var_change_optimizer_cons_eval_pattern*"), a) + .unwrap() } pub fn var_change_optimizer_cons_eval( @@ -323,7 +324,7 @@ pub fn var_change_optimizer_cons_eval( eval_f: Rc, ) -> Result where - A::NodePtr: Clone + A::NodePtr: Clone, { /* * This applies the transform @@ -342,7 +343,10 @@ where None => Ok(r.clone()), Some(t1) => { let original_args = t1.get("args").ok_or_else(|| { - ClError(allocator.loc(&r), EvalErr::InternalError(export_r, "bad pattern match on args".to_string())) + ClError( + allocator.loc(&r), + EvalErr::InternalError(export_r, "bad pattern match on args".to_string()), + ) })?; if DIAG_OPTIMIZATIONS { @@ -352,7 +356,10 @@ where ); }; let original_call = t1.get("sexp").ok_or_else(|| { - ClError(allocator.loc(&r), EvalErr::InternalError(export_r, "bad pattern match on sexp".to_string())) + ClError( + allocator.loc(&r), + EvalErr::InternalError(export_r, "bad pattern match on sexp".to_string()), + ) })?; if DIAG_OPTIMIZATIONS { @@ -447,7 +454,7 @@ pub fn children_optimizer( eval_f: Rc, ) -> Result where - A::NodePtr: Clone + A::NodePtr: Clone, { // Recursively apply optimizations to all non-quoted child nodes. match proper_list(allocator, r, true) { @@ -485,20 +492,18 @@ where } } -fn cons_optimizer_pattern_first( - allocator: &mut A -) -> A::NodePtr -{ +fn cons_optimizer_pattern_first(allocator: &mut A) -> A::NodePtr { let a = assemble(allocator.allocator(), "(f (c (: . first) (: . rest)))").unwrap(); - allocator.import(Srcloc::start("*cons_optimizer_pattern_first*"), a).unwrap() + allocator + .import(Srcloc::start("*cons_optimizer_pattern_first*"), a) + .unwrap() } -fn cons_optimizer_pattern_rest( - allocator: &mut A -) -> A::NodePtr -{ +fn cons_optimizer_pattern_rest(allocator: &mut A) -> A::NodePtr { let a = assemble(allocator.allocator(), "(r (c (: . first) (: . rest)))").unwrap(); - allocator.import(Srcloc::start("*cons_optimizer_pattern_rest*"), a).unwrap() + allocator + .import(Srcloc::start("*cons_optimizer_pattern_rest*"), a) + .unwrap() } fn cons_optimizer( @@ -508,7 +513,7 @@ fn cons_optimizer( _eval_f: Rc, ) -> Result where - A::NodePtr: Clone + A::NodePtr: Clone, { /* * This applies the transform @@ -542,12 +547,16 @@ where fn first_atom_pattern(allocator: &mut A) -> A::NodePtr { let a = assemble(allocator.allocator(), "(f ($ . atom))").unwrap(); - allocator.import(Srcloc::start("*first_atom_pattern*"), a).unwrap() + allocator + .import(Srcloc::start("*first_atom_pattern*"), a) + .unwrap() } fn rest_atom_pattern(allocator: &mut A) -> A::NodePtr { let a = assemble(allocator.allocator(), "(r ($ . atom))").unwrap(); - allocator.import(Srcloc::start("*first_atom_pattern*"), a).unwrap() + allocator + .import(Srcloc::start("*first_atom_pattern*"), a) + .unwrap() } fn path_optimizer( @@ -557,7 +566,7 @@ fn path_optimizer( _eval_f: Rc, ) -> Result where - A::NodePtr: Clone + A::NodePtr: Clone, { let first_atom_pattern = first_atom_pattern(allocator); let rest_atom_pattern = rest_atom_pattern(allocator); @@ -607,7 +616,9 @@ where fn quote_pattern_1(allocator: &mut A) -> A::NodePtr { let a = assemble(allocator.allocator(), "(q . 0)").unwrap(); - allocator.import(Srcloc::start("*quote_pattern_1*"), a).unwrap() + allocator + .import(Srcloc::start("*quote_pattern_1*"), a) + .unwrap() } fn quote_null_optimizer( @@ -617,7 +628,7 @@ fn quote_null_optimizer( _eval_f: Rc, ) -> Result where - A::NodePtr: Clone + A::NodePtr: Clone, { let quote_pattern_1 = quote_pattern_1(allocator); @@ -628,11 +639,11 @@ where Ok(t1.map(|_| imported_nil).unwrap_or_else(|| r.clone())) } -fn apply_null_pattern_1( - allocator: &mut A -) -> A::NodePtr { +fn apply_null_pattern_1(allocator: &mut A) -> A::NodePtr { let a = assemble(allocator.allocator(), "(a 0 . (: . rest))").unwrap(); - allocator.import(Srcloc::start("*apply_null_pattern_1*"), a).unwrap() + allocator + .import(Srcloc::start("*apply_null_pattern_1*"), a) + .unwrap() } fn apply_null_optimizer( @@ -642,7 +653,7 @@ fn apply_null_optimizer( _eval_f: Rc, ) -> Result where - A::NodePtr: Clone + A::NodePtr: Clone, { let apply_null_pattern_1 = apply_null_pattern_1(allocator); @@ -655,7 +666,7 @@ where struct OptimizerRunner<'a, A: ClassicAllocator> where - A::NodePtr: Clone + A::NodePtr: Clone, { pub name: String, #[allow(clippy::type_complexity)] @@ -669,7 +680,7 @@ where impl<'a, A: ClassicAllocator> OptimizerRunner<'a, A> where - A::NodePtr: Clone + A::NodePtr: Clone, { pub fn invoke( &self, @@ -705,7 +716,7 @@ pub fn optimize_sexp_( eval_f: Rc, ) -> Result where - A::NodePtr: Clone + A::NodePtr: Clone, { // First compare the NodePtr to see if we've cached this exact one. // Note that this scoping is here to prevent the borrowed mutable ref from @@ -785,7 +796,10 @@ where swap(&mut work, mr); work.insert(footprint.clone(), start_r_export); let r_export = allocator.export(&r); - work.insert(AllocatorRefOrTreeHash::new_from_nodeptr(r_export), start_r_export); + work.insert( + AllocatorRefOrTreeHash::new_from_nodeptr(r_export), + start_r_export, + ); work }); @@ -811,7 +825,7 @@ pub fn optimize_sexp( eval_f: Rc, ) -> Result where - A::NodePtr: Clone + A::NodePtr: Clone, { let optimized = RefCell::new(HashMap::new()); @@ -836,7 +850,7 @@ pub fn do_optimize( r: &A::NodePtr, ) -> Result where - A::NodePtr: Clone + A::NodePtr: Clone, { let r_first = first(allocator, &r)?; optimize_sexp_(allocator, memo, &r_first, runner.clone()) diff --git a/src/classic/clvm_tools/stages/stage_2/reader.rs b/src/classic/clvm_tools/stages/stage_2/reader.rs index a49c352ae..0ea39d108 100644 --- a/src/classic/clvm_tools/stages/stage_2/reader.rs +++ b/src/classic/clvm_tools/stages/stage_2/reader.rs @@ -2,17 +2,19 @@ use std::fs; use std::rc::Rc; use clvm_rs::error::EvalErr; -use clvmr::allocator::{NodePtr}; +use clvmr::allocator::NodePtr; use crate::classic::clvm::__type_compatibility__::{Bytes, Stream, UnvalidatedBytesFromType}; use crate::classic::clvm::serialize::{sexp_from_stream, SimpleCreateCLVMObject}; use crate::classic::clvm::sexp::{proper_list, rest}; use crate::classic::clvm_tools::stages::assemble; use crate::classic::clvm_tools::stages::stage_0::TRunProgram; +use crate::classic::clvm_tools::stages::stage_2::abstraction::{ + ASExp, BufCarrier, ClError, ClassicAllocator, +}; use crate::classic::clvm_tools::stages::stage_2::compile::get_search_paths; use crate::classic::clvm_tools::stages::stage_2::helpers::quote; use crate::classic::clvm_tools::stages::stage_2::operators::full_path_for_filename; -use crate::classic::clvm_tools::stages::stage_2::abstraction::{ASExp, BufCarrier, ClassicAllocator, ClError}; use crate::compiler::sexp::decode_string; @@ -32,23 +34,25 @@ pub fn convert_hex_to_sexp( file_data: &[u8], ) -> Result where - A::NodePtr: Clone + A::NodePtr: Clone, { let loc = allocator.loc(parent_sexp); let content_bytes = Bytes::new_validated(Some(UnvalidatedBytesFromType::Hex(decode_string( file_data, )))) .map_err(|e| { - ClError(loc.clone(), EvalErr::InternalError(NodePtr::NIL, e.to_string())) + ClError( + loc.clone(), + EvalErr::InternalError(NodePtr::NIL, e.to_string()), + ) })?; let mut reader_stream = Stream::new(Some(content_bytes)); let incoming_data = sexp_from_stream( allocator.allocator(), &mut reader_stream, Box::new(SimpleCreateCLVMObject {}), - ).map_err(|e| { - ClError(loc.clone(), e) - })?; + ) + .map_err(|e| ClError(loc.clone(), e))?; allocator.import(loc, incoming_data.1) } @@ -64,7 +68,7 @@ pub fn read_file( filename: &str, ) -> Result where - A::NodePtr: Clone + A::NodePtr: Clone, { let loc = allocator.loc(parent_sexp); let search_paths = get_search_paths(runner, loc.clone(), allocator)?; @@ -73,7 +77,10 @@ where let export = allocator.export(parent_sexp); fs::read(full_path.clone()) .map_err(|x| { - ClError(loc, EvalErr::InternalError(export, format!("error reading {full_path}: {x:?}"))) + ClError( + loc, + EvalErr::InternalError(export, format!("error reading {full_path}: {x:?}")), + ) }) .map(|data| PresentFile { data, @@ -93,7 +100,7 @@ pub fn process_embed_file( declaration_sexp: &A::NodePtr, ) -> Result<(Vec, A::NodePtr), ClError> where - A::NodePtr: Clone + A::NodePtr: Clone, { // Include the file's contents in the constant pool. // The user can specify the format to read: @@ -108,15 +115,10 @@ where if l.len() != 3 { let loc = allocator.loc(declaration_sexp); let dec_export = allocator.export(declaration_sexp); - return Err( - ClError( - loc, - EvalErr::InternalError( - dec_export, - "must have a type and a name".to_string(), - ) - ) - ); + return Err(ClError( + loc, + EvalErr::InternalError(dec_export, "must have a type and a name".to_string()), + )); } if let (ASExp::Atom, ASExp::Atom, ASExp::Atom) = ( @@ -155,41 +157,27 @@ where declaration_sexp, &decode_string(&filename_buf), )?; - let assembled = assemble(allocator.allocator(), &decode_string(&file.data)).map_err(|e| ClError(loc.clone(), e))?; + let assembled = assemble(allocator.allocator(), &decode_string(&file.data)) + .map_err(|e| ClError(loc.clone(), e))?; allocator.import(loc, assembled)? } else { - return Err( - ClError( - loc, - EvalErr::InternalError( - export, - "no such embed kind".to_string(), - ) - ) - ); + return Err(ClError( + loc, + EvalErr::InternalError(export, "no such embed kind".to_string()), + )); }; Ok((name_buf.to_vec(), quote(allocator, &file_data)?)) } else { - Err( - ClError( - loc, - EvalErr::InternalError( - export, - "malformed embed-file".to_string(), - ) - ) - ) + Err(ClError( + loc, + EvalErr::InternalError(export, "malformed embed-file".to_string()), + )) } } else { - Err( - ClError( - loc, - EvalErr::InternalError( - export, - "must be a proper list".to_string(), - ) - ) - ) + Err(ClError( + loc, + EvalErr::InternalError(export, "must be a proper list".to_string()), + )) } } diff --git a/src/tests/classic/stage_2.rs b/src/tests/classic/stage_2.rs index 5c7e109c2..dd1ad14f8 100644 --- a/src/tests/classic/stage_2.rs +++ b/src/tests/classic/stage_2.rs @@ -73,7 +73,15 @@ fn test_do_com_prog( let macro_lookup = assemble_from_ir(allocator, Rc::new(macro_ir)).unwrap(); let sym_ir = read_ir(&symbol_table_src, 0).unwrap(); let symbol_table = assemble_from_ir(allocator, Rc::new(sym_ir)).unwrap(); - let result = do_com_prog(allocator, 849, &program, ¯o_lookup, &symbol_table, runner).unwrap(); + let result = do_com_prog( + allocator, + 849, + &program, + ¯o_lookup, + &symbol_table, + runner, + ) + .unwrap(); disassemble(allocator, result, Some(0)) } From 5a8805e9ece4ce0493df696dd36199a3d20e1773 Mon Sep 17 00:00:00 2001 From: art yerkes Date: Fri, 24 Jul 2026 15:11:48 -0700 Subject: [PATCH 05/38] clippy --- src/classic/clvm/sexp.rs | 6 +- src/classic/clvm_tools/debug.rs | 2 +- src/classic/clvm_tools/pattern_match.rs | 2 +- .../clvm_tools/stages/stage_2/compile.rs | 86 +++++++++---------- .../clvm_tools/stages/stage_2/helpers.rs | 6 +- .../clvm_tools/stages/stage_2/inline.rs | 8 +- .../clvm_tools/stages/stage_2/module.rs | 15 ++-- .../clvm_tools/stages/stage_2/optimize.rs | 70 +++++++-------- 8 files changed, 97 insertions(+), 98 deletions(-) diff --git a/src/classic/clvm/sexp.rs b/src/classic/clvm/sexp.rs index e12f38d43..8e98e5bf6 100644 --- a/src/classic/clvm/sexp.rs +++ b/src/classic/clvm/sexp.rs @@ -427,10 +427,12 @@ where Ok(built) } -pub fn map_m( +type MapMTransform<'a, A, T> = &'a dyn Fn(&mut A, T) -> Result<::NodePtr, ClError>; + +pub fn map_m<'a, T, A: ClassicAllocator>( allocator: &mut A, iter: &mut impl Iterator, - f: &dyn Fn(&mut A, T) -> Result, + f: MapMTransform<'a, A, T>, ) -> Result, ClError> { let mut result = Vec::new(); loop { diff --git a/src/classic/clvm_tools/debug.rs b/src/classic/clvm_tools/debug.rs index ec99df3dd..609877377 100644 --- a/src/classic/clvm_tools/debug.rs +++ b/src/classic/clvm_tools/debug.rs @@ -326,7 +326,7 @@ pub fn trace_pre_eval( Ok(None) } else { let log_entry = enlist(allocator, &[sexp, args])?; - let _ = append_log(allocator, log_entry); + append_log(allocator, log_entry); Ok(Some(log_entry)) } } diff --git a/src/classic/clvm_tools/pattern_match.rs b/src/classic/clvm_tools/pattern_match.rs index 734fd0354..0a62fe34e 100644 --- a/src/classic/clvm_tools/pattern_match.rs +++ b/src/classic/clvm_tools/pattern_match.rs @@ -25,7 +25,7 @@ where let new_key_str = Bytes::new(Some(BytesFromType::Raw(new_key.to_vec()))).decode(); match bindings.get(&new_key_str) { Some(binding) => { - if !equal_to(allocator, binding, &new_value) { + if !equal_to(allocator, binding, new_value) { return None; } Some(bindings) diff --git a/src/classic/clvm_tools/stages/stage_2/compile.rs b/src/classic/clvm_tools/stages/stage_2/compile.rs index 47dbf615f..0663d0906 100644 --- a/src/classic/clvm_tools/stages/stage_2/compile.rs +++ b/src/classic/clvm_tools/stages/stage_2/compile.rs @@ -108,7 +108,7 @@ where if DIAG_OUTPUT { println!("com_qq {} {}", ident, allocator.disassemble(sexp, None)); } - do_com_prog(allocator, 110, sexp, macro_lookup, symbol_table, runner).map(|x| x) + do_com_prog(allocator, 110, sexp, macro_lookup, symbol_table, runner) } pub fn compile_qq( @@ -275,15 +275,15 @@ where return Ok(prog.clone()); } - if let Some(qlist) = proper_list(allocator, &prog, true) { + if let Some(qlist) = proper_list(allocator, prog, true) { if qlist.is_empty() { return Ok(prog.clone()); } // quote_node was Atom(q) let quote_node = &qlist[0]; - if let ASExp::Atom = allocator.sexp("e_node) { - let quote_atom = allocator.atom("e_node); + if let ASExp::Atom = allocator.sexp(quote_node) { + let quote_atom = allocator.atom(quote_node); if quote_atom.as_ref() == b"quote" { if qlist.len() != 2 { // quoted list should be 2: "(quote arg)" @@ -294,7 +294,7 @@ where exported, format!( "Compilation error while compiling [{}]. quote takes exactly one argument.", - allocator.disassemble(&prog, None) + allocator.disassemble(prog, None) ) ) ) @@ -367,18 +367,17 @@ where &[com_atom, imported_post, quoted_macros, quoted_symbols], )?; let top_path = allocator.new_atom(loc, NodePath::new(None).as_path().data())?; - evaluate(allocator, &to_eval, &top_path).map(|x| { + evaluate(allocator, &to_eval, &top_path).inspect(|x| { if DIAG_OUTPUT { print!( "TRY_EXPAND_MACRO {} WITH {} GIVES {} MACROS {} SYMBOLS {}", - allocator.disassemble(¯o_code, None), - allocator.disassemble(&prog_rest, None), - allocator.disassemble(&x, None), - allocator.disassemble(¯o_lookup, None), - allocator.disassemble(&symbol_table, None) + allocator.disassemble(macro_code, None), + allocator.disassemble(prog_rest, None), + allocator.disassemble(x, None), + allocator.disassemble(macro_lookup, None), + allocator.disassemble(symbol_table, None) ); } - x }) } @@ -403,7 +402,7 @@ fn get_macro_program( where A::NodePtr: Clone, { - if let Some(mlist) = proper_list(allocator, ¯o_lookup, true) { + if let Some(mlist) = proper_list(allocator, macro_lookup, true) { for macro_pair in mlist { match proper_list(allocator, ¯o_pair, true) { None => {} @@ -415,8 +414,7 @@ where mp_list[1].clone() } else { let loc = allocator.loc(macro_lookup); - let imported_nil = allocator.import(loc, NodePtr::NIL)?; - imported_nil + allocator.import(loc, NodePtr::NIL)? }; match allocator.sexp(&mp_list[0]) { @@ -454,7 +452,7 @@ where return allocator.new_atom(loc, NodePath::new(None).as_path().data()); } - match proper_list(allocator, &symbol_table, true) { + match proper_list(allocator, symbol_table, true) { None => {} Some(symlist) => { let nil_import = allocator.import(loc.clone(), NodePtr::NIL)?; @@ -489,7 +487,7 @@ where } } - quote(allocator, &prog) + quote(allocator, prog) } fn compile_operator_atom( @@ -510,19 +508,19 @@ where } if let Some(f) = compile_bindings.get(avec) { - let prog_rest = rest(allocator, &prog)?; + let prog_rest = rest(allocator, prog)?; let post_prog = (f.to_run)( allocator, &prog_rest, - ¯o_lookup, - &symbol_table, + macro_lookup, + symbol_table, run_program.clone(), 1, )?; let quoted_post_prog = quote(allocator, &post_prog)?; let loc = allocator.loc(prog); let top_atom = allocator.new_atom(loc, NodePath::new(None).as_path().data())?; - let _ = if DIAG_OUTPUT { + if DIAG_OUTPUT { print!( "COMPILE_BINDINGS {}", allocator.disassemble("ed_post_prog, None) @@ -548,7 +546,7 @@ fn find_symbol_match( where A::NodePtr: Clone, { - if let Some(symlist) = proper_list(allocator, &symbol_table, true) { + if let Some(symlist) = proper_list(allocator, symbol_table, true) { for sym in symlist { if let Some(symdef) = proper_list(allocator, &sym, true) { if symdef.is_empty() { @@ -606,25 +604,25 @@ where exported_prog, format!( "can't compile {}, unknown operator", - allocator.disassemble(&prog, None) + allocator.disassemble(prog, None) ), ), )); if *opbuf == vec![1] || *opbuf == vec![b'q'] { - let rest_loc = allocator.loc(&rest); + let rest_loc = allocator.loc(rest); return allocator.new_pair(rest_loc, operator, rest); } - match proper_list(allocator, &rest, true) { + match proper_list(allocator, rest, true) { Some(prog_args) => { let mut new_args = map_m(allocator, &mut prog_args.iter(), &|allocator, arg| { do_com_prog( allocator, 544, arg, - ¯o_lookup, - &symbol_table, + macro_lookup, + symbol_table, run_program.clone(), ) })?; @@ -638,7 +636,7 @@ where find_symbol_match(allocator, opbuf, &r, symbol_table).and_then(|x| match x { Some(SymbolResult::Direct(v)) => Ok(v), Some(SymbolResult::Matched(_symbol, value)) => { - match proper_list(allocator, &rest, true) { + match proper_list(allocator, rest, true) { Some(proglist) => { let loc = allocator.loc(&value); let apply_atom = allocator.new_atom(loc.clone(), &[2])?; @@ -657,8 +655,8 @@ where let list_application = allocator.new_pair(loc, &list_atom, &enlisted)?; let quoted_list = quote(allocator, &list_application)?; - let quoted_macros = quote(allocator, ¯o_lookup)?; - let quoted_symbols = quote(allocator, &symbol_table)?; + let quoted_macros = quote(allocator, macro_lookup)?; + let quoted_symbols = quote(allocator, symbol_table)?; let compiled = enlist( allocator, &[com_atom, quoted_list, quoted_macros, quoted_symbols], @@ -695,9 +693,9 @@ where println!( "START COMPILE {}: {} MACRO {} SYMBOLS {}", from, - allocator.disassemble(&prog, None), - allocator.disassemble(¯o_lookup, None), - allocator.disassemble(&symbol_table, None), + allocator.disassemble(prog, None), + allocator.disassemble(macro_lookup, None), + allocator.disassemble(symbol_table, None), ); } do_com_prog_(allocator, prog, macro_lookup, symbol_table, run_program).inspect(|x| { @@ -705,10 +703,10 @@ where println!( "DO_COM_PROG {}: {} MACRO {} SYMBOLS {} RESULT {}", from, - allocator.disassemble(&prog, None), - allocator.disassemble(¯o_lookup, None), - allocator.disassemble(&symbol_table, None), - allocator.disassemble(&x, None) + allocator.disassemble(prog, None), + allocator.disassemble(macro_lookup, None), + allocator.disassemble(symbol_table, None), + allocator.disassemble(x, None) ); } }) @@ -737,7 +735,7 @@ where // lower "quote" to "q" m! { - prog <- lower_quote(allocator, &prog_); + prog <- lower_quote(allocator, prog_); // quote atoms match allocator.sexp(&prog) { @@ -759,14 +757,14 @@ where // Note: can't co-borrow with allocator below. let op_atom = allocator.atom(&operator); let op_buf = op_atom.as_ref().to_vec(); - get_macro_program(allocator, &op_buf, ¯o_lookup). + get_macro_program(allocator, &op_buf, macro_lookup). and_then(|x| match x { Some(value) => { try_expand_macro_for_atom( allocator, &value, &prog_rest, - ¯o_lookup, + macro_lookup, symbol_table ) }, @@ -870,7 +868,7 @@ where //}) } _ => { - let exported = allocator.export(&sexp); + let exported = allocator.export(sexp); Err(ClError( allocator.loc(sexp), EvalErr::InternalError( @@ -925,10 +923,10 @@ where let mut res = Vec::new(); let search_paths_result_import = allocator.import(loc, search_paths_result.1)?; if let Some(l) = proper_list(allocator, &search_paths_result_import, true) { - for elt in l.iter().cloned() { - if let ASExp::Atom = allocator.sexp(&elt) { + for elt in l.iter() { + if let ASExp::Atom = allocator.sexp(elt) { // Only elt in scope. - let atom = allocator.atom(&elt); + let atom = allocator.atom(elt); res.push(Bytes::new(Some(BytesFromType::Raw(atom.as_ref().to_vec()))).decode()); } } diff --git a/src/classic/clvm_tools/stages/stage_2/helpers.rs b/src/classic/clvm_tools/stages/stage_2/helpers.rs index 2003b2937..830830770 100644 --- a/src/classic/clvm_tools/stages/stage_2/helpers.rs +++ b/src/classic/clvm_tools/stages/stage_2/helpers.rs @@ -15,8 +15,8 @@ pub fn quote( sexp: &A::NodePtr, ) -> Result { allocator - .new_atom(allocator.loc(&sexp), "E_ATOM) - .and_then(|q| allocator.new_pair(allocator.loc(&sexp), &q, &sexp)) + .new_atom(allocator.loc(sexp), "E_ATOM) + .and_then(|q| allocator.new_pair(allocator.loc(sexp), &q, sexp)) } // In original python code, the name of this function is `eval`, @@ -50,7 +50,7 @@ where */ let args = NodePath::new(None).as_path(); let loc = allocator.loc(prog); - let mac = quote(allocator, ¯o_lookup)?; + let mac = quote(allocator, macro_lookup)?; let com_sexp = allocator.new_atom(loc.clone(), &COM_ATOM)?; let arg_sexp = allocator.new_atom(loc, args.data())?; let to_eval = enlist(allocator, &[com_sexp, prog.clone(), mac])?; diff --git a/src/classic/clvm_tools/stages/stage_2/inline.rs b/src/classic/clvm_tools/stages/stage_2/inline.rs index 59c74471f..40aa45edb 100644 --- a/src/classic/clvm_tools/stages/stage_2/inline.rs +++ b/src/classic/clvm_tools/stages/stage_2/inline.rs @@ -22,7 +22,7 @@ where { if let (ASExp::Atom, Some(spec)) = ( allocator.sexp(tree_first), - proper_list(allocator, &tree_rest, true), + proper_list(allocator, tree_rest, true), ) { let first_atom = allocator.atom(tree_first); if first_atom.as_ref() == b"@" && spec.len() == 2 { @@ -62,9 +62,7 @@ where // Create the sequence of individual tree moves that will translate to // (f ...) and (r ...) wrapping to select the given path from a larger structure. fn create_path_selection_plan(path: Number, operators: &mut Vec) { - if path <= bi_one() { - return; - } else { + if path > bi_one() { operators.push(path.clone() % 2_u32.to_bigint().unwrap() == bi_one()); create_path_selection_plan(path / 2_u32.to_bigint().unwrap(), operators) } @@ -112,7 +110,7 @@ where A::NodePtr: Clone, { let loc = allocator.loc(arg_sexp); - match allocator.sexp(&arg_sexp) { + match allocator.sexp(arg_sexp) { ASExp::Pair(a, b) => { let next_depth = arg_depth.clone() * 2_u32.to_bigint().unwrap(); if let Some((capture, substructure)) = is_at_capture(allocator, &a, &b) { diff --git a/src/classic/clvm_tools/stages/stage_2/module.rs b/src/classic/clvm_tools/stages/stage_2/module.rs index 0790676aa..16d159272 100644 --- a/src/classic/clvm_tools/stages/stage_2/module.rs +++ b/src/classic/clvm_tools/stages/stage_2/module.rs @@ -480,7 +480,7 @@ where // eslint-disable-next-line no-constant-condition let loc = allocator.loc(args); - match proper_list(allocator, &args, true) { + match proper_list(allocator, args, true) { None => { let export_args = allocator.export(args); Err( @@ -636,12 +636,13 @@ where } // export type TSymbolTable = Array<[SExp, Bytes]>; +pub type Symbol = (::NodePtr, Vec); fn symbol_table_for_tree( allocator: &mut A, tree: &A::NodePtr, root_node: &NodePath, -) -> Result)>, ClError> +) -> Result>, ClError> where A::NodePtr: Clone, { @@ -702,7 +703,7 @@ where let opt_atom = allocator.new_atom(loc.clone(), "opt".as_bytes())?; let runner = || run_program.clone(); - let macro_lookup_program = quote(allocator, ¯o_lookup)?; + let macro_lookup_program = quote(allocator, macro_lookup)?; fold_m( allocator, &|allocator, macro_lookup_program, macro_def: &(Vec, A::NodePtr)| { @@ -735,7 +736,7 @@ fn add_one_function( allocator: &mut A, args_root_node: &NodePath, macro_lookup_program: &A::NodePtr, - constants_symbol_table: &[(A::NodePtr, Vec)], + constants_symbol_table: &[Symbol], name: &[u8], lambda_expression: &A::NodePtr, has_constants_tree: bool, @@ -748,11 +749,11 @@ where let com_atom = allocator.new_atom(loc.clone(), "com".as_bytes())?; let opt_atom = allocator.new_atom(loc.clone(), "opt".as_bytes())?; - let function_args = first(allocator, &lambda_expression)?; + let function_args = first(allocator, lambda_expression)?; let local_symbol_table = symbol_table_for_tree(allocator, &function_args, args_root_node)?; let mut all_symbols = local_symbol_table; all_symbols.append(&mut constants_symbol_table.to_owned()); - let lambda_form_content = rest(allocator, &lambda_expression)?; + let lambda_form_content = rest(allocator, lambda_expression)?; let lambda_body = first(allocator, &lambda_form_content)?; let quoted_lambda_expr = quote(allocator, &lambda_body)?; let all_symbols_list_sexp = map_m(allocator, &mut all_symbols.iter(), &|allocator, pair| { @@ -791,7 +792,7 @@ fn compile_functions( allocator: &mut A, functions: &HashMap, A::NodePtr>, macro_lookup_program: &A::NodePtr, - constants_symbol_table: &[(A::NodePtr, Vec)], + constants_symbol_table: &[Symbol], args_root_node: &NodePath, has_constants_tree: bool, ) -> Result, ClError> diff --git a/src/classic/clvm_tools/stages/stage_2/optimize.rs b/src/classic/clvm_tools/stages/stage_2/optimize.rs index 46b2a5c69..c7d4eba32 100644 --- a/src/classic/clvm_tools/stages/stage_2/optimize.rs +++ b/src/classic/clvm_tools/stages/stage_2/optimize.rs @@ -56,9 +56,9 @@ pub fn seems_constant(allocator: &mut A, sexp: &A::NodePtr) where A::NodePtr: Clone, { - match allocator.sexp(&sexp) { + match allocator.sexp(sexp) { ASExp::Atom => { - return allocator.is_nil(&sexp); + return allocator.is_nil(sexp); } ASExp::Pair(operator, r) => { match allocator.sexp(&operator) { @@ -101,7 +101,7 @@ where * it's a constant. So we can simply evaluate it and * return the quoted result. */ - if let ASExp::Pair(first, _) = allocator.sexp(&r) { + if let ASExp::Pair(first, _) = allocator.sexp(r) { // first relevant in scope. if let ASExp::Atom = allocator.sexp(&first) { let buf = allocator.atom(&first); @@ -112,26 +112,26 @@ where } } - let sc_r = seems_constant(allocator, &r); - let nn_r = !allocator.is_nil(&r); + let sc_r = seems_constant(allocator, r); + let nn_r = !allocator.is_nil(r); if DIAG_OPTIMIZATIONS { println!( "COPT SC_R {} NN_R {} {}", sc_r, nn_r, - allocator.disassemble(&r, None), + allocator.disassemble(r, None), ); } if sc_r && nn_r { - let r_export = allocator.export(&r); + let r_export = allocator.export(r); let res = runner .run_program(allocator.allocator(), r_export, NodePtr::NIL, None) - .map_err(|e| ClError(allocator.loc(&r), e))?; - let r1 = allocator.import(allocator.loc(&r), res.1)?; - let _ = if DIAG_OPTIMIZATIONS { + .map_err(|e| ClError(allocator.loc(r), e))?; + let r1 = allocator.import(allocator.loc(r), res.1)?; + if DIAG_OPTIMIZATIONS { println!( "CONSTANT_OPTIMIZER {} TO {}", - allocator.disassemble(&r, None), + allocator.disassemble(r, None), allocator.disassemble(&r1, None) ); }; @@ -174,7 +174,7 @@ where * (a (q . SEXP) @) => SEXP */ - let matched = match_sexp(allocator, &cons_q_a_optimizer_pattern, &r, HashMap::new()); + let matched = match_sexp(allocator, &cons_q_a_optimizer_pattern, r, HashMap::new()); match ( matched.as_ref().and_then(|t1| t1.get("args").cloned()), @@ -203,13 +203,13 @@ where A::NodePtr: Clone, { let cons_pattern = cons_pattern(allocator); - let pair_loc = allocator.loc(&args); - if let Some(first) = match_sexp(allocator, &cons_pattern, &args, HashMap::new()) + let pair_loc = allocator.loc(args); + if let Some(first) = match_sexp(allocator, &cons_pattern, args, HashMap::new()) .and_then(|t| t.get("first").cloned()) { Ok(first) } else { - let first_loc = allocator.loc(&args); + let first_loc = allocator.loc(args); let first_atom = allocator.new_atom(first_loc.clone(), &[5])?; let nil = allocator.import(first_loc, NodePtr::NIL)?; let tail = allocator.new_pair(pair_loc.clone(), args, &nil)?; @@ -222,13 +222,13 @@ where A::NodePtr: Clone, { let cons_pattern = cons_pattern(allocator); - let pair_loc = allocator.loc(&args); - if let Some(rest) = match_sexp(allocator, &cons_pattern, &args, HashMap::new()) + let pair_loc = allocator.loc(args); + if let Some(rest) = match_sexp(allocator, &cons_pattern, args, HashMap::new()) .and_then(|t| t.get("rest").cloned()) { Ok(rest) } else { - let rest_loc = allocator.loc(&args); + let rest_loc = allocator.loc(args); let rest_atom = allocator.new_atom(rest_loc.clone(), &[6])?; let nil = allocator.import(rest_loc, NodePtr::NIL)?; let tail = allocator.new_pair(pair_loc.clone(), args, &nil)?; @@ -244,7 +244,7 @@ fn path_from_args( where A::NodePtr: Clone, { - match allocator.sexp(&sexp) { + match allocator.sexp(sexp) { ASExp::Atom => { // Only sexp in scope. let atom = allocator.atom(sexp); @@ -252,13 +252,13 @@ where if v <= bi_one() { Ok(new_args.clone()) } else { - let loc = allocator.loc(&sexp); + let loc = allocator.loc(sexp); let sexp = allocator.new_atom(loc, &u8_from_number(v.clone() >> 1).to_vec())?; if (v & 1_u32.to_bigint().unwrap()) != bi_zero() { - let cons_r_res = cons_r(allocator, &new_args)?; + let cons_r_res = cons_r(allocator, new_args)?; path_from_args(allocator, &sexp, &cons_r_res) } else { - let cons_f_res = cons_f(allocator, &new_args)?; + let cons_f_res = cons_f(allocator, new_args)?; path_from_args(allocator, &sexp, &cons_f_res) } } @@ -276,13 +276,13 @@ where A::NodePtr: Clone, { match allocator.sexp(sexp) { - ASExp::Atom => path_from_args(allocator, &sexp, &new_args), + ASExp::Atom => path_from_args(allocator, sexp, new_args), ASExp::Pair(first_pre, rest) => { let first; match allocator.sexp(&first_pre) { ASExp::Pair(_, _) => { - first = sub_args(allocator, &first_pre, &new_args)?; + first = sub_args(allocator, &first_pre, new_args)?; } ASExp::Atom => { // Atom is a reflection of first_pre. @@ -298,7 +298,7 @@ where match proper_list(allocator, &rest, true) { Some(tail_args) => { let res = map_m(allocator, &mut tail_args.iter(), &|allocator, elt| { - Ok(sub_args(allocator, elt, new_args)?) + sub_args(allocator, elt, new_args) })?; let tail_list = enlist(allocator, &res)?; let first_loc = allocator.loc(&first); @@ -339,12 +339,12 @@ where let pattern = var_change_optimizer_cons_eval_pattern(allocator); let export_r = allocator.export(r); - match match_sexp(allocator, &pattern, &r, HashMap::new()).as_ref() { + match match_sexp(allocator, &pattern, r, HashMap::new()).as_ref() { None => Ok(r.clone()), Some(t1) => { let original_args = t1.get("args").ok_or_else(|| { ClError( - allocator.loc(&r), + allocator.loc(r), EvalErr::InternalError(export_r, "bad pattern match on args".to_string()), ) })?; @@ -357,7 +357,7 @@ where }; let original_call = t1.get("sexp").ok_or_else(|| { ClError( - allocator.loc(&r), + allocator.loc(r), EvalErr::InternalError(export_r, "bad pattern match on sexp".to_string()), ) })?; @@ -533,7 +533,7 @@ where _ => { m! { let t2 = match_sexp( - allocator, &cons_optimizer_pattern_rest, &r, HashMap::new() + allocator, &cons_optimizer_pattern_rest, r, HashMap::new() ); match t2.and_then(|t| t.get("rest").cloned()) { Some(rest) => Ok(rest), @@ -578,8 +578,8 @@ where * (r N) => B */ - let first_match = match_sexp(allocator, &first_atom_pattern, &r, HashMap::new()); - let rest_match = match_sexp(allocator, &rest_atom_pattern, &r, HashMap::new()); + let first_match = match_sexp(allocator, &first_atom_pattern, r, HashMap::new()); + let rest_match = match_sexp(allocator, &rest_atom_pattern, r, HashMap::new()); match (first_match, rest_match) { (Some(first), _) => { @@ -633,7 +633,7 @@ where let quote_pattern_1 = quote_pattern_1(allocator); // This applies the transform `(q . 0)` => `0` - let t1 = match_sexp(allocator, "e_pattern_1, &r, HashMap::new()); + let t1 = match_sexp(allocator, "e_pattern_1, r, HashMap::new()); let loc = allocator.loc(r); let imported_nil = allocator.import(loc, NodePtr::NIL)?; Ok(t1.map(|_| imported_nil).unwrap_or_else(|| r.clone())) @@ -830,13 +830,13 @@ where let optimized = RefCell::new(HashMap::new()); if DIAG_OPTIMIZATIONS { - println!("START OPTIMIZE {}", allocator.disassemble(&r, None)); + println!("START OPTIMIZE {}", allocator.disassemble(r, None)); } optimize_sexp_(allocator, &optimized, r, eval_f).inspect(|x| { if DIAG_OPTIMIZATIONS { println!( "OPTIMIZE_SEXP {} GIVING {}", - allocator.disassemble(&r, None), + allocator.disassemble(r, None), allocator.disassemble(x, None) ); } @@ -852,6 +852,6 @@ pub fn do_optimize( where A::NodePtr: Clone, { - let r_first = first(allocator, &r)?; + let r_first = first(allocator, r)?; optimize_sexp_(allocator, memo, &r_first, runner.clone()) } From 4eb95acdf8e3a9a4424c5b2d9989da98407845ad Mon Sep 17 00:00:00 2001 From: art yerkes Date: Fri, 24 Jul 2026 15:14:04 -0700 Subject: [PATCH 06/38] remove change menat for another branch --- Cargo.lock | 70 ------------------------------------------------------ Cargo.toml | 1 - 2 files changed, 71 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 977951a4c..e7a166390 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -214,7 +214,6 @@ dependencies = [ "getrandom 0.2.15", "hashlink", "hex", - "html_parser", "indoc", "js-sys", "lazy_static", @@ -362,12 +361,6 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3e16a80c1dda2cf52fa07106427d3d798b6331dca8155fcb8c39f7fc78f6dd2" -[[package]] -name = "doc-comment" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "780955b8b195a21ab8e4ac6b60dd1dbdcec1dc6c51c0617964b08c81785e12c9" - [[package]] name = "ecdsa" version = "0.16.8" @@ -580,21 +573,6 @@ dependencies = [ "digest 0.10.7", ] -[[package]] -name = "html_parser" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f56db07b6612644f6f7719f8ef944f75fff9d6378fdf3d316fd32194184abd" -dependencies = [ - "doc-comment", - "pest", - "pest_derive", - "serde", - "serde_derive", - "serde_json", - "thiserror", -] - [[package]] name = "hybrid-array" version = "0.4.12" @@ -867,48 +845,6 @@ dependencies = [ "base64ct", ] -[[package]] -name = "pest" -version = "2.8.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7df728be843c7070fab6ab7c328c4e9e9d78e23bf749c0669c86ee7ebfa050a2" -dependencies = [ - "memchr", - "ucd-trie", -] - -[[package]] -name = "pest_derive" -version = "2.8.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e2dd6fc3b26b3462ee188aac870f5a41d398f1cd5e2408d16531bd71c9591fd" -dependencies = [ - "pest", - "pest_generator", -] - -[[package]] -name = "pest_generator" -version = "2.8.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a7a9205cfb6f596a9e8b689c0a15f9ceb7a1aafae7aaf788150ac65b29975b6" -dependencies = [ - "pest", - "pest_meta", - "proc-macro2 1.0.95", - "quote 1.0.40", - "syn 2.0.104", -] - -[[package]] -name = "pest_meta" -version = "2.8.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85abd351c0de1e8384fc791a0737111a350394937e92b956b743dac12429f57c" -dependencies = [ - "pest", -] - [[package]] name = "pkcs8" version = "0.10.2" @@ -1412,12 +1348,6 @@ version = "1.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40ce102ab67701b8526c123c1bab5cbe42d7040ccfd0f64af1a385808d2f43de" -[[package]] -name = "ucd-trie" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" - [[package]] name = "unicode-ident" version = "1.0.11" diff --git a/Cargo.toml b/Cargo.toml index ef26bb1c9..ab0394b91 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,7 +39,6 @@ tempfile = "3.25.0" unicode-segmentation = "1.13.3" yaml-rust2 = "0.11.0" subprocess = { version = "1.1.0", optional = true } -html_parser = "0.7.0" [dependencies.pyo3] version = "0.29.0" From 30e51a455ce36f3c888c6e144291c2fac612de0d Mon Sep 17 00:00:00 2001 From: art yerkes Date: Fri, 24 Jul 2026 15:18:45 -0700 Subject: [PATCH 07/38] fix --- src/classic/clvm_tools/stages/stage_2/optimize.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/classic/clvm_tools/stages/stage_2/optimize.rs b/src/classic/clvm_tools/stages/stage_2/optimize.rs index c7d4eba32..758b08fe3 100644 --- a/src/classic/clvm_tools/stages/stage_2/optimize.rs +++ b/src/classic/clvm_tools/stages/stage_2/optimize.rs @@ -72,7 +72,7 @@ where } } ASExp::Pair(_, _) => { - if !seems_constant_tail(allocator, &operator) { + if !seems_constant(allocator, &operator) { return false; } } From a50e9812ba7545bf6f7cdac7d848ace8d5fcc81d Mon Sep 17 00:00:00 2001 From: art yerkes Date: Fri, 24 Jul 2026 15:25:26 -0700 Subject: [PATCH 08/38] fmt --- src/classic/clvm/sexp.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/classic/clvm/sexp.rs b/src/classic/clvm/sexp.rs index 8e98e5bf6..ebb691101 100644 --- a/src/classic/clvm/sexp.rs +++ b/src/classic/clvm/sexp.rs @@ -427,12 +427,13 @@ where Ok(built) } -type MapMTransform<'a, A, T> = &'a dyn Fn(&mut A, T) -> Result<::NodePtr, ClError>; +type MapMTransform<'a, A, T> = + &'a dyn Fn(&mut A, T) -> Result<::NodePtr, ClError>; pub fn map_m<'a, T, A: ClassicAllocator>( allocator: &mut A, iter: &mut impl Iterator, - f: MapMTransform<'a, A, T>, + f: MapMTransform<'a, A, T>, ) -> Result, ClError> { let mut result = Vec::new(); loop { From 7905ac3d6eeaf295b7064e1a9721f6be7732f84a Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 29 Jul 2026 20:47:39 +0000 Subject: [PATCH 09/38] Add modern frontend classic codegen dialect Co-authored-by: arty --- .../clvm_tools/stages/stage_2/abstraction.rs | 157 ++++++++++++++++++ src/compiler/compiler.rs | 58 ++++++- src/compiler/dialect.rs | 28 ++++ src/tests/compiler/cldb.rs | 70 ++++++++ src/tests/compiler/compiler.rs | 40 +++++ src/tests/compiler/optimizer/cse.rs | 2 + src/tests/compiler/optimizer/cse_fuzz.rs | 1 + .../compiler/optimizer/cse_regression.rs | 1 + src/tests/compiler/optimizer/deinline.rs | 3 + src/tests/compiler/optimizer/depgraph.rs | 1 + src/tests/compiler/optimizer/output.rs | 2 + src/tests/compiler/preprocessor.rs | 2 + 12 files changed, 364 insertions(+), 1 deletion(-) diff --git a/src/classic/clvm_tools/stages/stage_2/abstraction.rs b/src/classic/clvm_tools/stages/stage_2/abstraction.rs index 0cc15eae2..551f1f97b 100644 --- a/src/classic/clvm_tools/stages/stage_2/abstraction.rs +++ b/src/classic/clvm_tools/stages/stage_2/abstraction.rs @@ -1,10 +1,14 @@ +use std::rc::Rc; + use std::ops::Index; use clvm_rs::allocator::{Allocator, NodePtr, SExp}; use clvm_rs::error::EvalErr; use crate::classic::clvm_tools::binutils::disassemble; +use crate::compiler::sexp::SExp as ModernSExp; use crate::compiler::srcloc::Srcloc; +use crate::util::u8_from_number; pub enum ASExp { Pair(T, T), @@ -123,3 +127,156 @@ impl ClassicAllocator for Allocator { *node } } + +/// A stage-2 node backed by the compiler's location-aware S-expression. +/// +/// `raw` mirrors `sexp` in the contained CLVM allocator. Stage-2 occasionally +/// has to execute generated CLVM, so retaining both representations lets those +/// boundaries use clvmr without discarding locations from the source tree. +#[derive(Clone, Debug)] +pub struct SExpNode { + pub sexp: Rc, + raw: NodePtr, +} + +/// Adapts modern compiler S-expressions to the classic stage-2 compiler. +pub struct SExpClassicAllocator { + allocator: Allocator, +} + +impl Default for SExpClassicAllocator { + fn default() -> Self { + Self::new() + } +} + +impl SExpClassicAllocator { + pub fn new() -> Self { + Self { + allocator: Allocator::new(), + } + } + + pub fn from_sexp(&mut self, sexp: Rc) -> Result { + let loc = sexp.loc(); + let raw = match sexp.as_ref() { + ModernSExp::Nil(_) => NodePtr::NIL, + ModernSExp::Cons(_, first, rest) => { + let first = self.from_sexp(first.clone())?; + let rest = self.from_sexp(rest.clone())?; + self.allocator + .new_pair(first.raw, rest.raw) + .map_err(|e| self.map_err(loc.clone(), e))? + } + ModernSExp::Integer(_, value) => self + .allocator + .new_atom(&u8_from_number(value.clone())) + .map_err(|e| self.map_err(loc.clone(), e))?, + ModernSExp::QuotedString(_, _, value) | ModernSExp::Atom(_, value) => self + .allocator + .new_atom(value) + .map_err(|e| self.map_err(loc.clone(), e))?, + }; + Ok(SExpNode { sexp, raw }) + } +} + +impl ClassicAllocator for SExpClassicAllocator { + type NodePtr = SExpNode; + + fn loc(&self, node: &Self::NodePtr) -> Srcloc { + node.sexp.loc() + } + + fn sexp(&self, node: &Self::NodePtr) -> ASExp { + match node.sexp.as_ref() { + ModernSExp::Cons(_, first, rest) => { + let SExp::Pair(raw_first, raw_rest) = self.allocator.sexp(node.raw) else { + unreachable!("modern and raw S-expression representations diverged") + }; + ASExp::Pair( + SExpNode { + sexp: first.clone(), + raw: raw_first, + }, + SExpNode { + sexp: rest.clone(), + raw: raw_rest, + }, + ) + } + _ => ASExp::Atom, + } + } + + fn atom<'a>(&'a self, node: &Self::NodePtr) -> BufHolder<'a> { + BufHolder(self.allocator.atom(node.raw)) + } + + fn is_nil(&self, node: &Self::NodePtr) -> bool { + node.raw == NodePtr::NIL + } + + fn disassemble(&self, node: &Self::NodePtr, version: Option) -> String { + disassemble(&self.allocator, node.raw, version) + } + + fn allocator(&mut self) -> &mut Allocator { + &mut self.allocator + } + + fn node_equal(&self, a: &Self::NodePtr, b: &Self::NodePtr) -> bool { + a.raw == b.raw + } + + fn map_err(&self, loc: Srcloc, err: EvalErr) -> ClError { + ClError(loc, err) + } + + fn new_atom(&mut self, loc: Srcloc, value: &[u8]) -> Result { + let raw = self + .allocator + .new_atom(value) + .map_err(|e| ClError(loc.clone(), e))?; + Ok(SExpNode { + sexp: Rc::new(ModernSExp::Atom(loc, value.to_vec())), + raw, + }) + } + + fn new_pair( + &mut self, + loc: Srcloc, + a: &Self::NodePtr, + b: &Self::NodePtr, + ) -> Result { + let raw = self + .allocator + .new_pair(a.raw, b.raw) + .map_err(|e| ClError(loc.clone(), e))?; + Ok(SExpNode { + sexp: Rc::new(ModernSExp::Cons(loc, a.sexp.clone(), b.sexp.clone())), + raw, + }) + } + + fn import(&mut self, loc: Srcloc, node: NodePtr) -> Result { + let sexp = match self.allocator.sexp(node) { + SExp::Atom if node == NodePtr::NIL => Rc::new(ModernSExp::Nil(loc)), + SExp::Atom => Rc::new(ModernSExp::Atom( + loc, + self.allocator.atom(node).as_ref().to_vec(), + )), + SExp::Pair(first, rest) => { + let first = self.import(loc.clone(), first)?; + let rest = self.import(loc.clone(), rest)?; + Rc::new(ModernSExp::Cons(loc, first.sexp, rest.sexp)) + } + }; + Ok(SExpNode { sexp, raw: node }) + } + + fn export(&self, node: &Self::NodePtr) -> NodePtr { + node.raw + } +} diff --git a/src/compiler/compiler.rs b/src/compiler/compiler.rs index e75674405..17ad2c37d 100644 --- a/src/compiler/compiler.rs +++ b/src/compiler/compiler.rs @@ -14,6 +14,13 @@ use clvm_rs::allocator::Allocator; use crate::classic::clvm::__type_compatibility__::{bi_one, bi_zero}; use crate::classic::clvm_tools::ir::r#type::NEW_BIT_CONSTANTS; use crate::classic::clvm_tools::stages::stage_0::TRunProgram; +use crate::classic::clvm_tools::stages::stage_2::abstraction::{ + ClassicAllocator, SExpClassicAllocator, +}; +use crate::classic::clvm_tools::stages::stage_2::defaults::default_macro_lookup; +use crate::classic::clvm_tools::stages::stage_2::module::compile_mod; +use crate::classic::clvm_tools::stages::stage_2::operators::run_program_for_search_paths; +use crate::classic::clvm_tools::stages::stage_2::optimize::optimize_sexp; use crate::classic::clvm::__type_compatibility__::Stream; use crate::classic::clvm::sexp::sexp_as_bin; @@ -158,6 +165,10 @@ pub fn finish_compilation( opts: Rc, p2: CompileForm, ) -> Result { + if opts.dialect().classic_codegen { + return classic_codegen(opts, p2); + } + let p3 = context.post_desugar_optimization(opts.clone(), p2)?; // generate code from AST, optionally with optimization @@ -168,12 +179,57 @@ pub fn finish_compilation( Ok(g2) } +/// Generate code from a desugared modern CompileForm with classic stage 2. +/// +/// The classic compiler's normal final optimization is part of its code +/// generation contract. No modern optimization hooks run on this path. +fn classic_codegen(opts: Rc, program: CompileForm) -> Result { + let language_flags = if opts.dialect().extra_numeric_constants { + NEW_BIT_CONSTANTS + } else { + 0 + }; + let runner = run_program_for_search_paths( + &opts.filename(), + &opts.get_search_paths(), + false, + language_flags, + ); + runner.set_compiler_opts(Some(opts.clone())); + + let mut allocator = SExpClassicAllocator::new(); + let args = allocator + .from_sexp(program.to_sexp()) + .map_err(|e| CompileErr(e.0, e.1.to_string()))?; + let macro_lookup = default_macro_lookup(&mut allocator, runner.clone()); + let nil = allocator + .import(program.loc(), clvm_rs::allocator::NodePtr::NIL) + .map_err(|e| CompileErr(e.0, e.1.to_string()))?; + let generated = compile_mod( + &mut allocator, + &args, + ¯o_lookup, + &nil, + runner.clone(), + 0, + ) + .map_err(|e| CompileErr(e.0, e.1.to_string()))?; + let optimized = optimize_sexp(&mut allocator, &generated, runner) + .map_err(|e| CompileErr(e.0, e.1.to_string()))?; + + Ok(optimized.sexp.as_ref().clone()) +} + pub fn compile_from_compileform( context: &mut BasicCompileContext, opts: Rc, p0: CompileForm, ) -> Result { - let p1 = context.frontend_optimization(opts.clone(), p0)?; + let p1 = if opts.dialect().classic_codegen { + p0 + } else { + context.frontend_optimization(opts.clone(), p0)? + }; // Resolve includes, convert program source to lexemes let p2 = do_desugar(opts.clone(), &p1)?; diff --git a/src/compiler/dialect.rs b/src/compiler/dialect.rs index 06436bff2..8b7783de3 100644 --- a/src/compiler/dialect.rs +++ b/src/compiler/dialect.rs @@ -23,6 +23,8 @@ pub struct AcceptedDialect { pub extra_numeric_constants: bool, // Include fix for downstream cse dominance pub cse_dominance: bool, + // Use the modern frontend and desugaring with classic stage-2 code generation. + pub classic_codegen: bool, } /// A package containing the content we should insert when a dialect include is @@ -59,6 +61,7 @@ lazy_static! { int_fix: false, extra_numeric_constants: false, cse_dominance: false, + classic_codegen: false, }, content: indoc! {"( (defconstant *chialisp-version* 22) @@ -75,6 +78,7 @@ lazy_static! { int_fix: false, extra_numeric_constants: false, cse_dominance: false, + classic_codegen: false, }, content: indoc! {"( (defconstant *chialisp-version* 22) @@ -91,6 +95,7 @@ lazy_static! { int_fix: false, extra_numeric_constants: false, cse_dominance: false, + classic_codegen: false, }, content: indoc! {"( (defconstant *chialisp-version* 23) @@ -107,6 +112,7 @@ lazy_static! { int_fix: true, extra_numeric_constants: false, cse_dominance: false, + classic_codegen: false, }, content: indoc! {"( (defconstant *chialisp-version* 23) @@ -123,6 +129,7 @@ lazy_static! { int_fix: true, extra_numeric_constants: false, cse_dominance: false, + classic_codegen: false, }, content: indoc! {"( (defconstant *chialisp-version* 24) @@ -139,6 +146,7 @@ lazy_static! { int_fix: true, extra_numeric_constants: false, cse_dominance: false, + classic_codegen: false, }, content: indoc! {"( (defconstant *chialisp-version* 25) @@ -155,6 +163,7 @@ lazy_static! { int_fix: true, extra_numeric_constants: true, cse_dominance: false, + classic_codegen: false, }, content: indoc! {"( (defconstant *chialisp-version* 25) @@ -171,6 +180,7 @@ lazy_static! { int_fix: true, extra_numeric_constants: true, cse_dominance: false, + classic_codegen: false, }, content: indoc! {"()"}.to_string(), }, @@ -184,6 +194,24 @@ lazy_static! { int_fix: true, extra_numeric_constants: false, cse_dominance: false, + classic_codegen: false, + }, + content: indoc! {"( + (defconstant *chialisp-version* 26) + )"} + .to_string(), + }, + ), + ( + "*standard-cl-26-classic*", + DialectDescription { + accepted: AcceptedDialect { + stepping: Some(26), + strict: true, + int_fix: true, + extra_numeric_constants: false, + cse_dominance: false, + classic_codegen: true, }, content: indoc! {"( (defconstant *chialisp-version* 26) diff --git a/src/tests/compiler/cldb.rs b/src/tests/compiler/cldb.rs index 02d4128eb..b65a6fca1 100644 --- a/src/tests/compiler/cldb.rs +++ b/src/tests/compiler/cldb.rs @@ -125,6 +125,76 @@ fn test_run_clvm_in_cldb() { ); } +struct RecordsRecursiveSteps { + steps: Vec<(String, String)>, +} + +impl StepOfCldbViewer for RecordsRecursiveSteps { + fn show(&mut self, step: &RunStep, output: Option>) -> bool { + let arguments = output + .and_then(|values| values.get("Arguments").cloned()) + .unwrap_or_default(); + self.steps.push((step.loc().to_string(), arguments)); + true + } +} + +#[test] +fn test_classic_codegen_recursive_source_locations() { + let program_name = "classic_codegen_fact.clsp"; + let program_code = indoc! {" + (mod (N) + (include *standard-cl-26-classic*) + (defun fact (X) + (if (> X 1) + (* X (fact (- X 1))) + 1)) + (fact N)) + "}; + let mut allocator = Allocator::new(); + let runner = Rc::new(DefaultProgramRunner::new()); + let opts = Rc::new(DefaultCompilerOpts::new(program_name)); + let mut symbols = HashMap::new(); + let args = parse_sexp(Srcloc::start("*args*"), "(4)".bytes()).expect("should parse")[0].clone(); + let program = compile_file( + &mut allocator, + runner, + opts, + &program_code.to_string(), + &mut symbols, + ) + .expect("should compile"); + let mut watcher = RecordsRecursiveSteps { steps: Vec::new() }; + + assert_eq!( + run_clvm_in_cldb( + program_name, + Rc::new(program_code.lines().map(str::to_string).collect()), + Rc::new(program.to_sexp()), + symbols, + args, + &mut watcher, + 0, + ), + Some("24".to_string()) + ); + + let recursive_arguments: Vec<&str> = watcher + .steps + .iter() + .filter(|(loc, _)| loc == "classic_codegen_fact.clsp(4):10") + .map(|(_, arguments)| arguments.as_str()) + .collect(); + assert_eq!( + recursive_arguments, + vec!["(4 1)", "(3 1)", "(2 1)", "(1 1)"] + ); + assert!(watcher + .steps + .iter() + .any(|(loc, _)| loc.starts_with("classic_codegen_fact.clsp(5):"))); +} + #[test] fn test_cldb_hex_to_modern_sexp_smoke_0() { let mut allocator = Allocator::new(); diff --git a/src/tests/compiler/compiler.rs b/src/tests/compiler/compiler.rs index 91d4a5d92..60efb3301 100644 --- a/src/tests/compiler/compiler.rs +++ b/src/tests/compiler/compiler.rs @@ -1,4 +1,5 @@ use std::collections::{BTreeSet, HashMap}; +use std::fs; use std::rc::Rc; use clvm_rs::allocator::Allocator; @@ -52,6 +53,7 @@ fn run_string_maybe_opt( int_fix: false, extra_numeric_constants: false, cse_dominance: false, + classic_codegen: false, }); } @@ -89,6 +91,43 @@ pub fn run_string_strict(content: &String, args: &String) -> Result, Co run_string_maybe_opt(content, args, false, true) } +#[test] +fn test_modern_frontend_with_classic_codegen_semantics() { + let program = indoc! {" + (mod (N) + (include *standard-cl-26-classic*) + (defun fact (X) + (if (> X 1) + (let ((NEXT (- X 1))) + (assign REST (fact NEXT) + (* X REST))) + 1)) + (defun apply-one (F X) (a F (list X))) + (apply-one (lambda (X) (fact X)) N)) + "} + .to_string(); + + assert_eq!( + run_string(&program, &"(5)".to_string()) + .unwrap() + .to_string(), + "120" + ); +} + +#[test] +fn test_classic_codegen_matches_modern_bls_program_result() { + let modern = + fs::read_to_string("resources/tests/bls/modern-bls-verify-signature.clsp").unwrap(); + let classic_codegen = modern.replace("*standard-cl-21*", "*standard-cl-26-classic*"); + let args = "(0x0102030405)".to_string(); + + assert_eq!( + run_string(&classic_codegen, &args).unwrap(), + run_string(&modern, &args).unwrap() + ); +} + // Given some renaming that leaves behind gensym style names with _$_ in them, // order them and use a locally predictable renaming scheme to give them a final // test checkable value. @@ -2432,6 +2471,7 @@ fn test_handle_explicit_empty_atom() { int_fix: false, extra_numeric_constants: false, cse_dominance: false, + classic_codegen: false, }); let atom = |s: &str| Rc::new(SExp::Atom(srcloc.clone(), s.as_bytes().to_vec())); diff --git a/src/tests/compiler/optimizer/cse.rs b/src/tests/compiler/optimizer/cse.rs index 8b3f4c71e..5886ecb2d 100644 --- a/src/tests/compiler/optimizer/cse.rs +++ b/src/tests/compiler/optimizer/cse.rs @@ -888,6 +888,7 @@ fn test_generated_cse(n: u32) { int_fix: false, extra_numeric_constants: false, cse_dominance: false, + classic_codegen: false, }); let opts23 = opts .set_dialect(AcceptedDialect { @@ -896,6 +897,7 @@ fn test_generated_cse(n: u32) { int_fix: false, extra_numeric_constants: false, cse_dominance: false, + classic_codegen: false, }) .set_optimize(true); let mut symbols = HashMap::new(); diff --git a/src/tests/compiler/optimizer/cse_fuzz.rs b/src/tests/compiler/optimizer/cse_fuzz.rs index b9f26306a..140e5de07 100644 --- a/src/tests/compiler/optimizer/cse_fuzz.rs +++ b/src/tests/compiler/optimizer/cse_fuzz.rs @@ -249,6 +249,7 @@ impl PropertyTestState for TrickyAssignExpectation { int_fix: false, extra_numeric_constants: false, cse_dominance: false, + classic_codegen: false, }) .set_optimize(true), ) diff --git a/src/tests/compiler/optimizer/cse_regression.rs b/src/tests/compiler/optimizer/cse_regression.rs index 87c2bb5e3..3c8cbbdfc 100644 --- a/src/tests/compiler/optimizer/cse_regression.rs +++ b/src/tests/compiler/optimizer/cse_regression.rs @@ -110,6 +110,7 @@ fn test_cse_merge_regression() { int_fix: false, extra_numeric_constants: false, cse_dominance: false, + classic_codegen: false, }; let new_opts: Rc = Rc::new(DefaultCompilerOpts::new("test.clsp")) .set_dialect(dialect.clone()) diff --git a/src/tests/compiler/optimizer/deinline.rs b/src/tests/compiler/optimizer/deinline.rs index c66dd9bc1..c0b28a751 100644 --- a/src/tests/compiler/optimizer/deinline.rs +++ b/src/tests/compiler/optimizer/deinline.rs @@ -14,6 +14,7 @@ fn stepping_over_24_returns_true_for_module_compile_without_stepping() { int_fix: false, extra_numeric_constants: false, cse_dominance: false, + classic_codegen: false, }); assert!( !stepping_over_24(opts.clone()), @@ -36,6 +37,7 @@ fn stepping_over_24_returns_false_for_stepping_23() { int_fix: false, extra_numeric_constants: false, cse_dominance: false, + classic_codegen: false, }); assert!(!stepping_over_24(opts)); } @@ -49,6 +51,7 @@ fn stepping_over_24_returns_true_for_stepping_25() { int_fix: true, extra_numeric_constants: false, cse_dominance: false, + classic_codegen: false, }); assert!(stepping_over_24(opts)); } diff --git a/src/tests/compiler/optimizer/depgraph.rs b/src/tests/compiler/optimizer/depgraph.rs index 09b297cf8..bd11c92b3 100644 --- a/src/tests/compiler/optimizer/depgraph.rs +++ b/src/tests/compiler/optimizer/depgraph.rs @@ -17,6 +17,7 @@ fn get_depgraph_for_program(prog: &str) -> FunctionDependencyGraph { int_fix: false, extra_numeric_constants: false, cse_dominance: false, + classic_codegen: false, }); let compileform = frontend(opts.clone(), &forms).expect("should frontend"); diff --git a/src/tests/compiler/optimizer/output.rs b/src/tests/compiler/optimizer/output.rs index 8df13bc52..dd33c1312 100644 --- a/src/tests/compiler/optimizer/output.rs +++ b/src/tests/compiler/optimizer/output.rs @@ -137,6 +137,7 @@ fn run_string_get_program_and_output_with_includes( int_fix: false, extra_numeric_constants: false, cse_dominance: false, + classic_codegen: false, }, optimize: false, fe_opt, @@ -338,6 +339,7 @@ const SPEC_23: OptimizationRunSpec = OptimizationRunSpec { int_fix: false, extra_numeric_constants: false, cse_dominance: false, + classic_codegen: false, }, optimize: true, fe_opt: true, diff --git a/src/tests/compiler/preprocessor.rs b/src/tests/compiler/preprocessor.rs index c91ae6d9c..d9b8047c3 100644 --- a/src/tests/compiler/preprocessor.rs +++ b/src/tests/compiler/preprocessor.rs @@ -529,6 +529,7 @@ fn test_preprocess_basic_list() { int_fix: false, extra_numeric_constants: false, cse_dominance: false, + classic_codegen: false, }); let mut includes = Vec::new(); let parsed_lst: Vec> = parsed_forms[0] @@ -578,6 +579,7 @@ fn test_preprocessor_tours_includes_properly() { int_fix: false, extra_numeric_constants: false, cse_dominance: false, + classic_codegen: false, }); let parsed = parse_sexp(Srcloc::start(pname), prog.bytes()).expect("should parse"); let parsed_lst: Vec> = parsed[0] From 5a409e4d7c70a26ced7ccc8705232b6a4e8ca459 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 29 Jul 2026 20:49:33 +0000 Subject: [PATCH 10/38] Clean up recursive location test Co-authored-by: arty --- src/tests/compiler/cldb.rs | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/tests/compiler/cldb.rs b/src/tests/compiler/cldb.rs index b65a6fca1..1f86b6f76 100644 --- a/src/tests/compiler/cldb.rs +++ b/src/tests/compiler/cldb.rs @@ -156,14 +156,8 @@ fn test_classic_codegen_recursive_source_locations() { let opts = Rc::new(DefaultCompilerOpts::new(program_name)); let mut symbols = HashMap::new(); let args = parse_sexp(Srcloc::start("*args*"), "(4)".bytes()).expect("should parse")[0].clone(); - let program = compile_file( - &mut allocator, - runner, - opts, - &program_code.to_string(), - &mut symbols, - ) - .expect("should compile"); + let program = compile_file(&mut allocator, runner, opts, program_code, &mut symbols) + .expect("should compile"); let mut watcher = RecordsRecursiveSteps { steps: Vec::new() }; assert_eq!( From d3a3cb89980cb9b510bbf7d64795351980aff466 Mon Sep 17 00:00:00 2001 From: art yerkes Date: Thu, 30 Jul 2026 14:18:52 -0700 Subject: [PATCH 11/38] mark point where we need to assemble com's first argument in modern mode --- .../clvm_tools/stages/stage_2/abstraction.rs | 6 ++++++ src/classic/clvm_tools/stages/stage_2/compile.rs | 1 + src/classic/clvm_tools/stages/stage_2/operators.rs | 13 ++++++++++++- src/compiler/compiler.rs | 3 +++ 4 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/classic/clvm_tools/stages/stage_2/abstraction.rs b/src/classic/clvm_tools/stages/stage_2/abstraction.rs index 551f1f97b..4d34f27d2 100644 --- a/src/classic/clvm_tools/stages/stage_2/abstraction.rs +++ b/src/classic/clvm_tools/stages/stage_2/abstraction.rs @@ -139,6 +139,12 @@ pub struct SExpNode { raw: NodePtr, } +impl std::fmt::Display for SExpNode { + fn fmt(&self, formatter: &'_ mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { + write!(formatter, "{}", self.sexp) + } +} + /// Adapts modern compiler S-expressions to the classic stage-2 compiler. pub struct SExpClassicAllocator { allocator: Allocator, diff --git a/src/classic/clvm_tools/stages/stage_2/compile.rs b/src/classic/clvm_tools/stages/stage_2/compile.rs index 0663d0906..8248ab721 100644 --- a/src/classic/clvm_tools/stages/stage_2/compile.rs +++ b/src/classic/clvm_tools/stages/stage_2/compile.rs @@ -722,6 +722,7 @@ fn do_com_prog_( where A::NodePtr: Clone, { + eprintln!("do_com_prog {}", allocator.disassemble(prog_, None)); /* * Turn the given program `prog` into a clvm program using * the macros to do transformation. diff --git a/src/classic/clvm_tools/stages/stage_2/operators.rs b/src/classic/clvm_tools/stages/stage_2/operators.rs index fe47c99e4..f84f68f28 100644 --- a/src/classic/clvm_tools/stages/stage_2/operators.rs +++ b/src/classic/clvm_tools/stages/stage_2/operators.rs @@ -18,7 +18,7 @@ use crate::classic::clvm::OPERATORS_LATEST_VERSION; use crate::classic::clvm::keyword_from_atom; use crate::classic::clvm::sexp::proper_list; -use crate::classic::clvm_tools::binutils::{assemble_from_ir, disassemble_to_ir_with_kw}; +use crate::classic::clvm_tools::binutils::{assemble_from_ir, disassemble_to_ir_with_kw, disassemble}; use crate::classic::clvm_tools::ir::reader::read_ir; use crate::classic::clvm_tools::ir::writer::write_ir_to_stream; use crate::classic::clvm_tools::sha256tree::TreeHash; @@ -390,6 +390,12 @@ impl CompilerOperatorsInternal { .and_then(|o| o.disassembly_ver()) .unwrap_or(OPERATORS_LATEST_VERSION) } + + fn is_modern(&self) -> bool { + self.get_compiler_opts() + .map(|o| o.dialect().strict) + .unwrap_or(false) + } } impl Dialect for CompilerOperatorsInternal { @@ -440,6 +446,8 @@ impl Dialect for CompilerOperatorsInternal { // compiler doesn't itself run in a softfork. let extensions_to_clvmr_during_compile = self.get_operators_extension(); + eprintln!("op {} for {}", disassemble(allocator, op, None), disassemble(allocator, sexp, None)); + match allocator.sexp(op) { SExp::Atom => { // use of op obvious. @@ -450,6 +458,9 @@ impl Dialect for CompilerOperatorsInternal { } else if opbuf == b"_write" { self.write(allocator, sexp) } else if opbuf == b"com" { + if self.is_modern() { + todo!(); + } let result = do_com_prog_for_dialect(self.get_runner(), allocator, &sexp)?; Ok(Reduction(1, result)) } else if opbuf == b"opt" { diff --git a/src/compiler/compiler.rs b/src/compiler/compiler.rs index 17ad2c37d..d149d9dec 100644 --- a/src/compiler/compiler.rs +++ b/src/compiler/compiler.rs @@ -205,6 +205,8 @@ fn classic_codegen(opts: Rc, program: CompileForm) -> Result Result { let p1 = if opts.dialect().classic_codegen { + eprintln!("classic program {}", p0.to_sexp()); p0 } else { context.frontend_optimization(opts.clone(), p0)? From b380a1143f0f0e58bb72cb5b823c88b6338426f4 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 30 Jul 2026 21:26:56 +0000 Subject: [PATCH 12/38] Assemble modern classic compiler operators Co-authored-by: arty --- .../clvm_tools/stages/stage_2/operators.rs | 146 +++++++++++++++++- 1 file changed, 138 insertions(+), 8 deletions(-) diff --git a/src/classic/clvm_tools/stages/stage_2/operators.rs b/src/classic/clvm_tools/stages/stage_2/operators.rs index f84f68f28..d72246a91 100644 --- a/src/classic/clvm_tools/stages/stage_2/operators.rs +++ b/src/classic/clvm_tools/stages/stage_2/operators.rs @@ -15,17 +15,21 @@ use clvm_rs::run_program::run_program_with_pre_eval; use crate::classic::clvm::__type_compatibility__::{Bytes, BytesFromType, Stream}; use crate::classic::clvm::OPERATORS_LATEST_VERSION; -use crate::classic::clvm::keyword_from_atom; use crate::classic::clvm::sexp::proper_list; +use crate::classic::clvm::{keyword_from_atom, keyword_to_atom}; -use crate::classic::clvm_tools::binutils::{assemble_from_ir, disassemble_to_ir_with_kw, disassemble}; +use crate::classic::clvm_tools::binutils::{ + assemble_from_ir, disassemble, disassemble_to_ir_with_kw, +}; use crate::classic::clvm_tools::ir::reader::read_ir; use crate::classic::clvm_tools::ir::writer::write_ir_to_stream; use crate::classic::clvm_tools::sha256tree::TreeHash; use crate::classic::clvm_tools::stages::stage_0::{ DefaultProgramRunner, OriginalDialect, RunProgramOption, TRunProgram, }; -use crate::classic::clvm_tools::stages::stage_2::abstraction::{ClError, ClassicAllocator}; +use crate::classic::clvm_tools::stages::stage_2::abstraction::{ + ASExp, BufCarrier, ClError, ClassicAllocator, +}; use crate::classic::clvm_tools::stages::stage_2::compile::do_com_prog_for_dialect; use crate::classic::clvm_tools::stages::stage_2::optimize::do_optimize; @@ -398,6 +402,61 @@ impl CompilerOperatorsInternal { } } +fn assemble_operator_heads( + allocator: &mut A, + sexp: &A::NodePtr, + operator_position: bool, +) -> Result +where + A::NodePtr: Clone, +{ + match allocator.sexp(sexp) { + ASExp::Pair(first, rest) => { + // A pair in the head of an argument list starts a nested expression. + let first_is_expression = matches!(allocator.sexp(&first), ASExp::Pair(_, _)); + let first = assemble_operator_heads( + allocator, + &first, + operator_position || first_is_expression, + )?; + let rest = assemble_operator_heads(allocator, &rest, false)?; + allocator.new_pair(allocator.loc(sexp), &first, &rest) + } + ASExp::Atom => { + if !operator_position { + return Ok(sexp.clone()); + } + let replacement = { + let atom = allocator.atom(sexp); + std::str::from_utf8(atom.as_ref()) + .ok() + .and_then(|name| keyword_to_atom(OPERATORS_LATEST_VERSION).get(name)) + .cloned() + }; + replacement.map_or_else( + || Ok(sexp.clone()), + |value| allocator.new_atom(allocator.loc(sexp), &value), + ) + } + } +} + +fn assemble_com_program( + allocator: &mut A, + sexp: &A::NodePtr, +) -> Result +where + A::NodePtr: Clone, +{ + match allocator.sexp(sexp) { + ASExp::Pair(program, extras) => { + let program = assemble_operator_heads(allocator, &program, true)?; + allocator.new_pair(allocator.loc(sexp), &program, &extras) + } + ASExp::Atom => Ok(sexp.clone()), + } +} + impl Dialect for CompilerOperatorsInternal { fn quote_kw(&self) -> u32 { 1 @@ -446,7 +505,11 @@ impl Dialect for CompilerOperatorsInternal { // compiler doesn't itself run in a softfork. let extensions_to_clvmr_during_compile = self.get_operators_extension(); - eprintln!("op {} for {}", disassemble(allocator, op, None), disassemble(allocator, sexp, None)); + eprintln!( + "op {} for {}", + disassemble(allocator, op, None), + disassemble(allocator, sexp, None) + ); match allocator.sexp(op) { SExp::Atom => { @@ -458,10 +521,14 @@ impl Dialect for CompilerOperatorsInternal { } else if opbuf == b"_write" { self.write(allocator, sexp) } else if opbuf == b"com" { - if self.is_modern() { - todo!(); - } - let result = do_com_prog_for_dialect(self.get_runner(), allocator, &sexp)?; + let assembled; + let sexp = if self.is_modern() { + assembled = assemble_com_program(allocator, &sexp)?; + &assembled + } else { + &sexp + }; + let result = do_com_prog_for_dialect(self.get_runner(), allocator, sexp)?; Ok(Reduction(1, result)) } else if opbuf == b"opt" { let result = do_optimize(self.get_runner(), allocator, &self.opt_memo, &sexp)?; @@ -570,3 +637,66 @@ pub fn run_program_for_search_paths( ops.parent.set_runner(ops.parent.clone()); ops } + +#[cfg(test)] +mod tests { + use super::assemble_com_program; + use crate::classic::clvm_tools::stages::stage_2::abstraction::{ + ASExp, BufCarrier, ClassicAllocator, SExpClassicAllocator, + }; + use crate::compiler::sexp::{enlist, SExp}; + use crate::compiler::srcloc::Srcloc; + use std::rc::Rc; + + #[test] + fn modern_com_program_assembles_operator_heads_with_their_source_locations() { + let filename = Rc::new("*assemble-operator-test*".to_string()); + let program_loc = Srcloc::new(filename.clone(), 1, 1); + let quote_loc = Srcloc::new(filename.clone(), 1, 2); + let argument_loc = Srcloc::new(filename.clone(), 1, 4); + let nested_loc = Srcloc::new(filename.clone(), 1, 6); + let nested_quote_loc = Srcloc::new(filename, 1, 7); + + let nested = Rc::new(enlist( + nested_loc, + &[Rc::new(SExp::Atom(nested_quote_loc.clone(), b"q".to_vec()))], + )); + let program = Rc::new(enlist( + program_loc.clone(), + &[ + Rc::new(SExp::Atom(quote_loc.clone(), b"q".to_vec())), + Rc::new(SExp::Atom(argument_loc.clone(), b"q".to_vec())), + nested, + ], + )); + let com_arguments = Rc::new(enlist(program_loc, &[program])); + + let mut allocator = SExpClassicAllocator::new(); + let com_arguments = allocator.from_sexp(com_arguments).unwrap(); + let assembled = assemble_com_program(&mut allocator, &com_arguments).unwrap(); + + let ASExp::Pair(program, _) = allocator.sexp(&assembled) else { + panic!("com arguments must be a pair"); + }; + let ASExp::Pair(operator, arguments) = allocator.sexp(&program) else { + panic!("program must be a pair"); + }; + assert_eq!(allocator.atom(&operator).as_ref(), &[1]); + assert_eq!(allocator.loc(&operator), quote_loc); + + let ASExp::Pair(argument, remaining) = allocator.sexp(&arguments) else { + panic!("program arguments must be a pair"); + }; + assert_eq!(allocator.atom(&argument).as_ref(), b"q"); + assert_eq!(allocator.loc(&argument), argument_loc); + + let ASExp::Pair(nested, _) = allocator.sexp(&remaining) else { + panic!("nested expression must be present"); + }; + let ASExp::Pair(nested_operator, _) = allocator.sexp(&nested) else { + panic!("nested expression must be a pair"); + }; + assert_eq!(allocator.atom(&nested_operator).as_ref(), &[1]); + assert_eq!(allocator.loc(&nested_operator), nested_quote_loc); + } +} From 7341909ce6d490e7c4d7d822c1bf9918ebaa3ef6 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 30 Jul 2026 23:27:03 +0000 Subject: [PATCH 13/38] Support rest tails in classic function calls Co-authored-by: arty --- .../clvm_tools/stages/stage_2/compile.rs | 143 +++++++++++++----- src/tests/compiler/compiler.rs | 19 +++ 2 files changed, 127 insertions(+), 35 deletions(-) diff --git a/src/classic/clvm_tools/stages/stage_2/compile.rs b/src/classic/clvm_tools/stages/stage_2/compile.rs index 8248ab721..ef98ac044 100644 --- a/src/classic/clvm_tools/stages/stage_2/compile.rs +++ b/src/classic/clvm_tools/stages/stage_2/compile.rs @@ -580,6 +580,73 @@ where Ok(None) } +fn split_rest_tail( + allocator: &A, + args: &A::NodePtr, +) -> Result, Option)>, ClError> +where + A::NodePtr: Clone, +{ + let Some(mut args) = proper_list(allocator, args, true).map(|args| args.to_vec()) else { + return Ok(None); + }; + + let rest_index = args.iter().position(|arg| match allocator.sexp(arg) { + ASExp::Atom => allocator.atom(arg).as_ref() == b"&rest", + ASExp::Pair(_, _) => false, + }); + + let Some(rest_index) = rest_index else { + return Ok(Some((args, None))); + }; + + if rest_index + 2 != args.len() { + return Err(ClError( + allocator.loc(&args[rest_index]), + EvalErr::InternalError( + allocator.export(&args[rest_index]), + "&rest must be followed by exactly one tail expression".to_string(), + ), + )); + } + + let tail = args.pop(); + args.pop(); + Ok(Some((args, tail))) +} + +fn enlist_with_tail( + allocator: &mut A, + args: &[A::NodePtr], + tail: &A::NodePtr, +) -> Result +where + A::NodePtr: Clone, +{ + let mut result = tail.clone(); + for arg in args.iter().rev() { + result = allocator.new_pair(allocator.loc(arg), arg, &result)?; + } + Ok(result) +} + +fn rest_argument_source( + allocator: &mut A, + args: &[A::NodePtr], + tail: &A::NodePtr, +) -> Result +where + A::NodePtr: Clone, +{ + let mut result = tail.clone(); + for arg in args.iter().rev() { + let loc = allocator.loc(arg); + let cons = allocator.new_atom(loc.clone(), b"c")?; + result = enlist(allocator, &[cons, arg.clone(), result])?; + } + Ok(result) +} + #[allow(clippy::too_many_arguments)] fn compile_application( allocator: &mut A, @@ -614,8 +681,8 @@ where return allocator.new_pair(rest_loc, operator, rest); } - match proper_list(allocator, rest, true) { - Some(prog_args) => { + match split_rest_tail(allocator, rest)? { + Some((prog_args, tail_arg)) => { let mut new_args = map_m(allocator, &mut prog_args.iter(), &|allocator, arg| { do_com_prog( allocator, @@ -627,8 +694,19 @@ where ) })?; + let compiled_tail = match &tail_arg { + Some(tail) => do_com_prog( + allocator, + 544, + tail, + macro_lookup, + symbol_table, + run_program.clone(), + )?, + None => allocator.import(allocator.loc(rest), NodePtr::NIL)?, + }; compiled_args.append(&mut new_args); - let r = enlist(allocator, &compiled_args)?; + let r = enlist_with_tail(allocator, &compiled_args, &compiled_tail)?; if PASS_THROUGH_OPERATORS.contains(opbuf) || (!opbuf.is_empty() && opbuf[0] == b'_') { Ok(r) @@ -636,39 +714,34 @@ where find_symbol_match(allocator, opbuf, &r, symbol_table).and_then(|x| match x { Some(SymbolResult::Direct(v)) => Ok(v), Some(SymbolResult::Matched(_symbol, value)) => { - match proper_list(allocator, rest, true) { - Some(proglist) => { - let loc = allocator.loc(&value); - let apply_atom = allocator.new_atom(loc.clone(), &[2])?; - let list_atom = - allocator.new_atom(loc.clone(), "list".as_bytes())?; - let cons_atom = allocator.new_atom(loc.clone(), &[4])?; - let com_atom = allocator.new_atom(loc.clone(), "com".as_bytes())?; - let opt_atom = allocator.new_atom(loc.clone(), "opt".as_bytes())?; - let top_atom = allocator - .new_atom(loc.clone(), NodePath::new(None).as_path().data())?; - let left_atom = allocator.new_atom( - loc.clone(), - NodePath::new(None).first().as_path().data(), - )?; - let enlisted = enlist(allocator, &proglist)?; - let list_application = - allocator.new_pair(loc, &list_atom, &enlisted)?; - let quoted_list = quote(allocator, &list_application)?; - let quoted_macros = quote(allocator, macro_lookup)?; - let quoted_symbols = quote(allocator, symbol_table)?; - let compiled = enlist( - allocator, - &[com_atom, quoted_list, quoted_macros, quoted_symbols], - )?; - let to_run = enlist(allocator, &[opt_atom, compiled])?; - let new_args = evaluate(allocator, &to_run, &top_atom)?; - let cons_enlisted = - enlist(allocator, &[cons_atom, left_atom, new_args])?; - enlist(allocator, &[apply_atom, value, cons_enlisted]) + let loc = allocator.loc(&value); + let apply_atom = allocator.new_atom(loc.clone(), &[2])?; + let list_atom = allocator.new_atom(loc.clone(), "list".as_bytes())?; + let cons_atom = allocator.new_atom(loc.clone(), &[4])?; + let com_atom = allocator.new_atom(loc.clone(), "com".as_bytes())?; + let opt_atom = allocator.new_atom(loc.clone(), "opt".as_bytes())?; + let top_atom = allocator + .new_atom(loc.clone(), NodePath::new(None).as_path().data())?; + let left_atom = allocator + .new_atom(loc.clone(), NodePath::new(None).first().as_path().data())?; + let argument_source = match &tail_arg { + Some(tail) => rest_argument_source(allocator, &prog_args, tail)?, + None => { + let enlisted = enlist(allocator, &prog_args)?; + allocator.new_pair(loc, &list_atom, &enlisted)? } - None => error_result, - } + }; + let quoted_list = quote(allocator, &argument_source)?; + let quoted_macros = quote(allocator, macro_lookup)?; + let quoted_symbols = quote(allocator, symbol_table)?; + let compiled = enlist( + allocator, + &[com_atom, quoted_list, quoted_macros, quoted_symbols], + )?; + let to_run = enlist(allocator, &[opt_atom, compiled])?; + let new_args = evaluate(allocator, &to_run, &top_atom)?; + let cons_enlisted = enlist(allocator, &[cons_atom, left_atom, new_args])?; + enlist(allocator, &[apply_atom, value, cons_enlisted]) } None => error_result, }) diff --git a/src/tests/compiler/compiler.rs b/src/tests/compiler/compiler.rs index 60efb3301..7696b3364 100644 --- a/src/tests/compiler/compiler.rs +++ b/src/tests/compiler/compiler.rs @@ -115,6 +115,25 @@ fn test_modern_frontend_with_classic_codegen_semantics() { ); } +#[test] +fn test_classic_codegen_function_call_with_rest_tail() { + let program = indoc! {" + (mod (Xs) + (include *standard-cl-26-classic*) + (defun collect (A B C D) + (list A B C D)) + (collect 5 &rest Xs)) + "} + .to_string(); + + assert_eq!( + run_string(&program, &"((7 11 13))".to_string()) + .unwrap() + .to_string(), + "(5 7 11 13)" + ); +} + #[test] fn test_classic_codegen_matches_modern_bls_program_result() { let modern = From b718bcedc2caa8696ec8cb90d0aab26e3e7b0e76 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 30 Jul 2026 23:44:57 +0000 Subject: [PATCH 14/38] Support environment pseudo-variable in classic codegen Co-authored-by: arty --- .../clvm_tools/stages/stage_2/compile.rs | 2 +- .../clvm_tools/stages/stage_2/module.rs | 96 ++++++++++++++++++- src/tests/compiler/compiler.rs | 23 +++++ 3 files changed, 117 insertions(+), 4 deletions(-) diff --git a/src/classic/clvm_tools/stages/stage_2/compile.rs b/src/classic/clvm_tools/stages/stage_2/compile.rs index ef98ac044..c3cdddfe9 100644 --- a/src/classic/clvm_tools/stages/stage_2/compile.rs +++ b/src/classic/clvm_tools/stages/stage_2/compile.rs @@ -448,7 +448,7 @@ where A::NodePtr: Clone, { let loc = allocator.loc(prog); - if a == b"@" { + if a == b"@" || a == b"@*env*" { return allocator.new_atom(loc, NodePath::new(None).as_path().data()); } diff --git a/src/classic/clvm_tools/stages/stage_2/module.rs b/src/classic/clvm_tools/stages/stage_2/module.rs index 16d159272..114052aa4 100644 --- a/src/classic/clvm_tools/stages/stage_2/module.rs +++ b/src/classic/clvm_tools/stages/stage_2/module.rs @@ -257,6 +257,7 @@ fn unquote_args( code: &A::NodePtr, args: &[Vec], matches: &HashMap, A::NodePtr>, + inline_env: &A::NodePtr, ) -> Result where A::NodePtr: Clone, @@ -265,6 +266,9 @@ where ASExp::Atom => { // Only code in scope. let code_atom = allocator.atom(code); + if code_atom.as_ref() == b"@*env*" { + return Ok(inline_env.clone()); + } let matching_args = args .iter() .filter(|arg| *arg == code_atom.as_ref()) @@ -285,14 +289,89 @@ where Ok(code.clone()) } ASExp::Pair(c1, c2) => { - let unquoted_c2 = unquote_args(allocator, &c2, args, matches)?; - let unquoted_c1 = unquote_args(allocator, &c1, args, matches)?; + let unquoted_c2 = unquote_args(allocator, &c2, args, matches, inline_env)?; + let unquoted_c1 = unquote_args(allocator, &c1, args, matches, inline_env)?; let loc = allocator.loc(&c1); allocator.new_pair(loc, &unquoted_c1, &unquoted_c2) } } } +fn inline_argument_reference( + allocator: &mut A, + arg: &A::NodePtr, + matches: &HashMap, A::NodePtr>, +) -> Result +where + A::NodePtr: Clone, +{ + let reference = match allocator.sexp(arg) { + ASExp::Atom => { + let name = allocator.atom(arg).as_ref().to_vec(); + if let Some(selection) = matches.get(&name) { + return Ok(selection.clone()); + } + arg.clone() + } + ASExp::Pair(first, rest) => { + let Some((capture, _)) = is_at_capture(allocator, &first, &rest) else { + return Err(ClError( + allocator.loc(arg), + EvalErr::InternalError( + allocator.export(arg), + "inline argument destructuring is missing its source capture".to_string(), + ), + )); + }; + capture + } + }; + + let loc = allocator.loc(&reference); + let unquote = allocator.new_atom(loc, b"unquote")?; + enlist(allocator, &[unquote, reference]) +} + +fn inline_environment( + allocator: &mut A, + args: &A::NodePtr, + matches: &HashMap, A::NodePtr>, +) -> Result +where + A::NodePtr: Clone, +{ + let loc = allocator.loc(args); + let Some(args) = proper_list(allocator, args, true) else { + return Err(ClError( + loc, + EvalErr::InternalError( + allocator.export(args), + "@*env* does not yet support an inline function with a dotted argument list" + .to_string(), + ), + )); + }; + + let cons = allocator.new_atom(loc.clone(), b"c")?; + let left_environment = allocator.new_atom(loc.clone(), &[2])?; + let mut right_environment = allocator.import(loc.clone(), NodePtr::NIL)?; + for arg in args.iter().rev() { + let argument = inline_argument_reference(allocator, arg, matches)?; + right_environment = enlist(allocator, &[cons.clone(), argument, right_environment])?; + } + enlist(allocator, &[cons, left_environment, right_environment]) +} + +fn contains_inline_environment(allocator: &A, code: &A::NodePtr) -> bool { + match allocator.sexp(code) { + ASExp::Atom => allocator.atom(code).as_ref() == b"@*env*", + ASExp::Pair(first, rest) => { + contains_inline_environment(allocator, &first) + || contains_inline_environment(allocator, &rest) + } + } +} + fn defun_inline_to_macro( allocator: &mut A, declaration_sexp: &A::NodePtr, @@ -339,7 +418,18 @@ where .map(|v| v.as_ref().to_vec()) .collect::>>(); - let unquoted_code = unquote_args(allocator, &code, &arg_name_list, &destructure_matches)?; + let inline_env = if contains_inline_environment(allocator, &code) { + inline_environment(allocator, &use_args, &destructure_matches)? + } else { + code.clone() + }; + let unquoted_code = unquote_args( + allocator, + &code, + &arg_name_list, + &destructure_matches, + &inline_env, + )?; let loc = allocator.loc(&unquoted_code); let qq_atom = allocator.new_atom(loc, "qq".as_bytes())?; diff --git a/src/tests/compiler/compiler.rs b/src/tests/compiler/compiler.rs index 7696b3364..8ec4b6f17 100644 --- a/src/tests/compiler/compiler.rs +++ b/src/tests/compiler/compiler.rs @@ -134,6 +134,29 @@ fn test_classic_codegen_function_call_with_rest_tail() { ); } +#[test] +fn test_classic_codegen_environment_pseudo_variable() { + let program = indoc! {" + (mod (X) + (include *standard-cl-26-classic*) + (defun regular (A B) + (r @*env*)) + (defun-inline inlined (A B) + (r @*env*)) + (list + (regular X (+ X 1)) + (inlined (+ X 2) (+ X 3)))) + "} + .to_string(); + + assert_eq!( + run_string(&program, &"(5)".to_string()) + .unwrap() + .to_string(), + "((5 6) (7 8))" + ); +} + #[test] fn test_classic_codegen_matches_modern_bls_program_result() { let modern = From b5fdbe1ab1bfb62fc67063759bbc4577e47aefb8 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 30 Jul 2026 23:46:16 +0000 Subject: [PATCH 15/38] Reconstruct complex inline environments Co-authored-by: arty --- .../clvm_tools/stages/stage_2/module.rs | 31 ++++++++++++------- src/tests/compiler/compiler.rs | 10 ++++-- 2 files changed, 28 insertions(+), 13 deletions(-) diff --git a/src/classic/clvm_tools/stages/stage_2/module.rs b/src/classic/clvm_tools/stages/stage_2/module.rs index 114052aa4..537aba7a2 100644 --- a/src/classic/clvm_tools/stages/stage_2/module.rs +++ b/src/classic/clvm_tools/stages/stage_2/module.rs @@ -341,21 +341,30 @@ where A::NodePtr: Clone, { let loc = allocator.loc(args); - let Some(args) = proper_list(allocator, args, true) else { - return Err(ClError( - loc, - EvalErr::InternalError( - allocator.export(args), - "@*env* does not yet support an inline function with a dotted argument list" - .to_string(), - ), - )); + let argument_tree = match allocator.sexp(args) { + ASExp::Pair(first, rest) => is_at_capture(allocator, &first, &rest) + .map(|(_, destructure)| destructure) + .unwrap_or_else(|| args.clone()), + ASExp::Atom => args.clone(), }; let cons = allocator.new_atom(loc.clone(), b"c")?; let left_environment = allocator.new_atom(loc.clone(), &[2])?; - let mut right_environment = allocator.import(loc.clone(), NodePtr::NIL)?; - for arg in args.iter().rev() { + let mut argument_references = Vec::new(); + let mut remaining = argument_tree; + while let ASExp::Pair(first, rest) = allocator.sexp(&remaining) { + argument_references.push(first); + remaining = rest; + } + + let mut right_environment = if allocator.is_nil(&remaining) { + allocator.import(loc.clone(), NodePtr::NIL)? + } else { + let tail = inline_argument_reference(allocator, &remaining, matches)?; + let enlist_args = allocator.new_atom(loc.clone(), b"__chia__enlist")?; + enlist(allocator, &[enlist_args, tail])? + }; + for arg in argument_references.iter().rev() { let argument = inline_argument_reference(allocator, arg, matches)?; right_environment = enlist(allocator, &[cons.clone(), argument, right_environment])?; } diff --git a/src/tests/compiler/compiler.rs b/src/tests/compiler/compiler.rs index 8ec4b6f17..959923a04 100644 --- a/src/tests/compiler/compiler.rs +++ b/src/tests/compiler/compiler.rs @@ -143,9 +143,15 @@ fn test_classic_codegen_environment_pseudo_variable() { (r @*env*)) (defun-inline inlined (A B) (r @*env*)) + (defun-inline destructured ((A B) C) + (r @*env*)) + (defun-inline variadic (A . Rest) + (r @*env*)) (list (regular X (+ X 1)) - (inlined (+ X 2) (+ X 3)))) + (inlined (+ X 2) (+ X 3)) + (destructured (list X (+ X 1)) (+ X 2)) + (variadic (+ X 4) (+ X 5) (+ X 6)))) "} .to_string(); @@ -153,7 +159,7 @@ fn test_classic_codegen_environment_pseudo_variable() { run_string(&program, &"(5)".to_string()) .unwrap() .to_string(), - "((5 6) (7 8))" + "((5 6) (7 8) ((5 6) 7) (9 10 11))" ); } From 8ed3dfc2281d27a8c2704e11f4e211354dc4a53a Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 30 Jul 2026 23:47:02 +0000 Subject: [PATCH 16/38] Cover rest tails in inline environment test Co-authored-by: arty --- src/tests/compiler/compiler.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tests/compiler/compiler.rs b/src/tests/compiler/compiler.rs index 959923a04..ed2c00674 100644 --- a/src/tests/compiler/compiler.rs +++ b/src/tests/compiler/compiler.rs @@ -151,7 +151,7 @@ fn test_classic_codegen_environment_pseudo_variable() { (regular X (+ X 1)) (inlined (+ X 2) (+ X 3)) (destructured (list X (+ X 1)) (+ X 2)) - (variadic (+ X 4) (+ X 5) (+ X 6)))) + (variadic (+ X 4) &rest (list (+ X 5) (+ X 6))))) "} .to_string(); From b289e2ee35e2e828a7ee29aae592ddd242e67478 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 30 Jul 2026 23:47:45 +0000 Subject: [PATCH 17/38] Keep variadic environment coverage source-based Co-authored-by: arty --- src/tests/compiler/compiler.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tests/compiler/compiler.rs b/src/tests/compiler/compiler.rs index ed2c00674..959923a04 100644 --- a/src/tests/compiler/compiler.rs +++ b/src/tests/compiler/compiler.rs @@ -151,7 +151,7 @@ fn test_classic_codegen_environment_pseudo_variable() { (regular X (+ X 1)) (inlined (+ X 2) (+ X 3)) (destructured (list X (+ X 1)) (+ X 2)) - (variadic (+ X 4) &rest (list (+ X 5) (+ X 6))))) + (variadic (+ X 4) (+ X 5) (+ X 6)))) "} .to_string(); From f87c3a85b46d295ebba4ab464fe76eebfb91906c Mon Sep 17 00:00:00 2001 From: art yerkes Date: Thu, 30 Jul 2026 17:01:21 -0700 Subject: [PATCH 18/38] Add a smoke test --- .../clvm_tools/stages/stage_2/compile.rs | 1 - .../clvm_tools/stages/stage_2/operators.rs | 6 ----- src/compiler/compiler.rs | 3 --- src/tests/classic/run.rs | 25 +++++++++++++++++++ 4 files changed, 25 insertions(+), 10 deletions(-) diff --git a/src/classic/clvm_tools/stages/stage_2/compile.rs b/src/classic/clvm_tools/stages/stage_2/compile.rs index c3cdddfe9..fa3897bfc 100644 --- a/src/classic/clvm_tools/stages/stage_2/compile.rs +++ b/src/classic/clvm_tools/stages/stage_2/compile.rs @@ -795,7 +795,6 @@ fn do_com_prog_( where A::NodePtr: Clone, { - eprintln!("do_com_prog {}", allocator.disassemble(prog_, None)); /* * Turn the given program `prog` into a clvm program using * the macros to do transformation. diff --git a/src/classic/clvm_tools/stages/stage_2/operators.rs b/src/classic/clvm_tools/stages/stage_2/operators.rs index d72246a91..549e89889 100644 --- a/src/classic/clvm_tools/stages/stage_2/operators.rs +++ b/src/classic/clvm_tools/stages/stage_2/operators.rs @@ -505,12 +505,6 @@ impl Dialect for CompilerOperatorsInternal { // compiler doesn't itself run in a softfork. let extensions_to_clvmr_during_compile = self.get_operators_extension(); - eprintln!( - "op {} for {}", - disassemble(allocator, op, None), - disassemble(allocator, sexp, None) - ); - match allocator.sexp(op) { SExp::Atom => { // use of op obvious. diff --git a/src/compiler/compiler.rs b/src/compiler/compiler.rs index d149d9dec..17ad2c37d 100644 --- a/src/compiler/compiler.rs +++ b/src/compiler/compiler.rs @@ -205,8 +205,6 @@ fn classic_codegen(opts: Rc, program: CompileForm) -> Result Result { let p1 = if opts.dialect().classic_codegen { - eprintln!("classic program {}", p0.to_sexp()); p0 } else { context.frontend_optimization(opts.clone(), p0)? diff --git a/src/tests/classic/run.rs b/src/tests/classic/run.rs index 2b83fc331..4b6e2ae24 100644 --- a/src/tests/classic/run.rs +++ b/src/tests/classic/run.rs @@ -2775,3 +2775,28 @@ fn test_big_env_program_overflow_and_fix() { program_from_earlier_chialisp_hex.trim() ); } + +#[test] +fn test_equivalence_smoke_modern_neoclassic() { + let program_neo = indoc! {" +(mod (A) + (include *standard-cl-26-classic*) + + (let ((B (+ A 1))) (* B A)) + ) + "}.to_string(); + + let program_modern = program_neo.replace("cl-26-classic", "cl26").to_string(); + let clvm_modern = do_basic_run(&vec!["run".to_string(), program_modern.to_string()]); + let clvm_neo = do_basic_run(&vec!["run".to_string(), program_neo.to_string()]); + + for a in 0..12 { + let brun_arg = format!("({a})"); + let output_modern = do_basic_brun(&vec!["brun".to_string(), clvm_modern.to_string(), brun_arg.to_string()]); + let b = a + 1; + assert_eq!(output_modern.trim(), (a * b).to_string()); + + let output_neo = do_basic_brun(&vec!["brun".to_string(), clvm_neo.to_string(), brun_arg]); + assert_eq!(output_neo.trim(), output_modern.trim()); + } +} From 7c43c95c8354b26c8bf5257f5c2ca813dd754c5c Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Fri, 31 Jul 2026 00:29:40 +0000 Subject: [PATCH 19/38] Fix neoclassic let code generation Co-authored-by: arty --- src/compiler/codegen.rs | 10 +++++++++- src/compiler/compiler.rs | 15 ++++++++++++++- src/tests/classic/run.rs | 18 ++++++++++++++---- 3 files changed, 37 insertions(+), 6 deletions(-) diff --git a/src/compiler/codegen.rs b/src/compiler/codegen.rs index c12c250d9..3e54ace66 100644 --- a/src/compiler/codegen.rs +++ b/src/compiler/codegen.rs @@ -1575,7 +1575,7 @@ pub fn hoist_body_let_binding( })); } let generated_defun = generate_let_defun( - opts, + opts.clone(), letdata.loc.clone(), None, &defun_name, @@ -1590,6 +1590,14 @@ pub fn hoist_body_let_binding( let pass_env = outer_context .map(create_let_env_expression) .unwrap_or_else(|| { + // Modern codegen wraps the module arguments in its function + // environment. Classic stage 2 exposes them directly. + if opts.dialect().classic_codegen { + return BodyForm::Value(SExp::Atom( + letdata.loc.clone(), + "@*env*".as_bytes().to_vec(), + )); + } BodyForm::Call( letdata.loc.clone(), vec![ diff --git a/src/compiler/compiler.rs b/src/compiler/compiler.rs index 17ad2c37d..fcb8c9f1c 100644 --- a/src/compiler/compiler.rs +++ b/src/compiler/compiler.rs @@ -15,7 +15,7 @@ use crate::classic::clvm::__type_compatibility__::{bi_one, bi_zero}; use crate::classic::clvm_tools::ir::r#type::NEW_BIT_CONSTANTS; use crate::classic::clvm_tools::stages::stage_0::TRunProgram; use crate::classic::clvm_tools::stages::stage_2::abstraction::{ - ClassicAllocator, SExpClassicAllocator, + ASExp, BufCarrier, ClassicAllocator, SExpClassicAllocator, }; use crate::classic::clvm_tools::stages::stage_2::defaults::default_macro_lookup; use crate::classic::clvm_tools::stages::stage_2::module::compile_mod; @@ -217,6 +217,19 @@ fn classic_codegen(opts: Rc, program: CompileForm) -> Result Date: Fri, 31 Jul 2026 09:31:27 -0700 Subject: [PATCH 20/38] Add simple if smoke test --- src/tests/classic/run.rs | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/tests/classic/run.rs b/src/tests/classic/run.rs index 3ffb12b84..a5634e06d 100644 --- a/src/tests/classic/run.rs +++ b/src/tests/classic/run.rs @@ -2810,3 +2810,33 @@ fn test_equivalence_smoke_modern_neoclassic() { assert_eq!(output_neo.trim(), output_modern.trim()); } } + +#[test] +fn test_equivalence_smoke_if_macro_modern_neoclassic() { + let program_neo = indoc! {" +(mod (A) + (include *standard-cl-26-classic*) + + (if A (* A 2) (+ A 1)) + ) + "} + .to_string(); + + let program_modern = program_neo.replace("cl-26-classic", "cl-26").to_string(); + let clvm_modern = do_basic_run(&vec!["run".to_string(), program_modern.to_string()]); + let clvm_neo = do_basic_run(&vec!["run".to_string(), program_neo.to_string()]); + + for a in 0..2 { + let brun_arg = format!("({a})"); + let output_modern = do_basic_brun(&vec![ + "brun".to_string(), + clvm_modern.to_string(), + brun_arg.to_string(), + ]); + let expected = if a == 0 { 1 } else { a * 2 }.to_string(); + assert_eq!(output_modern.trim(), expected); + + let output_neo = do_basic_brun(&vec!["brun".to_string(), clvm_neo.to_string(), brun_arg]); + assert_eq!(output_neo.trim(), output_modern.trim()); + } +} From 265f7224058c970365a6a8a41b6f28a81865c9fe Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Fri, 31 Jul 2026 16:53:28 +0000 Subject: [PATCH 21/38] Expand modern com forms before classic codegen Co-authored-by: arty --- src/compiler/compiler.rs | 24 ++++++++++++++++-------- src/compiler/optimize/mod.rs | 25 ++++++++++++++++++++++--- src/compiler/optimize/strategy.rs | 2 +- 3 files changed, 39 insertions(+), 12 deletions(-) diff --git a/src/compiler/compiler.rs b/src/compiler/compiler.rs index fcb8c9f1c..c1c3984a2 100644 --- a/src/compiler/compiler.rs +++ b/src/compiler/compiler.rs @@ -35,7 +35,7 @@ use crate::compiler::comptypes::{ use crate::compiler::dialect::{AcceptedDialect, KNOWN_DIALECTS}; use crate::compiler::frontend::frontend; use crate::compiler::optimize::depgraph::{DepgraphOptions, FunctionDependencyGraph}; -use crate::compiler::optimize::get_optimizer; +use crate::compiler::optimize::{expand_com_forms, get_optimizer}; use crate::compiler::preprocessor::detect_chialisp_module; use crate::compiler::prims; use crate::compiler::resolve::{find_helper_target, resolve_namespaces}; @@ -166,7 +166,12 @@ pub fn finish_compilation( p2: CompileForm, ) -> Result { if opts.dialect().classic_codegen { - return classic_codegen(opts, p2); + let mut modern_dialect = opts.dialect(); + modern_dialect.classic_codegen = false; + let modern_opts = opts.set_dialect(modern_dialect); + let optimized = context.post_desugar_optimization(modern_opts.clone(), p2)?; + let expanded = expand_com_forms(context, modern_opts, optimized)?; + return classic_codegen(opts, expanded); } let p3 = context.post_desugar_optimization(opts.clone(), p2)?; @@ -238,11 +243,7 @@ pub fn compile_from_compileform( opts: Rc, p0: CompileForm, ) -> Result { - let p1 = if opts.dialect().classic_codegen { - p0 - } else { - context.frontend_optimization(opts.clone(), p0)? - }; + let p1 = context.frontend_optimization(opts.clone(), p0)?; // Resolve includes, convert program source to lexemes let p2 = do_desugar(opts.clone(), &p1)?; @@ -816,7 +817,14 @@ pub fn compile_pre_forms( opts = opts.set_stdenv(dialect.strict).set_dialect(dialect); } - let p0 = frontend(opts.clone(), pre_forms)?; + let frontend_opts = if opts.dialect().classic_codegen { + let mut modern_dialect = opts.dialect(); + modern_dialect.classic_codegen = false; + opts.set_dialect(modern_dialect) + } else { + opts.clone() + }; + let p0 = frontend(frontend_opts, pre_forms)?; match p0 { FrontendOutput::CompileForm(p0) => Ok(CompilerOutput::Program( diff --git a/src/compiler/optimize/mod.rs b/src/compiler/optimize/mod.rs index ae532fb4c..a4c5a9485 100644 --- a/src/compiler/optimize/mod.rs +++ b/src/compiler/optimize/mod.rs @@ -639,6 +639,8 @@ fn fe_opt( context: &mut BasicCompileContext, opts: Rc, compileform: CompileForm, + only_inline: bool, + use_main_env: bool, ) -> Result { let runner = context.runner(); let evaluator = Evaluator::new(opts.clone(), runner.clone(), compileform.helpers.clone()); @@ -671,13 +673,20 @@ fn fe_opt( } } let new_evaluator = Evaluator::new(opts.clone(), runner.clone(), optimized_helpers.clone()); + let mut main_env = HashMap::new(); + let main_args = if use_main_env { + build_reflex_captures(&mut main_env, compileform.args.clone()); + compileform.args.clone() + } else { + Rc::new(SExp::Nil(compileform.args.loc())) + }; let shrunk = new_evaluator.shrink_bodyform( context, - Rc::new(SExp::Nil(compileform.args.loc())), - &HashMap::new(), + main_args, + &main_env, compileform.exp.clone(), - true, + only_inline, Some(EVAL_STACK_LIMIT), )?; @@ -688,6 +697,16 @@ fn fe_opt( }) } +/// Expand compiler forms using the modern evaluator before handing a program to +/// a backend that does not implement modern compiler-form semantics. +pub fn expand_com_forms( + context: &mut BasicCompileContext, + opts: Rc, + compileform: CompileForm, +) -> Result { + fe_opt(context, opts, compileform, true, true) +} + pub fn run_optimizer( allocator: &mut Allocator, runner: Rc, diff --git a/src/compiler/optimize/strategy.rs b/src/compiler/optimize/strategy.rs index 075c37056..c9de18bc3 100644 --- a/src/compiler/optimize/strategy.rs +++ b/src/compiler/optimize/strategy.rs @@ -43,7 +43,7 @@ impl Optimization for ExistingStrategy { Box::new(self.clone()), ); // Front end optimization - fe_opt(wrapper.context(), opts.clone(), p0) + fe_opt(wrapper.context(), opts.clone(), p0, true, false) } else { Ok(p0) } From b7d6414243913dfebc83169300905070f7c5f0ab Mon Sep 17 00:00:00 2001 From: art yerkes Date: Fri, 31 Jul 2026 10:37:02 -0700 Subject: [PATCH 22/38] Allow testing with other sigils --- resources/tests/mandelbrot-cldb.py | 38 ++++++++++++++++++++++++++---- 1 file changed, 34 insertions(+), 4 deletions(-) diff --git a/resources/tests/mandelbrot-cldb.py b/resources/tests/mandelbrot-cldb.py index 11a915fb6..25bff8779 100644 --- a/resources/tests/mandelbrot-cldb.py +++ b/resources/tests/mandelbrot-cldb.py @@ -1,9 +1,23 @@ #!/usr/bin/env python import subprocess +import argparse +import yaml +import io -command = ['../../target/debug/cldb', '-p', './mandelbrot/mandelbrot.clsp', '(-192 -128 -144 -96 8)'] -want_output = """--- +parser = argparse.ArgumentParser() +parser.add_argument('-sigil', default=None) +args = parser.parse_args() + +infile = './mandelbrot/mandelbrot.clsp' +if args.sigil: + mb = open(infile).read() + infile = f'{infile}.sigil' + with open(infile, 'w') as outf: + outf.write(mb.replace('*standard-cl-21*', args.sigil)) + +command = ['../../target/debug/cldb', '-p', infile, '(-192 -128 -144 -96 8)'] +want_output = f"""--- - Print: ((escape-at -152 -104) 14) - Print: ((escape-at -160 -104) 14) - Print: ((escape-at -168 -104) 14) @@ -30,8 +44,24 @@ - Print: ((escape-at -192 -128) 5) - Print: "(\\"result\\" \\"||567AAC|68DEEE|78EEEE|78BEEE\\")" - Final: "3356114000950459963475899699747220812557867594760040767593731831711045" - Final-Location: "./mandelbrot/mandelbrot.clsp(8):43" + Final-Location: "{infile}(8):43" """ have_output = subprocess.check_output(command).decode('utf8') -assert have_output == want_output +parsed_have = yaml.safe_load(io.StringIO(have_output)) +parsed_want = yaml.safe_load(io.StringIO(want_output)) + +def dequote(kv): + result = {} + for k in kv.keys(): + result[k] = kv[k].replace('"','') + return result + +if args.sigil: + if 'Final-Location' in parsed_have[-1]: + del parsed_have[-1]['Final-Location'] + del parsed_want[-1]['Final-Location'] + parsed_have = list(map(dequote, parsed_have)) + parsed_want = list(map(dequote, parsed_want)) + +assert parsed_have == parsed_want From 91b0fa4ff21eaebcc0c58808ee382b0fef0ade69 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Fri, 31 Jul 2026 17:50:02 +0000 Subject: [PATCH 23/38] Fix classic codegen let environments Co-authored-by: arty --- src/compiler/codegen.rs | 6 +++++- src/tests/compiler/cldb.rs | 22 ++++++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/src/compiler/codegen.rs b/src/compiler/codegen.rs index 3e54ace66..fc4c943e7 100644 --- a/src/compiler/codegen.rs +++ b/src/compiler/codegen.rs @@ -1720,7 +1720,11 @@ pub fn process_helper_let_bindings( while i < result.len() { match result[i].clone() { HelperForm::Defun(inline, defun) => { - let context = if inline { + // Let helpers take the enclosing function's argument tree as + // their first argument. Classic codegen exposes function + // arguments directly via @*env*, so reconstruct that tree at + // the call site even for non-inline functions. + let context = if inline || opts.dialect().classic_codegen { Some(defun.args.clone()) } else { None diff --git a/src/tests/compiler/cldb.rs b/src/tests/compiler/cldb.rs index 1f86b6f76..27cca7d7f 100644 --- a/src/tests/compiler/cldb.rs +++ b/src/tests/compiler/cldb.rs @@ -352,6 +352,28 @@ fn test_cldb_hierarchy_mode() { compare_run_output(result, run_entries); } +#[test] +fn test_classic_codegen_mandelbrot() { + let input_file = "resources/tests/mandelbrot/mandelbrot.clsp"; + let input_program = fs::read_to_string(input_file) + .expect("test resource should exist") + .replace("*standard-cl-21*", "*standard-cl-26-classic*"); + let result = compile_and_run_program_with_tree( + input_file, + &input_program, + "(-192 -128 -144 -96 8)", + &vec![], + 0, + ); + + assert_eq!( + result.last().and_then(|entry| entry.get("Final")), + Some(&YamlElement::String( + "3356114000950459963475899699747220812557867594760040767593731831711045".to_string() + )) + ); +} + #[test] fn test_execute_program_and_capture_arguments() { let compiled_symbols_text = From 4a89b0868f4cf88122fa29c315f5b2ecb4251ea5 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Fri, 31 Jul 2026 17:52:06 +0000 Subject: [PATCH 24/38] Exercise Mandelbrot through cldb runner Co-authored-by: arty --- src/tests/compiler/cldb.rs | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/src/tests/compiler/cldb.rs b/src/tests/compiler/cldb.rs index 27cca7d7f..e95d0d4f5 100644 --- a/src/tests/compiler/cldb.rs +++ b/src/tests/compiler/cldb.rs @@ -358,19 +358,28 @@ fn test_classic_codegen_mandelbrot() { let input_program = fs::read_to_string(input_file) .expect("test resource should exist") .replace("*standard-cl-21*", "*standard-cl-26-classic*"); - let result = compile_and_run_program_with_tree( - input_file, - &input_program, - "(-192 -128 -144 -96 8)", - &vec![], - 0, - ); + let mut allocator = Allocator::new(); + let runner = Rc::new(DefaultProgramRunner::new()); + let opts = Rc::new(DefaultCompilerOpts::new(input_file)).set_optimize(false); + let mut symbols = HashMap::new(); + let program = compile_file(&mut allocator, runner, opts, &input_program, &mut symbols) + .expect("should compile"); + let args = parse_sexp(Srcloc::start("*args*"), "(-192 -128 -144 -96 8)".bytes()) + .expect("should parse args")[0] + .clone(); + let program_lines = Rc::new(input_program.lines().map(str::to_string).collect()); assert_eq!( - result.last().and_then(|entry| entry.get("Final")), - Some(&YamlElement::String( - "3356114000950459963475899699747220812557867594760040767593731831711045".to_string() - )) + run_clvm_in_cldb( + input_file, + program_lines, + Rc::new(program.to_sexp()), + symbols, + args, + &mut DoesntWatchCldb {}, + 0, + ), + Some("3356114000950459963475899699747220812557867594760040767593731831711045".to_string()) ); } From 8f4226c9ab2d4a99c0217d5dae33c9f34ea99472 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Fri, 31 Jul 2026 20:49:14 +0000 Subject: [PATCH 25/38] Compare run and cldb Mandelbrot output Co-authored-by: arty --- src/tests/compiler/cldb.rs | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/src/tests/compiler/cldb.rs b/src/tests/compiler/cldb.rs index e95d0d4f5..5a57fee81 100644 --- a/src/tests/compiler/cldb.rs +++ b/src/tests/compiler/cldb.rs @@ -360,10 +360,26 @@ fn test_classic_codegen_mandelbrot() { .replace("*standard-cl-21*", "*standard-cl-26-classic*"); let mut allocator = Allocator::new(); let runner = Rc::new(DefaultProgramRunner::new()); - let opts = Rc::new(DefaultCompilerOpts::new(input_file)).set_optimize(false); + let cldb_opts = Rc::new(DefaultCompilerOpts::new(input_file)).set_optimize(false); let mut symbols = HashMap::new(); - let program = compile_file(&mut allocator, runner, opts, &input_program, &mut symbols) - .expect("should compile"); + let cldb_program = compile_file( + &mut allocator, + runner.clone(), + cldb_opts, + &input_program, + &mut symbols, + ) + .expect("cldb should compile"); + let run_opts = Rc::new(DefaultCompilerOpts::new(input_file)).set_optimize(true); + let run_program = compile_file( + &mut allocator, + runner, + run_opts, + &input_program, + &mut HashMap::new(), + ) + .expect("run should compile"); + assert_eq!(cldb_program.to_sexp(), run_program.to_sexp()); let args = parse_sexp(Srcloc::start("*args*"), "(-192 -128 -144 -96 8)".bytes()) .expect("should parse args")[0] .clone(); @@ -373,7 +389,7 @@ fn test_classic_codegen_mandelbrot() { run_clvm_in_cldb( input_file, program_lines, - Rc::new(program.to_sexp()), + Rc::new(cldb_program.to_sexp()), symbols, args, &mut DoesntWatchCldb {}, From 65f18f1174643e7396ff9dbf473494f885ed00a0 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Fri, 31 Jul 2026 20:50:15 +0000 Subject: [PATCH 26/38] Match Mandelbrot test command options Co-authored-by: arty --- src/tests/compiler/cldb.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/tests/compiler/cldb.rs b/src/tests/compiler/cldb.rs index 5a57fee81..364444771 100644 --- a/src/tests/compiler/cldb.rs +++ b/src/tests/compiler/cldb.rs @@ -360,7 +360,9 @@ fn test_classic_codegen_mandelbrot() { .replace("*standard-cl-21*", "*standard-cl-26-classic*"); let mut allocator = Allocator::new(); let runner = Rc::new(DefaultProgramRunner::new()); - let cldb_opts = Rc::new(DefaultCompilerOpts::new(input_file)).set_optimize(false); + // The run and cldb commands both enable frontend optimization for + // stepping dialects newer than 22. + let cldb_opts = Rc::new(DefaultCompilerOpts::new(input_file)).set_optimize(true); let mut symbols = HashMap::new(); let cldb_program = compile_file( &mut allocator, From 1c528d2d5d74eb31cd54271f6d66156cbcb57c41 Mon Sep 17 00:00:00 2001 From: art yerkes Date: Fri, 31 Jul 2026 14:12:59 -0700 Subject: [PATCH 27/38] new test --- src/tests/classic/run.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/tests/classic/run.rs b/src/tests/classic/run.rs index a5634e06d..df9696c20 100644 --- a/src/tests/classic/run.rs +++ b/src/tests/classic/run.rs @@ -2840,3 +2840,12 @@ fn test_equivalence_smoke_if_macro_modern_neoclassic() { assert_eq!(output_neo.trim(), output_modern.trim()); } } + +#[test] +fn test_equivalence_mandelbrot_classic() { + let program = fs::read_to_string("resources/tests/mandelbrot/mandelbrot.clsp").unwrap(); + let classic = program.replace("cl-21", "cl-26-classic").to_string(); + let mandelbrot_clvm = do_basic_run(&vec!["run".to_string(), classic.to_string()]); + let mandelbrot_smoke_run = do_basic_brun(&vec!["brun".to_string(), mandelbrot_clvm, "(16 16 32 32 16)".to_string()]); + assert_eq!(mandelbrot_smoke_run.trim(), "||E"); +} From 5817ffaf3f385912a8b9b0854e340e1e5c530aab Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Fri, 31 Jul 2026 22:20:33 +0000 Subject: [PATCH 28/38] Preserve numeric atoms in classic codegen Co-authored-by: arty --- .../clvm_tools/stages/stage_2/abstraction.rs | 28 +++++++++++++++---- src/tests/classic/run.rs | 8 ++++-- 2 files changed, 28 insertions(+), 8 deletions(-) diff --git a/src/classic/clvm_tools/stages/stage_2/abstraction.rs b/src/classic/clvm_tools/stages/stage_2/abstraction.rs index 4d34f27d2..758678048 100644 --- a/src/classic/clvm_tools/stages/stage_2/abstraction.rs +++ b/src/classic/clvm_tools/stages/stage_2/abstraction.rs @@ -8,7 +8,7 @@ use clvm_rs::error::EvalErr; use crate::classic::clvm_tools::binutils::disassemble; use crate::compiler::sexp::SExp as ModernSExp; use crate::compiler::srcloc::Srcloc; -use crate::util::u8_from_number; +use crate::util::{number_from_u8, u8_from_number}; pub enum ASExp { Pair(T, T), @@ -244,8 +244,18 @@ impl ClassicAllocator for SExpClassicAllocator { .allocator .new_atom(value) .map_err(|e| ClError(loc.clone(), e))?; + let sexp = if value.is_empty() { + ModernSExp::Nil(loc) + } else { + let integer = number_from_u8(value); + if u8_from_number(integer.clone()) == value { + ModernSExp::Integer(loc, integer) + } else { + ModernSExp::Atom(loc, value.to_vec()) + } + }; Ok(SExpNode { - sexp: Rc::new(ModernSExp::Atom(loc, value.to_vec())), + sexp: Rc::new(sexp), raw, }) } @@ -269,10 +279,16 @@ impl ClassicAllocator for SExpClassicAllocator { fn import(&mut self, loc: Srcloc, node: NodePtr) -> Result { let sexp = match self.allocator.sexp(node) { SExp::Atom if node == NodePtr::NIL => Rc::new(ModernSExp::Nil(loc)), - SExp::Atom => Rc::new(ModernSExp::Atom( - loc, - self.allocator.atom(node).as_ref().to_vec(), - )), + SExp::Atom => { + let value = self.allocator.atom(node); + let value = value.as_ref(); + let integer = number_from_u8(value); + if u8_from_number(integer.clone()) == value { + Rc::new(ModernSExp::Integer(loc, integer)) + } else { + Rc::new(ModernSExp::Atom(loc, value.to_vec())) + } + } SExp::Pair(first, rest) => { let first = self.import(loc.clone(), first)?; let rest = self.import(loc.clone(), rest)?; diff --git a/src/tests/classic/run.rs b/src/tests/classic/run.rs index df9696c20..4f3f5a0cb 100644 --- a/src/tests/classic/run.rs +++ b/src/tests/classic/run.rs @@ -2846,6 +2846,10 @@ fn test_equivalence_mandelbrot_classic() { let program = fs::read_to_string("resources/tests/mandelbrot/mandelbrot.clsp").unwrap(); let classic = program.replace("cl-21", "cl-26-classic").to_string(); let mandelbrot_clvm = do_basic_run(&vec!["run".to_string(), classic.to_string()]); - let mandelbrot_smoke_run = do_basic_brun(&vec!["brun".to_string(), mandelbrot_clvm, "(16 16 32 32 16)".to_string()]); - assert_eq!(mandelbrot_smoke_run.trim(), "||E"); + let mandelbrot_smoke_run = do_basic_brun(&vec![ + "brun".to_string(), + mandelbrot_clvm, + "(16 16 32 32 16)".to_string(), + ]); + assert_eq!(mandelbrot_smoke_run.trim(), "\"||E\""); } From 7a0493288726c39381b0fece76749a1809b0b0a4 Mon Sep 17 00:00:00 2001 From: arty Date: Tue, 4 Aug 2026 15:40:38 -0700 Subject: [PATCH 29/38] test function interpretation of com --- .../clvm_tools/stages/stage_2/abstraction.rs | 14 +- .../clvm_tools/stages/stage_2/compile.rs | 8 +- .../clvm_tools/stages/stage_2/module.rs | 6 +- .../clvm_tools/stages/stage_2/operators.rs | 2 +- src/compiler/compiler.rs | 151 ++++++++++++++++-- src/compiler/optimize/mod.rs | 10 -- 6 files changed, 158 insertions(+), 33 deletions(-) diff --git a/src/classic/clvm_tools/stages/stage_2/abstraction.rs b/src/classic/clvm_tools/stages/stage_2/abstraction.rs index 758678048..088e1fd11 100644 --- a/src/classic/clvm_tools/stages/stage_2/abstraction.rs +++ b/src/classic/clvm_tools/stages/stage_2/abstraction.rs @@ -6,6 +6,7 @@ use clvm_rs::allocator::{Allocator, NodePtr, SExp}; use clvm_rs::error::EvalErr; use crate::classic::clvm_tools::binutils::disassemble; +use crate::classic::clvm::__type_compatibility__::bi_zero; use crate::compiler::sexp::SExp as ModernSExp; use crate::compiler::srcloc::Srcloc; use crate::util::{number_from_u8, u8_from_number}; @@ -174,10 +175,15 @@ impl SExpClassicAllocator { .new_pair(first.raw, rest.raw) .map_err(|e| self.map_err(loc.clone(), e))? } - ModernSExp::Integer(_, value) => self - .allocator - .new_atom(&u8_from_number(value.clone())) - .map_err(|e| self.map_err(loc.clone(), e))?, + ModernSExp::Integer(_, value) => { + if *value == bi_zero() { + self.allocator.new_atom(&[]) + } else { + self + .allocator + .new_atom(&u8_from_number(value.clone())) + }.map_err(|e| self.map_err(loc.clone(), e))? + } ModernSExp::QuotedString(_, _, value) | ModernSExp::Atom(_, value) => self .allocator .new_atom(value) diff --git a/src/classic/clvm_tools/stages/stage_2/compile.rs b/src/classic/clvm_tools/stages/stage_2/compile.rs index fa3897bfc..23132d63a 100644 --- a/src/classic/clvm_tools/stages/stage_2/compile.rs +++ b/src/classic/clvm_tools/stages/stage_2/compile.rs @@ -21,7 +21,7 @@ use crate::classic::clvm_tools::stages::stage_2::helpers::{brun, evaluate, quote use crate::classic::clvm_tools::stages::stage_2::module::compile_mod; use crate::compiler::srcloc::Srcloc; -const DIAG_OUTPUT: bool = false; +const DIAG_OUTPUT: bool = true; lazy_static! { static ref PASS_THROUGH_OPERATORS: HashSet> = { @@ -369,7 +369,7 @@ where let top_path = allocator.new_atom(loc, NodePath::new(None).as_path().data())?; evaluate(allocator, &to_eval, &top_path).inspect(|x| { if DIAG_OUTPUT { - print!( + println!( "TRY_EXPAND_MACRO {} WITH {} GIVES {} MACROS {} SYMBOLS {}", allocator.disassemble(macro_code, None), allocator.disassemble(prog_rest, None), @@ -764,7 +764,7 @@ where { if DIAG_OUTPUT { println!( - "START COMPILE {}: {} MACRO {} SYMBOLS {}", + "START COMPILE {}: {}\nMACRO {}\nSYMBOLS {}", from, allocator.disassemble(prog, None), allocator.disassemble(macro_lookup, None), @@ -774,7 +774,7 @@ where do_com_prog_(allocator, prog, macro_lookup, symbol_table, run_program).inspect(|x| { if DIAG_OUTPUT { println!( - "DO_COM_PROG {}: {} MACRO {} SYMBOLS {} RESULT {}", + "DO_COM_PROG {}: {}\nMACRO {}\nSYMBOLS {}\nRESULT {}", from, allocator.disassemble(prog, None), allocator.disassemble(macro_lookup, None), diff --git a/src/classic/clvm_tools/stages/stage_2/module.rs b/src/classic/clvm_tools/stages/stage_2/module.rs index 537aba7a2..b538a125b 100644 --- a/src/classic/clvm_tools/stages/stage_2/module.rs +++ b/src/classic/clvm_tools/stages/stage_2/module.rs @@ -601,8 +601,10 @@ where } let main_local_arguments = alist[0].clone(); + eprintln!("main_local_arguments {}", allocator.disassemble(&alist[0], None)); for arg in alist.iter().take(alist.len()-1).skip(1) { + eprintln!("parse_mod_sexp {}", allocator.disassemble(arg, None)); parse_mod_sexp( allocator, arg, @@ -1055,7 +1057,7 @@ pub fn compile_mod( allocator: &mut A, args: &A::NodePtr, macro_lookup: &A::NodePtr, - _symbol_table: &A::NodePtr, + symbol_table: &A::NodePtr, run_program: Rc, _level: usize, ) -> Result @@ -1063,6 +1065,8 @@ where A::NodePtr: Clone, { // Deal with the "mod" keyword. + eprintln!("compile_mod {}", allocator.disassemble(args, None)); + eprintln!("symbol_table {}", allocator.disassemble(symbol_table, None)); let loc = allocator.loc(macro_lookup); let produce_extra_info_prog = assemble(allocator.allocator(), "(_symbols_extra_info)") .map_err(|e| ClError(loc.clone(), e))?; diff --git a/src/classic/clvm_tools/stages/stage_2/operators.rs b/src/classic/clvm_tools/stages/stage_2/operators.rs index 16040d2e5..8592bcdca 100644 --- a/src/classic/clvm_tools/stages/stage_2/operators.rs +++ b/src/classic/clvm_tools/stages/stage_2/operators.rs @@ -19,7 +19,7 @@ use crate::classic::clvm::sexp::proper_list; use crate::classic::clvm::{keyword_from_atom, keyword_to_atom}; use crate::classic::clvm_tools::binutils::{ - assemble_from_ir, disassemble, disassemble_to_ir_with_kw, + assemble_from_ir, disassemble_to_ir_with_kw, }; use crate::classic::clvm_tools::ir::reader::read_ir; use crate::classic::clvm_tools::ir::writer::write_ir_to_stream; diff --git a/src/compiler/compiler.rs b/src/compiler/compiler.rs index c1c3984a2..041cf581c 100644 --- a/src/compiler/compiler.rs +++ b/src/compiler/compiler.rs @@ -1,5 +1,6 @@ use num_bigint::ToBigInt; +use std::sync::atomic::{AtomicI32, Ordering}; use std::borrow::Borrow; use std::collections::{BTreeMap, HashMap, HashSet}; use std::fs; @@ -15,7 +16,7 @@ use crate::classic::clvm::__type_compatibility__::{bi_one, bi_zero}; use crate::classic::clvm_tools::ir::r#type::NEW_BIT_CONSTANTS; use crate::classic::clvm_tools::stages::stage_0::TRunProgram; use crate::classic::clvm_tools::stages::stage_2::abstraction::{ - ASExp, BufCarrier, ClassicAllocator, SExpClassicAllocator, + ASExp, BufCarrier, ClassicAllocator, SExpClassicAllocator, SExpNode, }; use crate::classic::clvm_tools::stages::stage_2::defaults::default_macro_lookup; use crate::classic::clvm_tools::stages::stage_2::module::compile_mod; @@ -28,14 +29,14 @@ use crate::compiler::clvm::{convert_to_clvm_rs, run, sha256tree, NewStyleIntConv use crate::compiler::codegen::{codegen, hoist_body_let_binding, process_helper_let_bindings}; use crate::compiler::comptypes::{ BodyForm, CompileErr, CompileForm, CompileModuleComponent, CompileModuleOutput, CompilerOpts, - CompilerOutput, ConstantKind, DefunData, Export, FrontendOutput, HelperForm, ImportLongName, + CompilerOutput, ConstantKind, DefunData, DefmacData, Export, FrontendOutput, HelperForm, ImportLongName, IncludeDesc, IncludeProcessType, ModulePhase, PrimaryCodegen, StandalonePhaseInfo, SyntheticType, }; use crate::compiler::dialect::{AcceptedDialect, KNOWN_DIALECTS}; use crate::compiler::frontend::frontend; use crate::compiler::optimize::depgraph::{DepgraphOptions, FunctionDependencyGraph}; -use crate::compiler::optimize::{expand_com_forms, get_optimizer}; +use crate::compiler::optimize::get_optimizer; use crate::compiler::preprocessor::detect_chialisp_module; use crate::compiler::prims; use crate::compiler::resolve::{find_helper_target, resolve_namespaces}; @@ -44,6 +45,35 @@ use crate::compiler::srcloc::Srcloc; use crate::compiler::{BasicCompileContext, CompileContextWrapper}; use crate::util::Number; +lazy_static! { + pub static ref INDENT: AtomicI32 = AtomicI32::new(0); +} + +struct Indent; + +impl Indent { + fn new() -> Self { + INDENT.fetch_add(1, Ordering::Relaxed); + Indent + } +} + +impl std::fmt::Display for Indent { + fn fmt(&self, fmt: &'_ mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { + let indent = INDENT.fetch_add(0, Ordering::Relaxed); + for i in 0..indent { + write!(fmt, " ")?; + } + Ok(()) + } +} + +impl Drop for Indent { + fn drop(&mut self) { + INDENT.fetch_add(-1, Ordering::Relaxed); + } +} + pub const SHA256TREE_PROGRAM_CLVM: &str = "(2 (1 2 (3 (7 5) (1 11 (1 . 2) (2 2 (4 2 (4 9 ()))) (2 2 (4 2 (4 13 ())))) (1 11 (1 . 1) 5)) 1) (4 (1 2 (3 (7 5) (1 11 (1 . 2) (2 2 (4 2 (4 9 ()))) (2 2 (4 2 (4 13 ())))) (1 11 (1 . 1) 5)) 1) 1))"; pub const FUZZ_TEST_PRE_CSE_MERGE_FIX_FLAG: usize = 1; @@ -158,6 +188,77 @@ pub fn do_desugar( }) } +pub fn expand_com_forms_expr( + context: &mut BasicCompileContext, + opts: Rc, + body: Rc, +) -> Result, CompileErr> { + let indent = Indent::new(); + if let BodyForm::Call(l, c, tail) = &*body { + if c.is_empty() { + return Ok(body); + } + + if let BodyForm::Value(atom) = &*c[0] { + if atom.atomize() == SExp::Atom(atom.loc(), b"com".to_vec()) { + // Is a com form. + eprintln!("{indent}com {}", c[1].to_sexp()); + return Ok(Rc::new(BodyForm::Call(l.clone(), vec![ + Rc::new(BodyForm::Value(SExp::Atom(c[0].loc(), b"function".to_vec()))), + c[1].clone(), + ], None))); + } + } + + let mut call_args = vec![c[0].clone()]; + for item in c.iter().skip(1) { + call_args.push(expand_com_forms_expr( + context, + opts.clone(), + item.clone() + )?); + } + + let new_tail = + if let Some(t) = &tail { + Some(expand_com_forms_expr( + context, + opts, + t.clone() + )?) + } else { + None + }; + + return Ok(Rc::new(BodyForm::Call(l.clone(), call_args, new_tail))); + } + + Ok(body) +} + +/// Expand compiler forms using the modern evaluator before handing a program to +/// a backend that does not implement modern compiler-form semantics. +pub fn expand_com_forms( + context: &mut BasicCompileContext, + opts: Rc, + compileform: CompileForm, +) -> Result { + let helpers = compileform.helpers.iter().map(|h| { + todo!(); + }); + + let new_expr = expand_com_forms_expr( + context, + opts, + compileform.exp.clone() + )?; + + Ok(CompileForm { + exp: new_expr, + .. compileform + }) +} + /// Given a compileform, compile it to clvm. This comes after preprocessing /// and desugaring. pub fn finish_compilation( @@ -165,17 +266,19 @@ pub fn finish_compilation( opts: Rc, p2: CompileForm, ) -> Result { + let indent = Indent::new(); + let p3 = context.post_desugar_optimization(opts.clone(), p2)?; + if opts.dialect().classic_codegen { let mut modern_dialect = opts.dialect(); modern_dialect.classic_codegen = false; let modern_opts = opts.set_dialect(modern_dialect); - let optimized = context.post_desugar_optimization(modern_opts.clone(), p2)?; - let expanded = expand_com_forms(context, modern_opts, optimized)?; - return classic_codegen(opts, expanded); + eprintln!("{indent}p3 {}", p3.to_sexp()); + let p4 = expand_com_forms(context, modern_opts, p3)?; + eprintln!("{indent}expanded {}", p4.to_sexp()); + return classic_codegen(opts, p4); } - let p3 = context.post_desugar_optimization(opts.clone(), p2)?; - // generate code from AST, optionally with optimization let generated = codegen(context, opts.clone(), &p3)?; @@ -188,7 +291,7 @@ pub fn finish_compilation( /// /// The classic compiler's normal final optimization is part of its code /// generation contract. No modern optimization hooks run on this path. -fn classic_codegen(opts: Rc, program: CompileForm) -> Result { +fn classic_codegen(opts: Rc, mut program: CompileForm) -> Result { let language_flags = if opts.dialect().extra_numeric_constants { NEW_BIT_CONSTANTS } else { @@ -203,13 +306,27 @@ fn classic_codegen(opts: Rc, program: CompileForm) -> Result, p0: CompileForm, ) -> Result { - let p1 = context.frontend_optimization(opts.clone(), p0)?; + let indent = Indent::new(); + let p1 = + if opts.dialect().classic_codegen { + p0 + } else { + context.frontend_optimization(opts.clone(), p0)? + }; + eprintln!("{indent}frontend_opt {}", p1.to_sexp()); // Resolve includes, convert program source to lexemes let p2 = do_desugar(opts.clone(), &p1)?; + eprintln!("{indent}desugared {}", p2.to_sexp()); finish_compilation(context, opts, p2) } diff --git a/src/compiler/optimize/mod.rs b/src/compiler/optimize/mod.rs index a4c5a9485..1e800e678 100644 --- a/src/compiler/optimize/mod.rs +++ b/src/compiler/optimize/mod.rs @@ -697,16 +697,6 @@ fn fe_opt( }) } -/// Expand compiler forms using the modern evaluator before handing a program to -/// a backend that does not implement modern compiler-form semantics. -pub fn expand_com_forms( - context: &mut BasicCompileContext, - opts: Rc, - compileform: CompileForm, -) -> Result { - fe_opt(context, opts, compileform, true, true) -} - pub fn run_optimizer( allocator: &mut Allocator, runner: Rc, From 4343043b93da66ecb96fc82cff454710718ba535 Mon Sep 17 00:00:00 2001 From: arty Date: Tue, 4 Aug 2026 18:59:31 -0700 Subject: [PATCH 30/38] Set up to find cause --- .../clvm_tools/stages/stage_2/compile.rs | 21 +++++++--- .../clvm_tools/stages/stage_2/module.rs | 41 +++++++++++++++++-- src/compiler/compiler.rs | 36 ++-------------- 3 files changed, 57 insertions(+), 41 deletions(-) diff --git a/src/classic/clvm_tools/stages/stage_2/compile.rs b/src/classic/clvm_tools/stages/stage_2/compile.rs index 23132d63a..6bafaa6e3 100644 --- a/src/classic/clvm_tools/stages/stage_2/compile.rs +++ b/src/classic/clvm_tools/stages/stage_2/compile.rs @@ -21,7 +21,10 @@ use crate::classic::clvm_tools::stages::stage_2::helpers::{brun, evaluate, quote use crate::classic::clvm_tools::stages::stage_2::module::compile_mod; use crate::compiler::srcloc::Srcloc; -const DIAG_OUTPUT: bool = true; +use crate::classic::clvm_tools::stages::stage_2::module::Indent; +use crate::compiler::sexp::decode_string; + +const DIAG_OUTPUT: bool = false; lazy_static! { static ref PASS_THROUGH_OPERATORS: HashSet> = { @@ -546,8 +549,10 @@ fn find_symbol_match( where A::NodePtr: Clone, { + let indent = Indent::new(); if let Some(symlist) = proper_list(allocator, symbol_table, true) { for sym in symlist { + eprintln!("{indent}sym {}", allocator.disassemble(&sym, None)); if let Some(symdef) = proper_list(allocator, &sym, true) { if symdef.is_empty() { continue; @@ -661,6 +666,7 @@ fn compile_application( where A::NodePtr: Clone, { + let indent = Indent::new(); let mut compiled_args = vec![operator.clone()]; let loc = allocator.loc(prog); @@ -711,7 +717,9 @@ where if PASS_THROUGH_OPERATORS.contains(opbuf) || (!opbuf.is_empty() && opbuf[0] == b'_') { Ok(r) } else { - find_symbol_match(allocator, opbuf, &r, symbol_table).and_then(|x| match x { + eprintln!("{indent}find symbol {} in\n{indent}{}\n{indent}{}", decode_string(opbuf), allocator.disassemble(&prog, None), allocator.disassemble(&symbol_table, None)); + find_symbol_match(allocator, opbuf, &r, symbol_table).and_then(|x| { + match x { Some(SymbolResult::Direct(v)) => Ok(v), Some(SymbolResult::Matched(_symbol, value)) => { let loc = allocator.loc(&value); @@ -744,7 +752,7 @@ where enlist(allocator, &[apply_atom, value, cons_enlisted]) } None => error_result, - }) + }}) } } None => error_result, @@ -762,9 +770,10 @@ pub fn do_com_prog( where A::NodePtr: Clone, { + let indent = Indent::new(); if DIAG_OUTPUT { println!( - "START COMPILE {}: {}\nMACRO {}\nSYMBOLS {}", + "START COMPILE {}: {}\n{indent}MACRO {}\n{indent}SYMBOLS {}", from, allocator.disassemble(prog, None), allocator.disassemble(macro_lookup, None), @@ -774,7 +783,7 @@ where do_com_prog_(allocator, prog, macro_lookup, symbol_table, run_program).inspect(|x| { if DIAG_OUTPUT { println!( - "DO_COM_PROG {}: {}\nMACRO {}\nSYMBOLS {}\nRESULT {}", + "DO_COM_PROG {}: {}\n{indent}MACRO {}\n{indent}SYMBOLS {}\n{indent}RESULT {}", from, allocator.disassemble(prog, None), allocator.disassemble(macro_lookup, None), @@ -795,6 +804,7 @@ fn do_com_prog_( where A::NodePtr: Clone, { + let indent = Indent::new(); /* * Turn the given program `prog` into a clvm program using * the macros to do transformation. @@ -850,6 +860,7 @@ where symbol_table, run_program.clone() ).and_then(|x| x.map(Ok).unwrap_or_else(|| m! { + let _ = eprintln!("{indent}compile_application {} {}\n{indent}{}", allocator.disassemble(&operator, None), allocator.disassemble(&prog, None), allocator.disassemble(&prog_rest, None)); compile_application( allocator, &prog, diff --git a/src/classic/clvm_tools/stages/stage_2/module.rs b/src/classic/clvm_tools/stages/stage_2/module.rs index b538a125b..3adec76f3 100644 --- a/src/classic/clvm_tools/stages/stage_2/module.rs +++ b/src/classic/clvm_tools/stages/stage_2/module.rs @@ -1,3 +1,4 @@ +use std::sync::atomic::{AtomicI32, Ordering}; use std::collections::HashMap; use std::collections::HashSet; use std::rc::Rc; @@ -25,6 +26,35 @@ use crate::classic::clvm_tools::stages::stage_2::optimize::optimize_sexp; use crate::classic::clvm_tools::stages::stage_2::reader::process_embed_file; use crate::compiler::srcloc::Srcloc; +lazy_static! { + pub static ref INDENT: AtomicI32 = AtomicI32::new(0); +} + +pub struct Indent; + +impl Indent { + pub fn new() -> Self { + INDENT.fetch_add(1, Ordering::Relaxed); + Indent + } +} + +impl std::fmt::Display for Indent { + fn fmt(&self, fmt: &'_ mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { + let indent = INDENT.fetch_add(0, Ordering::Relaxed); + for i in 0..indent { + write!(fmt, " ")?; + } + Ok(()) + } +} + +impl Drop for Indent { + fn drop(&mut self) { + INDENT.fetch_add(-1, Ordering::Relaxed); + } +} + lazy_static! { pub static ref MAIN_NAME: String = "".to_string(); } @@ -569,6 +599,7 @@ fn compile_mod_stage_1( where A::NodePtr: Clone, { + let indent = Indent::new(); // stage 1: collect up names of globals (functions, constants, macros) m! { let mut functions = HashMap::new(); @@ -601,10 +632,10 @@ where } let main_local_arguments = alist[0].clone(); - eprintln!("main_local_arguments {}", allocator.disassemble(&alist[0], None)); + eprintln!("{indent}main_local_arguments {}", allocator.disassemble(&alist[0], None)); for arg in alist.iter().take(alist.len()-1).skip(1) { - eprintln!("parse_mod_sexp {}", allocator.disassemble(arg, None)); + eprintln!("{indent}parse_mod_sexp {}", allocator.disassemble(arg, None)); parse_mod_sexp( allocator, arg, @@ -1064,9 +1095,11 @@ pub fn compile_mod( where A::NodePtr: Clone, { + let indent = Indent::new(); + // Deal with the "mod" keyword. - eprintln!("compile_mod {}", allocator.disassemble(args, None)); - eprintln!("symbol_table {}", allocator.disassemble(symbol_table, None)); + eprintln!("{indent}compile_mod {}", allocator.disassemble(args, None)); + eprintln!("{indent}symbol_table {}", allocator.disassemble(symbol_table, None)); let loc = allocator.loc(macro_lookup); let produce_extra_info_prog = assemble(allocator.allocator(), "(_symbols_extra_info)") .map_err(|e| ClError(loc.clone(), e))?; diff --git a/src/compiler/compiler.rs b/src/compiler/compiler.rs index 041cf581c..25699e1c9 100644 --- a/src/compiler/compiler.rs +++ b/src/compiler/compiler.rs @@ -45,34 +45,7 @@ use crate::compiler::srcloc::Srcloc; use crate::compiler::{BasicCompileContext, CompileContextWrapper}; use crate::util::Number; -lazy_static! { - pub static ref INDENT: AtomicI32 = AtomicI32::new(0); -} - -struct Indent; - -impl Indent { - fn new() -> Self { - INDENT.fetch_add(1, Ordering::Relaxed); - Indent - } -} - -impl std::fmt::Display for Indent { - fn fmt(&self, fmt: &'_ mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { - let indent = INDENT.fetch_add(0, Ordering::Relaxed); - for i in 0..indent { - write!(fmt, " ")?; - } - Ok(()) - } -} - -impl Drop for Indent { - fn drop(&mut self) { - INDENT.fetch_add(-1, Ordering::Relaxed); - } -} +use crate::classic::clvm_tools::stages::stage_2::module::Indent; pub const SHA256TREE_PROGRAM_CLVM: &str = "(2 (1 2 (3 (7 5) (1 11 (1 . 2) (2 2 (4 2 (4 9 ()))) (2 2 (4 2 (4 13 ())))) (1 11 (1 . 1) 5)) 1) (4 (1 2 (3 (7 5) (1 11 (1 . 2) (2 2 (4 2 (4 9 ()))) (2 2 (4 2 (4 13 ())))) (1 11 (1 . 1) 5)) 1) 1))"; @@ -944,17 +917,16 @@ pub fn compile_pre_forms( let frontend_opts = if opts.dialect().classic_codegen { let mut modern_dialect = opts.dialect(); - modern_dialect.classic_codegen = false; - opts.set_dialect(modern_dialect) + opts.set_dialect(modern_dialect).set_frontend_opt(false) } else { opts.clone() }; - let p0 = frontend(frontend_opts, pre_forms)?; + let p0 = frontend(frontend_opts.clone(), pre_forms)?; match p0 { FrontendOutput::CompileForm(p0) => Ok(CompilerOutput::Program( p0.include_forms.clone(), - compile_from_compileform(context, opts, p0)?, + compile_from_compileform(context, frontend_opts, p0)?, )), FrontendOutput::Module(mut cf, exports) => { add_main_fingerprint(&mut cf, pre_forms); From 695660e685cfbf0f36ceef8105c05f89b46631d8 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 5 Aug 2026 02:10:38 +0000 Subject: [PATCH 31/38] Revert "Set up to find cause" This reverts commit 4343043b93da66ecb96fc82cff454710718ba535. --- .../clvm_tools/stages/stage_2/compile.rs | 21 +++------- .../clvm_tools/stages/stage_2/module.rs | 41 ++----------------- src/compiler/compiler.rs | 36 ++++++++++++++-- 3 files changed, 41 insertions(+), 57 deletions(-) diff --git a/src/classic/clvm_tools/stages/stage_2/compile.rs b/src/classic/clvm_tools/stages/stage_2/compile.rs index 6bafaa6e3..23132d63a 100644 --- a/src/classic/clvm_tools/stages/stage_2/compile.rs +++ b/src/classic/clvm_tools/stages/stage_2/compile.rs @@ -21,10 +21,7 @@ use crate::classic::clvm_tools::stages::stage_2::helpers::{brun, evaluate, quote use crate::classic::clvm_tools::stages::stage_2::module::compile_mod; use crate::compiler::srcloc::Srcloc; -use crate::classic::clvm_tools::stages::stage_2::module::Indent; -use crate::compiler::sexp::decode_string; - -const DIAG_OUTPUT: bool = false; +const DIAG_OUTPUT: bool = true; lazy_static! { static ref PASS_THROUGH_OPERATORS: HashSet> = { @@ -549,10 +546,8 @@ fn find_symbol_match( where A::NodePtr: Clone, { - let indent = Indent::new(); if let Some(symlist) = proper_list(allocator, symbol_table, true) { for sym in symlist { - eprintln!("{indent}sym {}", allocator.disassemble(&sym, None)); if let Some(symdef) = proper_list(allocator, &sym, true) { if symdef.is_empty() { continue; @@ -666,7 +661,6 @@ fn compile_application( where A::NodePtr: Clone, { - let indent = Indent::new(); let mut compiled_args = vec![operator.clone()]; let loc = allocator.loc(prog); @@ -717,9 +711,7 @@ where if PASS_THROUGH_OPERATORS.contains(opbuf) || (!opbuf.is_empty() && opbuf[0] == b'_') { Ok(r) } else { - eprintln!("{indent}find symbol {} in\n{indent}{}\n{indent}{}", decode_string(opbuf), allocator.disassemble(&prog, None), allocator.disassemble(&symbol_table, None)); - find_symbol_match(allocator, opbuf, &r, symbol_table).and_then(|x| { - match x { + find_symbol_match(allocator, opbuf, &r, symbol_table).and_then(|x| match x { Some(SymbolResult::Direct(v)) => Ok(v), Some(SymbolResult::Matched(_symbol, value)) => { let loc = allocator.loc(&value); @@ -752,7 +744,7 @@ where enlist(allocator, &[apply_atom, value, cons_enlisted]) } None => error_result, - }}) + }) } } None => error_result, @@ -770,10 +762,9 @@ pub fn do_com_prog( where A::NodePtr: Clone, { - let indent = Indent::new(); if DIAG_OUTPUT { println!( - "START COMPILE {}: {}\n{indent}MACRO {}\n{indent}SYMBOLS {}", + "START COMPILE {}: {}\nMACRO {}\nSYMBOLS {}", from, allocator.disassemble(prog, None), allocator.disassemble(macro_lookup, None), @@ -783,7 +774,7 @@ where do_com_prog_(allocator, prog, macro_lookup, symbol_table, run_program).inspect(|x| { if DIAG_OUTPUT { println!( - "DO_COM_PROG {}: {}\n{indent}MACRO {}\n{indent}SYMBOLS {}\n{indent}RESULT {}", + "DO_COM_PROG {}: {}\nMACRO {}\nSYMBOLS {}\nRESULT {}", from, allocator.disassemble(prog, None), allocator.disassemble(macro_lookup, None), @@ -804,7 +795,6 @@ fn do_com_prog_( where A::NodePtr: Clone, { - let indent = Indent::new(); /* * Turn the given program `prog` into a clvm program using * the macros to do transformation. @@ -860,7 +850,6 @@ where symbol_table, run_program.clone() ).and_then(|x| x.map(Ok).unwrap_or_else(|| m! { - let _ = eprintln!("{indent}compile_application {} {}\n{indent}{}", allocator.disassemble(&operator, None), allocator.disassemble(&prog, None), allocator.disassemble(&prog_rest, None)); compile_application( allocator, &prog, diff --git a/src/classic/clvm_tools/stages/stage_2/module.rs b/src/classic/clvm_tools/stages/stage_2/module.rs index 3adec76f3..b538a125b 100644 --- a/src/classic/clvm_tools/stages/stage_2/module.rs +++ b/src/classic/clvm_tools/stages/stage_2/module.rs @@ -1,4 +1,3 @@ -use std::sync::atomic::{AtomicI32, Ordering}; use std::collections::HashMap; use std::collections::HashSet; use std::rc::Rc; @@ -26,35 +25,6 @@ use crate::classic::clvm_tools::stages::stage_2::optimize::optimize_sexp; use crate::classic::clvm_tools::stages::stage_2::reader::process_embed_file; use crate::compiler::srcloc::Srcloc; -lazy_static! { - pub static ref INDENT: AtomicI32 = AtomicI32::new(0); -} - -pub struct Indent; - -impl Indent { - pub fn new() -> Self { - INDENT.fetch_add(1, Ordering::Relaxed); - Indent - } -} - -impl std::fmt::Display for Indent { - fn fmt(&self, fmt: &'_ mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { - let indent = INDENT.fetch_add(0, Ordering::Relaxed); - for i in 0..indent { - write!(fmt, " ")?; - } - Ok(()) - } -} - -impl Drop for Indent { - fn drop(&mut self) { - INDENT.fetch_add(-1, Ordering::Relaxed); - } -} - lazy_static! { pub static ref MAIN_NAME: String = "".to_string(); } @@ -599,7 +569,6 @@ fn compile_mod_stage_1( where A::NodePtr: Clone, { - let indent = Indent::new(); // stage 1: collect up names of globals (functions, constants, macros) m! { let mut functions = HashMap::new(); @@ -632,10 +601,10 @@ where } let main_local_arguments = alist[0].clone(); - eprintln!("{indent}main_local_arguments {}", allocator.disassemble(&alist[0], None)); + eprintln!("main_local_arguments {}", allocator.disassemble(&alist[0], None)); for arg in alist.iter().take(alist.len()-1).skip(1) { - eprintln!("{indent}parse_mod_sexp {}", allocator.disassemble(arg, None)); + eprintln!("parse_mod_sexp {}", allocator.disassemble(arg, None)); parse_mod_sexp( allocator, arg, @@ -1095,11 +1064,9 @@ pub fn compile_mod( where A::NodePtr: Clone, { - let indent = Indent::new(); - // Deal with the "mod" keyword. - eprintln!("{indent}compile_mod {}", allocator.disassemble(args, None)); - eprintln!("{indent}symbol_table {}", allocator.disassemble(symbol_table, None)); + eprintln!("compile_mod {}", allocator.disassemble(args, None)); + eprintln!("symbol_table {}", allocator.disassemble(symbol_table, None)); let loc = allocator.loc(macro_lookup); let produce_extra_info_prog = assemble(allocator.allocator(), "(_symbols_extra_info)") .map_err(|e| ClError(loc.clone(), e))?; diff --git a/src/compiler/compiler.rs b/src/compiler/compiler.rs index 25699e1c9..041cf581c 100644 --- a/src/compiler/compiler.rs +++ b/src/compiler/compiler.rs @@ -45,7 +45,34 @@ use crate::compiler::srcloc::Srcloc; use crate::compiler::{BasicCompileContext, CompileContextWrapper}; use crate::util::Number; -use crate::classic::clvm_tools::stages::stage_2::module::Indent; +lazy_static! { + pub static ref INDENT: AtomicI32 = AtomicI32::new(0); +} + +struct Indent; + +impl Indent { + fn new() -> Self { + INDENT.fetch_add(1, Ordering::Relaxed); + Indent + } +} + +impl std::fmt::Display for Indent { + fn fmt(&self, fmt: &'_ mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { + let indent = INDENT.fetch_add(0, Ordering::Relaxed); + for i in 0..indent { + write!(fmt, " ")?; + } + Ok(()) + } +} + +impl Drop for Indent { + fn drop(&mut self) { + INDENT.fetch_add(-1, Ordering::Relaxed); + } +} pub const SHA256TREE_PROGRAM_CLVM: &str = "(2 (1 2 (3 (7 5) (1 11 (1 . 2) (2 2 (4 2 (4 9 ()))) (2 2 (4 2 (4 13 ())))) (1 11 (1 . 1) 5)) 1) (4 (1 2 (3 (7 5) (1 11 (1 . 2) (2 2 (4 2 (4 9 ()))) (2 2 (4 2 (4 13 ())))) (1 11 (1 . 1) 5)) 1) 1))"; @@ -917,16 +944,17 @@ pub fn compile_pre_forms( let frontend_opts = if opts.dialect().classic_codegen { let mut modern_dialect = opts.dialect(); - opts.set_dialect(modern_dialect).set_frontend_opt(false) + modern_dialect.classic_codegen = false; + opts.set_dialect(modern_dialect) } else { opts.clone() }; - let p0 = frontend(frontend_opts.clone(), pre_forms)?; + let p0 = frontend(frontend_opts, pre_forms)?; match p0 { FrontendOutput::CompileForm(p0) => Ok(CompilerOutput::Program( p0.include_forms.clone(), - compile_from_compileform(context, frontend_opts, p0)?, + compile_from_compileform(context, opts, p0)?, )), FrontendOutput::Module(mut cf, exports) => { add_main_fingerprint(&mut cf, pre_forms); From 2a775748def97d4ebee8dccac14995bdc140f8a1 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 5 Aug 2026 02:11:55 +0000 Subject: [PATCH 32/38] Translate com forms throughout classic codegen inputs Co-authored-by: arty --- .../clvm_tools/stages/stage_2/compile.rs | 2 +- .../clvm_tools/stages/stage_2/module.rs | 6 +- src/compiler/compiler.rs | 162 ++++++++---------- 3 files changed, 74 insertions(+), 96 deletions(-) diff --git a/src/classic/clvm_tools/stages/stage_2/compile.rs b/src/classic/clvm_tools/stages/stage_2/compile.rs index 23132d63a..5c8d37bcb 100644 --- a/src/classic/clvm_tools/stages/stage_2/compile.rs +++ b/src/classic/clvm_tools/stages/stage_2/compile.rs @@ -21,7 +21,7 @@ use crate::classic::clvm_tools::stages::stage_2::helpers::{brun, evaluate, quote use crate::classic::clvm_tools::stages::stage_2::module::compile_mod; use crate::compiler::srcloc::Srcloc; -const DIAG_OUTPUT: bool = true; +const DIAG_OUTPUT: bool = false; lazy_static! { static ref PASS_THROUGH_OPERATORS: HashSet> = { diff --git a/src/classic/clvm_tools/stages/stage_2/module.rs b/src/classic/clvm_tools/stages/stage_2/module.rs index b538a125b..537aba7a2 100644 --- a/src/classic/clvm_tools/stages/stage_2/module.rs +++ b/src/classic/clvm_tools/stages/stage_2/module.rs @@ -601,10 +601,8 @@ where } let main_local_arguments = alist[0].clone(); - eprintln!("main_local_arguments {}", allocator.disassemble(&alist[0], None)); for arg in alist.iter().take(alist.len()-1).skip(1) { - eprintln!("parse_mod_sexp {}", allocator.disassemble(arg, None)); parse_mod_sexp( allocator, arg, @@ -1057,7 +1055,7 @@ pub fn compile_mod( allocator: &mut A, args: &A::NodePtr, macro_lookup: &A::NodePtr, - symbol_table: &A::NodePtr, + _symbol_table: &A::NodePtr, run_program: Rc, _level: usize, ) -> Result @@ -1065,8 +1063,6 @@ where A::NodePtr: Clone, { // Deal with the "mod" keyword. - eprintln!("compile_mod {}", allocator.disassemble(args, None)); - eprintln!("symbol_table {}", allocator.disassemble(symbol_table, None)); let loc = allocator.loc(macro_lookup); let produce_extra_info_prog = assemble(allocator.allocator(), "(_symbols_extra_info)") .map_err(|e| ClError(loc.clone(), e))?; diff --git a/src/compiler/compiler.rs b/src/compiler/compiler.rs index 041cf581c..15e54e827 100644 --- a/src/compiler/compiler.rs +++ b/src/compiler/compiler.rs @@ -1,6 +1,5 @@ use num_bigint::ToBigInt; -use std::sync::atomic::{AtomicI32, Ordering}; use std::borrow::Borrow; use std::collections::{BTreeMap, HashMap, HashSet}; use std::fs; @@ -29,9 +28,9 @@ use crate::compiler::clvm::{convert_to_clvm_rs, run, sha256tree, NewStyleIntConv use crate::compiler::codegen::{codegen, hoist_body_let_binding, process_helper_let_bindings}; use crate::compiler::comptypes::{ BodyForm, CompileErr, CompileForm, CompileModuleComponent, CompileModuleOutput, CompilerOpts, - CompilerOutput, ConstantKind, DefunData, DefmacData, Export, FrontendOutput, HelperForm, ImportLongName, - IncludeDesc, IncludeProcessType, ModulePhase, PrimaryCodegen, StandalonePhaseInfo, - SyntheticType, + CompilerOutput, ConstantKind, DefconstData, DefmacData, DefunData, Export, FrontendOutput, + HelperForm, ImportLongName, IncludeDesc, IncludeProcessType, ModulePhase, NamespaceData, + PrimaryCodegen, StandalonePhaseInfo, SyntheticType, }; use crate::compiler::dialect::{AcceptedDialect, KNOWN_DIALECTS}; use crate::compiler::frontend::frontend; @@ -45,35 +44,6 @@ use crate::compiler::srcloc::Srcloc; use crate::compiler::{BasicCompileContext, CompileContextWrapper}; use crate::util::Number; -lazy_static! { - pub static ref INDENT: AtomicI32 = AtomicI32::new(0); -} - -struct Indent; - -impl Indent { - fn new() -> Self { - INDENT.fetch_add(1, Ordering::Relaxed); - Indent - } -} - -impl std::fmt::Display for Indent { - fn fmt(&self, fmt: &'_ mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { - let indent = INDENT.fetch_add(0, Ordering::Relaxed); - for i in 0..indent { - write!(fmt, " ")?; - } - Ok(()) - } -} - -impl Drop for Indent { - fn drop(&mut self) { - INDENT.fetch_add(-1, Ordering::Relaxed); - } -} - pub const SHA256TREE_PROGRAM_CLVM: &str = "(2 (1 2 (3 (7 5) (1 11 (1 . 2) (2 2 (4 2 (4 9 ()))) (2 2 (4 2 (4 13 ())))) (1 11 (1 . 1) 5)) 1) (4 (1 2 (3 (7 5) (1 11 (1 . 2) (2 2 (4 2 (4 9 ()))) (2 2 (4 2 (4 13 ())))) (1 11 (1 . 1) 5)) 1) 1))"; pub const FUZZ_TEST_PRE_CSE_MERGE_FIX_FLAG: usize = 1; @@ -193,42 +163,37 @@ pub fn expand_com_forms_expr( opts: Rc, body: Rc, ) -> Result, CompileErr> { - let indent = Indent::new(); if let BodyForm::Call(l, c, tail) = &*body { if c.is_empty() { return Ok(body); } if let BodyForm::Value(atom) = &*c[0] { - if atom.atomize() == SExp::Atom(atom.loc(), b"com".to_vec()) { - // Is a com form. - eprintln!("{indent}com {}", c[1].to_sexp()); - return Ok(Rc::new(BodyForm::Call(l.clone(), vec![ - Rc::new(BodyForm::Value(SExp::Atom(c[0].loc(), b"function".to_vec()))), - c[1].clone(), - ], None))); + if c.len() == 2 && atom.atomize() == SExp::Atom(atom.loc(), b"com".to_vec()) { + return Ok(Rc::new(BodyForm::Call( + l.clone(), + vec![ + Rc::new(BodyForm::Value(SExp::Atom( + c[0].loc(), + b"function".to_vec(), + ))), + c[1].clone(), + ], + None, + ))); } } let mut call_args = vec![c[0].clone()]; for item in c.iter().skip(1) { - call_args.push(expand_com_forms_expr( - context, - opts.clone(), - item.clone() - )?); + call_args.push(expand_com_forms_expr(context, opts.clone(), item.clone())?); } - let new_tail = - if let Some(t) = &tail { - Some(expand_com_forms_expr( - context, - opts, - t.clone() - )?) - } else { - None - }; + let new_tail = if let Some(t) = &tail { + Some(expand_com_forms_expr(context, opts, t.clone())?) + } else { + None + }; return Ok(Rc::new(BodyForm::Call(l.clone(), call_args, new_tail))); } @@ -236,26 +201,63 @@ pub fn expand_com_forms_expr( Ok(body) } -/// Expand compiler forms using the modern evaluator before handing a program to -/// a backend that does not implement modern compiler-form semantics. +fn expand_com_forms_helper( + context: &mut BasicCompileContext, + opts: Rc, + helper: &HelperForm, +) -> Result { + match helper { + HelperForm::Defconstant(defconst) => Ok(HelperForm::Defconstant(DefconstData { + body: expand_com_forms_expr(context, opts, defconst.body.clone())?, + ..defconst.clone() + })), + HelperForm::Defmacro(defmacro) => Ok(HelperForm::Defmacro(DefmacData { + program: Rc::new(expand_com_forms( + context, + opts, + defmacro.program.as_ref().clone(), + )?), + ..defmacro.clone() + })), + HelperForm::Defun(inline, defun) => Ok(HelperForm::Defun( + *inline, + Box::new(DefunData { + body: expand_com_forms_expr(context, opts, defun.body.clone())?, + ..*defun.clone() + }), + )), + HelperForm::Defnamespace(namespace) => { + let helpers = namespace + .helpers + .iter() + .map(|helper| expand_com_forms_helper(context, opts.clone(), helper)) + .collect::, _>>()?; + Ok(HelperForm::Defnamespace(Box::new(NamespaceData { + helpers, + ..*namespace.clone() + }))) + } + HelperForm::Defnsref(_) => Ok(helper.clone()), + } +} + +/// Translate modern `com` forms for a backend that only implements `function`. pub fn expand_com_forms( context: &mut BasicCompileContext, opts: Rc, compileform: CompileForm, ) -> Result { - let helpers = compileform.helpers.iter().map(|h| { - todo!(); - }); - - let new_expr = expand_com_forms_expr( - context, - opts, - compileform.exp.clone() - )?; + let helpers = compileform + .helpers + .iter() + .map(|helper| expand_com_forms_helper(context, opts.clone(), helper)) + .collect::, _>>()?; + let exp = expand_com_forms_expr(context, opts, compileform.exp.clone())?; Ok(CompileForm { - exp: new_expr, - .. compileform + helpers, + exp, + ..compileform }) } @@ -266,16 +268,13 @@ pub fn finish_compilation( opts: Rc, p2: CompileForm, ) -> Result { - let indent = Indent::new(); let p3 = context.post_desugar_optimization(opts.clone(), p2)?; if opts.dialect().classic_codegen { let mut modern_dialect = opts.dialect(); modern_dialect.classic_codegen = false; let modern_opts = opts.set_dialect(modern_dialect); - eprintln!("{indent}p3 {}", p3.to_sexp()); let p4 = expand_com_forms(context, modern_opts, p3)?; - eprintln!("{indent}expanded {}", p4.to_sexp()); return classic_codegen(opts, p4); } @@ -291,7 +290,7 @@ pub fn finish_compilation( /// /// The classic compiler's normal final optimization is part of its code /// generation contract. No modern optimization hooks run on this path. -fn classic_codegen(opts: Rc, mut program: CompileForm) -> Result { +fn classic_codegen(opts: Rc, program: CompileForm) -> Result { let language_flags = if opts.dialect().extra_numeric_constants { NEW_BIT_CONSTANTS } else { @@ -306,27 +305,13 @@ fn classic_codegen(opts: Rc, mut program: CompileForm) -> Resu runner.set_compiler_opts(Some(opts.clone())); let mut allocator = SExpClassicAllocator::new(); - let x_atom = SExp::Atom(program.loc(), b"X".to_vec()); - let program_args = Rc::new(SExp::Cons( - program.loc(), - Rc::new(x_atom.clone()), - Rc::new(SExp::Nil(program.loc())), - )); - let cons = Rc::new(BodyForm::Value(SExp::Integer(program.loc(), 4_u32.to_bigint().unwrap()))); let macro_lookup = default_macro_lookup(&mut allocator, runner.clone()); let nil = allocator .import(program.loc(), clvm_rs::allocator::NodePtr::NIL) .map_err(|e| CompileErr(e.0, e.1.to_string()))?; - let final_program = Rc::new(SExp::Cons( - program.loc(), - Rc::new(SExp::Atom(program.loc(), b"mod".to_vec())), - program.to_sexp() - )); - let args = allocator .from_sexp(program.to_sexp()) .map_err(|e| CompileErr(e.0, e.1.to_string()))?; - // eprintln!("compile_mod {}", final_program); let generated = compile_mod( &mut allocator, &args, @@ -360,7 +345,6 @@ pub fn compile_from_compileform( opts: Rc, p0: CompileForm, ) -> Result { - let indent = Indent::new(); let p1 = if opts.dialect().classic_codegen { p0 @@ -368,11 +352,9 @@ pub fn compile_from_compileform( context.frontend_optimization(opts.clone(), p0)? }; - eprintln!("{indent}frontend_opt {}", p1.to_sexp()); // Resolve includes, convert program source to lexemes let p2 = do_desugar(opts.clone(), &p1)?; - eprintln!("{indent}desugared {}", p2.to_sexp()); finish_compilation(context, opts, p2) } From bc9263b91d1c84e27efd616aeb89735ea9857814 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 5 Aug 2026 02:19:23 +0000 Subject: [PATCH 33/38] Run modern optimization before classic codegen Co-authored-by: arty --- .../clvm_tools/stages/stage_2/abstraction.rs | 15 ++++++--------- .../clvm_tools/stages/stage_2/operators.rs | 4 +--- src/compiler/compiler.rs | 18 +++++++++--------- 3 files changed, 16 insertions(+), 21 deletions(-) diff --git a/src/classic/clvm_tools/stages/stage_2/abstraction.rs b/src/classic/clvm_tools/stages/stage_2/abstraction.rs index 088e1fd11..3e8fe3f0e 100644 --- a/src/classic/clvm_tools/stages/stage_2/abstraction.rs +++ b/src/classic/clvm_tools/stages/stage_2/abstraction.rs @@ -5,8 +5,8 @@ use std::ops::Index; use clvm_rs::allocator::{Allocator, NodePtr, SExp}; use clvm_rs::error::EvalErr; -use crate::classic::clvm_tools::binutils::disassemble; use crate::classic::clvm::__type_compatibility__::bi_zero; +use crate::classic::clvm_tools::binutils::disassemble; use crate::compiler::sexp::SExp as ModernSExp; use crate::compiler::srcloc::Srcloc; use crate::util::{number_from_u8, u8_from_number}; @@ -175,15 +175,12 @@ impl SExpClassicAllocator { .new_pair(first.raw, rest.raw) .map_err(|e| self.map_err(loc.clone(), e))? } - ModernSExp::Integer(_, value) => { - if *value == bi_zero() { - self.allocator.new_atom(&[]) - } else { - self - .allocator - .new_atom(&u8_from_number(value.clone())) - }.map_err(|e| self.map_err(loc.clone(), e))? + ModernSExp::Integer(_, value) => if *value == bi_zero() { + self.allocator.new_atom(&[]) + } else { + self.allocator.new_atom(&u8_from_number(value.clone())) } + .map_err(|e| self.map_err(loc.clone(), e))?, ModernSExp::QuotedString(_, _, value) | ModernSExp::Atom(_, value) => self .allocator .new_atom(value) diff --git a/src/classic/clvm_tools/stages/stage_2/operators.rs b/src/classic/clvm_tools/stages/stage_2/operators.rs index 8592bcdca..87db83739 100644 --- a/src/classic/clvm_tools/stages/stage_2/operators.rs +++ b/src/classic/clvm_tools/stages/stage_2/operators.rs @@ -18,9 +18,7 @@ use crate::classic::clvm::OPERATORS_LATEST_VERSION; use crate::classic::clvm::sexp::proper_list; use crate::classic::clvm::{keyword_from_atom, keyword_to_atom}; -use crate::classic::clvm_tools::binutils::{ - assemble_from_ir, disassemble_to_ir_with_kw, -}; +use crate::classic::clvm_tools::binutils::{assemble_from_ir, disassemble_to_ir_with_kw}; use crate::classic::clvm_tools::ir::reader::read_ir; use crate::classic::clvm_tools::ir::writer::write_ir_to_stream; use crate::classic::clvm_tools::sha256tree::TreeHash; diff --git a/src/compiler/compiler.rs b/src/compiler/compiler.rs index 15e54e827..d4b940aa0 100644 --- a/src/compiler/compiler.rs +++ b/src/compiler/compiler.rs @@ -15,7 +15,7 @@ use crate::classic::clvm::__type_compatibility__::{bi_one, bi_zero}; use crate::classic::clvm_tools::ir::r#type::NEW_BIT_CONSTANTS; use crate::classic::clvm_tools::stages::stage_0::TRunProgram; use crate::classic::clvm_tools::stages::stage_2::abstraction::{ - ASExp, BufCarrier, ClassicAllocator, SExpClassicAllocator, SExpNode, + ASExp, BufCarrier, ClassicAllocator, SExpClassicAllocator, }; use crate::classic::clvm_tools::stages::stage_2::defaults::default_macro_lookup; use crate::classic::clvm_tools::stages::stage_2::module::compile_mod; @@ -268,16 +268,17 @@ pub fn finish_compilation( opts: Rc, p2: CompileForm, ) -> Result { - let p3 = context.post_desugar_optimization(opts.clone(), p2)?; - if opts.dialect().classic_codegen { let mut modern_dialect = opts.dialect(); modern_dialect.classic_codegen = false; let modern_opts = opts.set_dialect(modern_dialect); + let p3 = context.post_desugar_optimization(modern_opts.clone(), p2)?; let p4 = expand_com_forms(context, modern_opts, p3)?; return classic_codegen(opts, p4); } + let p3 = context.post_desugar_optimization(opts.clone(), p2)?; + // generate code from AST, optionally with optimization let generated = codegen(context, opts.clone(), &p3)?; @@ -345,12 +346,11 @@ pub fn compile_from_compileform( opts: Rc, p0: CompileForm, ) -> Result { - let p1 = - if opts.dialect().classic_codegen { - p0 - } else { - context.frontend_optimization(opts.clone(), p0)? - }; + let p1 = if opts.dialect().classic_codegen { + p0 + } else { + context.frontend_optimization(opts.clone(), p0)? + }; // Resolve includes, convert program source to lexemes let p2 = do_desugar(opts.clone(), &p1)?; From 543658270ac1b13de45ab98cc1dda93720d4d8f5 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 5 Aug 2026 02:22:09 +0000 Subject: [PATCH 34/38] Keep classic let helpers out of modern deinline Co-authored-by: arty --- src/compiler/codegen.rs | 3 +++ src/compiler/compiler.rs | 5 ++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/compiler/codegen.rs b/src/compiler/codegen.rs index fc4c943e7..4433287a6 100644 --- a/src/compiler/codegen.rs +++ b/src/compiler/codegen.rs @@ -1306,6 +1306,9 @@ pub fn should_inline_let( opts: Rc, inline_hint: &Option, ) -> bool { + if opts.dialect().classic_codegen { + return false; + } let match_none = opts.module_phase().is_none(); let want_inline = matches!(inline_hint, Some(LetFormInlineHint::Inline(_))); want_inline || (match_none && inline_hint.is_none()) diff --git a/src/compiler/compiler.rs b/src/compiler/compiler.rs index d4b940aa0..7f6f4ce00 100644 --- a/src/compiler/compiler.rs +++ b/src/compiler/compiler.rs @@ -272,9 +272,8 @@ pub fn finish_compilation( let mut modern_dialect = opts.dialect(); modern_dialect.classic_codegen = false; let modern_opts = opts.set_dialect(modern_dialect); - let p3 = context.post_desugar_optimization(modern_opts.clone(), p2)?; - let p4 = expand_com_forms(context, modern_opts, p3)?; - return classic_codegen(opts, p4); + let p3 = expand_com_forms(context, modern_opts, p2)?; + return classic_codegen(opts, p3); } let p3 = context.post_desugar_optimization(opts.clone(), p2)?; From 9765e1e9aac5d8900ea90aa21b778d986ea1f145 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 5 Aug 2026 02:22:48 +0000 Subject: [PATCH 35/38] Keep nested classic let helpers non-inline Co-authored-by: arty --- src/compiler/codegen.rs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/compiler/codegen.rs b/src/compiler/codegen.rs index 4433287a6..0976b8afe 100644 --- a/src/compiler/codegen.rs +++ b/src/compiler/codegen.rs @@ -1306,9 +1306,6 @@ pub fn should_inline_let( opts: Rc, inline_hint: &Option, ) -> bool { - if opts.dialect().classic_codegen { - return false; - } let match_none = opts.module_phase().is_none(); let want_inline = matches!(inline_hint, Some(LetFormInlineHint::Inline(_))); want_inline || (match_none && inline_hint.is_none()) @@ -1560,6 +1557,7 @@ pub fn hoist_body_let_binding( BodyForm::Let(LetFormKind::Parallel, letdata) => { let mut out_defuns = Vec::new(); let defun_name = gensym("letbinding".as_bytes().to_vec()); + let force_non_inline = opts.dialect().classic_codegen && outer_context.is_some(); let mut revised_bindings = Vec::new(); for b in letdata.bindings.iter() { @@ -1587,6 +1585,15 @@ pub fn hoist_body_let_binding( revised_bindings.to_vec(), letdata.body.clone(), ); + let generated_defun = if force_non_inline { + if let HelperForm::Defun(_, defun) = generated_defun { + HelperForm::Defun(false, defun) + } else { + unreachable!() + } + } else { + generated_defun + }; out_defuns.push(generated_defun); let mut let_args = generate_let_args(letdata.loc.clone(), revised_bindings.to_vec()); From 0556b103f5c546369b86f1464ab9e0c3f3755c53 Mon Sep 17 00:00:00 2001 From: arty Date: Wed, 5 Aug 2026 10:16:46 -0700 Subject: [PATCH 36/38] fmt + clippy --- src/classic/clvm_tools/stages/stage_2/compile.rs | 7 ++++++- src/compiler/compiler.rs | 11 +++++------ 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/classic/clvm_tools/stages/stage_2/compile.rs b/src/classic/clvm_tools/stages/stage_2/compile.rs index 5c8d37bcb..2bddd44a0 100644 --- a/src/classic/clvm_tools/stages/stage_2/compile.rs +++ b/src/classic/clvm_tools/stages/stage_2/compile.rs @@ -580,10 +580,15 @@ where Ok(None) } +pub type SplitRestResult = Option<( + Vec<::NodePtr>, + Option<::NodePtr>, +)>; + fn split_rest_tail( allocator: &A, args: &A::NodePtr, -) -> Result, Option)>, ClError> +) -> Result, ClError> where A::NodePtr: Clone, { diff --git a/src/compiler/compiler.rs b/src/compiler/compiler.rs index 7f6f4ce00..0175a6111 100644 --- a/src/compiler/compiler.rs +++ b/src/compiler/compiler.rs @@ -159,7 +159,6 @@ pub fn do_desugar( } pub fn expand_com_forms_expr( - context: &mut BasicCompileContext, opts: Rc, body: Rc, ) -> Result, CompileErr> { @@ -186,11 +185,11 @@ pub fn expand_com_forms_expr( let mut call_args = vec![c[0].clone()]; for item in c.iter().skip(1) { - call_args.push(expand_com_forms_expr(context, opts.clone(), item.clone())?); + call_args.push(expand_com_forms_expr(opts.clone(), item.clone())?); } let new_tail = if let Some(t) = &tail { - Some(expand_com_forms_expr(context, opts, t.clone())?) + Some(expand_com_forms_expr(opts, t.clone())?) } else { None }; @@ -208,7 +207,7 @@ fn expand_com_forms_helper( ) -> Result { match helper { HelperForm::Defconstant(defconst) => Ok(HelperForm::Defconstant(DefconstData { - body: expand_com_forms_expr(context, opts, defconst.body.clone())?, + body: expand_com_forms_expr(opts, defconst.body.clone())?, ..defconst.clone() })), HelperForm::Defmacro(defmacro) => Ok(HelperForm::Defmacro(DefmacData { @@ -222,7 +221,7 @@ fn expand_com_forms_helper( HelperForm::Defun(inline, defun) => Ok(HelperForm::Defun( *inline, Box::new(DefunData { - body: expand_com_forms_expr(context, opts, defun.body.clone())?, + body: expand_com_forms_expr(opts, defun.body.clone())?, ..*defun.clone() }), )), @@ -252,7 +251,7 @@ pub fn expand_com_forms( .iter() .map(|helper| expand_com_forms_helper(context, opts.clone(), helper)) .collect::, _>>()?; - let exp = expand_com_forms_expr(context, opts, compileform.exp.clone())?; + let exp = expand_com_forms_expr(opts, compileform.exp.clone())?; Ok(CompileForm { helpers, From 299c335a164397248137bd973fa279486323b3b8 Mon Sep 17 00:00:00 2001 From: arty Date: Wed, 5 Aug 2026 10:51:56 -0700 Subject: [PATCH 37/38] install yaml --- .github/workflows/build-test.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index e0291442b..8cad1efef 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -130,6 +130,7 @@ jobs: pip install --no-index --find-links target/wheels/ chialisp pip install clvm_rs pip install clvm_tools + pip install yaml cd resources/tests python test_clvm_step.py python mandelbrot-cldb.py From 52995342a16dd11f8dda48da3947191dddd3df13 Mon Sep 17 00:00:00 2001 From: arty Date: Wed, 5 Aug 2026 11:05:11 -0700 Subject: [PATCH 38/38] yaml -> pyyaml --- .github/workflows/build-test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index 8cad1efef..ce1ffb135 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -130,7 +130,7 @@ jobs: pip install --no-index --find-links target/wheels/ chialisp pip install clvm_rs pip install clvm_tools - pip install yaml + pip install pyyaml cd resources/tests python test_clvm_step.py python mandelbrot-cldb.py