package com.sleekbyte.tailor.utils; import org.antlr.v4.runtime.ParserRuleContext; import org.antlr.v4.runtime.Token; import org.antlr.v4.runtime.tree.ParseTree; import org.antlr.v4.runtime.tree.TerminalNodeImpl; /** * Utils for traversing Parse Trees. */ public final class ParseTreeUtil { /** * Return parent `nval` levels above ctx. * * @param ctx Child node * @param nval 'n' value, number of levels to go up the tree * @return Parent node or null if parent does not exist */ public static ParserRuleContext getNthParent(ParserRuleContext ctx, int nval) { if (ctx == null) { return null; } while (nval != 0) { nval--; ctx = ctx.getParent(); if (ctx == null) { return null; } } return ctx; } /** * Returns node's index with in its parent's child array. * * @param node A child node * @return Node's index or -1 if node is null or doesn't have a parent */ public static int getNodeIndex(ParseTree node) { if (node == null || node.getParent() == null) { return -1; } ParseTree parent = node.getParent(); for (int i = 0; i < parent.getChildCount(); i++) { if (parent.getChild(i) == node) { return i; } } return -1; } /** * Gets left sibling of a parse tree node. * * @param ctx A node * @return Left sibling of a node, or null if no sibling is found */ public static ParseTree getLeftSibling(ParseTree ctx) { int index = ParseTreeUtil.getNodeIndex(ctx); if (index < 1) { return null; } return ctx.getParent().getChild(index - 1); } /** * Gets right sibling of a parse tree node. * * @param ctx A node * @return Right sibling of a node, or null if no sibling is found */ public static ParseTree getRightSibling(ParseTree ctx) { int index = ParseTreeUtil.getNodeIndex(ctx); ParseTree parent = ctx.getParent(); if (index < 0 || index >= parent.getChildCount() - 1) { return null; } return parent.getChild(index + 1); } /** * Gets last child of a parse tree node. * * @param ctx A node * @return Last child of a node, or null if node has no children */ public static ParseTree getLastChild(ParseTree ctx) { if (ctx.getChildCount() == 0) { return null; } return ctx.getChild(ctx.getChildCount() - 1); } /** * Return node situated on the left of the input node (does not have to be at the same level as the current node). * * @param ctx A node * @return The left node */ public static ParseTree getLeftNode(ParseTree ctx) { while (true) { if (ctx == null) { return null; } ParseTree left = getLeftSibling(ctx); if (left != null) { return left; } ctx = ctx.getParent(); } } /** * Return node situated on the right of the input node (does not have to be at the level as the current node). * * @param ctx A node * @return The right node */ public static ParseTree getRightNode(ParseTree ctx) { while (true) { if (ctx == null) { return null; } ParseTree right = getRightSibling(ctx); if (right != null) { return right; } ctx = ctx.getParent(); } } /** * Returns the starting token of the construct represented by node. * * @param node A node * @return Start token */ public static Token getStartTokenForNode(ParseTree node) { if (node instanceof TerminalNodeImpl) { return ((TerminalNodeImpl) node).getSymbol(); } else { return ((ParserRuleContext) node).getStart(); } } /** * Returns the last token of the construct represented by node. * * @param node A node * @return Stop token */ public static Token getStopTokenForNode(ParseTree node) { if (node instanceof TerminalNodeImpl) { return ((TerminalNodeImpl) node).getSymbol(); } else { return ((ParserRuleContext) node).getStop(); } } }