kibi-swipl-linux-x64-gnu 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (550) hide show
  1. package/README.md +29 -0
  2. package/bin/swipl +0 -0
  3. package/bin/swipl-win +0 -0
  4. package/build-manifest.json +70 -0
  5. package/lib/cmake/swipl/SWIPLConfig.cmake +21 -0
  6. package/lib/cmake/swipl/SWIPLConfigVersion.cmake +65 -0
  7. package/lib/cmake/swipl/SWIPLTargets-release.cmake +28 -0
  8. package/lib/cmake/swipl/SWIPLTargets.cmake +109 -0
  9. package/lib/swipl/ABI +1 -0
  10. package/lib/swipl/LICENSE +41 -0
  11. package/lib/swipl/README.md +116 -0
  12. package/lib/swipl/app/README.md +25 -0
  13. package/lib/swipl/app/app.pl +87 -0
  14. package/lib/swipl/app/pack.pl +350 -0
  15. package/lib/swipl/app/qlf.pl +521 -0
  16. package/lib/swipl/app/splfr.pl +177 -0
  17. package/lib/swipl/app/sys.pl +310 -0
  18. package/lib/swipl/bin/swipl.home +1 -0
  19. package/lib/swipl/boot/apply.pl +73 -0
  20. package/lib/swipl/boot/attvar.pl +344 -0
  21. package/lib/swipl/boot/autoload.pl +1226 -0
  22. package/lib/swipl/boot/bags.pl +283 -0
  23. package/lib/swipl/boot/dcg.pl +388 -0
  24. package/lib/swipl/boot/dicts.pl +193 -0
  25. package/lib/swipl/boot/dwim.pl +485 -0
  26. package/lib/swipl/boot/engines.pl +152 -0
  27. package/lib/swipl/boot/expand.pl +1628 -0
  28. package/lib/swipl/boot/gc.pl +65 -0
  29. package/lib/swipl/boot/history.pl +304 -0
  30. package/lib/swipl/boot/init.pl +4682 -0
  31. package/lib/swipl/boot/iri.pl +64 -0
  32. package/lib/swipl/boot/license.pl +390 -0
  33. package/lib/swipl/boot/load.pl +81 -0
  34. package/lib/swipl/boot/messages.pl +2521 -0
  35. package/lib/swipl/boot/packs.pl +306 -0
  36. package/lib/swipl/boot/predopts.pl +154 -0
  37. package/lib/swipl/boot/qlf.pl +141 -0
  38. package/lib/swipl/boot/rc.pl +263 -0
  39. package/lib/swipl/boot/syspred.pl +1634 -0
  40. package/lib/swipl/boot/tabling.pl +2321 -0
  41. package/lib/swipl/boot/threads.pl +42 -0
  42. package/lib/swipl/boot/toplevel.pl +2248 -0
  43. package/lib/swipl/boot/topvars.pl +261 -0
  44. package/lib/swipl/boot.prc +0 -0
  45. package/lib/swipl/cmake/swipl.cmake +131 -0
  46. package/lib/swipl/customize/README.md +23 -0
  47. package/lib/swipl/customize/edit +101 -0
  48. package/lib/swipl/customize/init.pl +164 -0
  49. package/lib/swipl/demo/README.md +33 -0
  50. package/lib/swipl/demo/likes.pl +44 -0
  51. package/lib/swipl/doc/packages/examples/chr/bool.chr +281 -0
  52. package/lib/swipl/doc/packages/examples/chr/chrdif.chr +87 -0
  53. package/lib/swipl/doc/packages/examples/chr/chrfreeze.chr +6 -0
  54. package/lib/swipl/doc/packages/examples/chr/family.chr +116 -0
  55. package/lib/swipl/doc/packages/examples/chr/fib.chr +24 -0
  56. package/lib/swipl/doc/packages/examples/chr/fibonacci.chr +31 -0
  57. package/lib/swipl/doc/packages/examples/chr/gcd.chr +28 -0
  58. package/lib/swipl/doc/packages/examples/chr/leq.chr +34 -0
  59. package/lib/swipl/doc/packages/examples/chr/listdom.chr +138 -0
  60. package/lib/swipl/doc/packages/examples/chr/primes.chr +29 -0
  61. package/lib/swipl/doc/packages/examples/http/README.md +98 -0
  62. package/lib/swipl/doc/packages/examples/http/calc.pl +92 -0
  63. package/lib/swipl/doc/packages/examples/http/demo_body.pl +232 -0
  64. package/lib/swipl/doc/packages/examples/http/demo_client.pl +165 -0
  65. package/lib/swipl/doc/packages/examples/http/demo_daemon.pl +38 -0
  66. package/lib/swipl/doc/packages/examples/http/demo_files.pl +50 -0
  67. package/lib/swipl/doc/packages/examples/http/demo_hello.pl +15 -0
  68. package/lib/swipl/doc/packages/examples/http/demo_login.pl +280 -0
  69. package/lib/swipl/doc/packages/examples/http/demo_openid.pl +177 -0
  70. package/lib/swipl/doc/packages/examples/http/demo_pwp.pl +20 -0
  71. package/lib/swipl/doc/packages/examples/http/demo_rest.pl +56 -0
  72. package/lib/swipl/doc/packages/examples/http/demo_threads.pl +52 -0
  73. package/lib/swipl/doc/packages/examples/http/linux-init-script +126 -0
  74. package/lib/swipl/doc/packages/examples/http/pwp/context.pwp +23 -0
  75. package/lib/swipl/doc/packages/examples/http/pwp/index.pwp +25 -0
  76. package/lib/swipl/doc/packages/examples/http/pwp/pwp1.pwp +12 -0
  77. package/lib/swipl/doc/packages/examples/http/pwp/pwp2.pwp +10 -0
  78. package/lib/swipl/doc/packages/examples/http/pwp/pwp3.pwp +25 -0
  79. package/lib/swipl/doc/packages/examples/http/pwp/pwp4.pwp +23 -0
  80. package/lib/swipl/doc/packages/examples/http/pwp/pwp5.pwp +11 -0
  81. package/lib/swipl/doc/packages/examples/http/pwp/pwp6.pwp +3 -0
  82. package/lib/swipl/doc/packages/examples/http/pwp/pwp7.pwp +3 -0
  83. package/lib/swipl/doc/packages/examples/http/pwp/pwp8.pwp +3 -0
  84. package/lib/swipl/doc/packages/examples/http/pwp/pwpdb.pl +12 -0
  85. package/lib/swipl/doc/packages/examples/http/systemd-script.service +18 -0
  86. package/lib/swipl/doc/packages/examples/http/upstart-script.conf +20 -0
  87. package/lib/swipl/doc/packages/examples/plunit/read.pl +17 -0
  88. package/lib/swipl/doc/packages/examples/plunit/simple.pl +33 -0
  89. package/lib/swipl/doc/packages/examples/ssl/client.pl +83 -0
  90. package/lib/swipl/doc/packages/examples/ssl/etc/README +52 -0
  91. package/lib/swipl/doc/packages/examples/ssl/etc/client/client-cert.p12 +0 -0
  92. package/lib/swipl/doc/packages/examples/ssl/etc/client/client-cert.pem +85 -0
  93. package/lib/swipl/doc/packages/examples/ssl/etc/client/client-key.pem +30 -0
  94. package/lib/swipl/doc/packages/examples/ssl/etc/demoCA/cacert.pem +82 -0
  95. package/lib/swipl/doc/packages/examples/ssl/etc/demoCA/careq.pem +18 -0
  96. package/lib/swipl/doc/packages/examples/ssl/etc/demoCA/crlnumber +1 -0
  97. package/lib/swipl/doc/packages/examples/ssl/etc/demoCA/index.txt +3 -0
  98. package/lib/swipl/doc/packages/examples/ssl/etc/demoCA/index.txt.attr +1 -0
  99. package/lib/swipl/doc/packages/examples/ssl/etc/demoCA/newcerts/9DC08797A0F63F7C.pem +85 -0
  100. package/lib/swipl/doc/packages/examples/ssl/etc/demoCA/newcerts/9DC08797A0F63F7D.pem +85 -0
  101. package/lib/swipl/doc/packages/examples/ssl/etc/demoCA/private/cakey.pem +30 -0
  102. package/lib/swipl/doc/packages/examples/ssl/etc/demoCA/serial +1 -0
  103. package/lib/swipl/doc/packages/examples/ssl/etc/server/server-cert.pem +85 -0
  104. package/lib/swipl/doc/packages/examples/ssl/etc/server/server-key.pem +30 -0
  105. package/lib/swipl/doc/packages/examples/ssl/https.pl +267 -0
  106. package/lib/swipl/doc/packages/examples/ssl/server.pl +106 -0
  107. package/lib/swipl/include/SWI-Prolog.h +1544 -0
  108. package/lib/swipl/include/SWI-Stream.h +537 -0
  109. package/lib/swipl/include/Yap/YapInterface.h +283 -0
  110. package/lib/swipl/include/sicstus/sicstus.h +452 -0
  111. package/lib/swipl/lib/x86_64-linux/cgi.so +0 -0
  112. package/lib/swipl/lib/x86_64-linux/crypt.so +0 -0
  113. package/lib/swipl/lib/x86_64-linux/crypto4pl.so +0 -0
  114. package/lib/swipl/lib/x86_64-linux/double_metaphone.so +0 -0
  115. package/lib/swipl/lib/x86_64-linux/files.so +0 -0
  116. package/lib/swipl/lib/x86_64-linux/hashstream.so +0 -0
  117. package/lib/swipl/lib/x86_64-linux/http_stream.so +0 -0
  118. package/lib/swipl/lib/x86_64-linux/isub.so +0 -0
  119. package/lib/swipl/lib/x86_64-linux/json.so +0 -0
  120. package/lib/swipl/lib/x86_64-linux/libswipl.so.10 +0 -0
  121. package/lib/swipl/lib/x86_64-linux/mallocinfo.so +0 -0
  122. package/lib/swipl/lib/x86_64-linux/md54pl.so +0 -0
  123. package/lib/swipl/lib/x86_64-linux/memfile.so +0 -0
  124. package/lib/swipl/lib/x86_64-linux/ntriples.so +0 -0
  125. package/lib/swipl/lib/x86_64-linux/pcre4pl.so +0 -0
  126. package/lib/swipl/lib/x86_64-linux/porter_stem.so +0 -0
  127. package/lib/swipl/lib/x86_64-linux/process.so +0 -0
  128. package/lib/swipl/lib/x86_64-linux/prolog_stream.so +0 -0
  129. package/lib/swipl/lib/x86_64-linux/rdf_db.so +0 -0
  130. package/lib/swipl/lib/x86_64-linux/readutil.so +0 -0
  131. package/lib/swipl/lib/x86_64-linux/rlimit.so +0 -0
  132. package/lib/swipl/lib/x86_64-linux/sched.so +0 -0
  133. package/lib/swipl/lib/x86_64-linux/sgml2pl.so +0 -0
  134. package/lib/swipl/lib/x86_64-linux/sha4pl.so +0 -0
  135. package/lib/swipl/lib/x86_64-linux/snowball.so +0 -0
  136. package/lib/swipl/lib/x86_64-linux/socket.so +0 -0
  137. package/lib/swipl/lib/x86_64-linux/ssl4pl.so +0 -0
  138. package/lib/swipl/lib/x86_64-linux/streaminfo.so +0 -0
  139. package/lib/swipl/lib/x86_64-linux/syslog.so +0 -0
  140. package/lib/swipl/lib/x86_64-linux/time.so +0 -0
  141. package/lib/swipl/lib/x86_64-linux/turtle.so +0 -0
  142. package/lib/swipl/lib/x86_64-linux/uid.so +0 -0
  143. package/lib/swipl/lib/x86_64-linux/unix.so +0 -0
  144. package/lib/swipl/lib/x86_64-linux/uri.so +0 -0
  145. package/lib/swipl/lib/x86_64-linux/websocket.so +0 -0
  146. package/lib/swipl/lib/x86_64-linux/zlib4pl.so +0 -0
  147. package/lib/swipl/library/INDEX.pl +1020 -0
  148. package/lib/swipl/library/aggregate.pl +755 -0
  149. package/lib/swipl/library/ansi_term.pl +653 -0
  150. package/lib/swipl/library/apply.pl +426 -0
  151. package/lib/swipl/library/apply_macros.pl +497 -0
  152. package/lib/swipl/library/arithmetic.pl +320 -0
  153. package/lib/swipl/library/assoc.pl +522 -0
  154. package/lib/swipl/library/atom.pl +169 -0
  155. package/lib/swipl/library/backcomp.pl +688 -0
  156. package/lib/swipl/library/base32.pl +291 -0
  157. package/lib/swipl/library/base64.pl +441 -0
  158. package/lib/swipl/library/broadcast.pl +161 -0
  159. package/lib/swipl/library/build/cmake.pl +137 -0
  160. package/lib/swipl/library/build/conan.pl +232 -0
  161. package/lib/swipl/library/build/make.pl +168 -0
  162. package/lib/swipl/library/build/tools.pl +825 -0
  163. package/lib/swipl/library/charsio.pl +195 -0
  164. package/lib/swipl/library/check.pl +1097 -0
  165. package/lib/swipl/library/check_installation.pl +912 -0
  166. package/lib/swipl/library/clp/INDEX.pl +157 -0
  167. package/lib/swipl/library/clp/bounds.pl +2036 -0
  168. package/lib/swipl/library/clp/clp_distinct.pl +263 -0
  169. package/lib/swipl/library/clp/clp_events.pl +92 -0
  170. package/lib/swipl/library/clp/clpb.pl +1896 -0
  171. package/lib/swipl/library/clp/clpfd.pl +7906 -0
  172. package/lib/swipl/library/clp/simplex.pl +1580 -0
  173. package/lib/swipl/library/codesio.pl +182 -0
  174. package/lib/swipl/library/coinduction.pl +183 -0
  175. package/lib/swipl/library/console_input.pl +289 -0
  176. package/lib/swipl/library/csv.pl +611 -0
  177. package/lib/swipl/library/ctypes.pl +161 -0
  178. package/lib/swipl/library/date.pl +370 -0
  179. package/lib/swipl/library/dcg/INDEX.pl +35 -0
  180. package/lib/swipl/library/dcg/basics.pl +514 -0
  181. package/lib/swipl/library/dcg/high_order.pl +227 -0
  182. package/lib/swipl/library/debug.pl +499 -0
  183. package/lib/swipl/library/dialect/bim.pl +186 -0
  184. package/lib/swipl/library/dialect/commons.pl +77 -0
  185. package/lib/swipl/library/dialect/eclipse/test_util_iso.pl +355 -0
  186. package/lib/swipl/library/dialect/hprolog/format.pl +44 -0
  187. package/lib/swipl/library/dialect/hprolog.pl +314 -0
  188. package/lib/swipl/library/dialect/ifprolog.pl +1317 -0
  189. package/lib/swipl/library/dialect/iso/iso_predicates.pl +277 -0
  190. package/lib/swipl/library/dialect/sicstus/README.TXT +1 -0
  191. package/lib/swipl/library/dialect/sicstus/arrays.pl +121 -0
  192. package/lib/swipl/library/dialect/sicstus/block.pl +278 -0
  193. package/lib/swipl/library/dialect/sicstus/lists.pl +167 -0
  194. package/lib/swipl/library/dialect/sicstus/ordsets.pl +52 -0
  195. package/lib/swipl/library/dialect/sicstus/sockets.pl +174 -0
  196. package/lib/swipl/library/dialect/sicstus/system.pl +262 -0
  197. package/lib/swipl/library/dialect/sicstus/terms.pl +57 -0
  198. package/lib/swipl/library/dialect/sicstus/timeout.pl +92 -0
  199. package/lib/swipl/library/dialect/sicstus.pl +582 -0
  200. package/lib/swipl/library/dialect/sicstus4/aggregate.pl +60 -0
  201. package/lib/swipl/library/dialect/sicstus4/between.pl +62 -0
  202. package/lib/swipl/library/dialect/sicstus4/clpfd.pl +75 -0
  203. package/lib/swipl/library/dialect/sicstus4/file_systems.pl +413 -0
  204. package/lib/swipl/library/dialect/sicstus4/lists.pl +274 -0
  205. package/lib/swipl/library/dialect/sicstus4/ordsets.pl +54 -0
  206. package/lib/swipl/library/dialect/sicstus4/samsort.pl +86 -0
  207. package/lib/swipl/library/dialect/sicstus4/sets.pl +124 -0
  208. package/lib/swipl/library/dialect/sicstus4/sockets.pl +164 -0
  209. package/lib/swipl/library/dialect/sicstus4/system.pl +80 -0
  210. package/lib/swipl/library/dialect/sicstus4/terms.pl +89 -0
  211. package/lib/swipl/library/dialect/sicstus4/timeout.pl +43 -0
  212. package/lib/swipl/library/dialect/sicstus4/types.pl +103 -0
  213. package/lib/swipl/library/dialect/sicstus4.pl +200 -0
  214. package/lib/swipl/library/dialect/swi/syspred_options.pl +250 -0
  215. package/lib/swipl/library/dialect/xsb/README.md +20 -0
  216. package/lib/swipl/library/dialect/xsb/basics.pl +318 -0
  217. package/lib/swipl/library/dialect/xsb/consult.pl +87 -0
  218. package/lib/swipl/library/dialect/xsb/curr_sym.pl +57 -0
  219. package/lib/swipl/library/dialect/xsb/error_handler.pl +111 -0
  220. package/lib/swipl/library/dialect/xsb/gensym.pl +53 -0
  221. package/lib/swipl/library/dialect/xsb/gpp.pl +138 -0
  222. package/lib/swipl/library/dialect/xsb/intern.pl +48 -0
  223. package/lib/swipl/library/dialect/xsb/lists.pl +47 -0
  224. package/lib/swipl/library/dialect/xsb/machine.pl +295 -0
  225. package/lib/swipl/library/dialect/xsb/ordsets.pl +53 -0
  226. package/lib/swipl/library/dialect/xsb/setof.pl +54 -0
  227. package/lib/swipl/library/dialect/xsb/source.pl +308 -0
  228. package/lib/swipl/library/dialect/xsb/standard.pl +169 -0
  229. package/lib/swipl/library/dialect/xsb/storage.pl +70 -0
  230. package/lib/swipl/library/dialect/xsb/string.pl +76 -0
  231. package/lib/swipl/library/dialect/xsb/thread.pl +62 -0
  232. package/lib/swipl/library/dialect/xsb/timed_call.pl +158 -0
  233. package/lib/swipl/library/dialect/xsb.pl +688 -0
  234. package/lib/swipl/library/dialect/yap/README.TXT +8 -0
  235. package/lib/swipl/library/dialect/yap.pl +283 -0
  236. package/lib/swipl/library/dialect.pl +123 -0
  237. package/lib/swipl/library/dicts.pl +399 -0
  238. package/lib/swipl/library/dif.pl +426 -0
  239. package/lib/swipl/library/edinburgh.pl +138 -0
  240. package/lib/swipl/library/edit.pl +783 -0
  241. package/lib/swipl/library/error.pl +495 -0
  242. package/lib/swipl/library/exceptions.pl +231 -0
  243. package/lib/swipl/library/explain.pl +543 -0
  244. package/lib/swipl/library/ext/RDF/INDEX.pl +25 -0
  245. package/lib/swipl/library/ext/RDF/rdf.pl +450 -0
  246. package/lib/swipl/library/ext/RDF/rdf_parser.pl +649 -0
  247. package/lib/swipl/library/ext/RDF/rdf_triple.pl +448 -0
  248. package/lib/swipl/library/ext/RDF/rdf_write.pl +701 -0
  249. package/lib/swipl/library/ext/RDF/rewrite_term.pl +177 -0
  250. package/lib/swipl/library/ext/chr/chr/a_star.pl +80 -0
  251. package/lib/swipl/library/ext/chr/chr/binomialheap.pl +142 -0
  252. package/lib/swipl/library/ext/chr/chr/builtins.pl +629 -0
  253. package/lib/swipl/library/ext/chr/chr/chr_compiler_errors.pl +179 -0
  254. package/lib/swipl/library/ext/chr/chr/chr_compiler_options.pl +382 -0
  255. package/lib/swipl/library/ext/chr/chr/chr_compiler_utility.pl +338 -0
  256. package/lib/swipl/library/ext/chr/chr/chr_debug.pl +59 -0
  257. package/lib/swipl/library/ext/chr/chr/chr_hashtable_store.pl +425 -0
  258. package/lib/swipl/library/ext/chr/chr/chr_integertable_store.pl +139 -0
  259. package/lib/swipl/library/ext/chr/chr/chr_messages.pl +176 -0
  260. package/lib/swipl/library/ext/chr/chr/chr_op.pl +53 -0
  261. package/lib/swipl/library/ext/chr/chr/chr_runtime.pl +1016 -0
  262. package/lib/swipl/library/ext/chr/chr/chr_translate.pl +25424 -0
  263. package/lib/swipl/library/ext/chr/chr/clean_code.pl +252 -0
  264. package/lib/swipl/library/ext/chr/chr/find.pl +78 -0
  265. package/lib/swipl/library/ext/chr/chr/guard_entailment.pl +19432 -0
  266. package/lib/swipl/library/ext/chr/chr/listmap.pl +108 -0
  267. package/lib/swipl/library/ext/chr/chr/pairlist.pl +78 -0
  268. package/lib/swipl/library/ext/chr/chr.pl +686 -0
  269. package/lib/swipl/library/ext/clib/INDEX.pl +158 -0
  270. package/lib/swipl/library/ext/clib/cgi.pl +86 -0
  271. package/lib/swipl/library/ext/clib/crypt.pl +44 -0
  272. package/lib/swipl/library/ext/clib/filesex.pl +644 -0
  273. package/lib/swipl/library/ext/clib/hash_stream.pl +119 -0
  274. package/lib/swipl/library/ext/clib/mallocinfo.pl +138 -0
  275. package/lib/swipl/library/ext/clib/md5.pl +68 -0
  276. package/lib/swipl/library/ext/clib/memfile.pl +61 -0
  277. package/lib/swipl/library/ext/clib/process.pl +573 -0
  278. package/lib/swipl/library/ext/clib/prolog_server.pl +157 -0
  279. package/lib/swipl/library/ext/clib/prolog_stream.pl +80 -0
  280. package/lib/swipl/library/ext/clib/rlimit.pl +54 -0
  281. package/lib/swipl/library/ext/clib/sched.pl +85 -0
  282. package/lib/swipl/library/ext/clib/sha.pl +162 -0
  283. package/lib/swipl/library/ext/clib/socket.pl +1102 -0
  284. package/lib/swipl/library/ext/clib/streaminfo.pl +103 -0
  285. package/lib/swipl/library/ext/clib/streampool.pl +136 -0
  286. package/lib/swipl/library/ext/clib/syslog.pl +180 -0
  287. package/lib/swipl/library/ext/clib/time.pl +194 -0
  288. package/lib/swipl/library/ext/clib/udp_broadcast.pl +975 -0
  289. package/lib/swipl/library/ext/clib/uid.pl +220 -0
  290. package/lib/swipl/library/ext/clib/unix.pl +317 -0
  291. package/lib/swipl/library/ext/clib/uri.pl +530 -0
  292. package/lib/swipl/library/ext/clib/uuid.pl +207 -0
  293. package/lib/swipl/library/ext/http/http/INDEX.pl +266 -0
  294. package/lib/swipl/library/ext/http/http/README.md +34 -0
  295. package/lib/swipl/library/ext/http/http/ax.pl +189 -0
  296. package/lib/swipl/library/ext/http/http/dcg_basics.pl +49 -0
  297. package/lib/swipl/library/ext/http/http/graphql.pl +1367 -0
  298. package/lib/swipl/library/ext/http/http/html_decl.pl +416 -0
  299. package/lib/swipl/library/ext/http/http/html_head.pl +654 -0
  300. package/lib/swipl/library/ext/http/http/html_quasiquotations.pl +120 -0
  301. package/lib/swipl/library/ext/http/http/html_write.pl +1425 -0
  302. package/lib/swipl/library/ext/http/http/htmx.pl +139 -0
  303. package/lib/swipl/library/ext/http/http/http_authenticate.pl +364 -0
  304. package/lib/swipl/library/ext/http/http/http_client.pl +412 -0
  305. package/lib/swipl/library/ext/http/http/http_cookie.pl +245 -0
  306. package/lib/swipl/library/ext/http/http/http_cors.pl +176 -0
  307. package/lib/swipl/library/ext/http/http/http_digest.pl +820 -0
  308. package/lib/swipl/library/ext/http/http/http_dirindex.pl +296 -0
  309. package/lib/swipl/library/ext/http/http/http_dispatch.pl +1552 -0
  310. package/lib/swipl/library/ext/http/http/http_dyn_workers.pl +214 -0
  311. package/lib/swipl/library/ext/http/http/http_error.pl +126 -0
  312. package/lib/swipl/library/ext/http/http/http_exception.pl +225 -0
  313. package/lib/swipl/library/ext/http/http/http_files.pl +163 -0
  314. package/lib/swipl/library/ext/http/http/http_header.pl +2820 -0
  315. package/lib/swipl/library/ext/http/http/http_hook.pl +106 -0
  316. package/lib/swipl/library/ext/http/http/http_host.pl +204 -0
  317. package/lib/swipl/library/ext/http/http/http_load.pl +113 -0
  318. package/lib/swipl/library/ext/http/http/http_log.pl +738 -0
  319. package/lib/swipl/library/ext/http/http/http_multipart_plugin.pl +232 -0
  320. package/lib/swipl/library/ext/http/http/http_open.pl +1821 -0
  321. package/lib/swipl/library/ext/http/http/http_openid.pl +1561 -0
  322. package/lib/swipl/library/ext/http/http/http_parameters.pl +504 -0
  323. package/lib/swipl/library/ext/http/http/http_path.pl +380 -0
  324. package/lib/swipl/library/ext/http/http/http_proxy.pl +80 -0
  325. package/lib/swipl/library/ext/http/http/http_pwp.pl +351 -0
  326. package/lib/swipl/library/ext/http/http/http_redis_plugin.pl +332 -0
  327. package/lib/swipl/library/ext/http/http/http_server.pl +105 -0
  328. package/lib/swipl/library/ext/http/http/http_server_files.pl +126 -0
  329. package/lib/swipl/library/ext/http/http/http_server_health.pl +186 -0
  330. package/lib/swipl/library/ext/http/http/http_session.pl +1040 -0
  331. package/lib/swipl/library/ext/http/http/http_sgml_plugin.pl +137 -0
  332. package/lib/swipl/library/ext/http/http/http_stream.pl +376 -0
  333. package/lib/swipl/library/ext/http/http/http_unix_daemon.pl +989 -0
  334. package/lib/swipl/library/ext/http/http/http_wrapper.pl +559 -0
  335. package/lib/swipl/library/ext/http/http/hub.pl +600 -0
  336. package/lib/swipl/library/ext/http/http/js_write.pl +461 -0
  337. package/lib/swipl/library/ext/http/http/mimepack.pl +224 -0
  338. package/lib/swipl/library/ext/http/http/mimetype.pl +276 -0
  339. package/lib/swipl/library/ext/http/http/term_html.pl +797 -0
  340. package/lib/swipl/library/ext/http/http/thread_httpd.pl +1181 -0
  341. package/lib/swipl/library/ext/http/http/web/css/dirindex.css +24 -0
  342. package/lib/swipl/library/ext/http/http/web/css/openid.css +57 -0
  343. package/lib/swipl/library/ext/http/http/web/icons/back.png +0 -0
  344. package/lib/swipl/library/ext/http/http/web/icons/c.png +0 -0
  345. package/lib/swipl/library/ext/http/http/web/icons/compressed.png +0 -0
  346. package/lib/swipl/library/ext/http/http/web/icons/folder.png +0 -0
  347. package/lib/swipl/library/ext/http/http/web/icons/generic.png +0 -0
  348. package/lib/swipl/library/ext/http/http/web/icons/layout.png +0 -0
  349. package/lib/swipl/library/ext/http/http/web/icons/openid-logo-square.png +0 -0
  350. package/lib/swipl/library/ext/http/http/web/icons/openid-logo-tiny.png +0 -0
  351. package/lib/swipl/library/ext/http/http/websocket.pl +729 -0
  352. package/lib/swipl/library/ext/http/http/yadis.pl +205 -0
  353. package/lib/swipl/library/ext/json/INDEX.pl +36 -0
  354. package/lib/swipl/library/ext/json/http/INDEX.pl +16 -0
  355. package/lib/swipl/library/ext/json/http/http_json.pl +490 -0
  356. package/lib/swipl/library/ext/json/http/js_grammar.pl +53 -0
  357. package/lib/swipl/library/ext/json/http/json.pl +48 -0
  358. package/lib/swipl/library/ext/json/http/json_convert.pl +48 -0
  359. package/lib/swipl/library/ext/json/json.pl +1215 -0
  360. package/lib/swipl/library/ext/json/json_convert.pl +681 -0
  361. package/lib/swipl/library/ext/json/json_grammar.pl +349 -0
  362. package/lib/swipl/library/ext/json/json_rpc_client.pl +423 -0
  363. package/lib/swipl/library/ext/json/json_rpc_common.pl +104 -0
  364. package/lib/swipl/library/ext/json/json_rpc_server.pl +489 -0
  365. package/lib/swipl/library/ext/json/json_schema.pl +2111 -0
  366. package/lib/swipl/library/ext/nlp/INDEX.pl +15 -0
  367. package/lib/swipl/library/ext/nlp/double_metaphone.pl +86 -0
  368. package/lib/swipl/library/ext/nlp/isub.pl +162 -0
  369. package/lib/swipl/library/ext/nlp/porter_stem.pl +50 -0
  370. package/lib/swipl/library/ext/nlp/snowball.pl +97 -0
  371. package/lib/swipl/library/ext/pcre/INDEX.pl +17 -0
  372. package/lib/swipl/library/ext/pcre/pcre.pl +974 -0
  373. package/lib/swipl/library/ext/plunit/INDEX.pl +20 -0
  374. package/lib/swipl/library/ext/plunit/plunit.pl +2631 -0
  375. package/lib/swipl/library/ext/plunit/test_wizard.pl +249 -0
  376. package/lib/swipl/library/ext/semweb/semweb/INDEX.pl +288 -0
  377. package/lib/swipl/library/ext/semweb/semweb/rdf11.pl +2106 -0
  378. package/lib/swipl/library/ext/semweb/semweb/rdf11_containers.pl +299 -0
  379. package/lib/swipl/library/ext/semweb/semweb/rdf_cache.pl +278 -0
  380. package/lib/swipl/library/ext/semweb/semweb/rdf_compare.pl +132 -0
  381. package/lib/swipl/library/ext/semweb/semweb/rdf_db.pl +3468 -0
  382. package/lib/swipl/library/ext/semweb/semweb/rdf_edit.pl +1145 -0
  383. package/lib/swipl/library/ext/semweb/semweb/rdf_http_plugin.pl +274 -0
  384. package/lib/swipl/library/ext/semweb/semweb/rdf_library.pl +1021 -0
  385. package/lib/swipl/library/ext/semweb/semweb/rdf_litindex.pl +1035 -0
  386. package/lib/swipl/library/ext/semweb/semweb/rdf_ntriples.pl +422 -0
  387. package/lib/swipl/library/ext/semweb/semweb/rdf_persistency.pl +1576 -0
  388. package/lib/swipl/library/ext/semweb/semweb/rdf_portray.pl +182 -0
  389. package/lib/swipl/library/ext/semweb/semweb/rdf_prefixes.pl +681 -0
  390. package/lib/swipl/library/ext/semweb/semweb/rdf_sandbox.pl +153 -0
  391. package/lib/swipl/library/ext/semweb/semweb/rdf_turtle.pl +54 -0
  392. package/lib/swipl/library/ext/semweb/semweb/rdf_turtle_write.pl +1587 -0
  393. package/lib/swipl/library/ext/semweb/semweb/rdf_zlib_plugin.pl +52 -0
  394. package/lib/swipl/library/ext/semweb/semweb/rdfa.pl +1510 -0
  395. package/lib/swipl/library/ext/semweb/semweb/rdfs.pl +475 -0
  396. package/lib/swipl/library/ext/semweb/semweb/sparql_client.pl +643 -0
  397. package/lib/swipl/library/ext/semweb/semweb/turtle.pl +455 -0
  398. package/lib/swipl/library/ext/sgml/DTD/HTML4.dcl +88 -0
  399. package/lib/swipl/library/ext/sgml/DTD/HTML4.dtd +1092 -0
  400. package/lib/swipl/library/ext/sgml/DTD/HTML4.soc +6 -0
  401. package/lib/swipl/library/ext/sgml/DTD/HTML5.dtd +906 -0
  402. package/lib/swipl/library/ext/sgml/DTD/HTMLlat1.ent +195 -0
  403. package/lib/swipl/library/ext/sgml/DTD/HTMLspec.ent +77 -0
  404. package/lib/swipl/library/ext/sgml/DTD/HTMLsym.ent +241 -0
  405. package/lib/swipl/library/ext/sgml/INDEX.pl +64 -0
  406. package/lib/swipl/library/ext/sgml/c14n2.pl +203 -0
  407. package/lib/swipl/library/ext/sgml/iso_639.pl +631 -0
  408. package/lib/swipl/library/ext/sgml/pwp.pl +705 -0
  409. package/lib/swipl/library/ext/sgml/sgml.pl +815 -0
  410. package/lib/swipl/library/ext/sgml/sgml_write.pl +1003 -0
  411. package/lib/swipl/library/ext/sgml/xpath.pl +620 -0
  412. package/lib/swipl/library/ext/sgml/xsdp_types.pl +232 -0
  413. package/lib/swipl/library/ext/ssl/INDEX.pl +60 -0
  414. package/lib/swipl/library/ext/ssl/crypto.pl +1015 -0
  415. package/lib/swipl/library/ext/ssl/http/INDEX.pl +5 -0
  416. package/lib/swipl/library/ext/ssl/http/http_ssl_plugin.pl +296 -0
  417. package/lib/swipl/library/ext/ssl/saml.pl +609 -0
  418. package/lib/swipl/library/ext/ssl/ssl.pl +647 -0
  419. package/lib/swipl/library/ext/ssl/xmldsig.pl +391 -0
  420. package/lib/swipl/library/ext/ssl/xmlenc.pl +354 -0
  421. package/lib/swipl/library/ext/zlib/INDEX.pl +8 -0
  422. package/lib/swipl/library/ext/zlib/zlib.pl +112 -0
  423. package/lib/swipl/library/fastrw.pl +105 -0
  424. package/lib/swipl/library/files.pl +80 -0
  425. package/lib/swipl/library/gensym.pl +111 -0
  426. package/lib/swipl/library/git.pl +1027 -0
  427. package/lib/swipl/library/hashtable.pl +365 -0
  428. package/lib/swipl/library/heaps.pl +252 -0
  429. package/lib/swipl/library/hotfix.pl +239 -0
  430. package/lib/swipl/library/http/jquery.pl +103 -0
  431. package/lib/swipl/library/http/web/js/jquery-3.6.0.min.js +2 -0
  432. package/lib/swipl/library/increval.pl +217 -0
  433. package/lib/swipl/library/intercept.pl +263 -0
  434. package/lib/swipl/library/iostream.pl +237 -0
  435. package/lib/swipl/library/iri_scheme/file.pl +70 -0
  436. package/lib/swipl/library/lazy_lists.pl +481 -0
  437. package/lib/swipl/library/listing.pl +1362 -0
  438. package/lib/swipl/library/lists.pl +860 -0
  439. package/lib/swipl/library/lynx/INDEX.pl +14 -0
  440. package/lib/swipl/library/lynx/format.pl +380 -0
  441. package/lib/swipl/library/lynx/html_style.pl +129 -0
  442. package/lib/swipl/library/lynx/html_text.pl +865 -0
  443. package/lib/swipl/library/lynx/pldoc_style.pl +87 -0
  444. package/lib/swipl/library/macros.pl +451 -0
  445. package/lib/swipl/library/main.pl +1200 -0
  446. package/lib/swipl/library/make.pl +197 -0
  447. package/lib/swipl/library/modules.pl +129 -0
  448. package/lib/swipl/library/nb_rbtrees.pl +267 -0
  449. package/lib/swipl/library/nb_set.pl +203 -0
  450. package/lib/swipl/library/obfuscate.pl +115 -0
  451. package/lib/swipl/library/occurs.pl +241 -0
  452. package/lib/swipl/library/operators.pl +182 -0
  453. package/lib/swipl/library/option.pl +388 -0
  454. package/lib/swipl/library/optparse.pl +966 -0
  455. package/lib/swipl/library/ordsets.pl +518 -0
  456. package/lib/swipl/library/oset.pl +160 -0
  457. package/lib/swipl/library/pairs.pl +187 -0
  458. package/lib/swipl/library/persistency.pl +713 -0
  459. package/lib/swipl/library/pio.pl +47 -0
  460. package/lib/swipl/library/portray_text.pl +277 -0
  461. package/lib/swipl/library/pprint.pl +940 -0
  462. package/lib/swipl/library/predicate_options.pl +975 -0
  463. package/lib/swipl/library/prolog_autoload.pl +233 -0
  464. package/lib/swipl/library/prolog_breakpoints.pl +396 -0
  465. package/lib/swipl/library/prolog_clause.pl +1104 -0
  466. package/lib/swipl/library/prolog_code.pl +416 -0
  467. package/lib/swipl/library/prolog_codewalk.pl +1248 -0
  468. package/lib/swipl/library/prolog_colour.pl +3301 -0
  469. package/lib/swipl/library/prolog_colour.qlf +0 -0
  470. package/lib/swipl/library/prolog_config.pl +154 -0
  471. package/lib/swipl/library/prolog_coverage.pl +1333 -0
  472. package/lib/swipl/library/prolog_debug.pl +404 -0
  473. package/lib/swipl/library/prolog_deps.pl +675 -0
  474. package/lib/swipl/library/prolog_evaluable.pl +234 -0
  475. package/lib/swipl/library/prolog_format.pl +219 -0
  476. package/lib/swipl/library/prolog_history.pl +161 -0
  477. package/lib/swipl/library/prolog_jiti.pl +333 -0
  478. package/lib/swipl/library/prolog_locale.pl +61 -0
  479. package/lib/swipl/library/prolog_metainference.pl +307 -0
  480. package/lib/swipl/library/prolog_pack.pl +3906 -0
  481. package/lib/swipl/library/prolog_profile.pl +346 -0
  482. package/lib/swipl/library/prolog_qlfmake.pl +533 -0
  483. package/lib/swipl/library/prolog_source.pl +1188 -0
  484. package/lib/swipl/library/prolog_stack.pl +794 -0
  485. package/lib/swipl/library/prolog_trace.pl +365 -0
  486. package/lib/swipl/library/prolog_versions.pl +347 -0
  487. package/lib/swipl/library/prolog_wrap.pl +164 -0
  488. package/lib/swipl/library/prolog_xref.pl +3090 -0
  489. package/lib/swipl/library/prolog_xref.qlf +0 -0
  490. package/lib/swipl/library/pure_input.pl +266 -0
  491. package/lib/swipl/library/qpforeign.pl +695 -0
  492. package/lib/swipl/library/qsave.pl +1516 -0
  493. package/lib/swipl/library/quasi_quotations.pl +300 -0
  494. package/lib/swipl/library/quintus.pl +437 -0
  495. package/lib/swipl/library/random.pl +459 -0
  496. package/lib/swipl/library/rbtrees.pl +1153 -0
  497. package/lib/swipl/library/readln.pl +256 -0
  498. package/lib/swipl/library/readutil.pl +304 -0
  499. package/lib/swipl/library/record.pl +544 -0
  500. package/lib/swipl/library/rwlocks.pl +172 -0
  501. package/lib/swipl/library/sandbox.pl +1384 -0
  502. package/lib/swipl/library/semweb/dc.rdfs +198 -0
  503. package/lib/swipl/library/semweb/eor.rdfs +95 -0
  504. package/lib/swipl/library/semweb/owl.owl +287 -0
  505. package/lib/swipl/library/semweb/rdf_library.ttl +90 -0
  506. package/lib/swipl/library/semweb/rdfs.rdfs +251 -0
  507. package/lib/swipl/library/settings.pl +755 -0
  508. package/lib/swipl/library/shell.pl +462 -0
  509. package/lib/swipl/library/shlib.pl +670 -0
  510. package/lib/swipl/library/solution_sequences.pl +439 -0
  511. package/lib/swipl/library/sort.pl +119 -0
  512. package/lib/swipl/library/statistics.pl +541 -0
  513. package/lib/swipl/library/streams.pl +118 -0
  514. package/lib/swipl/library/strings.pl +469 -0
  515. package/lib/swipl/library/system.pl +95 -0
  516. package/lib/swipl/library/tables.pl +381 -0
  517. package/lib/swipl/library/tableutil.pl +771 -0
  518. package/lib/swipl/library/tabling.pl +42 -0
  519. package/lib/swipl/library/terms.pl +457 -0
  520. package/lib/swipl/library/theme/auto.pl +61 -0
  521. package/lib/swipl/library/theme/dark.pl +455 -0
  522. package/lib/swipl/library/theme/light.pl +45 -0
  523. package/lib/swipl/library/thread.pl +810 -0
  524. package/lib/swipl/library/thread_pool.pl +508 -0
  525. package/lib/swipl/library/threadutil.pl +574 -0
  526. package/lib/swipl/library/tty.pl +316 -0
  527. package/lib/swipl/library/ugraphs.pl +644 -0
  528. package/lib/swipl/library/unicode/INDEX.pl +6 -0
  529. package/lib/swipl/library/unicode/blocks.pl +414 -0
  530. package/lib/swipl/library/url.pl +1129 -0
  531. package/lib/swipl/library/utf8.pl +145 -0
  532. package/lib/swipl/library/varnumbers.pl +221 -0
  533. package/lib/swipl/library/vm.pl +251 -0
  534. package/lib/swipl/library/wfs.pl +205 -0
  535. package/lib/swipl/library/when.pl +263 -0
  536. package/lib/swipl/library/writef.pl +278 -0
  537. package/lib/swipl/library/www_browser.pl +251 -0
  538. package/lib/swipl/library/yall.pl +574 -0
  539. package/lib/swipl/library/zip.pl +211 -0
  540. package/lib/swipl/swipl.home +1 -0
  541. package/lib/vendor/libcrypto.so.3 +0 -0
  542. package/lib/vendor/libpcre2-8.so.0 +0 -0
  543. package/lib/vendor/libssl.so.3 +0 -0
  544. package/lib/vendor/libz.so.1 +0 -0
  545. package/licenses/OpenSSL-LICENSE.txt +177 -0
  546. package/licenses/PCRE2-COPYING +5 -0
  547. package/licenses/README.md +12 -0
  548. package/licenses/SWI-Prolog-LICENSE +41 -0
  549. package/licenses/zlib-LICENSE +22 -0
  550. package/package.json +27 -0
