implement basics of generics and generic checking

This commit is contained in:
Schrottkatze 2023-11-24 12:57:06 +01:00
parent 1acb5ef3cf
commit ab7ff35d6c
6 changed files with 146 additions and 43 deletions

View file

@ -26,11 +26,12 @@ pub fn initialise_globals() -> GlobalNamespace {
// def math add [ Numeric Numeric ] -> Numeric // def math add [ Numeric Numeric ] -> Numeric
ns.register_command( ns.register_command(
CMD_ADD, CMD_ADD,
Some(TypeDef::List(vec![ vec![("T", vec![&numeric])],
TypeDef::Trait(numeric), Some(&TypeDef::List(vec![
TypeDef::Trait(numeric), TypeDef::Generic("T".to_owned()),
TypeDef::Generic("T".to_owned()),
])), ])),
Some(TypeDef::Trait(numeric)), Some(&TypeDef::Generic("T".to_owned())),
) )
.unwrap(); .unwrap();

26
src/globals.rs Normal file
View file

@ -0,0 +1,26 @@
// concepts
// root type namespace
// -> builtin types/traits/functions
// -> builtin constants
//
// generic item namespaces
// -> in generic functions ( `add<T: Num> [ T T ] -> T` )
// -> in generic types ( vec<T> )
// -> generic traits ( T: From<E> )
//
// TODO: how builtins? just compile everything to builtin types/defs?
/// This is the root type namespace.
struct RootTypeNamespace {
}
mod r#type {
}
struct Type<'a> {
pub(super) id: usize,
pub(super) namespace: &'a GlobalNamespace,
}
struct InternalType

View file

@ -1,8 +1,10 @@
use std::collections::HashMap;
use std::fmt::Debug; use std::fmt::Debug;
use std::fmt::Display; use std::fmt::Display;
use super::typedef::{InternalTypeDef, TypeDef}; use super::typedef::{InternalTypeDef, TypeDef};
use super::CommandId; use super::CommandId;
use super::TraitId;
use super::GlobalNamespace; use super::GlobalNamespace;
@ -25,7 +27,7 @@ impl<'a> Command<'a> {
.input .input
.as_ref() .as_ref()
.map(|inputs| match inputs { .map(|inputs| match inputs {
InternalTypeDef::Single(_) => 1, InternalTypeDef::Single(_) | InternalTypeDef::Generic(_) => 1,
InternalTypeDef::List(list) => list.len(), InternalTypeDef::List(list) => list.len(),
InternalTypeDef::Record(rec) => rec.len(), InternalTypeDef::Record(rec) => rec.len(),
}) })
@ -68,5 +70,4 @@ pub(super) struct InternalCommand {
// gosh this is hacky // gosh this is hacky
pub(super) input: Option<InternalTypeDef>, pub(super) input: Option<InternalTypeDef>,
pub(super) output: Option<InternalTypeDef>, pub(super) output: Option<InternalTypeDef>,
// generic_ns: HashMap<String, Vec<usize>>,
} }

View file

