#![allow(dead_code, private_interfaces, missing_docs)]
use crate::ast::{CaseLambdaClause, Expr, Formals};
use crate::diagnostics::{Span, Spanned};
use crate::effects::Effect;
use crate::utils::SymbolId;
use std::collections::HashMap;
use std::fmt;
use std::hash::{Hash, Hasher};
use std::sync::{Arc, RwLock};
type EnvironmentBindings = Arc<RwLock<Vec<(Arc<str>, OptimizedValue)>>>;
pub type Generation = u64;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(u8)]
pub enum ValueTag {
Nil = 0,
Boolean = 1,
Fixnum = 2, Character = 3, Unspecified = 4,
Symbol = 5,
String = 6,
Number = 7, Keyword = 8,
Pair = 9,
Vector = 10,
Procedure = 11,
CaseLambda = 12,
Primitive = 13,
Continuation = 14,
Port = 15,
Promise = 16,
Type = 17,
Foreign = 18,
ErrorObject = 19,
CharSet = 20,
Parameter = 21,
Record = 22,
Bytevector = 23,
Hashtable = 24,
Syntax = 25,
}
#[derive(Clone)]
pub struct OptimizedValue {
pub tag: ValueTag,
pub data: ValueData,
}
#[derive(Clone, Copy)]
pub union ValueData {
pub immediate: u64,
pub ptr: *const dyn ValueObj,
}
impl Hash for OptimizedValue {
fn hash<H: Hasher>(&self, state: &mut H) {
self.tag.hash(state);
match self.tag {
ValueTag::Nil | ValueTag::Unspecified => {},
ValueTag::Boolean | ValueTag::Fixnum | ValueTag::Character | ValueTag::Symbol => {
unsafe { self.data.immediate.hash(state); }
}
_ => {
unsafe {
let obj = &*self.data.ptr;
state.write_u64(obj.hash_obj());
}
}
}
}
}
trait ValueObj: Send + Sync + std::any::Any {
fn tag(&self) -> ValueTag;
fn clone_obj(&self) -> Box<dyn ValueObj>;
fn eq_obj(&self, other: &dyn ValueObj) -> bool;
fn hash_obj(&self) -> u64;
fn fmt_obj(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result;
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
struct StringObj {
content: Arc<str>,
}
#[derive(Debug, Clone, PartialEq)]
struct NumberObj {
value: f64,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
struct SymbolObj {
id: SymbolId,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
struct KeywordObj {
name: Arc<str>,
}
#[derive(Debug, Clone)]
struct PairObj {
car: OptimizedValue,
cdr: OptimizedValue,
}
#[derive(Debug, Clone)]
struct VectorObj {
elements: Arc<RwLock<Vec<OptimizedValue>>>,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
struct BytevectorObj {
bytes: Arc<[u8]>,
}
#[derive(Debug, Clone)]
struct ProcedureObj {
formals: Formals,
body: Vec<Spanned<Expr>>,
environment: Arc<OptimizedEnvironment>,
name: Option<Arc<str>>,
metadata: HashMap<String, OptimizedValue>,
source: Option<Span>,
}
#[derive(Debug, Clone)]
struct CaseLambdaObj {
clauses: Vec<CaseLambdaClause>,
environment: Arc<OptimizedEnvironment>,
name: Option<Arc<str>>,
metadata: HashMap<String, OptimizedValue>,
source: Option<Span>,
}
#[derive(Debug, Clone)]
struct PrimitiveObj {
name: Arc<str>,
arity_min: usize,
arity_max: Option<usize>,
implementation: PrimitiveImpl,
effects: Vec<Effect>,
}
#[derive(Debug, Clone)]
struct ContinuationObj {
stack: Vec<OptimizedFrame>,
environment: Arc<OptimizedEnvironment>,
id: u64,
current_expr: Option<Spanned<Expr>>,
invoked: Arc<std::sync::atomic::AtomicBool>,
}
#[derive(Debug, Clone)]
pub enum OptimizedFrame {
Application {
operator: OptimizedValue,
evaluated_args: Vec<OptimizedValue>,
remaining_args: Vec<Spanned<Expr>>,
environment: Arc<OptimizedEnvironment>,
source: Span,
},
If {
consequent: Spanned<Expr>,
alternative: Box<Option<Spanned<Expr>>>,
environment: Arc<OptimizedEnvironment>,
source: Span,
},
}
#[derive(Debug, Clone)]
pub struct OptimizedEnvironment {
bindings: EnvironmentBindings,
parent: Option<Arc<OptimizedEnvironment>>,
generation: Generation,
name: Option<Arc<str>>,
}
#[derive(Debug, Clone)]
pub enum PrimitiveImpl {
RustFn(fn(&[OptimizedValue]) -> crate::diagnostics::Result<OptimizedValue>),
ForeignFn {
library: String,
symbol: String,
},
}
impl OptimizedValue {
#[inline]
pub fn nil() -> Self {
Self {
tag: ValueTag::Nil,
data: ValueData { immediate: 0 },
}
}
#[inline]
pub fn boolean(b: bool) -> Self {
Self {
tag: ValueTag::Boolean,
data: ValueData { immediate: b as u64 },
}
}
#[inline]
pub fn fixnum(n: i64) -> Self {
if n >= i32::MIN as i64 && n <= i32::MAX as i64 {
Self {
tag: ValueTag::Fixnum,
data: ValueData { immediate: n as u32 as u64 },
}
} else {
Self::number(n as f64)
}
}
#[inline]
pub fn character(ch: char) -> Self {
Self {
tag: ValueTag::Character,
data: ValueData { immediate: ch as u32 as u64 },
}
}
#[inline]
pub fn unspecified() -> Self {
Self {
tag: ValueTag::Unspecified,
data: ValueData { immediate: 0 },
}
}
pub fn string(s: impl Into<String>) -> Self {
let string_obj = Box::new(StringObj {
content: Arc::from(s.into().as_str()),
});
Self {
tag: ValueTag::String,
data: ValueData { ptr: Box::into_raw(string_obj) as *const dyn ValueObj },
}
}
pub fn number(n: f64) -> Self {
if n.fract() == 0.0 && n >= i32::MIN as f64 && n <= i32::MAX as f64 {
Self::fixnum(n as i64)
} else {
let number_obj = Box::new(NumberObj { value: n });
Self {
tag: ValueTag::Number,
data: ValueData { ptr: Box::into_raw(number_obj) as *const dyn ValueObj },
}
}
}
pub fn symbol(id: SymbolId) -> Self {
let id_bits = id.id() as u64;
if id_bits <= u32::MAX as u64 {
Self {
tag: ValueTag::Symbol,
data: ValueData { immediate: id_bits },
}
} else {
let symbol_obj = Box::new(SymbolObj { id });
Self {
tag: ValueTag::Symbol,
data: ValueData { ptr: Box::into_raw(symbol_obj) as *const dyn ValueObj },
}
}
}
pub fn pair(car: OptimizedValue, cdr: OptimizedValue) -> Self {
let pair_obj = Box::new(PairObj { car, cdr });
Self {
tag: ValueTag::Pair,
data: ValueData { ptr: Box::into_raw(pair_obj) as *const dyn ValueObj },
}
}
pub fn vector(elements: Vec<OptimizedValue>) -> Self {
let vector_obj = Box::new(VectorObj {
elements: Arc::new(RwLock::new(elements)),
});
Self {
tag: ValueTag::Vector,
data: ValueData { ptr: Box::into_raw(vector_obj) as *const dyn ValueObj },
}
}
pub fn bytevector(bytes: Vec<u8>) -> Self {
let bytevector_obj = Box::new(BytevectorObj {
bytes: Arc::from(bytes),
});
Self {
tag: ValueTag::Bytevector,
data: ValueData { ptr: Box::into_raw(bytevector_obj) as *const dyn ValueObj },
}
}
#[inline]
pub fn is_truthy(&self) -> bool {
!matches!(self.tag, ValueTag::Boolean) || unsafe { self.data.immediate != 0 }
}
#[inline]
pub fn is_falsy(&self) -> bool {
matches!(self.tag, ValueTag::Boolean) && unsafe { self.data.immediate == 0 }
}
#[inline]
pub fn is_number(&self) -> bool {
matches!(self.tag, ValueTag::Fixnum | ValueTag::Number)
}
pub fn as_number(&self) -> Option<f64> {
match self.tag {
ValueTag::Fixnum => {
let n = unsafe { self.data.immediate as i32 };
Some(n as f64)
}
ValueTag::Number => {
let obj = unsafe { &*(self.data.ptr as *const NumberObj) };
Some(obj.value)
}
_ => None,
}
}
pub fn as_integer(&self) -> Option<i64> {
match self.tag {
ValueTag::Fixnum => {
let n = unsafe { self.data.immediate as i32 };
Some(n as i64)
}
ValueTag::Number => {
let obj = unsafe { &*(self.data.ptr as *const NumberObj) };
if obj.value.fract() == 0.0 {
Some(obj.value as i64)
} else {
None
}
}
_ => None,
}
}
pub fn as_string(&self) -> Option<&str> {
match self.tag {
ValueTag::String => {
let obj = unsafe { &*(self.data.ptr as *const StringObj) };
Some(&obj.content)
}
_ => None,
}
}
pub fn as_symbol(&self) -> Option<SymbolId> {
match self.tag {
ValueTag::Symbol => {
let id_bits = unsafe { self.data.immediate };
if id_bits <= u32::MAX as u64 {
Some(SymbolId::new(id_bits as usize))
} else {
let obj = unsafe { &*(self.data.ptr as *const SymbolObj) };
Some(obj.id)
}
}
_ => None,
}
}
pub fn list(values: Vec<OptimizedValue>) -> Self {
values.into_iter().rev().fold(Self::nil(), |acc, val| {
Self::pair(val, acc)
})
}
pub fn as_list(&self) -> Option<Vec<OptimizedValue>> {
let mut result = Vec::new();
let mut current = self;
loop {
match current.tag {
ValueTag::Nil => return Some(result),
ValueTag::Pair => {
let obj = unsafe { &*(current.data.ptr as *const PairObj) };
result.push(obj.car.clone());
current = &obj.cdr;
}
_ => return None, }
}
}
#[inline]
pub fn t() -> Self {
Self::boolean(true)
}
#[inline]
pub fn f() -> Self {
Self::boolean(false)
}
}
impl fmt::Debug for OptimizedValue {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.tag {
ValueTag::Nil => write!(f, "nil"),
ValueTag::Boolean => {
let b = unsafe { self.data.immediate != 0 };
write!(f, "{b}")
}
ValueTag::Fixnum => {
let n = unsafe { self.data.immediate as i32 };
write!(f, "{n}")
}
ValueTag::Character => {
let ch = unsafe { char::from_u32(self.data.immediate as u32).unwrap_or('?') };
write!(f, "#\\{ch}")
}
ValueTag::Unspecified => write!(f, "#<unspecified>"),
_ => {
let obj = unsafe { &*self.data.ptr };
obj.fmt_obj(f)
}
}
}
}
impl fmt::Display for OptimizedValue {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.tag {
ValueTag::Nil => write!(f, "()"),
ValueTag::Boolean => {
let b = unsafe { self.data.immediate != 0 };
write!(f, "#{}", if b { "t" } else { "f" })
}
ValueTag::Fixnum => {
let n = unsafe { self.data.immediate as i32 };
write!(f, "{n}")
}
ValueTag::Character => {
let ch = unsafe { char::from_u32(self.data.immediate as u32).unwrap_or('?') };
write!(f, "#\\{ch}")
}
ValueTag::Unspecified => write!(f, "#<unspecified>"),
ValueTag::String => {
let obj = unsafe { &*(self.data.ptr as *const StringObj) };
write!(f, "\"{}\"", obj.content)
}
ValueTag::Number => {
let obj = unsafe { &*(self.data.ptr as *const NumberObj) };
write!(f, "{value}", value = obj.value)
}
ValueTag::Symbol => {
let obj = unsafe { &*(self.data.ptr as *const SymbolObj) };
if let Some(name) = crate::utils::symbol_name(obj.id) {
write!(f, "{name}")
} else {
write!(f, "#<symbol:{}>", obj.id.id())
}
}
ValueTag::Pair => {
write!(f, "(")?;
self.write_list_contents(f, true)?;
write!(f, ")")
}
ValueTag::Vector => {
let obj = unsafe { &*(self.data.ptr as *const VectorObj) };
write!(f, "#(")?;
if let Ok(elements) = obj.elements.read() {
for (i, element) in elements.iter().enumerate() {
if i > 0 { write!(f, " ")?; }
write!(f, "{element}")?;
}
}
write!(f, ")")
}
_ => write!(f, "#<{:?}>", self.tag),
}
}
}
impl OptimizedValue {
fn write_list_contents(&self, f: &mut fmt::Formatter<'_>, first: bool) -> fmt::Result {
match self.tag {
ValueTag::Nil => Ok(()),
ValueTag::Pair => {
let obj = unsafe { &*(self.data.ptr as *const PairObj) };
if !first { write!(f, " ")?; }
write!(f, "{car}", car = obj.car)?;
match obj.cdr.tag {
ValueTag::Nil => Ok(()),
ValueTag::Pair => obj.cdr.write_list_contents(f, false),
_ => write!(f, " . {}", obj.cdr),
}
}
_ => write!(f, " . {self}"),
}
}
}
impl PartialEq for OptimizedValue {
fn eq(&self, other: &Self) -> bool {
if self.tag != other.tag {
return false;
}
match self.tag {
ValueTag::Nil | ValueTag::Unspecified => true,
ValueTag::Boolean | ValueTag::Fixnum | ValueTag::Character => {
unsafe { self.data.immediate == other.data.immediate }
}
_ => {
let self_obj = unsafe { &*self.data.ptr };
let other_obj = unsafe { &*other.data.ptr };
self_obj.eq_obj(other_obj)
}
}
}
}
impl Eq for OptimizedValue {}
impl Drop for OptimizedValue {
fn drop(&mut self) {
match self.tag {
ValueTag::Nil | ValueTag::Boolean | ValueTag::Fixnum |
ValueTag::Character | ValueTag::Unspecified => {
}
_ => {
unsafe {
let _boxed = Box::from_raw(self.data.ptr as *mut dyn ValueObj);
}
}
}
}
}
impl ValueObj for StringObj {
fn tag(&self) -> ValueTag { ValueTag::String }
fn clone_obj(&self) -> Box<dyn ValueObj> { Box::new(self.clone()) }
fn eq_obj(&self, other: &dyn ValueObj) -> bool {
if let Some(other_str) = (other as &dyn std::any::Any).downcast_ref::<StringObj>() {
self == other_str
} else {
false
}
}
fn hash_obj(&self) -> u64 {
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
let mut hasher = DefaultHasher::new();
self.content.hash(&mut hasher);
hasher.finish()
}
fn fmt_obj(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "\"{}\"", self.content)
}
}
impl ValueObj for NumberObj {
fn tag(&self) -> ValueTag { ValueTag::Number }
fn clone_obj(&self) -> Box<dyn ValueObj> { Box::new(self.clone()) }
fn eq_obj(&self, other: &dyn ValueObj) -> bool {
if let Some(other_num) = (other as &dyn std::any::Any).downcast_ref::<NumberObj>() {
self == other_num
} else {
false
}
}
fn hash_obj(&self) -> u64 {
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
let mut hasher = DefaultHasher::new();
self.value.to_bits().hash(&mut hasher);
hasher.finish()
}
fn fmt_obj(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{value}", value = self.value)
}
}
impl ValueObj for SymbolObj {
fn tag(&self) -> ValueTag { ValueTag::Symbol }
fn clone_obj(&self) -> Box<dyn ValueObj> { Box::new(self.clone()) }
fn eq_obj(&self, other: &dyn ValueObj) -> bool {
if let Some(other_sym) = (other as &dyn std::any::Any).downcast_ref::<SymbolObj>() {
self == other_sym
} else {
false
}
}
fn hash_obj(&self) -> u64 {
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
let mut hasher = DefaultHasher::new();
self.id.hash(&mut hasher);
hasher.finish()
}
fn fmt_obj(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Some(name) = crate::utils::symbol_name(self.id) {
write!(f, "{name}")
} else {
write!(f, "#<symbol:{}>", self.id.id())
}
}
}
impl ValueObj for PairObj {
fn tag(&self) -> ValueTag { ValueTag::Pair }
fn clone_obj(&self) -> Box<dyn ValueObj> { Box::new(self.clone()) }
fn eq_obj(&self, other: &dyn ValueObj) -> bool {
if let Some(other_pair) = (other as &dyn std::any::Any).downcast_ref::<PairObj>() {
self.car == other_pair.car && self.cdr == other_pair.cdr
} else {
false
}
}
fn hash_obj(&self) -> u64 {
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
let mut hasher = DefaultHasher::new();
self.car.hash(&mut hasher);
self.cdr.hash(&mut hasher);
hasher.finish()
}
fn fmt_obj(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "({} . {})", self.car, self.cdr)
}
}
impl ValueObj for VectorObj {
fn tag(&self) -> ValueTag { ValueTag::Vector }
fn clone_obj(&self) -> Box<dyn ValueObj> { Box::new(self.clone()) }
fn eq_obj(&self, other: &dyn ValueObj) -> bool {
if let Some(other_vec) = (other as &dyn std::any::Any).downcast_ref::<VectorObj>() {
if let (Ok(self_elements), Ok(other_elements)) = (self.elements.read(), other_vec.elements.read()) {
*self_elements == *other_elements
} else {
false
}
} else {
false
}
}
fn hash_obj(&self) -> u64 {
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
let mut hasher = DefaultHasher::new();
if let Ok(elements) = self.elements.read() {
for element in elements.iter() {
element.hash(&mut hasher);
}
}
hasher.finish()
}
fn fmt_obj(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "#(")?;
if let Ok(elements) = self.elements.read() {
for (i, element) in elements.iter().enumerate() {
if i > 0 { write!(f, " ")?; }
write!(f, "{element}")?;
}
}
write!(f, ")")
}
}
impl ValueObj for BytevectorObj {
fn tag(&self) -> ValueTag { ValueTag::Bytevector }
fn clone_obj(&self) -> Box<dyn ValueObj> { Box::new(self.clone()) }
fn eq_obj(&self, other: &dyn ValueObj) -> bool {
if let Some(other_bv) = (other as &dyn std::any::Any).downcast_ref::<BytevectorObj>() {
self == other_bv
} else {
false
}
}
fn hash_obj(&self) -> u64 {
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
let mut hasher = DefaultHasher::new();
self.bytes.hash(&mut hasher);
hasher.finish()
}
fn fmt_obj(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "#u8(")?;
for (i, byte) in self.bytes.iter().enumerate() {
if i > 0 { write!(f, " ")?; }
write!(f, "{byte}")?;
}
write!(f, ")")
}
}
impl OptimizedEnvironment {
pub fn new(parent: Option<Arc<OptimizedEnvironment>>, generation: Generation) -> Self {
Self {
bindings: Arc::new(RwLock::new(Vec::new())),
parent,
generation,
name: None,
}
}
pub fn lookup(&self, name: &str) -> Option<OptimizedValue> {
if let Ok(bindings) = self.bindings.read() {
for (var_name, value) in bindings.iter() {
if var_name.as_ref() == name {
return Some(value.clone());
}
}
}
if let Some(parent) = &self.parent {
parent.lookup(name)
} else {
None
}
}
pub fn define(&self, name: String, value: OptimizedValue) {
if let Ok(mut bindings) = self.bindings.write() {
bindings.retain(|(var_name, _)| var_name.as_ref() != name);
bindings.push((Arc::from(name.as_str()), value));
}
}
pub fn set(&self, name: &str, value: OptimizedValue) -> bool {
if let Ok(mut bindings) = self.bindings.write() {
for (var_name, var_value) in bindings.iter_mut() {
if var_name.as_ref() == name {
*var_value = value;
return true;
}
}
}
if let Some(parent) = &self.parent {
parent.set(name, value)
} else {
false
}
}
pub fn extend(&self, generation: Generation) -> Arc<OptimizedEnvironment> {
Arc::new(OptimizedEnvironment::new(
Some(Arc::new(self.clone())),
generation,
))
}
pub fn variable_names(&self) -> Vec<String> {
if let Ok(bindings) = self.bindings.read() {
bindings.iter().map(|(name, _)| name.to_string()).collect()
} else {
Vec::new()
}
}
}
unsafe impl Send for OptimizedValue {}
unsafe impl Sync for OptimizedValue {}
unsafe impl Send for OptimizedEnvironment {}
unsafe impl Sync for OptimizedEnvironment {}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_immediate_values() {
let nil = OptimizedValue::nil();
let t_val = OptimizedValue::boolean(true);
let f_val = OptimizedValue::boolean(false);
let num = OptimizedValue::fixnum(42i64);
let ch = OptimizedValue::character('A');
assert!(nil.tag == ValueTag::Nil);
assert!(t_val.is_truthy());
assert!(f_val.is_falsy());
assert_eq!(num.as_integer(), Some(42));
assert_eq!(ch.tag, ValueTag::Character);
}
#[test]
fn test_string_values() {
let s1 = OptimizedValue::string("hello");
let s2 = OptimizedValue::string("hello");
let s3 = OptimizedValue::string("world");
assert_eq!(s1.as_string(), Some("hello"));
assert_eq!(s1, s2); assert_ne!(s1, s3); }
#[test]
fn test_number_optimization() {
let small_int = OptimizedValue::number(42.0);
let large_int = OptimizedValue::number(1e10);
let float_val = OptimizedValue::number(3.4);
assert_eq!(small_int.tag, ValueTag::Fixnum);
assert_eq!(small_int.as_integer(), Some(42));
assert_eq!(large_int.tag, ValueTag::Number);
assert_eq!(float_val.tag, ValueTag::Number);
}
#[test]
fn test_list_operations() {
let list = OptimizedValue::list(vec![
OptimizedValue::fixnum(1i64),
OptimizedValue::fixnum(2i64),
OptimizedValue::fixnum(3i64),
]);
let as_vec = list.as_list().unwrap();
assert_eq!(as_vec.len(), 3);
assert_eq!(as_vec[0].as_integer(), Some(1));
assert_eq!(as_vec[1].as_integer(), Some(2));
assert_eq!(as_vec[2].as_integer(), Some(3));
}
#[test]
fn test_environment_operations() {
let env = OptimizedEnvironment::new(None, 0);
env.define("x".to_string(), OptimizedValue::fixnum(42i64));
env.define("y".to_string(), OptimizedValue::string("hello"));
assert_eq!(env.lookup("x").unwrap().as_integer(), Some(42));
assert_eq!(env.lookup("y").unwrap().as_string(), Some("hello"));
assert!(env.lookup("z").is_none());
assert!(env.set("x", OptimizedValue::fixnum(24i64)));
assert_eq!(env.lookup("x").unwrap().as_integer(), Some(24));
assert!(!env.set("z", OptimizedValue::fixnum(99i64)));
}
}