Dead end with macros

This commit is contained in:
2023-03-05 19:55:38 +00:00
parent ca23edabe4
commit b9d47c3181
30 changed files with 1518 additions and 332 deletions

54
Cargo.lock generated
View File

@@ -28,6 +28,18 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "bitvec"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c"
dependencies = [
"funty",
"radium",
"tap",
"wyz",
]
[[package]]
name = "cfg-if"
version = "1.0.0"
@@ -82,12 +94,24 @@ dependencies = [
"syn",
]
[[package]]
name = "dyn-clone"
version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68b0cf012f1230e43cd00ebb729c6bb58707ecfa8ad08b52ef3a4ccd2697fc30"
[[package]]
name = "either"
version = "1.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
[[package]]
name = "funty"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c"
[[package]]
name = "getrandom"
version = "0.2.6"
@@ -160,14 +184,17 @@ checksum = "7709cef83f0c1f58f666e746a08b21e0085f7440fa6a29cc194d68aac97a4225"
name = "orchid"
version = "0.1.0"
dependencies = [
"bitvec",
"chumsky",
"derivative",
"dyn-clone",
"hashbrown",
"implicit-clone",
"itertools",
"lazy_static",
"mappable-rc",
"ordered-float",
"paste",
"smallvec",
"thiserror",
]
@@ -181,6 +208,12 @@ dependencies = [
"num-traits",
]
[[package]]
name = "paste"
version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d01a5bd0424d00070b0098dd17ebca6f961a959dead1dbcbbbc1d1cd8d3deeba"
[[package]]
name = "proc-macro-hack"
version = "0.5.19"
@@ -205,6 +238,12 @@ dependencies = [
"proc-macro2",
]
[[package]]
name = "radium"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09"
[[package]]
name = "smallvec"
version = "1.10.0"
@@ -222,6 +261,12 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "tap"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
[[package]]
name = "thiserror"
version = "1.0.31"
@@ -268,3 +313,12 @@ name = "wasi"
version = "0.10.2+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
[[package]]
name = "wyz"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed"
dependencies = [
"tap",
]

View File

@@ -16,3 +16,6 @@ itertools = "0.10"
smallvec = { version = "1.10.0", features = ['const_generics'] }
lazy_static = "1.4.0"
implicit-clone = "0.3.5"
bitvec = "1.0.1"
dyn-clone = "1.0.11"
paste = "1.0.11"

15
examples/lite/main.orc Normal file
View File

@@ -0,0 +1,15 @@
TRUE := \t.\f.t
FALSE := \t.\f.f
NOT := \x.x FALSE TRUE
AND := \x.\y.x y FALSE
OR := \x.\y. x TRUE y
Y := \f.(\x.f (x x))(\x.f (x x))
(! ...$expr) =10=> (NOT ...$expr)
(...$lhs & ...$rhs) =10=> (AND (...$lhs) (...$rhs))
(...$lhs | ...$rhs) =20=> (OR (...$lhs) (...$rhs))
main := (TRUE & TRUE | FALSE & FALSE)
(start_token ...$rest) ==> (carriage(()) ...$rest)
(..$prefix carriage($state) $next ..$rest) ==> (..$prefix $out carriage(??) ..$rest)

30
src/external/assertion_error.rs vendored Normal file
View File

@@ -0,0 +1,30 @@
use std::rc::Rc;
use std::fmt::Display;
use crate::foreign::ExternError;
use crate::representations::interpreted::Clause;
#[derive(Clone)]
pub struct AssertionError{
pub value: Clause,
pub assertion: &'static str,
}
impl AssertionError {
pub fn fail(value: Clause, assertion: &'static str) -> Result<!, Rc<dyn ExternError>> {
return Err(Self { value, assertion }.into_extern())
}
pub fn into_extern(self) -> Rc<dyn ExternError> {
Rc::new(self)
}
}
impl Display for AssertionError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Error: {:?} is not {}", self.value, self.assertion)
}
}
impl ExternError for AssertionError{}

4
src/external/mod.rs vendored Normal file
View File

@@ -0,0 +1,4 @@
mod numbers;
mod assertion_error;
use numbers::Multiply2;

38
src/external/numbers/mod.rs vendored Normal file
View File

@@ -0,0 +1,38 @@
mod numeric;
use numeric::Numeric;
use std::fmt::Debug;
use std::rc::Rc;
use std::hash::Hash;
use crate::{atomic_impl, atomic_redirect, externfn_impl, xfn_initial, xfn_middle, xfn_last, xfn};
use crate::foreign::{ExternError, ExternFn, Atom, Atomic};
use crate::representations::Primitive;
use crate::representations::interpreted::{Clause, InternalError};
// xfn_initial!(
// /// Multiply function
// Multiply2, Multiply1
// );
// xfn_middle!(
// /// Partially applied multiply function
// Multiply2, Multiply1, Multiply0, (
// a: Numeric: |c: &Clause| c.clone().try_into()
// )
// );
// xfn_last!(
// /// Fully applied Multiply function.
// Multiply1, Multiply0, (
// b: Numeric: |c: &Clause| c.clone().try_into(),
// a: Numeric: |c: &Clause| c.clone().try_into()
// ), Ok((*a * b).into())
// );
xfn!((
/// Multiply function
a: Numeric: |c: &Clause| c.clone().try_into(),
/// Partially applied multiply function
b: Numeric: |c: &Clause| c.clone().try_into()
), {
Ok((*a * b).into())
});

102
src/external/numbers/numeric.rs vendored Normal file
View File

@@ -0,0 +1,102 @@
use std::ops::{Add, Sub, Mul, Div, Rem};
use std::rc::Rc;
use ordered_float::NotNan;
use crate::external::assertion_error::AssertionError;
use crate::foreign::ExternError;
use crate::representations::{Primitive, Literal};
use crate::representations::interpreted::Clause;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Numeric {
Int(u64),
Num(NotNan<f64>)
}
impl Add for Numeric {
type Output = Numeric;
fn add(self, rhs: Self) -> Self::Output {
match (self, rhs) {
(Numeric::Int(a), Numeric::Int(b)) => Numeric::Int(a + b),
(Numeric::Num(a), Numeric::Num(b)) => Numeric::Num(a + b),
(Numeric::Int(a), Numeric::Num(b)) | (Numeric::Num(b), Numeric::Int(a))
=> Numeric::Num(NotNan::new(a as f64).unwrap() + b)
}
}
}
impl Sub for Numeric {
type Output = Numeric;
fn sub(self, rhs: Self) -> Self::Output {
match (self, rhs) {
(Numeric::Int(a), Numeric::Int(b)) if b < a => Numeric::Int(a - b),
(Numeric::Int(a), Numeric::Int(b))
=> Numeric::Num(NotNan::new(a as f64 - b as f64).unwrap()),
(Numeric::Num(a), Numeric::Num(b)) => Numeric::Num(a - b),
(Numeric::Int(a), Numeric::Num(b)) | (Numeric::Num(b), Numeric::Int(a))
=> Numeric::Num(NotNan::new(a as f64).unwrap() - b)
}
}
}
impl Mul for Numeric {
type Output = Numeric;
fn mul(self, rhs: Self) -> Self::Output {
match (self, rhs) {
(Numeric::Int(a), Numeric::Int(b)) => Numeric::Int(a * b),
(Numeric::Num(a), Numeric::Num(b)) => Numeric::Num(a * b),
(Numeric::Int(a), Numeric::Num(b)) | (Numeric::Num(b), Numeric::Int(a))
=> Numeric::Num(NotNan::new(a as f64).unwrap() * b)
}
}
}
impl Div for Numeric {
type Output = Numeric;
fn div(self, rhs: Self) -> Self::Output {
let a = match self { Numeric::Int(i) => i as f64, Numeric::Num(f) => *f };
let b = match rhs { Numeric::Int(i) => i as f64, Numeric::Num(f) => *f };
Numeric::Num(NotNan::new(a / b).unwrap())
}
}
impl Rem for Numeric {
type Output = Numeric;
fn rem(self, rhs: Self) -> Self::Output {
match (self, rhs) {
(Numeric::Int(a), Numeric::Int(b)) => Numeric::Int(a % b),
(Numeric::Num(a), Numeric::Num(b)) => Numeric::Num(a % b),
(Numeric::Int(a), Numeric::Num(b)) | (Numeric::Num(b), Numeric::Int(a))
=> Numeric::Num(NotNan::new(a as f64).unwrap() % b)
}
}
}
impl TryFrom<Clause> for Numeric {
type Error = Rc<dyn ExternError>;
fn try_from(value: Clause) -> Result<Self, Self::Error> {
let l = if let Clause::P(Primitive::Literal(l)) = value.clone() {l} else {
AssertionError::fail(value, "a literal value")?
};
match l {
Literal::Int(i) => Ok(Numeric::Int(i)),
Literal::Num(n) => Ok(Numeric::Num(n)),
_ => AssertionError::fail(value, "an integer or number")?
}
}
}
impl From<Numeric> for Clause {
fn from(value: Numeric) -> Self {
Clause::P(Primitive::Literal(match value {
Numeric::Int(i) => Literal::Int(i),
Numeric::Num(n) => Literal::Num(n)
}))
}
}

View File

