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

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