Skip to content
markpaper

src/numbers/lossless.ts

v0.1.0 · 1.9 KB

Download file
const JSON_NUMBER_RE = /^-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?$/;
const QUOTE = 0x22;
const BACKSLASH = 0x5c;
const MINUS = 0x2d;
const isDigit = (c: number): boolean => c >= 0x30 && c <= 0x39;
const isNumberChar = (c: number): boolean =>
  isDigit(c) || c === MINUS || c === 0x2b || c === 0x2e || c === 0x65 || c === 0x45;
export function parseLossless(text: string): unknown {
  if (typeof text !== 'string') throw new TypeError('Lossless JSON input must be a string');
  const out: string[] = [];
  const stack: number[] = [];
  let expectKey = false;
  let from = 0;
  let i = 0;
  const n = text.length;
  while (i < n) {
    const c = text.charCodeAt(i);
    if (c === QUOTE) {
      i++;
      while (i < n) {
        const d = text.charCodeAt(i);
        if (d === BACKSLASH) i += 2;
        else if (d === QUOTE) {
          i++;
          break;
        } else i++;
      }
      expectKey = false;
      continue;
    }
    if (c === MINUS || isDigit(c)) {
      let j = i + 1;
      while (j < n && isNumberChar(text.charCodeAt(j))) j++;
      const lit = text.slice(i, j);
      if (!JSON_NUMBER_RE.test(lit)) throw new SyntaxError(`Invalid JSON number ${lit.slice(0, 40)} at offset ${i}`);
      if (expectKey) throw new SyntaxError(`JSON object key must be quoted; found ${lit.slice(0, 40)} at offset ${i}`);
      out.push(text.slice(from, i), '"', lit, '"');
      from = j;
      i = j;
      continue;
    }
    switch (c) {
      case 0x7b:
        stack.push(c);
        expectKey = true;
        break;
      case 0x5b:
        stack.push(c);
        expectKey = false;
        break;
      case 0x7d:
      case 0x5d:
        stack.pop();
        expectKey = false;
        break;
      case 0x2c:
        expectKey = stack[stack.length - 1] === 0x7b;
        break;
      case 0x3a:
        expectKey = false;
        break;
      default:
        break;
    }
    i++;
  }
  out.push(text.slice(from));
  return JSON.parse(out.join(''));
}
All files