98deb8f3fe
The `eval` and `eval_observed` methods in the `VM` struct were very similar, with the primary difference being the call to the `VMObserver` trait. This commit refactors these methods into a single macro, `dispatch_eval!`, which reduces code duplication and improves maintainability. The `Value::TailCallRequest` variant was also updated to use `Box` for its contents, which helps keep the `Value` enum size consistent. Finally, the benchmarks in the `examples` directory have been updated to reflect minor performance changes resulting from these code modifications.
194 lines
6.4 KiB
Rust
194 lines
6.4 KiB
Rust
use std::collections::{HashMap, BTreeMap};
|
|
use std::rc::Rc;
|
|
use std::cell::RefCell;
|
|
use std::fmt;
|
|
use lazy_static::lazy_static;
|
|
use std::sync::Mutex; // Still needed for global keyword registry
|
|
use std::any::Any;
|
|
|
|
/// Simple source location
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
|
pub struct SourceLocation {
|
|
pub line: u32,
|
|
pub col: u32,
|
|
}
|
|
|
|
/// Shared identity for nodes (Location, etc.)
|
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
|
pub struct NodeIdentity {
|
|
pub location: SourceLocation,
|
|
}
|
|
|
|
pub type Identity = Rc<NodeIdentity>;
|
|
|
|
/// Interned string identifier
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
|
pub struct Keyword(pub u32);
|
|
|
|
lazy_static! {
|
|
static ref KEYWORD_REGISTRY: Mutex<HashMap<String, u32>> = Mutex::new(HashMap::new());
|
|
static ref KEYWORD_REVERSE: Mutex<Vec<String>> = Mutex::new(Vec::new());
|
|
}
|
|
|
|
impl Keyword {
|
|
pub fn intern(name: &str) -> Self {
|
|
let mut reg = KEYWORD_REGISTRY.lock().unwrap();
|
|
if let Some(&id) = reg.get(name) {
|
|
Keyword(id)
|
|
} else {
|
|
let mut rev = KEYWORD_REVERSE.lock().unwrap();
|
|
let id = rev.len() as u32;
|
|
reg.insert(name.to_string(), id);
|
|
rev.push(name.to_string());
|
|
Keyword(id)
|
|
}
|
|
}
|
|
|
|
pub fn name(&self) -> String {
|
|
let rev = KEYWORD_REVERSE.lock().unwrap();
|
|
rev[self.0 as usize].clone()
|
|
}
|
|
}
|
|
|
|
/// Interface for custom objects (Closures, Series, Streams)
|
|
pub trait Object: fmt::Debug {
|
|
fn type_name(&self) -> &'static str;
|
|
fn as_any(&self) -> &dyn Any;
|
|
}
|
|
|
|
/// Core data value in Myc Script (similar to TDataValue)
|
|
#[derive(Clone)]
|
|
pub enum Value {
|
|
Void,
|
|
Bool(bool),
|
|
Int(i64),
|
|
Float(f64),
|
|
Text(Rc<str>),
|
|
Keyword(Keyword),
|
|
List(Rc<Vec<Value>>),
|
|
Record(Rc<HashMap<Keyword, Value>>),
|
|
Function(Rc<dyn Fn(Vec<Value>) -> Value>),
|
|
Object(Rc<dyn Object>), // For compiled Closures and other opaque types
|
|
Cell(Rc<RefCell<Value>>), // Boxed value for captures
|
|
TailCallRequest(Box<(Rc<dyn Object>, Vec<Value>)>), // Internal: For TCO (Boxed to keep Value small)
|
|
}
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
|
pub enum StaticType {
|
|
Any,
|
|
Void,
|
|
Bool,
|
|
Int,
|
|
Float,
|
|
Text,
|
|
Keyword,
|
|
List(Box<StaticType>),
|
|
Record(Rc<BTreeMap<Keyword, StaticType>>),
|
|
Function {
|
|
params: Vec<StaticType>,
|
|
ret: Box<StaticType>,
|
|
},
|
|
Object(&'static str),
|
|
}
|
|
|
|
impl fmt::Display for StaticType {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
match self {
|
|
StaticType::Any => write!(f, "any"),
|
|
StaticType::Void => write!(f, "void"),
|
|
StaticType::Bool => write!(f, "bool"),
|
|
StaticType::Int => write!(f, "int"),
|
|
StaticType::Float => write!(f, "float"),
|
|
StaticType::Text => write!(f, "text"),
|
|
StaticType::Keyword => write!(f, "keyword"),
|
|
StaticType::List(inner) => write!(f, "[{}]", inner),
|
|
StaticType::Record(fields) => {
|
|
write!(f, "{{")?;
|
|
for (i, (k, v)) in fields.iter().enumerate() {
|
|
if i > 0 { write!(f, ", ")?; }
|
|
write!(f, ":{} {}", k.name(), v)?;
|
|
}
|
|
write!(f, "}}")
|
|
},
|
|
StaticType::Function { params, ret } => {
|
|
write!(f, "fn(")?;
|
|
for (i, p) in params.iter().enumerate() {
|
|
if i > 0 { write!(f, " ")?; }
|
|
write!(f, "{}", p)?;
|
|
}
|
|
write!(f, ") -> {}", ret)
|
|
},
|
|
StaticType::Object(name) => write!(f, "{}", name),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Value {
|
|
pub fn is_truthy(&self) -> bool {
|
|
match self {
|
|
Value::Void => false,
|
|
Value::Bool(b) => *b,
|
|
Value::Cell(c) => c.borrow().is_truthy(),
|
|
_ => true,
|
|
}
|
|
}
|
|
|
|
pub fn static_type(&self) -> StaticType {
|
|
match self {
|
|
Value::Void => StaticType::Void,
|
|
Value::Bool(_) => StaticType::Bool,
|
|
Value::Int(_) => StaticType::Int,
|
|
Value::Float(_) => StaticType::Float,
|
|
Value::Text(_) => StaticType::Text,
|
|
Value::Keyword(_) => StaticType::Keyword,
|
|
Value::List(_) => StaticType::List(Box::new(StaticType::Any)),
|
|
Value::Record(_) => StaticType::Record(Rc::new(BTreeMap::new())), // Empty for now
|
|
Value::Function { .. } => StaticType::Any, // Dynamic function
|
|
Value::Object(o) => StaticType::Object(o.type_name()),
|
|
Value::Cell(c) => c.borrow().static_type(),
|
|
Value::TailCallRequest(_) => panic!("Internal VM state leaked: TailCallRequest"),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl fmt::Display for Value {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
match self {
|
|
Value::Void => write!(f, "void"),
|
|
Value::Bool(b) => write!(f, "{}", b),
|
|
Value::Int(i) => write!(f, "{}", i),
|
|
Value::Float(fl) => write!(f, "{}", fl),
|
|
Value::Text(t) => write!(f, "\"{}\"", t),
|
|
Value::Keyword(k) => write!(f, ":{}", k.name()),
|
|
Value::List(l) => {
|
|
write!(f, "[")?;
|
|
for (i, val) in l.iter().enumerate() {
|
|
if i > 0 { write!(f, " ")?; }
|
|
write!(f, "{}", val)?;
|
|
}
|
|
write!(f, "]")
|
|
},
|
|
Value::Record(r) => {
|
|
let mut entries: Vec<_> = r.iter().collect();
|
|
entries.sort_by_key(|(k, _)| k.name());
|
|
write!(f, "{{")?;
|
|
for (i, (k, v)) in entries.iter().enumerate() {
|
|
if i > 0 { write!(f, ", ")?; }
|
|
write!(f, ":{} {}", k.name(), v)?;
|
|
}
|
|
write!(f, "}}")
|
|
},
|
|
Value::Function(_) => write!(f, "<native fn>"),
|
|
Value::Object(o) => write!(f, "<{}>", o.type_name()),
|
|
Value::Cell(c) => write!(f, "{}", c.borrow()),
|
|
Value::TailCallRequest(_) => panic!("Internal VM state leaked: TailCallRequest"),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl fmt::Debug for Value {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
fmt::Display::fmt(self, f)
|
|
}
|
|
}
|