transfer commit
This commit is contained in:
@@ -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
2
rust-toolchain.toml
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
[toolchain]
|
||||||
|
channel = "nightly"
|
||||||
@@ -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))}
|
||||||
}
|
}
|
||||||
@@ -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())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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) }
|
||||||
|
|||||||
@@ -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(),
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
104
src/foreign.rs
Normal 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())
|
||||||
|
}
|
||||||
|
}
|
||||||
25
src/main.rs
25
src/main.rs
@@ -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();
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -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};
|
use super::{ast, typed};
|
||||||
|
|
||||||
@@ -51,7 +51,7 @@ const NAMES_INLINE_COUNT:usize = 3;
|
|||||||
/// Recursive state of [exprv]
|
/// Recursive state of [exprv]
|
||||||
fn exprv_rec(
|
fn exprv_rec(
|
||||||
v: &[ast::Expr],
|
v: &[ast::Expr],
|
||||||
names: ProtoMap<&str, u64, NAMES_INLINE_COUNT>,
|
names: ProtoMap<&str, (u64, bool), NAMES_INLINE_COUNT>,
|
||||||
next_id: &mut u64,
|
next_id: &mut u64,
|
||||||
explicits: Option<&Stackframe<Mrc<typed::Expr>>>,
|
explicits: Option<&Stackframe<Mrc<typed::Expr>>>,
|
||||||
) -> Result<(typed::Expr, usize), Error> {
|
) -> Result<(typed::Expr, usize), Error> {
|
||||||
@@ -80,7 +80,7 @@ fn exprv_rec(
|
|||||||
/// Recursive state of [expr]
|
/// Recursive state of [expr]
|
||||||
fn expr_rec(
|
fn expr_rec(
|
||||||
ast::Expr(val, typ): &ast::Expr,
|
ast::Expr(val, typ): &ast::Expr,
|
||||||
names: ProtoMap<&str, u64, NAMES_INLINE_COUNT>,
|
names: ProtoMap<&str, (u64, bool), NAMES_INLINE_COUNT>,
|
||||||
next_id: &mut u64,
|
next_id: &mut u64,
|
||||||
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)
|
||||||
@@ -107,7 +107,7 @@ fn expr_rec(
|
|||||||
/// Recursive state of [clause]
|
/// Recursive state of [clause]
|
||||||
fn clause_rec(
|
fn clause_rec(
|
||||||
cls: &ast::Clause,
|
cls: &ast::Clause,
|
||||||
names: ProtoMap<&str, u64, NAMES_INLINE_COUNT>,
|
names: ProtoMap<&str, (u64, bool), NAMES_INLINE_COUNT>,
|
||||||
next_id: &mut u64,
|
next_id: &mut u64,
|
||||||
mut explicits: Option<&Stackframe<Mrc<typed::Expr>>>
|
mut explicits: Option<&Stackframe<Mrc<typed::Expr>>>
|
||||||
) -> Result<(typed::Clause, usize), Error> {
|
) -> Result<(typed::Clause, usize), Error> {
|
||||||
@@ -126,15 +126,15 @@ fn clause_rec(
|
|||||||
).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, next_id, None
|
t.as_ref(), names, next_id, 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, next_id, explicits
|
b.as_ref(), names, next_id, explicits
|
||||||
)?;
|
)?;
|
||||||
@@ -153,14 +153,14 @@ fn clause_rec(
|
|||||||
let id = *next_id;
|
let id = *next_id;
|
||||||
*next_id += 1;
|
*next_id += 1;
|
||||||
// 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, next_id, None
|
t.as_ref(), names, next_id, 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(
|
let (body, used_expls) = exprv_rec(
|
||||||
b.as_ref(), names, next_id, explicits
|
b.as_ref(), names, next_id, explicits
|
||||||
)?;
|
)?;
|
||||||
@@ -168,9 +168,10 @@ fn clause_rec(
|
|||||||
}
|
}
|
||||||
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))}
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
51
src/scheduler/generator_task.rs
Normal file
51
src/scheduler/generator_task.rs
Normal 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
47
src/scheduler/mod.rs
Normal 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
3
src/scheduler/notes.md
Normal 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.
|
||||||
67
src/scheduler/task_pair.rs
Normal file
67
src/scheduler/task_pair.rs
Normal 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
107
src/scheduler/task_vec.rs
Normal 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() {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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 {
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -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
53
src/utils/product2.rs
Normal 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
|
||||||
|
}}
|
||||||
|
}
|
||||||
@@ -33,7 +33,7 @@ impl<'a, T: 'a> Stackframe<'a, T> {
|
|||||||
len: self.len + 1
|
len: self.len + 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn opush(prev: Option<&Self>, item: T) -> Self {
|
pub fn opush(prev: Option<&'a Self>, item: T) -> Self {
|
||||||
Self {
|
Self {
|
||||||
item,
|
item,
|
||||||
prev,
|
prev,
|
||||||
@@ -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
22
src/utils/translate.rs
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user