pub type VarMap = rustc_hash::FxHashMap<String, Value>;
use std::rc::Rc;
use std::cell::RefCell;
use std::sync::atomic::{AtomicU64, Ordering};
use crate::parser::Expr;
static GENSYM_CTR: AtomicU64 = AtomicU64::new(0);
pub fn gensym_name(prefix: &str) -> String {
let n = GENSYM_CTR.fetch_add(1, Ordering::Relaxed);
format!("{}__{}", prefix, n)
}
struct ListBuf {
cells: Vec<std::cell::UnsafeCell<Value>>,
floor: std::cell::Cell<usize>,
}
impl Drop for ListBuf {
fn drop(&mut self) {
let mut pending: Vec<Value> = Vec::new();
for cell in self.cells.drain(..) {
if let Value::List(ls) = cell.into_inner() {
if let Ok(mut buf) = Rc::try_unwrap(ls.data) {
pending.extend(buf.cells.drain(..).map(|c| c.into_inner()));
}
}
}
while let Some(v) = pending.pop() {
if let Value::List(ls) = v {
if let Ok(mut buf) = Rc::try_unwrap(ls.data) {
pending.extend(buf.cells.drain(..).map(|c| c.into_inner()));
}
}
}
}
}
impl ListBuf {
fn with_gap(gap: usize, head: Value, tail: &[Value]) -> LSlice {
let mut cells = Vec::with_capacity(gap + 1 + tail.len());
for _ in 0..gap { cells.push(std::cell::UnsafeCell::new(Value::Nil)); }
cells.push(std::cell::UnsafeCell::new(head));
for v in tail { cells.push(std::cell::UnsafeCell::new(v.clone())); }
LSlice { data: Rc::new(ListBuf { cells, floor: std::cell::Cell::new(gap) }), start: gap }
}
}
#[derive(Clone)]
pub struct LSlice {
data: Rc<ListBuf>,
start: usize,
}
impl LSlice {
pub fn new(vals: Vec<Value>) -> Self {
let cells: Vec<std::cell::UnsafeCell<Value>> =
vals.into_iter().map(std::cell::UnsafeCell::new).collect();
LSlice { data: Rc::new(ListBuf { cells, floor: std::cell::Cell::new(0) }), start: 0 }
}
pub fn advance(&self, n: usize) -> LSlice {
debug_assert!(n <= self.len(), "LSlice::advance past end");
LSlice { data: self.data.clone(), start: self.start + n }
}
pub fn tail(&self) -> LSlice {
debug_assert!(!self.is_empty(), "LSlice::tail of empty list");
self.advance(1)
}
pub fn prepend(&self, head: Value) -> LSlice {
if self.start > 0 && self.start == self.data.floor.get() {
unsafe { *self.data.cells[self.start - 1].get() = head; }
self.data.floor.set(self.start - 1);
return LSlice { data: self.data.clone(), start: self.start - 1 };
}
ListBuf::with_gap((self.len() + 1).max(4), head, self)
}
}
impl std::ops::Deref for LSlice {
type Target = [Value];
#[inline]
fn deref(&self) -> &[Value] {
unsafe {
std::slice::from_raw_parts(
self.data.cells.as_ptr().add(self.start) as *const Value,
self.data.cells.len() - self.start,
)
}
}
}
impl std::fmt::Debug for LSlice {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
(**self).fmt(f)
}
}
#[derive(Clone, Debug)]
pub enum Value {
Number(f64),
Bool(bool),
String(String),
Symbol(String),
List(LSlice), Builtin(&'static str, fn(&[Value]) -> Result<Value, String>),
Lambda {
params: Rc<Vec<String>>,
rest: Option<String>,
body: Rc<Vec<Expr>>,
env: Env,
},
Macro {
params: Rc<Vec<String>>,
rest: Option<String>,
body: Rc<Vec<Expr>>,
env: Env,
},
Tool {
name: String,
description: String,
params: Rc<Vec<String>>,
body: Rc<Vec<Expr>>,
env: Env,
},
Tensor {
data: Rc<Vec<f64>>,
shape: Vec<usize>,
},
Native {
name: String,
arity: usize,
#[allow(dead_code)] lib: Rc<libloading::Library>,
fn_ptr: *const (),
},
NativeGrad {
name: String,
#[allow(dead_code)] lib: Rc<libloading::Library>,
fn_ptr: *const (),
in_shapes: Rc<Vec<Option<Vec<usize>>>>,
out_shapes: Rc<Vec<Option<Vec<usize>>>>,
},
Nil,
}
const MAX_DISPLAY_DEPTH: usize = 4_000;
thread_local! {
static DISPLAY_DEPTH: std::cell::Cell<usize> = const { std::cell::Cell::new(0) };
}
struct DisplayDepthGuard;
impl Drop for DisplayDepthGuard {
fn drop(&mut self) { DISPLAY_DEPTH.with(|d| d.set(d.get().saturating_sub(1))); }
}
impl std::fmt::Display for Value {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Value::Number(n) => {
if n.fract() == 0.0 && n.abs() < 1e15 {
write!(f, "{}", *n as i64)
} else {
write!(f, "{}", n)
}
}
Value::Bool(true) => write!(f, "#t"),
Value::Bool(false) => write!(f, "#f"),
Value::String(s) => write!(f, "\"{}\"", s),
Value::Symbol(s) => write!(f, "{}", s),
Value::List(vs) => {
if DISPLAY_DEPTH.with(|d| d.get()) >= MAX_DISPLAY_DEPTH {
return write!(f, "(...)");
}
DISPLAY_DEPTH.with(|d| d.set(d.get() + 1));
let _dec = DisplayDepthGuard;
write!(f, "(")?;
for (i, v) in vs.iter().enumerate() {
if i > 0 { write!(f, " ")?; }
write!(f, "{}", v)?;
}
write!(f, ")")
}
Value::Builtin(name, _) => write!(f, "#<builtin:{}>", name),
Value::Macro { params, .. } => write!(f, "#<macro ({})>", params.join(" ")),
Value::Lambda { params, rest, .. } => {
write!(f, "#<lambda ({}", params.join(" "))?;
if let Some(r) = rest { write!(f, " . {}", r)?; }
write!(f, ")>")
}
Value::Tool { name, .. } => write!(f, "#<tool:{}>", name),
Value::Tensor { data, shape } => {
let dims: Vec<String> = shape.iter().map(|d| d.to_string()).collect();
if data.len() <= 8 {
let vals: Vec<String> = data.iter().map(|v| {
if v.fract() == 0.0 && v.abs() < 1e15 { format!("{}", *v as i64) } else { format!("{}", v) }
}).collect();
write!(f, "#<tensor {} [{}]>", dims.join("x"), vals.join(" "))
} else {
write!(f, "#<tensor {}>", dims.join("x"))
}
}
Value::Native { name, arity, .. } => write!(f, "#<native:{}/{}>", name, arity),
Value::NativeGrad { name, in_shapes, .. } => {
let dims: Vec<String> = in_shapes.iter().map(|s| match s {
None => "scalar".to_string(),
Some(sh) => sh.iter().map(|d| d.to_string()).collect::<Vec<_>>().join("x"),
}).collect();
write!(f, "#<native-grad:{} ({})>", name, dims.join(" "))
}
Value::Nil => write!(f, "()"),
}
}
}
pub type Env = Rc<RefCell<EnvFrame>>;
#[derive(Debug)]
pub enum Slots {
Small(Vec<(String, Value)>),
Map(VarMap),
Root { map: rustc_hash::FxHashMap<String, u32>, vals: Vec<Value> },
}
const SMALL_MAX: usize = 8;
#[derive(Debug)]
pub struct EnvFrame {
slots: Slots,
pub parent: Option<Env>,
dirty: bool,
}
impl Drop for EnvFrame {
fn drop(&mut self) {
match std::mem::replace(&mut self.slots, Slots::Small(Vec::new())) {
Slots::Small(mut v) => {
if v.capacity() == 0 { return; }
v.clear();
crate::arena::recycle_small(v);
}
Slots::Map(mut m) => {
if m.capacity() == 0 { return; }
m.clear();
crate::arena::recycle_map(m);
}
Slots::Root { .. } => {}
}
}
}
impl EnvFrame {
pub fn new(parent: Option<Env>) -> Env {
Rc::new(RefCell::new(EnvFrame { slots: Slots::Small(crate::arena::take_small()), parent, dirty: false }))
}
pub fn new_root() -> Env {
Rc::new(RefCell::new(EnvFrame {
slots: Slots::Root { map: rustc_hash::FxHashMap::default(), vals: Vec::new() },
parent: None,
dirty: false,
}))
}
#[inline]
fn get_here(&self, name: &str) -> Option<Value> {
match &self.slots {
Slots::Small(v) => v.iter().find(|(k, _)| k == name).map(|(_, val)| val.clone()),
Slots::Map(m) => m.get(name).cloned(),
Slots::Root { map, vals } => map.get(name).map(|i| vals[*i as usize].clone()),
}
}
#[inline]
fn has_here(&self, name: &str) -> bool {
match &self.slots {
Slots::Small(v) => v.iter().any(|(k, _)| k == name),
Slots::Map(m) => m.contains_key(name),
Slots::Root { map, .. } => map.contains_key(name),
}
}
fn insert_here(&mut self, name: String, value: Value) {
match &mut self.slots {
Slots::Small(v) => {
if let Some(slot) = v.iter_mut().find(|(k, _)| *k == name) {
slot.1 = value;
return;
}
if v.len() < SMALL_MAX {
v.push((name, value));
return;
}
let mut m = crate::arena::take_map();
for (k, val) in v.drain(..) { m.insert(k, val); }
m.insert(name, value);
let old = std::mem::replace(&mut self.slots, Slots::Map(m));
if let Slots::Small(sv) = old { crate::arena::recycle_small(sv); }
}
Slots::Map(m) => { m.insert(name, value); }
Slots::Root { map, vals } => {
match map.get(&name) {
Some(i) => vals[*i as usize] = value,
None => {
vals.push(value);
map.insert(name, (vals.len() - 1) as u32);
}
}
}
}
}
pub fn for_each_local<F: FnMut(&String, &Value)>(&self, mut f: F) {
match &self.slots {
Slots::Small(v) => { for (k, val) in v { f(k, val); } }
Slots::Map(m) => { for (k, val) in m { f(k, val); } }
Slots::Root { map, vals } => { for (k, i) in map { f(k, &vals[*i as usize]); } }
}
}
pub fn vars_snapshot(&self) -> VarMap {
let mut out = VarMap::default();
self.for_each_local(|k, v| { out.insert(k.clone(), v.clone()); });
out
}
pub fn get(env: &Env, name: &str) -> Option<Value> {
let frame = env.borrow();
if let Some(v) = frame.get_here(name) { return Some(v); }
frame.parent.as_ref().and_then(|p| EnvFrame::get(p, name))
}
pub fn get_slot(env: &Env, depth: u16, slot: u16, name: &str) -> Option<Value> {
let mut cur = env.clone();
for _ in 0..depth {
let next = {
let f = cur.borrow();
if f.dirty { return None; }
f.parent.clone()?
};
cur = next;
}
let f = cur.borrow();
match &f.slots {
Slots::Small(v) => {
let (k, val) = v.get(slot as usize)?;
if k == name { Some(val.clone()) } else { None }
}
_ => None,
}
}
pub fn get_global(env: &Env, name: &str, idx: &std::cell::Cell<u32>) -> Option<Value> {
let mut cur = env.clone();
loop {
let next = {
let f = cur.borrow();
match &f.parent {
Some(p) => {
if f.dirty {
if let Some(v) = f.get_here(name) { return Some(v); }
}
p.clone()
}
None => {
return match &f.slots {
Slots::Root { map, vals } => {
let i = idx.get();
if i != u32::MAX {
Some(vals[i as usize].clone())
} else {
let i = *map.get(name)?;
idx.set(i);
Some(vals[i as usize].clone())
}
}
_ => f.get_here(name),
};
}
}
};
cur = next;
}
}
pub fn set(env: &Env, name: String, value: Value) {
env.borrow_mut().insert_here(name, value);
}
pub fn define(env: &Env, name: String, value: Value) {
let mut f = env.borrow_mut();
if f.parent.is_some() && !f.has_here(&name) {
f.dirty = true;
}
f.insert_here(name, value);
}
pub fn set_existing(env: &Env, name: &str, value: Value) -> bool {
let mut frame = env.borrow_mut();
if frame.has_here(name) {
frame.insert_here(name.to_string(), value);
true
} else if let Some(ref parent) = frame.parent.clone() {
EnvFrame::set_existing(parent, name, value)
} else {
false
}
}
pub fn extend(parent: &Env, params: &[String], rest: &Option<String>, args: Vec<Value>) -> Result<Env, String> {
if rest.is_none() && args.len() != params.len() {
return Err(format!("Arity error: expected {} args, got {}", params.len(), args.len()));
}
if rest.is_some() && args.len() < params.len() {
return Err(format!("Arity error: expected at least {} args, got {}", params.len(), args.len()));
}
let child = EnvFrame::new(Some(parent.clone()));
for (p, a) in params.iter().zip(args.iter()) {
EnvFrame::set(&child, p.clone(), a.clone());
}
if let Some(r) = rest {
let tail: Vec<Value> = args[params.len()..].to_vec();
EnvFrame::set(&child, r.clone(), list(tail));
}
Ok(child)
}
}
pub fn list(vals: Vec<Value>) -> Value {
Value::List(LSlice::new(vals))
}
pub fn cons(head: Value, tail: Value) -> Value {
match tail {
Value::List(rc) => Value::List(rc.prepend(head)),
Value::Nil => Value::List(ListBuf::with_gap(4, head, &[])),
other => list(vec![head, other]),
}
}