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}