This commit is contained in:
2026-02-03 03:25:31 +01:00
parent a07a87a35f
commit e337eb35e7
8 changed files with 125 additions and 32 deletions

View File

@@ -2,12 +2,14 @@ use parking_lot::Mutex;
use rand::rngs::StdRng;
use rand::{Rng as RngTrait, SeedableRng};
use std::borrow::Cow;
use std::collections::HashMap;
use std::sync::Arc;
use super::compiler::compile_script;
use super::ops::Op;
use super::types::{
CmdRegister, Dictionary, ExecutionTrace, Rng, Stack, StepContext, Value, Variables,
VariablesMap,
};
pub struct Forth {
@@ -38,7 +40,9 @@ impl Forth {
}
pub fn evaluate(&self, script: &str, ctx: &StepContext) -> Result<Vec<String>, String> {
self.evaluate_impl(script, ctx, None)
let (outputs, var_writes) = self.evaluate_impl(script, ctx, None)?;
self.apply_var_writes(var_writes);
Ok(outputs)
}
pub fn evaluate_with_trace(
@@ -47,15 +51,37 @@ impl Forth {
ctx: &StepContext,
trace: &mut ExecutionTrace,
) -> Result<Vec<String>, String> {
let (outputs, var_writes) = self.evaluate_impl(script, ctx, Some(trace))?;
self.apply_var_writes(var_writes);
Ok(outputs)
}
pub fn evaluate_raw(
&self,
script: &str,
ctx: &StepContext,
trace: &mut ExecutionTrace,
) -> Result<(Vec<String>, HashMap<String, Value>), String> {
self.evaluate_impl(script, ctx, Some(trace))
}
fn apply_var_writes(&self, writes: HashMap<String, Value>) {
if writes.is_empty() {
return;
}
let mut new_vars = (**self.vars.load()).clone();
for (k, v) in writes {
new_vars.insert(k, v);
}
self.vars.store(Arc::new(new_vars));
}
fn evaluate_impl(
&self,
script: &str,
ctx: &StepContext,
trace: Option<&mut ExecutionTrace>,
) -> Result<Vec<String>, String> {
) -> Result<(Vec<String>, HashMap<String, Value>), String> {
if script.trim().is_empty() {
return Err("empty script".into());
}
@@ -69,14 +95,25 @@ impl Forth {
ops: &[Op],
ctx: &StepContext,
trace: Option<&mut ExecutionTrace>,
) -> Result<Vec<String>, 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 cmd = CmdRegister::new();
let vars_snapshot = self.vars.load();
let mut var_writes: HashMap<String, Value> = HashMap::new();
self.execute_ops(ops, ctx, &mut stack, &mut outputs, &mut cmd, trace)?;
self.execute_ops(
ops,
ctx,
&mut stack,
&mut outputs,
&mut cmd,
trace,
&vars_snapshot,
&mut var_writes,
)?;
Ok(outputs)
Ok((outputs, var_writes))
}
#[allow(clippy::too_many_arguments)]
@@ -89,9 +126,12 @@ impl Forth {
outputs: &mut Vec<String>,
cmd: &mut CmdRegister,
trace: Option<&mut ExecutionTrace>,
vars_snapshot: &VariablesMap,
var_writes: &mut HashMap<String, Value>,
) -> Result<(), String> {
let mut pc = 0;
let trace_cell = std::cell::RefCell::new(trace);
let var_writes_cell = std::cell::RefCell::new(Some(var_writes));
let run_quotation = |quot: Value,
stack: &mut Vec<Value>,
@@ -106,6 +146,8 @@ impl Forth {
}
}
let mut trace_opt = trace_cell.borrow_mut().take();
let mut var_writes_guard = var_writes_cell.borrow_mut();
let vw = var_writes_guard.as_mut().expect("var_writes taken");
self.execute_ops(
&quot_ops,
ctx,
@@ -113,7 +155,10 @@ impl Forth {
outputs,
cmd,
trace_opt.as_deref_mut(),
vars_snapshot,
vw,
)?;
drop(var_writes_guard);
*trace_cell.borrow_mut() = trace_opt;
Ok(())
}
@@ -475,15 +520,25 @@ impl Forth {
Op::Get => {
let name = stack.pop().ok_or("stack underflow")?;
let name = name.as_str()?;
let vars = self.vars.lock();
let val = vars.get(name).cloned().unwrap_or(Value::Int(0, None));
let vw = var_writes_cell.borrow();
let vw_ref = vw.as_ref().expect("var_writes taken");
let val = vw_ref
.get(name)
.or_else(|| vars_snapshot.get(name))
.cloned()
.unwrap_or(Value::Int(0, None));
drop(vw);
stack.push(val);
}
Op::Set => {
let name = stack.pop().ok_or("stack underflow")?;
let name = name.as_str()?.to_string();
let val = stack.pop().ok_or("stack underflow")?;
self.vars.lock().insert(name, val);
var_writes_cell
.borrow_mut()
.as_mut()
.expect("var_writes taken")
.insert(name, val);
}
Op::GetContext(name) => {
@@ -710,16 +765,20 @@ impl Forth {
Op::SetTempo => {
let tempo = stack.pop().ok_or("stack underflow")?.as_float()?;
let clamped = tempo.clamp(20.0, 300.0);
self.vars
.lock()
var_writes_cell
.borrow_mut()
.as_mut()
.expect("var_writes taken")
.insert("__tempo__".to_string(), Value::Float(clamped, None));
}
Op::SetSpeed => {
let speed = stack.pop().ok_or("stack underflow")?.as_float()?;
let clamped = speed.clamp(0.125, 8.0);
self.vars
.lock()
var_writes_cell
.borrow_mut()
.as_mut()
.expect("var_writes taken")
.insert(ctx.speed_key.to_string(), Value::Float(clamped, None));
}
@@ -735,7 +794,11 @@ impl Forth {
use std::fmt::Write;
let mut val = String::with_capacity(8);
let _ = write!(&mut val, "{bank}:{pattern}");
self.vars.lock().insert(ctx.chain_key.to_string(), Value::Str(Arc::from(val), None));
var_writes_cell
.borrow_mut()
.as_mut()
.expect("var_writes taken")
.insert(ctx.chain_key.to_string(), Value::Str(Arc::from(val), None));
}
}
@@ -849,8 +912,10 @@ impl Forth {
return Err("times count must be >= 0".into());
}
for i in 0..count {
self.vars
.lock()
var_writes_cell
.borrow_mut()
.as_mut()
.expect("var_writes taken")
.insert("i".to_string(), Value::Int(i, None));
run_quotation(quot.clone(), stack, outputs, cmd)?;
}