Skip to main content

lib/
fsm.rs

1use super::FSMRc;
2use std::collections::{HashMap, HashSet};
3use std::str::pattern::Pattern;
4
5use super::FSMLock;
6use super::get_id;
7use crate::NameShortener;
8
9pub type FSMNodeWrapper = FSMRc<FSMLock<FSMNode>>;
10trait FSMOp = FnMut(&mut HashSet<NodeId>, &FSMNodeWrapper, &FSMNodeWrapper, &mut isize) -> bool;
11trait FSMUnsafe = Fn(&mut HashSet<NodeId>, &FSMNode, &FSMNode, &mut isize) -> bool;
12pub trait CycleAwareOp<T> {
13    fn walk_fsm(&self, op: &mut T, greedy: bool, depth_search: bool) -> Option<FSMNodeWrapper>;
14    fn walk_fsm_internal(
15        &self,
16        op: &mut T,
17        greedy: bool,
18        depth_search: bool,
19        visited_nodes: &mut HashSet<NodeId>,
20    ) -> Option<FSMNodeWrapper>;
21    fn walk_fsm_breadth(&self, op: &mut T, greedy: bool) -> Option<FSMNodeWrapper> {
22        self.walk_fsm(op, greedy, false)
23    }
24    fn walk_fsm_depth(&self, op: &mut T, greedy: bool) -> Option<FSMNodeWrapper> {
25        self.walk_fsm(op, greedy, true)
26    }
27    // no this is not a workaround I swear
28    // Good things *can* come out of having something like this as constructs like Repeats need
29    // to loop back
30    fn walk_fsm_allow_cycle_to_self(
31        &self,
32        op: &mut T,
33        greedy: bool,
34        depth_search: bool,
35    ) -> Option<FSMNodeWrapper> {
36        self.walk_fsm_internal(op, greedy, depth_search, &mut HashSet::new())
37    }
38}
39
40// TODO: refactor
41impl<T> CycleAwareOp<T> for FSMNode
42where
43    T: FSMUnsafe,
44{
45    fn walk_fsm_internal(
46        &self,
47        op: &mut T,
48        greedy: bool,
49        depth_search: bool,
50        visited_nodes: &mut HashSet<NodeId>,
51    ) -> Option<FSMNodeWrapper> {
52        let children = self.children.clone();
53        let mut c_idx = 0;
54        for child in children.iter() {
55            if !visited_nodes.contains(&child.borrow().id) {
56                if depth_search {
57                    visited_nodes.insert(child.borrow().id);
58                    if (greedy || child.borrow().is_null())
59                        && let Some(child) = child.borrow().walk_fsm_internal(
60                            op,
61                            greedy,
62                            depth_search,
63                            visited_nodes,
64                        )
65                    {
66                        return Some(child);
67                    }
68                }
69                if op(visited_nodes, self, &child.borrow(), &mut c_idx) {
70                    return Some(child.clone());
71                }
72            }
73            c_idx += 1;
74        }
75        if !depth_search {
76            for child in children.iter() {
77                if (greedy || child.borrow().is_null())
78                    && !visited_nodes.contains(&child.borrow().id)
79                {
80                    visited_nodes.insert(child.borrow().id);
81
82                    if let Some(child) =
83                        child
84                            .borrow()
85                            .walk_fsm_internal(op, greedy, depth_search, visited_nodes)
86                    {
87                        return Some(child);
88                    }
89                }
90            }
91        }
92        None
93    }
94    fn walk_fsm(&self, op: &mut T, greedy: bool, depth_search: bool) -> Option<FSMNodeWrapper> {
95        let mut visisted_nodes = HashSet::new();
96        visisted_nodes.insert(self.id);
97        self.walk_fsm_internal(op, greedy, depth_search, &mut visisted_nodes)
98    }
99}
100
101impl<T> CycleAwareOp<T> for FSMNodeWrapper
102where
103    T: FSMOp,
104{
105    fn walk_fsm_internal(
106        &self,
107        op: &mut T,
108        greedy: bool,
109        depth_search: bool,
110        visited_nodes: &mut HashSet<NodeId>,
111    ) -> Option<FSMNodeWrapper> {
112        let children = self.borrow().children.clone();
113        let mut c_idx = 0;
114        for child in children.iter() {
115            if !visited_nodes.contains(&child.borrow().id) {
116                if depth_search {
117                    visited_nodes.insert(child.borrow().id);
118                    if (greedy || child.borrow().is_null())
119                        && let Some(child) =
120                            child.walk_fsm_internal(op, greedy, depth_search, visited_nodes)
121                    {
122                        return Some(child);
123                    }
124                }
125                if op(visited_nodes, self, child, &mut c_idx) {
126                    return Some(child.clone());
127                }
128            }
129            c_idx += 1;
130        }
131        if !depth_search {
132            for child in children.iter() {
133                if !visited_nodes.contains(&child.borrow().id) {
134                    visited_nodes.insert(child.borrow().id);
135                    if (greedy || child.borrow().is_null())
136                        && let Some(child) =
137                            child.walk_fsm_internal(op, greedy, depth_search, visited_nodes)
138                    {
139                        return Some(child);
140                    }
141                }
142            }
143        }
144        None
145    }
146    fn walk_fsm(&self, op: &mut T, greedy: bool, depth_search: bool) -> Option<FSMNodeWrapper> {
147        let mut visisted_nodes = HashSet::new();
148        visisted_nodes.insert(self.borrow().id);
149        self.walk_fsm_internal(op, greedy, depth_search, &mut visisted_nodes)
150    }
151}
152
153#[derive(Debug, Clone, PartialEq)]
154pub struct Keyword {
155    pub short: String,
156    pub expanded: String,
157    pub closing_token: Option<String>,
158}
159
160impl Keyword {
161    pub fn new(expanded: String, closing_token: Option<String>) -> Self {
162        Self {
163            short: expanded.chars().nth(0).unwrap().to_string(),
164            expanded,
165            closing_token,
166        }
167    }
168}
169
170impl Default for Keyword {
171    fn default() -> Self {
172        Self {
173            short: String::new(),
174            expanded: String::new(),
175            closing_token: None,
176        }
177    }
178}
179
180// TODO: combine UserDefined and UserDefinedRegex into one variant
181#[derive(Debug, Clone)]
182pub enum NodeType {
183    Keyword(Keyword),
184    #[deprecated]
185    UserDefined {
186        final_chars: Vec<char>,
187    },
188    #[deprecated]
189    UserDefinedRegex(Regex),
190    UserDefinedCombo(Regex, Vec<char>),
191    Null,
192}
193
194impl PartialEq for NodeType {
195    fn eq(&self, other: &Self) -> bool {
196        match self {
197            Keyword(k) => match other {
198                Keyword(k2) => k.eq(k2),
199                _ => false,
200            },
201            UserDefined { final_chars: fc } => match other {
202                UserDefined { final_chars: fc2 } => fc.eq(fc2),
203                _ => false,
204            },
205            UserDefinedRegex(r) => match other {
206                UserDefinedRegex(r2) => r.as_str().eq(r2.as_str()),
207                _ => false,
208            },
209            UserDefinedCombo(r, f) => match other {
210                UserDefinedCombo(r2, f2) => r.as_str().eq(r2.as_str()) && f.eq(f2),
211                _ => false,
212            },
213            Null => match other {
214                Null => true,
215                _ => false,
216            },
217        }
218    }
219    fn ne(&self, other: &Self) -> bool {
220        !self.eq(other)
221    }
222}
223
224use NodeType::*;
225use debug_print::debug_println;
226use regex::Regex;
227
228type NodeId = usize;
229#[derive(Debug, Clone, PartialEq)]
230pub struct FSMNode {
231    id: NodeId,
232    is_done: bool,
233    pub value: NodeType,
234    pub children: Vec<FSMRc<FSMLock<FSMNode>>>,
235}
236
237impl Default for FSMNode {
238    fn default() -> Self {
239        Self {
240            id: get_id(),
241            is_done: false,
242            value: Null,
243            children: Vec::new(),
244        }
245    }
246}
247
248impl FSMNode {
249    pub fn id(&self) -> NodeId {
250        self.id
251    }
252    pub fn is_done(&self) -> bool {
253        self.is_done
254    }
255    pub fn set_is_done(&mut self, val: bool) {
256        self.is_done = val;
257    }
258    pub fn is_null(&self) -> bool {
259        if let Null = self.value { true } else { false }
260    }
261    fn deep_clone_internal(
262        stub: &FSMRc<FSMLock<Self>>,
263        old: &FSMNode,
264        visited_nodes: &mut HashMap<usize, FSMRc<FSMLock<FSMNode>>>,
265    ) -> FSMRc<FSMLock<Self>> {
266        for child in &old.children {
267            if !visited_nodes.contains_key(&child.borrow().id) {
268                let clone = FSMRc::new(FSMLock::new(Self {
269                    value: child.borrow().value.clone(),
270                    ..Default::default()
271                }));
272                visited_nodes.insert(child.borrow().id, clone.clone());
273                FSMNode::deep_clone_internal(&clone, &child.borrow(), visited_nodes);
274                stub.borrow_mut().children.push(clone);
275            } else {
276                stub.borrow_mut()
277                    .children
278                    .push(visited_nodes.get(&child.borrow().id).unwrap().clone());
279            }
280        }
281        stub.clone()
282    }
283    pub fn deep_clone(&self) -> FSMRc<FSMLock<Self>> {
284        debug_println!("Deep cloning node {}", self.short_id());
285        let ret = FSMRc::new(FSMLock::new(Self {
286            value: self.value.clone(),
287            ..Default::default()
288        }));
289        let mut visited_nodes = HashMap::new();
290        visited_nodes.insert(self.id, ret.clone());
291        let ret = FSMNode::deep_clone_internal(&ret, self, &mut visited_nodes);
292        debug_println!("Finish deep clone:");
293        ret.borrow().dbg();
294        ret
295    }
296    fn has_direct_child(&self, id: usize) -> bool {
297        self.children.iter().find(|c| c.borrow().id == id).is_some()
298    }
299    pub fn node_cnt(this: &FSMRc<FSMLock<FSMNode>>) -> usize {
300        let mut ret = 1; // one root node
301        this.walk_fsm_depth(
302            &mut |_, parent, _, _| {
303                ret += parent.borrow().children.len();
304                false
305            },
306            true,
307        );
308        ret
309    }
310    fn get_direct_child_dups(&self) -> Vec<usize> {
311        let mut ids = HashSet::new();
312        let mut ret = Vec::new();
313        self.children.iter().enumerate().for_each(|(i, c)| {
314            if ids.contains(&c.borrow().id) {
315                ret.push(i);
316            } else {
317                ids.insert(c.borrow().id);
318            }
319        });
320        ret
321    }
322    pub fn set_userdef_links(this: &FSMRc<FSMLock<FSMNode>>) {
323        let mut userdefs = Vec::new();
324        this.walk_fsm_breadth(
325            &mut |_, p, _, _| {
326                if let UserDefinedCombo(_, _) = p.borrow().value {
327                    userdefs.push(FSMRc::clone(&p));
328                }
329                false
330            },
331            true,
332        );
333        println!("{}", userdefs.len());
334        for userdef in userdefs {
335            println!(
336                "{:?} {}",
337                userdef.borrow().value,
338                userdef.borrow().short_id()
339            );
340            userdef.walk_fsm_depth(
341                &mut |_, _, c, _| {
342                    println!("{:?} {}", c.borrow().value, c.borrow().short_id());
343                    if let Keyword(Keyword { short, .. }) = &c.borrow().value {
344                        if let UserDefinedCombo(_, fcs) = &mut userdef.borrow_mut().value {
345                            fcs.push(short.chars().nth(0).unwrap()); // bad handling, only possible when
346                            // there aren't any conflicts
347                        }
348                    }
349                    false
350                },
351                false,
352            );
353        }
354    }
355    pub fn minify(this: &FSMRc<FSMLock<FSMNode>>) {
356        debug_println!("before minify:");
357        this.borrow().dbg();
358        let mut cycle_translation_table = HashMap::new();
359        this.walk_fsm_depth(
360            &mut |_, parent, child, childidx| {
361                // TODO: figure out why the parent check needs to be here
362                if parent.borrow().is_null()
363                    && child.borrow().is_null()
364                    && parent.borrow().children.len() == 1
365                {
366                    cycle_translation_table.insert(child.borrow().id, parent.clone());
367                    parent.borrow_mut().children.remove(*childidx as usize);
368                    for child in &child.borrow().children {
369                        debug_println!("CID: {}", child.borrow().short_id());
370                        if child.borrow().id == parent.borrow().id
371                            || parent.borrow().has_direct_child(child.borrow().id)
372                        {
373                            continue;
374                        }
375                        parent.borrow_mut().children.push(child.clone());
376                    }
377                    child.borrow_mut().children.clear();
378                    *childidx -= 1;
379                }
380                let pborrow = parent.borrow();
381                let dup_idxs = pborrow.get_direct_child_dups();
382                drop(pborrow);
383                dup_idxs.iter().for_each(|ci| {
384                    parent.borrow_mut().children.remove(*ci);
385                });
386                false
387            },
388            true,
389        );
390        // fix any broken pointers the last op may have created
391        this.walk_fsm_depth(
392            &mut |_, parent, child, childidx| {
393                if let Some(new_child) = cycle_translation_table.get(&child.borrow().id) {
394                    if new_child.borrow().id == parent.borrow().id
395                        || parent.borrow().has_direct_child(new_child.borrow().id)
396                    {
397                        parent.borrow_mut().children.remove(*childidx as usize);
398                        return false;
399                    }
400                    parent.borrow_mut().children[*childidx as usize] = new_child.clone();
401                }
402                false
403            },
404            true,
405        );
406        debug_println!("after minify:");
407        this.borrow().dbg();
408    }
409
410    pub fn has_useful_children(&self) -> bool {
411        self.walk_fsm_breadth(
412            &mut |_, _, c, _| match c.value {
413                Null => false,
414                _ => true,
415            },
416            false,
417        )
418        .is_some()
419    }
420
421    pub fn get_last_child(&self) -> Option<FSMRc<FSMLock<FSMNode>>> {
422        self.children.last().cloned()
423    }
424    /// adds child to the children vector of self without doing collision checks first
425    pub unsafe fn add_child_unsafe(&mut self, child: &FSMRc<FSMLock<FSMNode>>) {
426        self.children.push(FSMRc::clone(&child));
427    }
428    pub fn add_child(&mut self, child: &FSMRc<FSMLock<FSMNode>>) {
429        while self.handle_potential_conflict(child) {}
430        unsafe {
431            self.add_child_unsafe(child);
432        }
433    }
434    pub fn add_child_cycle_safe(this: &FSMRc<FSMLock<FSMNode>>, child: &FSMRc<FSMLock<FSMNode>>) {
435        while this.borrow().handle_potential_conflict(child) {}
436        this.borrow_mut().children.push(FSMRc::clone(&child));
437    }
438    pub fn new_null(parent: Option<&FSMRc<FSMLock<FSMNode>>>) -> FSMRc<FSMLock<Self>> {
439        let ret = FSMRc::new(FSMLock::new(Self {
440            value: Null,
441            children: Vec::new(),
442            ..Default::default()
443        }));
444        if let Some(parent) = parent {
445            parent.borrow_mut().add_child(&ret);
446        }
447        ret
448    }
449
450    pub fn short_id(&self) -> String {
451        format!("{:#x}", self.id)
452    }
453
454    fn dbg_internal(&self, indent: usize, visited_nodes: &mut HashSet<usize>) {
455        println!("{}{:?} {}", " ".repeat(indent), self.value, self.short_id());
456        visited_nodes.insert(self.id);
457        for child in self.children.iter() {
458            if !visited_nodes.contains(&child.borrow().id) {
459                child.borrow().dbg_internal(indent + 4, visited_nodes);
460            } else {
461                println!(
462                    "{}Cycle to {}",
463                    " ".repeat(indent + 4),
464                    child.borrow().short_id()
465                );
466            }
467        }
468    }
469    fn get_all_leaves(&self, discovered_leaves: &mut Vec<FSMRc<FSMLock<FSMNode>>>) {
470        FSMRc::new(FSMLock::new(self.clone())).walk_fsm(
471            &mut |visited_nodes, _, child, _| {
472                if discovered_leaves
473                    .iter()
474                    .find(|dl| dl.borrow().id == child.borrow().id)
475                    .is_some()
476                {
477                    return false;
478                }
479                if child.borrow().children.is_empty() {
480                    debug_println!(
481                        "adding node {:?} {}",
482                        child.borrow().value,
483                        child.borrow().short_id()
484                    );
485                    discovered_leaves.push(child.clone());
486                } else {
487                    let mut has_only_cycles = true;
488                    for child in &child.borrow().children {
489                        if !visited_nodes.contains(&child.borrow().id) {
490                            has_only_cycles = false;
491                            break;
492                        }
493                    }
494                    if has_only_cycles {
495                        debug_println!(
496                            "adding node {:?} {} (has_only_cycles case)",
497                            child.borrow().value,
498                            child.borrow().short_id()
499                        );
500                        discovered_leaves.push(child.clone());
501                    }
502                }
503                false
504            },
505            true,
506            false,
507        );
508    }
509    pub fn add_child_to_all_leaves(
510        this: &FSMRc<FSMLock<FSMNode>>,
511        child: &FSMRc<FSMLock<FSMNode>>,
512    ) {
513        let mut leaves = Vec::new();
514        this.borrow().get_all_leaves(&mut leaves);
515        while let Some(node) = leaves.pop() {
516            FSMNode::add_child_cycle_safe(&node, child);
517            // NOTE: hopefully this isn't needed anymore
518            // if node.borrow().children.is_empty() {
519            //     FSMNode::add_child_cycle_safe(&node, child);
520            // }
521        }
522        if this.borrow().children.is_empty() {
523            FSMNode::add_child_cycle_safe(&this, child);
524        }
525    }
526
527    pub fn race_to_leaf(&self) -> Option<FSMRc<FSMLock<FSMNode>>> {
528        self.walk_fsm_depth(
529            &mut |visited_nodes, _, child, _| {
530                let mut ret = true;
531                // avoid going back to a node previously visited so do_stuff_cycle_aware doesn't return
532                // a false negative
533                for child in &child.children {
534                    if !visited_nodes.contains(&child.borrow().id) {
535                        ret = false;
536                        break;
537                    }
538                }
539                ret
540            },
541            true,
542        )
543    }
544    pub fn dbg(&self) {
545        #[cfg(debug_assertions)]
546        self.dbg_internal(0, &mut HashSet::new());
547    }
548
549    pub fn new_id(value: NodeType, id: NodeId) -> FSMRc<FSMLock<Self>> {
550        let ret = FSMRc::new(FSMLock::new(Self {
551            value,
552            children: Vec::new(),
553            id,
554            ..Default::default()
555        }));
556        ret
557    }
558
559    pub fn new(value: NodeType, parent: &FSMRc<FSMLock<FSMNode>>) -> FSMRc<FSMLock<Self>> {
560        let ret = FSMRc::new(FSMLock::new(Self {
561            value,
562            children: Vec::new(),
563            ..Default::default()
564        }));
565        parent.borrow_mut().add_child(&ret);
566        ret
567    }
568
569    pub fn new_required(value: NodeType, parent: &FSMRc<FSMLock<FSMNode>>) -> FSMRc<FSMLock<Self>> {
570        let ret = FSMRc::new(FSMLock::new(Self {
571            value,
572            children: Vec::new(),
573            ..Default::default()
574        }));
575        parent.borrow_mut().add_child(&ret);
576        ret
577    }
578
579    pub fn new_userdef(r: Regex, parent: &FSMRc<FSMLock<FSMNode>>) -> FSMRc<FSMLock<Self>> {
580        let ret = FSMRc::new(FSMLock::new(Self {
581            value: UserDefinedCombo(r, Vec::new()),
582            children: Vec::new(),
583            ..Default::default()
584        }));
585        FSMNode::add_child_cycle_safe(parent, &ret);
586        ret
587    }
588
589    pub fn new_keyword(expanded_name: String) -> FSMRc<FSMLock<Self>> {
590        FSMRc::new(FSMLock::new(Self {
591            value: Keyword(Keyword {
592                short: expanded_name.chars().nth(0).unwrap().to_string(),
593                expanded: expanded_name,
594                ..Default::default()
595            }),
596            children: Vec::new(),
597            ..Default::default()
598        }))
599    }
600
601    pub fn new_keyword_with_parent(
602        expanded_name: String,
603        parent: FSMRc<FSMLock<FSMNode>>,
604    ) -> FSMRc<FSMLock<Self>> {
605        let ret = Self::new_keyword(expanded_name);
606        FSMNode::add_child_cycle_safe(&parent, &ret);
607        ret
608    }
609    pub fn find_node_with_code(&self, short: &str) -> Option<FSMRc<FSMLock<FSMNode>>> {
610        for child in &self.children {
611            if let Keyword(Keyword { short: nshort, .. }) = &child.borrow().value
612                && nshort == short
613            {
614                return Some(FSMRc::clone(&child));
615            }
616        }
617        for child in &self.children {
618            let rec_res = child.borrow().find_node_with_code(short);
619            if rec_res.is_some() {
620                return rec_res;
621            }
622        }
623        None
624    }
625
626    pub fn check_for_conflicts(&self, short: &str) -> bool {
627        for child in &self.children {
628            let borrow = child.borrow();
629            match &borrow.value {
630                Keyword(Keyword { short: nshort, .. }) if nshort == short => return true,
631                Null => {
632                    let rec_res = borrow.check_for_conflicts(short);
633                    if rec_res {
634                        return true;
635                    }
636                }
637                _ => {}
638            }
639        }
640        false
641    }
642
643    fn get_conflicting_node(&self, short: &str) -> Option<FSMRc<FSMLock<FSMNode>>> {
644        let res = self.walk_fsm_breadth(
645            &mut |_, _, child, _| {
646                println!("awa?");
647                match &child.value {
648                    Keyword(Keyword { short: nshort, .. }) if short.starts_with(nshort) => {
649                        return true;
650                    }
651                    _ => false,
652                }
653            },
654            false,
655        );
656        debug_println!("get_conflicting_node finish");
657        res
658    }
659    fn handle_potential_conflict_internal(&self, child: &FSMRc<FSMLock<FSMNode>>) -> bool {
660        let child_borrow = child.borrow();
661        let mut ret = false;
662        if let Keyword(Keyword { short: cshort, .. }) = &child_borrow.value {
663            if let Some(node) = self.get_conflicting_node(cshort)
664                && node.borrow().value != child_borrow.value
665            {
666                node.replace_with(|node| {
667                    if let Keyword(keyword_struct) = &mut node.value {
668                        let new_short = NameShortener::expand(
669                            Some(&keyword_struct.short),
670                            &keyword_struct.expanded,
671                        );
672                        keyword_struct.short = new_short;
673                        debug_println!("conflict handler 2");
674                        ret = true;
675                        node.to_owned()
676                    } else {
677                        panic!(
678                            "What?! We got a non-keyword node from the get_conflicting_node fn! Anyways, I'm gonna snuggle some foxxos now..."
679                        )
680                    }
681                });
682            }
683        }
684        ret
685    }
686    pub fn handle_potential_conflict(&self, child: &FSMRc<FSMLock<FSMNode>>) -> bool {
687        let child_borrow = child.borrow();
688        if let Keyword(keyword_struct) = &child_borrow.value {
689            debug_println!("{:?}", self.value);
690            debug_println!("{:?}", child.borrow().value);
691            if self.handle_potential_conflict_internal(child) {
692                let short =
693                    NameShortener::expand(Some(&keyword_struct.short), &keyword_struct.expanded);
694                drop(child_borrow);
695                child.replace_with(|node| {
696                    if let Keyword(k) = &mut node.value {
697                        k.short = short;
698                    } else {
699                        unreachable!()
700                    }
701                    node.to_owned()
702                });
703                return true;
704            }
705        } else if let Null = &child_borrow.value {
706            // println!("awa?");
707            let mut ret = false;
708            let mut visited_nodes = HashSet::new();
709            // iterate over every child and return true if at least one had a conflict
710            for child in &child_borrow.children {
711                if !visited_nodes.contains(&child.borrow().id) {
712                    visited_nodes.insert(child.borrow().id);
713                    if self.handle_potential_conflict_internal(&child) {
714                        let mut mut_child = child.borrow_mut();
715                        if let Keyword(k) = &mut mut_child.value {
716                            k.short = NameShortener::expand(Some(&k.short), &k.expanded);
717                        }
718                        ret = true;
719                    }
720                }
721            }
722            if ret {
723                return true;
724            }
725        }
726        false
727    }
728    pub fn dump_children(&self) {
729        self.children
730            .iter()
731            .for_each(|child| println!("{:?}", child.borrow().value));
732    }
733}
734
735pub trait ToCSV {
736    fn to_csv(&self) -> String;
737    fn from_csv(csv: &str) -> Self;
738}
739
740impl ToCSV for NodeType {
741    fn to_csv(&self) -> String {
742        let mut ret = match self {
743            Null => "".to_owned(),
744            Keyword(Keyword {
745                short,
746                expanded,
747                closing_token,
748            }) => format!(
749                "{}\t{}{}",
750                short,
751                expanded,
752                if let Some(ct) = closing_token {
753                    "\t".to_owned() + ct
754                } else {
755                    "".to_owned()
756                }
757            ),
758            UserDefinedCombo(r, cts) => {
759                format!(
760                    "/{}{}",
761                    r.as_str(),
762                    cts.iter()
763                        .fold(String::new(), |acc, el| { format!("{}\t{}", acc, el) })
764                )
765            }
766            _ => panic!("FSMs with deprecated nodes will not be serialized!"),
767        };
768        ret.push('\n');
769        ret
770    }
771    fn from_csv(csv: &str) -> Self {
772        println!("csv: {csv}");
773        if csv.len() < 2 {
774            Null
775        } else if csv.chars().nth(0).unwrap() == '/' {
776            let mut iter = csv.split('\t');
777            let regex = Regex::new(&iter.next().expect("invalid NodeType format")[1..])
778                .expect("invalid Regex format");
779            let mut final_tokens = Vec::new();
780            iter.for_each(|ft| final_tokens.push(ft.chars().nth(0).unwrap()));
781            UserDefinedCombo(regex, final_tokens)
782        } else {
783            let parts: Vec<_> = csv.split('\t').collect();
784            if parts.len() < 3 {
785                Keyword(Keyword {
786                    short: parts[0].to_owned(),
787                    expanded: parts[1].to_owned(),
788                    closing_token: None,
789                })
790            } else {
791                Keyword(Keyword {
792                    short: parts[0].to_owned(),
793                    expanded: parts[1].to_owned(),
794                    closing_token: Some(parts[3].to_owned()),
795                })
796            }
797        }
798    }
799}
800
801impl ToCSV for FSMNodeWrapper {
802    fn to_csv(&self) -> String {
803        let mut nodes = HashMap::new();
804        self.walk_fsm_breadth(
805            &mut |_, _, c, _| {
806                nodes.insert(c.borrow().id, c.clone());
807                false
808            },
809            true,
810        );
811        let mut ret = format!("{}\t{}", self.borrow().id, self.borrow().value.to_csv());
812        nodes.keys().for_each(|id| {
813            ret.push_str(&id.to_string());
814            ret.push('\t');
815            ret.push_str(&nodes.get(id).unwrap().borrow().value.to_csv());
816        });
817        ret.push('\n');
818
819        ret.push_str(&self.borrow().id.to_string());
820        self.borrow().children.iter().for_each(|el| {
821            ret.push('\t');
822            ret.push_str(&el.borrow().id.to_string());
823        });
824        ret.push('\n');
825
826        nodes.keys().for_each(|id| {
827            ret.push_str(&id.to_string());
828            let node = nodes.get(id).unwrap();
829            node.borrow().children.iter().for_each(|el| {
830                ret.push('\t');
831                ret.push_str(&el.borrow().id.to_string());
832            });
833            ret.push('\n');
834        });
835        ret
836    }
837    fn from_csv(csv: &str) -> Self {
838        let mut iter = csv.split_indices('\n');
839        let mut nodes = HashMap::new();
840
841        // TODO: refactor
842        let line = iter.next().unwrap();
843        println!("from_csv at line '{line:?}'");
844        let mut line_iter = line.0.split_indices('\t');
845        let id: usize = line_iter.next().unwrap().0.parse().unwrap();
846        let ntype = match line_iter.next() {
847            Some(nval) => NodeType::from_csv(&line.0[nval.1..]),
848            None => Null,
849        };
850
851        let root = FSMNode::new_id(ntype, id);
852        nodes.insert(id, root.clone());
853        while let Some(part) = iter.next()
854            && !part.0.is_empty()
855        {
856            println!("from_csv at line '{part:?}'");
857            let mut line_iter = part.0.split_indices('\t');
858            let id: usize = line_iter.next().unwrap().0.parse().unwrap();
859            let ntype = match line_iter.next() {
860                Some(nval) => NodeType::from_csv(&part.0[nval.1..]),
861                None => Null,
862            };
863            nodes.insert(id, FSMNode::new_id(ntype, id));
864        }
865        // iter.next(); // consume separator line
866
867        // children logic
868        while let Some(part) = iter.next()
869            && !part.0.is_empty()
870        {
871            let mut iter = part.0.split('\t');
872            let id: usize = iter.next().unwrap().parse().unwrap();
873            let parent = nodes.get(&id).unwrap();
874            while let Some(part) = iter.next() {
875                let c_id: NodeId = part.parse().unwrap();
876                #[cfg(not(debug_assertions))]
877                unsafe {
878                    parent
879                        .borrow_mut()
880                        .add_child_unsafe(nodes.get(&c_id).unwrap());
881                }
882                #[cfg(debug_assertions)]
883                FSMNode::add_child_cycle_safe(parent, nodes.get(&c_id).unwrap());
884            }
885        }
886        root
887    }
888}
889
890trait SplitIndicesExt {
891    fn split_indices<P>(&self, pat: P) -> impl Iterator<Item = (&str, usize)>
892    where
893        P: Pattern;
894}
895
896impl SplitIndicesExt for &str {
897    fn split_indices<P>(&self, pat: P) -> impl Iterator<Item = (&str, usize)>
898    where
899        P: Pattern,
900    {
901        let mut start = 0;
902        self.match_indices(pat).map(move |(i, _)| {
903            let ret = (&self[start..i], start);
904            start = i + 1;
905            ret
906        })
907    }
908}
909
910#[cfg(test)]
911mod tests {
912    use crate::dbg_id;
913
914    use super::*;
915
916    #[test]
917    fn test_csv_simple() {
918        dbg_id();
919        let root = FSMNode::new_keyword("int".to_string());
920        let _other = FSMNode::new_keyword_with_parent("asdf".to_string(), root.clone());
921
922        let csv = root.to_csv();
923        assert_eq!("0\ti\tint\n1\ta\tasdf\n\n0\t1\n1\n", csv);
924        let new_root = FSMNodeWrapper::from_csv(&csv);
925        assert_eq!(root, new_root);
926    }
927}