use rand::Rng; use rand_distr::Distribution; use crate::params::Params; use crate::ast::{ Expr, Stat, Quantifier, BaseType, GlobalBlock, Block, Literal, Declaration, DeclarationBuilder, Variable, VariableBuilder, BinaryOperator, }; pub struct AstBuilder { params: Params, ast: GlobalBlock, name_counter: u64, rng: rand::rngs::ThreadRng, rng_float: FloatGenerator, rng_int: IntGenerator, } impl AstBuilder { pub fn from(params: Params) -> Self { Self { ast: GlobalBlock::default(), name_counter: 0, rng: rand::thread_rng(), rng_float: FloatGenerator::new(¶ms), rng_int: IntGenerator::new(¶ms), params, } } pub fn generate(&mut self) { let mut p: f64; loop { p = self.rng.gen(); if p < self.params.global_flow.end_generation { break; } else { let decl = self.gen_decl(); self.ast.push(decl); } } } fn gen_decl(&mut self) -> Stat { let p: f64 = self.rng.gen(); let ps = &self.params.statements.gen_decl; let t: BaseType; let assn: Expr; if p < ps.gen_integer { t = BaseType::Int; assn = self.gen_integer(); } else { t = BaseType::Real; assn = self.gen_real(); } let v = self.gen_variable_quantified(t, Quantifier::Const); Stat::new_decl( DeclarationBuilder::default() .variable(v) .assn(Some(assn)) .build() .unwrap() ) } fn gen_variable_quantified(&mut self, t: BaseType, q: Quantifier) -> Variable { VariableBuilder::default() .type_(t) .name(self.gen_name()) .quantifier(q) .build() .unwrap() } fn gen_integer(&mut self) -> Expr { Expr::new_literal(self.gen_literal(BaseType::Int)) } fn gen_real(&mut self) -> Expr { Expr::new_literal(self.gen_literal(BaseType::Real)) } fn gen_literal(&mut self, t: BaseType) -> Literal { match t { BaseType::Int => { let i = self.rng_int.sample(&mut self.rng); Literal::Int(i) } BaseType::Real => { let f = self.rng_float.sample(&mut self.rng); Literal::Real(f) } BaseType::Never => panic!("Attempted to generate literal of type Never"), BaseType::Unset => panic!("Attempted to generate literal of type Unset"), } } fn gen_name(&mut self) -> String { let s = format!("_{}", self.name_counter); self.name_counter += 1; s } } impl ToString for AstBuilder { fn to_string(&self) -> String { let mut s = format!("// Generated by {} v{}\n", env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION"), ); s.push_str(&self.ast.to_string()); s } } /// Generates values from regions of interest for an integer /// /// These are concentrated around i32::MIN, 0, and i32::MAX, though any i32 value is possible struct IntGenerator { distro_pos: rand_distr::Normal, distro_zero: rand_distr::Normal, distro_neg: rand_distr::Normal, } impl IntGenerator { fn new(p: &Params) -> Self { Self { distro_pos: rand_distr::Normal::new(0.0, p.dev.int_gen_distro_pos_stddiv).unwrap(), distro_zero: rand_distr::Normal::new(0.0, 3.0).unwrap(), distro_neg: rand_distr::Normal::new(0.0, p.dev.int_gen_distro_neg_stddiv).unwrap(), } } } impl Distribution for IntGenerator { fn sample(&self, rng: &mut R) -> i32 { let p: f64 = rng.gen(); if p < 0.3 { i32::MAX - (self.distro_pos.sample(rng).abs() as i32) } else if p < 0.7 { self.distro_zero.sample(rng) as i32 } else { -1 * (i32::MAX - (self.distro_pos.sample(rng).abs() as i32)) } } } /// Generates values from regions of interest for a float /// /// These are concentrated around f32::MIN, 0, and f32::MAX, though any f32 value is possible struct FloatGenerator { distro_pos: rand_distr::Normal, distro_zero: rand_distr::Normal, distro_neg: rand_distr::Normal, } impl FloatGenerator { fn new(p: &Params) -> Self { Self { distro_pos: rand_distr::Normal::new(1.0, p.dev.float_gen_distro_pos_stddiv).unwrap(), distro_zero: rand_distr::Normal::new(0.0, 3.0).unwrap(), distro_neg: rand_distr::Normal::new(1.0, p.dev.float_gen_distro_neg_stddiv).unwrap(), } } } impl Distribution for FloatGenerator { fn sample(&self, rng: &mut R) -> f32 { let p: f64 = rng.gen(); if p < 0.33 { f32::MAX - self.distro_pos.sample(rng).abs() } else if p < 0.66 { self.distro_zero.sample(rng) } else { -1.0 * (f32::MAX - self.distro_neg.sample(rng).abs()) } } }