@ -102,15 +102,37 @@ impl GlobalNamespace {
/// # Errors /// # Errors
/// ///
/// Will return `NsRegistrationError::NameAlreadyExists` if the desired name is already in use /// Will return `NsRegistrationError::NameAlreadyExists` if the desired name is already in use
pub fn register_command( pub fn register_command<'a>(
&self, &self,
name: &str, name: &'a str,
input: Option<TypeDef>, // TODO: refacto with builder pattern maybe?
output: Option<TypeDef>, generics: Vec<(&'a str, Vec<&Trait>)>,
) -> Result<Command, NsRegistrationError> { input: Option<&'a TypeDef>,
output: Option<&'a TypeDef>,
) -> Result<Command, NsRegistrationError<'a>> {
if self.data_namespace.borrow().contains_key(name) { if self.data_namespace.borrow().contains_key(name) {
Err(NsRegistrationError::NameAlreadyExists) Err(NsRegistrationError::NameAlreadyExists)
} else { } else {
let mut internal_generics_namespace: HashMap<String, Vec<TraitId>> = HashMap::new();
for (name, conditions) in generics {
if internal_generics_namespace.contains_key(name) {
return Err(NsRegistrationError::GenericNameAlreadyExists);
}
internal_generics_namespace
.insert(name.to_string(), conditions.iter().map(|t| t.id).collect());
}
if let Some(def) = input {
def.check_generics_exist(&internal_generics_namespace)
.map_err(NsRegistrationError::UnregisteredGenericsUsed)?;
}
if let Some(def) = output {
def.check_generics_exist(&internal_generics_namespace)
.map_err(NsRegistrationError::UnregisteredGenericsUsed)?;
}
self.commands.borrow_mut().push(InternalCommand { self.commands.borrow_mut().push(InternalCommand {
name: name.to_owned(), name: name.to_owned(),
input: input.map(std::convert::Into::into), input: input.map(std::convert::Into::into),
@ -184,6 +206,8 @@ impl GlobalNamespace {
} }
#[derive(Debug)] #[derive(Debug)]
pub enum NsRegistrationError { pub enum NsRegistrationError<'a> {
NameAlreadyExists, NameAlreadyExists,
GenericNameAlreadyExists,
UnregisteredGenericsUsed(Vec<&'a str>),
} }

View file

@ -1,6 +1,9 @@
use std::collections::HashMap;
use std::fmt::Debug; use std::fmt::Debug;
use std::fmt::Display; use std::fmt::Display;
use super::TraitId;
use super::TypeId;
use super::TypeNamespaceId; use super::TypeNamespaceId;
use super::GlobalNamespace; use super::GlobalNamespace;
@ -10,25 +13,76 @@ use super::r#type::Type;
pub enum TypeDef<'a> { pub enum TypeDef<'a> {
Type(Type<'a>), Type(Type<'a>),
Trait(Trait<'a>), Generic(String),
List(Vec<TypeDef<'a>>), List(Vec<TypeDef<'a>>),
Record(Vec<(String, TypeDef<'a>)>), Record(Vec<(String, TypeDef<'a>)>),
} }
impl<'a> TypeDef<'a> { impl<'a> TypeDef<'a> {
pub(super) fn check_generics_exist(
&'a self,
map: &HashMap<String, Vec<TraitId>>,
) -> Result<(), Vec<&'a str>> {
match self {
TypeDef::Type(_) => Ok(()),
TypeDef::Generic(name) => {
if map.contains_key(name) {
Ok(())
} else {
Err(vec![name])
}
}
TypeDef::List(defs) => {
let r = defs
.into_iter()
.map(|def| def.check_generics_exist(map))
.filter_map(|check_res| {
if let Err(invalid_names) = check_res {
Some(invalid_names)
} else {
None
}
})
.reduce(|mut acc, mut errs| {
acc.append(&mut errs);
acc
});
match r {
Some(e) => Err(e),
None => Ok(()),
}
}
TypeDef::Record(rec) => {
let r = rec
.into_iter()
.map(|(n, def)| def.check_generics_exist(map))
.filter_map(|check_res| {
if let Err(invalid_names) = check_res {
Some(invalid_names)
} else {
None
}
})
.reduce(|mut acc, mut errs| {
acc.append(&mut errs);
acc
});
match r {
Some(e) => Err(e),
None => Ok(()),
}
}
}
}
pub(super) fn from_internal(ns: &'a GlobalNamespace, def: &InternalTypeDef) -> TypeDef<'a> { pub(super) fn from_internal(ns: &'a GlobalNamespace, def: &InternalTypeDef) -> TypeDef<'a> {
match def { match def {
InternalTypeDef::Single(id) => match id { InternalTypeDef::Single(id) => TypeDef::Type(
// safe to unwrap because this is only used with internal representations
TypeNamespaceId::Types(id) => TypeDef::Type(
ns.get_type(*id) ns.get_type(*id)
.expect("Incorrect internal type id. This is a bug."), .expect("Unregistered internal type ID. This is a bug."),
), ),
TypeNamespaceId::Traits(id) => TypeDef::Trait( InternalTypeDef::Generic(name) => TypeDef::Generic(name.clone()),
ns.get_trait(*id)
.expect("Incorrect internal trait id. This is a bug."),
),
},
InternalTypeDef::List(list) => TypeDef::List( InternalTypeDef::List(list) => TypeDef::List(
list.iter() list.iter()
.map(|def| Self::from_internal(ns, def)) .map(|def| Self::from_internal(ns, def))
@ -47,13 +101,14 @@ impl Display for TypeDef<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self { match self {
TypeDef::Type(t) => Display::fmt(&t, f), TypeDef::Type(t) => Display::fmt(&t, f),
TypeDef::Trait(t) => Display::fmt(&t, f), TypeDef::Generic(name) => Display::fmt(name, f),
TypeDef::List(l) => { TypeDef::List(l) => {
f.write_str("[ ")?; f.write_str("[ ")?;
for (i, item) in l.iter().enumerate() { if let Some(first) = l.first() {
if i != 0 { Display::fmt(&first, f)?;
f.write_str(", ")?;
} }
for (i, item) in l.iter().skip(1).enumerate() {
f.write_str(", ")?;
Display::fmt(&item, f)?; Display::fmt(&item, f)?;
} }
f.write_str(" ]") f.write_str(" ]")
@ -76,7 +131,7 @@ impl Debug for TypeDef<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self { match self {
TypeDef::Type(t) => Debug::fmt(&t, f), TypeDef::Type(t) => Debug::fmt(&t, f),
TypeDef::Trait(t) => Debug::fmt(&t, f), TypeDef::Generic(name) => Debug::fmt(name, f),
TypeDef::List(l) => { TypeDef::List(l) => {
f.write_str("[ ")?; f.write_str("[ ")?;
for (i, item) in l.iter().enumerate() { for (i, item) in l.iter().enumerate() {
@ -107,29 +162,25 @@ impl<'a> From<Type<'a>> for TypeDef<'a> {
} }
} }
impl<'a> From<Trait<'a>> for TypeDef<'a> {
fn from(value: Trait<'a>) -> Self {
TypeDef::Trait(value)
}
}
pub(super) enum InternalTypeDef { pub(super) enum InternalTypeDef {
Single(TypeNamespaceId), Single(TypeId),
Generic(String),
List(Vec<InternalTypeDef>), List(Vec<InternalTypeDef>),
Record(Vec<(String, InternalTypeDef)>), Record(Vec<(String, InternalTypeDef)>),
} }
impl From<TypeDef<'_>> for InternalTypeDef { impl From<&TypeDef<'_>> for InternalTypeDef {
fn from(value: TypeDef) -> Self { fn from(value: &TypeDef) -> Self {
match value { match value {
TypeDef::Type(val) => Self::Single(TypeNamespaceId::Types(val.id)), TypeDef::Type(val) => Self::Single(val.id),
TypeDef::Trait(val) => Self::Single(TypeNamespaceId::Traits(val.id)), // TODO: rewrite this to be better
TypeDef::Generic(name) => Self::Generic(name.to_owned()),
TypeDef::List(list) => { TypeDef::List(list) => {
Self::List(list.into_iter().map(std::convert::Into::into).collect()) Self::List(list.into_iter().map(std::convert::Into::into).collect())
} }
TypeDef::Record(rec) => Self::Record( TypeDef::Record(rec) => Self::Record(
rec.into_iter() rec.into_iter()
.map(|(name, typ)| (name, typ.into())) .map(|(name, typ)| (name.to_owned(), typ.into()))
.collect(), .collect(),
), ),
} }

View file

@ -195,7 +195,7 @@ impl<'a> ConcreteTypeDef<'a> {
fn try_from_typedef(val: &TypeDef<'a>) -> Option<Self> { fn try_from_typedef(val: &TypeDef<'a>) -> Option<Self> {
match val { match val {
TypeDef::Type(typ) => Some(Self::Single(*typ)), TypeDef::Type(typ) => Some(Self::Single(*typ)),
TypeDef::Trait(_) => None, TypeDef::Generic(_) => todo!(),
TypeDef::List(list) => { TypeDef::List(list) => {
let out: Vec<ConcreteTypeDef<'a>> = let out: Vec<ConcreteTypeDef<'a>> =
list.iter().filter_map(Self::try_from_typedef).collect(); list.iter().filter_map(Self::try_from_typedef).collect();