/* *************************************************************************************** * Copyright (C) 2006 EsperTech, Inc. All rights reserved. * * http://www.espertech.com/esper * * http://www.espertech.com * * ---------------------------------------------------------------------------------- * * The software in this package is published under the terms of the GPL license * * a copy of which has been included with this distribution in the license.txt file. * *************************************************************************************** */ package com.espertech.esper.epl.parse; import com.espertech.esper.epl.generated.EsperEPL2GrammarParser; import org.antlr.v4.runtime.CommonTokenStream; 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.RuleNode; import org.antlr.v4.runtime.tree.TerminalNode; import org.antlr.v4.runtime.tree.Tree; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.PrintWriter; import java.io.StringWriter; import java.util.*; /** * Utility class for AST node handling. */ public class ASTUtil { private static Logger log = LoggerFactory.getLogger(ASTUtil.class); private final static String PROPERTY_ENABLED_AST_DUMP = "ENABLE_AST_DUMP"; public static List<String> getIdentList(EsperEPL2GrammarParser.ColumnListContext ctx) { if (ctx == null || ctx.isEmpty()) { return Collections.emptyList(); } List<TerminalNode> idents = ctx.IDENT(); List<String> parameters = new ArrayList<String>(idents.size()); for (TerminalNode ident : idents) { parameters.add(ident.getText()); } return parameters; } public static boolean isTerminatedOfType(Tree child, int tokenType) { if (!(child instanceof TerminalNode)) { return false; } TerminalNode termNode = (TerminalNode) child; return termNode.getSymbol().getType() == tokenType; } public static int getRuleIndexIfProvided(ParseTree tree) { if (!(tree instanceof RuleNode)) { return -1; } RuleNode ruleNode = (RuleNode) tree; return ruleNode.getRuleContext().getRuleIndex(); } public static int getAssertTerminatedTokenType(ParseTree child) { if (!(child instanceof TerminalNode)) { throw ASTWalkException.from("Unexpected exception walking AST, expected terminal node", child.getText()); } TerminalNode term = (TerminalNode) child; return term.getSymbol().getType(); } public static String printNode(Tree node) { StringWriter buf = new StringWriter(); PrintWriter writer = new PrintWriter(buf); ASTUtil.dumpAST(writer, node, 0); return buf.toString(); } public static boolean isRecursiveParentRule(ParserRuleContext ctx, Set<Integer> rulesIds) { ParserRuleContext parent = ctx.getParent(); if (parent == null) { return false; } return rulesIds.contains(parent.getRuleIndex()) || isRecursiveParentRule(parent, rulesIds); } /** * Dump the AST node to system.out. * * @param ast to dump */ public static void dumpAST(Tree ast) { if (System.getProperty(PROPERTY_ENABLED_AST_DUMP) != null) { StringWriter writer = new StringWriter(); PrintWriter printer = new PrintWriter(writer); renderNode(new char[0], ast, printer); dumpAST(printer, ast, 2); log.info(".dumpAST ANTLR Tree dump follows...\n" + writer.toString()); } } public static void dumpAST(PrintWriter printer, Tree ast, int ident) { char[] identChars = new char[ident]; Arrays.fill(identChars, ' '); if (ast == null) { renderNode(identChars, null, printer); return; } for (int i = 0; i < ast.getChildCount(); i++) { Tree node = ast.getChild(i); if (node == null) { throw new NullPointerException("Null AST node"); } renderNode(identChars, node, printer); dumpAST(printer, node, ident + 2); } } /** * Print the token stream to the logger. * * @param tokens to print */ public static void printTokens(CommonTokenStream tokens) { if (log.isDebugEnabled()) { List tokenList = tokens.getTokens(); StringWriter writer = new StringWriter(); PrintWriter printer = new PrintWriter(writer); for (int i = 0; i < tokens.size(); i++) { Token t = (Token) tokenList.get(i); String text = t.getText(); if (text.trim().length() == 0) { printer.print("'" + text + "'"); } else { printer.print(text); } printer.print('['); printer.print(t.getType()); printer.print(']'); printer.print(" "); } printer.println(); log.debug("Tokens: " + writer.toString()); } } private static void renderNode(char[] ident, Tree node, PrintWriter printer) { printer.print(ident); if (node == null) { printer.print("NULL NODE"); } else { if (node instanceof ParserRuleContext) { ParserRuleContext ctx = (ParserRuleContext) node; int ruleIndex = ctx.getRuleIndex(); String ruleName = EsperEPL2GrammarParser.ruleNames[ruleIndex]; printer.print(ruleName); } else { TerminalNode terminal = (TerminalNode) node; printer.print(terminal.getSymbol().getText()); printer.print(" ["); printer.print(terminal.getSymbol().getType()); printer.print("]"); } if (node instanceof ParseTree) { ParseTree parseTree = (ParseTree) node; if (parseTree.getText() == null) { printer.print(" (null value in text)"); } else if (parseTree.getText().contains("\\")) { int count = 0; for (int i = 0; i < parseTree.getText().length(); i++) { if (parseTree.getText().charAt(i) == '\\') { count++; } } printer.print(" (" + count + " backlashes)"); } } } printer.println(); } /** * Escape all unescape dot characters in the text (identifier only) passed in. * * @param identifierToEscape text to escape * @return text where dots are escaped */ protected static String escapeDot(String identifierToEscape) { int indexof = identifierToEscape.indexOf("."); if (indexof == -1) { return identifierToEscape; } StringBuilder builder = new StringBuilder(); for (int i = 0; i < identifierToEscape.length(); i++) { char c = identifierToEscape.charAt(i); if (c != '.') { builder.append(c); continue; } if (i > 0) { if (identifierToEscape.charAt(i - 1) == '\\') { builder.append('.'); continue; } } builder.append('\\'); builder.append('.'); } return builder.toString(); } /** * Find the index of an unescaped dot (.) character, or return -1 if none found. * * @param identifier text to find an un-escaped dot character * @return index of first unescaped dot */ public static int unescapedIndexOfDot(String identifier) { int indexof = identifier.indexOf("."); if (indexof == -1) { return -1; } for (int i = 0; i < identifier.length(); i++) { char c = identifier.charAt(i); if (c != '.') { continue; } if (i > 0) { if (identifier.charAt(i - 1) == '\\') { continue; } } return i; } return -1; } /** * Un-Escape all escaped dot characters in the text (identifier only) passed in. * * @param identifierToUnescape text to un-escape * @return string */ public static String unescapeDot(String identifierToUnescape) { int indexof = identifierToUnescape.indexOf("."); if (indexof == -1) { return identifierToUnescape; } indexof = identifierToUnescape.indexOf("\\"); if (indexof == -1) { return identifierToUnescape; } StringBuilder builder = new StringBuilder(); int index = -1; int max = identifierToUnescape.length() - 1; do { index++; char c = identifierToUnescape.charAt(index); if (c != '\\') { builder.append(c); continue; } if (index < identifierToUnescape.length() - 1) { if (identifierToUnescape.charAt(index + 1) == '.') { builder.append('.'); index++; } } } while (index < max); return builder.toString(); } public static String getPropertyName(EsperEPL2GrammarParser.EventPropertyContext ctx, int startNode) { StringBuilder buf = new StringBuilder(); for (int i = startNode; i < ctx.getChildCount(); i++) { ParseTree tree = ctx.getChild(i); buf.append(tree.getText()); } return buf.toString(); } public static String unescapeBacktick(String text) { int indexof = text.indexOf("`"); if (indexof == -1) { return text; } StringBuilder builder = new StringBuilder(); int index = -1; int max = text.length() - 1; boolean skip = false; do { index++; char c = text.charAt(index); if (c == '`') { skip = !skip; } else { builder.append(c); } } while (index < max); return builder.toString(); } public static String unescapeClassIdent(EsperEPL2GrammarParser.ClassIdentifierContext classIdentCtx) { return unescapeEscapableStr(classIdentCtx.escapableStr(), "."); } public static String unescapeSlashIdentifier(EsperEPL2GrammarParser.SlashIdentifierContext ctx) { String name = unescapeEscapableStr(ctx.escapableStr(), "/"); if (ctx.d != null) { name = "/" + name; } return name; } private static String unescapeEscapableStr(List<EsperEPL2GrammarParser.EscapableStrContext> ctxs, String delimiterConst) { if (ctxs.size() == 1) { return unescapeBacktick(unescapeDot(ctxs.get(0).getText())); } StringWriter writer = new StringWriter(); String delimiter = ""; for (EsperEPL2GrammarParser.EscapableStrContext ctx : ctxs) { writer.append(delimiter); writer.append(unescapeBacktick(unescapeDot(ctx.getText()))); delimiter = delimiterConst; } return writer.toString(); } }