use std::fmt;
use std::i32;
use std::borrow::Cow;
use std::collections::BTreeMap;
use kailua_syntax::Str;
use diag::{Origin, TypeReport, TypeResult};
use super::{Display, DisplayState, T, Ty, Slot, TypeContext, Union, Lattice, RVar};
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Key {
Int(i32),
Str(Str),
}
impl Key {
pub fn is_int(&self) -> bool { if let Key::Int(_) = *self { true } else { false } }
pub fn is_str(&self) -> bool { if let Key::Str(_) = *self { true } else { false } }
pub fn to_type<'a>(&'a self) -> T<'a> {
match *self {
Key::Int(v) => T::Int(v),
Key::Str(ref s) => T::Str(Cow::Borrowed(s)),
}
}
}
impl From<i32> for Key { fn from(v: i32) -> Key { Key::Int(v) } }
impl From<Str> for Key { fn from(s: Str) -> Key { Key::Str(s) } }
impl<'a> From<&'a Str> for Key { fn from(s: &Str) -> Key { Key::Str(s.to_owned()) } }
impl PartialEq<Str> for Key {
fn eq(&self, other: &Str) -> bool {
if let Key::Str(ref s) = *self { *s == *other } else { false }
}
}
impl<'a> PartialEq<&'a [u8]> for Key {
fn eq(&self, other: &&'a [u8]) -> bool {
if let Key::Str(ref s) = *self { **s == **other } else { false }
}
}
impl PartialEq<i32> for Key {
fn eq(&self, other: &i32) -> bool {
if let Key::Int(ref v) = *self { *v == *other } else { false }
}
}
impl fmt::Display for Key {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Key::Int(ref v) => write!(f, "{}", v),
Key::Str(ref s) if s.quote_required() => write!(f, "`{:-}`", s),
Key::Str(ref s) => write!(f, "{:-}", s),
}
}
}
impl fmt::Debug for Key {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Key::Int(ref v) => fmt::Debug::fmt(v, f),
Key::Str(ref s) => fmt::Debug::fmt(s, f),
}
}
}
#[derive(Clone)]
pub enum Tables {
Fields(RVar),
Array(Slot),
ArrayN(Slot),
Map(Ty, Slot),
All,
}
fn nslot(v: &Slot) -> Slot {
Slot::new(v.flex(), Ty::new(T::Integer))
}
impl Tables {
pub fn generalize(self, ctx: &mut TypeContext) -> Tables {
match self {
Tables::Fields(r) => Tables::Fields(ctx.copy_rvar(r)),
Tables::Array(v) => Tables::Array(v.generalize(ctx)),
Tables::ArrayN(v) => Tables::ArrayN(v.generalize(ctx)),
Tables::Map(k, v) => {
let k = k.generalize(ctx);
let v = v.generalize(ctx);
Tables::Map(k, v)
},
Tables::All => Tables::All,
}
}
fn fmt_generic<WriteTy, WriteSlot>(&self, f: &mut fmt::Formatter,
st: Option<&DisplayState>,
mut write_ty: WriteTy,
mut write_slot: WriteSlot,
expose_rvar: bool) -> fmt::Result
where WriteTy: FnMut(&Ty, &mut fmt::Formatter) -> fmt::Result,
WriteSlot: FnMut(&Slot, &mut fmt::Formatter, bool) -> fmt::Result {
match *self {
Tables::All => write!(f, "table"),
Tables::Fields(ref rvar) => {
if let Some(st) = st {
if !st.can_recurse() {
return match &st.locale[..] {
"ko" => write!(f, "<생략>"),
_ => write!(f, "<omitted>"),
};
}
if st.is_rvar_seen(rvar.clone()) {
return write!(f, "<...>");
}
}
let ret = (|| {
let mut fields = BTreeMap::new();
let mut morefields = 0;
const MAX_FIELDS: usize = 0x100;
let rvar = if let Some(st) = st {
st.context.list_rvar_fields(rvar.clone(), &mut |k, v| {
if fields.len() < MAX_FIELDS {
fields.insert(k.clone(), v.clone());
} else {
morefields += 1;
}
Ok(())
}).expect("list_rvar_fields exited early while we haven't break")
} else {
rvar.clone()
};
write!(f, "{{")?;
let mut first = true;
let mut nextlen = 1;
while let Some(t) = fields.get(&Key::Int(nextlen)) {
if first { first = false; } else { write!(f, ", ")?; }
write_slot(t, f, false)?;
nextlen += 1;
}
for (name, t) in fields.iter() {
match *name {
Key::Int(v) if 1 <= v && v < nextlen => continue, _ => {}
}
if first { first = false; } else { write!(f, ", ")?; }
write!(f, "{}: ", name)?;
write_slot(t, f, false)?;
}
if morefields > 0 {
if first { first = false; } else { write!(f, ", ")?; }
write!(f, "<{} fields omitted>", morefields)?;
}
if rvar != RVar::empty() {
if !first { write!(f, ", ")?; }
write!(f, "...")?;
if expose_rvar {
if rvar == RVar::any() {
write!(f, "?")?;
} else {
write!(f, "{}", rvar.to_usize())?;
}
}
}
write!(f, "}}")?;
Ok(())
})();
if let Some(st) = st {
st.unmark_rvar(rvar.clone());
}
ret
}
Tables::Array(ref t) => {
write!(f, "vector<")?;
write_slot(t, f, true)?;
write!(f, ">")?;
Ok(())
}
Tables::ArrayN(ref t) => {
write!(f, "vector<")?;
write_slot(t, f, true)?;
write!(f, "> & {{n: ")?;
write_slot(&nslot(t), f, true)?;
write!(f, "}}")?;
Ok(())
}
Tables::Map(ref k, ref v) => {
write!(f, "map<")?;
write_ty(k, f)?;
write!(f, ", ")?;
write_slot(v, f, true)?;
write!(f, ">")?;
Ok(())
}
}
}
}
impl Union for Tables {
type Output = Tables;
fn union(&self, other: &Tables, explicit: bool, ctx: &mut TypeContext) -> TypeResult<Tables> {
(|| {
match (self, other) {
(_, &Tables::All) | (&Tables::All, _) => Ok(Tables::All),
(&Tables::Array(ref a), &Tables::Array(ref b)) => {
a.assert_eq(b, ctx)?;
Ok(Tables::Array(a.clone()))
},
(&Tables::Array(ref a), &Tables::ArrayN(ref b)) |
(&Tables::ArrayN(ref a), &Tables::Array(ref b)) |
(&Tables::ArrayN(ref a), &Tables::ArrayN(ref b)) => {
a.assert_eq(b, ctx)?;
Ok(Tables::ArrayN(a.clone()))
},
(&Tables::Map(ref ak, ref av), &Tables::Map(ref bk, ref bv)) => {
ak.assert_eq(bk, ctx)?;
av.assert_eq(bv, ctx)?;
Ok(Tables::Map(ak.clone(), av.clone()))
},
(&Tables::Fields(ref ar), &Tables::Fields(ref br)) => {
ar.assert_eq(&br, ctx)?;
Ok(Tables::Fields(ar.clone()))
},
(lhs @ &Tables::Fields(_), rhs) => {
lhs.assert_sub(rhs, ctx)?;
Ok(rhs.clone())
},
(lhs, rhs @ &Tables::Fields(_)) => {
rhs.assert_sub(lhs, ctx)?;
Ok(lhs.clone())
},
(_, _) => Err(ctx.gen_report()),
}
})().map_err(|r: TypeReport| r.cannot_union(Origin::Tables, self, other, explicit, ctx))
}
}
impl Lattice for Tables {
fn assert_sub(&self, other: &Self, ctx: &mut TypeContext) -> TypeResult<()> {
(|| {
let ok = match (self, other) {
(_, &Tables::All) => true,
(&Tables::All, _) => false,
(&Tables::Fields(ref ar), &Tables::Fields(ref br)) => {
return ar.assert_sub(&br, ctx);
},
(&Tables::Fields(ref rvar), &Tables::Map(ref key, ref value)) => {
for (k, v) in ctx.get_rvar_fields(rvar.clone()) {
k.to_type().assert_sub(&**key, ctx)?;
v.assert_sub(&value.clone().with_nil(), ctx)?;
}
return ctx.assert_rvar_closed(rvar.clone());
},
(&Tables::Fields(ref rvar), &Tables::Array(ref value)) |
(&Tables::Fields(ref rvar), &Tables::ArrayN(ref value)) => {
let has_n = if let Tables::ArrayN(_) = *other { true } else { false };
let mut min = i32::MAX;
let mut max = i32::MIN;
let mut count = 0;
for (k, v) in ctx.get_rvar_fields(rvar.clone()) {
match k {
Key::Int(k) => {
count += 1;
if min > k { min = k; }
if max < k { max = k; }
v.assert_sub(&value.clone().with_nil(), ctx)?;
}
Key::Str(ref s) if has_n && &s[..] == &b"n"[..] => {
v.assert_sub(&nslot(value), ctx)?;
}
Key::Str(_) => {
k.to_type().assert_sub(&T::Integer, ctx)?;
panic!("non-integral {:?} is typed as integral {:?}",
k, k.to_type());
}
}
}
ctx.assert_rvar_closed(rvar.clone())?;
count == 0 || (min == 1 && max == count)
},
(_, &Tables::Fields(..)) => false,
(&Tables::Array(ref value1), &Tables::Array(ref value2)) |
(&Tables::Array(ref value1), &Tables::ArrayN(ref value2)) |
(&Tables::ArrayN(ref value1), &Tables::ArrayN(ref value2)) => {
value1.assert_sub(value2, ctx)?;
true
},
(&Tables::ArrayN(..), &Tables::Array(..)) => false,
(&Tables::Map(ref key1, ref value1), &Tables::Map(ref key2, ref value2)) => {
key1.assert_sub(key2, ctx)?;
value1.assert_sub(value2, ctx)?;
true
},
(&Tables::Array(ref value1), &Tables::Map(ref key2, ref value2)) => {
T::Integer.assert_sub(&**key2, ctx)?;
value1.assert_sub(value2, ctx)?;
true
},
(&Tables::ArrayN(ref value1), &Tables::Map(ref key2, ref value2)) => {
let akey = T::Integer | T::Str(Cow::Owned(Str::from(b"n"[..].to_owned())));
let avalue = value1.union(&nslot(value1), false, ctx)?;
akey.assert_sub(&**key2, ctx)?;
avalue.assert_sub(value2, ctx)?;
true
},
(&Tables::Map(..), &Tables::Array(..)) => false,
(&Tables::Map(..), &Tables::ArrayN(..)) => false,
};
if ok { Ok(()) } else { Err(ctx.gen_report()) }
})().map_err(|r: TypeReport| r.not_sub(Origin::Tables, self, other, ctx))
}
fn assert_eq(&self, other: &Self, ctx: &mut TypeContext) -> TypeResult<()> {
(|| {
let ok = match (self, other) {
(&Tables::All, &Tables::All) => true,
(&Tables::Array(ref a), &Tables::Array(ref b)) => return a.assert_eq(b, ctx),
(&Tables::ArrayN(ref a), &Tables::ArrayN(ref b)) => return a.assert_eq(b, ctx),
(&Tables::Map(ref ak, ref av), &Tables::Map(ref bk, ref bv)) => {
ak.assert_eq(bk, ctx)?;
av.assert_eq(bv, ctx)?;
true
}
(&Tables::Fields(ref ar), &Tables::Fields(ref br)) => return ar.assert_eq(&br, ctx),
(_, _) => false,
};
if ok { Ok(()) } else { Err(ctx.gen_report()) }
})().map_err(|r: TypeReport| r.not_eq(Origin::Tables, self, other, ctx))
}
}
impl PartialEq for Tables {
fn eq(&self, other: &Tables) -> bool {
match (self, other) {
(&Tables::All, &Tables::All) => true,
(&Tables::Array(ref a), &Tables::Array(ref b)) => *a == *b,
(&Tables::ArrayN(ref a), &Tables::ArrayN(ref b)) => *a == *b,
(&Tables::Map(ref ak, ref av), &Tables::Map(ref bk, ref bv)) =>
*ak == *bk && *av == *bv,
(&Tables::Fields(ref ar), &Tables::Fields(ref br)) => *ar == *br,
(_, _) => false,
}
}
}
impl Display for Tables {
fn fmt_displayed(&self, f: &mut fmt::Formatter, st: &DisplayState) -> fmt::Result {
self.fmt_generic(
f, Some(st),
|t, f| fmt::Display::fmt(&t.display(st), f),
|s, f, without_nil| {
let s = s.display(st);
if without_nil { write!(f, "{:#}", s) } else { write!(f, "{}", s) }
},
false
)
}
}
impl fmt::Debug for Tables {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.fmt_generic(
f, None,
|t, f| fmt::Debug::fmt(t, f),
|s, f, _| fmt::Debug::fmt(s, f),
true
)
}
}