forked from Orchid/orchid
in midst of refactor
This commit is contained in:
148
orchidlang/src/interpreter/nort_builder.rs
Normal file
148
orchidlang/src/interpreter/nort_builder.rs
Normal file
@@ -0,0 +1,148 @@
|
||||
//! Helper for generating the interpreter's internal representation
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::mem;
|
||||
|
||||
use substack::Substack;
|
||||
|
||||
use super::nort::{AsDerefMut, Clause, Expr};
|
||||
use super::path_set::PathSet;
|
||||
use crate::utils::pure_seq::pushed;
|
||||
|
||||
enum IntGenData<'a, T: ?Sized> {
|
||||
Lambda(&'a T, &'a RefCell<Option<PathSet>>),
|
||||
/// Counts left steps within a chain of [Clause::Apply] for collapsing.
|
||||
Apply(&'a RefCell<usize>),
|
||||
/// Replaces [IntGenData::Apply] when stepping left into non-apply to record
|
||||
/// a [None] [super::path_set::Step].
|
||||
AppF,
|
||||
/// Replaces [IntGenData::Apply] when stepping right to freeze the value.
|
||||
AppArg(usize),
|
||||
}
|
||||
|
||||
impl<'a, T: ?Sized> Copy for IntGenData<'a, T> {}
|
||||
impl<'a, T: ?Sized> Clone for IntGenData<'a, T> {
|
||||
fn clone(&self) -> Self { *self }
|
||||
}
|
||||
|
||||
struct ArgCollector(RefCell<Option<PathSet>>);
|
||||
impl ArgCollector {
|
||||
pub fn new() -> Self { Self(RefCell::new(None)) }
|
||||
pub fn into_path(self) -> Option<PathSet> { self.0.into_inner() }
|
||||
}
|
||||
|
||||
/// Strategy used to find the lambda corresponding to a given argument in the
|
||||
/// stack. The function is called on the data associated with the argument, then
|
||||
/// the callback it returns is called on every lambda ancestor's associated
|
||||
/// data from closest to outermost ancestor. The first lambda where this
|
||||
/// callback returns true is considered to own the argument.
|
||||
pub type LambdaPicker<'a, T, U> = &'a dyn for<'b> Fn(&'b U) -> Box<dyn FnMut(&T) -> bool + 'b>;
|
||||
|
||||
/// Bundle of information passed down through recursive fnuctions to instantiate
|
||||
/// runtime [Expr], [super::nort::ClauseInst] or [Clause].
|
||||
///
|
||||
/// The context used by [crate::gen::traits::Gen] to convert templates is which
|
||||
/// includes this type is constructed with [super::gen_nort::nort_gen].
|
||||
pub struct NortBuilder<'a, T: ?Sized, U: ?Sized> {
|
||||
stack: Substack<'a, IntGenData<'a, T>>,
|
||||
lambda_picker: LambdaPicker<'a, T, U>,
|
||||
}
|
||||
impl<'a, T: ?Sized, U: ?Sized> NortBuilder<'a, T, U> {
|
||||
/// Create a new recursive [super::nort] builder from a location that will be
|
||||
pub fn new(lambda_picker: LambdaPicker<'a, T, U>) -> Self {
|
||||
Self { stack: Substack::Bottom, lambda_picker }
|
||||
}
|
||||
/// [Substack::pop] and clone the location
|
||||
fn pop<'b>(&'b self, count: usize) -> NortBuilder<'b, T, U>
|
||||
where 'a: 'b {
|
||||
let mut new = *self;
|
||||
new.stack = *self.stack.pop(count);
|
||||
new
|
||||
}
|
||||
/// [Substack::push] and clone the location
|
||||
fn push<'b>(&'b self, data: IntGenData<'a, T>) -> NortBuilder<'b, T, U>
|
||||
where 'a: 'b {
|
||||
let mut new = *self;
|
||||
new.stack = self.stack.push(data);
|
||||
new
|
||||
}
|
||||
fn non_app_step<V>(self, f: impl FnOnce(NortBuilder<T, U>) -> V) -> V {
|
||||
if let Some(IntGenData::Apply(_)) = self.stack.value() {
|
||||
f(self.pop(1).push(IntGenData::AppF))
|
||||
} else {
|
||||
f(self)
|
||||
}
|
||||
}
|
||||
|
||||
/// Climb back through the stack and find a lambda associated with this
|
||||
/// argument, then record the path taken from the lambda to this argument in
|
||||
/// the lambda's mutable cell.
|
||||
pub fn arg_logic(self, name: &'a U) {
|
||||
let mut lambda_chk = (self.lambda_picker)(name);
|
||||
self.non_app_step(|ctx| {
|
||||
let res = ctx.stack.iter().try_fold(vec![], |path, item| match item {
|
||||
IntGenData::Apply(_) => panic!("This is removed after handling"),
|
||||
IntGenData::Lambda(n, rc) => match lambda_chk(n) {
|
||||
false => Ok(path),
|
||||
true => Err((path, *rc)),
|
||||
},
|
||||
IntGenData::AppArg(n) => Ok(pushed(path, Some(*n))),
|
||||
IntGenData::AppF => Ok(pushed(path, None)),
|
||||
});
|
||||
let (mut path, slot) = res.expect_err("Argument not wrapped in matching lambda");
|
||||
path.reverse();
|
||||
match &mut *slot.borrow_mut() {
|
||||
slot @ None => *slot = Some(PathSet::end(path)),
|
||||
Some(slot) => take_mut::take(slot, |p| p.overlay(PathSet::end(path))),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Push a stackframe corresponding to a lambda expression, build the body,
|
||||
/// then record the path set collected by [NortBuilder::arg_logic] calls
|
||||
/// within the body.
|
||||
pub fn lambda_logic(self, name: &T, body: impl FnOnce(NortBuilder<T, U>) -> Expr) -> Clause {
|
||||
let coll = ArgCollector::new();
|
||||
let frame = IntGenData::Lambda(name, &coll.0);
|
||||
let body = self.non_app_step(|ctx| body(ctx.push(frame)));
|
||||
let args = coll.into_path();
|
||||
Clause::Lambda { args, body }
|
||||
}
|
||||
|
||||
/// Logic for collapsing Apply clauses. Different steps of the logic
|
||||
/// communicate via mutable variables on the stack
|
||||
pub fn apply_logic(
|
||||
self,
|
||||
f: impl FnOnce(NortBuilder<T, U>) -> Expr,
|
||||
x: impl FnOnce(NortBuilder<T, U>) -> Expr,
|
||||
) -> Clause {
|
||||
let mut fun: Expr;
|
||||
let arg: Expr;
|
||||
if let Some(IntGenData::Apply(rc)) = self.stack.value() {
|
||||
// argument side commits backidx
|
||||
arg = x(self.pop(1).push(IntGenData::AppArg(*rc.borrow())));
|
||||
// function side increments backidx
|
||||
*rc.borrow_mut() += 1;
|
||||
fun = f(self);
|
||||
} else {
|
||||
// function side starts from backidx 1
|
||||
fun = f(self.push(IntGenData::Apply(&RefCell::new(1))));
|
||||
// argument side commits 0
|
||||
arg = x(self.push(IntGenData::AppArg(0)));
|
||||
};
|
||||
let mut cls_lk = fun.as_deref_mut();
|
||||
if let Clause::Apply { x, f: _ } = &mut *cls_lk {
|
||||
x.push_back(arg);
|
||||
mem::drop(cls_lk);
|
||||
fun.clause.into_cls()
|
||||
} else {
|
||||
mem::drop(cls_lk);
|
||||
Clause::Apply { f: fun, x: [arg].into() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: ?Sized, U: ?Sized> Copy for NortBuilder<'a, T, U> {}
|
||||
impl<'a, T: ?Sized, U: ?Sized> Clone for NortBuilder<'a, T, U> {
|
||||
fn clone(&self) -> Self { *self }
|
||||
}
|
||||
Reference in New Issue
Block a user