Dead end with macros
This commit is contained in:
30
src/external/assertion_error.rs
vendored
Normal file
30
src/external/assertion_error.rs
vendored
Normal 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
4
src/external/mod.rs
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
mod numbers;
|
||||
mod assertion_error;
|
||||
|
||||
use numbers::Multiply2;
|
||||
38
src/external/numbers/mod.rs
vendored
Normal file
38
src/external/numbers/mod.rs
vendored
Normal 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
102
src/external/numbers/numeric.rs
vendored
Normal 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)
|
||||
}))
|
||||
}
|
||||
}
|
||||
@@ -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
333
src/foreign_macros.rs
Normal 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
|
||||
);
|
||||
};
|
||||
}
|
||||
59
src/main.rs
59
src/main.rs
@@ -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() {
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
}
|
||||
|
||||
173
src/representations/ast_to_postmacro.rs
Normal file
173
src/representations/ast_to_postmacro.rs
Normal 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)
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
191
src/representations/interpreted.rs
Normal file
191
src/representations/interpreted.rs
Normal 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)
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
96
src/representations/path_set.rs
Normal file
96
src/representations/path_set.rs
Normal 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);
|
||||
}
|
||||
}
|
||||
120
src/representations/postmacro.rs
Normal file
120
src/representations/postmacro.rs
Normal 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)
|
||||
}
|
||||
}
|
||||
74
src/representations/postmacro_to_interpreted.rs
Normal file
74
src/representations/postmacro_to_interpreted.rs
Normal 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)}
|
||||
}
|
||||
49
src/representations/primitive.rs
Normal file
49
src/representations/primitive.rs
Normal 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:?}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ mod slice_matcher;
|
||||
mod state;
|
||||
mod execute;
|
||||
mod split_at_max_vec;
|
||||
mod update_first_seq_rec;
|
||||
|
||||
use state::State;
|
||||
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
53
src/rule/executor/update_first_seq_rec.rs
Normal file
53
src/rule/executor/update_first_seq_rec.rs
Normal 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)?)))
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
@@ -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;
|
||||
|
||||
14
src/utils/replace_first.rs
Normal file
14
src/utils/replace_first.rs
Normal 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
18
src/utils/visitor.rs
Normal 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>> {
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user