summaryrefslogtreecommitdiffstats
path: root/js/src/tests/non262/reflect-parse/Match.js
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/tests/non262/reflect-parse/Match.js')
-rw-r--r--js/src/tests/non262/reflect-parse/Match.js242
1 files changed, 242 insertions, 0 deletions
diff --git a/js/src/tests/non262/reflect-parse/Match.js b/js/src/tests/non262/reflect-parse/Match.js
new file mode 100644
index 0000000000..2a14d8a315
--- /dev/null
+++ b/js/src/tests/non262/reflect-parse/Match.js
@@ -0,0 +1,242 @@
+// |reftest| skip
+
+// A little pattern-matching library.
+var Match =
+
+(function() {
+
+ function Pattern(template) {
+ // act like a constructor even as a function
+ if (!(this instanceof Pattern))
+ return new Pattern(template);
+
+ this.template = template;
+ }
+
+ Pattern.prototype = {
+ match: function(act) {
+ return match(act, this.template);
+ },
+
+ matches: function(act) {
+ try {
+ return this.match(act);
+ }
+ catch (e) {
+ if (!(e instanceof MatchError))
+ throw e;
+ return false;
+ }
+ },
+
+ assert: function(act, message) {
+ try {
+ return this.match(act);
+ }
+ catch (e) {
+ if (!(e instanceof MatchError))
+ throw e;
+ throw new Error((message || "failed match") + ": " + e.message);
+ }
+ },
+
+ toString: () => "[object Pattern]"
+ };
+
+ Pattern.ANY = new Pattern;
+ Pattern.ANY.template = Pattern.ANY;
+
+ Pattern.NUMBER = new Pattern;
+ Pattern.NUMBER.match = function (act) {
+ if (typeof act !== 'number') {
+ throw new MatchError("Expected number, got: " + quote(act));
+ }
+ }
+
+ Pattern.NATURAL = new Pattern
+ Pattern.NATURAL.match = function (act) {
+ if (typeof act !== 'number' || act !== Math.floor(act) || act < 0) {
+ throw new MatchError("Expected natural number, got: " + quote(act));
+ }
+ }
+
+ class ObjectWithExactly extends Pattern {
+ constructor(template) {
+ super(template);
+ }
+
+ match(actual) {
+ return matchObjectWithExactly(actual, this.template)
+ }
+ }
+
+ Pattern.OBJECT_WITH_EXACTLY = function (template) {
+ return new ObjectWithExactly(template);
+ }
+
+ var quote = JSON.stringify;
+
+ class MatchError extends Error {
+ toString() {
+ return "match error: " + this.message;
+ }
+ };
+
+ Pattern.MatchError = MatchError;
+
+ function isAtom(x) {
+ return (typeof x === "number") ||
+ (typeof x === "string") ||
+ (typeof x === "boolean") ||
+ (x === null) ||
+ (x === undefined) ||
+ (typeof x === "object" && x instanceof RegExp) ||
+ (typeof x === "bigint");
+ }
+
+ function isObject(x) {
+ return (x !== null) && (typeof x === "object");
+ }
+
+ function isFunction(x) {
+ return typeof x === "function";
+ }
+
+ function isArrayLike(x) {
+ return isObject(x) && ("length" in x);
+ }
+
+ function matchAtom(act, exp) {
+ if ((typeof exp) === "number" && isNaN(exp)) {
+ if ((typeof act) !== "number" || !isNaN(act))
+ throw new MatchError("expected NaN, got: " + quote(act));
+ return true;
+ }
+
+ if (exp === null) {
+ if (act !== null)
+ throw new MatchError("expected null, got: " + quote(act));
+ return true;
+ }
+
+ if (exp instanceof RegExp) {
+ if (!(act instanceof RegExp) || exp.source !== act.source)
+ throw new MatchError("expected " + quote(exp) + ", got: " + quote(act));
+ return true;
+ }
+
+ switch (typeof exp) {
+ case "string":
+ case "undefined":
+ if (act !== exp)
+ throw new MatchError("expected " + quote(exp) + ", got " + quote(act));
+ return true;
+ case "boolean":
+ case "number":
+ case "bigint":
+ if (exp !== act)
+ throw new MatchError("expected " + exp + ", got " + quote(act));
+ return true;
+ }
+
+ throw new Error("bad pattern: " + JSON.stringify(exp));
+ }
+
+ // Match an object having at least the expected properties.
+ function matchObjectWithAtLeast(act, exp) {
+ if (!isObject(act))
+ throw new MatchError("expected object, got " + quote(act));
+
+ for (var key in exp) {
+ if (!(key in act))
+ throw new MatchError("expected property " + quote(key) + " not found in " + quote(act));
+ try {
+ match(act[key], exp[key]);
+ } catch (inner) {
+ if (!(inner instanceof MatchError)) {
+ throw inner;
+ }
+ inner.message = `matching property "${String(key)}":\n${inner.message}`;
+ throw inner;
+ }
+ }
+
+ return true;
+ }
+
+ // Match an object having all the expected properties and no more.
+ function matchObjectWithExactly(act, exp) {
+ matchObjectWithAtLeast(act, exp);
+
+ for (var key in act) {
+ if (!(key in exp)) {
+ throw new MatchError("unexpected property " + quote(key));
+ }
+ }
+
+ return true;
+ }
+
+ function matchFunction(act, exp) {
+ if (!isFunction(act))
+ throw new MatchError("expected function, got " + quote(act));
+
+ if (act !== exp)
+ throw new MatchError("expected function: " + exp +
+ "\nbut got different function: " + act);
+ }
+
+ function matchArray(act, exp) {
+ if (!isObject(act) || !("length" in act))
+ throw new MatchError("expected array-like object, got " + quote(act));
+
+ var length = exp.length;
+ if (act.length !== exp.length)
+ throw new MatchError("expected array-like object of length " + length + ", got " + quote(act));
+
+ for (var i = 0; i < length; i++) {
+ if (i in exp) {
+ if (!(i in act))
+ throw new MatchError("expected array property " + i + " not found in " + quote(act));
+ try {
+ match(act[i], exp[i]);
+ } catch (inner) {
+ if (!(inner instanceof MatchError)) {
+ throw inner;
+ }
+ inner.message = `matching array element [${i}]:\n${inner.message}`;
+ throw inner;
+ }
+ }
+ }
+
+ return true;
+ }
+
+ function match(act, exp) {
+ if (exp === Pattern.ANY)
+ return true;
+
+ if (exp instanceof Pattern)
+ return exp.match(act);
+
+ if (isAtom(exp))
+ return matchAtom(act, exp);
+
+ if (isArrayLike(exp))
+ return matchArray(act, exp);
+
+ if (isFunction(exp))
+ return matchFunction(act, exp);
+
+ if (isObject(exp))
+ return matchObjectWithAtLeast(act, exp);
+
+ throw new Error("bad pattern: " + JSON.stringify(exp));
+ }
+
+ return { Pattern: Pattern,
+ MatchError: MatchError };
+
+})();
+