transfer commit
This commit is contained in:
@@ -1,79 +1,84 @@
|
||||
use mappable_rc::Mrc;
|
||||
|
||||
use crate::utils::collect_to_mrc;
|
||||
use crate::utils::{collect_to_mrc, to_mrc_slice};
|
||||
|
||||
use super::super::representations::typed::{Clause, Expr};
|
||||
|
||||
pub fn apply_lambda(id: u64, value: Mrc<Expr>, body: Mrc<Expr>) -> Mrc<Expr> {
|
||||
apply_lambda_expr_rec(id, value, Mrc::clone(&body))
|
||||
.unwrap_or(body)
|
||||
apply_lambda_expr_rec(id, value, Mrc::clone(&body))
|
||||
.unwrap_or(body)
|
||||
}
|
||||
|
||||
fn apply_lambda_expr_rec(
|
||||
id: u64, value: Mrc<Expr>, expr: Mrc<Expr>
|
||||
id: u64, value: Mrc<Expr>, expr: Mrc<Expr>
|
||||
) -> Option<Mrc<Expr>> {
|
||||
let Expr(clause, typ) = expr.as_ref();
|
||||
match clause {
|
||||
Clause::Argument(arg_id) if *arg_id == id => {
|
||||
let full_typ = collect_to_mrc(
|
||||
value.1.iter()
|
||||
.chain(typ.iter())
|
||||
.cloned()
|
||||
);
|
||||
Some(Mrc::new(Expr(value.0.to_owned(), full_typ)))
|
||||
}
|
||||
cl => {
|
||||
apply_lambda_clause_rec(id, value, clause.clone())
|
||||
.map(|c| Mrc::new(Expr(c, Mrc::clone(typ))))
|
||||
}
|
||||
let Expr(clause, typ) = expr.as_ref();
|
||||
match clause {
|
||||
Clause::LambdaArg(arg_id) | Clause::AutoArg(arg_id) if *arg_id == id => {
|
||||
let full_typ = collect_to_mrc(
|
||||
value.1.iter()
|
||||
.chain(typ.iter())
|
||||
.cloned()
|
||||
);
|
||||
Some(Mrc::new(Expr(value.0.to_owned(), full_typ)))
|
||||
}
|
||||
cl => {
|
||||
apply_lambda_clause_rec(id, value, cl.clone())
|
||||
.map(|c| Mrc::new(Expr(c, Mrc::clone(typ))))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_lambda_clause_rec(
|
||||
id: u64, value: Mrc<Expr>, clause: Clause
|
||||
id: u64, value: Mrc<Expr>, clause: Clause
|
||||
) -> Option<Clause> {
|
||||
match clause {
|
||||
// Only element actually manipulated
|
||||
Clause::Argument(id) => panic!(
|
||||
"apply_lambda_expr_rec is supposed to eliminate this case"),
|
||||
// Traverse, yield Some if either had changed.
|
||||
Clause::Apply(f, x) => {
|
||||
let new_f = apply_lambda_expr_rec(
|
||||
id, Mrc::clone(&value), Mrc::clone(&f)
|
||||
);
|
||||
let new_x = apply_lambda_expr_rec(
|
||||
id, value, Mrc::clone(&x)
|
||||
);
|
||||
match (new_f, new_x) { // Mind the shadows
|
||||
(None, None) => None,
|
||||
(None, Some(x)) => Some(Clause::Apply(f, x)),
|
||||
(Some(f), None) => Some(Clause::Apply(f, x)),
|
||||
(Some(f), Some(x)) => Some(Clause::Apply(f, x))
|
||||
}
|
||||
},
|
||||
Clause::Lambda(own_id, t, b) => apply_lambda__traverse_param(id, value, own_id, t, b, Clause::Lambda),
|
||||
Clause::Auto(own_id, t, b) => apply_lambda__traverse_param(id, value, own_id, t, b, Clause::Auto),
|
||||
// Leaf nodes
|
||||
Clause::Atom(_) | Clause::ExternFn(_) | Clause::Literal(_) => None
|
||||
}
|
||||
match clause {
|
||||
// Only element actually manipulated
|
||||
Clause::LambdaArg(_) | Clause::AutoArg(_) => Some(clause),
|
||||
// Traverse, yield Some if either had changed.
|
||||
Clause::Apply(f, x) => {
|
||||
let new_f = apply_lambda_expr_rec(
|
||||
id, Mrc::clone(&value), Mrc::clone(&f)
|
||||
);
|
||||
let new_x = apply_lambda_expr_rec(
|
||||
id, value, Mrc::clone(&x)
|
||||
);
|
||||
match (new_f, new_x) { // Mind the shadows
|
||||
(None, None) => None,
|
||||
(None, Some(x)) => Some(Clause::Apply(f, x)),
|
||||
(Some(f), None) => Some(Clause::Apply(f, x)),
|
||||
(Some(f), Some(x)) => Some(Clause::Apply(f, x))
|
||||
}
|
||||
},
|
||||
Clause::Lambda(own_id, t, b) => apply_lambda__traverse_param(id, value, own_id, t, b, Clause::Lambda),
|
||||
Clause::Auto(own_id, t, b) => apply_lambda__traverse_param(id, value, own_id, t, b, Clause::Auto),
|
||||
// Leaf nodes
|
||||
Clause::Atom(_) | Clause::ExternFn(_) | Clause::Literal(_) => None
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_lambda__traverse_param(
|
||||
id: u64, value: Mrc<Expr>,
|
||||
own_id: u64, t: Option<Mrc<Clause>>, b: Mrc<Expr>,
|
||||
wrap: impl Fn(u64, Option<Mrc<Clause>>, Mrc<Expr>) -> Clause
|
||||
id: u64, value: Mrc<Expr>,
|
||||
own_id: u64, typ: Mrc<[Clause]>, b: Mrc<Expr>,
|
||||
wrap: impl Fn(u64, Mrc<[Clause]>, Mrc<Expr>) -> Clause
|
||||
) -> Option<Clause> {
|
||||
let new_t = t.and_then(|t| apply_lambda_clause_rec(
|
||||
id, Mrc::clone(&value), t.as_ref().clone()
|
||||
));
|
||||
// Respect shadowing
|
||||
let new_b = if own_id == id {None} else {
|
||||
apply_lambda_expr_rec(id, value, Mrc::clone(&b))
|
||||
};
|
||||
match (new_t, new_b) { // Mind the shadows
|
||||
(None, None) => None,
|
||||
(None, Some(b)) => Some(wrap(own_id, t, b)),
|
||||
(Some(t), None) => Some(wrap(own_id, Some(Mrc::new(t)), b)),
|
||||
(Some(t), Some(b)) => Some(wrap(own_id, Some(Mrc::new(t)), b))
|
||||
}
|
||||
let any_t = false;
|
||||
let mut t_acc = vec![];
|
||||
for t in typ.iter() {
|
||||
let newt = apply_lambda_clause_rec(id, Mrc::clone(&value), t.clone());
|
||||
any_t |= newt.is_some();
|
||||
t_acc.push(newt.unwrap_or_else(|| t.clone()))
|
||||
}
|
||||
// Respect shadowing
|
||||
let new_b = if own_id == id {None} else {
|
||||
apply_lambda_expr_rec(id, value, Mrc::clone(&b))
|
||||
};
|
||||
if any_t { // mind the shadows
|
||||
let typ = to_mrc_slice(t_acc);
|
||||
if let Some(b) = new_b {
|
||||
Some(wrap(own_id, typ, b))
|
||||
} else {Some(wrap(own_id, typ, b))}
|
||||
} else if let Some(b) = new_b {
|
||||
Some(wrap(own_id, typ, b))
|
||||
} else {Some(wrap(own_id, typ, b))}
|
||||
}
|
||||
@@ -1,104 +0,0 @@
|
||||
use std::any::Any;
|
||||
use std::fmt::{Display, Debug};
|
||||
use std::hash::Hash;
|
||||
|
||||
use mappable_rc::Mrc;
|
||||
|
||||
use crate::representations::typed::{Expr, Clause};
|
||||
|
||||
pub trait ExternError: Display {}
|
||||
|
||||
/// Represents an externally defined function from the perspective of the executor
|
||||
/// Since Orchid lacks basic numerical operations, these are also external functions.
|
||||
pub struct ExternFn {
|
||||
name: String, param: Mrc<Expr>, rttype: Mrc<Expr>,
|
||||
function: Mrc<dyn Fn(Clause) -> Result<Clause, Mrc<dyn ExternError>>>
|
||||
}
|
||||
|
||||
impl ExternFn {
|
||||
pub fn new<F: 'static + Fn(Clause) -> Result<Clause, Mrc<dyn ExternError>>>(
|
||||
name: String, param: Mrc<Expr>, rttype: Mrc<Expr>, f: F
|
||||
) -> Self {
|
||||
Self {
|
||||
name, param, rttype,
|
||||
function: Mrc::map(Mrc::new(f), |f| {
|
||||
f as &dyn Fn(Clause) -> Result<Clause, Mrc<dyn ExternError>>
|
||||
})
|
||||
}
|
||||
}
|
||||
pub fn name(&self) -> &str {&self.name}
|
||||
pub fn apply(&self, arg: Clause) -> Result<Clause, Mrc<dyn ExternError>> {(self.function)(arg)}
|
||||
}
|
||||
|
||||
impl Clone for ExternFn { fn clone(&self) -> Self { Self {
|
||||
name: self.name.clone(),
|
||||
param: Mrc::clone(&self.param),
|
||||
rttype: Mrc::clone(&self.rttype),
|
||||
function: Mrc::clone(&self.function)
|
||||
}}}
|
||||
impl Eq for ExternFn {}
|
||||
impl PartialEq for ExternFn {
|
||||
fn eq(&self, other: &Self) -> bool { self.name() == other.name() }
|
||||
}
|
||||
impl Hash for ExternFn {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) { self.name.hash(state) }
|
||||
}
|
||||
impl Debug for ExternFn {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "##EXTERN[{}]:{:?} -> {:?}##", self.name(), self.param, self.rttype)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Atomic: Any + Debug where Self: 'static {
|
||||
fn as_any(&self) -> &dyn Any;
|
||||
fn definitely_eq(&self, _other: &dyn Any) -> bool;
|
||||
fn hash(&self, hasher: &mut dyn std::hash::Hasher);
|
||||
}
|
||||
|
||||
/// Represents a unit of information from the perspective of the executor. This may be
|
||||
/// something like a file descriptor which functions can operate on, but it can also be
|
||||
/// information in the universe of types or kinds such as the type of signed integers or
|
||||
/// the kind of types. Ad absurdum it can also be just a number, although Literal is
|
||||
/// preferable for types it's defined on.
|
||||
pub struct Atom {
|
||||
typ: Mrc<Expr>,
|
||||
data: Mrc<dyn Atomic>
|
||||
}
|
||||
impl Atom {
|
||||
pub fn new<T: 'static + Atomic>(data: T, typ: Mrc<Expr>) -> Self { Self{
|
||||
typ,
|
||||
data: Mrc::map(Mrc::new(data), |d| d as &dyn Atomic)
|
||||
} }
|
||||
pub fn data(&self) -> &dyn Atomic { self.data.as_ref() as &dyn Atomic }
|
||||
pub fn try_cast<T: Atomic>(&self) -> Result<&T, ()> {
|
||||
self.data().as_any().downcast_ref().ok_or(())
|
||||
}
|
||||
pub fn is<T: 'static>(&self) -> bool { self.data().as_any().is::<T>() }
|
||||
pub fn cast<T: 'static>(&self) -> &T {
|
||||
self.data().as_any().downcast_ref().expect("Type mismatch on Atom::cast")
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for Atom {
|
||||
fn clone(&self) -> Self { Self {
|
||||
typ: Mrc::clone(&self.typ),
|
||||
data: Mrc::clone(&self.data)
|
||||
} }
|
||||
}
|
||||
impl Hash for Atom {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
self.data.hash(state);
|
||||
self.typ.hash(state)
|
||||
}
|
||||
}
|
||||
impl Debug for Atom {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "##ATOM[{:?}]:{:?}##", self.data(), self.typ)
|
||||
}
|
||||
}
|
||||
impl Eq for Atom {}
|
||||
impl PartialEq for Atom {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.data().definitely_eq(other.data().as_any())
|
||||
}
|
||||
}
|
||||
@@ -5,26 +5,26 @@ use crate::utils::collect_to_mrc;
|
||||
use super::super::representations::typed::{Clause, Expr};
|
||||
|
||||
fn normalize(Expr(clause, typ): Expr) -> Expr {
|
||||
todo!()
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn collect_autos(
|
||||
Expr(clause, typ): Expr,
|
||||
arg_types: Vec<Mrc<[Clause]>>,
|
||||
indirect_argt_trees: Vec<Mrc<[Clause]>>,
|
||||
sunk_types: &mut dyn Iterator<Item = Clause>
|
||||
Expr(clause, typ): Expr,
|
||||
arg_types: Vec<Mrc<[Clause]>>,
|
||||
indirect_argt_trees: Vec<Mrc<[Clause]>>,
|
||||
sunk_types: &mut dyn Iterator<Item = Clause>
|
||||
) -> (Vec<Mrc<[Clause]>>, Expr) {
|
||||
if let Clause::Auto(argt, body) = clause {
|
||||
|
||||
}
|
||||
else {(
|
||||
arg_types,
|
||||
Expr(
|
||||
clause,
|
||||
collect_to_mrc(
|
||||
typ.iter().cloned()
|
||||
.chain(sunk_types)
|
||||
)
|
||||
)
|
||||
)}
|
||||
if let Clause::Auto(argt, body) = clause {
|
||||
|
||||
}
|
||||
else {(
|
||||
arg_types,
|
||||
Expr(
|
||||
clause,
|
||||
collect_to_mrc(
|
||||
typ.iter().cloned()
|
||||
.chain(sunk_types)
|
||||
)
|
||||
)
|
||||
)}
|
||||
}
|
||||
@@ -8,49 +8,41 @@ use super::super::representations::typed::{Clause, Expr};
|
||||
use super::super::utils::Stackframe;
|
||||
|
||||
const PARAMETRICS_INLINE_COUNT:usize = 5;
|
||||
type Parametrics<'a> = ProtoMap<'a, u64, bool, PARAMETRICS_INLINE_COUNT>;
|
||||
// type Parametrics<'a> = ProtoMap<'a, u64, bool, PARAMETRICS_INLINE_COUNT>;
|
||||
|
||||
/// Hash the parts of an expression that are required to be equal for syntactic equality.
|
||||
pub fn partial_hash_rec<H: Hasher>(
|
||||
Expr(clause, _): &Expr, state: &mut H,
|
||||
mut parametrics: Parametrics
|
||||
Expr(clause, _): &Expr, state: &mut H,
|
||||
parametrics: Option<&Stackframe<u64>>
|
||||
) {
|
||||
match clause {
|
||||
// Skip autos
|
||||
Clause::Auto(id, _, body) => {
|
||||
parametrics.set(id, true);
|
||||
partial_hash_rec(body, state, parametrics)
|
||||
}
|
||||
// Annotate everything else with a prefix
|
||||
// - Recurse into the tree of lambdas and calls - classic lambda calc
|
||||
Clause::Lambda(id, _, body) => {
|
||||
state.write_u8(0);
|
||||
parametrics.set(id, false);
|
||||
partial_hash_rec(body, state, parametrics)
|
||||
}
|
||||
Clause::Apply(f, x) => {
|
||||
state.write_u8(1);
|
||||
partial_hash_rec(f, state, parametrics.clone());
|
||||
partial_hash_rec(x, state, parametrics);
|
||||
}
|
||||
// - Only recognize the depth of an argument if it refers to a non-auto parameter
|
||||
Clause::Argument(own_id) => {
|
||||
let (pos, is_auto) = parametrics.iter()
|
||||
.filter_map(|(id, is_auto)| is_auto.map(|is_auto| (*id, is_auto)))
|
||||
.find_position(|(id, is_auto)| id == own_id)
|
||||
.map(|(pos, (_, is_auto))| (pos, is_auto))
|
||||
.unwrap_or((usize::MAX, false));
|
||||
// If the argument references an auto, acknowledge its existence
|
||||
if is_auto {
|
||||
state.write_u8(2)
|
||||
} else {
|
||||
state.write_u8(3);
|
||||
state.write_usize(pos)
|
||||
}
|
||||
}
|
||||
// - Hash leaves like normal
|
||||
Clause::Literal(lit) => { state.write_u8(4); lit.hash(state) }
|
||||
Clause::Atom(at) => { state.write_u8(5); at.hash(state) }
|
||||
Clause::ExternFn(f) => { state.write_u8(6); f.hash(state) }
|
||||
match clause {
|
||||
// Skip autos
|
||||
Clause::Auto(id, _, body) => {
|
||||
partial_hash_rec(body, state, parametrics)
|
||||
}
|
||||
// Annotate everything else with a prefix
|
||||
// - Recurse into the tree of lambdas and calls - classic lambda calc
|
||||
Clause::Lambda(id, _, body) => {
|
||||
state.write_u8(0);
|
||||
partial_hash_rec(body, state, Some(&Stackframe::opush(parametrics, *id)))
|
||||
}
|
||||
Clause::Apply(f, x) => {
|
||||
state.write_u8(1);
|
||||
partial_hash_rec(f, state, parametrics.clone());
|
||||
partial_hash_rec(x, state, parametrics);
|
||||
}
|
||||
Clause::AutoArg(..) => state.write_u8(2),
|
||||
// - Only recognize the depth of an argument if it refers to a non-auto parameter
|
||||
Clause::LambdaArg(own_id) => {
|
||||
let pos = parametrics
|
||||
.and_then(|sf| sf.iter().position(|id| id == own_id))
|
||||
.unwrap_or(usize::MAX);
|
||||
state.write_u8(3);
|
||||
state.write_usize(pos)
|
||||
}
|
||||
// - Hash leaves like normal
|
||||
Clause::Literal(lit) => { state.write_u8(4); lit.hash(state) }
|
||||
Clause::Atom(at) => { state.write_u8(5); at.hash(state) }
|
||||
Clause::ExternFn(f) => { state.write_u8(6); f.hash(state) }
|
||||
}
|
||||
}
|
||||
@@ -10,88 +10,88 @@ use super::super::representations::typed::{Clause, Expr};
|
||||
/// Call the function with the first Expression that isn't an Auto,
|
||||
/// wrap all elements in the returned iterator back in the original sequence of Autos.
|
||||
pub fn skip_autos<'a,
|
||||
F: 'a + FnOnce(Mrc<Expr>) -> I,
|
||||
I: Iterator<Item = Mrc<Expr>> + 'static
|
||||
F: 'a + FnOnce(Mrc<Expr>) -> I,
|
||||
I: Iterator<Item = Mrc<Expr>> + 'static
|
||||
>(
|
||||
expr: Mrc<Expr>, function: F
|
||||
expr: Mrc<Expr>, function: F
|
||||
) -> BoxedIter<'static, Mrc<Expr>> {
|
||||
if let Expr(Clause::Auto(id, arg, body), typ) = expr.as_ref() {
|
||||
return Box::new(skip_autos(Mrc::clone(body), function).map({
|
||||
let arg = arg.as_ref().map(Mrc::clone);
|
||||
let typ = Mrc::clone(typ);
|
||||
move |body| {
|
||||
Mrc::new(Expr(Clause::Auto(
|
||||
*id,
|
||||
arg.as_ref().map(Mrc::clone),
|
||||
body
|
||||
), Mrc::clone(&typ)))
|
||||
}
|
||||
})) as BoxedIter<'static, Mrc<Expr>>
|
||||
}
|
||||
Box::new(function(expr))
|
||||
if let Expr(Clause::Auto(id, arg, body), typ) = expr.as_ref() {
|
||||
return Box::new(skip_autos(Mrc::clone(body), function).map({
|
||||
let arg = Mrc::clone(arg);
|
||||
let typ = Mrc::clone(typ);
|
||||
move |body| {
|
||||
Mrc::new(Expr(Clause::Auto(
|
||||
*id,
|
||||
Mrc::clone(&arg),
|
||||
body
|
||||
), Mrc::clone(&typ)))
|
||||
}
|
||||
})) as BoxedIter<'static, Mrc<Expr>>
|
||||
}
|
||||
Box::new(function(expr))
|
||||
}
|
||||
|
||||
/// Produces an iterator of every expression that can be produced from this one through B-reduction.
|
||||
fn direct_reductions(ex: Mrc<Expr>) -> impl Iterator<Item = Mrc<Expr>> {
|
||||
skip_autos(ex, |mexpr| {
|
||||
let Expr(clause, typ_ref) = mexpr.as_ref();
|
||||
match clause {
|
||||
Clause::Apply(f, x) => box_chain!(
|
||||
skip_autos(Mrc::clone(f), |mexpr| {
|
||||
let Expr(f, _) = mexpr.as_ref();
|
||||
match f {
|
||||
Clause::Lambda(id, _, body) => box_once(
|
||||
apply_lambda(*id, Mrc::clone(x), Mrc::clone(body))
|
||||
),
|
||||
Clause::ExternFn(xfn) => {
|
||||
let Expr(xval, xtyp) = x.as_ref();
|
||||
xfn.apply(xval.clone())
|
||||
.map(|ret| box_once(Mrc::new(Expr(ret, Mrc::clone(xtyp)))))
|
||||
.unwrap_or(box_empty())
|
||||
},
|
||||
// Parametric newtypes are atoms of function type
|
||||
Clause::Atom(..) | Clause::Argument(..) | Clause::Apply(..) => box_empty(),
|
||||
Clause::Literal(lit) =>
|
||||
panic!("Literal expression {lit:?} can't be applied as function"),
|
||||
Clause::Auto(..) => unreachable!("skip_autos should have filtered this"),
|
||||
}
|
||||
}),
|
||||
direct_reductions(Mrc::clone(f)).map({
|
||||
let typ = Mrc::clone(typ_ref);
|
||||
let x = Mrc::clone(x);
|
||||
move |f| Mrc::new(Expr(Clause::Apply(
|
||||
f,
|
||||
Mrc::clone(&x)
|
||||
), Mrc::clone(&typ)))
|
||||
}),
|
||||
direct_reductions(Mrc::clone(x)).map({
|
||||
let typ = Mrc::clone(typ_ref);
|
||||
let f = Mrc::clone(f);
|
||||
move |x| Mrc::new(Expr(Clause::Apply(
|
||||
Mrc::clone(&f),
|
||||
x
|
||||
), Mrc::clone(&typ)))
|
||||
})
|
||||
skip_autos(ex, |mexpr| {
|
||||
let Expr(clause, typ_ref) = mexpr.as_ref();
|
||||
match clause {
|
||||
Clause::Apply(f, x) => box_chain!(
|
||||
skip_autos(Mrc::clone(f), |mexpr| {
|
||||
let Expr(f, _) = mexpr.as_ref();
|
||||
match f {
|
||||
Clause::Lambda(id, _, body) => box_once(
|
||||
apply_lambda(*id, Mrc::clone(x), Mrc::clone(body))
|
||||
),
|
||||
Clause::Lambda(id, argt, body) => {
|
||||
let id = *id;
|
||||
let typ = Mrc::clone(typ_ref);
|
||||
let argt = argt.as_ref().map(Mrc::clone);
|
||||
let body = Mrc::clone(body);
|
||||
let body_reductions = direct_reductions(body)
|
||||
.map(move |body| {
|
||||
let argt = argt.as_ref().map(Mrc::clone);
|
||||
Mrc::new(Expr(
|
||||
Clause::Lambda(id, argt, body),
|
||||
Mrc::clone(&typ)
|
||||
))
|
||||
});
|
||||
Box::new(body_reductions)
|
||||
Clause::ExternFn(xfn) => {
|
||||
let Expr(xval, xtyp) = x.as_ref();
|
||||
xfn.apply(xval.clone())
|
||||
.map(|ret| box_once(Mrc::new(Expr(ret, Mrc::clone(xtyp)))))
|
||||
.unwrap_or(box_empty())
|
||||
},
|
||||
Clause::Literal(..) | Clause::ExternFn(..) | Clause::Atom(..) | Clause::Argument(..) =>
|
||||
box_empty(),
|
||||
// Parametric newtypes are atoms of function type
|
||||
Clause::Atom(..) | Clause::LambdaArg(..) | Clause::AutoArg(..) | Clause::Apply(..) => box_empty(),
|
||||
Clause::Literal(lit) =>
|
||||
panic!("Literal expression {lit:?} can't be applied as function"),
|
||||
Clause::Auto(..) => unreachable!("skip_autos should have filtered this"),
|
||||
}
|
||||
})
|
||||
}
|
||||
}),
|
||||
direct_reductions(Mrc::clone(f)).map({
|
||||
let typ = Mrc::clone(typ_ref);
|
||||
let x = Mrc::clone(x);
|
||||
move |f| Mrc::new(Expr(Clause::Apply(
|
||||
f,
|
||||
Mrc::clone(&x)
|
||||
), Mrc::clone(&typ)))
|
||||
}),
|
||||
direct_reductions(Mrc::clone(x)).map({
|
||||
let typ = Mrc::clone(typ_ref);
|
||||
let f = Mrc::clone(f);
|
||||
move |x| Mrc::new(Expr(Clause::Apply(
|
||||
Mrc::clone(&f),
|
||||
x
|
||||
), Mrc::clone(&typ)))
|
||||
})
|
||||
),
|
||||
Clause::Lambda(id, argt, body) => {
|
||||
let id = *id;
|
||||
let typ = Mrc::clone(typ_ref);
|
||||
let argt = Mrc::clone(argt);
|
||||
let body = Mrc::clone(body);
|
||||
let body_reductions = direct_reductions(body)
|
||||
.map(move |body| {
|
||||
let argt = Mrc::clone(&argt);
|
||||
Mrc::new(Expr(
|
||||
Clause::Lambda(id, argt, body),
|
||||
Mrc::clone(&typ)
|
||||
))
|
||||
});
|
||||
Box::new(body_reductions)
|
||||
},
|
||||
Clause::Auto(..) => unreachable!("skip_autos should have filtered this"),
|
||||
Clause::Literal(..) | Clause::ExternFn(..) | Clause::Atom(..)
|
||||
| Clause::LambdaArg(..) | Clause::AutoArg(..) => box_empty(),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
use std::collections::HashMap;
|
||||
use std::hash::{Hasher, Hash};
|
||||
use std::iter;
|
||||
|
||||
use itertools::Itertools;
|
||||
use mappable_rc::Mrc;
|
||||
|
||||
use crate::utils::{ProtoMap, Side};
|
||||
use crate::utils::{ProtoMap, Side, mrc_empty_slice, collect_to_mrc, Stackframe, mrc_concat, Product2};
|
||||
|
||||
use super::super::representations::typed::{Clause, Expr};
|
||||
use super::super::utils::Stackframe;
|
||||
|
||||
pub fn swap<T, U>((t, u): (T, U)) -> (U, T) { (u, t) }
|
||||
|
||||
@@ -17,24 +15,92 @@ pub fn swap<T, U>((t, u): (T, U)) -> (U, T) { (u, t) }
|
||||
// - get rid of leftovers from Explicit
|
||||
// - adapt to new index-based system
|
||||
|
||||
// =@= =&= =%= =#= =$= =?= =!= =/=
|
||||
// <@> <&> <%> <#> <$> <?> <!> </>
|
||||
// |@| |&| |%| |#| |$| |?| |!| |/|
|
||||
// {@} {&} {%} {#} {$} {?} {!} {/}
|
||||
// (@) (&) (%) (#) ($) (?) (!) (/)
|
||||
// [@] [&] [%] [#] [$] [?] [!] [/]
|
||||
enum UnifError {
|
||||
Conflict,
|
||||
}
|
||||
|
||||
type LambdaMap<'a> = Option<&'a Stackframe<'a, (u64, u64)>>;
|
||||
|
||||
/// The context associates a given variable (by absolute index) on a given side to
|
||||
/// an expression on the opposite side rooted at the specified depth.
|
||||
/// The root depths are used to translate betwee de Brujin arguments and absolute indices.
|
||||
struct Context(HashMap<u64, Mrc<Expr>>);
|
||||
impl Context {
|
||||
fn set(&mut self, id: u64, value: Mrc<Expr>) {
|
||||
// If already defined, then it must be an argument
|
||||
if let Some(value) = self.0.get(&id) {
|
||||
if let Clause::Argument(opposite_up) ex.0
|
||||
}
|
||||
}
|
||||
fn set(&mut self, id: u64, value: &Mrc<Expr>, lambdas: LambdaMap) -> Result<Option<Mrc<Expr>>, UnifError> {
|
||||
Ok(
|
||||
if let Some(local) = self.0.get(&id) {
|
||||
Some(
|
||||
self.unify_expr(local, value, lambdas)?
|
||||
.pick(Mrc::clone(local), Mrc::clone(value))
|
||||
)
|
||||
} else { None }
|
||||
)
|
||||
}
|
||||
|
||||
fn unify_expr(&mut self,
|
||||
left: &Mrc<Expr>, right: &Mrc<Expr>, lambdas: LambdaMap
|
||||
) -> Result<Product2<Mrc<Expr>>, UnifError> {
|
||||
let Expr(left_val, left_typs) = left.as_ref();
|
||||
let Expr(right_val, right_typs) = right.as_ref();
|
||||
let val = match (left_val, right_val) {
|
||||
(Clause::AutoArg(l), Clause::AutoArg(r)) if l == r => Product2::Either,
|
||||
(Clause::AutoArg(id), _) => self.set(*id, left, lambdas)?.as_ref()
|
||||
.map_or(Product2::Left, |e| Product2::New(e.0.clone())),
|
||||
(_, Clause::AutoArg(id)) => self.set(*id, right, lambdas)?.as_ref()
|
||||
.map_or(Product2::Right, |e| Product2::New(e.0.clone())),
|
||||
_ => self.unify_clause(left_val, right_val, lambdas)?
|
||||
};
|
||||
Ok(match val {
|
||||
Product2::Either if right_typs.is_empty() && left_typs.is_empty() => Product2::Either,
|
||||
Product2::Left | Product2::Either if right_typs.is_empty() => Product2::Left,
|
||||
Product2::Right | Product2::Either if left_typs.is_empty() => Product2::Right,
|
||||
product => {
|
||||
let all_types = mrc_concat(left_typs, right_typs);
|
||||
Product2::New(Mrc::new(Expr(
|
||||
product.pick(left_val.clone(), right_val.clone()),
|
||||
all_types
|
||||
)))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn unify_clause(&mut self,
|
||||
left: &Clause, right: &Clause, lambdas: LambdaMap
|
||||
) -> Result<Product2<Clause>, UnifError> {
|
||||
Ok(match (left, right) {
|
||||
(Clause::Literal(l), Clause::Literal(r)) if l == r => Product2::Either,
|
||||
(Clause::Atom(l), Clause::Atom(r)) if l == r => Product2::Either,
|
||||
(Clause::ExternFn(l), Clause::ExternFn(r)) if l == r => Product2::Either,
|
||||
(Clause::LambdaArg(l), Clause::LambdaArg(r)) => if l == r {Product2::Either} else {
|
||||
let is_equal = Stackframe::o_into_iter(lambdas)
|
||||
.first_some(|(l_candidate, r_candidate)| {
|
||||
if l_candidate == l && r_candidate == r {Some(true)} // match
|
||||
else if l_candidate == l || r_candidate == r {Some(false)} // shadow
|
||||
else {None} // irrelevant
|
||||
}).unwrap_or(false);
|
||||
// Reference:
|
||||
if is_equal {Product2::Left} else {return Err(UnifError::Conflict)}
|
||||
}
|
||||
(Clause::AutoArg(_), _) | (_, Clause::AutoArg(_)) => {
|
||||
unreachable!("unify_expr should have handled this")
|
||||
}
|
||||
(Clause::Lambda(l_id, l_arg, l_body), Clause::Lambda(r_id, r_arg, r_body)) => {
|
||||
let lambdas = Stackframe::opush(lambdas, (*l_id, *r_id));
|
||||
self.unify_expr(l_body, r_body, Some(&lambdas))?
|
||||
.map(|ex| Clause::Lambda(*l_id, mrc_empty_slice(), ex))
|
||||
}
|
||||
(Clause::Apply(l_f, l_x), Clause::Apply(r_f, r_x)) => {
|
||||
self.unify_expr(l_f, r_f, lambdas)?.join((Mrc::clone(l_f), Mrc::clone(r_f)),
|
||||
self.unify_expr(l_x, r_x, lambdas)?, (Mrc::clone(l_x), Mrc::clone(r_x))
|
||||
).map(|(f, x)| Clause::Apply(f, x))
|
||||
}
|
||||
(Clause::Auto(l_id, l_arg, l_body), Clause::Auto(r_id, r_arg, r_body)) => {
|
||||
let typ = self.unify(l_arg, r_arg, lambdas)?;
|
||||
let body = self.unify_expr(l_body, r_body, lambdas)?;
|
||||
typ.join((l_arg, r_arg), )
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const IS_AUTO_INLINE:usize = 5;
|
||||
@@ -42,22 +108,22 @@ const IS_AUTO_INLINE:usize = 5;
|
||||
// All data to be forwarded during recursion about one half of a unification task
|
||||
#[derive(Clone)]
|
||||
struct UnifHalfTask<'a> {
|
||||
/// The expression to be unified
|
||||
expr: &'a Expr,
|
||||
/// Stores whether a given uid is auto or lambda
|
||||
is_auto: ProtoMap<'a, usize, bool, IS_AUTO_INLINE>
|
||||
/// The expression to be unified
|
||||
expr: &'a Expr,
|
||||
/// Stores whether a given uid is auto or lambda
|
||||
is_auto: ProtoMap<'a, usize, bool, IS_AUTO_INLINE>
|
||||
}
|
||||
|
||||
impl<'a> UnifHalfTask<'a> {
|
||||
fn push_auto(&mut self, body: &Expr, key: usize) {
|
||||
self.expr = body;
|
||||
self.is_auto.set(&key, true);
|
||||
}
|
||||
fn push_auto(&mut self, body: &Expr, key: usize) {
|
||||
self.expr = body;
|
||||
self.is_auto.set(&key, true);
|
||||
}
|
||||
|
||||
fn push_lambda(&mut self, body: &Expr, key: usize) {
|
||||
self.expr = body;
|
||||
self.is_auto.set(&key, false);
|
||||
}
|
||||
fn push_lambda(&mut self, body: &Expr, key: usize) {
|
||||
self.expr = body;
|
||||
self.is_auto.set(&key, false);
|
||||
}
|
||||
}
|
||||
|
||||
type Ctx = HashMap<usize, Mrc<Expr>>;
|
||||
@@ -68,63 +134,63 @@ type Ctx = HashMap<usize, Mrc<Expr>>;
|
||||
///
|
||||
/// Context associates variables with subtrees resolved on the opposite side
|
||||
pub fn unify_syntax_rec( // the stacks store true for autos, false for lambdas
|
||||
ctx: &mut HashMap<(Side, usize), (usize, Mrc<Expr>)>,
|
||||
ltask@UnifHalfTask{ expr: lexpr@Expr(lclause, _), .. }: UnifHalfTask,
|
||||
rtask@UnifHalfTask{ expr: rexpr@Expr(rclause, _), .. }: UnifHalfTask
|
||||
ctx: &mut HashMap<(Side, usize), (usize, Mrc<Expr>)>,
|
||||
ltask@UnifHalfTask{ expr: lexpr@Expr(lclause, _), .. }: UnifHalfTask,
|
||||
rtask@UnifHalfTask{ expr: rexpr@Expr(rclause, _), .. }: UnifHalfTask
|
||||
) -> Option<(UnifResult, UnifResult)> {
|
||||
// Ensure that ex1 is a value-level construct
|
||||
match lclause {
|
||||
Clause::Auto(id, _, body) => {
|
||||
let res = unify_syntax_rec(ltask.push_auto(body).0, rtask);
|
||||
return if ltask.explicits.is_some() {
|
||||
res.map(|(r1, r2)| (r1.useExplicit(), r2))
|
||||
} else {res}
|
||||
}
|
||||
_ => ()
|
||||
};
|
||||
// Reduce ex2's auto handling to ex1's. In the optimizer we trust
|
||||
if let Clause::Auto(..) | Clause::Explicit(..) = rclause {
|
||||
return unify_syntax_rec(rtask, ltask).map(swap);
|
||||
// Ensure that ex1 is a value-level construct
|
||||
match lclause {
|
||||
Clause::Auto(id, _, body) => {
|
||||
let res = unify_syntax_rec(ltask.push_auto(body).0, rtask);
|
||||
return if ltask.explicits.is_some() {
|
||||
res.map(|(r1, r2)| (r1.useExplicit(), r2))
|
||||
} else {res}
|
||||
}
|
||||
// Neither ex1 nor ex2 can be Auto or Explicit
|
||||
match (lclause, rclause) {
|
||||
// recurse into both
|
||||
(Clause::Lambda(_, lbody), Clause::Lambda(_, rbody)) => unify_syntax_rec(
|
||||
ltask.push_lambda(lbody),
|
||||
rtask.push_lambda(rbody)
|
||||
),
|
||||
(Clause::Apply(lf, lx), Clause::Apply(rf, rx)) => {
|
||||
let (lpart, rpart) = unify_syntax_rec(
|
||||
ltask.push_expr(lf),
|
||||
rtask.push_expr(rf)
|
||||
)?;
|
||||
lpart.dropUsedExplicits(&mut ltask);
|
||||
rpart.dropUsedExplicits(&mut rtask);
|
||||
unify_syntax_rec(ltask.push_expr(lx), rtask.push_expr(rx))
|
||||
}
|
||||
(Clause::Atom(latom), Clause::Atom(ratom)) => {
|
||||
if latom != ratom { None }
|
||||
else { Some((UnifResult::default(), UnifResult::default())) }
|
||||
}
|
||||
(Clause::ExternFn(lf), Clause::ExternFn(rf)) => {
|
||||
if lf != rf { None }
|
||||
else { Some((UnifResult::default(), UnifResult::default())) }
|
||||
}
|
||||
(Clause::Literal(llit), Clause::Literal(rlit)) => {
|
||||
if llit != rlit { None }
|
||||
else { Some((UnifResult::default(), UnifResult::default())) }
|
||||
}
|
||||
// TODO Select a representative
|
||||
(Clause::Argument(depth1), Clause::Argument(depth2)) => {
|
||||
!*stack1.iter().nth(*depth1).unwrap_or(&false)
|
||||
&& !*stack2.iter().nth(*depth2).unwrap_or(&false)
|
||||
&& stack1.iter().count() - depth1 == stack2.iter().count() - depth2
|
||||
}
|
||||
// TODO Assign a substitute
|
||||
(Clause::Argument(placeholder), _) => {
|
||||
_ => ()
|
||||
};
|
||||
// Reduce ex2's auto handling to ex1's. In the optimizer we trust
|
||||
if let Clause::Auto(..) | Clause::Explicit(..) = rclause {
|
||||
return unify_syntax_rec(rtask, ltask).map(swap);
|
||||
}
|
||||
// Neither ex1 nor ex2 can be Auto or Explicit
|
||||
match (lclause, rclause) {
|
||||
// recurse into both
|
||||
(Clause::Lambda(_, lbody), Clause::Lambda(_, rbody)) => unify_syntax_rec(
|
||||
ltask.push_lambda(lbody),
|
||||
rtask.push_lambda(rbody)
|
||||
),
|
||||
(Clause::Apply(lf, lx), Clause::Apply(rf, rx)) => {
|
||||
let (lpart, rpart) = unify_syntax_rec(
|
||||
ltask.push_expr(lf),
|
||||
rtask.push_expr(rf)
|
||||
)?;
|
||||
lpart.dropUsedExplicits(&mut ltask);
|
||||
rpart.dropUsedExplicits(&mut rtask);
|
||||
unify_syntax_rec(ltask.push_expr(lx), rtask.push_expr(rx))
|
||||
}
|
||||
(Clause::Atom(latom), Clause::Atom(ratom)) => {
|
||||
if latom != ratom { None }
|
||||
else { Some((UnifResult::default(), UnifResult::default())) }
|
||||
}
|
||||
(Clause::ExternFn(lf), Clause::ExternFn(rf)) => {
|
||||
if lf != rf { None }
|
||||
else { Some((UnifResult::default(), UnifResult::default())) }
|
||||
}
|
||||
(Clause::Literal(llit), Clause::Literal(rlit)) => {
|
||||
if llit != rlit { None }
|
||||
else { Some((UnifResult::default(), UnifResult::default())) }
|
||||
}
|
||||
// TODO Select a representative
|
||||
(Clause::Argument(depth1), Clause::Argument(depth2)) => {
|
||||
!*stack1.iter().nth(*depth1).unwrap_or(&false)
|
||||
&& !*stack2.iter().nth(*depth2).unwrap_or(&false)
|
||||
&& stack1.iter().count() - depth1 == stack2.iter().count() - depth2
|
||||
}
|
||||
// TODO Assign a substitute
|
||||
(Clause::Argument(placeholder), _) => {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Tricky unifications
|
||||
|
||||
Reference in New Issue
Block a user