changeset 0:3d1b8e96f5dc

Initial commit
author Mike Pavone <pavone@retrodev.com>
date Sun, 18 Mar 2012 12:03:04 -0700
parents
children a57a12bdae96
files editor.css editor.js index.html mquery.js parser.js peg.js testparse.html
diffstat 7 files changed, 5381 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/editor.css	Sun Mar 18 12:03:04 2012 -0700
@@ -0,0 +1,75 @@
+
+body, html
+{
+	width: 100%;
+	height: 100%;
+	margin: 0;
+}
+
+body > div
+{
+	margin: 0;
+}
+
+.controls
+{
+	width: 15%;
+	height: 100%;
+	display: inline-block;
+	padding: 0;
+	margin: 0;
+	vertical-align: top;
+}
+
+#src
+{
+	width: 70%;
+	display: inline-block;
+	padding: 0;
+	margin: 0;
+}
+
+ul
+{
+	border-style: solid;
+	border-color: black;
+	border-width: 1px;
+	display: block;
+	padding: 2px;
+	box-sizing: border-box;
+	-moz-box-sizing: border-box;
+	-webkit-box-sizing: border-box;
+	height: 50%;
+	margin: 0;
+	overflow: auto;
+}
+
+ul:first-child
+{
+	border-bottom-width: 0px;
+}
+
+#operators, .showops > #builtin
+{
+	display: none;
+}
+
+.showops > #operators
+{
+	display: block;
+}
+
+li
+{
+	display: inline-block;
+	border-style: solid;
+	border-color: lightgrey;
+	border-width: 1px;
+	padding: 2px;
+	margin: 4px;
+	min-width: 9mm;
+	min-height: 9mm;
+	cursor: pointer;
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/editor.js	Sun Mar 18 12:03:04 2012 -0700
@@ -0,0 +1,15 @@
+
+
+onReady(function() {
+	each(qall('li'), function (idx,el) {
+		el.onclick = function (event) {
+			q('#src').innerHTML += el.innerHTML;
+		};
+	});
+	q('#ops_button').onclick = function (event) {
+		addClass(this.parentNode.parentNode, 'showops');
+	};
+	q('#builtin_button').onclick = function (event) {
+		removeClass(this.parentNode.parentNode, 'showops');
+	};
+});
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/index.html	Sun Mar 18 12:03:04 2012 -0700
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<html>
+<head>
+	<title>Awesome!</title>
+	<script src="mquery.js"></script>
+	<script src="editor.js"></script>
+	<link rel="stylesheet" href="editor.css">
+</head>
+<body>
+	<div class="controls">
+		<ul id="builtin">
+			<li id="ops_button">operators</li>
+			<li>get:</li>
+			<li>set:</li>
+			<li>length</li>
+			<li>if: then: else:</li>
+		</ul>
+		<ul id="operators">
+			<li id="builtin_button">builtins</li>
+			<li>&lt;</li>
+			<li>&gt;</li>
+			<li>+</li>
+			<li>-</li>
+			<li>*</li>
+			<li>/</li>
+			<li>&&</li>
+			<li>||</li>
+		</ul>
+		<ul id="inscope"></ul>
+	</div><div id="src"></div><div class="controls">
+		<ul></ul>
+		<ul></ul>
+	</div>
+</body>
+</html>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mquery.js	Sun Mar 18 12:03:04 2012 -0700
@@ -0,0 +1,70 @@
+function each(container, fun)
+{
+	if (container instanceof Array) {
+		for (var i = 0; i < container.length; i++) {
+			fun(i, conatiner[i]);
+		}
+	} else {
+		for (var i in container) {
+			if (container.hasOwnProperty(i)) {
+				fun(i, container[i]);
+			}
+		}
+	}
+}
+
+function q(query)
+{
+	return document.querySelector(query);
+}
+function qall(query)
+{
+	return document.querySelectorAll(query);
+}
+
+function hide(el)
+{
+	el.style.display = 'none';
+}
+
+function show(el, dtype)
+{
+	if (dtype === undefined) {
+		dtype = 'block';
+	}
+	el.style.display
+}
+
+function onReady(fun)
+{
+	if (document.readyState == 'complete') {
+		fun();
+	} else {
+		document.onreadystatechange = function() {
+			if (document.readyState == 'complete') {
+				fun();
+				document.onreadystatechange = null;
+			}
+		}
+	}
+}
+
+function addClass(el, classname)
+{
+	var classes = el.className.split(' ');
+	if (classes.indexOf(classname) == -1) {
+		classes.push(classname);
+		el.className = classes.join(' ');
+	}
+}
+
+function removeClass(el, classname)
+{
+	var classes = el.className.split(' ');
+	var idx = classes.indexOf(classname);
+	if (idx > -1) {
+		classes.splice(idx, 1);
+		el.className = classes.join(' ');
+	} 
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/parser.js	Sun Mar 18 12:03:04 2012 -0700
@@ -0,0 +1,30 @@
+var grammar = 
+'start = addsub;' +
+'addsub = left:muldiv " "* op:("+"/"-") " "* right:addsub [ \\n\\r]* { return {type: "op", left: left, op: op, right: right }; } / muldiv;'+
+'muldiv = left:primlitsym " "* op:("*"/"/") " "* right:muldiv { return {type: "op", left: left, op: op, right: right }; } / primlitsym;'+
+'primlitsym = [ \\n\\r]* val:(float / int / string / symbol / object / lambda / "(" expr:addsub " "* ")" { return expr; }) { return val; };' +
+'symbol = chars:[a-zA-Z_!?@]+ middle:[a-zA-Z_!?@0-9:]* trailing:[a-zA-Z_!?@0-9]* { return {type: "symbol", name: chars.join("") + middle.join("") + trailing.join("")}; };' +
+'float = digits:[0-9]+ "." decimals:[0-9]+ { return {type: "floatlit", value: parseFloat(digits.join("") + "." + decimals.join("")) }; };' +
+'int = digits:[0-9]+ { return {type: "intlit", value: parseInt(digits.join(""), 10)}; };' +
+'string = "\\"" text:[^\\"]* "\\"" { return {type: "strlit", value: text.join("")}; };' +
+'object = "#{" messages:assignment* "}" { return {type: "object", messages: messages}; };' +
+'assignment = [ \\n\\r]* sym:symbol " "* "<-" expr:addsub { return {type: "assignment", symbol: sym, expression: expr}; }' +
+'lambda = args:((& ":") argname+  )? "{" exprs:(assignment / addsub)* [ \\n\\r]* "}" { return {type: "lambda", args: args[1], expressions: exprs}; };' +
+'argname = chars:[a-zA-Z_!?@:]+ trailing:[a-zA-Z_!?@0-9]* " "* { return {type: "symbol", name: chars.join("") + trailing.join("")}; };';
+var parser = PEG.buildParser(grammar);
+
+//var parser = PEG.buildParser('start = expr; expr = int; int = digits:[0-9]+ { return parseInt(digits.join(""), 10); }');
+
+onReady(function() {
+	q('input[type=button]').onclick = function() {
+		var text = q('textarea').value;
+		try {
+			var parsed = parser.parse(text);
+			q('div').innerHTML = text + "<br><br>" + JSON.stringify(parsed);
+			console.log(parsed);
+		} catch(e) {
+			q('div').innerHTML = e.message + '<br>Line: ' + e.line + '<br>Col: ' + e.column;
+		}
+	}
+});
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/peg.js	Sun Mar 18 12:03:04 2012 -0700
@@ -0,0 +1,5141 @@
+/* PEG.js 0.6.2 (http://pegjs.majda.cz/) */
+
+(function() {
+
+var undefined;
+
+var PEG = {
+  /* PEG.js version. */
+  VERSION: "0.6.2",
+
+  /*
+   * Generates a parser from a specified grammar and returns it.
+   *
+   * The grammar must be a string in the format described by the metagramar in
+   * the parser.pegjs file.
+   *
+   * Throws |PEG.parser.SyntaxError| if the grammar contains a syntax error or
+   * |PEG.GrammarError| if it contains a semantic error. Note that not all
+   * errors are detected during the generation and some may protrude to the
+   * generated parser and cause its malfunction.
+   */
+  buildParser: function(grammar) {
+    return PEG.compiler.compile(PEG.parser.parse(grammar));
+  }
+};
+
+/* Thrown when the grammar contains an error. */
+
+PEG.GrammarError = function(message) {
+  this.name = "PEG.GrammarError";
+  this.message = message;
+};
+
+PEG.GrammarError.prototype = Error.prototype;
+
+function contains(array, value) {
+  /*
+   * Stupid IE does not have Array.prototype.indexOf, otherwise this function
+   * would be a one-liner.
+   */
+  var length = array.length;
+  for (var i = 0; i < length; i++) {
+    if (array[i] === value) {
+      return true;
+    }
+  }
+  return false;
+}
+
+function each(array, callback) {
+  var length = array.length;
+  for (var i = 0; i < length; i++) {
+    callback(array[i]);
+  }
+}
+
+function map(array, callback) {
+  var result = [];
+  var length = array.length;
+  for (var i = 0; i < length; i++) {
+    result[i] = callback(array[i]);
+  }
+  return result;
+}
+
+/*
+ * Returns a string padded on the left to a desired length with a character.
+ *
+ * The code needs to be in sync with th code template in the compilation
+ * function for "action" nodes.
+ */
+function padLeft(input, padding, length) {
+  var result = input;
+
+  var padLength = length - input.length;
+  for (var i = 0; i < padLength; i++) {
+    result = padding + result;
+  }
+
+  return result;
+}
+
+/*
+ * Returns an escape sequence for given character. Uses \x for characters <=
+ * 0xFF to save space, \u for the rest.
+ *
+ * The code needs to be in sync with th code template in the compilation
+ * function for "action" nodes.
+ */
+function escape(ch) {
+  var charCode = ch.charCodeAt(0);
+
+  if (charCode <= 0xFF) {
+    var escapeChar = 'x';
+    var length = 2;
+  } else {
+    var escapeChar = 'u';
+    var length = 4;
+  }
+
+  return '\\' + escapeChar + padLeft(charCode.toString(16).toUpperCase(), '0', length);
+}
+
+/*
+ * Surrounds the string with quotes and escapes characters inside so that the
+ * result is a valid JavaScript string.
+ *
+ * The code needs to be in sync with th code template in the compilation
+ * function for "action" nodes.
+ */
+function quote(s) {
+  /*
+   * ECMA-262, 5th ed., 7.8.4: All characters may appear literally in a string
+   * literal except for the closing quote character, backslash, carriage return,
+   * line separator, paragraph separator, and line feed. Any character may
+   * appear in the form of an escape sequence.
+   *
+   * For portability, we also escape escape all non-ASCII characters.
+   */
+  return '"' + s
+    .replace(/\\/g, '\\\\')            // backslash
+    .replace(/"/g, '\\"')              // closing quote character
+    .replace(/\r/g, '\\r')             // carriage return
+    .replace(/\n/g, '\\n')             // line feed
+    .replace(/[\x80-\uFFFF]/g, escape) // non-ASCII characters
+    + '"';
+};
+
+/*
+ * Escapes characters inside the string so that it can be used as a list of
+ * characters in a character class of a regular expression.
+ */
+function quoteForRegexpClass(s) {
+  /*
+   * Based on ECMA-262, 5th ed., 7.8.5 & 15.10.1.
+   *
+   * For portability, we also escape escape all non-ASCII characters.
+   */
+  return s
+    .replace(/\\/g, '\\\\')            // backslash
+    .replace(/\0/g, '\\0')             // null, IE needs this
+    .replace(/\//g, '\\/')             // closing slash
+    .replace(/]/g, '\\]')              // closing bracket
+    .replace(/-/g, '\\-')              // dash
+    .replace(/\r/g, '\\r')             // carriage return
+    .replace(/\n/g, '\\n')             // line feed
+    .replace(/[\x80-\uFFFF]/g, escape) // non-ASCII characters
+}
+
+/*
+ * Builds a node visitor -- a function which takes a node and any number of
+ * other parameters, calls an appropriate function according to the node type,
+ * passes it all its parameters and returns its value. The functions for various
+ * node types are passed in a parameter to |buildNodeVisitor| as a hash.
+ */
+function buildNodeVisitor(functions) {
+  return function(node) {
+    return functions[node.type].apply(null, arguments);
+  }
+}
+PEG.parser = (function(){
+  /* Generated by PEG.js 0.6.2 (http://pegjs.majda.cz/). */
+  
+  var result = {
+    /*
+     * Parses the input with a generated parser. If the parsing is successfull,
+     * returns a value explicitly or implicitly specified by the grammar from
+     * which the parser was generated (see |PEG.buildParser|). If the parsing is
+     * unsuccessful, throws |PEG.parser.SyntaxError| describing the error.
+     */
+    parse: function(input, startRule) {
+      var parseFunctions = {
+        "__": parse___,
+        "action": parse_action,
+        "and": parse_and,
+        "braced": parse_braced,
+        "bracketDelimitedCharacter": parse_bracketDelimitedCharacter,
+        "choice": parse_choice,
+        "class": parse_class,
+        "classCharacter": parse_classCharacter,
+        "classCharacterRange": parse_classCharacterRange,
+        "colon": parse_colon,
+        "comment": parse_comment,
+        "digit": parse_digit,
+        "dot": parse_dot,
+        "doubleQuotedCharacter": parse_doubleQuotedCharacter,
+        "doubleQuotedLiteral": parse_doubleQuotedLiteral,
+        "eol": parse_eol,
+        "eolChar": parse_eolChar,
+        "eolEscapeSequence": parse_eolEscapeSequence,
+        "equals": parse_equals,
+        "grammar": parse_grammar,
+        "hexDigit": parse_hexDigit,
+        "hexEscapeSequence": parse_hexEscapeSequence,
+        "identifier": parse_identifier,
+        "initializer": parse_initializer,
+        "labeled": parse_labeled,
+        "letter": parse_letter,
+        "literal": parse_literal,
+        "lowerCaseLetter": parse_lowerCaseLetter,
+        "lparen": parse_lparen,
+        "multiLineComment": parse_multiLineComment,
+        "nonBraceCharacter": parse_nonBraceCharacter,
+        "nonBraceCharacters": parse_nonBraceCharacters,
+        "not": parse_not,
+        "plus": parse_plus,
+        "prefixed": parse_prefixed,
+        "primary": parse_primary,
+        "question": parse_question,
+        "rparen": parse_rparen,
+        "rule": parse_rule,
+        "semicolon": parse_semicolon,
+        "sequence": parse_sequence,
+        "simpleBracketDelimitedCharacter": parse_simpleBracketDelimitedCharacter,
+        "simpleDoubleQuotedCharacter": parse_simpleDoubleQuotedCharacter,
+        "simpleEscapeSequence": parse_simpleEscapeSequence,
+        "simpleSingleQuotedCharacter": parse_simpleSingleQuotedCharacter,
+        "singleLineComment": parse_singleLineComment,
+        "singleQuotedCharacter": parse_singleQuotedCharacter,
+        "singleQuotedLiteral": parse_singleQuotedLiteral,
+        "slash": parse_slash,
+        "star": parse_star,
+        "suffixed": parse_suffixed,
+        "unicodeEscapeSequence": parse_unicodeEscapeSequence,
+        "upperCaseLetter": parse_upperCaseLetter,
+        "whitespace": parse_whitespace,
+        "zeroEscapeSequence": parse_zeroEscapeSequence
+      };
+      
+      if (startRule !== undefined) {
+        if (parseFunctions[startRule] === undefined) {
+          throw new Error("Invalid rule name: " + quote(startRule) + ".");
+        }
+      } else {
+        startRule = "grammar";
+      }
+      
+      var pos = 0;
+      var reportMatchFailures = true;
+      var rightmostMatchFailuresPos = 0;
+      var rightmostMatchFailuresExpected = [];
+      var cache = {};
+      
+      function padLeft(input, padding, length) {
+        var result = input;
+        
+        var padLength = length - input.length;
+        for (var i = 0; i < padLength; i++) {
+          result = padding + result;
+        }
+        
+        return result;
+      }
+      
+      function escape(ch) {
+        var charCode = ch.charCodeAt(0);
+        
+        if (charCode <= 0xFF) {
+          var escapeChar = 'x';
+          var length = 2;
+        } else {
+          var escapeChar = 'u';
+          var length = 4;
+        }
+        
+        return '\\' + escapeChar + padLeft(charCode.toString(16).toUpperCase(), '0', length);
+      }
+      
+      function quote(s) {
+        /*
+         * ECMA-262, 5th ed., 7.8.4: All characters may appear literally in a
+         * string literal except for the closing quote character, backslash,
+         * carriage return, line separator, paragraph separator, and line feed.
+         * Any character may appear in the form of an escape sequence.
+         */
+        return '"' + s
+          .replace(/\\/g, '\\\\')            // backslash
+          .replace(/"/g, '\\"')              // closing quote character
+          .replace(/\r/g, '\\r')             // carriage return
+          .replace(/\n/g, '\\n')             // line feed
+          .replace(/[\x80-\uFFFF]/g, escape) // non-ASCII characters
+          + '"';
+      }
+      
+      function matchFailed(failure) {
+        if (pos < rightmostMatchFailuresPos) {
+          return;
+        }
+        
+        if (pos > rightmostMatchFailuresPos) {
+          rightmostMatchFailuresPos = pos;
+          rightmostMatchFailuresExpected = [];
+        }
+        
+        rightmostMatchFailuresExpected.push(failure);
+      }
+      
+      function parse_grammar() {
+        var cacheKey = 'grammar@' + pos;
+        var cachedResult = cache[cacheKey];
+        if (cachedResult) {
+          pos = cachedResult.nextPos;
+          return cachedResult.result;
+        }
+        
+        
+        var savedPos0 = pos;
+        var savedPos1 = pos;
+        var result3 = parse___();
+        if (result3 !== null) {
+          var result7 = parse_initializer();
+          var result4 = result7 !== null ? result7 : '';
+          if (result4 !== null) {
+            var result6 = parse_rule();
+            if (result6 !== null) {
+              var result5 = [];
+              while (result6 !== null) {
+                result5.push(result6);
+                var result6 = parse_rule();
+              }
+            } else {
+              var result5 = null;
+            }
+            if (result5 !== null) {
+              var result1 = [result3, result4, result5];
+            } else {
+              var result1 = null;
+              pos = savedPos1;
+            }
+          } else {
+            var result1 = null;
+            pos = savedPos1;
+          }
+        } else {
+          var result1 = null;
+          pos = savedPos1;
+        }
+        var result2 = result1 !== null
+          ? (function(initializer, rules) {
+                var rulesConverted = {};
+                each(rules, function(rule) { rulesConverted[rule.name] = rule; });
+          
+                return {
+                  type:        "grammar",
+                  initializer: initializer !== "" ? initializer : null,
+                  rules:       rulesConverted,
+                  startRule:   rules[0].name
+                }
+              })(result1[1], result1[2])
+          : null;
+        if (result2 !== null) {
+          var result0 = result2;
+        } else {
+          var result0 = null;
+          pos = savedPos0;
+        }
+        
+        
+        
+        cache[cacheKey] = {
+          nextPos: pos,
+          result:  result0
+        };
+        return result0;
+      }
+      
+      function parse_initializer() {
+        var cacheKey = 'initializer@' + pos;
+        var cachedResult = cache[cacheKey];
+        if (cachedResult) {
+          pos = cachedResult.nextPos;
+          return cachedResult.result;
+        }
+        
+        
+        var savedPos0 = pos;
+        var savedPos1 = pos;
+        var result3 = parse_action();
+        if (result3 !== null) {
+          var result5 = parse_semicolon();
+          var result4 = result5 !== null ? result5 : '';
+          if (result4 !== null) {
+            var result1 = [result3, result4];
+          } else {
+            var result1 = null;
+            pos = savedPos1;
+          }
+        } else {
+          var result1 = null;
+          pos = savedPos1;
+        }
+        var result2 = result1 !== null
+          ? (function(code) {
+                return {
+                  type: "initializer",
+                  code: code
+                };
+              })(result1[0])
+          : null;
+        if (result2 !== null) {
+          var result0 = result2;
+        } else {
+          var result0 = null;
+          pos = savedPos0;
+        }
+        
+        
+        
+        cache[cacheKey] = {
+          nextPos: pos,
+          result:  result0
+        };
+        return result0;
+      }
+      
+      function parse_rule() {
+        var cacheKey = 'rule@' + pos;
+        var cachedResult = cache[cacheKey];
+        if (cachedResult) {
+          pos = cachedResult.nextPos;
+          return cachedResult.result;
+        }
+        
+        
+        var savedPos0 = pos;
+        var savedPos1 = pos;
+        var result3 = parse_identifier();
+        if (result3 !== null) {
+          var result10 = parse_literal();
+          if (result10 !== null) {
+            var result4 = result10;
+          } else {
+            if (input.substr(pos, 0) === "") {
+              var result9 = "";
+              pos += 0;
+            } else {
+              var result9 = null;
+              if (reportMatchFailures) {
+                matchFailed("\"\"");
+              }
+            }
+            if (result9 !== null) {
+              var result4 = result9;
+            } else {
+              var result4 = null;;
+            };
+          }
+          if (result4 !== null) {
+            var result5 = parse_equals();
+            if (result5 !== null) {
+              var result6 = parse_choice();
+              if (result6 !== null) {
+                var result8 = parse_semicolon();
+                var result7 = result8 !== null ? result8 : '';
+                if (result7 !== null) {
+                  var result1 = [result3, result4, result5, result6, result7];
+                } else {
+                  var result1 = null;
+                  pos = savedPos1;
+                }
+              } else {
+                var result1 = null;
+                pos = savedPos1;
+              }
+            } else {
+              var result1 = null;
+              pos = savedPos1;
+            }
+          } else {
+            var result1 = null;
+            pos = savedPos1;
+          }
+        } else {
+          var result1 = null;
+          pos = savedPos1;
+        }
+        var result2 = result1 !== null
+          ? (function(name, displayName, expression) {
+                return {
+                  type:        "rule",
+                  name:        name,
+                  displayName: displayName !== "" ? displayName : null,
+                  expression:  expression
+                };
+              })(result1[0], result1[1], result1[3])
+          : null;
+        if (result2 !== null) {
+          var result0 = result2;
+        } else {
+          var result0 = null;
+          pos = savedPos0;
+        }
+        
+        
+        
+        cache[cacheKey] = {
+          nextPos: pos,
+          result:  result0
+        };
+        return result0;
+      }
+      
+      function parse_choice() {
+        var cacheKey = 'choice@' + pos;
+        var cachedResult = cache[cacheKey];
+        if (cachedResult) {
+          pos = cachedResult.nextPos;
+          return cachedResult.result;
+        }
+        
+        
+        var savedPos0 = pos;
+        var savedPos1 = pos;
+        var result3 = parse_sequence();
+        if (result3 !== null) {
+          var result4 = [];
+          var savedPos2 = pos;
+          var result6 = parse_slash();
+          if (result6 !== null) {
+            var result7 = parse_sequence();
+            if (result7 !== null) {
+              var result5 = [result6, result7];
+            } else {
+              var result5 = null;
+              pos = savedPos2;
+            }
+          } else {
+            var result5 = null;
+            pos = savedPos2;
+          }
+          while (result5 !== null) {
+            result4.push(result5);
+            var savedPos2 = pos;
+            var result6 = parse_slash();
+            if (result6 !== null) {
+              var result7 = parse_sequence();
+              if (result7 !== null) {
+                var result5 = [result6, result7];
+              } else {
+                var result5 = null;
+                pos = savedPos2;
+              }
+            } else {
+              var result5 = null;
+              pos = savedPos2;
+            }
+          }
+          if (result4 !== null) {
+            var result1 = [result3, result4];
+          } else {
+            var result1 = null;
+            pos = savedPos1;
+          }
+        } else {
+          var result1 = null;
+          pos = savedPos1;
+        }
+        var result2 = result1 !== null
+          ? (function(head, tail) {
+                if (tail.length > 0) {
+                  var alternatives = [head].concat(map(
+                      tail,
+                      function(element) { return element[1]; }
+                  ));
+                  return {
+                    type:         "choice",
+                    alternatives: alternatives
+                  }
+                } else {
+                  return head;
+                }
+              })(result1[0], result1[1])
+          : null;
+        if (result2 !== null) {
+          var result0 = result2;
+        } else {
+          var result0 = null;
+          pos = savedPos0;
+        }
+        
+        
+        
+        cache[cacheKey] = {
+          nextPos: pos,
+          result:  result0
+        };
+        return result0;
+      }
+      
+      function parse_sequence() {
+        var cacheKey = 'sequence@' + pos;
+        var cachedResult = cache[cacheKey];
+        if (cachedResult) {
+          pos = cachedResult.nextPos;
+          return cachedResult.result;
+        }
+        
+        
+        var savedPos1 = pos;
+        var savedPos2 = pos;
+        var result8 = [];
+        var result10 = parse_labeled();
+        while (result10 !== null) {
+          result8.push(result10);
+          var result10 = parse_labeled();
+        }
+        if (result8 !== null) {
+          var result9 = parse_action();
+          if (result9 !== null) {
+            var result6 = [result8, result9];
+          } else {
+            var result6 = null;
+            pos = savedPos2;
+          }
+        } else {
+          var result6 = null;
+          pos = savedPos2;
+        }
+        var result7 = result6 !== null
+          ? (function(elements, code) {
+                var expression = elements.length != 1
+                  ? {
+                      type:     "sequence",
+                      elements: elements
+                    }
+                  : elements[0];
+                return {
+                  type:       "action",
+                  expression: expression,
+                  code:       code
+                };
+              })(result6[0], result6[1])
+          : null;
+        if (result7 !== null) {
+          var result5 = result7;
+        } else {
+          var result5 = null;
+          pos = savedPos1;
+        }
+        if (result5 !== null) {
+          var result0 = result5;
+        } else {
+          var savedPos0 = pos;
+          var result2 = [];
+          var result4 = parse_labeled();
+          while (result4 !== null) {
+            result2.push(result4);
+            var result4 = parse_labeled();
+          }
+          var result3 = result2 !== null
+            ? (function(elements) {
+                  return elements.length != 1
+                    ? {
+                        type:     "sequence",
+                        elements: elements
+                      }
+                    : elements[0];
+                })(result2)
+            : null;
+          if (result3 !== null) {
+            var result1 = result3;
+          } else {
+            var result1 = null;
+            pos = savedPos0;
+          }
+          if (result1 !== null) {
+            var result0 = result1;
+          } else {
+            var result0 = null;;
+          };
+        }
+        
+        
+        
+        cache[cacheKey] = {
+          nextPos: pos,
+          result:  result0
+        };
+        return result0;
+      }
+      
+      function parse_labeled() {
+        var cacheKey = 'labeled@' + pos;
+        var cachedResult = cache[cacheKey];
+        if (cachedResult) {
+          pos = cachedResult.nextPos;
+          return cachedResult.result;
+        }
+        
+        
+        var savedPos0 = pos;
+        var savedPos1 = pos;
+        var result5 = parse_identifier();
+        if (result5 !== null) {
+          var result6 = parse_colon();
+          if (result6 !== null) {
+            var result7 = parse_prefixed();
+            if (result7 !== null) {
+              var result3 = [result5, result6, result7];
+            } else {
+              var result3 = null;
+              pos = savedPos1;
+            }
+          } else {
+            var result3 = null;
+            pos = savedPos1;
+          }
+        } else {
+          var result3 = null;
+          pos = savedPos1;
+        }
+        var result4 = result3 !== null
+          ? (function(label, expression) {
+                return {
+                  type:       "labeled",
+                  label:      label,
+                  expression: expression
+                };
+              })(result3[0], result3[2])
+          : null;
+        if (result4 !== null) {
+          var result2 = result4;
+        } else {
+          var result2 = null;
+          pos = savedPos0;
+        }
+        if (result2 !== null) {
+          var result0 = result2;
+        } else {
+          var result1 = parse_prefixed();
+          if (result1 !== null) {
+            var result0 = result1;
+          } else {
+            var result0 = null;;
+          };
+        }
+        
+        
+        
+        cache[cacheKey] = {
+          nextPos: pos,
+          result:  result0
+        };
+        return result0;
+      }
+      
+      function parse_prefixed() {
+        var cacheKey = 'prefixed@' + pos;
+        var cachedResult = cache[cacheKey];
+        if (cachedResult) {
+          pos = cachedResult.nextPos;
+          return cachedResult.result;
+        }
+        
+        
+        var savedPos6 = pos;
+        var savedPos7 = pos;
+        var result20 = parse_and();
+        if (result20 !== null) {
+          var result21 = parse_action();
+          if (result21 !== null) {
+            var result18 = [result20, result21];
+          } else {
+            var result18 = null;
+            pos = savedPos7;
+          }
+        } else {
+          var result18 = null;
+          pos = savedPos7;
+        }
+        var result19 = result18 !== null
+          ? (function(code) {
+                return {
+                  type: "semantic_and",
+                  code: code
+                };
+              })(result18[1])
+          : null;
+        if (result19 !== null) {
+          var result17 = result19;
+        } else {
+          var result17 = null;
+          pos = savedPos6;
+        }
+        if (result17 !== null) {
+          var result0 = result17;
+        } else {
+          var savedPos4 = pos;
+          var savedPos5 = pos;
+          var result15 = parse_and();
+          if (result15 !== null) {
+            var result16 = parse_suffixed();
+            if (result16 !== null) {
+              var result13 = [result15, result16];
+            } else {
+              var result13 = null;
+              pos = savedPos5;
+            }
+          } else {
+            var result13 = null;
+            pos = savedPos5;
+          }
+          var result14 = result13 !== null
+            ? (function(expression) {
+                  return {
+                    type:       "simple_and",
+                    expression: expression
+                  };
+                })(result13[1])
+            : null;
+          if (result14 !== null) {
+            var result12 = result14;
+          } else {
+            var result12 = null;
+            pos = savedPos4;
+          }
+          if (result12 !== null) {
+            var result0 = result12;
+          } else {
+            var savedPos2 = pos;
+            var savedPos3 = pos;
+            var result10 = parse_not();
+            if (result10 !== null) {
+              var result11 = parse_action();
+              if (result11 !== null) {
+                var result8 = [result10, result11];
+              } else {
+                var result8 = null;
+                pos = savedPos3;
+              }
+            } else {
+              var result8 = null;
+              pos = savedPos3;
+            }
+            var result9 = result8 !== null
+              ? (function(code) {
+                    return {
+                      type: "semantic_not",
+                      code: code
+                    };
+                  })(result8[1])
+              : null;
+            if (result9 !== null) {
+              var result7 = result9;
+            } else {
+              var result7 = null;
+              pos = savedPos2;
+            }
+            if (result7 !== null) {
+              var result0 = result7;
+            } else {
+              var savedPos0 = pos;
+              var savedPos1 = pos;
+              var result5 = parse_not();
+              if (result5 !== null) {
+                var result6 = parse_suffixed();
+                if (result6 !== null) {
+                  var result3 = [result5, result6];
+                } else {
+                  var result3 = null;
+                  pos = savedPos1;
+                }
+              } else {
+                var result3 = null;
+                pos = savedPos1;
+              }
+              var result4 = result3 !== null
+                ? (function(expression) {
+                      return {
+                        type:       "simple_not",
+                        expression: expression
+                      };
+                    })(result3[1])
+                : null;
+              if (result4 !== null) {
+                var result2 = result4;
+              } else {
+                var result2 = null;
+                pos = savedPos0;
+              }
+              if (result2 !== null) {
+                var result0 = result2;
+              } else {
+                var result1 = parse_suffixed();
+                if (result1 !== null) {
+                  var result0 = result1;
+                } else {
+                  var result0 = null;;
+                };
+              };
+            };
+          };
+        }
+        
+        
+        
+        cache[cacheKey] = {
+          nextPos: pos,
+          result:  result0
+        };
+        return result0;
+      }
+      
+      function parse_suffixed() {
+        var cacheKey = 'suffixed@' + pos;
+        var cachedResult = cache[cacheKey];
+        if (cachedResult) {
+          pos = cachedResult.nextPos;
+          return cachedResult.result;
+        }
+        
+        
+        var savedPos4 = pos;
+        var savedPos5 = pos;
+        var result15 = parse_primary();
+        if (result15 !== null) {
+          var result16 = parse_question();
+          if (result16 !== null) {
+            var result13 = [result15, result16];
+          } else {
+            var result13 = null;
+            pos = savedPos5;
+          }
+        } else {
+          var result13 = null;
+          pos = savedPos5;
+        }
+        var result14 = result13 !== null
+          ? (function(expression) {
+                return {
+                  type:       "optional",
+                  expression: expression
+                };
+              })(result13[0])
+          : null;
+        if (result14 !== null) {
+          var result12 = result14;
+        } else {
+          var result12 = null;
+          pos = savedPos4;
+        }
+        if (result12 !== null) {
+          var result0 = result12;
+        } else {
+          var savedPos2 = pos;
+          var savedPos3 = pos;
+          var result10 = parse_primary();
+          if (result10 !== null) {
+            var result11 = parse_star();
+            if (result11 !== null) {
+              var result8 = [result10, result11];
+            } else {
+              var result8 = null;
+              pos = savedPos3;
+            }
+          } else {
+            var result8 = null;
+            pos = savedPos3;
+          }
+          var result9 = result8 !== null
+            ? (function(expression) {
+                  return {
+                    type:       "zero_or_more",
+                    expression: expression
+                  };
+                })(result8[0])
+            : null;
+          if (result9 !== null) {
+            var result7 = result9;
+          } else {
+            var result7 = null;
+            pos = savedPos2;
+          }
+          if (result7 !== null) {
+            var result0 = result7;
+          } else {
+            var savedPos0 = pos;
+            var savedPos1 = pos;
+            var result5 = parse_primary();
+            if (result5 !== null) {
+              var result6 = parse_plus();
+              if (result6 !== null) {
+                var result3 = [result5, result6];
+              } else {
+                var result3 = null;
+                pos = savedPos1;
+              }
+            } else {
+              var result3 = null;
+              pos = savedPos1;
+            }
+            var result4 = result3 !== null
+              ? (function(expression) {
+                    return {
+                      type:       "one_or_more",
+                      expression: expression
+                    };
+                  })(result3[0])
+              : null;
+            if (result4 !== null) {
+              var result2 = result4;
+            } else {
+              var result2 = null;
+              pos = savedPos0;
+            }
+            if (result2 !== null) {
+              var result0 = result2;
+            } else {
+              var result1 = parse_primary();
+              if (result1 !== null) {
+                var result0 = result1;
+              } else {
+                var result0 = null;;
+              };
+            };
+          };
+        }
+        
+        
+        
+        cache[cacheKey] = {
+          nextPos: pos,
+          result:  result0
+        };
+        return result0;
+      }
+      
+      function parse_primary() {
+        var cacheKey = 'primary@' + pos;
+        var cachedResult = cache[cacheKey];
+        if (cachedResult) {
+          pos = cachedResult.nextPos;
+          return cachedResult.result;
+        }
+        
+        
+        var savedPos4 = pos;
+        var savedPos5 = pos;
+        var result17 = parse_identifier();
+        if (result17 !== null) {
+          var savedPos6 = pos;
+          var savedReportMatchFailuresVar0 = reportMatchFailures;
+          reportMatchFailures = false;
+          var savedPos7 = pos;
+          var result23 = parse_literal();
+          if (result23 !== null) {
+            var result20 = result23;
+          } else {
+            if (input.substr(pos, 0) === "") {
+              var result22 = "";
+              pos += 0;
+            } else {
+              var result22 = null;
+              if (reportMatchFailures) {
+                matchFailed("\"\"");
+              }
+            }
+            if (result22 !== null) {
+              var result20 = result22;
+            } else {
+              var result20 = null;;
+            };
+          }
+          if (result20 !== null) {
+            var result21 = parse_equals();
+            if (result21 !== null) {
+              var result19 = [result20, result21];
+            } else {
+              var result19 = null;
+              pos = savedPos7;
+            }
+          } else {
+            var result19 = null;
+            pos = savedPos7;
+          }
+          reportMatchFailures = savedReportMatchFailuresVar0;
+          if (result19 === null) {
+            var result18 = '';
+          } else {
+            var result18 = null;
+            pos = savedPos6;
+          }
+          if (result18 !== null) {
+            var result15 = [result17, result18];
+          } else {
+            var result15 = null;
+            pos = savedPos5;
+          }
+        } else {
+          var result15 = null;
+          pos = savedPos5;
+        }
+        var result16 = result15 !== null
+          ? (function(name) {
+                return {
+                  type: "rule_ref",
+                  name: name
+                };
+              })(result15[0])
+          : null;
+        if (result16 !== null) {
+          var result14 = result16;
+        } else {
+          var result14 = null;
+          pos = savedPos4;
+        }
+        if (result14 !== null) {
+          var result0 = result14;
+        } else {
+          var savedPos3 = pos;
+          var result12 = parse_literal();
+          var result13 = result12 !== null
+            ? (function(value) {
+                  return {
+                    type:  "literal",
+                    value: value
+                  };
+                })(result12)
+            : null;
+          if (result13 !== null) {
+            var result11 = result13;
+          } else {
+            var result11 = null;
+            pos = savedPos3;
+          }
+          if (result11 !== null) {
+            var result0 = result11;
+          } else {
+            var savedPos2 = pos;
+            var result9 = parse_dot();
+            var result10 = result9 !== null
+              ? (function() { return { type: "any" }; })()
+              : null;
+            if (result10 !== null) {
+              var result8 = result10;
+            } else {
+              var result8 = null;
+              pos = savedPos2;
+            }
+            if (result8 !== null) {
+              var result0 = result8;
+            } else {
+              var result7 = parse_class();
+              if (result7 !== null) {
+                var result0 = result7;
+              } else {
+                var savedPos0 = pos;
+                var savedPos1 = pos;
+                var result4 = parse_lparen();
+                if (result4 !== null) {
+                  var result5 = parse_choice();
+                  if (result5 !== null) {
+                    var result6 = parse_rparen();
+                    if (result6 !== null) {
+                      var result2 = [result4, result5, result6];
+                    } else {
+                      var result2 = null;
+                      pos = savedPos1;
+                    }
+                  } else {
+                    var result2 = null;
+                    pos = savedPos1;
+                  }
+                } else {
+                  var result2 = null;
+                  pos = savedPos1;
+                }
+                var result3 = result2 !== null
+                  ? (function(expression) { return expression; })(result2[1])
+                  : null;
+                if (result3 !== null) {
+                  var result1 = result3;
+                } else {
+                  var result1 = null;
+                  pos = savedPos0;
+                }
+                if (result1 !== null) {
+                  var result0 = result1;
+                } else {
+                  var result0 = null;;
+                };
+              };
+            };
+          };
+        }
+        
+        
+        
+        cache[cacheKey] = {
+          nextPos: pos,
+          result:  result0
+        };
+        return result0;
+      }
+      
+      function parse_action() {
+        var cacheKey = 'action@' + pos;
+        var cachedResult = cache[cacheKey];
+        if (cachedResult) {
+          pos = cachedResult.nextPos;
+          return cachedResult.result;
+        }
+        
+        var savedReportMatchFailures = reportMatchFailures;
+        reportMatchFailures = false;
+        var savedPos0 = pos;
+        var savedPos1 = pos;
+        var result3 = parse_braced();
+        if (result3 !== null) {
+          var result4 = parse___();
+          if (result4 !== null) {
+            var result1 = [result3, result4];
+          } else {
+            var result1 = null;
+            pos = savedPos1;
+          }
+        } else {
+          var result1 = null;
+          pos = savedPos1;
+        }
+        var result2 = result1 !== null
+          ? (function(braced) { return braced.substr(1, braced.length - 2); })(result1[0])
+          : null;
+        if (result2 !== null) {
+          var result0 = result2;
+        } else {
+          var result0 = null;
+          pos = savedPos0;
+        }
+        reportMatchFailures = savedReportMatchFailures;
+        if (reportMatchFailures && result0 === null) {
+          matchFailed("action");
+        }
+        
+        cache[cacheKey] = {
+          nextPos: pos,
+          result:  result0
+        };
+        return result0;
+      }
+      
+      function parse_braced() {
+        var cacheKey = 'braced@' + pos;
+        var cachedResult = cache[cacheKey];
+        if (cachedResult) {
+          pos = cachedResult.nextPos;
+          return cachedResult.result;
+        }
+        
+        
+        var savedPos0 = pos;
+        var savedPos1 = pos;
+        if (input.substr(pos, 1) === "{") {
+          var result3 = "{";
+          pos += 1;
+        } else {
+          var result3 = null;
+          if (reportMatchFailures) {
+            matchFailed("\"{\"");
+          }
+        }
+        if (result3 !== null) {
+          var result4 = [];
+          var result8 = parse_braced();
+          if (result8 !== null) {
+            var result6 = result8;
+          } else {
+            var result7 = parse_nonBraceCharacter();
+            if (result7 !== null) {
+              var result6 = result7;
+            } else {
+              var result6 = null;;
+            };
+          }
+          while (result6 !== null) {
+            result4.push(result6);
+            var result8 = parse_braced();
+            if (result8 !== null) {
+              var result6 = result8;
+            } else {
+              var result7 = parse_nonBraceCharacter();
+              if (result7 !== null) {
+                var result6 = result7;
+              } else {
+                var result6 = null;;
+              };
+            }
+          }
+          if (result4 !== null) {
+            if (input.substr(pos, 1) === "}") {
+              var result5 = "}";
+              pos += 1;
+            } else {
+              var result5 = null;
+              if (reportMatchFailures) {
+                matchFailed("\"}\"");
+              }
+            }
+            if (result5 !== null) {
+              var result1 = [result3, result4, result5];
+            } else {
+              var result1 = null;
+              pos = savedPos1;
+            }
+          } else {
+            var result1 = null;
+            pos = savedPos1;
+          }
+        } else {
+          var result1 = null;
+          pos = savedPos1;
+        }
+        var result2 = result1 !== null
+          ? (function(parts) {
+                return "{" + parts.join("") + "}";
+              })(result1[1])
+          : null;
+        if (result2 !== null) {
+          var result0 = result2;
+        } else {
+          var result0 = null;
+          pos = savedPos0;
+        }
+        
+        
+        
+        cache[cacheKey] = {
+          nextPos: pos,
+          result:  result0
+        };
+        return result0;
+      }
+      
+      function parse_nonBraceCharacters() {
+        var cacheKey = 'nonBraceCharacters@' + pos;
+        var cachedResult = cache[cacheKey];
+        if (cachedResult) {
+          pos = cachedResult.nextPos;
+          return cachedResult.result;
+        }
+        
+        
+        var savedPos0 = pos;
+        var result3 = parse_nonBraceCharacter();
+        if (result3 !== null) {
+          var result1 = [];
+          while (result3 !== null) {
+            result1.push(result3);
+            var result3 = parse_nonBraceCharacter();
+          }
+        } else {
+          var result1 = null;
+        }
+        var result2 = result1 !== null
+          ? (function(chars) { return chars.join(""); })(result1)
+          : null;
+        if (result2 !== null) {
+          var result0 = result2;
+        } else {
+          var result0 = null;
+          pos = savedPos0;
+        }
+        
+        
+        
+        cache[cacheKey] = {
+          nextPos: pos,
+          result:  result0
+        };
+        return result0;
+      }
+      
+      function parse_nonBraceCharacter() {
+        var cacheKey = 'nonBraceCharacter@' + pos;
+        var cachedResult = cache[cacheKey];
+        if (cachedResult) {
+          pos = cachedResult.nextPos;
+          return cachedResult.result;
+        }
+        
+        
+        if (input.substr(pos).match(/^[^{}]/) !== null) {
+          var result0 = input.charAt(pos);
+          pos++;
+        } else {
+          var result0 = null;
+          if (reportMatchFailures) {
+            matchFailed("[^{}]");
+          }
+        }
+        
+        
+        
+        cache[cacheKey] = {
+          nextPos: pos,
+          result:  result0
+        };
+        return result0;
+      }
+      
+      function parse_equals() {
+        var cacheKey = 'equals@' + pos;
+        var cachedResult = cache[cacheKey];
+        if (cachedResult) {
+          pos = cachedResult.nextPos;
+          return cachedResult.result;
+        }
+        
+        
+        var savedPos0 = pos;
+        var savedPos1 = pos;
+        if (input.substr(pos, 1) === "=") {
+          var result3 = "=";
+          pos += 1;
+        } else {
+          var result3 = null;
+          if (reportMatchFailures) {
+            matchFailed("\"=\"");
+          }
+        }
+        if (result3 !== null) {
+          var result4 = parse___();
+          if (result4 !== null) {
+            var result1 = [result3, result4];
+          } else {
+            var result1 = null;
+            pos = savedPos1;
+          }
+        } else {
+          var result1 = null;
+          pos = savedPos1;
+        }
+        var result2 = result1 !== null
+          ? (function() { return "="; })()
+          : null;
+        if (result2 !== null) {
+          var result0 = result2;
+        } else {
+          var result0 = null;
+          pos = savedPos0;
+        }
+        
+        
+        
+        cache[cacheKey] = {
+          nextPos: pos,
+          result:  result0
+        };
+        return result0;
+      }
+      
+      function parse_colon() {
+        var cacheKey = 'colon@' + pos;
+        var cachedResult = cache[cacheKey];
+        if (cachedResult) {
+          pos = cachedResult.nextPos;
+          return cachedResult.result;
+        }
+        
+        
+        var savedPos0 = pos;
+        var savedPos1 = pos;
+        if (input.substr(pos, 1) === ":") {
+          var result3 = ":";
+          pos += 1;
+        } else {
+          var result3 = null;
+          if (reportMatchFailures) {
+            matchFailed("\":\"");
+          }
+        }
+        if (result3 !== null) {
+          var result4 = parse___();
+          if (result4 !== null) {
+            var result1 = [result3, result4];
+          } else {
+            var result1 = null;
+            pos = savedPos1;
+          }
+        } else {
+          var result1 = null;
+          pos = savedPos1;
+        }
+        var result2 = result1 !== null
+          ? (function() { return ":"; })()
+          : null;
+        if (result2 !== null) {
+          var result0 = result2;
+        } else {
+          var result0 = null;
+          pos = savedPos0;
+        }
+        
+        
+        
+        cache[cacheKey] = {
+          nextPos: pos,
+          result:  result0
+        };
+        return result0;
+      }
+      
+      function parse_semicolon() {
+        var cacheKey = 'semicolon@' + pos;
+        var cachedResult = cache[cacheKey];
+        if (cachedResult) {
+          pos = cachedResult.nextPos;
+          return cachedResult.result;
+        }
+        
+        
+        var savedPos0 = pos;
+        var savedPos1 = pos;
+        if (input.substr(pos, 1) === ";") {
+          var result3 = ";";
+          pos += 1;
+        } else {
+          var result3 = null;
+          if (reportMatchFailures) {
+            matchFailed("\";\"");
+          }
+        }
+        if (result3 !== null) {
+          var result4 = parse___();
+          if (result4 !== null) {
+            var result1 = [result3, result4];
+          } else {
+            var result1 = null;
+            pos = savedPos1;
+          }
+        } else {
+          var result1 = null;
+          pos = savedPos1;
+        }
+        var result2 = result1 !== null
+          ? (function() { return ";"; })()
+          : null;
+        if (result2 !== null) {
+          var result0 = result2;
+        } else {
+          var result0 = null;
+          pos = savedPos0;
+        }
+        
+        
+        
+        cache[cacheKey] = {
+          nextPos: pos,
+          result:  result0
+        };
+        return result0;
+      }
+      
+      function parse_slash() {
+        var cacheKey = 'slash@' + pos;
+        var cachedResult = cache[cacheKey];
+        if (cachedResult) {
+          pos = cachedResult.nextPos;
+          return cachedResult.result;
+        }
+        
+        
+        var savedPos0 = pos;
+        var savedPos1 = pos;
+        if (input.substr(pos, 1) === "/") {
+          var result3 = "/";
+          pos += 1;
+        } else {
+          var result3 = null;
+          if (reportMatchFailures) {
+            matchFailed("\"/\"");
+          }
+        }
+        if (result3 !== null) {
+          var result4 = parse___();
+          if (result4 !== null) {
+            var result1 = [result3, result4];
+          } else {
+            var result1 = null;
+            pos = savedPos1;
+          }
+        } else {
+          var result1 = null;
+          pos = savedPos1;
+        }
+        var result2 = result1 !== null
+          ? (function() { return "/"; })()
+          : null;
+        if (result2 !== null) {
+          var result0 = result2;
+        } else {
+          var result0 = null;
+          pos = savedPos0;
+        }
+        
+        
+        
+        cache[cacheKey] = {
+          nextPos: pos,
+          result:  result0
+        };
+        return result0;
+      }
+      
+      function parse_and() {
+        var cacheKey = 'and@' + pos;
+        var cachedResult = cache[cacheKey];
+        if (cachedResult) {
+          pos = cachedResult.nextPos;
+          return cachedResult.result;
+        }
+        
+        
+        var savedPos0 = pos;
+        var savedPos1 = pos;
+        if (input.substr(pos, 1) === "&") {
+          var result3 = "&";
+          pos += 1;
+        } else {
+          var result3 = null;
+          if (reportMatchFailures) {
+            matchFailed("\"&\"");
+          }
+        }
+        if (result3 !== null) {
+          var result4 = parse___();
+          if (result4 !== null) {
+            var result1 = [result3, result4];
+          } else {
+            var result1 = null;
+            pos = savedPos1;
+          }
+        } else {
+          var result1 = null;
+          pos = savedPos1;
+        }
+        var result2 = result1 !== null
+          ? (function() { return "&"; })()
+          : null;
+        if (result2 !== null) {
+          var result0 = result2;
+        } else {
+          var result0 = null;
+          pos = savedPos0;
+        }
+        
+        
+        
+        cache[cacheKey] = {
+          nextPos: pos,
+          result:  result0
+        };
+        return result0;
+      }
+      
+      function parse_not() {
+        var cacheKey = 'not@' + pos;
+        var cachedResult = cache[cacheKey];
+        if (cachedResult) {
+          pos = cachedResult.nextPos;
+          return cachedResult.result;
+        }
+        
+        
+        var savedPos0 = pos;
+        var savedPos1 = pos;
+        if (input.substr(pos, 1) === "!") {
+          var result3 = "!";
+          pos += 1;
+        } else {
+          var result3 = null;
+          if (reportMatchFailures) {
+            matchFailed("\"!\"");
+          }
+        }
+        if (result3 !== null) {
+          var result4 = parse___();
+          if (result4 !== null) {
+            var result1 = [result3, result4];
+          } else {
+            var result1 = null;
+            pos = savedPos1;
+          }
+        } else {
+          var result1 = null;
+          pos = savedPos1;
+        }
+        var result2 = result1 !== null
+          ? (function() { return "!"; })()
+          : null;
+        if (result2 !== null) {
+          var result0 = result2;
+        } else {
+          var result0 = null;
+          pos = savedPos0;
+        }
+        
+        
+        
+        cache[cacheKey] = {
+          nextPos: pos,
+          result:  result0
+        };
+        return result0;
+      }
+      
+      function parse_question() {
+        var cacheKey = 'question@' + pos;
+        var cachedResult = cache[cacheKey];
+        if (cachedResult) {
+          pos = cachedResult.nextPos;
+          return cachedResult.result;
+        }
+        
+        
+        var savedPos0 = pos;
+        var savedPos1 = pos;
+        if (input.substr(pos, 1) === "?") {
+          var result3 = "?";
+          pos += 1;
+        } else {
+          var result3 = null;
+          if (reportMatchFailures) {
+            matchFailed("\"?\"");
+          }
+        }
+        if (result3 !== null) {
+          var result4 = parse___();
+          if (result4 !== null) {
+            var result1 = [result3, result4];
+          } else {
+            var result1 = null;
+            pos = savedPos1;
+          }
+        } else {
+          var result1 = null;
+          pos = savedPos1;
+        }
+        var result2 = result1 !== null
+          ? (function() { return "?"; })()
+          : null;
+        if (result2 !== null) {
+          var result0 = result2;
+        } else {
+          var result0 = null;
+          pos = savedPos0;
+        }
+        
+        
+        
+        cache[cacheKey] = {
+          nextPos: pos,
+          result:  result0
+        };
+        return result0;
+      }
+      
+      function parse_star() {
+        var cacheKey = 'star@' + pos;
+        var cachedResult = cache[cacheKey];
+        if (cachedResult) {
+          pos = cachedResult.nextPos;
+          return cachedResult.result;
+        }
+        
+        
+        var savedPos0 = pos;
+        var savedPos1 = pos;
+        if (input.substr(pos, 1) === "*") {
+          var result3 = "*";
+          pos += 1;
+        } else {
+          var result3 = null;
+          if (reportMatchFailures) {
+            matchFailed("\"*\"");
+          }
+        }
+        if (result3 !== null) {
+          var result4 = parse___();
+          if (result4 !== null) {
+            var result1 = [result3, result4];
+          } else {
+            var result1 = null;
+            pos = savedPos1;
+          }
+        } else {
+          var result1 = null;
+          pos = savedPos1;
+        }
+        var result2 = result1 !== null
+          ? (function() { return "*"; })()
+          : null;
+        if (result2 !== null) {
+          var result0 = result2;
+        } else {
+          var result0 = null;
+          pos = savedPos0;
+        }
+        
+        
+        
+        cache[cacheKey] = {
+          nextPos: pos,
+          result:  result0
+        };
+        return result0;
+      }
+      
+      function parse_plus() {
+        var cacheKey = 'plus@' + pos;
+        var cachedResult = cache[cacheKey];
+        if (cachedResult) {
+          pos = cachedResult.nextPos;
+          return cachedResult.result;
+        }
+        
+        
+        var savedPos0 = pos;
+        var savedPos1 = pos;
+        if (input.substr(pos, 1) === "+") {
+          var result3 = "+";
+          pos += 1;
+        } else {
+          var result3 = null;
+          if (reportMatchFailures) {
+            matchFailed("\"+\"");
+          }
+        }
+        if (result3 !== null) {
+          var result4 = parse___();
+          if (result4 !== null) {
+            var result1 = [result3, result4];
+          } else {
+            var result1 = null;
+            pos = savedPos1;
+          }
+        } else {
+          var result1 = null;
+          pos = savedPos1;
+        }
+        var result2 = result1 !== null
+          ? (function() { return "+"; })()
+          : null;
+        if (result2 !== null) {
+          var result0 = result2;
+        } else {
+          var result0 = null;
+          pos = savedPos0;
+        }
+        
+        
+        
+        cache[cacheKey] = {
+          nextPos: pos,
+          result:  result0
+        };
+        return result0;
+      }
+      
+      function parse_lparen() {
+        var cacheKey = 'lparen@' + pos;
+        var cachedResult = cache[cacheKey];
+        if (cachedResult) {
+          pos = cachedResult.nextPos;
+          return cachedResult.result;
+        }
+        
+        
+        var savedPos0 = pos;
+        var savedPos1 = pos;
+        if (input.substr(pos, 1) === "(") {
+          var result3 = "(";
+          pos += 1;
+        } else {
+          var result3 = null;
+          if (reportMatchFailures) {
+            matchFailed("\"(\"");
+          }
+        }
+        if (result3 !== null) {
+          var result4 = parse___();
+          if (result4 !== null) {
+            var result1 = [result3, result4];
+          } else {
+            var result1 = null;
+            pos = savedPos1;
+          }
+        } else {
+          var result1 = null;
+          pos = savedPos1;
+        }
+        var result2 = result1 !== null
+          ? (function() { return "("; })()
+          : null;
+        if (result2 !== null) {
+          var result0 = result2;
+        } else {
+          var result0 = null;
+          pos = savedPos0;
+        }
+        
+        
+        
+        cache[cacheKey] = {
+          nextPos: pos,
+          result:  result0
+        };
+        return result0;
+      }
+      
+      function parse_rparen() {
+        var cacheKey = 'rparen@' + pos;
+        var cachedResult = cache[cacheKey];
+        if (cachedResult) {
+          pos = cachedResult.nextPos;
+          return cachedResult.result;
+        }
+        
+        
+        var savedPos0 = pos;
+        var savedPos1 = pos;
+        if (input.substr(pos, 1) === ")") {
+          var result3 = ")";
+          pos += 1;
+        } else {
+          var result3 = null;
+          if (reportMatchFailures) {
+            matchFailed("\")\"");
+          }
+        }
+        if (result3 !== null) {
+          var result4 = parse___();
+          if (result4 !== null) {
+            var result1 = [result3, result4];
+          } else {
+            var result1 = null;
+            pos = savedPos1;
+          }
+        } else {
+          var result1 = null;
+          pos = savedPos1;
+        }
+        var result2 = result1 !== null
+          ? (function() { return ")"; })()
+          : null;
+        if (result2 !== null) {
+          var result0 = result2;
+        } else {
+          var result0 = null;
+          pos = savedPos0;
+        }
+        
+        
+        
+        cache[cacheKey] = {
+          nextPos: pos,
+          result:  result0
+        };
+        return result0;
+      }
+      
+      function parse_dot() {
+        var cacheKey = 'dot@' + pos;
+        var cachedResult = cache[cacheKey];
+        if (cachedResult) {
+          pos = cachedResult.nextPos;
+          return cachedResult.result;
+        }
+        
+        
+        var savedPos0 = pos;
+        var savedPos1 = pos;
+        if (input.substr(pos, 1) === ".") {
+          var result3 = ".";
+          pos += 1;
+        } else {
+          var result3 = null;
+          if (reportMatchFailures) {
+            matchFailed("\".\"");
+          }
+        }
+        if (result3 !== null) {
+          var result4 = parse___();
+          if (result4 !== null) {
+            var result1 = [result3, result4];
+          } else {
+            var result1 = null;
+            pos = savedPos1;
+          }
+        } else {
+          var result1 = null;
+          pos = savedPos1;
+        }
+        var result2 = result1 !== null
+          ? (function() { return "."; })()
+          : null;
+        if (result2 !== null) {
+          var result0 = result2;
+        } else {
+          var result0 = null;
+          pos = savedPos0;
+        }
+        
+        
+        
+        cache[cacheKey] = {
+          nextPos: pos,
+          result:  result0
+        };
+        return result0;
+      }
+      
+      function parse_identifier() {
+        var cacheKey = 'identifier@' + pos;
+        var cachedResult = cache[cacheKey];
+        if (cachedResult) {
+          pos = cachedResult.nextPos;
+          return cachedResult.result;
+        }
+        
+        var savedReportMatchFailures = reportMatchFailures;
+        reportMatchFailures = false;
+        var savedPos0 = pos;
+        var savedPos1 = pos;
+        var result13 = parse_letter();
+        if (result13 !== null) {
+          var result3 = result13;
+        } else {
+          if (input.substr(pos, 1) === "_") {
+            var result12 = "_";
+            pos += 1;
+          } else {
+            var result12 = null;
+            if (reportMatchFailures) {
+              matchFailed("\"_\"");
+            }
+          }
+          if (result12 !== null) {
+            var result3 = result12;
+          } else {
+            if (input.substr(pos, 1) === "$") {
+              var result11 = "$";
+              pos += 1;
+            } else {
+              var result11 = null;
+              if (reportMatchFailures) {
+                matchFailed("\"$\"");
+              }
+            }
+            if (result11 !== null) {
+              var result3 = result11;
+            } else {
+              var result3 = null;;
+            };
+          };
+        }
+        if (result3 !== null) {
+          var result4 = [];
+          var result10 = parse_letter();
+          if (result10 !== null) {
+            var result6 = result10;
+          } else {
+            var result9 = parse_digit();
+            if (result9 !== null) {
+              var result6 = result9;
+            } else {
+              if (input.substr(pos, 1) === "_") {
+                var result8 = "_";
+                pos += 1;
+              } else {
+                var result8 = null;
+                if (reportMatchFailures) {
+                  matchFailed("\"_\"");
+                }
+              }
+              if (result8 !== null) {
+                var result6 = result8;
+              } else {
+                if (input.substr(pos, 1) === "$") {
+                  var result7 = "$";
+                  pos += 1;
+                } else {
+                  var result7 = null;
+                  if (reportMatchFailures) {
+                    matchFailed("\"$\"");
+                  }
+                }
+                if (result7 !== null) {
+                  var result6 = result7;
+                } else {
+                  var result6 = null;;
+                };
+              };
+            };
+          }
+          while (result6 !== null) {
+            result4.push(result6);
+            var result10 = parse_letter();
+            if (result10 !== null) {
+              var result6 = result10;
+            } else {
+              var result9 = parse_digit();
+              if (result9 !== null) {
+                var result6 = result9;
+              } else {
+                if (input.substr(pos, 1) === "_") {
+                  var result8 = "_";
+                  pos += 1;
+                } else {
+                  var result8 = null;
+                  if (reportMatchFailures) {
+                    matchFailed("\"_\"");
+                  }
+                }
+                if (result8 !== null) {
+                  var result6 = result8;
+                } else {
+                  if (input.substr(pos, 1) === "$") {
+                    var result7 = "$";
+                    pos += 1;
+                  } else {
+                    var result7 = null;
+                    if (reportMatchFailures) {
+                      matchFailed("\"$\"");
+                    }
+                  }
+                  if (result7 !== null) {
+                    var result6 = result7;
+                  } else {
+                    var result6 = null;;
+                  };
+                };
+              };
+            }
+          }
+          if (result4 !== null) {
+            var result5 = parse___();
+            if (result5 !== null) {
+              var result1 = [result3, result4, result5];
+            } else {
+              var result1 = null;
+              pos = savedPos1;
+            }
+          } else {
+            var result1 = null;
+            pos = savedPos1;
+          }
+        } else {
+          var result1 = null;
+          pos = savedPos1;
+        }
+        var result2 = result1 !== null
+          ? (function(head, tail) {
+                return head + tail.join("");
+              })(result1[0], result1[1])
+          : null;
+        if (result2 !== null) {
+          var result0 = result2;
+        } else {
+          var result0 = null;
+          pos = savedPos0;
+        }
+        reportMatchFailures = savedReportMatchFailures;
+        if (reportMatchFailures && result0 === null) {
+          matchFailed("identifier");
+        }
+        
+        cache[cacheKey] = {
+          nextPos: pos,
+          result:  result0
+        };
+        return result0;
+      }
+      
+      function parse_literal() {
+        var cacheKey = 'literal@' + pos;
+        var cachedResult = cache[cacheKey];
+        if (cachedResult) {
+          pos = cachedResult.nextPos;
+          return cachedResult.result;
+        }
+        
+        var savedReportMatchFailures = reportMatchFailures;
+        reportMatchFailures = false;
+        var savedPos0 = pos;
+        var savedPos1 = pos;
+        var result6 = parse_doubleQuotedLiteral();
+        if (result6 !== null) {
+          var result3 = result6;
+        } else {
+          var result5 = parse_singleQuotedLiteral();
+          if (result5 !== null) {
+            var result3 = result5;
+          } else {
+            var result3 = null;;
+          };
+        }
+        if (result3 !== null) {
+          var result4 = parse___();
+          if (result4 !== null) {
+            var result1 = [result3, result4];
+          } else {
+            var result1 = null;
+            pos = savedPos1;
+          }
+        } else {
+          var result1 = null;
+          pos = savedPos1;
+        }
+        var result2 = result1 !== null
+          ? (function(literal) { return literal; })(result1[0])
+          : null;
+        if (result2 !== null) {
+          var result0 = result2;
+        } else {
+          var result0 = null;
+          pos = savedPos0;
+        }
+        reportMatchFailures = savedReportMatchFailures;
+        if (reportMatchFailures && result0 === null) {
+          matchFailed("literal");
+        }
+        
+        cache[cacheKey] = {
+          nextPos: pos,
+          result:  result0
+        };
+        return result0;
+      }
+      
+      function parse_doubleQuotedLiteral() {
+        var cacheKey = 'doubleQuotedLiteral@' + pos;
+        var cachedResult = cache[cacheKey];
+        if (cachedResult) {
+          pos = cachedResult.nextPos;
+          return cachedResult.result;
+        }
+        
+        
+        var savedPos0 = pos;
+        var savedPos1 = pos;
+        if (input.substr(pos, 1) === "\"") {
+          var result3 = "\"";
+          pos += 1;
+        } else {
+          var result3 = null;
+          if (reportMatchFailures) {
+            matchFailed("\"\\\"\"");
+          }
+        }
+        if (result3 !== null) {
+          var result4 = [];
+          var result6 = parse_doubleQuotedCharacter();
+          while (result6 !== null) {
+            result4.push(result6);
+            var result6 = parse_doubleQuotedCharacter();
+          }
+          if (result4 !== null) {
+            if (input.substr(pos, 1) === "\"") {
+              var result5 = "\"";
+              pos += 1;
+            } else {
+              var result5 = null;
+              if (reportMatchFailures) {
+                matchFailed("\"\\\"\"");
+              }
+            }
+            if (result5 !== null) {
+              var result1 = [result3, result4, result5];
+            } else {
+              var result1 = null;
+              pos = savedPos1;
+            }
+          } else {
+            var result1 = null;
+            pos = savedPos1;
+          }
+        } else {
+          var result1 = null;
+          pos = savedPos1;
+        }
+        var result2 = result1 !== null
+          ? (function(chars) { return chars.join(""); })(result1[1])
+          : null;
+        if (result2 !== null) {
+          var result0 = result2;
+        } else {
+          var result0 = null;
+          pos = savedPos0;
+        }
+        
+        
+        
+        cache[cacheKey] = {
+          nextPos: pos,
+          result:  result0
+        };
+        return result0;
+      }
+      
+      function parse_doubleQuotedCharacter() {
+        var cacheKey = 'doubleQuotedCharacter@' + pos;
+        var cachedResult = cache[cacheKey];
+        if (cachedResult) {
+          pos = cachedResult.nextPos;
+          return cachedResult.result;
+        }
+        
+        
+        var result6 = parse_simpleDoubleQuotedCharacter();
+        if (result6 !== null) {
+          var result0 = result6;
+        } else {
+          var result5 = parse_simpleEscapeSequence();
+          if (result5 !== null) {
+            var result0 = result5;
+          } else {
+            var result4 = parse_zeroEscapeSequence();
+            if (result4 !== null) {
+              var result0 = result4;
+            } else {
+              var result3 = parse_hexEscapeSequence();
+              if (result3 !== null) {
+                var result0 = result3;
+              } else {
+                var result2 = parse_unicodeEscapeSequence();
+                if (result2 !== null) {
+                  var result0 = result2;
+                } else {
+                  var result1 = parse_eolEscapeSequence();
+                  if (result1 !== null) {
+                    var result0 = result1;
+                  } else {
+                    var result0 = null;;
+                  };
+                };
+              };
+            };
+          };
+        }
+        
+        
+        
+        cache[cacheKey] = {
+          nextPos: pos,
+          result:  result0
+        };
+        return result0;
+      }
+      
+      function parse_simpleDoubleQuotedCharacter() {
+        var cacheKey = 'simpleDoubleQuotedCharacter@' + pos;
+        var cachedResult = cache[cacheKey];
+        if (cachedResult) {
+          pos = cachedResult.nextPos;
+          return cachedResult.result;
+        }
+        
+        
+        var savedPos0 = pos;
+        var savedPos1 = pos;
+        var savedPos2 = pos;
+        var savedReportMatchFailuresVar0 = reportMatchFailures;
+        reportMatchFailures = false;
+        if (input.substr(pos, 1) === "\"") {
+          var result8 = "\"";
+          pos += 1;
+        } else {
+          var result8 = null;
+          if (reportMatchFailures) {
+            matchFailed("\"\\\"\"");
+          }
+        }
+        if (result8 !== null) {
+          var result5 = result8;
+        } else {
+          if (input.substr(pos, 1) === "\\") {
+            var result7 = "\\";
+            pos += 1;
+          } else {
+            var result7 = null;
+            if (reportMatchFailures) {
+              matchFailed("\"\\\\\"");
+            }
+          }
+          if (result7 !== null) {
+            var result5 = result7;
+          } else {
+            var result6 = parse_eolChar();
+            if (result6 !== null) {
+              var result5 = result6;
+            } else {
+              var result5 = null;;
+            };
+          };
+        }
+        reportMatchFailures = savedReportMatchFailuresVar0;
+        if (result5 === null) {
+          var result3 = '';
+        } else {
+          var result3 = null;
+          pos = savedPos2;
+        }
+        if (result3 !== null) {
+          if (input.length > pos) {
+            var result4 = input.charAt(pos);
+            pos++;
+          } else {
+            var result4 = null;
+            if (reportMatchFailures) {
+              matchFailed('any character');
+            }
+          }
+          if (result4 !== null) {
+            var result1 = [result3, result4];
+          } else {
+            var result1 = null;
+            pos = savedPos1;
+          }
+        } else {
+          var result1 = null;
+          pos = savedPos1;
+        }
+        var result2 = result1 !== null
+          ? (function(char_) { return char_; })(result1[1])
+          : null;
+        if (result2 !== null) {
+          var result0 = result2;
+        } else {
+          var result0 = null;
+          pos = savedPos0;
+        }
+        
+        
+        
+        cache[cacheKey] = {
+          nextPos: pos,
+          result:  result0
+        };
+        return result0;
+      }
+      
+      function parse_singleQuotedLiteral() {
+        var cacheKey = 'singleQuotedLiteral@' + pos;
+        var cachedResult = cache[cacheKey];
+        if (cachedResult) {
+          pos = cachedResult.nextPos;
+          return cachedResult.result;
+        }
+        
+        
+        var savedPos0 = pos;
+        var savedPos1 = pos;
+        if (input.substr(pos, 1) === "'") {
+          var result3 = "'";
+          pos += 1;
+        } else {
+          var result3 = null;
+          if (reportMatchFailures) {
+            matchFailed("\"'\"");
+          }
+        }
+        if (result3 !== null) {
+          var result4 = [];
+          var result6 = parse_singleQuotedCharacter();
+          while (result6 !== null) {
+            result4.push(result6);
+            var result6 = parse_singleQuotedCharacter();
+          }
+          if (result4 !== null) {
+            if (input.substr(pos, 1) === "'") {
+              var result5 = "'";
+              pos += 1;
+            } else {
+              var result5 = null;
+              if (reportMatchFailures) {
+                matchFailed("\"'\"");
+              }
+            }
+            if (result5 !== null) {
+              var result1 = [result3, result4, result5];
+            } else {
+              var result1 = null;
+              pos = savedPos1;
+            }
+          } else {
+            var result1 = null;
+            pos = savedPos1;
+          }
+        } else {
+          var result1 = null;
+          pos = savedPos1;
+        }
+        var result2 = result1 !== null
+          ? (function(chars) { return chars.join(""); })(result1[1])
+          : null;
+        if (result2 !== null) {
+          var result0 = result2;
+        } else {
+          var result0 = null;
+          pos = savedPos0;
+        }
+        
+        
+        
+        cache[cacheKey] = {
+          nextPos: pos,
+          result:  result0
+        };
+        return result0;
+      }
+      
+      function parse_singleQuotedCharacter() {
+        var cacheKey = 'singleQuotedCharacter@' + pos;
+        var cachedResult = cache[cacheKey];
+        if (cachedResult) {
+          pos = cachedResult.nextPos;
+          return cachedResult.result;
+        }
+        
+        
+        var result6 = parse_simpleSingleQuotedCharacter();
+        if (result6 !== null) {
+          var result0 = result6;
+        } else {
+          var result5 = parse_simpleEscapeSequence();
+          if (result5 !== null) {
+            var result0 = result5;
+          } else {
+            var result4 = parse_zeroEscapeSequence();
+            if (result4 !== null) {
+              var result0 = result4;
+            } else {
+              var result3 = parse_hexEscapeSequence();
+              if (result3 !== null) {
+                var result0 = result3;
+              } else {
+                var result2 = parse_unicodeEscapeSequence();
+                if (result2 !== null) {
+                  var result0 = result2;
+                } else {
+                  var result1 = parse_eolEscapeSequence();
+                  if (result1 !== null) {
+                    var result0 = result1;
+                  } else {
+                    var result0 = null;;
+                  };
+                };
+              };
+            };
+          };
+        }
+        
+        
+        
+        cache[cacheKey] = {
+          nextPos: pos,
+          result:  result0
+        };
+        return result0;
+      }
+      
+      function parse_simpleSingleQuotedCharacter() {
+        var cacheKey = 'simpleSingleQuotedCharacter@' + pos;
+        var cachedResult = cache[cacheKey];
+        if (cachedResult) {
+          pos = cachedResult.nextPos;
+          return cachedResult.result;
+        }
+        
+        
+        var savedPos0 = pos;
+        var savedPos1 = pos;
+        var savedPos2 = pos;
+        var savedReportMatchFailuresVar0 = reportMatchFailures;
+        reportMatchFailures = false;
+        if (input.substr(pos, 1) === "'") {
+          var result8 = "'";
+          pos += 1;
+        } else {
+          var result8 = null;
+          if (reportMatchFailures) {
+            matchFailed("\"'\"");
+          }
+        }
+        if (result8 !== null) {
+          var result5 = result8;
+        } else {
+          if (input.substr(pos, 1) === "\\") {
+            var result7 = "\\";
+            pos += 1;
+          } else {
+            var result7 = null;
+            if (reportMatchFailures) {
+              matchFailed("\"\\\\\"");
+            }
+          }
+          if (result7 !== null) {
+            var result5 = result7;
+          } else {
+            var result6 = parse_eolChar();
+            if (result6 !== null) {
+              var result5 = result6;
+            } else {
+              var result5 = null;;
+            };
+          };
+        }
+        reportMatchFailures = savedReportMatchFailuresVar0;
+        if (result5 === null) {
+          var result3 = '';
+        } else {
+          var result3 = null;
+          pos = savedPos2;
+        }
+        if (result3 !== null) {
+          if (input.length > pos) {
+            var result4 = input.charAt(pos);
+            pos++;
+          } else {
+            var result4 = null;
+            if (reportMatchFailures) {
+              matchFailed('any character');
+            }
+          }
+          if (result4 !== null) {
+            var result1 = [result3, result4];
+          } else {
+            var result1 = null;
+            pos = savedPos1;
+          }
+        } else {
+          var result1 = null;
+          pos = savedPos1;
+        }
+        var result2 = result1 !== null
+          ? (function(char_) { return char_; })(result1[1])
+          : null;
+        if (result2 !== null) {
+          var result0 = result2;
+        } else {
+          var result0 = null;
+          pos = savedPos0;
+        }
+        
+        
+        
+        cache[cacheKey] = {
+          nextPos: pos,
+          result:  result0
+        };
+        return result0;
+      }
+      
+      function parse_class() {
+        var cacheKey = 'class@' + pos;
+        var cachedResult = cache[cacheKey];
+        if (cachedResult) {
+          pos = cachedResult.nextPos;
+          return cachedResult.result;
+        }
+        
+        var savedReportMatchFailures = reportMatchFailures;
+        reportMatchFailures = false;
+        var savedPos0 = pos;
+        var savedPos1 = pos;
+        if (input.substr(pos, 1) === "[") {
+          var result3 = "[";
+          pos += 1;
+        } else {
+          var result3 = null;
+          if (reportMatchFailures) {
+            matchFailed("\"[\"");
+          }
+        }
+        if (result3 !== null) {
+          if (input.substr(pos, 1) === "^") {
+            var result11 = "^";
+            pos += 1;
+          } else {
+            var result11 = null;
+            if (reportMatchFailures) {
+              matchFailed("\"^\"");
+            }
+          }
+          var result4 = result11 !== null ? result11 : '';
+          if (result4 !== null) {
+            var result5 = [];
+            var result10 = parse_classCharacterRange();
+            if (result10 !== null) {
+              var result8 = result10;
+            } else {
+              var result9 = parse_classCharacter();
+              if (result9 !== null) {
+                var result8 = result9;
+              } else {
+                var result8 = null;;
+              };
+            }
+            while (result8 !== null) {
+              result5.push(result8);
+              var result10 = parse_classCharacterRange();
+              if (result10 !== null) {
+                var result8 = result10;
+              } else {
+                var result9 = parse_classCharacter();
+                if (result9 !== null) {
+                  var result8 = result9;
+                } else {
+                  var result8 = null;;
+                };
+              }
+            }
+            if (result5 !== null) {
+              if (input.substr(pos, 1) === "]") {
+                var result6 = "]";
+                pos += 1;
+              } else {
+                var result6 = null;
+                if (reportMatchFailures) {
+                  matchFailed("\"]\"");
+                }
+              }
+              if (result6 !== null) {
+                var result7 = parse___();
+                if (result7 !== null) {
+                  var result1 = [result3, result4, result5, result6, result7];
+                } else {
+                  var result1 = null;
+                  pos = savedPos1;
+                }
+              } else {
+                var result1 = null;
+                pos = savedPos1;
+              }
+            } else {
+              var result1 = null;
+              pos = savedPos1;
+            }
+          } else {
+            var result1 = null;
+            pos = savedPos1;
+          }
+        } else {
+          var result1 = null;
+          pos = savedPos1;
+        }
+        var result2 = result1 !== null
+          ? (function(inverted, parts) {
+                var partsConverted = map(parts, function(part) { return part.data; });
+                var rawText = "["
+                  + inverted
+                  + map(parts, function(part) { return part.rawText; }).join("")
+                  + "]";
+          
+                return {
+                  type:     "class",
+                  inverted: inverted === "^",
+                  parts:    partsConverted,
+                  // FIXME: Get the raw text from the input directly.
+                  rawText:  rawText
+                };
+              })(result1[1], result1[2])
+          : null;
+        if (result2 !== null) {
+          var result0 = result2;
+        } else {
+          var result0 = null;
+          pos = savedPos0;
+        }
+        reportMatchFailures = savedReportMatchFailures;
+        if (reportMatchFailures && result0 === null) {
+          matchFailed("character class");
+        }
+        
+        cache[cacheKey] = {
+          nextPos: pos,
+          result:  result0
+        };
+        return result0;
+      }
+      
+      function parse_classCharacterRange() {
+        var cacheKey = 'classCharacterRange@' + pos;
+        var cachedResult = cache[cacheKey];
+        if (cachedResult) {
+          pos = cachedResult.nextPos;
+          return cachedResult.result;
+        }
+        
+        
+        var savedPos0 = pos;
+        var savedPos1 = pos;
+        var result3 = parse_classCharacter();
+        if (result3 !== null) {
+          if (input.substr(pos, 1) === "-") {
+            var result4 = "-";
+            pos += 1;
+          } else {
+            var result4 = null;
+            if (reportMatchFailures) {
+              matchFailed("\"-\"");
+            }
+          }
+          if (result4 !== null) {
+            var result5 = parse_classCharacter();
+            if (result5 !== null) {
+              var result1 = [result3, result4, result5];
+            } else {
+              var result1 = null;
+              pos = savedPos1;
+            }
+          } else {
+            var result1 = null;
+            pos = savedPos1;
+          }
+        } else {
+          var result1 = null;
+          pos = savedPos1;
+        }
+        var result2 = result1 !== null
+          ? (function(begin, end) {
+                if (begin.data.charCodeAt(0) > end.data.charCodeAt(0)) {
+                  throw new this.SyntaxError(
+                    "Invalid character range: " + begin.rawText + "-" + end.rawText + "."
+                  );
+                }
+          
+                return {
+                  data:    [begin.data, end.data],
+                  // FIXME: Get the raw text from the input directly.
+                  rawText: begin.rawText + "-" + end.rawText
+                }
+              })(result1[0], result1[2])
+          : null;
+        if (result2 !== null) {
+          var result0 = result2;
+        } else {
+          var result0 = null;
+          pos = savedPos0;
+        }
+        
+        
+        
+        cache[cacheKey] = {
+          nextPos: pos,
+          result:  result0
+        };
+        return result0;
+      }
+      
+      function parse_classCharacter() {
+        var cacheKey = 'classCharacter@' + pos;
+        var cachedResult = cache[cacheKey];
+        if (cachedResult) {
+          pos = cachedResult.nextPos;
+          return cachedResult.result;
+        }
+        
+        
+        var savedPos0 = pos;
+        var result1 = parse_bracketDelimitedCharacter();
+        var result2 = result1 !== null
+          ? (function(char_) {
+                return {
+                  data:    char_,
+                  // FIXME: Get the raw text from the input directly.
+                  rawText: quoteForRegexpClass(char_)
+                };
+              })(result1)
+          : null;
+        if (result2 !== null) {
+          var result0 = result2;
+        } else {
+          var result0 = null;
+          pos = savedPos0;
+        }
+        
+        
+        
+        cache[cacheKey] = {
+          nextPos: pos,
+          result:  result0
+        };
+        return result0;
+      }
+      
+      function parse_bracketDelimitedCharacter() {
+        var cacheKey = 'bracketDelimitedCharacter@' + pos;
+        var cachedResult = cache[cacheKey];
+        if (cachedResult) {
+          pos = cachedResult.nextPos;
+          return cachedResult.result;
+        }
+        
+        
+        var result6 = parse_simpleBracketDelimitedCharacter();
+        if (result6 !== null) {
+          var result0 = result6;
+        } else {
+          var result5 = parse_simpleEscapeSequence();
+          if (result5 !== null) {
+            var result0 = result5;
+          } else {
+            var result4 = parse_zeroEscapeSequence();
+            if (result4 !== null) {
+              var result0 = result4;
+            } else {
+              var result3 = parse_hexEscapeSequence();
+              if (result3 !== null) {
+                var result0 = result3;
+              } else {
+                var result2 = parse_unicodeEscapeSequence();
+                if (result2 !== null) {
+                  var result0 = result2;
+                } else {
+                  var result1 = parse_eolEscapeSequence();
+                  if (result1 !== null) {
+                    var result0 = result1;
+                  } else {
+                    var result0 = null;;
+                  };
+                };
+              };
+            };
+          };
+        }
+        
+        
+        
+        cache[cacheKey] = {
+          nextPos: pos,
+          result:  result0
+        };
+        return result0;
+      }
+      
+      function parse_simpleBracketDelimitedCharacter() {
+        var cacheKey = 'simpleBracketDelimitedCharacter@' + pos;
+        var cachedResult = cache[cacheKey];
+        if (cachedResult) {
+          pos = cachedResult.nextPos;
+          return cachedResult.result;
+        }
+        
+        
+        var savedPos0 = pos;
+        var savedPos1 = pos;
+        var savedPos2 = pos;
+        var savedReportMatchFailuresVar0 = reportMatchFailures;
+        reportMatchFailures = false;
+        if (input.substr(pos, 1) === "]") {
+          var result8 = "]";
+          pos += 1;
+        } else {
+          var result8 = null;
+          if (reportMatchFailures) {
+            matchFailed("\"]\"");
+          }
+        }
+        if (result8 !== null) {
+          var result5 = result8;
+        } else {
+          if (input.substr(pos, 1) === "\\") {
+            var result7 = "\\";
+            pos += 1;
+          } else {
+            var result7 = null;
+            if (reportMatchFailures) {
+              matchFailed("\"\\\\\"");
+            }
+          }
+          if (result7 !== null) {
+            var result5 = result7;
+          } else {
+            var result6 = parse_eolChar();
+            if (result6 !== null) {
+              var result5 = result6;
+            } else {
+              var result5 = null;;
+            };
+          };
+        }
+        reportMatchFailures = savedReportMatchFailuresVar0;
+        if (result5 === null) {
+          var result3 = '';
+        } else {
+          var result3 = null;
+          pos = savedPos2;
+        }
+        if (result3 !== null) {
+          if (input.length > pos) {
+            var result4 = input.charAt(pos);
+            pos++;
+          } else {
+            var result4 = null;
+            if (reportMatchFailures) {
+              matchFailed('any character');
+            }
+          }
+          if (result4 !== null) {
+            var result1 = [result3, result4];
+          } else {
+            var result1 = null;
+            pos = savedPos1;
+          }
+        } else {
+          var result1 = null;
+          pos = savedPos1;
+        }
+        var result2 = result1 !== null
+          ? (function(char_) { return char_; })(result1[1])
+          : null;
+        if (result2 !== null) {
+          var result0 = result2;
+        } else {
+          var result0 = null;
+          pos = savedPos0;
+        }
+        
+        
+        
+        cache[cacheKey] = {
+          nextPos: pos,
+          result:  result0
+        };
+        return result0;
+      }
+      
+      function parse_simpleEscapeSequence() {
+        var cacheKey = 'simpleEscapeSequence@' + pos;
+        var cachedResult = cache[cacheKey];
+        if (cachedResult) {
+          pos = cachedResult.nextPos;
+          return cachedResult.result;
+        }
+        
+        
+        var savedPos0 = pos;
+        var savedPos1 = pos;
+        if (input.substr(pos, 1) === "\\") {
+          var result3 = "\\";
+          pos += 1;
+        } else {
+          var result3 = null;
+          if (reportMatchFailures) {
+            matchFailed("\"\\\\\"");
+          }
+        }
+        if (result3 !== null) {
+          var savedPos2 = pos;
+          var savedReportMatchFailuresVar0 = reportMatchFailures;
+          reportMatchFailures = false;
+          var result10 = parse_digit();
+          if (result10 !== null) {
+            var result6 = result10;
+          } else {
+            if (input.substr(pos, 1) === "x") {
+              var result9 = "x";
+              pos += 1;
+            } else {
+              var result9 = null;
+              if (reportMatchFailures) {
+                matchFailed("\"x\"");
+              }
+            }
+            if (result9 !== null) {
+              var result6 = result9;
+            } else {
+              if (input.substr(pos, 1) === "u") {
+                var result8 = "u";
+                pos += 1;
+              } else {
+                var result8 = null;
+                if (reportMatchFailures) {
+                  matchFailed("\"u\"");
+                }
+              }
+              if (result8 !== null) {
+                var result6 = result8;
+              } else {
+                var result7 = parse_eolChar();
+                if (result7 !== null) {
+                  var result6 = result7;
+                } else {
+                  var result6 = null;;
+                };
+              };
+            };
+          }
+          reportMatchFailures = savedReportMatchFailuresVar0;
+          if (result6 === null) {
+            var result4 = '';
+          } else {
+            var result4 = null;
+            pos = savedPos2;
+          }
+          if (result4 !== null) {
+            if (input.length > pos) {
+              var result5 = input.charAt(pos);
+              pos++;
+            } else {
+              var result5 = null;
+              if (reportMatchFailures) {
+                matchFailed('any character');
+              }
+            }
+            if (result5 !== null) {
+              var result1 = [result3, result4, result5];
+            } else {
+              var result1 = null;
+              pos = savedPos1;
+            }
+          } else {
+            var result1 = null;
+            pos = savedPos1;
+          }
+        } else {
+          var result1 = null;
+          pos = savedPos1;
+        }
+        var result2 = result1 !== null
+          ? (function(char_) {
+                return char_
+                  .replace("b", "\b")
+                  .replace("f", "\f")
+                  .replace("n", "\n")
+                  .replace("r", "\r")
+                  .replace("t", "\t")
+                  .replace("v", "\x0B") // IE does not recognize "\v".
+              })(result1[2])
+          : null;
+        if (result2 !== null) {
+          var result0 = result2;
+        } else {
+          var result0 = null;
+          pos = savedPos0;
+        }
+        
+        
+        
+        cache[cacheKey] = {
+          nextPos: pos,
+          result:  result0
+        };
+        return result0;
+      }
+      
+      function parse_zeroEscapeSequence() {
+        var cacheKey = 'zeroEscapeSequence@' + pos;
+        var cachedResult = cache[cacheKey];
+        if (cachedResult) {
+          pos = cachedResult.nextPos;
+          return cachedResult.result;
+        }
+        
+        
+        var savedPos0 = pos;
+        var savedPos1 = pos;
+        if (input.substr(pos, 2) === "\\0") {
+          var result3 = "\\0";
+          pos += 2;
+        } else {
+          var result3 = null;
+          if (reportMatchFailures) {
+            matchFailed("\"\\\\0\"");
+          }
+        }
+        if (result3 !== null) {
+          var savedPos2 = pos;
+          var savedReportMatchFailuresVar0 = reportMatchFailures;
+          reportMatchFailures = false;
+          var result5 = parse_digit();
+          reportMatchFailures = savedReportMatchFailuresVar0;
+          if (result5 === null) {
+            var result4 = '';
+          } else {
+            var result4 = null;
+            pos = savedPos2;
+          }
+          if (result4 !== null) {
+            var result1 = [result3, result4];
+          } else {
+            var result1 = null;
+            pos = savedPos1;
+          }
+        } else {
+          var result1 = null;
+          pos = savedPos1;
+        }
+        var result2 = result1 !== null
+          ? (function() { return "\0"; })()
+          : null;
+        if (result2 !== null) {
+          var result0 = result2;
+        } else {
+          var result0 = null;
+          pos = savedPos0;
+        }
+        
+        
+        
+        cache[cacheKey] = {
+          nextPos: pos,
+          result:  result0
+        };
+        return result0;
+      }
+      
+      function parse_hexEscapeSequence() {
+        var cacheKey = 'hexEscapeSequence@' + pos;
+        var cachedResult = cache[cacheKey];
+        if (cachedResult) {
+          pos = cachedResult.nextPos;
+          return cachedResult.result;
+        }
+        
+        
+        var savedPos0 = pos;
+        var savedPos1 = pos;
+        if (input.substr(pos, 2) === "\\x") {
+          var result3 = "\\x";
+          pos += 2;
+        } else {
+          var result3 = null;
+          if (reportMatchFailures) {
+            matchFailed("\"\\\\x\"");
+          }
+        }
+        if (result3 !== null) {
+          var result4 = parse_hexDigit();
+          if (result4 !== null) {
+            var result5 = parse_hexDigit();
+            if (result5 !== null) {
+              var result1 = [result3, result4, result5];
+            } else {
+              var result1 = null;
+              pos = savedPos1;
+            }
+          } else {
+            var result1 = null;
+            pos = savedPos1;
+          }
+        } else {
+          var result1 = null;
+          pos = savedPos1;
+        }
+        var result2 = result1 !== null
+          ? (function(h1, h2) {
+                return String.fromCharCode(parseInt("0x" + h1 + h2));
+              })(result1[1], result1[2])
+          : null;
+        if (result2 !== null) {
+          var result0 = result2;
+        } else {
+          var result0 = null;
+          pos = savedPos0;
+        }
+        
+        
+        
+        cache[cacheKey] = {
+          nextPos: pos,
+          result:  result0
+        };
+        return result0;
+      }
+      
+      function parse_unicodeEscapeSequence() {
+        var cacheKey = 'unicodeEscapeSequence@' + pos;
+        var cachedResult = cache[cacheKey];
+        if (cachedResult) {
+          pos = cachedResult.nextPos;
+          return cachedResult.result;
+        }
+        
+        
+        var savedPos0 = pos;
+        var savedPos1 = pos;
+        if (input.substr(pos, 2) === "\\u") {
+          var result3 = "\\u";
+          pos += 2;
+        } else {
+          var result3 = null;
+          if (reportMatchFailures) {
+            matchFailed("\"\\\\u\"");
+          }
+        }
+        if (result3 !== null) {
+          var result4 = parse_hexDigit();
+          if (result4 !== null) {
+            var result5 = parse_hexDigit();
+            if (result5 !== null) {
+              var result6 = parse_hexDigit();
+              if (result6 !== null) {
+                var result7 = parse_hexDigit();
+                if (result7 !== null) {
+                  var result1 = [result3, result4, result5, result6, result7];
+                } else {
+                  var result1 = null;
+                  pos = savedPos1;
+                }
+              } else {
+                var result1 = null;
+                pos = savedPos1;
+              }
+            } else {
+              var result1 = null;
+              pos = savedPos1;
+            }
+          } else {
+            var result1 = null;
+            pos = savedPos1;
+          }
+        } else {
+          var result1 = null;
+          pos = savedPos1;
+        }
+        var result2 = result1 !== null
+          ? (function(h1, h2, h3, h4) {
+                return String.fromCharCode(parseInt("0x" + h1 + h2 + h3 + h4));
+              })(result1[1], result1[2], result1[3], result1[4])
+          : null;
+        if (result2 !== null) {
+          var result0 = result2;
+        } else {
+          var result0 = null;
+          pos = savedPos0;
+        }
+        
+        
+        
+        cache[cacheKey] = {
+          nextPos: pos,
+          result:  result0
+        };
+        return result0;
+      }
+      
+      function parse_eolEscapeSequence() {
+        var cacheKey = 'eolEscapeSequence@' + pos;
+        var cachedResult = cache[cacheKey];
+        if (cachedResult) {
+          pos = cachedResult.nextPos;
+          return cachedResult.result;
+        }
+        
+        
+        var savedPos0 = pos;
+        var savedPos1 = pos;
+        if (input.substr(pos, 1) === "\\") {
+          var result3 = "\\";
+          pos += 1;
+        } else {
+          var result3 = null;
+          if (reportMatchFailures) {
+            matchFailed("\"\\\\\"");
+          }
+        }
+        if (result3 !== null) {
+          var result4 = parse_eol();
+          if (result4 !== null) {
+            var result1 = [result3, result4];
+          } else {
+            var result1 = null;
+            pos = savedPos1;
+          }
+        } else {
+          var result1 = null;
+          pos = savedPos1;
+        }
+        var result2 = result1 !== null
+          ? (function(eol) { return eol; })(result1[1])
+          : null;
+        if (result2 !== null) {
+          var result0 = result2;
+        } else {
+          var result0 = null;
+          pos = savedPos0;
+        }
+        
+        
+        
+        cache[cacheKey] = {
+          nextPos: pos,
+          result:  result0
+        };
+        return result0;
+      }
+      
+      function parse_digit() {
+        var cacheKey = 'digit@' + pos;
+        var cachedResult = cache[cacheKey];
+        if (cachedResult) {
+          pos = cachedResult.nextPos;
+          return cachedResult.result;
+        }
+        
+        
+        if (input.substr(pos).match(/^[0-9]/) !== null) {
+          var result0 = input.charAt(pos);
+          pos++;
+        } else {
+          var result0 = null;
+          if (reportMatchFailures) {
+            matchFailed("[0-9]");
+          }
+        }
+        
+        
+        
+        cache[cacheKey] = {
+          nextPos: pos,
+          result:  result0
+        };
+        return result0;
+      }
+      
+      function parse_hexDigit() {
+        var cacheKey = 'hexDigit@' + pos;
+        var cachedResult = cache[cacheKey];
+        if (cachedResult) {
+          pos = cachedResult.nextPos;
+          return cachedResult.result;
+        }
+        
+        
+        if (input.substr(pos).match(/^[0-9a-fA-F]/) !== null) {
+          var result0 = input.charAt(pos);
+          pos++;
+        } else {
+          var result0 = null;
+          if (reportMatchFailures) {
+            matchFailed("[0-9a-fA-F]");
+          }
+        }
+        
+        
+        
+        cache[cacheKey] = {
+          nextPos: pos,
+          result:  result0
+        };
+        return result0;
+      }
+      
+      function parse_letter() {
+        var cacheKey = 'letter@' + pos;
+        var cachedResult = cache[cacheKey];
+        if (cachedResult) {
+          pos = cachedResult.nextPos;
+          return cachedResult.result;
+        }
+        
+        
+        var result2 = parse_lowerCaseLetter();
+        if (result2 !== null) {
+          var result0 = result2;
+        } else {
+          var result1 = parse_upperCaseLetter();
+          if (result1 !== null) {
+            var result0 = result1;
+          } else {
+            var result0 = null;;
+          };
+        }
+        
+        
+        
+        cache[cacheKey] = {
+          nextPos: pos,
+          result:  result0
+        };
+        return result0;
+      }
+      
+      function parse_lowerCaseLetter() {
+        var cacheKey = 'lowerCaseLetter@' + pos;
+        var cachedResult = cache[cacheKey];
+        if (cachedResult) {
+          pos = cachedResult.nextPos;
+          return cachedResult.result;
+        }
+        
+        
+        if (input.substr(pos).match(/^[a-z]/) !== null) {
+          var result0 = input.charAt(pos);
+          pos++;
+        } else {
+          var result0 = null;
+          if (reportMatchFailures) {
+            matchFailed("[a-z]");
+          }
+        }
+        
+        
+        
+        cache[cacheKey] = {
+          nextPos: pos,
+          result:  result0
+        };
+        return result0;
+      }
+      
+      function parse_upperCaseLetter() {
+        var cacheKey = 'upperCaseLetter@' + pos;
+        var cachedResult = cache[cacheKey];
+        if (cachedResult) {
+          pos = cachedResult.nextPos;
+          return cachedResult.result;
+        }
+        
+        
+        if (input.substr(pos).match(/^[A-Z]/) !== null) {
+          var result0 = input.charAt(pos);
+          pos++;
+        } else {
+          var result0 = null;
+          if (reportMatchFailures) {
+            matchFailed("[A-Z]");
+          }
+        }
+        
+        
+        
+        cache[cacheKey] = {
+          nextPos: pos,
+          result:  result0
+        };
+        return result0;
+      }
+      
+      function parse___() {
+        var cacheKey = '__@' + pos;
+        var cachedResult = cache[cacheKey];
+        if (cachedResult) {
+          pos = cachedResult.nextPos;
+          return cachedResult.result;
+        }
+        
+        
+        var result0 = [];
+        var result4 = parse_whitespace();
+        if (result4 !== null) {
+          var result1 = result4;
+        } else {
+          var result3 = parse_eol();
+          if (result3 !== null) {
+            var result1 = result3;
+          } else {
+            var result2 = parse_comment();
+            if (result2 !== null) {
+              var result1 = result2;
+            } else {
+              var result1 = null;;
+            };
+          };
+        }
+        while (result1 !== null) {
+          result0.push(result1);
+          var result4 = parse_whitespace();
+          if (result4 !== null) {
+            var result1 = result4;
+          } else {
+            var result3 = parse_eol();
+            if (result3 !== null) {
+              var result1 = result3;
+            } else {
+              var result2 = parse_comment();
+              if (result2 !== null) {
+                var result1 = result2;
+              } else {
+                var result1 = null;;
+              };
+            };
+          }
+        }
+        
+        
+        
+        cache[cacheKey] = {
+          nextPos: pos,
+          result:  result0
+        };
+        return result0;
+      }
+      
+      function parse_comment() {
+        var cacheKey = 'comment@' + pos;
+        var cachedResult = cache[cacheKey];
+        if (cachedResult) {
+          pos = cachedResult.nextPos;
+          return cachedResult.result;
+        }
+        
+        var savedReportMatchFailures = reportMatchFailures;
+        reportMatchFailures = false;
+        var result2 = parse_singleLineComment();
+        if (result2 !== null) {
+          var result0 = result2;
+        } else {
+          var result1 = parse_multiLineComment();
+          if (result1 !== null) {
+            var result0 = result1;
+          } else {
+            var result0 = null;;
+          };
+        }
+        reportMatchFailures = savedReportMatchFailures;
+        if (reportMatchFailures && result0 === null) {
+          matchFailed("comment");
+        }
+        
+        cache[cacheKey] = {
+          nextPos: pos,
+          result:  result0
+        };
+        return result0;
+      }
+      
+      function parse_singleLineComment() {
+        var cacheKey = 'singleLineComment@' + pos;
+        var cachedResult = cache[cacheKey];
+        if (cachedResult) {
+          pos = cachedResult.nextPos;
+          return cachedResult.result;
+        }
+        
+        
+        var savedPos0 = pos;
+        if (input.substr(pos, 2) === "//") {
+          var result1 = "//";
+          pos += 2;
+        } else {
+          var result1 = null;
+          if (reportMatchFailures) {
+            matchFailed("\"//\"");
+          }
+        }
+        if (result1 !== null) {
+          var result2 = [];
+          var savedPos1 = pos;
+          var savedPos2 = pos;
+          var savedReportMatchFailuresVar0 = reportMatchFailures;
+          reportMatchFailures = false;
+          var result6 = parse_eolChar();
+          reportMatchFailures = savedReportMatchFailuresVar0;
+          if (result6 === null) {
+            var result4 = '';
+          } else {
+            var result4 = null;
+            pos = savedPos2;
+          }
+          if (result4 !== null) {
+            if (input.length > pos) {
+              var result5 = input.charAt(pos);
+              pos++;
+            } else {
+              var result5 = null;
+              if (reportMatchFailures) {
+                matchFailed('any character');
+              }
+            }
+            if (result5 !== null) {
+              var result3 = [result4, result5];
+            } else {
+              var result3 = null;
+              pos = savedPos1;
+            }
+          } else {
+            var result3 = null;
+            pos = savedPos1;
+          }
+          while (result3 !== null) {
+            result2.push(result3);
+            var savedPos1 = pos;
+            var savedPos2 = pos;
+            var savedReportMatchFailuresVar0 = reportMatchFailures;
+            reportMatchFailures = false;
+            var result6 = parse_eolChar();
+            reportMatchFailures = savedReportMatchFailuresVar0;
+            if (result6 === null) {
+              var result4 = '';
+            } else {
+              var result4 = null;
+              pos = savedPos2;
+            }
+            if (result4 !== null) {
+              if (input.length > pos) {
+                var result5 = input.charAt(pos);
+                pos++;
+              } else {
+                var result5 = null;
+                if (reportMatchFailures) {
+                  matchFailed('any character');
+                }
+              }
+              if (result5 !== null) {
+                var result3 = [result4, result5];
+              } else {
+                var result3 = null;
+                pos = savedPos1;
+              }
+            } else {
+              var result3 = null;
+              pos = savedPos1;
+            }
+          }
+          if (result2 !== null) {
+            var result0 = [result1, result2];
+          } else {
+            var result0 = null;
+            pos = savedPos0;
+          }
+        } else {
+          var result0 = null;
+          pos = savedPos0;
+        }
+        
+        
+        
+        cache[cacheKey] = {
+          nextPos: pos,
+          result:  result0
+        };
+        return result0;
+      }
+      
+      function parse_multiLineComment() {
+        var cacheKey = 'multiLineComment@' + pos;
+        var cachedResult = cache[cacheKey];
+        if (cachedResult) {
+          pos = cachedResult.nextPos;
+          return cachedResult.result;
+        }
+        
+        
+        var savedPos0 = pos;
+        if (input.substr(pos, 2) === "/*") {
+          var result1 = "/*";
+          pos += 2;
+        } else {
+          var result1 = null;
+          if (reportMatchFailures) {
+            matchFailed("\"/*\"");
+          }
+        }
+        if (result1 !== null) {
+          var result2 = [];
+          var savedPos1 = pos;
+          var savedPos2 = pos;
+          var savedReportMatchFailuresVar0 = reportMatchFailures;
+          reportMatchFailures = false;
+          if (input.substr(pos, 2) === "*/") {
+            var result7 = "*/";
+            pos += 2;
+          } else {
+            var result7 = null;
+            if (reportMatchFailures) {
+              matchFailed("\"*/\"");
+            }
+          }
+          reportMatchFailures = savedReportMatchFailuresVar0;
+          if (result7 === null) {
+            var result5 = '';
+          } else {
+            var result5 = null;
+            pos = savedPos2;
+          }
+          if (result5 !== null) {
+            if (input.length > pos) {
+              var result6 = input.charAt(pos);
+              pos++;
+            } else {
+              var result6 = null;
+              if (reportMatchFailures) {
+                matchFailed('any character');
+              }
+            }
+            if (result6 !== null) {
+              var result4 = [result5, result6];
+            } else {
+              var result4 = null;
+              pos = savedPos1;
+            }
+          } else {
+            var result4 = null;
+            pos = savedPos1;
+          }
+          while (result4 !== null) {
+            result2.push(result4);
+            var savedPos1 = pos;
+            var savedPos2 = pos;
+            var savedReportMatchFailuresVar0 = reportMatchFailures;
+            reportMatchFailures = false;
+            if (input.substr(pos, 2) === "*/") {
+              var result7 = "*/";
+              pos += 2;
+            } else {
+              var result7 = null;
+              if (reportMatchFailures) {
+                matchFailed("\"*/\"");
+              }
+            }
+            reportMatchFailures = savedReportMatchFailuresVar0;
+            if (result7 === null) {
+              var result5 = '';
+            } else {
+              var result5 = null;
+              pos = savedPos2;
+            }
+            if (result5 !== null) {
+              if (input.length > pos) {
+                var result6 = input.charAt(pos);
+                pos++;
+              } else {
+                var result6 = null;
+                if (reportMatchFailures) {
+                  matchFailed('any character');
+                }
+              }
+              if (result6 !== null) {
+                var result4 = [result5, result6];
+              } else {
+                var result4 = null;
+                pos = savedPos1;
+              }
+            } else {
+              var result4 = null;
+              pos = savedPos1;
+            }
+          }
+          if (result2 !== null) {
+            if (input.substr(pos, 2) === "*/") {
+              var result3 = "*/";
+              pos += 2;
+            } else {
+              var result3 = null;
+              if (reportMatchFailures) {
+                matchFailed("\"*/\"");
+              }
+            }
+            if (result3 !== null) {
+              var result0 = [result1, result2, result3];
+            } else {
+              var result0 = null;
+              pos = savedPos0;
+            }
+          } else {
+            var result0 = null;
+            pos = savedPos0;
+          }
+        } else {
+          var result0 = null;
+          pos = savedPos0;
+        }
+        
+        
+        
+        cache[cacheKey] = {
+          nextPos: pos,
+          result:  result0
+        };
+        return result0;
+      }
+      
+      function parse_eol() {
+        var cacheKey = 'eol@' + pos;
+        var cachedResult = cache[cacheKey];
+        if (cachedResult) {
+          pos = cachedResult.nextPos;
+          return cachedResult.result;
+        }
+        
+        var savedReportMatchFailures = reportMatchFailures;
+        reportMatchFailures = false;
+        if (input.substr(pos, 1) === "\n") {
+          var result5 = "\n";
+          pos += 1;
+        } else {
+          var result5 = null;
+          if (reportMatchFailures) {
+            matchFailed("\"\\n\"");
+          }
+        }
+        if (result5 !== null) {
+          var result0 = result5;
+        } else {
+          if (input.substr(pos, 2) === "\r\n") {
+            var result4 = "\r\n";
+            pos += 2;
+          } else {
+            var result4 = null;
+            if (reportMatchFailures) {
+              matchFailed("\"\\r\\n\"");
+            }
+          }
+          if (result4 !== null) {
+            var result0 = result4;
+          } else {
+            if (input.substr(pos, 1) === "\r") {
+              var result3 = "\r";
+              pos += 1;
+            } else {
+              var result3 = null;
+              if (reportMatchFailures) {
+                matchFailed("\"\\r\"");
+              }
+            }
+            if (result3 !== null) {
+              var result0 = result3;
+            } else {
+              if (input.substr(pos, 1) === "\u2028") {
+                var result2 = "\u2028";
+                pos += 1;
+              } else {
+                var result2 = null;
+                if (reportMatchFailures) {
+                  matchFailed("\"\\u2028\"");
+                }
+              }
+              if (result2 !== null) {
+                var result0 = result2;
+              } else {
+                if (input.substr(pos, 1) === "\u2029") {
+                  var result1 = "\u2029";
+                  pos += 1;
+                } else {
+                  var result1 = null;
+                  if (reportMatchFailures) {
+                    matchFailed("\"\\u2029\"");
+                  }
+                }
+                if (result1 !== null) {
+                  var result0 = result1;
+                } else {
+                  var result0 = null;;
+                };
+              };
+            };
+          };
+        }
+        reportMatchFailures = savedReportMatchFailures;
+        if (reportMatchFailures && result0 === null) {
+          matchFailed("end of line");
+        }
+        
+        cache[cacheKey] = {
+          nextPos: pos,
+          result:  result0
+        };
+        return result0;
+      }
+      
+      function parse_eolChar() {
+        var cacheKey = 'eolChar@' + pos;
+        var cachedResult = cache[cacheKey];
+        if (cachedResult) {
+          pos = cachedResult.nextPos;
+          return cachedResult.result;
+        }
+        
+        
+        if (input.substr(pos).match(/^[\n\r\u2028\u2029]/) !== null) {
+          var result0 = input.charAt(pos);
+          pos++;
+        } else {
+          var result0 = null;
+          if (reportMatchFailures) {
+            matchFailed("[\\n\\r\\u2028\\u2029]");
+          }
+        }
+        
+        
+        
+        cache[cacheKey] = {
+          nextPos: pos,
+          result:  result0
+        };
+        return result0;
+      }
+      
+      function parse_whitespace() {
+        var cacheKey = 'whitespace@' + pos;
+        var cachedResult = cache[cacheKey];
+        if (cachedResult) {
+          pos = cachedResult.nextPos;
+          return cachedResult.result;
+        }
+        
+        var savedReportMatchFailures = reportMatchFailures;
+        reportMatchFailures = false;
+        if (input.substr(pos).match(/^[ 	\xA0\uFEFF\u1680\u180E\u2000-\u200A\u202F\u205F\u3000]/) !== null) {
+          var result0 = input.charAt(pos);
+          pos++;
+        } else {
+          var result0 = null;
+          if (reportMatchFailures) {
+            matchFailed("[ 	\\xA0\\uFEFF\\u1680\\u180E\\u2000-\\u200A\\u202F\\u205F\\u3000]");
+          }
+        }
+        reportMatchFailures = savedReportMatchFailures;
+        if (reportMatchFailures && result0 === null) {
+          matchFailed("whitespace");
+        }
+        
+        cache[cacheKey] = {
+          nextPos: pos,
+          result:  result0
+        };
+        return result0;
+      }
+      
+      function buildErrorMessage() {
+        function buildExpected(failuresExpected) {
+          failuresExpected.sort();
+          
+          var lastFailure = null;
+          var failuresExpectedUnique = [];
+          for (var i = 0; i < failuresExpected.length; i++) {
+            if (failuresExpected[i] !== lastFailure) {
+              failuresExpectedUnique.push(failuresExpected[i]);
+              lastFailure = failuresExpected[i];
+            }
+          }
+          
+          switch (failuresExpectedUnique.length) {
+            case 0:
+              return 'end of input';
+            case 1:
+              return failuresExpectedUnique[0];
+            default:
+              return failuresExpectedUnique.slice(0, failuresExpectedUnique.length - 1).join(', ')
+                + ' or '
+                + failuresExpectedUnique[failuresExpectedUnique.length - 1];
+          }
+        }
+        
+        var expected = buildExpected(rightmostMatchFailuresExpected);
+        var actualPos = Math.max(pos, rightmostMatchFailuresPos);
+        var actual = actualPos < input.length
+          ? quote(input.charAt(actualPos))
+          : 'end of input';
+        
+        return 'Expected ' + expected + ' but ' + actual + ' found.';
+      }
+      
+      function computeErrorPosition() {
+        /*
+         * The first idea was to use |String.split| to break the input up to the
+         * error position along newlines and derive the line and column from
+         * there. However IE's |split| implementation is so broken that it was
+         * enough to prevent it.
+         */
+        
+        var line = 1;
+        var column = 1;
+        var seenCR = false;
+        
+        for (var i = 0; i <  rightmostMatchFailuresPos; i++) {
+          var ch = input.charAt(i);
+          if (ch === '\n') {
+            if (!seenCR) { line++; }
+            column = 1;
+            seenCR = false;
+          } else if (ch === '\r' | ch === '\u2028' || ch === '\u2029') {
+            line++;
+            column = 1;
+            seenCR = true;
+          } else {
+            column++;
+            seenCR = false;
+          }
+        }
+        
+        return { line: line, column: column };
+      }
+      
+      
+      
+      var result = parseFunctions[startRule]();
+      
+      /*
+       * The parser is now in one of the following three states:
+       *
+       * 1. The parser successfully parsed the whole input.
+       *
+       *    - |result !== null|
+       *    - |pos === input.length|
+       *    - |rightmostMatchFailuresExpected| may or may not contain something
+       *
+       * 2. The parser successfully parsed only a part of the input.
+       *
+       *    - |result !== null|
+       *    - |pos < input.length|
+       *    - |rightmostMatchFailuresExpected| may or may not contain something
+       *
+       * 3. The parser did not successfully parse any part of the input.
+       *
+       *   - |result === null|
+       *   - |pos === 0|
+       *   - |rightmostMatchFailuresExpected| contains at least one failure
+       *
+       * All code following this comment (including called functions) must
+       * handle these states.
+       */
+      if (result === null || pos !== input.length) {
+        var errorPosition = computeErrorPosition();
+        throw new this.SyntaxError(
+          buildErrorMessage(),
+          errorPosition.line,
+          errorPosition.column
+        );
+      }
+      
+      return result;
+    },
+    
+    /* Returns the parser source code. */
+    toSource: function() { return this._source; }
+  };
+  
+  /* Thrown when a parser encounters a syntax error. */
+  
+  result.SyntaxError = function(message, line, column) {
+    this.name = 'SyntaxError';
+    this.message = message;
+    this.line = line;
+    this.column = column;
+  };
+  
+  result.SyntaxError.prototype = Error.prototype;
+  
+  return result;
+})();
+PEG.compiler = {
+  /*
+   * Generates a parser from a specified grammar AST. Throws |PEG.GrammarError|
+   * if the AST contains a semantic error. Note that not all errors are detected
+   * during the generation and some may protrude to the generated parser and
+   * cause its malfunction.
+   */
+  compile: function(ast) {
+    var CHECK_NAMES = [
+      "missingReferencedRules",
+      "leftRecursion"
+    ];
+
+    var PASS_NAMES = [
+      "proxyRules"
+    ];
+
+    for (var i = 0; i < CHECK_NAMES.length; i++) {
+      this.checks[CHECK_NAMES[i]](ast);
+    }
+
+    for (var i = 0; i < PASS_NAMES.length; i++) {
+      ast = this.passes[PASS_NAMES[i]](ast);
+    }
+
+    var source = this.emitter(ast);
+    var result = eval(source);
+    result._source = source;
+
+    return result;
+  }
+};
+
+/*
+ * Checks made on the grammar AST before compilation. Each check is a function
+ * that is passed the AST and does not return anything. If the check passes, the
+ * function does not do anything special, otherwise it throws
+ * |PEG.GrammarError|. The order in which the checks are run is specified in
+ * |PEG.compiler.compile| and should be the same as the order of definitions
+ * here.
+ */
+PEG.compiler.checks = {
+  /* Checks that all referenced rules exist. */
+  missingReferencedRules: function(ast) {
+    function nop() {}
+
+    function checkExpression(node) { check(node.expression); }
+
+    function checkSubnodes(propertyName) {
+      return function(node) { each(node[propertyName], check); };
+    }
+
+    var check = buildNodeVisitor({
+      grammar:
+        function(node) {
+          for (var name in node.rules) {
+            check(node.rules[name]);
+          }
+        },
+
+      rule:         checkExpression,
+      choice:       checkSubnodes("alternatives"),
+      sequence:     checkSubnodes("elements"),
+      labeled:      checkExpression,
+      simple_and:   checkExpression,
+      simple_not:   checkExpression,
+      semantic_and: nop,
+      semantic_not: nop,
+      optional:     checkExpression,
+      zero_or_more: checkExpression,
+      one_or_more:  checkExpression,
+      action:       checkExpression,
+
+      rule_ref:
+        function(node) {
+          if (ast.rules[node.name] === undefined) {
+            throw new PEG.GrammarError(
+              "Referenced rule \"" + node.name + "\" does not exist."
+            );
+          }
+        },
+
+      literal:      nop,
+      any:          nop,
+      "class":      nop
+    });
+
+    check(ast);
+  },
+
+  /* Checks that no left recursion is present. */
+  leftRecursion: function(ast) {
+    function nop() {}
+
+    function checkExpression(node, appliedRules) {
+      check(node.expression, appliedRules);
+    }
+
+    var check = buildNodeVisitor({
+      grammar:
+        function(node, appliedRules) {
+          for (var name in node.rules) {
+            check(node.rules[name], appliedRules);
+          }
+        },
+
+      rule:
+        function(node, appliedRules) {
+          check(node.expression, appliedRules.concat(node.name));
+        },
+
+      choice:
+        function(node, appliedRules) {
+          each(node.alternatives, function(alternative) {
+            check(alternative, appliedRules);
+          });
+        },
+
+      sequence:
+        function(node, appliedRules) {
+          if (node.elements.length > 0) {
+            check(node.elements[0], appliedRules);
+          }
+        },
+
+      labeled:      checkExpression,
+      simple_and:   checkExpression,
+      simple_not:   checkExpression,
+      semantic_and: nop,
+      semantic_not: nop,
+      optional:     checkExpression,
+      zero_or_more: checkExpression,
+      one_or_more:  checkExpression,
+      action:       checkExpression,
+
+      rule_ref:
+        function(node, appliedRules) {
+          if (contains(appliedRules, node.name)) {
+            throw new PEG.GrammarError(
+              "Left recursion detected for rule \"" + node.name + "\"."
+            );
+          }
+          check(ast.rules[node.name], appliedRules);
+        },
+
+      literal:      nop,
+      any:          nop,
+      "class":      nop
+    });
+
+    check(ast, []);
+  }
+};
+/*
+ * Optimalization passes made on the grammar AST before compilation. Each pass
+ * is a function that is passed the AST and returns a new AST. The AST can be
+ * modified in-place by the pass. The order in which the passes are run is
+ * specified in |PEG.compiler.compile| and should be the same as the order of
+ * definitions here.
+ */
+PEG.compiler.passes = {
+  /*
+   * Removes proxy rules -- that is, rules that only delegate to other rule.
+   */
+  proxyRules: function(ast) {
+    function isProxyRule(node) {
+      return node.type === "rule" && node.expression.type === "rule_ref";
+    }
+
+    function replaceRuleRefs(ast, from, to) {
+      function nop() {}
+
+      function replaceInExpression(node, from, to) {
+        replace(node.expression, from, to);
+      }
+
+      function replaceInSubnodes(propertyName) {
+        return function(node, from, to) {
+          each(node[propertyName], function(subnode) {
+            replace(subnode, from, to);
+          });
+        };
+      }
+
+      var replace = buildNodeVisitor({
+        grammar:
+          function(node, from, to) {
+            for (var name in node.rules) {
+              replace(node.rules[name], from, to);
+            }
+          },
+
+        rule:         replaceInExpression,
+        choice:       replaceInSubnodes("alternatives"),
+        sequence:     replaceInSubnodes("elements"),
+        labeled:      replaceInExpression,
+        simple_and:   replaceInExpression,
+        simple_not:   replaceInExpression,
+        semantic_and: nop,
+        semantic_not: nop,
+        optional:     replaceInExpression,
+        zero_or_more: replaceInExpression,
+        one_or_more:  replaceInExpression,
+        action:       replaceInExpression,
+
+        rule_ref:
+          function(node, from, to) {
+            if (node.name === from) {
+              node.name = to;
+            }
+          },
+
+        literal:      nop,
+        any:          nop,
+        "class":      nop
+      });
+
+      replace(ast, from, to);
+    }
+
+    for (var name in ast.rules) {
+      if (isProxyRule(ast.rules[name])) {
+        replaceRuleRefs(ast, ast.rules[name].name, ast.rules[name].expression.name);
+        if (name === ast.startRule) {
+          ast.startRule = ast.rules[name].expression.name;
+        }
+        delete ast.rules[name];
+      }
+    }
+
+    return ast;
+  }
+};
+/* Emits the generated code for the AST. */
+PEG.compiler.emitter = function(ast) {
+  /*
+   * Takes parts of code, interpolates variables inside them and joins them with
+   * a newline.
+   *
+   * Variables are delimited with "${" and "}" and their names must be valid
+   * identifiers (i.e. they must match [a-zA-Z_][a-zA-Z0-9_]*). Variable values
+   * are specified as properties of the last parameter (if this is an object,
+   * otherwise empty variable set is assumed). Undefined variables result in
+   * throwing |Error|.
+   *
+   * There can be a filter specified after the variable name, prefixed with "|".
+   * The filter name must be a valid identifier. The only recognized filter
+   * right now is "string", which quotes the variable value as a JavaScript
+   * string. Unrecognized filters result in throwing |Error|.
+   *
+   * If any part has multiple lines and the first line is indented by some
+   * amount of whitespace (as defined by the /\s+/ JavaScript regular
+   * expression), second to last lines are indented by the same amount of
+   * whitespace. This results in nicely indented multiline code in variables
+   * without making the templates look ugly.
+   *
+   * Examples:
+   *
+   *   formatCode("foo", "bar");                           // "foo\nbar"
+   *   formatCode("foo", "${bar}", { bar: "baz" });        // "foo\nbaz"
+   *   formatCode("foo", "${bar}");                        // throws Error
+   *   formatCode("foo", "${bar|string}", { bar: "baz" }); // "foo\n\"baz\""
+   *   formatCode("foo", "${bar|eeek}", { bar: "baz" });   // throws Error
+   *   formatCode("foo", "${bar}", { bar: "  baz\nqux" }); // "foo\n  baz\n  qux"
+   */
+  function formatCode() {
+    function interpolateVariablesInParts(parts) {
+      return map(parts, function(part) {
+        return part.replace(
+          /\$\{([a-zA-Z_][a-zA-Z0-9_]*)(\|([a-zA-Z_][a-zA-Z0-9_]*))?\}/g,
+          function(match, name, dummy, filter) {
+            var value = vars[name];
+            if (value === undefined) {
+              throw new Error("Undefined variable: \"" + name + "\".");
+            }
+
+            if (filter !== undefined && filter != "") { // JavaScript engines differ here.
+              if (filter === "string") {
+                return quote(value);
+              } else {
+                throw new Error("Unrecognized filter: \"" + filter + "\".");
+              }
+            } else {
+              return value;
+            }
+          }
+        );
+      });
+    }
+
+    function indentMultilineParts(parts) {
+      return map(parts, function(part) {
+        if (!/\n/.test(part)) { return part; }
+
+        var firstLineWhitespacePrefix = part.match(/^\s*/)[0];
+        var lines = part.split("\n");
+        var linesIndented = [lines[0]].concat(
+          map(lines.slice(1), function(line) {
+            return firstLineWhitespacePrefix + line;
+          })
+        );
+        return linesIndented.join("\n");
+      });
+    }
+
+    var args = Array.prototype.slice.call(arguments);
+    var vars = args[args.length - 1] instanceof Object ? args.pop() : {};
+
+    return indentMultilineParts(interpolateVariablesInParts(args)).join("\n");
+  };
+
+  /* Unique ID generator. */
+  var UID = {
+    _counters: {},
+
+    next: function(prefix) {
+      this._counters[prefix] = this._counters[prefix] || 0;
+      return prefix + this._counters[prefix]++;
+    },
+
+    reset: function() {
+      this._counters = {};
+    }
+  };
+
+  var emit = buildNodeVisitor({
+    grammar: function(node) {
+      var initializerCode = node.initializer !== null
+        ? emit(node.initializer)
+        : "";
+
+      var parseFunctionTableItems = [];
+      for (var name in node.rules) {
+        parseFunctionTableItems.push(quote(name) + ": parse_" + name);
+      }
+      parseFunctionTableItems.sort();
+
+      var parseFunctionDefinitions = [];
+      for (var name in node.rules) {
+        parseFunctionDefinitions.push(emit(node.rules[name]));
+      }
+
+      return formatCode(
+        "(function(){",
+        "  /* Generated by PEG.js 0.6.2 (http://pegjs.majda.cz/). */",
+        "  ",
+        "  var result = {",
+        "    /*",
+        "     * Parses the input with a generated parser. If the parsing is successfull,",
+        "     * returns a value explicitly or implicitly specified by the grammar from",
+        "     * which the parser was generated (see |PEG.buildParser|). If the parsing is",
+        "     * unsuccessful, throws |PEG.parser.SyntaxError| describing the error.",
+        "     */",
+        "    parse: function(input, startRule) {",
+        "      var parseFunctions = {",
+        "        ${parseFunctionTableItems}",
+        "      };",
+        "      ",
+        "      if (startRule !== undefined) {",
+        "        if (parseFunctions[startRule] === undefined) {",
+        "          throw new Error(\"Invalid rule name: \" + quote(startRule) + \".\");",
+        "        }",
+        "      } else {",
+        "        startRule = ${startRule|string};",
+        "      }",
+        "      ",
+        "      var pos = 0;",
+        "      var reportMatchFailures = true;",
+        "      var rightmostMatchFailuresPos = 0;",
+        "      var rightmostMatchFailuresExpected = [];",
+        "      var cache = {};",
+        "      ",
+        /* This needs to be in sync with |padLeft| in utils.js. */
+        "      function padLeft(input, padding, length) {",
+        "        var result = input;",
+        "        ",
+        "        var padLength = length - input.length;",
+        "        for (var i = 0; i < padLength; i++) {",
+        "          result = padding + result;",
+        "        }",
+        "        ",
+        "        return result;",
+        "      }",
+        "      ",
+        /* This needs to be in sync with |escape| in utils.js. */
+        "      function escape(ch) {",
+        "        var charCode = ch.charCodeAt(0);",
+        "        ",
+        "        if (charCode <= 0xFF) {",
+        "          var escapeChar = 'x';",
+        "          var length = 2;",
+        "        } else {",
+        "          var escapeChar = 'u';",
+        "          var length = 4;",
+        "        }",
+        "        ",
+        "        return '\\\\' + escapeChar + padLeft(charCode.toString(16).toUpperCase(), '0', length);",
+        "      }",
+        "      ",
+        /* This needs to be in sync with |quote| in utils.js. */
+        "      function quote(s) {",
+        "        /*",
+        "         * ECMA-262, 5th ed., 7.8.4: All characters may appear literally in a",
+        "         * string literal except for the closing quote character, backslash,",
+        "         * carriage return, line separator, paragraph separator, and line feed.",
+        "         * Any character may appear in the form of an escape sequence.",
+        "         */",
+        "        return '\"' + s",
+        "          .replace(/\\\\/g, '\\\\\\\\')            // backslash",
+        "          .replace(/\"/g, '\\\\\"')              // closing quote character",
+        "          .replace(/\\r/g, '\\\\r')             // carriage return",
+        "          .replace(/\\n/g, '\\\\n')             // line feed",
+        "          .replace(/[\\x80-\\uFFFF]/g, escape) // non-ASCII characters",
+        "          + '\"';",
+        "      }",
+        "      ",
+        "      function matchFailed(failure) {",
+        "        if (pos < rightmostMatchFailuresPos) {",
+        "          return;",
+        "        }",
+        "        ",
+        "        if (pos > rightmostMatchFailuresPos) {",
+        "          rightmostMatchFailuresPos = pos;",
+        "          rightmostMatchFailuresExpected = [];",
+        "        }",
+        "        ",
+        "        rightmostMatchFailuresExpected.push(failure);",
+        "      }",
+        "      ",
+        "      ${parseFunctionDefinitions}",
+        "      ",
+        "      function buildErrorMessage() {",
+        "        function buildExpected(failuresExpected) {",
+        "          failuresExpected.sort();",
+        "          ",
+        "          var lastFailure = null;",
+        "          var failuresExpectedUnique = [];",
+        "          for (var i = 0; i < failuresExpected.length; i++) {",
+        "            if (failuresExpected[i] !== lastFailure) {",
+        "              failuresExpectedUnique.push(failuresExpected[i]);",
+        "              lastFailure = failuresExpected[i];",
+        "            }",
+        "          }",
+        "          ",
+        "          switch (failuresExpectedUnique.length) {",
+        "            case 0:",
+        "              return 'end of input';",
+        "            case 1:",
+        "              return failuresExpectedUnique[0];",
+        "            default:",
+        "              return failuresExpectedUnique.slice(0, failuresExpectedUnique.length - 1).join(', ')",
+        "                + ' or '",
+        "                + failuresExpectedUnique[failuresExpectedUnique.length - 1];",
+        "          }",
+        "        }",
+        "        ",
+        "        var expected = buildExpected(rightmostMatchFailuresExpected);",
+        "        var actualPos = Math.max(pos, rightmostMatchFailuresPos);",
+        "        var actual = actualPos < input.length",
+        "          ? quote(input.charAt(actualPos))",
+        "          : 'end of input';",
+        "        ",
+        "        return 'Expected ' + expected + ' but ' + actual + ' found.';",
+        "      }",
+        "      ",
+        "      function computeErrorPosition() {",
+        "        /*",
+        "         * The first idea was to use |String.split| to break the input up to the",
+        "         * error position along newlines and derive the line and column from",
+        "         * there. However IE's |split| implementation is so broken that it was",
+        "         * enough to prevent it.",
+        "         */",
+        "        ",
+        "        var line = 1;",
+        "        var column = 1;",
+        "        var seenCR = false;",
+        "        ",
+        "        for (var i = 0; i <  rightmostMatchFailuresPos; i++) {",
+        "          var ch = input.charAt(i);",
+        "          if (ch === '\\n') {",
+        "            if (!seenCR) { line++; }",
+        "            column = 1;",
+        "            seenCR = false;",
+        "          } else if (ch === '\\r' | ch === '\\u2028' || ch === '\\u2029') {",
+        "            line++;",
+        "            column = 1;",
+        "            seenCR = true;",
+        "          } else {",
+        "            column++;",
+        "            seenCR = false;",
+        "          }",
+        "        }",
+        "        ",
+        "        return { line: line, column: column };",
+        "      }",
+        "      ",
+        "      ${initializerCode}",
+        "      ",
+        "      var result = parseFunctions[startRule]();",
+        "      ",
+        "      /*",
+        "       * The parser is now in one of the following three states:",
+        "       *",
+        "       * 1. The parser successfully parsed the whole input.",
+        "       *",
+        "       *    - |result !== null|",
+        "       *    - |pos === input.length|",
+        "       *    - |rightmostMatchFailuresExpected| may or may not contain something",
+        "       *",
+        "       * 2. The parser successfully parsed only a part of the input.",
+        "       *",
+        "       *    - |result !== null|",
+        "       *    - |pos < input.length|",
+        "       *    - |rightmostMatchFailuresExpected| may or may not contain something",
+        "       *",
+        "       * 3. The parser did not successfully parse any part of the input.",
+        "       *",
+        "       *   - |result === null|",
+        "       *   - |pos === 0|",
+        "       *   - |rightmostMatchFailuresExpected| contains at least one failure",
+        "       *",
+        "       * All code following this comment (including called functions) must",
+        "       * handle these states.",
+        "       */",
+        "      if (result === null || pos !== input.length) {",
+        "        var errorPosition = computeErrorPosition();",
+        "        throw new this.SyntaxError(",
+        "          buildErrorMessage(),",
+        "          errorPosition.line,",
+        "          errorPosition.column",
+        "        );",
+        "      }",
+        "      ",
+        "      return result;",
+        "    },",
+        "    ",
+        "    /* Returns the parser source code. */",
+        "    toSource: function() { return this._source; }",
+        "  };",
+        "  ",
+        "  /* Thrown when a parser encounters a syntax error. */",
+        "  ",
+        "  result.SyntaxError = function(message, line, column) {",
+        "    this.name = 'SyntaxError';",
+        "    this.message = message;",
+        "    this.line = line;",
+        "    this.column = column;",
+        "  };",
+        "  ",
+        "  result.SyntaxError.prototype = Error.prototype;",
+        "  ",
+        "  return result;",
+        "})()",
+        {
+          initializerCode:          initializerCode,
+          parseFunctionTableItems:  parseFunctionTableItems.join(",\n"),
+          parseFunctionDefinitions: parseFunctionDefinitions.join("\n\n"),
+          startRule:                node.startRule
+        }
+      );
+    },
+
+    initializer: function(node) {
+      return node.code;
+    },
+
+    rule: function(node) {
+      /*
+       * We want to reset variable names at the beginning of every function so
+       * that a little change in the source grammar does not change variables in
+       * all the generated code. This is desired especially when one has the
+       * generated grammar stored in a VCS (this is true e.g. for our
+       * metagrammar).
+       */
+      UID.reset();
+
+      var resultVar = UID.next("result");
+
+      if (node.displayName !== null) {
+        var setReportMatchFailuresCode = formatCode(
+          "var savedReportMatchFailures = reportMatchFailures;",
+          "reportMatchFailures = false;"
+        );
+        var restoreReportMatchFailuresCode = formatCode(
+          "reportMatchFailures = savedReportMatchFailures;"
+        );
+        var reportMatchFailureCode = formatCode(
+          "if (reportMatchFailures && ${resultVar} === null) {",
+          "  matchFailed(${displayName|string});",
+          "}",
+          {
+            displayName: node.displayName,
+            resultVar:   resultVar
+          }
+        );
+      } else {
+        var setReportMatchFailuresCode = "";
+        var restoreReportMatchFailuresCode = "";
+        var reportMatchFailureCode = "";
+      }
+
+      return formatCode(
+        "function parse_${name}() {",
+        "  var cacheKey = '${name}@' + pos;",
+        "  var cachedResult = cache[cacheKey];",
+        "  if (cachedResult) {",
+        "    pos = cachedResult.nextPos;",
+        "    return cachedResult.result;",
+        "  }",
+        "  ",
+        "  ${setReportMatchFailuresCode}",
+        "  ${code}",
+        "  ${restoreReportMatchFailuresCode}",
+        "  ${reportMatchFailureCode}",
+        "  ",
+        "  cache[cacheKey] = {",
+        "    nextPos: pos,",
+        "    result:  ${resultVar}",
+        "  };",
+        "  return ${resultVar};",
+        "}",
+        {
+          name:                           node.name,
+          setReportMatchFailuresCode:     setReportMatchFailuresCode,
+          restoreReportMatchFailuresCode: restoreReportMatchFailuresCode,
+          reportMatchFailureCode:         reportMatchFailureCode,
+          code:                           emit(node.expression, resultVar),
+          resultVar:                      resultVar
+        }
+      );
+    },
+
+    /*
+     * The contract for all code fragments generated by the following functions
+     * is as follows:
+     *
+     * * The code fragment should try to match a part of the input starting with
+     * the position indicated in |pos|. That position may point past the end of
+     * the input.
+     *
+     * * If the code fragment matches the input, it advances |pos| after the
+     *   matched part of the input and sets variable with a name stored in
+     *   |resultVar| to appropriate value, which is always non-null.
+     *
+     * * If the code fragment does not match the input, it does not change |pos|
+     *   and it sets a variable with a name stored in |resultVar| to |null|.
+     */
+
+    choice: function(node, resultVar) {
+      var code = formatCode(
+        "var ${resultVar} = null;",
+        { resultVar: resultVar }
+      );
+
+      for (var i = node.alternatives.length - 1; i >= 0; i--) {
+        var alternativeResultVar = UID.next("result");
+        code = formatCode(
+          "${alternativeCode}",
+          "if (${alternativeResultVar} !== null) {",
+          "  var ${resultVar} = ${alternativeResultVar};",
+          "} else {",
+          "  ${code};",
+          "}",
+          {
+            alternativeCode:      emit(node.alternatives[i], alternativeResultVar),
+            alternativeResultVar: alternativeResultVar,
+            code:                 code,
+            resultVar:            resultVar
+          }
+        );
+      }
+
+      return code;
+    },
+
+    sequence: function(node, resultVar) {
+      var savedPosVar = UID.next("savedPos");
+
+      var elementResultVars = map(node.elements, function() {
+        return UID.next("result")
+      });
+
+      var code = formatCode(
+        "var ${resultVar} = ${elementResultVarArray};",
+        {
+          resultVar:             resultVar,
+          elementResultVarArray: "[" + elementResultVars.join(", ") + "]"
+        }
+      );
+
+      for (var i = node.elements.length - 1; i >= 0; i--) {
+        code = formatCode(
+          "${elementCode}",
+          "if (${elementResultVar} !== null) {",
+          "  ${code}",
+          "} else {",
+          "  var ${resultVar} = null;",
+          "  pos = ${savedPosVar};",
+          "}",
+          {
+            elementCode:      emit(node.elements[i], elementResultVars[i]),
+            elementResultVar: elementResultVars[i],
+            code:             code,
+            savedPosVar:      savedPosVar,
+            resultVar:        resultVar
+          }
+        );
+      }
+
+      return formatCode(
+        "var ${savedPosVar} = pos;",
+        "${code}",
+        {
+          code:        code,
+          savedPosVar: savedPosVar
+        }
+      );
+    },
+
+    labeled: function(node, resultVar) {
+      return emit(node.expression, resultVar);
+    },
+
+    simple_and: function(node, resultVar) {
+      var savedPosVar                 = UID.next("savedPos");
+      var savedReportMatchFailuresVar = UID.next("savedReportMatchFailuresVar");
+      var expressionResultVar         = UID.next("result");
+
+      return formatCode(
+        "var ${savedPosVar} = pos;",
+        "var ${savedReportMatchFailuresVar} = reportMatchFailures;",
+        "reportMatchFailures = false;",
+        "${expressionCode}",
+        "reportMatchFailures = ${savedReportMatchFailuresVar};",
+        "if (${expressionResultVar} !== null) {",
+        "  var ${resultVar} = '';",
+        "  pos = ${savedPosVar};",
+        "} else {",
+        "  var ${resultVar} = null;",
+        "}",
+        {
+          expressionCode:              emit(node.expression, expressionResultVar),
+          expressionResultVar:         expressionResultVar,
+          savedPosVar:                 savedPosVar,
+          savedReportMatchFailuresVar: savedReportMatchFailuresVar,
+          resultVar:                   resultVar
+        }
+      );
+    },
+
+    simple_not: function(node, resultVar) {
+      var savedPosVar                 = UID.next("savedPos");
+      var savedReportMatchFailuresVar = UID.next("savedReportMatchFailuresVar");
+      var expressionResultVar         = UID.next("result");
+
+      return formatCode(
+        "var ${savedPosVar} = pos;",
+        "var ${savedReportMatchFailuresVar} = reportMatchFailures;",
+        "reportMatchFailures = false;",
+        "${expressionCode}",
+        "reportMatchFailures = ${savedReportMatchFailuresVar};",
+        "if (${expressionResultVar} === null) {",
+        "  var ${resultVar} = '';",
+        "} else {",
+        "  var ${resultVar} = null;",
+        "  pos = ${savedPosVar};",
+        "}",
+        {
+          expressionCode:              emit(node.expression, expressionResultVar),
+          expressionResultVar:         expressionResultVar,
+          savedPosVar:                 savedPosVar,
+          savedReportMatchFailuresVar: savedReportMatchFailuresVar,
+          resultVar:                   resultVar
+        }
+      );
+    },
+
+    semantic_and: function(node, resultVar) {
+      return formatCode(
+        "var ${resultVar} = (function() {${actionCode}})() ? '' : null;",
+        {
+          actionCode:  node.code,
+          resultVar:   resultVar
+        }
+      );
+    },
+
+    semantic_not: function(node, resultVar) {
+      return formatCode(
+        "var ${resultVar} = (function() {${actionCode}})() ? null : '';",
+        {
+          actionCode:  node.code,
+          resultVar:   resultVar
+        }
+      );
+    },
+
+    optional: function(node, resultVar) {
+      var expressionResultVar = UID.next("result");
+
+      return formatCode(
+        "${expressionCode}",
+        "var ${resultVar} = ${expressionResultVar} !== null ? ${expressionResultVar} : '';",
+        {
+          expressionCode:      emit(node.expression, expressionResultVar),
+          expressionResultVar: expressionResultVar,
+          resultVar:           resultVar
+        }
+      );
+    },
+
+    zero_or_more: function(node, resultVar) {
+      var expressionResultVar = UID.next("result");
+
+      return formatCode(
+        "var ${resultVar} = [];",
+        "${expressionCode}",
+        "while (${expressionResultVar} !== null) {",
+        "  ${resultVar}.push(${expressionResultVar});",
+        "  ${expressionCode}",
+        "}",
+        {
+          expressionCode:      emit(node.expression, expressionResultVar),
+          expressionResultVar: expressionResultVar,
+          resultVar:           resultVar
+        }
+      );
+    },
+
+    one_or_more: function(node, resultVar) {
+      var expressionResultVar = UID.next("result");
+
+      return formatCode(
+        "${expressionCode}",
+        "if (${expressionResultVar} !== null) {",
+        "  var ${resultVar} = [];",
+        "  while (${expressionResultVar} !== null) {",
+        "    ${resultVar}.push(${expressionResultVar});",
+        "    ${expressionCode}",
+        "  }",
+        "} else {",
+        "  var ${resultVar} = null;",
+        "}",
+        {
+          expressionCode:      emit(node.expression, expressionResultVar),
+          expressionResultVar: expressionResultVar,
+          resultVar:           resultVar
+        }
+      );
+    },
+
+    action: function(node, resultVar) {
+      /*
+       * In case of sequences, we splat their elements into function arguments
+       * one by one. Example:
+       *
+       *   start: a:"a" b:"b" c:"c" { alert(arguments.length) }  // => 3
+       *
+       * This behavior is reflected in this function.
+       */
+
+      var expressionResultVar = UID.next("result");
+      var actionResultVar     = UID.next("result");
+      var savedPosVar         = UID.next("savedPos");
+
+      if (node.expression.type === "sequence") {
+        var formalParams = [];
+        var actualParams = [];
+
+        var elements = node.expression.elements;
+        var elementsLength = elements.length;
+        for (var i = 0; i < elementsLength; i++) {
+          if (elements[i].type === "labeled") {
+            formalParams.push(elements[i].label);
+            actualParams.push(expressionResultVar + "[" + i + "]");
+          }
+        }
+      } else if (node.expression.type === "labeled") {
+        var formalParams = [node.expression.label];
+        var actualParams = [expressionResultVar];
+      } else {
+        var formalParams = [];
+        var actualParams = [];
+      }
+
+      return formatCode(
+        "var ${savedPosVar} = pos;",
+        "${expressionCode}",
+        "var ${actionResultVar} = ${expressionResultVar} !== null",
+        "  ? (function(${formalParams}) {${actionCode}})(${actualParams})",
+        "  : null;",
+        "if (${actionResultVar} !== null) {",
+        "  var ${resultVar} = ${actionResultVar};",
+        "} else {",
+        "  var ${resultVar} = null;",
+        "  pos = ${savedPosVar};",
+        "}",
+        {
+          expressionCode:      emit(node.expression, expressionResultVar),
+          expressionResultVar: expressionResultVar,
+          actionCode:          node.code,
+          actionResultVar:     actionResultVar,
+          formalParams:        formalParams.join(", "),
+          actualParams:        actualParams.join(", "),
+          savedPosVar:         savedPosVar,
+          resultVar:           resultVar
+        }
+      );
+    },
+
+    rule_ref: function(node, resultVar) {
+      return formatCode(
+        "var ${resultVar} = ${ruleMethod}();",
+        {
+          ruleMethod: "parse_" + node.name,
+          resultVar:  resultVar
+        }
+      );
+    },
+
+    literal: function(node, resultVar) {
+      return formatCode(
+        "if (input.substr(pos, ${length}) === ${value|string}) {",
+        "  var ${resultVar} = ${value|string};",
+        "  pos += ${length};",
+        "} else {",
+        "  var ${resultVar} = null;",
+        "  if (reportMatchFailures) {",
+        "    matchFailed(${valueQuoted|string});",
+        "  }",
+        "}",
+        {
+          value:       node.value,
+          valueQuoted: quote(node.value),
+          length:      node.value.length,
+          resultVar:   resultVar
+        }
+      );
+    },
+
+    any: function(node, resultVar) {
+      return formatCode(
+        "if (input.length > pos) {",
+        "  var ${resultVar} = input.charAt(pos);",
+        "  pos++;",
+        "} else {",
+        "  var ${resultVar} = null;",
+        "  if (reportMatchFailures) {",
+        "    matchFailed('any character');",
+        "  }",
+        "}",
+        { resultVar: resultVar }
+      );
+    },
+
+    "class": function(node, resultVar) {
+      if (node.parts.length > 0) {
+        var regexp = "/^["
+          + (node.inverted ? "^" : "")
+          + map(node.parts, function(part) {
+              return part instanceof Array
+                ? quoteForRegexpClass(part[0])
+                  + "-"
+                  + quoteForRegexpClass(part[1])
+                : quoteForRegexpClass(part);
+            }).join("")
+          + "]/";
+      } else {
+        /*
+         * Stupid IE considers regexps /[]/ and /[^]/ syntactically invalid, so
+         * we translate them into euqivalents it can handle.
+         */
+        var regexp = node.inverted ? "/^[\\S\\s]/" : "/^(?!)/";
+      }
+
+      return formatCode(
+        "if (input.substr(pos).match(${regexp}) !== null) {",
+        "  var ${resultVar} = input.charAt(pos);",
+        "  pos++;",
+        "} else {",
+        "  var ${resultVar} = null;",
+        "  if (reportMatchFailures) {",
+        "    matchFailed(${rawText|string});",
+        "  }",
+        "}",
+        {
+          regexp:    regexp,
+          rawText:   node.rawText,
+          resultVar: resultVar
+        }
+      );
+    }
+  });
+
+  return emit(ast);
+};
+
+if (typeof module === "object") {
+  module.exports = PEG;
+} else if (typeof window === "object") {
+  window.PEG = PEG;
+} else {
+  throw new Error("Can't export PEG library (no \"module\" nor \"window\" object detected).");
+}
+
+})();
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/testparse.html	Sun Mar 18 12:03:04 2012 -0700
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+<head>
+	<title>Parser Test</title>
+	<script src="peg.js"></script>
+	<script src="mquery.js"></script>
+	<script src="parser.js"></script>
+</head>
+<body>
+	<textarea></textarea>
+	<input type="button" value="Parse!">
+	<div>
+	</div>
+</body>
+</html>