executor, mostly
This commit is contained in:
67
src/executor/apply_lambda.rs
Normal file
67
src/executor/apply_lambda.rs
Normal file
@@ -0,0 +1,67 @@
|
||||
use mappable_rc::Mrc;
|
||||
|
||||
use super::super::representations::typed::{Clause, Expr};
|
||||
|
||||
pub fn apply_lambda(body: Mrc<Expr>, arg: Mrc<Expr>) -> Mrc<Expr> {
|
||||
apply_lambda_expr_rec(Mrc::clone(&body), arg, 0)
|
||||
.unwrap_or(body)
|
||||
}
|
||||
|
||||
fn apply_lambda_expr_rec(
|
||||
item: Mrc<Expr>, arg: Mrc<Expr>, depth: usize
|
||||
) -> Option<Mrc<Expr>> {
|
||||
let Expr(clause, typ) = item.as_ref();
|
||||
apply_lambda_clause_rec(clause.clone(), arg, depth)
|
||||
.map(|c| Mrc::new(Expr(c, Mrc::clone(typ))))
|
||||
}
|
||||
|
||||
fn apply_lambda_clause_rec(
|
||||
clause: Clause, arg: Mrc<Expr>, depth: usize
|
||||
) -> Option<Clause> {
|
||||
match clause {
|
||||
// Only element actually manipulated
|
||||
Clause::Argument(d) => {
|
||||
if d == depth {Some(arg.0.clone())} // Resolve reference
|
||||
// Application eliminates a layer of indirection
|
||||
else if d > depth {Some(Clause::Argument(d - 1))}
|
||||
else {None} // Undisturbed ancestry
|
||||
}
|
||||
// Traverse, yield Some if either had changed.
|
||||
Clause::Apply(f, x) => apply_lambda__traverse_call(arg, depth, f, x, Clause::Apply),
|
||||
Clause::Explicit(f, t) => apply_lambda__traverse_call(arg, depth, f, t, Clause::Explicit),
|
||||
Clause::Lambda(t, b) => apply_lambda__traverse_param(arg, depth, t, b, Clause::Lambda),
|
||||
Clause::Auto(t, b) => apply_lambda__traverse_param(arg, depth, t, b, Clause::Auto),
|
||||
// Leaf nodes
|
||||
Clause::Atom(_) | Clause::ExternFn(_) | Clause::Literal(_) => None
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_lambda__traverse_call(
|
||||
arg: Mrc<Expr>, depth: usize, f: Mrc<Expr>, x: Mrc<Expr>,
|
||||
wrap: impl Fn(Mrc<Expr>, Mrc<Expr>) -> Clause
|
||||
) -> Option<Clause> {
|
||||
let new_f = apply_lambda_expr_rec(Mrc::clone(&f), Mrc::clone(&arg), depth);
|
||||
let new_x = apply_lambda_expr_rec(Mrc::clone(&x), arg, depth);
|
||||
match (new_f, new_x) {
|
||||
(None, None) => None,
|
||||
(None, Some(x)) => Some(wrap(f, x)),
|
||||
(Some(f), None) => Some(wrap(f, x)),
|
||||
(Some(f), Some(x)) => Some(wrap(f, x))
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_lambda__traverse_param(
|
||||
arg: Mrc<Expr>, depth: usize, t: Option<Mrc<Clause>>, b: Mrc<Expr>,
|
||||
wrap: impl Fn(Option<Mrc<Clause>>, Mrc<Expr>) -> Clause
|
||||
) -> Option<Clause> {
|
||||
let new_t = t.as_ref().and_then(|t| {
|
||||
apply_lambda_clause_rec(t.as_ref().clone(), Mrc::clone(&arg), depth)
|
||||
});
|
||||
let new_b = apply_lambda_expr_rec(Mrc::clone(&b), arg, depth + 1);
|
||||
match (new_t, new_b) {
|
||||
(None, None) => None,
|
||||
(None, Some(b)) => Some(Clause::Lambda(t, b)),
|
||||
(Some(t), None) => Some(Clause::Lambda(Some(Mrc::new(t)), b)),
|
||||
(Some(t), Some(b)) => Some(Clause::Lambda(Some(Mrc::new(t)), b))
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,6 @@ 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.
|
||||
#[derive(Eq)]
|
||||
pub struct ExternFn {
|
||||
name: String, param: Mrc<Expr>, rttype: Mrc<Expr>,
|
||||
function: Mrc<dyn Fn(Clause) -> Result<Clause, Mrc<dyn ExternError>>>
|
||||
@@ -27,8 +26,8 @@ impl ExternFn {
|
||||
})
|
||||
}
|
||||
}
|
||||
fn name(&self) -> &str {&self.name}
|
||||
fn apply(&self, arg: Clause) -> Result<Clause, Mrc<dyn ExternError>> {(self.function)(arg)}
|
||||
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 {
|
||||
@@ -37,7 +36,10 @@ impl Clone for ExternFn { fn clone(&self) -> Self { Self {
|
||||
rttype: Mrc::clone(&self.rttype),
|
||||
function: Mrc::clone(&self.function)
|
||||
}}}
|
||||
impl PartialEq for ExternFn { fn eq(&self, other: &Self) -> bool { self.name() == other.name() }}
|
||||
impl Eq for ExternFn {}
|
||||
impl PartialEq for 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) }
|
||||
}
|
||||
@@ -58,7 +60,6 @@ pub trait Atomic: Any + Debug where Self: 'static {
|
||||
/// 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.
|
||||
#[derive(Eq)]
|
||||
pub struct Atom {
|
||||
typ: Mrc<Expr>,
|
||||
data: Mrc<dyn Atomic>
|
||||
@@ -95,6 +96,7 @@ impl Debug for Atom {
|
||||
write!(f, "##ATOM[{:?}]:{:?}##", self.data(), self.typ)
|
||||
}
|
||||
}
|
||||
impl Eq for Atom {}
|
||||
impl PartialEq for Atom {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.data().definitely_eq(other.data().as_any())
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
mod foreign;
|
||||
// mod normalize;
|
||||
mod partial_hash;
|
||||
mod reduction_tree;
|
||||
mod apply_lambda;
|
||||
pub use foreign::ExternFn;
|
||||
pub use foreign::Atom;
|
||||
30
src/executor/normalize.rs
Normal file
30
src/executor/normalize.rs
Normal file
@@ -0,0 +1,30 @@
|
||||
use mappable_rc::Mrc;
|
||||
|
||||
use crate::utils::collect_to_mrc;
|
||||
|
||||
use super::super::representations::typed::{Clause, Expr};
|
||||
|
||||
fn normalize(Expr(clause, typ): Expr) -> Expr {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn collect_autos(
|
||||
Expr(clause, typ): Expr,
|
||||
arg_types: Vec<Mrc<[Clause]>>,
|
||||
indirect_argt_trees: Vec<Mrc<[Clause]>>,
|
||||
sunk_types: &mut dyn Iterator<Item = Clause>
|
||||
) -> (Vec<Mrc<[Clause]>>, Expr) {
|
||||
if let Clause::Auto(argt, body) = clause {
|
||||
|
||||
}
|
||||
else {(
|
||||
arg_types,
|
||||
Expr(
|
||||
clause,
|
||||
collect_to_mrc(
|
||||
typ.iter().cloned()
|
||||
.chain(sunk_types)
|
||||
)
|
||||
)
|
||||
)}
|
||||
}
|
||||
38
src/executor/partial_hash.rs
Normal file
38
src/executor/partial_hash.rs
Normal file
@@ -0,0 +1,38 @@
|
||||
use std::hash::{Hasher, Hash};
|
||||
|
||||
use super::super::representations::typed::{Clause, Expr};
|
||||
use super::super::utils::Stackframe;
|
||||
|
||||
/// Hash the parts of an expression that are required to be equal for syntactic equality.
|
||||
pub fn partial_hash_rec<H: Hasher>(Expr(clause, _): &Expr, state: &mut H, is_auto: Stackframe<bool>) {
|
||||
match clause {
|
||||
// Skip autos and explicits
|
||||
Clause::Auto(_, body) => partial_hash_rec(body, state, is_auto.push(true)),
|
||||
Clause::Explicit(f, _) => partial_hash_rec(f, state, is_auto),
|
||||
// Annotate everything else with a prefix
|
||||
// - Recurse into the tree of lambdas and calls - classic lambda calc
|
||||
Clause::Lambda(_, body) => {
|
||||
state.write_u8(0);
|
||||
partial_hash_rec(body, state, is_auto.push(false))
|
||||
}
|
||||
Clause::Apply(f, x) => {
|
||||
state.write_u8(1);
|
||||
partial_hash_rec(f, state, is_auto);
|
||||
partial_hash_rec(x, state, is_auto);
|
||||
}
|
||||
// - Only recognize the depth of an argument if it refers to a non-auto parameter
|
||||
Clause::Argument(depth) => {
|
||||
// If the argument references an auto, acknowledge its existence
|
||||
if *is_auto.iter().nth(*depth).unwrap_or(&false) {
|
||||
state.write_u8(2)
|
||||
} else {
|
||||
state.write_u8(3);
|
||||
state.write_usize(*depth)
|
||||
}
|
||||
}
|
||||
// - Hash leaves like normal
|
||||
Clause::Literal(lit) => { state.write_u8(4); lit.hash(state) }
|
||||
Clause::Atom(at) => { state.write_u8(5); at.hash(state) }
|
||||
Clause::ExternFn(f) => { state.write_u8(6); f.hash(state) }
|
||||
}
|
||||
}
|
||||
102
src/executor/reduction_tree.rs
Normal file
102
src/executor/reduction_tree.rs
Normal file
@@ -0,0 +1,102 @@
|
||||
use mappable_rc::Mrc;
|
||||
|
||||
use crate::box_chain;
|
||||
use crate::utils::BoxedIter;
|
||||
use crate::utils::iter::{box_once, box_empty};
|
||||
|
||||
use super::apply_lambda::apply_lambda;
|
||||
use super::super::representations::typed::{Clause, Expr};
|
||||
|
||||
/// Call the function with the first Expression that isn't an Auto,
|
||||
/// wrap all elements in the returned iterator back in the original sequence of Autos.
|
||||
fn skip_autos<'a,
|
||||
F: 'a + FnOnce(Mrc<Expr>, usize) -> I,
|
||||
I: Iterator<Item = Mrc<Expr>> + 'static
|
||||
>(
|
||||
depth: usize, expr: Mrc<Expr>, function: F
|
||||
) -> BoxedIter<'static, Mrc<Expr>> {
|
||||
match expr.as_ref() {
|
||||
Expr(Clause::Auto(arg, body), typ) => {
|
||||
return Box::new(skip_autos(depth + 1, Mrc::clone(body), function).map({
|
||||
let arg = arg.as_ref().map(Mrc::clone);
|
||||
let typ = Mrc::clone(typ);
|
||||
move |body| {
|
||||
Mrc::new(Expr(Clause::Auto(
|
||||
arg.as_ref().map(Mrc::clone),
|
||||
body
|
||||
), Mrc::clone(&typ)))
|
||||
}
|
||||
})) as BoxedIter<'static, Mrc<Expr>>
|
||||
}
|
||||
Expr(Clause::Explicit(expr, targ), typ) => {
|
||||
return Box::new(skip_autos(depth, Mrc::clone(expr), function).map({
|
||||
let (targ, typ) = (Mrc::clone(targ), Mrc::clone(typ));
|
||||
move |expr| {
|
||||
Mrc::new(Expr(Clause::Explicit(expr, Mrc::clone(&targ)), Mrc::clone(&typ)))
|
||||
}
|
||||
})) as BoxedIter<'static, Mrc<Expr>>
|
||||
}
|
||||
_ => ()
|
||||
}
|
||||
Box::new(function(expr, depth))
|
||||
}
|
||||
|
||||
/// Produces an iterator of every expression that can be produced from this one through B-reduction.
|
||||
fn direct_reductions(ex: Mrc<Expr>) -> impl Iterator<Item = Mrc<Expr>> {
|
||||
skip_autos(0, ex, |mexpr, _| {
|
||||
let Expr(clause, typ_ref) = mexpr.as_ref();
|
||||
match clause {
|
||||
Clause::Apply(f, x) => box_chain!(
|
||||
skip_autos(0, Mrc::clone(f), |mexpr, _| {
|
||||
let Expr(f, _) = mexpr.as_ref();
|
||||
match f {
|
||||
Clause::Lambda(_, body) => box_once(
|
||||
apply_lambda(Mrc::clone(body), Mrc::clone(x))
|
||||
),
|
||||
Clause::ExternFn(xfn) => {
|
||||
let Expr(xval, xtyp) = x.as_ref();
|
||||
xfn.apply(xval.clone())
|
||||
.map(|ret| box_once(Mrc::new(Expr(ret, Mrc::clone(xtyp)))))
|
||||
.unwrap_or(box_empty())
|
||||
},
|
||||
// Parametric newtypes are atoms of function type
|
||||
Clause::Atom(..) | Clause::Argument(..) | Clause::Apply(..) => box_empty(),
|
||||
Clause::Literal(lit) =>
|
||||
panic!("Literal expression {lit:?} can't be applied as function"),
|
||||
Clause::Auto(..) | Clause::Explicit(..) =>
|
||||
unreachable!("skip_autos should have filtered these"),
|
||||
}
|
||||
}),
|
||||
direct_reductions(Mrc::clone(f)).map({
|
||||
let typ = Mrc::clone(typ_ref);
|
||||
let x = Mrc::clone(x);
|
||||
move |f| Mrc::new(Expr(Clause::Apply(
|
||||
f,
|
||||
Mrc::clone(&x)
|
||||
), Mrc::clone(&typ)))
|
||||
}),
|
||||
direct_reductions(Mrc::clone(x)).map({
|
||||
let typ = Mrc::clone(typ_ref);
|
||||
let f = Mrc::clone(f);
|
||||
move |x| Mrc::new(Expr(Clause::Apply(
|
||||
Mrc::clone(&f),
|
||||
x
|
||||
), Mrc::clone(&typ)))
|
||||
})
|
||||
),
|
||||
Clause::Lambda(argt, body) => Box::new(direct_reductions(Mrc::clone(body)).map({
|
||||
let typ = Mrc::clone(typ_ref);
|
||||
let argt = argt.as_ref().map(Mrc::clone);
|
||||
move |body| Mrc::new(Expr(Clause::Lambda(
|
||||
argt.as_ref().map(Mrc::clone),
|
||||
body
|
||||
), Mrc::clone(&typ)))
|
||||
})),
|
||||
Clause::Literal(..) | Clause::ExternFn(..) | Clause::Atom(..) | Clause::Argument(..) =>
|
||||
box_empty(),
|
||||
Clause::Auto(..) | Clause::Explicit(..) =>
|
||||
unreachable!("skip_autos should have filtered these"),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
41
src/executor/syntax_eq.rs
Normal file
41
src/executor/syntax_eq.rs
Normal file
@@ -0,0 +1,41 @@
|
||||
use std::hash::{Hasher, Hash};
|
||||
|
||||
use super::super::representations::typed::{Clause, Expr};
|
||||
use super::super::utils::Stackframe;
|
||||
|
||||
/// Hash the parts of an expression that are required to be equal for syntactic equality.
|
||||
pub fn syntax_eq_rec<H: Hasher>(
|
||||
ex1: &Expr, ex1_stack: Stackframe<bool>,
|
||||
ex2: &Expr, ex2_stack: Stackframe<bool>
|
||||
) -> bool {
|
||||
match clause {
|
||||
// Skip autos and explicits
|
||||
Clause::Auto(_, body) => partial_hash_rec(body, state, is_auto.push(true)),
|
||||
Clause::Explicit(f, _) => partial_hash_rec(f, state, is_auto),
|
||||
// Annotate everything else with a prefix
|
||||
// - Recurse into the tree of lambdas and calls - classic lambda calc
|
||||
Clause::Lambda(_, body) => {
|
||||
state.write_u8(0);
|
||||
partial_hash_rec(body, state, is_auto.push(false))
|
||||
}
|
||||
Clause::Apply(f, x) => {
|
||||
state.write_u8(1);
|
||||
partial_hash_rec(f, state, is_auto);
|
||||
partial_hash_rec(x, state, is_auto);
|
||||
}
|
||||
// - Only recognize the depth of an argument if it refers to a non-auto parameter
|
||||
Clause::Argument(depth) => {
|
||||
// If the argument references an auto, acknowledge its existence
|
||||
if *is_auto.iter().nth(*depth).unwrap_or(&false) {
|
||||
state.write_u8(2)
|
||||
} else {
|
||||
state.write_u8(3);
|
||||
state.write_usize(*depth)
|
||||
}
|
||||
}
|
||||
// - Hash leaves like normal
|
||||
Clause::Literal(lit) => { state.write_u8(4); lit.hash(state) }
|
||||
Clause::Atom(at) => { state.write_u8(5); at.hash(state) }
|
||||
Clause::ExternFn(f) => { state.write_u8(6); f.hash(state) }
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
#![feature(specialization)]
|
||||
#![feature(core_intrinsics)]
|
||||
|
||||
use std::env::current_dir;
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use chumsky::{self, prelude::*, Parser};
|
||||
use mappable_rc::Mrc;
|
||||
use crate::enum_parser;
|
||||
use crate::representations::{Literal, ast::{Clause, Expr}};
|
||||
use crate::utils::{to_mrc_slice, one_mrc_slice};
|
||||
@@ -112,9 +113,8 @@ pub fn xpr_parser() -> impl Parser<Lexeme, Expr, Error = Simple<Lexeme>> {
|
||||
sexpr_parser(expr.clone()),
|
||||
lambda_parser(expr.clone()),
|
||||
auto_parser(expr.clone()),
|
||||
just(Lexeme::At).to(Clause::Name {
|
||||
local: Some("@".to_string()),
|
||||
qualified: one_mrc_slice("@".to_string())
|
||||
just(Lexeme::At).ignore_then(expr.clone()).map(|arg| {
|
||||
Clause::Explicit(Mrc::new(arg))
|
||||
})
|
||||
))).then_ignore(enum_parser!(Lexeme::Comment).repeated());
|
||||
clause.clone().then(
|
||||
|
||||
@@ -157,8 +157,13 @@ where
|
||||
.filter_map(|ent| {
|
||||
if let FileEntry::Rule(Rule{source, prio, target}, _) = ent {
|
||||
Some(Rule {
|
||||
source: source.iter().map(|ex| prefix_expr(ex, Mrc::clone(&path))).collect(),
|
||||
target: target.iter().map(|ex| prefix_expr(ex, Mrc::clone(&path))).collect(),
|
||||
source: source.iter()
|
||||
.map(|ex| {
|
||||
prefix_expr(ex, Mrc::clone(&path))
|
||||
}).collect(),
|
||||
target: target.iter().map(|ex| {
|
||||
prefix_expr(ex, Mrc::clone(&path))
|
||||
}).collect(),
|
||||
prio: *prio,
|
||||
})
|
||||
} else { None }
|
||||
|
||||
@@ -1,15 +1,22 @@
|
||||
use mappable_rc::Mrc;
|
||||
use itertools::Itertools;
|
||||
use ordered_float::NotNan;
|
||||
use std::hash::Hash;
|
||||
use std::{hash::Hash, intrinsics::likely};
|
||||
use std::fmt::Debug;
|
||||
use crate::executor::{ExternFn, Atom};
|
||||
use crate::utils::mrc_empty_slice;
|
||||
use crate::{executor::{ExternFn, Atom}, utils::one_mrc_slice};
|
||||
|
||||
use super::Literal;
|
||||
|
||||
/// An S-expression with a type
|
||||
#[derive(PartialEq, Eq, Hash)]
|
||||
pub struct Expr(pub Clause, pub Mrc<[Clause]>);
|
||||
impl Expr {
|
||||
pub fn into_clause(self) -> Clause {
|
||||
if likely(self.1.len() == 0) { self.0 }
|
||||
else { Clause::S('(', one_mrc_slice(self)) }
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for Expr {
|
||||
fn clone(&self) -> Self {
|
||||
@@ -37,6 +44,7 @@ pub enum Clause {
|
||||
qualified: Mrc<[String]>
|
||||
},
|
||||
S(char, Mrc<[Expr]>),
|
||||
Explicit(Mrc<Expr>),
|
||||
Lambda(String, Mrc<[Expr]>, Mrc<[Expr]>),
|
||||
Auto(Option<String>, Mrc<[Expr]>, Mrc<[Expr]>),
|
||||
ExternFn(ExternFn),
|
||||
@@ -53,37 +61,49 @@ pub enum Clause {
|
||||
impl Clause {
|
||||
pub fn body(&self) -> Option<Mrc<[Expr]>> {
|
||||
match self {
|
||||
Clause::Auto(_, _, body) |
|
||||
Clause::Lambda(_, _, body) |
|
||||
Clause::S(_, body) => Some(Mrc::clone(body)),
|
||||
Self::Auto(_, _, body) |
|
||||
Self::Lambda(_, _, body) |
|
||||
Self::S(_, body) => Some(Mrc::clone(body)),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
pub fn typ(&self) -> Option<Mrc<[Expr]>> {
|
||||
match self {
|
||||
Clause::Auto(_, typ, _) | Clause::Lambda(_, typ, _) => Some(Mrc::clone(typ)),
|
||||
Self::Auto(_, typ, _) | Self::Lambda(_, typ, _) => Some(Mrc::clone(typ)),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
pub fn into_expr(self) -> Expr {
|
||||
if let Self::S('(', body) = &self {
|
||||
if body.len() == 1 { body[0].clone() }
|
||||
else { Expr(self, mrc_empty_slice()) }
|
||||
} else { Expr(self, mrc_empty_slice()) }
|
||||
}
|
||||
pub fn from_exprv(exprv: Mrc<[Expr]>) -> Option<Clause> {
|
||||
if exprv.len() == 0 { None }
|
||||
else if exprv.len() == 1 { Some(exprv[0].clone().into_clause()) }
|
||||
else { Some(Self::S('(', exprv)) }
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for Clause {
|
||||
fn clone(&self) -> Self {
|
||||
match self {
|
||||
Clause::S(c, b) => Clause::S(*c, Mrc::clone(b)),
|
||||
Clause::Auto(n, t, b) => Clause::Auto(
|
||||
Self::S(c, b) => Self::S(*c, Mrc::clone(b)),
|
||||
Self::Auto(n, t, b) => Self::Auto(
|
||||
n.clone(), Mrc::clone(t), Mrc::clone(b)
|
||||
),
|
||||
Clause::Name { local: l, qualified: q } => Clause::Name {
|
||||
Self::Name { local: l, qualified: q } => Self::Name {
|
||||
local: l.clone(), qualified: Mrc::clone(q)
|
||||
},
|
||||
Clause::Lambda(n, t, b) => Clause::Lambda(
|
||||
Self::Lambda(n, t, b) => Self::Lambda(
|
||||
n.clone(), Mrc::clone(t), Mrc::clone(b)
|
||||
),
|
||||
Clause::Placeh{key, vec} => Clause::Placeh{key: key.clone(), vec: *vec},
|
||||
Clause::Literal(l) => Clause::Literal(l.clone()),
|
||||
Clause::ExternFn(nc) => Clause::ExternFn(nc.clone()),
|
||||
Clause::Atom(a) => Clause::Atom(a.clone())
|
||||
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::Explicit(expr) => Self::Explicit(Mrc::clone(expr))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -127,7 +147,8 @@ impl Debug for Clause {
|
||||
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::Atom(a) => write!(f, "{a:?}"),
|
||||
Self::Explicit(expr) => write!(f, "@{:?}", expr.as_ref())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use mappable_rc::Mrc;
|
||||
|
||||
use crate::utils::{Stackframe, to_mrc_slice};
|
||||
use crate::utils::{Stackframe, to_mrc_slice, mrc_empty_slice};
|
||||
|
||||
use super::{ast, typed};
|
||||
|
||||
@@ -26,7 +26,9 @@ pub enum Error {
|
||||
///
|
||||
/// [expr] handles this case, so it's only really possible to get this
|
||||
/// error if you're calling [clause] directly
|
||||
ExprToClause(typed::Expr)
|
||||
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
|
||||
@@ -44,21 +46,37 @@ pub fn exprv(exprv: &[ast::Expr]) -> Result<typed::Expr, Error> {
|
||||
exprv_rec(exprv, Stackframe::new(None))
|
||||
}
|
||||
|
||||
fn apply_rec(f: typed::Expr, x: &ast::Expr, names: Stackframe<Option<&str>>)
|
||||
-> Result<typed::Clause, Error> {
|
||||
if let ast::Expr(ast::Clause::Explicit(inner), empty_slice) = x {
|
||||
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)?;
|
||||
Ok(typed::Clause::Explicit(Mrc::new(f), Mrc::new(x)))
|
||||
} else {
|
||||
let x = expr_rec(x, names)?;
|
||||
Ok(typed::Clause::Apply(Mrc::new(f), Mrc::new(x)))
|
||||
}
|
||||
}
|
||||
|
||||
/// Recursive state of [exprv]
|
||||
fn exprv_rec(v: &[ast::Expr], names: Stackframe<Option<&str>>) -> Result<typed::Expr, Error> {
|
||||
if v.len() == 0 {return Err(Error::EmptyS)}
|
||||
if v.len() == 1 {return expr_rec(&v[0], names)}
|
||||
let (head, tail) = v.split_at(2);
|
||||
let f = expr_rec(&head[0], names)?;
|
||||
let x = expr_rec(&head[1], names)?;
|
||||
let x = &head[1];
|
||||
// TODO this could probably be normalized, it's a third copy.
|
||||
tail.iter().map(|e| expr_rec(e, names)).fold(
|
||||
Ok(typed::Clause::Apply(Mrc::new(f), Mrc::new(x))),
|
||||
|acc, e| Ok(typed::Clause::Apply(
|
||||
Mrc::new(typed::Expr(acc?, to_mrc_slice(vec![]))),
|
||||
Mrc::new(e?)
|
||||
))
|
||||
).map(|cls| typed::Expr(cls, to_mrc_slice(vec![])))
|
||||
tail.iter().fold(
|
||||
apply_rec(f, x, names),
|
||||
|acc, e| apply_rec(
|
||||
typed::Expr(acc?, mrc_empty_slice()),
|
||||
e,
|
||||
names
|
||||
)
|
||||
).map(|cls| typed::Expr(cls, mrc_empty_slice()))
|
||||
}
|
||||
|
||||
/// Recursive state of [expr]
|
||||
@@ -115,6 +133,7 @@ fn clause_rec(cls: &ast::Clause, names: Stackframe<Option<&str>>)
|
||||
else {Err(Error::ExprToClause(typed::Expr(val, typ)))}
|
||||
},
|
||||
ast::Clause::Name { local: None, .. } => Err(Error::Symbol),
|
||||
ast::Clause::Placeh { .. } => Err(Error::Placeholder)
|
||||
ast::Clause::Placeh { .. } => Err(Error::Placeholder),
|
||||
ast::Clause::Explicit(..) => Err(Error::NonInfixAt)
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,13 @@
|
||||
use hashbrown::HashMap;
|
||||
use mappable_rc::Mrc;
|
||||
|
||||
use crate::{ast::{Expr, Clause}, utils::{iter::{box_once, into_boxed_iter}, to_mrc_slice, one_mrc_slice}, unwrap_or};
|
||||
use super::{super::RuleError, state::{State, Entry}, slice_matcher::SliceMatcherDnC};
|
||||
use crate::unwrap_or;
|
||||
use crate::utils::{to_mrc_slice, one_mrc_slice, mrc_empty_slice};
|
||||
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;
|
||||
|
||||
fn verify_scalar_vec(pattern: &Expr, is_vec: &mut HashMap<String, bool>)
|
||||
-> Result<(), String> {
|
||||
@@ -86,10 +91,14 @@ where F: FnMut(Mrc<[Expr]>) -> Option<Mrc<[Expr]>> {
|
||||
None
|
||||
}
|
||||
|
||||
/// Fill in a template from a state as produced by a pattern
|
||||
fn write_slice(state: &State, tpl: &Mrc<[Expr]>) -> Mrc<[Expr]> {
|
||||
eprintln!("Writing {tpl:?} with state {state:?}");
|
||||
tpl.iter().flat_map(|Expr(clause, xpr_typ)| match clause {
|
||||
// fn write_clause_rec(state: &State, clause: &Clause) ->
|
||||
|
||||
fn write_expr_rec(state: &State, Expr(tpl_clause, tpl_typ): &Expr) -> Box<dyn Iterator<Item = Expr>> {
|
||||
let out_typ = tpl_typ.iter()
|
||||
.flat_map(|c| write_expr_rec(state, &c.clone().into_expr()))
|
||||
.map(Expr::into_clause)
|
||||
.collect::<Mrc<[Clause]>>();
|
||||
match tpl_clause {
|
||||
Clause::Auto(name_opt, typ, body) => box_once(Expr(Clause::Auto(
|
||||
name_opt.as_ref().and_then(|name| {
|
||||
if let Some(state_key) = name.strip_prefix('$') {
|
||||
@@ -102,9 +111,9 @@ fn write_slice(state: &State, tpl: &Mrc<[Expr]>) -> Mrc<[Expr]> {
|
||||
Some(name.to_owned())
|
||||
}
|
||||
}),
|
||||
write_slice(state, typ),
|
||||
write_slice(state, body)
|
||||
), xpr_typ.to_owned())),
|
||||
write_slice_rec(state, typ),
|
||||
write_slice_rec(state, body)
|
||||
), out_typ.to_owned())),
|
||||
Clause::Lambda(name, typ, body) => box_once(Expr(Clause::Lambda(
|
||||
if let Some(state_key) = name.strip_prefix('$') {
|
||||
if let Entry::Name(name) = &state[state_key] {
|
||||
@@ -113,13 +122,13 @@ fn write_slice(state: &State, tpl: &Mrc<[Expr]>) -> Mrc<[Expr]> {
|
||||
} else {
|
||||
name.to_owned()
|
||||
},
|
||||
write_slice(state, typ),
|
||||
write_slice(state, body)
|
||||
), xpr_typ.to_owned())),
|
||||
write_slice_rec(state, typ),
|
||||
write_slice_rec(state, body)
|
||||
), out_typ.to_owned())),
|
||||
Clause::S(c, body) => box_once(Expr(Clause::S(
|
||||
*c,
|
||||
write_slice(state, body)
|
||||
), xpr_typ.to_owned())),
|
||||
write_slice_rec(state, body)
|
||||
), out_typ.to_owned())),
|
||||
Clause::Placeh{key, vec: None} => {
|
||||
let real_key = unwrap_or!(key.strip_prefix('_'); key);
|
||||
match &state[real_key] {
|
||||
@@ -127,17 +136,30 @@ fn write_slice(state: &State, tpl: &Mrc<[Expr]>) -> Mrc<[Expr]> {
|
||||
Entry::Name(n) => box_once(Expr(Clause::Name {
|
||||
local: Some(n.as_ref().to_owned()),
|
||||
qualified: one_mrc_slice(n.as_ref().to_owned())
|
||||
}, to_mrc_slice(vec![]))),
|
||||
}, mrc_empty_slice())),
|
||||
_ => panic!("Scalar template may only be derived from scalar placeholder"),
|
||||
}
|
||||
},
|
||||
Clause::Placeh{key, vec: Some(_)} => if let Entry::Vec(v) = &state[key] {
|
||||
into_boxed_iter(v.as_ref().to_owned())
|
||||
} else {panic!("Vectorial template may only be derived from vectorial placeholder")},
|
||||
Clause::Explicit(param) => {
|
||||
assert!(out_typ.len() == 0, "Explicit should never have a type annotation");
|
||||
box_once(Clause::Explicit(Mrc::new(
|
||||
Clause::from_exprv(write_expr_rec(state, param).collect())
|
||||
.expect("Result shorter than template").into_expr()
|
||||
)).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(), xpr_typ.to_owned()))
|
||||
}).collect()
|
||||
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()
|
||||
}
|
||||
|
||||
/// Apply a rule (a pair of pattern and template) to an expression
|
||||
@@ -156,6 +178,6 @@ pub fn execute(mut src: Mrc<[Expr]>, mut tgt: Mrc<[Expr]>, input: Mrc<[Expr]>)
|
||||
let matcher_cache = SliceMatcherDnC::get_matcher_cache();
|
||||
Ok(update_all_seqs(Mrc::clone(&input), &mut |p| {
|
||||
let state = matcher.match_range_cached(p, &matcher_cache)?;
|
||||
Some(write_slice(&state, &tgt))
|
||||
Some(write_slice_rec(&state, &tgt))
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -28,6 +28,10 @@ where P: for<'a> FnOnce(&'a T) -> Option<&'a U> {
|
||||
Mrc::try_map(Mrc::clone(m), p).ok()
|
||||
}
|
||||
|
||||
pub fn mrc_empty_slice<T>() -> Mrc<[T]> {
|
||||
mrc_derive_slice(&Mrc::new(Vec::new()))
|
||||
}
|
||||
|
||||
pub fn to_mrc_slice<T>(v: Vec<T>) -> Mrc<[T]> {
|
||||
Mrc::map(Mrc::new(v), |v| v.as_slice())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user