executor, mostly

This commit is contained in:
2022-11-07 15:15:38 +00:00
parent d9c35c3591
commit 6900d1213a
18 changed files with 444 additions and 61 deletions

1
Cargo.lock generated
View File

@@ -158,6 +158,7 @@ dependencies = [
"derivative", "derivative",
"hashbrown", "hashbrown",
"itertools", "itertools",
"lazy_static",
"mappable-rc", "mappable-rc",
"ordered-float", "ordered-float",
"smallvec", "smallvec",

View File

@@ -14,3 +14,4 @@ mappable-rc = "0.1"
ordered-float = "3.0" ordered-float = "3.0"
itertools = "0.10" itertools = "0.10"
smallvec = "1.10.0" smallvec = "1.10.0"
lazy_static = "1.4.0"

View File

@@ -1,11 +1,20 @@
## Type definitions ## Type definitions
A new type can be created with the define expression, which associates a templated expression of
type `type` with a name and a template. The name allocated in this fashion is always representedas
an Atom of type `type` or some function that eventually returns `type`. The kind of the template
parameters is always inferred to be `type` rather than deduced from context.
The following type definition
```orc ```orc
define Cons as \T:type. loop \r. Option (Pair T r) define Cons $T as loop \r. Option (Pair $T r)
``` ```
Results in results in these conditions:
- (Cons Int) is not assignable to @T. Option T
- (Cons Int) is not assignable to @T. Option T, or any other type expression that its
definitions would be assignable to, and vice versa.
- An instance of (Cons Int) can be constructed with `categorise @(Cons Int) (some (pair 1 none))` - An instance of (Cons Int) can be constructed with `categorise @(Cons Int) (some (pair 1 none))`
but the type parameter can also be inferred from the expected return type but the type parameter can also be inferred from the expected return type
- An instance of (Cons Int) can be deconstructed with `generalise @(Cons Int) numbers` - An instance of (Cons Int) can be deconstructed with `generalise @(Cons Int) numbers`
@@ -28,8 +37,18 @@ The following must unify:
Mult Int (Cons Int) (Cons Int) Mult Int (Cons Int) (Cons Int)
``` ```
### Impls for types ## Typeclasses
Impls for types are generally not a good idea as autos with types like Int can Typeclasses and types use the same define syntax. In fact, much like a type is nothing but a
often be used in dependent typing to represent eg. an index into a type-level conslist to be distinguished instance of the underlying type with added meaning and constraints, a typeclass is
deduced by the compiler, and impls take precedence over resolution by unification. nothing but a distinguished instance of the underlying function (or collection of functions) with
added meaning and constraints. A typeclass definition is therefore perfectly identical to a type
definition:
```
define Add $T $U $R as $T -> $U -> $R
```
It is clear that the definition of this type would match many, many functions, including
multiplication, so functions that should be considered addition are [impls](./impls.md) of the
typeclass Add.

View File

@@ -11,6 +11,12 @@ An impl candidate can be used to resolve an auto if
- it is not present in any other matching impl's override tree - it is not present in any other matching impl's override tree
- all other candidates are present in its override tree - all other candidates are present in its override tree
### Impls for types
Impls for types are generally not a good idea as autos with types like Int can
often be used in dependent typing to represent eg. an index into a type-level conslist to be
deduced by the compiler, and impls take precedence over resolution by unification.
In Rust impls can be placed in one of two modules; the trait owner, and the type owner. In orchid In Rust impls can be placed in one of two modules; the trait owner, and the type owner. In orchid
that is not the case, so two additional possibilities arise that Rust's orphan rules prevent. that is not the case, so two additional possibilities arise that Rust's orphan rules prevent.

View 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))
}
}

View File