@@ -1,75 +1,59 @@
use std::any::Any;
use std::fmt::{Display, Debug};
use std::hash::Hash;
use std::rc::Rc;
use mappable_rc::Mrc;
use dyn_clone::DynClone;
use crate::representations::typed::{Expr, Clause};
use crate::representations::interpreted::{Clause, RuntimeError, InternalError};
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>>>
pub trait ExternFn: DynClone {
fn name(&self) -> &str;
fn apply(&self, arg: Clause) -> Result<Clause, Rc<dyn ExternError>>;
fn argstr(&self) -> &str { "clause" }
fn retstr(&self) -> &str { "clause" }
fn hash(&self, state: &mut dyn std::hash::Hasher) { state.write_str(self.name()) }
}
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 {
impl Eq for dyn ExternFn {}
impl PartialEq for dyn 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 Hash for dyn ExternFn {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) { self.name().hash(state) }
}
impl Debug for ExternFn {
impl Debug for dyn ExternFn {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "##EXTERN[{}]:{:?} -> {:?}##", self.name(), self.param, self.rttype)
write!(f, "##EXTERN[{}]:{:?} -> {:?}##", self.name(), self.argstr(), self.retstr())
}
}
pub trait Atomic: Any + Debug where Self: 'static {
pub trait Atomic: Any + Debug + DynClone 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);
fn run_once(&self) -> Result<Clause, InternalError>;
fn run_n_times(&self, n: usize) -> Result<(Clause, usize), RuntimeError>;
fn run_to_completion(&self) -> Result<Clause, RuntimeError>;
fn typestr(&self) -> &str { "clause" }
}
/// 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>
}
/// Represents a black box unit of code with its own normalization steps. Typically [ExternFn]
/// will produce an [Atom] when applied to a [Clause], this [Atom] will then forward `run_*` calls
/// to the argument until it yields [InternalError::NonReducible] at which point the [Atom] will
/// validate and process the argument, returning a different [Atom] intended for processing by
/// external code, a new [ExternFn] to capture an additional argument, or an Orchid expression
/// to pass control back to the interpreter.
pub struct Atom(pub Box<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 new<T: 'static + Atomic>(data: T) -> Self {
Self(Box::new(data) as Box<dyn Atomic>)
}
pub fn data(&self) -> &dyn Atomic { self.0.as_ref() as &dyn Atomic }
pub fn try_cast<T: Atomic>(&self) -> Result<&T, ()> {
self.data().as_any().downcast_ref().ok_or(())
}
@@ -80,20 +64,19 @@ impl Atom {
}
impl Clone for Atom {
fn clone(&self) -> Self { Self {
typ: Mrc::clone(&self.typ),
data: Mrc::clone(&self.data)
} }
fn clone(&self) -> Self {
Self(dyn_clone::clone_box(self.data()))
}
}
impl Hash for Atom {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.data.hash(state);
self.typ.hash(state)
self.0.hash(state)
}
}
impl Debug for Atom {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "##ATOM[{:?}]:{:?}##", self.data(), self.typ)
write!(f, "##ATOM[{:?}]:{:?}##", self.data(), self.data().typestr())
}
}
impl Eq for Atom {}

333
src/foreign_macros.rs Normal file
View File

@@ -0,0 +1,333 @@
#[allow(unused)] // for the doc comments
use crate::representations::Primitive;
#[allow(unused)] // for the doc comments
use crate::foreign::{Atomic, ExternFn};
#[allow(unused)] // for the doc comments
use std::any::Any;
#[allow(unused)] // for the doc comments
use std::hash::Hash;
#[allow(unused)] // for the doc comments
use dyn_clone::DynClone;
#[allow(unused)] // for the doc comments
use std::fmt::Debug;
/// A macro that generates the straightforward, syntactically invariant part of implementing
/// [Atomic]. Implemented fns are [Atomic::as_any], [Atomic::definitely_eq] and [Atomic::hash].
///
/// It depends on [Eq] and [Hash]
#[macro_export]
macro_rules! atomic_defaults {
() => {
fn as_any(&self) -> &dyn std::any::Any { self }
fn definitely_eq(&self, _other: &dyn std::any::Any) -> bool {
_other.downcast_ref().map(|o| self == o).unwrap_or(false)
}
fn hash(&self, mut hasher: &mut dyn std::hash::Hasher) { <Self as Hash>::hash(self, &mut hasher) }
};
}
/// A macro that generates implementations of [Atomic] to simplify the development of
/// external bindings for Orchid.
///
/// The macro depends on implementations of [AsRef<Clause>] and [From<(&Self, Clause)>] for
/// extracting the clause to be processed and then reconstructing the [Atomic]. Naturally,
/// supertraits of [Atomic] are also dependencies. These are [Any], [Debug] and [DynClone].
///
/// The simplest form just requires the typename to be specified. This additionally depends on an
/// implementation of [ExternFn] because after the clause is fully normalized it returns `Self`
/// wrapped in a [Primitive::ExternFn]. It is intended for intermediary
/// stages of the function where validation and the next state are defined in [ExternFn::apply].
///
/// ```
/// atomic_impl!(Multiply1)
/// ```
///
/// The last stage of the function should use the extended form of the macro which takes an
/// additional closure to explicitly describe what happens when the argument is fully processed.
///
/// ```
/// // excerpt from the exact implementation of Multiply
/// atomic_impl!(Multiply0, |Self(a, cls): &Self| {
/// let b: Numeric = cls.clone().try_into().map_err(AssertionError::into_extern)?;
/// Ok(*a * b).into())
/// })
/// ```
///
#[macro_export]
macro_rules! atomic_impl {
($typ:ident) => {
atomic_impl!{$typ, |this: &Self| Ok(Clause::P(Primitive::ExternFn(Box::new(this.clone()))))}
};
($typ:ident, $next_phase:expr) => {
impl Atomic for $typ {
$crate::atomic_defaults!{}
fn run_once(&self) -> Result<Clause, $crate::representations::interpreted::InternalError> {
match <Self as AsRef<Clause>>::as_ref(self).run_once() {
Err(InternalError::NonReducible) => {
($next_phase)(self)
.map_err($crate::representations::interpreted::RuntimeError::Extern)
.map_err(InternalError::Runtime)
}
Ok(arg) => Ok(Clause::P(Primitive::Atom(Atom::new(
<Self as From<(&Self, Clause)>>::from((self, arg))
)))),
Err(e) => Err(e),
}
}
fn run_n_times(&self, n: usize) -> Result<(Clause, usize), $crate::representations::interpreted::RuntimeError> {
match <Self as AsRef<Clause>>::as_ref(self).run_n_times(n) {
Ok((arg, k)) if k == n => Ok((Clause::P(Primitive::Atom(Atom::new(
<Self as From<(&Self, Clause)>>::from((self, arg))))), k)),
Ok((arg, k)) => {
let intermediate = <Self as From<(&Self, Clause)>>::from((self, arg));
($next_phase)(&intermediate)
.map(|cls| (cls, k))
.map_err($crate::representations::interpreted::RuntimeError::Extern)
}
Err(e) => Err(e),
}
}
fn run_to_completion(&self) -> Result<Clause, $crate::representations::interpreted::RuntimeError> {
match <Self as AsRef<Clause>>::as_ref(self).run_to_completion() {
Ok(arg) => {
let intermediate = <Self as From<(&Self, Clause)>>::from((self, arg));
($next_phase)(&intermediate)
.map_err($crate::representations::interpreted::RuntimeError::Extern)
},
Err(e) => Err(e)
}
}
}
};
}
/// Implement the traits required by [atomic_impl] to redirect run_* functions to a field
/// with a particular name.
#[macro_export]
macro_rules! atomic_redirect {
($typ:ident) => {
impl AsRef<Clause> for $typ {
fn as_ref(&self) -> &Clause { &self.0 }
}
impl From<(&Self, Clause)> for $typ {
fn from((old, clause): (&Self, Clause)) -> Self {
Self{ 0: clause, ..old.clone() }
}
}
};
($typ:ident, $field:ident) => {
impl AsRef<Clause> for $typ {
fn as_ref(&self) -> &Clause { &self.$field }
}
impl From<(&Self, Clause)> for $typ {
fn from((old, $field): (&Self, Clause)) -> Self {
Self{ $field, ..old.clone() }
}
}
};
}
/// Implement [ExternFn] with a closure that produces an [Atomic] from a reference to self
/// and a closure. This can be used in conjunction with [atomic_impl] and [atomic_redirect]
/// to normalize the argument automatically before using it.
#[macro_export]
macro_rules! externfn_impl {
($typ:ident, $next_atomic:expr) => {
impl ExternFn for $typ {
fn name(&self) -> &str {stringify!($typ)}
fn apply(&self, c: Clause) -> Result<Clause, Rc<dyn ExternError>> {
match ($next_atomic)(self, c) { // ? casts the result but we want to strictly forward it
Ok(r) => Ok(Clause::P(Primitive::Atom(Atom::new(r)))),
Err(e) => Err(e)
}
}
}
};
}
/// Define a new struct and implement [ExternFn] for it so that it combines with an argument into
/// the struct identified by `$next` with a single field called `clause`.
/// Also generates doc comment for the new struct
#[macro_export]
macro_rules! xfn_initial {
(#[$prefix:meta] $name:ident, $next:ident) => {
paste::paste!{
#[$prefix]
#[doc = "\n\nNext state: [" [<$next:camel>] "]"]
#[derive(Clone)]
pub struct [<$name:camel>];
externfn_impl!([<$name:camel>], |_:&Self, clause: Clause| Ok([<$next:camel>]{ clause }));
}
};
}
/// Define a struct with a field `clause: Clause` that forwards atomic reductions to that field and
/// then converts itself to an [ExternFn] which combines with an argument into the struct identified
/// by `$next` with all fields copied over, `$nname` converted via `$ntransform` and the
/// argument assigned to a field called `clause`.
#[macro_export]
macro_rules! xfn_middle {
(#[$prefix:meta] $prev:ident, $name:ident, $next:ident, (
$nname:ident : $ntype:ty
$(, $fname:ident : $ftype:ty)*
), $transform:expr) => {
paste::paste!{
#[$prefix]
#[doc = "\n\nPrev state: [" [<$prev:camel>] "], Next state: [" [<$next:camel>] "]"]
#[derive(Debug, Clone, PartialEq, Hash)]
pub struct [<$name:camel>] {
clause: Clause,
$($fname: $ftype),*
}
atomic_redirect!([<$name:camel>], clause);
atomic_impl!([<$name:camel>]);
externfn_impl!([<$name:camel>], |this: &Self, clause: Clause| {
let $nname: $ntype = match ($transform)(&this.clause) {
Ok(a) => a,
Err(e) => return Err(e)
};
Ok([<$next:camel>]{
clause,
$nname,
$($fname: this.$fname.clone()),*
})
});
}
};
}
#[macro_export]
macro_rules! xfn_last {
(#[$prefix:meta] $prev:ident, $name:ident, (
$nname:ident : $ntype:ty
$(, $fname:ident : $ftype:ty)*
), $transform:expr, $operation:expr) => {
paste!{
#[$prefix]
#[doc = "\n\nPrev state: [" [<$prev:camel>] "]" ]
#[derive(Debug, Clone, PartialEq, Hash)]
pub struct [<$name:camel>] {
clause: Clause,
$($fname: $ftype),*
}
}
atomic_redirect!([<$name:camel>], clause);
atomic_impl!([<$name:camel>], |this: &Self| {
let $nname: $ntype = match ($ntransform)(&this.clause) {
Ok(a) => a,
Err(e) => return Err(e)
};
$(
let $fname = &this.$fname;
)*
$operation
});
};
}
#[macro_export]
macro_rules! reverse_proplist {
(
#[$nprefix:meta] $nname:ident : $ntype:ty : $ntransform:expr
$(, #[$fprefix:meta] $fname:ident : $ftype:ty : $ftransform:expr)*
) => {
reverse_proplist!($($fname : $ftype : $ftransform),*)
$nname : $ntype : $ntranform
};
}
#[macro_export]
macro_rules! xfn_make_head {
(
#[$cprefix:meta] $nname:ident : $ntype:ty : $ntransform:expr
, #[$nprefix:meta] $cname:ident : $ctype:ty : $ctransform:expr
$(, #[$fprefix:meta] $fname:ident : $ftype:ty : $ftransform:expr)+
) => {
xfn_make_head!(
#[$nprefix] $cname : $ctype : $ctransform
$(, #[$fprefix] $fname : $ftype : $ftransform)+
)
}; // skip through all intermediate rows
(
#[$cprefix:meta] $nname:ident : $ntype:ty : $ntransform:expr
, #[$nprefix:meta] $cname:ident : $ctype:ty : $ctransform:expr
) => {
paste!{
xfn_initial!(#[$cprefix] $cname, $nname)
}
} // process first two rows
}
#[macro_export]
macro_rules! xfn_make_middle {
(
#[$nprefix:meta] $nname:ident : $ntype:ty : $ntransform:expr
, #[$cprefix:meta] $cname:ident : $ctype:ty : $ctransform:expr
, #[$pprefix:meta] $pname:ident : $ptype:ty : $ptransform:expr
$(, #[$fprefix:meta] $fname:ident : $ftype:ty : $ftransform:expr)*
) => {
// repeat on tail
xfn_make_middle!(
#[$cprefix:meta] $cname:ident : $ctype:ty : $ctransform:expr
, #[$pprefix:meta] $pname:ident : $ptype:ty : $ptransform:expr
$(, #[$fprefix:meta] $fname:ident : $ftype:ty : $ftransform:expr)*
)
xfn_middle!(#[$cprefix] $pname, $cname, $nname (
$cname : $ctype
, $pname : $ptype
$(, $fname : $ftype )*
), $ctransform) // note that the "next" row is not included
};
(
#[$nprefix:meta] $nname:ident : $ntype:ty : $ntransform:expr
, #[$cprefix:meta] $cname:ident : $ctype:ty : $ctransform:expr
) => {}; // discard last two rows (xfn_make_head handles those)
}
#[macro_export]
macro_rules! xfn_make_last {
((
#[$cprefix:meta] $cname:ident : $ctype:ty : $ctransform:expr
, #[$pprefix:meta] $pname:ident : $ptype:ty : $ptransform:expr
$(, #[$fprefix:meta] $fname:ident : $ftype:ty : $ftransform:expr)*
), $operation:expr) => {
xfn_last!(
#[$cprefix] $pname, $cname, (
$cname : $ctype
, $pname : $ptype
$(, $fname : $ftype)*
), $ctransform, $operation
)
};
}
#[macro_export]
macro_rules! xfn_reversed {
(
( $(#[$fprefix:meta] $fname:ident : $ftype:ty : $ftransform:expr),* ),
$operation:expr
) => {
xfn_make_head!($(#[$fprefix] $fname : $ftype : $ftransform),*)
xfn_make_middle!(
$(#[$fprefix] $fname : $ftype : $ftransform),*
)
xfn_make_last(
( $(#[$fprefix] $fname : $ftype : $ftransform),* ),
$operation
)
};
}
#[macro_export]
macro_rules! xfn {
(
( $(#[$fprefix:meta] $fname:ident : $ftype:ty : $ftransform:expr),* ),
$operation:expr
) => {
$crate::xfn_reversed!(
reverse_proplist!($(#[$fprefix] $fname : $ftype : $ftransform),*),
$operation
);
};
}

View File

@@ -3,11 +3,15 @@
#![feature(adt_const_params)]
#![feature(generic_const_exprs)]
#![feature(generators, generator_trait)]
#![feature(never_type)]
#![feature(unwrap_infallible)]
#![feature(arc_unwrap_or_clone)]
#![feature(hasher_prefixfree_extras)]
#![feature(closure_lifetime_binder)]
use std::{env::current_dir, io};
use std::env::current_dir;
mod executor;
// mod executor;
mod parse;
mod project;
mod utils;
@@ -15,15 +19,19 @@ mod representations;
mod rule;
mod scheduler;
pub(crate) mod foreign;
mod external;
mod foreign_macros;
use file_loader::LoadingError;
pub use representations::ast;
use ast::{Expr, Clause};
use representations::typed as t;
// use representations::typed as t;
use mappable_rc::Mrc;
use project::{rule_collector, Loaded, file_loader};
use rule::Repository;
use utils::{to_mrc_slice, mrc_empty_slice, one_mrc_slice};
use crate::representations::{ast_to_postmacro, postmacro_to_interpreted, interpreted};
fn literal(orig: &[&str]) -> Mrc<[String]> {
to_mrc_slice(vliteral(orig))
}
@@ -50,23 +58,23 @@ fn initial_tree() -> Mrc<[Expr]> {
}
#[allow(unused)]
fn typed_notation_debug() {
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, 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();
println!("{:?}", t::Clause::Apply(t::Clause::Apply(Mrc::clone(&true_ex), true_ex).wrap(), false_ex))
}
// fn typed_notation_debug() {
// 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, 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();
// println!("{:?}", t::Clause::Apply(t::Clause::Apply(Mrc::clone(&true_ex), true_ex).wrap(), false_ex))
// }
#[allow(unused)]
fn load_project() {
@@ -95,7 +103,14 @@ fn load_project() {
},
Err(e) => panic!("Rule error: {e:?}")
}
}; println!("Macro execution didn't halt"));
}; panic!("Macro execution didn't halt"));
let pmtree = ast_to_postmacro::exprv(tree.as_ref())
.unwrap_or_else(|e| panic!("Postmacro conversion error: {e}"));
let runtree = postmacro_to_interpreted::expr_rec(&pmtree)
.unwrap_or_else(|e| panic!("Interpreted conversion error: {e}"));
let stable = runtree.run_to_completion()
.unwrap_or_else(|e| panic!("Runtime error {e}"));
println!("Settled at {stable:?}")
}
fn main() {

View File

@@ -1,8 +1,9 @@
use chumsky::{self, prelude::*, Parser};
use mappable_rc::Mrc;
use crate::enum_parser;
use crate::representations::Primitive;
use crate::representations::{Literal, ast::{Clause, Expr}};
use crate::utils::{to_mrc_slice, one_mrc_slice};
use crate::utils::to_mrc_slice;
use super::lexer::Lexeme;
@@ -92,7 +93,7 @@ pub fn xpr_parser() -> impl Parser<Lexeme, Expr, Error = Simple<Lexeme>> {
let clause =
enum_parser!(Lexeme::Comment).repeated()
.ignore_then(choice((
enum_parser!(Lexeme >> Literal; Int, Num, Char, Str).map(Clause::Literal),
enum_parser!(Lexeme >> Literal; Int, Num, Char, Str).map(Primitive::Literal).map(Clause::P),
placeholder_parser().map(|key| Clause::Placeh{key, vec: None}),
just(Lexeme::name("...")).to(true)
.or(just(Lexeme::name("..")).to(false))

View File

@@ -4,9 +4,9 @@ use ordered_float::NotNan;
use std::{hash::Hash, intrinsics::likely};
use std::fmt::Debug;
use crate::utils::mrc_empty_slice;
use crate::{foreign::{ExternFn, Atom}, utils::one_mrc_slice};
use crate::utils::one_mrc_slice;
use super::Literal;
use super::primitive::Primitive;
/// An S-expression with a type
#[derive(PartialEq, Eq, Hash)]
@@ -38,8 +38,7 @@ impl Debug for Expr {
/// An S-expression as read from a source file
#[derive(PartialEq, Eq, Hash)]
pub enum Clause {
/// A literal value, eg. `1`, `"hello"`
Literal(Literal),
P(Primitive),
/// A c-style name or an operator, eg. `+`, `i`, `foo::bar`
Name{
local: Option<String>,
@@ -53,12 +52,6 @@ pub enum Clause {
Lambda(String, Mrc<[Expr]>, Mrc<[Expr]>),
/// A parameterized expression with type inference, eg. `@T. T -> T`
Auto(Option<String>, Mrc<[Expr]>, Mrc<[Expr]>),
/// An opaque function, eg. an effectful function employing CPS.
/// Preferably wrap these in an Orchid monad.
ExternFn(ExternFn),
/// An opaque non-callable value, eg. a file handle.
/// Preferably wrap these in an Orchid structure.
Atom(Atom),
/// A placeholder for macros, eg. `$name`, `...$body`, `...$lhs:1`
Placeh{
key: String,
@@ -111,9 +104,7 @@ impl Clone for Clause {
n.clone(), Mrc::clone(t), Mrc::clone(b)
),
Self::Placeh{key, vec} => Self::Placeh{key: key.clone(), vec: *vec},
Self::Literal(l) => Self::Literal(l.clone()),
Self::ExternFn(nc) => Self::ExternFn(nc.clone()),
Self::Atom(a) => Self::Atom(a.clone()),
Self::P(p) => Self::P(p.clone()),
Self::Explicit(expr) => Self::Explicit(Mrc::clone(expr))
}
}
@@ -130,7 +121,7 @@ fn fmt_expr_seq(it: &mut dyn Iterator<Item = &Expr>, f: &mut std::fmt::Formatter
impl Debug for Clause {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Literal(arg0) => write!(f, "{:?}", arg0),
Self::P(p) => write!(f, "{:?}", p),
Self::Name{local, qualified} =>
if let Some(local) = local {write!(f, "{}`{}`", qualified.join("::"), local)}
else {write!(f, "{}", qualified.join("::"))},
@@ -157,8 +148,6 @@ impl Debug for Clause {
Self::Placeh{key, vec: None} => write!(f, "${key}"),
Self::Placeh{key, vec: Some((prio, true))} => write!(f, "...${key}:{prio}"),
Self::Placeh{key, vec: Some((prio, false))} => write!(f, "..${key}:{prio}"),
Self::ExternFn(nc) => write!(f, "{nc:?}"),
Self::Atom(a) => write!(f, "{a:?}"),
Self::Explicit(expr) => write!(f, "@{:?}", expr.as_ref())
}
}

View File

@@ -0,0 +1,173 @@
use std::{rc::Rc, fmt::Display};
use crate::utils::Stackframe;
use super::{ast, postmacro};
#[derive(Clone)]
pub enum Error {
/// `()` as a clause is meaningless in lambda calculus
EmptyS,
/// Only `(...)` may be converted to typed lambdas. `[...]` and `{...}` left in the code are
/// signs of incomplete macro execution
BadGroup(char),
/// `foo:bar:baz` will be parsed as `(foo:bar):baz`. Explicitly specifying `foo:(bar:baz)`
/// is forbidden and it's also meaningless since `baz` can only ever be the kind of types
ExplicitKindOfType,
/// Name never bound in an enclosing scope - indicates incomplete macro substitution
Unbound(String),
/// Namespaced names can never occur in the code, these are signs of incomplete macro execution
Symbol,
/// Placeholders shouldn't even occur in the code during macro execution. Something is clearly
/// terribly wrong
Placeholder,
/// It's possible to try and transform the clause `(foo:bar)` into a typed clause,
/// however the correct value of this ast clause is a typed expression (included in the error)
///
/// [expr] handles this case, so it's only really possible to get this
/// error if you're calling [clause] directly
ExprToClause(postmacro::Expr),
/// @ tokens only ever occur between a function and a parameter
NonInfixAt
}
impl Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Error::EmptyS => write!(f, "`()` as a clause is meaningless in lambda calculus"),
Error::BadGroup(c) => write!(f, "Only `(...)` may be converted to typed lambdas. `[...]` and `{{...}}` left in the code are signs of incomplete macro execution"),
Error::ExplicitKindOfType => write!(f, "`foo:bar:baz` will be parsed as `(foo:bar):baz`. Explicitly specifying `foo:(bar:baz)` is forbidden and meaningless since `baz` can only ever be the kind of types"),
Error::Unbound(name) => write!(f, "Name \"{name}\" never bound in an enclosing scope. This indicates incomplete macro substitution"),
Error::Symbol => write!(f, "Namespaced names not matching any macros found in the code."),
Error::Placeholder => write!(f, "Placeholders shouldn't even occur in the code during macro execution, this is likely a compiler bug"),
Error::ExprToClause(expr) => write!(f, "Attempted to transform the clause (foo:bar) into a typed clause. This is likely a compiler bug"),
Error::NonInfixAt => write!(f, "@ as a token can only ever occur between a generic and a type parameter.")
}
}
}
/// Try to convert an expression from AST format to typed lambda
pub fn expr(expr: &ast::Expr) -> Result<postmacro::Expr, Error> {
expr_rec(expr, Context::default())
}
/// Try and convert a single clause from AST format to typed lambda
pub fn clause(clause: &ast::Clause) -> Result<postmacro::Clause, Error> {
clause_rec(clause, Context::default())
}
/// Try and convert a sequence of expressions from AST format to typed lambda
pub fn exprv(exprv: &[ast::Expr]) -> Result<postmacro::Expr, Error> {
exprv_rec(exprv, Context::default())
}
#[derive(Clone, Copy)]
struct Context<'a> {
names: Stackframe<'a, (&'a str, bool)>
}
impl<'a> Context<'a> {
fn w_name<'b>(&'b self, name: &'b str, is_auto: bool) -> Context<'b> where 'a: 'b {
Context { names: self.names.push((name, is_auto)) }
}
}
impl Default for Context<'static> {
fn default() -> Self {
Self { names: Stackframe::new(("", false)) }
}
}
/// Recursive state of [exprv]
fn exprv_rec<'a>(
v: &'a [ast::Expr],
ctx: Context<'a>,
) -> Result<postmacro::Expr, Error> {
let (last, rest) = v.split_last().ok_or(Error::EmptyS)?;
if rest.len() == 0 {return expr_rec(&v[0], ctx)}
let clause = if let ast::Expr(ast::Clause::Explicit(inner), empty_slice) = last {
assert!(empty_slice.len() == 0,
"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(), ctx)?;
postmacro::Clause::Explicit(Rc::new(exprv_rec(rest, ctx)?), Rc::new(x))
} else {
let f = exprv_rec(rest, ctx)?;
let x = expr_rec(last, ctx)?;
postmacro::Clause::Apply(Rc::new(f), Rc::new(x))
};
Ok(postmacro::Expr(clause, Rc::new(vec![])))
}
/// Recursive state of [expr]
fn expr_rec<'a>(
ast::Expr(val, typ): &'a ast::Expr,
ctx: Context<'a>
) -> Result<postmacro::Expr, Error> { // (output, used_explicits)
let typ: Vec<postmacro::Clause> = typ.iter()
.map(|c| clause_rec(c, ctx))
.collect::<Result<_, _>>()?;
if let ast::Clause::S(paren, body) = val {
if *paren != '(' {return Err(Error::BadGroup(*paren))}
let postmacro::Expr(inner, inner_t) = exprv_rec(body.as_ref(), ctx)?;
let new_t =
if typ.len() == 0 { inner_t }
else if inner_t.len() == 0 { Rc::new(typ) }
else { Rc::new(inner_t.iter().chain(typ.iter()).cloned().collect()) };
Ok(postmacro::Expr(inner, new_t))
} else {
let cls = clause_rec(&val, ctx)?;
Ok(postmacro::Expr(cls, Rc::new(typ)))
}
}
// (\t:(@T. Pair T T). t \left.\right. left) @number -- this will fail
// (@T. \t:Pair T T. t \left.\right. left) @number -- this is the correct phrasing
/// Recursive state of [clause]
fn clause_rec<'a>(
cls: &'a ast::Clause,
ctx: Context<'a>
) -> Result<postmacro::Clause, Error> {
match cls {
ast::Clause::P(p) => Ok(postmacro::Clause::P(p.clone())),
ast::Clause::Auto(no, t, b) => {
let typ = if t.len() == 0 {Rc::new(vec![])} else {
let postmacro::Expr(c, t) = exprv_rec(t.as_ref(), ctx)?;
if t.len() > 0 {return Err(Error::ExplicitKindOfType)}
else {Rc::new(vec![c])}
};
let body_ctx = if let Some(name) = no {
ctx.w_name(&&**name, true)
} else {ctx};
let body = exprv_rec(b.as_ref(), body_ctx)?;
Ok(postmacro::Clause::Auto(typ, Rc::new(body)))
}
ast::Clause::Lambda(n, t, b) => {
let typ = if t.len() == 0 {Rc::new(vec![])} else {
let postmacro::Expr(c, t) = exprv_rec(t.as_ref(), ctx)?;
if t.len() > 0 {return Err(Error::ExplicitKindOfType)}
else {Rc::new(vec![c])}
};
let body_ctx = ctx.w_name(&&**n, false);
let body = exprv_rec(b.as_ref(), body_ctx)?;
Ok(postmacro::Clause::Lambda(typ, Rc::new(body)))
}
ast::Clause::Name { local: Some(arg), .. } => {
let (level, (_, is_auto)) = ctx.names.iter().enumerate().find(|(_, (n, _))| n == arg)
.ok_or_else(|| Error::Unbound(arg.clone()))?;
let label = if *is_auto {postmacro::Clause::AutoArg} else {postmacro::Clause::LambdaArg};
Ok(label(level))
}
ast::Clause::S(paren, entries) => {
if *paren != '(' {return Err(Error::BadGroup(*paren))}
let postmacro::Expr(val, typ) = exprv_rec(entries.as_ref(), ctx)?;
if typ.len() == 0 {Ok(val)}
else {Err(Error::ExprToClause(postmacro::Expr(val, typ)))}
},
ast::Clause::Name { local: None, .. } => Err(Error::Symbol),
ast::Clause::Placeh { .. } => Err(Error::Placeholder),
ast::Clause::Explicit(..) => Err(Error::NonInfixAt)
}
}

View File

@@ -1,171 +0,0 @@
use mappable_rc::Mrc;
use crate::utils::{Stackframe, to_mrc_slice, mrc_empty_slice, ProtoMap, one_mrc_slice};
use super::{ast, typed, get_name::get_name};
#[derive(Clone)]
pub enum Error {
/// `()` as a clause is meaningless in lambda calculus
EmptyS,
/// Only `(...)` may be converted to typed lambdas. `[...]` and `{...}` left in the code are
/// signs of incomplete macro execution
BadGroup(char),
/// `foo:bar:baz` will be parsed as `(foo:bar):baz`, explicitly specifying `foo:(bar:baz)`
/// is forbidden and it's also meaningless since `baz` can only ever be the kind of types
ExplicitBottomKind,
/// Name never bound in an enclosing scope - indicates incomplete macro substitution
Unbound(String),
/// Namespaced names can never occur in the code, these are signs of incomplete macro execution
Symbol,
/// Placeholders shouldn't even occur in the code during macro execution. Something is clearly
/// terribly wrong
Placeholder,
/// It's possible to try and transform the clause `(foo:bar)` into a typed clause,
/// however the correct value of this ast clause is a typed expression (included in the error)
///
/// [expr] handles this case, so it's only really possible to get this
/// error if you're calling [clause] directly
ExprToClause(typed::Expr),
/// @ tokens only ever occur between a function and a parameter
NonInfixAt
}
/// Try to convert an expression from AST format to typed lambda
pub fn expr(expr: &ast::Expr) -> Result<typed::Expr, Error> {
Ok(expr_rec(expr, ProtoMap::new(), None)?.0)
}
/// Try and convert a single clause from AST format to typed lambda
pub fn clause(clause: &ast::Clause) -> Result<typed::Clause, Error> {
Ok(clause_rec(clause, ProtoMap::new(), None)?.0)
}
/// Try and convert a sequence of expressions from AST format to typed lambda
pub fn exprv(exprv: &[ast::Expr]) -> Result<typed::Expr, Error> {
Ok(exprv_rec(exprv, ProtoMap::new(), None)?.0)
}
const NAMES_INLINE_COUNT:usize = 3;
/// Recursive state of [exprv]
fn exprv_rec<'a>(
v: &'a [ast::Expr],
names: ProtoMap<&'a 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)?;
if rest.len() == 0 {return expr_rec(&v[0], names, explicits)}
if let ast::Expr(ast::Clause::Explicit(inner), empty_slice) = last {
assert!(empty_slice.len() == 0,
"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))?;
Ok((body, used_expls.saturating_sub(1)))
} else {
let (f, f_used_expls) = exprv_rec(rest, names.clone(), explicits)?;
let x_explicits = Stackframe::opop(explicits, f_used_expls);
let (x, x_used_expls) = expr_rec(last, names, x_explicits)?;
Ok((typed::Expr(
typed::Clause::Apply(Mrc::new(f), Mrc::new(x)),
mrc_empty_slice()
), x_used_expls + f_used_expls))
}
}
/// Recursive state of [expr]
fn expr_rec<'a>(
ast::Expr(val, typ): &'a ast::Expr,
names: ProtoMap<&'a 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))
.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 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()
})
};
Ok((typed::Expr(inner, new_t), used_expls))
} else {
let (cls, used_expls) = clause_rec(&val, names, explicits)?;
Ok((typed::Expr(cls, to_mrc_slice(typ)), used_expls))
}
}
/// Recursive state of [clause]
fn clause_rec<'a>(
cls: &'a ast::Clause,
mut names: ProtoMap<&'a 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
ast::Clause::ExternFn(e) => Ok((typed::Clause::ExternFn(e.clone()), 0)),
ast::Clause::Atom(a) => Ok((typed::Clause::Atom(a.clone()), 0)),
ast::Clause::Auto(no, t, b) => {
// Allocate id
let id = get_name();
// Pop an explicit if available
let (value, rest_explicits) = explicits.map(
|Stackframe{ prev, item, .. }| {
(Some(item), *prev)
}
).unwrap_or_default();
explicits = rest_explicits;
// Convert the type
let typ = if t.len() == 0 {mrc_empty_slice()} else {
let (typed::Expr(c, t), _) = exprv_rec(t.as_ref(), names.clone(), None)?;
if t.len() > 0 {return Err(Error::ExplicitBottomKind)}
else {one_mrc_slice(c)}
};
// Traverse body with extended context
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(
typed::Clause::Lambda(id, typ, Mrc::new(body)).wrap(),
Mrc::clone(known_value)
), used_expls + 1))
} else {
Ok((typed::Clause::Auto(id, typ, Mrc::new(body)), 0))
}
}
ast::Clause::Lambda(n, t, b) => {
// Allocate id
let id = get_name();
// Convert the type
let typ = if t.len() == 0 {mrc_empty_slice()} else {
let (typed::Expr(c, t), _) = exprv_rec(t.as_ref(), names.clone(), None)?;
if t.len() > 0 {return Err(Error::ExplicitBottomKind)}
else {one_mrc_slice(c)}
};
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, is_auto) = names.get(&&**arg)
.ok_or_else(|| Error::Unbound(arg.clone()))?;
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)?;
if typ.len() == 0 {Ok((val, used_expls))}
else {Err(Error::ExprToClause(typed::Expr(val, typ)))}
},
ast::Clause::Name { local: None, .. } => Err(Error::Symbol),
ast::Clause::Placeh { .. } => Err(Error::Placeholder),
ast::Clause::Explicit(..) => Err(Error::NonInfixAt)
}
}

View File

@@ -0,0 +1,191 @@
use std::fmt::{Display, Debug};
use std::rc::Rc;
use crate::utils::Side;
use crate::foreign::{ExternError, Atom};
use super::path_set::PathSet;
use super::primitive::Primitive;
#[derive(Clone, PartialEq, Eq, Hash)]
pub enum Clause {
P(Primitive),
Apply{
f: Rc<Self>,
x: Rc<Self>,
id: usize
},
Lambda{
args: Option<PathSet>,
body: Rc<Self>
},
LambdaArg,
}
impl Debug for Clause {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Clause::P(p) => write!(f, "{p:?}"),
Clause::LambdaArg => write!(f, "arg"),
Clause::Apply { f: fun, x, id } => write!(f, "({:?} {:?})@{}", fun.as_ref(), x.as_ref(), id),
Clause::Lambda { args, body } => {
write!(f, "\\")?;
match args {
Some(path) => write!(f, "{path:?}")?,
None => write!(f, "_")?,
}
write!(f, ".")?;
write!(f, "{:?}", body.as_ref())
}
}
}
}
/// Problems in the process of execution
#[derive(Clone)]
pub enum RuntimeError {
Extern(Rc<dyn ExternError>),
NonFunctionApplication(usize),
}
impl Display for RuntimeError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Extern(e) => write!(f, "Error in external function: {e}"),
Self::NonFunctionApplication(loc) => write!(f, "Primitive applied as function at {loc}")
}
}
}
/// Various reasons why a new clause might not have been produced
#[derive(Clone)]
pub enum InternalError {
Runtime(RuntimeError),
NonReducible
}
fn map_at<E, F: FnOnce(&Clause) -> Result<Clause, E>>(
path: &[Side], source: &Clause, mapper: F
) -> Result<Clause, E> {
// Pass right through lambdas
if let Clause::Lambda { args, body } = source {
return Ok(Clause::Lambda {
args: args.clone(),
body: Rc::new(map_at(path, body, mapper)?)
})
}
// If the path ends here, process the next (non-lambda) node
let (head, tail) = if let Some(sf) = path.split_first() {sf} else {
return mapper(source)
};
// If it's an Apply, execute the next step in the path
if let Clause::Apply { f, x, id } = source {
return Ok(match head {
Side::Left => Clause::Apply {
f: Rc::new(map_at(tail, f, mapper)?),
x: x.clone(),
id: *id
},
Side::Right => Clause::Apply {
f: f.clone(),
x: Rc::new(map_at(tail, x, mapper)?),
id: *id
}
})
}
panic!("Invalid path")
}
fn substitute(PathSet { steps, next }: &PathSet, value: &Clause, body: &Clause) -> Clause {
map_at(&steps, body, |checkpoint| -> Result<Clause, !> {
match (checkpoint, next) {
(Clause::Lambda{..}, _) => unreachable!("Handled by map_at"),
(Clause::Apply { f, x, id }, Some((left, right))) => Ok(Clause::Apply {
f: Rc::new(substitute(left, value, f)),
x: Rc::new(substitute(right, value, x)),
id: *id
}),
(Clause::LambdaArg, None) => Ok(value.clone()),
(_, None) => panic!("Substitution path ends in something other than LambdaArg"),
(_, Some(_)) => panic!("Substitution path leads into something other than Apply"),
}
}).into_ok()
}
fn apply(f: &Clause, x: Rc<Clause>, id: usize) -> Result<Clause, InternalError> {
match f {
Clause::P(Primitive::ExternFn(f)) => f.apply(x.as_ref().clone())
.map_err(|e| InternalError::Runtime(RuntimeError::Extern(e))),
fex@Clause::Apply{..} => Ok(Clause::Apply{ // Don't execute the pre-function expression
f: Rc::new(fex.run_once()?), // take a step in resolving it instead
x, id
}),
Clause::Lambda{args, body} => Ok(if let Some(args) = args {
substitute(args, x.as_ref(), body)
} else {body.as_ref().clone()}),
_ => Err(InternalError::Runtime(RuntimeError::NonFunctionApplication(id)))
}
}
impl Clause {
pub fn run_once(&self) -> Result<Self, InternalError> {
match self {
Clause::Apply{f, x, id} => apply(f.as_ref(), x.clone(), *id),
Clause::P(Primitive::Atom(Atom(data))) => data.run_once(),
_ => Err(InternalError::NonReducible)
}
}
pub fn run_n_times(&self, n: usize) -> Result<(Self, usize), RuntimeError> {
let mut i = self.clone();
let mut done = 0;
while done < n {
match match &i {
Clause::Apply{f, x, id} => match apply(f.as_ref(), x.clone(), *id) {
Err(e) => Err(e),
Ok(c) => {
i = c;
done += 1;
Ok(())
}
},
Clause::P(Primitive::Atom(Atom(data))) => match data.run_n_times(n - done) {
Err(e) => Err(InternalError::Runtime(e)),
Ok((c, n)) => {
i = c;
done += n;
Ok(())
}
},
_ => Err(InternalError::NonReducible)
} {
Err(InternalError::NonReducible) => return Ok((i, done)),
Err(InternalError::Runtime(e)) => return Err(e),
Ok(()) => ()
}
}
return Ok((i, done));
}
pub fn run_to_completion(&self) -> Result<Self, RuntimeError> {
let mut i = self.clone();
loop {
match match &i {
Clause::Apply { f, x, id } => match apply(f.as_ref(), x.clone(), *id) {
Err(e) => Err(e),
Ok(c) => Ok(i = c)
},
Clause::P(Primitive::Atom(Atom(data))) => match data.run_to_completion() {
Err(e) => Err(InternalError::Runtime(e)),
Ok(c) => Ok(i = c)
},
_ => Err(InternalError::NonReducible)
} {
Err(InternalError::NonReducible) => break,
Err(InternalError::Runtime(e)) => return Err(e),
Ok(()) => ()
}
};
Ok(i)
}
}

View File

@@ -1,6 +1,12 @@
pub mod ast;
pub mod typed;
// pub mod typed;
pub mod literal;
pub mod ast_to_typed;
pub mod ast_to_postmacro;
pub mod get_name;
pub(crate) mod interpreted;
mod postmacro;
mod primitive;
mod path_set;
pub use primitive::Primitive;
pub mod postmacro_to_interpreted;
pub use literal::Literal;

View File

@@ -0,0 +1,96 @@
use std::rc::Rc;
use std::ops::Add;
use std::fmt::Debug;
use crate::utils::Side;
/// A set of paths into a Lambda expression
#[derive(Clone, PartialEq, Eq, Hash)]
pub struct PathSet {
/// The definite steps
pub steps: Rc<Vec<Side>>,
/// if Some, it splits. If None, it ends.
pub next: Option<(Rc<PathSet>, Rc<PathSet>)>
}
impl Debug for PathSet {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
for s in self.steps.as_ref() {
match s {
Side::Left => write!(f, "L")?,
Side::Right => write!(f, "R")?,
}
}
match &self.next {
Some((l, r)) => write!(f, "({l:?}|{r:?})")?,
None => write!(f, "x")?,
}
Ok(())
}
}
impl Add for PathSet {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
Self {
steps: Rc::new(vec![]),
next: Some((Rc::new(self), Rc::new(rhs)))
}
}
}
impl Add<Side> for PathSet {
type Output = Self;
fn add(self, rhs: Side) -> Self::Output {
let PathSet{ steps, next } = self;
let mut new_steps = Rc::unwrap_or_clone(steps);
new_steps.insert(0, rhs);
Self {
steps: Rc::new(new_steps),
next,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_combine() -> Result<(), &'static str> {
let ps1 = PathSet { next: None, steps: Rc::new(vec![Side::Left, Side::Left]) };
let ps2 = PathSet { next: None, steps: Rc::new(vec![Side::Left, Side::Right]) };
let sum = ps1.clone() + ps2.clone();
assert_eq!(sum.steps.as_ref(), &[]);
let nexts = sum.next.ok_or("nexts not set")?;
assert_eq!(nexts.0.as_ref(), &ps1);
assert_eq!(nexts.1.as_ref(), &ps2);
Ok(())
}
fn extend_scaffold() -> PathSet {
PathSet {
next: Some((
Rc::new(PathSet { next: None, steps: Rc::new(vec![Side::Left, Side::Left]) }),
Rc::new(PathSet { next: None, steps: Rc::new(vec![Side::Left, Side::Right]) })
)),
steps: Rc::new(vec![Side::Left, Side::Right, Side::Left])
}
}
#[test]
fn test_extend_noclone() {
let ps = extend_scaffold();
let new = ps + Side::Left;
assert_eq!(new.steps.as_ref().as_slice(), &[Side::Left, Side::Left, Side::Right, Side::Left])
}
#[test]
fn test_extend_clone() {
let ps = extend_scaffold();
let _anchor = ps.clone();
let new = ps + Side::Left;
assert_eq!(new.steps.len(), 4);
}
}

View File

@@ -0,0 +1,120 @@
use crate::utils::string_from_charset;
use super::primitive::Primitive;
use super::ast_to_postmacro;
use super::ast;
use std::fmt::{Debug, Write};
use std::rc::Rc;
/// Indicates whether either side needs to be wrapped. Syntax whose end is ambiguous on that side
/// must use parentheses, or forward the flag
#[derive(PartialEq, Eq, Clone, Copy)]
struct Wrap(bool, bool);
#[derive(PartialEq, Eq, Hash, Clone)]
pub struct Expr(pub Clause, pub Rc<Vec<Clause>>);
impl Expr {
fn deep_fmt(&self, f: &mut std::fmt::Formatter<'_>, depth: usize, tr: Wrap) -> std::fmt::Result {
let Expr(val, typ) = self;
if typ.len() > 0 {
val.deep_fmt(f, depth, Wrap(true, true))?;
for typterm in typ.as_ref() {
f.write_char(':')?;
typterm.deep_fmt(f, depth, Wrap(true, true))?;
}
} else {
val.deep_fmt(f, depth, tr)?;
}
Ok(())
}
}
impl Debug for Expr {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.deep_fmt(f, 0, Wrap(false, false))
}
}
#[derive(PartialEq, Eq, Hash, Clone)]
pub enum Clause {
Apply(Rc<Expr>, Rc<Expr>),
Explicit(Rc<Expr>, Rc<Expr>),
Lambda(Rc<Vec<Clause>>, Rc<Expr>),
Auto(Rc<Vec<Clause>>, Rc<Expr>),
LambdaArg(usize),
AutoArg(usize),
P(Primitive),
}
const ARGNAME_CHARSET: &str = "abcdefghijklmnopqrstuvwxyz";
fn parametric_fmt(
f: &mut std::fmt::Formatter<'_>, depth: usize,
prefix: &str, argtyp: &[Clause], body: &Expr, wrap_right: bool
) -> std::fmt::Result {
if wrap_right { f.write_char('(')?; }
f.write_str(prefix)?;
f.write_str(&string_from_charset(depth as u64, ARGNAME_CHARSET))?;
for typ in argtyp.iter() {
f.write_str(":")?;
typ.deep_fmt(f, depth, Wrap(false, false))?;
}
f.write_str(".")?;
body.deep_fmt(f, depth + 1, Wrap(false, false))?;
if wrap_right { f.write_char(')')?; }
Ok(())
}
impl Clause {
fn deep_fmt(&self, f: &mut std::fmt::Formatter<'_>, depth: usize, Wrap(wl, wr): Wrap)
-> std::fmt::Result {
match self {
Self::P(p) => write!(f, "{p:?}"),
Self::Lambda(argtyp, body) => parametric_fmt(f, depth, "\\", argtyp, body, wr),
Self::Auto(argtyp, body) => parametric_fmt(f, depth, "@", argtyp, body, wr),
Self::LambdaArg(skip) | Self::AutoArg(skip) => {
let lambda_depth = (depth - skip - 1).try_into().unwrap();
f.write_str(&string_from_charset(lambda_depth, ARGNAME_CHARSET))
},
Self::Apply(func, x) => {
if wl { f.write_char('(')?; }
func.deep_fmt(f, depth, Wrap(false, true))?;
f.write_char(' ')?;
x.deep_fmt(f, depth, Wrap(true, wr && !wl))?;
if wl { f.write_char(')')?; }
Ok(())
}
Self::Explicit(gen, t) => {
if wl { f.write_char('(')?; }
gen.deep_fmt(f, depth, Wrap(false, true))?;
f.write_str(" @")?;
t.deep_fmt(f, depth, Wrap(true, wr && !wl))?;
if wl { f.write_char(')'); }
Ok(())
}
}
}
pub fn wrap(self) -> Box<Expr> { Box::new(Expr(self, Rc::new(vec![]))) }
pub fn wrap_t(self, t: Clause) -> Box<Expr> { Box::new(Expr(self, Rc::new(vec![t]))) }
}
impl Debug for Clause {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.deep_fmt(f, 0, Wrap(false, false))
}
}
impl TryFrom<&ast::Expr> for Expr {
type Error = ast_to_postmacro::Error;
fn try_from(value: &ast::Expr) -> Result<Self, Self::Error> {
ast_to_postmacro::expr(value)
}
}
impl TryFrom<&ast::Clause> for Clause {
type Error = ast_to_postmacro::Error;
fn try_from(value: &ast::Clause) -> Result<Self, Self::Error> {
ast_to_postmacro::clause(value)
}
}

View File

@@ -0,0 +1,74 @@
use std::{rc::Rc, fmt::Display};
use crate::utils::Side;
use super::{postmacro, interpreted, path_set::PathSet};
fn collect_paths_expr_rec(expr: &postmacro::Expr, depth: usize) -> Option<PathSet> {
collect_paths_cls_rec(&expr.0, depth)
}
fn collect_paths_cls_rec(cls: &postmacro::Clause, depth: usize) -> Option<PathSet> {
match cls {
postmacro::Clause::P(_) | postmacro::Clause::Auto(..) | postmacro::Clause::AutoArg(_)
| postmacro::Clause::Explicit(..) => None,
postmacro::Clause::LambdaArg(h) => if *h != depth {None} else {
Some(PathSet{ next: None, steps: Rc::new(vec![]) })
}
postmacro::Clause::Lambda(_, b) => collect_paths_expr_rec(b, depth + 1),
postmacro::Clause::Apply(f, x) => {
let f_opt = collect_paths_expr_rec(f, depth);
let x_opt = collect_paths_expr_rec(x, depth);
match (f_opt, x_opt) {
(Some(f_refs), Some(x_refs)) => Some(f_refs + x_refs),
(Some(f_refs), None) => Some(f_refs + Side::Left),
(None, Some(x_refs)) => Some(x_refs + Side::Right),
(None, None) => None
}
}
}
}
#[derive(Clone)]
pub enum Error {
/// Auto, Explicit and AutoArg are unsupported
GenericMention,
/// Type annotations are unsupported
ExplicitType
}
impl Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::ExplicitType => write!(f, "Type annotations are unsupported in the interpreter"),
Self::GenericMention
=> write!(f, "The interpreter is typeless and therefore can't resolve generics")
}
}
}
pub fn clause_rec(cls: &postmacro::Clause) -> Result<interpreted::Clause, Error> {
match cls {
postmacro::Clause::P(p) => Ok(interpreted::Clause::P(p.clone())),
postmacro::Clause::Explicit(..) | postmacro::Clause::AutoArg(..) | postmacro::Clause::Auto(..)
=> Err(Error::GenericMention),
postmacro::Clause::Apply(f, x) => Ok(interpreted::Clause::Apply {
f: Rc::new(expr_rec(f.as_ref())?),
x: Rc::new(expr_rec(x.as_ref())?),
id: 0
}),
postmacro::Clause::Lambda(typ, body) => if typ.len() != 0 {Err(Error::ExplicitType)} else {
Ok(interpreted::Clause::Lambda {
args: collect_paths_expr_rec(body, 0),
body: Rc::new(expr_rec(body)?)
})
},
postmacro::Clause::LambdaArg(_) => Ok(interpreted::Clause::LambdaArg)
}
}
pub fn expr_rec(expr: &postmacro::Expr) -> Result<interpreted::Clause, Error> {
let postmacro::Expr(c, t) = expr;
if t.len() != 0 {Err(Error::ExplicitType)}
else {clause_rec(c)}
}

View File

@@ -0,0 +1,49 @@
use std::fmt::Debug;
use crate::foreign::{ExternFn, Atom};
use super::Literal;
#[derive(Eq, Hash)]
pub enum Primitive {
/// A literal value, eg. `1`, `"hello"`
Literal(Literal),
/// An opaque function, eg. an effectful function employing CPS.
ExternFn(Box<dyn ExternFn>),
/// An opaque non-callable value, eg. a file handle.
Atom(Atom)
}
impl PartialEq for Primitive {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Self::Literal(l1), Self::Literal(l2)) => l1 == l2,
(Self::Atom(a1), Self::Atom(a2)) => a1 == a2,
(Self::ExternFn(efb1), Self::ExternFn(efb2)) => efb1 == efb2,
_ => false
}
}
}
impl Clone for Primitive {
fn clone(&self) -> Self {
match self {
Primitive::Literal(l) => Primitive::Literal(l.clone()),
Primitive::Atom(a) => Primitive::Atom(a.clone()),
Primitive::ExternFn(ef) => Primitive::ExternFn(
dyn_clone::clone_box(ef.as_ref())
)
}
}
}
impl Debug for Primitive {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Atom(a) => write!(f, "{a:?}"),
Self::ExternFn(ef) => write!(f, "{ef:?}"),
Self::Literal(l) => write!(f, "{l:?}"),
}
}
}

View File

