parser.js 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. exports.MalformedJSON = exports.PartialJSON = exports.partialParse = void 0;
  4. const STR = 0b000000001;
  5. const NUM = 0b000000010;
  6. const ARR = 0b000000100;
  7. const OBJ = 0b000001000;
  8. const NULL = 0b000010000;
  9. const BOOL = 0b000100000;
  10. const NAN = 0b001000000;
  11. const INFINITY = 0b010000000;
  12. const MINUS_INFINITY = 0b100000000;
  13. const INF = INFINITY | MINUS_INFINITY;
  14. const SPECIAL = NULL | BOOL | INF | NAN;
  15. const ATOM = STR | NUM | SPECIAL;
  16. const COLLECTION = ARR | OBJ;
  17. const ALL = ATOM | COLLECTION;
  18. const Allow = {
  19. STR,
  20. NUM,
  21. ARR,
  22. OBJ,
  23. NULL,
  24. BOOL,
  25. NAN,
  26. INFINITY,
  27. MINUS_INFINITY,
  28. INF,
  29. SPECIAL,
  30. ATOM,
  31. COLLECTION,
  32. ALL,
  33. };
  34. // The JSON string segment was unable to be parsed completely
  35. class PartialJSON extends Error {
  36. }
  37. exports.PartialJSON = PartialJSON;
  38. class MalformedJSON extends Error {
  39. }
  40. exports.MalformedJSON = MalformedJSON;
  41. /**
  42. * Parse incomplete JSON
  43. * @param {string} jsonString Partial JSON to be parsed
  44. * @param {number} allowPartial Specify what types are allowed to be partial, see {@link Allow} for details
  45. * @returns The parsed JSON
  46. * @throws {PartialJSON} If the JSON is incomplete (related to the `allow` parameter)
  47. * @throws {MalformedJSON} If the JSON is malformed
  48. */
  49. function parseJSON(jsonString, allowPartial = Allow.ALL) {
  50. if (typeof jsonString !== 'string') {
  51. throw new TypeError(`expecting str, got ${typeof jsonString}`);
  52. }
  53. if (!jsonString.trim()) {
  54. throw new Error(`${jsonString} is empty`);
  55. }
  56. return _parseJSON(jsonString.trim(), allowPartial);
  57. }
  58. const _parseJSON = (jsonString, allow) => {
  59. const length = jsonString.length;
  60. let index = 0;
  61. const markPartialJSON = (msg) => {
  62. throw new PartialJSON(`${msg} at position ${index}`);
  63. };
  64. const throwMalformedError = (msg) => {
  65. throw new MalformedJSON(`${msg} at position ${index}`);
  66. };
  67. const parseAny = () => {
  68. skipBlank();
  69. if (index >= length)
  70. markPartialJSON('Unexpected end of input');
  71. if (jsonString[index] === '"')
  72. return parseStr();
  73. if (jsonString[index] === '{')
  74. return parseObj();
  75. if (jsonString[index] === '[')
  76. return parseArr();
  77. if (jsonString.substring(index, index + 4) === 'null' ||
  78. (Allow.NULL & allow && length - index < 4 && 'null'.startsWith(jsonString.substring(index)))) {
  79. index += 4;
  80. return null;
  81. }
  82. if (jsonString.substring(index, index + 4) === 'true' ||
  83. (Allow.BOOL & allow && length - index < 4 && 'true'.startsWith(jsonString.substring(index)))) {
  84. index += 4;
  85. return true;
  86. }
  87. if (jsonString.substring(index, index + 5) === 'false' ||
  88. (Allow.BOOL & allow && length - index < 5 && 'false'.startsWith(jsonString.substring(index)))) {
  89. index += 5;
  90. return false;
  91. }
  92. if (jsonString.substring(index, index + 8) === 'Infinity' ||
  93. (Allow.INFINITY & allow && length - index < 8 && 'Infinity'.startsWith(jsonString.substring(index)))) {
  94. index += 8;
  95. return Infinity;
  96. }
  97. if (jsonString.substring(index, index + 9) === '-Infinity' ||
  98. (Allow.MINUS_INFINITY & allow &&
  99. 1 < length - index &&
  100. length - index < 9 &&
  101. '-Infinity'.startsWith(jsonString.substring(index)))) {
  102. index += 9;
  103. return -Infinity;
  104. }
  105. if (jsonString.substring(index, index + 3) === 'NaN' ||
  106. (Allow.NAN & allow && length - index < 3 && 'NaN'.startsWith(jsonString.substring(index)))) {
  107. index += 3;
  108. return NaN;
  109. }
  110. return parseNum();
  111. };
  112. const parseStr = () => {
  113. const start = index;
  114. let escape = false;
  115. index++; // skip initial quote
  116. while (index < length && (jsonString[index] !== '"' || (escape && jsonString[index - 1] === '\\'))) {
  117. escape = jsonString[index] === '\\' ? !escape : false;
  118. index++;
  119. }
  120. if (jsonString.charAt(index) == '"') {
  121. try {
  122. return JSON.parse(jsonString.substring(start, ++index - Number(escape)));
  123. }
  124. catch (e) {
  125. throwMalformedError(String(e));
  126. }
  127. }
  128. else if (Allow.STR & allow) {
  129. try {
  130. return JSON.parse(jsonString.substring(start, index - Number(escape)) + '"');
  131. }
  132. catch (e) {
  133. // SyntaxError: Invalid escape sequence
  134. return JSON.parse(jsonString.substring(start, jsonString.lastIndexOf('\\')) + '"');
  135. }
  136. }
  137. markPartialJSON('Unterminated string literal');
  138. };
  139. const parseObj = () => {
  140. index++; // skip initial brace
  141. skipBlank();
  142. const obj = {};
  143. try {
  144. while (jsonString[index] !== '}') {
  145. skipBlank();
  146. if (index >= length && Allow.OBJ & allow)
  147. return obj;
  148. const key = parseStr();
  149. skipBlank();
  150. index++; // skip colon
  151. try {
  152. const value = parseAny();
  153. Object.defineProperty(obj, key, { value, writable: true, enumerable: true, configurable: true });
  154. }
  155. catch (e) {
  156. if (Allow.OBJ & allow)
  157. return obj;
  158. else
  159. throw e;
  160. }
  161. skipBlank();
  162. if (jsonString[index] === ',')
  163. index++; // skip comma
  164. }
  165. }
  166. catch (e) {
  167. if (Allow.OBJ & allow)
  168. return obj;
  169. else
  170. markPartialJSON("Expected '}' at end of object");
  171. }
  172. index++; // skip final brace
  173. return obj;
  174. };
  175. const parseArr = () => {
  176. index++; // skip initial bracket
  177. const arr = [];
  178. try {
  179. while (jsonString[index] !== ']') {
  180. arr.push(parseAny());
  181. skipBlank();
  182. if (jsonString[index] === ',') {
  183. index++; // skip comma
  184. }
  185. }
  186. }
  187. catch (e) {
  188. if (Allow.ARR & allow) {
  189. return arr;
  190. }
  191. markPartialJSON("Expected ']' at end of array");
  192. }
  193. index++; // skip final bracket
  194. return arr;
  195. };
  196. const parseNum = () => {
  197. if (index === 0) {
  198. if (jsonString === '-' && Allow.NUM & allow)
  199. markPartialJSON("Not sure what '-' is");
  200. try {
  201. return JSON.parse(jsonString);
  202. }
  203. catch (e) {
  204. if (Allow.NUM & allow) {
  205. try {
  206. if ('.' === jsonString[jsonString.length - 1])
  207. return JSON.parse(jsonString.substring(0, jsonString.lastIndexOf('.')));
  208. return JSON.parse(jsonString.substring(0, jsonString.lastIndexOf('e')));
  209. }
  210. catch (e) { }
  211. }
  212. throwMalformedError(String(e));
  213. }
  214. }
  215. const start = index;
  216. if (jsonString[index] === '-')
  217. index++;
  218. while (jsonString[index] && !',]}'.includes(jsonString[index]))
  219. index++;
  220. if (index == length && !(Allow.NUM & allow))
  221. markPartialJSON('Unterminated number literal');
  222. try {
  223. return JSON.parse(jsonString.substring(start, index));
  224. }
  225. catch (e) {
  226. if (jsonString.substring(start, index) === '-' && Allow.NUM & allow)
  227. markPartialJSON("Not sure what '-' is");
  228. try {
  229. return JSON.parse(jsonString.substring(start, jsonString.lastIndexOf('e')));
  230. }
  231. catch (e) {
  232. throwMalformedError(String(e));
  233. }
  234. }
  235. };
  236. const skipBlank = () => {
  237. while (index < length && ' \n\r\t'.includes(jsonString[index])) {
  238. index++;
  239. }
  240. };
  241. return parseAny();
  242. };
  243. // using this function with malformed JSON is undefined behavior
  244. const partialParse = (input) => parseJSON(input, Allow.ALL ^ Allow.NUM);
  245. exports.partialParse = partialParse;
  246. //# sourceMappingURL=parser.js.map