@fugood/llama.node 0.3.9 → 0.3.11
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.
- package/bin/darwin/arm64/llama-node.node +0 -0
- package/bin/darwin/x64/llama-node.node +0 -0
- package/bin/linux/arm64/llama-node.node +0 -0
- package/bin/linux/x64/llama-node.node +0 -0
- package/bin/linux-cuda/arm64/llama-node.node +0 -0
- package/bin/linux-cuda/x64/llama-node.node +0 -0
- package/bin/linux-vulkan/arm64/llama-node.node +0 -0
- package/bin/linux-vulkan/x64/llama-node.node +0 -0
- package/bin/win32/arm64/llama-node.node +0 -0
- package/bin/win32/arm64/node.lib +0 -0
- package/bin/win32/x64/llama-node.node +0 -0
- package/bin/win32/x64/node.lib +0 -0
- package/bin/win32-vulkan/arm64/llama-node.node +0 -0
- package/bin/win32-vulkan/arm64/node.lib +0 -0
- package/bin/win32-vulkan/x64/llama-node.node +0 -0
- package/bin/win32-vulkan/x64/node.lib +0 -0
- package/lib/binding.js +2 -2
- package/lib/binding.ts +47 -8
- package/lib/index.js +21 -1
- package/lib/index.ts +31 -1
- package/package.json +12 -3
- package/src/LlamaCompletionWorker.cpp +33 -6
- package/src/LlamaCompletionWorker.h +3 -1
- package/src/LlamaContext.cpp +336 -28
- package/src/LlamaContext.h +2 -0
- package/src/common.hpp +19 -2
- package/src/llama.cpp/.github/workflows/build.yml +289 -107
- package/src/llama.cpp/.github/workflows/close-issue.yml +1 -1
- package/src/llama.cpp/.github/workflows/docker.yml +2 -1
- package/src/llama.cpp/.github/workflows/server.yml +25 -2
- package/src/llama.cpp/CMakeLists.txt +10 -19
- package/src/llama.cpp/cmake/build-info.cmake +1 -1
- package/src/llama.cpp/common/CMakeLists.txt +32 -0
- package/src/llama.cpp/common/arg.cpp +66 -16
- package/src/llama.cpp/common/chat-template.hpp +515 -0
- package/src/llama.cpp/common/chat.cpp +966 -0
- package/src/llama.cpp/common/chat.hpp +52 -0
- package/src/llama.cpp/common/common.cpp +159 -36
- package/src/llama.cpp/common/common.h +56 -14
- package/src/llama.cpp/common/json-schema-to-grammar.cpp +46 -66
- package/src/llama.cpp/common/json-schema-to-grammar.h +15 -1
- package/src/llama.cpp/common/llguidance.cpp +270 -0
- package/src/llama.cpp/common/log.cpp +1 -10
- package/src/llama.cpp/common/log.h +10 -0
- package/src/llama.cpp/common/minja.hpp +2868 -0
- package/src/llama.cpp/common/sampling.cpp +22 -1
- package/src/llama.cpp/common/sampling.h +3 -0
- package/src/llama.cpp/docs/build.md +54 -9
- package/src/llama.cpp/examples/export-lora/export-lora.cpp +12 -2
- package/src/llama.cpp/examples/gbnf-validator/gbnf-validator.cpp +1 -1
- package/src/llama.cpp/examples/llava/CMakeLists.txt +7 -0
- package/src/llama.cpp/examples/llava/clip-quantize-cli.cpp +59 -0
- package/src/llama.cpp/examples/llava/clip.cpp +133 -14
- package/src/llama.cpp/examples/llava/clip.h +2 -0
- package/src/llama.cpp/examples/llava/llava.cpp +22 -8
- package/src/llama.cpp/examples/llava/minicpmv-cli.cpp +9 -1
- package/src/llama.cpp/examples/main/main.cpp +26 -25
- package/src/llama.cpp/examples/run/linenoise.cpp/linenoise.cpp +136 -137
- package/src/llama.cpp/examples/run/linenoise.cpp/linenoise.h +18 -4
- package/src/llama.cpp/examples/run/run.cpp +224 -69
- package/src/llama.cpp/examples/server/server.cpp +252 -81
- package/src/llama.cpp/examples/server/utils.hpp +73 -21
- package/src/llama.cpp/examples/simple-chat/simple-chat.cpp +6 -4
- package/src/llama.cpp/examples/simple-cmake-pkg/CMakeLists.txt +11 -0
- package/src/llama.cpp/ggml/CMakeLists.txt +78 -1
- package/src/llama.cpp/ggml/include/ggml.h +1 -1
- package/src/llama.cpp/ggml/src/CMakeLists.txt +21 -4
- package/src/llama.cpp/ggml/src/ggml-alloc.c +1 -13
- package/src/llama.cpp/ggml/src/ggml-cpu/ggml-cpu-quants.c +91 -78
- package/src/llama.cpp/ggml/src/ggml-cpu/ggml-cpu.c +7 -7
- package/src/llama.cpp/ggml/src/ggml-cpu/ggml-cpu.cpp +2 -1
- package/src/llama.cpp/ggml/src/ggml-cuda/CMakeLists.txt +1 -1
- package/src/llama.cpp/ggml/src/ggml-cuda/vendors/hip.h +46 -0
- package/src/llama.cpp/ggml/src/ggml-hip/CMakeLists.txt +16 -1
- package/src/llama.cpp/ggml/src/ggml-musa/CMakeLists.txt +1 -1
- package/src/llama.cpp/ggml/src/ggml-rpc/ggml-rpc.cpp +28 -8
- package/src/llama.cpp/ggml/src/ggml-sycl/ggml-sycl.cpp +5 -7
- package/src/llama.cpp/ggml/src/ggml-sycl/softmax.cpp +33 -23
- package/src/llama.cpp/ggml/src/ggml-sycl/softmax.hpp +1 -5
- package/src/llama.cpp/ggml/src/ggml-vulkan/ggml-vulkan.cpp +323 -121
- package/src/llama.cpp/ggml/src/ggml-vulkan/vulkan-shaders/vulkan-shaders-gen.cpp +13 -3
- package/src/llama.cpp/ggml/src/ggml.c +23 -13
- package/src/llama.cpp/include/llama.h +14 -1
- package/src/llama.cpp/models/ggml-vocab-deepseek-r1-qwen.gguf.inp +112 -0
- package/src/llama.cpp/models/ggml-vocab-deepseek-r1-qwen.gguf.out +46 -0
- package/src/llama.cpp/src/CMakeLists.txt +1 -1
- package/src/llama.cpp/src/llama-arch.cpp +7 -2
- package/src/llama.cpp/src/llama-arch.h +3 -1
- package/src/llama.cpp/src/llama-chat.cpp +11 -2
- package/src/llama.cpp/src/llama-chat.h +1 -0
- package/src/llama.cpp/src/llama-grammar.cpp +86 -6
- package/src/llama.cpp/src/llama-grammar.h +22 -1
- package/src/llama.cpp/src/llama-mmap.cpp +1 -0
- package/src/llama.cpp/src/llama-model-loader.cpp +1 -1
- package/src/llama.cpp/src/llama-model.cpp +76 -6
- package/src/llama.cpp/src/llama-sampling.cpp +47 -4
- package/src/llama.cpp/src/llama-vocab.cpp +10 -4
- package/src/llama.cpp/src/llama.cpp +181 -123
- package/src/llama.cpp/tests/CMakeLists.txt +4 -0
- package/src/llama.cpp/tests/test-backend-ops.cpp +158 -57
- package/src/llama.cpp/tests/test-chat-template.cpp +154 -31
- package/src/llama.cpp/tests/test-chat.cpp +607 -0
- package/src/llama.cpp/tests/test-grammar-integration.cpp +2 -2
- package/src/llama.cpp/tests/test-grammar-llguidance.cpp +1140 -0
- package/src/llama.cpp/tests/test-json-schema-to-grammar.cpp +1 -1
- package/src/llama.cpp/examples/main-cmake-pkg/CMakeLists.txt +0 -32
|
@@ -0,0 +1,1140 @@
|
|
|
1
|
+
#ifdef NDEBUG
|
|
2
|
+
# undef NDEBUG
|
|
3
|
+
#endif
|
|
4
|
+
|
|
5
|
+
#include "unicode.h"
|
|
6
|
+
#include "sampling.h"
|
|
7
|
+
|
|
8
|
+
#include <cassert>
|
|
9
|
+
#include <string>
|
|
10
|
+
#include <vector>
|
|
11
|
+
|
|
12
|
+
static const llama_vocab * vocab;
|
|
13
|
+
|
|
14
|
+
static bool match_string(const std::string & input, llama_sampler * grammar) {
|
|
15
|
+
llama_sampler_reset(grammar);
|
|
16
|
+
auto tokens = common_tokenize(vocab, input, false, false);
|
|
17
|
+
|
|
18
|
+
auto n_vocab = llama_vocab_n_tokens(vocab);
|
|
19
|
+
|
|
20
|
+
std::vector<llama_token_data> cur;
|
|
21
|
+
cur.reserve(n_vocab);
|
|
22
|
+
for (llama_token token_id = 0; token_id < (llama_token) n_vocab; token_id++) {
|
|
23
|
+
cur.emplace_back(llama_token_data{ token_id, 0.0f, 0.0f });
|
|
24
|
+
}
|
|
25
|
+
auto tok_arr = llama_token_data_array{ cur.data(), cur.size(), -1, false };
|
|
26
|
+
|
|
27
|
+
for (const auto token : tokens) {
|
|
28
|
+
for (llama_token token_id = 0; token_id < (llama_token) n_vocab; token_id++) {
|
|
29
|
+
cur[token_id].logit = 0.0f;
|
|
30
|
+
}
|
|
31
|
+
llama_sampler_apply(grammar, &tok_arr);
|
|
32
|
+
if (cur[token].logit < 0.0f) {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
llama_sampler_accept(grammar, token);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// do we allow EOS at the end? if so the grammar is accepting
|
|
39
|
+
|
|
40
|
+
auto tok_eos = llama_vocab_eot(vocab);
|
|
41
|
+
if (tok_eos == LLAMA_TOKEN_NULL) {
|
|
42
|
+
tok_eos = llama_vocab_eos(vocab);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
cur[tok_eos].logit = 0.0f;
|
|
46
|
+
llama_sampler_apply(grammar, &tok_arr);
|
|
47
|
+
|
|
48
|
+
return cur[tok_eos].logit >= 0.0f;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
static void test(const std::string & test_desc, const std::string & grammar_str,
|
|
52
|
+
const std::vector<std::string> & passing_strings, const std::vector<std::string> & failing_strings) {
|
|
53
|
+
fprintf(stderr, "⚫ Testing %s\n%s\n", test_desc.c_str(), grammar_str.c_str());
|
|
54
|
+
fflush(stderr);
|
|
55
|
+
|
|
56
|
+
auto * grammar = llama_sampler_init_llg(vocab, "lark", grammar_str.c_str());
|
|
57
|
+
|
|
58
|
+
fprintf(stderr, " 🔵 Valid strings:\n");
|
|
59
|
+
|
|
60
|
+
// Passing strings
|
|
61
|
+
for (const auto & test_string : passing_strings) {
|
|
62
|
+
fprintf(stderr, " \"%s\" ", test_string.c_str());
|
|
63
|
+
fflush(stderr);
|
|
64
|
+
|
|
65
|
+
bool matched = match_string(test_string, grammar);
|
|
66
|
+
|
|
67
|
+
if (!matched) {
|
|
68
|
+
fprintf(stderr, "❌ (failed to match)\n");
|
|
69
|
+
|
|
70
|
+
// DEBUG: Write strings to files so that we can analyze more easily with gbnf-validator program to see exactly where things failed.
|
|
71
|
+
// DEBUG: Write the grammar_str to test-grammar-integration.grammar.gbnf
|
|
72
|
+
FILE * grammar_file = fopen("test-grammar-integration.grammar.gbnf", "w");
|
|
73
|
+
if (grammar_file) {
|
|
74
|
+
fprintf(grammar_file, "%s", grammar_str.c_str());
|
|
75
|
+
fclose(grammar_file);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// DEBUG: Write the test string to test-grammar-integration.string.txt
|
|
79
|
+
FILE * string_file = fopen("test-grammar-integration.string.txt", "w");
|
|
80
|
+
if (string_file) {
|
|
81
|
+
fprintf(string_file, "%s", test_string.c_str());
|
|
82
|
+
fclose(string_file);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
fprintf(stderr,
|
|
86
|
+
"\n NOTE: Debug grammar file generated. To analyze this failure in detail, run the following "
|
|
87
|
+
"command: ./llama-gbnf-validator test-grammar-integration.grammar.gbnf "
|
|
88
|
+
"test-grammar-integration.string.txt\n\n");
|
|
89
|
+
} else {
|
|
90
|
+
fprintf(stdout, "✅︎\n");
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
assert(matched);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
fprintf(stderr, " 🟠 Invalid strings:\n");
|
|
97
|
+
|
|
98
|
+
// Failing strings
|
|
99
|
+
for (const auto & test_string : failing_strings) {
|
|
100
|
+
fprintf(stderr, " \"%s\" ", test_string.c_str());
|
|
101
|
+
fflush(stderr);
|
|
102
|
+
|
|
103
|
+
bool matched = match_string(test_string, grammar);
|
|
104
|
+
|
|
105
|
+
if (matched) {
|
|
106
|
+
fprintf(stderr, "❌ (incorrectly matched)\n");
|
|
107
|
+
} else {
|
|
108
|
+
fprintf(stdout, "✅︎\n");
|
|
109
|
+
}
|
|
110
|
+
assert(!matched);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
llama_sampler_free(grammar);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
static void test_grammar(const std::string & test_desc, const std::string & grammar_str,
|
|
117
|
+
const std::vector<std::string> & passing_strings,
|
|
118
|
+
const std::vector<std::string> & failing_strings) {
|
|
119
|
+
test(test_desc + ". Grammar: " + grammar_str, grammar_str, passing_strings, failing_strings);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
static void test_schema(const std::string & test_desc, const std::string & schema_str,
|
|
123
|
+
const std::vector<std::string> & passing_strings,
|
|
124
|
+
const std::vector<std::string> & failing_strings) {
|
|
125
|
+
test(test_desc + ". Schema: " + schema_str, "%llguidance {}\nstart: %json " + schema_str, passing_strings,
|
|
126
|
+
failing_strings);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
static void test_simple_grammar() {
|
|
130
|
+
test_schema("min 0",
|
|
131
|
+
R"""({
|
|
132
|
+
"type": "integer",
|
|
133
|
+
"minimum": 0
|
|
134
|
+
})""",
|
|
135
|
+
// Passing strings
|
|
136
|
+
{
|
|
137
|
+
"0",
|
|
138
|
+
"10",
|
|
139
|
+
"12",
|
|
140
|
+
"10000",
|
|
141
|
+
},
|
|
142
|
+
// Failing strings
|
|
143
|
+
{
|
|
144
|
+
"-1",
|
|
145
|
+
"-10",
|
|
146
|
+
"-10000",
|
|
147
|
+
"-100000000000000000000000000000000",
|
|
148
|
+
// "100000000000000000000000000000000",
|
|
149
|
+
"00",
|
|
150
|
+
"01",
|
|
151
|
+
"-0",
|
|
152
|
+
});
|
|
153
|
+
test_schema("min 2",
|
|
154
|
+
// Schema
|
|
155
|
+
R"""({
|
|
156
|
+
"type": "integer",
|
|
157
|
+
"minimum": 2
|
|
158
|
+
})""",
|
|
159
|
+
// Passing strings
|
|
160
|
+
{
|
|
161
|
+
"2",
|
|
162
|
+
"3",
|
|
163
|
+
"4",
|
|
164
|
+
"10",
|
|
165
|
+
"20",
|
|
166
|
+
"1234567890000000",
|
|
167
|
+
},
|
|
168
|
+
// Failing strings
|
|
169
|
+
{
|
|
170
|
+
"0", "1", "-1", "-100", "0", "1", "01", "02",
|
|
171
|
+
// "12345678900000000",
|
|
172
|
+
});
|
|
173
|
+
test_schema("min 456",
|
|
174
|
+
R"""({
|
|
175
|
+
"type": "integer",
|
|
176
|
+
"minimum": 456
|
|
177
|
+
})""",
|
|
178
|
+
// Passing strings
|
|
179
|
+
{
|
|
180
|
+
"456",
|
|
181
|
+
"4560",
|
|
182
|
+
"457",
|
|
183
|
+
"460",
|
|
184
|
+
"500",
|
|
185
|
+
},
|
|
186
|
+
// Failing strings
|
|
187
|
+
{
|
|
188
|
+
"455",
|
|
189
|
+
"356",
|
|
190
|
+
"50",
|
|
191
|
+
"050",
|
|
192
|
+
"-1",
|
|
193
|
+
"-456",
|
|
194
|
+
});
|
|
195
|
+
test_schema("min -123",
|
|
196
|
+
R"""({
|
|
197
|
+
"type": "integer",
|
|
198
|
+
"minimum": -123
|
|
199
|
+
})""",
|
|
200
|
+
// Passing strings
|
|
201
|
+
{
|
|
202
|
+
"-123",
|
|
203
|
+
"-122",
|
|
204
|
+
"-11",
|
|
205
|
+
"-1",
|
|
206
|
+
"0",
|
|
207
|
+
"1",
|
|
208
|
+
"123",
|
|
209
|
+
"1234",
|
|
210
|
+
"2345",
|
|
211
|
+
},
|
|
212
|
+
// Failing strings
|
|
213
|
+
{
|
|
214
|
+
"-1234",
|
|
215
|
+
"-124",
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
test_schema("max 9999",
|
|
219
|
+
// Schema
|
|
220
|
+
R"""({
|
|
221
|
+
"type": "integer",
|
|
222
|
+
"maximum": 9999
|
|
223
|
+
})""",
|
|
224
|
+
// Passing strings
|
|
225
|
+
{
|
|
226
|
+
"-99999",
|
|
227
|
+
"0",
|
|
228
|
+
"9999",
|
|
229
|
+
},
|
|
230
|
+
// Failing strings
|
|
231
|
+
{
|
|
232
|
+
"10000",
|
|
233
|
+
"99991",
|
|
234
|
+
});
|
|
235
|
+
test_schema("max -9999",
|
|
236
|
+
// Schema
|
|
237
|
+
R"""({
|
|
238
|
+
"type": "integer",
|
|
239
|
+
"maximum": -9999
|
|
240
|
+
})""",
|
|
241
|
+
// Passing strings
|
|
242
|
+
{
|
|
243
|
+
"-10000",
|
|
244
|
+
"-9999",
|
|
245
|
+
},
|
|
246
|
+
// Failing strings
|
|
247
|
+
{
|
|
248
|
+
"-9998",
|
|
249
|
+
"0",
|
|
250
|
+
"9999",
|
|
251
|
+
});
|
|
252
|
+
test_schema("min 5 max 30",
|
|
253
|
+
// Schema
|
|
254
|
+
R"""({
|
|
255
|
+
"type": "integer",
|
|
256
|
+
"minimum": 5,
|
|
257
|
+
"maximum": 30
|
|
258
|
+
})""",
|
|
259
|
+
// Passing strings
|
|
260
|
+
{
|
|
261
|
+
"5",
|
|
262
|
+
"10",
|
|
263
|
+
"30",
|
|
264
|
+
},
|
|
265
|
+
// Failing strings
|
|
266
|
+
{
|
|
267
|
+
"05",
|
|
268
|
+
"4",
|
|
269
|
+
"-1",
|
|
270
|
+
"31",
|
|
271
|
+
"123",
|
|
272
|
+
"0123",
|
|
273
|
+
});
|
|
274
|
+
test_schema("min -1 max 1",
|
|
275
|
+
R"""({
|
|
276
|
+
"type": "integer",
|
|
277
|
+
"minimum": -1,
|
|
278
|
+
"maximum": 1
|
|
279
|
+
})""",
|
|
280
|
+
// Passing strings
|
|
281
|
+
{
|
|
282
|
+
"-1",
|
|
283
|
+
"0",
|
|
284
|
+
"1",
|
|
285
|
+
},
|
|
286
|
+
// Failing strings
|
|
287
|
+
{
|
|
288
|
+
"-11",
|
|
289
|
+
"-10",
|
|
290
|
+
"-2",
|
|
291
|
+
"2",
|
|
292
|
+
"10",
|
|
293
|
+
"11",
|
|
294
|
+
});
|
|
295
|
+
test_schema("min -123 max 42",
|
|
296
|
+
R"""({
|
|
297
|
+
"type": "integer",
|
|
298
|
+
"minimum": -123,
|
|
299
|
+
"maximum": 42
|
|
300
|
+
})""",
|
|
301
|
+
// Passing strings
|
|
302
|
+
{
|
|
303
|
+
"-123",
|
|
304
|
+
"-122",
|
|
305
|
+
"-13",
|
|
306
|
+
"-11",
|
|
307
|
+
"-2",
|
|
308
|
+
"-1",
|
|
309
|
+
"0",
|
|
310
|
+
"1",
|
|
311
|
+
"5",
|
|
312
|
+
"10",
|
|
313
|
+
"39",
|
|
314
|
+
"40",
|
|
315
|
+
"42",
|
|
316
|
+
},
|
|
317
|
+
// Failing strings
|
|
318
|
+
{
|
|
319
|
+
"-0123",
|
|
320
|
+
"-124",
|
|
321
|
+
"-1123",
|
|
322
|
+
"-200",
|
|
323
|
+
"43",
|
|
324
|
+
"123",
|
|
325
|
+
"0123",
|
|
326
|
+
});
|
|
327
|
+
test_schema("exclusive min / max",
|
|
328
|
+
// Schema
|
|
329
|
+
R"""({
|
|
330
|
+
"type": "integer",
|
|
331
|
+
"exclusiveMinimum": 0,
|
|
332
|
+
"exclusiveMaximum": 10000
|
|
333
|
+
})""",
|
|
334
|
+
// Passing strings
|
|
335
|
+
{
|
|
336
|
+
"1",
|
|
337
|
+
"9999",
|
|
338
|
+
},
|
|
339
|
+
// Failing strings
|
|
340
|
+
{
|
|
341
|
+
"0",
|
|
342
|
+
"01",
|
|
343
|
+
"10000",
|
|
344
|
+
"99999",
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
// Test case for a simple grammar
|
|
348
|
+
test_grammar("simple grammar",
|
|
349
|
+
R"""(
|
|
350
|
+
start: expr
|
|
351
|
+
expr: term ("+" term)*
|
|
352
|
+
term: number
|
|
353
|
+
number: /[0-9]+/ )""",
|
|
354
|
+
// Passing strings
|
|
355
|
+
{
|
|
356
|
+
"42",
|
|
357
|
+
"1+2+3+4+5",
|
|
358
|
+
"123+456",
|
|
359
|
+
},
|
|
360
|
+
// Failing strings
|
|
361
|
+
{
|
|
362
|
+
"+",
|
|
363
|
+
"/ 3",
|
|
364
|
+
"1+2+3+4+5+",
|
|
365
|
+
"12a45",
|
|
366
|
+
});
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
static void test_complex_grammar() {
|
|
370
|
+
// Test case for a more complex grammar, with both failure strings and success strings
|
|
371
|
+
test_grammar("medium complexity grammar",
|
|
372
|
+
// Grammar
|
|
373
|
+
R"""(
|
|
374
|
+
start: expression
|
|
375
|
+
expression: term ws (("+"|"-") ws term)*
|
|
376
|
+
term: factor ws (("*"|"/") ws factor)*
|
|
377
|
+
factor: number | variable | "(" expression ")" | function-call
|
|
378
|
+
number: /[0-9]+/
|
|
379
|
+
variable: /[a-zA-Z_][a-zA-Z0-9_]*/
|
|
380
|
+
function-call: variable ws "(" (expression ("," ws expression)*)? ")"
|
|
381
|
+
ws: /[ \t\n\r]?/ )""",
|
|
382
|
+
// Passing strings
|
|
383
|
+
{ "42",
|
|
384
|
+
"1*2*3*4*5",
|
|
385
|
+
"x",
|
|
386
|
+
"x+10",
|
|
387
|
+
"x1+y2",
|
|
388
|
+
"(a+b)*(c-d)",
|
|
389
|
+
"func()",
|
|
390
|
+
"func(x,y+2)",
|
|
391
|
+
"a*(b+c)-d/e",
|
|
392
|
+
"f(g(x),h(y,z))",
|
|
393
|
+
"x + 10",
|
|
394
|
+
"x1 + y2",
|
|
395
|
+
"(a + b) * (c - d)",
|
|
396
|
+
"func()",
|
|
397
|
+
"func(x, y + 2)",
|
|
398
|
+
"a * (b + c) - d / e",
|
|
399
|
+
"f(g(x), h(y, z))",
|
|
400
|
+
"123+456",
|
|
401
|
+
"123*456*789-123/456+789*123",
|
|
402
|
+
"123+456*789-123/456+789*123-456/789+123*456-789/123+456*789-123/456+789*123-456" },
|
|
403
|
+
// Failing strings
|
|
404
|
+
{
|
|
405
|
+
"+",
|
|
406
|
+
"/ 3x",
|
|
407
|
+
"x + + y",
|
|
408
|
+
"a * / b",
|
|
409
|
+
"func(,)",
|
|
410
|
+
"func(x y)",
|
|
411
|
+
"(a + b",
|
|
412
|
+
"x + y)",
|
|
413
|
+
"a + b * (c - d",
|
|
414
|
+
"42 +",
|
|
415
|
+
"x +",
|
|
416
|
+
"x + 10 +",
|
|
417
|
+
"(a + b) * (c - d",
|
|
418
|
+
"func(",
|
|
419
|
+
"func(x, y + 2",
|
|
420
|
+
"a * (b + c) - d /",
|
|
421
|
+
"f(g(x), h(y, z)",
|
|
422
|
+
"123+456*789-123/456+789*123-456/789+123*456-789/123+456*789-123/456+789*123-456/",
|
|
423
|
+
});
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
static void test_special_chars() {
|
|
427
|
+
// A collection of tests to exercise special characters such as "."
|
|
428
|
+
test_grammar("special characters",
|
|
429
|
+
// Grammar
|
|
430
|
+
R"""(
|
|
431
|
+
start: /.../ "abc" /.../
|
|
432
|
+
)""",
|
|
433
|
+
// Passing strings
|
|
434
|
+
{ "abcabcabc", "aaaabcccc",
|
|
435
|
+
// NOTE: Also ensures that multi-byte characters still count as a single character
|
|
436
|
+
"🔵🟠✅abc❌🟠🔵" },
|
|
437
|
+
// Failing strings
|
|
438
|
+
{ "aaabcccc", "aaaaabcccc", "aaaabccc", "aaaabccccc", "🔵🟠✅❌abc❌✅🟠🔵", "🔵🟠abc🟠🔵" });
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
static void test_quantifiers() {
|
|
442
|
+
// A collection of tests to exercise * + and ? quantifiers
|
|
443
|
+
|
|
444
|
+
test_grammar(
|
|
445
|
+
"* quantifier",
|
|
446
|
+
// Grammar
|
|
447
|
+
R"""(start: "a"*)""",
|
|
448
|
+
// Passing strings
|
|
449
|
+
{ "", "a", "aaaaa", "aaaaaaaaaaaaaaaaaa", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" },
|
|
450
|
+
// Failing strings
|
|
451
|
+
{ "b", "ab", "aab", "ba", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab" });
|
|
452
|
+
test_grammar(
|
|
453
|
+
"+ quantifier",
|
|
454
|
+
// Grammar
|
|
455
|
+
R"""(start: "a"+)""",
|
|
456
|
+
// Passing strings
|
|
457
|
+
{ "a", "aaaaa", "aaaaaaaaaaaaaaaaaa", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" },
|
|
458
|
+
// Failing strings
|
|
459
|
+
{ "", "b", "ab", "aab", "ba", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab" });
|
|
460
|
+
test_grammar("? quantifier",
|
|
461
|
+
// Grammar
|
|
462
|
+
R"""(start: "a"?)""",
|
|
463
|
+
// Passing strings
|
|
464
|
+
{ "", "a" },
|
|
465
|
+
// Failing strings
|
|
466
|
+
{
|
|
467
|
+
"b",
|
|
468
|
+
"ab",
|
|
469
|
+
"aa",
|
|
470
|
+
"ba",
|
|
471
|
+
});
|
|
472
|
+
test_grammar("mixed quantifiers",
|
|
473
|
+
// Grammar
|
|
474
|
+
R"""(
|
|
475
|
+
start: cons+ vowel* cons? (vowel cons)*
|
|
476
|
+
vowel: /[aeiouy]/
|
|
477
|
+
cons: /[bcdfghjklmnpqrstvwxyz]/
|
|
478
|
+
)""",
|
|
479
|
+
// Passing strings
|
|
480
|
+
{
|
|
481
|
+
"yes",
|
|
482
|
+
"no",
|
|
483
|
+
"noyes",
|
|
484
|
+
"crwth",
|
|
485
|
+
"four",
|
|
486
|
+
"bryyyy",
|
|
487
|
+
},
|
|
488
|
+
// Failing strings
|
|
489
|
+
{
|
|
490
|
+
"yess",
|
|
491
|
+
"yesno",
|
|
492
|
+
"forty",
|
|
493
|
+
"catyyy",
|
|
494
|
+
});
|
|
495
|
+
test_grammar("simple exact repetition",
|
|
496
|
+
// Grammar
|
|
497
|
+
R"""(
|
|
498
|
+
start: /[ab]{4}/
|
|
499
|
+
)""",
|
|
500
|
+
// Passing strings
|
|
501
|
+
{
|
|
502
|
+
"aaaa",
|
|
503
|
+
"bbbb",
|
|
504
|
+
"abab",
|
|
505
|
+
},
|
|
506
|
+
// Failing strings
|
|
507
|
+
{
|
|
508
|
+
"a",
|
|
509
|
+
"b",
|
|
510
|
+
"aaaaa",
|
|
511
|
+
});
|
|
512
|
+
test_grammar("simple min repetition",
|
|
513
|
+
// Grammar
|
|
514
|
+
R"""(
|
|
515
|
+
start: /[ab]{4,}/
|
|
516
|
+
)""",
|
|
517
|
+
// Passing strings
|
|
518
|
+
{
|
|
519
|
+
"aaaa",
|
|
520
|
+
"aaaaab",
|
|
521
|
+
"bbbb",
|
|
522
|
+
"ababab",
|
|
523
|
+
},
|
|
524
|
+
// Failing strings
|
|
525
|
+
{
|
|
526
|
+
"",
|
|
527
|
+
"aba",
|
|
528
|
+
});
|
|
529
|
+
test_grammar("simple max repetition",
|
|
530
|
+
// Grammar
|
|
531
|
+
R"""(
|
|
532
|
+
start: /[ab]{0,4}/
|
|
533
|
+
)""",
|
|
534
|
+
// Passing strings
|
|
535
|
+
{
|
|
536
|
+
"",
|
|
537
|
+
"a",
|
|
538
|
+
"aa",
|
|
539
|
+
"aaa",
|
|
540
|
+
"aaab",
|
|
541
|
+
},
|
|
542
|
+
// Failing strings
|
|
543
|
+
{
|
|
544
|
+
"aaaaa",
|
|
545
|
+
});
|
|
546
|
+
// test_grammar("min / max repetition",
|
|
547
|
+
// // Grammar
|
|
548
|
+
// R"""(
|
|
549
|
+
// start: ("0x" /[A-F0-9]{2}/ " "?){3,5}
|
|
550
|
+
// )""",
|
|
551
|
+
// // Passing strings
|
|
552
|
+
// {
|
|
553
|
+
// "0xFF 0x12 0xAB",
|
|
554
|
+
// "0xFF 0x12 0xAB 0x00 0x00",
|
|
555
|
+
// },
|
|
556
|
+
// // Failing strings
|
|
557
|
+
// {
|
|
558
|
+
// "",
|
|
559
|
+
// "0xFF",
|
|
560
|
+
// "0xFF 0x12",
|
|
561
|
+
// "0xFF 0x12 0xAB 0x00 0x00 0x00",
|
|
562
|
+
// });
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
static void test_json_schema() {
|
|
566
|
+
// Note that this is similar to the regular grammar tests,
|
|
567
|
+
// but we convert each json schema to a grammar before parsing.
|
|
568
|
+
// Otherwise, this test structure is the same.
|
|
569
|
+
|
|
570
|
+
test_schema("empty schema (object)",
|
|
571
|
+
// Schema
|
|
572
|
+
R"""(
|
|
573
|
+
{"type":"object"}
|
|
574
|
+
)""",
|
|
575
|
+
// Passing strings
|
|
576
|
+
{
|
|
577
|
+
R"""({})""",
|
|
578
|
+
R"""({"foo": "bar"})""",
|
|
579
|
+
},
|
|
580
|
+
// Failing strings
|
|
581
|
+
{
|
|
582
|
+
"",
|
|
583
|
+
"[]",
|
|
584
|
+
"null",
|
|
585
|
+
R"""("")""",
|
|
586
|
+
"true",
|
|
587
|
+
});
|
|
588
|
+
|
|
589
|
+
test_schema(
|
|
590
|
+
"exotic formats (list)",
|
|
591
|
+
// Schema
|
|
592
|
+
R"""({
|
|
593
|
+
"items": [
|
|
594
|
+
{ "format": "date" },
|
|
595
|
+
{ "format": "uuid" },
|
|
596
|
+
{ "format": "time" },
|
|
597
|
+
{ "format": "date-time" }
|
|
598
|
+
]
|
|
599
|
+
})""",
|
|
600
|
+
// Passing strings
|
|
601
|
+
{
|
|
602
|
+
// "{}", // NOTE: This string passes for this schema on https://www.jsonschemavalidator.net/ -- should it?
|
|
603
|
+
// "[]", // NOTE: This string passes for this schema on https://www.jsonschemavalidator.net/ -- should it?
|
|
604
|
+
R"""(["2012-04-23", "12345678-1234-1234-1234-1234567890ab", "18:25:43.511Z", "2012-04-23T18:25:43.511Z"])""",
|
|
605
|
+
//R"""(["2012-04-23","12345678-1234-1234-1234-1234567890ab"])""", // NOTE: This string passes for this schema on https://www.jsonschemavalidator.net/ -- should it?
|
|
606
|
+
//R"""({"foo": "bar"})""", // NOTE: This string passes for this schema on https://www.jsonschemavalidator.net/ -- should it?
|
|
607
|
+
},
|
|
608
|
+
// Failing strings
|
|
609
|
+
{
|
|
610
|
+
R"""(["foo", "bar"])""",
|
|
611
|
+
R"""(["12345678-1234-1234-1234-1234567890ab"])""",
|
|
612
|
+
});
|
|
613
|
+
|
|
614
|
+
test_schema("string",
|
|
615
|
+
// Schema
|
|
616
|
+
R"""({
|
|
617
|
+
"type": "string"
|
|
618
|
+
})""",
|
|
619
|
+
// Passing strings
|
|
620
|
+
{
|
|
621
|
+
R"""("foo")""",
|
|
622
|
+
R"""("bar")""",
|
|
623
|
+
R"""("")""",
|
|
624
|
+
},
|
|
625
|
+
// Failing strings
|
|
626
|
+
{
|
|
627
|
+
R"""({})""",
|
|
628
|
+
R"""("foo": "bar")""",
|
|
629
|
+
});
|
|
630
|
+
|
|
631
|
+
test_schema("string w/ min length 1",
|
|
632
|
+
// Schema
|
|
633
|
+
R"""({
|
|
634
|
+
"type": "string",
|
|
635
|
+
"minLength": 1
|
|
636
|
+
})""",
|
|
637
|
+
// Passing strings
|
|
638
|
+
{
|
|
639
|
+
R"""("foo")""",
|
|
640
|
+
R"""("bar")""",
|
|
641
|
+
},
|
|
642
|
+
// Failing strings
|
|
643
|
+
{
|
|
644
|
+
R"""("")""",
|
|
645
|
+
R"""({})""",
|
|
646
|
+
R"""("foo": "bar")""",
|
|
647
|
+
});
|
|
648
|
+
|
|
649
|
+
test_schema("string w/ min length 3",
|
|
650
|
+
// Schema
|
|
651
|
+
R"""({
|
|
652
|
+
"type": "string",
|
|
653
|
+
"minLength": 3
|
|
654
|
+
})""",
|
|
655
|
+
// Passing strings
|
|
656
|
+
{
|
|
657
|
+
R"""("foo")""",
|
|
658
|
+
R"""("bar")""",
|
|
659
|
+
R"""("foobar")""",
|
|
660
|
+
},
|
|
661
|
+
// Failing strings
|
|
662
|
+
{
|
|
663
|
+
R"""("")""",
|
|
664
|
+
R"""("f")""",
|
|
665
|
+
R"""("fo")""",
|
|
666
|
+
});
|
|
667
|
+
|
|
668
|
+
test_schema("string w/ max length",
|
|
669
|
+
// Schema
|
|
670
|
+
R"""({
|
|
671
|
+
"type": "string",
|
|
672
|
+
"maxLength": 3
|
|
673
|
+
})""",
|
|
674
|
+
// Passing strings
|
|
675
|
+
{
|
|
676
|
+
R"""("foo")""",
|
|
677
|
+
R"""("bar")""",
|
|
678
|
+
R"""("")""",
|
|
679
|
+
R"""("f")""",
|
|
680
|
+
R"""("fo")""",
|
|
681
|
+
},
|
|
682
|
+
// Failing strings
|
|
683
|
+
{
|
|
684
|
+
R"""("foobar")""",
|
|
685
|
+
});
|
|
686
|
+
|
|
687
|
+
test_schema("string w/ min & max length",
|
|
688
|
+
// Schema
|
|
689
|
+
R"""({
|
|
690
|
+
"type": "string",
|
|
691
|
+
"minLength": 1,
|
|
692
|
+
"maxLength": 4
|
|
693
|
+
})""",
|
|
694
|
+
// Passing strings
|
|
695
|
+
{
|
|
696
|
+
R"""("foo")""",
|
|
697
|
+
R"""("bar")""",
|
|
698
|
+
R"""("f")""",
|
|
699
|
+
R"""("barf")""",
|
|
700
|
+
},
|
|
701
|
+
// Failing strings
|
|
702
|
+
{
|
|
703
|
+
R"""("")""",
|
|
704
|
+
R"""("barfo")""",
|
|
705
|
+
R"""("foobar")""",
|
|
706
|
+
});
|
|
707
|
+
|
|
708
|
+
test_schema("boolean",
|
|
709
|
+
// Schema
|
|
710
|
+
R"""({
|
|
711
|
+
"type": "boolean"
|
|
712
|
+
})""",
|
|
713
|
+
// Passing strings
|
|
714
|
+
{
|
|
715
|
+
"true",
|
|
716
|
+
"false",
|
|
717
|
+
},
|
|
718
|
+
// Failing strings
|
|
719
|
+
{
|
|
720
|
+
R"""("")""",
|
|
721
|
+
R"""("true")""",
|
|
722
|
+
R"""(True)""",
|
|
723
|
+
R"""(FALSE)""",
|
|
724
|
+
});
|
|
725
|
+
|
|
726
|
+
test_schema("integer",
|
|
727
|
+
// Schema
|
|
728
|
+
R"""({
|
|
729
|
+
"type": "integer"
|
|
730
|
+
})""",
|
|
731
|
+
// Passing strings
|
|
732
|
+
{
|
|
733
|
+
R"""(0)""",
|
|
734
|
+
R"""(12345)""",
|
|
735
|
+
R"""(1234567890123456)""",
|
|
736
|
+
},
|
|
737
|
+
// Failing strings
|
|
738
|
+
{
|
|
739
|
+
R"""()""",
|
|
740
|
+
R"""(01)""",
|
|
741
|
+
R"""(007)""",
|
|
742
|
+
R"""(12345678901234567 )""",
|
|
743
|
+
});
|
|
744
|
+
|
|
745
|
+
test_schema("string const",
|
|
746
|
+
// Schema
|
|
747
|
+
R"""({
|
|
748
|
+
"const": "foo"
|
|
749
|
+
})""",
|
|
750
|
+
// Passing strings
|
|
751
|
+
{
|
|
752
|
+
R"""("foo")""",
|
|
753
|
+
},
|
|
754
|
+
// Failing strings
|
|
755
|
+
{
|
|
756
|
+
R"""(foo)""",
|
|
757
|
+
R"""("bar")""",
|
|
758
|
+
});
|
|
759
|
+
|
|
760
|
+
test_schema("non-string const",
|
|
761
|
+
// Schema
|
|
762
|
+
R"""({
|
|
763
|
+
"const": true
|
|
764
|
+
})""",
|
|
765
|
+
// Passing strings
|
|
766
|
+
{
|
|
767
|
+
R"""(true)""",
|
|
768
|
+
},
|
|
769
|
+
// Failing strings
|
|
770
|
+
{
|
|
771
|
+
R"""()""",
|
|
772
|
+
R"""(foo)""",
|
|
773
|
+
R"""("true")""",
|
|
774
|
+
});
|
|
775
|
+
|
|
776
|
+
test_schema("non-string const",
|
|
777
|
+
// Schema
|
|
778
|
+
R"""({
|
|
779
|
+
"enum": ["red", "amber", "green", null, 42, ["foo"]]
|
|
780
|
+
})""",
|
|
781
|
+
// Passing strings
|
|
782
|
+
{
|
|
783
|
+
R"""("red")""",
|
|
784
|
+
R"""(null)""",
|
|
785
|
+
R"""(42)""",
|
|
786
|
+
R"""(["foo"])""",
|
|
787
|
+
},
|
|
788
|
+
// Failing strings
|
|
789
|
+
{
|
|
790
|
+
R"""()""",
|
|
791
|
+
R"""(420)""",
|
|
792
|
+
R"""(true)""",
|
|
793
|
+
R"""(foo)""",
|
|
794
|
+
});
|
|
795
|
+
|
|
796
|
+
test_schema("simple pattern",
|
|
797
|
+
// Schema
|
|
798
|
+
R"""({
|
|
799
|
+
"pattern": "^[a-zA-Z0-9_-]*$"
|
|
800
|
+
})""",
|
|
801
|
+
// Passing strings
|
|
802
|
+
{
|
|
803
|
+
R"""("")""",
|
|
804
|
+
R"""("He_llo-12")""",
|
|
805
|
+
},
|
|
806
|
+
// Failing strings
|
|
807
|
+
{
|
|
808
|
+
R"""("!")""",
|
|
809
|
+
R"""("Hello World")""",
|
|
810
|
+
});
|
|
811
|
+
|
|
812
|
+
test_schema("pattern with escapes",
|
|
813
|
+
// Schema
|
|
814
|
+
R"""({
|
|
815
|
+
"pattern": "^a\\^\\$\\.\\[\\]\\(\\)\\|\\{\\}\\*\\+\\?b$"
|
|
816
|
+
})""",
|
|
817
|
+
// Passing strings
|
|
818
|
+
{
|
|
819
|
+
R"""("a^$.[]()|{}*+?b")""",
|
|
820
|
+
},
|
|
821
|
+
// Failing strings
|
|
822
|
+
{
|
|
823
|
+
R"""("ab")""",
|
|
824
|
+
});
|
|
825
|
+
|
|
826
|
+
test_schema("",
|
|
827
|
+
// Schema
|
|
828
|
+
R"""(
|
|
829
|
+
{
|
|
830
|
+
"type": ["array", "null"],
|
|
831
|
+
"items": { "type": "string" }
|
|
832
|
+
}
|
|
833
|
+
)""",
|
|
834
|
+
// Passing strings
|
|
835
|
+
{
|
|
836
|
+
"null",
|
|
837
|
+
"[]",
|
|
838
|
+
"[\"123\"]",
|
|
839
|
+
"[\"foo\", \"bar\"]",
|
|
840
|
+
},
|
|
841
|
+
// Failing strings
|
|
842
|
+
{
|
|
843
|
+
"",
|
|
844
|
+
"[123]",
|
|
845
|
+
"\"foo\"",
|
|
846
|
+
"[\"foo\", 42]",
|
|
847
|
+
});
|
|
848
|
+
|
|
849
|
+
test_schema("min+max items",
|
|
850
|
+
// Schema
|
|
851
|
+
R"""({
|
|
852
|
+
"items": {
|
|
853
|
+
"type": ["number", "integer"]
|
|
854
|
+
},
|
|
855
|
+
"minItems": 3,
|
|
856
|
+
"maxItems": 5
|
|
857
|
+
})""",
|
|
858
|
+
// Passing strings
|
|
859
|
+
{
|
|
860
|
+
R"""([1, 2, 3])""",
|
|
861
|
+
R"""([1, 2, 3, 4])""",
|
|
862
|
+
R"""([1, 2, 3, 4, 5])""",
|
|
863
|
+
// this is in fact correct; keyword do not apply if the type is wrong
|
|
864
|
+
R"""(1)""",
|
|
865
|
+
},
|
|
866
|
+
// Failing strings
|
|
867
|
+
{
|
|
868
|
+
R"""([1, 2])""",
|
|
869
|
+
R"""([1, 2, 3, 4, 5, 6])""",
|
|
870
|
+
});
|
|
871
|
+
|
|
872
|
+
// Properties (from: https://json-schema.org/understanding-json-schema/reference/object#properties)
|
|
873
|
+
test_schema("object properties",
|
|
874
|
+
// Schema
|
|
875
|
+
R"""({
|
|
876
|
+
"type": "object",
|
|
877
|
+
"properties": {
|
|
878
|
+
"number": { "type": "number" },
|
|
879
|
+
"street_name": { "type": "string" },
|
|
880
|
+
"street_type": { "enum": ["Street", "Avenue", "Boulevard"] }
|
|
881
|
+
},
|
|
882
|
+
"additionalProperties": false
|
|
883
|
+
})""",
|
|
884
|
+
// Passing strings
|
|
885
|
+
{
|
|
886
|
+
R"""({ "number": 1600, "street_name": "Pennsylvania", "street_type":"Avenue"})""",
|
|
887
|
+
// "By default, leaving out properties is valid"
|
|
888
|
+
R"""({ "street_name": "Pennsylvania" })""",
|
|
889
|
+
R"""({ "number": 1600, "street_name": "Pennsylvania" })""",
|
|
890
|
+
// "By extension, even an empty object is valid"
|
|
891
|
+
R"""({})""",
|
|
892
|
+
R"""({ "number": 1600, "street_name": "Pennsylvania", "street_type": "Avenue" })""",
|
|
893
|
+
},
|
|
894
|
+
// Failing strings
|
|
895
|
+
{
|
|
896
|
+
// Change datatype from number to string
|
|
897
|
+
R"""({ "number": "1600", "street_name": "Pennsylvania", "street_type":"Avenue"})""",
|
|
898
|
+
// Reorder properties
|
|
899
|
+
R"""({ "street_name": "Pennsylvania", "number": 1600 })""",
|
|
900
|
+
// Reorder properties
|
|
901
|
+
R"""({ "number": "1600", "street_name": "Pennsylvania", "street_type":"Avenue"})""",
|
|
902
|
+
// Additional properties set to false
|
|
903
|
+
R"""({ "number": 1600, "street_name": "Pennsylvania", "street_type":"Avenue", "direction":"NW"})""",
|
|
904
|
+
|
|
905
|
+
});
|
|
906
|
+
|
|
907
|
+
test_schema("additional properties can't override other properties",
|
|
908
|
+
R"""({
|
|
909
|
+
"properties": {
|
|
910
|
+
"a": {"type": "integer"},
|
|
911
|
+
"b": {"type": "integer"}
|
|
912
|
+
},
|
|
913
|
+
"additionalProperties": true
|
|
914
|
+
})""",
|
|
915
|
+
// Passing strings
|
|
916
|
+
{
|
|
917
|
+
R"""({"a": 42})""",
|
|
918
|
+
R"""({"c": ""})""",
|
|
919
|
+
R"""({"a": 42, "c": ""})""",
|
|
920
|
+
R"""({"a_": ""})""",
|
|
921
|
+
},
|
|
922
|
+
// Failing strings
|
|
923
|
+
{
|
|
924
|
+
R"""()""",
|
|
925
|
+
R"""({"a": ""})""",
|
|
926
|
+
R"""({"a": "", "b": ""})""",
|
|
927
|
+
});
|
|
928
|
+
|
|
929
|
+
// Properties (from: https://json-schema.org/understanding-json-schema/reference/object#properties)
|
|
930
|
+
test_schema("object properties, additionalProperties: true",
|
|
931
|
+
// Schema
|
|
932
|
+
R"""({
|
|
933
|
+
"type": "object",
|
|
934
|
+
"properties": {
|
|
935
|
+
"number": { "type": "number" },
|
|
936
|
+
"street_name": { "type": "string" },
|
|
937
|
+
"street_type": { "enum": ["Street", "Avenue", "Boulevard"] }
|
|
938
|
+
},
|
|
939
|
+
"additionalProperties": true
|
|
940
|
+
})""",
|
|
941
|
+
// Passing strings
|
|
942
|
+
{
|
|
943
|
+
// "By extension, even an empty object is valid"
|
|
944
|
+
R"""({})""",
|
|
945
|
+
R"""({"number":1600,"street_name":"Pennsylvania","street_type":"Avenue"})""",
|
|
946
|
+
// "By default, leaving out properties is valid"
|
|
947
|
+
R"""({ "street_name": "Pennsylvania" })""",
|
|
948
|
+
R"""({ "number": 1600, "street_name": "Pennsylvania" })""",
|
|
949
|
+
// "By default, providing additional properties is valid"
|
|
950
|
+
R"""({ "number": 1600, "street_name": "Pennsylvania", "street_type":"Avenue", "direction":"NW"})""",
|
|
951
|
+
R"""({ "number": 1600, "street_name": "Pennsylvania", "street_type": "Avenue" })""",
|
|
952
|
+
},
|
|
953
|
+
// Failing strings
|
|
954
|
+
{
|
|
955
|
+
// Change datatype from number to string
|
|
956
|
+
R"""({ "number": "1600", "street_name": "Pennsylvania", "street_type":"Avenue"})""",
|
|
957
|
+
// Reorder properties
|
|
958
|
+
R"""({ "street_name": "Pennsylvania", "number": 1600, "street_type":"Avenue"})""",
|
|
959
|
+
});
|
|
960
|
+
|
|
961
|
+
// Additional properties: false
|
|
962
|
+
test_schema(
|
|
963
|
+
"required + optional props each in original order",
|
|
964
|
+
// Schema
|
|
965
|
+
R"""({
|
|
966
|
+
"type": "object",
|
|
967
|
+
"properties": {
|
|
968
|
+
"number": { "type": "number" },
|
|
969
|
+
"street_name": { "type": "string" },
|
|
970
|
+
"street_type": { "enum": ["Street", "Avenue", "Boulevard"] }
|
|
971
|
+
},
|
|
972
|
+
"additionalProperties": false
|
|
973
|
+
})""",
|
|
974
|
+
// Passing strings
|
|
975
|
+
{
|
|
976
|
+
R"""({ "street_name": "Pennsylvania" })""",
|
|
977
|
+
R"""({ "number": 1600, "street_type":"Avenue"})""",
|
|
978
|
+
R"""({ "number": 1600, "street_name": "Pennsylvania" })""",
|
|
979
|
+
R"""({ "number": 1600, "street_name": "Pennsylvania", "street_type":"Avenue"})""",
|
|
980
|
+
// Spaces are permitted around enum values
|
|
981
|
+
R"""({ "number": 1600, "street_name": "Pennsylvania", "street_type": "Avenue" })""",
|
|
982
|
+
},
|
|
983
|
+
// Failing strings
|
|
984
|
+
{
|
|
985
|
+
// Reorder properties
|
|
986
|
+
R"""({ "street_type": "Avenue", "number": 1600 })""",
|
|
987
|
+
// Add "direction"
|
|
988
|
+
R"""({ "number": 1600, "street_name": "Pennsylvania", "street_type": "Avenue", "direction": "NW" })""",
|
|
989
|
+
});
|
|
990
|
+
|
|
991
|
+
test_schema("required + optional props each in original order",
|
|
992
|
+
// Schema
|
|
993
|
+
R"""({
|
|
994
|
+
"properties": {
|
|
995
|
+
"b": {"type": "string"},
|
|
996
|
+
"a": {"type": "string"},
|
|
997
|
+
"d": {"type": "string"},
|
|
998
|
+
"c": {"type": "string"}
|
|
999
|
+
},
|
|
1000
|
+
"required": ["a", "b"],
|
|
1001
|
+
"additionalProperties": false
|
|
1002
|
+
})""",
|
|
1003
|
+
// Passing strings
|
|
1004
|
+
{
|
|
1005
|
+
R"""({"b": "foo", "a": "bar"})""",
|
|
1006
|
+
R"""({"b":"foo","a":"bar","d":"qux"})""",
|
|
1007
|
+
R"""({"b":"foo", "a":"bar", "d":"qux", "c":"baz"})""",
|
|
1008
|
+
},
|
|
1009
|
+
// Failing strings
|
|
1010
|
+
{
|
|
1011
|
+
R"""({"a": "foo", "b": "bar"})""",
|
|
1012
|
+
R"""({"b": "bar"})""",
|
|
1013
|
+
R"""({"a": "foo", "c": "baz"})""",
|
|
1014
|
+
R"""({"a":"foo", "b":"bar", "c":"baz", "d":"qux"})""",
|
|
1015
|
+
});
|
|
1016
|
+
|
|
1017
|
+
// NOTE: Example from https://json-schema.org/learn/getting-started-step-by-step#define-required-properties
|
|
1018
|
+
test_schema(
|
|
1019
|
+
"required props",
|
|
1020
|
+
// Schema
|
|
1021
|
+
R"""({
|
|
1022
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
1023
|
+
"$id": "https://example.com/product.schema.json",
|
|
1024
|
+
"title": "Product",
|
|
1025
|
+
"description": "A product from Acme's catalog",
|
|
1026
|
+
"type": "object",
|
|
1027
|
+
"properties": {
|
|
1028
|
+
"productId": {
|
|
1029
|
+
"description": "The unique identifier for a product",
|
|
1030
|
+
"type": "integer"
|
|
1031
|
+
},
|
|
1032
|
+
"productName": {
|
|
1033
|
+
"description": "Name of the product",
|
|
1034
|
+
"type": "string"
|
|
1035
|
+
},
|
|
1036
|
+
"price": {
|
|
1037
|
+
"description": "The price of the product",
|
|
1038
|
+
"type": "number",
|
|
1039
|
+
"exclusiveMinimum": 0
|
|
1040
|
+
},
|
|
1041
|
+
"tags": {
|
|
1042
|
+
"description": "Tags for the product",
|
|
1043
|
+
"type": "array",
|
|
1044
|
+
"items": {
|
|
1045
|
+
"type": "string"
|
|
1046
|
+
},
|
|
1047
|
+
"minItems": 1,
|
|
1048
|
+
"DISABLED_uniqueItems": true
|
|
1049
|
+
},
|
|
1050
|
+
"dimensions": {
|
|
1051
|
+
"type": "object",
|
|
1052
|
+
"properties": {
|
|
1053
|
+
"length": {
|
|
1054
|
+
"type": "number"
|
|
1055
|
+
},
|
|
1056
|
+
"width": {
|
|
1057
|
+
"type": "number"
|
|
1058
|
+
},
|
|
1059
|
+
"height": {
|
|
1060
|
+
"type": "number"
|
|
1061
|
+
}
|
|
1062
|
+
},
|
|
1063
|
+
"required": [ "length", "width", "height" ]
|
|
1064
|
+
}
|
|
1065
|
+
},
|
|
1066
|
+
"required": [ "productId", "productName", "price" ]
|
|
1067
|
+
})""",
|
|
1068
|
+
// Passing strings
|
|
1069
|
+
{
|
|
1070
|
+
R"""({"productId": 1, "productName": "A green door", "price": 12.50})""",
|
|
1071
|
+
R"""({"productId": 1, "productName": "A green door", "price": 12.50, "tags": ["home", "green"]})""",
|
|
1072
|
+
R"""({"productId": 1, "productName": "A green door", "price": 12.50, "tags": ["home", "green"], "dimensions": {"length": 785, "width": 250.5, "height": -0.359}})""",
|
|
1073
|
+
},
|
|
1074
|
+
// Failing strings
|
|
1075
|
+
{
|
|
1076
|
+
R"""({})""", // Missing all required properties
|
|
1077
|
+
R"""({"productName": "A green door", "price": 12.50, "productId": 1})""", // Out of order properties
|
|
1078
|
+
// `exclusiveMinimum` is OK for llg
|
|
1079
|
+
R"""({"productId": 1, "productName": "A green door", "price": -12.50})""",
|
|
1080
|
+
R"""({"productId": 1, "productName": "A green door"})""", // Missing required property (price)
|
|
1081
|
+
R"""({"productName": "A green door", "price": 12.50})""", // Missing required property (productId)
|
|
1082
|
+
R"""({"productId": 1, "productName": "A green door", "price": 12.50, "tags": []})""", // tags is empty, but minItems is 1
|
|
1083
|
+
R"""({"productId": 1, "productName": "A green door", "price": 12.50, "dimensions": {"length": 785, "width": 250.5, "height": -0.359}, "tags": ["home", "green"]})""", // Tags and dimensions are out of order
|
|
1084
|
+
// TODO: The following line should fail, but currently it passes. `uniqueItems` is not supported, as it would likely be too difficult to implement.
|
|
1085
|
+
// R"""({"productId": 1, "productName": "A green door", "price": 12.50, "tags": ["home", "green", "home"]})""",
|
|
1086
|
+
});
|
|
1087
|
+
}
|
|
1088
|
+
|
|
1089
|
+
int main(int argc, const char ** argv) {
|
|
1090
|
+
fprintf(stdout, "Running llguidance integration tests...\n");
|
|
1091
|
+
|
|
1092
|
+
if (argc != 2) {
|
|
1093
|
+
fprintf(stderr, "Usage: %s <vocab-file>\n", argv[0]);
|
|
1094
|
+
return 1;
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1097
|
+
const char * vocab_file = argv[1];
|
|
1098
|
+
|
|
1099
|
+
fprintf(stderr, "reading vocab from: '%s'\n", vocab_file);
|
|
1100
|
+
|
|
1101
|
+
llama_model * model;
|
|
1102
|
+
llama_context * ctx;
|
|
1103
|
+
|
|
1104
|
+
llama_backend_init();
|
|
1105
|
+
|
|
1106
|
+
// load the vocab
|
|
1107
|
+
{
|
|
1108
|
+
auto mparams = llama_model_default_params();
|
|
1109
|
+
|
|
1110
|
+
mparams.vocab_only = true;
|
|
1111
|
+
|
|
1112
|
+
model = llama_model_load_from_file(vocab_file, mparams);
|
|
1113
|
+
|
|
1114
|
+
if (model == NULL) {
|
|
1115
|
+
fprintf(stderr, "%s: error: failed to load vocab '%s'\n", __func__, vocab_file);
|
|
1116
|
+
return 1;
|
|
1117
|
+
}
|
|
1118
|
+
|
|
1119
|
+
// needed?
|
|
1120
|
+
auto cparams = llama_context_default_params();
|
|
1121
|
+
|
|
1122
|
+
ctx = llama_init_from_model(model, cparams);
|
|
1123
|
+
|
|
1124
|
+
if (ctx == NULL) {
|
|
1125
|
+
fprintf(stderr, "%s: error: failed to load vocab '%s'\n", __func__, vocab_file);
|
|
1126
|
+
llama_model_free(model);
|
|
1127
|
+
return 1;
|
|
1128
|
+
}
|
|
1129
|
+
}
|
|
1130
|
+
|
|
1131
|
+
vocab = llama_model_get_vocab(model);
|
|
1132
|
+
|
|
1133
|
+
test_simple_grammar();
|
|
1134
|
+
test_complex_grammar();
|
|
1135
|
+
test_special_chars();
|
|
1136
|
+
test_quantifiers();
|
|
1137
|
+
test_json_schema();
|
|
1138
|
+
fprintf(stdout, "All tests passed.\n");
|
|
1139
|
+
return 0;
|
|
1140
|
+
}
|