chugging along with a small refactor and some more work toward currying

This commit is contained in:
microproofs 2024-01-30 09:04:59 -05:00 committed by Kasey
parent 2f72510102
commit 9a52258e14
1 changed files with 368 additions and 122 deletions

View File

@ -1,4 +1,4 @@
use std::{rc::Rc, vec}; use std::{cmp::Ordering, iter::Peekable, rc::Rc, vec};
use indexmap::IndexMap; use indexmap::IndexMap;
use itertools::Itertools; use itertools::Itertools;
@ -49,6 +49,10 @@ impl Scope {
.collect_vec(), .collect_vec(),
} }
} }
pub fn len(&self) -> usize {
self.scope.len()
}
} }
impl Default for Scope { impl Default for Scope {
@ -79,34 +83,33 @@ impl Default for IdGen {
#[derive(PartialEq, Clone, Debug)] #[derive(PartialEq, Clone, Debug)]
pub enum BuiltinArgs { pub enum BuiltinArgs {
TwoArgs(Term<Name>, Term<Name>), TwoArgs((usize, Term<Name>), (usize, Term<Name>)),
ThreeArgs(Term<Name>, Term<Name>, Term<Name>), ThreeArgs(
TwoArgsAnyOrder(Term<Name>, Term<Name>), (usize, Term<Name>),
(usize, Term<Name>),
(usize, Term<Name>),
),
TwoArgsAnyOrder((usize, Term<Name>), (usize, Term<Name>)),
} }
impl BuiltinArgs { impl BuiltinArgs {
fn args_from_arg_stack(stack: Vec<(usize, Term<Name>)>, is_order_agnostic: bool) -> Self { fn args_from_arg_stack(stack: Vec<(usize, Term<Name>)>, is_order_agnostic: bool) -> Self {
let mut ordered_arg_stack = let mut ordered_arg_stack = stack.into_iter().rev().sorted_by(|(_, arg1), (_, arg2)| {
stack // sort by constant first if the builtin is order agnostic
.into_iter() if is_order_agnostic {
.rev() if matches!(arg1, Term::Constant(_)) && matches!(arg2, Term::Constant(_)) {
.map(|(_, arg)| arg) std::cmp::Ordering::Equal
.sorted_by(|arg1, arg2| { } else if matches!(arg1, Term::Constant(_)) {
// sort by constant first if the builtin is order agnostic std::cmp::Ordering::Less
if is_order_agnostic { } else if matches!(arg2, Term::Constant(_)) {
if matches!(arg1, Term::Constant(_)) && matches!(arg2, Term::Constant(_)) { std::cmp::Ordering::Greater
std::cmp::Ordering::Equal } else {
} else if matches!(arg1, Term::Constant(_)) { std::cmp::Ordering::Equal
std::cmp::Ordering::Less }
} else if matches!(arg2, Term::Constant(_)) { } else {
std::cmp::Ordering::Greater std::cmp::Ordering::Equal
} else { }
std::cmp::Ordering::Equal });
}
} else {
std::cmp::Ordering::Equal
}
});
if ordered_arg_stack.len() == 2 && is_order_agnostic { if ordered_arg_stack.len() == 2 && is_order_agnostic {
// This is the special case where the order of args is irrelevant to the builtin // This is the special case where the order of args is irrelevant to the builtin
@ -134,10 +137,12 @@ impl BuiltinArgs {
match self { match self {
BuiltinArgs::TwoArgs(arg1, arg2) | BuiltinArgs::TwoArgsAnyOrder(arg1, arg2) => { BuiltinArgs::TwoArgs(arg1, arg2) | BuiltinArgs::TwoArgsAnyOrder(arg1, arg2) => {
CurriedTree::Branch { CurriedTree::Branch {
node: arg1, id: arg1.0,
node: arg1.1,
multiple_occurrences: false, multiple_occurrences: false,
children: vec![CurriedTree::Leaf { children: vec![CurriedTree::Leaf {
node: arg2, id: arg2.0,
node: arg2.1,
multiple_occurrences: false, multiple_occurrences: false,
scope: scope.clone(), scope: scope.clone(),
}], }],
@ -146,13 +151,16 @@ impl BuiltinArgs {
} }
BuiltinArgs::ThreeArgs(arg1, arg2, arg3) => CurriedTree::Branch { BuiltinArgs::ThreeArgs(arg1, arg2, arg3) => CurriedTree::Branch {
node: arg1, id: arg1.0,
node: arg1.1,
multiple_occurrences: false, multiple_occurrences: false,
children: vec![CurriedTree::Branch { children: vec![CurriedTree::Branch {
node: arg2, id: arg1.0,
node: arg2.1,
multiple_occurrences: false, multiple_occurrences: false,
children: vec![CurriedTree::Leaf { children: vec![CurriedTree::Leaf {
node: arg3, id: arg3.0,
node: arg3.1,
multiple_occurrences: false, multiple_occurrences: false,
scope: scope.clone(), scope: scope.clone(),
}], }],
@ -162,6 +170,16 @@ impl BuiltinArgs {
}, },
} }
} }
fn to_id_vec(&self) -> Vec<usize> {
match self {
BuiltinArgs::TwoArgs(arg1, arg2) | BuiltinArgs::TwoArgsAnyOrder(arg1, arg2) => {
vec![arg1.0, arg2.0]
}
BuiltinArgs::ThreeArgs(arg1, arg2, arg3) => vec![arg1.0, arg2.0, arg3.0],
}
}
} }
#[derive(PartialEq, Clone, Debug)] #[derive(PartialEq, Clone, Debug)]
@ -171,19 +189,32 @@ pub enum CurriedTree {
multiple_occurrences: bool, multiple_occurrences: bool,
children: Vec<CurriedTree>, children: Vec<CurriedTree>,
scope: Scope, scope: Scope,
id: usize,
}, },
Leaf { Leaf {
node: Term<Name>, node: Term<Name>,
multiple_occurrences: bool, multiple_occurrences: bool,
scope: Scope, scope: Scope,
id: usize,
}, },
} }
impl CurriedTree { impl CurriedTree {
pub fn node(&self) -> &Term<Name> { pub fn node(&self) -> &Term<Name> {
match self { match self {
CurriedTree::Branch { node, .. } => node, CurriedTree::Branch { node, .. } | CurriedTree::Leaf { node, .. } => node,
CurriedTree::Leaf { node, .. } => node, }
}
pub fn id(&self) -> usize {
match self {
CurriedTree::Branch { id, .. } | CurriedTree::Leaf { id, .. } => *id,
}
}
pub fn scope(&self) -> &Scope {
match self {
CurriedTree::Branch { scope, .. } | CurriedTree::Leaf { scope, .. } => scope,
} }
} }
@ -207,6 +238,48 @@ impl CurriedTree {
} }
} }
pub fn find_leaf_id_path(&self, path: &BuiltinArgs) -> Vec<usize> {
match (self, path) {
(
CurriedTree::Branch { children, .. },
BuiltinArgs::TwoArgs(_, (_, arg2)) | BuiltinArgs::TwoArgsAnyOrder(_, (_, arg2)),
) => {
if let Some(CurriedTree::Leaf { id: leaf_id, .. }) =
children.iter().find(|child| child.node() == arg2)
{
vec![*leaf_id]
} else {
vec![]
}
}
(
CurriedTree::Branch { children, .. },
BuiltinArgs::ThreeArgs(_, (_, arg2), (_, arg3)),
) => {
if let Some(CurriedTree::Branch {
children: child_children,
id: mid_id,
..
}) = children.iter().find(|child| child.node() == arg2)
{
if let Some(CurriedTree::Leaf { id: leaf_id, .. }) =
child_children.iter().find(|child| child.node() == arg3)
{
vec![*mid_id, *leaf_id]
} else {
vec![*mid_id]
}
} else {
vec![]
}
}
// Since all args are always added to the tree. The minimum depth of a tree is 2 and max is 3
// Therefore we can't have a leaf at the root level of the match
_ => unreachable!(),
}
}
pub fn merge_node_by_path(self, path: BuiltinArgs, scope: &Scope) -> CurriedTree { pub fn merge_node_by_path(self, path: BuiltinArgs, scope: &Scope) -> CurriedTree {
match (self, path) { match (self, path) {
( (
@ -214,9 +287,11 @@ impl CurriedTree {
node, node,
mut children, mut children,
scope: branch_scope, scope: branch_scope,
id,
.. ..
}, },
BuiltinArgs::TwoArgs(_, arg2) | BuiltinArgs::TwoArgsAnyOrder(_, arg2), BuiltinArgs::TwoArgs(_, (new_leaf_id, arg2))
| BuiltinArgs::TwoArgsAnyOrder(_, (new_leaf_id, arg2)),
) => { ) => {
if let Some(CurriedTree::Leaf { if let Some(CurriedTree::Leaf {
multiple_occurrences, multiple_occurrences,
@ -229,6 +304,7 @@ impl CurriedTree {
*multiple_occurrences = true; *multiple_occurrences = true;
*leaf_scope = leaf_scope.common_ancestor(scope); *leaf_scope = leaf_scope.common_ancestor(scope);
CurriedTree::Branch { CurriedTree::Branch {
id,
node, node,
multiple_occurrences: true, multiple_occurrences: true,
children, children,
@ -236,11 +312,13 @@ impl CurriedTree {
} }
} else { } else {
children.push(CurriedTree::Leaf { children.push(CurriedTree::Leaf {
id: new_leaf_id,
node: arg2, node: arg2,
multiple_occurrences: false, multiple_occurrences: false,
scope: scope.clone(), scope: scope.clone(),
}); });
CurriedTree::Branch { CurriedTree::Branch {
id,
node, node,
multiple_occurrences: true, multiple_occurrences: true,
children, children,
@ -253,9 +331,10 @@ impl CurriedTree {
node, node,
mut children, mut children,
scope: branch_scope, scope: branch_scope,
id: top_id,
.. ..
}, },
BuiltinArgs::ThreeArgs(_, arg2, arg3), BuiltinArgs::ThreeArgs(_, (new_branch_id, arg2), (new_leaf_id, arg3)),
) => { ) => {
if let Some(CurriedTree::Branch { if let Some(CurriedTree::Branch {
multiple_occurrences: child_multiple_occurrences, multiple_occurrences: child_multiple_occurrences,
@ -279,6 +358,7 @@ impl CurriedTree {
*child_multiple_occurrences = true; *child_multiple_occurrences = true;
*child_scope = child_scope.common_ancestor(scope); *child_scope = child_scope.common_ancestor(scope);
CurriedTree::Branch { CurriedTree::Branch {
id: top_id,
node, node,
multiple_occurrences: true, multiple_occurrences: true,
children, children,
@ -286,6 +366,7 @@ impl CurriedTree {
} }
} else { } else {
child_children.push(CurriedTree::Leaf { child_children.push(CurriedTree::Leaf {
id: new_leaf_id,
node: arg3, node: arg3,
multiple_occurrences: false, multiple_occurrences: false,
scope: scope.clone(), scope: scope.clone(),
@ -293,6 +374,7 @@ impl CurriedTree {
*child_multiple_occurrences = true; *child_multiple_occurrences = true;
*child_scope = child_scope.common_ancestor(scope); *child_scope = child_scope.common_ancestor(scope);
CurriedTree::Branch { CurriedTree::Branch {
id: top_id,
node, node,
multiple_occurrences: true, multiple_occurrences: true,
children, children,
@ -300,8 +382,12 @@ impl CurriedTree {
} }
} }
} else { } else {
children.push(BuiltinArgs::TwoArgs(arg2, arg3).args_to_curried_tree(scope)); children.push(
BuiltinArgs::TwoArgs((new_branch_id, arg2), (new_leaf_id, arg3))
.args_to_curried_tree(scope),
);
CurriedTree::Branch { CurriedTree::Branch {
id: top_id,
node, node,
multiple_occurrences: true, multiple_occurrences: true,
children, children,
@ -335,6 +421,49 @@ impl CurriedTree {
} }
self self
} }
fn to_scope_map(
&self,
mut acc: Vec<(Scope, Vec<Term<Name>>)>,
current_term: &Term<Name>,
) -> Vec<(Scope, Vec<Term<Name>>)> {
if let CurriedTree::Branch { node, children, .. } = self {
acc = children.iter().fold(acc, |acc, child| {
child.to_scope_map(acc, &current_term.clone().apply(node.clone()))
});
}
let insert_index = acc.iter().enumerate().find_map(|(index, (item_scope, _))| {
if item_scope.len() > self.scope().len() {
Some((Ordering::Less, index))
} else if item_scope == self.scope() {
// If scopes are exactly equal we keep them in the same scope grouping
Some((Ordering::Equal, index))
} else {
None
}
});
let term = current_term.clone().apply(self.node().clone());
if let Some(insert_index) = insert_index {
match insert_index.0 {
Ordering::Less => {
acc.insert(insert_index.1, (self.scope().clone(), vec![term]));
acc
}
Ordering::Equal => {
let item = acc.get_mut(insert_index.1).unwrap();
item.1.push(term);
acc
}
Ordering::Greater => unreachable!(),
}
} else {
acc.push((self.scope().clone(), vec![term]));
acc
}
}
} }
#[derive(PartialEq, Clone, Debug)] #[derive(PartialEq, Clone, Debug)]
@ -352,7 +481,7 @@ impl CurriedBuiltin {
match &path { match &path {
// First we just peak at the first arg to see if it was curried before // First we just peak at the first arg to see if it was curried before
BuiltinArgs::TwoArgs(arg1, _) | BuiltinArgs::ThreeArgs(arg1, _, _) => { BuiltinArgs::TwoArgs(arg1, _) | BuiltinArgs::ThreeArgs(arg1, _, _) => {
if let Some(child) = children.iter_mut().find(|child| child.node() == arg1) { if let Some(child) = children.iter_mut().find(|child| child.node() == &arg1.1) {
// mutate child here so we don't have to recreate the whole tree // mutate child here so we don't have to recreate the whole tree
// We pass in the scope so we can get the common ancestor if the arg was curried before // We pass in the scope so we can get the common ancestor if the arg was curried before
*child = child.clone().merge_node_by_path(path, scope); *child = child.clone().merge_node_by_path(path, scope);
@ -371,13 +500,15 @@ impl CurriedBuiltin {
} }
BuiltinArgs::TwoArgsAnyOrder(arg1, arg2) => { BuiltinArgs::TwoArgsAnyOrder(arg1, arg2) => {
// This is the special case where we search by both args before adding a new child // This is the special case where we search by both args before adding a new child
if let Some(child) = children.iter_mut().find(|child| child.node() == arg1) { if let Some(child) = children.iter_mut().find(|child| child.node() == &arg1.1) {
*child = child.clone().merge_node_by_path(path, scope); *child = child.clone().merge_node_by_path(path, scope);
CurriedBuiltin { CurriedBuiltin {
func: self.func, func: self.func,
children, children,
} }
} else if let Some(child) = children.iter_mut().find(|child| child.node() == arg2) { } else if let Some(child) =
children.iter_mut().find(|child| child.node() == &arg2.1)
{
// If we found a curried argument using arg2 then we flip the order of the args // If we found a curried argument using arg2 then we flip the order of the args
// before merging into the tree // before merging into the tree
*child = child.clone().merge_node_by_path( *child = child.clone().merge_node_by_path(
@ -410,74 +541,114 @@ impl CurriedBuiltin {
.collect_vec(); .collect_vec();
self self
} }
}
pub fn is_order_agnostic_builtin(func: DefaultFunction) -> bool { pub fn find_leaf_id_path(&self, path: &BuiltinArgs) -> Option<Vec<usize>> {
matches!( let children = &self.children;
func, let mut id_vec = vec![];
DefaultFunction::AddInteger
| DefaultFunction::MultiplyInteger
| DefaultFunction::EqualsInteger
| DefaultFunction::EqualsByteString
| DefaultFunction::EqualsString
| DefaultFunction::EqualsData
| DefaultFunction::Bls12_381_G1_Equal
| DefaultFunction::Bls12_381_G2_Equal
| DefaultFunction::Bls12_381_G1_Add
| DefaultFunction::Bls12_381_G2_Add
)
}
/// For now all of the curry builtins are not forceable
pub fn can_curry_builtin(func: DefaultFunction) -> bool {
matches!(
func,
DefaultFunction::AddInteger
| DefaultFunction::SubtractInteger
| DefaultFunction::MultiplyInteger
| DefaultFunction::EqualsInteger
| DefaultFunction::EqualsByteString
| DefaultFunction::EqualsString
| DefaultFunction::EqualsData
| DefaultFunction::Bls12_381_G1_Equal
| DefaultFunction::Bls12_381_G2_Equal
| DefaultFunction::LessThanInteger
| DefaultFunction::LessThanEqualsInteger
| DefaultFunction::AppendByteString
| DefaultFunction::ConsByteString
| DefaultFunction::SliceByteString
| DefaultFunction::IndexByteString
| DefaultFunction::LessThanEqualsByteString
| DefaultFunction::LessThanByteString
| DefaultFunction::Bls12_381_G1_Add
| DefaultFunction::Bls12_381_G2_Add
)
}
impl Program<Name> { match path {
fn traverse_uplc_with( // First we just peak at the first arg to see if it was curried before
self, BuiltinArgs::TwoArgs(arg1, _) | BuiltinArgs::ThreeArgs(arg1, _, _) => {
with: &mut impl FnMut(Option<usize>, &mut Term<Name>, Vec<(usize, Term<Name>)>, &Scope), if let Some(child) = children.iter().find(|child| child.node() == &arg1.1) {
) -> Self { // mutate child here so we don't have to recreate the whole tree
let mut term = self.term; // We pass in the scope so we can get the common ancestor if the arg was curried before
let scope = Scope { scope: vec![] }; id_vec.push(child.id());
let arg_stack = vec![];
let mut id_gen = IdGen::new();
Self::traverse_uplc_with_helper(&mut term, &scope, arg_stack, &mut id_gen, with); id_vec.extend(child.find_leaf_id_path(path));
Program { Some(id_vec)
version: self.version, } else {
term, None
}
}
BuiltinArgs::TwoArgsAnyOrder(arg1, arg2) => {
// This is the special case where we search by both args before adding a new child
if let Some(child) = children.iter().find(|child| child.node() == &arg1.1) {
id_vec.push(child.id());
id_vec.extend(child.find_leaf_id_path(path));
Some(id_vec)
} else if let Some(child) = children.iter().find(|child| child.node() == &arg2.1) {
// If we found a curried argument using arg2 then we flip the order of the args
// before merging into the tree
id_vec.push(child.id());
id_vec.extend(child.find_leaf_id_path(&BuiltinArgs::TwoArgsAnyOrder(
arg2.clone(),
arg1.clone(),
)));
Some(id_vec)
} else {
None
}
}
} }
} }
fn to_scope_map(&self) -> Vec<(Scope, Vec<Term<Name>>)> {
let scope_map = vec![];
let current_term = Term::Builtin(self.func);
self.children.iter().fold(scope_map, |acc, child| {
child.to_scope_map(acc, &current_term)
})
}
}
impl DefaultFunction {
pub fn is_order_agnostic_builtin(self) -> bool {
matches!(
self,
DefaultFunction::AddInteger
| DefaultFunction::MultiplyInteger
| DefaultFunction::EqualsInteger
| DefaultFunction::EqualsByteString
| DefaultFunction::EqualsString
| DefaultFunction::EqualsData
| DefaultFunction::Bls12_381_G1_Equal
| DefaultFunction::Bls12_381_G2_Equal
| DefaultFunction::Bls12_381_G1_Add
| DefaultFunction::Bls12_381_G2_Add
)
}
/// For now all of the curry builtins are not forceable
pub fn can_curry_builtin(self) -> bool {
matches!(
self,
DefaultFunction::AddInteger
| DefaultFunction::SubtractInteger
| DefaultFunction::MultiplyInteger
| DefaultFunction::EqualsInteger
| DefaultFunction::EqualsByteString
| DefaultFunction::EqualsString
| DefaultFunction::EqualsData
| DefaultFunction::Bls12_381_G1_Equal
| DefaultFunction::Bls12_381_G2_Equal
| DefaultFunction::LessThanInteger
| DefaultFunction::LessThanEqualsInteger
| DefaultFunction::AppendByteString
| DefaultFunction::ConsByteString
| DefaultFunction::SliceByteString
| DefaultFunction::IndexByteString
| DefaultFunction::LessThanEqualsByteString
| DefaultFunction::LessThanByteString
| DefaultFunction::Bls12_381_G1_Add
| DefaultFunction::Bls12_381_G2_Add
| DefaultFunction::ConstrData
)
}
}
impl Term<Name> {
fn traverse_uplc_with_helper( fn traverse_uplc_with_helper(
term: &mut Term<Name>, &mut self,
scope: &Scope, scope: &Scope,
mut arg_stack: Vec<(usize, Term<Name>)>, mut arg_stack: Vec<(usize, Term<Name>)>,
id_gen: &mut IdGen, id_gen: &mut IdGen,
with: &mut impl FnMut(Option<usize>, &mut Term<Name>, Vec<(usize, Term<Name>)>, &Scope), with: &mut impl FnMut(Option<usize>, &mut Term<Name>, Vec<(usize, Term<Name>)>, &Scope),
) { ) {
match term { match self {
Term::Apply { function, argument } => { Term::Apply { function, argument } => {
let arg = Rc::make_mut(argument); let arg = Rc::make_mut(argument);
let argument_arg_stack = vec![]; let argument_arg_stack = vec![];
@ -502,13 +673,13 @@ impl Program<Name> {
with, with,
); );
with(Some(apply_id), term, vec![], scope); with(Some(apply_id), self, vec![], scope);
} }
Term::Delay(d) => { Term::Delay(d) => {
let d = Rc::make_mut(d); let d = Rc::make_mut(d);
// First we recurse further to reduce the inner terms before coming back up to the Delay // First we recurse further to reduce the inner terms before coming back up to the Delay
Self::traverse_uplc_with_helper(d, scope, arg_stack, id_gen, with); Self::traverse_uplc_with_helper(d, scope, arg_stack, id_gen, with);
with(None, term, vec![], scope); with(None, self, vec![], scope);
} }
Term::Lambda { body, .. } => { Term::Lambda { body, .. } => {
let body = Rc::make_mut(body); let body = Rc::make_mut(body);
@ -517,13 +688,13 @@ impl Program<Name> {
// Pass in either one or zero args. // Pass in either one or zero args.
Self::traverse_uplc_with_helper(body, scope, arg_stack, id_gen, with); Self::traverse_uplc_with_helper(body, scope, arg_stack, id_gen, with);
with(None, term, args, scope); with(None, self, args, scope);
} }
Term::Force(f) => { Term::Force(f) => {
let f = Rc::make_mut(f); let f = Rc::make_mut(f);
Self::traverse_uplc_with_helper(f, scope, arg_stack, id_gen, with); Self::traverse_uplc_with_helper(f, scope, arg_stack, id_gen, with);
with(None, term, vec![], scope); with(None, self, vec![], scope);
} }
Term::Case { .. } => todo!(), Term::Case { .. } => todo!(),
Term::Constr { .. } => todo!(), Term::Constr { .. } => todo!(),
@ -537,7 +708,7 @@ impl Program<Name> {
} }
} }
// Pass in args up to function arity. // Pass in args up to function arity.
with(None, term, args, scope); with(None, self, args, scope);
} }
term => { term => {
with(None, term, vec![], scope); with(None, term, vec![], scope);
@ -545,6 +716,70 @@ impl Program<Name> {
} }
} }
fn traverse_to_scope<'a, 'b, I>(&'a mut self, scope: &mut Peekable<I>) -> &'a mut Self
where
I: Iterator<Item = &'b ScopePath>,
{
if scope.peek().is_none() {
self
} else {
match self {
Term::Apply { function, argument } => {
let scope_path = scope.next();
match scope_path.unwrap() {
ScopePath::FUNC => Self::traverse_to_scope(Rc::make_mut(function), scope),
ScopePath::ARG => Self::traverse_to_scope(Rc::make_mut(argument), scope),
}
}
Term::Delay(d) => {
let d = Rc::make_mut(d);
// First we recurse further to reduce the inner terms before coming back up to the Delay
Self::traverse_to_scope(d, scope)
}
Term::Lambda { body, .. } => {
let body = Rc::make_mut(body);
// First we recurse further to reduce the inner terms before coming back up to the Delay
Self::traverse_to_scope(body, scope)
}
Term::Force(f) => {
let f = Rc::make_mut(f);
// First we recurse further to reduce the inner terms before coming back up to the Delay
Self::traverse_to_scope(f, scope)
}
Term::Case { .. } => todo!(),
Term::Constr { .. } => todo!(),
_ => unreachable!("Incorrect scope path"),
}
}
}
}
impl Program<Name> {
fn traverse_uplc_with(
self,
with: &mut impl FnMut(Option<usize>, &mut Term<Name>, Vec<(usize, Term<Name>)>, &Scope),
) -> Self {
let mut term = self.term;
let scope = Scope { scope: vec![] };
let arg_stack = vec![];
let mut id_gen = IdGen::new();
term.traverse_uplc_with_helper(&scope, arg_stack, &mut id_gen, with);
Program {
version: self.version,
term,
}
}
fn find_node_by_scope(&mut self, scope: &Scope) -> &mut Term<Name> {
let term = &mut self.term;
let iter = &mut scope.scope.iter().peekable();
term.traverse_to_scope(iter)
}
pub fn lambda_reducer(self) -> Self { pub fn lambda_reducer(self) -> Self {
let mut lambda_applied_ids = vec![]; let mut lambda_applied_ids = vec![];
@ -585,7 +820,7 @@ impl Program<Name> {
}) })
} }
pub fn builtin_force_reducer(self) -> Program<Name> { pub fn builtin_force_reducer(self) -> Self {
let mut builtin_map = IndexMap::new(); let mut builtin_map = IndexMap::new();
let program = self.traverse_uplc_with(&mut |_id, term, _arg_stack, _scope| { let program = self.traverse_uplc_with(&mut |_id, term, _arg_stack, _scope| {
@ -638,7 +873,7 @@ impl Program<Name> {
} }
} }
pub fn inline_reducer(self) -> Program<Name> { pub fn inline_reducer(self) -> Self {
let mut lambda_applied_ids = vec![]; let mut lambda_applied_ids = vec![];
let mut identity_applied_ids = vec![]; let mut identity_applied_ids = vec![];
// TODO: Remove extra traversals // TODO: Remove extra traversals
@ -768,7 +1003,7 @@ impl Program<Name> {
}) })
} }
pub fn force_delay_reducer(self) -> Program<Name> { pub fn force_delay_reducer(self) -> Self {
self.traverse_uplc_with(&mut |_id, term, _arg_stack, _scope| { self.traverse_uplc_with(&mut |_id, term, _arg_stack, _scope| {
if let Term::Force(f) = term { if let Term::Force(f) = term {
let f = Rc::make_mut(f); let f = Rc::make_mut(f);
@ -780,7 +1015,7 @@ impl Program<Name> {
}) })
} }
pub fn cast_data_reducer(self) -> Program<Name> { pub fn cast_data_reducer(self) -> Self {
let mut applied_ids = vec![]; let mut applied_ids = vec![];
self.traverse_uplc_with(&mut |id, term, mut arg_stack, _scope| { self.traverse_uplc_with(&mut |id, term, mut arg_stack, _scope| {
@ -908,13 +1143,13 @@ impl Program<Name> {
} }
// WIP // WIP
pub fn builtin_curry_reducer(self) -> Program<Name> { pub fn builtin_curry_reducer(self) -> Self {
let mut curried_terms = vec![]; let mut curried_terms = vec![];
let mut curry_applied_ids: Vec<usize> = vec![]; let mut curry_applied_ids: Vec<usize> = vec![];
let a = self.traverse_uplc_with(&mut |_id, term, arg_stack, scope| match term { let a = self.traverse_uplc_with(&mut |_id, term, arg_stack, scope| match term {
Term::Builtin(func) => { Term::Builtin(func) => {
if can_curry_builtin(*func) && arg_stack.len() == func.arity() { if func.can_curry_builtin() && arg_stack.len() == func.arity() {
let mut scope = scope.clone(); let mut scope = scope.clone();
// Get upper scope of the function plus args // Get upper scope of the function plus args
@ -924,7 +1159,7 @@ impl Program<Name> {
scope = scope.pop(); scope = scope.pop();
} }
let is_order_agnostic = is_order_agnostic_builtin(*func); let is_order_agnostic = func.is_order_agnostic_builtin();
// In the case of order agnostic builtins we want to sort the args by constant first // In the case of order agnostic builtins we want to sort the args by constant first
// This gives us the opportunity to curry constants that often pop up in the code // This gives us the opportunity to curry constants that often pop up in the code
@ -968,39 +1203,50 @@ impl Program<Name> {
} }
// TODO: add function to generate names for curried_terms for generating vars to insert // TODO: add function to generate names for curried_terms for generating vars to insert
a.traverse_uplc_with(&mut |_id, term, arg_stack, scope| match term { let b = a.traverse_uplc_with(&mut |id, term, arg_stack, _scope| match term {
Term::Builtin(func) => { Term::Builtin(func) => {
if can_curry_builtin(*func) { if func.can_curry_builtin() {
let Some(curried_builtin) = let Some(curried_builtin) =
curried_terms.iter().find(|curry| curry.func == *func) curried_terms.iter().find(|curry| curry.func == *func)
else { else {
return; return;
}; };
let arg_stack_ids = arg_stack.iter().map(|(id, _)| *id).collect_vec();
let builtin_args = BuiltinArgs::args_from_arg_stack( let builtin_args = BuiltinArgs::args_from_arg_stack(
arg_stack, arg_stack,
is_order_agnostic_builtin(*func), func.is_order_agnostic_builtin(),
); );
if let Some(_) = curried_builtin.children.iter().find(|child| { let Some(id_vec) = curried_builtin.find_leaf_id_path(&builtin_args) else {
let x = (*child) return;
.clone() };
.merge_node_by_path(builtin_args.clone(), scope);
*child == &x let id_str = id_vec
}) { .iter()
curry_applied_ids.extend(arg_stack_ids); .map(|id| id.to_string())
} else { .collect::<Vec<String>>()
} .join("_");
let name = format!("{}_{}", func.aiken_name(), id_str);
curry_applied_ids.extend(builtin_args.to_id_vec().iter().take(id_vec.len()));
*term = Term::var(name);
}
}
Term::Apply { function, .. } => {
let id = id.unwrap();
if curry_applied_ids.contains(&id) {
*term = (**function).clone();
} }
} }
Term::Apply { function, argument } => todo!(),
Term::Constr { .. } => todo!(), Term::Constr { .. } => todo!(),
Term::Case { .. } => todo!(), Term::Case { .. } => todo!(),
_ => {} _ => {}
}) });
todo!()
} }
} }