use std::{collections::BTreeMap, sync::Arc};
use crate::{
ContentId,
capability::{CapabilityName, CapabilitySet, read_construct_capability},
control::{ControlPolicy, ControlPolicyRef, NoopControlPolicy},
datum_store::BTreeDatumStore,
effect_ledger::EffectLedger,
error::{Diagnostic, Error, Result},
eval::{EvalPolicy, EvalPolicyRef, MacroExpanderRef, Phase},
expr::{Expr, SourceRegistry},
fact_store::{BTreeFactStore, FactStore},
factory::Factory,
handle_store::BTreeHandleStore,
id::{LibId, Symbol},
library::{LoadCx, Registry},
list::ListRegistry,
number_domain::PromotionSearchLimits,
object::Args,
table::TableRegistry,
value::Value,
};
use super::{Diagnostics, Env};
pub type Capabilities = CapabilitySet;
pub struct Cx {
env: Env,
diagnostics: Diagnostics,
capabilities: Capabilities,
eval_policy: EvalPolicyRef,
macro_expander: Option<MacroExpanderRef>,
factory: Arc<dyn Factory>,
pub(crate) registry: Registry,
list_registry: ListRegistry,
table_registry: TableRegistry,
promotion_search_limits: PromotionSearchLimits,
sources: SourceRegistry,
datum_store: BTreeDatumStore,
handles: BTreeHandleStore,
facts: BTreeFactStore,
load_claims: BTreeMap<LibId, Vec<ContentId>>,
effect_ledger: EffectLedger,
control_policy: ControlPolicyRef,
}
impl Cx {
pub fn new(eval_policy: EvalPolicyRef, factory: Arc<dyn Factory>) -> Self {
let mut datum_store = BTreeDatumStore::default();
let mut facts = BTreeFactStore::default();
facts.insert_boot_claims(&mut datum_store);
Self {
env: Env::default(),
diagnostics: Diagnostics::default(),
capabilities: Capabilities::default(),
eval_policy,
macro_expander: None,
factory,
registry: Registry::default(),
list_registry: ListRegistry::default(),
table_registry: TableRegistry::default(),
promotion_search_limits: PromotionSearchLimits::default(),
sources: SourceRegistry::default(),
datum_store,
handles: BTreeHandleStore::default(),
facts,
load_claims: BTreeMap::new(),
effect_ledger: EffectLedger::default(),
control_policy: Arc::new(NoopControlPolicy),
}
}
pub fn env(&self) -> &Env {
&self.env
}
pub fn env_mut(&mut self) -> &mut Env {
&mut self.env
}
pub fn with_env<T>(&mut self, env: Env, f: impl FnOnce(&mut Self) -> Result<T>) -> Result<T> {
let saved = std::mem::replace(&mut self.env, env);
let result = f(self);
self.env = saved;
result
}
pub fn factory(&self) -> &dyn Factory {
self.factory.as_ref()
}
pub fn factory_ref(&self) -> Arc<dyn Factory> {
self.factory.clone()
}
pub fn with_factory<T>(
&mut self,
factory: Arc<dyn Factory>,
f: impl FnOnce(&mut Self) -> Result<T>,
) -> Result<T> {
let saved = std::mem::replace(&mut self.factory, factory);
let result = f(self);
self.factory = saved;
result
}
pub fn registry(&self) -> &Registry {
&self.registry
}
pub fn registry_mut(&mut self) -> &mut Registry {
&mut self.registry
}
pub fn list_registry(&self) -> &ListRegistry {
&self.list_registry
}
pub fn list_registry_mut(&mut self) -> &mut ListRegistry {
&mut self.list_registry
}
pub fn table_registry(&self) -> &TableRegistry {
&self.table_registry
}
pub fn table_registry_mut(&mut self) -> &mut TableRegistry {
&mut self.table_registry
}
pub fn with_registry<T>(
&mut self,
registry: Registry,
f: impl FnOnce(&mut Self) -> Result<T>,
) -> Result<T> {
let saved = std::mem::replace(&mut self.registry, registry);
let result = f(self);
self.registry = saved;
result
}
pub fn new_list(&mut self, items: Vec<Value>) -> Result<Value> {
let registry = std::mem::take(&mut self.list_registry);
let result = registry.new_list(self, items);
self.list_registry = registry;
result
}
pub fn new_cons(&mut self, car: Value, cdr: Value) -> Result<Value> {
let registry = std::mem::take(&mut self.list_registry);
let result = registry.new_cons(self, car, cdr);
self.list_registry = registry;
result
}
pub fn new_table(&mut self, entries: Vec<(Symbol, Value)>) -> Result<Value> {
let registry = std::mem::take(&mut self.table_registry);
let result = registry.new_table(self, entries);
self.table_registry = registry;
result
}
pub fn sources(&self) -> &SourceRegistry {
&self.sources
}
pub fn sources_mut(&mut self) -> &mut SourceRegistry {
&mut self.sources
}
pub fn datum_store(&self) -> &BTreeDatumStore {
&self.datum_store
}
pub fn datum_store_mut(&mut self) -> &mut BTreeDatumStore {
&mut self.datum_store
}
pub fn handles(&self) -> &BTreeHandleStore {
&self.handles
}
pub fn handles_mut(&mut self) -> &mut BTreeHandleStore {
&mut self.handles
}
pub fn facts(&self) -> &BTreeFactStore {
&self.facts
}
pub fn facts_mut(&mut self) -> &mut BTreeFactStore {
&mut self.facts
}
pub fn effect_ledger(&self) -> &EffectLedger {
&self.effect_ledger
}
pub fn effect_ledger_mut(&mut self) -> &mut EffectLedger {
&mut self.effect_ledger
}
pub fn control_policy(&self) -> &dyn ControlPolicy {
self.control_policy.as_ref()
}
pub fn control_policy_ref(&self) -> ControlPolicyRef {
self.control_policy.clone()
}
pub fn control_policy_name(&self) -> &'static str {
self.control_policy.name()
}
pub fn set_control_policy(&mut self, control_policy: ControlPolicyRef) {
self.control_policy = control_policy;
}
pub(crate) fn with_effect_ledger<T>(
&mut self,
f: impl FnOnce(&mut Self, &mut EffectLedger) -> Result<T>,
) -> Result<T> {
let mut ledger = std::mem::take(&mut self.effect_ledger);
let result = f(self, &mut ledger);
self.effect_ledger = ledger;
result
}
pub fn insert_fact(&mut self, claim: crate::Claim) -> Result<crate::Ref> {
self.facts
.insert_authorized(&self.capabilities, &mut self.datum_store, claim)
}
pub fn insert_fact_for_lib(
&mut self,
lib_id: LibId,
claim: crate::Claim,
) -> Result<crate::Ref> {
let (reference, inserted) = self.insert_recorded_fact(claim)?;
if let Some(inserted) = inserted {
self.record_load_claims(lib_id, vec![inserted]);
}
Ok(reference)
}
pub(crate) fn insert_recorded_fact(
&mut self,
claim: crate::Claim,
) -> Result<(crate::Ref, Option<ContentId>)> {
let id = claim.content_id(&mut self.datum_store)?;
let existed = self.facts.get(&id).is_some();
let inserted = self.insert_fact(claim)?;
let crate::Ref::Content(inserted_id) = inserted else {
return Err(Error::Lib(
"fact insertion returned a non-content reference".to_owned(),
));
};
debug_assert_eq!(inserted_id, id);
Ok((
crate::Ref::Content(inserted_id.clone()),
(!existed).then_some(inserted_id),
))
}
pub fn query_facts(&self, pattern: crate::ClaimPattern) -> Result<Vec<crate::Claim>> {
self.facts.query_authorized(self, pattern)
}
pub(crate) fn record_load_claims(&mut self, lib_id: LibId, claim_ids: Vec<ContentId>) {
if claim_ids.is_empty() {
return;
}
self.load_claims
.entry(lib_id)
.or_default()
.extend(claim_ids);
}
pub(crate) fn remove_load_claims(&mut self, lib_ids: &[LibId]) {
for lib_id in lib_ids {
if let Some(claim_ids) = self.load_claims.remove(lib_id) {
for claim_id in claim_ids {
self.facts.remove(&claim_id);
}
}
}
}
pub fn promotion_search_limits(&self) -> PromotionSearchLimits {
self.promotion_search_limits
}
pub fn set_promotion_search_limits(&mut self, limits: PromotionSearchLimits) {
self.promotion_search_limits = limits;
}
pub(crate) fn load_cx(&self) -> LoadCx {
LoadCx::new(
self.capabilities.clone(),
self.factory.clone(),
self.registry.clone(),
)
}
pub fn eval_policy(&self) -> &dyn EvalPolicy {
self.eval_policy.as_ref()
}
pub fn eval_policy_ref(&self) -> EvalPolicyRef {
self.eval_policy.clone()
}
pub fn eval_policy_name(&self) -> &'static str {
self.eval_policy.name()
}
pub fn set_eval_policy(&mut self, eval_policy: EvalPolicyRef) {
self.eval_policy = eval_policy;
}
pub fn set_macro_expander(&mut self, macro_expander: MacroExpanderRef) {
self.macro_expander = Some(macro_expander);
}
pub fn clear_macro_expander(&mut self) {
self.macro_expander = None;
}
pub fn macro_expander_ref(&self) -> Option<MacroExpanderRef> {
self.macro_expander.clone()
}
pub fn expand_macros(&mut self, phase: Phase, expr: Expr) -> Result<Expr> {
match self.macro_expander.clone() {
Some(expander) => expander.expand_expr(self, phase, expr),
None => Ok(expr),
}
}
pub fn diagnostics(&self) -> &Diagnostics {
&self.diagnostics
}
pub fn take_diagnostics(&mut self) -> Vec<Diagnostic> {
self.diagnostics.take()
}
pub fn push_diagnostic(&mut self, diagnostic: Diagnostic) {
self.diagnostics.push_diagnostic(diagnostic);
}
pub fn push_info(&mut self, message: impl Into<String>) {
self.diagnostics.push_info(message);
}
pub fn grant(&mut self, capability: CapabilityName) {
self.capabilities.insert(capability);
}
pub fn grant_named(&mut self, capability: &'static str) {
self.capabilities.insert(CapabilityName::new(capability));
}
pub fn capabilities(&self) -> &Capabilities {
&self.capabilities
}
pub fn with_capabilities<T>(
&mut self,
capabilities: Capabilities,
f: impl FnOnce(&mut Self) -> Result<T>,
) -> Result<T> {
let saved = std::mem::replace(&mut self.capabilities, capabilities);
let result = f(self);
self.capabilities = saved;
result
}
pub fn resolve_class(&self, symbol: &Symbol) -> Result<Value> {
self.registry()
.class_by_symbol(symbol)
.cloned()
.ok_or_else(|| Error::UnknownClass {
class: symbol.clone(),
})
}
pub fn resolve_function(&self, symbol: &Symbol) -> Result<Value> {
self.registry()
.function_by_symbol(symbol)
.cloned()
.ok_or_else(|| Error::UnknownFunction {
function: symbol.clone(),
})
}
pub fn resolve_macro(&self, symbol: &Symbol) -> Result<Value> {
self.registry()
.macro_by_symbol(symbol)
.cloned()
.ok_or_else(|| Error::UnknownSymbol {
symbol: symbol.clone(),
})
}
pub fn resolve_shape(&self, symbol: &Symbol) -> Result<Value> {
self.registry()
.shape_by_symbol(symbol)
.cloned()
.ok_or_else(|| Error::UnknownSymbol {
symbol: symbol.clone(),
})
}
pub fn resolve_codec(&self, symbol: &Symbol) -> Result<Value> {
self.registry()
.codec_by_symbol(symbol)
.cloned()
.ok_or_else(|| Error::UnknownSymbol {
symbol: symbol.clone(),
})
}
pub fn resolve_number_domain(&self, symbol: &Symbol) -> Result<Value> {
self.registry()
.number_domain_by_symbol(symbol)
.cloned()
.ok_or_else(|| Error::UnknownSymbol {
symbol: symbol.clone(),
})
}
pub fn resolve_value(&self, symbol: &Symbol) -> Result<Value> {
self.registry()
.value_by_symbol(symbol)
.cloned()
.ok_or_else(|| Error::UnknownSymbol {
symbol: symbol.clone(),
})
}
pub fn call_value(&mut self, value: Value, args: Args) -> Result<Value> {
let Some(callable) = value.object().as_callable() else {
return Err(Error::TypeMismatch {
expected: "callable",
found: "non-callable",
});
};
callable.call(self, args)
}
pub fn call_exprs(&mut self, value: Value, args: Vec<crate::expr::Expr>) -> Result<Value> {
let Some(callable) = value.object().as_callable() else {
return Err(Error::TypeMismatch {
expected: "callable",
found: "non-callable",
});
};
callable.call_exprs(self, crate::object::RawArgs::new(args))
}
pub fn call_function(&mut self, symbol: &Symbol, args: Args) -> Result<Value> {
let function = self.resolve_function(symbol)?;
self.call_value(function, args)
}
pub fn call_class(&mut self, symbol: &Symbol, args: Args) -> Result<Value> {
let class = self.resolve_class(symbol)?;
self.call_value(class, args)
}
pub fn read_construct(&mut self, class: &Symbol, args: Vec<Value>) -> Result<Value> {
self.require(&read_construct_capability())?;
let class_value = self.resolve_class(class)?;
let Some(class_impl) = class_value.object().as_class() else {
return Err(Error::TypeMismatch {
expected: "class",
found: "non-class",
});
};
let Some(read_constructor) = class_impl.read_constructor(self)? else {
return Err(Error::Eval(format!(
"class {} has no read constructor",
class
)));
};
let Some(read_impl) = read_constructor.object().as_read_constructor() else {
return Err(Error::TypeMismatch {
expected: "read-constructor",
found: "non-read-constructor",
});
};
read_impl.construct_read(self, args)
}
pub fn force(&mut self, value: Value, demand: crate::eval::Demand) -> Result<Value> {
let eval_policy = self.eval_policy.clone();
eval_policy.force(self, value, demand)
}
pub fn eval_expr(&mut self, expr: crate::expr::Expr) -> Result<Value> {
let eval_policy = self.eval_policy.clone();
eval_policy.eval_expr(self, expr)
}
pub fn symbol_is_bound(&mut self, name: &Symbol) -> bool {
self.env().get(name).is_some()
|| self.resolve_function(name).is_ok()
|| self.resolve_class(name).is_ok()
|| self.resolve_shape(name).is_ok()
|| self.resolve_value(name).is_ok()
}
pub fn resolve_unbound_symbol(&mut self, symbol: Symbol) -> Result<Value> {
let eval_policy = self.eval_policy_ref();
eval_policy.resolve_unbound_symbol(self, symbol)
}
pub fn resolve_unbound_call(
&mut self,
operator: Symbol,
args: Vec<crate::expr::Expr>,
) -> Result<Value> {
let eval_policy = self.eval_policy_ref();
eval_policy.resolve_unbound_call(self, operator, args)
}
pub fn require(&self, capability: &CapabilityName) -> Result<()> {
if self.capabilities.contains(capability) {
Ok(())
} else {
Err(Error::CapabilityDenied {
capability: capability.clone(),
})
}
}
pub fn require_all(&self, capabilities: &[CapabilityName]) -> Result<()> {
for capability in capabilities {
self.require(capability)?;
}
Ok(())
}
}