json.body.scm 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536
  1. ;; json.scm - JSON reader and writer
  2. ;; License: Expat (MIT)
  3. ;; Homepage: https://notabug.org/pangolinturtle/json-r7rs
  4. ;; Copyright (c) 2011-2014 by Marc Feeley, All Rights Reserved.
  5. ;; Copyright (c) 2015 by Jason K. MacDuffie
  6. ;;;
  7. ;;;; --
  8. ;;;; json-null is implemented as a record type
  9. ;;;
  10. (define-record-type json-null-type
  11. (make-json-null)
  12. json-null?)
  13. (define json-null
  14. (let ((result (make-json-null)))
  15. (lambda () result)))
  16. ;;;
  17. ;;;; --
  18. ;;;; JSON reader
  19. ;;;
  20. (define (json-read . port-option)
  21. (define port (if (null? port-option)
  22. (current-input-port)
  23. (if (null? (cdr port-option))
  24. (car port-option)
  25. (error "json-read" "Too many arguments"))))
  26. (define (rd)
  27. (read-char port))
  28. (define (pk)
  29. (peek-char port))
  30. (define (accum c i str)
  31. (string-set! str i c)
  32. str)
  33. (define (digit? c radix)
  34. (and (char? c)
  35. (let ((n
  36. (cond ((and (char>=? c #\0) (char<=? c #\9))
  37. (- (char->integer c) (char->integer #\0)))
  38. ((and (char>=? c #\a) (char<=? c #\z))
  39. (+ 10 (- (char->integer c) (char->integer #\a))))
  40. ((and (char>=? c #\A) (char<=? c #\Z))
  41. (+ 10 (- (char->integer c) (char->integer #\A))))
  42. (else
  43. 999))))
  44. (and (< n radix)
  45. n))))
  46. (define (space)
  47. (let ((c (pk)))
  48. (if (and (char? c)
  49. (char<=? c #\space))
  50. (begin (rd) (space)))))
  51. (define (parse-value)
  52. (space)
  53. (let ((c (pk)))
  54. (if (not (char? c))
  55. (error "parse-value" "EOF while parsing")
  56. (cond ((eqv? c #\{)
  57. (parse-object))
  58. ((eqv? c #\[)
  59. (parse-array))
  60. ((eqv? c #\")
  61. (parse-string))
  62. ((or (eqv? c #\-) (digit? c 10))
  63. (parse-number))
  64. ((eqv? c #\f)
  65. (rd)
  66. (if (not (and (eqv? (rd) #\a)
  67. (eqv? (rd) #\l)
  68. (eqv? (rd) #\s)
  69. (eqv? (rd) #\e)))
  70. (error "parse-value" "Invalid literal")
  71. #f))
  72. ((eqv? c #\t)
  73. (rd)
  74. (if (not (and (eqv? (rd) #\r)
  75. (eqv? (rd) #\u)
  76. (eqv? (rd) #\e)))
  77. (error "parse-value" "Invalid literal")
  78. #t))
  79. ((eqv? c #\n)
  80. (rd)
  81. (if (not (and (eqv? (rd) #\u)
  82. (eqv? (rd) #\l)
  83. (eqv? (rd) #\l)))
  84. (error "parse-value" "Invalid literal")
  85. (json-null)))
  86. (else
  87. (error "parse-value" "JSON could not be decoded"))))))
  88. (define (parse-object)
  89. (rd) ;; skip #\{
  90. (space)
  91. (if (eqv? (pk) #\})
  92. (begin (rd) '())
  93. (let loop ((rev-elements '()))
  94. (let ((str (if (not (eqv? (pk) #\"))
  95. (error "parse-object" "Key did not begin with quote")
  96. (parse-string))))
  97. (begin
  98. (space)
  99. (if (not (eqv? (pk) #\:))
  100. (error "parse-object" "Key not followed by a colon")
  101. (begin
  102. (rd)
  103. (space)
  104. (let ((val (parse-value)))
  105. (let ((new-rev-elements
  106. (cons (cons (string->symbol str) val) rev-elements)))
  107. (space)
  108. (let ((c (pk)))
  109. (cond ((eqv? c #\})
  110. (rd)
  111. (reverse new-rev-elements))
  112. ((eqv? c #\,)
  113. (rd)
  114. (space)
  115. (loop new-rev-elements))
  116. (else
  117. (error "Invalid character in JSON object")))))))))))))
  118. (define (parse-array)
  119. (rd) ;; skip #\[
  120. (space)
  121. (if (eqv? (pk) #\])
  122. (begin (rd) #())
  123. (let ((x (parse-value)))
  124. (let loop ((rev-elements (list x)))
  125. (space)
  126. (let ((c (pk)))
  127. (cond ((eqv? c #\])
  128. (rd)
  129. (list->vector (reverse rev-elements)))
  130. ((eqv? c #\,)
  131. (rd)
  132. (let ((y (parse-value)))
  133. (loop (cons y rev-elements))))
  134. (else
  135. (error "Invalid character in JSON array"))))))))
  136. (define (parse-string)
  137. (define (parse-str pos)
  138. (let ((c (rd)))
  139. (cond ((eqv? c #\")
  140. (make-string pos))
  141. ((eqv? c #\\)
  142. (let ((x (rd)))
  143. (if (eqv? x #\u)
  144. (let loop ((n 0) (i 4))
  145. (if (> i 0)
  146. (let ((h (rd)))
  147. (cond ((not (char? h))
  148. (error "parse-string" "EOF while reading string"))
  149. ((digit? h 16)
  150. =>
  151. (lambda (d)
  152. (loop (+ (* n 16) d) (- i 1))))
  153. (else
  154. (error "parse-string" "Invalid Unicode escape"))))
  155. (accum (integer->char n) pos (parse-str (+ pos 1)))))
  156. (let ((e (assv x json-string-escapes)))
  157. (if e
  158. (accum (cdr e) pos (parse-str (+ pos 1)))
  159. (error "parse-string" "Unrecognized escape character"))))))
  160. ((char? c)
  161. (accum c pos (parse-str (+ pos 1))))
  162. (else
  163. (error "parse-string" "EOF while reading string")))))
  164. (rd) ;; skip #\"
  165. (parse-str 0))
  166. (define (parse-number)
  167. (define (sign-part)
  168. (let ((c (pk)))
  169. (if (eqv? c #\-)
  170. (begin (rd) (accum c 0 (after-sign-part 1)))
  171. (after-sign-part 0))))
  172. (define (after-sign-part pos)
  173. (let ((c (pk)))
  174. (if (eqv? c #\0)
  175. (begin (rd) (accum c pos (after-zero-part (+ pos 1))))
  176. (after-first-digit pos))))
  177. (define (after-zero-part pos)
  178. (let ((c (pk)))
  179. (if (eqv? c #\.)
  180. (begin (rd) (accum c pos (decimals-part (+ pos 1))))
  181. (if (or (eqv? c #\e) (eqv? c #\E))
  182. (begin (rd) (accum c pos (exponent-sign-part (+ pos 1))))
  183. (done pos)))))
  184. (define (after-first-digit pos)
  185. (if (not (digit? (pk) 10))
  186. (error "parse-number" "Non-digit following a sign")
  187. (integer-part pos)))
  188. (define (integer-part pos)
  189. (let ((c (pk)))
  190. (if (digit? c 10)
  191. (begin (rd) (accum c pos (integer-part (+ pos 1))))
  192. (if (eqv? c #\.)
  193. (begin (rd) (accum c pos (decimals-part (+ pos 1))))
  194. (exponent-part pos)))))
  195. (define (decimals-part pos)
  196. (let ((c (pk)))
  197. (if (digit? c 10)
  198. (begin (rd) (accum c pos (after-first-decimal-digit (+ pos 1))))
  199. (error "parse-number" "Non-digit following a decimal point"))))
  200. (define (after-first-decimal-digit pos)
  201. (let ((c (pk)))
  202. (if (digit? c 10)
  203. (begin (rd) (accum c pos (after-first-decimal-digit (+ pos 1))))
  204. (exponent-part pos))))
  205. (define (exponent-part pos)
  206. (let ((c (pk)))
  207. (if (or (eqv? c #\e) (eqv? c #\E))
  208. (begin (rd) (accum c pos (exponent-sign-part (+ pos 1))))
  209. (done pos))))
  210. (define (exponent-sign-part pos)
  211. (let ((c (pk)))
  212. (if (or (eqv? c #\-) (eqv? c #\+))
  213. (begin (rd) (accum c pos (exponent-after-sign-part (+ pos 1))))
  214. (exponent-after-sign-part pos))))
  215. (define (exponent-after-sign-part pos)
  216. (if (not (digit? (pk) 10))
  217. (error "parse-number" "Non-digit following an exponent mark")
  218. (exponent-integer-part pos)))
  219. (define (exponent-integer-part pos)
  220. (let ((c (pk)))
  221. (if (digit? c 10)
  222. (begin (rd) (accum c pos (exponent-integer-part (+ pos 1))))
  223. (done pos))))
  224. (define (done pos)
  225. (make-string pos))
  226. (let ((str (sign-part)))
  227. (string->number str)))
  228. (let ((value (parse-value)))
  229. (let loop ((next-char (read-char port)))
  230. (if (eof-object? next-char)
  231. value
  232. (if (member next-char '(#\space #\newline #\tab #\return))
  233. (loop (read-char port))
  234. (error "json-read" "Extra data"))))))
  235. ;;;
  236. ;;;; --
  237. ;;;; JSON writer
  238. ;;;
  239. (define (json-write obj . port-option)
  240. (define port (if (null? port-option)
  241. (current-output-port)
  242. (if (null? (cdr port-option))
  243. (car port-option)
  244. (error "json-read" "Too many arguments"))))
  245. (define (wr-string s)
  246. (display #\" port)
  247. (let loop ((i 0) (j 0))
  248. (if (< j (string-length s))
  249. (let* ((c
  250. (string-ref s j))
  251. (n
  252. (char->integer c))
  253. (ctrl-char?
  254. (or (<= n 31) (>= n 127)))
  255. (x
  256. (cond ((or (char=? c #\\)
  257. (char=? c #\"))
  258. c)
  259. ((and ctrl-char?
  260. (assv c reverse-json-string-escapes))
  261. =>
  262. cdr)
  263. (else
  264. #f)))
  265. (j+1
  266. (+ j 1)))
  267. (if (or x ctrl-char?)
  268. (begin
  269. (display (substring s i j) port)
  270. (display #\\ port)
  271. (if x
  272. (begin
  273. (display x port)
  274. (loop j+1 j+1))
  275. (begin
  276. (display #\u port)
  277. (display (substring (number->string (+ n #x10000) 16)
  278. 1
  279. 5)
  280. port)
  281. (loop j+1 j+1))))
  282. (loop i j+1)))
  283. (begin
  284. (display (substring s i j) port)
  285. (display #\" port)))))
  286. (define (wr-prop prop)
  287. (wr (symbol->string (car prop)))
  288. (display ":" port)
  289. (wr (cdr prop)))
  290. (define (wr-object obj)
  291. (wr-props obj))
  292. (define (wr-props lst)
  293. (display "{" port)
  294. (if (pair? lst)
  295. (begin
  296. (wr-prop (car lst))
  297. (let loop ((lst (cdr lst)))
  298. (if (pair? lst)
  299. (begin
  300. (display "," port)
  301. (wr-prop (car lst))
  302. (loop (cdr lst)))))))
  303. (display "}" port))
  304. (define (wr-array obj)
  305. (display "[" port)
  306. (let loop ((not-first #f) (l (vector->list obj)))
  307. (if (not (null? l))
  308. (begin
  309. (if not-first (display "," port))
  310. (wr (car l))
  311. (loop #t (cdr l)))))
  312. (display "]" port))
  313. (define (wr obj)
  314. (cond ((number? obj)
  315. (write (if (integer? obj) obj (inexact obj)) port))
  316. ((string? obj)
  317. (wr-string obj))
  318. ((boolean? obj)
  319. (display (if obj "true" "false") port))
  320. ((json-null? obj)
  321. (display "null" port))
  322. ((vector? obj)
  323. (wr-array obj))
  324. ((list? obj)
  325. (wr-object obj))
  326. (else
  327. (error "unwritable object" obj))))
  328. (wr obj))
  329. (define json-string-escapes
  330. '((#\" . #\")
  331. (#\\ . #\\)
  332. (#\/ . #\/)
  333. (#\b . #\x08)
  334. (#\t . #\x09)
  335. (#\n . #\x0A)
  336. (#\v . #\x0B)
  337. (#\f . #\x0C)
  338. (#\r . #\x0D)))
  339. (define reverse-json-string-escapes
  340. (map (lambda (x)
  341. (cons (cdr x) (car x)))
  342. json-string-escapes))
  343. ;;;
  344. ;;;; --
  345. ;;;; Procedures for reading/writing for strings/files
  346. ;;;
  347. (define (json-read-string s)
  348. (define p (open-input-string s))
  349. (let ((result (json-read p)))
  350. (close-input-port p)
  351. result))
  352. (define (json-read-file filepath)
  353. (define p (open-input-file filepath))
  354. (let ((result (json-read p)))
  355. (close-input-port p)
  356. result))
  357. (define (json-write-string value . prettify-options)
  358. (define prettify (if (null? prettify-options)
  359. #f
  360. (car prettify-options)))
  361. (define space-char (if (< (length prettify-options) 2)
  362. #\tab
  363. (list-ref prettify-options 1)))
  364. (define space-count (if (< (length prettify-options) 3)
  365. 1
  366. (list-ref prettify-options 2)))
  367. (define p (open-output-string))
  368. (json-write value p)
  369. (let ((result (get-output-string p)))
  370. (close-output-port p)
  371. (if prettify
  372. (json-prettify result space-char space-count)
  373. result)))
  374. (define (json-write-file value filepath . prettify-options)
  375. (define prettify (if (null? prettify-options)
  376. #f
  377. (car prettify-options)))
  378. (define p (open-output-file filepath))
  379. (if prettify
  380. (display (apply json-write-string (cons value prettify-options)) p)
  381. (json-write value p))
  382. (close-output-port p))
  383. ;;;
  384. ;;;; --
  385. ;;;; Prettify procedure
  386. ;;;
  387. (define (json-prettify str space-char space-count)
  388. (define (add-spaces l level)
  389. (let loop ((i 0) (result l))
  390. (if (< i (* level space-count))
  391. (loop (+ i 1) (cons space-char result))
  392. result)))
  393. (define (is-empty slist char-look)
  394. (let loop ((l slist))
  395. (if (null? slist)
  396. #f
  397. (case (car l)
  398. ((#\])
  399. (if (equal? char-look #\[)
  400. (cdr l)
  401. #f))
  402. ((#\})
  403. (if (equal? char-look #\{)
  404. (cdr l)
  405. #f))
  406. ((#\space #\newline #\tab #\return)
  407. (loop (cdr l)))
  408. (else #f)))))
  409. (let loop ((l (string->list str))
  410. (level 0)
  411. (slist '())
  412. (in-string #f))
  413. (cond
  414. ((null? l)
  415. (list->string (reverse (cons #\newline
  416. slist))))
  417. ((equal? (car l) #\")
  418. (loop (cdr l)
  419. level
  420. (cons (car l) slist)
  421. (if (and (not (null? slist)) (equal? (car slist) #\\))
  422. in-string
  423. (not in-string))))
  424. (in-string
  425. (loop (cdr l) level (cons (car l) slist) #t))
  426. (else
  427. (case (car l)
  428. ((#\[ #\{)
  429. (if (is-empty (cdr l) (car l))
  430. (loop (is-empty (cdr l) (car l))
  431. level
  432. (cons (if (equal? (car l)
  433. #\[)
  434. #\]
  435. #\})
  436. (cons (car l)
  437. slist))
  438. #f)
  439. (loop (cdr l)
  440. (+ level 1)
  441. (add-spaces (cons #\newline
  442. (cons (car l)
  443. slist))
  444. (+ level 1))
  445. #f)))
  446. ((#\] #\})
  447. (loop (cdr l)
  448. (- level 1)
  449. (cons (car l)
  450. (add-spaces (cons #\newline
  451. slist)
  452. (- level 1)))
  453. #f))
  454. ((#\,)
  455. (loop (cdr l)
  456. level
  457. (add-spaces (cons #\newline
  458. (cons (car l)
  459. slist))
  460. level)
  461. #f))
  462. ((#\:)
  463. (loop (cdr l)
  464. level
  465. (cons #\space
  466. (cons (car l)
  467. slist))
  468. #f))
  469. ((#\space #\newline #\tab #\return)
  470. (loop (cdr l) level slist #f))
  471. (else
  472. (loop (cdr l)
  473. level
  474. (cons (car l) slist)
  475. #f)))))))