From b2083aec4cae5b7031c6928611bd6101d3ec6410 Mon Sep 17 00:00:00 2001
From: Michael Sippel <micha@fragmental.art>
Date: Mon, 28 Oct 2024 21:32:38 +0100
Subject: [PATCH] add dictionary example

---
 Cargo.toml                             |   1 +
 examples/tty-05-dictionary/Cargo.toml  |  19 ++
 examples/tty-05-dictionary/src/main.rs | 299 +++++++++++++++++++++++++
 3 files changed, 319 insertions(+)
 create mode 100644 examples/tty-05-dictionary/Cargo.toml
 create mode 100644 examples/tty-05-dictionary/src/main.rs

diff --git a/Cargo.toml b/Cargo.toml
index 44403c7..38b4295 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -10,5 +10,6 @@ members = [
     "examples/tty-02-digit",
     "examples/tty-03-string",
     "examples/tty-04-posint",
+    "examples/tty-05-dictionary",
 ]
 
diff --git a/examples/tty-05-dictionary/Cargo.toml b/examples/tty-05-dictionary/Cargo.toml
new file mode 100644
index 0000000..fef954f
--- /dev/null
+++ b/examples/tty-05-dictionary/Cargo.toml
@@ -0,0 +1,19 @@
+[package]
+name = "tty-05-dictionary"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+laddertypes = { path = "../../../lib-laddertypes" }
+r3vi = { path = "../../../lib-r3vi" }
+nested = { path = "../../lib-nested-core" }
+nested-tty = { path = "../../lib-nested-tty" }
+termion = "*"
+cgmath = "*"
+
+[dependencies.async-std]
+version = "1.9.0"
+features = ["unstable", "attributes"]
+
diff --git a/examples/tty-05-dictionary/src/main.rs b/examples/tty-05-dictionary/src/main.rs
new file mode 100644
index 0000000..d6daefc
--- /dev/null
+++ b/examples/tty-05-dictionary/src/main.rs
@@ -0,0 +1,299 @@
+extern crate cgmath;
+extern crate nested;
+extern crate nested_tty;
+extern crate r3vi;
+extern crate termion;
+
+use {
+    cgmath::Vector2,
+    nested::{
+        editors::{
+            ObjCommander
+        },
+        repr_tree::{Context, ReprTree, ReprTreeExt, ReprLeaf},
+        edit_tree::{EditTree, TreeNav, TreeCursor}
+    },
+    laddertypes::{
+        bimap::Bimap,
+        sugar::SugaredTypeTerm
+    },
+    nested_tty::{
+        DisplaySegment, TTYApplication,
+        TerminalCompositor, TerminalStyle, TerminalView,
+        TerminalAtom, TerminalEvent
+    },
+    r3vi::{
+        buffer::{singleton::*, vec::*},
+        view::{port::UpdateTask, singleton::*, list::*, sequence::*},
+        projection::*
+    },
+    std::sync::{Arc, RwLock},
+};
+
+#[async_std::main]
+async fn main() {
+    /* setup context
+     */
+    let ctx = Arc::new(RwLock::new(Context::new()));
+    nested::editors::char::init_ctx( ctx.clone() );
+    nested::editors::digit::init_ctx( ctx.clone() );
+    nested::editors::integer::init_ctx( ctx.clone() );
+    nested::editors::list::init_ctx( ctx.clone() );
+    nested_tty::setup_edittree_hook(&ctx);
+
+    let dict = Arc::new(RwLock::new(Bimap::<String, u64>::new()));
+    dict.write().unwrap().insert( "Nop".into(), 0 );
+    dict.write().unwrap().insert( "Jmp".into(), 1 );
+    dict.write().unwrap().insert( "Call".into(), 2 );
+    dict.write().unwrap().insert( "Branch".into(), 3 );
+    dict.write().unwrap().insert( "Ret".into(), 4 );
+    dict.write().unwrap().insert( "Lit".into(), 5 );
+    dict.write().unwrap().insert( "Pick".into(), 6 );
+    dict.write().unwrap().insert( "Roll".into(), 7 );
+    dict.write().unwrap().insert( "Dup".into(), 8 );
+    dict.write().unwrap().insert( "Drop".into(), 9 );
+    dict.write().unwrap().insert( "Swap".into(), 10 );
+    dict.write().unwrap().insert( "Rot".into(), 11 );
+    dict.write().unwrap().insert( "Fetch".into(), 13 );
+    dict.write().unwrap().insert( "Store".into(), 14 );
+    dict.write().unwrap().insert( "Accept".into(), 15 );
+    dict.write().unwrap().insert( "Emit".into(), 16 );
+    dict.write().unwrap().insert( "IntAdd".into(), 17 );
+    dict.write().unwrap().insert( "IntSub".into(), 18 );
+    dict.write().unwrap().insert( "IntMul".into(), 19 );
+    dict.write().unwrap().insert( "IntDiv".into(), 20 );
+    dict.write().unwrap().insert( "IntRem".into(), 21 );
+    dict.write().unwrap().insert( "FltAdd".into(), 22 );
+    dict.write().unwrap().insert( "FltSub".into(), 23 );
+    dict.write().unwrap().insert( "FltMul".into(), 24 );
+    dict.write().unwrap().insert( "FltDiv".into(), 25 );
+    dict.write().unwrap().insert( "FltRem".into(), 26 );
+    dict.write().unwrap().insert( "BitNeg".into(), 28 );
+    dict.write().unwrap().insert( "BitAnd".into(), 29 );
+    dict.write().unwrap().insert( "BitOr".into(), 30 );
+    dict.write().unwrap().insert( "BitXor".into(), 31 );
+    dict.write().unwrap().insert( "BitShl".into(), 32 );
+    dict.write().unwrap().insert( "BitShr".into(), 33 );
+
+    let symbol_morph_str_to_u64 = nested::repr_tree::GenericReprTreeMorphism::new(
+        Context::parse(&ctx, "Instruction ~ Mnemonic ~ <Seq~List~Vec Char>"),
+        Context::parse(&ctx, "Instruction ~ Opcode ~ ℕ ~ machine.UInt64"),
+
+        {
+            let ctx = ctx.clone();
+            let dict = dict.clone();
+
+            move |rt, σ| {
+                let str_view = rt
+                    .descend(Context::parse(&ctx, "Instruction ~ Mnemonic ~ <Seq~List~Vec Char>")).unwrap()
+                    .vec_buffer::<char>()
+                    .get_port();
+
+                let u64_view = str_view
+                    .to_singleton()
+                    .map({
+                        let old_value = Arc::new(RwLock::new(0));
+                        let dict = dict.clone();
+                        move |chars| {
+                            let str_val = chars.iter().collect::<String>();
+                            let dict = &dict.read().unwrap().mλ;
+
+                            let mut old_value = old_value.write().unwrap();
+                            if let Some( new_value ) = dict.get( &str_val ) {
+                                *old_value = new_value.clone();
+                                new_value.clone()
+                            } else {
+                                old_value.clone()
+                            }
+                        }
+                    });
+
+                rt.attach_leaf_to(
+                    Context::parse(&ctx, "Opcode ~ ℕ ~ machine.UInt64"),
+                    u64_view
+                );
+            }
+        }
+    );
+
+    let symbol_morph_u64_to_str = nested::repr_tree::GenericReprTreeMorphism::new(
+        Context::parse(&ctx, "Instruction ~ Opcode ~ ℕ ~ machine.UInt64"),
+        Context::parse(&ctx, "Instruction ~ Mnemonic ~ <Seq Char>"),
+
+        {
+            let ctx = ctx.clone();
+            let dict = dict.clone();
+
+            move |rt, σ| {
+                let u64_view = rt
+                    .descend(Context::parse(&ctx, "Instruction ~ Opcode ~ ℕ ~ machine.UInt64")).unwrap()
+                    .view_u64();
+
+                let str_view =
+                    u64_view
+                    .map({
+                        let dict = dict.clone();
+                        let old_value = Arc::new(RwLock::new( VecBuffer::<char>::new() ));
+                        move |idx| {
+                            let dict = &dict.read().unwrap().my;
+                            let mut old_value = old_value.write().unwrap();
+                            if let Some( new_value ) = dict.get( &idx ) {
+                                *old_value = VecBuffer::<char>::with_data(
+                                    new_value.chars().collect::<Vec<char>>()
+                                );
+                            }
+
+                            old_value.get_port().to_sequence()
+                        }
+                    })
+                    .to_sequence()
+                    .flatten();
+
+                rt.attach_leaf_to(
+                    Context::parse(&ctx, "Instruction ~ Mnemonic ~ <Seq Char>"),
+                    str_view
+                );
+            }
+        }
+    );
+
+    ctx.write().unwrap().morphisms.add_morphism( symbol_morph_u64_to_str );
+    ctx.write().unwrap().morphisms.add_morphism( symbol_morph_str_to_u64 );
+
+    let mut symbol_rt = nested::repr_tree::ReprTree::from_str(Context::parse(&ctx, "
+            Instruction ~ Mnemonic ~ <Seq~List~Vec Char>
+        "),
+        "Call"
+    );
+
+    // this is required to initialize the <Vec EditTree> representation,
+    // and to take the value from <Vec Char>
+    ctx.read().unwrap().build_repr_tree(
+        &symbol_rt,
+        Context::parse(&ctx, "Instruction ~ Mnemonic ~ <Seq~List~Vec Char>"),
+        vec![
+            Context::parse(&ctx, "Instruction ~ Opcode ~ ℕ ~ <PosInt 10 BigEndian> ~ EditTree"),
+            Context::parse(&ctx, "Instruction ~ Mnemonic ~ <Seq~List Char> ~ EditTree"),
+        ]);
+
+    symbol_rt.write().unwrap().detach( &ctx );
+
+    fn set_master(
+        ctx: &Arc<RwLock<Context>>,
+        rt: &Arc<RwLock<ReprTree>>,
+        mut leaves: Vec< laddertypes::TypeTerm >,
+        master_idx: usize
+    ) {
+        eprintln!("set master to {}", master_idx);
+        if master_idx < leaves.len() {
+            let master = leaves.remove( master_idx );
+            rt.write().unwrap().detach( &ctx );
+            ctx.read().unwrap().build_repr_tree(
+                rt,
+                master,
+                leaves
+            );
+        }
+    }
+
+    let editor_types = vec![
+         Context::parse(&ctx,
+             "Instruction ~ Mnemonic ~ <Seq~List Char> ~ EditTree"),
+         Context::parse(&ctx,
+             "Instruction ~ Opcode ~ ℕ ~ <PosInt 10 BigEndian> ~ EditTree"),
+         Context::parse(&ctx,
+             "Instruction ~ Opcode ~ ℕ ~ <PosInt 16 BigEndian> ~ EditTree"),
+    ];
+
+    set_master(&ctx, &symbol_rt, editor_types.clone(), 0);
+    let mut list_editor = nested::editors::list::ListEditor::new(ctx.clone(), Context::parse(&ctx, "<Seq Char>"));
+
+    // add all desired editors to the list
+    for leaf in editor_types.iter() {
+        let et =
+            symbol_rt
+                .descend(leaf.clone()).unwrap()
+                .edittree(&ctx).get();
+        et.write().unwrap().goto(TreeCursor::none());
+        list_editor.data.push(et);
+    }
+
+    let mut edittree = list_editor.into_node(SingletonBuffer::new(0).get_port());
+    edittree.goto(TreeCursor{
+        leaf_mode: nested::editors::list::ListCursorMode::Insert,
+        tree_addr: vec![0, 0]
+    });
+    let edittree = Arc::new(RwLock::new(edittree));
+
+    /* setup terminal
+     */
+    let app = TTYApplication::new({
+        /* event handler
+         */
+        let ctx = ctx.clone();
+        let symbol_rt = symbol_rt.clone();
+        let last_idx = RwLock::new(0);
+        let editor_types = editor_types.clone();
+        move |ev| {
+            let cur = edittree.read().unwrap().get_cursor();
+            if cur.tree_addr.len() > 0 {
+                let mut li = last_idx.write().unwrap();
+                let ci = cur.tree_addr[0];
+
+                if *li != ci {
+                    eprintln!("----------------------------------");
+                    set_master(
+                        &ctx,
+                        &symbol_rt,
+                        editor_types.clone(),
+                        ci as usize
+                    );
+                    *li = ci;
+                }
+            }
+
+            edittree.write().unwrap().send_cmd_obj(ev.to_repr_tree(&ctx));
+        }
+    });
+
+    /* Setup the compositor to serve as root-view
+     * by routing it to the `app.port` Viewport,
+     * so it will be displayed on TTY-output.
+     */
+    let compositor = TerminalCompositor::new(app.port.inner());
+
+    /* Now add some views to our compositor
+     */
+    {
+        let mut comp = compositor.write().unwrap();
+
+        fn show_edit_tree(
+            ctx: &Arc<RwLock<Context>>,
+            comp: &mut TerminalCompositor,
+            rt: &Arc<RwLock<ReprTree>>,
+            y: i16
+        ) {
+            let rt_edittree = rt.descend(Context::parse(&ctx, "EditTree")).expect("descend");
+            let halo_type = rt_edittree.read().unwrap().get_halo_type().clone();
+            let edittree = rt_edittree.read().unwrap().get_view::<dyn r3vi::view::singleton::SingletonView<Item = Arc<RwLock<EditTree>>>>().unwrap().get().read().unwrap().clone();
+
+            comp.push(  nested_tty::make_label( &ctx.read().unwrap().type_term_to_str(&halo_type) )
+                .map_item(|_pt, atom| atom.add_style_front(TerminalStyle::fg_color((90,90,90))))
+                .offset(Vector2::new(1,y)));
+
+            comp.push(  edittree.display_view()
+                .map_item(|_pt, atom|
+                    atom.add_style_front(TerminalStyle::bg_color((80,80,80)))
+                )
+                .offset(Vector2::new(1,y+1)));
+        }
+
+        show_edit_tree( &ctx, &mut comp, &symbol_rt.descend(Context::parse(&ctx, "Instruction ~ Mnemonic ~ <Seq~List Char>")).unwrap(), 1 );
+        show_edit_tree( &ctx, &mut comp, &symbol_rt.descend(Context::parse(&ctx, "Instruction ~ Opcode ~ ℕ ~ <PosInt 10 BigEndian>")).unwrap(), 3 );
+        show_edit_tree( &ctx, &mut comp, &symbol_rt.descend(Context::parse(&ctx, "Instruction ~ Opcode ~ ℕ ~ <PosInt 16 BigEndian>")).unwrap(), 5 );
+    }
+
+    /* write the changes in the view of `term_port` to the terminal
+     */
+    app.show().await.expect("output error!");
+}