Browse Source

wrap the randomness to enable seeding

Getty Ritter 2 years ago
parent
commit
4e95ae9f16
3 changed files with 57 additions and 6 deletions
  1. 6 6
      src/interp.rs
  2. 1 0
      src/lib.rs
  3. 50 0
      src/rand.rs

+ 6 - 6
src/interp.rs

@@ -1,7 +1,7 @@
 use crate::ast::*;
+use crate::rand::*;
 
 use anyhow::{anyhow, bail, Error};
-use rand::Rng;
 use std::cell::RefCell;
 use std::collections::HashMap;
 use std::fmt;
@@ -259,7 +259,7 @@ pub struct State {
     /// top-level definitions and builtins.
     root_scope: RefCell<HashMap<Name, Thunk>>,
     /// The thread-local RNG.
-    rand: RefCell<rand::rngs::ThreadRng>,
+    rand: RefCell<Box<dyn MatzoRand>>,
     /// The instantiated parser used to parse Matzo programs
     parser: crate::grammar::StmtsParser,
 }
@@ -276,7 +276,7 @@ impl State {
     pub fn new() -> State {
         let s = State {
             root_scope: RefCell::new(HashMap::new()),
-            rand: RefCell::new(rand::thread_rng()),
+            rand: RefCell::new(Box::new(DefaultRNG::new())),
             parser: crate::grammar::StmtsParser::new(),
             ast: RefCell::new(ASTArena::new()),
         };
@@ -428,7 +428,7 @@ impl State {
             // forcing it to a value if the assignment is `fixed`.
             Stmt::LitAssn(fixed, name, strs) => {
                 if *fixed {
-                    let choice = &strs[self.rand.borrow_mut().gen_range(0..strs.len())];
+                    let choice = &strs[self.rand.borrow_mut().gen_range_usize(0, strs.len())];
                     self.root_scope.borrow_mut().insert(
                         *name,
                         Thunk::Value(Value::Lit(Literal::Str(choice.clone()))),
@@ -544,7 +544,7 @@ impl State {
                 let from = self.eval(*from, env)?.as_num()?;
                 let to = self.eval(*to, env)?.as_num()?;
                 Ok(Value::Lit(Literal::Num(
-                    self.rand.borrow_mut().gen_range(from..=to),
+                    self.rand.borrow_mut().gen_range_i64(from, to+1),
                 )))
             }
 
@@ -748,7 +748,7 @@ impl State {
     // the weights
     fn choose(&self, choices: &[Choice], env: &Env) -> Result<Value, Error> {
         let max = choices.iter().map(Choice::weight).sum();
-        let mut choice = self.rand.borrow_mut().gen_range(0..max);
+        let mut choice = self.rand.borrow_mut().gen_range_i64(0, max);
         for ch in choices {
             if choice < ch.weight() {
                 return self.eval(ch.value, env);

+ 1 - 0
src/lib.rs

@@ -4,6 +4,7 @@ extern crate lalrpop_util;
 pub mod ast;
 pub mod interp;
 pub mod lexer;
+pub mod rand;
 pub mod repl;
 
 #[cfg(test)]

+ 50 - 0
src/rand.rs

@@ -0,0 +1,50 @@
+use rand::{SeedableRng, Rng};
+
+pub trait MatzoRand {
+    fn gen_range_i64(&mut self, min: i64, max: i64) -> i64;
+    fn gen_range_usize(&mut self, min: usize, max: usize) -> usize;
+}
+
+pub struct DefaultRNG {
+    rand: rand::rngs::ThreadRng,
+}
+
+impl DefaultRNG {
+    pub fn new() -> DefaultRNG {
+        DefaultRNG {
+            rand: rand::thread_rng()
+        }
+    }
+}
+
+impl MatzoRand for DefaultRNG {
+    fn gen_range_i64(&mut self, min: i64, max: i64) -> i64 {
+        self.rand.gen_range(min..max)
+    }
+
+    fn gen_range_usize(&mut self, min: usize, max: usize) -> usize {
+        self.rand.gen_range(min..max)
+    }
+}
+
+pub struct SeededRNG {
+    rand: rand::rngs::StdRng,
+}
+
+impl SeededRNG {
+    pub fn from_seed(seed: u64) -> SeededRNG {
+        SeededRNG {
+            rand: rand::rngs::StdRng::seed_from_u64(seed)
+        }
+    }
+}
+
+impl MatzoRand for SeededRNG {
+    fn gen_range_i64(&mut self, min: i64, max: i64) -> i64 {
+        self.rand.gen_range(min..max)
+    }
+
+    fn gen_range_usize(&mut self, min: usize, max: usize) -> usize {
+        self.rand.gen_range(min..max)
+    }
+}