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 moduleantlr.v4.runtime.atn.LexerActionExecutor;
8 9 importstd.conv;
10 importantlr.v4.runtime.InterfaceLexer;
11 importantlr.v4.runtime.CharStream;
12 importantlr.v4.runtime.atn.LexerAction;
13 importantlr.v4.runtime.atn.LexerIndexedCustomAction;
14 importantlr.v4.runtime.misc.MurmurHash;
15 16 /**
17 * @uml
18 * Represents an executor for a sequence of lexer actions which traversed during
19 * the matching operation of a lexer rule (token).
20 *
21 * <p>The executor tracks position information for position-dependent lexer actions
22 * efficiently, ensuring that actions appearing only at the end of the rule do
23 * not cause bloating of the {@link DFA} created for the lexer.</p>
24 *
25 * @author Sam Harwell
26 * @since 4.2
27 */28 classLexerActionExecutor29 {
30 31 privateLexerAction[] lexerActions;
32 33 /**
34 * @uml
35 * Caches the result of {@link #hashCode} since the hash code is an element
36 * of the performance-critical {@link LexerATNConfig#hashCode} operation.
37 */38 privatesize_thashCode_;
39 40 /**
41 * @uml
42 * Constructs an executor for a sequence of {@link LexerAction} actions.
43 * @param lexerActions The lexer actions to execute.
44 */45 publicthis(LexerAction[] lexerActions)
46 {
47 this.lexerActions = lexerActions;
48 49 size_thash = MurmurHash.initialize();
50 foreach (LexerActionlexerAction; lexerActions) {
51 hash = MurmurHash.update(hash, lexerAction);
52 }
53 this.hashCode_ = MurmurHash.finish(hash, lexerActions.length);
54 }
55 56 /**
57 * @uml
58 * Creates a {@link LexerActionExecutor} which executes the actions forthe input {@code lexerActionExecutor} followed by a specified
59 * {@code lexerAction}.
60 *
61 * @param lexerActionExecutor The executor for actions already traversed by
62 * the lexer while matching a token within a particular {@link LexerATNConfig}. If this is {@code null}, the method behaves as
63 * though it were an empty executor.
64 * @param lexerAction The lexer action to execute after the actions
65 * specified in {@code lexerActionExecutor}.
66 *
67 * @return A {@link LexerActionExecutor} for executing the combine actions
68 * of {@code lexerActionExecutor} and {@code lexerAction}.
69 */70 publicstaticLexerActionExecutorappend(LexerActionExecutorlexerActionExecutor, LexerActionlexerAction)
71 {
72 if (lexerActionExecutorisnull) {
73 LexerAction[] a = [lexerAction];
74 returnnewLexerActionExecutor(a);
75 }
76 77 LexerAction[] lexerActions = lexerActionExecutor.lexerActions;
78 lexerActions ~= lexerAction;
79 returnnewLexerActionExecutor(lexerActions);
80 }
81 82 /**
83 * @uml
84 * Creates a {@link LexerActionExecutor} which encodes the current offset
85 * for position-dependent lexer actions.
86 *
87 * <p>Normally, when the executor encounters lexer actions where
88 * {@link LexerAction#isPositionDependent} returns {@code true}, it calls
89 * {@link IntStream#seek} on the input {@link CharStream} to set the input
90 * position to the <em>end</em> of the current token. This behavior provides
91 * for efficient DFA representation of lexer actions which appear at the end
92 * of a lexer rule, even when the lexer rule matches a variable number of
93 * characters.</p>
94 *
95 * <p>Prior to traversing a match transition in the ATN, the current offset
96 * from the token start index is assigned to all position-dependent lexer
97 * actions which have not already been assigned a fixed offset. By storing
98 * the offsets relative to the token start index, the DFA representation of
99 * lexer actions which appear in the middle of tokens remains efficient due
100 * to sharing among tokens of the same length, regardless of their absolute
101 * position in the input stream.</p>
102 *
103 * <p>If the current executor already has offsets assigned to all
104 * position-dependent lexer actions, the method returns {@code this}.</p>
105 *
106 * @param offset The current offset to assign to all position-dependent
107 * lexer actions which do not already have offsets assigned.
108 *
109 * @return A {@link LexerActionExecutor} which stores input stream offsets
110 * for all position-dependent lexer actions.
111 */112 publicLexerActionExecutorfixOffsetBeforeMatch(size_toffset)
113 {
114 LexerAction[] updatedLexerActions;
115 116 for (size_ti = 0; i < lexerActions.length; i++)
117 {
118 if (lexerActions[i].isPositionDependent && !(cast(LexerIndexedCustomAction)lexerActions[i])) {
119 if (updatedLexerActionsisnull) {
120 updatedLexerActions = lexerActions.dup();
121 }
122 123 updatedLexerActions[i] = newLexerIndexedCustomAction(offset, lexerActions[i]);
124 }
125 }
126 127 if (!updatedLexerActions) {
128 returnthis;
129 }
130 131 returnnewLexerActionExecutor(updatedLexerActions);
132 }
133 134 publicLexerAction[] getLexerActions()
135 {
136 returnlexerActions;
137 }
138 139 /**
140 * Execute the actions encapsulated by this executor within the context of a
141 * particular {@link Lexer}.
142 *
143 * <p>This method calls {@link IntStream#seek} to set the position of the
144 * {@code input} {@link CharStream} prior to calling
145 * {@link LexerAction#execute} on a position-dependent action. Before the
146 * method returns, the input position will be restored to the same position
147 * it was in when the method was invoked.</p>
148 *
149 * @param lexer The lexer instance.
150 * @param input The input stream which is the source for the current token.
151 * When this method is called, the current {@link IntStream#index} for
152 * {@code input} should be the start of the following token, i.e. 1
153 * character past the end of the current token.
154 * @param startIndex The token start index. This value may be passed to
155 * {@link IntStream#seek} to set the {@code input} position to the beginning
156 * of the token.
157 */158 publicvoidexecute(InterfaceLexerlexer, CharStreaminput, size_tstartIndex)
159 {
160 boolrequiresSeek = false;
161 autostopIndex = input.index;
162 try {
163 foreach (LexerActionlexerAction; lexerActions) {
164 if (cast(LexerIndexedCustomAction)lexerAction) {
165 autooffset = (cast(LexerIndexedCustomAction)lexerAction).getOffset;
166 input.seek(startIndex + offset);
167 lexerAction = (cast(LexerIndexedCustomAction)lexerAction).getAction;
168 requiresSeek = (startIndex + offset) != stopIndex;
169 }
170 elseif (lexerAction.isPositionDependent) {
171 input.seek(stopIndex);
172 requiresSeek = false;
173 }
174 175 lexerAction.execute(lexer);
176 }
177 }
178 finally {
179 if (requiresSeek) {
180 input.seek(stopIndex);
181 }
182 }
183 184 }
185 186 /**
187 * @uml
188 * @safe
189 * @nothrow
190 * @override
191 */192 publicoverridesize_ttoHash() @safenothrow193 {
194 returnthis.hashCode_;
195 }
196 197 /**
198 * @uml
199 * @override
200 */201 publicoverrideboolopEquals(Objectobj)
202 {
203 if (objisthis) {
204 returntrue;
205 }
206 elseif (obj.classinfo != LexerActionExecutor.classinfo) {
207 returnfalse;
208 }
209 LexerActionExecutorother = cast(LexerActionExecutor)obj;
210 foreach (i, lexerA; lexerActions)
211 if (lexerA != other.lexerActions[i])
212 returnfalse;
213 returnthis.hashCode_ == other.toHash;
214 }
215 216 }