mirror of
https://github.com/ggerganov/llama.cpp.git
synced 2026-02-05 13:53:23 +02:00
* common : implement parser combinators to simplify chat parsing * add virtual destructor to parser_base * fix memory leak from circular references of rules * implement gbnf grammar building * remove unused private variable * create a base visitor and implement id assignment as a visitor * fix const ref for grammar builder * clean up types, friend classes, and class declarations * remove builder usage from until_parser * Use a counter class to help assign rule ids * cache everything * add short description for each parser * create a type for the root parser * implement repetition parser * Make optional, one_or_more, and zero_or_more subclasses of repetition * improve context constructor * improve until parsing and add benchmarks * remove cached() pattern, cache in parser_base with specialized parsing functions for each parser * improve json parsing performance to better match legacy parsing * fix const auto * it for windows * move id assignment to classes instead of using a visitor * create named rules in the command r7b example * use '.' for any in GBNF * fix parens around choices in gbnf grammar * add convenience operators to turn strings to literals * add free-form operators for const char * to simplify defining literals * simplify test case parser * implement semantic actions * remove groups in favor of actions and a scratchpad * add built in actions for common operations * add actions to command r7b example * use std::default_searcher for platforms that don't have bm * improve parser_type handling and add cast helper * add partial result type to better control when to run actions * fix bug in until() * run actions on partial results by default * use common_chat_msg for result * add qwen3 example wip * trash partial idea and simplify * move action arguments to a struct * implement aho-corasick matcher for until_parser and to build exclusion grammars * use std::string for input, since std::string_view is incompatible with std::regex * Refactor tests * improve qwen3 example * implement sax-style parsing and refactor * fix json string in test * rename classes to use common_chat_ prefix * remove is_ suffix from functions * rename from id_counter to just counter * Final refactored tests * Fix executable name and editorconfig-checker * Third time's the charm... * add trigger parser to begin lazy grammar rule generation * working lazy grammar * refactor json rules now that we check for reachability * reduce pointer usage * print out grammars in example * rename to chat-peg-parser* and common_chat_peg_parser* * Revert unrelated changes * New macros for CMakeLists to enable multi-file compilations * starting unicode support * add unicode support to char_parser * use unparsed args as additional sources * Refactor tests to new harness * Fix CMakeLists * fix rate calculation * add unicode tests * fix trailing whitespace and line endings skip-checks: true * Helpers + rewrite qwen3 with helpers * Fix whitespace * extract unicode functions to separate file * refactor parse unicode function * fix compiler error * improve construction of sequence/choice parsers * be less clever * add make_parser helper function * expand usage of make_parser, alias common_chat_msg_peg_parser_builder to builder in source * lower bench iterations * add unicode support to until_parser * add unicode support to json_string_parser * clean up unicode tests * reduce unicode details to match src/unicode.cpp * simplify even further * remove unused functions * fix type * reformat char class parsing * clean up json string parser * clean up + fix diagnostics * reorder includes * compact builder functions * replace action_parser with capture_parser, rename env to semantics * rename env to semantics * clean up common_chat_parse_context * move type() to below constant * use default constructor for common_chat_peg_parser * make all operators functions for consistency * fix compilation errors in test-optional.cpp * simplify result values * rename json_string_unquoted to json_string_content * Move helper to separate class, add separate explicit and helper classes * Whitespace * Change + to append() * Reformat * Add extra helpers, tests and Minimax example * Add some extra optional debugging prints + real example of how to use them * fix bug in repetitions when min_count = 0 reports failures * dump rule in debug * fix token accumulation and assert parsing never fails * indent debug by depth * use LOG_* in tests so logs sync up with test logs * - Add selective testing - Refactor all messaging to use LOG_ERR - Fix lack of argument / tool name capturing - Temporary fix for double event capture * refactor rule() and introduce ref() * clean up visitor * clean up indirection in root parser w.r.t rules * store shared ptr directly in parser classes * replace aho-corasick automation with a simple trie * Reset prev for qwen3 helper example variant * refactor to use value semantics with std::variant/std::visit * simplify trie_matcher result * fix linting issues * add annotations to rules * revert test workaround * implement serializing the parser * remove redundant parsers * remove tests * gbnf generation fixes * remove LOG_* use in tests * update gbnf tests to test entire grammar * clean up gbnf generation and fix a few bugs * fix typo in test output * remove implicit conversion rules * improve test output * rename trie_matcher to trie * simplify trie to just know if a node is the end of a word * remove common_chat_ prefix and ensure a common_peg_ prefix to all types * rename chat-peg-parser -> peg-parser * promote chat-peg-parser-helper to chat-peg-parser * checkpoint * use a static_assert to ensure we handle every branch * inline trivial peg parser builders * use json strings for now * implement basic and native chat peg parser builders/extractors * resolve refs to their rules * remove packrat caching (for now) * update tests * compare parsers with incremental input * benchmark both complete and incremental parsing * add raw string generation from json schema * add support for string schemas in gbnf generation * fix qwen example to include \n * tidy up example * rename extractor to mapper * rename ast_arena to ast * place basic tests into one * use gbnf_format_literal from json-schema-to-grammar * integrate parser with common/chat and server * clean up schema and serialization * add json-schema raw string tests * clean up json creation and remove capture parser * trim spaces from reasoning and content * clean up redundant rules and comments * rename input_is_complete to is_partial to match rest of project * simplify json rules * remove extraneous file * remove comment * implement += and |= operators * add comments to qwen3 implementation * reorder arguments to common_chat_peg_parse * remove commented outdated tests * add explicit copy constructor * fix operators and constness * wip: update test-chat for qwen3-coder * bring json parser closer to json-schema-to-grammar rules * trim trailing space for most things * fix qwen3 coder rules w.r.t. trailing spaces * group rules * do not trim trailing space from string args * tweak spacing of qwen3 grammar * update qwen3-coder tests * qwen3-coder small fixes * place parser in common_chat_syntax to simplify invocation * use std::set to collect rules to keep order predictable for tests * initialize parser to make certain platforms happy * revert back to std::unordered_set, sort rule names at the end instead * uncomment rest of chat tests * define explicit default constructor * improve arena init and server integration * fix chat test * add json_member() * add a comprehensive native example * clean up example qwen test and add response_format example to native test * make build_peg_parser accept std::function instead of template * change peg parser parameters into const ref * push tool call on tool open for constructed parser * add parsing documentation * clean up some comments * add json schema support to qwen3-coder * add id initializer in tests * remove grammar debug line from qwen3-coder * refactor qwen3-coder to use sequence over operators * only call common_chat_peg_parse if appropriate format * simplify qwen3-coder space handling * revert qwen3-coder implementation * revert json-schema-to-grammar changes * remove unnecessary forward declaration * small adjustment to until_parser * rename C/C++ files to use dashes * codeowners : add aldehir to peg-parser and related files --------- Co-authored-by: Piotr Wilkin <piotr.wilkin@syndatis.com>
455 lines
20 KiB
C++
455 lines
20 KiB
C++
#include "tests.h"
|
|
|
|
void test_basic(testing & t) {
|
|
t.test("chars", [](testing & t) {
|
|
// Test common escape sequences - newline
|
|
t.test("escape_sequence_newline", [](testing &t) {
|
|
auto common_chat_combinator_parser = build_peg_parser([](common_peg_parser_builder & p) { return p.chars("[\\n\\t\\\\]"); });
|
|
|
|
common_peg_parse_context ctx;
|
|
common_peg_parse_result result;
|
|
|
|
ctx = common_peg_parse_context("\n");
|
|
result = common_chat_combinator_parser.parse(ctx);
|
|
t.assert_equal("escape_sequence_newline", true, result.success());
|
|
});
|
|
|
|
// Test common escape sequences - tab
|
|
t.test("escape_sequence_tab", [](testing &t) {
|
|
auto common_chat_combinator_parser = build_peg_parser([](common_peg_parser_builder & p) { return p.chars("[\\n\\t\\\\]"); });
|
|
|
|
common_peg_parse_context ctx;
|
|
common_peg_parse_result result;
|
|
|
|
ctx = common_peg_parse_context("\t");
|
|
result = common_chat_combinator_parser.parse(ctx);
|
|
t.assert_equal("escape_sequence_tab", true, result.success());
|
|
});
|
|
|
|
// Test common escape sequences - backslash
|
|
t.test("escape_sequence_backslash", [](testing &t) {
|
|
auto common_chat_combinator_parser = build_peg_parser([](common_peg_parser_builder & p) { return p.chars("[\\n\\t\\\\]"); });
|
|
|
|
common_peg_parse_context ctx;
|
|
common_peg_parse_result result;
|
|
|
|
ctx = common_peg_parse_context("\\");
|
|
result = common_chat_combinator_parser.parse(ctx);
|
|
t.assert_equal("escape_sequence_backslash", true, result.success());
|
|
});
|
|
|
|
// Test common escape sequences - space (should ())
|
|
t.test("escape_sequence_space_fail", [](testing &t) {
|
|
auto common_chat_combinator_parser = build_peg_parser([](common_peg_parser_builder & p) { return p.chars("[\\n\\t\\\\]"); });
|
|
|
|
common_peg_parse_context ctx;
|
|
common_peg_parse_result result;
|
|
|
|
ctx = common_peg_parse_context(" ");
|
|
result = common_chat_combinator_parser.parse(ctx);
|
|
t.assert_equal("escape_sequence_space_fail", true, result.fail());
|
|
});
|
|
|
|
// Test escaped dash - 'a' should succeed
|
|
t.test("escaped_dash_a", [](testing &t) {
|
|
auto common_chat_combinator_parser = build_peg_parser([](common_peg_parser_builder & p) { return p.chars("[a\\-z]"); });
|
|
|
|
common_peg_parse_context ctx;
|
|
common_peg_parse_result result;
|
|
|
|
ctx = common_peg_parse_context("a");
|
|
result = common_chat_combinator_parser.parse(ctx);
|
|
t.assert_equal("escaped_dash_a", true, result.success());
|
|
});
|
|
|
|
// Test escaped dash - '-' should succeed (literal dash)
|
|
t.test("escaped_dash_literal", [](testing &t) {
|
|
auto common_chat_combinator_parser = build_peg_parser([](common_peg_parser_builder & p) { return p.chars("[a\\-z]"); });
|
|
|
|
common_peg_parse_context ctx;
|
|
common_peg_parse_result result;
|
|
|
|
ctx = common_peg_parse_context("-");
|
|
result = common_chat_combinator_parser.parse(ctx);
|
|
t.assert_equal("escaped_dash_literal", true, result.success());
|
|
});
|
|
|
|
// Test escaped dash - 'z' should succeed
|
|
t.test("escaped_dash_z", [](testing &t) {
|
|
auto common_chat_combinator_parser = build_peg_parser([](common_peg_parser_builder & p) { return p.chars("[a\\-z]"); });
|
|
|
|
common_peg_parse_context ctx;
|
|
common_peg_parse_result result;
|
|
|
|
ctx = common_peg_parse_context("z");
|
|
result = common_chat_combinator_parser.parse(ctx);
|
|
t.assert_equal("escaped_dash_z", true, result.success());
|
|
});
|
|
|
|
// Test escaped dash - 'b' should NOT match (since \- is literal dash, not range)
|
|
t.test("escaped_dash_b_fail", [](testing &t) {
|
|
auto common_chat_combinator_parser = build_peg_parser([](common_peg_parser_builder & p) { return p.chars("[a\\-z]"); });
|
|
|
|
common_peg_parse_context ctx;
|
|
common_peg_parse_result result;
|
|
|
|
ctx = common_peg_parse_context("b");
|
|
result = common_chat_combinator_parser.parse(ctx);
|
|
t.assert_equal("escaped_dash_b_fail", true, result.fail());
|
|
});
|
|
});
|
|
|
|
|
|
t.test("optional", [](testing & t) {
|
|
// Full match with optional part present
|
|
t.test("optional_present", [](testing &t) {
|
|
auto parser = build_peg_parser([](common_peg_parser_builder & p) {
|
|
return p.literal("hello") + p.optional(p.literal(" world"));
|
|
});
|
|
|
|
auto ctx = common_peg_parse_context("hello world");
|
|
auto result = parser.parse(ctx);
|
|
t.assert_equal("optional_present", true, result.success());
|
|
t.assert_equal("optional_present_end", 11u, result.end);
|
|
});
|
|
|
|
// Full match with optional part absent
|
|
t.test("optional_absent", [](testing &t) {
|
|
auto parser = build_peg_parser([](common_peg_parser_builder & p) {
|
|
return p.literal("hello") + p.optional(p.literal(" world"));
|
|
});
|
|
|
|
auto ctx = common_peg_parse_context("hello", false);
|
|
auto result = parser.parse(ctx);
|
|
t.assert_equal("optional_absent", true, result.success());
|
|
t.assert_equal("optional_absent_end", 5u, result.end);
|
|
});
|
|
|
|
// Partial match - waiting for more input to determine if optional matches
|
|
t.test("partial_match_need_more", [](testing &t) {
|
|
auto parser = build_peg_parser([](common_peg_parser_builder & p) {
|
|
return p.literal("hello") + p.optional(p.literal(" world"));
|
|
});
|
|
|
|
auto ctx = common_peg_parse_context("hello ", true);
|
|
auto result = parser.parse(ctx);
|
|
t.assert_equal("partial_match_need_more", true, result.need_more_input());
|
|
});
|
|
});
|
|
|
|
t.test("partial parsing", [](testing & t) {
|
|
// Literals - Basic Success
|
|
t.test("literal_success", [&](testing & t) {
|
|
auto parser = build_peg_parser([](common_peg_parser_builder & p) { return p.literal("hello"); });
|
|
|
|
common_peg_parse_context ctx;
|
|
common_peg_parse_result result;
|
|
|
|
ctx = common_peg_parse_context("hello");
|
|
result = parser.parse(ctx);
|
|
t.assert_equal("literal_success", true, result.success());
|
|
});
|
|
|
|
// Char Classes - Basic Lowercase Success
|
|
t.test("char_class_lowercase_success", [&](testing & t) {
|
|
auto parser = build_peg_parser([](common_peg_parser_builder & p) { return p.chars("a-z"); });
|
|
|
|
common_peg_parse_context ctx;
|
|
common_peg_parse_result result;
|
|
|
|
ctx = common_peg_parse_context("a");
|
|
result = parser.parse(ctx);
|
|
t.assert_equal("char_class_lowercase_success", true, result.success());
|
|
});
|
|
|
|
// Char Classes - Uppercase Fail
|
|
t.test("char_class_uppercase_fail", [&](testing & t) {
|
|
auto parser = build_peg_parser([](common_peg_parser_builder & p) { return p.chars("a-z"); });
|
|
|
|
common_peg_parse_context ctx;
|
|
common_peg_parse_result result;
|
|
|
|
ctx = common_peg_parse_context("A");
|
|
result = parser.parse(ctx);
|
|
t.assert_equal("char_class_uppercase_fail", true, result.fail());
|
|
});
|
|
|
|
// Char Classes with Dash - Lowercase Success
|
|
t.test("char_class_with_dash_lowercase", [&](testing & t) {
|
|
auto parser = build_peg_parser([](common_peg_parser_builder & p) { return p.chars("a-z-"); });
|
|
|
|
common_peg_parse_context ctx;
|
|
common_peg_parse_result result;
|
|
|
|
ctx = common_peg_parse_context("f");
|
|
result = parser.parse(ctx);
|
|
t.assert_equal("char_class_with_dash_lowercase", true, result.success());
|
|
});
|
|
|
|
// Char Classes with Dash - Literal Dash Success
|
|
t.test("char_class_with_dash_literal_dash", [&](testing & t) {
|
|
auto parser = build_peg_parser([](common_peg_parser_builder & p) { return p.chars("a-z-"); });
|
|
|
|
common_peg_parse_context ctx;
|
|
common_peg_parse_result result;
|
|
|
|
ctx = common_peg_parse_context("-");
|
|
result = parser.parse(ctx);
|
|
t.assert_equal("char_class_with_dash_literal_dash", true, result.success());
|
|
});
|
|
|
|
// Char Classes with Dash - Uppercase Fail
|
|
t.test("char_class_with_dash_uppercase_fail", [&](testing & t) {
|
|
auto parser = build_peg_parser([](common_peg_parser_builder & p) { return p.chars("a-z-"); });
|
|
|
|
common_peg_parse_context ctx;
|
|
common_peg_parse_result result;
|
|
|
|
ctx = common_peg_parse_context("A");
|
|
result = parser.parse(ctx);
|
|
t.assert_equal("char_class_with_dash_uppercase_fail", true, result.fail());
|
|
});
|
|
|
|
// Sequences - Partial Match 1
|
|
t.test("sequence_partial_match_1", [&](testing & t) {
|
|
auto parser = build_peg_parser([](common_peg_parser_builder & p) { return p.literal("<think>") + p.literal("</think>"); });
|
|
|
|
auto ctx = common_peg_parse_context("<thi", true);
|
|
auto result = parser.parse(ctx);
|
|
t.assert_equal("sequence_partial_match_1", true, result.need_more_input());
|
|
});
|
|
|
|
// Sequences - Partial Match 2
|
|
t.test("sequence_partial_match_2", [&](testing & t) {
|
|
auto parser = build_peg_parser([](common_peg_parser_builder & p) { return p.literal("begin") + p.literal("end"); });
|
|
|
|
auto ctx = common_peg_parse_context("begin", true);
|
|
auto result = parser.parse(ctx);
|
|
t.assert_equal("sequence_partial_match_2", true, result.need_more_input());
|
|
});
|
|
|
|
// Sequences - Partial Match 3
|
|
t.test("sequence_partial_match_3", [&](testing & t) {
|
|
auto parser = build_peg_parser([](common_peg_parser_builder & p) { return p.literal("<think>") + p.literal("</think>"); });
|
|
|
|
auto ctx = common_peg_parse_context("<think></", true);
|
|
auto result = parser.parse(ctx);
|
|
t.assert_equal("sequence_partial_match_3", true, result.need_more_input());
|
|
});
|
|
|
|
// Sequences - Full Match
|
|
t.test("sequence_full_match", [&](testing & t) {
|
|
auto common_chat_combinator_parser = build_peg_parser([](common_peg_parser_builder & p) { return p.literal("hello") + p.literal("world"); });
|
|
|
|
auto ctx = common_peg_parse_context("helloworld", false);
|
|
auto result = common_chat_combinator_parser.parse(ctx);
|
|
t.assert_equal("sequence_full_match", true, result.success());
|
|
});
|
|
|
|
// Sequences - No Match
|
|
t.test("sequence_no_match", [&](testing & t) {
|
|
auto parser = build_peg_parser([](common_peg_parser_builder & p) { return p.literal("<think>") + p.literal("</think>"); });
|
|
|
|
auto ctx = common_peg_parse_context("<think>I am common_chat_combinator_parser", true);
|
|
auto result = parser.parse(ctx);
|
|
t.assert_equal("sequence_no_match", true, result.fail());
|
|
});
|
|
|
|
// Choices - Partial Match 1
|
|
t.test("choices_partial_match_1", [&](testing & t) {
|
|
auto parser = build_peg_parser([](common_peg_parser_builder & p) { return p.literal("option1") | p.literal("option2"); });
|
|
|
|
auto ctx = common_peg_parse_context("opt", true);
|
|
auto result = parser.parse(ctx);
|
|
t.assert_equal("choices_partial_match_1", true, result.need_more_input());
|
|
});
|
|
|
|
// Choices - Partial Match 2
|
|
t.test("choices_partial_match_2", [&](testing & t) {
|
|
auto parser =
|
|
build_peg_parser([](common_peg_parser_builder & p) { return p.literal("choice_a") | p.literal("choice_b"); });
|
|
|
|
auto ctx = common_peg_parse_context("choice", true);
|
|
auto result = parser.parse(ctx);
|
|
t.assert_equal("choices_partial_match_2", true, result.need_more_input());
|
|
});
|
|
|
|
// Choices - Full Match 1
|
|
t.test("choices_full_match_1", [&](testing & t) {
|
|
auto parser = build_peg_parser([](common_peg_parser_builder & p) { return p.literal("first") | p.literal("second"); });
|
|
|
|
auto ctx = common_peg_parse_context("first", false);
|
|
auto result = parser.parse(ctx);
|
|
t.assert_equal("choices_full_match_1", true, result.success());
|
|
});
|
|
|
|
// Choices - Full Match 2
|
|
t.test("choices_full_match_2", [&](testing & t) {
|
|
auto parser = build_peg_parser([](common_peg_parser_builder & p) { return p.literal("alpha") | p.literal("beta"); });
|
|
|
|
auto ctx = common_peg_parse_context("beta", false);
|
|
auto result = parser.parse(ctx);
|
|
t.assert_equal("choices_full_match_2", true, result.success());
|
|
});
|
|
|
|
// Choices - No Match
|
|
t.test("choices_no_match", [&](testing & t) {
|
|
auto parser = build_peg_parser([](common_peg_parser_builder & p) { return p.literal("good") | p.literal("better"); });
|
|
|
|
auto ctx = common_peg_parse_context("best", false);
|
|
auto result = parser.parse(ctx);
|
|
t.assert_equal("choices_no_match", true, result.fail());
|
|
});
|
|
|
|
// Zero or More - Partial Match 1
|
|
t.test("zero_or_more_partial_match_1", [&](testing & t) {
|
|
auto parser = build_peg_parser([](common_peg_parser_builder & p) { return p.zero_or_more(p.literal("ab")); });
|
|
|
|
auto ctx = common_peg_parse_context("a", true);
|
|
auto result = parser.parse(ctx);
|
|
t.assert_equal("zero_or_more_partial_match_1", true, result.need_more_input());
|
|
});
|
|
|
|
// Zero or More - Partial Match 2
|
|
t.test("zero_or_more_partial_match_2", [&](testing & t) {
|
|
auto parser = build_peg_parser([](common_peg_parser_builder & p) { return p.zero_or_more(p.literal("xy")); });
|
|
|
|
auto ctx = common_peg_parse_context("xyx", true);
|
|
auto result = parser.parse(ctx);
|
|
t.assert_equal("zero_or_more_partial_match_2", true, result.need_more_input());
|
|
});
|
|
|
|
// Zero or More - Full Match
|
|
t.test("zero_or_more_full_match", [&](testing & t) {
|
|
auto parser = build_peg_parser([](common_peg_parser_builder & p) { return p.zero_or_more(p.literal("test")); });
|
|
|
|
auto ctx = common_peg_parse_context("test", false);
|
|
auto result = parser.parse(ctx);
|
|
t.assert_equal("zero_or_more_full_match", true, result.success());
|
|
});
|
|
|
|
// One or More - Partial Match 1
|
|
t.test("one_or_more_partial_match_1", [&](testing & t) {
|
|
auto parser = build_peg_parser([](common_peg_parser_builder & p) { return p.one_or_more(p.literal("repeat")); });
|
|
|
|
auto ctx = common_peg_parse_context("rep", true);
|
|
auto result = parser.parse(ctx);
|
|
t.assert_equal("one_or_more_partial_match_1", true, result.need_more_input());
|
|
});
|
|
|
|
// One or More - Partial Match 2
|
|
t.test("one_or_more_partial_match_2", [&](testing & t) {
|
|
auto parser = build_peg_parser([](common_peg_parser_builder & p) { return p.one_or_more(p.literal("ab")); });
|
|
|
|
auto ctx = common_peg_parse_context("aba", true);
|
|
auto result = parser.parse(ctx);
|
|
t.assert_equal("one_or_more_partial_match_2", true, result.need_more_input());
|
|
});
|
|
|
|
// One or More - Full Match
|
|
t.test("one_or_more_full_match", [&](testing & t) {
|
|
auto parser = build_peg_parser([](common_peg_parser_builder & p) { return p.one_or_more(p.literal("single")); });
|
|
|
|
auto ctx = common_peg_parse_context("single", false);
|
|
auto result = parser.parse(ctx);
|
|
t.assert_equal("one_or_more_full_match", true, result.success());
|
|
});
|
|
|
|
// One or More - No Match
|
|
t.test("one_or_more_no_match", [&](testing & t) {
|
|
auto parser = build_peg_parser([](common_peg_parser_builder & p) { return p.one_or_more(p.literal("()")); });
|
|
|
|
auto ctx = common_peg_parse_context("success", false);
|
|
auto result = parser.parse(ctx);
|
|
t.assert_equal("one_or_more_no_match", true, result.fail());
|
|
});
|
|
});
|
|
|
|
|
|
t.test("recursive rules", [](testing &t) {
|
|
// Test simple number
|
|
t.test("simple_number", [](testing &t) {
|
|
auto value_parser = build_peg_parser([](common_peg_parser_builder & p) {
|
|
p.rule("number", p.chars("0-9"));
|
|
p.rule("list", p.literal("[") + p.ref("value") + p.literal("]"));
|
|
return p.rule("value", p.ref("number") | p.ref("list"));
|
|
});
|
|
|
|
common_peg_parse_context ctx("1", false);
|
|
auto result = value_parser.parse(ctx);
|
|
|
|
t.assert_equal("result_is_success", true, result.success());
|
|
});
|
|
|
|
// Test simple list
|
|
t.test("simple_list", [](testing &t) {
|
|
auto value_parser = build_peg_parser([](common_peg_parser_builder & p) {
|
|
p.rule("number", p.chars("0-9"));
|
|
p.rule("list", p.literal("[") + p.ref("value") + p.literal("]"));
|
|
return p.rule("value", p.ref("number") | p.ref("list"));
|
|
});
|
|
|
|
common_peg_parse_context ctx("[1]", false);
|
|
auto result = value_parser.parse(ctx);
|
|
|
|
t.assert_equal("result_is_success", true, result.success());
|
|
});
|
|
|
|
// Test nested list
|
|
t.test("nested_list", [](testing &t) {
|
|
auto value_parser = build_peg_parser([](common_peg_parser_builder & p) {
|
|
p.rule("number", p.chars("0-9"));
|
|
p.rule("list", p.literal("[") + p.ref("value") + p.literal("]"));
|
|
return p.rule("value", p.ref("number") | p.ref("list"));
|
|
});
|
|
|
|
common_peg_parse_context ctx("[[2]]", false);
|
|
auto result = value_parser.parse(ctx);
|
|
|
|
t.assert_equal("result_is_success", true, result.success());
|
|
});
|
|
|
|
// Test deeply nested list
|
|
t.test("deeply_nested_list", [](testing &t) {
|
|
auto value_parser = build_peg_parser([](common_peg_parser_builder & p) {
|
|
p.rule("number", p.chars("0-9"));
|
|
p.rule("list", p.literal("[") + p.ref("value") + p.literal("]"));
|
|
return p.rule("value", p.ref("number") | p.ref("list"));
|
|
});
|
|
|
|
common_peg_parse_context ctx("[[[3]]]", false);
|
|
auto result = value_parser.parse(ctx);
|
|
|
|
t.assert_equal("result_is_success", true, result.success());
|
|
});
|
|
|
|
// Test need_more_input match
|
|
t.test("need_more_input_match", [](testing &t) {
|
|
auto value_parser = build_peg_parser([](common_peg_parser_builder & p) {
|
|
p.rule("number", p.chars("0-9"));
|
|
p.rule("list", p.literal("[") + p.ref("value") + p.literal("]"));
|
|
return p.rule("value", p.ref("number") | p.ref("list"));
|
|
});
|
|
|
|
common_peg_parse_context ctx("[[", true);
|
|
auto result = value_parser.parse(ctx);
|
|
|
|
t.assert_equal("result_is_need_more_input", true, result.need_more_input());
|
|
});
|
|
|
|
// Test no match
|
|
t.test("no_match", [](testing &t) {
|
|
auto value_parser = build_peg_parser([](common_peg_parser_builder & p) {
|
|
p.rule("number", p.chars("0-9"));
|
|
p.rule("list", p.literal("[") + p.ref("value") + p.literal("]"));
|
|
return p.rule("value", p.ref("number") | p.ref("list"));
|
|
});
|
|
|
|
common_peg_parse_context ctx("[a]", false);
|
|
auto result = value_parser.parse(ctx);
|
|
|
|
t.assert_equal("result_is_fail", true, result.fail());
|
|
});
|
|
});
|
|
}
|