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:
@@ -1,5 +1,6 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::rc::Rc;
|
||||||
|
use std::cell::RefCell;
|
||||||
use crate::ast::nodes::{Node, UntypedKind};
|
use crate::ast::nodes::{Node, UntypedKind};
|
||||||
use crate::ast::compiler::bound_nodes::{BoundKind, Address};
|
use crate::ast::compiler::bound_nodes::{BoundKind, Address};
|
||||||
use crate::ast::types::{Identity, StaticType};
|
use crate::ast::types::{Identity, StaticType};
|
||||||
@@ -61,11 +62,12 @@ impl FunctionCompiler {
|
|||||||
|
|
||||||
pub struct Binder {
|
pub struct Binder {
|
||||||
functions: Vec<FunctionCompiler>,
|
functions: Vec<FunctionCompiler>,
|
||||||
globals: Arc<Mutex<HashMap<String, (u32, StaticType)>>>,
|
// Globals mapping: Name -> (Index, Type)
|
||||||
|
globals: Rc<RefCell<HashMap<String, (u32, StaticType)>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Binder {
|
impl Binder {
|
||||||
pub fn new(globals: Arc<Mutex<HashMap<String, (u32, StaticType)>>>) -> Self {
|
pub fn new(globals: Rc<RefCell<HashMap<String, (u32, StaticType)>>>) -> Self {
|
||||||
let mut binder = Self {
|
let mut binder = Self {
|
||||||
functions: Vec::new(),
|
functions: Vec::new(),
|
||||||
globals,
|
globals,
|
||||||
@@ -121,7 +123,7 @@ impl Binder {
|
|||||||
let ty = val_node.ty.clone();
|
let ty = val_node.ty.clone();
|
||||||
|
|
||||||
if self.functions.len() == 1 {
|
if self.functions.len() == 1 {
|
||||||
let mut globals = self.globals.lock().unwrap();
|
let mut globals = self.globals.borrow_mut();
|
||||||
let idx = if let Some((idx, _)) = globals.get(name.as_ref()) {
|
let idx = if let Some((idx, _)) = globals.get(name.as_ref()) {
|
||||||
*idx
|
*idx
|
||||||
} else {
|
} else {
|
||||||
@@ -191,7 +193,7 @@ impl Binder {
|
|||||||
Ok(self.make_node(node.identity.clone(), BoundKind::Lambda {
|
Ok(self.make_node(node.identity.clone(), BoundKind::Lambda {
|
||||||
param_count: params.len() as u32,
|
param_count: params.len() as u32,
|
||||||
upvalues,
|
upvalues,
|
||||||
body: Arc::new(body_bound),
|
body: Rc::new(body_bound),
|
||||||
}, fn_ty))
|
}, fn_ty))
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -256,7 +258,7 @@ impl Binder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 3. Try Global
|
// 3. Try Global
|
||||||
let globals = self.globals.lock().unwrap();
|
let globals = self.globals.borrow();
|
||||||
if let Some((idx, ty)) = globals.get(name) {
|
if let Some((idx, ty)) = globals.get(name) {
|
||||||
return Ok((Address::Global(*idx), ty.clone()));
|
return Ok((Address::Global(*idx), ty.clone()));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use std::sync::Arc;
|
use std::rc::Rc;
|
||||||
use crate::ast::types::{Value, StaticType};
|
use crate::ast::types::{Value, StaticType};
|
||||||
use crate::ast::nodes::Node;
|
use crate::ast::nodes::Node;
|
||||||
|
|
||||||
@@ -39,7 +39,7 @@ pub enum BoundKind {
|
|||||||
param_count: u32,
|
param_count: u32,
|
||||||
// The list of variables captured from enclosing scopes
|
// The list of variables captured from enclosing scopes
|
||||||
upvalues: Vec<Address>,
|
upvalues: Vec<Address>,
|
||||||
body: Arc<Node<BoundKind, StaticType>>,
|
body: Rc<Node<BoundKind, StaticType>>,
|
||||||
},
|
},
|
||||||
|
|
||||||
Call {
|
Call {
|
||||||
|
|||||||
+10
-9
@@ -1,4 +1,5 @@
|
|||||||
use std::sync::{Arc, Mutex};
|
use std::rc::Rc;
|
||||||
|
use std::cell::RefCell;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use crate::ast::types::{Value, StaticType};
|
use crate::ast::types::{Value, StaticType};
|
||||||
use crate::ast::parser::Parser;
|
use crate::ast::parser::Parser;
|
||||||
@@ -6,8 +7,8 @@ use crate::ast::compiler::binder::Binder;
|
|||||||
use crate::ast::vm::VM;
|
use crate::ast::vm::VM;
|
||||||
|
|
||||||
pub struct Environment {
|
pub struct Environment {
|
||||||
pub global_names: Arc<Mutex<HashMap<String, (u32, StaticType)>>>,
|
pub global_names: Rc<RefCell<HashMap<String, (u32, StaticType)>>>,
|
||||||
pub global_values: Arc<Mutex<Vec<Value>>>,
|
pub global_values: Rc<RefCell<Vec<Value>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Environment {
|
impl Default for Environment {
|
||||||
@@ -19,20 +20,20 @@ impl Default for Environment {
|
|||||||
impl Environment {
|
impl Environment {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
let env = Self {
|
let env = Self {
|
||||||
global_names: Arc::new(Mutex::new(HashMap::new())),
|
global_names: Rc::new(RefCell::new(HashMap::new())),
|
||||||
global_values: Arc::new(Mutex::new(Vec::new())),
|
global_values: Rc::new(RefCell::new(Vec::new())),
|
||||||
};
|
};
|
||||||
env.register_stdlib();
|
env.register_stdlib();
|
||||||
env
|
env
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn register_native(&self, name: &str, ty: StaticType, func: fn(Vec<Value>) -> Value) {
|
pub fn register_native(&self, name: &str, ty: StaticType, func: impl Fn(Vec<Value>) -> Value + 'static) {
|
||||||
let mut names = self.global_names.lock().unwrap();
|
let mut names = self.global_names.borrow_mut();
|
||||||
let mut values = self.global_values.lock().unwrap();
|
let mut values = self.global_values.borrow_mut();
|
||||||
|
|
||||||
let idx = values.len() as u32;
|
let idx = values.len() as u32;
|
||||||
names.insert(name.to_string(), (idx, ty));
|
names.insert(name.to_string(), (idx, ty));
|
||||||
values.push(Value::Function(Arc::new(func)));
|
values.push(Value::Function(Rc::new(func)));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn register_stdlib(&self) {
|
fn register_stdlib(&self) {
|
||||||
|
|||||||
+7
-7
@@ -1,6 +1,6 @@
|
|||||||
use std::iter::Peekable;
|
use std::iter::Peekable;
|
||||||
use std::str::Chars;
|
use std::str::Chars;
|
||||||
use std::sync::Arc;
|
use std::rc::Rc;
|
||||||
use crate::ast::types::SourceLocation;
|
use crate::ast::types::SourceLocation;
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
@@ -9,11 +9,11 @@ pub enum TokenKind {
|
|||||||
LeftBracket, RightBracket,
|
LeftBracket, RightBracket,
|
||||||
LeftBrace, RightBrace,
|
LeftBrace, RightBrace,
|
||||||
Quote, Backtick, Tilde, At,
|
Quote, Backtick, Tilde, At,
|
||||||
Identifier(Arc<str>),
|
Identifier(Rc<str>),
|
||||||
Keyword(Arc<str>),
|
Keyword(Rc<str>),
|
||||||
Integer(i64),
|
Integer(i64),
|
||||||
Float(f64),
|
Float(f64),
|
||||||
String(Arc<str>),
|
String(Rc<str>),
|
||||||
EOF,
|
EOF,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,9 +65,9 @@ impl<'a> Lexer<'a> {
|
|||||||
'@' => TokenKind::At,
|
'@' => TokenKind::At,
|
||||||
':' => {
|
':' => {
|
||||||
let id = self.read_while(|c| !c.is_whitespace() && !"()[]{}\"'`~@;".contains(c));
|
let id = self.read_while(|c| !c.is_whitespace() && !"()[]{}\"'`~@;".contains(c));
|
||||||
TokenKind::Keyword(Arc::from(id))
|
TokenKind::Keyword(Rc::from(id))
|
||||||
}
|
}
|
||||||
'"' => TokenKind::String(Arc::from(self.read_string()?)),
|
'"' => TokenKind::String(Rc::from(self.read_string()?)),
|
||||||
c if c.is_ascii_digit() || (c == '-' && self.input.peek().is_some_and(|p| p.is_ascii_digit())) => {
|
c if c.is_ascii_digit() || (c == '-' && self.input.peek().is_some_and(|p| p.is_ascii_digit())) => {
|
||||||
let mut num_str = String::from(c);
|
let mut num_str = String::from(c);
|
||||||
while let Some(&next) = self.peek() {
|
while let Some(&next) = self.peek() {
|
||||||
@@ -88,7 +88,7 @@ impl<'a> Lexer<'a> {
|
|||||||
c if is_ident_start(c) => {
|
c if is_ident_start(c) => {
|
||||||
let mut id = String::from(c);
|
let mut id = String::from(c);
|
||||||
id.push_str(&self.read_while(is_ident_char));
|
id.push_str(&self.read_while(is_ident_char));
|
||||||
TokenKind::Identifier(Arc::from(id))
|
TokenKind::Identifier(Rc::from(id))
|
||||||
}
|
}
|
||||||
_ => return Err(format!("Unexpected character: {} at {}:{}", char, self.line, self.col - 1)),
|
_ => return Err(format!("Unexpected character: {} at {}:{}", char, self.line, self.col - 1)),
|
||||||
};
|
};
|
||||||
|
|||||||
+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::collections::HashMap;
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use crate::ast::types::{Identity, Value};
|
use crate::ast::types::{Identity, Value};
|
||||||
@@ -12,20 +13,22 @@ pub struct Node<K, T = ()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// The base for custom node types (extensions)
|
/// The base for custom node types (extensions)
|
||||||
pub trait CustomNode: Debug + Send + Sync {
|
pub trait CustomNode: Debug {
|
||||||
fn eval(&self, node: &Node<UntypedKind>, ctx: &mut Context) -> Result<Value, String>;
|
fn eval(&self, _ctx: &mut Context) -> Result<Value, String> {
|
||||||
|
Err("CustomNode eval not implemented".to_string())
|
||||||
|
}
|
||||||
fn display_name(&self) -> &'static str;
|
fn display_name(&self) -> &'static str;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Dynamic Scope for the interpreter
|
/// Dynamic Scope for the simple interpreter (Legacy / Macro expansion)
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Scope {
|
pub struct Scope {
|
||||||
values: HashMap<String, Value>,
|
values: HashMap<String, Value>,
|
||||||
parent: Option<Arc<Mutex<Scope>>>,
|
parent: Option<Rc<RefCell<Scope>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Scope {
|
impl Scope {
|
||||||
pub fn new(parent: Option<Arc<Mutex<Scope>>>) -> Self {
|
pub fn new(parent: Option<Rc<RefCell<Scope>>>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
values: HashMap::new(),
|
values: HashMap::new(),
|
||||||
parent,
|
parent,
|
||||||
@@ -36,14 +39,13 @@ impl Scope {
|
|||||||
self.values.insert(name.to_string(), value);
|
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> {
|
pub fn assign(&mut self, name: &str, value: Value) -> Result<(), String> {
|
||||||
if self.values.contains_key(name) {
|
if self.values.contains_key(name) {
|
||||||
self.values.insert(name.to_string(), value);
|
self.values.insert(name.to_string(), value);
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
if let Some(parent) = &self.parent {
|
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))
|
Err(format!("Cannot assign to undefined variable '{}'", name))
|
||||||
}
|
}
|
||||||
@@ -53,7 +55,7 @@ impl Scope {
|
|||||||
return Some(val.clone());
|
return Some(val.clone());
|
||||||
}
|
}
|
||||||
if let Some(parent) = &self.parent {
|
if let Some(parent) = &self.parent {
|
||||||
return parent.lock().unwrap().resolve(name);
|
return parent.borrow().resolve(name);
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
@@ -61,7 +63,7 @@ impl Scope {
|
|||||||
|
|
||||||
/// Evaluation Context (Scope management)
|
/// Evaluation Context (Scope management)
|
||||||
pub struct Context {
|
pub struct Context {
|
||||||
pub scope: Arc<Mutex<Scope>>,
|
pub scope: Rc<RefCell<Scope>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Context {
|
impl Default for Context {
|
||||||
@@ -72,81 +74,36 @@ impl Default for Context {
|
|||||||
|
|
||||||
impl Context {
|
impl Context {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
let mut root_scope = Scope::new(None);
|
let root_scope = Scope::new(None);
|
||||||
register_stdlib(&mut root_scope);
|
// 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 {
|
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)]
|
#[derive(Debug)]
|
||||||
pub enum UntypedKind {
|
pub enum UntypedKind {
|
||||||
Nop,
|
Nop,
|
||||||
Constant(Value),
|
Constant(Value),
|
||||||
Identifier(Arc<str>),
|
Identifier(Rc<str>),
|
||||||
If {
|
If {
|
||||||
cond: Box<Node<UntypedKind>>,
|
cond: Box<Node<UntypedKind>>,
|
||||||
then_br: Box<Node<UntypedKind>>,
|
then_br: Box<Node<UntypedKind>>,
|
||||||
else_br: Option<Box<Node<UntypedKind>>>,
|
else_br: Option<Box<Node<UntypedKind>>>,
|
||||||
},
|
},
|
||||||
Def {
|
Def {
|
||||||
name: Arc<str>,
|
name: Rc<str>,
|
||||||
value: Box<Node<UntypedKind>>,
|
value: Box<Node<UntypedKind>>,
|
||||||
},
|
},
|
||||||
// ASSIGN (Update existing variable)
|
|
||||||
Assign {
|
Assign {
|
||||||
target: Box<Node<UntypedKind>>,
|
target: Box<Node<UntypedKind>>,
|
||||||
value: Box<Node<UntypedKind>>,
|
value: Box<Node<UntypedKind>>,
|
||||||
},
|
},
|
||||||
Lambda {
|
Lambda {
|
||||||
params: Vec<Arc<str>>,
|
params: Vec<Rc<str>>,
|
||||||
body: Arc<Node<UntypedKind>>,
|
body: Rc<Node<UntypedKind>>,
|
||||||
},
|
},
|
||||||
Call {
|
Call {
|
||||||
callee: Box<Node<UntypedKind>>,
|
callee: Box<Node<UntypedKind>>,
|
||||||
@@ -159,13 +116,15 @@ pub enum UntypedKind {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Node<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> {
|
pub fn eval(&self, ctx: &mut Context) -> Result<Value, String> {
|
||||||
match &self.kind {
|
match &self.kind {
|
||||||
UntypedKind::Nop => Ok(Value::Void),
|
UntypedKind::Nop => Ok(Value::Void),
|
||||||
UntypedKind::Constant(v) => Ok(v.clone()),
|
UntypedKind::Constant(v) => Ok(v.clone()),
|
||||||
|
|
||||||
UntypedKind::Identifier(name) => {
|
UntypedKind::Identifier(name) => {
|
||||||
let scope = ctx.scope.lock().unwrap();
|
let scope = ctx.scope.borrow();
|
||||||
scope.resolve(name)
|
scope.resolve(name)
|
||||||
.ok_or_else(|| format!("Undefined variable: '{}'", name))
|
.ok_or_else(|| format!("Undefined variable: '{}'", name))
|
||||||
},
|
},
|
||||||
@@ -183,22 +142,18 @@ impl Node<UntypedKind> {
|
|||||||
|
|
||||||
UntypedKind::Def { name, value } => {
|
UntypedKind::Def { name, value } => {
|
||||||
let val = value.eval(ctx)?;
|
let val = value.eval(ctx)?;
|
||||||
let mut scope = ctx.scope.lock().unwrap();
|
let mut scope = ctx.scope.borrow_mut();
|
||||||
scope.define(name, val.clone());
|
scope.define(name, val.clone());
|
||||||
Ok(val)
|
Ok(val)
|
||||||
},
|
},
|
||||||
|
|
||||||
// --- ASSIGN IMPLEMENTATION ---
|
|
||||||
UntypedKind::Assign { target, value } => {
|
UntypedKind::Assign { target, value } => {
|
||||||
let val = value.eval(ctx)?;
|
let val = value.eval(ctx)?;
|
||||||
|
|
||||||
// We currently only support assigning to identifiers: (assign x 10)
|
|
||||||
if let UntypedKind::Identifier(name) = &target.kind {
|
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())?;
|
scope.assign(name, val.clone())?;
|
||||||
Ok(val)
|
Ok(val)
|
||||||
} else {
|
} else {
|
||||||
// Later: Support (assign (get arr 0) val) via set-element logic
|
|
||||||
Err("Assignment target must be an identifier".to_string())
|
Err("Assignment target must be an identifier".to_string())
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -216,7 +171,7 @@ impl Node<UntypedKind> {
|
|||||||
let params = params.clone();
|
let params = params.clone();
|
||||||
let body = body.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()));
|
let mut new_scope = Scope::new(Some(captured_scope.clone()));
|
||||||
for (i, param) in params.iter().enumerate() {
|
for (i, param) in params.iter().enumerate() {
|
||||||
if i < args.len() {
|
if i < args.len() {
|
||||||
@@ -225,7 +180,7 @@ impl Node<UntypedKind> {
|
|||||||
new_scope.define(param, Value::Void);
|
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)
|
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),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+11
-11
@@ -1,4 +1,4 @@
|
|||||||
use std::sync::Arc;
|
use std::rc::Rc;
|
||||||
use crate::ast::lexer::{Lexer, Token, TokenKind};
|
use crate::ast::lexer::{Lexer, Token, TokenKind};
|
||||||
use crate::ast::types::{Identity, NodeIdentity, Value, Keyword};
|
use crate::ast::types::{Identity, NodeIdentity, Value, Keyword};
|
||||||
use crate::ast::nodes::{Node, UntypedKind, Context};
|
use crate::ast::nodes::{Node, UntypedKind, Context};
|
||||||
@@ -30,7 +30,7 @@ impl<'a> Parser<'a> {
|
|||||||
TokenKind::LeftBracket => self.parse_vector_literal(),
|
TokenKind::LeftBracket => self.parse_vector_literal(),
|
||||||
TokenKind::LeftBrace => self.parse_map_literal(),
|
TokenKind::LeftBrace => self.parse_map_literal(),
|
||||||
TokenKind::Quote => {
|
TokenKind::Quote => {
|
||||||
let identity = Arc::new(NodeIdentity { location: self.current_token.location });
|
let identity = Rc::new(NodeIdentity { location: self.current_token.location });
|
||||||
self.advance()?; // consume '
|
self.advance()?; // consume '
|
||||||
let expr = self.parse_expression()?;
|
let expr = self.parse_expression()?;
|
||||||
Ok(Node {
|
Ok(Node {
|
||||||
@@ -48,7 +48,7 @@ impl<'a> Parser<'a> {
|
|||||||
|
|
||||||
fn parse_atom(&mut self) -> Result<Node<UntypedKind>, String> {
|
fn parse_atom(&mut self) -> Result<Node<UntypedKind>, String> {
|
||||||
let token = self.advance()?;
|
let token = self.advance()?;
|
||||||
let identity = Arc::new(NodeIdentity { location: token.location });
|
let identity = Rc::new(NodeIdentity { location: token.location });
|
||||||
|
|
||||||
let kind = match token.kind {
|
let kind = match token.kind {
|
||||||
TokenKind::Integer(n) => UntypedKind::Constant(Value::Int(n)),
|
TokenKind::Integer(n) => UntypedKind::Constant(Value::Int(n)),
|
||||||
@@ -72,7 +72,7 @@ impl<'a> Parser<'a> {
|
|||||||
|
|
||||||
fn parse_list(&mut self) -> Result<Node<UntypedKind>, String> {
|
fn parse_list(&mut self) -> Result<Node<UntypedKind>, String> {
|
||||||
let start_loc = self.advance()?.location; // consume '('
|
let start_loc = self.advance()?.location; // consume '('
|
||||||
let identity = Arc::new(NodeIdentity { location: start_loc });
|
let identity = Rc::new(NodeIdentity { location: start_loc });
|
||||||
|
|
||||||
if *self.peek() == TokenKind::RightParen {
|
if *self.peek() == TokenKind::RightParen {
|
||||||
return Err(format!("Empty list () is not a valid expression at {:?}", start_loc));
|
return Err(format!("Empty list () is not a valid expression at {:?}", start_loc));
|
||||||
@@ -161,13 +161,13 @@ impl<'a> Parser<'a> {
|
|||||||
identity,
|
identity,
|
||||||
kind: UntypedKind::Lambda {
|
kind: UntypedKind::Lambda {
|
||||||
params,
|
params,
|
||||||
body: Arc::new(body),
|
body: Rc::new(body),
|
||||||
},
|
},
|
||||||
ty: (),
|
ty: (),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_param_vector(&mut self) -> Result<Vec<Arc<str>>, String> {
|
fn parse_param_vector(&mut self) -> Result<Vec<Rc<str>>, String> {
|
||||||
if *self.peek() != TokenKind::LeftBracket {
|
if *self.peek() != TokenKind::LeftBracket {
|
||||||
return Err(format!("Expected parameter vector [...] for fn, found {:?}", self.peek()));
|
return Err(format!("Expected parameter vector [...] for fn, found {:?}", self.peek()));
|
||||||
}
|
}
|
||||||
@@ -215,8 +215,8 @@ impl<'a> Parser<'a> {
|
|||||||
self.expect(TokenKind::RightBracket)?;
|
self.expect(TokenKind::RightBracket)?;
|
||||||
|
|
||||||
Ok(Node {
|
Ok(Node {
|
||||||
identity: Arc::new(NodeIdentity { location: token.location }),
|
identity: Rc::new(NodeIdentity { location: token.location }),
|
||||||
kind: UntypedKind::Constant(Value::List(Arc::new(elements))),
|
kind: UntypedKind::Constant(Value::List(Rc::new(elements))),
|
||||||
ty: (),
|
ty: (),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -248,8 +248,8 @@ impl<'a> Parser<'a> {
|
|||||||
self.expect(TokenKind::RightBrace)?;
|
self.expect(TokenKind::RightBrace)?;
|
||||||
|
|
||||||
Ok(Node {
|
Ok(Node {
|
||||||
identity: Arc::new(NodeIdentity { location: token.location }),
|
identity: Rc::new(NodeIdentity { location: token.location }),
|
||||||
kind: UntypedKind::Constant(Value::Record(Arc::new(map))),
|
kind: UntypedKind::Constant(Value::Record(Rc::new(map))),
|
||||||
ty: (),
|
ty: (),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -266,7 +266,7 @@ impl<'a> Parser<'a> {
|
|||||||
fn make_id_node(&self, name: &str, identity: Identity) -> Node<UntypedKind> {
|
fn make_id_node(&self, name: &str, identity: Identity) -> Node<UntypedKind> {
|
||||||
Node {
|
Node {
|
||||||
identity,
|
identity,
|
||||||
kind: UntypedKind::Identifier(Arc::from(name)),
|
kind: UntypedKind::Identifier(Rc::from(name)),
|
||||||
ty: (),
|
ty: (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+16
-15
@@ -1,8 +1,9 @@
|
|||||||
use std::collections::{HashMap, BTreeMap};
|
use std::collections::{HashMap, BTreeMap};
|
||||||
use std::sync::Arc;
|
use std::rc::Rc;
|
||||||
|
use std::cell::RefCell;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::sync::Mutex;
|
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
|
use std::sync::Mutex; // Still needed for global keyword registry
|
||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
|
|
||||||
/// Simple source location
|
/// Simple source location
|
||||||
@@ -18,7 +19,7 @@ pub struct NodeIdentity {
|
|||||||
pub location: SourceLocation,
|
pub location: SourceLocation,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type Identity = Arc<NodeIdentity>;
|
pub type Identity = Rc<NodeIdentity>;
|
||||||
|
|
||||||
/// Interned string identifier
|
/// Interned string identifier
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||||
@@ -50,7 +51,7 @@ impl Keyword {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Interface for custom objects (Closures, Series, Streams)
|
/// Interface for custom objects (Closures, Series, Streams)
|
||||||
pub trait Object: fmt::Debug + Send + Sync {
|
pub trait Object: fmt::Debug {
|
||||||
fn type_name(&self) -> &'static str;
|
fn type_name(&self) -> &'static str;
|
||||||
fn as_any(&self) -> &dyn Any;
|
fn as_any(&self) -> &dyn Any;
|
||||||
}
|
}
|
||||||
@@ -62,13 +63,13 @@ pub enum Value {
|
|||||||
Bool(bool),
|
Bool(bool),
|
||||||
Int(i64),
|
Int(i64),
|
||||||
Float(f64),
|
Float(f64),
|
||||||
Text(Arc<str>),
|
Text(Rc<str>),
|
||||||
Keyword(Keyword),
|
Keyword(Keyword),
|
||||||
List(Arc<Vec<Value>>),
|
List(Rc<Vec<Value>>),
|
||||||
Record(Arc<HashMap<Keyword, Value>>),
|
Record(Rc<HashMap<Keyword, Value>>),
|
||||||
Function(Arc<dyn Fn(Vec<Value>) -> Value + Send + Sync>),
|
Function(Rc<dyn Fn(Vec<Value>) -> Value>),
|
||||||
Object(Arc<dyn Object>), // For compiled Closures and other opaque types
|
Object(Rc<dyn Object>), // For compiled Closures and other opaque types
|
||||||
Cell(Arc<Mutex<Value>>), // Boxed value for captures
|
Cell(Rc<RefCell<Value>>), // Boxed value for captures
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
@@ -81,7 +82,7 @@ pub enum StaticType {
|
|||||||
Text,
|
Text,
|
||||||
Keyword,
|
Keyword,
|
||||||
List(Box<StaticType>),
|
List(Box<StaticType>),
|
||||||
Record(Arc<BTreeMap<Keyword, StaticType>>),
|
Record(Rc<BTreeMap<Keyword, StaticType>>),
|
||||||
Function {
|
Function {
|
||||||
params: Vec<StaticType>,
|
params: Vec<StaticType>,
|
||||||
ret: Box<StaticType>,
|
ret: Box<StaticType>,
|
||||||
@@ -94,7 +95,7 @@ impl Value {
|
|||||||
match self {
|
match self {
|
||||||
Value::Void => false,
|
Value::Void => false,
|
||||||
Value::Bool(b) => *b,
|
Value::Bool(b) => *b,
|
||||||
Value::Cell(c) => c.lock().unwrap().is_truthy(),
|
Value::Cell(c) => c.borrow().is_truthy(),
|
||||||
_ => true,
|
_ => true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -108,10 +109,10 @@ impl Value {
|
|||||||
Value::Text(_) => StaticType::Text,
|
Value::Text(_) => StaticType::Text,
|
||||||
Value::Keyword(_) => StaticType::Keyword,
|
Value::Keyword(_) => StaticType::Keyword,
|
||||||
Value::List(_) => StaticType::List(Box::new(StaticType::Any)),
|
Value::List(_) => StaticType::List(Box::new(StaticType::Any)),
|
||||||
Value::Record(_) => StaticType::Record(Arc::new(BTreeMap::new())), // Empty for now
|
Value::Record(_) => StaticType::Record(Rc::new(BTreeMap::new())), // Empty for now
|
||||||
Value::Function { .. } => StaticType::Any, // Dynamic function
|
Value::Function { .. } => StaticType::Any, // Dynamic function
|
||||||
Value::Object(o) => StaticType::Object(o.type_name()),
|
Value::Object(o) => StaticType::Object(o.type_name()),
|
||||||
Value::Cell(c) => c.lock().unwrap().static_type(),
|
Value::Cell(c) => c.borrow().static_type(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -129,7 +130,7 @@ impl fmt::Display for Value {
|
|||||||
Value::Record(r) => write!(f, "<record[{}]>", r.len()),
|
Value::Record(r) => write!(f, "<record[{}]>", r.len()),
|
||||||
Value::Function(_) => write!(f, "<native fn>"),
|
Value::Function(_) => write!(f, "<native fn>"),
|
||||||
Value::Object(o) => write!(f, "<{}>", o.type_name()),
|
Value::Object(o) => write!(f, "<{}>", o.type_name()),
|
||||||
Value::Cell(c) => write!(f, "{}", c.lock().unwrap()),
|
Value::Cell(c) => write!(f, "{}", c.borrow()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+26
-22
@@ -1,4 +1,5 @@
|
|||||||
use std::sync::{Arc, Mutex};
|
use std::rc::Rc;
|
||||||
|
use std::cell::RefCell;
|
||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
use crate::ast::compiler::bound_nodes::{Address, BoundKind};
|
use crate::ast::compiler::bound_nodes::{Address, BoundKind};
|
||||||
use crate::ast::nodes::Node;
|
use crate::ast::nodes::Node;
|
||||||
@@ -6,8 +7,8 @@ use crate::ast::types::{Value, Object, StaticType};
|
|||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Closure {
|
pub struct Closure {
|
||||||
pub function_node: Arc<Node<BoundKind, StaticType>>,
|
pub function_node: Rc<Node<BoundKind, StaticType>>,
|
||||||
pub upvalues: Vec<Arc<Mutex<Value>>>,
|
pub upvalues: Vec<Rc<RefCell<Value>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Object for Closure {
|
impl Object for Closure {
|
||||||
@@ -22,17 +23,20 @@ impl Object for Closure {
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct CallFrame {
|
struct CallFrame {
|
||||||
stack_base: usize,
|
stack_base: usize,
|
||||||
closure: Option<Arc<Closure>>,
|
closure: Option<Rc<Closure>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct VM {
|
pub struct VM {
|
||||||
stack: Vec<Value>,
|
stack: Vec<Value>,
|
||||||
globals: Arc<Mutex<Vec<Value>>>,
|
// Globals are mutable shared state within the VM thread.
|
||||||
|
// However, if we move to frozen roots, this might change to a read-only structure.
|
||||||
|
// For now, mutable RefCell Vec is fine for single threaded execution.
|
||||||
|
globals: Rc<RefCell<Vec<Value>>>,
|
||||||
frames: Vec<CallFrame>,
|
frames: Vec<CallFrame>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VM {
|
impl VM {
|
||||||
pub fn new(globals: Arc<Mutex<Vec<Value>>>) -> Self {
|
pub fn new(globals: Rc<RefCell<Vec<Value>>>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
stack: Vec::new(),
|
stack: Vec::new(),
|
||||||
globals,
|
globals,
|
||||||
@@ -64,7 +68,7 @@ impl VM {
|
|||||||
BoundKind::DefGlobal { global_index, value } => {
|
BoundKind::DefGlobal { global_index, value } => {
|
||||||
let val = self.eval(value)?;
|
let val = self.eval(value)?;
|
||||||
let idx = *global_index as usize;
|
let idx = *global_index as usize;
|
||||||
let mut globals = self.globals.lock().unwrap();
|
let mut globals = self.globals.borrow_mut();
|
||||||
if idx >= globals.len() {
|
if idx >= globals.len() {
|
||||||
globals.resize(idx + 1, Value::Void);
|
globals.resize(idx + 1, Value::Void);
|
||||||
}
|
}
|
||||||
@@ -110,7 +114,7 @@ impl VM {
|
|||||||
upvalues: captured,
|
upvalues: captured,
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(Value::Object(Arc::new(closure)))
|
Ok(Value::Object(Rc::new(closure)))
|
||||||
},
|
},
|
||||||
|
|
||||||
BoundKind::Call { callee, args } => {
|
BoundKind::Call { callee, args } => {
|
||||||
@@ -126,13 +130,13 @@ impl VM {
|
|||||||
Value::Object(obj) => {
|
Value::Object(obj) => {
|
||||||
if let Some(closure) = obj.as_any().downcast_ref::<Closure>() {
|
if let Some(closure) = obj.as_any().downcast_ref::<Closure>() {
|
||||||
let old_stack_top = self.stack.len();
|
let old_stack_top = self.stack.len();
|
||||||
let closure_arc = Arc::new(closure.clone());
|
let closure_rc = Rc::new(closure.clone());
|
||||||
|
|
||||||
self.stack.extend(arg_vals);
|
self.stack.extend(arg_vals);
|
||||||
|
|
||||||
self.frames.push(CallFrame {
|
self.frames.push(CallFrame {
|
||||||
stack_base: old_stack_top,
|
stack_base: old_stack_top,
|
||||||
closure: Some(closure_arc.clone()),
|
closure: Some(closure_rc.clone()),
|
||||||
});
|
});
|
||||||
|
|
||||||
let result = self.eval(&closure.function_node);
|
let result = self.eval(&closure.function_node);
|
||||||
@@ -151,7 +155,7 @@ impl VM {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn capture_upvalue(&mut self, addr: Address) -> Result<Arc<Mutex<Value>>, String> {
|
fn capture_upvalue(&mut self, addr: Address) -> Result<Rc<RefCell<Value>>, String> {
|
||||||
match addr {
|
match addr {
|
||||||
Address::Local(idx) => {
|
Address::Local(idx) => {
|
||||||
let frame = self.frames.last().ok_or("No call frame")?;
|
let frame = self.frames.last().ok_or("No call frame")?;
|
||||||
@@ -163,7 +167,7 @@ impl VM {
|
|||||||
} else {
|
} else {
|
||||||
// Box it
|
// Box it
|
||||||
let val = self.stack[abs_index].clone();
|
let val = self.stack[abs_index].clone();
|
||||||
let cell = Arc::new(Mutex::new(val));
|
let cell = Rc::new(RefCell::new(val));
|
||||||
self.stack[abs_index] = Value::Cell(cell.clone());
|
self.stack[abs_index] = Value::Cell(cell.clone());
|
||||||
Ok(cell)
|
Ok(cell)
|
||||||
}
|
}
|
||||||
@@ -195,7 +199,7 @@ impl VM {
|
|||||||
let abs_index = frame.stack_base + (idx as usize);
|
let abs_index = frame.stack_base + (idx as usize);
|
||||||
if abs_index < self.stack.len() {
|
if abs_index < self.stack.len() {
|
||||||
match &self.stack[abs_index] {
|
match &self.stack[abs_index] {
|
||||||
Value::Cell(cell) => Ok(cell.lock().unwrap().clone()),
|
Value::Cell(cell) => Ok(cell.borrow().clone()),
|
||||||
val => Ok(val.clone()),
|
val => Ok(val.clone()),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -204,7 +208,7 @@ impl VM {
|
|||||||
},
|
},
|
||||||
Address::Global(idx) => {
|
Address::Global(idx) => {
|
||||||
let idx = idx as usize;
|
let idx = idx as usize;
|
||||||
let globals = self.globals.lock().unwrap();
|
let globals = self.globals.borrow();
|
||||||
if idx < globals.len() {
|
if idx < globals.len() {
|
||||||
Ok(globals[idx].clone())
|
Ok(globals[idx].clone())
|
||||||
} else {
|
} else {
|
||||||
@@ -216,7 +220,7 @@ impl VM {
|
|||||||
if let Some(closure) = &frame.closure {
|
if let Some(closure) = &frame.closure {
|
||||||
let idx = idx as usize;
|
let idx = idx as usize;
|
||||||
if idx < closure.upvalues.len() {
|
if idx < closure.upvalues.len() {
|
||||||
Ok(closure.upvalues[idx].lock().unwrap().clone())
|
Ok(closure.upvalues[idx].borrow().clone())
|
||||||
} else {
|
} else {
|
||||||
Err(format!("Upvalue access out of bounds {}", idx))
|
Err(format!("Upvalue access out of bounds {}", idx))
|
||||||
}
|
}
|
||||||
@@ -234,7 +238,7 @@ impl VM {
|
|||||||
let abs_index = frame.stack_base + (idx as usize);
|
let abs_index = frame.stack_base + (idx as usize);
|
||||||
if abs_index < self.stack.len() {
|
if abs_index < self.stack.len() {
|
||||||
if let Value::Cell(cell) = &self.stack[abs_index] {
|
if let Value::Cell(cell) = &self.stack[abs_index] {
|
||||||
*cell.lock().unwrap() = value;
|
*cell.borrow_mut() = value;
|
||||||
} else {
|
} else {
|
||||||
self.stack[abs_index] = value;
|
self.stack[abs_index] = value;
|
||||||
}
|
}
|
||||||
@@ -247,7 +251,7 @@ impl VM {
|
|||||||
},
|
},
|
||||||
Address::Global(idx) => {
|
Address::Global(idx) => {
|
||||||
let idx = idx as usize;
|
let idx = idx as usize;
|
||||||
let mut globals = self.globals.lock().unwrap();
|
let mut globals = self.globals.borrow_mut();
|
||||||
if idx >= globals.len() {
|
if idx >= globals.len() {
|
||||||
globals.resize(idx + 1, Value::Void);
|
globals.resize(idx + 1, Value::Void);
|
||||||
}
|
}
|
||||||
@@ -259,7 +263,7 @@ impl VM {
|
|||||||
if let Some(closure) = &frame.closure {
|
if let Some(closure) = &frame.closure {
|
||||||
let idx = idx as usize;
|
let idx = idx as usize;
|
||||||
if idx < closure.upvalues.len() {
|
if idx < closure.upvalues.len() {
|
||||||
*closure.upvalues[idx].lock().unwrap() = value;
|
*closure.upvalues[idx].borrow_mut() = value;
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err(format!("Upvalue assignment out of bounds {}", idx))
|
Err(format!("Upvalue assignment out of bounds {}", idx))
|
||||||
@@ -277,8 +281,8 @@ mod tests {
|
|||||||
use super::*;
|
use super::*;
|
||||||
use crate::ast::types::{SourceLocation, NodeIdentity};
|
use crate::ast::types::{SourceLocation, NodeIdentity};
|
||||||
|
|
||||||
fn make_dummy_identity() -> Arc<NodeIdentity> {
|
fn make_dummy_identity() -> Rc<NodeIdentity> {
|
||||||
Arc::new(NodeIdentity {
|
Rc::new(NodeIdentity {
|
||||||
location: SourceLocation { line: 0, col: 0 },
|
location: SourceLocation { line: 0, col: 0 },
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -338,7 +342,7 @@ mod tests {
|
|||||||
kind: BoundKind::Lambda {
|
kind: BoundKind::Lambda {
|
||||||
param_count: 0,
|
param_count: 0,
|
||||||
upvalues: vec![Address::Local(0)], // Capture x
|
upvalues: vec![Address::Local(0)], // Capture x
|
||||||
body: Arc::new(lambda_body),
|
body: Rc::new(lambda_body),
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
@@ -366,7 +370,7 @@ mod tests {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
let globals = Arc::new(Mutex::new(Vec::new()));
|
let globals = Rc::new(RefCell::new(Vec::new()));
|
||||||
let mut vm = VM::new(globals);
|
let mut vm = VM::new(globals);
|
||||||
|
|
||||||
let result = vm.run(&root);
|
let result = vm.run(&root);
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::sync::{Arc, Mutex};
|
use std::rc::Rc;
|
||||||
|
use std::cell::RefCell;
|
||||||
use crate::ast::parser::Parser;
|
use crate::ast::parser::Parser;
|
||||||
use crate::ast::compiler::binder::Binder;
|
use crate::ast::compiler::binder::Binder;
|
||||||
use crate::ast::vm::VM;
|
use crate::ast::vm::VM;
|
||||||
@@ -74,11 +75,11 @@ mod tests {
|
|||||||
let mut parser = Parser::new(source).expect("Failed to create parser");
|
let mut parser = Parser::new(source).expect("Failed to create parser");
|
||||||
let ast = parser.parse_expression().expect("Failed to parse");
|
let ast = parser.parse_expression().expect("Failed to parse");
|
||||||
|
|
||||||
let globals = Arc::new(Mutex::new(std::collections::HashMap::new()));
|
let globals = Rc::new(RefCell::new(std::collections::HashMap::new()));
|
||||||
let mut binder = Binder::new(globals.clone());
|
let mut binder = Binder::new(globals.clone());
|
||||||
let bound_ast = binder.bind(&ast).expect("Failed to bind");
|
let bound_ast = binder.bind(&ast).expect("Failed to bind");
|
||||||
|
|
||||||
let vm_globals = Arc::new(Mutex::new(Vec::new()));
|
let vm_globals = Rc::new(RefCell::new(Vec::new()));
|
||||||
let mut vm = VM::new(vm_globals);
|
let mut vm = VM::new(vm_globals);
|
||||||
let result = vm.run(&bound_ast);
|
let result = vm.run(&bound_ast);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user