use crate::collections::{HashMap, HashSet};
use crate::module::{
ModuleAssociatedFn, ModuleFn, ModuleInternalEnum, ModuleMacro, ModuleType, ModuleUnitType,
};
use crate::{
CompileMeta, CompileMetaKind, CompileMetaStruct, CompileMetaTuple, ComponentRef, Hash,
IntoComponent, Item, Module, Names, Stack, StaticType, Type, TypeCheck, TypeInfo, TypeOf,
VmError,
};
use std::any;
use std::fmt;
use std::sync::Arc;
use thiserror::Error;
#[derive(Debug, Error)]
pub enum ContextError {
#[error("`()` types are already present")]
UnitAlreadyPresent,
#[error("`{name}` types are already present")]
InternalAlreadyPresent {
name: &'static str,
},
#[error("conflicting meta {existing} while trying to insert {current}")]
ConflictingMeta {
current: Box<CompileMeta>,
existing: Box<CompileMeta>,
},
#[error("function `{signature}` ({hash}) already exists")]
ConflictingFunction {
signature: ContextSignature,
hash: Hash,
},
#[error("function with name `{name}` already exists")]
ConflictingFunctionName {
name: Item,
},
#[error("instance function `{name}` for type `{type_info}` already exists")]
ConflictingInstanceFunction {
type_info: TypeInfo,
name: String,
},
#[error("module `{item}` with hash `{hash}` already exists")]
ConflictingModule {
item: Item,
hash: Hash,
},
#[error("type `{item}` already exists `{existing}`")]
ConflictingType {
item: Item,
existing: TypeInfo,
},
#[error("tried to insert conflicting hash type `{hash}` (existing `{existing}`) for type `{type_of}`")]
ConflictingTypeHash {
hash: Hash,
existing: Hash,
type_of: Type,
},
#[error("variant with `{item}` already exists")]
ConflictingVariant {
item: Item,
},
#[error("instance `{instance_type}` does not exist in module")]
MissingInstance {
instance_type: TypeInfo,
},
}
pub(crate) type Handler = dyn Fn(&mut Stack, usize) -> Result<(), VmError> + Send + Sync;
pub(crate) type Macro =
dyn Fn(&dyn any::Any) -> Result<Box<dyn any::Any>, crate::Error> + Send + Sync;
#[derive(Debug, Clone)]
pub struct ContextTypeInfo {
pub type_check: TypeCheck,
pub item: Item,
pub type_of: Type,
pub type_info: TypeInfo,
}
impl fmt::Display for ContextTypeInfo {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(fmt, "{} => {}", self.item, self.type_info)?;
Ok(())
}
}
#[derive(Debug, Clone)]
pub enum ContextSignature {
Function {
item: Item,
args: Option<usize>,
},
Instance {
item: Item,
name: String,
args: Option<usize>,
self_type_info: TypeInfo,
},
}
impl fmt::Display for ContextSignature {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Function { item, args } => {
write!(fmt, "{}(", item)?;
if let Some(args) = args {
let mut it = 0..*args;
let last = it.next_back();
for n in it {
write!(fmt, "#{}, ", n)?;
}
if let Some(n) = last {
write!(fmt, "#{}", n)?;
}
} else {
write!(fmt, "...")?;
}
write!(fmt, ")")?;
}
Self::Instance {
item,
name,
self_type_info,
args,
} => {
write!(fmt, "{}::{}(self: {}", item, name, self_type_info)?;
if let Some(args) = args {
for n in 0..*args {
write!(fmt, ", #{}", n)?;
}
} else {
write!(fmt, ", ...")?;
}
write!(fmt, ")")?;
}
}
Ok(())
}
}
#[derive(Default)]
pub struct Context {
has_default_modules: bool,
meta: HashMap<Item, CompileMeta>,
functions: HashMap<Hash, Arc<Handler>>,
macros: HashMap<Hash, Arc<Macro>>,
functions_info: HashMap<Hash, ContextSignature>,
types: HashMap<Hash, ContextTypeInfo>,
types_rev: HashMap<Type, Hash>,
unit_type: Option<Hash>,
internal_enums: HashSet<&'static StaticType>,
names: Names<()>,
}
impl Context {
pub fn new() -> Self {
Context::default()
}
pub fn type_check_for(&self, item: &Item) -> Option<TypeCheck> {
let ty = self.types.get(&Hash::type_hash(item))?;
Some(ty.type_check)
}
pub fn with_default_modules() -> Result<Self, ContextError> {
Self::with_config(true)
}
pub fn with_config(stdio: bool) -> Result<Self, ContextError> {
let mut this = Self::new();
this.install(&crate::modules::core::module()?)?;
this.install(&crate::modules::generator::module()?)?;
this.install(&crate::modules::bytes::module()?)?;
this.install(&crate::modules::string::module()?)?;
this.install(&crate::modules::int::module()?)?;
this.install(&crate::modules::float::module()?)?;
this.install(&crate::modules::iter::module()?)?;
this.install(&crate::modules::vec::module()?)?;
this.install(&crate::modules::object::module()?)?;
this.install(&crate::modules::result::module()?)?;
this.install(&crate::modules::option::module()?)?;
this.install(&crate::modules::future::module()?)?;
this.install(&crate::modules::stream::module()?)?;
this.install(&crate::modules::io::module(stdio)?)?;
this.install(&crate::modules::fmt::module()?)?;
this.has_default_modules = true;
Ok(this)
}
pub fn has_default_modules(&self) -> bool {
self.has_default_modules
}
pub fn iter_components<'a, I: 'a>(
&'a self,
iter: I,
) -> impl Iterator<Item = ComponentRef<'a>> + 'a
where
I: IntoIterator,
I::Item: IntoComponent,
{
self.names.iter_components(iter)
}
pub fn unit_type(&self) -> Option<Hash> {
self.unit_type
}
pub fn contains_name(&self, item: &Item) -> bool {
self.names.contains(item)
}
pub fn contains_prefix(&self, item: &Item) -> bool {
self.names.contains_prefix(item)
}
pub fn lookup(&self, hash: Hash) -> Option<&Arc<Handler>> {
self.functions.get(&hash)
}
pub fn lookup_macro(&self, hash: Hash) -> Option<&Arc<Macro>> {
self.macros.get(&hash)
}
pub fn lookup_meta(&self, name: &Item) -> Option<CompileMeta> {
self.meta.get(name).cloned()
}
pub fn iter_functions(&self) -> impl Iterator<Item = (Hash, &ContextSignature)> {
let mut it = self.functions_info.iter();
std::iter::from_fn(move || {
let (hash, signature) = it.next()?;
Some((*hash, signature))
})
}
pub fn iter_types(&self) -> impl Iterator<Item = (Hash, &ContextTypeInfo)> {
let mut it = self.types.iter();
std::iter::from_fn(move || {
let (hash, ty) = it.next()?;
Some((*hash, ty))
})
}
pub fn install(&mut self, module: &Module) -> Result<(), ContextError> {
for (type_of, ty) in &module.types {
self.install_type(&module, *type_of, ty)?;
}
for (name, f) in &module.functions {
self.install_function(&module, name, f)?;
}
for (name, m) in &module.macros {
self.install_macro(&module, name, m)?;
}
if let Some(unit_type) = &module.unit_type {
self.install_unit_type(&module, unit_type)?;
}
for internal_enum in &module.internal_enums {
self.install_internal_enum(module, internal_enum)?;
}
for (key, inst) in &module.associated_functions {
self.install_associated_function(key.type_of, key.hash, inst, key.kind.into_hash_fn())?;
}
Ok(())
}
fn install_meta(&mut self, meta: CompileMeta) -> Result<(), ContextError> {
if let Some(existing) = self.meta.insert(meta.item.clone(), meta.clone()) {
return Err(ContextError::ConflictingMeta {
existing: Box::new(existing),
current: Box::new(meta),
});
}
Ok(())
}
fn install_type(
&mut self,
module: &Module,
type_of: Type,
ty: &ModuleType,
) -> Result<(), ContextError> {
let item = module.path.extended(&*ty.name);
let hash = Hash::type_hash(&item);
self.install_type_info(
hash,
ContextTypeInfo {
type_check: TypeCheck::Type(*type_of),
item: item.clone(),
type_of,
type_info: ty.type_info.clone(),
},
)?;
self.install_meta(CompileMeta {
item,
kind: CompileMetaKind::Struct {
type_of,
object: CompileMetaStruct { fields: None },
},
source: None,
})?;
Ok(())
}
fn install_type_info(&mut self, hash: Hash, info: ContextTypeInfo) -> Result<(), ContextError> {
self.names.insert(&info.item, ());
if let Some(existing) = self.types_rev.insert(info.type_of, hash) {
return Err(ContextError::ConflictingTypeHash {
hash,
existing,
type_of: info.type_of,
});
}
if let Some(existing) = self.types.insert(hash, info) {
return Err(ContextError::ConflictingType {
item: existing.item,
existing: existing.type_info,
});
}
Ok(())
}
fn install_function(
&mut self,
module: &Module,
item: &Item,
f: &ModuleFn,
) -> Result<(), ContextError> {
let item = module.path.join(item);
self.names.insert(&item, ());
let hash = Hash::type_hash(&item);
let signature = ContextSignature::Function {
item: item.clone(),
args: f.args,
};
if let Some(old) = self.functions_info.insert(hash, signature) {
return Err(ContextError::ConflictingFunction {
signature: old,
hash,
});
}
self.functions.insert(hash, f.handler.clone());
self.meta.insert(
item.clone(),
CompileMeta {
item,
kind: CompileMetaKind::Function {
type_of: Type::from(hash),
},
source: None,
},
);
Ok(())
}
fn install_macro(
&mut self,
module: &Module,
item: &Item,
m: &ModuleMacro,
) -> Result<(), ContextError> {
let item = module.path.join(item);
self.names.insert(&item, ());
let hash = Hash::type_hash(&item);
self.macros.insert(hash, m.handler.clone());
Ok(())
}
fn install_associated_function(
&mut self,
type_of: Type,
hash: Hash,
assoc: &ModuleAssociatedFn,
hash_fn: impl FnOnce(Type, Hash) -> Hash,
) -> Result<(), ContextError> {
let info = match self
.types_rev
.get(&type_of)
.and_then(|hash| self.types.get(&hash))
{
Some(info) => info,
None => {
return Err(ContextError::MissingInstance {
instance_type: assoc.type_info.clone(),
});
}
};
let hash = hash_fn(type_of, hash);
let signature = ContextSignature::Instance {
item: info.item.clone(),
name: assoc.name.clone(),
args: assoc.args,
self_type_info: info.type_info.clone(),
};
if let Some(old) = self.functions_info.insert(hash, signature) {
return Err(ContextError::ConflictingFunction {
signature: old,
hash,
});
}
self.functions.insert(hash, assoc.handler.clone());
Ok(())
}
fn install_unit_type(
&mut self,
module: &Module,
unit_type: &ModuleUnitType,
) -> Result<(), ContextError> {
if self.unit_type.is_some() {
return Err(ContextError::UnitAlreadyPresent);
}
let item = module.path.extended(&*unit_type.name);
let hash = Hash::type_hash(&item);
self.unit_type = Some(Hash::type_hash(&item));
self.add_internal_tuple(None, item.clone(), 0, || ())?;
self.install_type_info(
hash,
ContextTypeInfo {
type_check: TypeCheck::Unit,
item,
type_of: Type::from(crate::UNIT_TYPE),
type_info: TypeInfo::StaticType(crate::UNIT_TYPE),
},
)?;
Ok(())
}
fn install_internal_enum(
&mut self,
module: &Module,
internal_enum: &ModuleInternalEnum,
) -> Result<(), ContextError> {
if !self.internal_enums.insert(internal_enum.static_type) {
return Err(ContextError::InternalAlreadyPresent {
name: internal_enum.name,
});
}
let enum_item = module.path.join(&internal_enum.base_type);
let enum_hash = Hash::type_hash(&enum_item);
self.install_meta(CompileMeta {
item: enum_item.clone(),
kind: CompileMetaKind::Enum {
type_of: Type::from(internal_enum.static_type),
},
source: None,
})?;
self.install_type_info(
enum_hash,
ContextTypeInfo {
type_check: TypeCheck::Type(internal_enum.static_type.hash),
item: enum_item.clone(),
type_of: Type::from(internal_enum.static_type),
type_info: TypeInfo::StaticType(internal_enum.static_type),
},
)?;
for variant in &internal_enum.variants {
let item = enum_item.extended(variant.name);
let hash = Hash::type_hash(&item);
self.install_type_info(
hash,
ContextTypeInfo {
type_check: variant.type_check,
item: item.clone(),
type_of: Type::from(hash),
type_info: TypeInfo::StaticType(internal_enum.static_type),
},
)?;
self.install_meta(CompileMeta {
item: item.clone(),
kind: CompileMetaKind::TupleVariant {
type_of: variant.type_of,
enum_item: enum_item.clone(),
tuple: CompileMetaTuple {
args: variant.args,
hash,
},
},
source: None,
})?;
let signature = ContextSignature::Function {
item,
args: Some(variant.args),
};
if let Some(old) = self.functions_info.insert(hash, signature) {
return Err(ContextError::ConflictingFunction {
signature: old,
hash,
});
}
self.functions.insert(hash, variant.constructor.clone());
}
Ok(())
}
fn add_internal_tuple<C, Args>(
&mut self,
enum_item: Option<Item>,
item: Item,
args: usize,
constructor: C,
) -> Result<(), ContextError>
where
C: crate::module::Function<Args>,
C::Return: TypeOf,
{
let type_of = <C::Return as TypeOf>::type_of();
let hash = Hash::type_hash(&item);
let tuple = CompileMetaTuple { args, hash };
let meta = match enum_item {
Some(enum_item) => CompileMeta {
item: item.clone(),
kind: CompileMetaKind::TupleVariant {
type_of,
enum_item,
tuple,
},
source: None,
},
None => CompileMeta {
item: item.clone(),
kind: CompileMetaKind::TupleStruct { type_of, tuple },
source: None,
},
};
self.install_meta(meta)?;
let constructor: Arc<Handler> =
Arc::new(move |stack, args| constructor.fn_call(stack, args));
let signature = ContextSignature::Function {
item,
args: Some(args),
};
if let Some(old) = self.functions_info.insert(hash, signature) {
return Err(ContextError::ConflictingFunction {
signature: old,
hash,
});
}
self.functions.insert(hash, constructor);
Ok(())
}
}
impl fmt::Debug for Context {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Context")
}
}
#[cfg(test)]
mod tests {
use super::Context;
fn assert_send_sync<T>()
where
T: Send + Sync,
{
}
#[test]
fn assert_thread_safe_context() {
assert_send_sync::<Context>();
}
}