@@ -10,7 +10,6 @@ pub trait ExternError: Display {}
/// Represents an externally defined function from the perspective of the executor /// Represents an externally defined function from the perspective of the executor
/// Since Orchid lacks basic numerical operations, these are also external functions. /// Since Orchid lacks basic numerical operations, these are also external functions.
#[derive(Eq)]
pub struct ExternFn { pub struct ExternFn {
name: String, param: Mrc<Expr>, rttype: Mrc<Expr>, name: String, param: Mrc<Expr>, rttype: Mrc<Expr>,
function: Mrc<dyn Fn(Clause) -> Result<Clause, Mrc<dyn ExternError>>> function: Mrc<dyn Fn(Clause) -> Result<Clause, Mrc<dyn ExternError>>>
@@ -27,8 +26,8 @@ impl ExternFn {
}) })
} }
} }
fn name(&self) -> &str {&self.name} pub fn name(&self) -> &str {&self.name}
fn apply(&self, arg: Clause) -> Result<Clause, Mrc<dyn ExternError>> {(self.function)(arg)} pub fn apply(&self, arg: Clause) -> Result<Clause, Mrc<dyn ExternError>> {(self.function)(arg)}
} }
impl Clone for ExternFn { fn clone(&self) -> Self { Self { 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), rttype: Mrc::clone(&self.rttype),
function: Mrc::clone(&self.function) 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 { impl Hash for ExternFn {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) { self.name.hash(state) } 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 /// 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 /// the kind of types. Ad absurdum it can also be just a number, although Literal is
/// preferable for types it's defined on. /// preferable for types it's defined on.
#[derive(Eq)]
pub struct Atom { pub struct Atom {
typ: Mrc<Expr>, typ: Mrc<Expr>,
data: Mrc<dyn Atomic> data: Mrc<dyn Atomic>
@@ -95,6 +96,7 @@ impl Debug for Atom {
write!(f, "##ATOM[{:?}]:{:?}##", self.data(), self.typ) write!(f, "##ATOM[{:?}]:{:?}##", self.data(), self.typ)
} }
} }
impl Eq for Atom {}
impl PartialEq for Atom { impl PartialEq for Atom {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
self.data().definitely_eq(other.data().as_any()) self.data().definitely_eq(other.data().as_any())

View File

@@ -1,3 +1,7 @@
mod foreign; mod foreign;
// mod normalize;
mod partial_hash;
mod reduction_tree;
mod apply_lambda;
pub use foreign::ExternFn; pub use foreign::ExternFn;
pub use foreign::Atom; pub use foreign::Atom;

30
src/executor/normalize.rs Normal file
View 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)
)
)
)}
}

View 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) }
}
}

View 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
View 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) }
}
}

View File

@@ -1,4 +1,5 @@
#![feature(specialization)] #![feature(specialization)]
#![feature(core_intrinsics)]
use std::env::current_dir; use std::env::current_dir;

View File

