sourav-das/stem-separator
3
1const util = require('./util')2 3let source4let parseState5let stack6let pos7let line8let column9let token10let key11let root12 13module.exports = function parse (text, reviver) {14 source = String(text)15 parseState = 'start'16 stack = []17 pos = 018 line = 119 column = 020 token = undefined21 key = undefined22 root = undefined23 24 do {25 token = lex()26 27 // This code is unreachable.28 // if (!parseStates[parseState]) {29 // throw invalidParseState()30 // }31 32 parseStates[parseState]()33 } while (token.type !== 'eof')34 35 if (typeof reviver === 'function') {36 return internalize({'': root}, '', reviver)37 }38 39 return root40}41 42function internalize (holder, name, reviver) {43 const value = holder[name]44 if (value != null && typeof value === 'object') {45 if (Array.isArray(value)) {46 for (let i = 0; i < value.length; i++) {47 const key = String(i)48 const replacement = internalize(value, key, reviver)49 if (replacement === undefined) {50 delete value[key]51 } else {52 Object.defineProperty(value, key, {53 value: replacement,54 writable: true,55 enumerable: true,56 configurable: true,57 })58 }59 }60 } else {61 for (const key in value) {62 const replacement = internalize(value, key, reviver)63 if (replacement === undefined) {64 delete value[key]65 } else {66 Object.defineProperty(value, key, {67 value: replacement,68 writable: true,69 enumerable: true,70 configurable: true,71 })72 }73 }74 }75 }76 77 return reviver.call(holder, name, value)78}79 80let lexState81let buffer82let doubleQuote83let sign84let c85 86function lex () {87 lexState = 'default'88 buffer = ''89 doubleQuote = false90 sign = 191 92 for (;;) {93 c = peek()94 95 // This code is unreachable.96 // if (!lexStates[lexState]) {97 // throw invalidLexState(lexState)98 // }99 100 const token = lexStates[lexState]()101 if (token) {102 return token103 }104 }105}106 107function peek () {108 if (source[pos]) {109 return String.fromCodePoint(source.codePointAt(pos))110 }111}112 113function read () {114 const c = peek()115 116 if (c === '\n') {117 line++118 column = 0119 } else if (c) {120 column += c.length121 } else {122 column++123 }124 125 if (c) {126 pos += c.length127 }128 129 return c130}131 132const lexStates = {133 default () {134 switch (c) {135 case '\t':136 case '\v':137 case '\f':138 case ' ':139 case '\u00A0':140 case '\uFEFF':141 case '\n':142 case '\r':143 case '\u2028':144 case '\u2029':145 read()146 return147 148 case '/':149 read()150 lexState = 'comment'151 return152 153 case undefined:154 read()155 return newToken('eof')156 }157 158 if (util.isSpaceSeparator(c)) {159 read()160 return161 }162 163 // This code is unreachable.164 // if (!lexStates[parseState]) {165 // throw invalidLexState(parseState)166 // }167 168 return lexStates[parseState]()169 },170 171 comment () {172 switch (c) {173 case '*':174 read()175 lexState = 'multiLineComment'176 return177 178 case '/':179 read()180 lexState = 'singleLineComment'181 return182 }183 184 throw invalidChar(read())185 },186 187 multiLineComment () {188 switch (c) {189 case '*':190 read()191 lexState = 'multiLineCommentAsterisk'192 return193 194 case undefined:195 throw invalidChar(read())196 }197 198 read()199 },200 201 multiLineCommentAsterisk () {202 switch (c) {203 case '*':204 read()205 return206 207 case '/':208 read()209 lexState = 'default'210 return211 212 case undefined:213 throw invalidChar(read())214 }215 216 read()217 lexState = 'multiLineComment'218 },219 220 singleLineComment () {221 switch (c) {222 case '\n':223 case '\r':224 case '\u2028':225 case '\u2029':226 read()227 lexState = 'default'228 return229 230 case undefined:231 read()232 return newToken('eof')233 }234 235 read()236 },237 238 value () {239 switch (c) {240 case '{':241 case '[':242 return newToken('punctuator', read())243 244 case 'n':245 read()246 literal('ull')247 return newToken('null', null)248 249 case 't':250 read()251 literal('rue')252 return newToken('boolean', true)253 254 case 'f':255 read()256 literal('alse')257 return newToken('boolean', false)258 259 case '-':260 case '+':261 if (read() === '-') {262 sign = -1263 }264 265 lexState = 'sign'266 return267 268 case '.':269 buffer = read()270 lexState = 'decimalPointLeading'271 return272 273 case '0':274 buffer = read()275 lexState = 'zero'276 return277 278 case '1':279 case '2':280 case '3':281 case '4':282 case '5':283 case '6':284 case '7':285 case '8':286 case '9':287 buffer = read()288 lexState = 'decimalInteger'289 return290 291 case 'I':292 read()293 literal('nfinity')294 return newToken('numeric', Infinity)295 296 case 'N':297 read()298 literal('aN')299 return newToken('numeric', NaN)300 301 case '"':302 case "'":303 doubleQuote = (read() === '"')304 buffer = ''305 lexState = 'string'306 return307 }308 309 throw invalidChar(read())310 },311 312 identifierNameStartEscape () {313 if (c !== 'u') {314 throw invalidChar(read())315 }316 317 read()318 const u = unicodeEscape()319 switch (u) {320 case '$':321 case '_':322 break323 324 default:325 if (!util.isIdStartChar(u)) {326 throw invalidIdentifier()327 }328 329 break330 }331 332 buffer += u333 lexState = 'identifierName'334 },335 336 identifierName () {337 switch (c) {338 case '$':339 case '_':340 case '\u200C':341 case '\u200D':342 buffer += read()343 return344 345 case '\\':346 read()347 lexState = 'identifierNameEscape'348 return349 }350 351 if (util.isIdContinueChar(c)) {352 buffer += read()353 return354 }355 356 return newToken('identifier', buffer)357 },358 359 identifierNameEscape () {360 if (c !== 'u') {361 throw invalidChar(read())362 }363 364 read()365 const u = unicodeEscape()366 switch (u) {367 case '$':368 case '_':369 case '\u200C':370 case '\u200D':371 break372 373 default:374 if (!util.isIdContinueChar(u)) {375 throw invalidIdentifier()376 }377 378 break379 }380 381 buffer += u382 lexState = 'identifierName'383 },384 385 sign () {386 switch (c) {387 case '.':388 buffer = read()389 lexState = 'decimalPointLeading'390 return391 392 case '0':393 buffer = read()394 lexState = 'zero'395 return396 397 case '1':398 case '2':399 case '3':400 case '4':401 case '5':402 case '6':403 case '7':404 case '8':405 case '9':406 buffer = read()407 lexState = 'decimalInteger'408 return409 410 case 'I':411 read()412 literal('nfinity')413 return newToken('numeric', sign * Infinity)414 415 case 'N':416 read()417 literal('aN')418 return newToken('numeric', NaN)419 }420 421 throw invalidChar(read())422 },423 424 zero () {425 switch (c) {426 case '.':427 buffer += read()428 lexState = 'decimalPoint'429 return430 431 case 'e':432 case 'E':433 buffer += read()434 lexState = 'decimalExponent'435 return436 437 case 'x':438 case 'X':439 buffer += read()440 lexState = 'hexadecimal'441 return442 }443 444 return newToken('numeric', sign * 0)445 },446 447 decimalInteger () {448 switch (c) {449 case '.':450 buffer += read()451 lexState = 'decimalPoint'452 return453 454 case 'e':455 case 'E':456 buffer += read()457 lexState = 'decimalExponent'458 return459 }460 461 if (util.isDigit(c)) {462 buffer += read()463 return464 }465 466 return newToken('numeric', sign * Number(buffer))467 },468 469 decimalPointLeading () {470 if (util.isDigit(c)) {471 buffer += read()472 lexState = 'decimalFraction'473 return474 }475 476 throw invalidChar(read())477 },478 479 decimalPoint () {480 switch (c) {481 case 'e':482 case 'E':483 buffer += read()484 lexState = 'decimalExponent'485 return486 }487 488 if (util.isDigit(c)) {489 buffer += read()490 lexState = 'decimalFraction'491 return492 }493 494 return newToken('numeric', sign * Number(buffer))495 },496 497 decimalFraction () {498 switch (c) {499 case 'e':500 case 'E':501 buffer += read()502 lexState = 'decimalExponent'503 return504 }505 506 if (util.isDigit(c)) {507 buffer += read()508 return509 }510 511 return newToken('numeric', sign * Number(buffer))512 },513 514 decimalExponent () {515 switch (c) {516 case '+':517 case '-':518 buffer += read()519 lexState = 'decimalExponentSign'520 return521 }522 523 if (util.isDigit(c)) {524 buffer += read()525 lexState = 'decimalExponentInteger'526 return527 }528 529 throw invalidChar(read())530 },531 532 decimalExponentSign () {533 if (util.isDigit(c)) {534 buffer += read()535 lexState = 'decimalExponentInteger'536 return537 }538 539 throw invalidChar(read())540 },541 542 decimalExponentInteger () {543 if (util.isDigit(c)) {544 buffer += read()545 return546 }547 548 return newToken('numeric', sign * Number(buffer))549 },550 551 hexadecimal () {552 if (util.isHexDigit(c)) {553 buffer += read()554 lexState = 'hexadecimalInteger'555 return556 }557 558 throw invalidChar(read())559 },560 561 hexadecimalInteger () {562 if (util.isHexDigit(c)) {563 buffer += read()564 return565 }566 567 return newToken('numeric', sign * Number(buffer))568 },569 570 string () {571 switch (c) {572 case '\\':573 read()574 buffer += escape()575 return576 577 case '"':578 if (doubleQuote) {579 read()580 return newToken('string', buffer)581 }582 583 buffer += read()584 return585 586 case "'":587 if (!doubleQuote) {588 read()589 return newToken('string', buffer)590 }591 592 buffer += read()593 return594 595 case '\n':596 case '\r':597 throw invalidChar(read())598 599 case '\u2028':600 case '\u2029':601 separatorChar(c)602 break603 604 case undefined:605 throw invalidChar(read())606 }607 608 buffer += read()609 },610 611 start () {612 switch (c) {613 case '{':614 case '[':615 return newToken('punctuator', read())616 617 // This code is unreachable since the default lexState handles eof.618 // case undefined:619 // return newToken('eof')620 }621 622 lexState = 'value'623 },624 625 beforePropertyName () {626 switch (c) {627 case '$':628 case '_':629 buffer = read()630 lexState = 'identifierName'631 return632 633 case '\\':634 read()635 lexState = 'identifierNameStartEscape'636 return637 638 case '}':639 return newToken('punctuator', read())640 641 case '"':642 case "'":643 doubleQuote = (read() === '"')644 lexState = 'string'645 return646 }647 648 if (util.isIdStartChar(c)) {649 buffer += read()650 lexState = 'identifierName'651 return652 }653 654 throw invalidChar(read())655 },656 657 afterPropertyName () {658 if (c === ':') {659 return newToken('punctuator', read())660 }661 662 throw invalidChar(read())663 },664 665 beforePropertyValue () {666 lexState = 'value'667 },668 669 afterPropertyValue () {670 switch (c) {671 case ',':672 case '}':673 return newToken('punctuator', read())674 }675 676 throw invalidChar(read())677 },678 679 beforeArrayValue () {680 if (c === ']') {681 return newToken('punctuator', read())682 }683 684 lexState = 'value'685 },686 687 afterArrayValue () {688 switch (c) {689 case ',':690 case ']':691 return newToken('punctuator', read())692 }693 694 throw invalidChar(read())695 },696 697 end () {698 // This code is unreachable since it's handled by the default lexState.699 // if (c === undefined) {700 // read()701 // return newToken('eof')702 // }703 704 throw invalidChar(read())705 },706}707 708function newToken (type, value) {709 return {710 type,711 value,712 line,713 column,714 }715}716 717function literal (s) {718 for (const c of s) {719 const p = peek()720 721 if (p !== c) {722 throw invalidChar(read())723 }724 725 read()726 }727}728 729function escape () {730 const c = peek()731 switch (c) {732 case 'b':733 read()734 return '\b'735 736 case 'f':737 read()738 return '\f'739 740 case 'n':741 read()742 return '\n'743 744 case 'r':745 read()746 return '\r'747 748 case 't':749 read()750 return '\t'751 752 case 'v':753 read()754 return '\v'755 756 case '0':757 read()758 if (util.isDigit(peek())) {759 throw invalidChar(read())760 }761 762 return '\0'763 764 case 'x':765 read()766 return hexEscape()767 768 case 'u':769 read()770 return unicodeEscape()771 772 case '\n':773 case '\u2028':774 case '\u2029':775 read()776 return ''777 778 case '\r':779 read()780 if (peek() === '\n') {781 read()782 }783 784 return ''785 786 case '1':787 case '2':788 case '3':789 case '4':790 case '5':791 case '6':792 case '7':793 case '8':794 case '9':795 throw invalidChar(read())796 797 case undefined:798 throw invalidChar(read())799 }800 801 return read()802}803 804function hexEscape () {805 let buffer = ''806 let c = peek()807 808 if (!util.isHexDigit(c)) {809 throw invalidChar(read())810 }811 812 buffer += read()813 814 c = peek()815 if (!util.isHexDigit(c)) {816 throw invalidChar(read())817 }818 819 buffer += read()820 821 return String.fromCodePoint(parseInt(buffer, 16))822}823 824function unicodeEscape () {825 let buffer = ''826 let count = 4827 828 while (count-- > 0) {829 const c = peek()830 if (!util.isHexDigit(c)) {831 throw invalidChar(read())832 }833 834 buffer += read()835 }836 837 return String.fromCodePoint(parseInt(buffer, 16))838}839 840const parseStates = {841 start () {842 if (token.type === 'eof') {843 throw invalidEOF()844 }845 846 push()847 },848 849 beforePropertyName () {850 switch (token.type) {851 case 'identifier':852 case 'string':853 key = token.value854 parseState = 'afterPropertyName'855 return856 857 case 'punctuator':858 // This code is unreachable since it's handled by the lexState.859 // if (token.value !== '}') {860 // throw invalidToken()861 // }862 863 pop()864 return865 866 case 'eof':867 throw invalidEOF()868 }869 870 // This code is unreachable since it's handled by the lexState.871 // throw invalidToken()872 },873 874 afterPropertyName () {875 // This code is unreachable since it's handled by the lexState.876 // if (token.type !== 'punctuator' || token.value !== ':') {877 // throw invalidToken()878 // }879 880 if (token.type === 'eof') {881 throw invalidEOF()882 }883 884 parseState = 'beforePropertyValue'885 },886 887 beforePropertyValue () {888 if (token.type === 'eof') {889 throw invalidEOF()890 }891 892 push()893 },894 895 beforeArrayValue () {896 if (token.type === 'eof') {897 throw invalidEOF()898 }899 900 if (token.type === 'punctuator' && token.value === ']') {901 pop()902 return903 }904 905 push()906 },907 908 afterPropertyValue () {909 // This code is unreachable since it's handled by the lexState.910 // if (token.type !== 'punctuator') {911 // throw invalidToken()912 // }913 914 if (token.type === 'eof') {915 throw invalidEOF()916 }917 918 switch (token.value) {919 case ',':920 parseState = 'beforePropertyName'921 return922 923 case '}':924 pop()925 }926 927 // This code is unreachable since it's handled by the lexState.928 // throw invalidToken()929 },930 931 afterArrayValue () {932 // This code is unreachable since it's handled by the lexState.933 // if (token.type !== 'punctuator') {934 // throw invalidToken()935 // }936 937 if (token.type === 'eof') {938 throw invalidEOF()939 }940 941 switch (token.value) {942 case ',':943 parseState = 'beforeArrayValue'944 return945 946 case ']':947 pop()948 }949 950 // This code is unreachable since it's handled by the lexState.951 // throw invalidToken()952 },953 954 end () {955 // This code is unreachable since it's handled by the lexState.956 // if (token.type !== 'eof') {957 // throw invalidToken()958 // }959 },960}961 962function push () {963 let value964 965 switch (token.type) {966 case 'punctuator':967 switch (token.value) {968 case '{':969 value = {}970 break971 972 case '[':973 value = []974 break975 }976 977 break978 979 case 'null':980 case 'boolean':981 case 'numeric':982 case 'string':983 value = token.value984 break985 986 // This code is unreachable.987 // default:988 // throw invalidToken()989 }990 991 if (root === undefined) {992 root = value993 } else {994 const parent = stack[stack.length - 1]995 if (Array.isArray(parent)) {996 parent.push(value)997 } else {998 Object.defineProperty(parent, key, {999 value,1000 writable: true,1001 enumerable: true,1002 configurable: true,1003 })1004 }1005 }1006 1007 if (value !== null && typeof value === 'object') {1008 stack.push(value)1009 1010 if (Array.isArray(value)) {1011 parseState = 'beforeArrayValue'1012 } else {1013 parseState = 'beforePropertyName'1014 }1015 } else {1016 const current = stack[stack.length - 1]1017 if (current == null) {1018 parseState = 'end'1019 } else if (Array.isArray(current)) {1020 parseState = 'afterArrayValue'1021 } else {1022 parseState = 'afterPropertyValue'1023 }1024 }1025}1026 1027function pop () {1028 stack.pop()1029 1030 const current = stack[stack.length - 1]1031 if (current == null) {1032 parseState = 'end'1033 } else if (Array.isArray(current)) {1034 parseState = 'afterArrayValue'1035 } else {1036 parseState = 'afterPropertyValue'1037 }1038}1039 1040// This code is unreachable.1041// function invalidParseState () {1042// return new Error(`JSON5: invalid parse state '${parseState}'`)1043// }1044 1045// This code is unreachable.1046// function invalidLexState (state) {1047// return new Error(`JSON5: invalid lex state '${state}'`)1048// }1049 1050function invalidChar (c) {1051 if (c === undefined) {1052 return syntaxError(`JSON5: invalid end of input at ${line}:${column}`)1053 }1054 1055 return syntaxError(`JSON5: invalid character '${formatChar(c)}' at ${line}:${column}`)1056}1057 1058function invalidEOF () {1059 return syntaxError(`JSON5: invalid end of input at ${line}:${column}`)1060}1061 1062// This code is unreachable.1063// function invalidToken () {1064// if (token.type === 'eof') {1065// return syntaxError(`JSON5: invalid end of input at ${line}:${column}`)1066// }1067 1068// const c = String.fromCodePoint(token.value.codePointAt(0))1069// return syntaxError(`JSON5: invalid character '${formatChar(c)}' at ${line}:${column}`)1070// }1071 1072function invalidIdentifier () {1073 column -= 51074 return syntaxError(`JSON5: invalid identifier character at ${line}:${column}`)1075}1076 1077function separatorChar (c) {1078 console.warn(`JSON5: '${formatChar(c)}' in strings is not valid ECMAScript; consider escaping`)1079}1080 1081function formatChar (c) {1082 const replacements = {1083 "'": "\\'",1084 '"': '\\"',1085 '\\': '\\\\',1086 '\b': '\\b',1087 '\f': '\\f',1088 '\n': '\\n',1089 '\r': '\\r',1090 '\t': '\\t',1091 '\v': '\\v',1092 '\0': '\\0',1093 '\u2028': '\\u2028',1094 '\u2029': '\\u2029',1095 }1096 1097 if (replacements[c]) {1098 return replacements[c]1099 }1100 1101 if (c < ' ') {1102 const hexString = c.charCodeAt(0).toString(16)1103 return '\\x' + ('00' + hexString).substring(hexString.length)1104 }1105 1106 return c1107}1108 1109function syntaxError (message) {1110 const err = new SyntaxError(message)1111 err.lineNumber = line1112 err.columnNumber = column1113 return err1114}1115 