jsonParse.js 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318
  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. export default function jsonParse(str) {
  22. string = str
  23. strLen = str.length
  24. start = end = lastEnd = -1
  25. ch()
  26. lex()
  27. try {
  28. const ast = parseObj()
  29. expect("EOF")
  30. return ast
  31. } catch (e) {
  32. // Try parsing expecting a root array
  33. const ast = parseArr()
  34. expect("EOF")
  35. return ast
  36. }
  37. }
  38. let string
  39. let strLen
  40. let start
  41. let end
  42. let lastEnd
  43. let code
  44. let kind
  45. function parseObj() {
  46. const nodeStart = start
  47. const members = []
  48. expect("{")
  49. if (!skip("}")) {
  50. do {
  51. members.push(parseMember())
  52. } while (skip(","))
  53. expect("}")
  54. }
  55. return {
  56. kind: "Object",
  57. start: nodeStart,
  58. end: lastEnd,
  59. members,
  60. }
  61. }
  62. function parseMember() {
  63. const nodeStart = start
  64. const key = kind === "String" ? curToken() : null
  65. expect("String")
  66. expect(":")
  67. const value = parseVal()
  68. return {
  69. kind: "Member",
  70. start: nodeStart,
  71. end: lastEnd,
  72. key,
  73. value,
  74. }
  75. }
  76. function parseArr() {
  77. const nodeStart = start
  78. const values = []
  79. expect("[")
  80. if (!skip("]")) {
  81. do {
  82. values.push(parseVal())
  83. } while (skip(","))
  84. expect("]")
  85. }
  86. return {
  87. kind: "Array",
  88. start: nodeStart,
  89. end: lastEnd,
  90. values,
  91. }
  92. }
  93. function parseVal() {
  94. switch (kind) {
  95. case "[":
  96. return parseArr()
  97. case "{":
  98. return parseObj()
  99. case "String":
  100. case "Number":
  101. case "Boolean":
  102. case "Null":
  103. // eslint-disable-next-line no-case-declarations
  104. const token = curToken()
  105. lex()
  106. return token
  107. }
  108. return expect("Value")
  109. }
  110. function curToken() {
  111. return { kind, start, end, value: JSON.parse(string.slice(start, end)) }
  112. }
  113. function expect(str) {
  114. if (kind === str) {
  115. lex()
  116. return
  117. }
  118. let found
  119. if (kind === "EOF") {
  120. found = "[end of file]"
  121. } else if (end - start > 1) {
  122. found = `\`${string.slice(start, end)}\``
  123. } else {
  124. const match = string.slice(start).match(/^.+?\b/)
  125. found = `\`${match ? match[0] : string[start]}\``
  126. }
  127. throw syntaxError(`Expected ${str} but found ${found}.`)
  128. }
  129. function syntaxError(message) {
  130. return { message, start, end }
  131. }
  132. function skip(k) {
  133. if (kind === k) {
  134. lex()
  135. return true
  136. }
  137. }
  138. function ch() {
  139. if (end < strLen) {
  140. end++
  141. code = end === strLen ? 0 : string.charCodeAt(end)
  142. }
  143. }
  144. function lex() {
  145. lastEnd = end
  146. while (code === 9 || code === 10 || code === 13 || code === 32) {
  147. ch()
  148. }
  149. if (code === 0) {
  150. kind = "EOF"
  151. return
  152. }
  153. start = end
  154. switch (code) {
  155. // "
  156. case 34:
  157. kind = "String"
  158. return readString()
  159. // -, 0-9
  160. case 45:
  161. case 48:
  162. case 49:
  163. case 50:
  164. case 51:
  165. case 52:
  166. case 53:
  167. case 54:
  168. case 55:
  169. case 56:
  170. case 57:
  171. kind = "Number"
  172. return readNumber()
  173. // f
  174. case 102:
  175. if (string.slice(start, start + 5) !== "false") {
  176. break
  177. }
  178. end += 4
  179. ch()
  180. kind = "Boolean"
  181. return
  182. // n
  183. case 110:
  184. if (string.slice(start, start + 4) !== "null") {
  185. break
  186. }
  187. end += 3
  188. ch()
  189. kind = "Null"
  190. return
  191. // t
  192. case 116:
  193. if (string.slice(start, start + 4) !== "true") {
  194. break
  195. }
  196. end += 3
  197. ch()
  198. kind = "Boolean"
  199. return
  200. }
  201. kind = string[start]
  202. ch()
  203. }
  204. function readString() {
  205. ch()
  206. while (code !== 34 && code > 31) {
  207. if (code === 92) {
  208. // \
  209. ch()
  210. switch (code) {
  211. case 34: // "
  212. case 47: // /
  213. case 92: // \
  214. case 98: // b
  215. case 102: // f
  216. case 110: // n
  217. case 114: // r
  218. case 116: // t
  219. ch()
  220. break
  221. case 117: // u
  222. ch()
  223. readHex()
  224. readHex()
  225. readHex()
  226. readHex()
  227. break
  228. default:
  229. throw syntaxError("Bad character escape sequence.")
  230. }
  231. } else if (end === strLen) {
  232. throw syntaxError("Unterminated string.")
  233. } else {
  234. ch()
  235. }
  236. }
  237. if (code === 34) {
  238. ch()
  239. return
  240. }
  241. throw syntaxError("Unterminated string.")
  242. }
  243. function readHex() {
  244. if (
  245. (code >= 48 && code <= 57) || // 0-9
  246. (code >= 65 && code <= 70) || // A-F
  247. (code >= 97 && code <= 102) // a-f
  248. ) {
  249. return ch()
  250. }
  251. throw syntaxError("Expected hexadecimal digit.")
  252. }
  253. function readNumber() {
  254. if (code === 45) {
  255. // -
  256. ch()
  257. }
  258. if (code === 48) {
  259. // 0
  260. ch()
  261. } else {
  262. readDigits()
  263. }
  264. if (code === 46) {
  265. // .
  266. ch()
  267. readDigits()
  268. }
  269. if (code === 69 || code === 101) {
  270. // E e
  271. ch()
  272. if (code === 43 || code === 45) {
  273. // + -
  274. ch()
  275. }
  276. readDigits()
  277. }
  278. }
  279. function readDigits() {
  280. if (code < 48 || code > 57) {
  281. // 0 - 9
  282. throw syntaxError("Expected decimal digit.")
  283. }
  284. do {
  285. ch()
  286. } while (code >= 48 && code <= 57) // 0 - 9
  287. }