@@ -5,10 +5,12 @@ use crate::utils::{to_mrc_slice, one_mrc_slice};
use crate::utils::string_from_charset;
use super::get_name::get_name;
use super::{Literal, ast_to_typed, get_name};
use super::primitive::Primitive;
use super::{Literal, ast_to_postmacro, get_name};
use super::ast;
use std::fmt::{Debug, Write};
use std::rc::Rc;
/// Indicates whether either side needs to be wrapped. Syntax whose end is ambiguous on that side
/// must use parentheses, or forward the flag
@@ -18,16 +20,16 @@ struct Wrap(bool, bool);
#[derive(PartialEq, Eq, Hash, Clone)]
pub struct Expr(pub Clause, pub Vec<Clause>);
impl Expr {
fn deep_fmt(&self, f: &mut std::fmt::Formatter<'_>, tr: Wrap) -> std::fmt::Result {
fn deep_fmt(&self, f: &mut std::fmt::Formatter<'_>, depth: usize, tr: Wrap) -> std::fmt::Result {
let Expr(val, typ) = self;
if typ.len() > 0 {
val.deep_fmt(f, Wrap(true, true))?;
val.deep_fmt(f, depth, Wrap(true, true))?;
for typterm in typ {
f.write_char(':')?;
typterm.deep_fmt(f, Wrap(true, true))?;
typterm.deep_fmt(f, depth, Wrap(true, true))?;
}
} else {
val.deep_fmt(f, tr)?;
val.deep_fmt(f, depth, tr)?;
}
Ok(())
}
@@ -35,57 +37,54 @@ impl Expr {
impl Debug for Expr {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.deep_fmt(f, Wrap(false, false))
self.deep_fmt(f, 0, Wrap(false, false))
}
}
#[derive(PartialEq, Eq, Hash)]
pub enum Clause {
Literal(Literal),
Apply(Box<Expr>, Box<Expr>),
Lambda(u64, Box<[Clause]>, Box<Expr>),
Auto(u64, Box<[Clause]>, Box<Expr>),
LambdaArg(u64), AutoArg(u64),
ExternFn(ExternFn),
Atom(Atom)
P(Primitive),
Apply(Rc<Expr>, Rc<Expr>),
Lambda(Rc<[Clause]>, Rc<Expr>),
Auto(Rc<[Clause]>, Rc<Expr>),
LambdaArg(usize), AutoArg(usize),
}
const ARGNAME_CHARSET: &str = "abcdefghijklmnopqrstuvwxyz";
fn parametric_fmt(
f: &mut std::fmt::Formatter<'_>,
prefix: &str, argtyp: &[Clause], body: &Expr, uid: u64, wrap_right: bool
f: &mut std::fmt::Formatter<'_>, depth: usize,
prefix: &str, argtyp: &[Clause], body: &Expr, 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))?;
f.write_str(&string_from_charset(depth as u64, ARGNAME_CHARSET))?;
for typ in argtyp.iter() {
f.write_str(":")?;
typ.deep_fmt(f, Wrap(false, false))?;
typ.deep_fmt(f, depth, Wrap(false, false))?;
}
f.write_str(".")?;
body.deep_fmt(f, Wrap(false, false))?;
body.deep_fmt(f, depth + 1, Wrap(false, false))?;
if wrap_right { f.write_char(')')?; }
Ok(())
}
impl Clause {
fn deep_fmt(&self, f: &mut std::fmt::Formatter<'_>, Wrap(wl, wr): Wrap)
fn deep_fmt(&self, f: &mut std::fmt::Formatter<'_>, depth: usize, Wrap(wl, wr): Wrap)
-> std::fmt::Result {
match self {
Self::Literal(arg0) => write!(f, "{arg0:?}"),
Self::ExternFn(nc) => write!(f, "{nc:?}"),
Self::Atom(a) => write!(f, "{a:?}"),
Self::Lambda(uid, argtyp, body) => parametric_fmt(f, "\\", argtyp, body, *uid, wr),
Self::Auto(uid, argtyp, body) => parametric_fmt(f, "@", argtyp, body, *uid, wr),
Self::LambdaArg(uid) | Self::AutoArg(uid) => f.write_str(&
string_from_charset(*uid, ARGNAME_CHARSET)
),
Self::P(p) => write!(f, "{p:?}"),
Self::Lambda(argtyp, body) => parametric_fmt(f, depth, "\\", argtyp, body, wr),
Self::Auto(argtyp, body) => parametric_fmt(f, depth, "@", argtyp, body, wr),
Self::LambdaArg(skip) | Self::AutoArg(skip) => {
let lambda_depth = (depth - skip - 1).try_into().unwrap();
f.write_str(&string_from_charset(lambda_depth, ARGNAME_CHARSET))
},
Self::Apply(func, x) => {
if wl { f.write_char('(')?; }
func.deep_fmt(f, Wrap(false, true) )?;
func.deep_fmt(f, depth, Wrap(false, true) )?;
f.write_char(' ')?;
x.deep_fmt(f, Wrap(true, wr && !wl) )?;
x.deep_fmt(f, depth, Wrap(true, wr && !wl) )?;
if wl { f.write_char(')')?; }
Ok(())
}
@@ -98,7 +97,7 @@ impl Clause {
impl Clone for Clause {
fn clone(&self) -> Self {
match self {
Clause::Auto(uid, t, b) => {
Clause::Auto(t, b) => {
let new_id = get_name();
let new_body = apply_lambda(*uid, Clause::AutoArg(new_id).wrap(), b.clone());
Clause::Auto(new_id, t.clone(), new_body)
@@ -125,16 +124,16 @@ impl Debug for Clause {
}
impl TryFrom<&ast::Expr> for Expr {
type Error = ast_to_typed::Error;
type Error = ast_to_postmacro::Error;
fn try_from(value: &ast::Expr) -> Result<Self, Self::Error> {
ast_to_typed::expr(value)
ast_to_postmacro::expr(value)
}
}
impl TryFrom<&ast::Clause> for Clause {
type Error = ast_to_typed::Error;
type Error = ast_to_postmacro::Error;
fn try_from(value: &ast::Clause) -> Result<Self, Self::Error> {
ast_to_typed::clause(value)
ast_to_postmacro::clause(value)
}
}

View File

@@ -1,13 +1,16 @@
use std::iter;
use hashbrown::HashMap;
use mappable_rc::Mrc;
use crate::unwrap_or;
use crate::utils::{to_mrc_slice, one_mrc_slice, mrc_empty_slice};
use crate::utils::{to_mrc_slice, one_mrc_slice, mrc_empty_slice, replace_first};
use crate::utils::iter::{box_once, into_boxed_iter};
use crate::ast::{Expr, Clause};
use super::slice_matcher::SliceMatcherDnC;
use super::state::{State, Entry};
use super::super::RuleError;
use super::update_first_seq_rec;
fn verify_scalar_vec(pattern: &Expr, is_vec: &mut HashMap<String, bool>)
-> Result<(), String> {
@@ -62,30 +65,12 @@ fn slice_to_vec(src: &mut Mrc<[Expr]>, tgt: &mut Mrc<[Expr]>) {
*tgt = to_mrc_slice(prefix_vec.iter().chain(tgt.iter()).chain(postfix_vec.iter()).cloned().collect());
}
/// Traverse the tree, calling pred on every sibling list until it returns some vec
/// then replace the sibling list with that vec and return true
/// return false if pred never returned some
fn update_first_seq_rec<F>(input: Mrc<[Expr]>, pred: &mut F) -> Option<Mrc<[Expr]>>
where F: FnMut(Mrc<[Expr]>) -> Option<Mrc<[Expr]>> {
if let o@Some(_) = pred(Mrc::clone(&input)) {o} else {
for Expr(cls, _) in input.iter() {
if let Some(t) = cls.typ() {
if let o@Some(_) = update_first_seq_rec(t, pred) {return o}
}
if let Some(b) = cls.body() {
if let o@Some(_) = update_first_seq_rec(b, pred) {return o}
}
}
None
}
}
/// keep re-probing the input with pred until it stops matching
fn update_all_seqs<F>(input: Mrc<[Expr]>, pred: &mut F) -> Option<Mrc<[Expr]>>
where F: FnMut(Mrc<[Expr]>) -> Option<Mrc<[Expr]>> {
let mut tmp = update_first_seq_rec(input, pred);
let mut tmp = update_first_seq_rec::exprv(input, pred);
while let Some(xv) = tmp {
tmp = update_first_seq_rec(Mrc::clone(&xv), pred);
tmp = update_first_seq_rec::exprv(Mrc::clone(&xv), pred);
if tmp.is_none() {return Some(xv)}
}
None
@@ -151,14 +136,12 @@ fn write_expr_rec(state: &State, Expr(tpl_clause, tpl_typ): &Expr) -> Box<dyn It
)).into_expr())
},
// Explicit base case so that we get an error if Clause gets new values
c@Clause::Literal(_) | c@Clause::Name { .. } | c@Clause::ExternFn(_) | c@Clause::Atom(_) =>
box_once(Expr(c.to_owned(), out_typ.to_owned()))
c@Clause::P(_) | c@Clause::Name { .. } => box_once(Expr(c.to_owned(), out_typ.to_owned()))
}
}
/// Fill in a template from a state as produced by a pattern
fn write_slice_rec(state: &State, tpl: &Mrc<[Expr]>) -> Mrc<[Expr]> {
eprintln!("Writing {tpl:?} with state {state:?}");
tpl.iter().flat_map(|xpr| write_expr_rec(state, xpr)).collect()
}

View File

@@ -2,6 +2,7 @@ mod slice_matcher;
mod state;
mod execute;
mod split_at_max_vec;
mod update_first_seq_rec;
use state::State;

View File

@@ -211,7 +211,7 @@ impl SliceMatcherDnC {
+ self.apply_side_with_cache(Side::Right, mrc_derive(&target, |t| &t[pos+1..]), cache)
)?;
match (self.clause.as_ref(), &target.as_ref()[pos].0) {
(Clause::Literal(val), Clause::Literal(tgt)) => {
(Clause::P(val), Clause::P(tgt)) => {
if val == tgt {Some(own_state)} else {None}
}
(Clause::Placeh{key, vec: None}, tgt_clause) => {

View File

@@ -0,0 +1,53 @@
use mappable_rc::Mrc;
use crate::{ast::{Expr, Clause}, utils::{replace_first, to_mrc_slice}};
/// Traverse the tree, calling pred on every sibling list until it returns some vec
/// then replace the sibling list with that vec and return true
/// return false if pred never returned some
pub fn exprv<F>(input: Mrc<[Expr]>, pred: &mut F) -> Option<Mrc<[Expr]>>
where F: FnMut(Mrc<[Expr]>) -> Option<Mrc<[Expr]>> {
if let o@Some(_) = pred(Mrc::clone(&input)) {return o}
replace_first(input.as_ref(), |ex| expr(ex, pred))
.map(|i| to_mrc_slice(i.collect()))
}
pub fn expr<F>(Expr(cls, typ): &Expr, pred: &mut F) -> Option<Expr>
where F: FnMut(Mrc<[Expr]>) -> Option<Mrc<[Expr]>> {
if let Some(t) = clausev(Mrc::clone(typ), pred) {return Some(Expr(cls.clone(), t))}
if let Some(c) = clause(cls, pred) {return Some(Expr(c, Mrc::clone(typ)))}
None
}
pub fn clausev<F>(input: Mrc<[Clause]>, pred: &mut F) -> Option<Mrc<[Clause]>>
where F: FnMut(Mrc<[Expr]>) -> Option<Mrc<[Expr]>> {
replace_first(input.as_ref(), |c| clause(c, pred))
.map(|i| to_mrc_slice(i.collect()))
}
pub fn clause<F>(c: &Clause, pred: &mut F) -> Option<Clause>
where F: FnMut(Mrc<[Expr]>) -> Option<Mrc<[Expr]>> {
match c {
Clause::P(_) | Clause::Placeh {..} | Clause::Name {..} => None,
Clause::Lambda(n, typ, body) => {
if let Some(b) = exprv(Mrc::clone(body), pred) {
return Some(Clause::Lambda(n.clone(), Mrc::clone(typ), b))
}
if let Some(t) = exprv(Mrc::clone(typ), pred) {
return Some(Clause::Lambda(n.clone(), t, Mrc::clone(body)))
}
None
}
Clause::Auto(n, typ, body) => {
if let Some(b) = exprv(Mrc::clone(body), pred) {
return Some(Clause::Auto(n.clone(), Mrc::clone(typ), b))
}
if let Some(t) = exprv(Mrc::clone(typ), pred) {
return Some(Clause::Auto(n.clone(), t, Mrc::clone(body)))
}
None
}
Clause::S(c, body) => Some(Clause::S(*c, exprv(Mrc::clone(body), pred)?)),
Clause::Explicit(t) => Some(Clause::Explicit(Mrc::new(expr(t, pred)?)))
}
}

View File

@@ -10,12 +10,22 @@ use super::{super::ast::Rule, executor::execute, RuleError};
pub struct Repository(Vec<Rule>);
impl Repository {
pub fn new(mut rules: Vec<Rule>) -> Self {
rules.sort_by_key(|r| r.prio);
rules.sort_by_key(|r| -r.prio);
Self(rules)
}
pub fn step(&self, code: Mrc<[Expr]>) -> Result<Option<Mrc<[Expr]>>, RuleError> {
for rule in self.0.iter() {
if let Some(out) = execute(
Mrc::clone(&rule.source), Mrc::clone(&rule.target),
Mrc::clone(&code)
)? {return Ok(Some(out))}
}
Ok(None)
}
/// Attempt to run each rule in priority order once
pub fn step(&self, mut code: Mrc<[Expr]>) -> Result<Option<Mrc<[Expr]>>, RuleError> {
pub fn pass(&self, mut code: Mrc<[Expr]>) -> Result<Option<Mrc<[Expr]>>, RuleError> {
let mut ran_once = false;
for rule in self.0.iter() {
if let Some(tmp) = execute(
@@ -33,7 +43,7 @@ impl Repository {
/// tree and the number of iterations left to the limit.
pub fn long_step(&self, mut code: Mrc<[Expr]>, mut limit: usize)
-> Result<(Mrc<[Expr]>, usize), RuleError> {
while let Some(tmp) = self.step(Mrc::clone(&code))? {
while let Some(tmp) = self.pass(Mrc::clone(&code))? {
if 0 >= limit {break}
limit -= 1;
code = tmp

View File

@@ -54,10 +54,10 @@ macro_rules! xloop {
(for $p:pat in $it:expr; $body:stmt) => {
xloop!(for $p in $it; $body; ())
};
(for $p:pat in $it:expr; $body:stmt; $exit:stmt) => {
(for $p:pat in $iit:expr; $body:stmt; $exit:stmt) => {
{
let mut __xloop__ = $it.into_iter();
xloop!(let Some($p) = __xloop__.next(); $body; $exit)
let mut i = $iit.into_iter();
xloop!(let Some($p) = i.next(); $body; $exit)
}
};
(let $p:pat = $e:expr; $body:stmt) => {

View File

@@ -1,5 +1,8 @@
mod cache;
pub mod translate;
mod replace_first;
// mod visitor;
pub use replace_first::replace_first;
pub use cache::Cache;
mod substack;
pub use substack::Stackframe;

View File

@@ -0,0 +1,14 @@
use std::iter;
pub fn replace_first<'a, T, F>(slice: &'a [T], mut f: F) -> Option<impl Iterator<Item = T> + 'a>
where T: Clone, F: FnMut(&T) -> Option<T> {
for i in 0..slice.len() {
if let Some(new) = f(&slice[i]) {
let subbed_iter = slice[0..i].iter().cloned()
.chain(iter::once(new))
.chain(slice[i+1..].iter().cloned());
return Some(subbed_iter)
}
}
None
}

18
src/utils/visitor.rs Normal file
View File

@@ -0,0 +1,18 @@
pub trait Visit<T> {
type Return;
fn visit(&self, target: T) -> Return;
}
pub trait ImpureVisit<T> {
type Shard;
type Return;
fn impure_visit(&self, target: T) -> (Shard, Return);
fn merge(&mut self, s: Shard);
}
pub struct OverlayVisitor<VBase, VOver>(VBase, VOver);
impl<VBase, VOver, T, R> Visitor<T> for OverlayVisitor<VBase, VOver>
where VBase: Visitor<T, Return = Option<R>>, VOver: Visitor<T, Return = Option<R>> {
}