Skip to content

Instantly share code, notes, and snippets.

@ascjones
Last active May 1, 2020 10:33
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ascjones/77a1e4d69289129a17d923b111c77dda to your computer and use it in GitHub Desktop.
Save ascjones/77a1e4d69289129a17d923b111c77dda to your computer and use it in GitHub Desktop.
use std::any;
mod meta_type {
use super::*;
use super::TypeInfo;
use std::cmp::Ordering;
#[derive(Clone, Eq, PartialEq, PartialOrd, Ord, Debug)]
pub enum MetaType {
Parameter(MetaTypeParameter),
Concrete(MetaTypeConcrete),
Parameterized(MetaTypeParameterized),
Generic(MetaTypeGeneric),
}
impl MetaType
{
pub fn of<T>() -> Self
where
T: 'static + ?Sized + TypeInfo
{
MetaType::Concrete(MetaTypeConcrete::new::<T>())
}
pub fn parameter<T, P>(name: &'static str) -> Self
where
T: 'static + ?Sized + TypeInfo,
P: 'static + ?Sized + TypeInfo,
{
MetaType::Parameter(MetaTypeParameter::new::<T, P>(name))
}
pub fn parameterized<T>(params: Vec<MetaTypeParameterValue>) -> Self
where
T: 'static + ?Sized + TypeInfo
{
MetaType::Parameterized(MetaTypeParameterized {
concrete: MetaTypeConcrete::new::<T>(),
params,
})
}
}
#[derive(Clone, Debug)]
pub struct MetaTypeConcrete {
pub type_id: any::TypeId,
/// just for debugging
pub type_name: &'static str,
pub fn_type_info: fn() -> Type,
pub path: &'static str,
pub params: Vec<MetaTypeConcrete>,
}
impl PartialEq for MetaTypeConcrete {
fn eq(&self, other: &Self) -> bool {
self.type_id == other.type_id
}
}
impl Eq for MetaTypeConcrete {}
impl PartialOrd for MetaTypeConcrete {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
self.type_id.partial_cmp(&other.type_id)
}
}
impl Ord for MetaTypeConcrete {
fn cmp(&self, other: &Self) -> Ordering {
self.type_id.cmp(&other.type_id)
}
}
impl MetaTypeConcrete {
pub fn new<T>() -> Self
where
T: 'static + ?Sized + TypeInfo
{
Self {
type_id: any::TypeId::of::<T>(),
type_name: any::type_name::<T>(),
fn_type_info: T::type_info,
path: T::path(),
params: T::params(),
}
}
}
#[derive(Clone, Eq, PartialEq, PartialOrd, Ord, Debug)]
pub struct MetaTypeParameter {
pub name: &'static str,
pub parent: MetaTypeGeneric,
pub concrete: MetaTypeConcrete,
}
impl MetaTypeParameter {
pub fn new<T, P>(name: &'static str) -> Self
where
T: 'static + ?Sized + TypeInfo,
P: 'static + ?Sized + TypeInfo,
{
MetaTypeParameter {
name,
parent: MetaTypeGeneric::new::<T>(),
concrete: MetaTypeConcrete::new::<P>(),
}
}
}
#[derive(Clone, Eq, PartialEq, PartialOrd, Ord, Debug)]
pub struct MetaTypeParameterized {
pub concrete: MetaTypeConcrete,
pub params: Vec<MetaTypeParameterValue>,
}
#[derive(Clone, Eq, PartialEq, PartialOrd, Ord, Debug)]
pub enum MetaTypeParameterValue {
Concrete(MetaTypeConcrete),
Parameter(MetaTypeParameter),
}
impl MetaTypeParameterValue {
pub fn parameter<T, P>(name: &'static str) -> Self
where
T: 'static + ?Sized + TypeInfo,
P: 'static + ?Sized + TypeInfo,
{
MetaTypeParameterValue::Parameter(MetaTypeParameter {
name,
parent: MetaTypeGeneric::new::<T>(),
concrete: MetaTypeConcrete::new::<P>(),
})
}
pub fn concrete<T>() -> Self
where
T: 'static + ?Sized + TypeInfo
{
MetaTypeParameterValue::Concrete(MetaTypeConcrete::new::<T>())
}
pub fn concrete_type_id(&self) -> any::TypeId {
match self {
MetaTypeParameterValue::Concrete(concrete) => concrete.type_id,
MetaTypeParameterValue::Parameter(param) => param.concrete.type_id,
}
}
}
impl From<MetaTypeParameterValue> for MetaType {
fn from(p: MetaTypeParameterValue) -> Self {
match p {
MetaTypeParameterValue::Concrete(c) => MetaType::Concrete(c),
MetaTypeParameterValue::Parameter(p) => MetaType::Parameter(p),
}
}
}
#[derive(Clone, Eq, PartialEq, PartialOrd, Ord, Debug)]
pub struct MetaTypeGeneric {
pub fn_type_info: fn() -> Type,
pub path: &'static str,
}
impl MetaTypeGeneric {
fn new<T>() -> Self
where
T: 'static + ?Sized + TypeInfo
{
Self {
fn_type_info: T::type_info,
path: T::path(),
}
}
}
}
mod form {
use super::*;
pub trait Form {
type Type: Clone + Eq + PartialEq + Ord + PartialOrd + std::fmt::Debug;
}
#[derive(Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Debug)]
pub enum MetaForm {}
impl Form for MetaForm {
type Type = MetaType;
}
#[derive(Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Debug)]
pub enum CompactForm {}
impl Form for CompactForm {
type Type = u32;
}
}
mod registry {
use std::collections::btree_map::{BTreeMap, Entry};
use super::*;
use super::form::*;
use super::meta_type::*;
use super::Type;
use std::fmt::{Debug, Formatter, Result};
pub trait IntoCompact {
type Output;
fn into_compact(self, registry: &mut Registry) -> Self::Output;
}
#[derive(Debug, PartialEq, Eq, Ord, PartialOrd, Clone)]
pub enum TypeId {
Any(any::TypeId),
Path(&'static str),
Parameter(TypeParameter<CompactForm>),
Generic(RegistryTypeGeneric<CompactForm>),
}
#[derive(Debug)]
pub enum RegistryType<F: Form = MetaForm> {
Definition(&'static str, Type<F>),
Parameter(TypeParameter<F>),
Generic(RegistryTypeGeneric<F>),
}
impl IntoCompact for RegistryType<MetaForm> {
type Output = RegistryType<CompactForm>;
fn into_compact(self, registry: &mut Registry) -> Self::Output {
match self {
RegistryType::Definition(path, ty) => RegistryType::Definition(path, ty.into_compact(registry)),
RegistryType::Parameter(tp) => RegistryType::Parameter(tp.into_compact(registry)),
RegistryType::Generic(g) => RegistryType::Generic(g.into_compact(registry)),
}
}
}
/// Identity for already compact RegistryType
impl IntoCompact for RegistryType<CompactForm> {
type Output = RegistryType<CompactForm>;
fn into_compact(self, _registry: &mut Registry) -> Self::Output {
self
}
}
#[derive(Debug, PartialEq, Eq, Ord, PartialOrd, Clone)]
pub struct TypeParameter<F: Form = MetaForm> {
path: &'static str,
parent: F::Type,
}
impl IntoCompact for TypeParameter<MetaForm> {
type Output = TypeParameter<CompactForm>;
fn into_compact(self, registry: &mut Registry) -> Self::Output {
TypeParameter {
path: self.path,
parent: registry.register_type(&self.parent),
}
}
}
#[derive(Debug, PartialEq, Eq, Ord, PartialOrd, Clone)]
pub struct RegistryTypeGeneric<F: Form = MetaForm> {
ty: F::Type,
params: Vec<F::Type>,
}
impl IntoCompact for RegistryTypeGeneric<MetaForm> {
type Output = RegistryTypeGeneric<CompactForm>;
fn into_compact(self, registry: &mut Registry) -> Self::Output {
RegistryTypeGeneric {
ty: registry.register_type(&self.ty),
params: self.params.iter().map(|p| registry.register_type(p)).collect(),
}
}
}
#[derive(Default)]
pub struct Registry {
type_table: BTreeMap<TypeId, usize>,
type_ids: Vec<TypeId>,
params: Vec<MetaTypeParameterValue>,
types: BTreeMap<<CompactForm as Form>::Type, RegistryType<CompactForm>>,
}
impl Debug for Registry {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
for (id, ty) in self.types.iter() {
writeln!(f, "{:?} {:?}", id, ty)?;
}
Ok(())
}
}
impl Registry {
fn intern_type<F, T>(&mut self, type_id: TypeId, f: F) -> <CompactForm as Form>::Type
where
F: FnOnce () -> T,
T: IntoCompact<Output = RegistryType<CompactForm>>
{
let next_id = self.type_ids.len();
let (inserted, sym_id) = match self.type_table.entry(type_id.clone()) {
Entry::Vacant(vacant) => {
vacant.insert(next_id);
self.type_ids.push(type_id);
(true, next_id)
}
Entry::Occupied(occupied) => (false, *occupied.get()),
};
let symbol = (sym_id + 1) as u32;
if inserted {
let registry_type = f();
let compact_type = registry_type.into_compact(self);
self.types.insert(symbol.clone(), compact_type);
}
symbol
}
pub fn register_type(&mut self, ty: &MetaType) -> <CompactForm as Form>::Type {
match ty {
MetaType::Concrete(concrete) => {
if concrete.params.len() > 0 {
let generic_meta_type = MetaType::Generic(MetaTypeGeneric {
fn_type_info: concrete.fn_type_info.clone(),
path: concrete.path,
});
let generic = RegistryTypeGeneric {
ty: generic_meta_type,
params: concrete.params.iter().map(|p| MetaType::Concrete(p.clone())).collect(),
};
let compact_generic = generic.into_compact(self);
let type_id = TypeId::Generic(compact_generic.clone());
self.intern_type(type_id, || RegistryType::Generic(compact_generic))
} else {
let type_id = TypeId::Any(concrete.type_id);
self.intern_type(type_id, || {
let type_info = (concrete.fn_type_info)();
RegistryType::Definition(concrete.path, type_info)
})
}
}
MetaType::Generic(ty) => {
let type_id = TypeId::Path(ty.path);
self.intern_type(type_id, || {
let type_info = (ty.fn_type_info)();
RegistryType::Definition(ty.path, type_info)
})
}
MetaType::Parameter(p) => {
let generic_meta_type = MetaType::Generic(p.parent.clone());
let type_parameter = TypeParameter {
parent: generic_meta_type,
path: p.name,
};
let param_type_id = TypeId::Parameter(type_parameter.clone().into_compact(self));
self.intern_type(param_type_id, || {
RegistryType::Parameter(type_parameter)
})
}
MetaType::Parameterized(parameterized) => {
let generic_meta_type = MetaType::Generic(MetaTypeGeneric {
fn_type_info: parameterized.concrete.fn_type_info.clone(),
path: parameterized.concrete.path,
});
self.params.extend(parameterized.params.iter().cloned().rev());
let params = parameterized.concrete.params.iter().map(|concrete_param| {
if let Some(param) = self.params.pop() {
if param.concrete_type_id() == concrete_param.type_id {
self.register_type(&param.into())
} else if concrete_param.params.len() > 0 {
self.params.push(param);
// recurse
self.register_type(&MetaType::Parameterized(MetaTypeParameterized {
concrete: concrete_param.clone(),
params: Vec::new(),
}))
} else {
panic!("Should either be matching concrete type (e.g. bool) or parameterized e.g. Option<T>")
}
} else {
self.register_type(&&MetaType::Concrete(concrete_param.clone()))
}
}).collect::<Vec<_>>();
let generic = RegistryTypeGeneric {
ty: self.register_type(&generic_meta_type),
params
};
let type_id = TypeId::Generic(generic.clone());
self.intern_type(type_id, || RegistryType::Generic(generic))
}
}
}
}
}
use meta_type::*;
use form::*;
use registry::*;
#[derive(Debug)]
pub enum Type<F: Form = MetaForm> {
Primitive(Primitive),
Struct(Struct<F>),
}
impl IntoCompact for Type<MetaForm> {
type Output = Type<CompactForm>;
fn into_compact(self, registry: &mut Registry) -> Self::Output {
match self {
Type::Primitive(primitive) => Type::Primitive(primitive),
Type::Struct(s) => Type::Struct(s.into_compact(registry)),
}
}
}
#[derive(Debug)]
pub struct Struct<F: Form = MetaForm> {
fields: Vec<Field<F>>,
}
impl IntoCompact for Struct<MetaForm> {
type Output = Struct<CompactForm>;
fn into_compact(self, registry: &mut Registry) -> Self::Output {
Struct {
fields: self.fields.iter().map(|f| f.clone().into_compact(registry)).collect()
}
}
}
#[derive(Debug, Clone)]
pub struct Field<F: Form = MetaForm> {
name: &'static str,
ty: F::Type,
}
impl IntoCompact for Field<MetaForm> {
type Output = Field<CompactForm>;
fn into_compact(self, registry: &mut Registry) -> Self::Output {
Field {
name: self.name,
ty: registry.register_type(&self.ty),
}
}
}
impl Field {
pub fn new(name: &'static str, ty: MetaType) -> Self {
Field {
name,
ty
}
}
}
#[derive(Debug)]
pub enum Primitive {
Bool,
U32,
}
pub trait TypeInfo {
fn path() -> &'static str;
fn params() -> Vec<MetaTypeConcrete> {
Vec::new()
}
fn type_info() -> Type;
}
impl TypeInfo for bool {
fn path() -> &'static str {
""
}
fn type_info() -> Type {
Type::Primitive(Primitive::Bool)
}
}
impl TypeInfo for u32 {
fn path() -> &'static str {
""
}
fn type_info() -> Type {
Type::Primitive(Primitive::U32)
}
}
#[allow(unused)]
struct A<T> {
a: B<T, bool>,
b: B<B<T, T>, bool>,
}
impl<T> TypeInfo for A<T> where T: TypeInfo + 'static
{
fn path() -> &'static str {
"A"
}
fn params() -> Vec<MetaTypeConcrete> {
vec![MetaTypeConcrete::new::<T>()]
}
fn type_info() -> Type {
Type::Struct (Struct {
fields: vec! [
Field::new(
"a",
MetaType::parameterized::<B<T, bool>>(
vec![
MetaTypeParameterValue::parameter::<Self, T>("T"),
MetaTypeParameterValue::concrete::<bool>(),
]
)
),
Field::new(
"b",
MetaType::parameterized::<B<B<T, T>, bool>>(
vec![
MetaTypeParameterValue::parameter::<Self, T>("T"),
MetaTypeParameterValue::parameter::<Self, T>("T"),
MetaTypeParameterValue::concrete::<bool>(),
]
)),
]
})
}
}
#[allow(unused)]
struct B<T, U> {
a: T,
b: U,
c: C<T>,
}
impl<T, U> TypeInfo for B<T, U>
where
T: TypeInfo + 'static,
U: TypeInfo + 'static,
{
fn path() -> &'static str {
"B"
}
fn params() -> Vec<MetaTypeConcrete> {
vec![
MetaTypeConcrete::new::<T>(),
MetaTypeConcrete::new::<U>(),
]
}
fn type_info() -> Type {
Type::Struct (Struct {
fields: vec! [
Field::new("a", MetaType::parameter::<Self, T>("T")),
Field::new("b", MetaType::parameter::<Self, U>("U")),
Field::new("c", MetaType::parameterized::<C<T>>(
vec![MetaTypeParameterValue::parameter::<Self, T>("T")]
)),
]
})
}
}
#[allow(unused)]
struct C<T> {
a: T,
}
impl<T> TypeInfo for C<T> where T: TypeInfo + 'static
{
fn path() -> &'static str {
"C"
}
fn params() -> Vec<MetaTypeConcrete> {
vec![MetaTypeConcrete::new::<T>()]
}
fn type_info() -> Type {
Type::Struct (Struct {
fields: vec! [
Field::new("a", MetaType::parameter::<Self, T>("T")),
]
})
}
}
fn main() {
let mut registry = Registry::default();
registry.register_type(&MetaType::of::<B<bool, u32>>());
registry.register_type(&MetaType::of::<B<u32, bool>>());
registry.register_type(&MetaType::of::<A<bool>>());
registry.register_type(&MetaType::of::<A<A<bool>>>());
println!();
println!("{:?}", registry);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment