@fugood/llama.node 0.0.1-alpha.1

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 (204) hide show
  1. package/CMakeLists.txt +85 -0
  2. package/README.md +56 -0
  3. package/bin/darwin/arm64/llama-node.node +0 -0
  4. package/bin/darwin/x64/llama-node.node +0 -0
  5. package/bin/linux/arm64/llama-node.node +0 -0
  6. package/bin/linux/x64/llama-node.node +0 -0
  7. package/bin/win32/arm64/llama-node.node +0 -0
  8. package/bin/win32/arm64/node.lib +0 -0
  9. package/bin/win32/x64/llama-node.node +0 -0
  10. package/bin/win32/x64/node.lib +0 -0
  11. package/lib/binding.js +13 -0
  12. package/lib/binding.ts +57 -0
  13. package/lib/index.js +24 -0
  14. package/lib/index.ts +13 -0
  15. package/package.json +65 -0
  16. package/src/addons.cpp +506 -0
  17. package/src/llama.cpp/CMakeLists.txt +1320 -0
  18. package/src/llama.cpp/build.zig +172 -0
  19. package/src/llama.cpp/cmake/FindSIMD.cmake +100 -0
  20. package/src/llama.cpp/common/CMakeLists.txt +87 -0
  21. package/src/llama.cpp/common/base64.hpp +392 -0
  22. package/src/llama.cpp/common/common.cpp +2949 -0
  23. package/src/llama.cpp/common/common.h +324 -0
  24. package/src/llama.cpp/common/console.cpp +501 -0
  25. package/src/llama.cpp/common/console.h +19 -0
  26. package/src/llama.cpp/common/grammar-parser.cpp +440 -0
  27. package/src/llama.cpp/common/grammar-parser.h +29 -0
  28. package/src/llama.cpp/common/json-schema-to-grammar.cpp +764 -0
  29. package/src/llama.cpp/common/json-schema-to-grammar.h +4 -0
  30. package/src/llama.cpp/common/json.hpp +24766 -0
  31. package/src/llama.cpp/common/log.h +724 -0
  32. package/src/llama.cpp/common/ngram-cache.cpp +282 -0
  33. package/src/llama.cpp/common/ngram-cache.h +94 -0
  34. package/src/llama.cpp/common/sampling.cpp +353 -0
  35. package/src/llama.cpp/common/sampling.h +147 -0
  36. package/src/llama.cpp/common/stb_image.h +8396 -0
  37. package/src/llama.cpp/common/train.cpp +1513 -0
  38. package/src/llama.cpp/common/train.h +233 -0
  39. package/src/llama.cpp/examples/CMakeLists.txt +52 -0
  40. package/src/llama.cpp/examples/baby-llama/CMakeLists.txt +5 -0
  41. package/src/llama.cpp/examples/baby-llama/baby-llama.cpp +1640 -0
  42. package/src/llama.cpp/examples/batched/CMakeLists.txt +5 -0
  43. package/src/llama.cpp/examples/batched/batched.cpp +262 -0
  44. package/src/llama.cpp/examples/batched-bench/CMakeLists.txt +5 -0
  45. package/src/llama.cpp/examples/batched-bench/batched-bench.cpp +261 -0
  46. package/src/llama.cpp/examples/beam-search/CMakeLists.txt +5 -0
  47. package/src/llama.cpp/examples/beam-search/beam-search.cpp +188 -0
  48. package/src/llama.cpp/examples/benchmark/CMakeLists.txt +6 -0
  49. package/src/llama.cpp/examples/benchmark/benchmark-matmult.cpp +275 -0
  50. package/src/llama.cpp/examples/convert-llama2c-to-ggml/CMakeLists.txt +5 -0
  51. package/src/llama.cpp/examples/convert-llama2c-to-ggml/convert-llama2c-to-ggml.cpp +936 -0
  52. package/src/llama.cpp/examples/embedding/CMakeLists.txt +5 -0
  53. package/src/llama.cpp/examples/embedding/embedding.cpp +211 -0
  54. package/src/llama.cpp/examples/eval-callback/CMakeLists.txt +9 -0
  55. package/src/llama.cpp/examples/eval-callback/eval-callback.cpp +195 -0
  56. package/src/llama.cpp/examples/export-lora/CMakeLists.txt +5 -0
  57. package/src/llama.cpp/examples/export-lora/export-lora.cpp +462 -0
  58. package/src/llama.cpp/examples/finetune/CMakeLists.txt +5 -0
  59. package/src/llama.cpp/examples/finetune/finetune.cpp +1861 -0
  60. package/src/llama.cpp/examples/gbnf-validator/CMakeLists.txt +5 -0
  61. package/src/llama.cpp/examples/gbnf-validator/gbnf-validator.cpp +132 -0
  62. package/src/llama.cpp/examples/gguf/CMakeLists.txt +5 -0
  63. package/src/llama.cpp/examples/gguf/gguf.cpp +256 -0
  64. package/src/llama.cpp/examples/gguf-split/CMakeLists.txt +5 -0
  65. package/src/llama.cpp/examples/gguf-split/gguf-split.cpp +553 -0
  66. package/src/llama.cpp/examples/gritlm/CMakeLists.txt +5 -0
  67. package/src/llama.cpp/examples/gritlm/gritlm.cpp +215 -0
  68. package/src/llama.cpp/examples/imatrix/CMakeLists.txt +5 -0
  69. package/src/llama.cpp/examples/imatrix/imatrix.cpp +655 -0
  70. package/src/llama.cpp/examples/infill/CMakeLists.txt +5 -0
  71. package/src/llama.cpp/examples/infill/infill.cpp +767 -0
  72. package/src/llama.cpp/examples/jeopardy/questions.txt +100 -0
  73. package/src/llama.cpp/examples/llama-bench/CMakeLists.txt +5 -0
  74. package/src/llama.cpp/examples/llama-bench/llama-bench.cpp +1286 -0
  75. package/src/llama.cpp/examples/llama.android/app/src/main/cpp/CMakeLists.txt +50 -0
  76. package/src/llama.cpp/examples/llama.android/app/src/main/cpp/llama-android.cpp +443 -0
  77. package/src/llama.cpp/examples/llava/CMakeLists.txt +37 -0
  78. package/src/llama.cpp/examples/llava/clip.cpp +2027 -0
  79. package/src/llama.cpp/examples/llava/clip.h +85 -0
  80. package/src/llama.cpp/examples/llava/llava-cli.cpp +309 -0
  81. package/src/llama.cpp/examples/llava/llava.cpp +426 -0
  82. package/src/llama.cpp/examples/llava/llava.h +50 -0
  83. package/src/llama.cpp/examples/llava/requirements.txt +3 -0
  84. package/src/llama.cpp/examples/lookahead/CMakeLists.txt +5 -0
  85. package/src/llama.cpp/examples/lookahead/lookahead.cpp +485 -0
  86. package/src/llama.cpp/examples/lookup/CMakeLists.txt +23 -0
  87. package/src/llama.cpp/examples/lookup/lookup-create.cpp +41 -0
  88. package/src/llama.cpp/examples/lookup/lookup-merge.cpp +47 -0
  89. package/src/llama.cpp/examples/lookup/lookup-stats.cpp +160 -0
  90. package/src/llama.cpp/examples/lookup/lookup.cpp +258 -0
  91. package/src/llama.cpp/examples/main/CMakeLists.txt +5 -0
  92. package/src/llama.cpp/examples/main/main.cpp +957 -0
  93. package/src/llama.cpp/examples/main-cmake-pkg/CMakeLists.txt +33 -0
  94. package/src/llama.cpp/examples/parallel/CMakeLists.txt +5 -0
  95. package/src/llama.cpp/examples/parallel/parallel.cpp +427 -0
  96. package/src/llama.cpp/examples/passkey/CMakeLists.txt +5 -0
  97. package/src/llama.cpp/examples/passkey/passkey.cpp +302 -0
  98. package/src/llama.cpp/examples/perplexity/CMakeLists.txt +5 -0
  99. package/src/llama.cpp/examples/perplexity/perplexity.cpp +1943 -0
  100. package/src/llama.cpp/examples/quantize/CMakeLists.txt +6 -0
  101. package/src/llama.cpp/examples/quantize/quantize.cpp +423 -0
  102. package/src/llama.cpp/examples/quantize-stats/CMakeLists.txt +6 -0
  103. package/src/llama.cpp/examples/quantize-stats/quantize-stats.cpp +424 -0
  104. package/src/llama.cpp/examples/retrieval/CMakeLists.txt +5 -0
  105. package/src/llama.cpp/examples/retrieval/retrieval.cpp +350 -0
  106. package/src/llama.cpp/examples/save-load-state/CMakeLists.txt +5 -0
  107. package/src/llama.cpp/examples/save-load-state/save-load-state.cpp +246 -0
  108. package/src/llama.cpp/examples/server/CMakeLists.txt +40 -0
  109. package/src/llama.cpp/examples/server/bench/requirements.txt +2 -0
  110. package/src/llama.cpp/examples/server/httplib.h +9465 -0
  111. package/src/llama.cpp/examples/server/server.cpp +3826 -0
  112. package/src/llama.cpp/examples/server/tests/requirements.txt +6 -0
  113. package/src/llama.cpp/examples/server/utils.hpp +653 -0
  114. package/src/llama.cpp/examples/simple/CMakeLists.txt +5 -0
  115. package/src/llama.cpp/examples/simple/simple.cpp +183 -0
  116. package/src/llama.cpp/examples/speculative/CMakeLists.txt +5 -0
  117. package/src/llama.cpp/examples/speculative/speculative.cpp +614 -0
  118. package/src/llama.cpp/examples/sycl/CMakeLists.txt +9 -0
  119. package/src/llama.cpp/examples/sycl/ls-sycl-device.cpp +13 -0
  120. package/src/llama.cpp/examples/tokenize/CMakeLists.txt +5 -0
  121. package/src/llama.cpp/examples/tokenize/tokenize.cpp +42 -0
  122. package/src/llama.cpp/examples/train-text-from-scratch/CMakeLists.txt +5 -0
  123. package/src/llama.cpp/examples/train-text-from-scratch/train-text-from-scratch.cpp +1252 -0
  124. package/src/llama.cpp/ggml-alloc.c +985 -0
  125. package/src/llama.cpp/ggml-alloc.h +76 -0
  126. package/src/llama.cpp/ggml-backend-impl.h +141 -0
  127. package/src/llama.cpp/ggml-backend.c +2099 -0
  128. package/src/llama.cpp/ggml-backend.h +233 -0
  129. package/src/llama.cpp/ggml-common.h +1853 -0
  130. package/src/llama.cpp/ggml-cuda.h +43 -0
  131. package/src/llama.cpp/ggml-impl.h +265 -0
  132. package/src/llama.cpp/ggml-kompute.cpp +2006 -0
  133. package/src/llama.cpp/ggml-kompute.h +46 -0
  134. package/src/llama.cpp/ggml-metal.h +66 -0
  135. package/src/llama.cpp/ggml-mpi.c +216 -0
  136. package/src/llama.cpp/ggml-mpi.h +39 -0
  137. package/src/llama.cpp/ggml-opencl.cpp +2301 -0
  138. package/src/llama.cpp/ggml-opencl.h +36 -0
  139. package/src/llama.cpp/ggml-quants.c +12678 -0
  140. package/src/llama.cpp/ggml-quants.h +133 -0
  141. package/src/llama.cpp/ggml-sycl.cpp +17882 -0
  142. package/src/llama.cpp/ggml-sycl.h +49 -0
  143. package/src/llama.cpp/ggml-vulkan-shaders.hpp +69849 -0
  144. package/src/llama.cpp/ggml-vulkan.cpp +6442 -0
  145. package/src/llama.cpp/ggml-vulkan.h +29 -0
  146. package/src/llama.cpp/ggml.c +21819 -0
  147. package/src/llama.cpp/ggml.h +2403 -0
  148. package/src/llama.cpp/llama.cpp +17468 -0
  149. package/src/llama.cpp/llama.h +1117 -0
  150. package/src/llama.cpp/pocs/CMakeLists.txt +12 -0
  151. package/src/llama.cpp/pocs/vdot/CMakeLists.txt +9 -0
  152. package/src/llama.cpp/pocs/vdot/q8dot.cpp +172 -0
  153. package/src/llama.cpp/pocs/vdot/vdot.cpp +310 -0
  154. package/src/llama.cpp/prompts/LLM-questions.txt +49 -0
  155. package/src/llama.cpp/prompts/alpaca.txt +1 -0
  156. package/src/llama.cpp/prompts/assistant.txt +31 -0
  157. package/src/llama.cpp/prompts/chat-with-baichuan.txt +4 -0
  158. package/src/llama.cpp/prompts/chat-with-bob.txt +7 -0
  159. package/src/llama.cpp/prompts/chat-with-qwen.txt +1 -0
  160. package/src/llama.cpp/prompts/chat-with-vicuna-v0.txt +7 -0
  161. package/src/llama.cpp/prompts/chat-with-vicuna-v1.txt +7 -0
  162. package/src/llama.cpp/prompts/chat.txt +28 -0
  163. package/src/llama.cpp/prompts/dan-modified.txt +1 -0
  164. package/src/llama.cpp/prompts/dan.txt +1 -0
  165. package/src/llama.cpp/prompts/mnemonics.txt +93 -0
  166. package/src/llama.cpp/prompts/parallel-questions.txt +43 -0
  167. package/src/llama.cpp/prompts/reason-act.txt +18 -0
  168. package/src/llama.cpp/requirements/requirements-convert-hf-to-gguf.txt +3 -0
  169. package/src/llama.cpp/requirements/requirements-convert-llama-ggml-to-gguf.txt +1 -0
  170. package/src/llama.cpp/requirements/requirements-convert-lora-to-ggml.txt +2 -0
  171. package/src/llama.cpp/requirements/requirements-convert-persimmon-to-gguf.txt +2 -0
  172. package/src/llama.cpp/requirements/requirements-convert.txt +5 -0
  173. package/src/llama.cpp/requirements.txt +12 -0
  174. package/src/llama.cpp/scripts/gen-build-info-cpp.cmake +24 -0
  175. package/src/llama.cpp/scripts/xxd.cmake +16 -0
  176. package/src/llama.cpp/sgemm.cpp +999 -0
  177. package/src/llama.cpp/sgemm.h +12 -0
  178. package/src/llama.cpp/tests/CMakeLists.txt +78 -0
  179. package/src/llama.cpp/tests/get-model.cpp +21 -0
  180. package/src/llama.cpp/tests/get-model.h +2 -0
  181. package/src/llama.cpp/tests/test-autorelease.cpp +24 -0
  182. package/src/llama.cpp/tests/test-backend-ops.cpp +2266 -0
  183. package/src/llama.cpp/tests/test-c.c +7 -0
  184. package/src/llama.cpp/tests/test-chat-template.cpp +107 -0
  185. package/src/llama.cpp/tests/test-double-float.cpp +57 -0
  186. package/src/llama.cpp/tests/test-grad0.cpp +1606 -0
  187. package/src/llama.cpp/tests/test-grammar-integration.cpp +243 -0
  188. package/src/llama.cpp/tests/test-grammar-parser.cpp +250 -0
  189. package/src/llama.cpp/tests/test-json-schema-to-grammar.cpp +899 -0
  190. package/src/llama.cpp/tests/test-llama-grammar.cpp +402 -0
  191. package/src/llama.cpp/tests/test-model-load-cancel.cpp +27 -0
  192. package/src/llama.cpp/tests/test-opt.cpp +181 -0
  193. package/src/llama.cpp/tests/test-quantize-fns.cpp +185 -0
  194. package/src/llama.cpp/tests/test-quantize-perf.cpp +363 -0
  195. package/src/llama.cpp/tests/test-rope.cpp +221 -0
  196. package/src/llama.cpp/tests/test-sampling.cpp +301 -0
  197. package/src/llama.cpp/tests/test-tokenizer-0-falcon.cpp +187 -0
  198. package/src/llama.cpp/tests/test-tokenizer-0-llama.cpp +190 -0
  199. package/src/llama.cpp/tests/test-tokenizer-1-bpe.cpp +123 -0
  200. package/src/llama.cpp/tests/test-tokenizer-1-llama.cpp +111 -0
  201. package/src/llama.cpp/unicode-data.cpp +1651 -0
  202. package/src/llama.cpp/unicode-data.h +16 -0
  203. package/src/llama.cpp/unicode.cpp +277 -0
  204. package/src/llama.cpp/unicode.h +28 -0
