From a500f8b87a389a73cee650728d69ef82a0f6cb0c Mon Sep 17 00:00:00 2001 From: Lawrence Bethlenfalvy Date: Thu, 8 Dec 2022 17:52:57 +0000 Subject: [PATCH] Sync commit --- .vscode/orchid.code-workspace | 16 +++ src/executor/apply_lambda.rs | 94 +++++++++------- src/executor/partial_hash.rs | 40 +++++-- src/executor/reduction_tree.rs | 73 ++++++------ src/executor/syntax_eq.rs | 109 +++++++----------- src/main.rs | 16 +-- src/representations/ast_to_typed.rs | 166 ++++++++++++++++++---------- src/representations/typed.rs | 57 ++++------ src/utils/protomap.rs | 16 ++- src/utils/string_from_charset.rs | 12 +- src/utils/substack.rs | 14 ++- 11 files changed, 338 insertions(+), 275 deletions(-) create mode 100644 .vscode/orchid.code-workspace diff --git a/.vscode/orchid.code-workspace b/.vscode/orchid.code-workspace new file mode 100644 index 0000000..4591f6b --- /dev/null +++ b/.vscode/orchid.code-workspace @@ -0,0 +1,16 @@ +{ + "folders": [ + { + "path": ".." + } + ], + "settings": {}, + "extensions": { + "recommendations": [ + "tomoki1207.pdf", + "James-Yu.latex-workshop", + "rust-lang.rust-analyzer", + "bungcip.better-toml" + ] + } +} \ No newline at end of file diff --git a/src/executor/apply_lambda.rs b/src/executor/apply_lambda.rs index 7f9e5c9..fedc0bc 100644 --- a/src/executor/apply_lambda.rs +++ b/src/executor/apply_lambda.rs @@ -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, arg: Mrc) -> Mrc { - apply_lambda_expr_rec(Mrc::clone(&body), arg, 0) +pub fn apply_lambda(id: u64, value: Mrc, body: Mrc) -> Mrc { + apply_lambda_expr_rec(id, value, Mrc::clone(&body)) .unwrap_or(body) } fn apply_lambda_expr_rec( - item: Mrc, arg: Mrc, depth: usize + id: u64, value: Mrc, expr: Mrc ) -> Option> { - 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, depth: usize + id: u64, value: Mrc, clause: Clause ) -> Option { 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, depth: usize, f: Mrc, x: Mrc, - wrap: impl Fn(Mrc, Mrc) -> Clause -) -> Option { - 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, depth: usize, t: Option>, b: Mrc, - wrap: impl Fn(Option>, Mrc) -> Clause + id: u64, value: Mrc, + own_id: u64, t: Option>, b: Mrc, + wrap: impl Fn(u64, Option>, Mrc) -> Clause ) -> Option { - 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)) } } \ No newline at end of file diff --git a/src/executor/partial_hash.rs b/src/executor/partial_hash.rs index 03170c6..a659852 100644 --- a/src/executor/partial_hash.rs +++ b/src/executor/partial_hash.rs @@ -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(Expr(clause, _): &Expr, state: &mut H, is_auto: Stackframe) { +pub fn partial_hash_rec( + 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 diff --git a/src/executor/reduction_tree.rs b/src/executor/reduction_tree.rs index 160ed63..f5adb1d 100644 --- a/src/executor/reduction_tree.rs +++ b/src/executor/reduction_tree.rs @@ -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, usize) -> I, + F: 'a + FnOnce(Mrc) -> I, I: Iterator> + 'static >( - depth: usize, expr: Mrc, function: F + expr: Mrc, function: F ) -> BoxedIter<'static, Mrc> { - 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(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> - } - _ => () + 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> } - 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) -> impl Iterator> { - 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) -> impl Iterator> { 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) -> impl Iterator> { ), 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"), } }) } diff --git a/src/executor/syntax_eq.rs b/src/executor/syntax_eq.rs index 9c8f77e..39be4a3 100644 --- a/src/executor/syntax_eq.rs +++ b/src/executor/syntax_eq.rs @@ -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)) -> (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>); +impl Context { + fn set(&mut self, id: u64, value: Mrc) { + // 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>, - /// Stores whether a given relative upreference is auto or lambda - is_auto: Option>, - /// 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>> + /// 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) -> 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>, - /// 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>; + /// 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)>, 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 diff --git a/src/main.rs b/src/main.rs index 1b32fa2..f2d8a22 100644 --- a/src/main.rs +++ b/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(); diff --git a/src/representations/ast_to_typed.rs b/src/representations/ast_to_typed.rs index 6801cd8..fe7ae4a 100644 --- a/src/representations/ast_to_typed.rs +++ b/src/representations/ast_to_typed.rs @@ -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 { - 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 { - 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 { - 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>) --> Result { - 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>>, +) -> 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>) -> Result { - 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>) --> Result { +fn expr_rec( + ast::Expr(val, typ): &ast::Expr, + names: ProtoMap<&str, u64, NAMES_INLINE_COUNT>, + next_id: &mut u64, + explicits: Option<&Stackframe>> // known explicit values +) -> Result<(typed::Expr, usize), Error> { // (output, used_explicits) let typ: Vec = typ.iter() - .map(|c| clause_rec(c, names)) + .map(|c| Ok(clause_rec(c, names, next_id, None)?.0)) .collect::>()?; 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>) --> Result { - 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>> +) -> 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), diff --git a/src/representations/typed.rs b/src/representations/typed.rs index 2eb7c9b..4e43cbc 100644 --- a/src/representations/typed.rs +++ b/src/representations/typed.rs @@ -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, Mrc), - /// Explicit specification of an Auto value - Explicit(Mrc, Mrc), - Lambda(Option>, Mrc), - Auto(Option>, Mrc), - Argument(usize), + Lambda(u64, Option>, Mrc), + Auto(u64, Option>, Mrc), + 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>, body: Mrc, depth: usize, wrap_right: bool + prefix: &str, argtyp: Option>, body: Mrc, 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)) } } diff --git a/src/utils/protomap.rs b/src/utils/protomap.rs index 8811a1f..3d56246 100644 --- a/src/utils/protomap.rs +++ b/src/utils/protomap.rs @@ -118,7 +118,9 @@ impl<'a, K, V, const STACK_COUNT: usize> ProtoMap<'a, K, V, STACK_COUNT> { } } -impl From for ProtoMap<'_, K, V> where T: IntoIterator { +impl +From for ProtoMap<'_, K, V, STACK_COUNT> +where T: IntoIterator { fn from(value: T) -> Self { Self { entries: value.into_iter().map(|(k, v)| (k, Some(v))).collect(), @@ -127,14 +129,17 @@ impl From for ProtoMap<'_, K, V> where T: IntoIterator Index<&Q> for ProtoMap<'_, K, V> where K: Borrow, Q: Eq { +impl +Index<&Q> for ProtoMap<'_, K, V, STACK_COUNT> +where K: Borrow, Q: Eq { type Output = V; fn index(&self, index: &Q) -> &Self::Output { self.get(index).expect("Index not found in map") } } -impl Clone for ProtoMap<'_, K, V> { +impl +Clone for ProtoMap<'_, K, V, STACK_COUNT> { fn clone(&self) -> Self { Self { entries: self.entries.clone(), @@ -143,8 +148,9 @@ impl 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) } diff --git a/src/utils/string_from_charset.rs b/src/utils/string_from_charset.rs index 7d925b8..7cf0943 100644 --- a/src/utils/string_from_charset.rs +++ b/src/utils/string_from_charset.rs @@ -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) } \ No newline at end of file diff --git a/src/utils/substack.rs b/src/utils/substack.rs index 83c9457..8cbf8e6 100644 --- a/src/utils/substack.rs +++ b/src/utils/substack.rs @@ -33,17 +33,21 @@ impl<'a, T: 'a> Stackframe<'a, T> { len: self.len + 1 } } - pub fn opush(prev: &Option, item: T) -> Option { - 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)} } }