parsejson.nim 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523
  1. #
  2. #
  3. # Nim's Runtime Library
  4. # (c) Copyright 2018 Nim contributors
  5. #
  6. # See the file "copying.txt", included in this
  7. # distribution, for details about the copyright.
  8. #
  9. ## This module implements a json parser. It is used
  10. ## and exported by the `json` standard library
  11. ## module, but can also be used in its own right.
  12. import strutils, lexbase, streams, unicode
  13. import std/private/decode_helpers
  14. when defined(nimPreviewSlimSystem):
  15. import std/assertions
  16. type
  17. JsonEventKind* = enum ## enumeration of all events that may occur when parsing
  18. jsonError, ## an error occurred during parsing
  19. jsonEof, ## end of file reached
  20. jsonString, ## a string literal
  21. jsonInt, ## an integer literal
  22. jsonFloat, ## a float literal
  23. jsonTrue, ## the value `true`
  24. jsonFalse, ## the value `false`
  25. jsonNull, ## the value `null`
  26. jsonObjectStart, ## start of an object: the `{` token
  27. jsonObjectEnd, ## end of an object: the `}` token
  28. jsonArrayStart, ## start of an array: the `[` token
  29. jsonArrayEnd ## end of an array: the `]` token
  30. TokKind* = enum # must be synchronized with TJsonEventKind!
  31. tkError,
  32. tkEof,
  33. tkString,
  34. tkInt,
  35. tkFloat,
  36. tkTrue,
  37. tkFalse,
  38. tkNull,
  39. tkCurlyLe,
  40. tkCurlyRi,
  41. tkBracketLe,
  42. tkBracketRi,
  43. tkColon,
  44. tkComma
  45. JsonError* = enum ## enumeration that lists all errors that can occur
  46. errNone, ## no error
  47. errInvalidToken, ## invalid token
  48. errStringExpected, ## string expected
  49. errColonExpected, ## `:` expected
  50. errCommaExpected, ## `,` expected
  51. errBracketRiExpected, ## `]` expected
  52. errCurlyRiExpected, ## `}` expected
  53. errQuoteExpected, ## `"` or `'` expected
  54. errEOC_Expected, ## `*/` expected
  55. errEofExpected, ## EOF expected
  56. errExprExpected ## expr expected
  57. ParserState = enum
  58. stateEof, stateStart, stateObject, stateArray, stateExpectArrayComma,
  59. stateExpectObjectComma, stateExpectColon, stateExpectValue
  60. JsonParser* = object of BaseLexer ## the parser object.
  61. a*: string
  62. tok*: TokKind
  63. kind: JsonEventKind
  64. err: JsonError
  65. state: seq[ParserState]
  66. filename: string
  67. rawStringLiterals: bool
  68. JsonKindError* = object of ValueError ## raised by the `to` macro if the
  69. ## JSON kind is incorrect.
  70. JsonParsingError* = object of ValueError ## is raised for a JSON error
  71. const
  72. errorMessages*: array[JsonError, string] = [
  73. "no error",
  74. "invalid token",
  75. "string expected",
  76. "':' expected",
  77. "',' expected",
  78. "']' expected",
  79. "'}' expected",
  80. "'\"' or \"'\" expected",
  81. "'*/' expected",
  82. "EOF expected",
  83. "expression expected"
  84. ]
  85. tokToStr: array[TokKind, string] = [
  86. "invalid token",
  87. "EOF",
  88. "string literal",
  89. "int literal",
  90. "float literal",
  91. "true",
  92. "false",
  93. "null",
  94. "{", "}", "[", "]", ":", ","
  95. ]
  96. proc open*(my: var JsonParser, input: Stream, filename: string;
  97. rawStringLiterals = false) =
  98. ## initializes the parser with an input stream. `Filename` is only used
  99. ## for nice error messages. If `rawStringLiterals` is true, string literals
  100. ## are kept with their surrounding quotes and escape sequences in them are
  101. ## left untouched too.
  102. lexbase.open(my, input)
  103. my.filename = filename
  104. my.state = @[stateStart]
  105. my.kind = jsonError
  106. my.a = ""
  107. my.rawStringLiterals = rawStringLiterals
  108. proc close*(my: var JsonParser) {.inline.} =
  109. ## closes the parser `my` and its associated input stream.
  110. lexbase.close(my)
  111. proc str*(my: JsonParser): string {.inline.} =
  112. ## returns the character data for the events: `jsonInt`, `jsonFloat`,
  113. ## `jsonString`
  114. assert(my.kind in {jsonInt, jsonFloat, jsonString})
  115. return my.a
  116. proc getInt*(my: JsonParser): BiggestInt {.inline.} =
  117. ## returns the number for the event: `jsonInt`
  118. assert(my.kind == jsonInt)
  119. return parseBiggestInt(my.a)
  120. proc getFloat*(my: JsonParser): float {.inline.} =
  121. ## returns the number for the event: `jsonFloat`
  122. assert(my.kind == jsonFloat)
  123. return parseFloat(my.a)
  124. proc kind*(my: JsonParser): JsonEventKind {.inline.} =
  125. ## returns the current event type for the JSON parser
  126. return my.kind
  127. proc getColumn*(my: JsonParser): int {.inline.} =
  128. ## get the current column the parser has arrived at.
  129. result = getColNumber(my, my.bufpos)
  130. proc getLine*(my: JsonParser): int {.inline.} =
  131. ## get the current line the parser has arrived at.
  132. result = my.lineNumber
  133. proc getFilename*(my: JsonParser): string {.inline.} =
  134. ## get the filename of the file that the parser processes.
  135. result = my.filename
  136. proc errorMsg*(my: JsonParser): string =
  137. ## returns a helpful error message for the event `jsonError`
  138. assert(my.kind == jsonError)
  139. result = "$1($2, $3) Error: $4" % [
  140. my.filename, $getLine(my), $getColumn(my), errorMessages[my.err]]
  141. proc errorMsgExpected*(my: JsonParser, e: string): string =
  142. ## returns an error message "`e` expected" in the same format as the
  143. ## other error messages
  144. result = "$1($2, $3) Error: $4" % [
  145. my.filename, $getLine(my), $getColumn(my), e & " expected"]
  146. proc parseEscapedUTF16*(buf: cstring, pos: var int): int =
  147. result = 0
  148. #UTF-16 escape is always 4 bytes.
  149. for _ in 0..3:
  150. # if char in '0' .. '9', 'a' .. 'f', 'A' .. 'F'
  151. if handleHexChar(buf[pos], result):
  152. inc(pos)
  153. else:
  154. return -1
  155. proc parseString(my: var JsonParser): TokKind =
  156. result = tkString
  157. var pos = my.bufpos + 1
  158. if my.rawStringLiterals:
  159. add(my.a, '"')
  160. while true:
  161. case my.buf[pos]
  162. of '\0':
  163. my.err = errQuoteExpected
  164. result = tkError
  165. break
  166. of '"':
  167. if my.rawStringLiterals:
  168. add(my.a, '"')
  169. inc(pos)
  170. break
  171. of '\\':
  172. if my.rawStringLiterals:
  173. add(my.a, '\\')
  174. case my.buf[pos+1]
  175. of '\\', '"', '\'', '/':
  176. add(my.a, my.buf[pos+1])
  177. inc(pos, 2)
  178. of 'b':
  179. add(my.a, '\b')
  180. inc(pos, 2)
  181. of 'f':
  182. add(my.a, '\f')
  183. inc(pos, 2)
  184. of 'n':
  185. add(my.a, '\L')
  186. inc(pos, 2)
  187. of 'r':
  188. add(my.a, '\C')
  189. inc(pos, 2)
  190. of 't':
  191. add(my.a, '\t')
  192. inc(pos, 2)
  193. of 'v':
  194. add(my.a, '\v')
  195. inc(pos, 2)
  196. of 'u':
  197. if my.rawStringLiterals:
  198. add(my.a, 'u')
  199. inc(pos, 2)
  200. var pos2 = pos
  201. var r = parseEscapedUTF16(cstring(my.buf), pos)
  202. if r < 0:
  203. my.err = errInvalidToken
  204. break
  205. # Deal with surrogates
  206. if (r and 0xfc00) == 0xd800:
  207. if my.buf[pos] != '\\' or my.buf[pos+1] != 'u':
  208. my.err = errInvalidToken
  209. break
  210. inc(pos, 2)
  211. var s = parseEscapedUTF16(cstring(my.buf), pos)
  212. if (s and 0xfc00) == 0xdc00 and s > 0:
  213. r = 0x10000 + (((r - 0xd800) shl 10) or (s - 0xdc00))
  214. else:
  215. my.err = errInvalidToken
  216. break
  217. if my.rawStringLiterals:
  218. let length = pos - pos2
  219. for i in 1 .. length:
  220. if my.buf[pos2] in {'0'..'9', 'A'..'F', 'a'..'f'}:
  221. add(my.a, my.buf[pos2])
  222. inc pos2
  223. else:
  224. break
  225. else:
  226. add(my.a, toUTF8(Rune(r)))
  227. else:
  228. # don't bother with the error
  229. add(my.a, my.buf[pos])
  230. inc(pos)
  231. of '\c':
  232. pos = lexbase.handleCR(my, pos)
  233. add(my.a, '\c')
  234. of '\L':
  235. pos = lexbase.handleLF(my, pos)
  236. add(my.a, '\L')
  237. else:
  238. add(my.a, my.buf[pos])
  239. inc(pos)
  240. my.bufpos = pos # store back
  241. proc skip(my: var JsonParser) =
  242. var pos = my.bufpos
  243. while true:
  244. case my.buf[pos]
  245. of '/':
  246. if my.buf[pos+1] == '/':
  247. # skip line comment:
  248. inc(pos, 2)
  249. while true:
  250. case my.buf[pos]
  251. of '\0':
  252. break
  253. of '\c':
  254. pos = lexbase.handleCR(my, pos)
  255. break
  256. of '\L':
  257. pos = lexbase.handleLF(my, pos)
  258. break
  259. else:
  260. inc(pos)
  261. elif my.buf[pos+1] == '*':
  262. # skip long comment:
  263. inc(pos, 2)
  264. while true:
  265. case my.buf[pos]
  266. of '\0':
  267. my.err = errEOC_Expected
  268. break
  269. of '\c':
  270. pos = lexbase.handleCR(my, pos)
  271. of '\L':
  272. pos = lexbase.handleLF(my, pos)
  273. of '*':
  274. inc(pos)
  275. if my.buf[pos] == '/':
  276. inc(pos)
  277. break
  278. else:
  279. inc(pos)
  280. else:
  281. break
  282. of ' ', '\t':
  283. inc(pos)
  284. of '\c':
  285. pos = lexbase.handleCR(my, pos)
  286. of '\L':
  287. pos = lexbase.handleLF(my, pos)
  288. else:
  289. break
  290. my.bufpos = pos
  291. proc parseNumber(my: var JsonParser) =
  292. var pos = my.bufpos
  293. if my.buf[pos] == '-':
  294. add(my.a, '-')
  295. inc(pos)
  296. if my.buf[pos] == '.':
  297. add(my.a, "0.")
  298. inc(pos)
  299. else:
  300. while my.buf[pos] in Digits:
  301. add(my.a, my.buf[pos])
  302. inc(pos)
  303. if my.buf[pos] == '.':
  304. add(my.a, '.')
  305. inc(pos)
  306. # digits after the dot:
  307. while my.buf[pos] in Digits:
  308. add(my.a, my.buf[pos])
  309. inc(pos)
  310. if my.buf[pos] in {'E', 'e'}:
  311. add(my.a, my.buf[pos])
  312. inc(pos)
  313. if my.buf[pos] in {'+', '-'}:
  314. add(my.a, my.buf[pos])
  315. inc(pos)
  316. while my.buf[pos] in Digits:
  317. add(my.a, my.buf[pos])
  318. inc(pos)
  319. my.bufpos = pos
  320. proc parseName(my: var JsonParser) =
  321. var pos = my.bufpos
  322. if my.buf[pos] in IdentStartChars:
  323. while my.buf[pos] in IdentChars:
  324. add(my.a, my.buf[pos])
  325. inc(pos)
  326. my.bufpos = pos
  327. proc getTok*(my: var JsonParser): TokKind =
  328. setLen(my.a, 0)
  329. skip(my) # skip whitespace, comments
  330. case my.buf[my.bufpos]
  331. of '-', '.', '0'..'9':
  332. parseNumber(my)
  333. if {'.', 'e', 'E'} in my.a:
  334. result = tkFloat
  335. else:
  336. result = tkInt
  337. of '"':
  338. result = parseString(my)
  339. of '[':
  340. inc(my.bufpos)
  341. result = tkBracketLe
  342. of '{':
  343. inc(my.bufpos)
  344. result = tkCurlyLe
  345. of ']':
  346. inc(my.bufpos)
  347. result = tkBracketRi
  348. of '}':
  349. inc(my.bufpos)
  350. result = tkCurlyRi
  351. of ',':
  352. inc(my.bufpos)
  353. result = tkComma
  354. of ':':
  355. inc(my.bufpos)
  356. result = tkColon
  357. of '\0':
  358. result = tkEof
  359. of 'a'..'z', 'A'..'Z', '_':
  360. parseName(my)
  361. case my.a
  362. of "null": result = tkNull
  363. of "true": result = tkTrue
  364. of "false": result = tkFalse
  365. else: result = tkError
  366. else:
  367. inc(my.bufpos)
  368. result = tkError
  369. my.tok = result
  370. proc next*(my: var JsonParser) =
  371. ## retrieves the first/next event. This controls the parser.
  372. var tk = getTok(my)
  373. var i = my.state.len-1
  374. # the following code is a state machine. If we had proper coroutines,
  375. # the code could be much simpler.
  376. case my.state[i]
  377. of stateEof:
  378. if tk == tkEof:
  379. my.kind = jsonEof
  380. else:
  381. my.kind = jsonError
  382. my.err = errEofExpected
  383. of stateStart:
  384. # tokens allowed?
  385. case tk
  386. of tkString, tkInt, tkFloat, tkTrue, tkFalse, tkNull:
  387. my.state[i] = stateEof # expect EOF next!
  388. my.kind = JsonEventKind(ord(tk))
  389. of tkBracketLe:
  390. my.state.add(stateArray) # we expect any
  391. my.kind = jsonArrayStart
  392. of tkCurlyLe:
  393. my.state.add(stateObject)
  394. my.kind = jsonObjectStart
  395. of tkEof:
  396. my.kind = jsonEof
  397. else:
  398. my.kind = jsonError
  399. my.err = errEofExpected
  400. of stateObject:
  401. case tk
  402. of tkString, tkInt, tkFloat, tkTrue, tkFalse, tkNull:
  403. my.state.add(stateExpectColon)
  404. my.kind = JsonEventKind(ord(tk))
  405. of tkBracketLe:
  406. my.state.add(stateExpectColon)
  407. my.state.add(stateArray)
  408. my.kind = jsonArrayStart
  409. of tkCurlyLe:
  410. my.state.add(stateExpectColon)
  411. my.state.add(stateObject)
  412. my.kind = jsonObjectStart
  413. of tkCurlyRi:
  414. my.kind = jsonObjectEnd
  415. discard my.state.pop()
  416. else:
  417. my.kind = jsonError
  418. my.err = errCurlyRiExpected
  419. of stateArray:
  420. case tk
  421. of tkString, tkInt, tkFloat, tkTrue, tkFalse, tkNull:
  422. my.state.add(stateExpectArrayComma) # expect value next!
  423. my.kind = JsonEventKind(ord(tk))
  424. of tkBracketLe:
  425. my.state.add(stateExpectArrayComma)
  426. my.state.add(stateArray)
  427. my.kind = jsonArrayStart
  428. of tkCurlyLe:
  429. my.state.add(stateExpectArrayComma)
  430. my.state.add(stateObject)
  431. my.kind = jsonObjectStart
  432. of tkBracketRi:
  433. my.kind = jsonArrayEnd
  434. discard my.state.pop()
  435. else:
  436. my.kind = jsonError
  437. my.err = errBracketRiExpected
  438. of stateExpectArrayComma:
  439. case tk
  440. of tkComma:
  441. discard my.state.pop()
  442. next(my)
  443. of tkBracketRi:
  444. my.kind = jsonArrayEnd
  445. discard my.state.pop() # pop stateExpectArrayComma
  446. discard my.state.pop() # pop stateArray
  447. else:
  448. my.kind = jsonError
  449. my.err = errBracketRiExpected
  450. of stateExpectObjectComma:
  451. case tk
  452. of tkComma:
  453. discard my.state.pop()
  454. next(my)
  455. of tkCurlyRi:
  456. my.kind = jsonObjectEnd
  457. discard my.state.pop() # pop stateExpectObjectComma
  458. discard my.state.pop() # pop stateObject
  459. else:
  460. my.kind = jsonError
  461. my.err = errCurlyRiExpected
  462. of stateExpectColon:
  463. case tk
  464. of tkColon:
  465. my.state[i] = stateExpectValue
  466. next(my)
  467. else:
  468. my.kind = jsonError
  469. my.err = errColonExpected
  470. of stateExpectValue:
  471. case tk
  472. of tkString, tkInt, tkFloat, tkTrue, tkFalse, tkNull:
  473. my.state[i] = stateExpectObjectComma
  474. my.kind = JsonEventKind(ord(tk))
  475. of tkBracketLe:
  476. my.state[i] = stateExpectObjectComma
  477. my.state.add(stateArray)
  478. my.kind = jsonArrayStart
  479. of tkCurlyLe:
  480. my.state[i] = stateExpectObjectComma
  481. my.state.add(stateObject)
  482. my.kind = jsonObjectStart
  483. else:
  484. my.kind = jsonError
  485. my.err = errExprExpected
  486. proc raiseParseErr*(p: JsonParser, msg: string) {.noinline, noreturn.} =
  487. ## raises an `EJsonParsingError` exception.
  488. raise newException(JsonParsingError, errorMsgExpected(p, msg))
  489. proc eat*(p: var JsonParser, tok: TokKind) =
  490. if p.tok == tok: discard getTok(p)
  491. else: raiseParseErr(p, tokToStr[tok])