use crate::collections::{HashMap, HashSet};
use crate::module::{ModuleAssociatedFn, ModuleFn, ModuleInternalEnum, ModuleType, ModuleUnitType};
use crate::{
Component, Hash, Item, Meta, MetaStruct, MetaTuple, Module, Names, ReflectValueType, Stack,
StaticType, TypeCheck, ValueType, ValueTypeInfo, VmError,
};
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 item `{item}`, inserted `{current}` while `{existing}` already existed")]
ConflictingMeta {
item: Item,
current: Box<Meta>,
existing: Box<Meta>,
},
#[error("function `{signature}` ({hash}) already exists")]
ConflictingFunction {
signature: FnSignature,
hash: Hash,
},
#[error("function with name `{name}` already exists")]
ConflictingFunctionName {
name: Item,
},
#[error("instance function `{name}` for type `{value_type_info}` already exists")]
ConflictingInstanceFunction {
value_type_info: ValueTypeInfo,
name: String,
},
#[error("module `{name}` with hash `{hash}` already exists")]
ConflictingModule {
name: Item,
hash: Hash,
},
#[error("type with name `{name}` already exists `{existing}`")]
ConflictingType {
name: Item,
existing: ValueTypeInfo,
},
#[error("tried to insert conflicting hash type `{hash}` (existing `{existing}`) for type `{value_type}`")]
ConflictingTypeHash {
hash: Hash,
existing: Hash,
value_type: ValueType,
},
#[error("variant with name `{name}` already exists")]
ConflictingVariant {
name: Item,
},
#[error("instance `{instance_type}` does not exist in module")]
MissingInstance {
instance_type: ValueTypeInfo,
},
#[error("type `{value_type}` cannot be defined dynamically")]
UnsupportedValueType {
value_type: ValueType,
},
}
pub(crate) type Handler = dyn Fn(&mut Stack, usize) -> Result<(), VmError> + Sync;
#[derive(Debug, Clone)]
pub struct TypeInfo {
pub type_check: TypeCheck,
pub name: Item,
pub value_type: ValueType,
pub value_type_info: ValueTypeInfo,
}
impl fmt::Display for TypeInfo {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(fmt, "{} => {}", self.name, self.value_type_info)?;
Ok(())
}
}
#[derive(Debug, Clone)]
pub enum FnSignature {
Free {
path: Item,
args: Option<usize>,
},
Instance {
path: Item,
name: String,
args: Option<usize>,
self_type_info: ValueTypeInfo,
},
}
impl FnSignature {
pub fn new_free(path: Item, args: Option<usize>) -> Self {
Self::Free { path, args }
}
pub fn new_inst(
path: Item,
name: String,
args: Option<usize>,
self_type_info: ValueTypeInfo,
) -> Self {
Self::Instance {
path,
name,
args,
self_type_info,
}
}
}
impl fmt::Display for FnSignature {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Free { path, args } => {
write!(fmt, "{}(", path)?;
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 {
path,
name,
self_type_info,
args,
} => {
write!(fmt, "{}::{}(self: {}", path, 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 {
meta: HashMap<Item, Meta>,
functions: HashMap<Hash, Arc<Handler>>,
functions_info: HashMap<Hash, FnSignature>,
types: HashMap<Hash, TypeInfo>,
types_rev: HashMap<ValueType, 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> {
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::test::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()?)?;
this.install(&crate::modules::fmt::module()?)?;
Ok(this)
}
pub fn iter_components<'a, I>(&'a self, iter: I) -> impl Iterator<Item = &'a Component>
where
I: IntoIterator,
I::Item: Into<Component>,
{
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_meta(&self, name: &Item) -> Option<Meta> {
self.meta.get(name).cloned()
}
pub fn iter_functions(&self) -> impl Iterator<Item = (Hash, &FnSignature)> {
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, &TypeInfo)> {
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 (value_type, ty) in &module.types {
self.install_type(&module, *value_type, ty)?;
}
for (name, f) in &module.functions {
self.install_function(&module, name, f)?;
}
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.value_type,
key.hash,
inst,
key.kind.into_hash_fn(),
)?;
}
Ok(())
}
fn install_meta(&mut self, item: Item, meta: Meta) -> Result<(), ContextError> {
if let Some(existing) = self.meta.insert(item.clone(), meta.clone()) {
return Err(ContextError::ConflictingMeta {
item,
existing: Box::new(existing),
current: Box::new(meta),
});
}
Ok(())
}
fn install_type(
&mut self,
module: &Module,
value_type: ValueType,
ty: &ModuleType,
) -> Result<(), ContextError> {
let name = module.path.join(&ty.name);
let hash = Hash::type_hash(&name);
self.install_type_info(
hash,
TypeInfo {
type_check: TypeCheck::Type(value_type.as_type_hash()),
name: name.clone(),
value_type,
value_type_info: ty.value_type_info,
},
)?;
self.install_meta(
name.clone(),
Meta::MetaStruct {
value_type,
object: MetaStruct {
item: name.clone(),
fields: None,
},
},
)?;
Ok(())
}
fn install_type_info(&mut self, hash: Hash, info: TypeInfo) -> Result<(), ContextError> {
self.names.insert(&info.name);
if let Some(existing) = self.types_rev.insert(info.value_type, hash) {
return Err(ContextError::ConflictingTypeHash {
hash,
existing,
value_type: info.value_type,
});
}
if let Some(existing) = self.types.insert(hash, info) {
return Err(ContextError::ConflictingType {
name: existing.name,
existing: existing.value_type_info,
});
}
Ok(())
}
fn install_function(
&mut self,
module: &Module,
name: &Item,
f: &ModuleFn,
) -> Result<(), ContextError> {
let name = module.path.join(name);
self.names.insert(&name);
let hash = Hash::type_hash(&name);
let signature = FnSignature::new_free(name.clone(), 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(
name.clone(),
Meta::MetaFunction {
value_type: ValueType::Type(hash),
item: name.clone(),
},
);
Ok(())
}
fn install_associated_function(
&mut self,
value_type: ValueType,
hash: Hash,
assoc: &ModuleAssociatedFn,
hash_fn: impl FnOnce(ValueType, Hash) -> Hash,
) -> Result<(), ContextError> {
let info = match self
.types_rev
.get(&value_type)
.and_then(|hash| self.types.get(&hash))
{
Some(info) => info,
None => {
return Err(ContextError::MissingInstance {
instance_type: assoc.value_type_info,
});
}
};
let hash = hash_fn(value_type, hash);
let signature = FnSignature::new_inst(
info.name.clone(),
assoc.name.clone(),
assoc.args,
info.value_type_info,
);
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.join(&unit_type.item);
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,
TypeInfo {
type_check: TypeCheck::Unit,
name: item.clone(),
value_type: ValueType::StaticType(crate::UNIT_TYPE),
value_type_info: ValueTypeInfo::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(
enum_item.clone(),
Meta::MetaEnum {
value_type: ValueType::StaticType(internal_enum.static_type),
item: enum_item.clone(),
},
)?;
self.install_type_info(
enum_hash,
TypeInfo {
type_check: TypeCheck::Type(internal_enum.static_type.hash),
name: enum_item.clone(),
value_type: ValueType::StaticType(internal_enum.static_type),
value_type_info: ValueTypeInfo::StaticType(internal_enum.static_type),
},
)?;
for variant in &internal_enum.variants {
let item = enum_item.clone().extended(variant.name);
let hash = Hash::type_hash(&item);
self.install_type_info(
hash,
TypeInfo {
type_check: variant.type_check,
name: item.clone(),
value_type: ValueType::Type(hash),
value_type_info: ValueTypeInfo::StaticType(internal_enum.static_type),
},
)?;
let tuple = MetaTuple {
item: item.clone(),
args: variant.args,
hash,
};
let meta = Meta::MetaVariantTuple {
value_type: variant.value_type,
enum_item: enum_item.clone(),
tuple,
};
self.install_meta(item.clone(), meta)?;
let signature = FnSignature::new_free(item, 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: ReflectValueType,
{
let value_type = <C::Return as ReflectValueType>::value_type();
let hash = Hash::type_hash(&item);
let tuple = MetaTuple {
item: item.clone(),
args,
hash,
};
let meta = match enum_item {
Some(enum_item) => Meta::MetaVariantTuple {
value_type,
enum_item,
tuple,
},
None => Meta::MetaTuple { value_type, tuple },
};
self.install_meta(item.clone(), meta)?;
let constructor: Arc<Handler> =
Arc::new(move |stack, args| constructor.fn_call(stack, args));
let signature = FnSignature::new_free(item, 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(())
}
pub(crate) fn lookup(&self, hash: Hash) -> Option<&Arc<Handler>> {
self.functions.get(&hash)
}
}
impl fmt::Debug for Context {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Context")
}
}
pub trait IntoInstFnHash: Copy {
fn to_hash(self) -> Hash;
fn to_name(self) -> String;
}
impl<'a> IntoInstFnHash for &'a str {
fn to_hash(self) -> Hash {
Hash::of(self)
}
fn to_name(self) -> String {
self.to_owned()
}
}