lib/
frontend.rs

1use std::collections::HashMap;
2
3use debug_print::debug_println;
4use ebnf::{Expression, Grammar, Node, RegexExtKind, SymbolKind};
5use regex::Regex;
6
7use super::FSMLock;
8use super::FSMRc;
9use crate::FSMNode;
10
11pub fn print_parsed_ebnf(syntax: &str) {
12    let grammar = ebnf::get_grammar(&syntax).unwrap();
13    for node in grammar.expressions {
14        println!("{:?}", node);
15    }
16}
17
18enum TerminalState {
19    Stub,
20    Created,
21}
22
23fn handle_node(
24    grammar: &Grammar,
25    cur_node: &Node,
26    cur_root: &FSMRc<FSMLock<FSMNode>>,
27    terminals: &mut HashMap<String, (FSMRc<FSMLock<FSMNode>>, TerminalState)>,
28) -> FSMRc<FSMLock<FSMNode>> {
29    debug_println!("handle_node got {:?}", cur_node);
30    let ret = match &cur_node {
31        Node::String(str) => {
32            FSMNode::new_keyword_with_parent(str.to_string(), FSMRc::clone(cur_root))
33        }
34        Node::RegexString(r) => FSMNode::new_userdef(Regex::new(r).unwrap(), cur_root),
35        Node::Terminal(name) => {
36            if terminals.contains_key(name) {
37                debug_println!("Found {name} in cache!");
38                let term = terminals.get(name).unwrap();
39                let term_clone = match term.1 {
40                    TerminalState::Stub => term.0.clone(),
41                    TerminalState::Created => term.0.borrow().deep_clone(),
42                };
43                println!("linking back to {}", term_clone.borrow().short_id());
44                FSMNode::add_child_cycle_safe(cur_root, &term_clone);
45                println!("after add:");
46                cur_root.borrow().dbg();
47                term_clone
48            } else {
49                debug_println!("Creating terminal {name}...");
50                let terminal = find_terminal(&grammar, &name);
51                if terminal.is_none() {
52                    panic!("Terminal reference '{name}' not found!");
53                }
54                let terminal = terminal.unwrap();
55                let term_root = FSMNode::new_null(None);
56                debug_println!("term_root: {}", term_root.borrow().short_id());
57                terminals.insert(
58                    name.to_string(),
59                    (FSMRc::clone(&term_root), TerminalState::Stub),
60                );
61                handle_node(grammar, &terminal.rhs, &term_root, terminals);
62                terminals.insert(
63                    name.to_string(),
64                    (FSMRc::clone(&term_root), TerminalState::Created),
65                );
66                debug_println!("Finish terminal");
67                debug_println!("young {}:", name);
68                term_root.borrow().dbg();
69                let ret = term_root.borrow().deep_clone();
70                FSMNode::add_child_cycle_safe(cur_root, &ret);
71                ret
72            }
73        }
74        Node::Multiple(nodes) => {
75            let mut cur_treenode = cur_root.clone();
76            nodes.iter().for_each(|node| {
77                debug_println!("Multiple at {node:?}");
78                let tree_bit = handle_node(grammar, &node, &cur_treenode, terminals);
79                debug_println!("Multiple got back:");
80                tree_bit.borrow().dbg();
81                // NOTE: this will only work as long as the other node handlers nicely merge their
82                // diverging branches back into one single Null leaf
83                cur_treenode = tree_bit.borrow().race_to_leaf().unwrap_or(tree_bit.clone());
84                debug_println!("cur_treenode now at: {}", cur_treenode.borrow().short_id());
85            });
86            cur_treenode
87        }
88        Node::RegexExt(node, RegexExtKind::Optional) | Node::Optional(node) => {
89            let tree_bit = handle_node(grammar, &node, cur_root, terminals);
90            let dummy = FSMNode::new_null(None);
91            FSMNode::add_child_to_all_leaves(&tree_bit, &dummy);
92            FSMNode::add_child_cycle_safe(&cur_root, &dummy);
93            tree_bit
94        }
95        Node::Symbol(n1, SymbolKind::Concatenation, n2) => {
96            let t1 = handle_node(grammar, &n1.to_owned(), &cur_root, terminals);
97            let t2 = handle_node(grammar, &n2.to_owned(), &t1, terminals);
98            t1
99        }
100        Node::Symbol(n1, SymbolKind::Alternation, n2) => {
101            let root = FSMNode::new_null(Some(cur_root));
102            let t1 = handle_node(grammar, &n1.to_owned(), &root, terminals);
103            let t2 = handle_node(grammar, &n2.to_owned(), &root, terminals);
104            let child = FSMNode::new_null(None);
105            debug_println!("Alternation dummy child: {}", child.borrow().short_id());
106            FSMNode::add_child_to_all_leaves(&root, &child);
107            debug_println!("Finished alternation:");
108            root.borrow().dbg();
109            root
110        }
111        Node::Group(node) => handle_node(grammar, node, cur_root, terminals),
112        Node::Repeat(node) => {
113            // need to guarantee this is a null so search_rec won't prematurely stop, e.g. when
114            // cur_root is a Keyword
115            let dummy_parent = FSMNode::new_null(Some(&cur_root));
116            let subroot = handle_node(grammar, &node, &dummy_parent, terminals);
117
118            let dummy = FSMNode::new_null(None);
119            debug_println!("Repeat dummy child: {}", dummy.borrow().short_id());
120            FSMNode::add_child_to_all_leaves(&subroot, &dummy);
121            // must have the option to skip it entirely
122            FSMNode::add_child_cycle_safe(&cur_root, &dummy);
123
124            FSMNode::add_child_to_all_leaves(&subroot, &dummy_parent);
125            dummy_parent
126        }
127        _ => {
128            eprintln!("Unimplemented: {cur_node:?}");
129            todo!()
130        }
131    };
132    ret
133}
134
135fn find_terminal<'a>(grammer: &'a Grammar, name: &'a str) -> Option<&'a Expression> {
136    grammer.expressions.iter().find(|expr| expr.lhs == name)
137}
138
139pub fn create_graph_from_ebnf(ebnf: &str) -> Result<FSMRc<FSMLock<FSMNode>>, String> {
140    match ebnf::get_grammar(ebnf) {
141        Ok(grammar) => {
142            let root = FSMNode::new_null(None);
143            let root_node = grammar.expressions.get(0).expect("Empty BNF!");
144            let mut terminals = HashMap::new();
145            handle_node(
146                &grammar,
147                &Node::Terminal(root_node.lhs.to_owned()),
148                &root,
149                &mut terminals,
150            );
151            // sanity op, is_done() won't cancel preemptively
152            FSMNode::add_child_to_all_leaves(&root, &FSMNode::new_null(None));
153            FSMNode::minify(&root);
154            FSMNode::set_userdef_links(&root);
155            debug_println!("Total node cnt: {}", FSMNode::node_cnt(&root));
156            // for (name, term) in terminals.iter() {
157            //     println!("Term {}", name);
158            //     term.0.borrow().dbg();
159            // }
160            Ok(root)
161        }
162        Err(err) => Err(err.to_string()),
163    }
164}