parseJson.ts 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. const rxParseJson = /position\s(\d+)(?: \(line \d+ column \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. // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
  124. if (c === undefined) {
  125. errorMessage("unexpected end")
  126. return undefined
  127. }
  128. c = c.toLowerCase()
  129. if (c >= "a" && c <= "f") {
  130. code += c.charCodeAt(0) - CODE_A + 10
  131. } else if (c >= "0" && c <= "9") {
  132. code += c.charCodeAt(0) - CODE_0
  133. } else {
  134. errorMessage(`unexpected token ${c}`)
  135. return undefined
  136. }
  137. pos++
  138. }
  139. str += String.fromCharCode(code)
  140. } else {
  141. errorMessage(`unexpected token ${c}`)
  142. return undefined
  143. }
  144. // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
  145. } else if (c === undefined) {
  146. errorMessage("unexpected end")
  147. return undefined
  148. } else {
  149. if (c.charCodeAt(0) >= 0x20) {
  150. str += c
  151. } else {
  152. errorMessage(`unexpected token ${c}`)
  153. return undefined
  154. }
  155. }
  156. }
  157. parseJsonString.position = pos
  158. return str
  159. function errorMessage(msg: string): void {
  160. parseJsonString.position = pos
  161. parseJsonString.message = msg
  162. }
  163. }
  164. parseJsonString.message = undefined as string | undefined
  165. parseJsonString.position = 0 as number
  166. parseJsonString.code = 'require("ajv/dist/runtime/parseJson").parseJsonString'