use std::collections::{HashMap, HashSet};
use std::sync::{Arc, Mutex, RwLock};
use crate::error::EvalResult;
use cljrs_gc::{GcConfig, GcPtr};
use cljrs_logging::feat_trace;
use cljrs_reader::Form;
use cljrs_value::{CljxFn, Namespace, Value, Var};
#[derive(Debug, Clone)]
pub enum RequireRefer {
None,
All,
Named(Vec<Arc<str>>),
}
#[derive(Debug, Clone)]
pub struct RequireSpec {
pub ns: Arc<str>,
pub alias: Option<Arc<str>>,
pub refer: RequireRefer,
}
pub struct Frame {
pub bindings: Vec<(Arc<str>, Value)>,
}
impl Default for Frame {
fn default() -> Self {
Self::new()
}
}
impl Frame {
pub fn new() -> Self {
Self {
bindings: Vec::new(),
}
}
pub fn bind(&mut self, name: Arc<str>, val: Value) {
self.bindings.push((name, val));
}
pub fn lookup(&self, name: &str) -> Option<&Value> {
feat_trace!("env", "lookup {}", name);
for (n, v) in self.bindings.iter().rev() {
if n.as_ref() == name {
return Some(v);
}
}
None
}
}
pub struct GlobalEnv {
pub namespaces: RwLock<HashMap<Arc<str>, GcPtr<Namespace>>>,
pub source_paths: RwLock<Vec<std::path::PathBuf>>,
pub loaded: Mutex<HashSet<Arc<str>>>,
pub loading: Mutex<HashSet<Arc<str>>>,
pub builtin_sources: RwLock<HashMap<Arc<str>, &'static str>>,
pub gc_config: RwLock<Option<Arc<GcConfig>>>,
pub compiler_ready: std::sync::atomic::AtomicBool,
pub eval_fn: fn(&Form, &mut Env) -> EvalResult,
pub call_cljrs_fn: fn(&CljxFn, &[Value], &mut Env) -> EvalResult,
on_fn_defined: Option<fn(&CljxFn, &mut Env)>,
}
impl std::fmt::Debug for GlobalEnv {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "GlobalEnv {{ ... }}")
}
}
impl GlobalEnv {
pub fn new(
eval_fn: fn(&Form, &mut Env) -> EvalResult,
call_cljrs_fn: fn(&CljxFn, &[Value], &mut Env) -> EvalResult,
on_fn_defined: Option<fn(&CljxFn, &mut Env)>,
) -> Arc<Self> {
Arc::new(Self {
namespaces: RwLock::new(HashMap::new()),
source_paths: RwLock::new(Vec::new()),
loaded: Mutex::new(HashSet::new()),
loading: Mutex::new(HashSet::new()),
builtin_sources: RwLock::new(HashMap::new()),
gc_config: RwLock::new(None),
compiler_ready: std::sync::atomic::AtomicBool::new(false),
eval_fn,
call_cljrs_fn,
on_fn_defined,
})
}
pub fn set_source_paths(&self, paths: Vec<std::path::PathBuf>) {
*self.source_paths.write().unwrap() = paths;
}
pub fn register_builtin_source(&self, ns: &str, src: &'static str) {
self.builtin_sources
.write()
.unwrap()
.insert(Arc::from(ns), src);
}
pub fn builtin_source(&self, ns: &str) -> Option<&'static str> {
self.builtin_sources.read().unwrap().get(ns).copied()
}
pub fn mark_loaded(&self, ns: &str) {
self.loaded.lock().unwrap().insert(Arc::from(ns));
}
pub fn is_loaded(&self, ns: &str) -> bool {
self.loaded.lock().unwrap().contains(ns)
}
pub fn set_gc_config(&self, config: Arc<GcConfig>) {
*self.gc_config.write().unwrap() = Some(config);
}
pub fn gc_config(&self) -> Option<Arc<GcConfig>> {
self.gc_config.read().unwrap().clone()
}
pub fn resolve_alias(&self, current_ns: &str, alias: &str) -> Option<Arc<str>> {
let map = self.namespaces.read().unwrap();
let ns = map.get(current_ns)?;
let aliases = ns.get().aliases.lock().unwrap();
aliases.get(alias).cloned()
}
pub fn get_or_create_ns(&self, name: &str) -> GcPtr<Namespace> {
{
let map = self.namespaces.read().unwrap();
if let Some(ns) = map.get(name) {
return ns.clone();
}
}
let mut map = self.namespaces.write().unwrap();
if let Some(ns) = map.get(name) {
return ns.clone();
}
let ns = GcPtr::new(Namespace::new(name));
map.insert(Arc::from(name), ns.clone());
ns
}
pub fn intern(&self, ns_name: &str, name: Arc<str>, val: Value) -> GcPtr<Var> {
let ns = self.get_or_create_ns(ns_name);
let mut interns = ns.get().interns.lock().unwrap();
if let Some(var) = interns.get(&name) {
var.get().bind(val);
return var.clone();
}
let var = GcPtr::new(Var::new(ns_name, name.as_ref()));
var.get().bind(val);
interns.insert(name, var.clone());
var
}
pub fn lookup_var(&self, ns_name: &str, sym_name: &str) -> Option<GcPtr<Var>> {
let map = self.namespaces.read().unwrap();
let ns = map.get(ns_name)?;
let interns = ns.get().interns.lock().unwrap();
interns.get(sym_name).cloned()
}
pub fn lookup_in_ns(&self, ns_name: &str, sym_name: &str) -> Option<Value> {
let map = self.namespaces.read().unwrap();
let ns = map.get(ns_name)?;
let ns_ref = ns.get();
{
let interns = ns_ref.interns.lock().unwrap();
if let Some(var) = interns.get(sym_name) {
return crate::dynamics::deref_var(var);
}
}
{
let refers = ns_ref.refers.lock().unwrap();
if let Some(var) = refers.get(sym_name) {
return crate::dynamics::deref_var(var);
}
}
None
}
pub fn lookup_var_in_ns(&self, ns_name: &str, sym_name: &str) -> Option<GcPtr<Var>> {
let map = self.namespaces.read().unwrap();
let ns = map.get(ns_name)?;
let ns_ref = ns.get();
{
let interns = ns_ref.interns.lock().unwrap();
if let Some(var) = interns.get(sym_name) {
return Some(var.clone());
}
}
{
let refers = ns_ref.refers.lock().unwrap();
if let Some(var) = refers.get(sym_name) {
return Some(var.clone());
}
}
None
}
pub fn refer_all(&self, dst_ns: &str, src_ns: &str) {
let map = self.namespaces.read().unwrap();
let src = match map.get(src_ns) {
Some(ns) => ns.clone(),
None => return,
};
let dst = match map.get(dst_ns) {
Some(ns) => ns.clone(),
None => return,
};
let src_interns = src.get().interns.lock().unwrap();
let mut dst_refers = dst.get().refers.lock().unwrap();
for (name, var) in src_interns.iter() {
dst_refers.insert(name.clone(), var.clone());
}
}
pub fn refer_named(&self, dst_ns: &str, src_ns: &str, names: &[Arc<str>]) {
let map = self.namespaces.read().unwrap();
let src = match map.get(src_ns) {
Some(ns) => ns.clone(),
None => return,
};
let dst = match map.get(dst_ns) {
Some(ns) => ns.clone(),
None => return,
};
let src_interns = src.get().interns.lock().unwrap();
let mut dst_refers = dst.get().refers.lock().unwrap();
for name in names {
if let Some(var) = src_interns.get(name) {
dst_refers
.entry(name.clone())
.or_insert_with(|| var.clone());
}
}
}
pub fn add_alias(&self, current_ns: &str, alias: &str, full_ns: &str) {
let ns_ptr = self.get_or_create_ns(current_ns);
let mut aliases = ns_ptr.get().aliases.lock().unwrap();
aliases.insert(Arc::from(alias), Arc::from(full_ns));
}
#[inline(always)]
pub fn eval(&self, form: &Form, env: &mut Env) -> EvalResult {
(self.eval_fn)(form, env)
}
#[inline(always)]
pub fn call_cljrs_fn(&self, func: &CljxFn, args: &[Value], env: &mut Env) -> EvalResult {
(self.call_cljrs_fn)(func, args, env)
}
#[inline(always)]
pub fn on_fn_defined(&self, f: &CljxFn, env: &mut Env) {
if let Some(hook) = self.on_fn_defined {
hook(f, env);
}
}
pub fn set_on_fn_defined(&mut self, hook: fn(&CljxFn, &mut Env)) {
self.on_fn_defined = Some(hook);
}
}
pub struct Env {
pub frames: Vec<Frame>,
pub current_ns: Arc<str>,
pub globals: Arc<GlobalEnv>,
}
impl Env {
pub fn new(globals: Arc<GlobalEnv>, ns: &str) -> Self {
Self {
frames: Vec::new(),
current_ns: Arc::from(ns),
globals,
}
}
pub fn with_closure(globals: Arc<GlobalEnv>, ns: &str, f: &CljxFn) -> Self {
let mut env = Self::new(globals, ns);
if !f.closed_over_names.is_empty() {
env.push_frame();
for (name, val) in f.closed_over_names.iter().zip(f.closed_over_vals.iter()) {
env.bind(name.clone(), val.clone());
}
}
env
}
pub fn push_frame(&mut self) {
self.frames.push(Frame::new());
}
pub fn pop_frame(&mut self) {
self.frames.pop();
}
pub fn bind(&mut self, name: Arc<str>, val: Value) {
if let Some(frame) = self.frames.last_mut() {
frame.bind(name, val);
}
}
pub fn lookup(&self, name: &str) -> Option<Value> {
feat_trace!("env", "lookup {} in {} frames", name, self.frames.len());
for frame in self.frames.iter().rev() {
if let Some(v) = frame.lookup(name) {
return Some(v.clone());
}
}
self.globals.lookup_in_ns(&self.current_ns, name)
}
pub fn lookup_var(&self, name: &str) -> Option<GcPtr<Var>> {
self.globals.lookup_var_in_ns(&self.current_ns, name)
}
pub fn all_local_bindings(&self) -> (Vec<Arc<str>>, Vec<Value>) {
let mut names = Vec::new();
let mut vals = Vec::new();
for frame in &self.frames {
for (n, v) in &frame.bindings {
names.push(n.clone());
vals.push(v.clone());
}
}
(names, vals)
}
pub fn child(&self) -> Self {
let (names, vals) = self.all_local_bindings();
let mut child = Self::new(self.globals.clone(), &self.current_ns);
if !names.is_empty() {
child.push_frame();
for (n, v) in names.into_iter().zip(vals) {
child.bind(n, v);
}
}
child
}
#[inline(always)]
pub fn eval(&mut self, form: &Form) -> EvalResult {
let globals = self.globals.clone();
globals.eval(form, self)
}
#[inline(always)]
pub fn call_cljrs_fn(&mut self, func: &CljxFn, args: &[Value]) -> EvalResult {
let globals = self.globals.clone();
globals.call_cljrs_fn(func, args, self)
}
#[inline(always)]
pub fn on_fn_defined(&mut self, func: &CljxFn) {
let globals = self.globals.clone();
globals.on_fn_defined(func, self);
}
}