test-json-schema-to-grammar.cpp 40 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292
  1. #ifdef NDEBUG
  2. #undef NDEBUG
  3. #endif
  4. #include <cassert>
  5. #include <fstream>
  6. #include <sstream>
  7. #include <regex>
  8. #include "json-schema-to-grammar.h"
  9. #include "grammar-parser.h"
  10. static std::string trim(const std::string & source) {
  11. std::string s(source);
  12. s.erase(0,s.find_first_not_of(" \n\r\t"));
  13. s.erase(s.find_last_not_of(" \n\r\t")+1);
  14. return std::regex_replace(s, std::regex("(^|\n)[ \t]+"), "$1");
  15. }
  16. enum TestCaseStatus {
  17. SUCCESS,
  18. FAILURE
  19. };
  20. struct TestCase {
  21. TestCaseStatus expected_status;
  22. std::string name;
  23. std::string schema;
  24. std::string expected_grammar;
  25. void _print_failure_header() const {
  26. fprintf(stderr, "#\n# Test '%s' failed.\n#\n%s\n", name.c_str(), schema.c_str());
  27. }
  28. void verify(const std::string & actual_grammar) const {
  29. if (trim(actual_grammar) != trim(expected_grammar)) {
  30. _print_failure_header();
  31. fprintf(stderr, "# EXPECTED:\n%s\n# ACTUAL:\n%s\n", expected_grammar.c_str(), actual_grammar.c_str());
  32. assert(false);
  33. }
  34. }
  35. void verify_expectation_parseable() const {
  36. try {
  37. auto state = grammar_parser::parse(expected_grammar.c_str());
  38. if (state.symbol_ids.find("root") == state.symbol_ids.end()) {
  39. throw std::runtime_error("Grammar failed to parse:\n" + expected_grammar);
  40. }
  41. } catch (const std::runtime_error & ex) {
  42. _print_failure_header();
  43. fprintf(stderr, "# GRAMMAR ERROR: %s\n", ex.what());
  44. assert(false);
  45. }
  46. }
  47. void verify_status(TestCaseStatus status) const {
  48. if (status != expected_status) {
  49. _print_failure_header();
  50. fprintf(stderr, "# EXPECTED STATUS: %s\n", expected_status == SUCCESS ? "SUCCESS" : "FAILURE");
  51. fprintf(stderr, "# ACTUAL STATUS: %s\n", status == SUCCESS ? "SUCCESS" : "FAILURE");
  52. assert(false);
  53. }
  54. }
  55. };
  56. static void write(const std::string & file, const std::string & content) {
  57. std::ofstream f;
  58. f.open(file.c_str());
  59. f << content.c_str();
  60. f.close();
  61. }
  62. static std::string read(const std::string & file) {
  63. std::ostringstream actuals;
  64. actuals << std::ifstream(file.c_str()).rdbuf();
  65. return actuals.str();
  66. }
  67. static void test_all(const std::string & lang, std::function<void(const TestCase &)> runner) {
  68. fprintf(stderr, "#\n# Testing JSON schema conversion (%s)\n#\n", lang.c_str());
  69. auto test = [&](const TestCase & tc) {
  70. fprintf(stderr, "- %s%s\n", tc.name.c_str(), tc.expected_status == FAILURE ? " (failure expected)" : "");
  71. runner(tc);
  72. };
  73. test({
  74. SUCCESS,
  75. "min 0",
  76. R"""({
  77. "type": "integer",
  78. "minimum": 0
  79. })""",
  80. R"""(
  81. root ::= ([0] | [1-9] [0-9]{0,15}) space
  82. space ::= | " " | "\n" [ \t]{0,20}
  83. )"""
  84. });
  85. test({
  86. SUCCESS,
  87. "min 1",
  88. R"""({
  89. "type": "integer",
  90. "minimum": 1
  91. })""",
  92. R"""(
  93. root ::= ([1-9] [0-9]{0,15}) space
  94. space ::= | " " | "\n" [ \t]{0,20}
  95. )"""
  96. });
  97. test({
  98. SUCCESS,
  99. "min 3",
  100. R"""({
  101. "type": "integer",
  102. "minimum": 3
  103. })""",
  104. R"""(
  105. root ::= ([1-2] [0-9]{1,15} | [3-9] [0-9]{0,15}) space
  106. space ::= | " " | "\n" [ \t]{0,20}
  107. )"""
  108. });
  109. test({
  110. SUCCESS,
  111. "min 9",
  112. R"""({
  113. "type": "integer",
  114. "minimum": 9
  115. })""",
  116. R"""(
  117. root ::= ([1-8] [0-9]{1,15} | [9] [0-9]{0,15}) space
  118. space ::= | " " | "\n" [ \t]{0,20}
  119. )"""
  120. });
  121. test({
  122. SUCCESS,
  123. "min 10",
  124. R"""({
  125. "type": "integer",
  126. "minimum": 10
  127. })""",
  128. R"""(
  129. root ::= ([1] ([0-9]{1,15}) | [2-9] [0-9]{1,15}) space
  130. space ::= | " " | "\n" [ \t]{0,20}
  131. )"""
  132. });
  133. test({
  134. SUCCESS,
  135. "min 25",
  136. R"""({
  137. "type": "integer",
  138. "minimum": 25
  139. })""",
  140. R"""(
  141. root ::= ([1] [0-9]{2,15} | [2] ([0-4] [0-9]{1,14} | [5-9] [0-9]{0,14}) | [3-9] [0-9]{1,15}) space
  142. space ::= | " " | "\n" [ \t]{0,20}
  143. )"""
  144. });
  145. test({
  146. SUCCESS,
  147. "max 30",
  148. R"""({
  149. "type": "integer",
  150. "maximum": 30
  151. })""",
  152. R"""(
  153. root ::= ("-" [1-9] [0-9]{0,15} | [0-9] | ([1-2] [0-9] | [3] "0")) space
  154. space ::= | " " | "\n" [ \t]{0,20}
  155. )"""
  156. });
  157. test({
  158. SUCCESS,
  159. "min -5",
  160. R"""({
  161. "type": "integer",
  162. "minimum": -5
  163. })""",
  164. R"""(
  165. root ::= ("-" ([0-5]) | [0] | [1-9] [0-9]{0,15}) space
  166. space ::= | " " | "\n" [ \t]{0,20}
  167. )"""
  168. });
  169. test({
  170. SUCCESS,
  171. "min -123",
  172. R"""({
  173. "type": "integer",
  174. "minimum": -123
  175. })""",
  176. R"""(
  177. root ::= ("-" ([0-9] | ([1-8] [0-9] | [9] [0-9]) | "1" ([0-1] [0-9] | [2] [0-3])) | [0] | [1-9] [0-9]{0,15}) space
  178. space ::= | " " | "\n" [ \t]{0,20}
  179. )"""
  180. });
  181. test({
  182. SUCCESS,
  183. "max -5",
  184. R"""({
  185. "type": "integer",
  186. "maximum": -5
  187. })""",
  188. R"""(
  189. root ::= ("-" ([0-4] [0-9]{1,15} | [5-9] [0-9]{0,15})) space
  190. space ::= | " " | "\n" [ \t]{0,20}
  191. )"""
  192. });
  193. test({
  194. SUCCESS,
  195. "max 1",
  196. R"""({
  197. "type": "integer",
  198. "maximum": 1
  199. })""",
  200. R"""(
  201. root ::= ("-" [1-9] [0-9]{0,15} | [0-1]) space
  202. space ::= | " " | "\n" [ \t]{0,20}
  203. )"""
  204. });
  205. test({
  206. SUCCESS,
  207. "max 100",
  208. R"""({
  209. "type": "integer",
  210. "maximum": 100
  211. })""",
  212. R"""(
  213. root ::= ("-" [1-9] [0-9]{0,15} | [0-9] | ([1-8] [0-9] | [9] [0-9]) | "100") space
  214. space ::= | " " | "\n" [ \t]{0,20}
  215. )"""
  216. });
  217. test({
  218. SUCCESS,
  219. "min 0 max 23",
  220. R"""({
  221. "type": "integer",
  222. "minimum": 0,
  223. "maximum": 23
  224. })""",
  225. R"""(
  226. root ::= ([0-9] | ([1] [0-9] | [2] [0-3])) space
  227. space ::= | " " | "\n" [ \t]{0,20}
  228. )"""
  229. });
  230. test({
  231. SUCCESS,
  232. "min 15 max 300",
  233. R"""({
  234. "type": "integer",
  235. "minimum": 15,
  236. "maximum": 300
  237. })""",
  238. R"""(
  239. root ::= (([1] ([5-9]) | [2-9] [0-9]) | ([1-2] [0-9]{2} | [3] "00")) space
  240. space ::= | " " | "\n" [ \t]{0,20}
  241. )"""
  242. });
  243. test({
  244. SUCCESS,
  245. "min 5 max 30",
  246. R"""({
  247. "type": "integer",
  248. "minimum": 5,
  249. "maximum": 30
  250. })""",
  251. R"""(
  252. root ::= ([5-9] | ([1-2] [0-9] | [3] "0")) space
  253. space ::= | " " | "\n" [ \t]{0,20}
  254. )"""
  255. });
  256. test({
  257. SUCCESS,
  258. "min -123 max 42",
  259. R"""({
  260. "type": "integer",
  261. "minimum": -123,
  262. "maximum": 42
  263. })""",
  264. R"""(
  265. root ::= ("-" ([0-9] | ([1-8] [0-9] | [9] [0-9]) | "1" ([0-1] [0-9] | [2] [0-3])) | [0-9] | ([1-3] [0-9] | [4] [0-2])) space
  266. space ::= | " " | "\n" [ \t]{0,20}
  267. )"""
  268. });
  269. test({
  270. SUCCESS,
  271. "min -10 max 10",
  272. R"""({
  273. "type": "integer",
  274. "minimum": -10,
  275. "maximum": 10
  276. })""",
  277. R"""(
  278. root ::= ("-" ([0-9] | "10") | [0-9] | "10") space
  279. space ::= | " " | "\n" [ \t]{0,20}
  280. )"""
  281. });
  282. test({
  283. FAILURE,
  284. "unknown type",
  285. R"""({
  286. "type": "kaboom"
  287. })""",
  288. ""
  289. });
  290. test({
  291. FAILURE,
  292. "invalid type",
  293. R"""({
  294. "type": 123
  295. })""",
  296. ""
  297. });
  298. test({
  299. SUCCESS,
  300. "empty schema (object)",
  301. "{}",
  302. R"""(
  303. array ::= "[" space ( value ("," space value)* )? "]" space
  304. boolean ::= ("true" | "false") space
  305. char ::= [^"\\\x7F\x00-\x1F] | [\\] (["\\bfnrt] | "u" [0-9a-fA-F]{4})
  306. decimal-part ::= [0-9]{1,16}
  307. integral-part ::= [0] | [1-9] [0-9]{0,15}
  308. null ::= "null" space
  309. number ::= ("-"? integral-part) ("." decimal-part)? ([eE] [-+]? integral-part)? space
  310. object ::= "{" space ( string ":" space value ("," space string ":" space value)* )? "}" space
  311. root ::= object
  312. space ::= | " " | "\n" [ \t]{0,20}
  313. string ::= "\"" char* "\"" space
  314. value ::= object | array | string | number | boolean | null
  315. )"""
  316. });
  317. test({
  318. SUCCESS,
  319. "exotic formats",
  320. R"""({
  321. "items": [
  322. { "format": "date" },
  323. { "format": "uuid" },
  324. { "format": "time" },
  325. { "format": "date-time" }
  326. ]
  327. })""",
  328. R"""(
  329. date ::= [0-9]{4} "-" ( "0" [1-9] | "1" [0-2] ) "-" ( "0" [1-9] | [1-2] [0-9] | "3" [0-1] )
  330. date-string ::= "\"" date "\"" space
  331. date-time ::= date "T" time
  332. date-time-string ::= "\"" date-time "\"" space
  333. root ::= "[" space tuple-0 "," space uuid "," space tuple-2 "," space tuple-3 "]" space
  334. space ::= | " " | "\n" [ \t]{0,20}
  335. time ::= ([01] [0-9] | "2" [0-3]) ":" [0-5] [0-9] ":" [0-5] [0-9] ( "." [0-9]{3} )? ( "Z" | ( "+" | "-" ) ( [01] [0-9] | "2" [0-3] ) ":" [0-5] [0-9] )
  336. time-string ::= "\"" time "\"" space
  337. tuple-0 ::= date-string
  338. tuple-2 ::= time-string
  339. tuple-3 ::= date-time-string
  340. uuid ::= "\"" [0-9a-fA-F]{8} "-" [0-9a-fA-F]{4} "-" [0-9a-fA-F]{4} "-" [0-9a-fA-F]{4} "-" [0-9a-fA-F]{12} "\"" space
  341. )"""
  342. });
  343. test({
  344. SUCCESS,
  345. "string",
  346. R"""({
  347. "type": "string"
  348. })""",
  349. R"""(
  350. char ::= [^"\\\x7F\x00-\x1F] | [\\] (["\\bfnrt] | "u" [0-9a-fA-F]{4})
  351. root ::= "\"" char* "\"" space
  352. space ::= | " " | "\n" [ \t]{0,20}
  353. )"""
  354. });
  355. test({
  356. SUCCESS,
  357. "string w/ min length 1",
  358. R"""({
  359. "type": "string",
  360. "minLength": 1
  361. })""",
  362. R"""(
  363. char ::= [^"\\\x7F\x00-\x1F] | [\\] (["\\bfnrt] | "u" [0-9a-fA-F]{4})
  364. root ::= "\"" char+ "\"" space
  365. space ::= | " " | "\n" [ \t]{0,20}
  366. )"""
  367. });
  368. test({
  369. SUCCESS,
  370. "string w/ min length 3",
  371. R"""({
  372. "type": "string",
  373. "minLength": 3
  374. })""",
  375. R"""(
  376. char ::= [^"\\\x7F\x00-\x1F] | [\\] (["\\bfnrt] | "u" [0-9a-fA-F]{4})
  377. root ::= "\"" char{3,} "\"" space
  378. space ::= | " " | "\n" [ \t]{0,20}
  379. )"""
  380. });
  381. test({
  382. SUCCESS,
  383. "string w/ max length",
  384. R"""({
  385. "type": "string",
  386. "maxLength": 3
  387. })""",
  388. R"""(
  389. char ::= [^"\\\x7F\x00-\x1F] | [\\] (["\\bfnrt] | "u" [0-9a-fA-F]{4})
  390. root ::= "\"" char{0,3} "\"" space
  391. space ::= | " " | "\n" [ \t]{0,20}
  392. )"""
  393. });
  394. test({
  395. SUCCESS,
  396. "string w/ min & max length",
  397. R"""({
  398. "type": "string",
  399. "minLength": 1,
  400. "maxLength": 4
  401. })""",
  402. R"""(
  403. char ::= [^"\\\x7F\x00-\x1F] | [\\] (["\\bfnrt] | "u" [0-9a-fA-F]{4})
  404. root ::= "\"" char{1,4} "\"" space
  405. space ::= | " " | "\n" [ \t]{0,20}
  406. )"""
  407. });
  408. test({
  409. SUCCESS,
  410. "boolean",
  411. R"""({
  412. "type": "boolean"
  413. })""",
  414. R"""(
  415. root ::= ("true" | "false") space
  416. space ::= | " " | "\n" [ \t]{0,20}
  417. )"""
  418. });
  419. test({
  420. SUCCESS,
  421. "integer",
  422. R"""({
  423. "type": "integer"
  424. })""",
  425. R"""(
  426. integral-part ::= [0] | [1-9] [0-9]{0,15}
  427. root ::= ("-"? integral-part) space
  428. space ::= | " " | "\n" [ \t]{0,20}
  429. )"""
  430. });
  431. test({
  432. SUCCESS,
  433. "string const",
  434. R"""({
  435. "const": "foo"
  436. })""",
  437. R"""(
  438. root ::= "\"foo\"" space
  439. space ::= | " " | "\n" [ \t]{0,20}
  440. )"""
  441. });
  442. test({
  443. SUCCESS,
  444. "non-string const",
  445. R"""({
  446. "const": 123
  447. })""",
  448. R"""(
  449. root ::= "123" space
  450. space ::= | " " | "\n" [ \t]{0,20}
  451. )"""
  452. });
  453. test({
  454. SUCCESS,
  455. "non-string enum",
  456. R"""({
  457. "enum": ["red", "amber", "green", null, 42, ["foo"]]
  458. })""",
  459. R"""(
  460. root ::= ("\"red\"" | "\"amber\"" | "\"green\"" | "null" | "42" | "[\"foo\"]") space
  461. space ::= | " " | "\n" [ \t]{0,20}
  462. )"""
  463. });
  464. test({
  465. SUCCESS,
  466. "string array",
  467. R"""({
  468. "type": "array",
  469. "prefixItems": { "type": "string" }
  470. })""",
  471. R"""(
  472. char ::= [^"\\\x7F\x00-\x1F] | [\\] (["\\bfnrt] | "u" [0-9a-fA-F]{4})
  473. root ::= "[" space (string ("," space string)*)? "]" space
  474. space ::= | " " | "\n" [ \t]{0,20}
  475. string ::= "\"" char* "\"" space
  476. )"""
  477. });
  478. test({
  479. SUCCESS,
  480. "nullable string array",
  481. R"""({
  482. "type": ["array", "null"],
  483. "prefixItems": { "type": "string" }
  484. })""",
  485. R"""(
  486. alternative-0 ::= "[" space (string ("," space string)*)? "]" space
  487. char ::= [^"\\\x7F\x00-\x1F] | [\\] (["\\bfnrt] | "u" [0-9a-fA-F]{4})
  488. null ::= "null" space
  489. root ::= alternative-0 | null
  490. space ::= | " " | "\n" [ \t]{0,20}
  491. string ::= "\"" char* "\"" space
  492. )"""
  493. });
  494. test({
  495. SUCCESS,
  496. "tuple1",
  497. R"""({
  498. "prefixItems": [{ "type": "string" }]
  499. })""",
  500. R"""(
  501. char ::= [^"\\\x7F\x00-\x1F] | [\\] (["\\bfnrt] | "u" [0-9a-fA-F]{4})
  502. root ::= "[" space string "]" space
  503. space ::= | " " | "\n" [ \t]{0,20}
  504. string ::= "\"" char* "\"" space
  505. )"""
  506. });
  507. test({
  508. SUCCESS,
  509. "tuple2",
  510. R"""({
  511. "prefixItems": [{ "type": "string" }, { "type": "number" }]
  512. })""",
  513. R"""(
  514. char ::= [^"\\\x7F\x00-\x1F] | [\\] (["\\bfnrt] | "u" [0-9a-fA-F]{4})
  515. decimal-part ::= [0-9]{1,16}
  516. integral-part ::= [0] | [1-9] [0-9]{0,15}
  517. number ::= ("-"? integral-part) ("." decimal-part)? ([eE] [-+]? integral-part)? space
  518. root ::= "[" space string "," space number "]" space
  519. space ::= | " " | "\n" [ \t]{0,20}
  520. string ::= "\"" char* "\"" space
  521. )"""
  522. });
  523. test({
  524. SUCCESS,
  525. "number",
  526. R"""({
  527. "type": "number"
  528. })""",
  529. R"""(
  530. decimal-part ::= [0-9]{1,16}
  531. integral-part ::= [0] | [1-9] [0-9]{0,15}
  532. root ::= ("-"? integral-part) ("." decimal-part)? ([eE] [-+]? integral-part)? space
  533. space ::= | " " | "\n" [ \t]{0,20}
  534. )"""
  535. });
  536. test({
  537. SUCCESS,
  538. "minItems",
  539. R"""({
  540. "items": {
  541. "type": "boolean"
  542. },
  543. "minItems": 2
  544. })""",
  545. R"""(
  546. boolean ::= ("true" | "false") space
  547. root ::= "[" space boolean ("," space boolean)+ "]" space
  548. space ::= | " " | "\n" [ \t]{0,20}
  549. )"""
  550. });
  551. test({
  552. SUCCESS,
  553. "maxItems 1",
  554. R"""({
  555. "items": {
  556. "type": "boolean"
  557. },
  558. "maxItems": 1
  559. })""",
  560. R"""(
  561. boolean ::= ("true" | "false") space
  562. root ::= "[" space boolean? "]" space
  563. space ::= | " " | "\n" [ \t]{0,20}
  564. )"""
  565. });
  566. test({
  567. SUCCESS,
  568. "maxItems 2",
  569. R"""({
  570. "items": {
  571. "type": "boolean"
  572. },
  573. "maxItems": 2
  574. })""",
  575. R"""(
  576. boolean ::= ("true" | "false") space
  577. root ::= "[" space (boolean ("," space boolean)?)? "]" space
  578. space ::= | " " | "\n" [ \t]{0,20}
  579. )"""
  580. });
  581. test({
  582. SUCCESS,
  583. "min + maxItems",
  584. R"""({
  585. "items": {
  586. "type": ["number", "integer"]
  587. },
  588. "minItems": 3,
  589. "maxItems": 5
  590. })""",
  591. R"""(
  592. decimal-part ::= [0-9]{1,16}
  593. integer ::= ("-"? integral-part) space
  594. integral-part ::= [0] | [1-9] [0-9]{0,15}
  595. item ::= number | integer
  596. number ::= ("-"? integral-part) ("." decimal-part)? ([eE] [-+]? integral-part)? space
  597. root ::= "[" space item ("," space item){2,4} "]" space
  598. space ::= | " " | "\n" [ \t]{0,20}
  599. )"""
  600. });
  601. test({
  602. SUCCESS,
  603. "min + max items with min + max values across zero",
  604. R"""({
  605. "items": {
  606. "type": "integer",
  607. "minimum": -12,
  608. "maximum": 207
  609. },
  610. "minItems": 3,
  611. "maxItems": 5
  612. })""",
  613. R"""(
  614. item ::= ("-" ([0-9] | "1" [0-2]) | [0-9] | ([1-8] [0-9] | [9] [0-9]) | ([1] [0-9]{2} | [2] "0" [0-7])) space
  615. root ::= "[" space item ("," space item){2,4} "]" space
  616. space ::= | " " | "\n" [ \t]{0,20}
  617. )"""
  618. });
  619. test({
  620. SUCCESS,
  621. "min + max items with min + max values",
  622. R"""({
  623. "items": {
  624. "type": "integer",
  625. "minimum": 12,
  626. "maximum": 207
  627. },
  628. "minItems": 3,
  629. "maxItems": 5
  630. })""",
  631. R"""(
  632. item ::= (([1] ([2-9]) | [2-9] [0-9]) | ([1] [0-9]{2} | [2] "0" [0-7])) space
  633. root ::= "[" space item ("," space item){2,4} "]" space
  634. space ::= | " " | "\n" [ \t]{0,20}
  635. )"""
  636. });
  637. test({
  638. SUCCESS,
  639. "simple regexp",
  640. R"""({
  641. "type": "string",
  642. "pattern": "^abc?d*efg+(hij)?kl$"
  643. })""",
  644. R"""(
  645. root ::= "\"" "ab" "c"? "d"* "ef" "g"+ ("hij")? "kl" "\"" space
  646. space ::= | " " | "\n" [ \t]{0,20}
  647. )"""
  648. });
  649. test({
  650. SUCCESS,
  651. "regexp escapes",
  652. R"""({
  653. "type": "string",
  654. "pattern": "^\\[\\]\\{\\}\\(\\)\\|\\+\\*\\?$"
  655. })""",
  656. R"""(
  657. root ::= "\"" "[]{}()|+*?" "\"" space
  658. space ::= | " " | "\n" [ \t]{0,20}
  659. )"""
  660. });
  661. test({
  662. SUCCESS,
  663. "regexp quote",
  664. R"""({
  665. "type": "string",
  666. "pattern": "^\"$"
  667. })""",
  668. R"""(
  669. root ::= "\"" "\"" "\"" space
  670. space ::= | " " | "\n" [ \t]{0,20}
  671. )"""
  672. });
  673. test({
  674. SUCCESS,
  675. "regexp",
  676. R"""({
  677. "type": "string",
  678. "pattern": "^(\\([0-9]{1,3}\\))?[0-9]{3}-[0-9]{4} a{3,5}nd...$"
  679. })""",
  680. R"""(
  681. dot ::= [^\x0A\x0D]
  682. root ::= "\"" ("(" root-1{1,3} ")")? root-1{3,3} "-" root-1{4,4} " " "a"{3,5} "nd" dot dot dot "\"" space
  683. root-1 ::= [0-9]
  684. space ::= | " " | "\n" [ \t]{0,20}
  685. )"""
  686. });
  687. test({
  688. SUCCESS,
  689. "required props in original order",
  690. R"""({
  691. "type": "object",
  692. "properties": {
  693. "b": {"type": "string"},
  694. "c": {"type": "string"},
  695. "a": {"type": "string"}
  696. },
  697. "required": [
  698. "a",
  699. "b",
  700. "c"
  701. ],
  702. "additionalProperties": false,
  703. "definitions": {}
  704. })""",
  705. R"""(
  706. a-kv ::= "\"a\"" space ":" space string
  707. b-kv ::= "\"b\"" space ":" space string
  708. c-kv ::= "\"c\"" space ":" space string
  709. char ::= [^"\\\x7F\x00-\x1F] | [\\] (["\\bfnrt] | "u" [0-9a-fA-F]{4})
  710. root ::= "{" space b-kv "," space c-kv "," space a-kv "}" space
  711. space ::= | " " | "\n" [ \t]{0,20}
  712. string ::= "\"" char* "\"" space
  713. )"""
  714. });
  715. test({
  716. SUCCESS,
  717. "1 optional prop",
  718. R"""({
  719. "properties": {
  720. "a": {
  721. "type": "string"
  722. }
  723. },
  724. "additionalProperties": false
  725. })""",
  726. R"""(
  727. a-kv ::= "\"a\"" space ":" space string
  728. char ::= [^"\\\x7F\x00-\x1F] | [\\] (["\\bfnrt] | "u" [0-9a-fA-F]{4})
  729. root ::= "{" space (a-kv )? "}" space
  730. space ::= | " " | "\n" [ \t]{0,20}
  731. string ::= "\"" char* "\"" space
  732. )"""
  733. });
  734. test({
  735. SUCCESS,
  736. "N optional props",
  737. R"""({
  738. "properties": {
  739. "a": {"type": "string"},
  740. "b": {"type": "string"},
  741. "c": {"type": "string"}
  742. },
  743. "additionalProperties": false
  744. })""",
  745. R"""(
  746. a-kv ::= "\"a\"" space ":" space string
  747. a-rest ::= ( "," space b-kv )? b-rest
  748. b-kv ::= "\"b\"" space ":" space string
  749. b-rest ::= ( "," space c-kv )?
  750. c-kv ::= "\"c\"" space ":" space string
  751. char ::= [^"\\\x7F\x00-\x1F] | [\\] (["\\bfnrt] | "u" [0-9a-fA-F]{4})
  752. root ::= "{" space (a-kv a-rest | b-kv b-rest | c-kv )? "}" space
  753. space ::= | " " | "\n" [ \t]{0,20}
  754. string ::= "\"" char* "\"" space
  755. )"""
  756. });
  757. test({
  758. SUCCESS,
  759. "required + optional props each in original order",
  760. R"""({
  761. "properties": {
  762. "b": {"type": "string"},
  763. "a": {"type": "string"},
  764. "d": {"type": "string"},
  765. "c": {"type": "string"}
  766. },
  767. "required": ["a", "b"],
  768. "additionalProperties": false
  769. })""",
  770. R"""(
  771. a-kv ::= "\"a\"" space ":" space string
  772. b-kv ::= "\"b\"" space ":" space string
  773. c-kv ::= "\"c\"" space ":" space string
  774. char ::= [^"\\\x7F\x00-\x1F] | [\\] (["\\bfnrt] | "u" [0-9a-fA-F]{4})
  775. d-kv ::= "\"d\"" space ":" space string
  776. d-rest ::= ( "," space c-kv )?
  777. root ::= "{" space b-kv "," space a-kv ( "," space ( d-kv d-rest | c-kv ) )? "}" space
  778. space ::= | " " | "\n" [ \t]{0,20}
  779. string ::= "\"" char* "\"" space
  780. )"""
  781. });
  782. test({
  783. SUCCESS,
  784. "additional props",
  785. R"""({
  786. "type": "object",
  787. "additionalProperties": {"type": "array", "items": {"type": "number"}}
  788. })""",
  789. R"""(
  790. additional-kv ::= string ":" space additional-value
  791. additional-value ::= "[" space (number ("," space number)*)? "]" space
  792. char ::= [^"\\\x7F\x00-\x1F] | [\\] (["\\bfnrt] | "u" [0-9a-fA-F]{4})
  793. decimal-part ::= [0-9]{1,16}
  794. integral-part ::= [0] | [1-9] [0-9]{0,15}
  795. number ::= ("-"? integral-part) ("." decimal-part)? ([eE] [-+]? integral-part)? space
  796. root ::= "{" space (additional-kv ( "," space additional-kv )* )? "}" space
  797. space ::= | " " | "\n" [ \t]{0,20}
  798. string ::= "\"" char* "\"" space
  799. )"""
  800. });
  801. test({
  802. SUCCESS,
  803. "additional props (true)",
  804. R"""({
  805. "type": "object",
  806. "additionalProperties": true
  807. })""",
  808. R"""(
  809. array ::= "[" space ( value ("," space value)* )? "]" space
  810. boolean ::= ("true" | "false") space
  811. char ::= [^"\\\x7F\x00-\x1F] | [\\] (["\\bfnrt] | "u" [0-9a-fA-F]{4})
  812. decimal-part ::= [0-9]{1,16}
  813. integral-part ::= [0] | [1-9] [0-9]{0,15}
  814. null ::= "null" space
  815. number ::= ("-"? integral-part) ("." decimal-part)? ([eE] [-+]? integral-part)? space
  816. object ::= "{" space ( string ":" space value ("," space string ":" space value)* )? "}" space
  817. root ::= object
  818. space ::= | " " | "\n" [ \t]{0,20}
  819. string ::= "\"" char* "\"" space
  820. value ::= object | array | string | number | boolean | null
  821. )"""
  822. });
  823. test({
  824. SUCCESS,
  825. "additional props (implicit)",
  826. R"""({
  827. "type": "object"
  828. })""",
  829. R"""(
  830. array ::= "[" space ( value ("," space value)* )? "]" space
  831. boolean ::= ("true" | "false") space
  832. char ::= [^"\\\x7F\x00-\x1F] | [\\] (["\\bfnrt] | "u" [0-9a-fA-F]{4})
  833. decimal-part ::= [0-9]{1,16}
  834. integral-part ::= [0] | [1-9] [0-9]{0,15}
  835. null ::= "null" space
  836. number ::= ("-"? integral-part) ("." decimal-part)? ([eE] [-+]? integral-part)? space
  837. object ::= "{" space ( string ":" space value ("," space string ":" space value)* )? "}" space
  838. root ::= object
  839. space ::= | " " | "\n" [ \t]{0,20}
  840. string ::= "\"" char* "\"" space
  841. value ::= object | array | string | number | boolean | null
  842. )"""
  843. });
  844. test({
  845. SUCCESS,
  846. "empty w/o additional props",
  847. R"""({
  848. "type": "object",
  849. "additionalProperties": false
  850. })""",
  851. R"""(
  852. root ::= "{" space "}" space
  853. space ::= | " " | "\n" [ \t]{0,20}
  854. )"""
  855. });
  856. test({
  857. SUCCESS,
  858. "required + additional props",
  859. R"""({
  860. "type": "object",
  861. "properties": {
  862. "a": {"type": "number"}
  863. },
  864. "required": ["a"],
  865. "additionalProperties": {"type": "string"}
  866. })""",
  867. R"""(
  868. a-kv ::= "\"a\"" space ":" space number
  869. additional-k ::= ["] ( [a] char+ | [^"a] char* )? ["] space
  870. additional-kv ::= additional-k ":" space string
  871. char ::= [^"\\\x7F\x00-\x1F] | [\\] (["\\bfnrt] | "u" [0-9a-fA-F]{4})
  872. decimal-part ::= [0-9]{1,16}
  873. integral-part ::= [0] | [1-9] [0-9]{0,15}
  874. number ::= ("-"? integral-part) ("." decimal-part)? ([eE] [-+]? integral-part)? space
  875. root ::= "{" space a-kv ( "," space ( additional-kv ( "," space additional-kv )* ) )? "}" space
  876. space ::= | " " | "\n" [ \t]{0,20}
  877. string ::= "\"" char* "\"" space
  878. )"""
  879. });
  880. test({
  881. SUCCESS,
  882. "optional + additional props",
  883. R"""({
  884. "type": "object",
  885. "properties": {
  886. "a": {"type": "number"}
  887. },
  888. "additionalProperties": {"type": "number"}
  889. })""",
  890. R"""(
  891. a-kv ::= "\"a\"" space ":" space number
  892. a-rest ::= ( "," space additional-kv )*
  893. additional-k ::= ["] ( [a] char+ | [^"a] char* )? ["] space
  894. additional-kv ::= additional-k ":" space number
  895. char ::= [^"\\\x7F\x00-\x1F] | [\\] (["\\bfnrt] | "u" [0-9a-fA-F]{4})
  896. decimal-part ::= [0-9]{1,16}
  897. integral-part ::= [0] | [1-9] [0-9]{0,15}
  898. number ::= ("-"? integral-part) ("." decimal-part)? ([eE] [-+]? integral-part)? space
  899. root ::= "{" space (a-kv a-rest | additional-kv ( "," space additional-kv )* )? "}" space
  900. space ::= | " " | "\n" [ \t]{0,20}
  901. )"""
  902. });
  903. test({
  904. SUCCESS,
  905. "required + optional + additional props",
  906. R"""({
  907. "type": "object",
  908. "properties": {
  909. "and": {"type": "number"},
  910. "also": {"type": "number"}
  911. },
  912. "required": ["and"],
  913. "additionalProperties": {"type": "number"}
  914. })""",
  915. R"""(
  916. additional-k ::= ["] ( [a] ([l] ([s] ([o] char+ | [^"o] char*) | [^"s] char*) | [n] ([d] char+ | [^"d] char*) | [^"ln] char*) | [^"a] char* )? ["] space
  917. additional-kv ::= additional-k ":" space number
  918. also-kv ::= "\"also\"" space ":" space number
  919. also-rest ::= ( "," space additional-kv )*
  920. and-kv ::= "\"and\"" space ":" space number
  921. char ::= [^"\\\x7F\x00-\x1F] | [\\] (["\\bfnrt] | "u" [0-9a-fA-F]{4})
  922. decimal-part ::= [0-9]{1,16}
  923. integral-part ::= [0] | [1-9] [0-9]{0,15}
  924. number ::= ("-"? integral-part) ("." decimal-part)? ([eE] [-+]? integral-part)? space
  925. root ::= "{" space and-kv ( "," space ( also-kv also-rest | additional-kv ( "," space additional-kv )* ) )? "}" space
  926. space ::= | " " | "\n" [ \t]{0,20}
  927. )"""
  928. });
  929. test({
  930. SUCCESS,
  931. "optional props with empty name",
  932. R"""({
  933. "properties": {
  934. "": {"type": "integer"},
  935. "a": {"type": "integer"}
  936. },
  937. "additionalProperties": {"type": "integer"}
  938. })""",
  939. R"""(
  940. -kv ::= "\"\"" space ":" space root
  941. -rest ::= ( "," space a-kv )? a-rest
  942. a-kv ::= "\"a\"" space ":" space integer
  943. a-rest ::= ( "," space additional-kv )*
  944. additional-k ::= ["] ( [a] char+ | [^"a] char* ) ["] space
  945. additional-kv ::= additional-k ":" space integer
  946. char ::= [^"\\\x7F\x00-\x1F] | [\\] (["\\bfnrt] | "u" [0-9a-fA-F]{4})
  947. integer ::= ("-"? integral-part) space
  948. integral-part ::= [0] | [1-9] [0-9]{0,15}
  949. root ::= ("-"? integral-part) space
  950. root0 ::= "{" space (-kv -rest | a-kv a-rest | additional-kv ( "," space additional-kv )* )? "}" space
  951. space ::= | " " | "\n" [ \t]{0,20}
  952. )"""
  953. });
  954. test({
  955. SUCCESS,
  956. "optional props with nested names",
  957. R"""({
  958. "properties": {
  959. "a": {"type": "integer"},
  960. "aa": {"type": "integer"}
  961. },
  962. "additionalProperties": {"type": "integer"}
  963. })""",
  964. R"""(
  965. a-kv ::= "\"a\"" space ":" space integer
  966. a-rest ::= ( "," space aa-kv )? aa-rest
  967. aa-kv ::= "\"aa\"" space ":" space integer
  968. aa-rest ::= ( "," space additional-kv )*
  969. additional-k ::= ["] ( [a] ([a] char+ | [^"a] char*) | [^"a] char* )? ["] space
  970. additional-kv ::= additional-k ":" space integer
  971. char ::= [^"\\\x7F\x00-\x1F] | [\\] (["\\bfnrt] | "u" [0-9a-fA-F]{4})
  972. integer ::= ("-"? integral-part) space
  973. integral-part ::= [0] | [1-9] [0-9]{0,15}
  974. root ::= "{" space (a-kv a-rest | aa-kv aa-rest | additional-kv ( "," space additional-kv )* )? "}" space
  975. space ::= | " " | "\n" [ \t]{0,20}
  976. )"""
  977. });
  978. test({
  979. SUCCESS,
  980. "optional props with common prefix",
  981. R"""({
  982. "properties": {
  983. "ab": {"type": "integer"},
  984. "ac": {"type": "integer"}
  985. },
  986. "additionalProperties": {"type": "integer"}
  987. })""",
  988. R"""(
  989. ab-kv ::= "\"ab\"" space ":" space integer
  990. ab-rest ::= ( "," space ac-kv )? ac-rest
  991. ac-kv ::= "\"ac\"" space ":" space integer
  992. ac-rest ::= ( "," space additional-kv )*
  993. additional-k ::= ["] ( [a] ([b] char+ | [c] char+ | [^"bc] char*) | [^"a] char* )? ["] space
  994. additional-kv ::= additional-k ":" space integer
  995. char ::= [^"\\\x7F\x00-\x1F] | [\\] (["\\bfnrt] | "u" [0-9a-fA-F]{4})
  996. integer ::= ("-"? integral-part) space
  997. integral-part ::= [0] | [1-9] [0-9]{0,15}
  998. root ::= "{" space (ab-kv ab-rest | ac-kv ac-rest | additional-kv ( "," space additional-kv )* )? "}" space
  999. space ::= | " " | "\n" [ \t]{0,20}
  1000. )"""
  1001. });
  1002. test({
  1003. SUCCESS,
  1004. "top-level $ref",
  1005. R"""({
  1006. "$ref": "#/definitions/foo",
  1007. "definitions": {
  1008. "foo": {
  1009. "type": "object",
  1010. "properties": {
  1011. "a": {
  1012. "type": "string"
  1013. }
  1014. },
  1015. "required": [
  1016. "a"
  1017. ],
  1018. "additionalProperties": false
  1019. }
  1020. }
  1021. })""",
  1022. R"""(
  1023. char ::= [^"\\\x7F\x00-\x1F] | [\\] (["\\bfnrt] | "u" [0-9a-fA-F]{4})
  1024. foo ::= "{" space foo-a-kv "}" space
  1025. foo-a-kv ::= "\"a\"" space ":" space string
  1026. root ::= foo
  1027. space ::= | " " | "\n" [ \t]{0,20}
  1028. string ::= "\"" char* "\"" space
  1029. )"""
  1030. });
  1031. test({
  1032. SUCCESS,
  1033. "anyOf",
  1034. R"""({
  1035. "anyOf": [
  1036. {"$ref": "#/definitions/foo"},
  1037. {"$ref": "#/definitions/bar"}
  1038. ],
  1039. "definitions": {
  1040. "foo": {
  1041. "properties": {"a": {"type": "number"}}
  1042. },
  1043. "bar": {
  1044. "properties": {"b": {"type": "number"}}
  1045. }
  1046. },
  1047. "type": "object"
  1048. })""",
  1049. R"""(
  1050. alternative-0 ::= foo
  1051. alternative-1 ::= bar
  1052. array ::= "[" space ( value ("," space value)* )? "]" space
  1053. bar ::= "{" space (bar-b-kv bar-b-rest | bar-additional-kv ( "," space bar-additional-kv )* )? "}" space
  1054. bar-additional-k ::= ["] ( [b] char+ | [^"b] char* )? ["] space
  1055. bar-additional-kv ::= bar-additional-k ":" space value
  1056. bar-b-kv ::= "\"b\"" space ":" space number
  1057. bar-b-rest ::= ( "," space bar-additional-kv )*
  1058. boolean ::= ("true" | "false") space
  1059. char ::= [^"\\\x7F\x00-\x1F] | [\\] (["\\bfnrt] | "u" [0-9a-fA-F]{4})
  1060. decimal-part ::= [0-9]{1,16}
  1061. foo ::= "{" space (foo-a-kv foo-a-rest | foo-additional-kv ( "," space foo-additional-kv )* )? "}" space
  1062. foo-a-kv ::= "\"a\"" space ":" space number
  1063. foo-a-rest ::= ( "," space foo-additional-kv )*
  1064. foo-additional-k ::= ["] ( [a] char+ | [^"a] char* )? ["] space
  1065. foo-additional-kv ::= foo-additional-k ":" space value
  1066. integral-part ::= [0] | [1-9] [0-9]{0,15}
  1067. null ::= "null" space
  1068. number ::= ("-"? integral-part) ("." decimal-part)? ([eE] [-+]? integral-part)? space
  1069. object ::= "{" space ( string ":" space value ("," space string ":" space value)* )? "}" space
  1070. root ::= alternative-0 | alternative-1
  1071. space ::= | " " | "\n" [ \t]{0,20}
  1072. string ::= "\"" char* "\"" space
  1073. value ::= object | array | string | number | boolean | null
  1074. )"""
  1075. });
  1076. test({
  1077. SUCCESS,
  1078. "mix of allOf, anyOf and $ref (similar to https://json.schemastore.org/tsconfig.json)",
  1079. R"""({
  1080. "allOf": [
  1081. {"$ref": "#/definitions/foo"},
  1082. {"$ref": "#/definitions/bar"},
  1083. {
  1084. "anyOf": [
  1085. {"$ref": "#/definitions/baz"},
  1086. {"$ref": "#/definitions/bam"}
  1087. ]
  1088. }
  1089. ],
  1090. "definitions": {
  1091. "foo": {
  1092. "properties": {"a": {"type": "number"}}
  1093. },
  1094. "bar": {
  1095. "properties": {"b": {"type": "number"}}
  1096. },
  1097. "bam": {
  1098. "properties": {"c": {"type": "number"}}
  1099. },
  1100. "baz": {
  1101. "properties": {"d": {"type": "number"}}
  1102. }
  1103. },
  1104. "type": "object"
  1105. })""",
  1106. R"""(
  1107. a-kv ::= "\"a\"" space ":" space number
  1108. additional-k ::= ["] ( [a] char+ | [b] char+ | [c] char+ | [d] char+ | [^"abcd] char* )? ["] space
  1109. additional-kv ::= additional-k ":" space value
  1110. array ::= "[" space ( value ("," space value)* )? "]" space
  1111. b-kv ::= "\"b\"" space ":" space number
  1112. boolean ::= ("true" | "false") space
  1113. c-kv ::= "\"c\"" space ":" space number
  1114. c-rest ::= ( "," space additional-kv )*
  1115. char ::= [^"\\\x7F\x00-\x1F] | [\\] (["\\bfnrt] | "u" [0-9a-fA-F]{4})
  1116. d-kv ::= "\"d\"" space ":" space number
  1117. d-rest ::= ( "," space c-kv )? c-rest
  1118. decimal-part ::= [0-9]{1,16}
  1119. integral-part ::= [0] | [1-9] [0-9]{0,15}
  1120. null ::= "null" space
  1121. number ::= ("-"? integral-part) ("." decimal-part)? ([eE] [-+]? integral-part)? space
  1122. object ::= "{" space ( string ":" space value ("," space string ":" space value)* )? "}" space
  1123. root ::= "{" space a-kv "," space b-kv ( "," space ( d-kv d-rest | c-kv c-rest | additional-kv ( "," space additional-kv )* ) )? "}" space
  1124. space ::= | " " | "\n" [ \t]{0,20}
  1125. string ::= "\"" char* "\"" space
  1126. value ::= object | array | string | number | boolean | null
  1127. )"""
  1128. });
  1129. test({
  1130. SUCCESS,
  1131. "conflicting names",
  1132. R"""({
  1133. "type": "object",
  1134. "properties": {
  1135. "number": {
  1136. "type": "object",
  1137. "properties": {
  1138. "number": {
  1139. "type": "object",
  1140. "properties": {
  1141. "root": {
  1142. "type": "number"
  1143. }
  1144. },
  1145. "required": [
  1146. "root"
  1147. ],
  1148. "additionalProperties": false
  1149. }
  1150. },
  1151. "required": [
  1152. "number"
  1153. ],
  1154. "additionalProperties": false
  1155. }
  1156. },
  1157. "required": [
  1158. "number"
  1159. ],
  1160. "additionalProperties": false,
  1161. "definitions": {}
  1162. })""",
  1163. R"""(
  1164. decimal-part ::= [0-9]{1,16}
  1165. integral-part ::= [0] | [1-9] [0-9]{0,15}
  1166. number ::= ("-"? integral-part) ("." decimal-part)? ([eE] [-+]? integral-part)? space
  1167. number- ::= "{" space number-number-kv "}" space
  1168. number-kv ::= "\"number\"" space ":" space number-
  1169. number-number ::= "{" space number-number-root-kv "}" space
  1170. number-number-kv ::= "\"number\"" space ":" space number-number
  1171. number-number-root-kv ::= "\"root\"" space ":" space number
  1172. root ::= "{" space number-kv "}" space
  1173. space ::= | " " | "\n" [ \t]{0,20}
  1174. )"""
  1175. });
  1176. }
  1177. int main() {
  1178. fprintf(stderr, "LLAMA_NODE_AVAILABLE = %s\n", getenv("LLAMA_NODE_AVAILABLE") ? "true" : "false");
  1179. fprintf(stderr, "LLAMA_PYTHON_AVAILABLE = %s\n", getenv("LLAMA_PYTHON_AVAILABLE") ? "true" : "false");
  1180. test_all("C++", [](const TestCase & tc) {
  1181. try {
  1182. tc.verify(json_schema_to_grammar(nlohmann::ordered_json::parse(tc.schema)));
  1183. tc.verify_status(SUCCESS);
  1184. } catch (const std::runtime_error & ex) {
  1185. fprintf(stderr, "Error: %s\n", ex.what());
  1186. tc.verify_status(FAILURE);
  1187. }
  1188. });
  1189. if (getenv("LLAMA_PYTHON_AVAILABLE") || (std::system("python -c \"import sys; exit(1) if sys.version_info < (3, 8) else print('Python version is sufficient')\"") == 0)) {
  1190. test_all("Python", [](const TestCase & tc) {
  1191. write("test-json-schema-input.tmp", tc.schema);
  1192. tc.verify_status(std::system(
  1193. "python ./examples/json_schema_to_grammar.py test-json-schema-input.tmp > test-grammar-output.tmp") == 0 ? SUCCESS : FAILURE);
  1194. tc.verify(read("test-grammar-output.tmp"));
  1195. });
  1196. } else {
  1197. fprintf(stderr, "\033[33mWARNING: Python not found (min version required is 3.8), skipping Python JSON schema -> grammar tests.\n\033[0m");
  1198. }
  1199. if (getenv("LLAMA_NODE_AVAILABLE") || (std::system("node --version") == 0)) {
  1200. test_all("JavaScript", [](const TestCase & tc) {
  1201. write("test-json-schema-input.tmp", tc.schema);
  1202. tc.verify_status(std::system(
  1203. "node ./tests/run-json-schema-to-grammar.mjs test-json-schema-input.tmp > test-grammar-output.tmp") == 0 ? SUCCESS : FAILURE);
  1204. tc.verify(read("test-grammar-output.tmp"));
  1205. });
  1206. } else {
  1207. fprintf(stderr, "\033[33mWARNING: Node not found, skipping JavaScript JSON schema -> grammar tests.\n\033[0m");
  1208. }
  1209. test_all("Check Expectations Validity", [](const TestCase & tc) {
  1210. if (tc.expected_status == SUCCESS) {
  1211. tc.verify_expectation_parseable();
  1212. }
  1213. });
  1214. }