nfa.rs 8.34 KB
use std::rc::Rc;
use std::cell::{Ref, RefMut, RefCell};
use crate::earley_parse::{ASTNode, Terminal, NonTerminal};
use crate::types::Elem;
use crate::types::Elem::{*};

pub enum Type {
    Accept,
    Split,
    Single,
}

pub struct State {
    t: Type,
    key: Elem,
    out1: Link,
    out2: Link,
}

pub type Link = Option<Rc<RefCell<State>>>;

impl State {
    pub fn new(key: Elem, t: Type) -> Rc<RefCell<Self>> {
        Rc::new(RefCell::new(
            State {t: t, key: key, out1: None, out2: None}))
    }
    
    pub fn add(&mut self, next: Link) {
        match self.out1 {
            | None => self.out1 = next.clone(),
            | Some(_) => {
                match self.out2 {
                    | None => self.out2 = next.clone(),
                    | Some(_) => panic!("max capacity"),
                }
            }
        }
    }

    pub fn show(&self, space: &str) {
        println!("{}{}", space, self.key.to_str());
        match self.t {
            | Type::Split => 
                {
                    self.out1.as_ref().map(|out| 
                        {out.borrow_mut().show(&(space.to_owned() + "   "))});
                    self.out2.as_ref().map(|out| 
                        {out.borrow_mut().show(&(space.to_owned() + "   "))});
                },
            | Type::Single => {self.out1.as_ref().map(|out| 
                {out.borrow_mut().show(&(space.to_owned() + "   "))});},
            | Type::Accept => (),
        }
    }

    pub fn next(&self){
        // match self.t {
        //     | Type::Single => self.out1.as_ref().map(|out1|
        //                         out1).cloned(),
        //     | Type::Split => self.out1.as_ref().map(|out1|
        //                             out1).cloned(),
        //     | Type::Accept => None
        // }
        match self.t {
            | Type::Single => println!("single"),
            | Type::Split => println!("split"),
            | Type::Accept => println!("this is an accept, no next")
        }
    }
}

pub struct Fragment {
    head: Link,
    outlst: Option<Vec<Link>>,
}

impl Fragment {
    fn make_outlst(&mut self) {
        match self.outlst {
            | Some (_) => (),
            | None => 
                {
                    let mut newlst = Vec::new();
                    newlst.push(self.head.clone());
                    self.outlst = Some(newlst);
                }
        }
    }
    fn new_from_type(key: Elem, t: Type) -> Self {
        Fragment {head: Some(State::new(key, t)), 
            outlst: None}
    }

    pub fn new(key: Elem) -> Self {
        let mut new_frag = Self::new_from_type(key, Type::Single);
        new_frag.make_outlst();
        new_frag
    }

    pub fn new_split(key: Elem) -> Self {
        let mut new_frag = Self::new_from_type(key, Type::Split);
        new_frag.make_outlst();
        new_frag
    }

    pub fn new_accept() -> Self {
        let mut new_frag = Self::new_from_type(Elem::Accept, Type::Accept);
        new_frag.make_outlst();
        new_frag
    }

    pub fn head(&self) -> &Link {
        &self.head
    }

    pub fn next(&self) -> Option<Ref<Link>> {
       self.head.as_ref().map(|node| {
            Ref::map(node.borrow(), |node| &node.out1)
       })
    }

    fn outlst_to_state(&mut self, state: Link) {
        // connect each node in outlst to head of fragment
        match self.outlst.take() {
            | Some(outlst) => 
                {
                    for mut out in outlst.clone() {
                        match out.take() {
                            |Some(old_out) =>
                                {old_out.borrow_mut().add(state.clone());
                                out = Some(old_out);},
                            | None => (),
                        }
                    }
                    self.outlst = Some(outlst);
                },
            | None => panic!("empty outlist in outlst_to_state")
        }
    }

