diff --git a/peg.js b/peg.js
index c8545f3..5f2c76b 100644
--- a/peg.js
+++ b/peg.js
@@ -4,118 +4,131 @@
  * If it matches successfully, it returns some captured value, and the index following the match.
  *
  * @template T
- * @typedef {(source: string, index: number) => ([T, number] | null)} Peg.Pattern
+ * @typedef {{
+ *   (source: string, index: number): ([T, number] | null)
+ * }} Peg.Pattern
  */
-var Peg = {
-  /**
-   * Creates a pattern that wraps another pattern, transforming the returned value on a match
-   * @template T, U
-   * @param {Peg.Pattern<T>} pattern
-   * @param {(value: T)=> U} map
-   * @return {Peg.Pattern<U>}
-   */
-  Map(pattern, map) {
-    return function (source, index) {
+var Peg = window.Peg ?? {};
+
+/**
+ * Makes a pattern from a function, adding helper methods.
+ *
+ * @template T
+ * @param {(source: string, index: number) => ([T, number] | null)} pattern
+ * @returns {Peg.Pattern<T>}
+ */
+Peg.WrapPattern = function (pattern) {
+  return /** @type {Peg.Pattern<T>} */ (pattern);
+};
+
+/**
+ * Creates a pattern that wraps another pattern, transforming the returned value on a match
+ * @template T, U
+ * @param {Peg.Pattern<T>} pattern
+ * @param {(value: T)=> U} map
+ * @return {Peg.Pattern<U>}
+ */
+Peg.Map = function (pattern, map) {
+  return Peg.WrapPattern(function (source, index) {
+    const match = pattern(source, index);
+    return match ? [map(match[0]), match[1]] : null;
+  });
+};
+
+/**
+ * Creates a pattern matching a regex & returning any captures. The regex needs to be sticky (using the //y modifier)
+ * @param {RegExp} regex
+ * @return {Peg.Pattern<RegExpExecArray>}
+ */
+Peg.Regex = function (regex) {
+  return Peg.WrapPattern(function (source, index) {
+    regex.lastIndex = index;
+    const matches = regex.exec(source);
+    return matches ? [matches, regex.lastIndex] : null;
+  });
+};
+
+/**
+ * Creates a pattern that tries the given patterns, in order, until it finds one that matches at the current index.
+ * @template T
+ * @param {...Peg.Pattern<T>} patterns
+ * @return {Peg.Pattern<T>}
+ */
+Peg.Choose = function (...patterns) {
+  return Peg.WrapPattern(function (source, index) {
+    for (const pattern of patterns) {
       const match = pattern(source, index);
-      return match ? [map(match[0]), match[1]] : null;
-    };
-  },
-
-  /**
-   * Creates a pattern matching a regex & returning any captures. The regex needs to be sticky (using the //y modifier)
-   * @param {RegExp} regex
-   * @return {Peg.Pattern<RegExpExecArray>}
-   */
-  Regex(regex) {
-    return function (source, index) {
-      regex.lastIndex = index;
-      const matches = regex.exec(source);
-      return matches ? [matches, regex.lastIndex] : null;
-    };
-  },
-
-  /**
-   * Creates a pattern that tries the given patterns, in order, until it finds one that matches at the current index.
-   * @template T
-   * @param {...Peg.Pattern<T>} patterns
-   * @return {Peg.Pattern<T>}
-   */
-  Choose(...patterns) {
-    return function (source, index) {
-      for (const pattern of patterns) {
-        const match = pattern(source, index);
-        if (match) {
-          return match;
-        }
+      if (match) {
+        return match;
       }
-      return null;
-    };
-  },
+    }
+    return null;
+  });
+};
 
-  /**
-   * Creates a pattern that concatenates the given patterns, returning a tuple of their captured values.
-   *
-   * For example, if A matches "a" and captures 1, while B matches "b" and captures null,
-   * then `Sequence(A,B)` will match "ab" and capture [1, null]
-   * @template {unknown[]} T
-   * @param {{[K in keyof T]: Peg.Pattern<T[K]>}} patterns
-   * @return {Peg.Pattern<T>}
-   */
-  Sequence(...patterns) {
-    return function (source, index) {
-      const values = /** @type {T} */ (/** @type {unknown} */ ([]));
-      for (const pattern of patterns) {
-        const match = pattern(source, index);
-        if (match == null) {
-          return null;
-        }
-        values.push(match[0]);
-        index = match[1];
-      }
-      return [values, index];
-    };
-  },
-
-  /**
-   * Creates a pattern that matches consecutive runs of the given pattern, returning an array of all captures.
-   *
-   * The match only succeeds if the run is at least {min} instances long.
-   *
-   * If the given pattern does not consume input, the matching will be terminated to prevent an eternal loop.
-   *
-   * Note that if the minimum run is zero, this pattern will always succeed, but might not consume any input.
-   * @template {unknown} T
-   * @param {number} min
-   * @param {Peg.Pattern<T>} pattern
-   * @return {Peg.Pattern<T[]>}
-   */
-  AtLeast(min, pattern) {
-    return function (source, index) {
-      const values = /** @type {T[]} */ ([]);
-      do {
-        const match = pattern(source, index);
-        if (match == null) break;
-        values.push(match[0]);
-        if (index == match[1]) break;
-        index = match[1];
-      } while (true);
-      if (values.length >= min) {
-        return [values, index];
-      } else {
+/**
+ * Creates a pattern that concatenates the given patterns, returning a tuple of their captured values.
+ *
+ * For example, if A matches "a" and captures 1, while B matches "b" and captures null,
+ * then `Sequence(A,B)` will match "ab" and capture [1, null]
+ * @template {unknown[]} T
+ * @param {{[K in keyof T]: Peg.Pattern<T[K]>}} patterns
+ * @return {Peg.Pattern<T>}
+ */
+Peg.Sequence = function (...patterns) {
+  return Peg.WrapPattern(function (source, index) {
+    const values = /** @type {T} */ (/** @type {unknown} */ ([]));
+    for (const pattern of patterns) {
+      const match = pattern(source, index);
+      if (match == null) {
         return null;
       }
-    };
-  },
+      values.push(match[0]);
+      index = match[1];
+    }
+    return [values, index];
+  });
+};
 
-  /**
-   * Pattern that matches the end of input
-   * @type {Peg.Pattern<true>}
-   */
-  End(source, index) {
-    if (source.length == index) {
-      return [true, index];
+/**
+ * Creates a pattern that matches consecutive runs of the given pattern, returning an array of all captures.
+ *
+ * The match only succeeds if the run is at least {min} instances long.
+ *
+ * If the given pattern does not consume input, the matching will be terminated to prevent an eternal loop.
+ *
+ * Note that if the minimum run is zero, this pattern will always succeed, but might not consume any input.
+ * @template {unknown} T
+ * @param {number} min
+ * @param {Peg.Pattern<T>} pattern
+ * @return {Peg.Pattern<T[]>}
+ */
+Peg.AtLeast = function (min, pattern) {
+  return Peg.WrapPattern(function (source, index) {
+    const values = /** @type {T[]} */ ([]);
+    do {
+      const match = pattern(source, index);
+      if (match == null) break;
+      values.push(match[0]);
+      if (index == match[1]) break;
+      index = match[1];
+    } while (true);
+    if (values.length >= min) {
+      return [values, index];
     } else {
       return null;
     }
-  },
+  });
 };
+
+/**
+ * Pattern that matches the end of input
+ * @type {Peg.Pattern<true>}
+ */
+Peg.End = Peg.WrapPattern(function End(source, index) {
+  if (source.length == index) {
+    return [true, index];
+  } else {
+    return null;
+  }
+});