From f69a41ed8183a01f701271008ecd9ddc904297f5 Mon Sep 17 00:00:00 2001 From: arty Date: Mon, 22 Apr 2024 09:28:51 -0700 Subject: [PATCH 1/6] Add simple compile for property tests --- src/tests/compiler/fuzz.rs | 165 ++++++++++++++++++++++++++++++++++++- 1 file changed, 162 insertions(+), 3 deletions(-) diff --git a/src/tests/compiler/fuzz.rs b/src/tests/compiler/fuzz.rs index 764a8293..361d8533 100644 --- a/src/tests/compiler/fuzz.rs +++ b/src/tests/compiler/fuzz.rs @@ -5,6 +5,7 @@ use rand::prelude::Distribution; use rand::{Rng, SeedableRng}; use rand_chacha::ChaCha8Rng; use std::borrow::Borrow; +use std::cell::RefCell; use std::collections::{BTreeSet, HashMap}; use std::fmt::Debug; use std::rc::Rc; @@ -12,9 +13,10 @@ use std::rc::Rc; use clvmr::Allocator; use crate::classic::clvm_tools::stages::stage_0::{DefaultProgramRunner, TRunProgram}; -use crate::compiler::clvm::run; -use crate::compiler::compiler::DefaultCompilerOpts; -use crate::compiler::comptypes::{BodyForm, CompileErr, CompilerOpts}; +use crate::compiler::clvm::{convert_to_clvm_rs, run}; +use crate::compiler::compiler::{compile_file, DefaultCompilerOpts}; +use crate::compiler::comptypes::{BodyForm, CompileErr, CompilerOpts, PrimaryCodegen}; +use crate::compiler::dialect::{detect_modern, AcceptedDialect}; use crate::compiler::fuzz::{ExprModifier, FuzzChoice, FuzzGenerator, FuzzTypeParams, Rule}; use crate::compiler::prims::primquote; use crate::compiler::sexp::{enlist, extract_atom_replacement, parse_sexp, SExp}; @@ -36,6 +38,163 @@ pub fn compose_sexp(loc: Srcloc, s: &str) -> Rc { parse_sexp(loc, s.bytes()).expect("should parse")[0].clone() } +#[derive(Clone)] +pub struct TestModuleCompilerOpts { + opts: Rc, + written_files: Rc>>>, +} + +impl TestModuleCompilerOpts { + pub fn new(opts: Rc) -> Self { + TestModuleCompilerOpts { + opts: opts, + written_files: Rc::new(RefCell::new(HashMap::new())), + } + } + + fn new_opts(&self, opts: Rc) -> Rc { + Rc::new(TestModuleCompilerOpts { + opts, + written_files: self.written_files.clone(), + }) + } +} + +impl CompilerOpts for TestModuleCompilerOpts { + fn filename(&self) -> String { + self.opts.filename() + } + + fn code_generator(&self) -> Option { + self.opts.code_generator() + } + fn dialect(&self) -> AcceptedDialect { + self.opts.dialect() + } + fn in_defun(&self) -> bool { + self.opts.in_defun() + } + fn stdenv(&self) -> bool { + self.opts.stdenv() + } + fn optimize(&self) -> bool { + self.opts.optimize() + } + fn frontend_opt(&self) -> bool { + self.opts.frontend_opt() + } + fn frontend_check_live(&self) -> bool { + self.opts.frontend_check_live() + } + fn start_env(&self) -> Option> { + self.opts.start_env() + } + fn disassembly_ver(&self) -> Option { + self.opts.disassembly_ver() + } + fn prim_map(&self) -> Rc, Rc>> { + self.opts.prim_map() + } + fn get_search_paths(&self) -> Vec { + self.opts.get_search_paths() + } + fn set_dialect(&self, dialect: AcceptedDialect) -> Rc { + self.new_opts(self.opts.set_dialect(dialect)) + } + fn set_search_paths(&self, dirs: &[String]) -> Rc { + self.new_opts(self.opts.set_search_paths(dirs)) + } + fn set_in_defun(&self, new_in_defun: bool) -> Rc { + self.new_opts(self.opts.set_in_defun(new_in_defun)) + } + fn set_stdenv(&self, new_stdenv: bool) -> Rc { + self.new_opts(self.opts.set_stdenv(new_stdenv)) + } + fn set_optimize(&self, opt: bool) -> Rc { + self.new_opts(self.opts.set_optimize(opt)) + } + fn set_frontend_opt(&self, opt: bool) -> Rc { + self.new_opts(self.opts.set_frontend_opt(opt)) + } + fn set_frontend_check_live(&self, check: bool) -> Rc { + self.new_opts(self.opts.set_frontend_check_live(check)) + } + fn set_code_generator(&self, new_compiler: PrimaryCodegen) -> Rc { + self.new_opts(self.opts.set_code_generator(new_compiler)) + } + fn set_start_env(&self, start_env: Option>) -> Rc { + self.new_opts(self.opts.set_start_env(start_env)) + } + fn set_prim_map(&self, prims: Rc, Rc>>) -> Rc { + self.new_opts(self.opts.set_prim_map(prims)) + } + fn set_disassembly_ver(&self, ver: Option) -> Rc { + self.new_opts(self.opts.set_disassembly_ver(ver)) + } + fn read_new_file( + &self, + inc_from: String, + filename: String, + ) -> Result<(String, Vec), CompileErr> { + self.opts.read_new_file(inc_from, filename) + } + fn compile_program( + &self, + allocator: &mut Allocator, + runner: Rc, + sexp: Rc, + symbol_table: &mut HashMap, + ) -> Result { + self.opts + .compile_program(allocator, runner, sexp, symbol_table) + } +} + +pub struct PerformCompileResult { + pub compiled: Rc, + pub source_opts: TestModuleCompilerOpts, +} + +pub fn perform_compile_of_file( + allocator: &mut Allocator, + runner: Rc, + filename: &str, + content: &str, +) -> Result { + let loc = Srcloc::start(filename); + let parsed: Vec> = parse_sexp(loc.clone(), content.bytes()).expect("should parse"); + let listed = Rc::new(enlist(loc.clone(), &parsed)); + let nodeptr = convert_to_clvm_rs(allocator, listed.clone()).expect("should convert"); + let dialect = detect_modern(allocator, nodeptr); + let orig_opts: Rc = Rc::new(DefaultCompilerOpts::new(filename)) + .set_optimize(true) + .set_frontend_opt(false) + .set_dialect(dialect) + .set_search_paths(&["resources/tests/module".to_string()]); + let source_opts = TestModuleCompilerOpts::new(orig_opts); + let opts: Rc = Rc::new(source_opts.clone()); + let mut symbol_table = HashMap::new(); + let compiled = compile_file(allocator, runner.clone(), opts, &content, &mut symbol_table)?; + Ok(PerformCompileResult { + compiled: Rc::new(compiled), + source_opts, + }) +} + +#[test] +fn test_perform_compile_of_file() { + let mut allocator = Allocator::new(); + let runner = Rc::new(DefaultProgramRunner::new()); + let result = perform_compile_of_file( + &mut allocator, + runner, + "test.clsp", + "(mod (A) (include *standard-cl-23*) (+ A 1))" + ).expect("should compile"); + assert_eq!(result.source_opts.dialect().stepping, Some(23)); + assert_eq!(result.compiled.to_string(), "(16 2 (1 . 1))"); +} + pub fn simple_run( opts: Rc, expr: Rc, From 3193aa76a5553ea7f7a4c8c0ff4c4e6a277530c2 Mon Sep 17 00:00:00 2001 From: arty Date: Mon, 22 Apr 2024 10:08:13 -0700 Subject: [PATCH 2/6] Move code --- src/tests/compiler/fuzz_assign.rs | 453 ++++++++++++++++++++++++++++++ src/tests/compiler/mod.rs | 1 + 2 files changed, 454 insertions(+) create mode 100644 src/tests/compiler/fuzz_assign.rs diff --git a/src/tests/compiler/fuzz_assign.rs b/src/tests/compiler/fuzz_assign.rs new file mode 100644 index 00000000..0cb60ac7 --- /dev/null +++ b/src/tests/compiler/fuzz_assign.rs @@ -0,0 +1,453 @@ +use num_bigint::ToBigInt; +use rand::Rng; +use std::collections::{BTreeMap, BTreeSet}; +use std::fmt::Debug; +use std::rc::Rc; + +use crate::compiler::comptypes::{Binding, BindingPattern, BodyForm, LetData, LetFormKind}; +use crate::compiler::sexp::{decode_string, enlist, SExp}; +use crate::compiler::srcloc::Srcloc; + +use crate::tests::compiler::fuzz::{simple_seeded_rng, SupportedOperators, ValueSpecification}; + +pub fn create_variable_set(_srcloc: Srcloc, vars: usize) -> BTreeSet> { + (0..vars) + .map(|n| format!("v{n}").bytes().collect()) + .collect() +} + +#[derive(Default, Clone)] +pub struct ExprVariableUsage { + pub toplevel: BTreeSet>, + pub bindings: BTreeMap, Vec>>, +} + +#[derive(Debug, Clone)] +pub struct GeneratedExpr { + definition: Rc, + sexp: Rc, +} + +impl ExprVariableUsage { + fn fmtvar( + &self, + writer: &mut std::fmt::Formatter<'_>, + lvl: usize, + v: &[u8], + ) -> Result<(), std::fmt::Error> { + for _ in 0..(2 * lvl) { + write!(writer, " ")?; + } + writeln!(writer, "{}:", decode_string(v))?; + if let Some(children) = self.bindings.get(v) { + for c in children.iter() { + self.fmtvar(writer, lvl + 1, c)?; + } + } + + Ok(()) + } + + // Find the parent of this var. + pub fn find_parent_of_var<'a>(&'a self, var: &[u8]) -> Option<&'a Vec> { + for (parent, bindings) in self.bindings.iter() { + if bindings.iter().any(|c| c == var) { + return Some(parent); + } + } + + None + } + + // Find the path to this var. + pub fn find_path_to_var<'a>(&'a self, var: &[u8]) -> Vec<&'a Vec> { + let mut result = Vec::new(); + let mut checking = var; + while let Some(parent) = self.find_parent_of_var(checking) { + checking = parent; + result.push(parent); + } + result + } + + // Give the set of variables in scope for the definition of var. + pub fn variables_in_scope<'a>(&'a self, var: &[u8]) -> Vec<&'a Vec> { + // If this variable itself use an assign form as its definition, then + // all the innermost bindings are in scope. + let mut result = self + .bindings + .get(var) + .map(|v| v.iter().map(|e| &(*e)).collect()) + .unwrap_or_else(|| vec![]); + + // Get the parents of var. + let parents = self.find_path_to_var(var); + eprintln!("for {}", decode_string(var)); + for p in parents.iter() { + eprintln!("parent {}", decode_string(p)); + } + + // If there are no parents, then the variables in scope are the toplevel + // ones that appear before var. + let mut from_scopes = if parents.is_empty() { + self.toplevel + .iter() + .take_while(|t| *t != var) + .map(|t| &(*t)) + .collect() + } else { + let mut scopes = Vec::new(); + let mut target = var; + for p in parents.iter() { + let p_borrowed: &[u8] = &p; + if let Some(children) = self.bindings.get(p_borrowed) { + eprintln!("checking parent: {}", decode_string(p)); + let mut appear_before_in_parent: Vec<&'a Vec> = children + .iter() + .take_while(|c| *c != target) + .map(|t| &(*t)) + .collect(); + eprintln!( + "{} in scope with parent {} of {}", + appear_before_in_parent.len(), + decode_string(p), + decode_string(var) + ); + scopes.append(&mut appear_before_in_parent); + target = p; + } + } + scopes + }; + + // Add the visible toplevel definitions if they won + result.append(&mut from_scopes); + result + } + + // Generate an expression to define one variable. + pub fn generate_expression( + &self, + srcloc: &Srcloc, + wanted_complexity: usize, + rng: &mut R, + args: &[Vec], + var: &[u8], + ) -> GeneratedExpr { + let mut in_scope: Vec<&Vec> = args.iter().collect(); + let mut assignments_in_scope = self.variables_in_scope(var); + in_scope.append(&mut assignments_in_scope); + eprintln!("for {}", decode_string(var)); + for s in in_scope.iter() { + eprintln!("in scope {}", decode_string(s)); + } + + let generate_constant = |rng: &mut R| { + // Constant value + let random_number: i8 = rng.gen(); + let sexp = Rc::new(SExp::Integer( + srcloc.clone(), + random_number.to_bigint().unwrap(), + )); + let definition = Rc::new(ValueSpecification::ConstantValue(sexp.clone())); + GeneratedExpr { definition, sexp } + }; + + let generate_reference = |rng: &mut R| { + let variable_choice: usize = rng.gen(); + let variable = in_scope[variable_choice % in_scope.len()].to_vec(); + let var_sexp = Rc::new(SExp::Atom(srcloc.clone(), variable.clone())); + let reference = Rc::new(ValueSpecification::VarRef(variable.clone())); + + GeneratedExpr { + definition: reference, + sexp: var_sexp, + } + }; + + let generate_simple = |rng: &mut R| { + if in_scope.is_empty() || rng.gen() { + generate_constant(rng) + } else { + generate_reference(rng) + } + }; + + let mut result = generate_simple(rng); + let complexity: usize = rng.gen(); + + // Generate up to a certain number of operations. + for _ in 0..(complexity % wanted_complexity) { + // Generate the other branch. + let other_result = generate_simple(rng); + + // Generate a binop. + let random_op: SupportedOperators = rng.gen(); + let (left, right) = if rng.gen() { + (result, other_result) + } else { + (other_result, result) + }; + + result = GeneratedExpr { + sexp: Rc::new(enlist( + srcloc.clone(), + &[Rc::new(random_op.to_sexp(&srcloc)), left.sexp, right.sexp], + )), + definition: Rc::new(ValueSpecification::ClvmBinop( + random_op, + left.definition, + right.definition, + )), + }; + } + + result + } + + // Create the assignments for the assign form. + fn create_assign_form_for_var( + &self, + srcloc: &Srcloc, + expressions: &BTreeMap, GeneratedExpr>, + var: &[u8], + ) -> BodyForm { + let bound_in_var = self.bindings.get(var).cloned().unwrap_or_else(|| vec![]); + let expr = expressions.get(var).unwrap(); + + // No bindings below: just output the expr. + if bound_in_var.is_empty() { + return expr.definition.to_bodyform(srcloc); + } + + let bindings: Vec> = bound_in_var + .iter() + .map(|t| { + let body = self.create_assign_form_for_var(srcloc, expressions, t); + Rc::new(Binding { + loc: srcloc.clone(), + nl: srcloc.clone(), + body: Rc::new(body), + pattern: BindingPattern::Complex(Rc::new(SExp::Atom( + srcloc.clone(), + t.to_vec(), + ))), + }) + }) + .collect(); + + BodyForm::Let( + LetFormKind::Assign, + Box::new(LetData { + kw: None, + loc: srcloc.clone(), + bindings, + body: Rc::new(expr.definition.to_bodyform(srcloc)), + inline_hint: None, + }), + ) + } + + pub fn create_assign_form( + &self, + srcloc: &Srcloc, + expressions: &BTreeMap, GeneratedExpr>, + ) -> BodyForm { + assert!(!self.toplevel.is_empty()); + let last_top = self + .toplevel + .iter() + .skip(self.toplevel.len() - 1) + .next() + .unwrap(); + let bindings: Vec> = self + .toplevel + .iter() + .map(|t| { + let body = self.create_assign_form_for_var(srcloc, expressions, t); + Rc::new(Binding { + loc: srcloc.clone(), + nl: srcloc.clone(), + body: Rc::new(body), + pattern: BindingPattern::Complex(Rc::new(SExp::Atom( + srcloc.clone(), + t.to_vec(), + ))), + }) + }) + .collect(); + + BodyForm::Let( + LetFormKind::Assign, + Box::new(LetData { + loc: srcloc.clone(), + kw: None, + bindings, + inline_hint: None, + body: Rc::new(BodyForm::Value(SExp::Atom( + srcloc.clone(), + last_top.clone(), + ))), + }), + ) + } +} + +#[test] +fn test_expr_variable_usage() { + let srcloc = Srcloc::start("*test*"); + let mut rng = simple_seeded_rng(0x02020202); + let vars = create_variable_set(srcloc.clone(), 5); + let structure_graph = create_structure_from_variables(&mut rng, &vars); + + assert_eq!( + format!("{structure_graph:?}"), + indoc! {" + v0: + v4: + v1: + v2: + v3: + "} + ); + assert_eq!( + structure_graph.find_parent_of_var(b"v1"), + Some(&b"v0".to_vec()) + ); + assert_eq!(structure_graph.find_path_to_var(b"v1"), vec![b"v0"]); + assert_eq!(structure_graph.variables_in_scope(b"v1"), vec![b"v4"]); + assert_eq!( + structure_graph.variables_in_scope(b"v0"), + vec![b"v4", b"v1"] + ); + assert_eq!( + structure_graph.variables_in_scope(b"v3"), + vec![b"v0", b"v2"] + ); + let g3 = structure_graph.generate_expression( + &srcloc, + 5, + &mut rng, + &[b"a1".to_vec(), b"a2".to_vec()], + b"v3", + ); + assert_eq!(g3.sexp.to_string(), "(18 (16 122 (17 a1 43)) -53)"); + assert_eq!( + g3.definition, + Rc::new(ValueSpecification::ClvmBinop( + SupportedOperators::Times, + Rc::new(ValueSpecification::ClvmBinop( + SupportedOperators::Plus, + Rc::new(ValueSpecification::ConstantValue(Rc::new(SExp::Integer( + srcloc.clone(), + 122.to_bigint().unwrap() + )))), + Rc::new(ValueSpecification::ClvmBinop( + SupportedOperators::Minus, + Rc::new(ValueSpecification::VarRef(b"a1".to_vec())), + Rc::new(ValueSpecification::ConstantValue(Rc::new(SExp::Integer( + srcloc.clone(), + 43.to_bigint().unwrap() + )))) + )), + )), + Rc::new(ValueSpecification::ConstantValue(Rc::new(SExp::Integer( + srcloc.clone(), + -53.to_bigint().unwrap() + )))) + )) + ); + let g1 = structure_graph.generate_expression(&srcloc, 10, &mut rng, &[b"a1".to_vec()], b"v1"); + assert_eq!( + g1.sexp.to_string(), + "(16 v4 (16 (17 (17 (16 v4 v4) 29) 109) a1))" + ); + assert_eq!( + g1.definition, + Rc::new(ValueSpecification::ClvmBinop( + SupportedOperators::Plus, + Rc::new(ValueSpecification::VarRef(b"v4".to_vec())), + Rc::new(ValueSpecification::ClvmBinop( + SupportedOperators::Plus, + Rc::new(ValueSpecification::ClvmBinop( + SupportedOperators::Minus, + Rc::new(ValueSpecification::ClvmBinop( + SupportedOperators::Minus, + Rc::new(ValueSpecification::ClvmBinop( + SupportedOperators::Plus, + Rc::new(ValueSpecification::VarRef(b"v4".to_vec())), + Rc::new(ValueSpecification::VarRef(b"v4".to_vec())) + )), + Rc::new(ValueSpecification::ConstantValue(Rc::new(SExp::Integer( + srcloc.clone(), + 29.to_bigint().unwrap() + )))) + )), + Rc::new(ValueSpecification::ConstantValue(Rc::new(SExp::Integer( + srcloc.clone(), + 109.to_bigint().unwrap() + )))) + )), + Rc::new(ValueSpecification::VarRef(b"a1".to_vec())) + )) + )) + ); + let free_vars: Vec> = g1.definition.get_free_vars().into_iter().collect(); + assert_eq!(free_vars, vec![b"a1".to_vec(), b"v4".to_vec()]); +} + +impl Debug for ExprVariableUsage { + fn fmt(&self, writer: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { + for t in self.toplevel.iter() { + self.fmtvar(writer, 0, t)?; + } + Ok(()) + } +} + +pub fn create_structure_from_variables( + rng: &mut R, + v: &BTreeSet>, +) -> ExprVariableUsage { + let mut v_start = v.clone(); + let mut usage = ExprVariableUsage::default(); + + while !v_start.is_empty() { + // Choose a variable. + let chosen_idx: usize = rng.gen(); + let chosen = v_start + .iter() + .skip(chosen_idx % v_start.len()) + .next() + .cloned() + .unwrap(); + + // Decide whether it's toplevel (we always choose one if there are + // no toplevel choices. + let coin_flip_toplevel: usize = rng.gen(); + if (usage.toplevel.is_empty() || (coin_flip_toplevel % 3) == 0) && usage.toplevel.len() < 5 + { + // if so, copy it to the toplevel set. + usage.toplevel.insert(chosen.clone()); + } else { + // otherwise, choose a key from result, add it there. + let parent_idx: usize = rng.gen(); + let parent = usage + .bindings + .keys() + .skip(parent_idx % usage.bindings.len()) + .next() + .cloned() + .unwrap(); + if let Some(children) = usage.bindings.get_mut(&parent) { + children.push(chosen.clone()); + } + } + + // Remove the chosen var from v_start, add an empty entry to result. + v_start.remove(&chosen); + usage.bindings.insert(chosen, Vec::new()); + } + + usage +} diff --git a/src/tests/compiler/mod.rs b/src/tests/compiler/mod.rs index 2d7f5764..1c5db92c 100644 --- a/src/tests/compiler/mod.rs +++ b/src/tests/compiler/mod.rs @@ -10,6 +10,7 @@ mod clvm; mod compiler; mod evaluate; mod fuzz; +mod fuzz_assign; mod optimizer; mod preprocessor; mod repl; From d8b6d1611c0899e33a7d8e5cd8fb357f6e5b3958 Mon Sep 17 00:00:00 2001 From: arty Date: Mon, 22 Apr 2024 10:29:08 -0700 Subject: [PATCH 3/6] Add comments, test --- src/tests/compiler/fuzz_assign.rs | 49 ++++++++++++++++++++++++++++--- 1 file changed, 45 insertions(+), 4 deletions(-) diff --git a/src/tests/compiler/fuzz_assign.rs b/src/tests/compiler/fuzz_assign.rs index 0cb60ac7..218b6a3a 100644 --- a/src/tests/compiler/fuzz_assign.rs +++ b/src/tests/compiler/fuzz_assign.rs @@ -10,6 +10,7 @@ use crate::compiler::srcloc::Srcloc; use crate::tests::compiler::fuzz::{simple_seeded_rng, SupportedOperators, ValueSpecification}; +/// Make a set of numbered variables to use for fuzzing. pub fn create_variable_set(_srcloc: Srcloc, vars: usize) -> BTreeSet> { (0..vars) .map(|n| format!("v{n}").bytes().collect()) @@ -17,6 +18,13 @@ pub fn create_variable_set(_srcloc: Srcloc, vars: usize) -> BTreeSet> { } #[derive(Default, Clone)] +/// An object that records information about some variables that are defined in +/// a complex, possibly multilevel assign form. This object has the ability to +/// both generate this relationship based on randomness and also to extract +/// needed information about it. +/// +/// In particular, we can ask it what variable this variable's definition +/// contributes to in the hierarchy (find_parent_of_var), pub struct ExprVariableUsage { pub toplevel: BTreeSet>, pub bindings: BTreeMap, Vec>>, @@ -48,7 +56,9 @@ impl ExprVariableUsage { Ok(()) } - // Find the parent of this var. + /// Find the parent of this var. + /// If its definition occurs in an assign form which gives another variable + /// its value, give the name of that variable. pub fn find_parent_of_var<'a>(&'a self, var: &[u8]) -> Option<&'a Vec> { for (parent, bindings) in self.bindings.iter() { if bindings.iter().any(|c| c == var) { @@ -59,7 +69,9 @@ impl ExprVariableUsage { None } - // Find the path to this var. + /// Find the path to this var. + /// Given a variable name, return the list of parents whose values it appears + /// in. pub fn find_path_to_var<'a>(&'a self, var: &[u8]) -> Vec<&'a Vec> { let mut result = Vec::new(); let mut checking = var; @@ -70,7 +82,9 @@ impl ExprVariableUsage { result } - // Give the set of variables in scope for the definition of var. + /// Give the set of variables in scope for the definition of var. + /// This allows us, given a variable name to determine which other variables + /// could appear in an expression that gives the indicated var its value. pub fn variables_in_scope<'a>(&'a self, var: &[u8]) -> Vec<&'a Vec> { // If this variable itself use an assign form as its definition, then // all the innermost bindings are in scope. @@ -125,7 +139,12 @@ impl ExprVariableUsage { result } - // Generate an expression to define one variable. + /// Generate an expression to define one variable. + /// Given a source of randomness, a desired complexity a set of variables that + /// are bound from elsewhere (args) and a variable name, generate a candidate + /// expression which respects the available scope at the definition of var. + /// The resulting expression is given as sexp (to be compiled as chialisp) + /// and as ValueSpecification to be separately evaulated. pub fn generate_expression( &self, srcloc: &Srcloc, @@ -248,6 +267,8 @@ impl ExprVariableUsage { ) } + /// Output the assign form whose structure is defined by this object and given + /// a map of expressions to use for each variable's definition. pub fn create_assign_form( &self, srcloc: &Srcloc, @@ -394,6 +415,26 @@ fn test_expr_variable_usage() { ); let free_vars: Vec> = g1.definition.get_free_vars().into_iter().collect(); assert_eq!(free_vars, vec![b"a1".to_vec(), b"v4".to_vec()]); + + // Generate simple constant expressions to make it clear how these relate to + // the definitions that are emitted. + let expressions: BTreeMap, GeneratedExpr> = (0..=4).map(|n| { + let name = format!("v{n}").as_bytes().to_vec(); + let sexp = Rc::new(SExp::Integer(srcloc.clone(), n.to_bigint().unwrap())); + let expr = GeneratedExpr { + definition: Rc::new(ValueSpecification::ConstantValue(sexp.clone())), + sexp + }; + (name, expr) + }).collect(); + + let assign_form = structure_graph.create_assign_form( + &srcloc, + &expressions + ); + // Each variable is defined as a constant with the same number in this + // example elaboration. + assert_eq!(assign_form.to_sexp().to_string(), "(assign v0 (assign v4 (q . 4) v1 (q . 1) (q)) v2 (q . 2) v3 (q . 3) v3)"); } impl Debug for ExprVariableUsage { From ea8ca181c0158c4b9243a0ae5a67b2bb413b346f Mon Sep 17 00:00:00 2001 From: arty Date: Mon, 22 Apr 2024 10:33:01 -0700 Subject: [PATCH 4/6] Rename to complex assign --- src/tests/compiler/fuzz_assign.rs | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/tests/compiler/fuzz_assign.rs b/src/tests/compiler/fuzz_assign.rs index 218b6a3a..aac4f4f6 100644 --- a/src/tests/compiler/fuzz_assign.rs +++ b/src/tests/compiler/fuzz_assign.rs @@ -25,7 +25,7 @@ pub fn create_variable_set(_srcloc: Srcloc, vars: usize) -> BTreeSet> { /// /// In particular, we can ask it what variable this variable's definition /// contributes to in the hierarchy (find_parent_of_var), -pub struct ExprVariableUsage { +pub struct ComplexAssignExpression { pub toplevel: BTreeSet>, pub bindings: BTreeMap, Vec>>, } @@ -36,7 +36,7 @@ pub struct GeneratedExpr { sexp: Rc, } -impl ExprVariableUsage { +impl ComplexAssignExpression { fn fmtvar( &self, writer: &mut std::fmt::Formatter<'_>, @@ -315,11 +315,11 @@ impl ExprVariableUsage { } #[test] -fn test_expr_variable_usage() { +fn test_complex_assign_expression() { let srcloc = Srcloc::start("*test*"); let mut rng = simple_seeded_rng(0x02020202); let vars = create_variable_set(srcloc.clone(), 5); - let structure_graph = create_structure_from_variables(&mut rng, &vars); + let structure_graph = create_complex_assign_structure(&mut rng, &vars); assert_eq!( format!("{structure_graph:?}"), @@ -437,7 +437,7 @@ fn test_expr_variable_usage() { assert_eq!(assign_form.to_sexp().to_string(), "(assign v0 (assign v4 (q . 4) v1 (q . 1) (q)) v2 (q . 2) v3 (q . 3) v3)"); } -impl Debug for ExprVariableUsage { +impl Debug for ComplexAssignExpression { fn fmt(&self, writer: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { for t in self.toplevel.iter() { self.fmtvar(writer, 0, t)?; @@ -446,12 +446,15 @@ impl Debug for ExprVariableUsage { } } -pub fn create_structure_from_variables( +/// Create a complex assign structure and provide methods for generating +/// expressions that can be a candidate definition for it. +/// Useful for fuzzing code that relates to assign forms. +pub fn create_complex_assign_structure( rng: &mut R, v: &BTreeSet>, -) -> ExprVariableUsage { +) -> ComplexAssignExpression { let mut v_start = v.clone(); - let mut usage = ExprVariableUsage::default(); + let mut usage = ComplexAssignExpression::default(); while !v_start.is_empty() { // Choose a variable. From 7738fe377e0de518a547b070b89c951130792afd Mon Sep 17 00:00:00 2001 From: arty Date: Mon, 22 Apr 2024 10:34:30 -0700 Subject: [PATCH 5/6] Finalize names --- src/tests/compiler/fuzz_assign.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tests/compiler/fuzz_assign.rs b/src/tests/compiler/fuzz_assign.rs index aac4f4f6..d0fe5180 100644 --- a/src/tests/compiler/fuzz_assign.rs +++ b/src/tests/compiler/fuzz_assign.rs @@ -319,7 +319,7 @@ fn test_complex_assign_expression() { let srcloc = Srcloc::start("*test*"); let mut rng = simple_seeded_rng(0x02020202); let vars = create_variable_set(srcloc.clone(), 5); - let structure_graph = create_complex_assign_structure(&mut rng, &vars); + let structure_graph = create_complex_assign_expression(&mut rng, &vars); assert_eq!( format!("{structure_graph:?}"), @@ -449,7 +449,7 @@ impl Debug for ComplexAssignExpression { /// Create a complex assign structure and provide methods for generating /// expressions that can be a candidate definition for it. /// Useful for fuzzing code that relates to assign forms. -pub fn create_complex_assign_structure( +pub fn create_complex_assign_expression( rng: &mut R, v: &BTreeSet>, ) -> ComplexAssignExpression { From ec734d1622a4172f8c5a64dcb4e3365a8c87044a Mon Sep 17 00:00:00 2001 From: arty Date: Mon, 22 Apr 2024 10:37:58 -0700 Subject: [PATCH 6/6] fmt --- src/tests/compiler/fuzz.rs | 5 +++-- src/tests/compiler/fuzz_assign.rs | 32 ++++++++++++++++--------------- 2 files changed, 20 insertions(+), 17 deletions(-) diff --git a/src/tests/compiler/fuzz.rs b/src/tests/compiler/fuzz.rs index 361d8533..51a5e537 100644 --- a/src/tests/compiler/fuzz.rs +++ b/src/tests/compiler/fuzz.rs @@ -189,8 +189,9 @@ fn test_perform_compile_of_file() { &mut allocator, runner, "test.clsp", - "(mod (A) (include *standard-cl-23*) (+ A 1))" - ).expect("should compile"); + "(mod (A) (include *standard-cl-23*) (+ A 1))", + ) + .expect("should compile"); assert_eq!(result.source_opts.dialect().stepping, Some(23)); assert_eq!(result.compiled.to_string(), "(16 2 (1 . 1))"); } diff --git a/src/tests/compiler/fuzz_assign.rs b/src/tests/compiler/fuzz_assign.rs index d0fe5180..8054b905 100644 --- a/src/tests/compiler/fuzz_assign.rs +++ b/src/tests/compiler/fuzz_assign.rs @@ -24,7 +24,7 @@ pub fn create_variable_set(_srcloc: Srcloc, vars: usize) -> BTreeSet> { /// needed information about it. /// /// In particular, we can ask it what variable this variable's definition -/// contributes to in the hierarchy (find_parent_of_var), +/// contributes to in the hierarchy (find_parent_of_var), pub struct ComplexAssignExpression { pub toplevel: BTreeSet>, pub bindings: BTreeMap, Vec>>, @@ -418,23 +418,25 @@ fn test_complex_assign_expression() { // Generate simple constant expressions to make it clear how these relate to // the definitions that are emitted. - let expressions: BTreeMap, GeneratedExpr> = (0..=4).map(|n| { - let name = format!("v{n}").as_bytes().to_vec(); - let sexp = Rc::new(SExp::Integer(srcloc.clone(), n.to_bigint().unwrap())); - let expr = GeneratedExpr { - definition: Rc::new(ValueSpecification::ConstantValue(sexp.clone())), - sexp - }; - (name, expr) - }).collect(); + let expressions: BTreeMap, GeneratedExpr> = (0..=4) + .map(|n| { + let name = format!("v{n}").as_bytes().to_vec(); + let sexp = Rc::new(SExp::Integer(srcloc.clone(), n.to_bigint().unwrap())); + let expr = GeneratedExpr { + definition: Rc::new(ValueSpecification::ConstantValue(sexp.clone())), + sexp, + }; + (name, expr) + }) + .collect(); - let assign_form = structure_graph.create_assign_form( - &srcloc, - &expressions - ); + let assign_form = structure_graph.create_assign_form(&srcloc, &expressions); // Each variable is defined as a constant with the same number in this // example elaboration. - assert_eq!(assign_form.to_sexp().to_string(), "(assign v0 (assign v4 (q . 4) v1 (q . 1) (q)) v2 (q . 2) v3 (q . 3) v3)"); + assert_eq!( + assign_form.to_sexp().to_string(), + "(assign v0 (assign v4 (q . 4) v1 (q . 1) (q)) v2 (q . 2) v3 (q . 3) v3)" + ); } impl Debug for ComplexAssignExpression {