view peg.js @ 251:2557ce4e671f

Fix a couple of compiler bugs. topenv was getting initialized in multiple places. This resulted in multiple copies of modules getting created which caused problems for macro expansion. Additionally, arguments were not being marked as declared during code generation so assigning to an argument that was not closed over generated invalid C code.
author Michael Pavone <pavone@retrodev.com>
date Fri, 11 Apr 2014 22:29:32 -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).");
}

})();