Fix: revert optimizations

This commit is contained in:
2026-02-23 00:51:01 +01:00
parent 057ba5b2f3
commit f6c7438886
20 changed files with 134 additions and 377 deletions

View File

@@ -29,7 +29,7 @@ fn word_with_param() {
#[test]
fn word_available_across_evaluations() {
let mut f = forth();
let f = forth();
let ctx = default_ctx();
f.evaluate(": hi 42 ;", &ctx).unwrap();
f.evaluate("hi", &ctx).unwrap();
@@ -38,7 +38,7 @@ fn word_available_across_evaluations() {
#[test]
fn word_defined_in_one_forth_available_in_same() {
let mut f = forth();
let f = forth();
let ctx = default_ctx();
f.evaluate(": ten 10 ;", &ctx).unwrap();
f.clear_stack();
@@ -104,7 +104,7 @@ fn define_word_with_sound() {
#[test]
fn define_word_with_conditional() {
let mut f = forth();
let f = forth();
let ctx = default_ctx();
f.evaluate(": maybe-double dup 5 gt if 2 * then ;", &ctx).unwrap();
f.clear_stack();
@@ -117,7 +117,7 @@ fn define_word_with_conditional() {
#[test]
fn forget_removes_word() {
let mut f = forth();
let f = forth();
let ctx = default_ctx();
f.evaluate(": double 2 * ;", &ctx).unwrap();
f.evaluate("5 double", &ctx).unwrap();
@@ -135,7 +135,7 @@ fn forget_removes_word() {
#[test]
fn forget_nonexistent_is_noop() {
let mut f = forth();
let f = forth();
let ctx = default_ctx();
f.evaluate("\"nosuchword\" forget", &ctx).unwrap();
f.evaluate("42", &ctx).unwrap();
@@ -144,7 +144,7 @@ fn forget_nonexistent_is_noop() {
#[test]
fn forget_and_redefine() {
let mut f = forth();
let f = forth();
let ctx = default_ctx();
f.evaluate(": foo 10 ;", &ctx).unwrap();
f.evaluate("foo", &ctx).unwrap();

View File

@@ -65,7 +65,7 @@ fn conditional_based_on_step() {
#[test]
fn accumulator() {
let mut f = forth();
let f = forth();
let ctx = default_ctx();
f.evaluate(r#"0 !acc"#, &ctx).unwrap();
for _ in 0..5 {

View File

@@ -55,13 +55,13 @@ pub fn forth_seeded(seed: u64) -> Forth {
}
pub fn run(script: &str) -> Forth {
let mut f = forth();
let f = forth();
f.evaluate(script, &default_ctx()).unwrap();
f
}
pub fn run_ctx(script: &str, ctx: &StepContext) -> Forth {
let mut f = forth();
let f = forth();
f.evaluate(script, ctx).unwrap();
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) {
let mut f = forth();
let f = forth();
let result = f.evaluate(script, &default_ctx());
assert!(result.is_err(), "expected error for '{}'", script);
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> {
let mut f = forth();
let f = forth();
let outputs = f.evaluate(script, &default_ctx()).unwrap();
assert_eq!(outputs.len(), count, "expected {} outputs", count);
outputs
}
pub fn run_with_trace(script: &str) -> (Forth, ExecutionTrace) {
let mut f = forth();
let f = forth();
let mut trace = ExecutionTrace::default();
f.evaluate_with_trace(script, &default_ctx(), &mut trace)
.unwrap();

View File

@@ -2,7 +2,7 @@ use super::harness::*;
#[test]
fn choose_from_stack() {
let mut f = forth();
let f = forth();
let ctx = default_ctx();
f.evaluate("1 2 3 3 choose", &ctx).unwrap();
let val = stack_int(&f);

View File

@@ -32,7 +32,7 @@ fn test_midi_cc_with_channel() {
#[test]
fn test_ccval_returns_zero_without_cc_memory() {
let mut f = forth();
let f = forth();
let ctx = default_ctx();
let outputs = f.evaluate("1 1 ccval", &ctx).unwrap();
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, 5, 74, 127); // device 0, channel 6 (0-indexed), CC 74, value 127
let mut f = forth();
let f = forth();
let ctx = StepContext {
cc_access: Some(&cc_memory as &dyn CcAccess),
..default_ctx()
@@ -316,7 +316,7 @@ fn test_midi_note_long_duration() {
#[test]
fn test_midi_note_duration_with_tempo() {
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
let ctx = ctx_with(|c| c.tempo = 60.0);
let outputs = f.evaluate("60 note m.", &ctx).unwrap();
@@ -326,7 +326,7 @@ fn test_midi_note_duration_with_tempo() {
#[test]
fn test_midi_note_duration_with_speed() {
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
let ctx = ctx_with(|c| c.speed = 2.0);
let outputs = f.evaluate("60 note m.", &ctx).unwrap();

View File

@@ -3,7 +3,7 @@ use super::harness::*;
#[test]
fn quotation_on_stack() {
// Quotation should be pushable to stack
let mut f = forth();
let f = forth();
let result = f.evaluate("{ 1 2 + }", &default_ctx());
assert!(result.is_ok());
}
@@ -79,7 +79,7 @@ fn quotation_with_emit() {
#[test]
fn quotation_skips_emit() {
// When false, . should not fire
let mut f = forth();
let f = forth();
let outputs = f
.evaluate(r#""kick" s { . } 0 ?"#, &default_ctx())
.unwrap();
@@ -107,7 +107,7 @@ fn every_with_quotation_integration() {
// { 2 distort } 2 every — on even iterations, distort is applied
for iter in 0..4 {
let ctx = ctx_with(|c| c.iter = iter);
let mut f = forth();
let f = forth();
let outputs = f
.evaluate(r#""kick" s { 2 distort } 2 every ."#, &ctx)
.unwrap();
@@ -132,7 +132,7 @@ fn every_with_quotation_integration() {
#[test]
fn bjork_with_sound() {
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
.evaluate(r#""kick" s { 2 distort } 3 8 bjork ."#, &ctx)
.unwrap();
@@ -158,7 +158,7 @@ fn when_and_unless_complementary() {
// Using iter mod + ?/!? for if-else behavior (every no longer pushes bool)
for iter in 0..4 {
let ctx = ctx_with(|c| c.iter = iter);
let mut f = forth();
let f = forth();
let outputs = f
.evaluate(
r#""kick" s { 2 distort } iter 2 mod 0 = ? { 4 distort } iter 2 mod 0 = !? ."#,

View File

@@ -3,7 +3,7 @@ use super::harness::*;
#[test]
fn ramp_at_beat_zero() {
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();
let val = stack_float(&f);
assert!((val - 0.0).abs() < 1e-9, "expected 0.0, got {}", val);
@@ -12,7 +12,7 @@ fn ramp_at_beat_zero() {
#[test]
fn ramp_linear() {
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();
let val = stack_float(&f);
assert!((val - 0.5).abs() < 1e-9, "expected 0.5, got {}", val);
@@ -21,7 +21,7 @@ fn ramp_linear() {
#[test]
fn ramp_quadratic() {
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();
let val = stack_float(&f);
assert!((val - 0.25).abs() < 1e-9, "expected 0.25, got {}", val);
@@ -30,7 +30,7 @@ fn ramp_quadratic() {
#[test]
fn ramp_sqrt() {
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();
let val = stack_float(&f);
assert!((val - 0.5).abs() < 1e-9, "expected 0.5, got {}", val);
@@ -39,7 +39,7 @@ fn ramp_sqrt() {
#[test]
fn ramp_wraps() {
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();
let val = stack_float(&f);
assert!((val - 0.5).abs() < 1e-9, "expected 0.5, got {}", val);
@@ -48,7 +48,7 @@ fn ramp_wraps() {
#[test]
fn ramp_freq_half() {
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();
let val = stack_float(&f);
assert!((val - 0.5).abs() < 1e-9, "expected 0.5, got {}", val);
@@ -62,7 +62,7 @@ fn ramp_underflow() {
#[test]
fn range_mid() {
let ctx = default_ctx();
let mut f = forth();
let f = forth();
f.evaluate("0.5 100.0 200.0 range", &ctx).unwrap();
let val = stack_float(&f);
assert!((val - 150.0).abs() < 1e-9, "expected 150.0, got {}", val);
@@ -71,7 +71,7 @@ fn range_mid() {
#[test]
fn range_at_zero() {
let ctx = default_ctx();
let mut f = forth();
let f = forth();
f.evaluate("0.0 100.0 200.0 range", &ctx).unwrap();
let val = stack_float(&f);
assert!((val - 100.0).abs() < 1e-9, "expected 100.0, got {}", val);
@@ -80,7 +80,7 @@ fn range_at_zero() {
#[test]
fn range_at_one() {
let ctx = default_ctx();
let mut f = forth();
let f = forth();
f.evaluate("1.0 100.0 200.0 range", &ctx).unwrap();
let val = stack_float(&f);
assert!((val - 200.0).abs() < 1e-9, "expected 200.0, got {}", val);
@@ -94,7 +94,7 @@ fn range_underflow() {
#[test]
fn linramp() {
let ctx = ctx_with(|c| c.beat = 0.5);
let mut f = forth();
let f = forth();
f.evaluate("1.0 linramp", &ctx).unwrap();
let val = stack_float(&f);
assert!((val - 0.5).abs() < 1e-9, "expected 0.5, got {}", val);
@@ -103,7 +103,7 @@ fn linramp() {
#[test]
fn expramp() {
let ctx = ctx_with(|c| c.beat = 0.5);
let mut f = forth();
let f = forth();
f.evaluate("1.0 expramp", &ctx).unwrap();
let val = stack_float(&f);
let expected = 0.5_f64.powf(3.0);
@@ -113,7 +113,7 @@ fn expramp() {
#[test]
fn logramp() {
let ctx = ctx_with(|c| c.beat = 0.5);
let mut f = forth();
let f = forth();
f.evaluate("1.0 logramp", &ctx).unwrap();
let val = stack_float(&f);
let expected = 0.5_f64.powf(0.3);
@@ -123,7 +123,7 @@ fn logramp() {
#[test]
fn ramp_with_range() {
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();
let val = stack_float(&f);
assert!((val - 500.0).abs() < 1e-9, "expected 500.0, got {}", val);
@@ -132,7 +132,7 @@ fn ramp_with_range() {
#[test]
fn perlin_deterministic() {
let ctx = ctx_with(|c| c.beat = 2.7);
let mut f = forth();
let f = forth();
f.evaluate("1.0 perlin", &ctx).unwrap();
let val1 = stack_float(&f);
f.evaluate("1.0 perlin", &ctx).unwrap();
@@ -144,7 +144,7 @@ fn perlin_deterministic() {
fn perlin_in_range() {
for i in 0..100 {
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();
let val = stack_float(&f);
assert!(val >= 0.0 && val <= 1.0, "perlin out of range: {}", val);
@@ -155,7 +155,7 @@ fn perlin_in_range() {
fn perlin_varies() {
let ctx1 = ctx_with(|c| c.beat = 0.5);
let ctx2 = ctx_with(|c| c.beat = 1.5);
let mut f = forth();
let f = forth();
f.evaluate("1.0 perlin", &ctx1).unwrap();
let val1 = stack_float(&f);
f.evaluate("1.0 perlin", &ctx2).unwrap();
@@ -165,7 +165,7 @@ fn perlin_varies() {
#[test]
fn perlin_smooth() {
let mut f = forth();
let f = forth();
let mut prev = 0.0;
for i in 0..100 {
let ctx = ctx_with(|c| c.beat = i as f64 * 0.01);
@@ -181,7 +181,7 @@ fn perlin_smooth() {
#[test]
fn perlin_with_range() {
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();
let val = stack_float(&f);
assert!(val >= 200.0 && val <= 800.0, "perlin+range out of bounds: {}", val);

View File

@@ -3,7 +3,7 @@ use cagire::forth::ResolvedValue;
#[test]
fn rand_in_range() {
let mut f = forth_seeded(12345);
let f = forth_seeded(12345);
f.evaluate("0 10 rand", &default_ctx()).unwrap();
let val = stack_float(&f);
assert!(val >= 0.0 && val < 10.0, "rand {} not in [0, 10)", val);
@@ -11,8 +11,8 @@ fn rand_in_range() {
#[test]
fn rand_deterministic() {
let mut f1 = forth_seeded(99);
let mut f2 = forth_seeded(99);
let f1 = forth_seeded(99);
let f2 = forth_seeded(99);
f1.evaluate("0 100 rand", &default_ctx()).unwrap();
f2.evaluate("0 100 rand", &default_ctx()).unwrap();
assert_eq!(f1.stack(), f2.stack());
@@ -20,16 +20,16 @@ fn rand_deterministic() {
#[test]
fn seed_resets() {
let mut f1 = forth_seeded(1);
let f1 = forth_seeded(1);
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();
assert_eq!(f1.stack(), f2.stack());
}
#[test]
fn coin_binary() {
let mut f = forth_seeded(42);
let f = forth_seeded(42);
f.evaluate("coin", &default_ctx()).unwrap();
let val = stack_int(&f);
assert!(val == 0 || val == 1);
@@ -51,7 +51,7 @@ fn chance_one() {
#[test]
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();
let val = stack_int(&f);
assert!(val == 10 || val == 20 || val == 30);
@@ -109,7 +109,7 @@ fn mtof_ftom_roundtrip() {
#[test]
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();
let val = stack_float(&f);
assert!(val >= 1.0 && val <= 100.0, "exprand {} not in [1, 100]", val);
@@ -117,8 +117,8 @@ fn exprand_in_range() {
#[test]
fn exprand_deterministic() {
let mut f1 = forth_seeded(99);
let mut f2 = forth_seeded(99);
let f1 = forth_seeded(99);
let f2 = forth_seeded(99);
f1.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());
@@ -126,8 +126,8 @@ fn exprand_deterministic() {
#[test]
fn exprand_swapped_args() {
let mut f1 = forth_seeded(42);
let mut f2 = forth_seeded(42);
let f1 = forth_seeded(42);
let f2 = forth_seeded(42);
f1.evaluate("1.0 100.0 exprand", &default_ctx()).unwrap();
f2.evaluate("100.0 1.0 exprand", &default_ctx()).unwrap();
assert_eq!(f1.stack(), f2.stack());
@@ -142,7 +142,7 @@ fn exprand_requires_positive() {
#[test]
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();
let val = stack_float(&f);
assert!(val >= 1.0 && val <= 100.0, "logrand {} not in [1, 100]", val);
@@ -150,8 +150,8 @@ fn logrand_in_range() {
#[test]
fn logrand_deterministic() {
let mut f1 = forth_seeded(99);
let mut f2 = forth_seeded(99);
let f1 = forth_seeded(99);
let f2 = forth_seeded(99);
f1.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());
@@ -159,8 +159,8 @@ fn logrand_deterministic() {
#[test]
fn logrand_swapped_args() {
let mut f1 = forth_seeded(42);
let mut f2 = forth_seeded(42);
let f1 = forth_seeded(42);
let f2 = forth_seeded(42);
f1.evaluate("1.0 100.0 logrand", &default_ctx()).unwrap();
f2.evaluate("100.0 1.0 logrand", &default_ctx()).unwrap();
assert_eq!(f1.stack(), f2.stack());
@@ -214,7 +214,7 @@ fn bounce_underflow() {
#[test]
fn wchoose_all_weight_one_item() {
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())
.unwrap();
assert_eq!(stack_int(&f), 20);
@@ -223,8 +223,8 @@ fn wchoose_all_weight_one_item() {
#[test]
fn wchoose_deterministic() {
let mut f1 = forth_seeded(99);
let mut f2 = forth_seeded(99);
let f1 = forth_seeded(99);
let f2 = forth_seeded(99);
f1.evaluate("60 0.6 64 0.3 67 0.1 3 wchoose", &default_ctx())
.unwrap();
f2.evaluate("60 0.6 64 0.3 67 0.1 3 wchoose", &default_ctx())
@@ -249,7 +249,7 @@ fn wchoose_negative_weight() {
#[test]
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())
.unwrap();
assert_eq!(stack_int(&f), 20);

View File

@@ -137,7 +137,7 @@ fn over2_underflow() {
#[test]
fn stack_persists() {
let mut f = forth();
let f = forth();
let ctx = default_ctx();
f.evaluate("1 2 3", &ctx).unwrap();
assert_eq!(f.stack(), vec![int(1), int(2), int(3)]);
@@ -147,7 +147,7 @@ fn stack_persists() {
#[test]
fn clear_stack() {
let mut f = forth();
let f = forth();
f.evaluate("1 2 3", &default_ctx()).unwrap();
f.clear_stack();
assert!(f.stack().is_empty());
@@ -180,7 +180,7 @@ fn rev_underflow() {
#[test]
fn shuffle_preserves_elements() {
let mut f = forth();
let f = forth();
f.evaluate("1 2 3 4 4 shuffle", &default_ctx()).unwrap();
let mut stack: Vec<i64> = f
.stack()

View File

@@ -98,7 +98,7 @@ fn dur_is_step_duration() {
fn cycle_picks_by_runs() {
for runs in 0..4 {
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();
if runs % 2 == 0 {
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() {
for iter in 0..4 {
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();
if iter % 2 == 0 {
assert_eq!(outputs.len(), 1, "iter={}: emit should be picked", iter);
@@ -126,7 +126,7 @@ fn pcycle_picks_by_iter() {
fn cycle_with_sounds() {
for runs in 0..3 {
let ctx = ctx_with(|c| c.runs = runs);
let mut f = forth();
let f = forth();
let outputs = f.evaluate(
r#"{ "kick" s . } { "hat" s . } { "snare" s . } 3 cycle"#,
&ctx
@@ -194,7 +194,7 @@ fn clear_resets_at_deltas() {
fn at_records_selected_spans() {
use cagire::forth::ExecutionTrace;
let mut f = forth();
let f = forth();
let mut trace = ExecutionTrace::default();
let script = r#"0 0.5 0.75 at "kick" s ."#;
f.evaluate_with_trace(script, &default_ctx(), &mut trace).unwrap();

View File

@@ -12,7 +12,7 @@ fn fetch_nonexistent() {
#[test]
fn persistence_across_evals() {
let mut f = forth();
let f = forth();
let ctx = default_ctx();
f.evaluate(r#"10 !counter"#, &ctx).unwrap();
f.clear_stack();
@@ -50,7 +50,7 @@ fn set_keep() {
#[test]
fn set_keep_stores() {
let mut f = forth();
let f = forth();
let ctx = default_ctx();
f.evaluate(r#"42 ,x"#, &ctx).unwrap();
f.clear_stack();
@@ -60,7 +60,7 @@ fn set_keep_stores() {
#[test]
fn set_keep_chain() {
let mut f = forth();
let f = forth();
let ctx = default_ctx();
f.evaluate(r#"10 ,a ,b"#, &ctx).unwrap();
f.clear_stack();