Again
This commit is contained in:
@@ -14,3 +14,4 @@ desktop = []
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
rand = "0.8"
|
rand = "0.8"
|
||||||
parking_lot = "0.12"
|
parking_lot = "0.12"
|
||||||
|
arc-swap = "1"
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ mod words;
|
|||||||
|
|
||||||
pub use types::{
|
pub use types::{
|
||||||
CcAccess, Dictionary, ExecutionTrace, Rng, SourceSpan, StepContext, Value, Variables,
|
CcAccess, Dictionary, ExecutionTrace, Rng, SourceSpan, StepContext, Value, Variables,
|
||||||
|
VariablesMap,
|
||||||
};
|
};
|
||||||
pub use vm::Forth;
|
pub use vm::Forth;
|
||||||
pub use words::{lookup_word, Word, WordCompile, WORDS};
|
pub use words::{lookup_word, Word, WordCompile, WORDS};
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
use arc_swap::ArcSwap;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use rand::rngs::StdRng;
|
use rand::rngs::StdRng;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
@@ -53,7 +54,8 @@ impl StepContext<'_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type Variables = Arc<Mutex<HashMap<String, Value>>>;
|
pub type VariablesMap = HashMap<String, Value>;
|
||||||
|
pub type Variables = Arc<ArcSwap<VariablesMap>>;
|
||||||
pub type Dictionary = Arc<Mutex<HashMap<String, Vec<Op>>>>;
|
pub type Dictionary = Arc<Mutex<HashMap<String, Vec<Op>>>>;
|
||||||
pub type Rng = Arc<Mutex<StdRng>>;
|
pub type Rng = Arc<Mutex<StdRng>>;
|
||||||
pub type Stack = Arc<Mutex<Vec<Value>>>;
|
pub type Stack = Arc<Mutex<Vec<Value>>>;
|
||||||
|
|||||||
@@ -2,12 +2,14 @@ use parking_lot::Mutex;
|
|||||||
use rand::rngs::StdRng;
|
use rand::rngs::StdRng;
|
||||||
use rand::{Rng as RngTrait, SeedableRng};
|
use rand::{Rng as RngTrait, SeedableRng};
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
use std::collections::HashMap;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use super::compiler::compile_script;
|
use super::compiler::compile_script;
|
||||||
use super::ops::Op;
|
use super::ops::Op;
|
||||||
use super::types::{
|
use super::types::{
|
||||||
CmdRegister, Dictionary, ExecutionTrace, Rng, Stack, StepContext, Value, Variables,
|
CmdRegister, Dictionary, ExecutionTrace, Rng, Stack, StepContext, Value, Variables,
|
||||||
|
VariablesMap,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct Forth {
|
pub struct Forth {
|
||||||
@@ -38,7 +40,9 @@ impl Forth {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn evaluate(&self, script: &str, ctx: &StepContext) -> Result<Vec<String>, String> {
|
pub fn evaluate(&self, script: &str, ctx: &StepContext) -> Result<Vec<String>, String> {
|
||||||
self.evaluate_impl(script, ctx, None)
|
let (outputs, var_writes) = self.evaluate_impl(script, ctx, None)?;
|
||||||
|
self.apply_var_writes(var_writes);
|
||||||
|
Ok(outputs)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn evaluate_with_trace(
|
pub fn evaluate_with_trace(
|
||||||
@@ -47,15 +51,37 @@ impl Forth {
|
|||||||
ctx: &StepContext,
|
ctx: &StepContext,
|
||||||
trace: &mut ExecutionTrace,
|
trace: &mut ExecutionTrace,
|
||||||
) -> Result<Vec<String>, String> {
|
) -> Result<Vec<String>, String> {
|
||||||
|
let (outputs, var_writes) = self.evaluate_impl(script, ctx, Some(trace))?;
|
||||||
|
self.apply_var_writes(var_writes);
|
||||||
|
Ok(outputs)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn evaluate_raw(
|
||||||
|
&self,
|
||||||
|
script: &str,
|
||||||
|
ctx: &StepContext,
|
||||||
|
trace: &mut ExecutionTrace,
|
||||||
|
) -> Result<(Vec<String>, HashMap<String, Value>), String> {
|
||||||
self.evaluate_impl(script, ctx, Some(trace))
|
self.evaluate_impl(script, ctx, Some(trace))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn apply_var_writes(&self, writes: HashMap<String, Value>) {
|
||||||
|
if writes.is_empty() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let mut new_vars = (**self.vars.load()).clone();
|
||||||
|
for (k, v) in writes {
|
||||||
|
new_vars.insert(k, v);
|
||||||
|
}
|
||||||
|
self.vars.store(Arc::new(new_vars));
|
||||||
|
}
|
||||||
|
|
||||||
fn evaluate_impl(
|
fn evaluate_impl(
|
||||||
&self,
|
&self,
|
||||||
script: &str,
|
script: &str,
|
||||||
ctx: &StepContext,
|
ctx: &StepContext,
|
||||||
trace: Option<&mut ExecutionTrace>,
|
trace: Option<&mut ExecutionTrace>,
|
||||||
) -> Result<Vec<String>, String> {
|
) -> Result<(Vec<String>, HashMap<String, Value>), String> {
|
||||||
if script.trim().is_empty() {
|
if script.trim().is_empty() {
|
||||||
return Err("empty script".into());
|
return Err("empty script".into());
|
||||||
}
|
}
|
||||||
@@ -69,14 +95,25 @@ impl Forth {
|
|||||||
ops: &[Op],
|
ops: &[Op],
|
||||||
ctx: &StepContext,
|
ctx: &StepContext,
|
||||||
trace: Option<&mut ExecutionTrace>,
|
trace: Option<&mut ExecutionTrace>,
|
||||||
) -> Result<Vec<String>, String> {
|
) -> Result<(Vec<String>, HashMap<String, Value>), String> {
|
||||||
let mut stack = self.stack.lock();
|
let mut stack = self.stack.lock();
|
||||||
let mut outputs: Vec<String> = Vec::with_capacity(8);
|
let mut outputs: Vec<String> = Vec::with_capacity(8);
|
||||||
let mut cmd = CmdRegister::new();
|
let mut cmd = CmdRegister::new();
|
||||||
|
let vars_snapshot = self.vars.load();
|
||||||
|
let mut var_writes: HashMap<String, Value> = HashMap::new();
|
||||||
|
|
||||||
self.execute_ops(ops, ctx, &mut stack, &mut outputs, &mut cmd, trace)?;
|
self.execute_ops(
|
||||||
|
ops,
|
||||||
|
ctx,
|
||||||
|
&mut stack,
|
||||||
|
&mut outputs,
|
||||||
|
&mut cmd,
|
||||||
|
trace,
|
||||||
|
&vars_snapshot,
|
||||||
|
&mut var_writes,
|
||||||
|
)?;
|
||||||
|
|
||||||
Ok(outputs)
|
Ok((outputs, var_writes))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
@@ -89,9 +126,12 @@ impl Forth {
|
|||||||
outputs: &mut Vec<String>,
|
outputs: &mut Vec<String>,
|
||||||
cmd: &mut CmdRegister,
|
cmd: &mut CmdRegister,
|
||||||
trace: Option<&mut ExecutionTrace>,
|
trace: Option<&mut ExecutionTrace>,
|
||||||
|
vars_snapshot: &VariablesMap,
|
||||||
|
var_writes: &mut HashMap<String, Value>,
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
let mut pc = 0;
|
let mut pc = 0;
|
||||||
let trace_cell = std::cell::RefCell::new(trace);
|
let trace_cell = std::cell::RefCell::new(trace);
|
||||||
|
let var_writes_cell = std::cell::RefCell::new(Some(var_writes));
|
||||||
|
|
||||||
let run_quotation = |quot: Value,
|
let run_quotation = |quot: Value,
|
||||||
stack: &mut Vec<Value>,
|
stack: &mut Vec<Value>,
|
||||||
@@ -106,6 +146,8 @@ impl Forth {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
let mut trace_opt = trace_cell.borrow_mut().take();
|
let mut trace_opt = trace_cell.borrow_mut().take();
|
||||||
|
let mut var_writes_guard = var_writes_cell.borrow_mut();
|
||||||
|
let vw = var_writes_guard.as_mut().expect("var_writes taken");
|
||||||
self.execute_ops(
|
self.execute_ops(
|
||||||
"_ops,
|
"_ops,
|
||||||
ctx,
|
ctx,
|
||||||
@@ -113,7 +155,10 @@ impl Forth {
|
|||||||
outputs,
|
outputs,
|
||||||
cmd,
|
cmd,
|
||||||
trace_opt.as_deref_mut(),
|
trace_opt.as_deref_mut(),
|
||||||
|
vars_snapshot,
|
||||||
|
vw,
|
||||||
)?;
|
)?;
|
||||||
|
drop(var_writes_guard);
|
||||||
*trace_cell.borrow_mut() = trace_opt;
|
*trace_cell.borrow_mut() = trace_opt;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -475,15 +520,25 @@ impl Forth {
|
|||||||
Op::Get => {
|
Op::Get => {
|
||||||
let name = stack.pop().ok_or("stack underflow")?;
|
let name = stack.pop().ok_or("stack underflow")?;
|
||||||
let name = name.as_str()?;
|
let name = name.as_str()?;
|
||||||
let vars = self.vars.lock();
|
let vw = var_writes_cell.borrow();
|
||||||
let val = vars.get(name).cloned().unwrap_or(Value::Int(0, None));
|
let vw_ref = vw.as_ref().expect("var_writes taken");
|
||||||
|
let val = vw_ref
|
||||||
|
.get(name)
|
||||||
|
.or_else(|| vars_snapshot.get(name))
|
||||||
|
.cloned()
|
||||||
|
.unwrap_or(Value::Int(0, None));
|
||||||
|
drop(vw);
|
||||||
stack.push(val);
|
stack.push(val);
|
||||||
}
|
}
|
||||||
Op::Set => {
|
Op::Set => {
|
||||||
let name = stack.pop().ok_or("stack underflow")?;
|
let name = stack.pop().ok_or("stack underflow")?;
|
||||||
let name = name.as_str()?.to_string();
|
let name = name.as_str()?.to_string();
|
||||||
let val = stack.pop().ok_or("stack underflow")?;
|
let val = stack.pop().ok_or("stack underflow")?;
|
||||||
self.vars.lock().insert(name, val);
|
var_writes_cell
|
||||||
|
.borrow_mut()
|
||||||
|
.as_mut()
|
||||||
|
.expect("var_writes taken")
|
||||||
|
.insert(name, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
Op::GetContext(name) => {
|
Op::GetContext(name) => {
|
||||||
@@ -710,16 +765,20 @@ impl Forth {
|
|||||||
Op::SetTempo => {
|
Op::SetTempo => {
|
||||||
let tempo = stack.pop().ok_or("stack underflow")?.as_float()?;
|
let tempo = stack.pop().ok_or("stack underflow")?.as_float()?;
|
||||||
let clamped = tempo.clamp(20.0, 300.0);
|
let clamped = tempo.clamp(20.0, 300.0);
|
||||||
self.vars
|
var_writes_cell
|
||||||
.lock()
|
.borrow_mut()
|
||||||
|
.as_mut()
|
||||||
|
.expect("var_writes taken")
|
||||||
.insert("__tempo__".to_string(), Value::Float(clamped, None));
|
.insert("__tempo__".to_string(), Value::Float(clamped, None));
|
||||||
}
|
}
|
||||||
|
|
||||||
Op::SetSpeed => {
|
Op::SetSpeed => {
|
||||||
let speed = stack.pop().ok_or("stack underflow")?.as_float()?;
|
let speed = stack.pop().ok_or("stack underflow")?.as_float()?;
|
||||||
let clamped = speed.clamp(0.125, 8.0);
|
let clamped = speed.clamp(0.125, 8.0);
|
||||||
self.vars
|
var_writes_cell
|
||||||
.lock()
|
.borrow_mut()
|
||||||
|
.as_mut()
|
||||||
|
.expect("var_writes taken")
|
||||||
.insert(ctx.speed_key.to_string(), Value::Float(clamped, None));
|
.insert(ctx.speed_key.to_string(), Value::Float(clamped, None));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -735,7 +794,11 @@ impl Forth {
|
|||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
let mut val = String::with_capacity(8);
|
let mut val = String::with_capacity(8);
|
||||||
let _ = write!(&mut val, "{bank}:{pattern}");
|
let _ = write!(&mut val, "{bank}:{pattern}");
|
||||||
self.vars.lock().insert(ctx.chain_key.to_string(), Value::Str(Arc::from(val), None));
|
var_writes_cell
|
||||||
|
.borrow_mut()
|
||||||
|
.as_mut()
|
||||||
|
.expect("var_writes taken")
|
||||||
|
.insert(ctx.chain_key.to_string(), Value::Str(Arc::from(val), None));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -849,8 +912,10 @@ impl Forth {
|
|||||||
return Err("times count must be >= 0".into());
|
return Err("times count must be >= 0".into());
|
||||||
}
|
}
|
||||||
for i in 0..count {
|
for i in 0..count {
|
||||||
self.vars
|
var_writes_cell
|
||||||
.lock()
|
.borrow_mut()
|
||||||
|
.as_mut()
|
||||||
|
.expect("var_writes taken")
|
||||||
.insert("i".to_string(), Value::Int(i, None));
|
.insert("i".to_string(), Value::Int(i, None));
|
||||||
run_quotation(quot.clone(), stack, outputs, cmd)?;
|
run_quotation(quot.clone(), stack, outputs, cmd)?;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
use arc_swap::ArcSwap;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use rand::rngs::StdRng;
|
use rand::rngs::StdRng;
|
||||||
use rand::SeedableRng;
|
use rand::SeedableRng;
|
||||||
@@ -60,7 +61,7 @@ impl Default for App {
|
|||||||
|
|
||||||
impl App {
|
impl App {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
let variables = Arc::new(Mutex::new(HashMap::new()));
|
let variables = Arc::new(ArcSwap::from_pointee(HashMap::new()));
|
||||||
let dict = Arc::new(Mutex::new(HashMap::new()));
|
let dict = Arc::new(Mutex::new(HashMap::new()));
|
||||||
let rng = Arc::new(Mutex::new(StdRng::seed_from_u64(0)));
|
let rng = Arc::new(Mutex::new(StdRng::seed_from_u64(0)));
|
||||||
let script_engine =
|
let script_engine =
|
||||||
@@ -606,7 +607,7 @@ impl App {
|
|||||||
link.set_tempo(tempo);
|
link.set_tempo(tempo);
|
||||||
|
|
||||||
self.playback.clear_queues();
|
self.playback.clear_queues();
|
||||||
self.variables.lock().clear();
|
self.variables.store(Arc::new(HashMap::new()));
|
||||||
self.dict.lock().clear();
|
self.dict.lock().clear();
|
||||||
|
|
||||||
for (bank, pattern) in playing {
|
for (bank, pattern) in playing {
|
||||||
|
|||||||
@@ -810,10 +810,10 @@ impl SequencerState {
|
|||||||
|
|
||||||
self.speed_overrides.clear();
|
self.speed_overrides.clear();
|
||||||
{
|
{
|
||||||
let vars = self.variables.lock();
|
let vars = self.variables.load();
|
||||||
for id in self.audio_state.active_patterns.keys() {
|
for id in self.audio_state.active_patterns.keys() {
|
||||||
let key = self.key_cache.speed_key(id.bank, id.pattern);
|
let key = self.key_cache.speed_key(id.bank, id.pattern);
|
||||||
if let Some(v) = vars.get(key).and_then(|v| v.as_float().ok()) {
|
if let Some(v) = vars.get(key).and_then(|v: &Value| v.as_float().ok()) {
|
||||||
self.speed_overrides.insert((id.bank, id.pattern), v);
|
self.speed_overrides.insert((id.bank, id.pattern), v);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -931,8 +931,8 @@ impl SequencerState {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut vars = self.variables.lock();
|
let vars = self.variables.load();
|
||||||
let new_tempo = vars.remove("__tempo__").and_then(|v| v.as_float().ok());
|
let new_tempo = vars.get("__tempo__").and_then(|v: &Value| v.as_float().ok());
|
||||||
|
|
||||||
let mut chain_transitions = Vec::new();
|
let mut chain_transitions = Vec::new();
|
||||||
for id in completed {
|
for id in completed {
|
||||||
@@ -942,12 +942,29 @@ impl SequencerState {
|
|||||||
chain_transitions.push((*id, target));
|
chain_transitions.push((*id, target));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
vars.remove(chain_key);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for id in stopped {
|
// Remove consumed variables (tempo and chain keys)
|
||||||
let chain_key = self.key_cache.chain_key(id.bank, id.pattern);
|
let needs_removal = new_tempo.is_some()
|
||||||
vars.remove(chain_key);
|
|| completed.iter().any(|id| {
|
||||||
|
let chain_key = self.key_cache.chain_key(id.bank, id.pattern);
|
||||||
|
vars.contains_key(chain_key)
|
||||||
|
})
|
||||||
|
|| stopped.iter().any(|id| {
|
||||||
|
let chain_key = self.key_cache.chain_key(id.bank, id.pattern);
|
||||||
|
vars.contains_key(chain_key)
|
||||||
|
});
|
||||||
|
|
||||||
|
if needs_removal {
|
||||||
|
let mut new_vars = (**vars).clone();
|
||||||
|
new_vars.remove("__tempo__");
|
||||||
|
for id in completed {
|
||||||
|
new_vars.remove(self.key_cache.chain_key(id.bank, id.pattern));
|
||||||
|
}
|
||||||
|
for id in stopped {
|
||||||
|
new_vars.remove(self.key_cache.chain_key(id.bank, id.pattern));
|
||||||
|
}
|
||||||
|
self.variables.store(Arc::new(new_vars));
|
||||||
}
|
}
|
||||||
|
|
||||||
VariableReads {
|
VariableReads {
|
||||||
@@ -1316,10 +1333,11 @@ fn parse_midi_command(cmd: &str) -> Option<(MidiCommand, Option<f64>)> {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use arc_swap::ArcSwap;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
|
|
||||||
fn make_state() -> SequencerState {
|
fn make_state() -> SequencerState {
|
||||||
let variables: Variables = Arc::new(Mutex::new(HashMap::new()));
|
let variables: Variables = Arc::new(ArcSwap::from_pointee(HashMap::new()));
|
||||||
let dict: Dictionary = Arc::new(Mutex::new(HashMap::new()));
|
let dict: Dictionary = Arc::new(Mutex::new(HashMap::new()));
|
||||||
let rng: Rng = Arc::new(Mutex::new(
|
let rng: Rng = Arc::new(Mutex::new(
|
||||||
<rand::rngs::StdRng as rand::SeedableRng>::seed_from_u64(0),
|
<rand::rngs::StdRng as rand::SeedableRng>::seed_from_u64(0),
|
||||||
@@ -1496,11 +1514,12 @@ mod tests {
|
|||||||
|
|
||||||
// Set chain variable
|
// Set chain variable
|
||||||
{
|
{
|
||||||
let mut vars = state.variables.lock();
|
let mut vars = (**state.variables.load()).clone();
|
||||||
vars.insert(
|
vars.insert(
|
||||||
"__chain_0_0__".to_string(),
|
"__chain_0_0__".to_string(),
|
||||||
Value::Str(std::sync::Arc::from("0:1"), None),
|
Value::Str(std::sync::Arc::from("0:1"), None),
|
||||||
);
|
);
|
||||||
|
state.variables.store(Arc::new(vars));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pattern 0 completes iteration AND gets stopped immediately in the same tick.
|
// Pattern 0 completes iteration AND gets stopped immediately in the same tick.
|
||||||
@@ -1745,11 +1764,12 @@ mod tests {
|
|||||||
|
|
||||||
// Set chain: 0:0 -> 0:1
|
// Set chain: 0:0 -> 0:1
|
||||||
{
|
{
|
||||||
let mut vars = state.variables.lock();
|
let mut vars = (**state.variables.load()).clone();
|
||||||
vars.insert(
|
vars.insert(
|
||||||
"__chain_0_0__".to_string(),
|
"__chain_0_0__".to_string(),
|
||||||
Value::Str(std::sync::Arc::from("0:1"), None),
|
Value::Str(std::sync::Arc::from("0:1"), None),
|
||||||
);
|
);
|
||||||
|
state.variables.store(Arc::new(vars));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pattern 0 (length 1) completes iteration at beat=1.0 AND
|
// Pattern 0 (length 1) completes iteration at beat=1.0 AND
|
||||||
@@ -1996,8 +2016,9 @@ mod tests {
|
|||||||
|
|
||||||
// Script fires at beat 1.0 (step 0). Set __tempo__ as if the script did.
|
// Script fires at beat 1.0 (step 0). Set __tempo__ as if the script did.
|
||||||
{
|
{
|
||||||
let mut vars = state.variables.lock();
|
let mut vars = (**state.variables.load()).clone();
|
||||||
vars.insert("__tempo__".to_string(), Value::Float(140.0, None));
|
vars.insert("__tempo__".to_string(), Value::Float(140.0, None));
|
||||||
|
state.variables.store(Arc::new(vars));
|
||||||
}
|
}
|
||||||
|
|
||||||
let output = state.tick(tick_at(1.0, true));
|
let output = state.tick(tick_at(1.0, true));
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
use arc_swap::ArcSwap;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use std::collections::hash_map::DefaultHasher;
|
use std::collections::hash_map::DefaultHasher;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
@@ -63,7 +64,7 @@ fn compute_stack_display(
|
|||||||
let result = if script.trim().is_empty() {
|
let result = if script.trim().is_empty() {
|
||||||
"Stack: []".to_string()
|
"Stack: []".to_string()
|
||||||
} else {
|
} else {
|
||||||
let vars = Arc::new(Mutex::new(HashMap::new()));
|
let vars = Arc::new(ArcSwap::from_pointee(HashMap::new()));
|
||||||
let dict = Arc::new(Mutex::new(HashMap::new()));
|
let dict = Arc::new(Mutex::new(HashMap::new()));
|
||||||
let rng = Arc::new(Mutex::new(StdRng::seed_from_u64(42)));
|
let rng = Arc::new(Mutex::new(StdRng::seed_from_u64(42)));
|
||||||
let forth = Forth::new(vars, dict, rng);
|
let forth = Forth::new(vars, dict, rng);
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
use arc_swap::ArcSwap;
|
||||||
use cagire::forth::{Dictionary, Forth, Rng, StepContext, Value, Variables};
|
use cagire::forth::{Dictionary, Forth, Rng, StepContext, Value, Variables};
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use rand::rngs::StdRng;
|
use rand::rngs::StdRng;
|
||||||
@@ -32,7 +33,7 @@ pub fn ctx_with(f: impl FnOnce(&mut StepContext<'static>)) -> StepContext<'stati
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_vars() -> Variables {
|
pub fn new_vars() -> Variables {
|
||||||
Arc::new(Mutex::new(HashMap::new()))
|
Arc::new(ArcSwap::from_pointee(HashMap::new()))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_dict() -> Dictionary {
|
pub fn new_dict() -> Dictionary {
|
||||||
|
|||||||
Reference in New Issue
Block a user