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 }