Java tutorial
/* * Copyright (c) 2012-2017 The ANTLR Project. All rights reserved. * Use of this file is governed by the BSD 3-clause license that * can be found in the LICENSE.txt file in the project root. */ package org.antlr.v4.runtime.tree; import org.antlr.v4.runtime.CommonToken; import org.antlr.v4.runtime.Parser; import org.antlr.v4.runtime.ParserRuleContext; import org.antlr.v4.runtime.RuleContext; import org.antlr.v4.runtime.Token; import org.antlr.v4.runtime.atn.ATN; import org.antlr.v4.runtime.misc.Interval; import org.antlr.v4.runtime.misc.Predicate; import org.antlr.v4.runtime.misc.Utils; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; /** A set of utility routines useful for all kinds of ANTLR trees. */ public class Trees { /** Print out a whole tree in LISP form. {@link #getNodeText} is used on the * node payloads to get the text for the nodes. Detect * parse trees and extract data appropriately. */ public static String toStringTree(Tree t) { return toStringTree(t, (List<String>) null); } /** Print out a whole tree in LISP form. {@link #getNodeText} is used on the * node payloads to get the text for the nodes. Detect * parse trees and extract data appropriately. */ public static String toStringTree(Tree t, Parser recog) { String[] ruleNames = recog != null ? recog.getRuleNames() : null; List<String> ruleNamesList = ruleNames != null ? Arrays.asList(ruleNames) : null; return toStringTree(t, ruleNamesList); } /** Print out a whole tree in LISP form. {@link #getNodeText} is used on the * node payloads to get the text for the nodes. */ public static String toStringTree(final Tree t, final List<String> ruleNames) { String s = Utils.escapeWhitespace(getNodeText(t, ruleNames), false); if (t.getChildCount() == 0) return s; StringBuilder buf = new StringBuilder(); buf.append("("); s = Utils.escapeWhitespace(getNodeText(t, ruleNames), false); buf.append(s); buf.append(' '); for (int i = 0; i < t.getChildCount(); i++) { if (i > 0) buf.append(' '); buf.append(toStringTree(t.getChild(i), ruleNames)); } buf.append(")"); return buf.toString(); } public static String getNodeText(Tree t, Parser recog) { String[] ruleNames = recog != null ? recog.getRuleNames() : null; List<String> ruleNamesList = ruleNames != null ? Arrays.asList(ruleNames) : null; return getNodeText(t, ruleNamesList); } public static String getNodeText(Tree t, List<String> ruleNames) { if (ruleNames != null) { if (t instanceof RuleContext) { int ruleIndex = ((RuleContext) t).getRuleContext().getRuleIndex(); String ruleName = ruleNames.get(ruleIndex); int altNumber = ((RuleContext) t).getAltNumber(); if (altNumber != ATN.INVALID_ALT_NUMBER) { return ruleName + ":" + altNumber; } return ruleName; } else if (t instanceof ErrorNode) { return t.toString(); } else if (t instanceof TerminalNode) { Token symbol = ((TerminalNode) t).getSymbol(); if (symbol != null) { String s = symbol.getText(); return s; } } } // no recog for rule names Object payload = t.getPayload(); if (payload instanceof Token) { return ((Token) payload).getText(); } return t.getPayload().toString(); } /** Return ordered list of all children of this node */ public static List<Tree> getChildren(Tree t) { List<Tree> kids = new ArrayList<Tree>(); for (int i = 0; i < t.getChildCount(); i++) { kids.add(t.getChild(i)); } return kids; } /** Return a list of all ancestors of this node. The first node of * list is the root and the last is the parent of this node. * * @since 4.5.1 */ public static List<? extends Tree> getAncestors(Tree t) { if (t.getParent() == null) return Collections.emptyList(); List<Tree> ancestors = new ArrayList<Tree>(); t = t.getParent(); while (t != null) { ancestors.add(0, t); // insert at start t = t.getParent(); } return ancestors; } /** Return true if t is u's parent or a node on path to root from u. * Use == not equals(). * * @since 4.5.1 */ public static boolean isAncestorOf(Tree t, Tree u) { if (t == null || u == null || t.getParent() == null) return false; Tree p = u.getParent(); while (p != null) { if (t == p) return true; p = p.getParent(); } return false; } public static Collection<ParseTree> findAllTokenNodes(ParseTree t, int ttype) { return findAllNodes(t, ttype, true); } public static Collection<ParseTree> findAllRuleNodes(ParseTree t, int ruleIndex) { return findAllNodes(t, ruleIndex, false); } public static List<ParseTree> findAllNodes(ParseTree t, int index, boolean findTokens) { List<ParseTree> nodes = new ArrayList<ParseTree>(); _findAllNodes(t, index, findTokens, nodes); return nodes; } public static void _findAllNodes(ParseTree t, int index, boolean findTokens, List<? super ParseTree> nodes) { // check this node (the root) first if (findTokens && t instanceof TerminalNode) { TerminalNode tnode = (TerminalNode) t; if (tnode.getSymbol().getType() == index) nodes.add(t); } else if (!findTokens && t instanceof ParserRuleContext) { ParserRuleContext ctx = (ParserRuleContext) t; if (ctx.getRuleIndex() == index) nodes.add(t); } // check children for (int i = 0; i < t.getChildCount(); i++) { _findAllNodes(t.getChild(i), index, findTokens, nodes); } } /** Get all descendents; includes t itself. * * @since 4.5.1 */ public static List<ParseTree> getDescendants(ParseTree t) { List<ParseTree> nodes = new ArrayList<ParseTree>(); nodes.add(t); int n = t.getChildCount(); for (int i = 0; i < n; i++) { nodes.addAll(getDescendants(t.getChild(i))); } return nodes; } /** @deprecated */ public static List<ParseTree> descendants(ParseTree t) { return getDescendants(t); } /** Find smallest subtree of t enclosing range startTokenIndex..stopTokenIndex * inclusively using postorder traversal. Recursive depth-first-search. * * @since 4.5.1 */ public static ParserRuleContext getRootOfSubtreeEnclosingRegion(ParseTree t, int startTokenIndex, // inclusive int stopTokenIndex) // inclusive { int n = t.getChildCount(); for (int i = 0; i < n; i++) { ParseTree child = t.getChild(i); ParserRuleContext r = getRootOfSubtreeEnclosingRegion(child, startTokenIndex, stopTokenIndex); if (r != null) return r; } if (t instanceof ParserRuleContext) { ParserRuleContext r = (ParserRuleContext) t; if (startTokenIndex >= r.getStart().getTokenIndex() && // is range fully contained in t? (r.getStop() == null || stopTokenIndex <= r.getStop().getTokenIndex())) { // note: r.getStop()==null likely implies that we bailed out of parser and there's nothing to the right return r; } } return null; } /** Replace any subtree siblings of root that are completely to left * or right of lookahead range with a CommonToken(Token.INVALID_TYPE,"...") * node. The source interval for t is not altered to suit smaller range! * * WARNING: destructive to t. * * @since 4.5.1 */ public static void stripChildrenOutOfRange(ParserRuleContext t, ParserRuleContext root, int startIndex, int stopIndex) { if (t == null) return; for (int i = 0; i < t.getChildCount(); i++) { ParseTree child = t.getChild(i); Interval range = child.getSourceInterval(); if (child instanceof ParserRuleContext && (range.b < startIndex || range.a > stopIndex)) { if (isAncestorOf(child, root)) { // replace only if subtree doesn't have displayed root CommonToken abbrev = new CommonToken(Token.INVALID_TYPE, "..."); t.children.set(i, new TerminalNodeImpl(abbrev)); } } } } /** Return first node satisfying the pred * * @since 4.5.1 */ public static Tree findNodeSuchThat(Tree t, Predicate<Tree> pred) { if (pred.test(t)) return t; if (t == null) return null; int n = t.getChildCount(); for (int i = 0; i < n; i++) { Tree u = findNodeSuchThat(t.getChild(i), pred); if (u != null) return u; } return null; } private Trees() { } }