jsonParse.js 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317
  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. const token = curToken()
  104. lex()
  105. return token
  106. }
  107. return expect("Value")
  108. }
  109. function curToken() {
  110. return { kind, start, end, value: JSON.parse(string.slice(start, end)) }
  111. }
  112. function expect(str) {
  113. if (kind === str) {
  114. lex()
  115. return
  116. }
  117. let found
  118. if (kind === "EOF") {
  119. found = "[end of file]"
  120. } else if (end - start > 1) {
  121. found = `\`${string.slice(start, end)}\``
  122. } else {
  123. const match = string.slice(start).match(/^.+?\b/)
  124. found = `\`${match ? match[0] : string[start]}\``
  125. }
  126. throw syntaxError(`Expected ${str} but found ${found}.`)
  127. }
  128. function syntaxError(message) {
  129. return { message, start, end }
  130. }
  131. function skip(k) {
  132. if (kind === k) {
  133. lex()
  134. return true
  135. }
  136. }
  137. function ch() {
  138. if (end < strLen) {
  139. end++
  140. code = end === strLen ? 0 : string.charCodeAt(end)
  141. }
  142. }
  143. function lex() {
  144. lastEnd = end
  145. while (code === 9 || code === 10 || code === 13 || code === 32) {
  146. ch()
  147. }
  148. if (code === 0) {
  149. kind = "EOF"
  150. return
  151. }
  152. start = end
  153. switch (code) {
  154. // "
  155. case 34:
  156. kind = "String"
  157. return readString()
  158. // -, 0-9
  159. case 45:
  160. case 48:
  161. case 49:
  162. case 50:
  163. case 51:
  164. case 52:
  165. case 53:
  166. case 54:
  167. case 55:
  168. case 56:
  169. case 57:
  170. kind = "Number"
  171. return readNumber()
  172. // f
  173. case 102:
  174. if (string.slice(start, start + 5) !== "false") {
  175. break
  176. }
  177. end += 4
  178. ch()
  179. kind = "Boolean"
  180. return
  181. // n
  182. case 110:
  183. if (string.slice(start, start + 4) !== "null") {
  184. break
  185. }
  186. end += 3
  187. ch()
  188. kind = "Null"
  189. return
  190. // t
  191. case 116:
  192. if (string.slice(start, start + 4) !== "true") {
  193. break
  194. }
  195. end += 3
  196. ch()
  197. kind = "Boolean"
  198. return
  199. }
  200. kind = string[start]
  201. ch()
  202. }
  203. function readString() {
  204. ch()
  205. while (code !== 34 && code > 31) {
  206. if (code === 92) {
  207. // \
  208. ch()
  209. switch (code) {
  210. case 34: // "
  211. case 47: // /
  212. case 92: // \
  213. case 98: // b
  214. case 102: // f
  215. case 110: // n
  216. case 114: // r
  217. case 116: // t
  218. ch()
  219. break
  220. case 117: // u
  221. ch()
  222. readHex()
  223. readHex()
  224. readHex()
  225. readHex()
  226. break
  227. default:
  228. throw syntaxError("Bad character escape sequence.")
  229. }
  230. } else if (end === strLen) {
  231. throw syntaxError("Unterminated string.")
  232. } else {
  233. ch()
  234. }
  235. }
  236. if (code === 34) {
  237. ch()
  238. return
  239. }
  240. throw syntaxError("Unterminated string.")
  241. }
  242. function readHex() {
  243. if (
  244. (code >= 48 && code <= 57) || // 0-9
  245. (code >= 65 && code <= 70) || // A-F
  246. (code >= 97 && code <= 102) // a-f
  247. ) {
  248. return ch()
  249. }
  250. throw syntaxError("Expected hexadecimal digit.")
  251. }
  252. function readNumber() {
  253. if (code === 45) {
  254. // -
  255. ch()
  256. }
  257. if (code === 48) {
  258. // 0
  259. ch()
  260. } else {
  261. readDigits()
  262. }
  263. if (code === 46) {
  264. // .
  265. ch()
  266. readDigits()
  267. }
  268. if (code === 69 || code === 101) {
  269. // E e
  270. ch()
  271. if (code === 43 || code === 45) {
  272. // + -
  273. ch()
  274. }
  275. readDigits()
  276. }
  277. }
  278. function readDigits() {
  279. if (code < 48 || code > 57) {
  280. // 0 - 9
  281. throw syntaxError("Expected decimal digit.")
  282. }
  283. do {
  284. ch()
  285. } while (code >= 48 && code <= 57) // 0 - 9
  286. }