Fix: revert optimizations
This commit is contained in:
@@ -15,10 +15,3 @@ desktop = []
|
|||||||
rand = "0.8"
|
rand = "0.8"
|
||||||
parking_lot = "0.12"
|
parking_lot = "0.12"
|
||||||
arc-swap = "1"
|
arc-swap = "1"
|
||||||
|
|
||||||
[dev-dependencies]
|
|
||||||
criterion = { version = "0.5", features = ["html_reports"] }
|
|
||||||
|
|
||||||
[[bench]]
|
|
||||||
name = "forth_vm"
|
|
||||||
harness = false
|
|
||||||
|
|||||||
@@ -1,201 +0,0 @@
|
|||||||
use arc_swap::ArcSwap;
|
|
||||||
use cagire_forth::{Dictionary, ExecutionTrace, Forth, Rng, StepContext, Variables};
|
|
||||||
use criterion::{black_box, criterion_group, criterion_main, Criterion};
|
|
||||||
use parking_lot::Mutex;
|
|
||||||
use rand::rngs::StdRng;
|
|
||||||
use rand::SeedableRng;
|
|
||||||
use std::collections::HashMap;
|
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
fn new_vars() -> Variables {
|
|
||||||
Arc::new(ArcSwap::from_pointee(HashMap::new()))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn new_dict() -> Dictionary {
|
|
||||||
Arc::new(Mutex::new(HashMap::new()))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn new_rng() -> Rng {
|
|
||||||
Arc::new(Mutex::new(StdRng::seed_from_u64(42)))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn new_forth() -> Forth {
|
|
||||||
Forth::new(new_vars(), new_dict(), new_rng())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn ctx() -> StepContext<'static> {
|
|
||||||
StepContext {
|
|
||||||
step: 3,
|
|
||||||
beat: 7.5,
|
|
||||||
bank: 0,
|
|
||||||
pattern: 1,
|
|
||||||
tempo: 120.0,
|
|
||||||
phase: 0.75,
|
|
||||||
slot: 0,
|
|
||||||
runs: 5,
|
|
||||||
iter: 5,
|
|
||||||
speed: 1.0,
|
|
||||||
fill: false,
|
|
||||||
nudge_secs: 0.0,
|
|
||||||
cc_access: None,
|
|
||||||
speed_key: "__speed_0_1__",
|
|
||||||
mouse_x: 0.5,
|
|
||||||
mouse_y: 0.5,
|
|
||||||
mouse_down: 0.0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const SCRIPTS: &[(&str, &str)] = &[
|
|
||||||
("trivial", "60"),
|
|
||||||
("drum_hit", "\"kick\" s ."),
|
|
||||||
("synth_params", "\"sine\" s 60 note 0.5 amp 2000 lpf ."),
|
|
||||||
("cycle_notes", "{ 60 } { 64 } { 67 } 3 cycle note"),
|
|
||||||
(
|
|
||||||
"conditional",
|
|
||||||
"step 2 mod 0 = if \"kick\" s . else \"hat\" s . then",
|
|
||||||
),
|
|
||||||
("variables", "beat 4 mod \"myvar\" !"),
|
|
||||||
(
|
|
||||||
"multi_emit",
|
|
||||||
"\"kick\" s . \"hat\" s 0.7 amp . \"snare\" s 0.5 amp .",
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"complex",
|
|
||||||
"{ 60 } { 64 } { 67 } { 72 } 4 cycle note \"sine\" s 0.01 0.1 0.7 0.3 adsr 2000 lpf .",
|
|
||||||
),
|
|
||||||
];
|
|
||||||
|
|
||||||
fn bench_evaluate(c: &mut Criterion) {
|
|
||||||
let mut group = c.benchmark_group("evaluate");
|
|
||||||
let step_ctx = ctx();
|
|
||||||
|
|
||||||
for &(name, script) in SCRIPTS {
|
|
||||||
group.bench_function(name, |b| {
|
|
||||||
let mut forth = new_forth();
|
|
||||||
b.iter(|| {
|
|
||||||
let _ = black_box(forth.evaluate(black_box(script), &step_ctx));
|
|
||||||
forth.clear_stack();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
group.finish();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn bench_evaluate_with_trace(c: &mut Criterion) {
|
|
||||||
let mut group = c.benchmark_group("evaluate_with_trace");
|
|
||||||
let step_ctx = ctx();
|
|
||||||
|
|
||||||
for &(name, script) in SCRIPTS {
|
|
||||||
group.bench_function(name, |b| {
|
|
||||||
let mut forth = new_forth();
|
|
||||||
let mut trace = ExecutionTrace::default();
|
|
||||||
b.iter(|| {
|
|
||||||
trace.executed_spans.clear();
|
|
||||||
trace.selected_spans.clear();
|
|
||||||
trace.resolved.clear();
|
|
||||||
let _ = black_box(forth.evaluate_with_trace(black_box(script), &step_ctx, &mut trace));
|
|
||||||
forth.clear_stack();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
group.finish();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn bench_trace_overhead(c: &mut Criterion) {
|
|
||||||
let mut group = c.benchmark_group("trace_overhead");
|
|
||||||
let step_ctx = ctx();
|
|
||||||
let script = "{ 60 } { 64 } { 67 } { 72 } 4 cycle note \"sine\" s 0.01 0.1 0.7 0.3 adsr 2000 lpf .";
|
|
||||||
|
|
||||||
group.bench_function("no_trace", |b| {
|
|
||||||
let mut forth = new_forth();
|
|
||||||
b.iter(|| {
|
|
||||||
let _ = black_box(forth.evaluate(black_box(script), &step_ctx));
|
|
||||||
forth.clear_stack();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
group.bench_function("with_trace", |b| {
|
|
||||||
let mut forth = new_forth();
|
|
||||||
let mut trace = ExecutionTrace::default();
|
|
||||||
b.iter(|| {
|
|
||||||
trace.executed_spans.clear();
|
|
||||||
trace.selected_spans.clear();
|
|
||||||
trace.resolved.clear();
|
|
||||||
let _ = black_box(forth.evaluate_with_trace(black_box(script), &step_ctx, &mut trace));
|
|
||||||
forth.clear_stack();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
group.finish();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn bench_tick_simulation(c: &mut Criterion) {
|
|
||||||
let mut group = c.benchmark_group("tick_simulation");
|
|
||||||
|
|
||||||
let patterns: &[(&str, &str)] = &[
|
|
||||||
("drum_pattern", "\"kick\" s ."),
|
|
||||||
("melodic_pattern", "{ 60 } { 64 } { 67 } { 72 } 4 cycle note \"sine\" s 0.5 amp ."),
|
|
||||||
("generative_pattern", "1 8 rand note \"pluck\" s 0.3 0.8 rand amp ."),
|
|
||||||
("conditional_pattern", "step 4 mod 0 = if \"kick\" s . else step 2 mod 0 = if \"hat\" s 0.5 amp . then then"),
|
|
||||||
];
|
|
||||||
|
|
||||||
for &(name, script) in patterns {
|
|
||||||
group.bench_function(name, |b| {
|
|
||||||
let mut forth = new_forth();
|
|
||||||
let step_ctx = ctx();
|
|
||||||
let mut trace = ExecutionTrace::default();
|
|
||||||
b.iter(|| {
|
|
||||||
trace.executed_spans.clear();
|
|
||||||
trace.selected_spans.clear();
|
|
||||||
trace.resolved.clear();
|
|
||||||
let _ = black_box(forth.evaluate_with_trace(black_box(script), &step_ctx, &mut trace));
|
|
||||||
forth.clear_stack();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
group.finish();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn bench_throughput(c: &mut Criterion) {
|
|
||||||
let mut group = c.benchmark_group("throughput");
|
|
||||||
|
|
||||||
let medium_scripts: &[&str] = &[
|
|
||||||
"\"kick\" s .",
|
|
||||||
"\"hat\" s 0.5 amp .",
|
|
||||||
"\"snare\" s 0.8 amp 1000 lpf .",
|
|
||||||
"{ 60 } { 64 } { 67 } 3 cycle note \"sine\" s .",
|
|
||||||
"\"bass\" s 36 note 0.7 amp .",
|
|
||||||
"{ 48 } { 55 } { 60 } { 64 } 4 cycle note \"pad\" s 0.3 amp 800 lpf .",
|
|
||||||
"\"pluck\" s 72 note 0.4 amp 3000 lpf .",
|
|
||||||
"step 4 mod 0 = if \"kick\" s . then",
|
|
||||||
];
|
|
||||||
|
|
||||||
group.bench_function("8_patterns_one_step", |b| {
|
|
||||||
let mut forths: Vec<Forth> = (0..8).map(|_| new_forth()).collect();
|
|
||||||
let step_ctx = ctx();
|
|
||||||
let mut trace = ExecutionTrace::default();
|
|
||||||
b.iter(|| {
|
|
||||||
for (i, forth) in forths.iter_mut().enumerate() {
|
|
||||||
trace.executed_spans.clear();
|
|
||||||
trace.selected_spans.clear();
|
|
||||||
trace.resolved.clear();
|
|
||||||
let _ = black_box(forth.evaluate_with_trace(
|
|
||||||
black_box(medium_scripts[i]),
|
|
||||||
&step_ctx,
|
|
||||||
&mut trace,
|
|
||||||
));
|
|
||||||
forth.clear_stack();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
group.finish();
|
|
||||||
}
|
|
||||||
|
|
||||||
criterion_group!(
|
|
||||||
benches,
|
|
||||||
bench_evaluate,
|
|
||||||
bench_evaluate_with_trace,
|
|
||||||
bench_trace_overhead,
|
|
||||||
bench_tick_simulation,
|
|
||||||
bench_throughput,
|
|
||||||
);
|
|
||||||
criterion_main!(benches);
|
|
||||||
@@ -76,6 +76,7 @@ pub type VariablesMap = HashMap<String, Value>;
|
|||||||
pub type Variables = Arc<ArcSwap<VariablesMap>>;
|
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 = Mutex<Vec<Value>>;
|
||||||
pub(super) type CmdSnapshot<'a> = (Option<&'a Value>, &'a [(&'static str, Value)]);
|
pub(super) type CmdSnapshot<'a> = (Option<&'a Value>, &'a [(&'static str, Value)]);
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
//! Stack-based Forth interpreter with audio command generation.
|
//! Stack-based Forth interpreter with audio command generation.
|
||||||
|
|
||||||
|
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;
|
||||||
@@ -9,45 +10,43 @@ 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, ResolvedValue, Rng, SourceSpan, StepContext, Value,
|
CmdRegister, Dictionary, ExecutionTrace, ResolvedValue, Rng, SourceSpan, Stack, StepContext,
|
||||||
Variables, VariablesMap,
|
Value, Variables, VariablesMap,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct Forth {
|
pub struct Forth {
|
||||||
stack: Vec<Value>,
|
stack: Stack,
|
||||||
vars: Variables,
|
vars: Variables,
|
||||||
dict: Dictionary,
|
dict: Dictionary,
|
||||||
rng: Rng,
|
rng: Rng,
|
||||||
cache: HashMap<String, Arc<[Op]>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Forth {
|
impl Forth {
|
||||||
pub fn new(vars: Variables, dict: Dictionary, rng: Rng) -> Self {
|
pub fn new(vars: Variables, dict: Dictionary, rng: Rng) -> Self {
|
||||||
Self {
|
Self {
|
||||||
stack: Vec::new(),
|
stack: Mutex::new(Vec::new()),
|
||||||
vars,
|
vars,
|
||||||
dict,
|
dict,
|
||||||
rng,
|
rng,
|
||||||
cache: HashMap::new(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn stack(&self) -> Vec<Value> {
|
pub fn stack(&self) -> Vec<Value> {
|
||||||
self.stack.clone()
|
self.stack.lock().clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn clear_stack(&mut self) {
|
pub fn clear_stack(&self) {
|
||||||
self.stack.clear();
|
self.stack.lock().clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn evaluate(&mut self, script: &str, ctx: &StepContext) -> Result<Vec<String>, String> {
|
pub fn evaluate(&self, script: &str, ctx: &StepContext) -> Result<Vec<String>, String> {
|
||||||
let (outputs, var_writes) = self.evaluate_impl(script, ctx, None)?;
|
let (outputs, var_writes) = self.evaluate_impl(script, ctx, None)?;
|
||||||
self.apply_var_writes(var_writes);
|
self.apply_var_writes(var_writes);
|
||||||
Ok(outputs)
|
Ok(outputs)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn evaluate_with_trace(
|
pub fn evaluate_with_trace(
|
||||||
&mut self,
|
&self,
|
||||||
script: &str,
|
script: &str,
|
||||||
ctx: &StepContext,
|
ctx: &StepContext,
|
||||||
trace: &mut ExecutionTrace,
|
trace: &mut ExecutionTrace,
|
||||||
@@ -58,7 +57,7 @@ impl Forth {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn evaluate_raw(
|
pub fn evaluate_raw(
|
||||||
&mut self,
|
&self,
|
||||||
script: &str,
|
script: &str,
|
||||||
ctx: &StepContext,
|
ctx: &StepContext,
|
||||||
trace: &mut ExecutionTrace,
|
trace: &mut ExecutionTrace,
|
||||||
@@ -78,7 +77,7 @@ impl Forth {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn evaluate_impl(
|
fn evaluate_impl(
|
||||||
&mut self,
|
&self,
|
||||||
script: &str,
|
script: &str,
|
||||||
ctx: &StepContext,
|
ctx: &StepContext,
|
||||||
trace: Option<&mut ExecutionTrace>,
|
trace: Option<&mut ExecutionTrace>,
|
||||||
@@ -87,35 +86,26 @@ impl Forth {
|
|||||||
return Err("empty script".into());
|
return Err("empty script".into());
|
||||||
}
|
}
|
||||||
|
|
||||||
let ops = if let Some(cached) = self.cache.get(script) {
|
let ops = compile_script(script, &self.dict)?;
|
||||||
Arc::clone(cached)
|
|
||||||
} else {
|
|
||||||
let compiled = compile_script(script, &self.dict)?;
|
|
||||||
let ops: Arc<[Op]> = Arc::from(compiled);
|
|
||||||
self.cache.insert(script.to_owned(), Arc::clone(&ops));
|
|
||||||
ops
|
|
||||||
};
|
|
||||||
self.execute(&ops, ctx, trace)
|
self.execute(&ops, ctx, trace)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn execute(
|
fn execute(
|
||||||
&mut self,
|
&self,
|
||||||
ops: &[Op],
|
ops: &[Op],
|
||||||
ctx: &StepContext,
|
ctx: &StepContext,
|
||||||
trace: Option<&mut ExecutionTrace>,
|
trace: Option<&mut ExecutionTrace>,
|
||||||
) -> Result<(Vec<String>, HashMap<String, Value>), String> {
|
) -> Result<(Vec<String>, HashMap<String, Value>), String> {
|
||||||
|
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_full();
|
let vars_snapshot = self.vars.load_full();
|
||||||
let mut var_writes: Vec<(String, Value)> = Vec::new();
|
let mut var_writes: HashMap<String, Value> = HashMap::new();
|
||||||
|
|
||||||
Self::execute_ops(
|
self.execute_ops(
|
||||||
&self.rng,
|
|
||||||
&self.dict,
|
|
||||||
&mut self.cache,
|
|
||||||
ops,
|
ops,
|
||||||
ctx,
|
ctx,
|
||||||
&mut self.stack,
|
&mut stack,
|
||||||
&mut outputs,
|
&mut outputs,
|
||||||
&mut cmd,
|
&mut cmd,
|
||||||
trace,
|
trace,
|
||||||
@@ -123,15 +113,13 @@ impl Forth {
|
|||||||
&mut var_writes,
|
&mut var_writes,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let var_map: HashMap<String, Value> = var_writes.into_iter().collect();
|
Ok((outputs, var_writes))
|
||||||
Ok((outputs, var_map))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
#[allow(clippy::only_used_in_recursion)]
|
||||||
fn execute_ops(
|
fn execute_ops(
|
||||||
rng: &Rng,
|
&self,
|
||||||
dict: &Dictionary,
|
|
||||||
cache: &mut HashMap<String, Arc<[Op]>>,
|
|
||||||
ops: &[Op],
|
ops: &[Op],
|
||||||
ctx: &StepContext,
|
ctx: &StepContext,
|
||||||
stack: &mut Vec<Value>,
|
stack: &mut Vec<Value>,
|
||||||
@@ -139,14 +127,11 @@ impl Forth {
|
|||||||
cmd: &mut CmdRegister,
|
cmd: &mut CmdRegister,
|
||||||
trace: Option<&mut ExecutionTrace>,
|
trace: Option<&mut ExecutionTrace>,
|
||||||
vars_snapshot: &VariablesMap,
|
vars_snapshot: &VariablesMap,
|
||||||
var_writes: &mut Vec<(String, Value)>,
|
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 var_writes_cell = std::cell::RefCell::new(Some(var_writes));
|
||||||
let rng_ref = rng;
|
|
||||||
let dict_ref = dict;
|
|
||||||
let cache_cell = std::cell::RefCell::new(Some(cache));
|
|
||||||
|
|
||||||
let run_quotation = |quot: Value,
|
let run_quotation = |quot: Value,
|
||||||
stack: &mut Vec<Value>,
|
stack: &mut Vec<Value>,
|
||||||
@@ -163,12 +148,7 @@ 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 mut var_writes_guard = var_writes_cell.borrow_mut();
|
||||||
let vw = var_writes_guard.as_mut().expect("var_writes taken");
|
let vw = var_writes_guard.as_mut().expect("var_writes taken");
|
||||||
let mut cache_guard = cache_cell.borrow_mut();
|
self.execute_ops(
|
||||||
let c = cache_guard.as_mut().expect("cache taken");
|
|
||||||
Self::execute_ops(
|
|
||||||
rng_ref,
|
|
||||||
dict_ref,
|
|
||||||
c,
|
|
||||||
"_ops,
|
"_ops,
|
||||||
ctx,
|
ctx,
|
||||||
stack,
|
stack,
|
||||||
@@ -178,7 +158,6 @@ impl Forth {
|
|||||||
vars_snapshot,
|
vars_snapshot,
|
||||||
vw,
|
vw,
|
||||||
)?;
|
)?;
|
||||||
drop(cache_guard);
|
|
||||||
drop(var_writes_guard);
|
drop(var_writes_guard);
|
||||||
*trace_cell.borrow_mut() = trace_opt;
|
*trace_cell.borrow_mut() = trace_opt;
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -376,9 +355,9 @@ impl Forth {
|
|||||||
ensure(stack, count)?;
|
ensure(stack, count)?;
|
||||||
let start = stack.len() - count;
|
let start = stack.len() - count;
|
||||||
let slice = &mut stack[start..];
|
let slice = &mut stack[start..];
|
||||||
let mut rng_guard = rng.lock();
|
let mut rng = self.rng.lock();
|
||||||
for i in (1..slice.len()).rev() {
|
for i in (1..slice.len()).rev() {
|
||||||
let j = rng_guard.gen_range(0..=i);
|
let j = rng.gen_range(0..=i);
|
||||||
slice.swap(i, j);
|
slice.swap(i, j);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -641,10 +620,7 @@ impl Forth {
|
|||||||
let vw = var_writes_cell.borrow();
|
let vw = var_writes_cell.borrow();
|
||||||
let vw_ref = vw.as_ref().expect("var_writes taken");
|
let vw_ref = vw.as_ref().expect("var_writes taken");
|
||||||
let val = vw_ref
|
let val = vw_ref
|
||||||
.iter()
|
.get(name)
|
||||||
.rev()
|
|
||||||
.find(|(k, _)| k == name)
|
|
||||||
.map(|(_, v)| v)
|
|
||||||
.or_else(|| vars_snapshot.get(name))
|
.or_else(|| vars_snapshot.get(name))
|
||||||
.cloned()
|
.cloned()
|
||||||
.unwrap_or(Value::Int(0, None));
|
.unwrap_or(Value::Int(0, None));
|
||||||
@@ -659,7 +635,7 @@ impl Forth {
|
|||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
.as_mut()
|
.as_mut()
|
||||||
.expect("var_writes taken")
|
.expect("var_writes taken")
|
||||||
.push((name, val));
|
.insert(name, val);
|
||||||
}
|
}
|
||||||
Op::SetKeep => {
|
Op::SetKeep => {
|
||||||
let name = pop(stack)?;
|
let name = pop(stack)?;
|
||||||
@@ -669,7 +645,7 @@ impl Forth {
|
|||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
.as_mut()
|
.as_mut()
|
||||||
.expect("var_writes taken")
|
.expect("var_writes taken")
|
||||||
.push((name, val));
|
.insert(name, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
Op::GetContext(name) => {
|
Op::GetContext(name) => {
|
||||||
@@ -704,7 +680,7 @@ impl Forth {
|
|||||||
} else {
|
} else {
|
||||||
(*b_i, *a_i)
|
(*b_i, *a_i)
|
||||||
};
|
};
|
||||||
let val = rng.lock().gen_range(lo..=hi);
|
let val = self.rng.lock().gen_range(lo..=hi);
|
||||||
record_resolved(&trace_cell, *word_span, ResolvedValue::Int(val));
|
record_resolved(&trace_cell, *word_span, ResolvedValue::Int(val));
|
||||||
stack.push(Value::Int(val, None));
|
stack.push(Value::Int(val, None));
|
||||||
}
|
}
|
||||||
@@ -715,7 +691,7 @@ impl Forth {
|
|||||||
let val = if (hi - lo).abs() < f64::EPSILON {
|
let val = if (hi - lo).abs() < f64::EPSILON {
|
||||||
lo
|
lo
|
||||||
} else {
|
} else {
|
||||||
rng.lock().gen_range(lo..hi)
|
self.rng.lock().gen_range(lo..hi)
|
||||||
};
|
};
|
||||||
record_resolved(&trace_cell, *word_span, ResolvedValue::Float(val));
|
record_resolved(&trace_cell, *word_span, ResolvedValue::Float(val));
|
||||||
stack.push(Value::Float(val, None));
|
stack.push(Value::Float(val, None));
|
||||||
@@ -729,7 +705,7 @@ impl Forth {
|
|||||||
return Err("exprand requires positive values".into());
|
return Err("exprand requires positive values".into());
|
||||||
}
|
}
|
||||||
let (lo, hi) = if lo <= hi { (lo, hi) } else { (hi, lo) };
|
let (lo, hi) = if lo <= hi { (lo, hi) } else { (hi, lo) };
|
||||||
let u: f64 = rng.lock().gen();
|
let u: f64 = self.rng.lock().gen();
|
||||||
let val = lo * (hi / lo).powf(u);
|
let val = lo * (hi / lo).powf(u);
|
||||||
record_resolved(&trace_cell, *word_span, ResolvedValue::Float(val));
|
record_resolved(&trace_cell, *word_span, ResolvedValue::Float(val));
|
||||||
stack.push(Value::Float(val, None));
|
stack.push(Value::Float(val, None));
|
||||||
@@ -741,14 +717,14 @@ impl Forth {
|
|||||||
return Err("logrand requires positive values".into());
|
return Err("logrand requires positive values".into());
|
||||||
}
|
}
|
||||||
let (lo, hi) = if lo <= hi { (lo, hi) } else { (hi, lo) };
|
let (lo, hi) = if lo <= hi { (lo, hi) } else { (hi, lo) };
|
||||||
let u: f64 = rng.lock().gen();
|
let u: f64 = self.rng.lock().gen();
|
||||||
let val = hi * (lo / hi).powf(u);
|
let val = hi * (lo / hi).powf(u);
|
||||||
record_resolved(&trace_cell, *word_span, ResolvedValue::Float(val));
|
record_resolved(&trace_cell, *word_span, ResolvedValue::Float(val));
|
||||||
stack.push(Value::Float(val, None));
|
stack.push(Value::Float(val, None));
|
||||||
}
|
}
|
||||||
Op::Seed => {
|
Op::Seed => {
|
||||||
let s = pop_int(stack)?;
|
let s = pop_int(stack)?;
|
||||||
*rng.lock() = StdRng::seed_from_u64(s as u64);
|
*self.rng.lock() = StdRng::seed_from_u64(s as u64);
|
||||||
}
|
}
|
||||||
|
|
||||||
Op::Cycle(word_span) | Op::PCycle(word_span) => {
|
Op::Cycle(word_span) | Op::PCycle(word_span) => {
|
||||||
@@ -775,7 +751,7 @@ impl Forth {
|
|||||||
if count == 0 {
|
if count == 0 {
|
||||||
return Err("choose count must be > 0".into());
|
return Err("choose count must be > 0".into());
|
||||||
}
|
}
|
||||||
let idx = rng.lock().gen_range(0..count);
|
let idx = self.rng.lock().gen_range(0..count);
|
||||||
if let Some(span) = word_span {
|
if let Some(span) = word_span {
|
||||||
if stack.len() >= count {
|
if stack.len() >= count {
|
||||||
let start = stack.len() - count;
|
let start = stack.len() - count;
|
||||||
@@ -832,7 +808,7 @@ impl Forth {
|
|||||||
if total <= 0.0 {
|
if total <= 0.0 {
|
||||||
return Err("wchoose: total weight must be > 0".into());
|
return Err("wchoose: total weight must be > 0".into());
|
||||||
}
|
}
|
||||||
let threshold: f64 = rng.lock().gen::<f64>() * total;
|
let threshold: f64 = self.rng.lock().gen::<f64>() * total;
|
||||||
let mut cumulative = 0.0;
|
let mut cumulative = 0.0;
|
||||||
let mut selected_idx = count - 1;
|
let mut selected_idx = count - 1;
|
||||||
for (i, &w) in weights.iter().enumerate() {
|
for (i, &w) in weights.iter().enumerate() {
|
||||||
@@ -850,7 +826,7 @@ impl Forth {
|
|||||||
Op::ChanceExec(word_span) | Op::ProbExec(word_span) => {
|
Op::ChanceExec(word_span) | Op::ProbExec(word_span) => {
|
||||||
let threshold = pop_float(stack)?;
|
let threshold = pop_float(stack)?;
|
||||||
let quot = pop(stack)?;
|
let quot = pop(stack)?;
|
||||||
let val: f64 = rng.lock().gen();
|
let val: f64 = self.rng.lock().gen();
|
||||||
let limit = match &ops[pc] {
|
let limit = match &ops[pc] {
|
||||||
Op::ChanceExec(_) => threshold,
|
Op::ChanceExec(_) => threshold,
|
||||||
_ => threshold / 100.0,
|
_ => threshold / 100.0,
|
||||||
@@ -863,7 +839,7 @@ impl Forth {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Op::Coin(word_span) => {
|
Op::Coin(word_span) => {
|
||||||
let val: f64 = rng.lock().gen();
|
let val: f64 = self.rng.lock().gen();
|
||||||
let result = val < 0.5;
|
let result = val < 0.5;
|
||||||
record_resolved(&trace_cell, *word_span, ResolvedValue::Bool(result));
|
record_resolved(&trace_cell, *word_span, ResolvedValue::Bool(result));
|
||||||
stack.push(Value::Int(if result { 1 } else { 0 }, None));
|
stack.push(Value::Int(if result { 1 } else { 0 }, None));
|
||||||
@@ -1003,7 +979,7 @@ impl Forth {
|
|||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
.as_mut()
|
.as_mut()
|
||||||
.expect("var_writes taken")
|
.expect("var_writes taken")
|
||||||
.push(("__tempo__".to_string(), Value::Float(clamped, None)));
|
.insert("__tempo__".to_string(), Value::Float(clamped, None));
|
||||||
}
|
}
|
||||||
|
|
||||||
Op::SetSpeed => {
|
Op::SetSpeed => {
|
||||||
@@ -1013,7 +989,7 @@ impl Forth {
|
|||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
.as_mut()
|
.as_mut()
|
||||||
.expect("var_writes taken")
|
.expect("var_writes taken")
|
||||||
.push((ctx.speed_key.to_string(), Value::Float(clamped, None)));
|
.insert(ctx.speed_key.to_string(), Value::Float(clamped, None));
|
||||||
}
|
}
|
||||||
|
|
||||||
Op::Loop => {
|
Op::Loop => {
|
||||||
@@ -1175,7 +1151,7 @@ impl Forth {
|
|||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
.as_mut()
|
.as_mut()
|
||||||
.expect("var_writes taken")
|
.expect("var_writes taken")
|
||||||
.push(("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)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1411,12 +1387,7 @@ impl Forth {
|
|||||||
}
|
}
|
||||||
Op::Forget => {
|
Op::Forget => {
|
||||||
let name = pop(stack)?;
|
let name = pop(stack)?;
|
||||||
dict.lock().remove(name.as_str()?);
|
self.dict.lock().remove(name.as_str()?);
|
||||||
cache_cell
|
|
||||||
.borrow_mut()
|
|
||||||
.as_mut()
|
|
||||||
.expect("cache taken")
|
|
||||||
.clear();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pc += 1;
|
pc += 1;
|
||||||
|
|||||||
@@ -122,7 +122,7 @@ impl App {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn execute_script_oneshot(
|
pub fn execute_script_oneshot(
|
||||||
&mut self,
|
&self,
|
||||||
script: &str,
|
script: &str,
|
||||||
link: &LinkState,
|
link: &LinkState,
|
||||||
audio_tx: &arc_swap::ArcSwap<Sender<crate::engine::AudioCommand>>,
|
audio_tx: &arc_swap::ArcSwap<Sender<crate::engine::AudioCommand>>,
|
||||||
|
|||||||
@@ -872,8 +872,6 @@ impl SequencerState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut trace = ExecutionTrace::default();
|
|
||||||
|
|
||||||
for (_id, active) in self.audio_state.active_patterns.iter_mut() {
|
for (_id, active) in self.audio_state.active_patterns.iter_mut() {
|
||||||
let Some(pattern) = self.pattern_cache.get(active.bank, active.pattern) else {
|
let Some(pattern) = self.pattern_cache.get(active.bank, active.pattern) else {
|
||||||
continue;
|
continue;
|
||||||
@@ -939,9 +937,7 @@ impl SequencerState {
|
|||||||
mouse_down,
|
mouse_down,
|
||||||
};
|
};
|
||||||
if let Some(script) = resolved_script {
|
if let Some(script) = resolved_script {
|
||||||
trace.executed_spans.clear();
|
let mut trace = ExecutionTrace::default();
|
||||||
trace.selected_spans.clear();
|
|
||||||
trace.resolved.clear();
|
|
||||||
if let Ok(cmds) = self
|
if let Ok(cmds) = self
|
||||||
.script_engine
|
.script_engine
|
||||||
.evaluate_with_trace(script, &ctx, &mut trace)
|
.evaluate_with_trace(script, &ctx, &mut trace)
|
||||||
|
|||||||
@@ -147,26 +147,23 @@ pub(super) fn handle_main_page(ctx: &mut InputContext, key: KeyEvent, ctrl: bool
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
KeyCode::Char('r') if ctrl => {
|
KeyCode::Char('r') if ctrl => {
|
||||||
let script = ctx
|
let pattern = ctx.app.current_edit_pattern();
|
||||||
.app
|
if let Some(script) = pattern.resolve_script(ctx.app.editor_ctx.step) {
|
||||||
.current_edit_pattern()
|
if !script.trim().is_empty() {
|
||||||
.resolve_script(ctx.app.editor_ctx.step)
|
match ctx
|
||||||
.filter(|s| !s.trim().is_empty())
|
|
||||||
.map(|s| s.to_owned());
|
|
||||||
if let Some(script) = script {
|
|
||||||
match ctx
|
|
||||||
.app
|
|
||||||
.execute_script_oneshot(&script, ctx.link, ctx.audio_tx)
|
|
||||||
{
|
|
||||||
Ok(()) => ctx
|
|
||||||
.app
|
.app
|
||||||
.ui
|
.execute_script_oneshot(script, ctx.link, ctx.audio_tx)
|
||||||
.flash("Executed", 100, crate::state::FlashKind::Info),
|
{
|
||||||
Err(e) => ctx.app.ui.flash(
|
Ok(()) => ctx
|
||||||
&format!("Error: {e}"),
|
.app
|
||||||
200,
|
.ui
|
||||||
crate::state::FlashKind::Error,
|
.flash("Executed", 100, crate::state::FlashKind::Info),
|
||||||
),
|
Err(e) => ctx.app.ui.flash(
|
||||||
|
&format!("Error: {e}"),
|
||||||
|
200,
|
||||||
|
crate::state::FlashKind::Error,
|
||||||
|
),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,12 +11,12 @@ impl ScriptEngine {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn evaluate(&mut self, script: &str, ctx: &StepContext) -> Result<Vec<String>, String> {
|
pub fn evaluate(&self, script: &str, ctx: &StepContext) -> Result<Vec<String>, String> {
|
||||||
self.forth.evaluate(script, ctx)
|
self.forth.evaluate(script, ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn evaluate_with_trace(
|
pub fn evaluate_with_trace(
|
||||||
&mut self,
|
&self,
|
||||||
script: &str,
|
script: &str,
|
||||||
ctx: &StepContext,
|
ctx: &StepContext,
|
||||||
trace: &mut ExecutionTrace,
|
trace: &mut ExecutionTrace,
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ pub fn update_cache(editor_ctx: &EditorContext) {
|
|||||||
let vars = Arc::new(ArcSwap::from_pointee(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 mut engine = ScriptEngine::new(vars, dict, rng);
|
let engine = ScriptEngine::new(vars, dict, rng);
|
||||||
|
|
||||||
let ctx = StepContext {
|
let ctx = StepContext {
|
||||||
step: 0,
|
step: 0,
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ fn word_with_param() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn word_available_across_evaluations() {
|
fn word_available_across_evaluations() {
|
||||||
let mut f = forth();
|
let f = forth();
|
||||||
let ctx = default_ctx();
|
let ctx = default_ctx();
|
||||||
f.evaluate(": hi 42 ;", &ctx).unwrap();
|
f.evaluate(": hi 42 ;", &ctx).unwrap();
|
||||||
f.evaluate("hi", &ctx).unwrap();
|
f.evaluate("hi", &ctx).unwrap();
|
||||||
@@ -38,7 +38,7 @@ fn word_available_across_evaluations() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn word_defined_in_one_forth_available_in_same() {
|
fn word_defined_in_one_forth_available_in_same() {
|
||||||
let mut f = forth();
|
let f = forth();
|
||||||
let ctx = default_ctx();
|
let ctx = default_ctx();
|
||||||
f.evaluate(": ten 10 ;", &ctx).unwrap();
|
f.evaluate(": ten 10 ;", &ctx).unwrap();
|
||||||
f.clear_stack();
|
f.clear_stack();
|
||||||
@@ -104,7 +104,7 @@ fn define_word_with_sound() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn define_word_with_conditional() {
|
fn define_word_with_conditional() {
|
||||||
let mut f = forth();
|
let f = forth();
|
||||||
let ctx = default_ctx();
|
let ctx = default_ctx();
|
||||||
f.evaluate(": maybe-double dup 5 gt if 2 * then ;", &ctx).unwrap();
|
f.evaluate(": maybe-double dup 5 gt if 2 * then ;", &ctx).unwrap();
|
||||||
f.clear_stack();
|
f.clear_stack();
|
||||||
@@ -117,7 +117,7 @@ fn define_word_with_conditional() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn forget_removes_word() {
|
fn forget_removes_word() {
|
||||||
let mut f = forth();
|
let f = forth();
|
||||||
let ctx = default_ctx();
|
let ctx = default_ctx();
|
||||||
f.evaluate(": double 2 * ;", &ctx).unwrap();
|
f.evaluate(": double 2 * ;", &ctx).unwrap();
|
||||||
f.evaluate("5 double", &ctx).unwrap();
|
f.evaluate("5 double", &ctx).unwrap();
|
||||||
@@ -135,7 +135,7 @@ fn forget_removes_word() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn forget_nonexistent_is_noop() {
|
fn forget_nonexistent_is_noop() {
|
||||||
let mut f = forth();
|
let f = forth();
|
||||||
let ctx = default_ctx();
|
let ctx = default_ctx();
|
||||||
f.evaluate("\"nosuchword\" forget", &ctx).unwrap();
|
f.evaluate("\"nosuchword\" forget", &ctx).unwrap();
|
||||||
f.evaluate("42", &ctx).unwrap();
|
f.evaluate("42", &ctx).unwrap();
|
||||||
@@ -144,7 +144,7 @@ fn forget_nonexistent_is_noop() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn forget_and_redefine() {
|
fn forget_and_redefine() {
|
||||||
let mut f = forth();
|
let f = forth();
|
||||||
let ctx = default_ctx();
|
let ctx = default_ctx();
|
||||||
f.evaluate(": foo 10 ;", &ctx).unwrap();
|
f.evaluate(": foo 10 ;", &ctx).unwrap();
|
||||||
f.evaluate("foo", &ctx).unwrap();
|
f.evaluate("foo", &ctx).unwrap();
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ fn conditional_based_on_step() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn accumulator() {
|
fn accumulator() {
|
||||||
let mut f = forth();
|
let f = forth();
|
||||||
let ctx = default_ctx();
|
let ctx = default_ctx();
|
||||||
f.evaluate(r#"0 !acc"#, &ctx).unwrap();
|
f.evaluate(r#"0 !acc"#, &ctx).unwrap();
|
||||||
for _ in 0..5 {
|
for _ in 0..5 {
|
||||||
|
|||||||
@@ -55,13 +55,13 @@ pub fn forth_seeded(seed: u64) -> Forth {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn run(script: &str) -> Forth {
|
pub fn run(script: &str) -> Forth {
|
||||||
let mut f = forth();
|
let f = forth();
|
||||||
f.evaluate(script, &default_ctx()).unwrap();
|
f.evaluate(script, &default_ctx()).unwrap();
|
||||||
f
|
f
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run_ctx(script: &str, ctx: &StepContext) -> Forth {
|
pub fn run_ctx(script: &str, ctx: &StepContext) -> Forth {
|
||||||
let mut f = forth();
|
let f = forth();
|
||||||
f.evaluate(script, ctx).unwrap();
|
f.evaluate(script, ctx).unwrap();
|
||||||
f
|
f
|
||||||
}
|
}
|
||||||
@@ -124,7 +124,7 @@ pub fn expect_floats_close(script: &str, expected: f64, epsilon: f64) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn expect_error(script: &str, expected_substr: &str) {
|
pub fn expect_error(script: &str, expected_substr: &str) {
|
||||||
let mut f = forth();
|
let f = forth();
|
||||||
let result = f.evaluate(script, &default_ctx());
|
let result = f.evaluate(script, &default_ctx());
|
||||||
assert!(result.is_err(), "expected error for '{}'", script);
|
assert!(result.is_err(), "expected error for '{}'", script);
|
||||||
let err = result.unwrap_err();
|
let err = result.unwrap_err();
|
||||||
@@ -137,14 +137,14 @@ pub fn expect_error(script: &str, expected_substr: &str) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn expect_outputs(script: &str, count: usize) -> Vec<String> {
|
pub fn expect_outputs(script: &str, count: usize) -> Vec<String> {
|
||||||
let mut f = forth();
|
let f = forth();
|
||||||
let outputs = f.evaluate(script, &default_ctx()).unwrap();
|
let outputs = f.evaluate(script, &default_ctx()).unwrap();
|
||||||
assert_eq!(outputs.len(), count, "expected {} outputs", count);
|
assert_eq!(outputs.len(), count, "expected {} outputs", count);
|
||||||
outputs
|
outputs
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run_with_trace(script: &str) -> (Forth, ExecutionTrace) {
|
pub fn run_with_trace(script: &str) -> (Forth, ExecutionTrace) {
|
||||||
let mut f = forth();
|
let f = forth();
|
||||||
let mut trace = ExecutionTrace::default();
|
let mut trace = ExecutionTrace::default();
|
||||||
f.evaluate_with_trace(script, &default_ctx(), &mut trace)
|
f.evaluate_with_trace(script, &default_ctx(), &mut trace)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use super::harness::*;
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn choose_from_stack() {
|
fn choose_from_stack() {
|
||||||
let mut f = forth();
|
let f = forth();
|
||||||
let ctx = default_ctx();
|
let ctx = default_ctx();
|
||||||
f.evaluate("1 2 3 3 choose", &ctx).unwrap();
|
f.evaluate("1 2 3 3 choose", &ctx).unwrap();
|
||||||
let val = stack_int(&f);
|
let val = stack_int(&f);
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ fn test_midi_cc_with_channel() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_ccval_returns_zero_without_cc_memory() {
|
fn test_ccval_returns_zero_without_cc_memory() {
|
||||||
let mut f = forth();
|
let f = forth();
|
||||||
let ctx = default_ctx();
|
let ctx = default_ctx();
|
||||||
let outputs = f.evaluate("1 1 ccval", &ctx).unwrap();
|
let outputs = f.evaluate("1 1 ccval", &ctx).unwrap();
|
||||||
assert!(outputs.is_empty());
|
assert!(outputs.is_empty());
|
||||||
@@ -50,7 +50,7 @@ fn test_ccval_reads_from_cc_memory() {
|
|||||||
cc_memory.set_cc(0, 0, 1, 64); // device 0, channel 1 (0-indexed), CC 1, value 64
|
cc_memory.set_cc(0, 0, 1, 64); // device 0, channel 1 (0-indexed), CC 1, value 64
|
||||||
cc_memory.set_cc(0, 5, 74, 127); // device 0, channel 6 (0-indexed), CC 74, value 127
|
cc_memory.set_cc(0, 5, 74, 127); // device 0, channel 6 (0-indexed), CC 74, value 127
|
||||||
|
|
||||||
let mut f = forth();
|
let f = forth();
|
||||||
let ctx = StepContext {
|
let ctx = StepContext {
|
||||||
cc_access: Some(&cc_memory as &dyn CcAccess),
|
cc_access: Some(&cc_memory as &dyn CcAccess),
|
||||||
..default_ctx()
|
..default_ctx()
|
||||||
@@ -316,7 +316,7 @@ fn test_midi_note_long_duration() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_midi_note_duration_with_tempo() {
|
fn test_midi_note_duration_with_tempo() {
|
||||||
use crate::harness::{ctx_with, forth};
|
use crate::harness::{ctx_with, forth};
|
||||||
let mut f = forth();
|
let f = forth();
|
||||||
// At tempo=60, step_duration = 60/60/4/1 = 0.25 seconds
|
// At tempo=60, step_duration = 60/60/4/1 = 0.25 seconds
|
||||||
let ctx = ctx_with(|c| c.tempo = 60.0);
|
let ctx = ctx_with(|c| c.tempo = 60.0);
|
||||||
let outputs = f.evaluate("60 note m.", &ctx).unwrap();
|
let outputs = f.evaluate("60 note m.", &ctx).unwrap();
|
||||||
@@ -326,7 +326,7 @@ fn test_midi_note_duration_with_tempo() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_midi_note_duration_with_speed() {
|
fn test_midi_note_duration_with_speed() {
|
||||||
use crate::harness::{ctx_with, forth};
|
use crate::harness::{ctx_with, forth};
|
||||||
let mut f = forth();
|
let f = forth();
|
||||||
// At tempo=120 speed=2, step_duration = 60/120/4/2 = 0.0625 seconds
|
// At tempo=120 speed=2, step_duration = 60/120/4/2 = 0.0625 seconds
|
||||||
let ctx = ctx_with(|c| c.speed = 2.0);
|
let ctx = ctx_with(|c| c.speed = 2.0);
|
||||||
let outputs = f.evaluate("60 note m.", &ctx).unwrap();
|
let outputs = f.evaluate("60 note m.", &ctx).unwrap();
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ use super::harness::*;
|
|||||||
#[test]
|
#[test]
|
||||||
fn quotation_on_stack() {
|
fn quotation_on_stack() {
|
||||||
// Quotation should be pushable to stack
|
// Quotation should be pushable to stack
|
||||||
let mut f = forth();
|
let f = forth();
|
||||||
let result = f.evaluate("{ 1 2 + }", &default_ctx());
|
let result = f.evaluate("{ 1 2 + }", &default_ctx());
|
||||||
assert!(result.is_ok());
|
assert!(result.is_ok());
|
||||||
}
|
}
|
||||||
@@ -79,7 +79,7 @@ fn quotation_with_emit() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn quotation_skips_emit() {
|
fn quotation_skips_emit() {
|
||||||
// When false, . should not fire
|
// When false, . should not fire
|
||||||
let mut f = forth();
|
let f = forth();
|
||||||
let outputs = f
|
let outputs = f
|
||||||
.evaluate(r#""kick" s { . } 0 ?"#, &default_ctx())
|
.evaluate(r#""kick" s { . } 0 ?"#, &default_ctx())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@@ -107,7 +107,7 @@ fn every_with_quotation_integration() {
|
|||||||
// { 2 distort } 2 every — on even iterations, distort is applied
|
// { 2 distort } 2 every — on even iterations, distort is applied
|
||||||
for iter in 0..4 {
|
for iter in 0..4 {
|
||||||
let ctx = ctx_with(|c| c.iter = iter);
|
let ctx = ctx_with(|c| c.iter = iter);
|
||||||
let mut f = forth();
|
let f = forth();
|
||||||
let outputs = f
|
let outputs = f
|
||||||
.evaluate(r#""kick" s { 2 distort } 2 every ."#, &ctx)
|
.evaluate(r#""kick" s { 2 distort } 2 every ."#, &ctx)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@@ -132,7 +132,7 @@ fn every_with_quotation_integration() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn bjork_with_sound() {
|
fn bjork_with_sound() {
|
||||||
let ctx = ctx_with(|c| c.runs = 2); // position 2 is a hit for (3,8)
|
let ctx = ctx_with(|c| c.runs = 2); // position 2 is a hit for (3,8)
|
||||||
let mut f = forth();
|
let f = forth();
|
||||||
let outputs = f
|
let outputs = f
|
||||||
.evaluate(r#""kick" s { 2 distort } 3 8 bjork ."#, &ctx)
|
.evaluate(r#""kick" s { 2 distort } 3 8 bjork ."#, &ctx)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@@ -158,7 +158,7 @@ fn when_and_unless_complementary() {
|
|||||||
// Using iter mod + ?/!? for if-else behavior (every no longer pushes bool)
|
// Using iter mod + ?/!? for if-else behavior (every no longer pushes bool)
|
||||||
for iter in 0..4 {
|
for iter in 0..4 {
|
||||||
let ctx = ctx_with(|c| c.iter = iter);
|
let ctx = ctx_with(|c| c.iter = iter);
|
||||||
let mut f = forth();
|
let f = forth();
|
||||||
let outputs = f
|
let outputs = f
|
||||||
.evaluate(
|
.evaluate(
|
||||||
r#""kick" s { 2 distort } iter 2 mod 0 = ? { 4 distort } iter 2 mod 0 = !? ."#,
|
r#""kick" s { 2 distort } iter 2 mod 0 = ? { 4 distort } iter 2 mod 0 = !? ."#,
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ use super::harness::*;
|
|||||||
#[test]
|
#[test]
|
||||||
fn ramp_at_beat_zero() {
|
fn ramp_at_beat_zero() {
|
||||||
let ctx = ctx_with(|c| c.beat = 0.0);
|
let ctx = ctx_with(|c| c.beat = 0.0);
|
||||||
let mut f = forth();
|
let f = forth();
|
||||||
f.evaluate("1.0 2.0 ramp", &ctx).unwrap();
|
f.evaluate("1.0 2.0 ramp", &ctx).unwrap();
|
||||||
let val = stack_float(&f);
|
let val = stack_float(&f);
|
||||||
assert!((val - 0.0).abs() < 1e-9, "expected 0.0, got {}", val);
|
assert!((val - 0.0).abs() < 1e-9, "expected 0.0, got {}", val);
|
||||||
@@ -12,7 +12,7 @@ fn ramp_at_beat_zero() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn ramp_linear() {
|
fn ramp_linear() {
|
||||||
let ctx = ctx_with(|c| c.beat = 0.5);
|
let ctx = ctx_with(|c| c.beat = 0.5);
|
||||||
let mut f = forth();
|
let f = forth();
|
||||||
f.evaluate("1.0 1.0 ramp", &ctx).unwrap();
|
f.evaluate("1.0 1.0 ramp", &ctx).unwrap();
|
||||||
let val = stack_float(&f);
|
let val = stack_float(&f);
|
||||||
assert!((val - 0.5).abs() < 1e-9, "expected 0.5, got {}", val);
|
assert!((val - 0.5).abs() < 1e-9, "expected 0.5, got {}", val);
|
||||||
@@ -21,7 +21,7 @@ fn ramp_linear() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn ramp_quadratic() {
|
fn ramp_quadratic() {
|
||||||
let ctx = ctx_with(|c| c.beat = 0.5);
|
let ctx = ctx_with(|c| c.beat = 0.5);
|
||||||
let mut f = forth();
|
let f = forth();
|
||||||
f.evaluate("1.0 2.0 ramp", &ctx).unwrap();
|
f.evaluate("1.0 2.0 ramp", &ctx).unwrap();
|
||||||
let val = stack_float(&f);
|
let val = stack_float(&f);
|
||||||
assert!((val - 0.25).abs() < 1e-9, "expected 0.25, got {}", val);
|
assert!((val - 0.25).abs() < 1e-9, "expected 0.25, got {}", val);
|
||||||
@@ -30,7 +30,7 @@ fn ramp_quadratic() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn ramp_sqrt() {
|
fn ramp_sqrt() {
|
||||||
let ctx = ctx_with(|c| c.beat = 0.25);
|
let ctx = ctx_with(|c| c.beat = 0.25);
|
||||||
let mut f = forth();
|
let f = forth();
|
||||||
f.evaluate("1.0 0.5 ramp", &ctx).unwrap();
|
f.evaluate("1.0 0.5 ramp", &ctx).unwrap();
|
||||||
let val = stack_float(&f);
|
let val = stack_float(&f);
|
||||||
assert!((val - 0.5).abs() < 1e-9, "expected 0.5, got {}", val);
|
assert!((val - 0.5).abs() < 1e-9, "expected 0.5, got {}", val);
|
||||||
@@ -39,7 +39,7 @@ fn ramp_sqrt() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn ramp_wraps() {
|
fn ramp_wraps() {
|
||||||
let ctx = ctx_with(|c| c.beat = 1.5);
|
let ctx = ctx_with(|c| c.beat = 1.5);
|
||||||
let mut f = forth();
|
let f = forth();
|
||||||
f.evaluate("1.0 1.0 ramp", &ctx).unwrap();
|
f.evaluate("1.0 1.0 ramp", &ctx).unwrap();
|
||||||
let val = stack_float(&f);
|
let val = stack_float(&f);
|
||||||
assert!((val - 0.5).abs() < 1e-9, "expected 0.5, got {}", val);
|
assert!((val - 0.5).abs() < 1e-9, "expected 0.5, got {}", val);
|
||||||
@@ -48,7 +48,7 @@ fn ramp_wraps() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn ramp_freq_half() {
|
fn ramp_freq_half() {
|
||||||
let ctx = ctx_with(|c| c.beat = 1.0);
|
let ctx = ctx_with(|c| c.beat = 1.0);
|
||||||
let mut f = forth();
|
let f = forth();
|
||||||
f.evaluate("0.5 1.0 ramp", &ctx).unwrap();
|
f.evaluate("0.5 1.0 ramp", &ctx).unwrap();
|
||||||
let val = stack_float(&f);
|
let val = stack_float(&f);
|
||||||
assert!((val - 0.5).abs() < 1e-9, "expected 0.5, got {}", val);
|
assert!((val - 0.5).abs() < 1e-9, "expected 0.5, got {}", val);
|
||||||
@@ -62,7 +62,7 @@ fn ramp_underflow() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn range_mid() {
|
fn range_mid() {
|
||||||
let ctx = default_ctx();
|
let ctx = default_ctx();
|
||||||
let mut f = forth();
|
let f = forth();
|
||||||
f.evaluate("0.5 100.0 200.0 range", &ctx).unwrap();
|
f.evaluate("0.5 100.0 200.0 range", &ctx).unwrap();
|
||||||
let val = stack_float(&f);
|
let val = stack_float(&f);
|
||||||
assert!((val - 150.0).abs() < 1e-9, "expected 150.0, got {}", val);
|
assert!((val - 150.0).abs() < 1e-9, "expected 150.0, got {}", val);
|
||||||
@@ -71,7 +71,7 @@ fn range_mid() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn range_at_zero() {
|
fn range_at_zero() {
|
||||||
let ctx = default_ctx();
|
let ctx = default_ctx();
|
||||||
let mut f = forth();
|
let f = forth();
|
||||||
f.evaluate("0.0 100.0 200.0 range", &ctx).unwrap();
|
f.evaluate("0.0 100.0 200.0 range", &ctx).unwrap();
|
||||||
let val = stack_float(&f);
|
let val = stack_float(&f);
|
||||||
assert!((val - 100.0).abs() < 1e-9, "expected 100.0, got {}", val);
|
assert!((val - 100.0).abs() < 1e-9, "expected 100.0, got {}", val);
|
||||||
@@ -80,7 +80,7 @@ fn range_at_zero() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn range_at_one() {
|
fn range_at_one() {
|
||||||
let ctx = default_ctx();
|
let ctx = default_ctx();
|
||||||
let mut f = forth();
|
let f = forth();
|
||||||
f.evaluate("1.0 100.0 200.0 range", &ctx).unwrap();
|
f.evaluate("1.0 100.0 200.0 range", &ctx).unwrap();
|
||||||
let val = stack_float(&f);
|
let val = stack_float(&f);
|
||||||
assert!((val - 200.0).abs() < 1e-9, "expected 200.0, got {}", val);
|
assert!((val - 200.0).abs() < 1e-9, "expected 200.0, got {}", val);
|
||||||
@@ -94,7 +94,7 @@ fn range_underflow() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn linramp() {
|
fn linramp() {
|
||||||
let ctx = ctx_with(|c| c.beat = 0.5);
|
let ctx = ctx_with(|c| c.beat = 0.5);
|
||||||
let mut f = forth();
|
let f = forth();
|
||||||
f.evaluate("1.0 linramp", &ctx).unwrap();
|
f.evaluate("1.0 linramp", &ctx).unwrap();
|
||||||
let val = stack_float(&f);
|
let val = stack_float(&f);
|
||||||
assert!((val - 0.5).abs() < 1e-9, "expected 0.5, got {}", val);
|
assert!((val - 0.5).abs() < 1e-9, "expected 0.5, got {}", val);
|
||||||
@@ -103,7 +103,7 @@ fn linramp() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn expramp() {
|
fn expramp() {
|
||||||
let ctx = ctx_with(|c| c.beat = 0.5);
|
let ctx = ctx_with(|c| c.beat = 0.5);
|
||||||
let mut f = forth();
|
let f = forth();
|
||||||
f.evaluate("1.0 expramp", &ctx).unwrap();
|
f.evaluate("1.0 expramp", &ctx).unwrap();
|
||||||
let val = stack_float(&f);
|
let val = stack_float(&f);
|
||||||
let expected = 0.5_f64.powf(3.0);
|
let expected = 0.5_f64.powf(3.0);
|
||||||
@@ -113,7 +113,7 @@ fn expramp() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn logramp() {
|
fn logramp() {
|
||||||
let ctx = ctx_with(|c| c.beat = 0.5);
|
let ctx = ctx_with(|c| c.beat = 0.5);
|
||||||
let mut f = forth();
|
let f = forth();
|
||||||
f.evaluate("1.0 logramp", &ctx).unwrap();
|
f.evaluate("1.0 logramp", &ctx).unwrap();
|
||||||
let val = stack_float(&f);
|
let val = stack_float(&f);
|
||||||
let expected = 0.5_f64.powf(0.3);
|
let expected = 0.5_f64.powf(0.3);
|
||||||
@@ -123,7 +123,7 @@ fn logramp() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn ramp_with_range() {
|
fn ramp_with_range() {
|
||||||
let ctx = ctx_with(|c| c.beat = 0.5);
|
let ctx = ctx_with(|c| c.beat = 0.5);
|
||||||
let mut f = forth();
|
let f = forth();
|
||||||
f.evaluate("1.0 1.0 ramp 200.0 800.0 range", &ctx).unwrap();
|
f.evaluate("1.0 1.0 ramp 200.0 800.0 range", &ctx).unwrap();
|
||||||
let val = stack_float(&f);
|
let val = stack_float(&f);
|
||||||
assert!((val - 500.0).abs() < 1e-9, "expected 500.0, got {}", val);
|
assert!((val - 500.0).abs() < 1e-9, "expected 500.0, got {}", val);
|
||||||
@@ -132,7 +132,7 @@ fn ramp_with_range() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn perlin_deterministic() {
|
fn perlin_deterministic() {
|
||||||
let ctx = ctx_with(|c| c.beat = 2.7);
|
let ctx = ctx_with(|c| c.beat = 2.7);
|
||||||
let mut f = forth();
|
let f = forth();
|
||||||
f.evaluate("1.0 perlin", &ctx).unwrap();
|
f.evaluate("1.0 perlin", &ctx).unwrap();
|
||||||
let val1 = stack_float(&f);
|
let val1 = stack_float(&f);
|
||||||
f.evaluate("1.0 perlin", &ctx).unwrap();
|
f.evaluate("1.0 perlin", &ctx).unwrap();
|
||||||
@@ -144,7 +144,7 @@ fn perlin_deterministic() {
|
|||||||
fn perlin_in_range() {
|
fn perlin_in_range() {
|
||||||
for i in 0..100 {
|
for i in 0..100 {
|
||||||
let ctx = ctx_with(|c| c.beat = i as f64 * 0.1);
|
let ctx = ctx_with(|c| c.beat = i as f64 * 0.1);
|
||||||
let mut f = forth();
|
let f = forth();
|
||||||
f.evaluate("1.0 perlin", &ctx).unwrap();
|
f.evaluate("1.0 perlin", &ctx).unwrap();
|
||||||
let val = stack_float(&f);
|
let val = stack_float(&f);
|
||||||
assert!(val >= 0.0 && val <= 1.0, "perlin out of range: {}", val);
|
assert!(val >= 0.0 && val <= 1.0, "perlin out of range: {}", val);
|
||||||
@@ -155,7 +155,7 @@ fn perlin_in_range() {
|
|||||||
fn perlin_varies() {
|
fn perlin_varies() {
|
||||||
let ctx1 = ctx_with(|c| c.beat = 0.5);
|
let ctx1 = ctx_with(|c| c.beat = 0.5);
|
||||||
let ctx2 = ctx_with(|c| c.beat = 1.5);
|
let ctx2 = ctx_with(|c| c.beat = 1.5);
|
||||||
let mut f = forth();
|
let f = forth();
|
||||||
f.evaluate("1.0 perlin", &ctx1).unwrap();
|
f.evaluate("1.0 perlin", &ctx1).unwrap();
|
||||||
let val1 = stack_float(&f);
|
let val1 = stack_float(&f);
|
||||||
f.evaluate("1.0 perlin", &ctx2).unwrap();
|
f.evaluate("1.0 perlin", &ctx2).unwrap();
|
||||||
@@ -165,7 +165,7 @@ fn perlin_varies() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn perlin_smooth() {
|
fn perlin_smooth() {
|
||||||
let mut f = forth();
|
let f = forth();
|
||||||
let mut prev = 0.0;
|
let mut prev = 0.0;
|
||||||
for i in 0..100 {
|
for i in 0..100 {
|
||||||
let ctx = ctx_with(|c| c.beat = i as f64 * 0.01);
|
let ctx = ctx_with(|c| c.beat = i as f64 * 0.01);
|
||||||
@@ -181,7 +181,7 @@ fn perlin_smooth() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn perlin_with_range() {
|
fn perlin_with_range() {
|
||||||
let ctx = ctx_with(|c| c.beat = 1.3);
|
let ctx = ctx_with(|c| c.beat = 1.3);
|
||||||
let mut f = forth();
|
let f = forth();
|
||||||
f.evaluate("1.0 perlin 200.0 800.0 range", &ctx).unwrap();
|
f.evaluate("1.0 perlin 200.0 800.0 range", &ctx).unwrap();
|
||||||
let val = stack_float(&f);
|
let val = stack_float(&f);
|
||||||
assert!(val >= 200.0 && val <= 800.0, "perlin+range out of bounds: {}", val);
|
assert!(val >= 200.0 && val <= 800.0, "perlin+range out of bounds: {}", val);
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ use cagire::forth::ResolvedValue;
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn rand_in_range() {
|
fn rand_in_range() {
|
||||||
let mut f = forth_seeded(12345);
|
let f = forth_seeded(12345);
|
||||||
f.evaluate("0 10 rand", &default_ctx()).unwrap();
|
f.evaluate("0 10 rand", &default_ctx()).unwrap();
|
||||||
let val = stack_float(&f);
|
let val = stack_float(&f);
|
||||||
assert!(val >= 0.0 && val < 10.0, "rand {} not in [0, 10)", val);
|
assert!(val >= 0.0 && val < 10.0, "rand {} not in [0, 10)", val);
|
||||||
@@ -11,8 +11,8 @@ fn rand_in_range() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn rand_deterministic() {
|
fn rand_deterministic() {
|
||||||
let mut f1 = forth_seeded(99);
|
let f1 = forth_seeded(99);
|
||||||
let mut f2 = forth_seeded(99);
|
let f2 = forth_seeded(99);
|
||||||
f1.evaluate("0 100 rand", &default_ctx()).unwrap();
|
f1.evaluate("0 100 rand", &default_ctx()).unwrap();
|
||||||
f2.evaluate("0 100 rand", &default_ctx()).unwrap();
|
f2.evaluate("0 100 rand", &default_ctx()).unwrap();
|
||||||
assert_eq!(f1.stack(), f2.stack());
|
assert_eq!(f1.stack(), f2.stack());
|
||||||
@@ -20,16 +20,16 @@ fn rand_deterministic() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn seed_resets() {
|
fn seed_resets() {
|
||||||
let mut f1 = forth_seeded(1);
|
let f1 = forth_seeded(1);
|
||||||
f1.evaluate("42 seed 0 100 rand", &default_ctx()).unwrap();
|
f1.evaluate("42 seed 0 100 rand", &default_ctx()).unwrap();
|
||||||
let mut f2 = forth_seeded(999);
|
let f2 = forth_seeded(999);
|
||||||
f2.evaluate("42 seed 0 100 rand", &default_ctx()).unwrap();
|
f2.evaluate("42 seed 0 100 rand", &default_ctx()).unwrap();
|
||||||
assert_eq!(f1.stack(), f2.stack());
|
assert_eq!(f1.stack(), f2.stack());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn coin_binary() {
|
fn coin_binary() {
|
||||||
let mut f = forth_seeded(42);
|
let f = forth_seeded(42);
|
||||||
f.evaluate("coin", &default_ctx()).unwrap();
|
f.evaluate("coin", &default_ctx()).unwrap();
|
||||||
let val = stack_int(&f);
|
let val = stack_int(&f);
|
||||||
assert!(val == 0 || val == 1);
|
assert!(val == 0 || val == 1);
|
||||||
@@ -51,7 +51,7 @@ fn chance_one() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn choose_from_list() {
|
fn choose_from_list() {
|
||||||
let mut f = forth_seeded(42);
|
let f = forth_seeded(42);
|
||||||
f.evaluate("10 20 30 3 choose", &default_ctx()).unwrap();
|
f.evaluate("10 20 30 3 choose", &default_ctx()).unwrap();
|
||||||
let val = stack_int(&f);
|
let val = stack_int(&f);
|
||||||
assert!(val == 10 || val == 20 || val == 30);
|
assert!(val == 10 || val == 20 || val == 30);
|
||||||
@@ -109,7 +109,7 @@ fn mtof_ftom_roundtrip() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn exprand_in_range() {
|
fn exprand_in_range() {
|
||||||
let mut f = forth_seeded(12345);
|
let f = forth_seeded(12345);
|
||||||
f.evaluate("1.0 100.0 exprand", &default_ctx()).unwrap();
|
f.evaluate("1.0 100.0 exprand", &default_ctx()).unwrap();
|
||||||
let val = stack_float(&f);
|
let val = stack_float(&f);
|
||||||
assert!(val >= 1.0 && val <= 100.0, "exprand {} not in [1, 100]", val);
|
assert!(val >= 1.0 && val <= 100.0, "exprand {} not in [1, 100]", val);
|
||||||
@@ -117,8 +117,8 @@ fn exprand_in_range() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn exprand_deterministic() {
|
fn exprand_deterministic() {
|
||||||
let mut f1 = forth_seeded(99);
|
let f1 = forth_seeded(99);
|
||||||
let mut f2 = forth_seeded(99);
|
let f2 = forth_seeded(99);
|
||||||
f1.evaluate("1.0 100.0 exprand", &default_ctx()).unwrap();
|
f1.evaluate("1.0 100.0 exprand", &default_ctx()).unwrap();
|
||||||
f2.evaluate("1.0 100.0 exprand", &default_ctx()).unwrap();
|
f2.evaluate("1.0 100.0 exprand", &default_ctx()).unwrap();
|
||||||
assert_eq!(f1.stack(), f2.stack());
|
assert_eq!(f1.stack(), f2.stack());
|
||||||
@@ -126,8 +126,8 @@ fn exprand_deterministic() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn exprand_swapped_args() {
|
fn exprand_swapped_args() {
|
||||||
let mut f1 = forth_seeded(42);
|
let f1 = forth_seeded(42);
|
||||||
let mut f2 = forth_seeded(42);
|
let f2 = forth_seeded(42);
|
||||||
f1.evaluate("1.0 100.0 exprand", &default_ctx()).unwrap();
|
f1.evaluate("1.0 100.0 exprand", &default_ctx()).unwrap();
|
||||||
f2.evaluate("100.0 1.0 exprand", &default_ctx()).unwrap();
|
f2.evaluate("100.0 1.0 exprand", &default_ctx()).unwrap();
|
||||||
assert_eq!(f1.stack(), f2.stack());
|
assert_eq!(f1.stack(), f2.stack());
|
||||||
@@ -142,7 +142,7 @@ fn exprand_requires_positive() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn logrand_in_range() {
|
fn logrand_in_range() {
|
||||||
let mut f = forth_seeded(12345);
|
let f = forth_seeded(12345);
|
||||||
f.evaluate("1.0 100.0 logrand", &default_ctx()).unwrap();
|
f.evaluate("1.0 100.0 logrand", &default_ctx()).unwrap();
|
||||||
let val = stack_float(&f);
|
let val = stack_float(&f);
|
||||||
assert!(val >= 1.0 && val <= 100.0, "logrand {} not in [1, 100]", val);
|
assert!(val >= 1.0 && val <= 100.0, "logrand {} not in [1, 100]", val);
|
||||||
@@ -150,8 +150,8 @@ fn logrand_in_range() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn logrand_deterministic() {
|
fn logrand_deterministic() {
|
||||||
let mut f1 = forth_seeded(99);
|
let f1 = forth_seeded(99);
|
||||||
let mut f2 = forth_seeded(99);
|
let f2 = forth_seeded(99);
|
||||||
f1.evaluate("1.0 100.0 logrand", &default_ctx()).unwrap();
|
f1.evaluate("1.0 100.0 logrand", &default_ctx()).unwrap();
|
||||||
f2.evaluate("1.0 100.0 logrand", &default_ctx()).unwrap();
|
f2.evaluate("1.0 100.0 logrand", &default_ctx()).unwrap();
|
||||||
assert_eq!(f1.stack(), f2.stack());
|
assert_eq!(f1.stack(), f2.stack());
|
||||||
@@ -159,8 +159,8 @@ fn logrand_deterministic() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn logrand_swapped_args() {
|
fn logrand_swapped_args() {
|
||||||
let mut f1 = forth_seeded(42);
|
let f1 = forth_seeded(42);
|
||||||
let mut f2 = forth_seeded(42);
|
let f2 = forth_seeded(42);
|
||||||
f1.evaluate("1.0 100.0 logrand", &default_ctx()).unwrap();
|
f1.evaluate("1.0 100.0 logrand", &default_ctx()).unwrap();
|
||||||
f2.evaluate("100.0 1.0 logrand", &default_ctx()).unwrap();
|
f2.evaluate("100.0 1.0 logrand", &default_ctx()).unwrap();
|
||||||
assert_eq!(f1.stack(), f2.stack());
|
assert_eq!(f1.stack(), f2.stack());
|
||||||
@@ -214,7 +214,7 @@ fn bounce_underflow() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn wchoose_all_weight_one_item() {
|
fn wchoose_all_weight_one_item() {
|
||||||
for _ in 0..10 {
|
for _ in 0..10 {
|
||||||
let mut f = forth_seeded(42);
|
let f = forth_seeded(42);
|
||||||
f.evaluate("10 0.0 20 1.0 2 wchoose", &default_ctx())
|
f.evaluate("10 0.0 20 1.0 2 wchoose", &default_ctx())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(stack_int(&f), 20);
|
assert_eq!(stack_int(&f), 20);
|
||||||
@@ -223,8 +223,8 @@ fn wchoose_all_weight_one_item() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn wchoose_deterministic() {
|
fn wchoose_deterministic() {
|
||||||
let mut f1 = forth_seeded(99);
|
let f1 = forth_seeded(99);
|
||||||
let mut f2 = forth_seeded(99);
|
let f2 = forth_seeded(99);
|
||||||
f1.evaluate("60 0.6 64 0.3 67 0.1 3 wchoose", &default_ctx())
|
f1.evaluate("60 0.6 64 0.3 67 0.1 3 wchoose", &default_ctx())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
f2.evaluate("60 0.6 64 0.3 67 0.1 3 wchoose", &default_ctx())
|
f2.evaluate("60 0.6 64 0.3 67 0.1 3 wchoose", &default_ctx())
|
||||||
@@ -249,7 +249,7 @@ fn wchoose_negative_weight() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn wchoose_quotation() {
|
fn wchoose_quotation() {
|
||||||
let mut f = forth_seeded(42);
|
let f = forth_seeded(42);
|
||||||
f.evaluate("{ 10 } 0.0 { 20 } 1.0 2 wchoose", &default_ctx())
|
f.evaluate("{ 10 } 0.0 { 20 } 1.0 2 wchoose", &default_ctx())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(stack_int(&f), 20);
|
assert_eq!(stack_int(&f), 20);
|
||||||
|
|||||||
@@ -137,7 +137,7 @@ fn over2_underflow() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn stack_persists() {
|
fn stack_persists() {
|
||||||
let mut f = forth();
|
let f = forth();
|
||||||
let ctx = default_ctx();
|
let ctx = default_ctx();
|
||||||
f.evaluate("1 2 3", &ctx).unwrap();
|
f.evaluate("1 2 3", &ctx).unwrap();
|
||||||
assert_eq!(f.stack(), vec![int(1), int(2), int(3)]);
|
assert_eq!(f.stack(), vec![int(1), int(2), int(3)]);
|
||||||
@@ -147,7 +147,7 @@ fn stack_persists() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn clear_stack() {
|
fn clear_stack() {
|
||||||
let mut f = forth();
|
let f = forth();
|
||||||
f.evaluate("1 2 3", &default_ctx()).unwrap();
|
f.evaluate("1 2 3", &default_ctx()).unwrap();
|
||||||
f.clear_stack();
|
f.clear_stack();
|
||||||
assert!(f.stack().is_empty());
|
assert!(f.stack().is_empty());
|
||||||
@@ -180,7 +180,7 @@ fn rev_underflow() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn shuffle_preserves_elements() {
|
fn shuffle_preserves_elements() {
|
||||||
let mut f = forth();
|
let f = forth();
|
||||||
f.evaluate("1 2 3 4 4 shuffle", &default_ctx()).unwrap();
|
f.evaluate("1 2 3 4 4 shuffle", &default_ctx()).unwrap();
|
||||||
let mut stack: Vec<i64> = f
|
let mut stack: Vec<i64> = f
|
||||||
.stack()
|
.stack()
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ fn dur_is_step_duration() {
|
|||||||
fn cycle_picks_by_runs() {
|
fn cycle_picks_by_runs() {
|
||||||
for runs in 0..4 {
|
for runs in 0..4 {
|
||||||
let ctx = ctx_with(|c| c.runs = runs);
|
let ctx = ctx_with(|c| c.runs = runs);
|
||||||
let mut f = forth();
|
let f = forth();
|
||||||
let outputs = f.evaluate(r#""kick" s { . } { } 2 cycle"#, &ctx).unwrap();
|
let outputs = f.evaluate(r#""kick" s { . } { } 2 cycle"#, &ctx).unwrap();
|
||||||
if runs % 2 == 0 {
|
if runs % 2 == 0 {
|
||||||
assert_eq!(outputs.len(), 1, "runs={}: emit should be picked", runs);
|
assert_eq!(outputs.len(), 1, "runs={}: emit should be picked", runs);
|
||||||
@@ -112,7 +112,7 @@ fn cycle_picks_by_runs() {
|
|||||||
fn pcycle_picks_by_iter() {
|
fn pcycle_picks_by_iter() {
|
||||||
for iter in 0..4 {
|
for iter in 0..4 {
|
||||||
let ctx = ctx_with(|c| c.iter = iter);
|
let ctx = ctx_with(|c| c.iter = iter);
|
||||||
let mut f = forth();
|
let f = forth();
|
||||||
let outputs = f.evaluate(r#""kick" s { . } { } 2 pcycle"#, &ctx).unwrap();
|
let outputs = f.evaluate(r#""kick" s { . } { } 2 pcycle"#, &ctx).unwrap();
|
||||||
if iter % 2 == 0 {
|
if iter % 2 == 0 {
|
||||||
assert_eq!(outputs.len(), 1, "iter={}: emit should be picked", iter);
|
assert_eq!(outputs.len(), 1, "iter={}: emit should be picked", iter);
|
||||||
@@ -126,7 +126,7 @@ fn pcycle_picks_by_iter() {
|
|||||||
fn cycle_with_sounds() {
|
fn cycle_with_sounds() {
|
||||||
for runs in 0..3 {
|
for runs in 0..3 {
|
||||||
let ctx = ctx_with(|c| c.runs = runs);
|
let ctx = ctx_with(|c| c.runs = runs);
|
||||||
let mut f = forth();
|
let f = forth();
|
||||||
let outputs = f.evaluate(
|
let outputs = f.evaluate(
|
||||||
r#"{ "kick" s . } { "hat" s . } { "snare" s . } 3 cycle"#,
|
r#"{ "kick" s . } { "hat" s . } { "snare" s . } 3 cycle"#,
|
||||||
&ctx
|
&ctx
|
||||||
@@ -194,7 +194,7 @@ fn clear_resets_at_deltas() {
|
|||||||
fn at_records_selected_spans() {
|
fn at_records_selected_spans() {
|
||||||
use cagire::forth::ExecutionTrace;
|
use cagire::forth::ExecutionTrace;
|
||||||
|
|
||||||
let mut f = forth();
|
let f = forth();
|
||||||
let mut trace = ExecutionTrace::default();
|
let mut trace = ExecutionTrace::default();
|
||||||
let script = r#"0 0.5 0.75 at "kick" s ."#;
|
let script = r#"0 0.5 0.75 at "kick" s ."#;
|
||||||
f.evaluate_with_trace(script, &default_ctx(), &mut trace).unwrap();
|
f.evaluate_with_trace(script, &default_ctx(), &mut trace).unwrap();
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ fn fetch_nonexistent() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn persistence_across_evals() {
|
fn persistence_across_evals() {
|
||||||
let mut f = forth();
|
let f = forth();
|
||||||
let ctx = default_ctx();
|
let ctx = default_ctx();
|
||||||
f.evaluate(r#"10 !counter"#, &ctx).unwrap();
|
f.evaluate(r#"10 !counter"#, &ctx).unwrap();
|
||||||
f.clear_stack();
|
f.clear_stack();
|
||||||
@@ -50,7 +50,7 @@ fn set_keep() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn set_keep_stores() {
|
fn set_keep_stores() {
|
||||||
let mut f = forth();
|
let f = forth();
|
||||||
let ctx = default_ctx();
|
let ctx = default_ctx();
|
||||||
f.evaluate(r#"42 ,x"#, &ctx).unwrap();
|
f.evaluate(r#"42 ,x"#, &ctx).unwrap();
|
||||||
f.clear_stack();
|
f.clear_stack();
|
||||||
@@ -60,7 +60,7 @@ fn set_keep_stores() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn set_keep_chain() {
|
fn set_keep_chain() {
|
||||||
let mut f = forth();
|
let f = forth();
|
||||||
let ctx = default_ctx();
|
let ctx = default_ctx();
|
||||||
f.evaluate(r#"10 ,a ,b"#, &ctx).unwrap();
|
f.evaluate(r#"10 ,a ,b"#, &ctx).unwrap();
|
||||||
f.clear_stack();
|
f.clear_stack();
|
||||||
|
|||||||
Reference in New Issue
Block a user