--- /dev/null 2016-01-22 18:25:55.206427233 -0800 +++ new/src/java.base/share/classes/java/util/regex/PrintPattern.java 2016-03-10 15:34:04.484535766 -0800 @@ -0,0 +1,195 @@ +package java.util.regex; + +import java.util.HashMap; +import java.util.regex.Pattern.CharPredicate; +import java.util.regex.CharPredicates; +import static java.util.regex.ASCII.*; + +/** + * A utility class to print out the pattern node tree. + */ + +class PrintPattern { + + private static HashMap ids = new HashMap<>(); + + private static void print(Pattern.Node node, String text, int depth) { + if (!ids.containsKey(node)) + ids.put(node, ids.size()); + Print("%6d:%" + (depth==0? "": depth<<1) + "s<%s>", ids.get(node), "", text); + if (ids.containsKey(node.next)) + Print(" (=>%d)", ids.get(node.next)); + Print("%n"); + } + + private static void print(String s, int depth) { + Print(" %" + (depth==0?"":depth<<1) + "s<%s>%n", "", s); + } + + private static void Print(String fmt, Object ... args) { + System.err.printf(fmt, args); + } + + private static String toStringCPS(int[] cps) { + StringBuilder sb = new StringBuilder(cps.length); + for (int cp : cps) + sb.append(toStringCP(cp)); + return sb.toString(); + } + + private static String toStringCP(int cp) { + return (isPrint(cp) ? "" + (char)cp + : "\\u" + Integer.toString(cp, 16)); + } + + private static String toStringRange(int min, int max) { + if (max == Pattern.MAX_REPS) { + if (min == 0) + return " * "; + else if (min == 1) + return " + "; + return "{" + min + ", max}"; + } + return "{" + min + ", " + max + "}"; + } + + private static String toStringCtype(int type) { + switch(type) { + case UPPER: return "ASCII.UPPER"; + case LOWER: return "ASCII.LOWER"; + case DIGIT: return "ASCII.DIGIT"; + case SPACE: return "ASCII.SPACE"; + case PUNCT: return "ASCII.PUNCT"; + case CNTRL: return "ASCII.CNTRL"; + case BLANK: return "ASCII.BLANK"; + case UNDER: return "ASCII.UNDER"; + case ASCII: return "ASCII.ASCII"; + case ALPHA: return "ASCII.ALPHA"; + case ALNUM: return "ASCII.ALNUM"; + case GRAPH: return "ASCII.GRAPH"; + case WORD: return "ASCII.WORD"; + case XDIGIT: return "ASCII.XDIGIT"; + default: return "ASCII ?"; + } + } + + private static String toString(Pattern.Node node) { + String name = node.getClass().getName(); + return name.substring(name.lastIndexOf('$') + 1); + } + + static HashMap pmap; + static { + pmap = new HashMap<>(); + pmap.put(Pattern.ALL, "All"); + pmap.put(Pattern.DOT, "Dot"); + pmap.put(Pattern.UNIXDOT, "UnixDot"); + pmap.put(Pattern.VertWS, "VertWS"); + pmap.put(Pattern.HorizWS, "HorizWS"); + + pmap.put(CharPredicates.ASCII_DIGIT, "ASCII.DIGIT"); + pmap.put(CharPredicates.ASCII_WORD, "ASCII.WORD"); + pmap.put(CharPredicates.ASCII_SPACE, "ASCII.SPACE"); + } + + private static void walk(Pattern.Node node, int depth) { + depth++; + while(node != null) { + String name = toString(node); + String str; + if (node instanceof Pattern.Prolog) { + print(node, name, depth); + // print the loop here + Pattern.Loop loop = ((Pattern.Prolog)node).loop; + name = toString(loop); + str = name + " " + toStringRange(loop.cmin, loop.cmax); + print(loop, str, depth); + walk(loop.body, depth); + print("/" + name, depth); + node = loop; + } else if (node instanceof Pattern.Loop) { + return; // stop here, body.next -> loop + } else if (node instanceof Pattern.Curly) { + Pattern.Curly c = (Pattern.Curly)node; + str = "Curly " + c.type + " " + toStringRange(c.cmin, c.cmax); + print(node, str, depth); + walk(c.atom, depth); + print("/Curly", depth); + } else if (node instanceof Pattern.GroupCurly) { + Pattern.GroupCurly gc = (Pattern.GroupCurly)node; + str = "GroupCurly " + gc.groupIndex / 2 + + ", " + gc.type + " " + toStringRange(gc.cmin, gc.cmax); + print(node, str, depth); + walk(gc.atom, depth); + print("/GroupCurly", depth); + } else if (node instanceof Pattern.GroupHead) { + Pattern.GroupHead head = (Pattern.GroupHead)node; + Pattern.GroupTail tail = head.tail; + print(head, "Group.head " + (tail.groupIndex / 2), depth); + walk(head.next, depth); + print(tail, "/Group.tail " + (tail.groupIndex / 2), depth); + node = tail; + } else if (node instanceof Pattern.GroupTail) { + return; // stopper + } else if (node instanceof Pattern.Ques) { + print(node, "Ques " + ((Pattern.Ques)node).type, depth); + walk(((Pattern.Ques)node).atom, depth); + print("/Ques", depth); + } else if (node instanceof Pattern.Branch) { + Pattern.Branch b = (Pattern.Branch)node; + print(b, name, depth); + int i = 0; + while (true) { + if (b.atoms[i] != null) { + walk(b.atoms[i], depth); + } else { + print(" (accepted)", depth); + } + if (++i == b.size) + break; + print("-branch.separator-", depth); + } + node = b.conn; + print(node, "/Branch", depth); + } else if (node instanceof Pattern.BranchConn) { + return; + } else if (node instanceof Pattern.CharProperty) { + str = pmap.get(((Pattern.CharProperty)node).predicate); + if (str == null) + str = toString(node); + else + str = "Single \"" + str + "\""; + print(node, str, depth); + } else if (node instanceof Pattern.SliceNode) { + str = name + " \"" + + toStringCPS(((Pattern.SliceNode)node).buffer) + "\""; + print(node, str, depth); + } else if (node instanceof Pattern.CharPropertyGreedy) { + Pattern.CharPropertyGreedy gcp = (Pattern.CharPropertyGreedy)node; + String pstr = pmap.get(gcp.predicate); + if (pstr == null) + pstr = gcp.predicate.toString(); + else + pstr = "Single \"" + pstr + "\""; + str = name + " " + pstr + ((gcp.cmin == 0) ? "*" : "+"); + print(node, str, depth); + } else if (node instanceof Pattern.BackRef) { + str = "GroupBackRef " + ((Pattern.BackRef)node).groupIndex / 2; + print(node, str, depth); + } else if (node instanceof Pattern.LastNode) { + print(node, "END", depth); + } else if (node == Pattern.accept) { + return; + } else { + print(node, name, depth); + } + node = node.next; + } + } + + public static void main(String[] args) { + Pattern p = Pattern.compile(args[0]); + System.out.println(" Pattern: " + p); + walk(p.root, 0); + } +}