UNPKG

10.8 kBJavaScriptView Raw
1/**
2 * Copyright 2014 Shape Security, Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License")
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17"use strict";
18
19// istanbul ignore next
20
21var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
22
23// istanbul ignore next
24
25function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
26
27var PatternAcceptor = (function () {
28 function PatternAcceptor(pattern, u) {
29 _classCallCheck(this, PatternAcceptor);
30
31 this.index = 0;
32 this.nCapturingParens = 0;
33 // constants
34 this.length = pattern.length;
35 this.pattern = pattern;
36 this.u = u;
37 }
38
39 _createClass(PatternAcceptor, [{
40 key: "eat",
41 value: function eat(ch) {
42 if (this.index >= this.length || this.pattern[this.index] !== ch) return false;
43 ++this.index;
44 return true;
45 }
46 }, {
47 key: "eatRegExp",
48 value: function eatRegExp(r) {
49 if (this.index >= this.length || !r.test(this.pattern[this.index])) return false;
50 ++this.index;
51 return true;
52 }
53 }, {
54 key: "eatN",
55 value: function eatN(n, r) {
56 if (this.index + n <= this.length && r.test(this.pattern.slice(this.index, this.index + n))) {
57 this.index += n;
58 return true;
59 }
60 return false;
61 }
62 }, {
63 key: "match",
64 value: function match(ch) {
65 return this.index < this.length && this.pattern[this.index] === ch;
66 }
67 }, {
68 key: "matchRegExp",
69 value: function matchRegExp(r) {
70 return this.index < this.length && r.test(this.pattern[this.index]);
71 }
72 }, {
73 key: "trackback",
74 value: function trackback(start, result) {
75 if (result) return true;
76 this.index = start;
77 return false;
78 }
79 }, {
80 key: "readDisjunction",
81 value: function readDisjunction() {
82 return this.readAlternative() && (this.eat("|") ? this.readDisjunction() : true);
83 }
84 }, {
85 key: "readAlternative",
86 value: function readAlternative() {
87 var savedIndex = this.index;
88 while (this.readTerm()) {
89 savedIndex = this.index;
90 }
91 this.index = savedIndex;
92 return true;
93 }
94 }, {
95 key: "readTerm",
96 value: function readTerm() {
97 if (!this.u) return this.readExtendedTerm();
98 return this.readAssertion() || this.readQuantifiableAssertion() || this.readAtom() && (this.readQuantifier(), true);
99 }
100 }, {
101 key: "readExtendedTerm",
102 value: function readExtendedTerm() {
103 return this.readQuantifiableAssertion() && (this.readQuantifier(), true) || this.readAssertion() || this.readAtomNoBrace() && (this.readQuantifier(), true) || this.readAtom();
104 }
105 }, {
106 key: "readAssertion",
107 value: function readAssertion() {
108 return this.eat("^") || this.eat("$") || this.eatN(2, /^\\[bB]$/);
109 }
110 }, {
111 key: "readQuantifiableAssertion",
112 value: function readQuantifiableAssertion() {
113 var start = this.index;
114 return this.eatN(3, /^\(\?[=!]$/) && this.trackback(start, this.readDisjunction() && this.eat(")"));
115 }
116 }, {
117 key: "readQuantifier",
118 value: function readQuantifier() {
119 return this.readQuantifierPrefix() && (this.eat("?"), true);
120 }
121 }, {
122 key: "readQuantifierPrefix",
123 value: function readQuantifierPrefix() {
124 if (this.eat("*") || this.eat("+") || this.eat("?")) return true;
125 if (this.eat("{") && this.readDecimalDigits()) {
126 if (this.eat(",")) this.readDecimalDigits();
127 return this.eat("}");
128 }
129 return false;
130 }
131 }, {
132 key: "readDecimalDigits",
133 value: function readDecimalDigits() {
134 var start = this.index;
135 while (this.eatRegExp(/^\d$/));
136 return this.index > start;
137 }
138 }, {
139 key: "readAtomNoBrace",
140 value: function readAtomNoBrace() {
141 var start = this.index;
142 var startingParens = this.nCapturingParens;
143 if (this.readPatternCharacterNoBrace() || this.eat(".")) return true;
144 if (this.eat("\\")) return this.trackback(start, this.readAtomEscape());
145 if (this.readCharacterClass()) return true;
146 if (this.eat("(")) {
147 if (!this.eatN(2, /^\?:$/)) ++this.nCapturingParens;
148 if (this.readDisjunction() && this.eat(")")) return true;
149 this.nCapturingParens = startingParens;
150 this.index = start;
151 return false;
152 }
153 return false;
154 }
155 }, {
156 key: "readAtom",
157 value: function readAtom() {
158 return this.readAtomNoBrace() || this.eat("{") || this.eat("}");
159 }
160 }, {
161 key: "readSyntaxCharacter",
162 value: function readSyntaxCharacter() {
163 return this.eatRegExp(/^[\^$\\.*+?()[\]{}|]$/);
164 }
165 }, {
166 key: "readPatternCharacterNoBrace",
167 value: function readPatternCharacterNoBrace() {
168 return this.eatRegExp(/^[^\^$\\.*+?()[\]{}|]$/);
169 }
170 }, {
171 key: "readAtomEscape",
172 value: function readAtomEscape() {
173 return this.readDecimalEscape() || this.readCharacterEscape() || this.readCharacterClassEscape();
174 }
175 }, {
176 key: "readCharacterEscape",
177 value: function readCharacterEscape() {
178 return this.readControlEscape() || this.eat("c") && this.readControlLetter() || this.readHexEscapeSequence() || this.readRegExpUnicodeEscapeSequence() || this.readIdentityEscape();
179 }
180 }, {
181 key: "readControlEscape",
182 value: function readControlEscape() {
183 return this.eatRegExp(/^[fnrtv]$/);
184 }
185 }, {
186 key: "readControlLetter",
187 value: function readControlLetter() {
188 return this.eatRegExp(/^[a-zA-Z]$/);
189 }
190 }, {
191 key: "readHexEscapeSequence",
192 value: function readHexEscapeSequence() {
193 return this.eat("x") && this.readHexDigit() && this.readHexDigit();
194 }
195 }, {
196 key: "readHexDigit",
197 value: function readHexDigit() {
198 return this.eatRegExp(/^[a-fA-F0-9]$/);
199 }
200 }, {
201 key: "readRegExpUnicodeEscapeSequence",
202 value: function readRegExpUnicodeEscapeSequence() {
203 if (!this.eat("u")) return false;
204 if (this.u) {
205 if (this.eatN(4, /^D[abAB89][a-fA-F0-9]{2}$/)) {
206 this.eatN(6, /^\\u[dD][c-fC-F0-9][a-fA-F0-9]{2}$/);
207 return true;
208 }
209 return this.readHex4Digits() || this.eat("{") && this.readHexDigits() && this.eat("}");
210 } else {
211 return this.readHex4Digits();
212 }
213 }
214 }, {
215 key: "readHex4Digits",
216 value: function readHex4Digits() {
217 var k = 4;
218 while (k > 0) {
219 --k;
220 if (!this.readHexDigit()) return false;
221 }
222 return true;
223 }
224 }, {
225 key: "readHexDigits",
226 value: function readHexDigits() {
227 var start = this.index;
228 while (this.readHexDigit());
229 return this.index > start;
230 }
231 }, {
232 key: "readIdentityEscape",
233 value: function readIdentityEscape() {
234 if (this.u) {
235 return this.readSyntaxCharacter() || this.eat("/");
236 } else {
237 return this.eatRegExp(/^[^a-zA-Z0-9_]$/); // TODO: SourceCharacter but not UnicodeIDContinue
238 }
239 }
240 }, {
241 key: "readDecimalEscape",
242 value: function readDecimalEscape() {
243 if (this.eat("0")) {
244 if (!this.matchRegExp(/^\d$/)) return true;
245 --this.index;
246 return false;
247 }
248 var start = this.index;
249 while (this.eatRegExp(/^\d$/));
250 return this.trackback(start, this.index > start && (this.u || +this.pattern.slice(start, this.index) <= this.nCapturingParens));
251 }
252 }, {
253 key: "readCharacterClassEscape",
254 value: function readCharacterClassEscape() {
255 return this.eatRegExp(/^[dDsSwW]$/);
256 }
257 }, {
258 key: "readCharacterClass",
259 value: function readCharacterClass() {
260 var start = this.index;
261 return this.eat("[") && this.trackback(start, (this.eat("^"), true) && this.readClassRanges() && this.eat("]"));
262 }
263 }, {
264 key: "readClassRanges",
265 value: function readClassRanges() {
266 var start = this.index;
267 if (!this.readNonemptyClassRanges()) {
268 this.index = start;
269 }
270 return true;
271 }
272 }, {
273 key: "readNonemptyClassRanges",
274 value: function readNonemptyClassRanges() {
275 if (!this.readClassAtom()) return false;
276 if (this.match("]")) return true;
277 if (this.eat("-")) {
278 if (this.match("]")) return true;
279 return this.readClassAtom() && this.readClassRanges();
280 }
281 return this.readNonemptyClassRangesNoDash();
282 }
283 }, {
284 key: "readNonemptyClassRangesNoDash",
285 value: function readNonemptyClassRangesNoDash() {
286 // NOTE: it is impossible to reach this next line with a value matched by RegularExpressionLiteral;
287 // the pattern "[-a" would reach here if it could get past RegularExpressionLiteral
288 /* istanbul ignore next */
289 if (!this.readClassAtomNoDash()) return false;
290 if (this.match("]")) return true;
291 if (this.eat("-")) {
292 if (this.match("]")) return true;
293 return this.readClassAtom() && this.readClassRanges();
294 }
295 return this.readNonemptyClassRangesNoDash();
296 }
297 }, {
298 key: "readClassAtom",
299 value: function readClassAtom() {
300 return this.eat("-") || this.readClassAtomNoDash();
301 }
302 }, {
303 key: "readClassAtomNoDash",
304 value: function readClassAtomNoDash() {
305 return this.eatRegExp(/^[^\\\]-]$/) || this.eat("\\") && this.readClassEscape();
306 }
307 }, {
308 key: "readClassEscape",
309 value: function readClassEscape() {
310 return this.readDecimalEscape() || this.eat("b") || this.u && this.eat("-") || this.readCharacterEscape() || this.readCharacterClassEscape();
311 }
312 }], [{
313 key: "test",
314 value: function test(pattern, u) {
315 var acceptor = new PatternAcceptor(pattern, u);
316 return acceptor.readDisjunction() && acceptor.index === acceptor.length;
317 }
318 }]);
319
320 return PatternAcceptor;
321})();
322
323exports.PatternAcceptor = PatternAcceptor;
\No newline at end of file