@@ -0,0 +1,2111 @@
1
+ /* Part of SWI-Prolog
2
+
3
+ Author: Jan Wielemaker
4
+ E-mail: jan@swi-prolog.org
5
+ WWW: http://www.swi-prolog.org
6
+ Copyright (c) 2021-2025, SWI-Prolog Solutions b.v.
7
+ All rights reserved.
8
+
9
+ Redistribution and use in source and binary forms, with or without
10
+ modification, are permitted provided that the following conditions
11
+ are met:
12
+
13
+ 1. Redistributions of source code must retain the above copyright
14
+ notice, this list of conditions and the following disclaimer.
15
+
16
+ 2. Redistributions in binary form must reproduce the above copyright
17
+ notice, this list of conditions and the following disclaimer in
18
+ the documentation and/or other materials provided with the
19
+ distribution.
20
+
21
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24
+ FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25
+ COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27
+ BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30
+ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31
+ ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32
+ POSSIBILITY OF SUCH DAMAGE.
33
+ */
34
+
35
+ :- module(json_schema,
36
+ [ json_validate/3, % +Schema, +DataDict, +Options
37
+ json_compile_schema/3, % +Schema, -CompiledType, +Options
38
+ json_check/3 % +CompiledType, +Data, +Options
39
+ ]).
40
+ :- use_module(library(apply_macros), []).
41
+ :- use_module(library(debug), [assertion/1, debug/3]).
42
+ :- autoload(library(apply), [maplist/3, partition/4, maplist/2, include/3]).
43
+ :- autoload(library(base64), [base64/2]).
44
+ :- autoload(library(error),
45
+ [ is_of_type/2,
46
+ existence_error/3,
47
+ type_error/2,
48
+ must_be/2,
49
+ permission_error/3
50
+ ]).
51
+ :- autoload(library(lists),
52
+ [ member/2,
53
+ nth0/3,
54
+ append/3,
55
+ select/3,
56
+ selectchk/4,
57
+ selectchk/3,
58
+ delete/3
59
+ ]).
60
+ :- autoload(library(option),
61
+ [option/2, option/3, merge_options/3, select_option/4]).
62
+ :- autoload(library(pcre), [re_match/2]).
63
+ :- autoload(library(sgml), [xsd_time_string/3]).
64
+ :- autoload(library(uri),
65
+ [ uri_file_name/2,
66
+ uri_normalized/3,
67
+ uri_components/2,
68
+ uri_data/3,
69
+ uri_encoded/3,
70
+ uri_data/4,
71
+ uri_authority_components/2,
72
+ uri_authority_data/3
73
+ ]).
74
+ :- if(exists_source(library(http/http_open))).
75
+ :- autoload(library(http/http_open), [http_open/3]).
76
+ :- endif.
77
+ :- autoload(library(url), [is_absolute_url/1]).
78
+ :- autoload(library(dicts), [dict_keys/2, mapdict/3, mapdict/2]).
79
+ :- autoload(library(json), [json_read_dict/2]).
80
+ :- autoload(library(pairs), [pairs_keys/2]).
81
+
82
+ :- multifile
83
+ json_schema/2. % +URL, -Schema
84
+
85
+ /** <module> JSON Schema reader and validator
86
+
87
+ This module provides a JSON Schema reader and validator. This module is
88
+ based on the 2020-12 draft of the specification.
89
+
90
+ The API consists of two primitives and a simple high level predicate
91
+ (json_validate/3):
92
+
93
+ - json_compile_schema/3 translates a file or parsed JSON schema data
94
+ into a Prolog term that represents the schema in a way that is more
95
+ suitable for checking a JSON document.
96
+ - json_check/3
97
+ validates a document against the compiled schema.
98
+
99
+ ## Status {#json-schema-status}
100
+
101
+ The implementation is validated against
102
+ https://github.com/json-schema-org/JSON-Schema-Test-Suite.git. It fails
103
+ 4 out of 1,261 tests. Issues:
104
+
105
+ - ``$dynamicRef`` fails 3 tests, probably mixing up physical context
106
+ and resolution context that defines the _dynamic scopes_.
107
+ - There is no support for non-default vocabulary selection.
108
+
109
+ The current implementation is the result of an incremental process. It
110
+ should be refactored to make it a cleaner translation of the
111
+ specification.
112
+
113
+ ## Predicates {#json-schema-predicates}
114
+ */
115
+
116
+ :- meta_predicate
117
+ findall_vars(?, 0, ?, -).
118
+
119
+ %! json_validate(+SchemaFile, +DataDict, +Options) is det.
120
+ %
121
+ % Given a file holding a JSON Schema and a Prolog dict holding JSON
122
+ % data, validate the data against the schema. Options are passed to
123
+ % json_compile_schema/3 and json_check/3.
124
+ %
125
+ % @throws error(Formal, json_path(Path)), where `Path` is a list of
126
+ % properties from the root element to the culprit element. Formal is
127
+ % typically a type, domain or existence error. This file contains the
128
+ % message hooks to generate a human readable error from these
129
+ % exceptions using print_message/2.
130
+
131
+ json_validate(SchemaFile, DataDict, Options) :-
132
+ json_compile_schema(SchemaFile, Type, Options),
133
+ merge_options(Options,
134
+ [ on_error(error),
135
+ value_string_as(atom)
136
+ ], Options1),
137
+ json_check(Type, DataDict, Options1).
138
+
139
+ %! json_compile_schema(+Input, -Type, +Options) is det.
140
+ %
141
+ % Load and translated a JSON Schema. Input is either a file name, a
142
+ % specification for absolute_file_name/3 or the output of
143
+ % json_read_dict/2.
144
+ %
145
+ % If Input is a file name, the loaded and compiled schema is cached.
146
+ % Reusing the cache validates the modification file of the schema file
147
+ % and reloads it if the file's time stamp has changed. Note that
148
+ % `true` and `false` are valid schemas and cannot be used as file
149
+ % names.
150
+
151
+ :- dynamic
152
+ schema_cache/5.
153
+
154
+ json_compile_schema(JSON, Type, Options) :-
155
+ is_dict(JSON),
156
+ !,
157
+ merge_options(Options, [base_uri('')], Options1),
158
+ option(base_uri(Base), Options1),
159
+ json_root_type(JSON, Base, Type, Options).
160
+ json_compile_schema(Bool, Type, _Options) :-
161
+ boolean(Bool, Prolog),
162
+ !,
163
+ boolean_schema(Prolog, Type).
164
+ json_compile_schema(Spec, Type, Options) :-
165
+ schema_cache(Spec, File, Options, Time, Type0),
166
+ catch(time_file(File, Modified), error(_,_), fail),
167
+ abs(Time-Modified) < 1,
168
+ !,
169
+ Type = Type0.
170
+ json_compile_schema(Spec, Type, Options) :-
171
+ absolute_file_name(Spec, File,
172
+ [ extensions(['', json]),
173
+ access(read)
174
+ ]),
175
+ uri_file_name(URI, File),
176
+ json_read_file(File, JSON),
177
+ json_root_type(JSON, URI, Type0, Options),
178
+ time_file(File, Time),
179
+ retractall(schema_cache(_,_,_,_,_)),
180
+ asserta(schema_cache(Spec, File, Options, Time, Type0)),
181
+ Type = Type0.
182
+
183
+ %! json_root_type(+JSONSpec, +URI, -PrologType, +Options)
184
+
185
+ json_root_type(JSONSpec, URI, PrologType, Options) :-
186
+ Id = JSONSpec.get('$id'), !,
187
+ uri_normalized(Id, URI, Base),
188
+ json_root_type_(JSONSpec, Base, PrologType, Options).
189
+ json_root_type(JSONSpec, URI, PrologType, Options) :-
190
+ json_root_type_(JSONSpec, URI, PrologType, Options).
191
+
192
+ json_root_type_(JSON, URI, PrologType, Options) :-
193
+ push_dynamic_scope(JSON, [], Scopes),
194
+ json_type(JSON, PrologType,
195
+ [ base_uri(URI),
196
+ schema(JSON), % The schema we are working on
197
+ root(PrologType), % Its type (may be incomplete)
198
+ types(#{}), % $id --> Type map
199
+ schemas([URI-JSON|_]), % URL --> JSON map (open list)
200
+ dynamic_scopes(Scopes) % Handle $dynamicRef
201
+ | Options
202
+ ]).
203
+
204
+ %! json_type(+JSONSpec, -PrologType, +Options)
205
+ %
206
+ % True when PrologType is a canonical Prolog representation of the
207
+ % JSON schema defines in JSONSpec.
208
+
209
+ json_type(true, any, _) :-
210
+ !.
211
+ json_type(false, nothing, _) :-
212
+ !.
213
+ json_type(JSON, Prolog, Options) :-
214
+ Id = JSON.get('$id'),
215
+ atomic(Id), % Avoid error on meta schema
216
+ !,
217
+ option(base_uri(Base0), Options),
218
+ option(types(Types0), Options),
219
+ option(dynamic_scopes(Scopes0), Options),
220
+ uri_normalized(Id, Base0, Base),
221
+ ( \+ JSON.get('$$nolink') == true,
222
+ option(types(Types), Options),
223
+ Type = Types.get(Base)
224
+ -> Prolog = Type
225
+ ; push_dynamic_scope(JSON, Scopes0, Scopes),
226
+ merge_options([ base_uri(Base),
227
+ types(Types0.put(Base,Prolog)),
228
+ dynamic_scopes(Scopes)
229
+ ], Options, Options1),
230
+ json_type_(JSON.put('$$nolink', true), Prolog, Options1)
231
+ ).
232
+ json_type(JSON, Prolog, Options) :-
233
+ json_type_(JSON, Prolog, Options).
234
+
235
+ %! json_type_(+JSON, -PrologType, +Options)
236
+
237
+ json_type_(Spec, const(Const), Options) :-
238
+ select_dict(#{const:Const}, Spec, Rest),
239
+ !,
240
+ json_type_(Rest, Type, Options),
241
+ ( json_check(Type, Const, Options)
242
+ -> true
243
+ ; type_error(Type, Const)
244
+ ).
245
+ json_type_(Spec, unevaluated_properties(Type, UnevalType), Options) :-
246
+ select_dict(#{unevaluatedProperties:Uneval}, Spec, Rest),
247
+ !,
248
+ bool_or_type(Uneval, UnevalType, Options),
249
+ json_type_(Rest, Type, Options).
250
+ json_type_(Spec, unevaluated_items(Type, UnevalType), Options) :-
251
+ select_dict(#{unevaluatedItems:Uneval}, Spec, Rest),
252
+ !,
253
+ bool_or_type(Uneval, UnevalType, Options),
254
+ json_type_(Rest, Type, Options).
255
+ json_type_(Spec, allOf([RestType, if_then_else(If,Then,Else)]), Options) :-
256
+ select_dict(#{if:IfSpec, then:ThenSpec, else:ElseSpec},
257
+ Spec, Rest,
258
+
259
+ #{then: true, else: true}),
260
+ !,
261
+ json_type(IfSpec, If, Options),
262
+ json_type(ThenSpec, Then, Options),
263
+ json_type(ElseSpec, Else, Options),
264
+ json_type_(Rest, RestType, Options).
265
+ json_type_(Spec, not(Type), Options) :-
266
+ Disallow = Spec.get(disallow),
267
+ !,
268
+ explicit_type(Disallow, Spec, Type, Options).
269
+ json_type_(Spec, allOf(Types), Options) :-
270
+ select_dict(#{allOf:List}, Spec, Rest),
271
+ !,
272
+ maplist(opts_json_type(Options), List, Types0),
273
+ json_type_(Rest, Type, Options),
274
+ ( Type == untyped
275
+ -> Types = Types0
276
+ ; append(Types0, [Type], Types)
277
+ ).
278
+ json_type_(Spec, Type, Options) :-
279
+ select_dict(#{anyOf:List}, Spec, Rest),
280
+ !,
281
+ maplist(opts_json_type(Options), List, Types),
282
+ json_type_(Rest, RestType, Options),
283
+ ( RestType == untyped
284
+ -> Type = anyOf(Types)
285
+ ; Type = allOf([RestType,anyOf(Types)])
286
+ ).
287
+ json_type_(Spec, Type, Options) :-
288
+ select_dict(#{oneOf:List}, Spec, Rest),
289
+ !,
290
+ maplist(opts_json_type(Options), List, Types),
291
+ json_type_(Rest, RestType, Options),
292
+ ( RestType == any
293
+ -> Type = oneOf(Types)
294
+ ; Type = allOf([RestType,oneOf(Types)])
295
+ ).
296
+ json_type_(Spec, Type, Options) :-
297
+ select_dict(#{not:NSpec}, Spec, Rest),
298
+ !,
299
+ json_type(NSpec, NType, Options),
300
+ json_type_(Rest, RestType, Options),
301
+ ( RestType == untyped
302
+ -> Type = not(NType)
303
+ ; Type = allOf([RestType,not(NType)])
304
+ ).
305
+ json_type_(Spec, enum(Values), _) :-
306
+ _{enum:Values} :< Spec,
307
+ !.
308
+ json_type_(Spec, Type, _Options) :-
309
+ empty_schema(Spec),
310
+ !,
311
+ Type = untyped.
312
+ json_type_(Spec, Type, Options) :-
313
+ _{'$ref':_Ref} :< Spec,
314
+ !,
315
+ type_ref(Spec, Type, Options).
316
+ json_type_(Spec, Type, Options) :-
317
+ _{'$dynamicRef':_Ref} :< Spec,
318
+ !,
319
+ dynamic_type_ref(Spec, Type, Options).
320
+ json_type_(Spec, Type, Options) :-
321
+ SType = Spec.get(type),
322
+ !,
323
+ explicit_type(SType, Spec, Type, Options).
324
+ json_type_(Spec, Dict, Options) :-
325
+ findall_vars(JSONType-Type,
326
+ ( basic_type(JSONType),
327
+ json_type(JSONType, Spec, Type, Options)
328
+ ), Options, Pairs),
329
+ dict_pairs(Dict, any, Pairs).
330
+
331
+ basic_type(integer).
332
+ basic_type(number).
333
+ basic_type(string).
334
+ basic_type(object).
335
+ basic_type(array).
336
+
337
+ bool_or_type(BoolSpec, Bool, _Options) :-
338
+ boolean(BoolSpec, Bool),
339
+ !.
340
+ bool_or_type(TypeSpec, type(Type), Options) :-
341
+ json_type(TypeSpec, Type, Options).
342
+
343
+ %! explicit_type(+SpecifiedType, +Spec, -Type, +Options)
344
+ %
345
+ % Deal with expicitly specified types using `{"type": Type}` or
346
+ % `{disallow: Type}`
347
+
348
+ explicit_type(_SType, Spec, Type, Options) :-
349
+ _{'$ref':_Ref} :< Spec,
350
+ !,
351
+ type_ref(Spec, Type, Options).
352
+ explicit_type(SType, Spec, FinalType, Options) :-
353
+ ( is_list(SType)
354
+ -> partition(atomic, SType, Atomics, Schemas),
355
+ maplist(atom_string, ATypes, Atomics),
356
+ findall_vars(JSONType-Type,
357
+ ( member(JSONType, ATypes),
358
+ json_type(JSONType, Spec, Type, Options)
359
+ ), Options, Pairs),
360
+ maplist(opts_json_type(Options), Schemas, SchemaTypes),
361
+ join_types(Pairs, SchemaTypes, type-one_of(ATypes), FinalType)
362
+ ; atom_string(JSONType, SType),
363
+ json_type(JSONType, Spec, FinalType, Options)
364
+ ).
365
+
366
+ join_types(Pairs, [], TypePair, AType) :-
367
+ !,
368
+ dict_pairs(AType, type, [TypePair|Pairs]).
369
+ join_types([], SchemaTypes, _, anyOf(SchemaTypes)) :-
370
+ !.
371
+ join_types(Pairs, SchemaTypes, TypePair, anyOf([AType|SchemaTypes])) :-
372
+ dict_pairs(AType, type, [TypePair|Pairs]).
373
+
374
+ %! json_type(+BasicType, +JSON, -PrologType, +Options) is semidet.
375
+ %
376
+ % Produce a type description if JSON contains the attributes demanded
377
+ % by BasicType. See basic_type/1.
378
+
379
+ json_type(object, Spec, Type, Options) =>
380
+ Type = object(Constraints),
381
+ object_properties(Spec, Constraints, Options).
382
+ json_type(array, Spec, Type, Options) =>
383
+ Type = array(Constraints),
384
+ array_properties(Spec, Constraints, Options).
385
+ json_type(JSONType, _, Type, _),
386
+ nonvar(JSONType), simple_type(JSONType, Type0) =>
387
+ Type = Type0.
388
+ json_type(JSONType, Spec, Type, _), var(JSONType) =>
389
+ ( FormatS = Spec.get(format)
390
+ -> atom_string(Format, FormatS)
391
+ ; Format = '-'
392
+ ),
393
+ api_type(_, JSONType, Format, Type1),
394
+ numeric_domain(Spec, JSONType, Type1, Type),
395
+ Type \= numeric(_, []).
396
+ json_type(JSONType, Spec, Type, _),
397
+ api_type(_, JSONType, -, Type1) =>
398
+ numeric_domain(Spec, JSONType, Type1, Type).
399
+ json_type(_, _, _, _) =>
400
+ fail.
401
+
402
+ %! json_type_r(+Options, +JSON, -Prolog)
403
+ %
404
+ % Maplist support predicate.
405
+
406
+ json_type_r(Options, JSON, Prolog) :-
407
+ json_type(JSON, Prolog, Options).
408
+
409
+ simple_type(null, null).
410
+ simple_type(any, any).
411
+
412
+ opts_json_type(Options, Spec, Type) :-
413
+ json_type(Spec, Type, Options).
414
+
415
+ pattern_property(Options, Pattern-Spec, Pattern-Type) :-
416
+ json_type(Spec, Type, Options).
417
+
418
+ schema_property(Options, Name-Spec, p(Name, Type, _Req)) :-
419
+ json_type(Spec, Type, Options).
420
+
421
+ %! object_properties(+Spec, -Constraints, +Options) is semidet.
422
+ %
423
+ % True when Constraints is a set of constraints to apply on an object.
424
+ % Fails if there are no constraints on objects.
425
+
426
+ object_properties(Spec, Constraints, Options) :-
427
+ findall_vars(C, object_property(Spec, C, Options), Options, Constraints0),
428
+ split_dependencies(Constraints0, Constraints1, Options),
429
+ join_required(Constraints1, C0),
430
+ select_constraint(properties(Props0), [], C0, C1),
431
+ select_constraint(additionalProperties(Add), implicit, C1, C2),
432
+ select_constraint(patternProperties(Patterns), [], C2, C3),
433
+ apply_patterns(Patterns, Props0, Props),
434
+ Constraints = [ properties(Props, Add, Patterns) | C3 ].
435
+
436
+ object_property(Spec, Constraint, Options) :-
437
+ object_property_decl(P, Cvt, Options),
438
+ Value0 = Spec.get(P),
439
+ call(Cvt, Value0, Value),
440
+ Constraint =.. [P,Value].
441
+
442
+ object_property_decl(properties, cvt_property(Options), Options).
443
+ object_property_decl(required, cvt_required, _).
444
+ object_property_decl(additionalProperties, cvt_bool_or_type(Options), Options).
445
+ object_property_decl(patternProperties, cvt_pattern_props(Options), Options).
446
+ object_property_decl(dependencies, =, _).
447
+ object_property_decl(dependentRequired, cvt_dependent_required, _).
448
+ object_property_decl(dependentSchemas, cvt_dependent_schemas(Options), Options).
449
+ object_property_decl(minProperties, integral_min, _).
450
+ object_property_decl(maxProperties, integral_max, _).
451
+ object_property_decl(propertyNames, cvt_property_names(Options), Options).
452
+
453
+ cvt_property(Options, PropSpecs, Props) :-
454
+ dict_pairs(PropSpecs, _, Pairs),
455
+ maplist(schema_property(Options), Pairs, Props0),
456
+ sort(Props0, Props).
457
+
458
+ cvt_bool_or_type(Options, Spec, Type) :-
459
+ bool_or_type(Spec, Type, Options).
460
+
461
+ cvt_required(Strings, Properties) :-
462
+ maplist(atom_string, Properties, Strings).
463
+
464
+ cvt_pattern_props(Options, PProps, Patterns) :-
465
+ dict_pairs(PProps, _, PropPairs),
466
+ maplist(pattern_property(Options), PropPairs, Patterns).
467
+
468
+ %! cvt_dependent_required(+Spec, -Required) is det.
469
+ %
470
+ % Convert `dependentRequired`, which is a dict with property-name ->
471
+ % list of required property names. This states that if the key is
472
+ % present as property, the dependendents must be present as well.
473
+
474
+ :- det(cvt_dependent_required/2).
475
+ cvt_dependent_required(Dict0, Dict) :-
476
+ mapdict(dep_required_prop, Dict0, Dict).
477
+
478
+ dep_required_prop(_, Strings, Atoms) :-
479
+ must_be(list, Strings),
480
+ maplist(atom_string, Atoms, Strings).
481
+
482
+ %! cvt_dependent_schemas(+Options, +Dict0, -Dict) is det.
483
+ %
484
+ % Deal with `dependentSchemas`. The specification is an object mapping
485
+ % property names to schemas.
486
+
487
+ :- det(cvt_dependent_schemas/3).
488
+ cvt_dependent_schemas(Options, Dict0, Dict) :-
489
+ mapdict(dep_required_schema(Options), Dict0, Dict).
490
+
491
+ dep_required_schema(Options, _Key, TypeSpec, Type) :-
492
+ json_type(TypeSpec, Type, Options).
493
+
494
+ cvt_property_names(Options, TypeSpec, Type) :-
495
+ json_type(TypeSpec, Type, Options).
496
+
497
+ %! split_dependencies(+ConstraintsIn, -Constraints, +Options) is det.
498
+ %
499
+ % Up to draft-7, `dependentRequired` and `dependentSchemas` were
500
+ % combine as `dependencies`. This splits the `dependencies` in the two
501
+ % sections such the remainder of this code need not be aware of the
502
+ % deprecated `dependencies` keyword.
503
+
504
+ :- det(split_dependencies/3).
505
+ split_dependencies(Constraints0, Constraints, Options) :-
506
+ select(dependencies(Deps), Constraints0, Constraints1),
507
+ !,
508
+ dict_pairs(Deps, _, Pairs),
509
+ partition(is_required_dependency, Pairs, RequiredP, SchemasP),
510
+ dict_pairs(Required, #, RequiredP),
511
+ dict_pairs(Schemas, #, SchemasP),
512
+ cvt_dependent_required(Required, Required1),
513
+ cvt_dependent_schemas(Options, Schemas, Schemas1),
514
+ Constraints = [ dependentRequired(Required1),
515
+ dependentSchemas(Schemas1)
516
+ | Constraints1
517
+ ].
518
+ split_dependencies(Constraints, Constraints, _).
519
+
520
+ is_required_dependency(_Key-Value) :-
521
+ maplist(atomic, Value).
522
+
523
+ %! join_required(+Constraints0, -Constraints) is det.
524
+ %
525
+ % Combine the `properties` and `required` constraints into a list or
526
+ % ordered p(Name,Type,Required) triples. Note that this assumes that
527
+ % these two properties are ordered according to
528
+ % object_property_decl/3.
529
+
530
+ :- det(join_required/2).
531
+ join_required([properties(Props0), required(Reqs)|Rest],
532
+ [properties(Props)|Rest]) :-
533
+ !,
534
+ join_required_(Props0, Reqs, RestReqs),
535
+ maplist(required_prop, RestReqs, Props1),
536
+ append(Props0, Props1, Props2),
537
+ sort(Props2, Props).
538
+ join_required([properties(Props)|Rest],
539
+ [properties(Props)|Rest]) :-
540
+ !,
541
+ maplist(not_required, Props).
542
+ join_required([required(Reqs)|Rest],
543
+ [properties(Props)|Rest]) :-
544
+ !,
545
+ maplist(required_prop, Reqs, Props0),
546
+ sort(Props0, Props).
547
+ join_required(Constraints, Constraints).
548
+
549
+ join_required_([], Reqs, Reqs).
550
+ join_required_([p(Name,_Type,true)|Props], Reqs0, Reqs) :-
551
+ selectchk(Name, Reqs0, Reqs1),
552
+ !,
553
+ join_required_(Props, Reqs1, Reqs).
554
+ join_required_([p(_Name,_Type,false)|Props], Reqs0, Reqs) :-
555
+ join_required_(Props, Reqs0, Reqs).
556
+
557
+ required_prop(Name, p(Name,any,true)).
558
+
559
+ not_required(p(_,_,false)).
560
+
561
+ %! apply_patterns(+Patterns, +Props0, -Props) is det.
562
+ %
563
+ % The `patternProperties` keyword also applies to explicit properties.
564
+ % Here, we change the type into an allOf() type containing the
565
+ % explicit property and all matching patternProperty statements.
566
+
567
+ :- det(apply_patterns/3).
568
+ apply_patterns([], Props0, Props) =>
569
+ Props = Props0.
570
+ apply_patterns(Patterns, Props0, Props) =>
571
+ maplist(apply_patterns_to_prop(Patterns), Props0, Props).
572
+
573
+ apply_patterns_to_prop(Patterns,
574
+ p(Name, Type, Req),
575
+ p(Name, allOf([Type|PTypes]), Req)) :-
576
+ findall(PType, ( member(Pattern-PType, Patterns),
577
+ re_match(Pattern, Name) ), PTypes),
578
+ PTypes \== [],
579
+ !.
580
+ apply_patterns_to_prop(_, Prop, Prop).
581
+
582
+ :- det(select_constraint/4).
583
+ select_constraint(C, _, Cs0, Cs) :-
584
+ selectchk(C, Cs0, Cs),
585
+ !.
586
+ select_constraint(C, Default, Cs, Cs) :-
587
+ arg(1, C, Default).
588
+
589
+
590
+ %! array_properties(+Spec, -Constraints, +Options) is det.
591
+ %
592
+ % True when Constraints is a list of constraints on an array according
593
+ % to Spec. The `contains`, `maxContains` and `minContains` keywords
594
+ % are translated to a single term contains(Type, MinContains,
595
+ % MaxContains). The `prefixItems` and `items` are combined to a single
596
+ % term items(PrefixItems, Items).
597
+
598
+ array_properties(Spec, Constraints, Options) :-
599
+ select_dict(_{items: PrefixItems}, Spec, Spec1),
600
+ is_list(PrefixItems),
601
+ !, % Deprecated (draft-7 and older)
602
+ ( select_dict(_{additionalItems: Items}, Spec1, Spec2)
603
+ -> true
604
+ ; Items = true,
605
+ Spec2 = Spec1
606
+ ),
607
+ array_properties(Spec2.put(#{prefixItems:PrefixItems, items:Items}),
608
+ Constraints, Options).
609
+ array_properties(Spec, Constraints, Options) :-
610
+ findall_vars(C, array_property(Spec, C, Options), Options, Constraints0),
611
+ ( select(minContains(Min), Constraints0, Constraints1)
612
+ -> true
613
+ ; Min = 1,
614
+ Constraints1 = Constraints0
615
+ ),
616
+ ( select(maxContains(Max), Constraints1, Constraints2)
617
+ -> true
618
+ ; Max = infinite,
619
+ Constraints2 = Constraints1
620
+ ),
621
+ ( selectchk(contains(Type), Constraints2, contains(Type,Min,Max), Constraints3)
622
+ -> true
623
+ ; Constraints3 = Constraints2
624
+ ),
625
+ select_constraint(prefixItems(PrefixItems), [], Constraints3, Constraints4),
626
+ select_constraint(items(Items), untyped, Constraints4, Constraints5),
627
+ Constraints = [items(PrefixItems, Items)|Constraints5].
628
+
629
+ %! array_property(+JSONSpec, -Property, +Options) is nondet.
630
+ %
631
+ % True when Property is an array constraint defined in JSONSpec.
632
+
633
+ array_property(Spec, Property, Options) :-
634
+ is_dict(Spec),
635
+ array_property_decl(P, Convert, Options),
636
+ Value0 = Spec.get(P),
637
+ call(Convert, Value0, Value),
638
+ Property =.. [P,Value].
639
+
640
+ array_property_decl(items, json_type_r(Options), Options).
641
+ array_property_decl(uniqueItems, boolean, _).
642
+ array_property_decl(prefixItems, cvt_types(Options), Options).
643
+ array_property_decl(minItems, integral_min, _).
644
+ array_property_decl(maxItems, integral_max, _).
645
+ array_property_decl(contains, json_type_r(Options), Options).
646
+ array_property_decl(minContains, integral_min, _).
647
+ array_property_decl(maxContains, integral_max, _).
648
+
649
+ :- public boolean/2.
650
+ boolean(true, true).
651
+ boolean(false, false).
652
+ boolean(@(true), true).
653
+ boolean(@(false), false).
654
+
655
+ boolean_schema(true, any).
656
+ boolean_schema(false, nothing).
657
+
658
+ cvt_types(Options, Specs, Types) :-
659
+ maplist(json_type_r(Options), Specs, Types).
660
+
661
+ %! integral_max(+Spec, -Max) is det.
662
+ %! integral_min(+Spec, -Min) is det.
663
+ %
664
+ % Deal with the argument of `minItems` and `maxItems`. These may be
665
+ % unspecified. They must be non-negative. They can be floats, implying
666
+ % the first integer below/above the given value.
667
+
668
+ integral_max(Var, Max), var(Var) => Max = infinite.
669
+ integral_max(Num, Max), number(Num), Num >=0 => Max is floor(Num).
670
+ integral_max(Trm, _) => type_error(nonneg, Trm).
671
+
672
+ integral_min(Var, Min), var(Var) => Min = 0.
673
+ integral_min(Num, Min), number(Num), Num >=0 => Min is ceil(Num).
674
+ integral_min(Trm, _) => type_error(nonneg, Trm).
675
+
676
+ %! numeric_domain(+Spec, +JSONType, +TypeIn, -TypeOut)
677
+
678
+ numeric_domain(Spec, Type0, Type1, Type) :-
679
+ numeric_type(Type0),
680
+ !,
681
+ ( _{minimum:Min, maximum:Max} :< Spec
682
+ -> ( _{exclusiveMaximum:true,
683
+ exclusiveMinimum:true} :< Spec
684
+ -> Type = numeric(Type1, [>(Min), <(Max)|More])
685
+ ; _{exclusiveMaximum:true} :< Spec
686
+ -> Type = numeric(Type1, [>=(Min), <(Max)|More])
687
+ ; _{exclusiveMinimum:true} :< Spec
688
+ -> Type = numeric(Type1, [>(Min), =<(Max)|More])
689
+ ; Type = numeric(Type1, [>=(Min), =<(Max)|More])
690
+ )
691
+ ; _{minimum:Min, exclusiveMinimum:true} :< Spec % deprecated
692
+ -> Type = numeric(Type1, [>(Min)|More])
693
+ ; _{exclusiveMinimum:Min} :< Spec, number(Min) % 2020-12
694
+ -> Type = numeric(Type1, [>(Min)|More])
695
+ ; _{minimum:Min} :< Spec
696
+ -> Type = numeric(Type1, [>=(Min)|More])
697
+ ; _{maximum:Max, exclusiveMaximum:true} :< Spec % deprecated
698
+ -> Type = numeric(Type1, [<(Max)|More])
699
+ ; _{exclusiveMaximum:Max} :< Spec, number(Max) % 2020-12
700
+ -> Type = numeric(Type1, [<(Max)|More])
701
+ ; _{maximum:Max} :< Spec
702
+ -> Type = numeric(Type1, [=<(Max)|More])
703
+ ; Type = numeric(Type1, More)
704
+ ),
705
+ ( ( _{divisibleBy:Num} :< Spec % old
706
+ ; _{multipleOf:Num} :< Spec % current (2020-12)
707
+ )
708
+ -> More = [multiple_of(Num)]
709
+ ; More = []
710
+ ).
711
+ numeric_domain(Spec, string, Format, string(Format, Constraints)) :-
712
+ !,
713
+ findall(C, string_property(Spec, C), Constraints).
714
+ numeric_domain(_, _Type0, Type, Type).
715
+
716
+ numeric_type(integer).
717
+ numeric_type(number).
718
+
719
+ string_property(Spec, Prop) :-
720
+ string_property_decl(P, Normalize),
721
+ get_dict(P, Spec, Value0),
722
+ call(Normalize, Value0, Value),
723
+ Prop =.. [P,Value].
724
+
725
+ string_property_decl(maxLength, integral_max).
726
+ string_property_decl(minLength, integral_min).
727
+ string_property_decl(pattern, =).
728
+ string_property_decl(contentEncoding, =).
729
+ string_property_decl(contentMediaType, =).
730
+ string_property_decl(contentSchema, =).
731
+
732
+ %! type_ref(+Spec, -Type, +Options).
733
+ %! type_ref(+Ref:string, +Spec, -Type, +Options)
734
+ %
735
+ % Handle a ``$ref`` attribute.
736
+ %
737
+ % @arg Spec is the dict that contains `"$ref": Ref`. We must also use
738
+ % the properties thereof.
739
+
740
+ type_ref(Spec, Type, Options) :-
741
+ select_dict(_{'$ref':Ref}, Spec, Rest),
742
+ type_ref(Ref, Rest, Type, Options).
743
+
744
+ type_ref("#", _, Type, Options) :-
745
+ option(root(RootType), Options),
746
+ !,
747
+ Type = RootType.
748
+ type_ref(URLS, Spec, Type, Options) :-
749
+ atom_concat(#, Fragment, URLS),
750
+ option(schema(Schema), Options),
751
+ !,
752
+ json_fragment(Fragment, Schema, SubDoc, [ref(relative)|Options]),
753
+ join_type_spec(Spec, SubDoc, Combined),
754
+ push_dynamic_scope_option(SubDoc, Options, Options1),
755
+ json_type(Combined, Type, Options1).
756
+ type_ref(URLS, _, Type, Options) :-
757
+ option(base_uri(Base), Options),
758
+ uri_normalized(URLS, Base, URL),
759
+ option(types(Types), Options),
760
+ Type = Types.get(URL),
761
+ !.
762
+ type_ref(URLS, Spec, Type, Options) :-
763
+ option(base_uri(Base), Options),
764
+ uri_normalized(URLS, Base, URL),
765
+ ( url_type_doc(URL, Doc, SubDoc, Options)
766
+ -> atom_string(NewBase, URL),
767
+ update_id(Doc, SubDoc, SubDoc1, NewBase),
768
+ join_type_spec(Spec, SubDoc1, Combined),
769
+ select_option(root(_), Options, Options1, _),
770
+ ( SubDoc1 == Doc
771
+ -> Extra = [root(Type)]
772
+ ; Extra = []
773
+ ),
774
+ merge_options([ base_uri(NewBase),
775
+ schema(Doc)
776
+ | Extra
777
+ ], Options1, Options2),
778
+ push_dynamic_scope_option(SubDoc1, Options2, Options3),
779
+ json_type(Combined, Type, Options3)
780
+ ; Type = url(URL)
781
+ ).
782
+
783
+ %! update_id(+Doc, -SubDoc0, -SubDoc, +BaseURI) is det.
784
+ %
785
+ % We extracted Doc at BaseURI and Doc has a `'$id'` holding a relative
786
+ % URL. Now, if SubDoc0 == Doc, json_type/3 will process the `'$id'`
787
+ % again. This is fine if the relative URI is a normal file, but not if
788
+ % it is a directory. In that case we will end up with the relative URI
789
+ % being applied twice. We solve this by injecting the absolute URI as
790
+ % `'$id'`.
791
+
792
+ update_id(Doc, Doc, SubDoc, NewBase) :-
793
+ _Id = Doc.get('$id'),
794
+ !,
795
+ SubDoc = Doc.put('$id', NewBase).
796
+ update_id(_, SubDoc, SubDoc, _).
797
+
798
+ %! dynamic_type_ref(+Spec, -Type, +Options) is det.
799
+ %
800
+ % Handle ``$dynamicRef``.
801
+
802
+ dynamic_type_ref(Spec, Type, Options) :-
803
+ select_dict(_{'$dynamicRef':Ref}, Spec, Rest),
804
+ dynamic_type_ref(Ref, Rest, Type, Options).
805
+
806
+ dynamic_type_ref(Ref, Spec, Type, Options) :-
807
+ atom_concat(#, Anchor, Ref),
808
+ \+ sub_atom(Anchor, _, _, _, /),
809
+ option(dynamic_scopes([Self|_]), Options),
810
+ json_fragment(Anchor, Self, RefSubDoc, [ref(relative)|Options]),
811
+ !,
812
+ dynamic_type_ref_2(Anchor, Spec, RefSubDoc, Type, Options).
813
+ dynamic_type_ref(Ref, Spec, Type, Options) :-
814
+ sub_atom(Ref, 0, _, _, '#/'), %" $dynamicRef": "#/<pointer>" is "$ref"
815
+ !,
816
+ type_ref(Ref, Spec, Type, Options).
817
+ dynamic_type_ref(Ref, Spec, Type, Options) :-
818
+ option(base_uri(Base), Options),
819
+ uri_fragment(Ref, Anchor),
820
+ uri_normalized(Ref, Base, URL),
821
+ url_type_doc(URL, _Doc, RefSubDoc, Options),
822
+ !,
823
+ dynamic_type_ref_2(Anchor, Spec, RefSubDoc, Type, Options).
824
+
825
+ dynamic_type_ref(Ref, Spec, Type, Options) :-
826
+ type_ref(Ref, Spec, Type, Options).
827
+
828
+ dynamic_type_ref_2(Anchor, Spec, RefSubDoc, Type, Options) :-
829
+ option(dynamic_scopes([Self|Scopes]), Options),
830
+ ( atom_string(Anchor, RefSubDoc.get('$dynamicAnchor')),
831
+ ( empty_schema(RefSubDoc)
832
+ -> debug(json_schema(dynamic_ref), 'Empty: ~p', [RefSubDoc])
833
+ ; json_contains([Self|Scopes], InScope),
834
+ InScope == RefSubDoc
835
+ -> debug(json_schema(dynamic_ref), 'In scope: ~p', [RefSubDoc])
836
+ ; debug(json_schema(dynamic_ref), 'Non-empty: ~p', [RefSubDoc]),
837
+ fail
838
+ ),
839
+ member(Doc, Scopes),
840
+ json_fragment(Anchor, Doc, SubDoc,
841
+ [ dynamic(Self),
842
+ ref(relative),
843
+ on_error(silent)
844
+ | Options
845
+ ]),
846
+ RefSubDoc \== SubDoc % already resolved to this
847
+ -> true
848
+ ; SubDoc = RefSubDoc
849
+ ),
850
+ join_type_spec(Spec, SubDoc, Combined),
851
+ push_dynamic_scope_option(SubDoc, Options, Options1),
852
+ json_type(Combined, Type, Options1).
853
+
854
+
855
+ %! push_dynamic_scope_option(+Spec, +OptionsIn, =Options)
856
+ %
857
+ % Update the option dynamic_scopes(List) to by pushing the scope Spec.
858
+ % We must push a scope while (1) following a '$ref' or '$dynamicRef'
859
+ % or (2) entering a node with a '$id'. We should not push twice
860
+ % however if we follow to a ref holding a '$id'.
861
+
862
+ push_dynamic_scope_option(JSONSpec, Options0, Options) :-
863
+ option(dynamic_scopes(Scopes0), Options0, []),
864
+ push_dynamic_scope(JSONSpec, Scopes0, Scopes),
865
+ merge_options([dynamic_scopes(Scopes)], Options0, Options).
866
+
867
+ push_dynamic_scope(JSON, Scopes, Scopes) :-
868
+ Scopes = [Current|_],
869
+ JSON == Current, % Already pushed.
870
+ !.
871
+ push_dynamic_scope(JSON, Scopes0, Scopes) :-
872
+ Scopes = [JSON|Scopes0].
873
+
874
+ %! join_type_spec(+Local, +Reffed, -Spec) is det.
875
+ %
876
+ % Local is a dict holding the properties of the '$ref' dict except
877
+ % '$ref' itself. Reffed is the references type. We must combine these
878
+ % to get the final type.
879
+
880
+ join_type_spec(Local, Reffed, Reffed) :-
881
+ empty_schema(Local),
882
+ !.
883
+ join_type_spec(Local, true, Local) :-
884
+ !.
885
+ join_type_spec(_Local, false, false) :-
886
+ !.
887
+ join_type_spec(Local, Reffed, Spec) :-
888
+ Spec = #{allOf: [Reffed,Local]}.
889
+
890
+ %! empty_schema(+Schema) is semidet.
891
+ %
892
+ % True if Schema contains no JSON schema semantics. Currently detected
893
+ % by excluding properties that have no semantics. Possibly we should
894
+ % have a set of properties that do have semantics?
895
+
896
+ empty_schema(_{}) :-
897
+ !.
898
+ empty_schema(Dict) :-
899
+ dict_keys(Dict, Keys),
900
+ maplist(annotation_property, Keys),
901
+ !.
902
+
903
+ %! annotation_property(?Property)
904
+ %
905
+ % True when Property is a property that adds no semantics to the
906
+ % schema node.
907
+
908
+ annotation_property('$id').
909
+ annotation_property('$anchor').
910
+ annotation_property('$dynamicAnchor').
911
+ annotation_property('$schema').
912
+ annotation_property('$comment').
913
+ annotation_property('$vocabulary').
914
+ annotation_property('$recursiveRef').
915
+ annotation_property('$recursiveAnchor').
916
+ annotation_property('title').
917
+ annotation_property('description').
918
+ annotation_property('examples').
919
+
920
+
921
+ %! url_type_doc(+URL, -Doc, -SubDoc, +Options) is semidet.
922
+ %
923
+ % Fetch the JSON document referenced by URL, returning both the full
924
+ % document and the SubDoc pointed at by the (optional) fragment.
925
+
926
+ url_type_doc(URL, Doc, SubDoc, Options) :-
927
+ uri_components(URL, Components),
928
+ uri_data(scheme, Components, file),
929
+ !,
930
+ uri_data(path, Components, FileEnc),
931
+ uri_data(fragment, Components, Fragment),
932
+ uri_data(fragment, Components, _, Components2),
933
+ uri_components(FileURL, Components2),
934
+ uri_encoded(path, File, FileEnc),
935
+ json_read_file(File, Doc),
936
+ json_fragment(Fragment, Doc, SubDoc,
937
+ [ base_uri(FileURL),
938
+ ref(absolute)
939
+ | Options
940
+ ]).
941
+ url_type_doc(URL, Doc, SubDoc, Options) :-
942
+ uri_components(URL, Components),
943
+ uri_data(fragment, Components, Fragment),
944
+ uri_data(fragment, Components, _, Components2),
945
+ uri_components(FetchURL, Components2),
946
+ url_fetch_json(FetchURL, Doc, Options),
947
+ json_fragment(Fragment, Doc, SubDoc,
948
+ [ base_uri(FetchURL),
949
+ ref(absolute)
950
+ | Options
951
+ ]).
952
+
953
+ %! url_fetch_json(+FetchURL:atom, -Doc, +Options) is det.
954
+ %
955
+ % Fetch a JSON document referenced by a URI. This document can be a
956
+ % sub-document using ``$id: URI``.
957
+
958
+ url_fetch_json(FetchURL, Doc, Options) :-
959
+ option(schemas(Schemas), Options),
960
+ open_list_member(_UIR-Schema, Schemas),
961
+ option(base_uri(Base0), Options),
962
+ json_contains(Schema, Doc, Base0, Base),
963
+ is_dict(Doc),
964
+ _{'$id': Id} :< Doc,
965
+ atomic(Id),
966
+ ( Base == FetchURL
967
+ -> true % Fixes refRemote test schema "base URI change - change folder"
968
+ ; uri_normalized(Id, Base, FetchURL)
969
+ ),
970
+ !.
971
+ url_fetch_json(FetchURL, Doc, Options) :-
972
+ option(schemas(Schemas), Options, _),
973
+ ( open_list_member(FetchURL-JSON, Schemas)
974
+ -> Doc = JSON
975
+ ; fake_url(FetchURL)
976
+ -> permission_error(access, url, FetchURL)
977
+ ; load_json_from_http(FetchURL, JSON),
978
+ open_list_add(FetchURL-JSON, Schemas)
979
+ -> Doc = JSON
980
+ ).
981
+
982
+ :- if(current_predicate(http_open/3)).
983
+ load_json_from_http(URL, JSON) :-
984
+ setup_call_cleanup(
985
+ http_open(URL, In, []),
986
+ json_read_dict(In, JSON),
987
+ close(In)).
988
+ :- endif.
989
+ load_json_from_http(URL, _) :-
990
+ permission_error(access, url, URL).
991
+
992
+
993
+ fake_url(URL) :-
994
+ uri_components(URL, Components),
995
+ uri_data(authority, Components, Authority),
996
+ nonvar(Authority),
997
+ uri_authority_components(Authority, AuthComponents),
998
+ uri_authority_data(host, AuthComponents, Host),
999
+ fake_host(Host).
1000
+
1001
+ fake_host('www.example.com').
1002
+ fake_host('example.com').
1003
+
1004
+ %! json_fragment(+Fragment, +Document, -SubDoc, +Options) is semidet.
1005
+ %
1006
+ % Find a sub document from an Fragment locator. If Fragment starts
1007
+ % with `/`, we must follow the property path from the root. Else, we
1008
+ % must look for documents holding ``$anchor`` or ``$dynamicAnchor``.
1009
+ % Specific options:
1010
+ %
1011
+ % - ref(Type)
1012
+ % One of `relative` or `absolute`.
1013
+
1014
+ json_fragment(Fragment, Doc, SubDoc, _) :-
1015
+ var(Fragment),
1016
+ !,
1017
+ SubDoc = Doc.
1018
+ json_fragment(FragmentEnc, Doc, SubDoc, Options) :-
1019
+ uri_encoded(fragment, Fragment, FragmentEnc),
1020
+ atomic_list_concat(SegmentsEnc, /, Fragment),
1021
+ maplist(tilde_unescape, SegmentsEnc, Segments),
1022
+ json_subdoc(Segments, Doc, SubDoc, Options).
1023
+
1024
+ tilde_unescape(In, Out) :-
1025
+ sub_atom(In, _, _, _, ~),
1026
+ !,
1027
+ atom_codes(In, Codes),
1028
+ phrase(tilde_unescape(OutCodes), Codes),
1029
+ atom_codes(Out, OutCodes).
1030
+ tilde_unescape(In, In).
1031
+
1032
+ tilde_unescape([H|T]) -->
1033
+ "~", tunescape(H), !,
1034
+ tilde_unescape(T).
1035
+ tilde_unescape([H|T]) -->
1036
+ [H], !,
1037
+ tilde_unescape(T).
1038
+ tilde_unescape([]) -->
1039
+ [].
1040
+
1041
+ tunescape(0'~) --> "0".
1042
+ tunescape(0'/) --> "1".
1043
+
1044
+ %! json_subdoc(+Segments, +Doc, -SubDoc, +Options) is semidet.
1045
+ %
1046
+ % Find a JSON sub document of Doc according to Segments. If there is
1047
+ % only one segment, we must find an anchored node. Else we must
1048
+ % dereference a path.
1049
+
1050
+ json_subdoc([''], Doc, SubDoc, _Options) =>
1051
+ SubDoc = Doc.
1052
+ json_subdoc([Anchor], Doc, SubDoc, Options) =>
1053
+ json_anchored(Anchor, Doc, SubDoc, Options).
1054
+ json_subdoc([''|Segments], Doc, SubDoc, Options) =>
1055
+ json_extract_path(Segments, Doc, SubDoc, Options).
1056
+
1057
+ json_extract_path([], Doc, Doc, _).
1058
+ json_extract_path([H|T], Doc, Sub, Options) :-
1059
+ ( is_dict(Doc)
1060
+ -> Sub0 = Doc.H
1061
+ ; is_list(Doc),
1062
+ atom_number(H, N),
1063
+ nth0(N, Doc, Sub0)
1064
+ -> true
1065
+ ; option(on_error(silent), Options)
1066
+ -> fail
1067
+ ; existence_error(json_path, H, Doc)
1068
+ ),
1069
+ json_extract_path(T, Sub0, Sub, Options).
1070
+
1071
+ %! json_anchored(+Anchor:atom, +Doc, -SubDoc, +Options) is semidet.
1072
+ %
1073
+ % Find an anchored sub schema. Options
1074
+ %
1075
+ % - ref(Type)
1076
+ % If `relative`, we are dereferencing `#anchor`, which we can find
1077
+ % anywhere in the document. If `absolute`, we must find the anchor
1078
+ % in the referenced document.
1079
+ % - dynamic(Self)
1080
+ % Find a dynamic anchor.
1081
+
1082
+ json_anchored(Anchor, Doc, SubDoc, Options) :-
1083
+ option(base_uri(Base0), Options),
1084
+ option(dynamic(Self), Options, NotDynamic),
1085
+ json_contains(Doc, SubDoc, Base0, Base),
1086
+ is_dict(SubDoc),
1087
+ ( Self == NotDynamic
1088
+ -> ( atom_string(Anchor, SubDoc.get('$anchor'))
1089
+ ; atom_string(Anchor, SubDoc.get('$dynamicAnchor'))
1090
+ )
1091
+ ; \+ ( json_contains(Self, Sub),
1092
+ Sub == SubDoc
1093
+ ),
1094
+ atom_string(Anchor, SubDoc.get('$dynamicAnchor'))
1095
+ ),
1096
+ ( option(ref(relative), Options)
1097
+ -> true
1098
+ ; Base == Base0
1099
+ ),
1100
+ !.
1101
+
1102
+ %! json_contains(+Doc, -SubDoc, +Base0, -Base) is nondet.
1103
+ %
1104
+ % True when SubDoc is a node (possibly a leave) from Doc and Base is
1105
+ % the base URI of SubDoc. This predicate is used to find anchored
1106
+ % nodes.
1107
+ %
1108
+ % @arg Base0 is the context base uri.
1109
+
1110
+ json_contains(Doc, SubDoc, Base0, Base) :-
1111
+ update_base_uri(Doc, Base0, Base1),
1112
+ json_contains_(Doc, SubDoc, Base1, Base).
1113
+
1114
+ json_contains_(Doc, Doc, Base, Base).
1115
+ json_contains_(Doc, SubDoc, Base0, Base) :-
1116
+ is_dict(Doc),
1117
+ !,
1118
+ get_dict(_, Doc, SubDoc0),
1119
+ json_contains(SubDoc0, SubDoc, Base0, Base).
1120
+ json_contains_(Doc, SubDoc, Base0, Base) :-
1121
+ is_list(Doc),
1122
+ member(SubDoc0, Doc),
1123
+ json_contains(SubDoc0, SubDoc, Base0, Base).
1124
+
1125
+ update_base_uri(Doc, Base0, Base) :-
1126
+ is_dict(Doc),
1127
+ Id = Doc.get('$id'),
1128
+ atomic(Id), % Avoid error on property definition in meta schema
1129
+ !,
1130
+ uri_normalized(Id, Base0, Base).
1131
+ update_base_uri(_, Base, Base).
1132
+
1133
+ json_contains(Doc, Doc).
1134
+ json_contains(Doc, SubDoc) :-
1135
+ is_dict(Doc),
1136
+ !,
1137
+ get_dict(_, Doc, SubDoc0),
1138
+ json_contains(SubDoc0, SubDoc).
1139
+ json_contains(Doc, SubDoc) :-
1140
+ is_list(Doc),
1141
+ member(SubDoc0, Doc),
1142
+ json_contains(SubDoc0, SubDoc).
1143
+
1144
+ %! json_read_file(+File, -Term) is det.
1145
+ %
1146
+ % Read a JSON Scheme file.
1147
+
1148
+ json_read_file(File, Term) :-
1149
+ setup_call_cleanup(
1150
+ open(File, read, In, [encoding(utf8)]),
1151
+ json_read_dict(In, Term),
1152
+ close(In)).
1153
+
1154
+ /*******************************
1155
+ * TYPES *
1156
+ *******************************/
1157
+
1158
+ %! api_type(?Name, ?Type, ?Format, ?TypeID)
1159
+ %
1160
+ % Define the formats defined by the OAS.
1161
+ %
1162
+ % @arg Name is unused
1163
+ % @arg Type is the basic JSON type
1164
+ % @arg Format is the JSON schema _format_
1165
+ % @arg TypeID is a local alias for the type that encodes both the
1166
+ % JSON type and format.
1167
+
1168
+ api_type(integer, integer, int32, int32).
1169
+ api_type(long, integer, int64, int64).
1170
+ api_type(long, integer, -, integer).
1171
+ api_type(float, number, float, float).
1172
+ api_type(double, number, double, float).
1173
+ api_type(double, number, -, number).
1174
+ api_type(string, string, -, string).
1175
+ api_type(byte, string, byte, base64).
1176
+ api_type(binary, string, binary, binary).
1177
+ api_type(boolean, boolean, -, boolean).
1178
+ api_type(date, string, date, date).
1179
+ api_type(dateTime, string, 'date-time', date_time).
1180
+ api_type(password, string, password, password).
1181
+ api_type(regex, string, regex, regex).
1182
+ api_type(email, string, email, email).
1183
+ api_type(uri, string, uri, uri).
1184
+ api_type(uri, string, 'uri-reference', uri_reference).
1185
+
1186
+ %! json_check(+Spec, ?JSON, +Options) is semidet.
1187
+ %
1188
+ % Validate a JSON object. Spec is a Prolog representation of the
1189
+ % schema that is optimized for validation. This representation is
1190
+ % derived from JSON data using json_compile_schema/3. Options:
1191
+ %
1192
+ % - on_error(Mode)
1193
+ % What to do if an error is found. Defined modes are
1194
+ % - error
1195
+ % Raise an exception. This is the default. Note that
1196
+ % only the first error is reported this way.
1197
+ % - warning
1198
+ % Print a message
1199
+ % - silent
1200
+ % Fail
1201
+ % - value_string_as(Type)
1202
+ % Same as for json_read/3.
1203
+ %
1204
+ % This predicate is often used through validate_json_dict/3, which
1205
+ % mantains a cached mapping from the JSON Schema to Spec.
1206
+
1207
+ json_check(Spec, In, Options) :-
1208
+ json_check_(Spec, In, Options).
1209
+
1210
+ json_check_silent(Type, In, Options) :-
1211
+ dict_create(Dict, options, Options),
1212
+ json_check(Type, In, [], Dict.put(on_error, silent)).
1213
+
1214
+ json_check_(Spec, In, Options) :-
1215
+ dict_create(Dict, options, Options),
1216
+ json_check(Spec, In, [], Dict).
1217
+
1218
+ %! json_check(+Type, +Data, +Path, +Options:dict) is semidet.
1219
+ %
1220
+ % Check Data to satisfy Type. Path is a list representing a JSON
1221
+ % pointer from the root. The Path is used for error message. By
1222
+ % default, this predicate raises an exception on error. If the option
1223
+ % #{on_error: silent} is present it fails silently.
1224
+ %
1225
+ % @throw error(Error, json_path(Path)).
1226
+
1227
+ json_check(unevaluated_properties(Type, UnevalType), Data, Path, Options) :-
1228
+ is_dict(Data),
1229
+ !,
1230
+ Data2 = Data.put('$evaluated$', []),
1231
+ json_check2(Type, Data2, Path, Options),
1232
+ check_unevaluated(UnevalType, Data2, Path, Options),
1233
+ join_has_evaluated(Data, Data2).
1234
+ json_check(unevaluated_properties(Type, _UnevalType), Data, Path, Options) :-
1235
+ !,
1236
+ json_check2(Type, Data, Path, Options).
1237
+ json_check(unevaluated_items(Type, Uneval), Data, Path, Options) :-
1238
+ !,
1239
+ ( is_list(Data)
1240
+ -> array_type(Type, AType),
1241
+ json_check2(AType, array_evaluated(Data, Evd), Path, Options),
1242
+ check_unevaluated_items(Uneval, Data, Evd, Path, Options)
1243
+ ; Data = array_evaluated(Data2, Evd0)
1244
+ -> array_type(Type, AType),
1245
+ json_check2(AType, array_evaluated(Data2, Evd), Path, Options),
1246
+ check_unevaluated_items(Uneval, Data2, Evd, Path, Options),
1247
+ Evd = Evd0 % join evaluated
1248
+ ; json_check2(Type, Data, Path, Options)
1249
+ ).
1250
+ json_check(Type, Data, Path, Options) :-
1251
+ json_check2(Type, Data, Path, Options).
1252
+
1253
+ json_check2(Dict, In, Path, Options) :- % Explicit types
1254
+ is_dict(Dict),
1255
+ #{type: one_of(TypeNames)} :< Dict,
1256
+ !,
1257
+ ( member(TypeName, TypeNames),
1258
+ Type = Dict.get(TypeName),
1259
+ json_check_silent(Type, In, Options)
1260
+ -> true
1261
+ ; json_error(type_error(oneOf(TypeNames), In), Path, Options)
1262
+ ).
1263
+ json_check2(Dict, In, Path, Options) :- % only constraints
1264
+ is_dict(Dict, How),
1265
+ !,
1266
+ ( integer(In),
1267
+ Type = Dict.get(integer)
1268
+ -> json_check(Type, In, Path, Options)
1269
+ ; number(In),
1270
+ Type = Dict.get(number)
1271
+ -> json_check(Type, In, Path, Options)
1272
+ ; string(In),
1273
+ Type = Dict.get(string)
1274
+ -> json_check(Type, In, Path, Options)
1275
+ ; atom(In),
1276
+ not_json_special(In, Options),
1277
+ Type = Dict.get(string)
1278
+ -> json_check(Type, In, Path, Options)
1279
+ ; atom(In), boolean(In),
1280
+ Type = Dict.get(boolean)
1281
+ -> json_check(Type, In, Path, Options)
1282
+ ; In == null,
1283
+ Type = Dict.get(null)
1284
+ -> json_check(Type, In, Path, Options)
1285
+ ; is_dict(In, _),
1286
+ Type = Dict.get(object)
1287
+ -> json_check(Type, In, Path, Options)
1288
+ ; is_array(In)
1289
+ -> Type = Dict.get(array),
1290
+ json_check(Type, In, Path, Options)
1291
+ ; How == any
1292
+ -> true
1293
+ ; How == untyped
1294
+ -> true
1295
+ ; json_error(type_error(Dict.type, In), Path, Options)
1296
+ ).
1297
+ json_check2(url(URL), In, Path, Options) :-
1298
+ !,
1299
+ ( json_schema(URL, Type)
1300
+ -> json_check(Type, In, Path, Options)
1301
+ ; json_error(existence_error(json_schema, URL), Path, Options)
1302
+ ).
1303
+ json_check2(object(Constraints), In, Path, Options) :-
1304
+ !,
1305
+ json_must_be(object, In, Path, Options),
1306
+ json_check_object_constraints(Constraints, In, Path, Options).
1307
+ json_check2(array(Constraints), array_evaluated(In, Evd), Path, Options) :-
1308
+ !,
1309
+ json_must_be(array, In, Path, Options),
1310
+ json_check_array_constraints(Constraints, In, Evd, Path, Options).
1311
+ json_check2(array(Constraints), In, Path, Options) :-
1312
+ !,
1313
+ json_must_be(array, In, Path, Options),
1314
+ json_check_array_constraints(Constraints, In, _Evd, Path, Options).
1315
+ json_check2(oneOf(Types), In, Path, Options) :-
1316
+ !,
1317
+ ( SOptions = Options.put(on_error, silent),
1318
+ E = error(_,_),
1319
+ append(_, [Type|Rest], Types),
1320
+ catch(json_check(Type, In, Path, SOptions), E, fail)
1321
+ -> ( member(T2, Rest),
1322
+ catch(json_check(T2, In, Path, SOptions), E, fail)
1323
+ -> json_error(type_error(oneOf(Types), In), Path, Options)
1324
+ ; true
1325
+ )
1326
+ ; json_error(type_error(oneOf(Types), In), Path, Options)
1327
+ ).
1328
+ json_check2(allOf(Types), In, Path, Options) :-
1329
+ !,
1330
+ maplist(json_check_in_type(In, Path, Options), Types).
1331
+ json_check2(anyOf(Types), In, Path, Options) :-
1332
+ !,
1333
+ ( SOptions = Options.put(on_error, silent),
1334
+ ( is_dict(In),
1335
+ _ = In.get('$evaluated$')
1336
+ -> include(try_check(In, Path, SOptions), Types, Satisfied),
1337
+ Satisfied \== []
1338
+ ; In = array_evaluated(_, _)
1339
+ -> include(try_check(In, Path, SOptions), Types, Satisfied),
1340
+ Satisfied \== []
1341
+ ; E = error(_,_),
1342
+ member(Type, Types),
1343
+ catch(json_check(Type, In, Path, SOptions), E, fail)
1344
+ )
1345
+ -> true
1346
+ ; json_error(type_error(anyOf(Types), In), Path, Options)
1347
+ ).
1348
+ json_check2(not(Type), In, Path, Options) :-
1349
+ !,
1350
+ ( SOptions = Options.put(on_error, silent),
1351
+ E = error(_,_),
1352
+ \+ catch(json_check(Type, In, Path, SOptions), E, fail)
1353
+ -> true
1354
+ ; json_error(type_error(not(Type), In), Path, Options)
1355
+ ).
1356
+ json_check2(if_then_else(If,Then,Else), In, Path, Options) :-
1357
+ ( json_check_silent(If, In, Options)
1358
+ -> json_check(Then, In, Path, Options)
1359
+ ; json_check(Else, In, Path, Options)
1360
+ ).
1361
+ json_check2(enum(Values), In, Path, Options) :-
1362
+ !,
1363
+ ( memberchk(In, Values)
1364
+ -> true
1365
+ ; member(Candidate, Values),
1366
+ json_equal(Candidate, In, Options)
1367
+ -> true
1368
+ ; json_error(domain_error(oneof(Values), In), Path, Options)
1369
+ ).
1370
+ json_check2(numeric(Type, Constraints), In, Path, Options) :-
1371
+ !,
1372
+ json_must_be(Type, In, Path, Options),
1373
+ maplist(numeric_constraint(In, Path, Options), Constraints).
1374
+ json_check2(null, In, Path, Options) :-
1375
+ !,
1376
+ json_must_be(null, In, Path, Options).
1377
+ json_check2(any, _, _, _) :-
1378
+ !.
1379
+ json_check2(untyped, _, _, _) :-
1380
+ !.
1381
+ json_check2(nothing, In, Path, Options) :-
1382
+ !,
1383
+ json_error(type_error(nothing, In), Path, Options).
1384
+ json_check2(string(Format, Constraints), In, Path, Options) :-
1385
+ !,
1386
+ json_must_be(Format, In, Path, Options),
1387
+ maplist(check_string_constraint(In, Path, Options), Constraints).
1388
+ json_check2(const(Const), In, Path, Options) :-
1389
+ !,
1390
+ ( json_equal(Const, In, Options)
1391
+ -> true
1392
+ ; json_error(type_error(const(Const), In), Path, Options)
1393
+ ).
1394
+ json_check2(Type, In, Path, Options) :-
1395
+ json_must_be(Type, In, Path, Options).
1396
+
1397
+ json_check_in_type(In, Path, Options, Type) :-
1398
+ json_check(Type, In, Path, Options).
1399
+
1400
+ %! try_check(+In, +Path, +Options, +Type) is semidet.
1401
+ %
1402
+ % Helper for anyOf(Types). It we are running under
1403
+ % unevaluatedProperties testing, we must evaluate all types of the
1404
+ % anyOf.
1405
+
1406
+ try_check(In, Path, Options, Type) :-
1407
+ E = error(_,_),
1408
+ catch(json_check(Type, In, Path, Options), E, fail).
1409
+
1410
+ %! json_check_array_items(+Array:list, -Evaluated, +Type, +Path, +Nth,
1411
+ %! +Options)
1412
+ %
1413
+ % Verify that all elements of Array satisfy Type.
1414
+
1415
+ json_check_array_items([], [], _Type, _Path, _Nth, _Options).
1416
+ json_check_array_items([H|T], [EH|Evd], Type, Path, Nth, Options) :-
1417
+ json_check(Type, H, [Nth|Path], Options),
1418
+ has_evaluated_item(Type, EH),
1419
+ Nth1 is Nth+1,
1420
+ json_check_array_items(T, Evd, Type, Path, Nth1, Options).
1421
+
1422
+ has_evaluated_item(untyped, _Bool) =>
1423
+ true.
1424
+ has_evaluated_item(_Type, Bool) =>
1425
+ Bool = true.
1426
+
1427
+ %! json_check_prefix_items(+Types, +ArrayIn, -Evaluated, -ArrayLeft,
1428
+ %! -LeftEvaluated, +Path, +Nth0, -Nth,
1429
+ %! +Options) is det.
1430
+
1431
+ json_check_prefix_items([], Items0, Evd0, Items, Evd, _Path, Nth0, Nth, _Options) =>
1432
+ Items = Items0,
1433
+ Evd = Evd0,
1434
+ Nth = Nth0.
1435
+ json_check_prefix_items([Type|Types], [H|T], Evd0, Rest, Evd, Path,
1436
+ Nth0, Nth, Options) =>
1437
+ json_check(Type, H, [Nth0|Path], Options),
1438
+ Evd0 = [EH|Evd1],
1439
+ has_evaluated_item(Type, EH),
1440
+ Nth1 is Nth0+1,
1441
+ json_check_prefix_items(Types, T, Evd1, Rest, Evd, Path, Nth1, Nth, Options).
1442
+ json_check_prefix_items(_Types, [], Evd0, Items, Evd, _Path, Nth0, Nth, _) =>
1443
+ Items = [],
1444
+ Evd = Evd0,
1445
+ Nth = Nth0.
1446
+
1447
+ numeric_constraint(In, Path, Options, Constraint) :-
1448
+ ( number_in_domain(Constraint, In)
1449
+ -> true
1450
+ ; json_error(domain_error(Constraint, In), Path, Options)
1451
+ ).
1452
+
1453
+ number_in_domain(>=(Min), Value) :-
1454
+ Value >= Min.
1455
+ number_in_domain(>(Min), Value) :-
1456
+ Value > Min.
1457
+ number_in_domain(=<(Max), Value) :-
1458
+ Value =< Max.
1459
+ number_in_domain(<(Max), Value) :-
1460
+ Value < Max.
1461
+ number_in_domain(multiple_of(N), Value) :-
1462
+ ( integer(N),
1463
+ integer(Value)
1464
+ -> Value mod N =:= 0
1465
+ ; Times is Value/N,
1466
+ Times =:= round(Times)
1467
+ ).
1468
+
1469
+ check_string_constraint(String, Path, Options, minLength(MinLen)) =>
1470
+ string_length(String, Len),
1471
+ ( Len >= MinLen
1472
+ -> true
1473
+ ; json_error(domain_error(min_length(MinLen), String), Path, Options)
1474
+ ).
1475
+ check_string_constraint(String, Path, Options, maxLength(MaxLen)) =>
1476
+ string_length(String, Len),
1477
+ ( Len =< MaxLen
1478
+ -> true
1479
+ ; json_error(domain_error(max_length(MaxLen), String), Path, Options)
1480
+ ).
1481
+ check_string_constraint(String, Path, Options, pattern(Regex)) =>
1482
+ ( re_match(Regex, String)
1483
+ -> true
1484
+ ; json_error(domain_error(pattern(Regex), String), Path, Options)
1485
+ ).
1486
+ check_string_constraint(_String, _Path, _Options, contentEncoding(_Enc)) =>
1487
+ true.
1488
+ check_string_constraint(_String, _Path, _Options, contentMediaType(_Type)) =>
1489
+ true.
1490
+ check_string_constraint(_String, _Path, _Options, contentSchema(_Schema)) =>
1491
+ true.
1492
+
1493
+
1494
+ %! check_property(+Obj, +Path, +Options, +PropertyDescr)
1495
+
1496
+ check_property(Obj, Path, Options, p(Name,Type,Req)) :-
1497
+ ( get_dict(Name, Obj, Value)
1498
+ -> json_check(Type, Value, [Name|Path], Options),
1499
+ has_evaluated(Obj, Name)
1500
+ ; Req == false
1501
+ -> true
1502
+ ; json_error(existence_error(json_property, Name), Path, Options)
1503
+ ).
1504
+
1505
+ %! json_check_object_constraints(+Constraints, +In, +Path, +Options)
1506
+ %
1507
+ % Verify Constraints on the object In.
1508
+
1509
+ json_check_object_constraints(Constraints, In, Path, Options) :-
1510
+ maplist(json_check_object_constraint_(In, Path, Options), Constraints).
1511
+
1512
+ json_check_object_constraint_(In, Path, Options, Constraint) :-
1513
+ json_check_object_constraint(Constraint, In, Path, Options).
1514
+
1515
+ json_check_object_constraint(properties(Props, implicit, []),
1516
+ In, Path, Options) =>
1517
+ maplist(check_property(In, Path, Options), Props).
1518
+ json_check_object_constraint(properties(Props, Addn, Patterns),
1519
+ In, Path, Options) =>
1520
+ dict_pairs(In, _, Pairs0),
1521
+ delete(Pairs0, '$evaluated$'-_, Pairs),
1522
+ obj_properties(Pairs, Props, In, Addn, Patterns, Path, Options).
1523
+ json_check_object_constraint(minProperties(Min), In, Path, Options) =>
1524
+ property_count(In, Count),
1525
+ ( Count >= Min
1526
+ -> true
1527
+ ; json_error(json_error(minProperties(Min), In), Path, Options)
1528
+ ).
1529
+ json_check_object_constraint(maxProperties(Max), In, Path, Options) =>
1530
+ property_count(In, Count),
1531
+ ( Count =< Max
1532
+ -> true
1533
+ ; json_error(json_error(maxProperties(Max), In), Path, Options)
1534
+ ).
1535
+ json_check_object_constraint(dependentRequired(Dict), In, Path, Options) =>
1536
+ mapdict(check_dependent_required(In, Path, Options), Dict).
1537
+ json_check_object_constraint(dependentSchemas(Dict), In, Path, Options) =>
1538
+ mapdict(check_dependent_schema(In, Path, Options), Dict).
1539
+ json_check_object_constraint(propertyNames(Type), In, Path, Options) =>
1540
+ dict_keys(In, Properties),
1541
+ maplist(check_property_name(Type, Path, Options), Properties).
1542
+
1543
+ property_count(Data, Count) :-
1544
+ dict_keys(Data, Keys),
1545
+ delete(Keys, '$evaluated$', Keys1),
1546
+ length(Keys1, Count).
1547
+
1548
+ %! check_dependent_required(+In, +Path, +Options, +Property, +Required)
1549
+ %
1550
+ % If In has Property, it also must have all properties in Required.
1551
+
1552
+ check_dependent_required(In, Path, Options, Prop, Required) :-
1553
+ ( _ = In.get(Prop)
1554
+ -> maplist(check_dependent_prop_exists(In, Path, Options), Required)
1555
+ ; true
1556
+ ).
1557
+
1558
+ check_dependent_prop_exists(In, Path, Options, Required) :-
1559
+ ( _ = In.get(Required)
1560
+ -> true
1561
+ ; json_error(existence_error(json_property, Required), Path, Options)
1562
+ ).
1563
+
1564
+ %! check_dependent_schema(+In, +Path, +Options, +Prop, +Type) is det.
1565
+ %
1566
+ % Verify that In satisfies Type if In has Prop.
1567
+
1568
+ check_dependent_schema(In, Path, Options, Prop, Type) :-
1569
+ ( _ = In.get(Prop)
1570
+ -> json_check(Type, In, Path, Options)
1571
+ ; true
1572
+ ).
1573
+
1574
+ %! check_property_name(+Type, +Path, +Options, +PropertyName) is det.
1575
+ %
1576
+ % Verify that PropertyName satisfies Type.
1577
+
1578
+ check_property_name(_Type, _Path, _Options, '$evaluated$') :-
1579
+ !.
1580
+ check_property_name(Type, Path, Options, PropertyName) :-
1581
+ atom_string(PropertyName, String), % make sure specials are handled
1582
+ ( json_check_silent(Type, String, Options)
1583
+ -> true
1584
+ ; json_error(json_error(propertyNames(Type), String), Path, Options)
1585
+ ).
1586
+
1587
+ %! obj_properties(+InPairs, +Spec, +Data:dict, +Additional,
1588
+ %! +Patterns, +Path, +Options)
1589
+ %
1590
+ % Verify that all object properties constraints are respected.
1591
+ %
1592
+ % @arg InPairs is a set of `Prop-Value` pairs created from the data
1593
+ % object to tbe verified.
1594
+ % @arg Spec is a list of p(Name,Type,Required) triples that specify
1595
+ % the constraints for the property `Name`.
1596
+ % @arg Additional is either a boolean or type(Type), indicating there
1597
+ % may be any number of additional properties as long as thety satisfy
1598
+ % `Type`.
1599
+
1600
+ obj_properties([], Spec, _In, _Addn, _Pats, Path, Options) :-
1601
+ !,
1602
+ check_missing(Spec, Path, Options).
1603
+ obj_properties(More, [], In, Addn, Pats, Path, Options) :-
1604
+ !,
1605
+ maplist(additional_property(Addn, In, Pats, Path, Options), More).
1606
+ obj_properties([NV|T], PL, In, Addn, Pats, Path, Options) :-
1607
+ PL = [p(P,_,_)|_],
1608
+ NV = N-_,
1609
+ N @< P,
1610
+ !,
1611
+ additional_property(Addn, In, Pats, Path, Options, NV),
1612
+ obj_properties(T, PL, In, Addn, Pats, Path, Options).
1613
+ obj_properties([N-V|T], [p(N,Type,_Req)|PT], In, Addn, Pats, Path, Options) :-
1614
+ !,
1615
+ json_check(Type, V, [N|Path], Options),
1616
+ has_evaluated(In, N),
1617
+ obj_properties(T, PT, In, Addn, Pats, Path, Options).
1618
+ obj_properties(T, [p(N,_Type,Req)|PT], In, Addn, Pats, Path, Options) :-
1619
+ ( Req == false
1620
+ -> true
1621
+ ; json_error(existence_error(json_property, N), Path, Options)
1622
+ ),
1623
+ obj_properties(T, PT, In, Addn, Pats, Path, Options).
1624
+
1625
+ additional_property(Addn, In, Pats, Path, Options, Name-Value) :-
1626
+ ( append(_, [Pattern-Type|Rest], Pats),
1627
+ re_match(Pattern, Name)
1628
+ -> json_check(Type, Value, [Name|Path], Options),
1629
+ forall(( member(Pattern2-Type2, Rest),
1630
+ re_match(Pattern2, Name)
1631
+ ),
1632
+ json_check(Type2, Value, [Name|Path], Options)),
1633
+ has_evaluated(In, Name)
1634
+ ; Addn == false
1635
+ -> json_error(json_error(additional_property, Name), Path, Options)
1636
+ ; Addn = type(Type)
1637
+ -> json_check(Type, Value, [Name|Path], Options),
1638
+ has_evaluated(In, Name)
1639
+ ; Addn == true % specified "additionalProperties": true
1640
+ -> has_evaluated(In, Name)
1641
+ ; Addn == implicit % Not explicitly specified
1642
+ -> true
1643
+ ; assertion(false)
1644
+ ).
1645
+
1646
+ check_missing([], _, _).
1647
+ check_missing([p(N,_Type,Req)|T], Path, Options) :-
1648
+ ( Req == false
1649
+ -> check_missing(T, Path, Options)
1650
+ ; json_error(existence_error(json_property, N), Path, Options)
1651
+ ).
1652
+
1653
+ %! has_evaluated(!Data:dict, +Prop:atom) is det.
1654
+ %
1655
+ % Add Prop to Data.'$evaluated$' using destructive assignment.
1656
+
1657
+ has_evaluated(Data, Prop) :-
1658
+ Evaluated = Data.get('$evaluated$'),
1659
+ \+ memberchk(Prop, Evaluated),
1660
+ !,
1661
+ b_set_dict('$evaluated$', Data, [Prop|Evaluated]).
1662
+ has_evaluated(_, _).
1663
+
1664
+ %! join_has_evaluated(!Outer, +Inner) is det.
1665
+ %
1666
+ % Inner was subject to unevaluatedProperties evaluation. If it
1667
+ % evaluated properties and Outer is also subject to
1668
+ % unevaluatedProperties, we must join the evaluated properties.
1669
+
1670
+ join_has_evaluated(Outer, Inner) :-
1671
+ OuterEvaluated = Outer.get('$evaluated$'),
1672
+ InnerEvaluated = Inner.get('$evaluated$'),
1673
+ !,
1674
+ append(InnerEvaluated, OuterEvaluated, All),
1675
+ sort(All, New),
1676
+ b_set_dict('$evaluated$', Outer, New).
1677
+ join_has_evaluated(_, _).
1678
+
1679
+
1680
+ %! check_unevaluated(+Type, +Data:dict, +Path, +Options)
1681
+ %
1682
+ % Check unevaluated properties of Data against Type.
1683
+
1684
+ check_unevaluated(Type, Data, Path, Options) :-
1685
+ Evaluated = Data.'$evaluated$',
1686
+ dict_pairs(Data, _, Pairs),
1687
+ include(unevaluated(Evaluated), Pairs, UnEvaluated),
1688
+ ( UnEvaluated == []
1689
+ -> true
1690
+ ; Type == false
1691
+ -> pairs_keys(UnEvaluated, PropNames),
1692
+ json_error(json_error(unevaluatedProperties, PropNames), Path, Options)
1693
+ ; Type = type(T)
1694
+ -> maplist(json_check_pair(T, Path, Options), UnEvaluated)
1695
+ ; Type == true
1696
+ -> pairs_keys(UnEvaluated, PropNames),
1697
+ maplist(has_evaluated(Data), PropNames)
1698
+ ).
1699
+
1700
+ unevaluated(_, '$evaluated$'-_) =>
1701
+ fail.
1702
+ unevaluated(Evaluated, Prop-_) =>
1703
+ \+ memberchk(Prop, Evaluated).
1704
+
1705
+ json_check_pair(Type, Path, Options, Prop-Value) :-
1706
+ json_check(Type, Value, [Prop|Path], Options).
1707
+
1708
+
1709
+ /*******************************
1710
+ * ARRAYS *
1711
+ *******************************/
1712
+
1713
+ array_type(untyped, Type) =>
1714
+ Type = array([]).
1715
+ array_type(Type0, Type) =>
1716
+ Type = Type0.
1717
+
1718
+ is_array(array_evaluated(_,_)) => true.
1719
+ is_array(List) => is_list(List).
1720
+
1721
+ %! json_check_array_constraints(+Constraints:list, +In,
1722
+ %! -Evaluated, +Path, +Options)
1723
+ %
1724
+ % Check the constraints on an array as generated from the schema by
1725
+ % array_property/3.
1726
+
1727
+ json_check_array_constraints(Constraints, In, Evd, Path, Options) :-
1728
+ maplist(json_check_array_constraint_r(In, Evd, Path, Options), Constraints).
1729
+
1730
+ json_check_array_constraint_r(In, Evd, Path, Options, Constraint) :-
1731
+ json_check_array_constraint(Constraint, In, Evd, Path, Options).
1732
+
1733
+ json_check_array_constraint(minItems(MinLen), Array, _Evd, Path, Options) =>
1734
+ length(Array, Len),
1735
+ ( Len >= MinLen
1736
+ -> true
1737
+ ; json_error(json_error(minItems(MinLen), Array), Path, Options)
1738
+ ).
1739
+ json_check_array_constraint(maxItems(MaxLen), Array, _Evd, Path, Options) =>
1740
+ length(Array, Len),
1741
+ ( Len =< MaxLen
1742
+ -> true
1743
+ ; json_error(json_error(maxItems(MaxLen), Array), Path, Options)
1744
+ ).
1745
+ json_check_array_constraint(items(PrefixItems, Type), Array,
1746
+ Evd, Path, Options) =>
1747
+ json_check_prefix_items(PrefixItems, Array, Evd, Rest, REvd, Path, 0, I, Options),
1748
+ json_check_array_items(Rest, REvd, Type, Path, I, Options).
1749
+ json_check_array_constraint(uniqueItems(Unique), Array,
1750
+ _Evd, Path, Options) =>
1751
+ json_check_array_unique(Unique, Array, Path, Options).
1752
+ json_check_array_constraint(contains(Type, Min, Max),
1753
+ Array, Evd, Path, Options) =>
1754
+ eval_contained(Array, Evd, Matched, Type, Options),
1755
+ length(Matched, NMatched),
1756
+ ( between(Min, Max, NMatched)
1757
+ -> true
1758
+ ; Min == 1, Max == infinite
1759
+ -> json_error(json_error(contains(Type), Array), Path, Options)
1760
+ ; Max == infinite
1761
+ -> json_error(json_error(minContains(Min,Type), Array), Path, Options)
1762
+ ; NMatched < Min
1763
+ -> json_error(json_error(minContains(Min,Type), Array), Path, Options)
1764
+ ; json_error(json_error(maxContains(Max, Type), Array), Path, Options)
1765
+ ).
1766
+
1767
+ %! eval_contained(+Array, -Evaluated, -Matched, +Type, +Options)
1768
+ %
1769
+ % True when Matched contains the items of Array that match Type.
1770
+
1771
+ eval_contained([], [], [], _Type, _Options).
1772
+ eval_contained([H|T], [true|EvdT], [H|MatchT], Type, Options) :-
1773
+ json_check_silent(Type, H, Options),
1774
+ !,
1775
+ eval_contained(T, EvdT, MatchT, Type, Options).
1776
+ eval_contained([_|T], [_|EvdT], Match, Type, Options) :-
1777
+ eval_contained(T, EvdT, Match, Type, Options).
1778
+
1779
+ json_check_array_unique(false, _List, _Path, _Option) :-
1780
+ !.
1781
+ json_check_array_unique(true, List, Path, Options) :-
1782
+ msort(List, Sorted),
1783
+ ( append(_, [X,Y|_], Sorted),
1784
+ X =@= Y
1785
+ -> json_error(json_error(unique, List), Path, Options)
1786
+ ; true
1787
+ ).
1788
+
1789
+ %! check_unevaluated_items(+Uneval, +In, +Evd, +Path, +Options)
1790
+ %
1791
+ % Check unevaluatedItems.
1792
+ %
1793
+ % @arg In is the array
1794
+ % @arg Evd is a, possibly partial, list holding `true` for any
1795
+ % element that has been evaluated and a variable for the non-evaluated
1796
+ % elements.
1797
+
1798
+ check_unevaluated_items(true, In, Evd, _Path, _Options) =>
1799
+ maplist(mark_evaluated_item, In, Evd).
1800
+ check_unevaluated_items(false, In, Evd, Path, Options) =>
1801
+ unevaluated_items(In, Evd, Unevaluated),
1802
+ ( Unevaluated == []
1803
+ -> true
1804
+ ; json_error(json_error(unevaluatedItems, Unevaluated), Path, Options)
1805
+ ).
1806
+ check_unevaluated_items(type(Type), In, Evd, Path, Options) =>
1807
+ eval_unevaluated_items(In, Evd, Type, 0, Path, Options).
1808
+
1809
+ mark_evaluated_item(_, true).
1810
+
1811
+ unevaluated_items([], _, Unevaluated) =>
1812
+ Unevaluated = [].
1813
+ unevaluated_items([_|T], [true|Evd], Unevaluated) =>
1814
+ unevaluated_items(T, Evd, Unevaluated).
1815
+ unevaluated_items([H|T], [_|Evd], Unevaluated) =>
1816
+ Unevaluated = [H|UT],
1817
+ unevaluated_items(T, Evd, UT).
1818
+ unevaluated_items(Items, Evd, Unevaluated), var(Evd) =>
1819
+ Unevaluated = Items.
1820
+
1821
+ %! eval_unevaluated_items(+Array, ?Evaluated, +Type, +Nth, +Path,
1822
+ %! +Options)
1823
+ %
1824
+ % Evaluate the non-evaluated elements of Array.
1825
+
1826
+ eval_unevaluated_items([], _Evd, _Type, _N, _Path, _Options) =>
1827
+ true.
1828
+ eval_unevaluated_items([_|T], [true|Evd], Type, Nth, Path, Options) =>
1829
+ Nth1 is Nth+1,
1830
+ eval_unevaluated_items(T, Evd, Type, Nth1, Path, Options).
1831
+ eval_unevaluated_items([H|T], [U|Evd], Type, Nth, Path, Options) =>
1832
+ json_check(Type, H, [Nth|Path], Options),
1833
+ U = true,
1834
+ Nth1 is Nth+1,
1835
+ eval_unevaluated_items(T, Evd, Type, Nth1, Path, Options).
1836
+ eval_unevaluated_items([H|T], Evd, Type, Nth, Path, Options), var(Evd) =>
1837
+ json_check(Type, H, [Nth|Path], Options),
1838
+ Evd = [true|ET],
1839
+ Nth1 is Nth+1,
1840
+ eval_unevaluated_items(T, ET, Type, Nth1, Path, Options).
1841
+
1842
+ %! json_equal(+Const, +In, +Options) is semidet.
1843
+ %
1844
+ % True if Const and In represent the same JSON data. This is normally
1845
+ % Prolog equivalence, but we take care of possible different
1846
+ % representations. Also, numbers are compared by value, so 2.0 equals
1847
+ % 2 in JSON.
1848
+
1849
+ json_equal(Const, Const, _) =>
1850
+ true.
1851
+ json_equal(J1, J2, _), number(J1), number(J2) =>
1852
+ J1 =:= J2.
1853
+ json_equal(@(X), X, _) =>
1854
+ true.
1855
+ json_equal(X, @(X), _) =>
1856
+ true.
1857
+ json_equal(S, A, _), string(S), atom(A) =>
1858
+ atom_string(S, A).
1859
+ json_equal(A, S, _), string(S), atom(A) =>
1860
+ atom_string(S, A).
1861
+ json_equal(J1, J2, _), atomic(J1) =>
1862
+ J1 == J2.
1863
+ json_equal(J1, J2, _), atomic(J2) =>
1864
+ J1 == J2.
1865
+ json_equal(J1, J2, Options), is_dict(J1), is_dict(J2) =>
1866
+ dict_pairs(J1, _, P10),
1867
+ dict_pairs(J2, _, P20),
1868
+ delete(P10, '$evaluated$'-_, P1),
1869
+ delete(P20, '$evaluated$'-_, P2),
1870
+ maplist(equal_kv(Options), P1, P2).
1871
+ json_equal(J1, J2, Options), is_list(J1), is_list(J2) =>
1872
+ maplist(equal_v(Options), J1, J2).
1873
+ json_equal(_, _, _) =>
1874
+ false.
1875
+
1876
+ equal_kv(Options, K-V1, K-V2) :-
1877
+ json_equal(V1, V2, Options).
1878
+
1879
+ equal_v(Options, V1, V2) :-
1880
+ json_equal(V1, V2, Options).
1881
+
1882
+
1883
+ %! json_error(+Error, +Path, +Options)
1884
+ %
1885
+ %
1886
+
1887
+ json_error(Error, Path, Options) :-
1888
+ ErrorTerm = error(Error, json_path(Path)),
1889
+ option(on_error(Mode), Options, error),
1890
+ ( Mode == error
1891
+ -> throw(ErrorTerm)
1892
+ ; Mode == silent
1893
+ -> fail
1894
+ ; print_message(warning, ErrorTerm)
1895
+ ).
1896
+
1897
+ %! json_must_be(+Format, +Value, +Path, +Options)
1898
+ %
1899
+
1900
+ json_must_be(date, Value, Path, Options) :-
1901
+ !,
1902
+ ( catch(xsd_time_string(_,
1903
+ 'http://www.w3.org/2001/XMLSchema#date',
1904
+ Value),
1905
+ error(_, _),
1906
+ fail)
1907
+ -> true
1908
+ ; json_error(type_error(date, Value), Path, Options)
1909
+ ).
1910
+ json_must_be(date_time, Value, Path, Options) :-
1911
+ !,
1912
+ ( catch(xsd_time_string(_,
1913
+ 'http://www.w3.org/2001/XMLSchema#dateTime',
1914
+ Value),
1915
+ error(_, _),
1916
+ fail)
1917
+ -> true
1918
+ ; json_error(type_error(date_time, Value), Path, Options)
1919
+ ).
1920
+ json_must_be(base64, Value, Path, Options) :-
1921
+ !,
1922
+ ( catch(base64(_, Value), error(_,_), fail)
1923
+ -> true
1924
+ ; json_error(type_error(base64, Value), Path, Options)
1925
+ ).
1926
+ json_must_be(email, Value, Path, Options) :-
1927
+ !,
1928
+ ( is_json_string(Value, Options)
1929
+ -> regex(email, Re),
1930
+ re_match(Re, Value)
1931
+ ; json_error(type_error(email, Value), Path, Options)
1932
+ ).
1933
+ json_must_be(uri, Value, Path, Options) :-
1934
+ !,
1935
+ ( is_json_string(Value, Options),
1936
+ is_absolute_url(Value)
1937
+ -> true
1938
+ ; json_error(type_error(uri, Value), Path, Options)
1939
+ ).
1940
+ json_must_be(string, Value, Path, Options) :-
1941
+ !,
1942
+ ( is_json_string(Value, Options)
1943
+ -> true
1944
+ ; json_error(type_error(string, Value), Path, Options)
1945
+ ).
1946
+ json_must_be(null, Value, Path, Options) :-
1947
+ !,
1948
+ ( Value == null
1949
+ -> true
1950
+ ; json_error(type_error(null, Value), Path, Options)
1951
+ ).
1952
+ json_must_be(JSONType, Value, Path, Options) :-
1953
+ ( ( json_type_alias(JSONType, Type)
1954
+ -> true
1955
+ ; Type = JSONType
1956
+ ),
1957
+ json_is_of_type(Type, Value)
1958
+ -> true
1959
+ ; json_error(type_error(JSONType, Value), Path, Options)
1960
+ ).
1961
+
1962
+ %! regex(+Format, -Re) is det.
1963
+ %
1964
+ % Quick and dirty way to validate some formats. Probably better to
1965
+ % write as DCG.
1966
+
1967
+ :- det(regex/2).
1968
+ regex(email, '^[A-Za-z0-9.!#$%&\'*+/=?^_`{|}~-]+\c
1969
+ @[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?(?:\\.[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?)*$').
1970
+
1971
+ json_is_of_type(between(Low, High), Value), integer(Low), integer(High) =>
1972
+ json_is_integer(Value),
1973
+ Value >= Low, Value =< High.
1974
+ json_is_of_type(integer, Value) =>
1975
+ json_is_integer(Value).
1976
+ json_is_of_type(Type, Value) =>
1977
+ is_of_type(Type, Value).
1978
+
1979
+ json_is_integer(Value) :-
1980
+ integer(Value),
1981
+ !.
1982
+ json_is_integer(Value) :-
1983
+ float(Value),
1984
+ 0.0 =:= float_fractional_part(Value).
1985
+
1986
+ %! is_json_string(+Value, +Options) is semidet.
1987
+ %
1988
+ % True if Value is a valid Prolog term for a JSON string. Prolog
1989
+ % strings are always valid. Atoms if they are not `null`, `true` or
1990
+ % `false`, depending on Options.
1991
+
1992
+ is_json_string(Value, _) :-
1993
+ string(Value),
1994
+ !.
1995
+ is_json_string(Value, Options) :-
1996
+ atom(Value),
1997
+ not_json_special(Value, Options).
1998
+
1999
+ json_type_alias(int32, between(-2147483648, 2147483647)).
2000
+ json_type_alias(int64, between(-9223372036854775808, 9223372036854775807)).
2001
+ json_type_alias(binary, string).
2002
+ json_type_alias(password, string).
2003
+ json_type_alias(uri, string).
2004
+ json_type_alias(uri_reference, string).
2005
+ json_type_alias(regex, string).
2006
+ json_type_alias(object, dict).
2007
+ json_type_alias(array, list).
2008
+
2009
+ not_json_special(Atom, _Options) :-
2010
+ \+ json_special(Atom),
2011
+ !.
2012
+ not_json_special(_Atom, Options) :-
2013
+ option(value_string_as(atom), Options, string).
2014
+
2015
+ json_special(true).
2016
+ json_special(false).
2017
+ json_special(null).
2018
+
2019
+ boolean(true).
2020
+ boolean(false).
2021
+
2022
+ open_list_member(_, Var) :-
2023
+ var(Var),
2024
+ !,
2025
+ fail.
2026
+ open_list_member(E, [E|_]).
2027
+ open_list_member(E, [_|T]) :-
2028
+ open_list_member(E, T).
2029
+
2030
+ open_list_add(E, List) :-
2031
+ memberchk(E, List).
2032
+
2033
+ %! select_dict(+Select:dict, +From:dict, -Rest:dict, +Default) is semidet.
2034
+ %
2035
+ % As select_dict/3, providing defaults. Succeeds if keys from Select
2036
+ % appear either (or both) in From and Default.
2037
+
2038
+ select_dict(Select, From, Rest, Default) :-
2039
+ select_dict(Select, Default.put(From), Rest).
2040
+
2041
+ %! findall_vars(?Skeleton, :Goal, +Preserve, =List) is det.
2042
+ %
2043
+ % Same as findall/3, but rebinds variables from the context that
2044
+ % appear in Preserve.
2045
+
2046
+ findall_vars(Skeleton, Goal, Preserve, List) :-
2047
+ term_variables(Preserve, Vars),
2048
+ V =.. [v|Vars],
2049
+ findall(V-Skeleton, Goal, Pairs),
2050
+ maplist(fa_rebind(V), Pairs, List).
2051
+
2052
+ fa_rebind(V, V-Sol, Sol).
2053
+
2054
+ uri_fragment(URI, Fragment) :-
2055
+ uri_components(URI, Components),
2056
+ uri_data(fragment, Components, Fragment),
2057
+ nonvar(Fragment).
2058
+
2059
+ /*******************************
2060
+ * MESSAGES *
2061
+ *******************************/
2062
+
2063
+ :- multifile
2064
+ prolog:message_context//1,
2065
+ prolog:error_message//1.
2066
+
2067
+ prolog:message_context(json_path(Path)) -->
2068
+ [ nl, ' in JSON object at '-[] ],
2069
+ path(Path).
2070
+
2071
+ path([]) -->
2072
+ [ '/'-[] ].
2073
+ path([H|T]) -->
2074
+ ( {T==[]}
2075
+ -> []
2076
+ ; path(T)
2077
+ ),
2078
+ [ '/~w'-[H] ].
2079
+
2080
+ prolog:error_message(json_error(Error, Context)) -->
2081
+ json_error_message(Error, Context).
2082
+
2083
+ json_error_message(unique, _List) -->
2084
+ [ 'Array contains duplicate items'-[] ].
2085
+ json_error_message(length(0,Max), List) -->
2086
+ { length(List, Len) },
2087
+ [ 'Array contains more than ~D items (found ~D)'-[Max, Len] ].
2088
+ json_error_message(length(Min,infinite), List) -->
2089
+ { length(List, Len) },
2090
+ [ 'Array contains less than ~D items (found ~D)'-[Min, Len] ].
2091
+ json_error_message(length(Min,Max), List) -->
2092
+ { length(List, Len) },
2093
+ [ 'Array must contain betweem ~D and ~D items (found ~D)'-[Min,Max,Len] ].
2094
+ json_error_message(additional_property, Name) -->
2095
+ [ 'Property ~p is not allowed'-[Name] ].
2096
+ json_error_message(additional_items, Extra) -->
2097
+ [ 'Additional item ~p is not allowed'-[Extra] ].
2098
+ json_error_message(unevaluatedProperties, PropNames) -->
2099
+ [ 'Unevaluated properties: ~p'-[PropNames] ].
2100
+ json_error_message(unevaluatedItems, Items) -->
2101
+ [ 'Unevaluated items: ~p'-[Items] ].
2102
+ json_error_message(minContains(Min,Type), _Array) -->
2103
+ [ 'Array contains less than ~D items of type ~p'-[Min,Type] ].
2104
+ json_error_message(maxContains(Min,Type), _Array) -->
2105
+ [ 'Array contains more than ~D items of type ~p'-[Min,Type] ].
2106
+ json_error_message(minProperties(Min), _Object) -->
2107
+ [ 'Object must have at least ~D properties'-[Min] ].
2108
+ json_error_message(maxProperties(Max), _Object) -->
2109
+ [ 'Object must have at most ~D properties'-[Max] ].
2110
+ json_error_message(propertyNames(_Type), Key) -->
2111
+ [ 'Invalid property name: ~p'-[Key] ].