use crate::collections::HashMap;
use crate::hash::Hash;
use crate::value::{ValueType, ValueTypeInfo};
use crate::vm::{Vm, VmError};
use std::fmt;
use std::future::Future;
use std::pin::Pin;
use thiserror::Error;
mod item;
mod module;
pub use self::item::Item;
pub use self::module::Module;
#[derive(Debug, Error)]
pub enum ContextError {
#[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 `{type_info}` already exists")]
ConflictingInstanceFunction {
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("instance `{instance_type}` does not exist in module")]
MissingInstance {
instance_type: ValueTypeInfo,
},
}
type BoxFuture<'a, T> = Pin<Box<dyn Future<Output = T> + 'a>>;
pub(crate) enum Handler {
Async(Box<dyn for<'vm> Fn(&'vm mut Vm, usize) -> BoxFuture<'vm, Result<(), VmError>>>),
Regular(Box<dyn Fn(&mut Vm, usize) -> Result<(), VmError>>),
}
#[derive(Debug, Clone)]
pub struct TypeInfo {
pub name: Item,
pub value_type: ValueType,
pub type_info: ValueTypeInfo,
}
impl fmt::Display for TypeInfo {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(fmt, "{} => {}", self.name, self.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 {
functions: HashMap<Hash, Handler>,
functions_info: HashMap<Hash, FnSignature>,
types: HashMap<Hash, TypeInfo>,
types_rev: HashMap<ValueType, Hash>,
}
impl Context {
pub fn new() -> Self {
Context::default()
}
pub fn with_default_packages() -> Result<Self, ContextError> {
let mut this = Self::new();
this.install(crate::packages::core::module()?)?;
this.install(crate::packages::bytes::module()?)?;
this.install(crate::packages::string::module()?)?;
this.install(crate::packages::int::module()?)?;
this.install(crate::packages::float::module()?)?;
this.install(crate::packages::test::module()?)?;
this.install(crate::packages::iter::module()?)?;
this.install(crate::packages::array::module()?)?;
this.install(crate::packages::object::module()?)?;
Ok(this)
}
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, (type_info, name)) in module.types.into_iter() {
let name = module.path.join(&name);
let hash = Hash::of_type(&name);
let type_info = TypeInfo {
name,
value_type,
type_info,
};
if let Some(existing) = self.types.insert(hash, type_info) {
return Err(ContextError::ConflictingType {
name: existing.name,
existing: existing.type_info,
});
}
self.types_rev.insert(value_type, hash);
}
for (name, (handler, args)) in module.functions.into_iter() {
let name = module.path.join(&name);
let hash = Hash::function(&name);
let signature = FnSignature::new_free(name, args);
if let Some(old) = self.functions_info.insert(hash, signature) {
return Err(ContextError::ConflictingFunction {
signature: old,
hash,
});
}
self.functions.insert(hash, handler);
}
for ((ty, hash), (handler, args, instance_type, name)) in
module.instance_functions.into_iter()
{
let type_info = match self
.types_rev
.get(&ty)
.and_then(|hash| self.types.get(&hash))
{
Some(type_info) => type_info,
None => {
return Err(ContextError::MissingInstance { instance_type });
}
};
let hash = Hash::instance_function(ty, hash);
let signature =
FnSignature::new_inst(type_info.name.clone(), name, args, type_info.type_info);
if let Some(old) = self.functions_info.insert(hash, signature) {
return Err(ContextError::ConflictingFunction {
signature: old,
hash,
});
}
self.functions.insert(hash, handler);
}
Ok(())
}
pub(crate) fn lookup(&self, hash: Hash) -> Option<&Handler> {
let handler = self.functions.get(&hash)?;
Some(&*handler)
}
pub(crate) fn lookup_type(&self, hash: Hash) -> Option<&TypeInfo> {
self.types.get(&hash)
}
}
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()
}
}
#[derive(Debug, Clone, Copy)]
pub struct FnHash {
pub(crate) hash: Hash,
name: &'static str,
}
impl IntoInstFnHash for FnHash {
fn to_hash(self) -> Hash {
self.hash
}
fn to_name(self) -> String {
String::from(self.name)
}
}
impl std::ops::Deref for FnHash {
type Target = Hash;
fn deref(&self) -> &Self::Target {
&self.hash
}
}
pub const NEXT: FnHash = FnHash {
name: "next",
hash: Hash(0xc3cde069de2ba320),
};
pub const INDEX_GET: FnHash = FnHash {
name: "index_get",
hash: Hash(0xadb5b27e2a4d2dec),
};
pub const INDEX_SET: FnHash = FnHash {
name: "index_set",
hash: Hash(0x162943f7bd03ad36),
};
pub const ADD: FnHash = FnHash {
name: "add",
hash: Hash(0xe4ecf51fa0bf1076),
};
pub const ADD_ASSIGN: FnHash = FnHash {
name: "add_assign",
hash: Hash(0x42451ccb0a2071a9),
};
pub const SUB: FnHash = FnHash {
name: "sub",
hash: Hash(0x6fa86a5f18d0bf71),
};
pub const SUB_ASSIGN: FnHash = FnHash {
name: "sub_assign",
hash: Hash(0x5939bb56a1415284),
};
pub const MUL: FnHash = FnHash {
name: "mul",
hash: Hash(0xb09e99dc94091d1c),
};
pub const MUL_ASSIGN: FnHash = FnHash {
name: "mul_assign",
hash: Hash(0x29a54b727f980ebf),
};
pub const DIV: FnHash = FnHash {
name: "div",
hash: Hash(0xf26d6eea1afca6e8),
};
pub const DIV_ASSIGN: FnHash = FnHash {
name: "div_assign",
hash: Hash(0x4dd087a8281c04e6),
};