@@ -0,0 +1,899 @@
1
+ #ifdef NDEBUG
2
+ #undef NDEBUG
3
+ #endif
4
+
5
+ #include <fstream>
6
+ #include <sstream>
7
+ #include <regex>
8
+
9
+ #include "json-schema-to-grammar.h"
10
+ #include "grammar-parser.h"
11
+
12
+ static std::string trim(const std::string & source) {
13
+ std::string s(source);
14
+ s.erase(0,s.find_first_not_of(" \n\r\t"));
15
+ s.erase(s.find_last_not_of(" \n\r\t")+1);
16
+ return std::regex_replace(s, std::regex("(^|\n)[ \t]+"), "$1");
17
+ }
18
+
19
+ enum TestCaseStatus {
20
+ SUCCESS,
21
+ FAILURE
22
+ };
23
+
24
+ struct TestCase {
25
+ TestCaseStatus expected_status;
26
+ std::string name;
27
+ std::string schema;
28
+ std::string expected_grammar;
29
+
30
+ void _print_failure_header() const {
31
+ fprintf(stderr, "#\n# Test '%s' failed.\n#\n%s\n", name.c_str(), schema.c_str());
32
+ }
33
+ void verify(const std::string & actual_grammar) const {
34
+ if (trim(actual_grammar) != trim(expected_grammar)) {
35
+ _print_failure_header();
36
+ fprintf(stderr, "# EXPECTED:\n%s\n# ACTUAL:\n%s\n", expected_grammar.c_str(), actual_grammar.c_str());
37
+ assert(false);
38
+ }
39
+ }
40
+ void verify_expectation_parseable() const {
41
+ try {
42
+ auto state = grammar_parser::parse(expected_grammar.c_str());
43
+ if (state.symbol_ids.find("root") == state.symbol_ids.end()) {
44
+ throw std::runtime_error("Grammar failed to parse:\n" + expected_grammar);
45
+ }
46
+ } catch (const std::runtime_error & ex) {
47
+ _print_failure_header();
48
+ fprintf(stderr, "# GRAMMAR ERROR: %s\n", ex.what());
49
+ assert(false);
50
+ }
51
+ }
52
+ void verify_status(TestCaseStatus status) const {
53
+ if (status != expected_status) {
54
+ _print_failure_header();
55
+ fprintf(stderr, "# EXPECTED STATUS: %s\n", expected_status == SUCCESS ? "SUCCESS" : "FAILURE");
56
+ fprintf(stderr, "# ACTUAL STATUS: %s\n", status == SUCCESS ? "SUCCESS" : "FAILURE");
57
+ assert(false);
58
+ }
59
+ }
60
+ };
61
+
62
+ static void write(const std::string & file, const std::string & content) {
63
+ std::ofstream f;
64
+ f.open(file.c_str());
65
+ f << content.c_str();
66
+ f.close();
67
+ }
68
+
69
+ static std::string read(const std::string & file) {
70
+ std::ostringstream actuals;
71
+ actuals << std::ifstream(file.c_str()).rdbuf();
72
+ return actuals.str();
73
+ }
74
+
75
+ static void test_all(const std::string & lang, std::function<void(const TestCase &)> runner) {
76
+ fprintf(stderr, "#\n# Testing JSON schema conversion (%s)\n#\n", lang.c_str());
77
+ auto test = [&](const TestCase & tc) {
78
+ fprintf(stderr, "- %s%s\n", tc.name.c_str(), tc.expected_status == FAILURE ? " (failure expected)" : "");
79
+ runner(tc);
80
+ };
81
+
82
+ test({
83
+ FAILURE,
84
+ "unknown type",
85
+ R"""({
86
+ "type": "kaboom"
87
+ })""",
88
+ ""
89
+ });
90
+
91
+ test({
92
+ FAILURE,
93
+ "invalid type",
94
+ R"""({
95
+ "type": 123
96
+ })""",
97
+ ""
98
+ });
99
+
100
+ test({
101
+ SUCCESS,
102
+ "empty schema (object)",
103
+ "{}",
104
+ R"""(
105
+ array ::= "[" space ( value ("," space value)* )? "]" space
106
+ boolean ::= ("true" | "false") space
107
+ char ::= [^"\\] | "\\" (["\\/bfnrt] | "u" [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F])
108
+ decimal-part ::= [0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9])?)?)?)?)?)?)?)?)?)?)?)?)?)?)?
109
+ integral-part ::= [0-9] | [1-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9])?)?)?)?)?)?)?)?)?)?)?)?)?)?)?
110
+ null ::= "null" space
111
+ number ::= ("-"? integral-part) ("." decimal-part)? ([eE] [-+]? integral-part)? space
112
+ object ::= "{" space ( string ":" space value ("," space string ":" space value)* )? "}" space
113
+ root ::= object
114
+ space ::= " "?
115
+ string ::= "\"" char* "\"" space
116
+ value ::= object | array | string | number | boolean | null
117
+ )"""
118
+ });
119
+
120
+ test({
121
+ SUCCESS,
122
+ "exotic formats",
123
+ R"""({
124
+ "items": [
125
+ { "format": "date" },
126
+ { "format": "uuid" },
127
+ { "format": "time" },
128
+ { "format": "date-time" }
129
+ ]
130
+ })""",
131
+ R"""(
132
+ date ::= [0-9] [0-9] [0-9] [0-9] "-" ( "0" [1-9] | "1" [0-2] ) "-" ( "0" [1-9] | [1-2] [0-9] | "3" [0-1] )
133
+ date-string ::= "\"" date "\"" space
134
+ date-time ::= date "T" time
135
+ date-time-string ::= "\"" date-time "\"" space
136
+ root ::= "[" space tuple-0 "," space uuid "," space tuple-2 "," space tuple-3 "]" space
137
+ space ::= " "?
138
+ time ::= ([01] [0-9] | "2" [0-3]) ":" [0-5] [0-9] ":" [0-5] [0-9] ( "." [0-9] [0-9] [0-9] )? ( "Z" | ( "+" | "-" ) ( [01] [0-9] | "2" [0-3] ) ":" [0-5] [0-9] )
139
+ time-string ::= "\"" time "\"" space
140
+ tuple-0 ::= date-string
141
+ tuple-2 ::= time-string
142
+ tuple-3 ::= date-time-string
143
+ uuid ::= "\"" [0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F] "-" [0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F] "-" [0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F] "-" [0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F] "-" [0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F] "\"" space
144
+ )"""
145
+ });
146
+
147
+ test({
148
+ SUCCESS,
149
+ "string",
150
+ R"""({
151
+ "type": "string"
152
+ })""",
153
+ R"""(
154
+ char ::= [^"\\] | "\\" (["\\/bfnrt] | "u" [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F])
155
+ root ::= "\"" char* "\"" space
156
+ space ::= " "?
157
+ )"""
158
+ });
159
+
160
+ test({
161
+ SUCCESS,
162
+ "string w/ min length 1",
163
+ R"""({
164
+ "type": "string",
165
+ "minLength": 1
166
+ })""",
167
+ R"""(
168
+ char ::= [^"\\] | "\\" (["\\/bfnrt] | "u" [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F])
169
+ root ::= "\"" char+ "\"" space
170
+ space ::= " "?
171
+ )"""
172
+ });
173
+
174
+ test({
175
+ SUCCESS,
176
+ "string w/ min length 3",
177
+ R"""({
178
+ "type": "string",
179
+ "minLength": 3
180
+ })""",
181
+ R"""(
182
+ char ::= [^"\\] | "\\" (["\\/bfnrt] | "u" [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F])
183
+ root ::= "\"" char char char (char)* "\"" space
184
+ space ::= " "?
185
+ )"""
186
+ });
187
+
188
+ test({
189
+ SUCCESS,
190
+ "string w/ max length",
191
+ R"""({
192
+ "type": "string",
193
+ "maxLength": 3
194
+ })""",
195
+ R"""(
196
+ char ::= [^"\\] | "\\" (["\\/bfnrt] | "u" [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F])
197
+ root ::= "\"" (char (char (char)?)?)? "\"" space
198
+ space ::= " "?
199
+ )"""
200
+ });
201
+
202
+ test({
203
+ SUCCESS,
204
+ "string w/ min & max length",
205
+ R"""({
206
+ "type": "string",
207
+ "minLength": 1,
208
+ "maxLength": 4
209
+ })""",
210
+ R"""(
211
+ char ::= [^"\\] | "\\" (["\\/bfnrt] | "u" [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F])
212
+ root ::= "\"" char (char (char (char)?)?)? "\"" space
213
+ space ::= " "?
214
+ )"""
215
+ });
216
+
217
+ test({
218
+ SUCCESS,
219
+ "boolean",
220
+ R"""({
221
+ "type": "boolean"
222
+ })""",
223
+ R"""(
224
+ root ::= ("true" | "false") space
225
+ space ::= " "?
226
+ )"""
227
+ });
228
+
229
+ test({
230
+ SUCCESS,
231
+ "integer",
232
+ R"""({
233
+ "type": "integer"
234
+ })""",
235
+ R"""(
236
+ integral-part ::= [0-9] | [1-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9])?)?)?)?)?)?)?)?)?)?)?)?)?)?)?
237
+ root ::= ("-"? integral-part) space
238
+ space ::= " "?
239
+ )"""
240
+ });
241
+
242
+ test({
243
+ SUCCESS,
244
+ "string const",
245
+ R"""({
246
+ "const": "foo"
247
+ })""",
248
+ R"""(
249
+ root ::= "\"foo\""
250
+ space ::= " "?
251
+ )"""
252
+ });
253
+
254
+ test({
255
+ SUCCESS,
256
+ "non-string const",
257
+ R"""({
258
+ "const": 123
259
+ })""",
260
+ R"""(
261
+ root ::= "123"
262
+ space ::= " "?
263
+ )"""
264
+ });
265
+
266
+ test({
267
+ SUCCESS,
268
+ "non-string enum",
269
+ R"""({
270
+ "enum": ["red", "amber", "green", null, 42, ["foo"]]
271
+ })""",
272
+ R"""(
273
+ root ::= "\"red\"" | "\"amber\"" | "\"green\"" | "null" | "42" | "[\"foo\"]"
274
+ space ::= " "?
275
+ )"""
276
+ });
277
+
278
+ test({
279
+ SUCCESS,
280
+ "tuple1",
281
+ R"""({
282
+ "prefixItems": [{ "type": "string" }]
283
+ })""",
284
+ R"""(
285
+ char ::= [^"\\] | "\\" (["\\/bfnrt] | "u" [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F])
286
+ root ::= "[" space string "]" space
287
+ space ::= " "?
288
+ string ::= "\"" char* "\"" space
289
+ )"""
290
+ });
291
+
292
+ test({
293
+ SUCCESS,
294
+ "tuple2",
295
+ R"""({
296
+ "prefixItems": [{ "type": "string" }, { "type": "number" }]
297
+ })""",
298
+ R"""(
299
+ char ::= [^"\\] | "\\" (["\\/bfnrt] | "u" [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F])
300
+ decimal-part ::= [0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9])?)?)?)?)?)?)?)?)?)?)?)?)?)?)?
301
+ integral-part ::= [0-9] | [1-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9])?)?)?)?)?)?)?)?)?)?)?)?)?)?)?
302
+ number ::= ("-"? integral-part) ("." decimal-part)? ([eE] [-+]? integral-part)? space
303
+ root ::= "[" space string "," space number "]" space
304
+ space ::= " "?
305
+ string ::= "\"" char* "\"" space
306
+ )"""
307
+ });
308
+
309
+ test({
310
+ SUCCESS,
311
+ "number",
312
+ R"""({
313
+ "type": "number"
314
+ })""",
315
+ R"""(
316
+ decimal-part ::= [0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9])?)?)?)?)?)?)?)?)?)?)?)?)?)?)?
317
+ integral-part ::= [0-9] | [1-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9])?)?)?)?)?)?)?)?)?)?)?)?)?)?)?
318
+ root ::= ("-"? integral-part) ("." decimal-part)? ([eE] [-+]? integral-part)? space
319
+ space ::= " "?
320
+ )"""
321
+ });
322
+
323
+ test({
324
+ SUCCESS,
325
+ "minItems",
326
+ R"""({
327
+ "items": {
328
+ "type": "boolean"
329
+ },
330
+ "minItems": 2
331
+ })""",
332
+ R"""(
333
+ boolean ::= ("true" | "false") space
334
+ root ::= "[" space boolean "," space boolean ("," space boolean)* "]" space
335
+ space ::= " "?
336
+ )"""
337
+ });
338
+
339
+ test({
340
+ SUCCESS,
341
+ "maxItems 1",
342
+ R"""({
343
+ "items": {
344
+ "type": "boolean"
345
+ },
346
+ "maxItems": 1
347
+ })""",
348
+ R"""(
349
+ boolean ::= ("true" | "false") space
350
+ root ::= "[" space (boolean)? "]" space
351
+ space ::= " "?
352
+ )"""
353
+ });
354
+
355
+ test({
356
+ SUCCESS,
357
+ "maxItems 2",
358
+ R"""({
359
+ "items": {
360
+ "type": "boolean"
361
+ },
362
+ "maxItems": 2
363
+ })""",
364
+ R"""(
365
+ boolean ::= ("true" | "false") space
366
+ root ::= "[" space (boolean ("," space boolean)?)? "]" space
367
+ space ::= " "?
368
+ )"""
369
+ });
370
+
371
+ test({
372
+ SUCCESS,
373
+ "min + maxItems",
374
+ R"""({
375
+ "items": {
376
+ "type": ["number", "integer"]
377
+ },
378
+ "minItems": 3,
379
+ "maxItems": 5
380
+ })""",
381
+ R"""(
382
+ decimal-part ::= [0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9])?)?)?)?)?)?)?)?)?)?)?)?)?)?)?
383
+ integer ::= ("-"? integral-part) space
384
+ integral-part ::= [0-9] | [1-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9])?)?)?)?)?)?)?)?)?)?)?)?)?)?)?
385
+ item ::= number | integer
386
+ number ::= ("-"? integral-part) ("." decimal-part)? ([eE] [-+]? integral-part)? space
387
+ root ::= "[" space item "," space item "," space item ("," space item ("," space item)?)? "]" space
388
+ space ::= " "?
389
+ )"""
390
+ });
391
+
392
+ test({
393
+ SUCCESS,
394
+ "simple regexp",
395
+ R"""({
396
+ "type": "string",
397
+ "pattern": "^abc?d*efg+(hij)?kl$"
398
+ })""",
399
+ R"""(
400
+ root ::= "\"" "ab" "c"? "d"* "ef" "g"+ ("hij")? "kl" "\"" space
401
+ space ::= " "?
402
+ )"""
403
+ });
404
+
405
+ test({
406
+ SUCCESS,
407
+ "regexp escapes",
408
+ R"""({
409
+ "type": "string",
410
+ "pattern": "^\\[\\]\\{\\}\\(\\)\\|\\+\\*\\?$"
411
+ })""",
412
+ R"""(
413
+ root ::= "\"" "[]{}()|+*?" "\"" space
414
+ space ::= " "?
415
+ )"""
416
+ });
417
+
418
+ test({
419
+ SUCCESS,
420
+ "regexp quote",
421
+ R"""({
422
+ "type": "string",
423
+ "pattern": "^\"$"
424
+ })""",
425
+ R"""(
426
+ root ::= "\"" "\"" "\"" space
427
+ space ::= " "?
428
+ )"""
429
+ });
430
+
431
+ test({
432
+ SUCCESS,
433
+ "regexp",
434
+ R"""({
435
+ "type": "string",
436
+ "pattern": "^(\\([0-9]{1,3}\\))?[0-9]{3}-[0-9]{4} a{3,5}nd...$"
437
+ })""",
438
+ R"""(
439
+ dot ::= [^\x0A\x0D]
440
+ root ::= "\"" ("(" root-1 (root-1 (root-1)?)? ")")? root-1 root-1 root-1 "-" root-1 root-1 root-1 root-1 " " "aaa" ("a" ("a")?)? "nd" dot dot dot "\"" space
441
+ root-1 ::= [0-9]
442
+ space ::= " "?
443
+ )"""
444
+ });
445
+
446
+ test({
447
+ SUCCESS,
448
+ "required props in original order",
449
+ R"""({
450
+ "type": "object",
451
+ "properties": {
452
+ "b": {"type": "string"},
453
+ "c": {"type": "string"},
454
+ "a": {"type": "string"}
455
+ },
456
+ "required": [
457
+ "a",
458
+ "b",
459
+ "c"
460
+ ],
461
+ "additionalProperties": false,
462
+ "definitions": {}
463
+ })""",
464
+ R"""(
465
+ a-kv ::= "\"a\"" space ":" space string
466
+ b-kv ::= "\"b\"" space ":" space string
467
+ c-kv ::= "\"c\"" space ":" space string
468
+ char ::= [^"\\] | "\\" (["\\/bfnrt] | "u" [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F])
469
+ root ::= "{" space b-kv "," space c-kv "," space a-kv "}" space
470
+ space ::= " "?
471
+ string ::= "\"" char* "\"" space
472
+ )"""
473
+ });
474
+
475
+ test({
476
+ SUCCESS,
477
+ "1 optional prop",
478
+ R"""({
479
+ "properties": {
480
+ "a": {
481
+ "type": "string"
482
+ }
483
+ },
484
+ "additionalProperties": false
485
+ })""",
486
+ R"""(
487
+ a-kv ::= "\"a\"" space ":" space string
488
+ char ::= [^"\\] | "\\" (["\\/bfnrt] | "u" [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F])
489
+ root ::= "{" space (a-kv )? "}" space
490
+ space ::= " "?
491
+ string ::= "\"" char* "\"" space
492
+ )"""
493
+ });
494
+
495
+ test({
496
+ SUCCESS,
497
+ "N optional props",
498
+ R"""({
499
+ "properties": {
500
+ "a": {"type": "string"},
501
+ "b": {"type": "string"},
502
+ "c": {"type": "string"}
503
+ },
504
+ "additionalProperties": false
505
+ })""",
506
+ R"""(
507
+ a-kv ::= "\"a\"" space ":" space string
508
+ a-rest ::= ( "," space b-kv )? b-rest
509
+ b-kv ::= "\"b\"" space ":" space string
510
+ b-rest ::= ( "," space c-kv )?
511
+ c-kv ::= "\"c\"" space ":" space string
512
+ char ::= [^"\\] | "\\" (["\\/bfnrt] | "u" [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F])
513
+ root ::= "{" space (a-kv a-rest | b-kv b-rest | c-kv )? "}" space
514
+ space ::= " "?
515
+ string ::= "\"" char* "\"" space
516
+ )"""
517
+ });
518
+
519
+ test({
520
+ SUCCESS,
521
+ "required + optional props each in original order",
522
+ R"""({
523
+ "properties": {
524
+ "b": {"type": "string"},
525
+ "a": {"type": "string"},
526
+ "d": {"type": "string"},
527
+ "c": {"type": "string"}
528
+ },
529
+ "required": ["a", "b"],
530
+ "additionalProperties": false
531
+ })""",
532
+ R"""(
533
+ a-kv ::= "\"a\"" space ":" space string
534
+ b-kv ::= "\"b\"" space ":" space string
535
+ c-kv ::= "\"c\"" space ":" space string
536
+ char ::= [^"\\] | "\\" (["\\/bfnrt] | "u" [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F])
537
+ d-kv ::= "\"d\"" space ":" space string
538
+ d-rest ::= ( "," space c-kv )?
539
+ root ::= "{" space b-kv "," space a-kv ( "," space ( d-kv d-rest | c-kv ) )? "}" space
540
+ space ::= " "?
541
+ string ::= "\"" char* "\"" space
542
+ )"""
543
+ });
544
+
545
+ test({
546
+ SUCCESS,
547
+ "additional props",
548
+ R"""({
549
+ "type": "object",
550
+ "additionalProperties": {"type": "array", "items": {"type": "number"}}
551
+ })""",
552
+ R"""(
553
+ additional-kv ::= string ":" space additional-value
554
+ additional-kvs ::= additional-kv ( "," space additional-kv )*
555
+ additional-value ::= "[" space (number ("," space number)*)? "]" space
556
+ char ::= [^"\\] | "\\" (["\\/bfnrt] | "u" [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F])
557
+ decimal-part ::= [0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9])?)?)?)?)?)?)?)?)?)?)?)?)?)?)?
558
+ integral-part ::= [0-9] | [1-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9])?)?)?)?)?)?)?)?)?)?)?)?)?)?)?
559
+ number ::= ("-"? integral-part) ("." decimal-part)? ([eE] [-+]? integral-part)? space
560
+ root ::= "{" space (additional-kvs )? "}" space
561
+ space ::= " "?
562
+ string ::= "\"" char* "\"" space
563
+ )"""
564
+ });
565
+
566
+ test({
567
+ SUCCESS,
568
+ "additional props (true)",
569
+ R"""({
570
+ "type": "object",
571
+ "additionalProperties": true
572
+ })""",
573
+ R"""(
574
+ array ::= "[" space ( value ("," space value)* )? "]" space
575
+ boolean ::= ("true" | "false") space
576
+ char ::= [^"\\] | "\\" (["\\/bfnrt] | "u" [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F])
577
+ decimal-part ::= [0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9])?)?)?)?)?)?)?)?)?)?)?)?)?)?)?
578
+ integral-part ::= [0-9] | [1-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9])?)?)?)?)?)?)?)?)?)?)?)?)?)?)?
579
+ null ::= "null" space
580
+ number ::= ("-"? integral-part) ("." decimal-part)? ([eE] [-+]? integral-part)? space
581
+ object ::= "{" space ( string ":" space value ("," space string ":" space value)* )? "}" space
582
+ root ::= object
583
+ space ::= " "?
584
+ string ::= "\"" char* "\"" space
585
+ value ::= object | array | string | number | boolean | null
586
+ )"""
587
+ });
588
+
589
+ test({
590
+ SUCCESS,
591
+ "additional props (implicit)",
592
+ R"""({
593
+ "type": "object"
594
+ })""",
595
+ R"""(
596
+ array ::= "[" space ( value ("," space value)* )? "]" space
597
+ boolean ::= ("true" | "false") space
598
+ char ::= [^"\\] | "\\" (["\\/bfnrt] | "u" [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F])
599
+ decimal-part ::= [0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9])?)?)?)?)?)?)?)?)?)?)?)?)?)?)?
600
+ integral-part ::= [0-9] | [1-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9])?)?)?)?)?)?)?)?)?)?)?)?)?)?)?
601
+ null ::= "null" space
602
+ number ::= ("-"? integral-part) ("." decimal-part)? ([eE] [-+]? integral-part)? space
603
+ object ::= "{" space ( string ":" space value ("," space string ":" space value)* )? "}" space
604
+ root ::= object
605
+ space ::= " "?
606
+ string ::= "\"" char* "\"" space
607
+ value ::= object | array | string | number | boolean | null
608
+ )"""
609
+ });
610
+
611
+ test({
612
+ SUCCESS,
613
+ "empty w/o additional props",
614
+ R"""({
615
+ "type": "object",
616
+ "additionalProperties": false
617
+ })""",
618
+ R"""(
619
+ root ::= "{" space "}" space
620
+ space ::= " "?
621
+ )"""
622
+ });
623
+
624
+ test({
625
+ SUCCESS,
626
+ "required + additional props",
627
+ R"""({
628
+ "type": "object",
629
+ "properties": {
630
+ "a": {"type": "number"}
631
+ },
632
+ "required": ["a"],
633
+ "additionalProperties": {"type": "string"}
634
+ })""",
635
+ R"""(
636
+ a-kv ::= "\"a\"" space ":" space number
637
+ additional-kv ::= string ":" space string
638
+ additional-kvs ::= additional-kv ( "," space additional-kv )*
639
+ char ::= [^"\\] | "\\" (["\\/bfnrt] | "u" [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F])
640
+ decimal-part ::= [0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9])?)?)?)?)?)?)?)?)?)?)?)?)?)?)?
641
+ integral-part ::= [0-9] | [1-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9])?)?)?)?)?)?)?)?)?)?)?)?)?)?)?
642
+ number ::= ("-"? integral-part) ("." decimal-part)? ([eE] [-+]? integral-part)? space
643
+ root ::= "{" space a-kv ( "," space ( additional-kvs ) )? "}" space
644
+ space ::= " "?
645
+ string ::= "\"" char* "\"" space
646
+ )"""
647
+ });
648
+
649
+ test({
650
+ SUCCESS,
651
+ "optional + additional props",
652
+ R"""({
653
+ "type": "object",
654
+ "properties": {
655
+ "a": {"type": "number"}
656
+ },
657
+ "additionalProperties": {"type": "number"}
658
+ })""",
659
+ R"""(
660
+ a-kv ::= "\"a\"" space ":" space number
661
+ a-rest ::= additional-kvs
662
+ additional-kv ::= string ":" space number
663
+ additional-kvs ::= additional-kv ( "," space additional-kv )*
664
+ char ::= [^"\\] | "\\" (["\\/bfnrt] | "u" [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F])
665
+ decimal-part ::= [0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9])?)?)?)?)?)?)?)?)?)?)?)?)?)?)?
666
+ integral-part ::= [0-9] | [1-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9])?)?)?)?)?)?)?)?)?)?)?)?)?)?)?
667
+ number ::= ("-"? integral-part) ("." decimal-part)? ([eE] [-+]? integral-part)? space
668
+ root ::= "{" space (a-kv a-rest | additional-kvs )? "}" space
669
+ space ::= " "?
670
+ string ::= "\"" char* "\"" space
671
+ )"""
672
+ });
673
+
674
+ test({
675
+ SUCCESS,
676
+ "required + optional + additional props",
677
+ R"""({
678
+ "type": "object",
679
+ "properties": {
680
+ "a": {"type": "number"},
681
+ "b": {"type": "number"}
682
+ },
683
+ "required": ["a"],
684
+ "additionalProperties": {"type": "number"}
685
+ })""",
686
+ R"""(
687
+ a-kv ::= "\"a\"" space ":" space number
688
+ additional-kv ::= string ":" space number
689
+ additional-kvs ::= additional-kv ( "," space additional-kv )*
690
+ b-kv ::= "\"b\"" space ":" space number
691
+ b-rest ::= additional-kvs
692
+ char ::= [^"\\] | "\\" (["\\/bfnrt] | "u" [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F])
693
+ decimal-part ::= [0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9])?)?)?)?)?)?)?)?)?)?)?)?)?)?)?
694
+ integral-part ::= [0-9] | [1-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9])?)?)?)?)?)?)?)?)?)?)?)?)?)?)?
695
+ number ::= ("-"? integral-part) ("." decimal-part)? ([eE] [-+]? integral-part)? space
696
+ root ::= "{" space a-kv ( "," space ( b-kv b-rest | additional-kvs ) )? "}" space
697
+ space ::= " "?
698
+ string ::= "\"" char* "\"" space
699
+ )"""
700
+ });
701
+
702
+ test({
703
+ SUCCESS,
704
+ "top-level $ref",
705
+ R"""({
706
+ "$ref": "#/definitions/foo",
707
+ "definitions": {
708
+ "foo": {
709
+ "type": "object",
710
+ "properties": {
711
+ "a": {
712
+ "type": "string"
713
+ }
714
+ },
715
+ "required": [
716
+ "a"
717
+ ],
718
+ "additionalProperties": false
719
+ }
720
+ }
721
+ })""",
722
+ R"""(
723
+ char ::= [^"\\] | "\\" (["\\/bfnrt] | "u" [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F])
724
+ foo ::= "{" space foo-a-kv "}" space
725
+ foo-a-kv ::= "\"a\"" space ":" space string
726
+ root ::= foo
727
+ space ::= " "?
728
+ string ::= "\"" char* "\"" space
729
+ )"""
730
+ });
731
+
732
+ test({
733
+ SUCCESS,
734
+ "anyOf",
735
+ R"""({
736
+ "anyOf": [
737
+ {"$ref": "#/definitions/foo"},
738
+ {"$ref": "#/definitions/bar"}
739
+ ],
740
+ "definitions": {
741
+ "foo": {
742
+ "properties": {"a": {"type": "number"}}
743
+ },
744
+ "bar": {
745
+ "properties": {"b": {"type": "number"}}
746
+ }
747
+ },
748
+ "type": "object"
749
+ })""",
750
+ R"""(
751
+ alternative-0 ::= foo
752
+ alternative-1 ::= bar
753
+ bar ::= "{" space (bar-b-kv )? "}" space
754
+ bar-b-kv ::= "\"b\"" space ":" space number
755
+ decimal-part ::= [0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9])?)?)?)?)?)?)?)?)?)?)?)?)?)?)?
756
+ foo ::= "{" space (foo-a-kv )? "}" space
757
+ foo-a-kv ::= "\"a\"" space ":" space number
758
+ integral-part ::= [0-9] | [1-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9])?)?)?)?)?)?)?)?)?)?)?)?)?)?)?
759
+ number ::= ("-"? integral-part) ("." decimal-part)? ([eE] [-+]? integral-part)? space
760
+ root ::= alternative-0 | alternative-1
761
+ space ::= " "?
762
+ )"""
763
+ });
764
+
765
+ test({
766
+ SUCCESS,
767
+ "mix of allOf, anyOf and $ref (similar to https://json.schemastore.org/tsconfig.json)",
768
+ R"""({
769
+ "allOf": [
770
+ {"$ref": "#/definitions/foo"},
771
+ {"$ref": "#/definitions/bar"},
772
+ {
773
+ "anyOf": [
774
+ {"$ref": "#/definitions/baz"},
775
+ {"$ref": "#/definitions/bam"}
776
+ ]
777
+ }
778
+ ],
779
+ "definitions": {
780
+ "foo": {
781
+ "properties": {"a": {"type": "number"}}
782
+ },
783
+ "bar": {
784
+ "properties": {"b": {"type": "number"}}
785
+ },
786
+ "bam": {
787
+ "properties": {"c": {"type": "number"}}
788
+ },
789
+ "baz": {
790
+ "properties": {"d": {"type": "number"}}
791
+ }
792
+ },
793
+ "type": "object"
794
+ })""",
795
+ R"""(
796
+ a-kv ::= "\"a\"" space ":" space number
797
+ b-kv ::= "\"b\"" space ":" space number
798
+ c-kv ::= "\"c\"" space ":" space number
799
+ d-kv ::= "\"d\"" space ":" space number
800
+ d-rest ::= ( "," space c-kv )?
801
+ decimal-part ::= [0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9])?)?)?)?)?)?)?)?)?)?)?)?)?)?)?
802
+ integral-part ::= [0-9] | [1-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9])?)?)?)?)?)?)?)?)?)?)?)?)?)?)?
803
+ number ::= ("-"? integral-part) ("." decimal-part)? ([eE] [-+]? integral-part)? space
804
+ root ::= "{" space a-kv "," space b-kv ( "," space ( d-kv d-rest | c-kv ) )? "}" space
805
+ space ::= " "?
806
+ )"""
807
+ });
808
+
809
+ test({
810
+ SUCCESS,
811
+ "conflicting names",
812
+ R"""({
813
+ "type": "object",
814
+ "properties": {
815
+ "number": {
816
+ "type": "object",
817
+ "properties": {
818
+ "number": {
819
+ "type": "object",
820
+ "properties": {
821
+ "root": {
822
+ "type": "number"
823
+ }
824
+ },
825
+ "required": [
826
+ "root"
827
+ ],
828
+ "additionalProperties": false
829
+ }
830
+ },
831
+ "required": [
832
+ "number"
833
+ ],
834
+ "additionalProperties": false
835
+ }
836
+ },
837
+ "required": [
838
+ "number"
839
+ ],
840
+ "additionalProperties": false,
841
+ "definitions": {}
842
+ })""",
843
+ R"""(
844
+ decimal-part ::= [0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9])?)?)?)?)?)?)?)?)?)?)?)?)?)?)?
845
+ integral-part ::= [0-9] | [1-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9] ([0-9])?)?)?)?)?)?)?)?)?)?)?)?)?)?)?
846
+ number ::= ("-"? integral-part) ("." decimal-part)? ([eE] [-+]? integral-part)? space
847
+ number- ::= "{" space number-number-kv "}" space
848
+ number-kv ::= "\"number\"" space ":" space number-
849
+ number-number ::= "{" space number-number-root-kv "}" space
850
+ number-number-kv ::= "\"number\"" space ":" space number-number
851
+ number-number-root-kv ::= "\"root\"" space ":" space number
852
+ root ::= "{" space number-kv "}" space
853
+ space ::= " "?
854
+ )"""
855
+ });
856
+ }
857
+
858
+ int main() {
859
+ fprintf(stderr, "LLAMA_NODE_AVAILABLE = %s\n", getenv("LLAMA_NODE_AVAILABLE") ? "true" : "false");
860
+ fprintf(stderr, "LLAMA_PYTHON_AVAILABLE = %s\n", getenv("LLAMA_PYTHON_AVAILABLE") ? "true" : "false");
861
+
862
+ test_all("C++", [](const TestCase & tc) {
863
+ try {
864
+ tc.verify(json_schema_to_grammar(nlohmann::ordered_json::parse(tc.schema)));
865
+ tc.verify_status(SUCCESS);
866
+ } catch (const std::runtime_error & ex) {
867
+ fprintf(stderr, "Error: %s\n", ex.what());
868
+ tc.verify_status(FAILURE);
869
+ }
870
+ });
871
+
872
+ if (getenv("LLAMA_PYTHON_AVAILABLE") || (std::system("python --version") == 0)) {
873
+ test_all("Python", [](const TestCase & tc) {
874
+ write("test-json-schema-input.tmp", tc.schema);
875
+ tc.verify_status(std::system(
876
+ "python ./examples/json_schema_to_grammar.py test-json-schema-input.tmp > test-grammar-output.tmp") == 0 ? SUCCESS : FAILURE);
877
+ tc.verify(read("test-grammar-output.tmp"));
878
+ });
879
+ } else {
880
+ fprintf(stderr, "\033[33mWARNING: Python not found, skipping Python JSON schema -> grammar tests.\n\033[0m");
881
+ }
882
+
883
+ if (getenv("LLAMA_NODE_AVAILABLE") || (std::system("node --version") == 0)) {
884
+ test_all("JavaScript", [](const TestCase & tc) {
885
+ write("test-json-schema-input.tmp", tc.schema);
886
+ tc.verify_status(std::system(
887
+ "node ./tests/run-json-schema-to-grammar.mjs test-json-schema-input.tmp > test-grammar-output.tmp") == 0 ? SUCCESS : FAILURE);
888
+ tc.verify(read("test-grammar-output.tmp"));
889
+ });
890
+ } else {
891
+ fprintf(stderr, "\033[33mWARNING: Node not found, skipping JavaScript JSON schema -> grammar tests.\n\033[0m");
892
+ }
893
+
894
+ test_all("Check Expectations Validity", [](const TestCase & tc) {
895
+ if (tc.expected_status == SUCCESS) {
896
+ tc.verify_expectation_parseable();
897
+ }
898
+ });
899
+ }