use std::cmp::Ordering;
use std::rc::Rc;
use super::body::Pending;
use super::env::Env;
use crate::check::model::UnitId;
use crate::check::types::{GlobalId, StructId, SumId, SymbolId, Types};
use crate::check::{FileId, ImportRoot};
use crate::span::Span;
use crate::value::Value;
#[derive(Debug, Clone)]
pub(crate) enum Val {
None,
Bool(bool),
Int(i64),
Float(f64),
Str(Rc<str>),
Path(Rc<PathVal>),
Symbol(Rc<SymbolVal>),
Ref(Rc<RefVal>),
List(Rc<Seq>),
Set(Rc<Seq>),
Map(Rc<MapVal>),
Struct(Rc<StructVal>),
Tag(SumId, u32),
Variant(Rc<VariantVal>),
Fn(Rc<FnVal>),
Part(Rc<PartVal>),
}
#[derive(Debug)]
pub(crate) struct PathVal {
pub(crate) root: usize,
pub(crate) rel: String,
}
#[derive(Debug)]
pub(crate) struct SymbolVal {
pub(crate) ty: SymbolId,
pub(crate) text: String,
pub(crate) file: FileId,
pub(crate) span: Span,
}
#[derive(Debug)]
pub(crate) struct RefVal {
pub(crate) target: StructId,
pub(crate) label: String,
pub(crate) file: FileId,
pub(crate) span: Span,
}
#[derive(Debug)]
pub(crate) struct Seq {
pub(crate) items: Vec<Val>,
pub(crate) depth: u32,
}
#[derive(Debug)]
pub(crate) struct MapVal {
pub(crate) entries: Vec<(Val, Val)>,
pub(crate) depth: u32,
}
#[derive(Debug)]
pub(crate) struct StructVal {
pub(crate) id: StructId,
pub(crate) fields: Vec<Val>,
pub(crate) set: Vec<bool>,
pub(crate) depth: u32,
}
#[derive(Debug)]
pub(crate) struct VariantVal {
pub(crate) sum: SumId,
pub(crate) index: u32,
pub(crate) fields: Vec<Val>,
pub(crate) set: Vec<bool>,
pub(crate) depth: u32,
}
#[derive(Debug)]
pub(crate) enum FnVal {
Global(GlobalId),
Lambda {
unit: UnitId,
node: usize,
env: Env,
},
}
#[derive(Debug)]
pub(crate) struct PartVal {
pub(crate) contributions: Vec<Pending>,
}
pub(crate) fn depth_of<'a>(items: impl IntoIterator<Item = &'a Val>) -> u32 {
items
.into_iter()
.map(Val::depth)
.max()
.unwrap_or(0)
.saturating_add(1)
}
#[derive(Debug, Clone)]
pub(crate) struct Key(pub(crate) Val);
impl PartialEq for Key {
fn eq(&self, other: &Self) -> bool {
self.cmp(other) == Ordering::Equal
}
}
impl Eq for Key {}
impl PartialOrd for Key {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Key {
fn cmp(&self, other: &Self) -> Ordering {
self.0.order(&other.0)
}
}
impl Val {
pub(crate) fn depth(&self) -> u32 {
match self {
Val::List(s) | Val::Set(s) => s.depth,
Val::Map(m) => m.depth,
Val::Struct(s) => s.depth,
Val::Variant(v) => v.depth,
_ => 0,
}
}
pub(crate) fn is_none(&self) -> bool {
matches!(self, Val::None)
}
pub(crate) fn equals(&self, other: &Val) -> bool {
match (self, other) {
(Val::None, Val::None) => true,
(Val::Bool(a), Val::Bool(b)) => a == b,
(Val::Int(a), Val::Int(b)) => a == b,
#[allow(clippy::float_cmp, reason = "equality of values is exact")]
(Val::Float(a), Val::Float(b)) => a == b,
(Val::Str(a), Val::Str(b)) => a == b,
(Val::Path(a), Val::Path(b)) => a.root == b.root && a.rel == b.rel,
(Val::Symbol(a), Val::Symbol(b)) => a.ty == b.ty && a.text == b.text,
(Val::Ref(a), Val::Ref(b)) => a.target == b.target && a.label == b.label,
(Val::List(a), Val::List(b)) | (Val::Set(a), Val::Set(b)) => {
all_equal(&a.items, &b.items)
}
(Val::Map(a), Val::Map(b)) => {
a.entries.len() == b.entries.len()
&& a.entries
.iter()
.zip(&b.entries)
.all(|((k1, v1), (k2, v2))| k1.equals(k2) && v1.equals(v2))
}
(Val::Struct(a), Val::Struct(b)) => a.id == b.id && all_equal(&a.fields, &b.fields),
(Val::Tag(s1, i1), Val::Tag(s2, i2)) => s1 == s2 && i1 == i2,
(Val::Variant(a), Val::Variant(b)) => {
a.sum == b.sum && a.index == b.index && all_equal(&a.fields, &b.fields)
}
_ => false,
}
}
fn rank(&self) -> u8 {
match self {
Val::None => 0,
Val::Bool(_) => 1,
Val::Int(_) => 2,
Val::Float(_) => 3,
Val::Str(_) => 4,
Val::Path(_) => 5,
Val::Symbol(_) => 6,
Val::Ref(_) => 7,
Val::Tag(..) => 8,
Val::Variant(_) => 9,
Val::List(_) => 10,
Val::Set(_) => 11,
Val::Map(_) => 12,
Val::Struct(_) => 13,
Val::Fn(_) => 14,
Val::Part(_) => 15,
}
}
pub(crate) fn order(&self, other: &Val) -> Ordering {
match (self, other) {
(Val::Bool(a), Val::Bool(b)) => a.cmp(b),
(Val::Int(a), Val::Int(b)) => a.cmp(b),
(Val::Float(a), Val::Float(b)) => a.partial_cmp(b).unwrap_or(Ordering::Equal),
(Val::Str(a), Val::Str(b)) => a.as_bytes().cmp(b.as_bytes()),
(Val::Path(a), Val::Path(b)) => (a.root, &a.rel).cmp(&(b.root, &b.rel)),
(Val::Symbol(a), Val::Symbol(b)) => (a.ty, &a.text).cmp(&(b.ty, &b.text)),
(Val::Ref(a), Val::Ref(b)) => (a.target, &a.label).cmp(&(b.target, &b.label)),
(Val::Tag(s1, i1), Val::Tag(s2, i2)) => (s1, i1).cmp(&(s2, i2)),
(Val::Variant(a), Val::Variant(b)) => (a.sum, a.index)
.cmp(&(b.sum, b.index))
.then_with(|| order_all(&a.fields, &b.fields)),
(Val::List(a), Val::List(b)) | (Val::Set(a), Val::Set(b)) => {
order_all(&a.items, &b.items)
}
(Val::Map(a), Val::Map(b)) => {
for ((k1, v1), (k2, v2)) in a.entries.iter().zip(&b.entries) {
let found = k1.order(k2).then_with(|| v1.order(v2));
if found != Ordering::Equal {
return found;
}
}
a.entries.len().cmp(&b.entries.len())
}
(Val::Struct(a), Val::Struct(b)) => {
a.id.cmp(&b.id)
.then_with(|| order_all(&a.fields, &b.fields))
}
_ => self.rank().cmp(&other.rank()),
}
}
pub(crate) fn text(&self, types: &Types) -> String {
match self {
Val::Str(s) => s.to_string(),
Val::Int(i) => i.to_string(),
Val::Float(f) => format!("{f:?}"),
Val::Bool(b) => b.to_string(),
Val::Tag(sum, index) => types
.sum(*sum)
.variants
.get(*index as usize)
.map_or_else(String::new, |v| v.name.clone()),
Val::Symbol(s) => s.text.clone(),
Val::Ref(r) => r.label.clone(),
_ => String::new(),
}
}
pub(crate) fn items(&self) -> &[Val] {
match self {
Val::List(s) | Val::Set(s) => &s.items,
_ => &[],
}
}
pub(crate) fn to_value(&self, types: &Types, roots: &[ImportRoot]) -> Value {
let fields = |defs: Vec<&str>, values: &[Val]| -> Vec<(String, Value)> {
defs.into_iter()
.zip(values)
.map(|(name, v)| (name.to_owned(), v.to_value(types, roots)))
.collect()
};
match self {
Val::None | Val::Fn(_) | Val::Part(_) => Value::None,
Val::Bool(b) => Value::Bool(*b),
Val::Int(i) => Value::Int(*i),
Val::Float(f) => Value::Float(*f),
Val::Str(s) => Value::Str(s.to_string()),
Val::Path(p) => {
let root = roots
.get(p.root)
.map(|r| r.path().to_path_buf())
.unwrap_or_default();
Value::Path(if p.rel.is_empty() {
root
} else {
root.join(&p.rel)
})
}
Val::Symbol(s) => Value::Symbol {
ty: types
.symbols
.get(s.ty.index())
.map_or_else(String::new, |d| d.name.clone()),
text: s.text.clone(),
},
Val::Ref(r) => Value::Ref(r.label.clone()),
Val::List(s) => Value::List(s.items.iter().map(|v| v.to_value(types, roots)).collect()),
Val::Set(s) => Value::Set(s.items.iter().map(|v| v.to_value(types, roots)).collect()),
Val::Map(m) => Value::Map(
m.entries
.iter()
.map(|(k, v)| (k.text(types), v.to_value(types, roots)))
.collect(),
),
Val::Struct(s) => {
let def = types.structure(s.id);
Value::Struct {
name: def.name.clone(),
fields: fields(
def.fields.iter().map(|f| f.name.as_str()).collect(),
&s.fields,
),
}
}
Val::Tag(sum, _) => {
let def = types.sum(*sum);
Value::Variant {
ty: def.name.clone(),
name: self.text(types),
fields: None,
}
}
Val::Variant(v) => {
let def = types.sum(v.sum);
let variant = def.variants.get(v.index as usize);
let names = variant
.and_then(|v| v.fields.as_ref())
.map(|f| f.iter().map(|p| p.name.as_str()).collect())
.unwrap_or_default();
Value::Variant {
ty: def.name.clone(),
name: variant.map_or_else(String::new, |v| v.name.clone()),
fields: Some(fields(names, &v.fields)),
}
}
}
}
}
fn all_equal(a: &[Val], b: &[Val]) -> bool {
a.len() == b.len() && a.iter().zip(b).all(|(x, y)| x.equals(y))
}
fn order_all(a: &[Val], b: &[Val]) -> Ordering {
for (x, y) in a.iter().zip(b) {
let found = x.order(y);
if found != Ordering::Equal {
return found;
}
}
a.len().cmp(&b.len())
}
pub(crate) fn canonical(mut items: Vec<Val>) -> Vec<Val> {
items.sort_by(Val::order);
items.dedup_by(|a, b| a.equals(b));
items
}