1 /* 2 * Copyright (c) 2012-2017 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 module antlr.v4.runtime.dfa.DFA; 8 9 import antlr.v4.runtime.IllegalStateException; 10 import antlr.v4.runtime.UnsupportedOperationException; 11 import antlr.v4.runtime.Vocabulary; 12 import antlr.v4.runtime.VocabularyImpl; 13 import antlr.v4.runtime.atn.ATNConfigSet; 14 import antlr.v4.runtime.atn.DecisionState; 15 import antlr.v4.runtime.atn.StarLoopEntryState; 16 import antlr.v4.runtime.dfa.DFASerializer; 17 import antlr.v4.runtime.dfa.DFAState; 18 import antlr.v4.runtime.dfa.LexerDFASerializer; 19 import std.algorithm.sorting; 20 import std.conv; 21 22 // Class DFA 23 /** 24 * A set of DFA states 25 */ 26 class DFA 27 { 28 29 /** 30 * A set of all DFA states. Use {@link Map} so we can get old state back 31 * ({@link Set} only allows you to see if it's there). 32 */ 33 public DFAState[DFAState] states; 34 35 public DFAState s0; 36 37 public int decision; 38 39 /** 40 * From which ATN state did we create this DFA? 41 */ 42 public DecisionState atnStartState; 43 44 /** 45 * {@code true} if this DFA is for a precedence decision; otherwise, 46 * {@code false}. This is the backing field for {@link #isPrecedenceDfa}. 47 */ 48 public bool precedenceDfa; 49 50 public this(DecisionState atnStartState) 51 { 52 this(atnStartState, 0); 53 } 54 55 public this(DecisionState atnStartState, int decision) 56 { 57 this.atnStartState = atnStartState; 58 this.decision = decision; 59 bool precedenceDfa = false; 60 if (cast(StarLoopEntryState)atnStartState) { 61 if ((cast(StarLoopEntryState)atnStartState).isPrecedenceDecision) { 62 precedenceDfa = true; 63 DFAState precedenceState = new DFAState(new ATNConfigSet()); 64 precedenceState.edges = new DFAState[0]; 65 precedenceState.isAcceptState = false; 66 precedenceState.requiresFullContext = false; 67 this.s0 = precedenceState; 68 } 69 } 70 this.precedenceDfa = precedenceDfa; 71 } 72 73 /** 74 * Gets whether this DFA is a precedence DFA. Precedence DFAs use a special 75 * start state {@link #s0} which is not stored in {@link #states}. The 76 * {@link DFAState#edges} array for this start state contains outgoing edges 77 * supplying individual start states corresponding to specific precedence 78 * values. 79 * 80 * @return {@code true} if this is a precedence DFA; otherwise, 81 * {@code false}. 82 * @see Parser#getPrecedence() 83 */ 84 public bool isPrecedenceDfa() 85 { 86 return precedenceDfa; 87 } 88 89 /** 90 * Get the start state for a specific precedence value. 91 * 92 * @param precedence The current precedence. 93 * @return The start state corresponding to the specified precedence, or 94 * {@code null} if no start state exists for the specified precedence. 95 * 96 * @throws IllegalStateException if this is not a precedence DFA. 97 * @see #isPrecedenceDfa() 98 */ 99 public DFAState getPrecedenceStartState(int precedence) 100 { 101 if (!isPrecedenceDfa()) { 102 throw new IllegalStateException("Only precedence DFAs may contain a precedence start state."); 103 } 104 // s0.edges is never null for a precedence DFA 105 if (precedence < 0 || precedence >= s0.edges.length) { 106 return null; 107 } 108 return s0.edges[precedence]; 109 } 110 111 /** 112 * Set the start state for a specific precedence value. 113 * 114 * @param precedence The current precedence. 115 * @param startState The start state corresponding to the specified 116 * precedence. 117 * 118 * @throws IllegalStateException if this is not a precedence DFA. 119 * @see #isPrecedenceDfa() 120 * @uml 121 * @final 122 */ 123 public final void setPrecedenceStartState(int precedence, DFAState startState) 124 { 125 if (!isPrecedenceDfa()) { 126 throw new IllegalStateException("Only precedence DFAs may contain a precedence start state."); 127 } 128 129 if (precedence < 0) { 130 return; 131 } 132 133 // synchronization on s0 here is ok. when the DFA is turned into a 134 // precedence DFA, s0 will be initialized once and not updated again 135 synchronized (s0) { 136 // s0.edges is never null for a precedence DFA 137 if (precedence >= s0.edges.length) { 138 s0.edges.length = precedence + 1; 139 } 140 s0.edges[precedence] = startState; 141 } 142 } 143 144 /** 145 * Sets whether this is a precedence DFA. 146 * 147 * @param precedenceDfa {@code true} if this is a precedence DFA; otherwise, 148 * {@code false} 149 * 150 * @throws UnsupportedOperationException if {@code precedenceDfa} does not 151 * match the value of {@link #isPrecedenceDfa} for the current DFA. 152 * 153 * @deprecated This method no longer performs any action. 154 */ 155 public void setPrecedenceDfa(bool precedenceDfa) 156 { 157 if (precedenceDfa != isPrecedenceDfa()) { 158 throw new UnsupportedOperationException("The precedenceDfa field cannot change after a DFA is constructed."); 159 } 160 } 161 162 public DFAState[] getStates() 163 { 164 DFAState[] result = states.keys; 165 result.sort!("a.stateNumber < b.stateNumber"); 166 return result; 167 } 168 169 /** 170 * @uml 171 * @override 172 */ 173 public override string toString() 174 { 175 return to!string(new VocabularyImpl(null, null, null)); 176 } 177 178 public string toString(string[] tokenNames) 179 { 180 if (s0 is null) 181 return ""; 182 DFASerializer serializer = new DFASerializer(this, tokenNames); 183 return serializer.toString(); 184 } 185 186 public string toString(Vocabulary vocabulary) 187 { 188 if (s0 is null) { 189 return ""; 190 } 191 192 DFASerializer serializer = new DFASerializer(this, vocabulary); 193 return serializer.toString(); 194 } 195 196 public string toLexerString() 197 { 198 if (s0 is null) return ""; 199 DFASerializer serializer = new LexerDFASerializer(this); 200 return serializer.toString(); 201 } 202 203 } 204 205 version(unittest) { 206 import dshould : equal, not, be, should; 207 import unit_threaded; 208 209 @Tags("DFA") 210 @("Construction") 211 unittest 212 { 213 import std.stdio; 214 import antlr.v4.runtime.atn.TokensStartState; 215 DecisionState startState = new TokensStartState; 216 DFA dfa = new DFA(startState); 217 dfa.should.not.be(null); 218 } 219 }