388 lines
14 KiB
JavaScript
388 lines
14 KiB
JavaScript
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
exports.RegExpCreate = RegExpCreate;
|
|
exports.RegExpAlloc = RegExpAlloc;
|
|
exports.RegExpInitialize = RegExpInitialize;
|
|
exports.RegExpExec = RegExpExec;
|
|
exports.RegExpBuiltinExec = RegExpBuiltinExec;
|
|
exports.AdvanceStringIndex = AdvanceStringIndex;
|
|
exports.EscapeRegExpPattern = EscapeRegExpPattern;
|
|
|
|
var _invariant = require("../invariant.js");
|
|
|
|
var _invariant2 = _interopRequireDefault(_invariant);
|
|
|
|
var _index = require("../values/index.js");
|
|
|
|
var _get = require("./get.js");
|
|
|
|
var _is = require("./is.js");
|
|
|
|
var _call = require("./call.js");
|
|
|
|
var _has = require("./has.js");
|
|
|
|
var _singletons = require("../singletons.js");
|
|
|
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
|
|
// ECMA262 21.2.3.2.3
|
|
/**
|
|
* Copyright (c) 2017-present, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*/
|
|
|
|
function RegExpCreate(realm, P, F) {
|
|
// 1. Let obj be ? RegExpAlloc(%RegExp%).
|
|
var obj = RegExpAlloc(realm, realm.intrinsics.RegExp);
|
|
|
|
// 2. Return ? RegExpInitialize(obj, P, F).
|
|
return RegExpInitialize(realm, obj, P, F);
|
|
}
|
|
|
|
// ECMA262 21.2.3.2.1
|
|
function RegExpAlloc(realm, newTarget) {
|
|
// 1. Let obj be ? OrdinaryCreateFromConstructor(newTarget, "%RegExpPrototype%", « [[RegExpMatcher]],
|
|
// [[OriginalSource]], [[OriginalFlags]] »).
|
|
var obj = _singletons.Create.OrdinaryCreateFromConstructor(realm, newTarget, "RegExpPrototype", {
|
|
$RegExpMatcher: undefined, // always initialized to not undefined before use
|
|
$OriginalSource: undefined, // ditto
|
|
$OriginalFlags: undefined // ditto
|
|
});
|
|
|
|
// 2. Perform ! DefinePropertyOrThrow(obj, "lastIndex", PropertyDescriptor {[[Writable]]: true,
|
|
// [[Enumerable]]: false, [[Configurable]]: false}).
|
|
_singletons.Properties.DefinePropertyOrThrow(realm, obj, "lastIndex", {
|
|
writable: true,
|
|
enumerable: false,
|
|
configurable: false
|
|
});
|
|
|
|
// 3. Return obj.
|
|
return obj;
|
|
}
|
|
|
|
// ECMA262 21.2.3.2.2
|
|
function RegExpInitialize(realm, obj, pattern, flags) {
|
|
// Note that obj is a new object, and we can thus write to internal slots
|
|
(0, _invariant2.default)(realm.isNewObject(obj));
|
|
|
|
// 1. If pattern is undefined, let P be the empty String.
|
|
var P = void 0;
|
|
if (!pattern || (0, _has.HasCompatibleType)(pattern, _index.UndefinedValue)) {
|
|
P = "";
|
|
} else {
|
|
// 2. Else, let P be ? ToString(pattern).
|
|
P = _singletons.To.ToStringPartial(realm, pattern);
|
|
}
|
|
|
|
// 3. If flags is undefined, let F be the empty String.
|
|
var F = void 0;
|
|
if (!flags || (0, _has.HasCompatibleType)(flags, _index.UndefinedValue)) {
|
|
F = "";
|
|
} else {
|
|
// 4. Else, let F be ? ToString(flags).
|
|
F = _singletons.To.ToStringPartial(realm, flags);
|
|
}
|
|
|
|
// 5. If F contains any code unit other than "g", "i", "m", "u", or "y" or if it contains the same code unit more than once, throw a SyntaxError exception.
|
|
for (var i = 0; i < F.length; ++i) {
|
|
if ("gimuy".indexOf(F.charAt(i)) < 0) {
|
|
throw realm.createErrorThrowCompletion(realm.intrinsics.SyntaxError, "invalid RegExp flag");
|
|
}
|
|
for (var j = i + 1; j < F.length; ++j) {
|
|
if (F.charAt(i) === F.charAt(j)) {
|
|
throw realm.createErrorThrowCompletion(realm.intrinsics.SyntaxError, "duplicate RegExp flag");
|
|
}
|
|
}
|
|
}
|
|
|
|
// 6. If F contains "u", let BMP be false; else let BMP be true.
|
|
var BMP = F.indexOf("u") >= 0 ? false : true;
|
|
|
|
// 7. If BMP is true, then
|
|
if (BMP) {
|
|
// a. Parse P using the grammars in 21.2.1 and interpreting each of its 16-bit elements as a Unicode BMP
|
|
// code point. UTF-16 decoding is not applied to the elements. The goal symbol for the parse is
|
|
// Pattern. Throw a SyntaxError exception if P did not conform to the grammar, if any elements of P
|
|
// were not matched by the parse, or if any Early Error conditions exist.
|
|
// b. Let patternCharacters be a List whose elements are the code unit elements of P.
|
|
} else {}
|
|
// 8. Else,
|
|
// a. Parse P using the grammars in 21.2.1 and interpreting P as UTF-16 encoded Unicode code points
|
|
// (6.1.4). The goal symbol for the parse is Pattern[U]. Throw a SyntaxError exception if P did not
|
|
// conform to the grammar, if any elements of P were not matched by the parse, or if any Early Error
|
|
// conditions exist.
|
|
// b. Let patternCharacters be a List whose elements are the code points resulting from applying UTF-16
|
|
// decoding to P's sequence of elements.
|
|
|
|
|
|
// 9. Set the value of obj's [[OriginalSource]] internal slot to P.
|
|
obj.$OriginalSource = P;
|
|
|
|
// 10. Set the value of obj's [[OriginalFlags]] internal slot to F.
|
|
obj.$OriginalFlags = F;
|
|
|
|
// 11. Set obj's [[RegExpMatcher]] internal slot to the internal procedure that evaluates the above parse of
|
|
// P by applying the semantics provided in 21.2.2 using patternCharacters as the pattern's List of
|
|
// SourceCharacter values and F as the flag parameters.
|
|
try {
|
|
var computedFlags = "y";
|
|
if (F.indexOf("i") >= 0) computedFlags += "i";
|
|
if (F.indexOf("u") >= 0) computedFlags += "u";
|
|
if (F.indexOf("m") >= 0) computedFlags += "m";
|
|
var matcher = new RegExp(P, computedFlags);
|
|
|
|
obj.$RegExpMatcher = function (S, lastIndex) {
|
|
matcher.lastIndex = lastIndex;
|
|
var match = matcher.exec(S);
|
|
if (!match) {
|
|
return null;
|
|
}
|
|
return {
|
|
endIndex: match.index + match[0].length,
|
|
captures: match
|
|
};
|
|
};
|
|
} catch (e) {
|
|
if (e instanceof SyntaxError) {
|
|
throw realm.createErrorThrowCompletion(realm.intrinsics.SyntaxError, "invalid RegExp");
|
|
} else throw e;
|
|
}
|
|
|
|
// 12. Perform ? Set(obj, "lastIndex", 0, true).
|
|
_singletons.Properties.Set(realm, obj, "lastIndex", realm.intrinsics.zero, true);
|
|
|
|
// 13. Return obj.
|
|
return obj;
|
|
}
|
|
|
|
// ECMA262 21.2.5.2.1
|
|
function RegExpExec(realm, R, S) {
|
|
// 1. Assert: Type(R) is Object.
|
|
(0, _invariant2.default)(R instanceof _index.ObjectValue, "Type(R) is Object");
|
|
|
|
// 2. Assert: Type(S) is String.
|
|
(0, _invariant2.default)(typeof S === "string", "Type(S) is String");
|
|
|
|
// 3. Let exec be ? Get(R, "exec").
|
|
var exec = (0, _get.Get)(realm, R, "exec");
|
|
|
|
// 4. If IsCallable(exec) is true, then
|
|
if ((0, _is.IsCallable)(realm, exec)) {
|
|
// a. Let result be ? Call(exec, R, « S »).
|
|
var result = (0, _call.Call)(realm, exec, R, [new _index.StringValue(realm, S)]);
|
|
|
|
// b. If Type(result) is neither Object or Null, throw a TypeError exception.
|
|
if (!(0, _has.HasSomeCompatibleType)(result, _index.ObjectValue, _index.NullValue)) {
|
|
throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError, "Type(result) is neither Object or Null");
|
|
}
|
|
|
|
// c. Return result.
|
|
return result.throwIfNotConcrete();
|
|
}
|
|
|
|
// 5. If R does not have a [[RegExpMatcher]] internal slot, throw a TypeError exception.
|
|
if (R.$RegExpMatcher === undefined) {
|
|
throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError, "R does not have a [[RegExpMatcher]] internal slot");
|
|
}
|
|
|
|
// 6. Return ? RegExpBuiltinExec(R, S).
|
|
return RegExpBuiltinExec(realm, R, S);
|
|
}
|
|
|
|
// ECMA262 21.2.5.2.2
|
|
function RegExpBuiltinExec(realm, R, S) {
|
|
// 1. Assert: R is an initialized RegExp instance.
|
|
(0, _invariant2.default)(R.$RegExpMatcher !== undefined && R.$OriginalSource !== undefined && R.$OriginalFlags !== undefined, "R is an initialized RegExp instance");
|
|
|
|
// 2. Assert: Type(S) is String.
|
|
(0, _invariant2.default)(typeof S === "string", "Type(S) is String");
|
|
|
|
// 3. Let length be the number of code units in S.
|
|
var length = S.length;
|
|
|
|
// 4. Let lastIndex be ? ToLength(? Get(R, "lastIndex")).
|
|
var lastIndex = _singletons.To.ToLength(realm, (0, _get.Get)(realm, R, "lastIndex"));
|
|
|
|
// 5. Let flags be R.[[OriginalFlags]].
|
|
var flags = R.$OriginalFlags;
|
|
(0, _invariant2.default)(typeof flags === "string");
|
|
|
|
// 6 .If flags contains "g", let global be true, else let global be false.
|
|
var global = flags.indexOf("g") >= 0 ? true : false;
|
|
|
|
// 7. If flags contains "y", let sticky be true, else let sticky be false.
|
|
var sticky = flags.indexOf("y") >= 0 ? true : false;
|
|
|
|
// 8. If global is false and sticky is false, let lastIndex be 0.
|
|
if (global === false && sticky === false) lastIndex = 0;
|
|
|
|
// 9. Let matcher be the value of R's [[RegExpMatcher]] internal slot.
|
|
var matcher = R.$RegExpMatcher;
|
|
(0, _invariant2.default)(matcher !== undefined);
|
|
|
|
// 10. If flags contains "u", let fullUnicode be true, else let fullUnicode be false.
|
|
var fullUnicode = flags.indexOf("u") >= 0 ? true : false;
|
|
|
|
// 11. Let matchSucceeded be false.
|
|
var matchSucceeded = false;
|
|
|
|
var r = null;
|
|
// 12. Repeat, while matchSucceeded is false
|
|
while (!matchSucceeded) {
|
|
// a. If lastIndex > length, then
|
|
if (lastIndex > length) {
|
|
// i. Perform ? Set(R, "lastIndex", 0, true).
|
|
_singletons.Properties.Set(realm, R, "lastIndex", realm.intrinsics.zero, true);
|
|
// ii. Return null.
|
|
return realm.intrinsics.null;
|
|
}
|
|
|
|
// b. Let r be matcher(S, lastIndex).
|
|
r = matcher(S, lastIndex);
|
|
|
|
// c. If r is failure, then
|
|
if (r == null) {
|
|
// i. If sticky is true, then
|
|
if (sticky) {
|
|
// 1. Perform ? Set(R, "lastIndex", 0, true).
|
|
_singletons.Properties.Set(realm, R, "lastIndex", realm.intrinsics.zero, true);
|
|
|
|
// 2. Return null.
|
|
return realm.intrinsics.null;
|
|
}
|
|
// ii. Let lastIndex be AdvanceStringIndex(S, lastIndex, fullUnicode).
|
|
lastIndex = AdvanceStringIndex(realm, S, lastIndex, fullUnicode);
|
|
} else {
|
|
// d. Else,
|
|
// i. Assert: r is a State.
|
|
(0, _invariant2.default)(r, "r is a State");
|
|
|
|
// ii. Set matchSucceeded to true.
|
|
matchSucceeded = true;
|
|
|
|
// (not in standard) Let lastIndex be the index of the captures
|
|
lastIndex = r.captures.index;
|
|
}
|
|
}
|
|
(0, _invariant2.default)(r != null);
|
|
|
|
// 13. Let e be r's endIndex value.
|
|
var e = r.endIndex;
|
|
|
|
// 14. If fullUnicode is true, then
|
|
if (fullUnicode) {}
|
|
// TODO #1018 a. e is an index into the Input character list, derived from S, matched by matcher. Let eUTF be the smallest index into S that corresponds to the character at element e of Input. If e is greater than or equal to the length of Input, then eUTF is the number of code units in S.
|
|
// b. Let e be eUTF.
|
|
|
|
|
|
// 15. If global is true or sticky is true, then
|
|
if (global === true || sticky === true) {
|
|
// a. Perform ? Set(R, "lastIndex", e, true).
|
|
_singletons.Properties.Set(realm, R, "lastIndex", new _index.NumberValue(realm, e), true);
|
|
}
|
|
|
|
// 16. Let n be the length of r's captures List. (This is the same value as 21.2.2.1's NcapturingParens.)
|
|
var n = r.captures.length - 1;
|
|
|
|
// 17. Let A be ArrayCreate(n + 1).
|
|
var A = _singletons.Create.ArrayCreate(realm, n + 1);
|
|
|
|
// 18. Assert: The value of A's "length" property is n + 1.
|
|
var lengthOfA = (0, _get.Get)(realm, A, "length").throwIfNotConcrete();
|
|
(0, _invariant2.default)(lengthOfA instanceof _index.NumberValue);
|
|
(0, _invariant2.default)(lengthOfA.value === n + 1, 'The value of A\'s "length" property is n + 1');
|
|
|
|
// 19. Let matchIndex be lastIndex.
|
|
var matchIndex = lastIndex;
|
|
|
|
// 20. Perform ! CreateDataProperty(A, "index", matchIndex).
|
|
_singletons.Create.CreateDataProperty(realm, A, "index", new _index.NumberValue(realm, matchIndex));
|
|
|
|
// 21. Perform ! CreateDataProperty(A, "input", S).
|
|
_singletons.Create.CreateDataProperty(realm, A, "input", new _index.StringValue(realm, S));
|
|
|
|
// 22. Let matchedSubstr be the matched substring (i.e. the portion of S between offset lastIndex inclusive and offset e exclusive).
|
|
var matchedSubstr = S.substr(lastIndex, e - lastIndex);
|
|
|
|
// 23. Perform ! CreateDataProperty(A, "0", matchedSubstr).
|
|
_singletons.Create.CreateDataProperty(realm, A, "0", new _index.StringValue(realm, matchedSubstr));
|
|
|
|
// 24. For each integer i such that i > 0 and i ≤ n
|
|
for (var i = 1; i <= n; ++i) {
|
|
// a. Let captureI be ith element of r's captures List.
|
|
var captureI = r.captures[i];
|
|
|
|
var capturedValue = void 0;
|
|
// b. If captureI is undefined, let capturedValue be undefined.
|
|
if (captureI === undefined) {
|
|
capturedValue = realm.intrinsics.undefined;
|
|
} else if (fullUnicode) {
|
|
// c. Else if fullUnicode is true, then
|
|
// TODO #1018: i. Assert: captureI is a List of code points.
|
|
// ii. Let capturedValue be a string whose code units are the UTF16Encoding of the code points of captureI.
|
|
capturedValue = realm.intrinsics.undefined;
|
|
} else {
|
|
// d. Else, fullUnicode is false,
|
|
// i. Assert: captureI is a List of code units.
|
|
(0, _invariant2.default)(typeof captureI === "string");
|
|
|
|
// ii. Let capturedValue be a string consisting of the code units of captureI.
|
|
capturedValue = new _index.StringValue(realm, captureI);
|
|
}
|
|
|
|
// e. Perform ! CreateDataProperty(A, ! ToString(i), capturedValue).
|
|
_singletons.Create.CreateDataProperty(realm, A, _singletons.To.ToString(realm, new _index.NumberValue(realm, i)), capturedValue);
|
|
}
|
|
|
|
// 25. Return A.
|
|
return A;
|
|
}
|
|
|
|
function AdvanceStringIndex(realm, S, index, unicode) {
|
|
// 1. Assert: Type(S) is String.
|
|
(0, _invariant2.default)(typeof S === "string", "Type(S) is String");
|
|
|
|
// 2. Assert: index is an integer such that 0≤index≤253-1.
|
|
(0, _invariant2.default)(index >= 0 && index <= Math.pow(2, 53) - 1, "index is an integer such that 0≤index≤253-1");
|
|
|
|
// 3. Assert: Type(unicode) is Boolean.
|
|
(0, _invariant2.default)(typeof unicode === "boolean", "Type(unicode) is Boolean");
|
|
|
|
// 4. If unicode is false, return index+1.
|
|
if (unicode === false) return index + 1;
|
|
|
|
// 5. Let length be the number of code units in S.
|
|
var length = S.length;
|
|
|
|
// 6. If index+1 ≥ length, return index+1.
|
|
if (index + 1 >= length) return index + 1;
|
|
|
|
// 7. Let first be the code unit value at index index in S.
|
|
var first = S.charCodeAt(index);
|
|
|
|
// 8. If first < 0xD800 or first > 0xDBFF, return index+1.
|
|
if (first < 0xd800 || first > 0xdbff) return index + 1;
|
|
|
|
// 9. Let second be the code unit value at index index+1 in S.
|
|
var second = S.charCodeAt(index + 1);
|
|
|
|
// 10. If second < 0xDC00 or second > 0xDFFF, return index+1.
|
|
if (second < 0xdc00 || second > 0xdfff) return index + 1;
|
|
|
|
// 11. Return index+2.
|
|
return index + 2;
|
|
}
|
|
|
|
function EscapeRegExpPattern(realm, P, F) {
|
|
return P.replace("/", "/");
|
|
}
|
|
//# sourceMappingURL=regexp.js.map
|