Files
RustAst/src/ast/types.rs
T
Michael Schimmel 98deb8f3fe Refactor VM evaluation to use macro
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.
2026-02-18 02:01:27 +01:00

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)
}
}