UNPKG

@selaliadobor/incomplete-json-parser

Version:

A JSON parser that can parse incomplete JSON strings.

106 lines (105 loc) 3.37 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.LiteralScope = void 0; const scope_interface_1 = require("./scope.interface"); const NUMBER_REGEX = /^-?\d+(\.\d*)?$/; const PARTIAL_UNICODE_REGEX = /\\u[\da-fA-F]{0,3}$/; const NULL_STR = "null"; const TRUE_STR = "true"; const FALSE_STR = "false"; class LiteralScope extends scope_interface_1.Scope { content = ""; write(letter) { if (this.finish) { throw new Error("Literal already finished"); } this.content += letter; const assume = this.getOrAssume(); if (assume === undefined) { this.content = this.content.slice(0, -1); return false; } this.checkCompletion(); return true; } checkCompletion() { const c = this.content; const len = c.length; if (len === 0) return; const firstChar = c[0]; switch (firstChar) { case '"': if (len >= 2 && c[len - 1] === '"' && !this.endsWithEscapedQuote()) { this.finish = true; } break; case 't': if (c === TRUE_STR) { this.finish = true; } break; case 'f': if (c === FALSE_STR) { this.finish = true; } break; case 'n': if (c === NULL_STR) { this.finish = true; } break; } } getOrAssume() { const c = this.content; const len = c.length; if (len === 0) { return null; } const firstChar = c[0]; switch (firstChar) { case 'n': return len <= NULL_STR.length && NULL_STR.startsWith(c) ? null : undefined; case 't': return len <= TRUE_STR.length && TRUE_STR.startsWith(c) ? true : undefined; case 'f': return len <= FALSE_STR.length && FALSE_STR.startsWith(c) ? false : undefined; case '"': return this.parseStringLike(); case '-': if (len === 1) return 0; default: return NUMBER_REGEX.test(c) ? parseFloat(c) : undefined; } } parseStringLike() { const c = this.content; let jsonedString = c; const len = c.length; const isCompleted = len >= 2 && c[len - 1] === '"' && !this.endsWithEscapedQuote(); if (!isCompleted) { const unicodeMatch = PARTIAL_UNICODE_REGEX.exec(jsonedString); if (unicodeMatch) { jsonedString = jsonedString.slice(0, unicodeMatch.index); } if (jsonedString.endsWith("\\") && !jsonedString.endsWith("\\\\")) { jsonedString = jsonedString.slice(0, -1); } jsonedString += '"'; } try { return JSON.parse(jsonedString); } catch { return undefined; } } endsWithEscapedQuote() { const c = this.content; const len = c.length; return len >= 2 && c[len - 1] === '"' && c[len - 2] === '\\'; } } exports.LiteralScope = LiteralScope;