From 8b9a69b89886f79958a4074b6ac49670e3c92959 Mon Sep 17 00:00:00 2001 From: John Doty Date: Wed, 7 Feb 2024 07:45:30 -0800 Subject: [PATCH] [fine] Increasingly elaborate alternate types --- fine/src/semantics.rs | 135 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 130 insertions(+), 5 deletions(-) diff --git a/fine/src/semantics.rs b/fine/src/semantics.rs index df9dba94..71f0001d 100644 --- a/fine/src/semantics.rs +++ b/fine/src/semantics.rs @@ -148,7 +148,7 @@ pub enum Type { Object(TreeRef, Rc), // An alternate is one or another type. - Alternate(Box, Box), + Alternate(Box<[Type]>), } impl Type { @@ -158,6 +158,26 @@ impl Type { _ => false, } } + + fn discriminant_number(&self) -> i8 { + match self { + Type::Error => 0, + Type::Unreachable => 1, + Type::Assignment(_) => 2, + Type::TypeVariable(_) => 3, + Type::Nothing => 4, + Type::F64 => 5, + Type::I64 => 6, + Type::String => 7, + Type::Bool => 8, + Type::Function(_, _) => 9, + Type::Method(_, _, _) => 10, + Type::List(_) => 11, + Type::Class(_, _) => 12, + Type::Object(_, _) => 13, + Type::Alternate(_) => 14, + } + } } impl fmt::Debug for Type { @@ -211,7 +231,70 @@ impl fmt::Display for Type { List(t) => write!(f, "list<{t}>"), Object(_, name) => write!(f, "{} instance", name), Class(_, name) => write!(f, "class {}", name), - Alternate(l, r) => write!(f, "{l} or {r}"), + Alternate(ts) => { + let mut first = true; + for t in ts.iter() { + if !first { + write!(f, " or ")?; + } + write!(f, "{t}")?; + first = false; + } + Ok(()) + } + } + } +} + +impl std::cmp::PartialEq for Type { + fn eq(&self, other: &Self) -> bool { + self.cmp(other).is_eq() + } +} + +impl std::cmp::Eq for Type {} + +impl std::cmp::PartialOrd for Type { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl std::cmp::Ord for Type { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + use std::cmp::Ordering; + + let li = self.discriminant_number(); + let ri = other.discriminant_number(); + if li < ri { + Ordering::Less + } else if li > ri { + Ordering::Greater + } else { + match (self, other) { + (Type::Assignment(x), Type::Assignment(y)) => x.cmp(y), + + // NOTE: This is wrong! Type variables *cannot* be compared + // like this without binding information. + (Type::TypeVariable(x), Type::TypeVariable(y)) => x.index().cmp(&y.index()), + + (Type::Function(la, lr), Type::Function(ra, rr)) => { + let lv = (la, lr); + let rv = (ra, rr); + lv.cmp(&rv) + } + (Type::Method(lo, la, lr), Type::Method(ro, ra, rr)) => { + let lv = (lo, la, lr); + let rv = (ro, ra, rr); + lv.cmp(&rv) + } + (Type::List(x), Type::List(y)) => x.cmp(y), + (Type::Class(lt, _), Type::Class(rt, _)) => lt.index().cmp(&rt.index()), + (Type::Object(lt, _), Type::Object(rt, _)) => lt.index().cmp(&rt.index()), + (Type::Alternate(ll), Type::Alternate(rr)) => ll.cmp(rr), + + _ => Ordering::Equal, + } } } } @@ -1070,6 +1153,34 @@ impl<'a> Semantics<'a> { result } + pub fn build_alternate(&self, left: &Type, right: &Type) -> Type { + if left.is_error() { + left.clone() + } else if right.is_error() { + right.clone() + } else if self.can_convert(left, right) { + right.clone() + } else if self.can_convert(right, left) { + left.clone() + } else { + let mut types: Vec = Vec::new(); + if let Type::Alternate(ts) = left { + types.extend_from_slice(&*ts); + } else { + types.push(left.clone()); + } + if let Type::Alternate(ts) = right { + types.extend_from_slice(&*ts); + } else { + types.push(right.clone()); + } + types.sort(); + types.dedup(); + + Type::Alternate(types.into()) + } + } + pub fn can_convert(&self, from: &Type, to: &Type) -> bool { // TODO: This is wrong; we because of numeric literals etc. match (from, to) { @@ -1105,8 +1216,22 @@ impl<'a> Semantics<'a> { (Type::Unreachable, _) => true, // Alternates convert if either side can convert. - (_, Type::Alternate(left, right)) => { - self.can_convert(from, left) || self.can_convert(from, right) + (Type::Alternate(lts), Type::Alternate(_)) => { + for lt in lts.iter() { + if !self.can_convert(lt, to) { + return false; + } + } + true + } + + (_, Type::Alternate(rts)) => { + for rt in rts.iter() { + if self.can_convert(from, rt) { + return true; + } + } + false } // TODO: Unification on type variables! :D @@ -1948,7 +2073,7 @@ impl<'a> Semantics<'a> { // TODO: IDEA: nth_tree returns a bogus tree if not a tree, stop returning Option? let left = self.type_of(tree.nth_tree(0)?); let right = self.type_of(tree.nth_tree(2)?); - Some(Type::Alternate(Box::new(left), Box::new(right))) + Some(self.build_alternate(&left, &right)) } fn type_of_match_expression(&self, tree: &Tree) -> Option {