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