@@ -1,4 +1,5 @@
use chumsky::{self, prelude::*, Parser}; use chumsky::{self, prelude::*, Parser};
use mappable_rc::Mrc;
use crate::enum_parser; use crate::enum_parser;
use crate::representations::{Literal, ast::{Clause, Expr}}; use crate::representations::{Literal, ast::{Clause, Expr}};
use crate::utils::{to_mrc_slice, one_mrc_slice}; 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()), sexpr_parser(expr.clone()),
lambda_parser(expr.clone()), lambda_parser(expr.clone()),
auto_parser(expr.clone()), auto_parser(expr.clone()),
just(Lexeme::At).to(Clause::Name { just(Lexeme::At).ignore_then(expr.clone()).map(|arg| {
local: Some("@".to_string()), Clause::Explicit(Mrc::new(arg))
qualified: one_mrc_slice("@".to_string())
}) })
))).then_ignore(enum_parser!(Lexeme::Comment).repeated()); ))).then_ignore(enum_parser!(Lexeme::Comment).repeated());
clause.clone().then( clause.clone().then(

View File

@@ -157,8 +157,13 @@ where
.filter_map(|ent| { .filter_map(|ent| {
if let FileEntry::Rule(Rule{source, prio, target}, _) = ent { if let FileEntry::Rule(Rule{source, prio, target}, _) = ent {
Some(Rule { Some(Rule {
source: source.iter().map(|ex| prefix_expr(ex, Mrc::clone(&path))).collect(), source: source.iter()
target: target.iter().map(|ex| prefix_expr(ex, Mrc::clone(&path))).collect(), .map(|ex| {
prefix_expr(ex, Mrc::clone(&path))
}).collect(),
target: target.iter().map(|ex| {
prefix_expr(ex, Mrc::clone(&path))
}).collect(),
prio: *prio, prio: *prio,
}) })
} else { None } } else { None }

View File

@@ -1,15 +1,22 @@
use mappable_rc::Mrc; use mappable_rc::Mrc;
use itertools::Itertools; use itertools::Itertools;
use ordered_float::NotNan; use ordered_float::NotNan;
use std::hash::Hash; use std::{hash::Hash, intrinsics::likely};
use std::fmt::Debug; 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; use super::Literal;
/// An S-expression with a type /// An S-expression with a type
#[derive(PartialEq, Eq, Hash)] #[derive(PartialEq, Eq, Hash)]
pub struct Expr(pub Clause, pub Mrc<[Clause]>); 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 { impl Clone for Expr {
fn clone(&self) -> Self { fn clone(&self) -> Self {
@@ -37,6 +44,7 @@ pub enum Clause {
qualified: Mrc<[String]> qualified: Mrc<[String]>
}, },
S(char, Mrc<[Expr]>), S(char, Mrc<[Expr]>),
Explicit(Mrc<Expr>),
Lambda(String, Mrc<[Expr]>, Mrc<[Expr]>), Lambda(String, Mrc<[Expr]>, Mrc<[Expr]>),
Auto(Option<String>, Mrc<[Expr]>, Mrc<[Expr]>), Auto(Option<String>, Mrc<[Expr]>, Mrc<[Expr]>),
ExternFn(ExternFn), ExternFn(ExternFn),
@@ -53,37 +61,49 @@ pub enum Clause {
impl Clause { impl Clause {
pub fn body(&self) -> Option<Mrc<[Expr]>> { pub fn body(&self) -> Option<Mrc<[Expr]>> {
match self { match self {
Clause::Auto(_, _, body) | Self::Auto(_, _, body) |
Clause::Lambda(_, _, body) | Self::Lambda(_, _, body) |
Clause::S(_, body) => Some(Mrc::clone(body)), Self::S(_, body) => Some(Mrc::clone(body)),
_ => None _ => None
} }
} }
pub fn typ(&self) -> Option<Mrc<[Expr]>> { pub fn typ(&self) -> Option<Mrc<[Expr]>> {
match self { match self {
Clause::Auto(_, typ, _) | Clause::Lambda(_, typ, _) => Some(Mrc::clone(typ)), Self::Auto(_, typ, _) | Self::Lambda(_, typ, _) => Some(Mrc::clone(typ)),
_ => None _ => 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 { impl Clone for Clause {
fn clone(&self) -> Self { fn clone(&self) -> Self {
match self { match self {
Clause::S(c, b) => Clause::S(*c, Mrc::clone(b)), Self::S(c, b) => Self::S(*c, Mrc::clone(b)),
Clause::Auto(n, t, b) => Clause::Auto( Self::Auto(n, t, b) => Self::Auto(
n.clone(), Mrc::clone(t), Mrc::clone(b) 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) 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) n.clone(), Mrc::clone(t), Mrc::clone(b)
), ),
Clause::Placeh{key, vec} => Clause::Placeh{key: key.clone(), vec: *vec}, Self::Placeh{key, vec} => Self::Placeh{key: key.clone(), vec: *vec},
Clause::Literal(l) => Clause::Literal(l.clone()), Self::Literal(l) => Self::Literal(l.clone()),
Clause::ExternFn(nc) => Clause::ExternFn(nc.clone()), Self::ExternFn(nc) => Self::ExternFn(nc.clone()),
Clause::Atom(a) => Clause::Atom(a.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, true))} => write!(f, "...${key}:{prio}"),
Self::Placeh{key, vec: Some((prio, false))} => write!(f, "..${key}:{prio}"), Self::Placeh{key, vec: Some((prio, false))} => write!(f, "..${key}:{prio}"),
Self::ExternFn(nc) => write!(f, "{nc:?}"), 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())
} }
} }
} }

View File

@@ -1,6 +1,6 @@
use mappable_rc::Mrc; 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}; use super::{ast, typed};
@@ -26,7 +26,9 @@ pub enum Error {
/// ///
/// [expr] handles this case, so it's only really possible to get this /// [expr] handles this case, so it's only really possible to get this
/// error if you're calling [clause] directly /// 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 /// 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)) 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] /// Recursive state of [exprv]
fn exprv_rec(v: &[ast::Expr], names: Stackframe<Option<&str>>) -> Result<typed::Expr, Error> { 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() == 0 {return Err(Error::EmptyS)}
if v.len() == 1 {return expr_rec(&v[0], names)} if v.len() == 1 {return expr_rec(&v[0], names)}
let (head, tail) = v.split_at(2); let (head, tail) = v.split_at(2);
let f = expr_rec(&head[0], names)?; 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. // TODO this could probably be normalized, it's a third copy.
tail.iter().map(|e| expr_rec(e, names)).fold( tail.iter().fold(
Ok(typed::Clause::Apply(Mrc::new(f), Mrc::new(x))), apply_rec(f, x, names),
|acc, e| Ok(typed::Clause::Apply( |acc, e| apply_rec(
Mrc::new(typed::Expr(acc?, to_mrc_slice(vec![]))), typed::Expr(acc?, mrc_empty_slice()),
Mrc::new(e?) e,
)) names
).map(|cls| typed::Expr(cls, to_mrc_slice(vec![]))) )
).map(|cls| typed::Expr(cls, mrc_empty_slice()))
} }
/// Recursive state of [expr] /// 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)))} else {Err(Error::ExprToClause(typed::Expr(val, typ)))}
}, },
ast::Clause::Name { local: None, .. } => Err(Error::Symbol), 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)
} }
} }

View File

@@ -1,8 +1,13 @@
use hashbrown::HashMap; use hashbrown::HashMap;
use mappable_rc::Mrc; 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 crate::unwrap_or;
use super::{super::RuleError, state::{State, Entry}, slice_matcher::SliceMatcherDnC}; 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>) fn verify_scalar_vec(pattern: &Expr, is_vec: &mut HashMap<String, bool>)
-> Result<(), String> { -> Result<(), String> {
@@ -86,10 +91,14 @@ where F: FnMut(Mrc<[Expr]>) -> Option<Mrc<[Expr]>> {
None None
} }
/// Fill in a template from a state as produced by a pattern // fn write_clause_rec(state: &State, clause: &Clause) ->
fn write_slice(state: &State, tpl: &Mrc<[Expr]>) -> Mrc<[Expr]> {
eprintln!("Writing {tpl:?} with state {state:?}"); fn write_expr_rec(state: &State, Expr(tpl_clause, tpl_typ): &Expr) -> Box<dyn Iterator<Item = Expr>> {
tpl.iter().flat_map(|Expr(clause, xpr_typ)| match clause { 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( Clause::Auto(name_opt, typ, body) => box_once(Expr(Clause::Auto(
name_opt.as_ref().and_then(|name| { name_opt.as_ref().and_then(|name| {
if let Some(state_key) = name.strip_prefix('$') { 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()) Some(name.to_owned())
} }
}), }),
write_slice(state, typ), write_slice_rec(state, typ),
write_slice(state, body) write_slice_rec(state, body)
), xpr_typ.to_owned())), ), out_typ.to_owned())),
Clause::Lambda(name, typ, body) => box_once(Expr(Clause::Lambda( Clause::Lambda(name, typ, body) => box_once(Expr(Clause::Lambda(
if let Some(state_key) = name.strip_prefix('$') { if let Some(state_key) = name.strip_prefix('$') {
if let Entry::Name(name) = &state[state_key] { if let Entry::Name(name) = &state[state_key] {
@@ -113,13 +122,13 @@ fn write_slice(state: &State, tpl: &Mrc<[Expr]>) -> Mrc<[Expr]> {
} else { } else {
name.to_owned() name.to_owned()
}, },
write_slice(state, typ), write_slice_rec(state, typ),
write_slice(state, body) write_slice_rec(state, body)
), xpr_typ.to_owned())), ), out_typ.to_owned())),
Clause::S(c, body) => box_once(Expr(Clause::S( Clause::S(c, body) => box_once(Expr(Clause::S(
*c, *c,
write_slice(state, body) write_slice_rec(state, body)
), xpr_typ.to_owned())), ), out_typ.to_owned())),
Clause::Placeh{key, vec: None} => { Clause::Placeh{key, vec: None} => {
let real_key = unwrap_or!(key.strip_prefix('_'); key); let real_key = unwrap_or!(key.strip_prefix('_'); key);
match &state[real_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 { Entry::Name(n) => box_once(Expr(Clause::Name {
local: Some(n.as_ref().to_owned()), local: Some(n.as_ref().to_owned()),
qualified: one_mrc_slice(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"), _ => panic!("Scalar template may only be derived from scalar placeholder"),
} }
}, },
Clause::Placeh{key, vec: Some(_)} => if let Entry::Vec(v) = &state[key] { Clause::Placeh{key, vec: Some(_)} => if let Entry::Vec(v) = &state[key] {
into_boxed_iter(v.as_ref().to_owned()) into_boxed_iter(v.as_ref().to_owned())
} else {panic!("Vectorial template may only be derived from vectorial placeholder")}, } 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 // 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(_) => c@Clause::Literal(_) | c@Clause::Name { .. } | c@Clause::ExternFn(_) | c@Clause::Atom(_) =>
box_once(Expr(c.to_owned(), xpr_typ.to_owned())) box_once(Expr(c.to_owned(), out_typ.to_owned()))
}).collect() }
}
/// 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 /// 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(); let matcher_cache = SliceMatcherDnC::get_matcher_cache();
Ok(update_all_seqs(Mrc::clone(&input), &mut |p| { Ok(update_all_seqs(Mrc::clone(&input), &mut |p| {
let state = matcher.match_range_cached(p, &matcher_cache)?; let state = matcher.match_range_cached(p, &matcher_cache)?;
Some(write_slice(&state, &tgt)) Some(write_slice_rec(&state, &tgt))
})) }))
} }

View File

@@ -28,6 +28,10 @@ where P: for<'a> FnOnce(&'a T) -> Option<&'a U> {
Mrc::try_map(Mrc::clone(m), p).ok() 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]> { pub fn to_mrc_slice<T>(v: Vec<T>) -> Mrc<[T]> {
Mrc::map(Mrc::new(v), |v| v.as_slice()) Mrc::map(Mrc::new(v), |v| v.as_slice())
} }