package org.batfish.grammar; import java.util.Arrays; import java.util.List; import org.antlr.v4.runtime.Lexer; import org.antlr.v4.runtime.ParserRuleContext; import org.antlr.v4.runtime.RecognitionException; import org.antlr.v4.runtime.Recognizer; import org.antlr.v4.runtime.Token; import org.batfish.common.DebugBatfishException; import org.batfish.common.util.CommonUtil; public class BatfishParserErrorListener extends BatfishGrammarErrorListener { public BatfishParserErrorListener(String grammarName, BatfishCombinedParser<?, ?> parser) { super(grammarName, parser); } private String printToken(Token token) { int modeAsInt = _combinedParser.getTokenMode(token); String mode = _combinedParser.getLexer().getModeNames()[modeAsInt]; String rawTokenText = token.getText(); String tokenText = CommonUtil.escape(rawTokenText); int tokenType = token.getType(); String channel = token.getChannel() == Lexer.HIDDEN ? "(HIDDEN) " : ""; String tokenName; int line = token.getLine(); int col = token.getCharPositionInLine(); if (tokenType == -1) { tokenName = "EOF"; } else { tokenName = _combinedParser.getParser().getVocabulary() .getSymbolicName(tokenType); tokenText = "'" + tokenText + "'"; } return " line " + line + ":" + col + " " + channel + " " + tokenName + ":" + tokenText + " <== mode:" + mode; } public void syntaxError(ParserRuleContext ctx, Object offendingSymbol, int line, int charPositionInLine, String msg) { if (_syntaxErrorHandler != null && _syntaxErrorHandler.handle(ctx, offendingSymbol, line, charPositionInLine, msg)) { return; } BatfishParser parser = _combinedParser.getParser(); List<String> ruleNames = Arrays.asList(parser.getRuleNames()); String ruleStack = ctx.toString(ruleNames); List<Token> tokens = _combinedParser.getTokens().getTokens(); int startTokenIndex = parser.getInputStream().index(); int lookbackIndex = Math.max(0, startTokenIndex - _settings.getMaxParserContextTokens()); int endTokenIndex = tokens.size(); StringBuilder sb = new StringBuilder(); sb.append("parser: " + _grammarName + ": line " + line + ":" + charPositionInLine + ": " + msg + "\n"); Token offendingToken = (Token) offendingSymbol; String offendingTokenText = printToken(offendingToken); sb.append("Offending Token: " + offendingTokenText + "\n"); sb.append("Error parsing top (leftmost) parser rule in stack: '" + ruleStack + "'.\n"); String ctxParseTree = ParseTreePrettyPrinter.print(ctx, _combinedParser); sb.append("Parse tree of current rule:\n" + ctxParseTree + "\n"); sb.append("Unconsumed tokens:\n"); for (int i = startTokenIndex; i < endTokenIndex; i++) { Token token = tokens.get(i); String tokenText = printToken(token); sb.append(tokenText + "\n"); } if (lookbackIndex < startTokenIndex) { int numLookbackTokens = startTokenIndex - lookbackIndex; sb.append("Previous " + numLookbackTokens + " tokens:\n"); for (int i = lookbackIndex; i < startTokenIndex; i++) { Token lookbackToken = tokens.get(i); String tokenText = printToken(lookbackToken); sb.append(tokenText + "\n"); } } if (offendingToken.getType() == Token.EOF) { sb.append("Lexer mode at EOF: " + _combinedParser.getLexer().getMode() + "\n"); } String stateInfo = parser.getStateInfo(); if (stateInfo != null) { sb.append("Parser state info:\n" + stateInfo + "\n"); } // collect context from text String text = _combinedParser.getInput(); String[] lines = text.split("\n", -1); int errorLineIndex = offendingToken.getLine() - 1; int errorContextStartLine = Math .max(errorLineIndex - _settings.getMaxParserContextLines(), 0); int errorContextEndLine = Math.min( errorLineIndex + _settings.getMaxParserContextLines(), lines.length - 1); sb.append("Error context lines:\n"); for (int i = errorContextStartLine; i < errorLineIndex; i++) { sb.append(String.format("%-11s%s\n", " " + (i + 1) + ":", lines[i])); } sb.append(String.format("%-11s%s\n", ">>>" + (errorLineIndex + 1) + ":", lines[errorLineIndex])); for (int i = errorLineIndex + 1; i <= errorContextEndLine; i++) { sb.append(String.format("%-11s%s\n", " " + (i + 1) + ":", lines[i])); } String error = sb.toString(); if (_settings.getThrowOnParserError()) { throw new DebugBatfishException("\n" + error); } else { _combinedParser.getErrors().add(error); } } @Override public void syntaxError(Recognizer<?, ?> recognizer, Object offendingSymbol, int line, int charPositionInLine, String msg, RecognitionException e) { BatfishParser parser = _combinedParser.getParser(); syntaxError(parser.getContext(), offendingSymbol, line, charPositionInLine, msg); } }