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
ns.register_command(
CMD_ADD,
Some(TypeDef::List(vec![
TypeDef::Trait(numeric),
TypeDef::Trait(numeric),
vec![("T", vec![&numeric])],
Some(&TypeDef::List(vec![
TypeDef::Generic("T".to_owned()),
TypeDef::Generic("T".to_owned()),
])),
Some(TypeDef::Trait(numeric)),
Some(&TypeDef::Generic("T".to_owned())),
)
.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::Display;
use super::typedef::{InternalTypeDef, TypeDef};
use super::CommandId;
use super::TraitId;
use super::GlobalNamespace;
@ -25,7 +27,7 @@ impl<'a> Command<'a> {
.input
.as_ref()
.map(|inputs| match inputs {
InternalTypeDef::Single(_) => 1,
InternalTypeDef::Single(_) | InternalTypeDef::Generic(_) => 1,
InternalTypeDef::List(list) => list.len(),
InternalTypeDef::Record(rec) => rec.len(),
})
@ -68,5 +70,4 @@ pub(super) struct InternalCommand {
// gosh this is hacky
pub(super) input: Option<InternalTypeDef>,
pub(super) output: Option<InternalTypeDef>,
// generic_ns: HashMap<String, Vec<usize>>,
}

View file

@ -102,15 +102,37 @@ impl GlobalNamespace {
/// # Errors
///
/// Will return `NsRegistrationError::NameAlreadyExists` if the desired name is already in use
pub fn register_command(
pub fn register_command<'a>(
&self,
name: &str,
input: Option<TypeDef>,
output: Option<TypeDef>,
) -> Result<Command, NsRegistrationError> {
name: &'a str,
// TODO: refacto with builder pattern maybe?
generics: Vec<(&'a str, Vec<&Trait>)>,
input: Option<&'a TypeDef>,
output: Option<&'a TypeDef>,
) -> Result<Command, NsRegistrationError<'a>> {
if self.data_namespace.borrow().contains_key(name) {
Err(NsRegistrationError::NameAlreadyExists)
} 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 {
name: name.to_owned(),
input: input.map(std::convert::Into::into),
@ -184,6 +206,8 @@ impl GlobalNamespace {
}
#[derive(Debug)]
pub enum NsRegistrationError {
pub enum NsRegistrationError<'a> {
NameAlreadyExists,
GenericNameAlreadyExists,
UnregisteredGenericsUsed(Vec<&'a str>),
}

View file

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

View file

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