parseJson.ts 4.3 KB

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