Merge remote-tracking branch 'origin/master'

This commit is contained in:
2023-02-16 20:54:58 +00:00
63 changed files with 3224 additions and 2855 deletions

View File

@@ -1,15 +1,14 @@
{ {
"folders": [ "folders": [
{ {
"path": ".." "path": "."
} }
], ],
"settings": {}, "settings": {},
"extensions": { "extensions": {
"recommendations": [ "recommendations": [
"tomoki1207.pdf", "tomoki1207.pdf",
"James-Yu.latex-workshop", "james-yu.latex-workshop",
"rust-lang.rust-analyzer",
"bungcip.better-toml" "bungcip.better-toml"
] ]
} }

2
rust-toolchain.toml Normal file
View File

@@ -0,0 +1,2 @@
[toolchain]
channel = "nightly"

View File

@@ -1,6 +1,6 @@
use mappable_rc::Mrc; 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}; use super::super::representations::typed::{Clause, Expr};
@@ -14,7 +14,7 @@ fn apply_lambda_expr_rec(
) -> Option<Mrc<Expr>> { ) -> Option<Mrc<Expr>> {
let Expr(clause, typ) = expr.as_ref(); let Expr(clause, typ) = expr.as_ref();
match clause { match clause {
Clause::Argument(arg_id) if *arg_id == id => { Clause::LambdaArg(arg_id) | Clause::AutoArg(arg_id) if *arg_id == id => {
let full_typ = collect_to_mrc( let full_typ = collect_to_mrc(
value.1.iter() value.1.iter()
.chain(typ.iter()) .chain(typ.iter())
@@ -23,7 +23,7 @@ fn apply_lambda_expr_rec(
Some(Mrc::new(Expr(value.0.to_owned(), full_typ))) Some(Mrc::new(Expr(value.0.to_owned(), full_typ)))
} }
cl => { cl => {
apply_lambda_clause_rec(id, value, clause.clone()) apply_lambda_clause_rec(id, value, cl.clone())
.map(|c| Mrc::new(Expr(c, Mrc::clone(typ)))) .map(|c| Mrc::new(Expr(c, Mrc::clone(typ))))
} }
} }
@@ -34,8 +34,7 @@ fn apply_lambda_clause_rec(
) -> Option<Clause> { ) -> Option<Clause> {
match clause { match clause {
// Only element actually manipulated // Only element actually manipulated
Clause::Argument(id) => panic!( Clause::LambdaArg(_) | Clause::AutoArg(_) => Some(clause),
"apply_lambda_expr_rec is supposed to eliminate this case"),
// Traverse, yield Some if either had changed. // Traverse, yield Some if either had changed.
Clause::Apply(f, x) => { Clause::Apply(f, x) => {
let new_f = apply_lambda_expr_rec( let new_f = apply_lambda_expr_rec(
@@ -60,20 +59,26 @@ fn apply_lambda_clause_rec(
fn apply_lambda__traverse_param( fn apply_lambda__traverse_param(
id: u64, value: Mrc<Expr>, id: u64, value: Mrc<Expr>,
own_id: u64, t: Option<Mrc<Clause>>, b: Mrc<Expr>, own_id: u64, typ: Mrc<[Clause]>, b: Mrc<Expr>,
wrap: impl Fn(u64, Option<Mrc<Clause>>, Mrc<Expr>) -> Clause wrap: impl Fn(u64, Mrc<[Clause]>, Mrc<Expr>) -> Clause
) -> Option<Clause> { ) -> Option<Clause> {
let new_t = t.and_then(|t| apply_lambda_clause_rec( let any_t = false;
id, Mrc::clone(&value), t.as_ref().clone() 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 // Respect shadowing
let new_b = if own_id == id {None} else { let new_b = if own_id == id {None} else {
apply_lambda_expr_rec(id, value, Mrc::clone(&b)) apply_lambda_expr_rec(id, value, Mrc::clone(&b))
}; };
match (new_t, new_b) { // Mind the shadows if any_t { // mind the shadows
(None, None) => None, let typ = to_mrc_slice(t_acc);
(None, Some(b)) => Some(wrap(own_id, t, b)), if let Some(b) = new_b {
(Some(t), None) => Some(wrap(own_id, Some(Mrc::new(t)), b)), Some(wrap(own_id, typ, b))
(Some(t), Some(b)) => Some(wrap(own_id, Some(Mrc::new(t)), 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))}
} }

View File

@@ -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())
}
}

View File

@@ -8,46 +8,38 @@ use super::super::representations::typed::{Clause, Expr};
use super::super::utils::Stackframe; use super::super::utils::Stackframe;
const PARAMETRICS_INLINE_COUNT:usize = 5; 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. /// Hash the parts of an expression that are required to be equal for syntactic equality.
pub fn partial_hash_rec<H: Hasher>( pub fn partial_hash_rec<H: Hasher>(
Expr(clause, _): &Expr, state: &mut H, Expr(clause, _): &Expr, state: &mut H,
mut parametrics: Parametrics parametrics: Option<&Stackframe<u64>>
) { ) {
match clause { match clause {
// Skip autos // Skip autos
Clause::Auto(id, _, body) => { Clause::Auto(id, _, body) => {
parametrics.set(id, true);
partial_hash_rec(body, state, parametrics) partial_hash_rec(body, state, parametrics)
} }
// Annotate everything else with a prefix // Annotate everything else with a prefix
// - Recurse into the tree of lambdas and calls - classic lambda calc // - Recurse into the tree of lambdas and calls - classic lambda calc
Clause::Lambda(id, _, body) => { Clause::Lambda(id, _, body) => {
state.write_u8(0); state.write_u8(0);
parametrics.set(id, false); partial_hash_rec(body, state, Some(&Stackframe::opush(parametrics, *id)))
partial_hash_rec(body, state, parametrics)
} }
Clause::Apply(f, x) => { Clause::Apply(f, x) => {
state.write_u8(1); state.write_u8(1);
partial_hash_rec(f, state, parametrics.clone()); partial_hash_rec(f, state, parametrics.clone());
partial_hash_rec(x, state, parametrics); 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 // - Only recognize the depth of an argument if it refers to a non-auto parameter
Clause::Argument(own_id) => { Clause::LambdaArg(own_id) => {
let (pos, is_auto) = parametrics.iter() let pos = parametrics
.filter_map(|(id, is_auto)| is_auto.map(|is_auto| (*id, is_auto))) .and_then(|sf| sf.iter().position(|id| id == own_id))
.find_position(|(id, is_auto)| id == own_id) .unwrap_or(usize::MAX);
.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_u8(3);
state.write_usize(pos) state.write_usize(pos)
} }
}
// - Hash leaves like normal // - Hash leaves like normal
Clause::Literal(lit) => { state.write_u8(4); lit.hash(state) } Clause::Literal(lit) => { state.write_u8(4); lit.hash(state) }
Clause::Atom(at) => { state.write_u8(5); at.hash(state) } Clause::Atom(at) => { state.write_u8(5); at.hash(state) }

View File

@@ -17,12 +17,12 @@ pub fn skip_autos<'a,
) -> BoxedIter<'static, Mrc<Expr>> { ) -> BoxedIter<'static, Mrc<Expr>> {
if let Expr(Clause::Auto(id, arg, body), typ) = expr.as_ref() { if let Expr(Clause::Auto(id, arg, body), typ) = expr.as_ref() {
return Box::new(skip_autos(Mrc::clone(body), function).map({ return Box::new(skip_autos(Mrc::clone(body), function).map({
let arg = arg.as_ref().map(Mrc::clone); let arg = Mrc::clone(arg);
let typ = Mrc::clone(typ); let typ = Mrc::clone(typ);
move |body| { move |body| {
Mrc::new(Expr(Clause::Auto( Mrc::new(Expr(Clause::Auto(
*id, *id,
arg.as_ref().map(Mrc::clone), Mrc::clone(&arg),
body body
), Mrc::clone(&typ))) ), Mrc::clone(&typ)))
} }
@@ -50,7 +50,7 @@ fn direct_reductions(ex: Mrc<Expr>) -> impl Iterator<Item = Mrc<Expr>> {
.unwrap_or(box_empty()) .unwrap_or(box_empty())
}, },
// Parametric newtypes are atoms of function type // Parametric newtypes are atoms of function type
Clause::Atom(..) | Clause::Argument(..) | Clause::Apply(..) => box_empty(), Clause::Atom(..) | Clause::LambdaArg(..) | Clause::AutoArg(..) | Clause::Apply(..) => box_empty(),
Clause::Literal(lit) => Clause::Literal(lit) =>
panic!("Literal expression {lit:?} can't be applied as function"), panic!("Literal expression {lit:?} can't be applied as function"),
Clause::Auto(..) => unreachable!("skip_autos should have filtered this"), Clause::Auto(..) => unreachable!("skip_autos should have filtered this"),
@@ -76,11 +76,11 @@ fn direct_reductions(ex: Mrc<Expr>) -> impl Iterator<Item = Mrc<Expr>> {
Clause::Lambda(id, argt, body) => { Clause::Lambda(id, argt, body) => {
let id = *id; let id = *id;
let typ = Mrc::clone(typ_ref); let typ = Mrc::clone(typ_ref);
let argt = argt.as_ref().map(Mrc::clone); let argt = Mrc::clone(argt);
let body = Mrc::clone(body); let body = Mrc::clone(body);
let body_reductions = direct_reductions(body) let body_reductions = direct_reductions(body)
.map(move |body| { .map(move |body| {
let argt = argt.as_ref().map(Mrc::clone); let argt = Mrc::clone(&argt);
Mrc::new(Expr( Mrc::new(Expr(
Clause::Lambda(id, argt, body), Clause::Lambda(id, argt, body),
Mrc::clone(&typ) Mrc::clone(&typ)
@@ -88,9 +88,9 @@ fn direct_reductions(ex: Mrc<Expr>) -> impl Iterator<Item = Mrc<Expr>> {
}); });
Box::new(body_reductions) Box::new(body_reductions)
}, },
Clause::Literal(..) | Clause::ExternFn(..) | Clause::Atom(..) | Clause::Argument(..) =>
box_empty(),
Clause::Auto(..) => unreachable!("skip_autos should have filtered this"), Clause::Auto(..) => unreachable!("skip_autos should have filtered this"),
Clause::Literal(..) | Clause::ExternFn(..) | Clause::Atom(..)
| Clause::LambdaArg(..) | Clause::AutoArg(..) => box_empty(),
} }
}) })
} }

View File

@@ -1,13 +1,11 @@
use std::collections::HashMap; use std::collections::HashMap;
use std::hash::{Hasher, Hash};
use std::iter;
use itertools::Itertools;
use mappable_rc::Mrc; 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::representations::typed::{Clause, Expr};
use super::super::utils::Stackframe;
pub fn swap<T, U>((t, u): (T, U)) -> (U, T) { (u, t) } pub fn swap<T, U>((t, u): (T, U)) -> (U, T) { (u, t) }
@@ -17,23 +15,91 @@ pub fn swap<T, U>((t, u): (T, U)) -> (U, T) { (u, t) }
// - get rid of leftovers from Explicit // - get rid of leftovers from Explicit
// - adapt to new index-based system // - 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 /// 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. /// 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. /// The root depths are used to translate betwee de Brujin arguments and absolute indices.
struct Context(HashMap<u64, Mrc<Expr>>); struct Context(HashMap<u64, Mrc<Expr>>);
impl Context { impl Context {
fn set(&mut self, id: u64, value: Mrc<Expr>) { fn set(&mut self, id: u64, value: &Mrc<Expr>, lambdas: LambdaMap) -> Result<Option<Mrc<Expr>>, UnifError> {
// If already defined, then it must be an argument Ok(
if let Some(value) = self.0.get(&id) { if let Some(local) = self.0.get(&id) {
if let Clause::Argument(opposite_up) ex.0 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), )
}
})
} }
} }

104
src/foreign.rs Normal file
View File

@@ -0,0 +1,104 @@
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())
}
}

View File

@@ -2,16 +2,19 @@
#![feature(core_intrinsics)] #![feature(core_intrinsics)]
#![feature(adt_const_params)] #![feature(adt_const_params)]
#![feature(generic_const_exprs)] #![feature(generic_const_exprs)]
#![feature(generators, generator_trait)]
use std::env::current_dir; use std::env::current_dir;
mod executor; // mod executor;
mod parse; mod parse;
mod project; mod project;
mod utils; mod utils;
mod representations; mod representations;
mod rule; mod rule;
mod types; mod scheduler;
pub(crate) mod foreign;
use file_loader::LoadingError; use file_loader::LoadingError;
pub use representations::ast; pub use representations::ast;
use ast::{Expr, Clause}; use ast::{Expr, Clause};
@@ -19,7 +22,7 @@ use representations::typed as t;
use mappable_rc::Mrc; use mappable_rc::Mrc;
use project::{rule_collector, Loaded, file_loader}; use project::{rule_collector, Loaded, file_loader};
use rule::Repository; use rule::Repository;
use utils::to_mrc_slice; use utils::{to_mrc_slice, mrc_empty_slice, one_mrc_slice};
fn literal(orig: &[&str]) -> Mrc<[String]> { fn literal(orig: &[&str]) -> Mrc<[String]> {
to_mrc_slice(vliteral(orig)) to_mrc_slice(vliteral(orig))
@@ -48,17 +51,17 @@ fn initial_tree() -> Mrc<[Expr]> {
#[allow(unused)] #[allow(unused)]
fn typed_notation_debug() { fn typed_notation_debug() {
let true_ex = t::Clause::Auto(0, None, let true_ex = t::Clause::Auto(0, mrc_empty_slice(),
t::Clause::Lambda(1, Some(Mrc::new(t::Clause::Argument(0))), t::Clause::Lambda(1, one_mrc_slice(t::Clause::AutoArg(0)),
t::Clause::Lambda(2, Some(Mrc::new(t::Clause::Argument(0))), t::Clause::Lambda(2, one_mrc_slice(t::Clause::AutoArg(0)),
t::Clause::Argument(1).wrap_t(t::Clause::Argument(0)) t::Clause::LambdaArg(1).wrap_t(t::Clause::AutoArg(0))
).wrap() ).wrap()
).wrap() ).wrap()
).wrap(); ).wrap();
let false_ex = t::Clause::Auto(0, None, let false_ex = t::Clause::Auto(0, mrc_empty_slice(),
t::Clause::Lambda(1, Some(Mrc::new(t::Clause::Argument(0))), t::Clause::Lambda(1, one_mrc_slice(t::Clause::AutoArg(0)),
t::Clause::Lambda(2, Some(Mrc::new(t::Clause::Argument(0))), t::Clause::Lambda(2, one_mrc_slice(t::Clause::AutoArg(0)),
t::Clause::Argument(2).wrap_t(t::Clause::Argument(0)) t::Clause::LambdaArg(2).wrap_t(t::Clause::AutoArg(0))
).wrap() ).wrap()
).wrap() ).wrap()
).wrap(); ).wrap();

View File

@@ -4,7 +4,7 @@ use ordered_float::NotNan;
use std::{hash::Hash, intrinsics::likely}; use std::{hash::Hash, intrinsics::likely};
use std::fmt::Debug; use std::fmt::Debug;
use crate::utils::mrc_empty_slice; use crate::utils::mrc_empty_slice;
use crate::{executor::{ExternFn, Atom}, utils::one_mrc_slice}; use crate::{foreign::{ExternFn, Atom}, utils::one_mrc_slice};
use super::Literal; use super::Literal;

View File

@@ -1,6 +1,6 @@
use mappable_rc::Mrc; use mappable_rc::Mrc;
use crate::utils::{Stackframe, to_mrc_slice, mrc_empty_slice, ProtoMap}; use crate::utils::{Stackframe, to_mrc_slice, mrc_empty_slice, ProtoMap, one_mrc_slice};
use super::{ast, typed, get_name::get_name}; use super::{ast, typed, get_name::get_name};
@@ -49,9 +49,9 @@ pub fn exprv(exprv: &[ast::Expr]) -> Result<typed::Expr, Error> {
const NAMES_INLINE_COUNT:usize = 3; const NAMES_INLINE_COUNT:usize = 3;
/// Recursive state of [exprv] /// Recursive state of [exprv]
fn exprv_rec<'a>( fn exprv_rec(
v: &'a [ast::Expr], v: &[ast::Expr],
names: ProtoMap<&'a str, u64, NAMES_INLINE_COUNT>, names: ProtoMap<&str, (u64, bool), NAMES_INLINE_COUNT>,
explicits: Option<&Stackframe<Mrc<typed::Expr>>>, explicits: Option<&Stackframe<Mrc<typed::Expr>>>,
) -> Result<(typed::Expr, usize), Error> { ) -> Result<(typed::Expr, usize), Error> {
let (last, rest) = v.split_last().ok_or(Error::EmptyS)?; let (last, rest) = v.split_last().ok_or(Error::EmptyS)?;
@@ -61,12 +61,12 @@ fn exprv_rec<'a>(
"It is assumed that Explicit nodes can never have type annotations as the \ "It is assumed that Explicit nodes can never have type annotations as the \
wrapped expression node matches all trailing colons." wrapped expression node matches all trailing colons."
); );
let (x, _) = expr_rec(inner.as_ref(), names.clone(), None)?; let (x, _) = expr_rec(inner.as_ref(), names, None)?;
let new_explicits = Stackframe::opush(explicits, Mrc::new(x)); let new_explicits = Some(&Stackframe::opush(explicits, Mrc::new(x)));
let (body, used_expls) = exprv_rec(rest, names, Some(&new_explicits))?; let (body, used_expls) = exprv_rec(rest, names, new_explicits)?;
Ok((body, used_expls.saturating_sub(1))) Ok((body, used_expls.saturating_sub(1)))
} else { } else {
let (f, f_used_expls) = exprv_rec(rest, names.clone(), explicits)?; let (f, f_used_expls) = exprv_rec(rest, names, explicits)?;
let x_explicits = Stackframe::opop(explicits, f_used_expls); let x_explicits = Stackframe::opop(explicits, f_used_expls);
let (x, x_used_expls) = expr_rec(last, names, x_explicits)?; let (x, x_used_expls) = expr_rec(last, names, x_explicits)?;
Ok((typed::Expr( Ok((typed::Expr(
@@ -77,19 +77,17 @@ fn exprv_rec<'a>(
} }
/// Recursive state of [expr] /// Recursive state of [expr]
fn expr_rec<'a>( fn expr_rec(
ast::Expr(val, typ): &'a ast::Expr, ast::Expr(val, typ): &ast::Expr,
names: ProtoMap<&'a str, u64, NAMES_INLINE_COUNT>, names: ProtoMap<&str, (u64, bool), NAMES_INLINE_COUNT>,
explicits: Option<&Stackframe<Mrc<typed::Expr>>> // known explicit values explicits: Option<&Stackframe<Mrc<typed::Expr>>> // known explicit values
) -> Result<(typed::Expr, usize), Error> { // (output, used_explicits) ) -> Result<(typed::Expr, usize), Error> { // (output, used_explicits)
let typ: Vec<typed::Clause> = typ.iter() let typ: Vec<typed::Clause> = typ.iter()
.map(|c| Ok(clause_rec(c, names.clone(), None)?.0)) .map(|c| Ok(clause_rec(c, names, None)?.0))
.collect::<Result<_, _>>()?; .collect::<Result<_, _>>()?;
if let ast::Clause::S(paren, body) = val { if let ast::Clause::S(paren, body) = val {
if *paren != '(' {return Err(Error::BadGroup(*paren))} if *paren != '(' {return Err(Error::BadGroup(*paren))}
let (typed::Expr(inner, inner_t), used_expls) = exprv_rec( let (typed::Expr(inner, inner_t), used_expls) = exprv_rec(body.as_ref(), names, explicits)?;
body.as_ref(), names, explicits
)?;
let new_t = if typ.len() == 0 { inner_t } else { let new_t = if typ.len() == 0 { inner_t } else {
to_mrc_slice(if inner_t.len() == 0 { typ } else { to_mrc_slice(if inner_t.len() == 0 { typ } else {
inner_t.iter().chain(typ.iter()).cloned().collect() inner_t.iter().chain(typ.iter()).cloned().collect()
@@ -103,9 +101,9 @@ fn expr_rec<'a>(
} }
/// Recursive state of [clause] /// Recursive state of [clause]
fn clause_rec<'a>( fn clause_rec(
cls: &'a ast::Clause, cls: &ast::Clause,
mut names: ProtoMap<&'a str, u64, NAMES_INLINE_COUNT>, names: ProtoMap<&str, (u64, bool), NAMES_INLINE_COUNT>,
mut explicits: Option<&Stackframe<Mrc<typed::Expr>>> mut explicits: Option<&Stackframe<Mrc<typed::Expr>>>
) -> Result<(typed::Clause, usize), Error> { ) -> Result<(typed::Clause, usize), Error> {
match cls { // (\t:(@T. Pair T T). t \left.\right. left) @number -- this will fail match cls { // (\t:(@T. Pair T T). t \left.\right. left) @number -- this will fail
@@ -122,18 +120,14 @@ fn clause_rec<'a>(
).unwrap_or_default(); ).unwrap_or_default();
explicits = rest_explicits; explicits = rest_explicits;
// Convert the type // Convert the type
let typ = if t.len() == 0 {None} else { let typ = if t.len() == 0 {mrc_empty_slice()} else {
let (typed::Expr(c, t), _) = exprv_rec( let (typed::Expr(c, t), _) = exprv_rec(t.as_ref(), names, None)?;
t.as_ref(), names.clone(), None
)?;
if t.len() > 0 {return Err(Error::ExplicitBottomKind)} if t.len() > 0 {return Err(Error::ExplicitBottomKind)}
else {Some(Mrc::new(c))} else {one_mrc_slice(c)}
}; };
// Traverse body with extended context // Traverse body with extended context
if let Some(name) = no {names.set(&&**name, id)} if let Some(name) = no {names.set(&&**name, (id, true))}
let (body, used_expls) = exprv_rec( let (body, used_expls) = exprv_rec(b.as_ref(), names, explicits)?;
b.as_ref(), names, explicits
)?;
// Produce a binding instead of an auto if explicit was available // Produce a binding instead of an auto if explicit was available
if let Some(known_value) = value { if let Some(known_value) = value {
Ok((typed::Clause::Apply( Ok((typed::Clause::Apply(
@@ -148,26 +142,25 @@ fn clause_rec<'a>(
// Allocate id // Allocate id
let id = get_name(); let id = get_name();
// Convert the type // Convert the type
let typ = if t.len() == 0 {None} else { let typ = if t.len() == 0 {mrc_empty_slice()} else {
let (typed::Expr(c, t), _) = exprv_rec(t.as_ref(), names.clone(), None)?; let (typed::Expr(c, t), _) = exprv_rec(t.as_ref(), names, None)?;
if t.len() > 0 {return Err(Error::ExplicitBottomKind)} if t.len() > 0 {return Err(Error::ExplicitBottomKind)}
else {Some(Mrc::new(c))} else {one_mrc_slice(c)}
}; };
names.set(&&**n, id); names.set(&&**n, (id, false));
let (body, used_expls) = exprv_rec(b.as_ref(), names, explicits)?; let (body, used_expls) = exprv_rec(b.as_ref(), names, explicits)?;
Ok((typed::Clause::Lambda(id, typ, Mrc::new(body)), used_expls)) Ok((typed::Clause::Lambda(id, typ, Mrc::new(body)), used_expls))
} }
ast::Clause::Literal(l) => Ok((typed::Clause::Literal(l.clone()), 0)), ast::Clause::Literal(l) => Ok((typed::Clause::Literal(l.clone()), 0)),
ast::Clause::Name { local: Some(arg), .. } => { ast::Clause::Name { local: Some(arg), .. } => {
let uid = names.get(&&**arg) let (uid, is_auto) = names.get(&&**arg)
.ok_or_else(|| Error::Unbound(arg.clone()))?; .ok_or_else(|| Error::Unbound(arg.clone()))?;
Ok((typed::Clause::Argument(*uid), 0)) let label = if *is_auto {typed::Clause::AutoArg} else {typed::Clause::LambdaArg};
Ok((label(*uid), 0))
} }
ast::Clause::S(paren, entries) => { ast::Clause::S(paren, entries) => {
if *paren != '(' {return Err(Error::BadGroup(*paren))} if *paren != '(' {return Err(Error::BadGroup(*paren))}
let (typed::Expr(val, typ), used_expls) = exprv_rec( let (typed::Expr(val, typ), used_expls) = exprv_rec(entries.as_ref(), names, explicits)?;
entries.as_ref(), names, explicits
)?;
if typ.len() == 0 {Ok((val, used_expls))} if typ.len() == 0 {Ok((val, used_expls))}
else {Err(Error::ExprToClause(typed::Expr(val, typ)))} else {Err(Error::ExprToClause(typed::Expr(val, typ)))}
}, },

View File

@@ -1,7 +1,7 @@
use mappable_rc::Mrc; use mappable_rc::Mrc;
use crate::executor::Atom; use crate::foreign::{Atom, ExternFn};
use crate::utils::{to_mrc_slice, one_mrc_slice}; use crate::utils::{to_mrc_slice, one_mrc_slice};
use crate::{executor::ExternFn, utils::string_from_charset}; use crate::utils::string_from_charset;
use super::{Literal, ast_to_typed}; use super::{Literal, ast_to_typed};
use super::ast; use super::ast;
@@ -47,9 +47,9 @@ impl Debug for Expr {
pub enum Clause { pub enum Clause {
Literal(Literal), Literal(Literal),
Apply(Mrc<Expr>, Mrc<Expr>), Apply(Mrc<Expr>, Mrc<Expr>),
Lambda(u64, Option<Mrc<Clause>>, Mrc<Expr>), Lambda(u64, Mrc<[Clause]>, Mrc<Expr>),
Auto(u64, Option<Mrc<Clause>>, Mrc<Expr>), Auto(u64, Mrc<[Clause]>, Mrc<Expr>),
Argument(u64), LambdaArg(u64), AutoArg(u64),
ExternFn(ExternFn), ExternFn(ExternFn),
Atom(Atom) Atom(Atom)
} }
@@ -58,12 +58,12 @@ const ARGNAME_CHARSET: &str = "abcdefghijklmnopqrstuvwxyz";
fn parametric_fmt( fn parametric_fmt(
f: &mut std::fmt::Formatter<'_>, f: &mut std::fmt::Formatter<'_>,
prefix: &str, argtyp: Option<Mrc<Clause>>, body: Mrc<Expr>, uid: u64, wrap_right: bool prefix: &str, argtyp: Mrc<[Clause]>, body: Mrc<Expr>, uid: u64, wrap_right: bool
) -> std::fmt::Result { ) -> std::fmt::Result {
if wrap_right { f.write_char('(')?; } if wrap_right { f.write_char('(')?; }
f.write_str(prefix)?; f.write_str(prefix)?;
f.write_str(&string_from_charset(uid, ARGNAME_CHARSET))?; f.write_str(&string_from_charset(uid, ARGNAME_CHARSET))?;
if let Some(typ) = argtyp { for typ in argtyp.iter() {
f.write_str(":")?; f.write_str(":")?;
typ.deep_fmt(f, Wrap(false, false))?; typ.deep_fmt(f, Wrap(false, false))?;
} }
@@ -81,12 +81,14 @@ impl Clause {
Self::ExternFn(nc) => write!(f, "{nc:?}"), Self::ExternFn(nc) => write!(f, "{nc:?}"),
Self::Atom(a) => write!(f, "{a:?}"), Self::Atom(a) => write!(f, "{a:?}"),
Self::Lambda(uid, argtyp, body) => parametric_fmt(f, Self::Lambda(uid, argtyp, body) => parametric_fmt(f,
"\\", argtyp.as_ref().map(Mrc::clone), Mrc::clone(body), *uid, wr "\\", Mrc::clone(argtyp), Mrc::clone(body), *uid, wr
), ),
Self::Auto(uid, argtyp, body) => parametric_fmt(f, Self::Auto(uid, argtyp, body) => parametric_fmt(f,
"@", argtyp.as_ref().map(Mrc::clone), Mrc::clone(body), *uid, wr "@", Mrc::clone(argtyp), Mrc::clone(body), *uid, wr
),
Self::LambdaArg(uid) | Self::AutoArg(uid) => f.write_str(&
string_from_charset(*uid, ARGNAME_CHARSET)
), ),
Self::Argument(uid) => f.write_str(&string_from_charset(*uid, ARGNAME_CHARSET)),
Self::Apply(func, x) => { Self::Apply(func, x) => {
if wl { f.write_char('(')?; } if wl { f.write_char('(')?; }
func.deep_fmt(f, Wrap(false, true) )?; func.deep_fmt(f, Wrap(false, true) )?;
@@ -104,13 +106,14 @@ impl Clause {
impl Clone for Clause { impl Clone for Clause {
fn clone(&self) -> Self { fn clone(&self) -> Self {
match self { match self {
Clause::Auto(uid,t, b) => Clause::Auto(*uid, t.as_ref().map(Mrc::clone), Mrc::clone(b)), Clause::Auto(uid,t, b) => Clause::Auto(*uid, Mrc::clone(t), Mrc::clone(b)),
Clause::Lambda(uid, t, b) => Clause::Lambda(*uid, t.as_ref().map(Mrc::clone), Mrc::clone(b)), Clause::Lambda(uid, t, b) => Clause::Lambda(*uid, Mrc::clone(t), Mrc::clone(b)),
Clause::Literal(l) => Clause::Literal(l.clone()), Clause::Literal(l) => Clause::Literal(l.clone()),
Clause::ExternFn(nc) => Clause::ExternFn(nc.clone()), Clause::ExternFn(nc) => Clause::ExternFn(nc.clone()),
Clause::Atom(a) => Clause::Atom(a.clone()), Clause::Atom(a) => Clause::Atom(a.clone()),
Clause::Apply(f, x) => Clause::Apply(Mrc::clone(f), Mrc::clone(x)), Clause::Apply(f, x) => Clause::Apply(Mrc::clone(f), Mrc::clone(x)),
Clause::Argument(lvl) => Clause::Argument(*lvl) Clause::LambdaArg(id) => Clause::LambdaArg(*id),
Clause::AutoArg(id) => Clause::AutoArg(*id)
} }
} }
} }

View File

@@ -0,0 +1,51 @@
use std::{ops::{Generator, GeneratorState}, pin::Pin};
use super::{Task, Nice, TaskState};
pub struct GeneratorTask<G: Generator<(), Yield = ()>> {
nice: Nice,
generator: Pin<Box<G>>
}
impl<G> GeneratorTask<G> where G: Generator<(), Yield = ()> {
fn new(nice: Nice, generator: G) -> Self { Self {
nice,
generator: Box::pin(generator)
} }
}
impl<G> Task for GeneratorTask<G>
where G: Generator<(), Yield = ()> {
type Result = G::Return;
fn run_once(&mut self) -> super::TaskState<Self::Result> {
match self.generator.as_mut().resume(()) {
GeneratorState::Yielded(()) => super::TaskState::Yield,
GeneratorState::Complete(r) => super::TaskState::Complete(r)
}
}
}
impl<T> Task for Pin<Box<T>> where T: Generator<(), Yield = ()> {
type Result = T::Return;
fn run_once(&mut self) -> super::TaskState<Self::Result> {
match self.as_mut().resume(()) {
GeneratorState::Yielded(()) => TaskState::Yield,
GeneratorState::Complete(r) => TaskState::Complete(r)
}
}
}
#[macro_export]
macro_rules! subtask {
($g:tt) => { {
let task = $g;
loop {
match task.run_once() {
TaskState::Yield => yield;
TaskState::Complete(r) => break r;
}
}
} };
}

47
src/scheduler/mod.rs Normal file
View File

@@ -0,0 +1,47 @@
mod generator_task;
mod task_pair;
mod task_vec;
pub type Nice = u16;
pub type Priority = i32;
pub enum TaskState<R> {
Yield,
Complete(R)
}
pub trait Task {
type Result;
fn run_once(&mut self) -> TaskState<Self::Result>;
fn run_n_times(&mut self, count: u64) -> TaskState<Self::Result> {
for _ in 0..count {
if let r@TaskState::Complete(_) = self.run_once() {
return r
}
}
return TaskState::Yield
}
fn run_to_completion(&mut self) -> Self::Result {
loop { if let TaskState::Complete(r) = self.run_once() {return r} }
}
fn boxed<'a>(self) -> TaskBox<'a, Self::Result> where Self: 'a + Sized { Box::new(self) }
}
pub type TaskBox<'a, T> = Box<dyn Task<Result = T> + 'a>;
impl<'a, R> Task for TaskBox<'a, R> {
type Result = R;
fn run_once(&mut self) -> TaskState<Self::Result> { self.as_mut().run_once() }
fn run_n_times(&mut self, count: u64) -> TaskState<Self::Result> {
self.as_mut().run_n_times(count)
}
fn run_to_completion(&mut self) -> Self::Result {
self.as_mut().run_to_completion()
}
}

3
src/scheduler/notes.md Normal file
View File

@@ -0,0 +1,3 @@
# Purpose
Type expressions are trees. Any single branch could terminate the solver and any branch may be nonterminating, therefore all of them must be run concurrently. Thread-based concurrency isn't an option because a compiler must be perfectly deterministic. It is also beneficial to have fine-grained control over the relative priority of different tasks.

View File

@@ -0,0 +1,67 @@
use crate::utils::translate::process;
use super::{Task, Nice, Priority, TaskState};
enum TaskPairState<T: Task, U: Task> {
Empty,
Left(T, U::Result),
Right(T::Result, U),
Both(T, U)
}
pub struct TaskPair<T: Task, U: Task> {
l_nice: Nice,
r_nice: Nice,
state: TaskPairState<T, U>,
tally: Priority,
}
impl<T: Task, U: Task> TaskPair<T, U> {
pub fn new(l_nice: Nice, left: T, r_nice: Nice, right: U) -> Self {
Self {
l_nice, r_nice,
tally: 0,
state: TaskPairState::Both(left, right)
}
}
}
impl<T: Task, U: Task> Task for TaskPair<T, U> {
type Result = (T::Result, U::Result);
fn run_once(&mut self) -> TaskState<Self::Result> {
let TaskPair{ state, tally, l_nice, r_nice } = self;
let ret = process(state, |s| match s {
TaskPairState::Empty => panic!("Generator completed and empty"),
TaskPairState::Left(mut l_task, r_res) => {
match l_task.run_once() {
TaskState::Complete(r) => (TaskPairState::Empty, TaskState::Complete((r, r_res))),
TaskState::Yield => (TaskPairState::Left(l_task, r_res), TaskState::Yield),
}
}
TaskPairState::Right(l_res, mut r_task) => {
match r_task.run_once() {
TaskState::Complete(r) => (TaskPairState::Empty, TaskState::Complete((l_res, r))),
TaskState::Yield => (TaskPairState::Right(l_res, r_task), TaskState::Yield),
}
}
TaskPairState::Both(mut l_task, mut r_task) => {
let state = if 0 <= *tally {
*tally -= *l_nice as Priority;
match l_task.run_once() {
TaskState::Complete(r) => TaskPairState::Right(r, r_task),
TaskState::Yield => TaskPairState::Both(l_task, r_task),
}
} else {
*tally += *r_nice as Priority;
match r_task.run_once() {
TaskState::Complete(r) => TaskPairState::Left(l_task, r),
TaskState::Yield => TaskPairState::Both(l_task, r_task),
}
};
(state, TaskState::Yield)
}
});
ret
}
}

107
src/scheduler/task_vec.rs Normal file
View File

@@ -0,0 +1,107 @@
use std::iter;
use itertools::Itertools;
use super::{Task, Nice, TaskState};
const NORMALIZATION_THRESHOLD:Nice = Nice::MAX / 4;
struct TaskEntry<T: Task> {
nice: Nice,
position: usize,
tally: Nice,
task: T
}
struct TaskVec<T: Task> {
results: Vec<Option<T::Result>>,
task_heap: Vec<Option<TaskEntry<T>>>,
}
impl<T: Task> TaskVec<T> {
pub fn new(tasks: Vec<(Nice, T)>) -> Self {
let mut results = Vec::with_capacity(tasks.len());
results.resize_with(tasks.len(), || None);
let task_heap = tasks.into_iter().enumerate()
.map(|(position, (nice, task))| Some(TaskEntry{ nice, task, position, tally: 1 }))
.collect_vec();
Self { results, task_heap }
}
fn entry(&self, i: usize) -> Option<&TaskEntry<T>> {
if self.task_heap.len() <= i {None}
else {self.task_heap[i].as_ref()}
}
fn entry_mut(&mut self, i: usize) -> Option<&mut TaskEntry<T>> {
if self.task_heap.len() <= i {None}
else {self.task_heap[i].as_mut()}
}
fn tally(&self, i: usize) -> Nice {
self.task_heap[i].as_ref().map(|e| e.tally).unwrap_or(0)
}
fn swap(&mut self, a: usize, b: usize) {
self.task_heap.swap(a, b);
}
fn iter_mut(&mut self) -> impl Iterator<Item = &mut TaskEntry<T>> {
self.task_heap.iter_mut().filter_map(|e| e.as_mut())
}
fn normalize(&mut self) {
let shrink_count = self.task_heap.iter().rev().take_while(|e| e.is_none()).count();
let new_len = self.task_heap.len() - shrink_count;
self.task_heap.splice(0..new_len, iter::empty());
let head = self.entry_mut(0);
let offset = if let Some(e) = head {
let offset = e.tally - 1;
if offset < NORMALIZATION_THRESHOLD {return}
e.tally = 1;
offset
} else {return};
for entry in self.iter_mut() { entry.tally -= offset }
}
fn sink(&mut self, i: usize) {
let lchi = 2*i + 1;
let rchi = 2*i + 2;
let t = self.tally(i);
let lcht = if let Some(e) = self.entry(lchi) {e.tally} else {
if self.tally(rchi) < t {
self.swap(rchi, i);
self.sink(rchi);
}
return
};
let rcht = if let Some(e) = self.entry(rchi) {e.tally} else {
if self.tally(lchi) < t {
self.swap(lchi, i);
self.sink(lchi);
}
return
};
let mchi = {
if rcht < t && rcht < lcht {rchi}
else if lcht < t && lcht < rcht {lchi}
else {return}
};
self.swap(i, mchi);
self.sink(mchi);
}
}
impl<T: Task> Task for TaskVec<T> {
fn run_once(&mut self) -> super::TaskState<Self::Result> {
let head = &mut self.task_heap[0];
let head_entry = head.as_mut().expect("All completed, cannot run further");
head_entry.tally += head_entry.nice;
match head_entry.task.run_once() {
TaskState::Complete(r) => {
self.results[head_entry.position] = Some(r);
*head = None;
self.sink(0);
if self.entry(0).is_some() {
}
}
}
}
}

View File

@@ -1,52 +0,0 @@
use std::{borrow::Borrow};
use std::hash::Hash;
use hashbrown::HashMap;
use mappable_rc::Mrc;
use crate::{ast::{Expr, Clause}, utils::mrc_to_iter};
pub struct Substitution(HashMap<String, Mrc<Expr>>);
impl Substitution {
fn new() -> Self { Self(HashMap::new()) }
fn apply<Q: ?Sized + Hash + Eq>(&self, q: &Q) -> Option<Mrc<Expr>>
where String: Borrow<Q> {
self.0.get(q).map(Mrc::clone)
}
}
pub fn hindley_milner(a: Mrc<[Expr]>, b: Mrc<[Expr]>) -> Result<Substitution, ()> {
hindley_milner_rec(Substitution::new(), a, b)
}
pub fn hindley_milner_rec(mut s: Substitution, a: Mrc<[Expr]>, b: Mrc<[Expr]>)
-> Result<Substitution, ()> {
if a.len() != b.len() {return Err(())}
for (mut a, mut b) in mrc_to_iter(a).zip(mrc_to_iter(b)) {
if let Clause::Placeh{key, ..} = &a.0 {
if let Some(ex) = s.apply(key) { a = ex }
}
if let Clause::Placeh{key, ..} = &b.0 {
if let Some(ex) = s.apply(key) { b = ex }
}
if !matches!(&a.0, Clause::Placeh{..}) { (a, b) = (b, a) }
match (&a.0, &b.0) {
(Clause::Placeh{key:a_key,..}, Clause::Placeh{key:b_key,..}) =>
if a_key == b_key {return Ok(s)},
_ => return Err(())
}
if let (Clause::Placeh{key: a_key,..}, Clause::Placeh{key: b_key,..}) = (&a.0, &b.0) {
if a_key == b_key {return Ok(s)}
} else if let (Clause::S(_, a_body), Clause::S(_, b_body)) = (&a.0, &b.0) {
s = hindley_milner_rec(s, Mrc::clone(a_body), Mrc::clone(b_body))?
} else if let ()
}
Ok(s)
}
pub fn occurs(key: &str, val: &Expr) -> bool {
match val.0 {
Clause::Auto(_, _, body) => body.
}
}

View File

@@ -1,13 +0,0 @@
// mod hindley_milner;
#[derive(Clone, Hash, PartialEq, Eq)]
pub enum Expression<L, V, O, F> {
Literal(L),
Variable(V),
Operation(O, Vec<Expression<L, V, O, F>>),
Lazy(F)
}
pub struct Rule {
}

View File

View File

@@ -1,23 +1,26 @@
mod cache; mod cache;
pub mod translate;
pub use cache::Cache;
mod substack; mod substack;
pub use substack::Stackframe;
mod side; mod side;
pub use side::Side;
mod merge_sorted; mod merge_sorted;
pub use merge_sorted::merge_sorted;
mod unwrap_or; mod unwrap_or;
pub mod iter; pub mod iter;
pub use iter::BoxedIter;
mod bfs; mod bfs;
mod unless_let; mod unless_let;
mod string_from_charset; mod string_from_charset;
pub use string_from_charset::string_from_charset;
mod for_loop; mod for_loop;
mod protomap; mod protomap;
pub use cache::Cache;
use mappable_rc::Mrc;
pub use substack::Stackframe;
pub use side::Side;
pub use merge_sorted::merge_sorted;
pub use iter::BoxedIter;
pub use string_from_charset::string_from_charset;
pub use protomap::ProtoMap; pub use protomap::ProtoMap;
mod product2;
pub use product2::Product2;
use mappable_rc::Mrc;
pub fn mrc_derive<T: ?Sized, P, U: ?Sized>(m: &Mrc<T>, p: P) -> Mrc<U> pub fn mrc_derive<T: ?Sized, P, U: ?Sized>(m: &Mrc<T>, p: P) -> Mrc<U>
where P: for<'a> FnOnce(&'a T) -> &'a U { where P: for<'a> FnOnce(&'a T) -> &'a U {
@@ -76,3 +79,7 @@ pub fn mrc_slice_to_only_option<T>(m: Mrc<[T]>) -> Result<Option<Mrc<T>>, ()> {
else {Some(&slice[0])} else {Some(&slice[0])}
}).ok()) }).ok())
} }
pub fn mrc_concat<T: Clone>(a: &Mrc<[T]>, b: &Mrc<[T]>) -> Mrc<[T]> {
collect_to_mrc(a.iter().chain(b.iter()).cloned())
}

53
src/utils/product2.rs Normal file
View File

@@ -0,0 +1,53 @@
use super::Side;
/// The output of a two-part algorithm. The values are
///
/// - [Product2::Left] or [Product2::Right] if one of the arguments is the product
/// - [Product2::Either] if the arguments are identical
/// - [Product2::New] if the product is a different value from either
pub enum Product2<T> {
Left,
Right,
Either,
New(T)
}
impl<T> Product2<T> {
/// Convert the product into a concrete value by providing the original arguments
pub fn pick(self, left: T, right: T) -> T {
match self {
Self::Left | Self::Either => left,
Self::Right => right,
Self::New(t) => t
}
}
/// Combine some subresults into a tuple representing a greater result
pub fn join<U>(
self, (lt, rt): (T, T),
second: Product2<U>, (lu, ru): (U, U)
) -> Product2<(T, U)> {
match (self, second) {
(Self::Either, Product2::Either) => Product2::Either,
(Self::Left | Self::Either, Product2::Left | Product2::Either) => Product2::Left,
(Self::Right | Self::Either, Product2::Right | Product2::Either) => Product2::Right,
(t, u) => Product2::New((t.pick(lt, rt), u.pick(lu, ru)))
}
}
/// Translate results back into the type of the original problem.
pub fn map<A, F: FnOnce(T) -> A>(self, f: F) -> Product2<A> {
match self {
Product2::Left => Product2::Left, Product2::Right => Product2::Right,
Product2::Either => Product2::Either,
Product2::New(t) => Product2::New(f(t))
}
}
}
/// Technically very different but sometimes neecessary to translate
impl<T> From<Side> for Product2<T> {
fn from(value: Side) -> Self {match value {
Side::Left => Self::Left,
Side::Right => Self::Right
}}
}

View File

@@ -49,6 +49,9 @@ impl<'a, T: 'a> Stackframe<'a, T> {
if count == 0 {cur} if count == 0 {cur}
else {Self::opop(cur.expect("Index out of range").prev, count - 1)} else {Self::opop(cur.expect("Index out of range").prev, count - 1)}
} }
pub fn o_into_iter(curr: Option<&Self>) -> StackframeIterator<T> {
StackframeIterator { curr }
}
} }
impl<'a, T> Debug for Stackframe<'a, T> where T: Debug { impl<'a, T> Debug for Stackframe<'a, T> where T: Debug {
@@ -62,6 +65,17 @@ pub struct StackframeIterator<'a, T> {
curr: Option<&'a Stackframe<'a, T>> curr: Option<&'a Stackframe<'a, T>>
} }
impl<'a, T> StackframeIterator<'a, T> {
pub fn first_some<U, F: Fn(&T) -> Option<U>>(&mut self, f: F) -> Option<U> {
while let Some(x) = self.next() {
if let Some(result) = f(x) {
return Some(result)
}
}
None
}
}
impl<'a, T> Iterator for StackframeIterator<'a, T> { impl<'a, T> Iterator for StackframeIterator<'a, T> {
type Item = &'a T; type Item = &'a T;
fn next(&mut self) -> Option<&'a T> { fn next(&mut self) -> Option<&'a T> {

22
src/utils/translate.rs Normal file
View File

@@ -0,0 +1,22 @@
use std::mem;
pub fn translate<T, F: FnOnce(T) -> T>(data: &mut T, f: F) {
unsafe {
let mut acc = mem::MaybeUninit::<T>::uninit().assume_init();
mem::swap(&mut acc, data);
let mut new = f(acc);
mem::swap(&mut new, data);
mem::forget(new);
}
}
pub fn process<T, U, F: FnOnce(T) -> (T, U)>(data: &mut T, f: F) -> U {
unsafe {
let mut acc = mem::MaybeUninit::<T>::uninit().assume_init();
mem::swap(&mut acc, data);
let (mut new, ret) = f(acc);
mem::swap(&mut new, data);
mem::forget(new);
ret
}
}