1 /* 2 * Copyright (c) 2012-2019 The ANTLR Project. All rights reserved. 3 * Use of this file is governed by the BSD 3-clause license that 4 * can be found in the LICENSE.txt file in the project root. 5 * 6 */ 7 8 module antlr.v4.runtime.DiagnosticErrorListener; 9 10 import antlr.v4.runtime.BaseErrorListener; 11 import antlr.v4.runtime.InterfaceParser; 12 import antlr.v4.runtime.atn.ATNConfig; 13 import antlr.v4.runtime.atn.ATNConfigSet; 14 import antlr.v4.runtime.dfa.DFA; 15 import antlr.v4.runtime.misc.BitSet; 16 import antlr.v4.runtime.misc.Interval; 17 import std.conv; 18 import std.format; 19 20 /** 21 * This implementation of {@link ANTLRErrorListener} can be used to identify 22 * certain potential correctness and performance problems in grammars. "Reports" 23 * are made by calling {@link Parser#notifyErrorListeners} with the appropriate 24 * message. 25 * 26 * <ul> 27 * <li><b>Ambiguities</b>: These are cases where more than one path through the 28 * grammar can match the input.</li> 29 * <li><b>Weak context sensitivity</b>: These are cases where full-context 30 * prediction resolved an SLL conflict to a unique alternative which equaled the 31 * minimum alternative of the SLL conflict.</li> 32 * <li><b>Strong (forced) context sensitivity</b>: These are cases where the 33 * full-context prediction resolved an SLL conflict to a unique alternative, 34 * <em>and</em> the minimum alternative of the SLL conflict was found to not be 35 * a truly viable alternative. Two-stage parsing cannot be used for inputs where 36 * this situation occurs.</li> 37 * </ul> 38 * 39 * @author Sam Harwell 40 */ 41 class DiagnosticErrorListener(U, V) : BaseErrorListener!(U, V) 42 { 43 44 /** 45 * When {@code true}, only exactly known ambiguities are reported. 46 */ 47 protected bool exactOnly; 48 49 /** 50 * Initializes a new instance of {@link DiagnosticErrorListener} which only 51 * reports exact ambiguities. 52 */ 53 public this() 54 { 55 this(true); 56 } 57 58 /** 59 * Initializes a new instance of {@link DiagnosticErrorListener}, specifying 60 * whether all ambiguities or only exact ambiguities are reported. 61 * 62 * @param exactOnly {@code true} to report only exact ambiguities, otherwise 63 * {@code false} to report all ambiguities. 64 */ 65 public this(bool exactOnly) 66 { 67 this.exactOnly = exactOnly; 68 } 69 70 /** 71 * @uml 72 * @override 73 */ 74 public override void reportAmbiguity(InterfaceParser recognizer, DFA dfa, int startIndex, int stopIndex, 75 bool exact, BitSet ambigAlts, ATNConfigSet configs) 76 { 77 if (exactOnly && !exact) { 78 return; 79 } 80 81 string format_info = "reportAmbiguity d=%s: ambigAlts=%s, input='%s'"; 82 string decision = getDecisionDescription(recognizer, dfa); 83 BitSet conflictingAlts = getConflictingAlts(ambigAlts, configs); 84 string text = to!string(recognizer.getTokenStream.getText(Interval.of(startIndex, stopIndex))); 85 string message = format(format_info, decision, conflictingAlts, text); 86 recognizer.notifyErrorListeners(message); 87 } 88 89 /** 90 * @uml 91 * @override 92 */ 93 public override void reportAttemptingFullContext(InterfaceParser recognizer, DFA dfa, int startIndex, 94 int stopIndex, BitSet conflictingAlts, ATNConfigSet configs) 95 { 96 string format_info = "reportAttemptingFullContext d=%s, input='%s'"; 97 string decision = getDecisionDescription(recognizer, dfa); 98 string text = to!string(recognizer.getTokenStream().getText(Interval.of(startIndex, stopIndex))); 99 string message = format(format_info, decision, text); 100 recognizer.notifyErrorListeners(message); 101 } 102 103 /** 104 * @uml 105 * @override 106 */ 107 public override void reportContextSensitivity(InterfaceParser recognizer, DFA dfa, int startIndex, 108 int stopIndex, int prediction, ATNConfigSet configs) 109 { 110 string format_info = "reportContextSensitivity d=%s, input='%s'"; 111 string decision = getDecisionDescription(recognizer, dfa); 112 string text = to!string(recognizer.getTokenStream().getText(Interval.of(startIndex, stopIndex))); 113 string message = format(format_info, decision, text); 114 recognizer.notifyErrorListeners(message); 115 } 116 117 protected string getDecisionDescription(InterfaceParser recognizer, DFA dfa) 118 { 119 int decision = dfa.decision; 120 int ruleIndex = dfa.atnStartState.ruleIndex; 121 122 string[] ruleNames = recognizer.getRuleNames(); 123 if (ruleIndex < 0 || ruleIndex >= ruleNames.length) { 124 return to!string(decision); 125 } 126 127 string ruleName = ruleNames[ruleIndex]; 128 if (ruleName is null || ruleName.length == 0) { 129 return to!string(decision); 130 } 131 return format("%d (%s)", decision, ruleName); 132 133 } 134 135 /** 136 * Computes the set of conflicting or ambiguous alternatives from a 137 * configuration set, if that information was not already provided by the 138 * parser. 139 * 140 * @param reportedAlts The set of conflicting or ambiguous alternatives, as 141 * reported by the parser. 142 * @param configs The conflicting or ambiguous configuration set. 143 * @return Returns {@code reportedAlts} if it is not {@code null}, otherwise 144 * returns the set of alternatives represented in {@code configs}. 145 */ 146 protected BitSet getConflictingAlts(BitSet reportedAlts, ATNConfigSet configs) 147 { 148 if (reportedAlts.length) { 149 return reportedAlts; 150 } 151 152 BitSet* result = new BitSet(); 153 foreach (ATNConfig config; configs.configs) { 154 result.set(config.alt, true); 155 } 156 157 return *result; 158 } 159 160 }