parseJson.ts 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. const rxParseJson = /position\s(\d+)$/
  2. export function parseJson(s: string, pos: number): unknown {
  3. let endPos: number | undefined
  4. parseJson.message = undefined
  5. let matches: RegExpExecArray | null
  6. if (pos) s = s.slice(pos)
  7. try {
  8. parseJson.position = pos + s.length
  9. return JSON.parse(s)
  10. } catch (e) {
  11. matches = rxParseJson.exec((e as Error).message)
  12. if (!matches) {
  13. parseJson.message = "unexpected end"
  14. return undefined
  15. }
  16. endPos = +matches[1]
  17. const c = s[endPos]
  18. s = s.slice(0, endPos)
  19. parseJson.position = pos + endPos
  20. try {
  21. return JSON.parse(s)
  22. } catch (e1) {
  23. parseJson.message = `unexpected token ${c}`
  24. return undefined
  25. }
  26. }
  27. }
  28. parseJson.message = undefined as string | undefined
  29. parseJson.position = 0 as number
  30. parseJson.code = 'require("ajv/dist/runtime/parseJson").parseJson'
  31. export function parseJsonNumber(s: string, pos: number, maxDigits?: number): number | undefined {
  32. let numStr = ""
  33. let c: string
  34. parseJsonNumber.message = undefined
  35. if (s[pos] === "-") {
  36. numStr += "-"
  37. pos++
  38. }
  39. if (s[pos] === "0") {
  40. numStr += "0"
  41. pos++
  42. } else {
  43. if (!parseDigits(maxDigits)) {
  44. errorMessage()
  45. return undefined
  46. }
  47. }
  48. if (maxDigits) {
  49. parseJsonNumber.position = pos
  50. return +numStr
  51. }
  52. if (s[pos] === ".") {
  53. numStr += "."
  54. pos++
  55. if (!parseDigits()) {
  56. errorMessage()
  57. return undefined
  58. }
  59. }
  60. if (((c = s[pos]), c === "e" || c === "E")) {
  61. numStr += "e"
  62. pos++
  63. if (((c = s[pos]), c === "+" || c === "-")) {
  64. numStr += c
  65. pos++
  66. }
  67. if (!parseDigits()) {
  68. errorMessage()
  69. return undefined
  70. }
  71. }
  72. parseJsonNumber.position = pos
  73. return +numStr
  74. function parseDigits(maxLen?: number): boolean {
  75. let digit = false
  76. while (((c = s[pos]), c >= "0" && c <= "9" && (maxLen === undefined || maxLen-- > 0))) {
  77. digit = true
  78. numStr += c
  79. pos++
  80. }
  81. return digit
  82. }
  83. function errorMessage(): void {
  84. parseJsonNumber.position = pos
  85. parseJsonNumber.message = pos < s.length ? `unexpected token ${s[pos]}` : "unexpected end"
  86. }
  87. }
  88. parseJsonNumber.message = undefined as string | undefined
  89. parseJsonNumber.position = 0 as number
  90. parseJsonNumber.code = 'require("ajv/dist/runtime/parseJson").parseJsonNumber'
  91. const escapedChars: {[X in string]?: string} = {
  92. b: "\b",
  93. f: "\f",
  94. n: "\n",
  95. r: "\r",
  96. t: "\t",
  97. '"': '"',
  98. "/": "/",
  99. "\\": "\\",
  100. }
  101. const CODE_A: number = "a".charCodeAt(0)
  102. const CODE_0: number = "0".charCodeAt(0)
  103. export function parseJsonString(s: string, pos: number): string | undefined {
  104. let str = ""
  105. let c: string | undefined
  106. parseJsonString.message = undefined
  107. // eslint-disable-next-line no-constant-condition, @typescript-eslint/no-unnecessary-condition
  108. while (true) {
  109. c = s[pos++]
  110. if (c === '"') break
  111. if (c === "\\") {
  112. c = s[pos]
  113. if (c in escapedChars) {
  114. str += escapedChars[c]
  115. pos++
  116. } else if (c === "u") {
  117. pos++
  118. let count = 4
  119. let code = 0
  120. while (count--) {
  121. code <<= 4
  122. c = s[pos]
  123. if (c === undefined) {
  124. errorMessage("unexpected end")
  125. return undefined
  126. }
  127. c = c.toLowerCase()
  128. if (c >= "a" && c <= "f") {
  129. code += c.charCodeAt(0) - CODE_A + 10
  130. } else if (c >= "0" && c <= "9") {
  131. code += c.charCodeAt(0) - CODE_0
  132. } else {
  133. errorMessage(`unexpected token ${c}`)
  134. return undefined
  135. }
  136. pos++
  137. }
  138. str += String.fromCharCode(code)
  139. } else {
  140. errorMessage(`unexpected token ${c}`)
  141. return undefined
  142. }
  143. // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
  144. } else if (c === undefined) {
  145. errorMessage("unexpected end")
  146. return undefined
  147. } else {
  148. if (c.charCodeAt(0) >= 0x20) {
  149. str += c
  150. } else {
  151. errorMessage(`unexpected token ${c}`)
  152. return undefined
  153. }
  154. }
  155. }
  156. parseJsonString.position = pos
  157. return str
  158. function errorMessage(msg: string): void {
  159. parseJsonString.position = pos
  160. parseJsonString.message = msg
  161. }
  162. }
  163. parseJsonString.message = undefined as string | undefined
  164. parseJsonString.position = 0 as number
  165. parseJsonString.code = 'require("ajv/dist/runtime/parseJson").parseJsonString'