1#include "tests.h"
  2
  3void test_basic(testing & t) {
  4    t.test("chars", [](testing & t) {
  5        // Test common escape sequences - newline
  6        t.test("escape_sequence_newline", [](testing &t) {
  7            auto common_chat_combinator_parser = build_peg_parser([](common_peg_parser_builder & p) { return p.chars("[\\n\\t\\\\]"); });
  8
  9            common_peg_parse_context ctx;
 10            common_peg_parse_result  result;
 11
 12            ctx    = common_peg_parse_context("\n");
 13            result = common_chat_combinator_parser.parse(ctx);
 14            t.assert_equal("escape_sequence_newline", true, result.success());
 15        });
 16
 17        // Test common escape sequences - tab
 18        t.test("escape_sequence_tab", [](testing &t) {
 19            auto common_chat_combinator_parser = build_peg_parser([](common_peg_parser_builder & p) { return p.chars("[\\n\\t\\\\]"); });
 20
 21            common_peg_parse_context ctx;
 22            common_peg_parse_result  result;
 23
 24            ctx    = common_peg_parse_context("\t");
 25            result = common_chat_combinator_parser.parse(ctx);
 26            t.assert_equal("escape_sequence_tab", true, result.success());
 27        });
 28
 29        // Test common escape sequences - backslash
 30        t.test("escape_sequence_backslash", [](testing &t) {
 31            auto common_chat_combinator_parser = build_peg_parser([](common_peg_parser_builder & p) { return p.chars("[\\n\\t\\\\]"); });
 32
 33            common_peg_parse_context ctx;
 34            common_peg_parse_result  result;
 35
 36            ctx    = common_peg_parse_context("\\");
 37            result = common_chat_combinator_parser.parse(ctx);
 38            t.assert_equal("escape_sequence_backslash", true, result.success());
 39        });
 40
 41        // Test common escape sequences - space (should ())
 42        t.test("escape_sequence_space_fail", [](testing &t) {
 43            auto common_chat_combinator_parser = build_peg_parser([](common_peg_parser_builder & p) { return p.chars("[\\n\\t\\\\]"); });
 44
 45            common_peg_parse_context ctx;
 46            common_peg_parse_result  result;
 47
 48            ctx    = common_peg_parse_context(" ");
 49            result = common_chat_combinator_parser.parse(ctx);
 50            t.assert_equal("escape_sequence_space_fail", true, result.fail());
 51        });
 52
 53        // Test escaped dash - 'a' should succeed
 54        t.test("escaped_dash_a", [](testing &t) {
 55            auto common_chat_combinator_parser = build_peg_parser([](common_peg_parser_builder & p) { return p.chars("[a\\-z]"); });
 56
 57            common_peg_parse_context ctx;
 58            common_peg_parse_result  result;
 59
 60            ctx    = common_peg_parse_context("a");
 61            result = common_chat_combinator_parser.parse(ctx);
 62            t.assert_equal("escaped_dash_a", true, result.success());
 63        });
 64
 65        // Test escaped dash - '-' should succeed (literal dash)
 66        t.test("escaped_dash_literal", [](testing &t) {
 67            auto common_chat_combinator_parser = build_peg_parser([](common_peg_parser_builder & p) { return p.chars("[a\\-z]"); });
 68
 69            common_peg_parse_context ctx;
 70            common_peg_parse_result  result;
 71
 72            ctx    = common_peg_parse_context("-");
 73            result = common_chat_combinator_parser.parse(ctx);
 74            t.assert_equal("escaped_dash_literal", true, result.success());
 75        });
 76
 77        // Test escaped dash - 'z' should succeed
 78        t.test("escaped_dash_z", [](testing &t) {
 79            auto common_chat_combinator_parser = build_peg_parser([](common_peg_parser_builder & p) { return p.chars("[a\\-z]"); });
 80
 81            common_peg_parse_context ctx;
 82            common_peg_parse_result  result;
 83
 84            ctx    = common_peg_parse_context("z");
 85            result = common_chat_combinator_parser.parse(ctx);
 86            t.assert_equal("escaped_dash_z", true, result.success());
 87        });
 88
 89        // Test escaped dash - 'b' should NOT match (since \- is literal dash, not range)
 90        t.test("escaped_dash_b_fail", [](testing &t) {
 91            auto common_chat_combinator_parser = build_peg_parser([](common_peg_parser_builder & p) { return p.chars("[a\\-z]"); });
 92
 93            common_peg_parse_context ctx;
 94            common_peg_parse_result  result;
 95
 96            ctx    = common_peg_parse_context("b");
 97            result = common_chat_combinator_parser.parse(ctx);
 98            t.assert_equal("escaped_dash_b_fail", true, result.fail());
 99        });
100    });
101
102
103    t.test("optional", [](testing & t) {
104        // Full match with optional part present
105        t.test("optional_present", [](testing &t) {
106            auto parser = build_peg_parser([](common_peg_parser_builder & p) {
107                return p.literal("hello") + p.optional(p.literal(" world"));
108            });
109
110            auto ctx    = common_peg_parse_context("hello world");
111            auto result = parser.parse(ctx);
112            t.assert_equal("optional_present", true, result.success());
113            t.assert_equal("optional_present_end", 11u, result.end);
114        });
115
116        // Full match with optional part absent
117        t.test("optional_absent", [](testing &t) {
118            auto parser = build_peg_parser([](common_peg_parser_builder & p) {
119                return p.literal("hello") + p.optional(p.literal(" world"));
120            });
121
122            auto ctx    = common_peg_parse_context("hello", false);
123            auto result = parser.parse(ctx);
124            t.assert_equal("optional_absent", true, result.success());
125            t.assert_equal("optional_absent_end", 5u, result.end);
126        });
127
128        // Partial match - waiting for more input to determine if optional matches
129        t.test("partial_match_need_more", [](testing &t) {
130            auto parser = build_peg_parser([](common_peg_parser_builder & p) {
131                return p.literal("hello") + p.optional(p.literal(" world"));
132            });
133
134            auto ctx    = common_peg_parse_context("hello ", true);
135            auto result = parser.parse(ctx);
136            t.assert_equal("partial_match_need_more", true, result.need_more_input());
137        });
138    });
139
140    t.test("partial parsing", [](testing & t) {
141        // Literals - Basic Success
142        t.test("literal_success", [&](testing & t) {
143            auto parser = build_peg_parser([](common_peg_parser_builder & p) { return p.literal("hello"); });
144
145            common_peg_parse_context ctx;
146            common_peg_parse_result  result;
147
148            ctx    = common_peg_parse_context("hello");
149            result = parser.parse(ctx);
150            t.assert_equal("literal_success", true, result.success());
151        });
152
153        // Char Classes - Basic Lowercase Success
154        t.test("char_class_lowercase_success", [&](testing & t) {
155            auto parser = build_peg_parser([](common_peg_parser_builder & p) { return p.chars("a-z"); });
156
157            common_peg_parse_context ctx;
158            common_peg_parse_result  result;
159
160            ctx    = common_peg_parse_context("a");
161            result = parser.parse(ctx);
162            t.assert_equal("char_class_lowercase_success", true, result.success());
163        });
164
165        // Char Classes - Uppercase Fail
166        t.test("char_class_uppercase_fail", [&](testing & t) {
167            auto parser = build_peg_parser([](common_peg_parser_builder & p) { return p.chars("a-z"); });
168
169            common_peg_parse_context ctx;
170            common_peg_parse_result  result;
171
172            ctx    = common_peg_parse_context("A");
173            result = parser.parse(ctx);
174            t.assert_equal("char_class_uppercase_fail", true, result.fail());
175        });
176
177        // Char Classes with Dash - Lowercase Success
178        t.test("char_class_with_dash_lowercase", [&](testing & t) {
179            auto parser = build_peg_parser([](common_peg_parser_builder & p) { return p.chars("a-z-"); });
180
181            common_peg_parse_context ctx;
182            common_peg_parse_result  result;
183
184            ctx    = common_peg_parse_context("f");
185            result = parser.parse(ctx);
186            t.assert_equal("char_class_with_dash_lowercase", true, result.success());
187        });
188
189        // Char Classes with Dash - Literal Dash Success
190        t.test("char_class_with_dash_literal_dash", [&](testing & t) {
191            auto parser = build_peg_parser([](common_peg_parser_builder & p) { return p.chars("a-z-"); });
192
193            common_peg_parse_context ctx;
194            common_peg_parse_result  result;
195
196            ctx    = common_peg_parse_context("-");
197            result = parser.parse(ctx);
198            t.assert_equal("char_class_with_dash_literal_dash", true, result.success());
199        });
200
201        // Char Classes with Dash - Uppercase Fail
202        t.test("char_class_with_dash_uppercase_fail", [&](testing & t) {
203            auto parser = build_peg_parser([](common_peg_parser_builder & p) { return p.chars("a-z-"); });
204
205            common_peg_parse_context ctx;
206            common_peg_parse_result  result;
207
208            ctx    = common_peg_parse_context("A");
209            result = parser.parse(ctx);
210            t.assert_equal("char_class_with_dash_uppercase_fail", true, result.fail());
211        });
212
213        // Sequences - Partial Match 1
214        t.test("sequence_partial_match_1", [&](testing & t) {
215            auto parser = build_peg_parser([](common_peg_parser_builder & p) { return p.literal("<think>") + p.literal("</think>"); });
216
217            auto ctx    = common_peg_parse_context("<thi", true);
218            auto result = parser.parse(ctx);
219            t.assert_equal("sequence_partial_match_1", true, result.need_more_input());
220        });
221
222        // Sequences - Partial Match 2
223        t.test("sequence_partial_match_2", [&](testing & t) {
224            auto parser = build_peg_parser([](common_peg_parser_builder & p) { return p.literal("begin") + p.literal("end"); });
225
226            auto ctx    = common_peg_parse_context("begin", true);
227            auto result = parser.parse(ctx);
228            t.assert_equal("sequence_partial_match_2", true, result.need_more_input());
229        });
230
231        // Sequences - Partial Match 3
232        t.test("sequence_partial_match_3", [&](testing & t) {
233            auto parser = build_peg_parser([](common_peg_parser_builder & p) { return p.literal("<think>") + p.literal("</think>"); });
234
235            auto ctx    = common_peg_parse_context("<think></", true);
236            auto result = parser.parse(ctx);
237            t.assert_equal("sequence_partial_match_3", true, result.need_more_input());
238        });
239
240        // Sequences - Full Match
241        t.test("sequence_full_match", [&](testing & t) {
242            auto common_chat_combinator_parser = build_peg_parser([](common_peg_parser_builder & p) { return p.literal("hello") + p.literal("world"); });
243
244            auto ctx    = common_peg_parse_context("helloworld", false);
245            auto result = common_chat_combinator_parser.parse(ctx);
246            t.assert_equal("sequence_full_match", true, result.success());
247        });
248
249        // Sequences - No Match
250        t.test("sequence_no_match", [&](testing & t) {
251            auto parser = build_peg_parser([](common_peg_parser_builder & p) { return p.literal("<think>") + p.literal("</think>"); });
252
253            auto ctx    = common_peg_parse_context("<think>I am common_chat_combinator_parser", true);
254            auto result = parser.parse(ctx);
255            t.assert_equal("sequence_no_match", true, result.fail());
256        });
257
258        // Choices - Partial Match 1
259        t.test("choices_partial_match_1", [&](testing & t) {
260            auto parser = build_peg_parser([](common_peg_parser_builder & p) { return p.literal("option1") | p.literal("option2"); });
261
262            auto ctx    = common_peg_parse_context("opt", true);
263            auto result = parser.parse(ctx);
264            t.assert_equal("choices_partial_match_1", true, result.need_more_input());
265        });
266
267        // Choices - Partial Match 2
268        t.test("choices_partial_match_2", [&](testing & t) {
269            auto parser =
270                build_peg_parser([](common_peg_parser_builder & p) { return p.literal("choice_a") | p.literal("choice_b"); });
271
272            auto ctx    = common_peg_parse_context("choice", true);
273            auto result = parser.parse(ctx);
274            t.assert_equal("choices_partial_match_2", true, result.need_more_input());
275        });
276
277        // Choices - Full Match 1
278        t.test("choices_full_match_1", [&](testing & t) {
279            auto parser = build_peg_parser([](common_peg_parser_builder & p) { return p.literal("first") | p.literal("second"); });
280
281            auto ctx    = common_peg_parse_context("first", false);
282            auto result = parser.parse(ctx);
283            t.assert_equal("choices_full_match_1", true, result.success());
284        });
285
286        // Choices - Full Match 2
287        t.test("choices_full_match_2", [&](testing & t) {
288            auto parser = build_peg_parser([](common_peg_parser_builder & p) { return p.literal("alpha") | p.literal("beta"); });
289
290            auto ctx    = common_peg_parse_context("beta", false);
291            auto result = parser.parse(ctx);
292            t.assert_equal("choices_full_match_2", true, result.success());
293        });
294
295        // Choices - No Match
296        t.test("choices_no_match", [&](testing & t) {
297            auto parser = build_peg_parser([](common_peg_parser_builder & p) { return p.literal("good") | p.literal("better"); });
298
299            auto ctx    = common_peg_parse_context("best", false);
300            auto result = parser.parse(ctx);
301            t.assert_equal("choices_no_match", true, result.fail());
302        });
303
304        // Zero or More - Partial Match 1
305        t.test("zero_or_more_partial_match_1", [&](testing & t) {
306            auto parser = build_peg_parser([](common_peg_parser_builder & p) { return p.zero_or_more(p.literal("ab")); });
307
308            auto ctx    = common_peg_parse_context("a", true);
309            auto result = parser.parse(ctx);
310            t.assert_equal("zero_or_more_partial_match_1", true, result.need_more_input());
311        });
312
313        // Zero or More - Partial Match 2
314        t.test("zero_or_more_partial_match_2", [&](testing & t) {
315            auto parser = build_peg_parser([](common_peg_parser_builder & p) { return p.zero_or_more(p.literal("xy")); });
316
317            auto ctx    = common_peg_parse_context("xyx", true);
318            auto result = parser.parse(ctx);
319            t.assert_equal("zero_or_more_partial_match_2", true, result.need_more_input());
320        });
321
322        // Zero or More - Full Match
323        t.test("zero_or_more_full_match", [&](testing & t) {
324            auto parser = build_peg_parser([](common_peg_parser_builder & p) { return p.zero_or_more(p.literal("test")); });
325
326            auto ctx    = common_peg_parse_context("test", false);
327            auto result = parser.parse(ctx);
328            t.assert_equal("zero_or_more_full_match", true, result.success());
329        });
330
331        // One or More - Partial Match 1
332        t.test("one_or_more_partial_match_1", [&](testing & t) {
333            auto parser = build_peg_parser([](common_peg_parser_builder & p) { return p.one_or_more(p.literal("repeat")); });
334
335            auto ctx    = common_peg_parse_context("rep", true);
336            auto result = parser.parse(ctx);
337            t.assert_equal("one_or_more_partial_match_1", true, result.need_more_input());
338        });
339
340        // One or More - Partial Match 2
341        t.test("one_or_more_partial_match_2", [&](testing & t) {
342            auto parser = build_peg_parser([](common_peg_parser_builder & p) { return p.one_or_more(p.literal("ab")); });
343
344            auto ctx    = common_peg_parse_context("aba", true);
345            auto result = parser.parse(ctx);
346            t.assert_equal("one_or_more_partial_match_2", true, result.need_more_input());
347        });
348
349        // One or More - Full Match
350        t.test("one_or_more_full_match", [&](testing & t) {
351            auto parser = build_peg_parser([](common_peg_parser_builder & p) { return p.one_or_more(p.literal("single")); });
352
353            auto ctx    = common_peg_parse_context("single", false);
354            auto result = parser.parse(ctx);
355            t.assert_equal("one_or_more_full_match", true, result.success());
356        });
357
358        // One or More - No Match
359        t.test("one_or_more_no_match", [&](testing & t) {
360            auto parser = build_peg_parser([](common_peg_parser_builder & p) { return p.one_or_more(p.literal("()")); });
361
362            auto ctx    = common_peg_parse_context("success", false);
363            auto result = parser.parse(ctx);
364            t.assert_equal("one_or_more_no_match", true, result.fail());
365        });
366    });
367
368
369    t.test("recursive rules", [](testing &t) {
370        // Test simple number
371        t.test("simple_number", [](testing &t) {
372            auto value_parser = build_peg_parser([](common_peg_parser_builder & p) {
373                p.rule("number", p.chars("0-9"));
374                p.rule("list", p.literal("[") + p.ref("value") + p.literal("]"));
375                return p.rule("value", p.ref("number") | p.ref("list"));
376            });
377
378            common_peg_parse_context ctx("1", false);
379            auto           result = value_parser.parse(ctx);
380
381            t.assert_equal("result_is_success", true, result.success());
382        });
383
384        // Test simple list
385        t.test("simple_list", [](testing &t) {
386            auto value_parser = build_peg_parser([](common_peg_parser_builder & p) {
387                p.rule("number", p.chars("0-9"));
388                p.rule("list", p.literal("[") + p.ref("value") + p.literal("]"));
389                return p.rule("value", p.ref("number") | p.ref("list"));
390            });
391
392            common_peg_parse_context ctx("[1]", false);
393            auto           result = value_parser.parse(ctx);
394
395            t.assert_equal("result_is_success", true, result.success());
396        });
397
398        // Test nested list
399        t.test("nested_list", [](testing &t) {
400            auto value_parser = build_peg_parser([](common_peg_parser_builder & p) {
401                p.rule("number", p.chars("0-9"));
402                p.rule("list", p.literal("[") + p.ref("value") + p.literal("]"));
403                return p.rule("value", p.ref("number") | p.ref("list"));
404            });
405
406            common_peg_parse_context ctx("[[2]]", false);
407            auto           result = value_parser.parse(ctx);
408
409            t.assert_equal("result_is_success", true, result.success());
410        });
411
412        // Test deeply nested list
413        t.test("deeply_nested_list", [](testing &t) {
414            auto value_parser = build_peg_parser([](common_peg_parser_builder & p) {
415                p.rule("number", p.chars("0-9"));
416                p.rule("list", p.literal("[") + p.ref("value") + p.literal("]"));
417                return p.rule("value", p.ref("number") | p.ref("list"));
418            });
419
420            common_peg_parse_context ctx("[[[3]]]", false);
421            auto           result = value_parser.parse(ctx);
422
423            t.assert_equal("result_is_success", true, result.success());
424        });
425
426        // Test need_more_input match
427        t.test("need_more_input_match", [](testing &t) {
428            auto value_parser = build_peg_parser([](common_peg_parser_builder & p) {
429                p.rule("number", p.chars("0-9"));
430                p.rule("list", p.literal("[") + p.ref("value") + p.literal("]"));
431                return p.rule("value", p.ref("number") | p.ref("list"));
432            });
433
434            common_peg_parse_context ctx("[[", true);
435            auto           result = value_parser.parse(ctx);
436
437            t.assert_equal("result_is_need_more_input", true, result.need_more_input());
438        });
439
440        // Test no match
441        t.test("no_match", [](testing &t) {
442            auto value_parser = build_peg_parser([](common_peg_parser_builder & p) {
443                p.rule("number", p.chars("0-9"));
444                p.rule("list", p.literal("[") + p.ref("value") + p.literal("]"));
445                return p.rule("value", p.ref("number") | p.ref("list"));
446            });
447
448            common_peg_parse_context ctx("[a]", false);
449            auto           result = value_parser.parse(ctx);
450
451            t.assert_equal("result_is_fail", true, result.fail());
452        });
453    });
454}