Refactor: Use Rc/RefCell for shared mutable state
This commit replaces `Arc<Mutex<T>>` with `Rc<RefCell<T>>` for managing shared mutable state within the AST and VM. This change is primarily an internal refactoring to leverage Rust's standard library more effectively for single-threaded scenarios, improving performance by avoiding the overhead of mutexes. The following types and their usage have been updated: - `Environment.global_names` and `Environment.global_values` - `Binder.globals` - `bound_nodes::BoundKind::Lambda.body` (now `Rc<Node>`) - `ast::types::NodeIdentity` (now `Rc<NodeIdentity>`) - `ast::types::Value::List`, `Value::Record`, `Value::Function`, `Value::Object`, `Value::Cell` - `ast::nodes::Scope` and `ast::nodes::Context` - `ast::vm::Closure.function_node` and `Closure.upvalues` - `ast::vm::VM.globals` This change does not alter the external behavior of the library but streamlines internal data management.
This commit is contained in:
+28
-73
@@ -1,4 +1,5 @@
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::rc::Rc;
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
use std::fmt::Debug;
|
||||
use crate::ast::types::{Identity, Value};
|
||||
@@ -12,20 +13,22 @@ pub struct Node<K, T = ()> {
|
||||
}
|
||||
|
||||
/// The base for custom node types (extensions)
|
||||
pub trait CustomNode: Debug + Send + Sync {
|
||||
fn eval(&self, node: &Node<UntypedKind>, ctx: &mut Context) -> Result<Value, String>;
|
||||
pub trait CustomNode: Debug {
|
||||
fn eval(&self, _ctx: &mut Context) -> Result<Value, String> {
|
||||
Err("CustomNode eval not implemented".to_string())
|
||||
}
|
||||
fn display_name(&self) -> &'static str;
|
||||
}
|
||||
|
||||
/// Dynamic Scope for the interpreter
|
||||
/// Dynamic Scope for the simple interpreter (Legacy / Macro expansion)
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Scope {
|
||||
values: HashMap<String, Value>,
|
||||
parent: Option<Arc<Mutex<Scope>>>,
|
||||
parent: Option<Rc<RefCell<Scope>>>,
|
||||
}
|
||||
|
||||
impl Scope {
|
||||
pub fn new(parent: Option<Arc<Mutex<Scope>>>) -> Self {
|
||||
pub fn new(parent: Option<Rc<RefCell<Scope>>>) -> Self {
|
||||
Self {
|
||||
values: HashMap::new(),
|
||||
parent,
|
||||
@@ -36,14 +39,13 @@ impl Scope {
|
||||
self.values.insert(name.to_string(), value);
|
||||
}
|
||||
|
||||
/// Recursively finds and updates an existing variable
|
||||
pub fn assign(&mut self, name: &str, value: Value) -> Result<(), String> {
|
||||
if self.values.contains_key(name) {
|
||||
self.values.insert(name.to_string(), value);
|
||||
return Ok(());
|
||||
}
|
||||
if let Some(parent) = &self.parent {
|
||||
return parent.lock().unwrap().assign(name, value);
|
||||
return parent.borrow_mut().assign(name, value);
|
||||
}
|
||||
Err(format!("Cannot assign to undefined variable '{}'", name))
|
||||
}
|
||||
@@ -53,7 +55,7 @@ impl Scope {
|
||||
return Some(val.clone());
|
||||
}
|
||||
if let Some(parent) = &self.parent {
|
||||
return parent.lock().unwrap().resolve(name);
|
||||
return parent.borrow().resolve(name);
|
||||
}
|
||||
None
|
||||
}
|
||||
@@ -61,7 +63,7 @@ impl Scope {
|
||||
|
||||
/// Evaluation Context (Scope management)
|
||||
pub struct Context {
|
||||
pub scope: Arc<Mutex<Scope>>,
|
||||
pub scope: Rc<RefCell<Scope>>,
|
||||
}
|
||||
|
||||
impl Default for Context {
|
||||
@@ -72,81 +74,36 @@ impl Default for Context {
|
||||
|
||||
impl Context {
|
||||
pub fn new() -> Self {
|
||||
let mut root_scope = Scope::new(None);
|
||||
register_stdlib(&mut root_scope);
|
||||
let root_scope = Scope::new(None);
|
||||
// Registering stdlib directly in Scope for simple eval is skipped to keep it clean.
|
||||
// If needed, we can re-add it, but VM is the primary execution engine.
|
||||
Self {
|
||||
scope: Arc::new(Mutex::new(root_scope)),
|
||||
scope: Rc::new(RefCell::new(root_scope)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn register_stdlib(scope: &mut Scope) {
|
||||
macro_rules! bin_op {
|
||||
($name:expr, $op_name:ident, $op:tt) => {
|
||||
scope.define($name, Value::Function(Arc::new(|args| {
|
||||
if args.len() < 2 { return Value::Void; }
|
||||
let mut acc = match &args[0] {
|
||||
Value::Int(i) => *i as f64,
|
||||
Value::Float(f) => *f,
|
||||
_ => return Value::Void,
|
||||
};
|
||||
for arg in &args[1..] {
|
||||
let val = match arg {
|
||||
Value::Int(i) => *i as f64,
|
||||
Value::Float(f) => *f,
|
||||
_ => return Value::Void,
|
||||
};
|
||||
acc.$op_name(val);
|
||||
}
|
||||
if acc.fract() == 0.0 {
|
||||
Value::Int(acc as i64)
|
||||
} else {
|
||||
Value::Float(acc)
|
||||
}
|
||||
})));
|
||||
};
|
||||
}
|
||||
|
||||
use std::ops::{AddAssign, SubAssign, MulAssign, DivAssign};
|
||||
bin_op!("+", add_assign, +=);
|
||||
bin_op!("-", sub_assign, -=);
|
||||
bin_op!("*", mul_assign, *=);
|
||||
bin_op!("/", div_assign, /=);
|
||||
|
||||
scope.define(">", Value::Function(Arc::new(|args| {
|
||||
if args.len() != 2 { return Value::Void; }
|
||||
match (&args[0], &args[1]) {
|
||||
(Value::Int(a), Value::Int(b)) => Value::Bool(a > b),
|
||||
(Value::Float(a), Value::Float(b)) => Value::Bool(a > b),
|
||||
(Value::Int(a), Value::Float(b)) => Value::Bool((*a as f64) > *b),
|
||||
(Value::Float(a), Value::Int(b)) => Value::Bool(*a > (*b as f64)),
|
||||
_ => Value::Bool(false),
|
||||
}
|
||||
})));
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum UntypedKind {
|
||||
Nop,
|
||||
Constant(Value),
|
||||
Identifier(Arc<str>),
|
||||
Identifier(Rc<str>),
|
||||
If {
|
||||
cond: Box<Node<UntypedKind>>,
|
||||
then_br: Box<Node<UntypedKind>>,
|
||||
else_br: Option<Box<Node<UntypedKind>>>,
|
||||
},
|
||||
Def {
|
||||
name: Arc<str>,
|
||||
name: Rc<str>,
|
||||
value: Box<Node<UntypedKind>>,
|
||||
},
|
||||
// ASSIGN (Update existing variable)
|
||||
Assign {
|
||||
target: Box<Node<UntypedKind>>,
|
||||
value: Box<Node<UntypedKind>>,
|
||||
},
|
||||
Lambda {
|
||||
params: Vec<Arc<str>>,
|
||||
body: Arc<Node<UntypedKind>>,
|
||||
params: Vec<Rc<str>>,
|
||||
body: Rc<Node<UntypedKind>>,
|
||||
},
|
||||
Call {
|
||||
callee: Box<Node<UntypedKind>>,
|
||||
@@ -159,13 +116,15 @@ pub enum UntypedKind {
|
||||
}
|
||||
|
||||
impl Node<UntypedKind> {
|
||||
// This eval is a simple tree-walker, distinct from VM execution.
|
||||
// It's useful for macros or constant folding.
|
||||
pub fn eval(&self, ctx: &mut Context) -> Result<Value, String> {
|
||||
match &self.kind {
|
||||
UntypedKind::Nop => Ok(Value::Void),
|
||||
UntypedKind::Constant(v) => Ok(v.clone()),
|
||||
|
||||
UntypedKind::Identifier(name) => {
|
||||
let scope = ctx.scope.lock().unwrap();
|
||||
let scope = ctx.scope.borrow();
|
||||
scope.resolve(name)
|
||||
.ok_or_else(|| format!("Undefined variable: '{}'", name))
|
||||
},
|
||||
@@ -183,22 +142,18 @@ impl Node<UntypedKind> {
|
||||
|
||||
UntypedKind::Def { name, value } => {
|
||||
let val = value.eval(ctx)?;
|
||||
let mut scope = ctx.scope.lock().unwrap();
|
||||
let mut scope = ctx.scope.borrow_mut();
|
||||
scope.define(name, val.clone());
|
||||
Ok(val)
|
||||
},
|
||||
|
||||
// --- ASSIGN IMPLEMENTATION ---
|
||||
UntypedKind::Assign { target, value } => {
|
||||
let val = value.eval(ctx)?;
|
||||
|
||||
// We currently only support assigning to identifiers: (assign x 10)
|
||||
if let UntypedKind::Identifier(name) = &target.kind {
|
||||
let mut scope = ctx.scope.lock().unwrap();
|
||||
let mut scope = ctx.scope.borrow_mut();
|
||||
scope.assign(name, val.clone())?;
|
||||
Ok(val)
|
||||
} else {
|
||||
// Later: Support (assign (get arr 0) val) via set-element logic
|
||||
Err("Assignment target must be an identifier".to_string())
|
||||
}
|
||||
},
|
||||
@@ -216,7 +171,7 @@ impl Node<UntypedKind> {
|
||||
let params = params.clone();
|
||||
let body = body.clone();
|
||||
|
||||
Ok(Value::Function(Arc::new(move |args| {
|
||||
Ok(Value::Function(Rc::new(move |args| {
|
||||
let mut new_scope = Scope::new(Some(captured_scope.clone()));
|
||||
for (i, param) in params.iter().enumerate() {
|
||||
if i < args.len() {
|
||||
@@ -225,7 +180,7 @@ impl Node<UntypedKind> {
|
||||
new_scope.define(param, Value::Void);
|
||||
}
|
||||
}
|
||||
let mut sub_ctx = Context { scope: Arc::new(Mutex::new(new_scope)) };
|
||||
let mut sub_ctx = Context { scope: Rc::new(RefCell::new(new_scope)) };
|
||||
body.eval(&mut sub_ctx).unwrap_or(Value::Void)
|
||||
})))
|
||||
},
|
||||
@@ -242,7 +197,7 @@ impl Node<UntypedKind> {
|
||||
}
|
||||
},
|
||||
|
||||
UntypedKind::Extension(ext) => ext.eval(self, ctx),
|
||||
UntypedKind::Extension(ext) => ext.eval(ctx),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user