forked from Orchid/orchid
Merge remote-tracking branch 'origin/master'
This commit is contained in:
@@ -1,15 +1,14 @@
|
||||
{
|
||||
"folders": [
|
||||
{
|
||||
"path": ".."
|
||||
"path": "."
|
||||
}
|
||||
],
|
||||
"settings": {},
|
||||
"extensions": {
|
||||
"recommendations": [
|
||||
"tomoki1207.pdf",
|
||||
"James-Yu.latex-workshop",
|
||||
"rust-lang.rust-analyzer",
|
||||
"james-yu.latex-workshop",
|
||||
"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 crate::utils::collect_to_mrc;
|
||||
use crate::utils::{collect_to_mrc, to_mrc_slice};
|
||||
|
||||
use super::super::representations::typed::{Clause, Expr};
|
||||
|
||||
@@ -14,7 +14,7 @@ fn apply_lambda_expr_rec(
|
||||
) -> Option<Mrc<Expr>> {
|
||||
let Expr(clause, typ) = expr.as_ref();
|
||||
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(
|
||||
value.1.iter()
|
||||
.chain(typ.iter())
|
||||
@@ -23,7 +23,7 @@ fn apply_lambda_expr_rec(
|
||||
Some(Mrc::new(Expr(value.0.to_owned(), full_typ)))
|
||||
}
|
||||
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))))
|
||||
}
|
||||
}
|
||||
@@ -34,8 +34,7 @@ fn apply_lambda_clause_rec(
|
||||
) -> Option<Clause> {
|
||||
match clause {
|
||||
// Only element actually manipulated
|
||||
Clause::Argument(id) => panic!(
|
||||
"apply_lambda_expr_rec is supposed to eliminate this case"),
|
||||
Clause::LambdaArg(_) | Clause::AutoArg(_) => Some(clause),
|
||||
// Traverse, yield Some if either had changed.
|
||||
Clause::Apply(f, x) => {
|
||||
let new_f = apply_lambda_expr_rec(
|
||||
@@ -60,20 +59,26 @@ fn apply_lambda_clause_rec(
|
||||
|
||||
fn apply_lambda__traverse_param(
|
||||
id: u64, value: Mrc<Expr>,
|
||||
own_id: u64, t: Option<Mrc<Clause>>, b: Mrc<Expr>,
|
||||
wrap: impl Fn(u64, Option<Mrc<Clause>>, Mrc<Expr>) -> Clause
|
||||
own_id: u64, typ: Mrc<[Clause]>, b: Mrc<Expr>,
|
||||
wrap: impl Fn(u64, Mrc<[Clause]>, Mrc<Expr>) -> Clause
|
||||
) -> Option<Clause> {
|
||||
let new_t = t.and_then(|t| apply_lambda_clause_rec(
|
||||
id, Mrc::clone(&value), t.as_ref().clone()
|
||||
));
|
||||
let any_t = false;
|
||||
let mut t_acc = vec![];
|
||||
for t in typ.iter() {
|
||||
let newt = apply_lambda_clause_rec(id, Mrc::clone(&value), t.clone());
|
||||
any_t |= newt.is_some();
|
||||
t_acc.push(newt.unwrap_or_else(|| t.clone()))
|
||||
}
|
||||
// Respect shadowing
|
||||
let new_b = if own_id == id {None} else {
|
||||
apply_lambda_expr_rec(id, value, Mrc::clone(&b))
|
||||
};
|
||||
match (new_t, new_b) { // Mind the shadows
|
||||
(None, None) => None,
|
||||
(None, Some(b)) => Some(wrap(own_id, t, b)),
|
||||
(Some(t), None) => Some(wrap(own_id, Some(Mrc::new(t)), b)),
|
||||
(Some(t), Some(b)) => Some(wrap(own_id, Some(Mrc::new(t)), b))
|
||||
}
|
||||
if any_t { // mind the shadows
|
||||
let typ = to_mrc_slice(t_acc);
|
||||
if let Some(b) = new_b {
|
||||
Some(wrap(own_id, typ, b))
|
||||
} else {Some(wrap(own_id, typ, b))}
|
||||
} else if let Some(b) = new_b {
|
||||
Some(wrap(own_id, typ, b))
|
||||
} else {Some(wrap(own_id, typ, b))}
|
||||
}
|
||||
@@ -1,104 +0,0 @@
|
||||
use std::any::Any;
|
||||
use std::fmt::{Display, Debug};
|
||||
use std::hash::Hash;
|
||||
|
||||
use mappable_rc::Mrc;
|
||||
|
||||
use crate::representations::typed::{Expr, Clause};
|
||||
|
||||
pub trait ExternError: Display {}
|
||||
|
||||
/// Represents an externally defined function from the perspective of the executor
|
||||
/// Since Orchid lacks basic numerical operations, these are also external functions.
|
||||
pub struct ExternFn {
|
||||
name: String, param: Mrc<Expr>, rttype: Mrc<Expr>,
|
||||
function: Mrc<dyn Fn(Clause) -> Result<Clause, Mrc<dyn ExternError>>>
|
||||
}
|
||||
|
||||
impl ExternFn {
|
||||
pub fn new<F: 'static + Fn(Clause) -> Result<Clause, Mrc<dyn ExternError>>>(
|
||||
name: String, param: Mrc<Expr>, rttype: Mrc<Expr>, f: F
|
||||
) -> Self {
|
||||
Self {
|
||||
name, param, rttype,
|
||||
function: Mrc::map(Mrc::new(f), |f| {
|
||||
f as &dyn Fn(Clause) -> Result<Clause, Mrc<dyn ExternError>>
|
||||
})
|
||||
}
|
||||
}
|
||||
pub fn name(&self) -> &str {&self.name}
|
||||
pub fn apply(&self, arg: Clause) -> Result<Clause, Mrc<dyn ExternError>> {(self.function)(arg)}
|
||||
}
|
||||
|
||||
impl Clone for ExternFn { fn clone(&self) -> Self { Self {
|
||||
name: self.name.clone(),
|
||||
param: Mrc::clone(&self.param),
|
||||
rttype: Mrc::clone(&self.rttype),
|
||||
function: Mrc::clone(&self.function)
|
||||
}}}
|
||||
impl Eq for ExternFn {}
|
||||
impl PartialEq for ExternFn {
|
||||
fn eq(&self, other: &Self) -> bool { self.name() == other.name() }
|
||||
}
|
||||
impl Hash for ExternFn {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) { self.name.hash(state) }
|
||||
}
|
||||
impl Debug for ExternFn {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "##EXTERN[{}]:{:?} -> {:?}##", self.name(), self.param, self.rttype)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Atomic: Any + Debug where Self: 'static {
|
||||
fn as_any(&self) -> &dyn Any;
|
||||
fn definitely_eq(&self, _other: &dyn Any) -> bool;
|
||||
fn hash(&self, hasher: &mut dyn std::hash::Hasher);
|
||||
}
|
||||
|
||||
/// Represents a unit of information from the perspective of the executor. This may be
|
||||
/// something like a file descriptor which functions can operate on, but it can also be
|
||||
/// information in the universe of types or kinds such as the type of signed integers or
|
||||
/// the kind of types. Ad absurdum it can also be just a number, although Literal is
|
||||
/// preferable for types it's defined on.
|
||||
pub struct Atom {
|
||||
typ: Mrc<Expr>,
|
||||
data: Mrc<dyn Atomic>
|
||||
}
|
||||
impl Atom {
|
||||
pub fn new<T: 'static + Atomic>(data: T, typ: Mrc<Expr>) -> Self { Self{
|
||||
typ,
|
||||
data: Mrc::map(Mrc::new(data), |d| d as &dyn Atomic)
|
||||
} }
|
||||
pub fn data(&self) -> &dyn Atomic { self.data.as_ref() as &dyn Atomic }
|
||||
pub fn try_cast<T: Atomic>(&self) -> Result<&T, ()> {
|
||||
self.data().as_any().downcast_ref().ok_or(())
|
||||
}
|
||||
pub fn is<T: 'static>(&self) -> bool { self.data().as_any().is::<T>() }
|
||||
pub fn cast<T: 'static>(&self) -> &T {
|
||||
self.data().as_any().downcast_ref().expect("Type mismatch on Atom::cast")
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for Atom {
|
||||
fn clone(&self) -> Self { Self {
|
||||
typ: Mrc::clone(&self.typ),
|
||||
data: Mrc::clone(&self.data)
|
||||
} }
|
||||
}
|
||||
impl Hash for Atom {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
self.data.hash(state);
|
||||
self.typ.hash(state)
|
||||
}
|
||||
}
|
||||
impl Debug for Atom {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "##ATOM[{:?}]:{:?}##", self.data(), self.typ)
|
||||
}
|
||||
}
|
||||
impl Eq for Atom {}
|
||||
impl PartialEq for Atom {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.data().definitely_eq(other.data().as_any())
|
||||
}
|
||||
}
|
||||
@@ -8,46 +8,38 @@ use super::super::representations::typed::{Clause, Expr};
|
||||
use super::super::utils::Stackframe;
|
||||
|
||||
const PARAMETRICS_INLINE_COUNT:usize = 5;
|
||||
type Parametrics<'a> = ProtoMap<'a, u64, bool, PARAMETRICS_INLINE_COUNT>;
|
||||
// type Parametrics<'a> = ProtoMap<'a, u64, bool, PARAMETRICS_INLINE_COUNT>;
|
||||
|
||||
/// Hash the parts of an expression that are required to be equal for syntactic equality.
|
||||
pub fn partial_hash_rec<H: Hasher>(
|
||||
Expr(clause, _): &Expr, state: &mut H,
|
||||
mut parametrics: Parametrics
|
||||
parametrics: Option<&Stackframe<u64>>
|
||||
) {
|
||||
match clause {
|
||||
// Skip autos
|
||||
Clause::Auto(id, _, body) => {
|
||||
parametrics.set(id, true);
|
||||
partial_hash_rec(body, state, parametrics)
|
||||
}
|
||||
// Annotate everything else with a prefix
|
||||
// - Recurse into the tree of lambdas and calls - classic lambda calc
|
||||
Clause::Lambda(id, _, body) => {
|
||||
state.write_u8(0);
|
||||
parametrics.set(id, false);
|
||||
partial_hash_rec(body, state, parametrics)
|
||||
partial_hash_rec(body, state, Some(&Stackframe::opush(parametrics, *id)))
|
||||
}
|
||||
Clause::Apply(f, x) => {
|
||||
state.write_u8(1);
|
||||
partial_hash_rec(f, state, parametrics.clone());
|
||||
partial_hash_rec(x, state, parametrics);
|
||||
}
|
||||
Clause::AutoArg(..) => state.write_u8(2),
|
||||
// - Only recognize the depth of an argument if it refers to a non-auto parameter
|
||||
Clause::Argument(own_id) => {
|
||||
let (pos, is_auto) = parametrics.iter()
|
||||
.filter_map(|(id, is_auto)| is_auto.map(|is_auto| (*id, is_auto)))
|
||||
.find_position(|(id, is_auto)| id == own_id)
|
||||
.map(|(pos, (_, is_auto))| (pos, is_auto))
|
||||
.unwrap_or((usize::MAX, false));
|
||||
// If the argument references an auto, acknowledge its existence
|
||||
if is_auto {
|
||||
state.write_u8(2)
|
||||
} else {
|
||||
Clause::LambdaArg(own_id) => {
|
||||
let pos = parametrics
|
||||
.and_then(|sf| sf.iter().position(|id| id == own_id))
|
||||
.unwrap_or(usize::MAX);
|
||||
state.write_u8(3);
|
||||
state.write_usize(pos)
|
||||
}
|
||||
}
|
||||
// - Hash leaves like normal
|
||||
Clause::Literal(lit) => { state.write_u8(4); lit.hash(state) }
|
||||
Clause::Atom(at) => { state.write_u8(5); at.hash(state) }
|
||||
|
||||
@@ -17,12 +17,12 @@ pub fn skip_autos<'a,
|
||||
) -> BoxedIter<'static, Mrc<Expr>> {
|
||||
if let Expr(Clause::Auto(id, arg, body), typ) = expr.as_ref() {
|
||||
return Box::new(skip_autos(Mrc::clone(body), function).map({
|
||||
let arg = arg.as_ref().map(Mrc::clone);
|
||||
let arg = Mrc::clone(arg);
|
||||
let typ = Mrc::clone(typ);
|
||||
move |body| {
|
||||
Mrc::new(Expr(Clause::Auto(
|
||||
*id,
|
||||
arg.as_ref().map(Mrc::clone),
|
||||
Mrc::clone(&arg),
|
||||
body
|
||||
), Mrc::clone(&typ)))
|
||||
}
|
||||
@@ -50,7 +50,7 @@ fn direct_reductions(ex: Mrc<Expr>) -> impl Iterator<Item = Mrc<Expr>> {
|
||||
.unwrap_or(box_empty())
|
||||
},
|
||||
// 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) =>
|
||||
panic!("Literal expression {lit:?} can't be applied as function"),
|
||||
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) => {
|
||||
let id = *id;
|
||||
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_reductions = direct_reductions(body)
|
||||
.map(move |body| {
|
||||
let argt = argt.as_ref().map(Mrc::clone);
|
||||
let argt = Mrc::clone(&argt);
|
||||
Mrc::new(Expr(
|
||||
Clause::Lambda(id, argt, body),
|
||||
Mrc::clone(&typ)
|
||||
@@ -88,9 +88,9 @@ fn direct_reductions(ex: Mrc<Expr>) -> impl Iterator<Item = Mrc<Expr>> {
|
||||
});
|
||||
Box::new(body_reductions)
|
||||
},
|
||||
Clause::Literal(..) | Clause::ExternFn(..) | Clause::Atom(..) | Clause::Argument(..) =>
|
||||
box_empty(),
|
||||
Clause::Auto(..) => unreachable!("skip_autos should have filtered this"),
|
||||
Clause::Literal(..) | Clause::ExternFn(..) | Clause::Atom(..)
|
||||
| Clause::LambdaArg(..) | Clause::AutoArg(..) => box_empty(),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
use std::collections::HashMap;
|
||||
use std::hash::{Hasher, Hash};
|
||||
use std::iter;
|
||||
|
||||
use itertools::Itertools;
|
||||
use mappable_rc::Mrc;
|
||||
|
||||
use crate::utils::{ProtoMap, Side};
|
||||
use crate::utils::{ProtoMap, Side, mrc_empty_slice, collect_to_mrc, Stackframe, mrc_concat, Product2};
|
||||
|
||||
use super::super::representations::typed::{Clause, Expr};
|
||||
use super::super::utils::Stackframe;
|
||||
|
||||
pub fn swap<T, U>((t, u): (T, U)) -> (U, T) { (u, t) }
|
||||
|
||||
@@ -17,23 +15,91 @@ pub fn swap<T, U>((t, u): (T, U)) -> (U, T) { (u, t) }
|
||||
// - get rid of leftovers from Explicit
|
||||
// - adapt to new index-based system
|
||||
|
||||
// =@= =&= =%= =#= =$= =?= =!= =/=
|
||||
// <@> <&> <%> <#> <$> <?> <!> </>
|
||||
// |@| |&| |%| |#| |$| |?| |!| |/|
|
||||
// {@} {&} {%} {#} {$} {?} {!} {/}
|
||||
// (@) (&) (%) (#) ($) (?) (!) (/)
|
||||
// [@] [&] [%] [#] [$] [?] [!] [/]
|
||||
enum UnifError {
|
||||
Conflict,
|
||||
}
|
||||
|
||||
type LambdaMap<'a> = Option<&'a Stackframe<'a, (u64, u64)>>;
|
||||
|
||||
/// The context associates a given variable (by absolute index) on a given side to
|
||||
/// an expression on the opposite side rooted at the specified depth.
|
||||
/// The root depths are used to translate betwee de Brujin arguments and absolute indices.
|
||||
struct Context(HashMap<u64, Mrc<Expr>>);
|
||||
impl Context {
|
||||
fn set(&mut self, id: u64, value: Mrc<Expr>) {
|
||||
// If already defined, then it must be an argument
|
||||
if let Some(value) = self.0.get(&id) {
|
||||
if let Clause::Argument(opposite_up) ex.0
|
||||
fn set(&mut self, id: u64, value: &Mrc<Expr>, lambdas: LambdaMap) -> Result<Option<Mrc<Expr>>, UnifError> {
|
||||
Ok(
|
||||
if let Some(local) = self.0.get(&id) {
|
||||
Some(
|
||||
self.unify_expr(local, value, lambdas)?
|
||||
.pick(Mrc::clone(local), Mrc::clone(value))
|
||||
)
|
||||
} else { None }
|
||||
)
|
||||
}
|
||||
|
||||
fn unify_expr(&mut self,
|
||||
left: &Mrc<Expr>, right: &Mrc<Expr>, lambdas: LambdaMap
|
||||
) -> Result<Product2<Mrc<Expr>>, UnifError> {
|
||||
let Expr(left_val, left_typs) = left.as_ref();
|
||||
let Expr(right_val, right_typs) = right.as_ref();
|
||||
let val = match (left_val, right_val) {
|
||||
(Clause::AutoArg(l), Clause::AutoArg(r)) if l == r => Product2::Either,
|
||||
(Clause::AutoArg(id), _) => self.set(*id, left, lambdas)?.as_ref()
|
||||
.map_or(Product2::Left, |e| Product2::New(e.0.clone())),
|
||||
(_, Clause::AutoArg(id)) => self.set(*id, right, lambdas)?.as_ref()
|
||||
.map_or(Product2::Right, |e| Product2::New(e.0.clone())),
|
||||
_ => self.unify_clause(left_val, right_val, lambdas)?
|
||||
};
|
||||
Ok(match val {
|
||||
Product2::Either if right_typs.is_empty() && left_typs.is_empty() => Product2::Either,
|
||||
Product2::Left | Product2::Either if right_typs.is_empty() => Product2::Left,
|
||||
Product2::Right | Product2::Either if left_typs.is_empty() => Product2::Right,
|
||||
product => {
|
||||
let all_types = mrc_concat(left_typs, right_typs);
|
||||
Product2::New(Mrc::new(Expr(
|
||||
product.pick(left_val.clone(), right_val.clone()),
|
||||
all_types
|
||||
)))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn unify_clause(&mut self,
|
||||
left: &Clause, right: &Clause, lambdas: LambdaMap
|
||||
) -> Result<Product2<Clause>, UnifError> {
|
||||
Ok(match (left, right) {
|
||||
(Clause::Literal(l), Clause::Literal(r)) if l == r => Product2::Either,
|
||||
(Clause::Atom(l), Clause::Atom(r)) if l == r => Product2::Either,
|
||||
(Clause::ExternFn(l), Clause::ExternFn(r)) if l == r => Product2::Either,
|
||||
(Clause::LambdaArg(l), Clause::LambdaArg(r)) => if l == r {Product2::Either} else {
|
||||
let is_equal = Stackframe::o_into_iter(lambdas)
|
||||
.first_some(|(l_candidate, r_candidate)| {
|
||||
if l_candidate == l && r_candidate == r {Some(true)} // match
|
||||
else if l_candidate == l || r_candidate == r {Some(false)} // shadow
|
||||
else {None} // irrelevant
|
||||
}).unwrap_or(false);
|
||||
// Reference:
|
||||
if is_equal {Product2::Left} else {return Err(UnifError::Conflict)}
|
||||
}
|
||||
(Clause::AutoArg(_), _) | (_, Clause::AutoArg(_)) => {
|
||||
unreachable!("unify_expr should have handled this")
|
||||
}
|
||||
(Clause::Lambda(l_id, l_arg, l_body), Clause::Lambda(r_id, r_arg, r_body)) => {
|
||||
let lambdas = Stackframe::opush(lambdas, (*l_id, *r_id));
|
||||
self.unify_expr(l_body, r_body, Some(&lambdas))?
|
||||
.map(|ex| Clause::Lambda(*l_id, mrc_empty_slice(), ex))
|
||||
}
|
||||
(Clause::Apply(l_f, l_x), Clause::Apply(r_f, r_x)) => {
|
||||
self.unify_expr(l_f, r_f, lambdas)?.join((Mrc::clone(l_f), Mrc::clone(r_f)),
|
||||
self.unify_expr(l_x, r_x, lambdas)?, (Mrc::clone(l_x), Mrc::clone(r_x))
|
||||
).map(|(f, x)| Clause::Apply(f, x))
|
||||
}
|
||||
(Clause::Auto(l_id, l_arg, l_body), Clause::Auto(r_id, r_arg, r_body)) => {
|
||||
let typ = self.unify(l_arg, r_arg, lambdas)?;
|
||||
let body = self.unify_expr(l_body, r_body, lambdas)?;
|
||||
typ.join((l_arg, r_arg), )
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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(adt_const_params)]
|
||||
#![feature(generic_const_exprs)]
|
||||
#![feature(generators, generator_trait)]
|
||||
|
||||
|
||||
use std::env::current_dir;
|
||||
|
||||
mod executor;
|
||||
// mod executor;
|
||||
mod parse;
|
||||
mod project;
|
||||
mod utils;
|
||||
mod representations;
|
||||
mod rule;
|
||||
mod types;
|
||||
mod scheduler;
|
||||
pub(crate) mod foreign;
|
||||
use file_loader::LoadingError;
|
||||
pub use representations::ast;
|
||||
use ast::{Expr, Clause};
|
||||
@@ -19,7 +22,7 @@ use representations::typed as t;
|
||||
use mappable_rc::Mrc;
|
||||
use project::{rule_collector, Loaded, file_loader};
|
||||
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]> {
|
||||
to_mrc_slice(vliteral(orig))
|
||||
@@ -48,17 +51,17 @@ fn initial_tree() -> Mrc<[Expr]> {
|
||||
|
||||
#[allow(unused)]
|
||||
fn typed_notation_debug() {
|
||||
let true_ex = t::Clause::Auto(0, None,
|
||||
t::Clause::Lambda(1, Some(Mrc::new(t::Clause::Argument(0))),
|
||||
t::Clause::Lambda(2, Some(Mrc::new(t::Clause::Argument(0))),
|
||||
t::Clause::Argument(1).wrap_t(t::Clause::Argument(0))
|
||||
let true_ex = t::Clause::Auto(0, mrc_empty_slice(),
|
||||
t::Clause::Lambda(1, one_mrc_slice(t::Clause::AutoArg(0)),
|
||||
t::Clause::Lambda(2, one_mrc_slice(t::Clause::AutoArg(0)),
|
||||
t::Clause::LambdaArg(1).wrap_t(t::Clause::AutoArg(0))
|
||||
).wrap()
|
||||
).wrap()
|
||||
).wrap();
|
||||
let false_ex = t::Clause::Auto(0, None,
|
||||
t::Clause::Lambda(1, Some(Mrc::new(t::Clause::Argument(0))),
|
||||
t::Clause::Lambda(2, Some(Mrc::new(t::Clause::Argument(0))),
|
||||
t::Clause::Argument(2).wrap_t(t::Clause::Argument(0))
|
||||
let false_ex = t::Clause::Auto(0, mrc_empty_slice(),
|
||||
t::Clause::Lambda(1, one_mrc_slice(t::Clause::AutoArg(0)),
|
||||
t::Clause::Lambda(2, one_mrc_slice(t::Clause::AutoArg(0)),
|
||||
t::Clause::LambdaArg(2).wrap_t(t::Clause::AutoArg(0))
|
||||
).wrap()
|
||||
).wrap()
|
||||
).wrap();
|
||||
|
||||
@@ -4,7 +4,7 @@ use ordered_float::NotNan;
|
||||
use std::{hash::Hash, intrinsics::likely};
|
||||
use std::fmt::Debug;
|
||||
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;
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
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};
|
||||
|
||||
@@ -49,9 +49,9 @@ pub fn exprv(exprv: &[ast::Expr]) -> Result<typed::Expr, Error> {
|
||||
const NAMES_INLINE_COUNT:usize = 3;
|
||||
|
||||
/// Recursive state of [exprv]
|
||||
fn exprv_rec<'a>(
|
||||
v: &'a [ast::Expr],
|
||||
names: ProtoMap<&'a str, u64, NAMES_INLINE_COUNT>,
|
||||
fn exprv_rec(
|
||||
v: &[ast::Expr],
|
||||
names: ProtoMap<&str, (u64, bool), NAMES_INLINE_COUNT>,
|
||||
explicits: Option<&Stackframe<Mrc<typed::Expr>>>,
|
||||
) -> Result<(typed::Expr, usize), Error> {
|
||||
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 \
|
||||
wrapped expression node matches all trailing colons."
|
||||
);
|
||||
let (x, _) = expr_rec(inner.as_ref(), names.clone(), None)?;
|
||||
let new_explicits = Stackframe::opush(explicits, Mrc::new(x));
|
||||
let (body, used_expls) = exprv_rec(rest, names, Some(&new_explicits))?;
|
||||
let (x, _) = expr_rec(inner.as_ref(), names, None)?;
|
||||
let new_explicits = Some(&Stackframe::opush(explicits, Mrc::new(x)));
|
||||
let (body, used_expls) = exprv_rec(rest, names, new_explicits)?;
|
||||
Ok((body, used_expls.saturating_sub(1)))
|
||||
} 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, x_used_expls) = expr_rec(last, names, x_explicits)?;
|
||||
Ok((typed::Expr(
|
||||
@@ -77,19 +77,17 @@ fn exprv_rec<'a>(
|
||||
}
|
||||
|
||||
/// Recursive state of [expr]
|
||||
fn expr_rec<'a>(
|
||||
ast::Expr(val, typ): &'a ast::Expr,
|
||||
names: ProtoMap<&'a str, u64, NAMES_INLINE_COUNT>,
|
||||
fn expr_rec(
|
||||
ast::Expr(val, typ): &ast::Expr,
|
||||
names: ProtoMap<&str, (u64, bool), NAMES_INLINE_COUNT>,
|
||||
explicits: Option<&Stackframe<Mrc<typed::Expr>>> // known explicit values
|
||||
) -> Result<(typed::Expr, usize), Error> { // (output, used_explicits)
|
||||
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<_, _>>()?;
|
||||
if let ast::Clause::S(paren, body) = val {
|
||||
if *paren != '(' {return Err(Error::BadGroup(*paren))}
|
||||
let (typed::Expr(inner, inner_t), used_expls) = exprv_rec(
|
||||
body.as_ref(), names, explicits
|
||||
)?;
|
||||
let (typed::Expr(inner, inner_t), used_expls) = exprv_rec(body.as_ref(), names, explicits)?;
|
||||
let new_t = if typ.len() == 0 { inner_t } else {
|
||||
to_mrc_slice(if inner_t.len() == 0 { typ } else {
|
||||
inner_t.iter().chain(typ.iter()).cloned().collect()
|
||||
@@ -103,9 +101,9 @@ fn expr_rec<'a>(
|
||||
}
|
||||
|
||||
/// Recursive state of [clause]
|
||||
fn clause_rec<'a>(
|
||||
cls: &'a ast::Clause,
|
||||
mut names: ProtoMap<&'a str, u64, NAMES_INLINE_COUNT>,
|
||||
fn clause_rec(
|
||||
cls: &ast::Clause,
|
||||
names: ProtoMap<&str, (u64, bool), NAMES_INLINE_COUNT>,
|
||||
mut explicits: Option<&Stackframe<Mrc<typed::Expr>>>
|
||||
) -> Result<(typed::Clause, usize), Error> {
|
||||
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();
|
||||
explicits = rest_explicits;
|
||||
// Convert the type
|
||||
let typ = if t.len() == 0 {None} else {
|
||||
let (typed::Expr(c, t), _) = exprv_rec(
|
||||
t.as_ref(), names.clone(), None
|
||||
)?;
|
||||
let typ = if t.len() == 0 {mrc_empty_slice()} else {
|
||||
let (typed::Expr(c, t), _) = exprv_rec(t.as_ref(), names, None)?;
|
||||
if t.len() > 0 {return Err(Error::ExplicitBottomKind)}
|
||||
else {Some(Mrc::new(c))}
|
||||
else {one_mrc_slice(c)}
|
||||
};
|
||||
// Traverse body with extended context
|
||||
if let Some(name) = no {names.set(&&**name, id)}
|
||||
let (body, used_expls) = exprv_rec(
|
||||
b.as_ref(), names, explicits
|
||||
)?;
|
||||
if let Some(name) = no {names.set(&&**name, (id, true))}
|
||||
let (body, used_expls) = exprv_rec(b.as_ref(), names, explicits)?;
|
||||
// Produce a binding instead of an auto if explicit was available
|
||||
if let Some(known_value) = value {
|
||||
Ok((typed::Clause::Apply(
|
||||
@@ -148,26 +142,25 @@ fn clause_rec<'a>(
|
||||
// Allocate id
|
||||
let id = get_name();
|
||||
// Convert the type
|
||||
let typ = if t.len() == 0 {None} else {
|
||||
let (typed::Expr(c, t), _) = exprv_rec(t.as_ref(), names.clone(), None)?;
|
||||
let typ = if t.len() == 0 {mrc_empty_slice()} else {
|
||||
let (typed::Expr(c, t), _) = exprv_rec(t.as_ref(), names, None)?;
|
||||
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)?;
|
||||
Ok((typed::Clause::Lambda(id, typ, Mrc::new(body)), used_expls))
|
||||
}
|
||||
ast::Clause::Literal(l) => Ok((typed::Clause::Literal(l.clone()), 0)),
|
||||
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((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) => {
|
||||
if *paren != '(' {return Err(Error::BadGroup(*paren))}
|
||||
let (typed::Expr(val, typ), used_expls) = exprv_rec(
|
||||
entries.as_ref(), names, explicits
|
||||
)?;
|
||||
let (typed::Expr(val, typ), used_expls) = exprv_rec(entries.as_ref(), names, explicits)?;
|
||||
if typ.len() == 0 {Ok((val, used_expls))}
|
||||
else {Err(Error::ExprToClause(typed::Expr(val, typ)))}
|
||||
},
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use mappable_rc::Mrc;
|
||||
use crate::executor::Atom;
|
||||
use crate::foreign::{Atom, ExternFn};
|
||||
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::ast;
|
||||
@@ -47,9 +47,9 @@ impl Debug for Expr {
|
||||
pub enum Clause {
|
||||
Literal(Literal),
|
||||
Apply(Mrc<Expr>, Mrc<Expr>),
|
||||
Lambda(u64, Option<Mrc<Clause>>, Mrc<Expr>),
|
||||
Auto(u64, Option<Mrc<Clause>>, Mrc<Expr>),
|
||||
Argument(u64),
|
||||
Lambda(u64, Mrc<[Clause]>, Mrc<Expr>),
|
||||
Auto(u64, Mrc<[Clause]>, Mrc<Expr>),
|
||||
LambdaArg(u64), AutoArg(u64),
|
||||
ExternFn(ExternFn),
|
||||
Atom(Atom)
|
||||
}
|
||||
@@ -58,12 +58,12 @@ const ARGNAME_CHARSET: &str = "abcdefghijklmnopqrstuvwxyz";
|
||||
|
||||
fn parametric_fmt(
|
||||
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 {
|
||||
if wrap_right { f.write_char('(')?; }
|
||||
f.write_str(prefix)?;
|
||||
f.write_str(&string_from_charset(uid, ARGNAME_CHARSET))?;
|
||||
if let Some(typ) = argtyp {
|
||||
for typ in argtyp.iter() {
|
||||
f.write_str(":")?;
|
||||
typ.deep_fmt(f, Wrap(false, false))?;
|
||||
}
|
||||
@@ -81,12 +81,14 @@ impl Clause {
|
||||
Self::ExternFn(nc) => write!(f, "{nc:?}"),
|
||||
Self::Atom(a) => write!(f, "{a:?}"),
|
||||
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,
|
||||
"@", 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) => {
|
||||
if wl { f.write_char('(')?; }
|
||||
func.deep_fmt(f, Wrap(false, true) )?;
|
||||
@@ -104,13 +106,14 @@ impl Clause {
|
||||
impl Clone for Clause {
|
||||
fn clone(&self) -> Self {
|
||||
match self {
|
||||
Clause::Auto(uid,t, b) => Clause::Auto(*uid, t.as_ref().map(Mrc::clone), Mrc::clone(b)),
|
||||
Clause::Lambda(uid, t, b) => Clause::Lambda(*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, Mrc::clone(t), Mrc::clone(b)),
|
||||
Clause::Literal(l) => Clause::Literal(l.clone()),
|
||||
Clause::ExternFn(nc) => Clause::ExternFn(nc.clone()),
|
||||
Clause::Atom(a) => Clause::Atom(a.clone()),
|
||||
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;
|
||||
pub mod translate;
|
||||
pub use cache::Cache;
|
||||
mod substack;
|
||||
pub use substack::Stackframe;
|
||||
mod side;
|
||||
pub use side::Side;
|
||||
mod merge_sorted;
|
||||
pub use merge_sorted::merge_sorted;
|
||||
mod unwrap_or;
|
||||
pub mod iter;
|
||||
pub use iter::BoxedIter;
|
||||
mod bfs;
|
||||
mod unless_let;
|
||||
mod string_from_charset;
|
||||
pub use string_from_charset::string_from_charset;
|
||||
mod for_loop;
|
||||
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;
|
||||
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>
|
||||
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])}
|
||||
}).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
|
||||
}}
|
||||
}
|
||||
@@ -49,6 +49,9 @@ impl<'a, T: 'a> Stackframe<'a, T> {
|
||||
if count == 0 {cur}
|
||||
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 {
|
||||
@@ -62,6 +65,17 @@ pub struct StackframeIterator<'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> {
|
||||
type Item = &'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