From 2d3b234e6ed3fbd48f1c79e28a80c3b9b5301cbe Mon Sep 17 00:00:00 2001
From: Michael Sippel <micha@fragmental.art>
Date: Mon, 2 Oct 2023 01:45:54 +0200
Subject: [PATCH] add curry/decurry

---
 src/curry.rs      | 63 +++++++++++++++++++++++++++++++++++++++++++++++
 src/lib.rs        |  1 +
 src/test/curry.rs | 54 +++++++++++++++++++++++++++++++++++++++-
 3 files changed, 117 insertions(+), 1 deletion(-)
 create mode 100644 src/curry.rs

diff --git a/src/curry.rs b/src/curry.rs
new file mode 100644
index 0000000..7ee04d8
--- /dev/null
+++ b/src/curry.rs
@@ -0,0 +1,63 @@
+use crate::term::*;
+
+//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>\\
+
+impl TypeTerm {
+    /// transform term to have at max 2 entries in Application list
+    pub fn curry(mut self) -> TypeTerm {
+        match self {
+            TypeTerm::App(args) => {
+                if args.len() >= 2 {
+                    let mut old_args = args.into_iter();
+                    let mut new_args = vec![
+                        old_args.next().unwrap(),
+                        old_args.next().unwrap()
+                    ];
+
+                    for x in old_args {
+                        new_args = vec![
+                            TypeTerm::App(new_args),
+                            x
+                        ];
+                    }
+
+                    TypeTerm::App(new_args)
+                } else {
+                    TypeTerm::App(args)
+                }
+            }
+
+            TypeTerm::Ladder(rungs) => {
+                TypeTerm::Ladder(rungs.into_iter().map(|r| r.curry()).collect())
+            }
+
+            _ => self
+        }
+    }
+
+    /// summarize all curried applications into one vec
+    pub fn decurry(mut self) -> Self {
+        match self {
+            TypeTerm::App(mut args) => {
+                if args.len() > 0 {
+                    let mut a0 = args.remove(0).decurry();
+                    match a0 {
+                        TypeTerm::App(sub_args) => {
+                            for (i,x) in sub_args.into_iter().enumerate() {
+                                args.insert(i, x);
+                            }
+                        }
+                        other => { args.insert(0, other); }
+                    }
+                }
+                TypeTerm::App(args)
+            }
+            TypeTerm::Ladder(args) => {
+                TypeTerm::Ladder(args.into_iter().map(|a| a.decurry()).collect())
+            }
+            _ => self
+        }
+    }
+}
+
+//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>\\
diff --git a/src/lib.rs b/src/lib.rs
index 1f1ffcb..0319a0c 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -4,6 +4,7 @@ pub mod dict;
 pub mod term;
 pub mod lexer;
 pub mod parser;
+pub mod curry;
 
 #[cfg(test)]
 mod test;
diff --git a/src/test/curry.rs b/src/test/curry.rs
index 1e6b676..8ad1cdf 100644
--- a/src/test/curry.rs
+++ b/src/test/curry.rs
@@ -1,6 +1,58 @@
+use {
+    crate::{term::*, dict::*, parser::*},
+    std::str::FromStr
+};
+
+//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>\\
 
 #[test]
 fn test_curry() {
-    // todo
+    assert_eq!(
+        TypeTerm::from_str("<A B C>").unwrap().curry(),
+        TypeTerm::from_str("<<A B> C>").unwrap()
+    );
+    assert_eq!(
+        TypeTerm::from_str("<A B C D>").unwrap().curry(),
+        TypeTerm::from_str("<<<A B> C> D>").unwrap()
+    );
+    assert_eq!(
+        TypeTerm::from_str("<A B C D E F G H I J K>").unwrap().curry(),
+        TypeTerm::from_str("<<<<<<<<<<A B> C> D> E> F> G> H> I> J> K>").unwrap()
+    );
+
+    assert_eq!(
+        TypeTerm::from_str("<A~X B C>").unwrap().curry(),
+        TypeTerm::from_str("<<A~X B> C>").unwrap()
+    );
+    assert_eq!(
+        TypeTerm::from_str("<A B C~Y~Z> ~ K").unwrap().curry(),
+        TypeTerm::from_str("< <A B> C~Y~Z > ~ K").unwrap()
+    );
 }
 
+#[test]
+fn test_decurry() {
+    assert_eq!(
+        TypeTerm::from_str("<<A B> C>").unwrap().decurry(),
+        TypeTerm::from_str("<A B C>").unwrap()
+    );
+    assert_eq!(
+        TypeTerm::from_str("<<<A B> C> D>").unwrap().decurry(),
+        TypeTerm::from_str("<A B C D>").unwrap(),
+    );
+    assert_eq!(
+        TypeTerm::from_str("<<<<<<<<<<A B> C> D> E> F> G> H> I> J> K>").unwrap().decurry(),
+        TypeTerm::from_str("<A B C D E F G H I J K>").unwrap()
+    );
+    
+    assert_eq!(
+        TypeTerm::from_str("<<A~X B> C>").unwrap().decurry(),
+        TypeTerm::from_str("<A~X B C>").unwrap()
+    );
+    assert_eq!(
+        TypeTerm::from_str("<<A~X B> C~Y>~K").unwrap().decurry(),
+        TypeTerm::from_str("<A~X B C~Y> ~K").unwrap()
+    );
+}
+
+//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>\\