tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

Match.sys.mjs (4748B)


      1 // A little pattern-matching library.
      2 //
      3 // Ported from js/src/tests/js1_8_5/reflect-parse/Match.js for use with devtools
      4 // server xpcshell tests.
      5 
      6 export const Match = (function () {
      7  class Pattern {
      8    constructor(template) {
      9      this.template = template;
     10    }
     11 
     12    match(act) {
     13      return match(act, this.template);
     14    }
     15 
     16    matches(act) {
     17      try {
     18        return this.match(act);
     19      } catch (e) {
     20        if (e instanceof MatchError) {
     21          return false;
     22        }
     23      }
     24      return false;
     25    }
     26 
     27    assert(act, message) {
     28      try {
     29        return this.match(act);
     30      } catch (e) {
     31        if (e instanceof MatchError) {
     32          throw new Error((message || "failed match") + ": " + e.message);
     33        }
     34      }
     35      return false;
     36    }
     37 
     38    toString() {
     39      return "[object Pattern]";
     40    }
     41  }
     42 
     43  Pattern.ANY = new Pattern();
     44  Pattern.ANY.template = Pattern.ANY;
     45 
     46  Pattern.NUMBER = new Pattern();
     47  Pattern.NUMBER.match = function (act) {
     48    if (typeof act !== "number") {
     49      throw new MatchError("Expected number, got: " + quote(act));
     50    }
     51  };
     52 
     53  Pattern.NATURAL = new Pattern();
     54  Pattern.NATURAL.match = function (act) {
     55    if (typeof act !== "number" || act !== Math.floor(act) || act < 0) {
     56      throw new MatchError("Expected natural number, got: " + quote(act));
     57    }
     58  };
     59 
     60  const quote = uneval;
     61 
     62  class MatchError {
     63    constructor(msg) {
     64      this.message = msg;
     65    }
     66    toString() {
     67      return "match error: " + this.message;
     68    }
     69  }
     70 
     71  function isAtom(x) {
     72    return (
     73      typeof x === "number" ||
     74      typeof x === "string" ||
     75      typeof x === "boolean" ||
     76      x === null ||
     77      (typeof x === "object" && x instanceof RegExp)
     78    );
     79  }
     80 
     81  function isObject(x) {
     82    return x !== null && typeof x === "object";
     83  }
     84 
     85  function isFunction(x) {
     86    return typeof x === "function";
     87  }
     88 
     89  function isArrayLike(x) {
     90    return isObject(x) && "length" in x;
     91  }
     92 
     93  function matchAtom(act, exp) {
     94    if (typeof exp === "number" && isNaN(exp)) {
     95      if (typeof act !== "number" || !isNaN(act)) {
     96        throw new MatchError("expected NaN, got: " + quote(act));
     97      }
     98      return true;
     99    }
    100 
    101    if (exp === null) {
    102      if (act !== null) {
    103        throw new MatchError("expected null, got: " + quote(act));
    104      }
    105      return true;
    106    }
    107 
    108    if (exp instanceof RegExp) {
    109      if (!(act instanceof RegExp) || exp.source !== act.source) {
    110        throw new MatchError("expected " + quote(exp) + ", got: " + quote(act));
    111      }
    112      return true;
    113    }
    114 
    115    switch (typeof exp) {
    116      case "string":
    117        if (act !== exp) {
    118          throw new MatchError(
    119            "expected " + quote(exp) + ", got " + quote(act)
    120          );
    121        }
    122        return true;
    123      case "boolean":
    124      case "number":
    125        if (exp !== act) {
    126          throw new MatchError("expected " + exp + ", got " + quote(act));
    127        }
    128        return true;
    129    }
    130 
    131    throw new Error("bad pattern: " + exp.toSource());
    132  }
    133 
    134  function matchObject(act, exp) {
    135    if (!isObject(act)) {
    136      throw new MatchError("expected object, got " + quote(act));
    137    }
    138 
    139    for (const key in exp) {
    140      if (!(key in act)) {
    141        throw new MatchError(
    142          "expected property " + quote(key) + " not found in " + quote(act)
    143        );
    144      }
    145      match(act[key], exp[key]);
    146    }
    147 
    148    return true;
    149  }
    150 
    151  function matchFunction(act, exp) {
    152    if (!isFunction(act)) {
    153      throw new MatchError("expected function, got " + quote(act));
    154    }
    155 
    156    if (act !== exp) {
    157      throw new MatchError(
    158        "expected function: " + exp + "\nbut got different function: " + act
    159      );
    160    }
    161  }
    162 
    163  function matchArray(act, exp) {
    164    if (!isObject(act) || !("length" in act)) {
    165      throw new MatchError("expected array-like object, got " + quote(act));
    166    }
    167 
    168    const length = exp.length;
    169    if (act.length !== exp.length) {
    170      throw new MatchError(
    171        "expected array-like object of length " + length + ", got " + quote(act)
    172      );
    173    }
    174 
    175    for (let i = 0; i < length; i++) {
    176      if (i in exp) {
    177        if (!(i in act)) {
    178          throw new MatchError(
    179            "expected array property " + i + " not found in " + quote(act)
    180          );
    181        }
    182        match(act[i], exp[i]);
    183      }
    184    }
    185 
    186    return true;
    187  }
    188 
    189  function match(act, exp) {
    190    if (exp === Pattern.ANY) {
    191      return true;
    192    }
    193 
    194    if (exp instanceof Pattern) {
    195      return exp.match(act);
    196    }
    197 
    198    if (isAtom(exp)) {
    199      return matchAtom(act, exp);
    200    }
    201 
    202    if (isArrayLike(exp)) {
    203      return matchArray(act, exp);
    204    }
    205 
    206    if (isFunction(exp)) {
    207      return matchFunction(act, exp);
    208    }
    209 
    210    return matchObject(act, exp);
    211  }
    212 
    213  return { Pattern, MatchError };
    214 })();