λ Support functions! #5
@@ -15,6 +15,7 @@
|
|||||||
mod arbitrary;
|
mod arbitrary;
|
||||||
pub mod ast;
|
pub mod ast;
|
||||||
mod eval;
|
mod eval;
|
||||||
|
mod fields;
|
||||||
mod pretty;
|
mod pretty;
|
||||||
mod strings;
|
mod strings;
|
||||||
mod top_level;
|
mod top_level;
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
use crate::eval::PrimitiveType;
|
use crate::eval::PrimitiveType;
|
||||||
|
pub use crate::ir::fields::Fields;
|
||||||
use crate::syntax::{ConstantType, Location};
|
use crate::syntax::{ConstantType, Location};
|
||||||
use internment::ArcIntern;
|
use internment::ArcIntern;
|
||||||
use proptest::arbitrary::Arbitrary;
|
use proptest::arbitrary::Arbitrary;
|
||||||
@@ -247,7 +248,7 @@ impl Value {
|
|||||||
pub enum Type {
|
pub enum Type {
|
||||||
Primitive(PrimitiveType),
|
Primitive(PrimitiveType),
|
||||||
Function(Vec<Type>, Box<Type>),
|
Function(Vec<Type>, Box<Type>),
|
||||||
Structure(HashMap<ArcIntern<String>, Type>),
|
Structure(Fields<Type>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Type {
|
impl Type {
|
||||||
@@ -281,7 +282,7 @@ pub enum TypeOrVar {
|
|||||||
Primitive(PrimitiveType),
|
Primitive(PrimitiveType),
|
||||||
Variable(Location, ArcIntern<String>),
|
Variable(Location, ArcIntern<String>),
|
||||||
Function(Vec<TypeOrVar>, Box<TypeOrVar>),
|
Function(Vec<TypeOrVar>, Box<TypeOrVar>),
|
||||||
Structure(HashMap<ArcIntern<String>, TypeOrVar>),
|
Structure(Fields<TypeOrVar>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for TypeOrVar {
|
impl Default for TypeOrVar {
|
||||||
@@ -330,7 +331,7 @@ impl TypeOrVar {
|
|||||||
TypeOrVar::Primitive(_) => false,
|
TypeOrVar::Primitive(_) => false,
|
||||||
|
|
||||||
TypeOrVar::Structure(fields) => {
|
TypeOrVar::Structure(fields) => {
|
||||||
fields.values_mut().any(|x| x.replace(name, replace_with))
|
fields.types_mut().any(|x| x.replace(name, replace_with))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -344,7 +345,7 @@ impl TypeOrVar {
|
|||||||
TypeOrVar::Function(args, ret) => {
|
TypeOrVar::Function(args, ret) => {
|
||||||
args.iter().all(TypeOrVar::is_resolved) && ret.is_resolved()
|
args.iter().all(TypeOrVar::is_resolved) && ret.is_resolved()
|
||||||
}
|
}
|
||||||
TypeOrVar::Structure(fields) => fields.values().all(TypeOrVar::is_resolved),
|
TypeOrVar::Structure(fields) => fields.types().all(TypeOrVar::is_resolved),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -364,7 +365,7 @@ impl PartialEq<Type> for TypeOrVar {
|
|||||||
|
|
||||||
Type::Structure(fields1) => match self {
|
Type::Structure(fields1) => match self {
|
||||||
TypeOrVar::Structure(fields2) => {
|
TypeOrVar::Structure(fields2) => {
|
||||||
fields1.len() == fields2.len()
|
fields1.count() == fields2.count()
|
||||||
&& fields1.iter().all(|(name, subtype)| {
|
&& fields1.iter().all(|(name, subtype)| {
|
||||||
fields2.get(name).map(|x| x == subtype).unwrap_or(false)
|
fields2.get(name).map(|x| x == subtype).unwrap_or(false)
|
||||||
})
|
})
|
||||||
@@ -418,9 +419,7 @@ impl<T: Into<Type>> From<T> for TypeOrVar {
|
|||||||
args.into_iter().map(Into::into).collect(),
|
args.into_iter().map(Into::into).collect(),
|
||||||
Box::new((*ret).into()),
|
Box::new((*ret).into()),
|
||||||
),
|
),
|
||||||
Type::Structure(fields) => {
|
Type::Structure(fields) => TypeOrVar::Structure(fields.map(Into::into)),
|
||||||
TypeOrVar::Structure(fields.into_iter().map(|(n, t)| (n, t.into())).collect())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -450,16 +449,21 @@ impl TryFrom<TypeOrVar> for Type {
|
|||||||
TypeOrVar::Primitive(t) => Ok(Type::Primitive(t)),
|
TypeOrVar::Primitive(t) => Ok(Type::Primitive(t)),
|
||||||
|
|
||||||
TypeOrVar::Structure(fields) => {
|
TypeOrVar::Structure(fields) => {
|
||||||
let mut new_fields = HashMap::with_capacity(fields.len());
|
let mut new_fields = Fields::new(fields.ordering());
|
||||||
|
let mut errored = false;
|
||||||
|
|
||||||
for (name, field) in fields.iter() {
|
for (name, field) in fields.iter() {
|
||||||
if let Ok(new_field) = field.clone().try_into() {
|
if let Ok(new_field) = field.clone().try_into() {
|
||||||
new_fields.insert(name.clone(), new_field);
|
new_fields.insert(name.clone(), new_field);
|
||||||
} else {
|
} else {
|
||||||
return Err(TypeOrVar::Structure(fields));
|
errored = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if errored {
|
||||||
|
return Err(TypeOrVar::Structure(fields));
|
||||||
|
}
|
||||||
|
|
||||||
Ok(Type::Structure(new_fields))
|
Ok(Type::Structure(new_fields))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
101
src/ir/fields.rs
Normal file
101
src/ir/fields.rs
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
use internment::ArcIntern;
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
#[derive(Clone, PartialEq, Eq)]
|
||||||
|
pub struct Fields<T> {
|
||||||
|
ordering: FieldOrdering,
|
||||||
|
fields: Vec<(ArcIntern<String>, T)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: fmt::Debug> fmt::Debug for Fields<T> {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "Fields:")?;
|
||||||
|
self.fields.fmt(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
|
pub enum FieldOrdering {
|
||||||
|
Standard,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Default for Fields<T> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new(FieldOrdering::Standard)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Fields<T> {
|
||||||
|
pub fn new(ordering: FieldOrdering) -> Fields<T> {
|
||||||
|
Fields {
|
||||||
|
ordering,
|
||||||
|
fields: vec![],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ordering(&self) -> FieldOrdering {
|
||||||
|
self.ordering
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn insert(&mut self, name: ArcIntern<String>, t: T) {
|
||||||
|
self.fields.push((name, t));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get(&self, name: &ArcIntern<String>) -> Option<&T> {
|
||||||
|
for (n, res) in self.fields.iter() {
|
||||||
|
if n == name {
|
||||||
|
return Some(res);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn map<T2, F: Fn(T) -> T2>(self, f: F) -> Fields<T2> {
|
||||||
|
Fields {
|
||||||
|
ordering: self.ordering,
|
||||||
|
fields: self.fields.into_iter().map(|(n, t)| (n, f(t))).collect(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn count(&self) -> usize {
|
||||||
|
self.fields.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn has_field(&self, name: &ArcIntern<String>) -> bool {
|
||||||
|
self.fields.iter().any(|(current, _)| current == name)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove_field(&mut self, name: &ArcIntern<String>) -> Option<T> {
|
||||||
|
let mut field_index = None;
|
||||||
|
|
||||||
|
for (idx, (current, _)) in self.fields.iter().enumerate() {
|
||||||
|
if current == name {
|
||||||
|
field_index = Some(idx);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
field_index.map(|i| self.fields.remove(i).1)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn iter(&self) -> impl Iterator<Item = (&ArcIntern<String>, &T)> {
|
||||||
|
self.fields.iter().map(|(x, y)| (x, y))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn into_iter(self) -> impl Iterator<Item = (ArcIntern<String>, T)> {
|
||||||
|
self.fields.into_iter()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn field_names(&self) -> impl Iterator<Item = &ArcIntern<String>> {
|
||||||
|
self.fields.iter().map(|(n, _)| n)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn types(&self) -> impl Iterator<Item = &T> {
|
||||||
|
self.fields.iter().map(|(_, x)| x)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn types_mut(&mut self) -> impl Iterator<Item = &mut T> {
|
||||||
|
self.fields.iter_mut().map(|(_, x)| x)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -149,15 +149,15 @@ fn convert_top_level(
|
|||||||
convert_statement(stmt, constraint_db, renames, bindings),
|
convert_statement(stmt, constraint_db, renames, bindings),
|
||||||
)),
|
)),
|
||||||
|
|
||||||
syntax::TopLevel::Structure(_loc, name, fields) => TopLevelItem::Type(
|
syntax::TopLevel::Structure(_loc, name, fields) => {
|
||||||
name.intern(),
|
let mut updated_fields = ir::Fields::default();
|
||||||
ir::TypeOrVar::Structure(
|
|
||||||
fields
|
for (name, field_type) in fields.into_iter() {
|
||||||
.into_iter()
|
updated_fields.insert(name.intern(), convert_type(field_type, constraint_db));
|
||||||
.map(|(name, t)| (name.intern(), convert_type(t, constraint_db)))
|
}
|
||||||
.collect(),
|
|
||||||
),
|
TopLevelItem::Type(name.intern(), ir::TypeOrVar::Structure(updated_fields))
|
||||||
),
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -294,7 +294,7 @@ fn convert_expression(
|
|||||||
|
|
||||||
syntax::Expression::Constructor(loc, name, fields) => {
|
syntax::Expression::Constructor(loc, name, fields) => {
|
||||||
let mut result_fields = HashMap::new();
|
let mut result_fields = HashMap::new();
|
||||||
let mut type_fields = HashMap::new();
|
let mut type_fields = ir::Fields::default();
|
||||||
let mut prereqs = vec![];
|
let mut prereqs = vec![];
|
||||||
let result_type = ir::TypeOrVar::new();
|
let result_type = ir::TypeOrVar::new();
|
||||||
|
|
||||||
@@ -479,18 +479,18 @@ fn convert_type(ty: syntax::Type, constraint_db: &mut Vec<Constraint>) -> ir::Ty
|
|||||||
}
|
}
|
||||||
Ok(v) => ir::TypeOrVar::Primitive(v),
|
Ok(v) => ir::TypeOrVar::Primitive(v),
|
||||||
},
|
},
|
||||||
syntax::Type::Struct(fields) => ir::TypeOrVar::Structure(
|
syntax::Type::Struct(fields) => {
|
||||||
fields
|
let mut new_fields = ir::Fields::default();
|
||||||
.into_iter()
|
|
||||||
.map(|(n, t)| {
|
for (name, field_type) in fields.into_iter() {
|
||||||
(
|
let new_field_type = field_type
|
||||||
n.intern(),
|
.map(|x| convert_type(x, constraint_db))
|
||||||
t.map(|x| convert_type(x, constraint_db))
|
.unwrap_or_else(ir::TypeOrVar::new);
|
||||||
.unwrap_or_else(ir::TypeOrVar::new),
|
new_fields.insert(name.intern(), new_field_type);
|
||||||
)
|
}
|
||||||
})
|
|
||||||
.collect(),
|
ir::TypeOrVar::Structure(new_fields)
|
||||||
),
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -134,12 +134,9 @@ fn finalize_type(ty: TypeOrVar, resolutions: &TypeResolutions) -> Type {
|
|||||||
.collect(),
|
.collect(),
|
||||||
Box::new(finalize_type(*ret, resolutions)),
|
Box::new(finalize_type(*ret, resolutions)),
|
||||||
),
|
),
|
||||||
TypeOrVar::Structure(fields) => Type::Structure(
|
TypeOrVar::Structure(fields) => {
|
||||||
fields
|
Type::Structure(fields.map(|subtype| finalize_type(subtype, resolutions)))
|
||||||
.into_iter()
|
}
|
||||||
.map(|(name, subtype)| (name, finalize_type(subtype, resolutions)))
|
|
||||||
.collect(),
|
|
||||||
),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -446,7 +446,7 @@ pub fn solve_constraints(
|
|||||||
TypeOrVar::Structure(mut fields),
|
TypeOrVar::Structure(mut fields),
|
||||||
field,
|
field,
|
||||||
result_type,
|
result_type,
|
||||||
) => match fields.remove(&field) {
|
) => match fields.remove_field(&field) {
|
||||||
None => {
|
None => {
|
||||||
let reconstituted = TypeOrVar::Structure(fields);
|
let reconstituted = TypeOrVar::Structure(fields);
|
||||||
tracing::trace!(structure_type = %reconstituted, %field, "no field found in type");
|
tracing::trace!(structure_type = %reconstituted, %field, "no field found in type");
|
||||||
@@ -772,12 +772,12 @@ pub fn solve_constraints(
|
|||||||
TypeOrVar::Structure(fields1),
|
TypeOrVar::Structure(fields1),
|
||||||
TypeOrVar::Structure(mut fields2),
|
TypeOrVar::Structure(mut fields2),
|
||||||
) => {
|
) => {
|
||||||
if fields1.len() == fields2.len()
|
if fields1.count() == fields2.count()
|
||||||
&& fields1.keys().all(|x| fields2.contains_key(x))
|
&& fields1.field_names().all(|x| fields2.has_field(x))
|
||||||
{
|
{
|
||||||
for (name, subtype1) in fields1.into_iter() {
|
for (name, subtype1) in fields1.into_iter() {
|
||||||
let subtype2 = fields2
|
let subtype2 = fields2
|
||||||
.remove(&name)
|
.remove_field(&name)
|
||||||
.expect("can find matching field after equivalence check");
|
.expect("can find matching field after equivalence check");
|
||||||
new_constraints.push(Constraint::Equivalent(
|
new_constraints.push(Constraint::Equivalent(
|
||||||
loc.clone(),
|
loc.clone(),
|
||||||
|
|||||||
Reference in New Issue
Block a user