forked from Orchid/orchid
in midst of refactor
This commit is contained in:
67
orchid-base/src/gen/impls.rs
Normal file
67
orchid-base/src/gen/impls.rs
Normal 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))))
|
||||
}
|
||||
}
|
||||
8
orchid-base/src/gen/mod.rs
Normal file
8
orchid-base/src/gen/mod.rs
Normal 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;
|
||||
61
orchid-base/src/gen/tpl.rs
Normal file
61
orchid-base/src/gen/tpl.rs
Normal 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() }
|
||||
}
|
||||
78
orchid-base/src/gen/traits.rs
Normal file
78
orchid-base/src/gen/traits.rs
Normal 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
|
||||
}
|
||||
}
|
||||
79
orchid-base/src/gen/tree.rs
Normal file
79
orchid-base/src/gen/tree.rs
Normal 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, (), ()>;
|
||||
Reference in New Issue
Block a user