use super::*;
#[derive(Clone, PartialEq, Eq, TryHash, PartialOrd, Ord)]
pub enum TypeShape {
Unit,
Bool,
Int32,
Int64,
Float64,
Char,
String,
List(#[try_hash] Type),
Variant(#[try_hash] Vec<VariantType>),
Tuple(#[try_hash] Tuple<Type>),
Function(#[try_hash] Box<FnType>),
Template(MaybeCompiledFn),
Macro(#[try_hash] Box<FnType>),
Multiset,
Contexts,
Ast,
#[allow(clippy::enum_variant_names)]
Type,
SyntaxModule,
SyntaxDefinition,
UnwindHandle(#[try_hash] Type),
Binding(Parc<Binding>),
Symbol,
HashMap(#[try_hash] HashMapType),
Ref(#[try_hash] Type),
}
impl std::fmt::Debug for TypeShape {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{self}")
}
}
impl ShowShort for TypeShape {
fn show_short(&self) -> &'static str {
match self {
TypeShape::Unit => "unit",
TypeShape::Bool => "bool",
TypeShape::Int32 => "int32",
TypeShape::Int64 => "int64",
TypeShape::Float64 => "float64",
TypeShape::Char => "char",
TypeShape::String => "string",
TypeShape::List(_) => "list",
TypeShape::Variant(_) => "variant",
TypeShape::Tuple(_) => "tuple",
TypeShape::Function(_) => "function",
TypeShape::Template(_) => "template",
TypeShape::Macro(_) => "macro",
TypeShape::Multiset => "multiset",
TypeShape::Contexts => "contexts",
TypeShape::Ast => "ast",
TypeShape::Type => "type",
TypeShape::SyntaxModule => "syntax module",
TypeShape::SyntaxDefinition => "syntax def",
TypeShape::UnwindHandle(_) => "unwind handle",
TypeShape::Binding(_) => "binding",
TypeShape::Symbol => "symbol",
TypeShape::HashMap(_) => "hash_map",
TypeShape::Ref(_) => "&",
}
}
}
pub type Type = inference::MaybeNotInferred<TypeShape>;
#[derive(Debug, Clone, PartialEq, Eq, TryHash, PartialOrd, Ord)]
pub struct HashMapType {
#[try_hash]
pub key: Type,
#[try_hash]
pub value: Type,
}
impl std::fmt::Display for HashMapType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "HashMap[{}, {}]", self.key, self.value)
}
}
#[derive(Debug, Clone, PartialEq, Eq, TryHash, PartialOrd, Ord)]
pub struct VariantType {
pub name: String,
#[try_hash]
pub value: Option<Box<Type>>,
}
impl std::fmt::Display for VariantType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, ":{}", self.name)?;
if let Some(value) = &self.value {
write!(f, " {value}")?;
}
Ok(())
}
}
impl TypeShape {
pub fn expect_function(self) -> Result<FnType, ExpectError<TypeShape, &'static str>> {
match self {
Self::Function(ty) => Ok(*ty),
_ => Err(ExpectError {
value: self,
expected: "function",
}),
}
}
pub fn expect_tuple(self) -> Result<Tuple<Type>, ExpectError<TypeShape, &'static str>> {
match self {
Self::Tuple(tuple) => Ok(tuple),
_ => Err(ExpectError {
value: self,
expected: "tuple",
}),
}
}
pub fn expect_variant(self) -> Result<Vec<VariantType>, ExpectError<TypeShape, &'static str>> {
match self {
Self::Variant(variants) => Ok(variants),
_ => Err(ExpectError {
value: self,
expected: "variant",
}),
}
}
pub fn expect_template(self) -> Result<MaybeCompiledFn, ExpectError<TypeShape, &'static str>> {
match self {
Self::Template(t) => Ok(t),
_ => Err(ExpectError {
value: self,
expected: "template",
}),
}
}
}
impl Inferrable for TypeShape {
fn make_same(a: Self, b: Self) -> eyre::Result<Self> {
macro_rules! fail {
() => {
eyre::bail!("expected type {a}, got {b}")
};
}
Ok(match (a.clone(), b.clone()) {
(Self::UnwindHandle(a), Self::UnwindHandle(b)) => {
Self::UnwindHandle(Inferrable::make_same(a, b)?)
}
(Self::UnwindHandle(_), _) => fail!(),
(Self::Unit, Self::Unit) => Self::Unit,
(Self::Unit, _) => fail!(),
(Self::Contexts, Self::Contexts) => Self::Contexts,
(Self::Contexts, _) => fail!(),
(Self::Bool, Self::Bool) => Self::Bool,
(Self::Bool, _) => fail!(),
(Self::Int32, Self::Int32) => Self::Int32,
(Self::Int32, _) => fail!(),
(Self::Int64, Self::Int64) => Self::Int64,
(Self::Int64, _) => fail!(),
(Self::Float64, Self::Float64) => Self::Float64,
(Self::Float64, _) => fail!(),
(Self::Char, Self::Char) => Self::Char,
(Self::Char, _) => fail!(),
(Self::String, Self::String) => Self::String,
(Self::String, _) => fail!(),
(Self::List(a), Self::List(b)) => Self::List(Inferrable::make_same(a, b)?),
(Self::List(_), _) => fail!(),
(Self::Tuple(a), Self::Tuple(b)) => {
let mut result = Tuple::empty();
for (name, (a, b)) in a.zip(b)?.into_iter() {
let value = Inferrable::make_same(a, b)?;
result.add(name, value);
}
Self::Tuple(result)
}
(Self::Tuple(_), _) => fail!(),
(Self::Function(a), Self::Function(b)) => {
Self::Function(Box::new(Inferrable::make_same(*a, *b)?))
}
(Self::Function(_), _) => fail!(),
(Self::Template(a), Self::Template(b)) if a == b => Self::Template(a),
(Self::Template(_), _) => fail!(),
(Self::Macro(a), Self::Macro(b)) => {
Self::Macro(Box::new(Inferrable::make_same(*a, *b)?))
}
(Self::Macro(_), _) => fail!(),
(Self::Multiset, Self::Multiset) => Self::Multiset,
(Self::Multiset, _) => fail!(),
(Self::Ast, Self::Ast) => Self::Ast,
(Self::Ast, _) => fail!(),
(Self::Type, Self::Type) => Self::Type,
(Self::Type, _) => fail!(),
(Self::SyntaxModule, Self::SyntaxModule) => Self::SyntaxModule,
(Self::SyntaxModule, _) => fail!(),
(Self::SyntaxDefinition, Self::SyntaxDefinition) => Self::SyntaxDefinition,
(Self::SyntaxDefinition, _) => fail!(),
(Self::Binding(a), Self::Binding(b)) if a == b => Self::Binding(a),
(Self::Binding(_), _) => fail!(),
(Self::Variant(a), Self::Variant(b)) => {
if a == b {
Self::Variant(a)
} else {
fail!()
}
}
(Self::Variant(_), _) => fail!(),
(Self::Symbol, Self::Symbol) => Self::Symbol,
(Self::Symbol, _) => fail!(),
(Self::HashMap(a), Self::HashMap(b)) => Self::HashMap(HashMapType {
key: Inferrable::make_same(a.key, b.key)?,
value: Inferrable::make_same(a.value, b.value)?,
}),
(Self::HashMap(_), _) => fail!(),
(Self::Ref(a), Self::Ref(b)) => Self::Ref(Inferrable::make_same(a, b)?),
(Self::Ref(_), _) => fail!(),
})
}
}
impl std::fmt::Display for TypeShape {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::UnwindHandle(ty) => write!(f, "unwindable block handle {ty}"),
Self::Unit => write!(f, "unit"),
Self::Bool => write!(f, "bool"),
Self::Int32 => write!(f, "int32"),
Self::Int64 => write!(f, "int64"),
Self::Float64 => write!(f, "float64"),
Self::Char => write!(f, "char"),
Self::String => write!(f, "string"),
Self::List(ty) => write!(f, "list[{ty}]"),
Self::Tuple(tuple) => tuple.fmt(f),
Self::Function(ty) => ty.fmt(f),
Self::Template(_template) => write!(f, "template"),
Self::Macro(ty) => write!(f, "macro {ty}"),
Self::Multiset => write!(f, "multiset"),
Self::Ast => write!(f, "ast"),
Self::Type => write!(f, "type"),
Self::Contexts => write!(f, "contexts"),
Self::SyntaxModule => write!(f, "syntax module"),
Self::SyntaxDefinition => write!(f, "syntax definition"),
Self::Binding(binding) => binding.fmt(f),
Self::Variant(variants) => {
for (index, variant) in variants.iter().enumerate() {
if index != 0 {
write!(f, " ")?;
}
write!(f, "| {variant}")?;
}
Ok(())
}
Self::Symbol => write!(f, "symbol"),
Self::HashMap(map) => map.fmt(f),
Self::Ref(ty) => write!(f, "&{ty}"),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, TryHash, PartialOrd, Ord)]
pub struct FnType {
#[try_hash]
pub arg: Type,
#[try_hash]
pub contexts: Contexts,
#[try_hash]
pub result: Type,
}
impl std::fmt::Display for FnType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{} -> {}", self.arg, self.result)?;
if !self.contexts.is_empty() {
write!(f, " with {}", self.contexts)?;
}
Ok(())
}
}
impl Inferrable for FnType {
fn make_same(a: Self, b: Self) -> eyre::Result<Self> {
Ok(Self {
arg: Inferrable::make_same(a.arg, b.arg)?,
contexts: Inferrable::make_same(a.contexts, b.contexts)?,
result: Inferrable::make_same(a.result, b.result)?,
})
}
}
impl SubstituteBindings for HashMapType {
fn substitute_bindings(self, kast: &Kast, cache: &mut RecurseCache) -> Self {
Self {
key: self.key.substitute_bindings(kast, cache),
value: self.value.substitute_bindings(kast, cache),
}
}
}
impl SubstituteBindings for Type {
fn substitute_bindings(self, kast: &Kast, cache: &mut RecurseCache) -> Self {
let inferred = match self.inferred() {
Ok(inferred) => inferred,
Err(_) => {
return self;
}
};
if let Some(result) = cache.get(self.var()) {
return result;
}
cache.insert(self.var(), self.clone());
tracing::trace!("subbing {}", inferred.show_short());
let result = match inferred {
TypeShape::Unit
| TypeShape::Bool
| TypeShape::Int32
| TypeShape::Int64
| TypeShape::Float64
| TypeShape::Char
| TypeShape::String
| TypeShape::Ast
| TypeShape::Multiset
| TypeShape::Contexts
| TypeShape::Type
| TypeShape::Symbol
| TypeShape::SyntaxModule
| TypeShape::SyntaxDefinition => self.clone(),
TypeShape::List(a) => TypeShape::List(a.substitute_bindings(kast, cache)).into(),
TypeShape::Variant(variants) => TypeShape::Variant(
variants
.into_iter()
.map(|variant| VariantType {
name: variant.name.clone(),
value: variant
.value
.map(|ty| Box::new(ty.substitute_bindings(kast, cache))),
})
.collect(),
)
.into(),
TypeShape::UnwindHandle(ty) => {
TypeShape::UnwindHandle(ty.substitute_bindings(kast, cache)).into()
}
TypeShape::Tuple(tuple) => {
TypeShape::Tuple(tuple.map(|ty| ty.substitute_bindings(kast, cache))).into()
}
TypeShape::Function(f) => {
TypeShape::Function(Box::new((*f).substitute_bindings(kast, cache))).into()
}
TypeShape::Template(t) => TypeShape::Template(t).into(),
TypeShape::Macro(f) => {
TypeShape::Macro(Box::new((*f).substitute_bindings(kast, cache))).into()
}
TypeShape::Binding(binding) => match kast.scopes.interpreter.get(&binding.symbol) {
Some(value) => value
.clone_value()
.unwrap()
.inferred()
.unwrap()
.expect_type()
.unwrap_or_else(|e| {
panic!("{} expected to be a type: {e}", binding.symbol.name())
}),
None => TypeShape::Binding(binding.clone()).into(),
},
TypeShape::HashMap(map) => {
TypeShape::HashMap(map.substitute_bindings(kast, cache)).into()
}
TypeShape::Ref(ty) => TypeShape::Ref(ty.substitute_bindings(kast, cache)).into(),
};
cache.insert(self.var(), result.clone());
tracing::trace!("subbed as {result}");
result
}
}
impl SubstituteBindings for FnType {
fn substitute_bindings(self, kast: &Kast, cache: &mut RecurseCache) -> Self {
Self {
arg: self.arg.substitute_bindings(kast, cache),
contexts: self.contexts.substitute_bindings(kast, cache),
result: self.result.substitute_bindings(kast, cache),
}
}
}