in midst of refactor

This commit is contained in:
2024-04-29 21:46:42 +02:00
parent ed0d64d52e
commit aa3f7e99ab
221 changed files with 5431 additions and 685 deletions

View File

@@ -0,0 +1,67 @@
use std::any::TypeId;
use itertools::Itertools;
use orchid_api::expr::{Clause, Expr};
use orchid_api::location::Location;
use super::traits::{GenClause, Generable};
use crate::expr::RtExpr;
use crate::host::AtomHand;
use crate::intern::{deintern, intern};
fn safely_reinterpret<In: 'static, Out: 'static>(x: In) -> Result<Out, In> {
if TypeId::of::<In>() != TypeId::of::<Out>() {
return Err(x);
}
let bx = Box::new(x);
// SAFETY: type sameness asserted above, from_raw and into_raw pair up
Ok(*unsafe { Box::from_raw(Box::into_raw(bx) as *mut Out) })
}
/// impls of the gen traits for external types
impl GenClause for Expr {
fn generate<T: super::traits::Generable>(&self, ctx: T::Ctx<'_>, pop: &impl Fn() -> T) -> T {
match &self.clause {
Clause::Arg(arg) => T::arg(ctx, deintern(*arg).as_str()),
Clause::Atom(atom) => T::atom(ctx, AtomHand::from_api(atom.clone())),
Clause::Call(f, x) => T::apply(ctx, |c| f.generate(c, pop), |c| x.generate(c, pop)),
Clause::Lambda(arg, b) => T::lambda(ctx, deintern(*arg).as_str(), |ctx| b.generate(ctx, pop)),
Clause::Seq(n1, n2) => T::seq(ctx, |c| n1.generate(c, pop), |c| n2.generate(c, pop)),
Clause::Const(int) => T::constant(ctx, deintern(*int).iter().map(|t| t.as_str())),
Clause::Slot(expr) => {
let rte = RtExpr::resolve(*expr).expect("expired ticket");
safely_reinterpret(rte).expect("ticket slots make no sense for anything other than rte")
},
}
}
}
fn to_expr(clause: Clause) -> Expr { Expr { clause, location: Location::None } }
impl Generable for Expr {
type Ctx<'a> = ();
fn arg(_ctx: Self::Ctx<'_>, name: &str) -> Self { to_expr(Clause::Arg(intern(name).marker())) }
fn apply(
ctx: Self::Ctx<'_>,
f: impl FnOnce(Self::Ctx<'_>) -> Self,
x: impl FnOnce(Self::Ctx<'_>) -> Self,
) -> Self {
to_expr(Clause::Call(Box::new(f(ctx)), Box::new(x(ctx))))
}
fn atom(_ctx: Self::Ctx<'_>, a: crate::host::AtomHand) -> Self { to_expr(Clause::Atom(a.api_ref())) }
fn constant<'a>(_ctx: Self::Ctx<'_>, name: impl IntoIterator<Item = &'a str>) -> Self {
to_expr(Clause::Const(intern(&name.into_iter().map(intern).collect_vec()[..]).marker()))
}
fn lambda(ctx: Self::Ctx<'_>, name: &str, body: impl FnOnce(Self::Ctx<'_>) -> Self) -> Self {
to_expr(Clause::Lambda(intern(name).marker(), Box::new(body(ctx))))
}
fn seq(
ctx: Self::Ctx<'_>,
a: impl FnOnce(Self::Ctx<'_>) -> Self,
b: impl FnOnce(Self::Ctx<'_>) -> Self,
) -> Self {
to_expr(Clause::Seq(Box::new(a(ctx)), Box::new(b(ctx))))
}
}

View File

@@ -0,0 +1,8 @@
//! Abstractions and primitives for defining Orchid code in compile-time Rust
//! constants. This is used both to generate glue code such as function call
//! expressions at runtime and to define completely static intrinsics and
//! constants accessible to usercode.
pub mod tpl;
pub mod traits;
pub mod tree;
pub mod impls;

View File

@@ -0,0 +1,61 @@
//! Various elemental components to build expression trees that all implement
//! [GenClause].
use super::traits::{GenClause, Generable};
use crate::host::AtomHand;
/// A trivial atom
#[derive(Clone, Debug)]
pub struct SysAtom(pub AtomHand);
impl GenClause for SysAtom {
fn generate<T: Generable>(&self, ctx: T::Ctx<'_>, _: &impl Fn() -> T) -> T {
T::atom(ctx, self.0.clone())
}
}
/// Const, Reference a constant from the execution environment. Unlike Orchid
/// syntax, this doesn't include lambda arguments. For that, use [P]
#[derive(Debug, Clone)]
pub struct C(pub &'static str);
impl GenClause for C {
fn generate<T: Generable>(&self, ctx: T::Ctx<'_>, _: &impl Fn() -> T) -> T {
T::constant(ctx, self.0.split("::"))
}
}
/// Apply a function to a value provided by [L]
#[derive(Debug, Clone)]
pub struct A<F: GenClause, X: GenClause>(pub F, pub X);
impl<F: GenClause, X: GenClause> GenClause for A<F, X> {
fn generate<T: Generable>(&self, ctx: T::Ctx<'_>, p: &impl Fn() -> T) -> T {
T::apply(ctx, |gen| self.0.generate(gen, p), |gen| self.1.generate(gen, p))
}
}
/// Apply a function to two arguments
pub fn a2(f: impl GenClause, x: impl GenClause, y: impl GenClause) -> impl GenClause {
A(A(f, x), y)
}
/// Lambda expression. The argument can be referenced with [P]
#[derive(Debug, Clone)]
pub struct L<B: GenClause>(pub &'static str, pub B);
impl<B: GenClause> GenClause for L<B> {
fn generate<T: Generable>(&self, ctx: T::Ctx<'_>, p: &impl Fn() -> T) -> T {
T::lambda(ctx, self.0, |gen| self.1.generate(gen, p))
}
}
/// Parameter to a lambda expression
#[derive(Debug, Clone)]
pub struct P(pub &'static str);
impl GenClause for P {
fn generate<T: Generable>(&self, ctx: T::Ctx<'_>, _: &impl Fn() -> T) -> T { T::arg(ctx, self.0) }
}
/// Slot for an Orchid value to be specified during execution
#[derive(Debug, Clone)]
pub struct Slot;
impl GenClause for Slot {
fn generate<T: Generable>(&self, _: T::Ctx<'_>, pop: &impl Fn() -> T) -> T { pop() }
}

View File

@@ -0,0 +1,78 @@
//! Abstractions used to generate Orchid expressions
use std::backtrace::Backtrace;
use std::cell::RefCell;
use std::collections::VecDeque;
use std::fmt;
use crate::host::AtomHand;
/// Representations of the Orchid expression tree that can describe basic
/// language elements.
pub trait Generable: Sized + 'static {
/// Context information defined by parents. Generators just forward this.
type Ctx<'a>: Sized;
/// Wrap external data.
fn atom(ctx: Self::Ctx<'_>, a: AtomHand) -> Self;
/// Generate a reference to a constant
fn constant<'a>(ctx: Self::Ctx<'_>, name: impl IntoIterator<Item = &'a str>) -> Self;
/// Generate a function call given the function and its argument
fn apply(
ctx: Self::Ctx<'_>,
f: impl FnOnce(Self::Ctx<'_>) -> Self,
x: impl FnOnce(Self::Ctx<'_>) -> Self,
) -> Self;
/// Generate a dependency between the top clause and the bottom clause
fn seq(
ctx: Self::Ctx<'_>,
a: impl FnOnce(Self::Ctx<'_>) -> Self,
b: impl FnOnce(Self::Ctx<'_>) -> Self,
) -> Self;
/// Generate a function. The argument name is only valid within the same
/// [Generable].
fn lambda(ctx: Self::Ctx<'_>, name: &str, body: impl FnOnce(Self::Ctx<'_>) -> Self) -> Self;
/// Generate a reference to a function argument. The argument name is only
/// valid within the same [Generable].
fn arg(ctx: Self::Ctx<'_>, name: &str) -> Self;
}
/// Expression templates which can be instantiated in multiple representations
/// of Orchid. Expressions can be built from the elements defined in
/// [super::tpl].
///
/// Do not depend on this trait, use [Gen] instead. Conversely, implement this
/// instead of [Gen].
pub trait GenClause: fmt::Debug + Sized {
/// Enact the template at runtime to build a given type.
/// `pop` pops from the runtime template parameter list passed to the
/// generator.
///
/// Do not call this, it's the backing operation of [Gen#template]
fn generate<T: Generable>(&self, ctx: T::Ctx<'_>, pop: &impl Fn() -> T) -> T;
}
/// Expression generators
///
/// Do not implement this trait, it's the frontend for [GenClause]. Conversely,
/// do not consume [GenClause].
pub trait Gen<T: Generable, U>: fmt::Debug {
/// Create an instance of this template with some parameters
fn template(&self, ctx: T::Ctx<'_>, params: U) -> T;
}
impl<T: Generable, I: IntoIterator<Item = T>, G: GenClause> Gen<T, I> for G {
fn template(&self, ctx: T::Ctx<'_>, params: I) -> T {
let values = RefCell::new(params.into_iter().collect::<VecDeque<_>>());
let t =
self.generate(ctx, &|| values.borrow_mut().pop_front().expect("Not enough values provided"));
let leftover = values.borrow().len();
assert_eq!(
leftover,
0,
"Too many values provided ({leftover} left) {}",
Backtrace::force_capture()
);
t
}
}

View File

@@ -0,0 +1,79 @@
//! Components to build in-memory module trees that in Orchid. These modules
//! can only contain constants and other modules.
use std::fmt;
use dyn_clone::{clone_box, DynClone};
use orchid_api::expr::Expr;
use trait_set::trait_set;
use super::tpl;
use super::traits::{Gen, GenClause};
use crate::combine::Combine;
use crate::host::AtomHand;
use crate::tree::{ModEntry, ModMember, TreeConflict};
trait_set! {
trait TreeLeaf = Gen<Expr, [Expr; 0]> + DynClone + Send;
}
/// A leaf in the [ConstTree]
pub struct GenConst(Box<dyn TreeLeaf>);
impl GenConst {
fn c(data: impl GenClause + Send + Clone + 'static) -> Self { Self(Box::new(data)) }
}
impl fmt::Debug for GenConst {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{:?}", self.0) }
}
impl Clone for GenConst {
fn clone(&self) -> Self { Self(clone_box(&*self.0)) }
}
/// Error condition when constant trees that define the the same constant are
/// merged. Produced during system loading if multiple modules define the
/// same constant
#[derive(Debug, Clone)]
pub struct ConflictingConsts;
impl Combine for GenConst {
type Error = ConflictingConsts;
fn combine(self, _: Self) -> Result<Self, Self::Error> { Err(ConflictingConsts) }
}
/// A lightweight module tree that can be built declaratively by hand to
/// describe libraries of external functions in Rust. It implements [Combine]
/// for merging libraries.
pub type ConstTree = ModEntry<GenConst, (), ()>;
/// Describe a constant
#[must_use]
pub fn leaf(value: impl GenClause + Clone + Send + 'static) -> ConstTree {
ModEntry::wrap(ModMember::Item(GenConst::c(value)))
}
/// Describe a constant which appears in [ConstTree::tree].
///
/// The unarray tricks rustfmt into keeping this call as a single line even if
/// it chooses to break the argument into a block.
pub fn ent<K: AsRef<str>>(
key: K,
[g]: [impl GenClause + Clone + Send + 'static; 1],
) -> (K, ConstTree) {
(key, leaf(g))
}
/// Describe an [Atomic]
#[must_use]
pub fn atom_leaf(atom: AtomHand) -> ConstTree { leaf(tpl::SysAtom(atom)) }
/// Describe an [Atomic] which appears as an entry in a [ConstTree::tree]
///
/// The unarray is used to trick rustfmt into breaking the atom into a block
/// without breaking this call into a block
#[must_use]
pub fn atom_ent<K: AsRef<str>>(key: K, [atom]: [AtomHand; 1]) -> (K, ConstTree) {
(key, atom_leaf(atom))
}
/// Errors produced duriung the merger of constant trees
pub type ConstCombineErr = TreeConflict<GenConst, (), ()>;