    pub fn concatenate(&mut self, mut frag: Fragment) {
        let outlst2 = frag.outlst.clone();
        self.outlst_to_state(frag.head);
        self.outlst = frag.outlst;
    }

    pub fn alternate(&mut self, mut frag1: Fragment, mut frag2: Fragment) {
        // append outlsts before fragments go out of scope
        let mut outlst_final = frag1.outlst.clone();
        match outlst_final.take() {
            | Some(mut outlst) => 
                {
                    outlst.append(&mut frag2.outlst.unwrap());
                    outlst_final = Some(outlst);
                },
            | None => panic!("empty outlist in alternate")
        }
        // concatenate current fragment to both fragments
        self.outlst_to_state(frag1.head);
        self.outlst_to_state(frag2.head);
        self.outlst = outlst_final;
    }

    pub fn qm(&mut self, mut frag: Fragment) {
        // question mark
        let mut outlst_final = self.outlst.clone();
        match outlst_final.take() {
            | Some(mut outlst) => 
                {
                    outlst.append(&mut frag.outlst.unwrap());
                    outlst_final = Some(outlst);
                },
            | None => panic!("empty outlist in qm")
        }
        self.outlst_to_state(frag.head);
        self.outlst = outlst_final;
    }

    pub fn star(&mut self, mut frag: Fragment) {
        frag.outlst_to_state(self.head.clone());
        self.outlst_to_state(frag.head);
    }

    pub fn plus(&mut self, mut frag: Fragment) {
        let outlst_final = frag.outlst.clone();
        frag.outlst_to_state(self.head.clone());
        self.outlst_to_state(frag.head);
        self.outlst = outlst_final;
    }

    pub fn show(&self) {
        match &self.head {
            | Some(node) => node.borrow_mut().show(""),
            | None => (),
        }
    }
}

impl ASTNode {
    pub fn to_Fragment(&self) -> Fragment {
        match self {
            | Self::Terminal(c) => {
                if *c != '.' {
                    Fragment::new(Elem::Unique(*c))

                }
                else {
                    Fragment::new(Elem::Dot)
                }
            },
            | Self::NonTerminal {sym, children} => 
                match *sym {
                    | "CONCAT" => 
                        {
                            let mut frag_A = children[0].to_Fragment();
                            frag_A.concatenate(children[1].to_Fragment());
                            frag_A
                        },
                    | "UNION" => 
                        {   
                            let mut new_frag = Fragment::new_split(Elem::Epsilon);
                            new_frag.alternate(children[0].to_Fragment(), 
                                               children[2].to_Fragment());
                            new_frag
                        },
                    | "Q" => 
                        {
                            let mut new_frag = Fragment::new_split(Elem::Epsilon);
                            let frag = children[0].to_Fragment();

                            new_frag.qm(frag);
                            new_frag
                        },
                    | "STAR" =>
                        {
                            let mut new_frag = Fragment::new_split(Elem::Epsilon);
                            let frag = children[0].to_Fragment();

                            new_frag.star(frag);
                            new_frag
                        },
                    | "PLUS" =>
                        {
                            let frag = Fragment::new_split(Elem::Epsilon);
                            let mut new_frag = children[0].to_Fragment();

                            new_frag.plus(frag);
                            new_frag
                        },
                    | "DIGIT" => Fragment::new(Elem::Digit),
                    | "NON-DIGIT" => Fragment::new(Elem::NonDigit),
                    | "LETTER" => Fragment::new(Elem::Letter),
                    | "NON-LETTER" => Fragment::new(Elem::NonLetter),
                    | "WHITESPACE" => Fragment::new(Elem::Whitespace),
                    | "NON-WHITESPACE" => Fragment::new(Elem::NonWhitespace),
                    | "SPECIAL" | "EXPR" => children[1].to_Fragment(),
                    | "LITERAL-DOT" => Fragment::new(Elem::Unique('.')),
                    | _ => 
                        {
                            unimplemented!();
                        }
                }
        }
    }
}