package pl.edu.amu.wmi.daut.re; import java.util.Stack; import java.util.Map; import java.util.HashMap; import java.util.List; import java.util.ArrayList; import pl.edu.amu.wmi.daut.base.AutomatonSpecification; /** * Klasa z pomocniczymi funkcjami operującymi na wyrażeniach regularnych. */ public class RegexpUtilities { protected RegexpUtilities() { throw new UnsupportedOperationException(); } /** * Metoda, która z drzewa operatorów robi automat. */ public static AutomatonSpecification createAutomatonFromOperatorTree(RegexpOperatorTree tree) { //przejdź przez drzewo stanów metodą post-order, przy pomocy dwóch stosów. Stack<RegexpOperatorTree> child = new Stack<RegexpOperatorTree>(); Stack<RegexpOperatorTree> parent = new Stack<RegexpOperatorTree>(); child.push(tree); while (!child.empty()) { RegexpOperatorTree current = child.peek(); parent.push(current); child.pop(); for (RegexpOperatorTree subTree : current.getSubtrees()) child.push(subTree); } //na stosie "parent" mamy teraz wierzchołki w porządku post-order! //w porządku post-order chodzi o to, że zawsze zaczynamy od nieodwiedzonych liści //i idziemy powoli w kierunku korzenia drzewa. //utwórz mapę poddrzew na automaty przez nich utworzone. Map<RegexpOperatorTree, AutomatonSpecification> map = new HashMap<RegexpOperatorTree, AutomatonSpecification>(); while (!parent.empty()) { RegexpOperatorTree current = parent.peek(); //utwórz listę automatów utworzonych przez synów wierzchołka. List<AutomatonSpecification> arguments = new ArrayList<AutomatonSpecification>(); for (RegexpOperatorTree subTree : current.getSubtrees()) { //nie będzie tutaj odwołania do nieistniejących kluczy ze //wzgl. na charakter porządku post-order. jeśli wystąpi tutaj //exception, to znaczy, że źle zaimplementowaliśmy coś wcześniej. AutomatonSpecification subTreeAutomaton = map.get(subTree); arguments.add(subTreeAutomaton); } //utwórz automat, którego argumentami są automaty wszystkich synów. AutomatonSpecification currentAutomaton = current.getRoot().createAutomaton( arguments); //zapamiętaj automat dla danego wierzchołka. ponieważ liście się //wykonają "najpierw", to nadchodzący po tym rodzice tych liści //będą mieli pełną informację o automatach utworzonych przez //swoich synów... map.put(current, currentAutomaton); parent.pop(); //usunęliśmy właśnie wierzchołek-korzeń - zostaliśmy z pustym stosem, //możemy zwrócić automat. if (parent.empty()) return currentAutomaton; } throw new IllegalStateException(); } }