@@ -153,6 +153,115 @@ ws ::= [ \t\n\r]?)""";
153
153
llama_grammar_free (grammar);
154
154
}
155
155
156
+ static void test_quantifiers () {
157
+ // Populate test data with grammar strings and their associated collections of expected passing and failing strings
158
+ const std::vector<
159
+ std::tuple<
160
+ std::string,
161
+ std::vector<std::string>,
162
+ std::vector<std::string>>>
163
+ test_data = {
164
+ {
165
+ // Grammar
166
+ R"""( root ::= "a"*)""" ,
167
+ // Passing strings
168
+ {
169
+ " " ,
170
+ " a" ,
171
+ " aaaaa" ,
172
+ " aaaaaaaaaaaaaaaaaa" ,
173
+ " aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
174
+ },
175
+ // Failing strings
176
+ {
177
+ " b" ,
178
+ " ab" ,
179
+ " aab" ,
180
+ " ba" ,
181
+ " aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab"
182
+ }
183
+ },
184
+ {
185
+ // Grammar
186
+ R"""( root ::= "a"+)""" ,
187
+ // Passing strings
188
+ {
189
+ " a" ,
190
+ " aaaaa" ,
191
+ " aaaaaaaaaaaaaaaaaa" ,
192
+ " aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
193
+ },
194
+ // Failing strings
195
+ {
196
+ " " ,
197
+ " b" ,
198
+ " ab" ,
199
+ " aab" ,
200
+ " ba" ,
201
+ " aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab"
202
+ }
203
+ },
204
+ {
205
+ // Grammar
206
+ R"""( root ::= "a"?)""" ,
207
+ // Passing strings
208
+ {
209
+ " " ,
210
+ " a"
211
+ },
212
+ // Failing strings
213
+ {
214
+ " b" ,
215
+ " ab" ,
216
+ " aa" ,
217
+ " ba" ,
218
+ }
219
+ }
220
+ };
221
+
222
+ for (const auto & test_datum : test_data) {
223
+ const auto & [grammar_str, passing_strings, failing_strings] = test_datum;
224
+
225
+ auto grammar = build_grammar (grammar_str);
226
+
227
+ // Save the original grammar stacks so that we can reset after every new string we want to test
228
+ auto original_stacks = grammar->stacks ;
229
+
230
+ // Passing strings
231
+ for (const auto & test_string : passing_strings) {
232
+ bool matched = match_string (test_string, grammar);
233
+
234
+ if (!matched) {
235
+ fprintf (stderr, " Against grammar: %s\n " , grammar_str.c_str ());
236
+ fprintf (stderr, " Failed to match string: %s\n " , test_string.c_str ());
237
+ }
238
+
239
+ assert (matched);
240
+
241
+ // Reset the grammar stacks
242
+ grammar->stacks = original_stacks;
243
+ }
244
+
245
+ // Failing strings
246
+ for (const auto & test_string : failing_strings) {
247
+ bool matched = match_string (test_string, grammar);
248
+
249
+ if (matched) {
250
+ fprintf (stderr, " Against grammar: %s\n " , grammar_str.c_str ());
251
+ fprintf (stderr, " Improperly matched string: %s\n " , test_string.c_str ());
252
+ }
253
+
254
+ assert (!matched);
255
+
256
+ // Reset the grammar stacks
257
+ grammar->stacks = original_stacks;
258
+ }
259
+
260
+ // Clean up allocated memory
261
+ llama_grammar_free (grammar);
262
+ }
263
+ }
264
+
156
265
static void test_failure_missing_root () {
157
266
// Test case for a grammar that is missing a root rule
158
267
const std::string grammar_str = R"""( rot ::= expr
@@ -189,6 +298,7 @@ number ::= [0-9]+)""";
189
298
int main () {
190
299
test_simple_grammar ();
191
300
test_complex_grammar ();
301
+ test_quantifiers ();
192
302
test_failure_missing_root ();
193
303
test_failure_missing_reference ();
194
304
fprintf (stdout, " All tests passed.\n " );
0 commit comments