jsonParse.ts 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397
  1. /**
  2. * Copyright (c) 2019 GraphQL Contributors
  3. * All rights reserved.
  4. *
  5. * This source code is licensed under the BSD-style license found in the
  6. * LICENSE file in the root directory of this source tree. An additional grant
  7. * of patent rights can be found in the PATENTS file in the same directory.
  8. */
  9. /**
  10. * This JSON parser simply walks the input, generating an AST. Use this in lieu
  11. * of JSON.parse if you need character offset parse errors and an AST parse tree
  12. * with location information.
  13. *
  14. * If an error is encountered, a SyntaxError will be thrown, with properties:
  15. *
  16. * - message: string
  17. * - start: int - the start inclusive offset of the syntax error
  18. * - end: int - the end exclusive offset of the syntax error
  19. *
  20. */
  21. type JSONEOFValue = {
  22. kind: "EOF"
  23. start: number
  24. end: number
  25. }
  26. type JSONNullValue = {
  27. kind: "Null"
  28. start: number
  29. end: number
  30. }
  31. type JSONNumberValue = {
  32. kind: "Number"
  33. start: number
  34. end: number
  35. value: number
  36. }
  37. type JSONStringValue = {
  38. kind: "String"
  39. start: number
  40. end: number
  41. value: string
  42. }
  43. type JSONBooleanValue = {
  44. kind: "Boolean"
  45. start: number
  46. end: number
  47. value: boolean
  48. }
  49. type JSONPrimitiveValue =
  50. | JSONNullValue
  51. | JSONEOFValue
  52. | JSONStringValue
  53. | JSONNumberValue
  54. | JSONBooleanValue
  55. export type JSONObjectValue = {
  56. kind: "Object"
  57. start: number
  58. end: number
  59. // eslint-disable-next-line no-use-before-define
  60. members: JSONObjectMember[]
  61. }
  62. export type JSONArrayValue = {
  63. kind: "Array"
  64. start: number
  65. end: number
  66. // eslint-disable-next-line no-use-before-define
  67. values: JSONValue[]
  68. }
  69. export type JSONValue = JSONObjectValue | JSONArrayValue | JSONPrimitiveValue
  70. export type JSONObjectMember = {
  71. kind: "Member"
  72. start: number
  73. end: number
  74. key: JSONStringValue
  75. value: JSONValue
  76. }
  77. export default function jsonParse(
  78. str: string
  79. ): JSONObjectValue | JSONArrayValue {
  80. string = str
  81. strLen = str.length
  82. start = end = lastEnd = -1
  83. ch()
  84. lex()
  85. try {
  86. const ast = parseObj()
  87. expect("EOF")
  88. return ast
  89. } catch (e) {
  90. // Try parsing expecting a root array
  91. const ast = parseArr()
  92. expect("EOF")
  93. return ast
  94. }
  95. }
  96. let string: string
  97. let strLen: number
  98. let start: number
  99. let end: number
  100. let lastEnd: number
  101. let code: number
  102. let kind: string
  103. function parseObj(): JSONObjectValue {
  104. const nodeStart = start
  105. const members = []
  106. expect("{")
  107. if (!skip("}")) {
  108. do {
  109. members.push(parseMember())
  110. } while (skip(","))
  111. expect("}")
  112. }
  113. return {
  114. kind: "Object",
  115. start: nodeStart,
  116. end: lastEnd,
  117. members,
  118. }
  119. }
  120. function parseMember(): JSONObjectMember {
  121. const nodeStart = start
  122. const key = kind === "String" ? (curToken() as JSONStringValue) : null
  123. expect("String")
  124. expect(":")
  125. const value = parseVal()
  126. return {
  127. kind: "Member",
  128. start: nodeStart,
  129. end: lastEnd,
  130. key: key!,
  131. value,
  132. }
  133. }
  134. function parseArr(): JSONArrayValue {
  135. const nodeStart = start
  136. const values: JSONValue[] = []
  137. expect("[")
  138. if (!skip("]")) {
  139. do {
  140. values.push(parseVal())
  141. } while (skip(","))
  142. expect("]")
  143. }
  144. return {
  145. kind: "Array",
  146. start: nodeStart,
  147. end: lastEnd,
  148. values,
  149. }
  150. }
  151. function parseVal(): JSONValue {
  152. switch (kind) {
  153. case "[":
  154. return parseArr()
  155. case "{":
  156. return parseObj()
  157. case "String":
  158. case "Number":
  159. case "Boolean":
  160. case "Null":
  161. // eslint-disable-next-line no-case-declarations
  162. const token = curToken()
  163. lex()
  164. return token
  165. }
  166. return expect("Value") as never
  167. }
  168. function curToken(): JSONPrimitiveValue {
  169. return {
  170. kind: kind as any,
  171. start,
  172. end,
  173. value: JSON.parse(string.slice(start, end)),
  174. }
  175. }
  176. function expect(str: string) {
  177. if (kind === str) {
  178. lex()
  179. return
  180. }
  181. let found
  182. if (kind === "EOF") {
  183. found = "[end of file]"
  184. } else if (end - start > 1) {
  185. found = `\`${string.slice(start, end)}\``
  186. } else {
  187. const match = string.slice(start).match(/^.+?\b/)
  188. found = `\`${match ? match[0] : string[start]}\``
  189. }
  190. throw syntaxError(`Expected ${str} but found ${found}.`)
  191. }
  192. type SyntaxError = {
  193. message: string
  194. start: number
  195. end: number
  196. }
  197. function syntaxError(message: string): SyntaxError {
  198. return { message, start, end }
  199. }
  200. function skip(k: string) {
  201. if (kind === k) {
  202. lex()
  203. return true
  204. }
  205. }
  206. function ch() {
  207. if (end < strLen) {
  208. end++
  209. code = end === strLen ? 0 : string.charCodeAt(end)
  210. }
  211. }
  212. function lex() {
  213. lastEnd = end
  214. while (code === 9 || code === 10 || code === 13 || code === 32) {
  215. ch()
  216. }
  217. if (code === 0) {
  218. kind = "EOF"
  219. return
  220. }
  221. start = end
  222. switch (code) {
  223. // "
  224. case 34:
  225. kind = "String"
  226. return readString()
  227. // -, 0-9
  228. case 45:
  229. case 48:
  230. case 49:
  231. case 50:
  232. case 51:
  233. case 52:
  234. case 53:
  235. case 54:
  236. case 55:
  237. case 56:
  238. case 57:
  239. kind = "Number"
  240. return readNumber()
  241. // f
  242. case 102:
  243. if (string.slice(start, start + 5) !== "false") {
  244. break
  245. }
  246. end += 4
  247. ch()
  248. kind = "Boolean"
  249. return
  250. // n
  251. case 110:
  252. if (string.slice(start, start + 4) !== "null") {
  253. break
  254. }
  255. end += 3
  256. ch()
  257. kind = "Null"
  258. return
  259. // t
  260. case 116:
  261. if (string.slice(start, start + 4) !== "true") {
  262. break
  263. }
  264. end += 3
  265. ch()
  266. kind = "Boolean"
  267. return
  268. }
  269. kind = string[start]
  270. ch()
  271. }
  272. function readString() {
  273. ch()
  274. while (code !== 34 && code > 31) {
  275. if (code === (92 as any)) {
  276. // \
  277. ch()
  278. switch (code) {
  279. case 34: // "
  280. case 47: // /
  281. case 92: // \
  282. case 98: // b
  283. case 102: // f
  284. case 110: // n
  285. case 114: // r
  286. case 116: // t
  287. ch()
  288. break
  289. case 117: // u
  290. ch()
  291. readHex()
  292. readHex()
  293. readHex()
  294. readHex()
  295. break
  296. default:
  297. throw syntaxError("Bad character escape sequence.")
  298. }
  299. } else if (end === strLen) {
  300. throw syntaxError("Unterminated string.")
  301. } else {
  302. ch()
  303. }
  304. }
  305. if (code === 34) {
  306. ch()
  307. return
  308. }
  309. throw syntaxError("Unterminated string.")
  310. }
  311. function readHex() {
  312. if (
  313. (code >= 48 && code <= 57) || // 0-9
  314. (code >= 65 && code <= 70) || // A-F
  315. (code >= 97 && code <= 102) // a-f
  316. ) {
  317. return ch()
  318. }
  319. throw syntaxError("Expected hexadecimal digit.")
  320. }
  321. function readNumber() {
  322. if (code === 45) {
  323. // -
  324. ch()
  325. }
  326. if (code === 48) {
  327. // 0
  328. ch()
  329. } else {
  330. readDigits()
  331. }
  332. if (code === 46) {
  333. // .
  334. ch()
  335. readDigits()
  336. }
  337. if (code === 69 || code === 101) {
  338. // E e
  339. ch()
  340. if (code === (43 as any) || code === (45 as any)) {
  341. // + -
  342. ch()
  343. }
  344. readDigits()
  345. }
  346. }
  347. function readDigits() {
  348. if (code < 48 || code > 57) {
  349. // 0 - 9
  350. throw syntaxError("Expected decimal digit.")
  351. }
  352. do {
  353. ch()
  354. } while (code >= 48 && code <= 57) // 0 - 9
  355. }