text: // json5.js
  // Modern JSON. See README.md for details.
  //
  // This file is based directly off of Douglas Crockford's json_parse.js:
  // https://github.com/douglascrockford/JSON-js/blob/master/json_parse.js
  
  var JSON5 = (typeof exports === 'object' ? exports : {});
  
  JSON5.parse = (function () {
      "use strict";
  
  // This is a function that can parse a JSON5 text, producing a JavaScript
  // data structure. It is a simple, recursive descent parser. It does not use
  // eval or regular expressions, so it can be used as a model for implementing
  // a JSON5 parser in other languages.
  
  // We are defining the function inside of another function to avoid creating
  // global variables.
  
      var at,     // The index of the current character
          ch,     // The current character
          escapee = {
              "'":  "'",
              '"':  '"',
              '\\': '\\',
              '/':  '/',
              '\n': '',       // Replace escaped newlines in strings w/ empty string
              b:    '\b',
              f:    '\f',
              n:    '\n',
              r:    '\r',
              t:    '\t'
          },
          text,
  
          error = function (m) {
  
  // Call error when something is wrong.
  
              var error = new SyntaxError();
              error.message = m;
              error.at = at;
              error.text = text;
              throw error;
          },
  
          next = function (c) {
  
  // If a c parameter is provided, verify that it matches the current character.
  
              if (c && c !== ch) {
                  error("Expected '" + c + "' instead of '" + ch + "'");
              }
  
  // Get the next character. When there are no more characters,
  // return the empty string.
  
              ch = text.charAt(at);
              at += 1;
              return ch;
          },
  
          identifier = function () {
  
  // Parse an identifier. Normally, reserved words are disallowed here, but we
  // only use this for unquoted object keys, where reserved words are allowed,
  // so we don't check for those here. References:
  // - http://es5.github.com/#x7.6
  // - https://developer.mozilla.org/en/Core_JavaScript_1.5_Guide/Core_Language_Features#Variables
  // - http://docstore.mik.ua/orelly/webprog/jscript/ch02_07.htm
  // TODO Identifiers can have Unicode "letters" in them; add support for those.
  
              var key = ch;
  
              // Identifiers must start with a letter, _ or $.
              if ((ch !== '_' && ch !== '$') &&
                      (ch < 'a' || ch > 'z') &&
                      (ch < 'A' || ch > 'Z')) {
                  error("Bad identifier");
              }
  
              // Subsequent characters can contain digits.
              while (next() && (
                      ch === '_' || ch === '$' ||
                      (ch >= 'a' && ch <= 'z') ||
                      (ch >= 'A' && ch <= 'Z') ||
                      (ch >= '0' && ch <= '9'))) {
                  key += ch;
              }
  
              return key;
          },
  
          number = function () {
  
  // Parse a number value.
  
              var number,
                  sign = '',
                  string = '',
                  base = 10;
  
              if (ch === '-' || ch === '+') {
                  sign = string = ch;
                  next(ch);
              }
  
              // support for Infinity (could tweak to allow other words):
              if (ch === 'I') {
                  number = word();
                  if (typeof number !== 'number' || isNaN(number)) {
                      error('Unexpected word for number');
                  }
                  return (sign === '-') ? -number : number;
              }
  
              if (ch === '0') {
                  string += ch;
                  next();
                  if (ch === 'x' || ch === 'X') {
                      string += ch;
                      next();
                      base = 16;
                  } else if (ch >= '0' && ch <= '9') {
                      error('Octal literal');
                  }
              }
  
              // https://github.com/aseemk/json5/issues/36
              if (base === 16 && sign) {
                  error('Signed hexadecimal literal');
              }
  
              switch (base) {
              case 10:
                  while (ch >= '0' && ch <= '9' ) {
                      string += ch;
                      next();
                  }
                  if (ch === '.') {
                      string += '.';
                      while (next() && ch >= '0' && ch <= '9') {
                          string += ch;
                      }
                  }
                  if (ch === 'e' || ch === 'E') {
                      string += ch;
                      next();
                      if (ch === '-' || ch === '+') {
                          string += ch;
                          next();
                      }
                      while (ch >= '0' && ch <= '9') {
                          string += ch;
                          next();
                      }
                  }
                  break;
              case 16:
                  while (ch >= '0' && ch <= '9' || ch >= 'A' && ch <= 'F' || ch >= 'a' && ch <= 'f') {
                      string += ch;
                      next();
                  }
                  break;
              }
              number = +string;
              if (!isFinite(number)) {
                  error("Bad number");
              } else {
                  return number;
              }
          },
  
          string = function () {
  
  // Parse a string value.
  
              var hex,
                  i,
                  string = '',
                  delim,      // double quote or single quote
                  uffff;
  
  // When parsing for string values, we must look for ' or " and \ characters.
  
              if (ch === '"' || ch === "'") {
                  delim = ch;
                  while (next()) {
                      if (ch === delim) {
                          next();
                          return string;
                      } else if (ch === '\\') {
                          next();
                          if (ch === 'u') {
                              uffff = 0;
                              for (i = 0; i < 4; i += 1) {
                                  hex = parseInt(next(), 16);
                                  if (!isFinite(hex)) {
                                      break;
                                  }
                                  uffff = uffff * 16 + hex;
                              }
                              string += String.fromCharCode(uffff);
                          } else if (typeof escapee[ch] === 'string') {
                              string += escapee[ch];
                          } else {
                              break;
                          }
                      } else if (ch === '\n') {
                          // unescaped newlines are invalid; see:
                          // https://github.com/aseemk/json5/issues/24
                          // TODO this feels special-cased; are there other
                          // invalid unescaped chars?
                          break;
                      } else {
                          string += ch;
                      }
                  }
              }
              error("Bad string");
          },
  
          inlineComment = function () {
  
  // Skip an inline comment, assuming this is one. The current character should
  // be the second / character in the // pair that begins this inline comment.
  // To finish the inline comment, we look for a newline or the end of the text.
  
              if (ch !== '/') {
                  error("Not an inline comment");
              }
  
              do {
                  next();
                  if (ch === '\n') {
                      next('\n');
                      return;
                  }
              } while (ch);
          },
  
          blockComment = function () {
  
  // Skip a block comment, assuming this is one. The current character should be
  // the * character in the /* pair that begins this block comment.
  // To finish the block comment, we look for an ending */ pair of characters,
  // but we also watch for the end of text before the comment is terminated.
  
              if (ch !== '*') {
                  error("Not a block comment");
              }
  
              do {
                  next();
                  while (ch === '*') {
                      next('*');
                      if (ch === '/') {
                          next('/');
                          return;
                      }
                  }
              } while (ch);
  
              error("Unterminated block comment");
          },
  
          comment = function () {
  
  // Skip a comment, whether inline or block-level, assuming this is one.
  // Comments always begin with a / character.
  
              if (ch !== '/') {
                  error("Not a comment");
              }
  
              next('/');
  
              if (ch === '/') {
                  inlineComment();
              } else if (ch === '*') {
                  blockComment();
              } else {
                  error("Unrecognized comment");
              }
          },
  
          white = function () {
  
  // Skip whitespace and comments.
  // Note that we're detecting comments by only a single / character.
  // This works since regular expressions are not valid JSON(5), but this will
  // break if there are other valid values that begin with a / character!
  
              while (ch) {
                  if (ch === '/') {
                      comment();
                  } else if (ch <= ' ') {
                      next();
                  } else {
                      return;
                  }
              }
          },
  
          word = function () {
  
  // true, false, or null.
  
              switch (ch) {
              case 't':
                  next('t');
                  next('r');
                  next('u');
                  next('e');
                  return true;
              case 'f':
                  next('f');
                  next('a');
                  next('l');
                  next('s');
                  next('e');
                  return false;
              case 'n':
                  next('n');
                  next('u');
                  next('l');
                  next('l');
                  return null;
              case 'I':
                  next('I');
                  next('n');
                  next('f');
                  next('i');
                  next('n');
                  next('i');
                  next('t');
                  next('y');
                  return Infinity;
              }
              error("Unexpected '" + ch + "'");
          },
  
          value,  // Place holder for the value function.
  
          array = function () {
  
  // Parse an array value.
  
              var array = [];
  
              if (ch === '[') {
                  next('[');
                  white();
                  while (ch) {
                      if (ch === ']') {
                          next(']');
                          return array;   // Potentially empty array
                      }
                      // ES5 allows omitting elements in arrays, e.g. [,] and
                      // [,null]. We don't allow this in JSON5.
                      if (ch === ',') {
                          error("Missing array element");
                      } else {
                          array.push(value());
                      }
                      white();
                      // If there's no comma after this value, this needs to
                      // be the end of the array.
                      if (ch !== ',') {
                          next(']');
                          return array;
                      }
                      next(',');
                      white();
                  }
              }
              error("Bad array");
          },
  
          object = function () {
  
  // Parse an object value.
  
              var key,
                  object = {};
  
              if (ch === '{') {
                  next('{');
                  white();
                  while (ch) {
                      if (ch === '}') {
                          next('}');
                          return object;   // Potentially empty object
                      }
  
                      // Keys can be unquoted. If they are, they need to be
                      // valid JS identifiers.
                      if (ch === '"' || ch === "'") {
                          key = string();
                      } else {
                          key = identifier();
                      }
  
                      white();
                      next(':');
                      if (Object.hasOwnProperty.call(object, key)) {
                          error('Duplicate key "' + key + '"');
                      }
                      object[key] = value();
                      white();
                      // If there's no comma after this pair, this needs to be
                      // the end of the object.
                      if (ch !== ',') {
                          next('}');
                          return object;
                      }
                      next(',');
                      white();
                  }
              }
              error("Bad object");
          };
  
      value = function () {
  
  // Parse a JSON value. It could be an object, an array, a string, a number,
  // or a word.
  
          white();
          switch (ch) {
          case '{':
              return object();
          case '[':
              return array();
          case '"':
          case "'":
              return string();
          case '-':
          case '+':
          case '.':
              return number();
          default:
              return ch >= '0' && ch <= '9' ? number() : word();
          }
      };
  
  // Return the json_parse function. It will have access to all of the above
  // functions and variables.
  
      return function (source, reviver) {
          var result;
  
          text = source;
          at = 0;
          ch = ' ';
          result = value();
          white();
          if (ch) {
              error("Syntax error");
          }
  
  // If there is a reviver function, we recursively walk the new structure,
  // passing each name/value pair to the reviver function for possible
  // transformation, starting with a temporary root object that holds the result
  // in an empty key. If there is not a reviver function, we simply return the
  // result.
  
          return typeof reviver === 'function' ? (function walk(holder, key) {
              var k, v, value = holder[key];
              if (value && typeof value === 'object') {
                  for (k in value) {
                      if (Object.prototype.hasOwnProperty.call(value, k)) {
                          v = walk(value, k);
                          if (v !== undefined) {
                              value[k] = v;
                          } else {
                              delete value[k];
                          }
                      }
                  }
              }
              return reviver.call(holder, key, value);
          }({'': result}, '')) : result;
      };
  }());
  
  JSON5.stringify = function (obj, replacer, space) {
      // Since regular JSON is a strict subset of JSON5, we'll always output
      // regular JSON to foster better interoperability. TODO Should we not?
      return JSON.stringify.apply(JSON, arguments);
  };
  