forked from Orchid/orchid
Sync commit
This commit is contained in:
@@ -1,67 +1,79 @@
|
||||
use mappable_rc::Mrc;
|
||||
|
||||
use crate::utils::collect_to_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)
|
||||
pub fn apply_lambda(id: u64, value: Mrc<Expr>, body: Mrc<Expr>) -> Mrc<Expr> {
|
||||
apply_lambda_expr_rec(id, value, Mrc::clone(&body))
|
||||
.unwrap_or(body)
|
||||
}
|
||||
|
||||
fn apply_lambda_expr_rec(
|
||||
item: Mrc<Expr>, arg: Mrc<Expr>, depth: usize
|
||||
id: u64, value: Mrc<Expr>, expr: Mrc<Expr>
|
||||
) -> 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))))
|
||||
let Expr(clause, typ) = expr.as_ref();
|
||||
match clause {
|
||||
Clause::Argument(arg_id) if *arg_id == id => {
|
||||
let full_typ = collect_to_mrc(
|
||||
value.1.iter()
|
||||
.chain(typ.iter())
|
||||
.cloned()
|
||||
);
|
||||
Some(Mrc::new(Expr(value.0.to_owned(), full_typ)))
|
||||
}
|
||||
cl => {
|
||||
apply_lambda_clause_rec(id, value, clause.clone())
|
||||
.map(|c| Mrc::new(Expr(c, Mrc::clone(typ))))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_lambda_clause_rec(
|
||||
clause: Clause, arg: Mrc<Expr>, depth: usize
|
||||
id: u64, value: Mrc<Expr>, clause: Clause
|
||||
) -> 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
|
||||
}
|
||||
Clause::Argument(id) => panic!(
|
||||
"apply_lambda_expr_rec is supposed to eliminate this case"),
|
||||
// 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),
|
||||
Clause::Apply(f, x) => {
|
||||
let new_f = apply_lambda_expr_rec(
|
||||
id, Mrc::clone(&value), Mrc::clone(&f)
|
||||
);
|
||||
let new_x = apply_lambda_expr_rec(
|
||||
id, value, Mrc::clone(&x)
|
||||
);
|
||||
match (new_f, new_x) { // Mind the shadows
|
||||
(None, None) => None,
|
||||
(None, Some(x)) => Some(Clause::Apply(f, x)),
|
||||
(Some(f), None) => Some(Clause::Apply(f, x)),
|
||||
(Some(f), Some(x)) => Some(Clause::Apply(f, x))
|
||||
}
|
||||
},
|
||||
Clause::Lambda(own_id, t, b) => apply_lambda__traverse_param(id, value, own_id, t, b, Clause::Lambda),
|
||||
Clause::Auto(own_id, t, b) => apply_lambda__traverse_param(id, value, own_id, 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
|
||||
id: u64, value: Mrc<Expr>,
|
||||
own_id: u64, t: Option<Mrc<Clause>>, b: Mrc<Expr>,
|
||||
wrap: impl Fn(u64, 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) {
|
||||
let new_t = t.and_then(|t| apply_lambda_clause_rec(
|
||||
id, Mrc::clone(&value), t.as_ref().clone()
|
||||
));
|
||||
// Respect shadowing
|
||||
let new_b = if own_id == id {None} else {
|
||||
apply_lambda_expr_rec(id, value, Mrc::clone(&b))
|
||||
};
|
||||
match (new_t, new_b) { // Mind the shadows
|
||||
(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))
|
||||
(None, Some(b)) => Some(wrap(own_id, t, b)),
|
||||
(Some(t), None) => Some(wrap(own_id, Some(Mrc::new(t)), b)),
|
||||
(Some(t), Some(b)) => Some(wrap(own_id, Some(Mrc::new(t)), b))
|
||||
}
|
||||
}
|
||||
@@ -1,33 +1,51 @@
|
||||
use std::hash::{Hasher, Hash};
|
||||
|
||||
use itertools::Itertools;
|
||||
|
||||
use crate::utils::ProtoMap;
|
||||
|
||||
use super::super::representations::typed::{Clause, Expr};
|
||||
use super::super::utils::Stackframe;
|
||||
|
||||
const PARAMETRICS_INLINE_COUNT:usize = 5;
|
||||
type Parametrics<'a> = ProtoMap<'a, u64, bool, PARAMETRICS_INLINE_COUNT>;
|
||||
|
||||
/// 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>) {
|
||||
pub fn partial_hash_rec<H: Hasher>(
|
||||
Expr(clause, _): &Expr, state: &mut H,
|
||||
mut parametrics: Parametrics
|
||||
) {
|
||||
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),
|
||||
// Skip autos
|
||||
Clause::Auto(id, _, body) => {
|
||||
parametrics.set(id, true);
|
||||
partial_hash_rec(body, state, parametrics)
|
||||
}
|
||||
// Annotate everything else with a prefix
|
||||
// - Recurse into the tree of lambdas and calls - classic lambda calc
|
||||
Clause::Lambda(_, body) => {
|
||||
Clause::Lambda(id, _, body) => {
|
||||
state.write_u8(0);
|
||||
partial_hash_rec(body, state, is_auto.push(false))
|
||||
parametrics.set(id, false);
|
||||
partial_hash_rec(body, state, parametrics)
|
||||
}
|
||||
Clause::Apply(f, x) => {
|
||||
state.write_u8(1);
|
||||
partial_hash_rec(f, state, is_auto);
|
||||
partial_hash_rec(x, state, is_auto);
|
||||
partial_hash_rec(f, state, parametrics.clone());
|
||||
partial_hash_rec(x, state, parametrics);
|
||||
}
|
||||
// - Only recognize the depth of an argument if it refers to a non-auto parameter
|
||||
Clause::Argument(depth) => {
|
||||
Clause::Argument(own_id) => {
|
||||
let (pos, is_auto) = parametrics.iter()
|
||||
.filter_map(|(id, is_auto)| is_auto.map(|is_auto| (*id, is_auto)))
|
||||
.find_position(|(id, is_auto)| id == own_id)
|
||||
.map(|(pos, (_, is_auto))| (pos, is_auto))
|
||||
.unwrap_or((usize::MAX, false));
|
||||
// If the argument references an auto, acknowledge its existence
|
||||
if *is_auto.iter().nth(*depth).unwrap_or(&false) {
|
||||
if is_auto {
|
||||
state.write_u8(2)
|
||||
} else {
|
||||
state.write_u8(3);
|
||||
state.write_usize(*depth)
|
||||
state.write_usize(pos)
|
||||
}
|
||||
}
|
||||
// - Hash leaves like normal
|
||||
|
||||
@@ -10,48 +10,38 @@ 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.
|
||||
pub fn skip_autos<'a,
|
||||
F: 'a + FnOnce(Mrc<Expr>, usize) -> I,
|
||||
F: 'a + FnOnce(Mrc<Expr>) -> I,
|
||||
I: Iterator<Item = Mrc<Expr>> + 'static
|
||||
>(
|
||||
depth: usize, expr: Mrc<Expr>, function: F
|
||||
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>>
|
||||
}
|
||||
_ => ()
|
||||
if let Expr(Clause::Auto(id, arg, body), typ) = expr.as_ref() {
|
||||
return Box::new(skip_autos(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(
|
||||
*id,
|
||||
arg.as_ref().map(Mrc::clone),
|
||||
body
|
||||
), Mrc::clone(&typ)))
|
||||
}
|
||||
})) as BoxedIter<'static, Mrc<Expr>>
|
||||
}
|
||||
Box::new(function(expr, depth))
|
||||
Box::new(function(expr))
|
||||
}
|
||||
|
||||
/// 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, _| {
|
||||
skip_autos(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, _| {
|
||||
skip_autos(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::Lambda(id, _, body) => box_once(
|
||||
apply_lambda(*id, Mrc::clone(x), Mrc::clone(body))
|
||||
),
|
||||
Clause::ExternFn(xfn) => {
|
||||
let Expr(xval, xtyp) = x.as_ref();
|
||||
@@ -63,8 +53,7 @@ fn direct_reductions(ex: Mrc<Expr>) -> impl Iterator<Item = Mrc<Expr>> {
|
||||
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"),
|
||||
Clause::Auto(..) => unreachable!("skip_autos should have filtered this"),
|
||||
}
|
||||
}),
|
||||
direct_reductions(Mrc::clone(f)).map({
|
||||
@@ -84,18 +73,24 @@ fn direct_reductions(ex: Mrc<Expr>) -> impl Iterator<Item = Mrc<Expr>> {
|
||||
), Mrc::clone(&typ)))
|
||||
})
|
||||
),
|
||||
Clause::Lambda(argt, body) => Box::new(direct_reductions(Mrc::clone(body)).map({
|
||||
Clause::Lambda(id, argt, body) => {
|
||||
let id = *id;
|
||||
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)))
|
||||
})),
|
||||
let body = Mrc::clone(body);
|
||||
let body_reductions = direct_reductions(body)
|
||||
.map(move |body| {
|
||||
let argt = argt.as_ref().map(Mrc::clone);
|
||||
Mrc::new(Expr(
|
||||
Clause::Lambda(id, argt, body),
|
||||
Mrc::clone(&typ)
|
||||
))
|
||||
});
|
||||
Box::new(body_reductions)
|
||||
},
|
||||
Clause::Literal(..) | Clause::ExternFn(..) | Clause::Atom(..) | Clause::Argument(..) =>
|
||||
box_empty(),
|
||||
Clause::Auto(..) | Clause::Explicit(..) =>
|
||||
unreachable!("skip_autos should have filtered these"),
|
||||
Clause::Auto(..) => unreachable!("skip_autos should have filtered this"),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,110 +1,85 @@
|
||||
use std::collections::HashMap;
|
||||
use std::hash::{Hasher, Hash};
|
||||
use std::iter;
|
||||
|
||||
use mappable_rc::Mrc;
|
||||
|
||||
use crate::utils::ProtoMap;
|
||||
use crate::utils::{ProtoMap, Side};
|
||||
|
||||
use super::super::representations::typed::{Clause, Expr};
|
||||
use super::super::utils::Stackframe;
|
||||
|
||||
pub fn swap<T, U>((t, u): (T, U)) -> (U, T) { (u, t) }
|
||||
|
||||
// @ @ (0, (foo 1)) ~ @ (0, 0)
|
||||
|
||||
// TODO:
|
||||
// - get rid of leftovers from Explicit
|
||||
// - adapt to new index-based system
|
||||
|
||||
// =@= =&= =%= =#= =$= =?= =!= =/=
|
||||
// <@> <&> <%> <#> <$> <?> <!> </>
|
||||
// |@| |&| |%| |#| |$| |?| |!| |/|
|
||||
// {@} {&} {%} {#} {$} {?} {!} {/}
|
||||
// (@) (&) (%) (#) ($) (?) (!) (/)
|
||||
// [@] [&] [%] [#] [$] [?] [!] [/]
|
||||
|
||||
/// The context associates a given variable (by absolute index) on a given side to
|
||||
/// an expression on the opposite side rooted at the specified depth.
|
||||
/// The root depths are used to translate betwee de Brujin arguments and absolute indices.
|
||||
struct Context(HashMap<u64, Mrc<Expr>>);
|
||||
impl Context {
|
||||
fn set(&mut self, id: u64, value: Mrc<Expr>) {
|
||||
// If already defined, then it must be an argument
|
||||
if let Some(value) = self.0.get(&id) {
|
||||
if let Clause::Argument(opposite_up) ex.0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const IS_AUTO_INLINE:usize = 5;
|
||||
|
||||
// All data to be forwarded during recursion about one half of a unification task
|
||||
#[derive(Clone)]
|
||||
struct UnifHalfTask<'a> {
|
||||
/// The expression to be unified
|
||||
expr: &'a Expr,
|
||||
/// Auto parameters with their values from the opposite side
|
||||
ctx: &'a ProtoMap<'a, usize, Mrc<Expr>>,
|
||||
/// Stores whether a given relative upreference is auto or lambda
|
||||
is_auto: Option<Stackframe<'a, bool>>,
|
||||
/// Metastack of explicit arguments not yet resolved. An explicit will always exactly pair with
|
||||
/// the first auto below it. Disjoint autos always bubble with a left-to-right precedence.
|
||||
explicits: Option<Stackframe<'a, Mrc<Expr>>>
|
||||
/// Stores whether a given uid is auto or lambda
|
||||
is_auto: ProtoMap<'a, usize, bool, IS_AUTO_INLINE>
|
||||
}
|
||||
|
||||
impl<'a> UnifHalfTask<'a> {
|
||||
fn push_auto(&self, body: &Expr) -> (Self, bool) {
|
||||
if let Some(Stackframe{ prev, .. }) = self.explicits {(
|
||||
Self{
|
||||
expr: body,
|
||||
is_auto: Stackframe::opush(&self.is_auto, false),
|
||||
explicits: prev.cloned(),
|
||||
..*self
|
||||
},
|
||||
true
|
||||
)} else {(
|
||||
Self{
|
||||
expr: body,
|
||||
is_auto: Stackframe::opush(&self.is_auto, true),
|
||||
..*self
|
||||
},
|
||||
false
|
||||
)}
|
||||
fn push_auto(&mut self, body: &Expr, key: usize) {
|
||||
self.expr = body;
|
||||
self.is_auto.set(&key, true);
|
||||
}
|
||||
|
||||
fn push_lambda(&self, body: &Expr) -> Self {Self{
|
||||
expr: body,
|
||||
is_auto: Stackframe::opush(&self.is_auto, false),
|
||||
..*self
|
||||
}}
|
||||
|
||||
fn push_explicit(&self, subexpr: &Expr, arg: Mrc<Expr>) -> Self {Self{
|
||||
expr: subexpr,
|
||||
explicits: Stackframe::opush(&self.explicits, arg),
|
||||
..*self
|
||||
}}
|
||||
|
||||
fn push_expr(&self, f: &Expr) -> Self {Self{
|
||||
expr: f,
|
||||
..*self
|
||||
}}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct UnifResult {
|
||||
/// Collected identities for the given side
|
||||
context: HashMap<usize, Mrc<Expr>>,
|
||||
/// Number of explicits to be eliminated from task before forwarding to the next branch
|
||||
usedExplicits: usize,
|
||||
}
|
||||
|
||||
impl UnifResult {
|
||||
fn useExplicit(self) -> Self{Self{
|
||||
usedExplicits: self.usedExplicits + 1,
|
||||
context: self.context.clone()
|
||||
}}
|
||||
|
||||
fn dropUsedExplicits(&mut self, task: &mut UnifHalfTask) {
|
||||
task.explicits = task.explicits.map(|s| {
|
||||
s.pop(self.usedExplicits).expect("More explicits used than provided")
|
||||
}).cloned();
|
||||
self.usedExplicits = 0;
|
||||
fn push_lambda(&mut self, body: &Expr, key: usize) {
|
||||
self.expr = body;
|
||||
self.is_auto.set(&key, false);
|
||||
}
|
||||
}
|
||||
|
||||
type Ctx = HashMap<usize, Mrc<Expr>>;
|
||||
|
||||
/// Ascertain syntactic equality. Syntactic equality means that
|
||||
/// - lambda elements are verbatim equal
|
||||
/// - auto constraints are pairwise syntactically equal after sorting
|
||||
///
|
||||
/// Context associates variables with subtrees resolved on the opposite side
|
||||
pub fn unify_syntax_rec( // the stacks store true for autos, false for lambdas
|
||||
ctx: &mut HashMap<(Side, usize), (usize, Mrc<Expr>)>,
|
||||
ltask@UnifHalfTask{ expr: lexpr@Expr(lclause, _), .. }: UnifHalfTask,
|
||||
rtask@UnifHalfTask{ expr: rexpr@Expr(rclause, _), .. }: UnifHalfTask
|
||||
) -> Option<(UnifResult, UnifResult)> {
|
||||
// Ensure that ex1 is a value-level construct
|
||||
match lclause {
|
||||
Clause::Auto(_, body) => {
|
||||
Clause::Auto(id, _, body) => {
|
||||
let res = unify_syntax_rec(ltask.push_auto(body).0, rtask);
|
||||
return if ltask.explicits.is_some() {
|
||||
res.map(|(r1, r2)| (r1.useExplicit(), r2))
|
||||
} else {res}
|
||||
}
|
||||
Clause::Explicit(subexpr, arg) => {
|
||||
let new_ltask = ltask.push_explicit(subexpr, Mrc::clone(arg));
|
||||
return unify_syntax_rec(new_ltask, rtask)
|
||||
}
|
||||
_ => ()
|
||||
};
|
||||
// Reduce ex2's auto handling to ex1's. In the optimizer we trust
|
||||
|
||||
16
src/main.rs
16
src/main.rs
@@ -48,17 +48,17 @@ fn initial_tree() -> Mrc<[Expr]> {
|
||||
|
||||
#[allow(unused)]
|
||||
fn typed_notation_debug() {
|
||||
let true_ex = t::Clause::Auto(None,
|
||||
t::Clause::Lambda(Some(Mrc::new(t::Clause::Argument(0))),
|
||||
t::Clause::Lambda(Some(Mrc::new(t::Clause::Argument(1))),
|
||||
t::Clause::Argument(1).wrap_t(t::Clause::Argument(2))
|
||||
let true_ex = t::Clause::Auto(0, None,
|
||||
t::Clause::Lambda(1, Some(Mrc::new(t::Clause::Argument(0))),
|
||||
t::Clause::Lambda(2, Some(Mrc::new(t::Clause::Argument(0))),
|
||||
t::Clause::Argument(1).wrap_t(t::Clause::Argument(0))
|
||||
).wrap()
|
||||
).wrap()
|
||||
).wrap();
|
||||
let false_ex = t::Clause::Auto(None,
|
||||
t::Clause::Lambda(Some(Mrc::new(t::Clause::Argument(0))),
|
||||
t::Clause::Lambda(Some(Mrc::new(t::Clause::Argument(1))),
|
||||
t::Clause::Argument(0).wrap_t(t::Clause::Argument(2))
|
||||
let false_ex = t::Clause::Auto(0, None,
|
||||
t::Clause::Lambda(1, Some(Mrc::new(t::Clause::Argument(0))),
|
||||
t::Clause::Lambda(2, Some(Mrc::new(t::Clause::Argument(0))),
|
||||
t::Clause::Argument(2).wrap_t(t::Clause::Argument(0))
|
||||
).wrap()
|
||||
).wrap()
|
||||
).wrap();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use mappable_rc::Mrc;
|
||||
|
||||
use crate::utils::{Stackframe, to_mrc_slice, mrc_empty_slice};
|
||||
use crate::utils::{Stackframe, to_mrc_slice, mrc_empty_slice, ProtoMap};
|
||||
|
||||
use super::{ast, typed};
|
||||
|
||||
@@ -33,103 +33,151 @@ pub enum Error {
|
||||
|
||||
/// Try to convert an expression from AST format to typed lambda
|
||||
pub fn expr(expr: &ast::Expr) -> Result<typed::Expr, Error> {
|
||||
expr_rec(expr, Stackframe::new(None))
|
||||
Ok(expr_rec(expr, ProtoMap::new(), &mut 0, None)?.0)
|
||||
}
|
||||
|
||||
/// Try and convert a single clause from AST format to typed lambda
|
||||
pub fn clause(clause: &ast::Clause) -> Result<typed::Clause, Error> {
|
||||
clause_rec(clause, Stackframe::new(None))
|
||||
Ok(clause_rec(clause, ProtoMap::new(), &mut 0, 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> {
|
||||
exprv_rec(exprv, Stackframe::new(None))
|
||||
Ok(exprv_rec(exprv, ProtoMap::new(), &mut 0, None)?.0)
|
||||
}
|
||||
|
||||
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 {
|
||||
const NAMES_INLINE_COUNT:usize = 3;
|
||||
|
||||
/// Recursive state of [exprv]
|
||||
fn exprv_rec(
|
||||
v: &[ast::Expr],
|
||||
names: ProtoMap<&str, u64, NAMES_INLINE_COUNT>,
|
||||
next_id: &mut u64,
|
||||
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, next_id, 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)?;
|
||||
Ok(typed::Clause::Explicit(Mrc::new(f), Mrc::new(x)))
|
||||
let (x, _) = expr_rec(inner.as_ref(), names, next_id, None)?;
|
||||
let new_explicits = Some(&Stackframe::opush(explicits, Mrc::new(x)));
|
||||
let (body, used_expls) = exprv_rec(rest, names, next_id, new_explicits)?;
|
||||
Ok((body, used_expls.saturating_sub(1)))
|
||||
} else {
|
||||
let x = expr_rec(x, names)?;
|
||||
Ok(typed::Clause::Apply(Mrc::new(f), Mrc::new(x)))
|
||||
let (f, f_used_expls) = exprv_rec(rest, names, next_id, explicits)?;
|
||||
let x_explicits = Stackframe::opop(explicits, f_used_expls);
|
||||
let (x, x_used_expls) = expr_rec(last, names, next_id, 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 [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 = &head[1];
|
||||
// TODO this could probably be normalized, it's a third copy.
|
||||
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]
|
||||
fn expr_rec(ast::Expr(val, typ): &ast::Expr, names: Stackframe<Option<&str>>)
|
||||
-> Result<typed::Expr, Error> {
|
||||
fn expr_rec(
|
||||
ast::Expr(val, typ): &ast::Expr,
|
||||
names: ProtoMap<&str, u64, NAMES_INLINE_COUNT>,
|
||||
next_id: &mut u64,
|
||||
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| clause_rec(c, names))
|
||||
.map(|c| Ok(clause_rec(c, names, next_id, 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) = exprv_rec(body.as_ref(), names)?;
|
||||
let (typed::Expr(inner, inner_t), used_expls) = exprv_rec(
|
||||
body.as_ref(), names, next_id, 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))
|
||||
Ok((typed::Expr(inner, new_t), used_expls))
|
||||
} else {
|
||||
Ok(typed::Expr(clause_rec(&val, names)?, to_mrc_slice(typ)))
|
||||
let (cls, used_expls) = clause_rec(&val, names, next_id, explicits)?;
|
||||
Ok((typed::Expr(cls, to_mrc_slice(typ)), used_expls))
|
||||
}
|
||||
}
|
||||
|
||||
/// Recursive state of [clause]
|
||||
fn clause_rec(cls: &ast::Clause, names: Stackframe<Option<&str>>)
|
||||
-> Result<typed::Clause, Error> {
|
||||
match cls {
|
||||
ast::Clause::ExternFn(e) => Ok(typed::Clause::ExternFn(e.clone())),
|
||||
ast::Clause::Atom(a) => Ok(typed::Clause::Atom(a.clone())),
|
||||
ast::Clause::Auto(no, t, b) => Ok(typed::Clause::Auto(
|
||||
if t.len() == 0 {None} else {
|
||||
let typed::Expr(c, t) = exprv_rec(t.as_ref(), names)?;
|
||||
fn clause_rec(
|
||||
cls: &ast::Clause,
|
||||
names: ProtoMap<&str, u64, NAMES_INLINE_COUNT>,
|
||||
next_id: &mut u64,
|
||||
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 = *next_id;
|
||||
*next_id += 1;
|
||||
// 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 {None} else {
|
||||
let (typed::Expr(c, t), _) = exprv_rec(
|
||||
t.as_ref(), names, next_id, None
|
||||
)?;
|
||||
if t.len() > 0 {return Err(Error::ExplicitBottomKind)}
|
||||
else {Some(Mrc::new(c))}
|
||||
},
|
||||
Mrc::new(exprv_rec(b.as_ref(), names.push(no.as_ref().map(|n| &**n)))?)
|
||||
)),
|
||||
ast::Clause::Lambda(n, t, b) => Ok(typed::Clause::Lambda(
|
||||
if t.len() == 0 {None} else {
|
||||
let typed::Expr(c, t) = exprv_rec(t.as_ref(), names)?;
|
||||
};
|
||||
// Traverse body with extended context
|
||||
if let Some(name) = no {names.set(&&**name, id)}
|
||||
let (body, used_expls) = exprv_rec(
|
||||
b.as_ref(), names, next_id, 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 = *next_id;
|
||||
*next_id += 1;
|
||||
// Convert the type
|
||||
let typ = if t.len() == 0 {None} else {
|
||||
let (typed::Expr(c, t), _) = exprv_rec(
|
||||
t.as_ref(), names, next_id, None
|
||||
)?;
|
||||
if t.len() > 0 {return Err(Error::ExplicitBottomKind)}
|
||||
else {Some(Mrc::new(c))}
|
||||
},
|
||||
Mrc::new(exprv_rec(b.as_ref(), names.push(Some(&**n)))?)
|
||||
)),
|
||||
ast::Clause::Literal(l) => Ok(typed::Clause::Literal(l.clone())),
|
||||
ast::Clause::Name { local: Some(arg), .. } => Ok(typed::Clause::Argument(
|
||||
names.iter().position(|no| no == &Some(&**arg))
|
||||
.ok_or_else(|| Error::Unbound(arg.clone()))?
|
||||
)),
|
||||
};
|
||||
names.set(&&**n, id);
|
||||
let (body, used_expls) = exprv_rec(
|
||||
b.as_ref(), names, next_id, 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 = names.get(&&**arg)
|
||||
.ok_or_else(|| Error::Unbound(arg.clone()))?;
|
||||
Ok((typed::Clause::Argument(*uid), 0))
|
||||
}
|
||||
ast::Clause::S(paren, entries) => {
|
||||
if *paren != '(' {return Err(Error::BadGroup(*paren))}
|
||||
let typed::Expr(val, typ) = exprv_rec(entries.as_ref(), names)?;
|
||||
if typ.len() == 0 {Ok(val)}
|
||||
let (typed::Expr(val, typ), used_expls) = exprv_rec(
|
||||
entries.as_ref(), names, next_id, 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),
|
||||
|
||||
@@ -16,16 +16,16 @@ struct Wrap(bool, bool);
|
||||
#[derive(PartialEq, Eq, Hash)]
|
||||
pub struct Expr(pub Clause, pub Mrc<[Clause]>);
|
||||
impl Expr {
|
||||
fn deep_fmt(&self, f: &mut std::fmt::Formatter<'_>, depth: usize, tr: Wrap) -> std::fmt::Result {
|
||||
fn deep_fmt(&self, f: &mut std::fmt::Formatter<'_>, tr: Wrap) -> std::fmt::Result {
|
||||
let Expr(val, typ) = self;
|
||||
if typ.len() > 0 {
|
||||
val.deep_fmt(f, depth, Wrap(true, true))?;
|
||||
val.deep_fmt(f, Wrap(true, true))?;
|
||||
for typ in typ.as_ref() {
|
||||
f.write_char(':')?;
|
||||
typ.deep_fmt(f, depth, Wrap(true, true))?;
|
||||
typ.deep_fmt(f, Wrap(true, true))?;
|
||||
}
|
||||
} else {
|
||||
val.deep_fmt(f, depth, tr)?;
|
||||
val.deep_fmt(f, tr)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -39,7 +39,7 @@ impl Clone for Expr {
|
||||
|
||||
impl Debug for Expr {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
self.deep_fmt(f, 0, Wrap(false, false))
|
||||
self.deep_fmt(f, Wrap(false, false))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,11 +47,9 @@ impl Debug for Expr {
|
||||
pub enum Clause {
|
||||
Literal(Literal),
|
||||
Apply(Mrc<Expr>, Mrc<Expr>),
|
||||
/// Explicit specification of an Auto value
|
||||
Explicit(Mrc<Expr>, Mrc<Expr>),
|
||||
Lambda(Option<Mrc<Clause>>, Mrc<Expr>),
|
||||
Auto(Option<Mrc<Clause>>, Mrc<Expr>),
|
||||
Argument(usize),
|
||||
Lambda(u64, Option<Mrc<Clause>>, Mrc<Expr>),
|
||||
Auto(u64, Option<Mrc<Clause>>, Mrc<Expr>),
|
||||
Argument(u64),
|
||||
ExternFn(ExternFn),
|
||||
Atom(Atom)
|
||||
}
|
||||
@@ -60,48 +58,40 @@ const ARGNAME_CHARSET: &str = "abcdefghijklmnopqrstuvwxyz";
|
||||
|
||||
fn parametric_fmt(
|
||||
f: &mut std::fmt::Formatter<'_>,
|
||||
prefix: &str, argtyp: Option<Mrc<Clause>>, body: Mrc<Expr>, depth: usize, wrap_right: bool
|
||||
prefix: &str, argtyp: Option<Mrc<Clause>>, body: Mrc<Expr>, uid: u64, wrap_right: bool
|
||||
) -> std::fmt::Result {
|
||||
if wrap_right { f.write_char('(')?; }
|
||||
f.write_str(prefix)?;
|
||||
f.write_str(&string_from_charset(depth, ARGNAME_CHARSET))?;
|
||||
f.write_str(&string_from_charset(uid, ARGNAME_CHARSET))?;
|
||||
if let Some(typ) = argtyp {
|
||||
f.write_str(":")?;
|
||||
typ.deep_fmt(f, depth, Wrap(false, false))?;
|
||||
typ.deep_fmt(f, Wrap(false, false))?;
|
||||
}
|
||||
f.write_str(".")?;
|
||||
body.deep_fmt(f, depth + 1, Wrap(false, false))?;
|
||||
body.deep_fmt(f, 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)
|
||||
fn deep_fmt(&self, f: &mut std::fmt::Formatter<'_>, 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(argtyp, body) => parametric_fmt(f,
|
||||
"\\", argtyp.as_ref().map(Mrc::clone), Mrc::clone(body), depth, wr
|
||||
Self::Lambda(uid, argtyp, body) => parametric_fmt(f,
|
||||
"\\", argtyp.as_ref().map(Mrc::clone), Mrc::clone(body), *uid, wr
|
||||
),
|
||||
Self::Auto(argtyp, body) => parametric_fmt(f,
|
||||
"@", argtyp.as_ref().map(Mrc::clone), Mrc::clone(body), depth, wr
|
||||
Self::Auto(uid, argtyp, body) => parametric_fmt(f,
|
||||
"@", argtyp.as_ref().map(Mrc::clone), Mrc::clone(body), *uid, wr
|
||||
),
|
||||
Self::Argument(up) => f.write_str(&string_from_charset(depth - up - 1, ARGNAME_CHARSET)),
|
||||
Self::Explicit(expr, param) => {
|
||||
if wl { f.write_char('(')?; }
|
||||
expr.deep_fmt(f, depth, Wrap(false, true))?;
|
||||
f.write_str(" @")?;
|
||||
param.deep_fmt(f, depth, Wrap(true, wr && !wl))?;
|
||||
if wl { f.write_char(')')?; }
|
||||
Ok(())
|
||||
}
|
||||
Self::Argument(uid) => f.write_str(&string_from_charset(*uid, ARGNAME_CHARSET)),
|
||||
Self::Apply(func, x) => {
|
||||
if wl { f.write_char('(')?; }
|
||||
func.deep_fmt(f, depth, Wrap(false, true) )?;
|
||||
func.deep_fmt(f, Wrap(false, true) )?;
|
||||
f.write_char(' ')?;
|
||||
x.deep_fmt(f, depth, Wrap(true, wr && !wl) )?;
|
||||
x.deep_fmt(f, Wrap(true, wr && !wl) )?;
|
||||
if wl { f.write_char(')')?; }
|
||||
Ok(())
|
||||
}
|
||||
@@ -114,13 +104,12 @@ impl Clause {
|
||||
impl Clone for Clause {
|
||||
fn clone(&self) -> Self {
|
||||
match self {
|
||||
Clause::Auto(t, b) => Clause::Auto(t.as_ref().map(Mrc::clone), Mrc::clone(b)),
|
||||
Clause::Lambda(t, b) => Clause::Lambda(t.as_ref().map(Mrc::clone), Mrc::clone(b)),
|
||||
Clause::Auto(uid,t, b) => Clause::Auto(*uid, t.as_ref().map(Mrc::clone), Mrc::clone(b)),
|
||||
Clause::Lambda(uid, t, b) => Clause::Lambda(*uid, t.as_ref().map(Mrc::clone), Mrc::clone(b)),
|
||||
Clause::Literal(l) => Clause::Literal(l.clone()),
|
||||
Clause::ExternFn(nc) => Clause::ExternFn(nc.clone()),
|
||||
Clause::Atom(a) => Clause::Atom(a.clone()),
|
||||
Clause::Apply(f, x) => Clause::Apply(Mrc::clone(f), Mrc::clone(x)),
|
||||
Clause::Explicit(f, x) => Clause::Explicit(Mrc::clone(f), Mrc::clone(x)),
|
||||
Clause::Argument(lvl) => Clause::Argument(*lvl)
|
||||
}
|
||||
}
|
||||
@@ -128,7 +117,7 @@ impl Clone for Clause {
|
||||
|
||||
impl Debug for Clause {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
self.deep_fmt(f, 0, Wrap(false, false))
|
||||
self.deep_fmt(f, Wrap(false, false))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -118,7 +118,9 @@ impl<'a, K, V, const STACK_COUNT: usize> ProtoMap<'a, K, V, STACK_COUNT> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, K, V> From<T> for ProtoMap<'_, K, V> where T: IntoIterator<Item = (K, V)> {
|
||||
impl<T, K, V, const STACK_COUNT: usize>
|
||||
From<T> for ProtoMap<'_, K, V, STACK_COUNT>
|
||||
where T: IntoIterator<Item = (K, V)> {
|
||||
fn from(value: T) -> Self {
|
||||
Self {
|
||||
entries: value.into_iter().map(|(k, v)| (k, Some(v))).collect(),
|
||||
@@ -127,14 +129,17 @@ impl<T, K, V> From<T> for ProtoMap<'_, K, V> where T: IntoIterator<Item = (K, V)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Q: ?Sized, K, V> Index<&Q> for ProtoMap<'_, K, V> where K: Borrow<Q>, Q: Eq {
|
||||
impl<Q: ?Sized, K, V, const STACK_COUNT: usize>
|
||||
Index<&Q> for ProtoMap<'_, K, V, STACK_COUNT>
|
||||
where K: Borrow<Q>, Q: Eq {
|
||||
type Output = V;
|
||||
fn index(&self, index: &Q) -> &Self::Output {
|
||||
self.get(index).expect("Index not found in map")
|
||||
}
|
||||
}
|
||||
|
||||
impl<K: Clone, V: Clone> Clone for ProtoMap<'_, K, V> {
|
||||
impl<K: Clone, V: Clone, const STACK_COUNT: usize>
|
||||
Clone for ProtoMap<'_, K, V, STACK_COUNT> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
entries: self.entries.clone(),
|
||||
@@ -143,8 +148,9 @@ impl<K: Clone, V: Clone> Clone for ProtoMap<'_, K, V> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, K: 'a, V: 'a> Add<(K, V)> for &'a ProtoMap<'a, K, V> {
|
||||
type Output = ProtoMap<'a, K, V>;
|
||||
impl<'a, K: 'a, V: 'a, const STACK_COUNT: usize>
|
||||
Add<(K, V)> for &'a ProtoMap<'a, K, V, STACK_COUNT> {
|
||||
type Output = ProtoMap<'a, K, V, STACK_COUNT>;
|
||||
fn add(self, rhs: (K, V)) -> Self::Output {
|
||||
ProtoMap::from([rhs]).set_proto(self)
|
||||
}
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
fn string_from_charset_rec(val: usize, digits: &str) -> String {
|
||||
let radix = digits.len();
|
||||
fn string_from_charset_rec(val: u64, digits: &str) -> String {
|
||||
let radix = digits.len() as u64;
|
||||
let mut prefix = if val > radix {
|
||||
string_from_charset_rec(val / radix, digits)
|
||||
} else {String::new()};
|
||||
prefix.push(digits.chars().nth(val - 1).unwrap_or_else(|| {
|
||||
panic!("Overindexed digit set \"{}\" with {}", digits, val - 1)
|
||||
}));
|
||||
prefix.push(digits.chars().nth(val as usize - 1).unwrap_or_else(
|
||||
|| panic!("Overindexed digit set \"{}\" with {}", digits, val - 1)
|
||||
));
|
||||
prefix
|
||||
}
|
||||
|
||||
pub fn string_from_charset(val: usize, digits: &str) -> String {
|
||||
pub fn string_from_charset(val: u64, digits: &str) -> String {
|
||||
string_from_charset_rec(val + 1, digits)
|
||||
}
|
||||
@@ -33,17 +33,21 @@ impl<'a, T: 'a> Stackframe<'a, T> {
|
||||
len: self.len + 1
|
||||
}
|
||||
}
|
||||
pub fn opush(prev: &Option<Self>, item: T) -> Option<Self> {
|
||||
Some(Self {
|
||||
pub fn opush(prev: Option<&Self>, item: T) -> Self {
|
||||
Self {
|
||||
item,
|
||||
prev: prev.as_ref(),
|
||||
prev,
|
||||
len: prev.map_or(1, |s| s.len)
|
||||
})
|
||||
}
|
||||
}
|
||||
pub fn len(&self) -> usize { self.len }
|
||||
pub fn pop(&self, count: usize) -> Option<&Self> {
|
||||
if count == 0 {Some(self)}
|
||||
else {self.prev.and_then(|prev| prev.pop(count - 1))}
|
||||
else {self.prev.expect("Index out of range").pop(count - 1)}
|
||||
}
|
||||
pub fn opop(cur: Option<&Self>, count: usize) -> Option<&Self> {
|
||||
if count == 0 {cur}
|
||||
else {Self::opop(cur.expect("Index out of range").prev, count - 1)}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user