Fixes
This commit is contained in:
@@ -190,9 +190,7 @@ impl Forth {
|
||||
outputs: &mut Vec<String>,
|
||||
cmd: &mut CmdRegister|
|
||||
-> Result<(), String> {
|
||||
if stack.len() < count {
|
||||
return Err("stack underflow".into());
|
||||
}
|
||||
ensure(stack, count)?;
|
||||
let start = stack.len() - count;
|
||||
let selected = stack[start + idx].clone();
|
||||
stack.truncate(start);
|
||||
@@ -280,106 +278,81 @@ impl Forth {
|
||||
Op::PushStr(s, span) => stack.push(Value::Str(s.clone(), *span)),
|
||||
|
||||
Op::Dup => {
|
||||
let v = stack.last().ok_or("stack underflow")?.clone();
|
||||
ensure(stack, 1)?;
|
||||
let v = stack.last().unwrap().clone();
|
||||
stack.push(v);
|
||||
}
|
||||
Op::Dupn => {
|
||||
let n = stack.pop().ok_or("stack underflow")?.as_int()?;
|
||||
let v = stack.pop().ok_or("stack underflow")?;
|
||||
let n = pop_int(stack)?;
|
||||
let v = pop(stack)?;
|
||||
for _ in 0..n {
|
||||
stack.push(v.clone());
|
||||
}
|
||||
}
|
||||
Op::Drop => {
|
||||
stack.pop().ok_or("stack underflow")?;
|
||||
pop(stack)?;
|
||||
}
|
||||
Op::Swap => {
|
||||
ensure(stack, 2)?;
|
||||
let len = stack.len();
|
||||
if len < 2 {
|
||||
return Err("stack underflow".into());
|
||||
}
|
||||
stack.swap(len - 1, len - 2);
|
||||
}
|
||||
Op::Over => {
|
||||
let len = stack.len();
|
||||
if len < 2 {
|
||||
return Err("stack underflow".into());
|
||||
}
|
||||
let v = stack[len - 2].clone();
|
||||
ensure(stack, 2)?;
|
||||
let v = stack[stack.len() - 2].clone();
|
||||
stack.push(v);
|
||||
}
|
||||
Op::Rot => {
|
||||
let len = stack.len();
|
||||
if len < 3 {
|
||||
return Err("stack underflow".into());
|
||||
}
|
||||
let v = stack.remove(len - 3);
|
||||
ensure(stack, 3)?;
|
||||
let v = stack.remove(stack.len() - 3);
|
||||
stack.push(v);
|
||||
}
|
||||
Op::Nip => {
|
||||
let len = stack.len();
|
||||
if len < 2 {
|
||||
return Err("stack underflow".into());
|
||||
}
|
||||
stack.remove(len - 2);
|
||||
ensure(stack, 2)?;
|
||||
stack.remove(stack.len() - 2);
|
||||
}
|
||||
Op::Tuck => {
|
||||
ensure(stack, 2)?;
|
||||
let len = stack.len();
|
||||
if len < 2 {
|
||||
return Err("stack underflow".into());
|
||||
}
|
||||
let v = stack[len - 1].clone();
|
||||
stack.insert(len - 2, v);
|
||||
}
|
||||
Op::Dup2 => {
|
||||
ensure(stack, 2)?;
|
||||
let len = stack.len();
|
||||
if len < 2 {
|
||||
return Err("stack underflow".into());
|
||||
}
|
||||
let a = stack[len - 2].clone();
|
||||
let b = stack[len - 1].clone();
|
||||
stack.push(a);
|
||||
stack.push(b);
|
||||
}
|
||||
Op::Drop2 => {
|
||||
let len = stack.len();
|
||||
if len < 2 {
|
||||
return Err("stack underflow".into());
|
||||
}
|
||||
ensure(stack, 2)?;
|
||||
stack.pop();
|
||||
stack.pop();
|
||||
}
|
||||
Op::Swap2 => {
|
||||
ensure(stack, 4)?;
|
||||
let len = stack.len();
|
||||
if len < 4 {
|
||||
return Err("stack underflow".into());
|
||||
}
|
||||
stack.swap(len - 4, len - 2);
|
||||
stack.swap(len - 3, len - 1);
|
||||
}
|
||||
Op::Over2 => {
|
||||
ensure(stack, 4)?;
|
||||
let len = stack.len();
|
||||
if len < 4 {
|
||||
return Err("stack underflow".into());
|
||||
}
|
||||
let a = stack[len - 4].clone();
|
||||
let b = stack[len - 3].clone();
|
||||
stack.push(a);
|
||||
stack.push(b);
|
||||
}
|
||||
Op::Rev => {
|
||||
let count = stack.pop().ok_or("stack underflow")?.as_int()? as usize;
|
||||
if count > stack.len() {
|
||||
return Err("stack underflow".into());
|
||||
}
|
||||
let count = pop_int(stack)? as usize;
|
||||
ensure(stack, count)?;
|
||||
let start = stack.len() - count;
|
||||
stack[start..].reverse();
|
||||
}
|
||||
Op::Shuffle => {
|
||||
let count = stack.pop().ok_or("stack underflow")?.as_int()? as usize;
|
||||
if count > stack.len() {
|
||||
return Err("stack underflow".into());
|
||||
}
|
||||
let count = pop_int(stack)? as usize;
|
||||
ensure(stack, count)?;
|
||||
let start = stack.len() - count;
|
||||
let slice = &mut stack[start..];
|
||||
let mut rng = self.rng.lock();
|
||||
@@ -389,10 +362,8 @@ impl Forth {
|
||||
}
|
||||
}
|
||||
Op::Sort => {
|
||||
let count = stack.pop().ok_or("stack underflow")?.as_int()? as usize;
|
||||
if count > stack.len() {
|
||||
return Err("stack underflow".into());
|
||||
}
|
||||
let count = pop_int(stack)? as usize;
|
||||
ensure(stack, count)?;
|
||||
let start = stack.len() - count;
|
||||
stack[start..].sort_by(|a, b| {
|
||||
a.as_float()
|
||||
@@ -402,10 +373,8 @@ impl Forth {
|
||||
});
|
||||
}
|
||||
Op::RSort => {
|
||||
let count = stack.pop().ok_or("stack underflow")?.as_int()? as usize;
|
||||
if count > stack.len() {
|
||||
return Err("stack underflow".into());
|
||||
}
|
||||
let count = pop_int(stack)? as usize;
|
||||
ensure(stack, count)?;
|
||||
let start = stack.len() - count;
|
||||
stack[start..].sort_by(|a, b| {
|
||||
b.as_float()
|
||||
@@ -415,10 +384,8 @@ impl Forth {
|
||||
});
|
||||
}
|
||||
Op::Sum => {
|
||||
let count = stack.pop().ok_or("stack underflow")?.as_int()? as usize;
|
||||
if count > stack.len() {
|
||||
return Err("stack underflow".into());
|
||||
}
|
||||
let count = pop_int(stack)? as usize;
|
||||
ensure(stack, count)?;
|
||||
let start = stack.len() - count;
|
||||
let total: f64 = stack
|
||||
.drain(start..)
|
||||
@@ -427,10 +394,8 @@ impl Forth {
|
||||
stack.push(float_to_value(total));
|
||||
}
|
||||
Op::Prod => {
|
||||
let count = stack.pop().ok_or("stack underflow")?.as_int()? as usize;
|
||||
if count > stack.len() {
|
||||
return Err("stack underflow".into());
|
||||
}
|
||||
let count = pop_int(stack)? as usize;
|
||||
ensure(stack, count)?;
|
||||
let start = stack.len() - count;
|
||||
let product: f64 = stack
|
||||
.drain(start..)
|
||||
@@ -443,16 +408,16 @@ impl Forth {
|
||||
Op::Sub => binary_op(stack, |a, b| a - b)?,
|
||||
Op::Mul => binary_op(stack, |a, b| a * b)?,
|
||||
Op::Div => {
|
||||
let b = stack.pop().ok_or("stack underflow")?;
|
||||
let a = stack.pop().ok_or("stack underflow")?;
|
||||
let b = pop(stack)?;
|
||||
let a = pop(stack)?;
|
||||
if b.as_float().map_or(true, |v| v == 0.0) {
|
||||
return Err("division by zero".into());
|
||||
}
|
||||
stack.push(lift_binary(a, b, |x, y| x / y)?);
|
||||
}
|
||||
Op::Mod => {
|
||||
let b = stack.pop().ok_or("stack underflow")?;
|
||||
let a = stack.pop().ok_or("stack underflow")?;
|
||||
let b = pop(stack)?;
|
||||
let a = pop(stack)?;
|
||||
if b.as_float().map_or(true, |v| v == 0.0) {
|
||||
return Err("modulo by zero".into());
|
||||
}
|
||||
@@ -460,42 +425,42 @@ impl Forth {
|
||||
stack.push(result);
|
||||
}
|
||||
Op::Neg => {
|
||||
let v = stack.pop().ok_or("stack underflow")?;
|
||||
let v = pop(stack)?;
|
||||
stack.push(lift_unary(v, |x| -x)?);
|
||||
}
|
||||
Op::Abs => {
|
||||
let v = stack.pop().ok_or("stack underflow")?;
|
||||
let v = pop(stack)?;
|
||||
stack.push(lift_unary(v, |x| x.abs())?);
|
||||
}
|
||||
Op::Floor => {
|
||||
let v = stack.pop().ok_or("stack underflow")?;
|
||||
let v = pop(stack)?;
|
||||
stack.push(lift_unary(v, |x| x.floor())?);
|
||||
}
|
||||
Op::Ceil => {
|
||||
let v = stack.pop().ok_or("stack underflow")?;
|
||||
let v = pop(stack)?;
|
||||
stack.push(lift_unary(v, |x| x.ceil())?);
|
||||
}
|
||||
Op::Round => {
|
||||
let v = stack.pop().ok_or("stack underflow")?;
|
||||
let v = pop(stack)?;
|
||||
stack.push(lift_unary(v, |x| x.round())?);
|
||||
}
|
||||
Op::Min => binary_op(stack, |a, b| a.min(b))?,
|
||||
Op::Max => binary_op(stack, |a, b| a.max(b))?,
|
||||
Op::Pow => binary_op(stack, |a, b| a.powf(b))?,
|
||||
Op::Sqrt => {
|
||||
let v = stack.pop().ok_or("stack underflow")?;
|
||||
let v = pop(stack)?;
|
||||
stack.push(lift_unary(v, |x| x.sqrt())?);
|
||||
}
|
||||
Op::Sin => {
|
||||
let v = stack.pop().ok_or("stack underflow")?;
|
||||
let v = pop(stack)?;
|
||||
stack.push(lift_unary(v, |x| x.sin())?);
|
||||
}
|
||||
Op::Cos => {
|
||||
let v = stack.pop().ok_or("stack underflow")?;
|
||||
let v = pop(stack)?;
|
||||
stack.push(lift_unary(v, |x| x.cos())?);
|
||||
}
|
||||
Op::Log => {
|
||||
let v = stack.pop().ok_or("stack underflow")?;
|
||||
let v = pop(stack)?;
|
||||
stack.push(lift_unary(v, |x| x.ln())?);
|
||||
}
|
||||
|
||||
@@ -507,37 +472,37 @@ impl Forth {
|
||||
Op::Ge => cmp_op(stack, |a, b| a >= b)?,
|
||||
|
||||
Op::And => {
|
||||
let b = stack.pop().ok_or("stack underflow")?.is_truthy();
|
||||
let a = stack.pop().ok_or("stack underflow")?.is_truthy();
|
||||
let b = pop_bool(stack)?;
|
||||
let a = pop_bool(stack)?;
|
||||
stack.push(Value::Int(if a && b { 1 } else { 0 }, None));
|
||||
}
|
||||
Op::Or => {
|
||||
let b = stack.pop().ok_or("stack underflow")?.is_truthy();
|
||||
let a = stack.pop().ok_or("stack underflow")?.is_truthy();
|
||||
let b = pop_bool(stack)?;
|
||||
let a = pop_bool(stack)?;
|
||||
stack.push(Value::Int(if a || b { 1 } else { 0 }, None));
|
||||
}
|
||||
Op::Not => {
|
||||
let v = stack.pop().ok_or("stack underflow")?.is_truthy();
|
||||
let v = pop_bool(stack)?;
|
||||
stack.push(Value::Int(if v { 0 } else { 1 }, None));
|
||||
}
|
||||
Op::Xor => {
|
||||
let b = stack.pop().ok_or("stack underflow")?.is_truthy();
|
||||
let a = stack.pop().ok_or("stack underflow")?.is_truthy();
|
||||
let b = pop_bool(stack)?;
|
||||
let a = pop_bool(stack)?;
|
||||
stack.push(Value::Int(if a ^ b { 1 } else { 0 }, None));
|
||||
}
|
||||
Op::Nand => {
|
||||
let b = stack.pop().ok_or("stack underflow")?.is_truthy();
|
||||
let a = stack.pop().ok_or("stack underflow")?.is_truthy();
|
||||
let b = pop_bool(stack)?;
|
||||
let a = pop_bool(stack)?;
|
||||
stack.push(Value::Int(if !(a && b) { 1 } else { 0 }, None));
|
||||
}
|
||||
Op::Nor => {
|
||||
let b = stack.pop().ok_or("stack underflow")?.is_truthy();
|
||||
let a = stack.pop().ok_or("stack underflow")?.is_truthy();
|
||||
let b = pop_bool(stack)?;
|
||||
let a = pop_bool(stack)?;
|
||||
stack.push(Value::Int(if !(a || b) { 1 } else { 0 }, None));
|
||||
}
|
||||
|
||||
Op::BranchIfZero(offset, then_span, else_span) => {
|
||||
let v = stack.pop().ok_or("stack underflow")?;
|
||||
let v = pop(stack)?;
|
||||
if !v.is_truthy() {
|
||||
if let Some(span) = else_span {
|
||||
if let Some(trace) = trace_cell.borrow_mut().as_mut() {
|
||||
@@ -556,9 +521,7 @@ impl Forth {
|
||||
}
|
||||
|
||||
Op::NewCmd => {
|
||||
if stack.is_empty() {
|
||||
return Err("stack underflow".into());
|
||||
}
|
||||
ensure(stack, 1)?;
|
||||
let values = std::mem::take(stack);
|
||||
let val = if values.len() == 1 {
|
||||
values.into_iter().next().unwrap()
|
||||
@@ -568,9 +531,7 @@ impl Forth {
|
||||
cmd.set_sound(val);
|
||||
}
|
||||
Op::SetParam(param) => {
|
||||
if stack.is_empty() {
|
||||
return Err("stack underflow".into());
|
||||
}
|
||||
ensure(stack, 1)?;
|
||||
let values = std::mem::take(stack);
|
||||
let val = if values.len() == 1 {
|
||||
values.into_iter().next().unwrap()
|
||||
@@ -654,7 +615,7 @@ impl Forth {
|
||||
}
|
||||
|
||||
Op::Get => {
|
||||
let name = stack.pop().ok_or("stack underflow")?;
|
||||
let name = pop(stack)?;
|
||||
let name = name.as_str()?;
|
||||
let vw = var_writes_cell.borrow();
|
||||
let vw_ref = vw.as_ref().expect("var_writes taken");
|
||||
@@ -667,9 +628,9 @@ impl Forth {
|
||||
stack.push(val);
|
||||
}
|
||||
Op::Set => {
|
||||
let name = stack.pop().ok_or("stack underflow")?;
|
||||
let name = pop(stack)?;
|
||||
let name = name.as_str()?.to_string();
|
||||
let val = stack.pop().ok_or("stack underflow")?;
|
||||
let val = pop(stack)?;
|
||||
var_writes_cell
|
||||
.borrow_mut()
|
||||
.as_mut()
|
||||
@@ -703,8 +664,8 @@ impl Forth {
|
||||
}
|
||||
|
||||
Op::Rand(word_span) => {
|
||||
let b = stack.pop().ok_or("stack underflow")?;
|
||||
let a = stack.pop().ok_or("stack underflow")?;
|
||||
let b = pop(stack)?;
|
||||
let a = pop(stack)?;
|
||||
match (&a, &b) {
|
||||
(Value::Int(a_i, _), Value::Int(b_i, _)) => {
|
||||
let (lo, hi) = if a_i <= b_i {
|
||||
@@ -731,8 +692,8 @@ impl Forth {
|
||||
}
|
||||
}
|
||||
Op::ExpRand(word_span) => {
|
||||
let hi = stack.pop().ok_or("stack underflow")?.as_float()?;
|
||||
let lo = stack.pop().ok_or("stack underflow")?.as_float()?;
|
||||
let hi = pop_float(stack)?;
|
||||
let lo = pop_float(stack)?;
|
||||
if lo <= 0.0 || hi <= 0.0 {
|
||||
return Err("exprand requires positive values".into());
|
||||
}
|
||||
@@ -743,8 +704,8 @@ impl Forth {
|
||||
stack.push(Value::Float(val, None));
|
||||
}
|
||||
Op::LogRand(word_span) => {
|
||||
let hi = stack.pop().ok_or("stack underflow")?.as_float()?;
|
||||
let lo = stack.pop().ok_or("stack underflow")?.as_float()?;
|
||||
let hi = pop_float(stack)?;
|
||||
let lo = pop_float(stack)?;
|
||||
if lo <= 0.0 || hi <= 0.0 {
|
||||
return Err("logrand requires positive values".into());
|
||||
}
|
||||
@@ -755,12 +716,12 @@ impl Forth {
|
||||
stack.push(Value::Float(val, None));
|
||||
}
|
||||
Op::Seed => {
|
||||
let s = stack.pop().ok_or("stack underflow")?.as_int()?;
|
||||
let s = pop_int(stack)?;
|
||||
*self.rng.lock() = StdRng::seed_from_u64(s as u64);
|
||||
}
|
||||
|
||||
Op::Cycle(word_span) | Op::PCycle(word_span) => {
|
||||
let count = stack.pop().ok_or("stack underflow")?.as_int()? as usize;
|
||||
let count = pop_int(stack)? as usize;
|
||||
if count == 0 {
|
||||
return Err("cycle count must be > 0".into());
|
||||
}
|
||||
@@ -779,7 +740,7 @@ impl Forth {
|
||||
}
|
||||
|
||||
Op::Choose(word_span) => {
|
||||
let count = stack.pop().ok_or("stack underflow")?.as_int()? as usize;
|
||||
let count = pop_int(stack)? as usize;
|
||||
if count == 0 {
|
||||
return Err("choose count must be > 0".into());
|
||||
}
|
||||
@@ -795,7 +756,7 @@ impl Forth {
|
||||
}
|
||||
|
||||
Op::Bounce(word_span) => {
|
||||
let count = stack.pop().ok_or("stack underflow")?.as_int()? as usize;
|
||||
let count = pop_int(stack)? as usize;
|
||||
if count == 0 {
|
||||
return Err("bounce count must be > 0".into());
|
||||
}
|
||||
@@ -817,14 +778,12 @@ impl Forth {
|
||||
}
|
||||
|
||||
Op::WChoose(word_span) => {
|
||||
let count = stack.pop().ok_or("stack underflow")?.as_int()? as usize;
|
||||
let count = pop_int(stack)? as usize;
|
||||
if count == 0 {
|
||||
return Err("wchoose count must be > 0".into());
|
||||
}
|
||||
let pairs_needed = count * 2;
|
||||
if stack.len() < pairs_needed {
|
||||
return Err("stack underflow".into());
|
||||
}
|
||||
ensure(stack, pairs_needed)?;
|
||||
let start = stack.len() - pairs_needed;
|
||||
let mut values = Vec::with_capacity(count);
|
||||
let mut weights = Vec::with_capacity(count);
|
||||
@@ -858,8 +817,8 @@ impl Forth {
|
||||
}
|
||||
|
||||
Op::ChanceExec(word_span) | Op::ProbExec(word_span) => {
|
||||
let threshold = stack.pop().ok_or("stack underflow")?.as_float()?;
|
||||
let quot = stack.pop().ok_or("stack underflow")?;
|
||||
let threshold = pop_float(stack)?;
|
||||
let quot = pop(stack)?;
|
||||
let val: f64 = self.rng.lock().gen();
|
||||
let limit = match &ops[pc] {
|
||||
Op::ChanceExec(_) => threshold,
|
||||
@@ -880,7 +839,7 @@ impl Forth {
|
||||
}
|
||||
|
||||
Op::Every => {
|
||||
let n = stack.pop().ok_or("stack underflow")?.as_int()?;
|
||||
let n = pop_int(stack)?;
|
||||
if n <= 0 {
|
||||
return Err("every count must be > 0".into());
|
||||
}
|
||||
@@ -893,8 +852,8 @@ impl Forth {
|
||||
}
|
||||
|
||||
Op::When | Op::Unless => {
|
||||
let cond = stack.pop().ok_or("stack underflow")?;
|
||||
let quot = stack.pop().ok_or("stack underflow")?;
|
||||
let cond = pop(stack)?;
|
||||
let quot = pop(stack)?;
|
||||
let should_run = match &ops[pc] {
|
||||
Op::When => cond.is_truthy(),
|
||||
_ => !cond.is_truthy(),
|
||||
@@ -905,9 +864,9 @@ impl Forth {
|
||||
}
|
||||
|
||||
Op::IfElse => {
|
||||
let cond = stack.pop().ok_or("stack underflow")?;
|
||||
let false_quot = stack.pop().ok_or("stack underflow")?;
|
||||
let true_quot = stack.pop().ok_or("stack underflow")?;
|
||||
let cond = pop(stack)?;
|
||||
let false_quot = pop(stack)?;
|
||||
let true_quot = pop(stack)?;
|
||||
let quot = if cond.is_truthy() {
|
||||
true_quot
|
||||
} else {
|
||||
@@ -917,7 +876,7 @@ impl Forth {
|
||||
}
|
||||
|
||||
Op::Pick => {
|
||||
let idx_i = stack.pop().ok_or("stack underflow")?.as_int()?;
|
||||
let idx_i = pop_int(stack)?;
|
||||
if idx_i < 0 {
|
||||
return Err(format!("pick index must be >= 0, got {idx_i}"));
|
||||
}
|
||||
@@ -944,13 +903,13 @@ impl Forth {
|
||||
}
|
||||
|
||||
Op::Mtof => {
|
||||
let note = stack.pop().ok_or("stack underflow")?.as_float()?;
|
||||
let note = pop_float(stack)?;
|
||||
let freq = 440.0 * 2.0_f64.powf((note - 69.0) / 12.0);
|
||||
stack.push(Value::Float(freq, None));
|
||||
}
|
||||
|
||||
Op::Ftom => {
|
||||
let freq = stack.pop().ok_or("stack underflow")?.as_float()?;
|
||||
let freq = pop_float(stack)?;
|
||||
let note = 69.0 + 12.0 * (freq / 440.0).log2();
|
||||
stack.push(Value::Float(note, None));
|
||||
}
|
||||
@@ -959,7 +918,7 @@ impl Forth {
|
||||
if pattern.is_empty() {
|
||||
return Err("empty scale pattern".into());
|
||||
}
|
||||
let val = stack.pop().ok_or("stack underflow")?;
|
||||
let val = pop(stack)?;
|
||||
let len = pattern.len() as i64;
|
||||
let result = lift_unary_int(val, |degree| {
|
||||
let octave_offset = degree.div_euclid(len);
|
||||
@@ -970,21 +929,21 @@ impl Forth {
|
||||
}
|
||||
|
||||
Op::Chord(intervals) => {
|
||||
let root = stack.pop().ok_or("stack underflow")?.as_int()?;
|
||||
let root = pop_int(stack)?;
|
||||
for &interval in *intervals {
|
||||
stack.push(Value::Int(root + interval, None));
|
||||
}
|
||||
}
|
||||
|
||||
Op::Oct => {
|
||||
let shift = stack.pop().ok_or("stack underflow")?;
|
||||
let note = stack.pop().ok_or("stack underflow")?;
|
||||
let shift = pop(stack)?;
|
||||
let note = pop(stack)?;
|
||||
let result = lift_binary(note, shift, |n, s| n + s * 12.0)?;
|
||||
stack.push(result);
|
||||
}
|
||||
|
||||
Op::SetTempo => {
|
||||
let tempo = stack.pop().ok_or("stack underflow")?.as_float()?;
|
||||
let tempo = pop_float(stack)?;
|
||||
let clamped = tempo.clamp(20.0, 300.0);
|
||||
var_writes_cell
|
||||
.borrow_mut()
|
||||
@@ -994,7 +953,7 @@ impl Forth {
|
||||
}
|
||||
|
||||
Op::SetSpeed => {
|
||||
let speed = stack.pop().ok_or("stack underflow")?.as_float()?;
|
||||
let speed = pop_float(stack)?;
|
||||
let clamped = speed.clamp(0.125, 8.0);
|
||||
var_writes_cell
|
||||
.borrow_mut()
|
||||
@@ -1004,8 +963,8 @@ impl Forth {
|
||||
}
|
||||
|
||||
Op::Chain => {
|
||||
let pattern = stack.pop().ok_or("stack underflow")?.as_int()? - 1;
|
||||
let bank = stack.pop().ok_or("stack underflow")?.as_int()? - 1;
|
||||
let pattern = pop_int(stack)? - 1;
|
||||
let bank = pop_int(stack)? - 1;
|
||||
if bank < 0 || pattern < 0 {
|
||||
return Err("chain: bank and pattern must be >= 1".into());
|
||||
}
|
||||
@@ -1024,7 +983,7 @@ impl Forth {
|
||||
}
|
||||
|
||||
Op::Loop => {
|
||||
let beats = stack.pop().ok_or("stack underflow")?.as_float()?;
|
||||
let beats = pop_float(stack)?;
|
||||
if ctx.tempo == 0.0 || ctx.speed == 0.0 {
|
||||
return Err("tempo and speed must be non-zero".into());
|
||||
}
|
||||
@@ -1034,26 +993,22 @@ impl Forth {
|
||||
}
|
||||
|
||||
Op::At => {
|
||||
if stack.is_empty() {
|
||||
return Err("stack underflow".into());
|
||||
}
|
||||
ensure(stack, 1)?;
|
||||
let deltas = std::mem::take(stack);
|
||||
cmd.set_deltas(deltas);
|
||||
}
|
||||
|
||||
Op::Arp => {
|
||||
if stack.is_empty() {
|
||||
return Err("stack underflow".into());
|
||||
}
|
||||
ensure(stack, 1)?;
|
||||
let values = std::mem::take(stack);
|
||||
stack.push(Value::ArpList(Arc::from(values)));
|
||||
}
|
||||
|
||||
Op::Adsr => {
|
||||
let r = stack.pop().ok_or("stack underflow")?;
|
||||
let s = stack.pop().ok_or("stack underflow")?;
|
||||
let d = stack.pop().ok_or("stack underflow")?;
|
||||
let a = stack.pop().ok_or("stack underflow")?;
|
||||
let r = pop(stack)?;
|
||||
let s = pop(stack)?;
|
||||
let d = pop(stack)?;
|
||||
let a = pop(stack)?;
|
||||
cmd.set_param("attack", a);
|
||||
cmd.set_param("decay", d);
|
||||
cmd.set_param("sustain", s);
|
||||
@@ -1061,41 +1016,41 @@ impl Forth {
|
||||
}
|
||||
|
||||
Op::Ad => {
|
||||
let d = stack.pop().ok_or("stack underflow")?;
|
||||
let a = stack.pop().ok_or("stack underflow")?;
|
||||
let d = pop(stack)?;
|
||||
let a = pop(stack)?;
|
||||
cmd.set_param("attack", a);
|
||||
cmd.set_param("decay", d);
|
||||
cmd.set_param("sustain", Value::Int(0, None));
|
||||
}
|
||||
|
||||
Op::Apply => {
|
||||
let quot = stack.pop().ok_or("stack underflow")?;
|
||||
let quot = pop(stack)?;
|
||||
run_quotation(quot, stack, outputs, cmd)?;
|
||||
}
|
||||
|
||||
Op::Ramp => {
|
||||
let curve = stack.pop().ok_or("stack underflow")?.as_float()?;
|
||||
let freq = stack.pop().ok_or("stack underflow")?.as_float()?;
|
||||
let curve = pop_float(stack)?;
|
||||
let freq = pop_float(stack)?;
|
||||
let phase = (freq * ctx.beat).fract();
|
||||
let phase = if phase < 0.0 { phase + 1.0 } else { phase };
|
||||
let val = phase.powf(curve);
|
||||
stack.push(Value::Float(val, None));
|
||||
}
|
||||
Op::Triangle => {
|
||||
let freq = stack.pop().ok_or("stack underflow")?.as_float()?;
|
||||
let freq = pop_float(stack)?;
|
||||
let phase = (freq * ctx.beat).fract();
|
||||
let phase = if phase < 0.0 { phase + 1.0 } else { phase };
|
||||
let val = 1.0 - (2.0 * phase - 1.0).abs();
|
||||
stack.push(Value::Float(val, None));
|
||||
}
|
||||
Op::Range => {
|
||||
let max = stack.pop().ok_or("stack underflow")?.as_float()?;
|
||||
let min = stack.pop().ok_or("stack underflow")?.as_float()?;
|
||||
let val = stack.pop().ok_or("stack underflow")?.as_float()?;
|
||||
let max = pop_float(stack)?;
|
||||
let min = pop_float(stack)?;
|
||||
let val = pop_float(stack)?;
|
||||
stack.push(Value::Float(min + val * (max - min), None));
|
||||
}
|
||||
Op::Perlin => {
|
||||
let freq = stack.pop().ok_or("stack underflow")?.as_float()?;
|
||||
let freq = pop_float(stack)?;
|
||||
let val = perlin_noise_1d(freq * ctx.beat);
|
||||
stack.push(Value::Float(val, None));
|
||||
}
|
||||
@@ -1105,8 +1060,8 @@ impl Forth {
|
||||
}
|
||||
|
||||
Op::IntRange => {
|
||||
let end = stack.pop().ok_or("stack underflow")?.as_int()?;
|
||||
let start = stack.pop().ok_or("stack underflow")?.as_int()?;
|
||||
let end = pop_int(stack)?;
|
||||
let start = pop_int(stack)?;
|
||||
if start <= end {
|
||||
for i in start..=end {
|
||||
stack.push(Value::Int(i, None));
|
||||
@@ -1119,9 +1074,9 @@ impl Forth {
|
||||
}
|
||||
|
||||
Op::StepRange => {
|
||||
let step = stack.pop().ok_or("stack underflow")?.as_float()?;
|
||||
let end = stack.pop().ok_or("stack underflow")?.as_float()?;
|
||||
let start = stack.pop().ok_or("stack underflow")?.as_float()?;
|
||||
let step = pop_float(stack)?;
|
||||
let end = pop_float(stack)?;
|
||||
let start = pop_float(stack)?;
|
||||
if step == 0.0 {
|
||||
return Err("step cannot be zero".into());
|
||||
}
|
||||
@@ -1138,8 +1093,8 @@ impl Forth {
|
||||
}
|
||||
|
||||
Op::Generate => {
|
||||
let count = stack.pop().ok_or("stack underflow")?.as_int()?;
|
||||
let quot = stack.pop().ok_or("stack underflow")?;
|
||||
let count = pop_int(stack)?;
|
||||
let quot = pop(stack)?;
|
||||
if count < 0 {
|
||||
return Err("gen count must be >= 0".into());
|
||||
}
|
||||
@@ -1154,8 +1109,8 @@ impl Forth {
|
||||
}
|
||||
|
||||
Op::Times => {
|
||||
let quot = stack.pop().ok_or("stack underflow")?;
|
||||
let count = stack.pop().ok_or("stack underflow")?.as_int()?;
|
||||
let quot = pop(stack)?;
|
||||
let count = pop_int(stack)?;
|
||||
if count < 0 {
|
||||
return Err("times count must be >= 0".into());
|
||||
}
|
||||
@@ -1170,9 +1125,9 @@ impl Forth {
|
||||
}
|
||||
|
||||
Op::GeomRange => {
|
||||
let count = stack.pop().ok_or("stack underflow")?.as_int()?;
|
||||
let ratio = stack.pop().ok_or("stack underflow")?.as_float()?;
|
||||
let start = stack.pop().ok_or("stack underflow")?.as_float()?;
|
||||
let count = pop_int(stack)?;
|
||||
let ratio = pop_float(stack)?;
|
||||
let start = pop_float(stack)?;
|
||||
if count < 0 {
|
||||
return Err("geom.. count must be >= 0".into());
|
||||
}
|
||||
@@ -1184,8 +1139,8 @@ impl Forth {
|
||||
}
|
||||
|
||||
Op::Euclid => {
|
||||
let n = stack.pop().ok_or("stack underflow")?.as_int()?;
|
||||
let k = stack.pop().ok_or("stack underflow")?.as_int()?;
|
||||
let n = pop_int(stack)?;
|
||||
let k = pop_int(stack)?;
|
||||
if k < 0 || n < 0 {
|
||||
return Err("euclid: k and n must be >= 0".into());
|
||||
}
|
||||
@@ -1195,9 +1150,9 @@ impl Forth {
|
||||
}
|
||||
|
||||
Op::EuclidRot => {
|
||||
let r = stack.pop().ok_or("stack underflow")?.as_int()?;
|
||||
let n = stack.pop().ok_or("stack underflow")?.as_int()?;
|
||||
let k = stack.pop().ok_or("stack underflow")?.as_int()?;
|
||||
let r = pop_int(stack)?;
|
||||
let n = pop_int(stack)?;
|
||||
let k = pop_int(stack)?;
|
||||
if k < 0 || n < 0 || r < 0 {
|
||||
return Err("euclidrot: k, n, and r must be >= 0".into());
|
||||
}
|
||||
@@ -1207,33 +1162,31 @@ impl Forth {
|
||||
}
|
||||
|
||||
Op::ModLfo(shape) => {
|
||||
let period = stack.pop().ok_or("stack underflow")?.as_float()? * ctx.step_duration();
|
||||
let max = stack.pop().ok_or("stack underflow")?.as_float()?;
|
||||
let min = stack.pop().ok_or("stack underflow")?.as_float()?;
|
||||
let period = pop_float(stack)? * ctx.step_duration();
|
||||
let max = pop_float(stack)?;
|
||||
let min = pop_float(stack)?;
|
||||
let suffix = match shape { 1 => "t", 2 => "w", 3 => "q", _ => "" };
|
||||
let s = format!("{min}~{max}:{period}{suffix}");
|
||||
stack.push(Value::Str(s.into(), None));
|
||||
}
|
||||
Op::ModSlide(curve) => {
|
||||
let dur = stack.pop().ok_or("stack underflow")?.as_float()? * ctx.step_duration();
|
||||
let end = stack.pop().ok_or("stack underflow")?.as_float()?;
|
||||
let start = stack.pop().ok_or("stack underflow")?.as_float()?;
|
||||
let dur = pop_float(stack)? * ctx.step_duration();
|
||||
let end = pop_float(stack)?;
|
||||
let start = pop_float(stack)?;
|
||||
let suffix = match curve { 1 => "e", 2 => "s", _ => "" };
|
||||
let s = format!("{start}>{end}:{dur}{suffix}");
|
||||
stack.push(Value::Str(s.into(), None));
|
||||
}
|
||||
Op::ModRnd(dist) => {
|
||||
let period = stack.pop().ok_or("stack underflow")?.as_float()? * ctx.step_duration();
|
||||
let max = stack.pop().ok_or("stack underflow")?.as_float()?;
|
||||
let min = stack.pop().ok_or("stack underflow")?.as_float()?;
|
||||
let period = pop_float(stack)? * ctx.step_duration();
|
||||
let max = pop_float(stack)?;
|
||||
let min = pop_float(stack)?;
|
||||
let suffix = match dist { 1 => "s", 2 => "d", _ => "" };
|
||||
let s = format!("{min}?{max}:{period}{suffix}");
|
||||
stack.push(Value::Str(s.into(), None));
|
||||
}
|
||||
Op::ModEnv => {
|
||||
if stack.is_empty() {
|
||||
return Err("stack underflow".into());
|
||||
}
|
||||
ensure(stack, 1)?;
|
||||
let values = std::mem::take(stack);
|
||||
let mut floats = Vec::with_capacity(values.len());
|
||||
for v in &values {
|
||||
@@ -1319,8 +1272,8 @@ impl Forth {
|
||||
outputs.push(format!("/midi/continue/dev/{dev}"));
|
||||
}
|
||||
Op::GetMidiCC => {
|
||||
let chan = stack.pop().ok_or("stack underflow")?.as_int()?;
|
||||
let cc = stack.pop().ok_or("stack underflow")?.as_int()?;
|
||||
let chan = pop_int(stack)?;
|
||||
let cc = pop_int(stack)?;
|
||||
let cc_clamped = (cc.clamp(0, 127)) as usize;
|
||||
let chan_clamped = (chan.clamp(1, 16) - 1) as usize;
|
||||
let (_, params) = cmd.snapshot().unwrap_or((None, &[]));
|
||||
@@ -1333,8 +1286,8 @@ impl Forth {
|
||||
stack.push(Value::Int(val as i64, None));
|
||||
}
|
||||
Op::Forget => {
|
||||
let name = stack.pop().ok_or("stack underflow")?.as_str()?.to_string();
|
||||
self.dict.lock().remove(&name);
|
||||
let name = pop(stack)?;
|
||||
self.dict.lock().remove(name.as_str()?);
|
||||
}
|
||||
}
|
||||
pc += 1;
|
||||
@@ -1543,6 +1496,29 @@ fn perlin_noise_1d(x: f64) -> f64 {
|
||||
(d0 + s * (d1 - d0)) * 0.5 + 0.5
|
||||
}
|
||||
|
||||
fn pop(stack: &mut Vec<Value>) -> Result<Value, String> {
|
||||
stack.pop().ok_or_else(|| "stack underflow".to_string())
|
||||
}
|
||||
|
||||
fn pop_int(stack: &mut Vec<Value>) -> Result<i64, String> {
|
||||
pop(stack)?.as_int()
|
||||
}
|
||||
|
||||
fn pop_float(stack: &mut Vec<Value>) -> Result<f64, String> {
|
||||
pop(stack)?.as_float()
|
||||
}
|
||||
|
||||
fn pop_bool(stack: &mut Vec<Value>) -> Result<bool, String> {
|
||||
Ok(pop(stack)?.is_truthy())
|
||||
}
|
||||
|
||||
fn ensure(stack: &[Value], n: usize) -> Result<(), String> {
|
||||
if stack.len() < n {
|
||||
return Err("stack underflow".into());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn float_to_value(result: f64) -> Value {
|
||||
if result.fract() == 0.0 && result.abs() < i64::MAX as f64 {
|
||||
Value::Int(result as i64, None)
|
||||
@@ -1576,8 +1552,8 @@ fn binary_op<F>(stack: &mut Vec<Value>, f: F) -> Result<(), String>
|
||||
where
|
||||
F: Fn(f64, f64) -> f64 + Copy,
|
||||
{
|
||||
let b = stack.pop().ok_or("stack underflow")?;
|
||||
let a = stack.pop().ok_or("stack underflow")?;
|
||||
let b = pop(stack)?;
|
||||
let a = pop(stack)?;
|
||||
stack.push(lift_binary(a, b, f)?);
|
||||
Ok(())
|
||||
}
|
||||
@@ -1586,8 +1562,8 @@ fn cmp_op<F>(stack: &mut Vec<Value>, f: F) -> Result<(), String>
|
||||
where
|
||||
F: Fn(f64, f64) -> bool,
|
||||
{
|
||||
let b = stack.pop().ok_or("stack underflow")?;
|
||||
let a = stack.pop().ok_or("stack underflow")?;
|
||||
let b = pop(stack)?;
|
||||
let a = pop(stack)?;
|
||||
let result = if f(a.as_float()?, b.as_float()?) {
|
||||
1
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user