view peg.js @ 366:810b6115c1d4

Add a pop method to array
author Michael Pavone <pavone@retrodev.com>
date Sat, 08 Aug 2015 21:13:26 -0700
parents 3d1b8e96f5dc
children
line wrap: on
line source

/* 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).");
}

})();