mirror of
https://github.com/ikawrakow/ik_llama.cpp.git
synced 2026-01-26 17:20:01 +00:00
* port upstream https://github.com/ggml-org/llama.cpp/pull/16932 * Add fixed chat templates. * fix grammar when tool have no argument * Insert additional stops for Kimi-K2 * Fix `no triggers set for lazy grammar!` for GLM4.5/4.6 * update chat.cpp * fix grammar for GLM 4.5/4.6 * chat: Fix streaming parser for granite models (#15682) * fix(chat): fix streaming parser for granite models * tests: add test cases for Granite models chat parser * common : Fix corrupted memory error on json grammar initialization (#16038) Initalizing RESERVED_NAME in is_reserved_name() is not thread safe and leads to corrupted memory when used from multiple threads as can be seen in the asan trace below. This fixes the initialization to make it thread-safe. #0 0x000100abd018 in std::__1::pair<std::__1::__hash_iterator<std::__1::__hash_node<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>, void*>*>, bool> std::__1::__hash_table<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>, std::__1::hash<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>>, std::__1::equal_to<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>>, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>>>::__emplace_unique_key_args<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>> const&>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>> const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>> const&) __hash_table:1565 #1 0x000100ab0320 in SchemaConverter::visit(nlohmann::json_abi_v3_12_0::basic_json<nlohmann::json_abi_v3_12_0::ordered_map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>, bool, long long, unsigned long long, double, std::__1::allocator, nlohmann::json_abi_v3_12_0::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char>>, void> const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>> const&) json-schema-to-grammar.cpp:802 #2 0x000100aafc48 in std::__1::__function::__func<build_grammar(std::__1::function<void (common_grammar_builder const&)> const&, common_grammar_options const&)::$_2, std::__1::allocator<build_grammar(std::__1::function<void (common_grammar_builder const&)> const&, common_grammar_options const&)::$_2>, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>> (std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>> const&, nlohmann::json_abi_v3_12_0::basic_json<nlohmann::json_abi_v3_12_0::ordered_map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>, bool, long long, unsigned long long, double, std::__1::allocator, nlohmann::json_abi_v3_12_0::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char>>, void> const&)>::operator()(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>> const&, nlohmann::json_abi_v3_12_0::basic_json<nlohmann::json_abi_v3_12_0::ordered_map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>, bool, long long, unsigned long long, double, std::__1::allocator, nlohmann::json_abi_v3_12_0::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char>>, void> const&) function.h:319 #3 0x000100a2c938 in std::__1::__function::__func<common_chat_params_init_llama_3_x(minja::chat_template const&, templates_params const&, bool)::$_0::operator()(common_grammar_builder const&) const::'lambda'(nlohmann::json_abi_v3_12_0::basic_json<nlohmann::json_abi_v3_12_0::ordered_map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>, bool, long long, unsigned long long, double, std::__1::allocator, nlohmann::json_abi_v3_12_0::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char>>, void> const&), std::__1::allocator<common_chat_params_init_llama_3_x(minja::chat_template const&, templates_params const&, bool)::$_0::operator()(common_grammar_builder const&) const::'lambda'(nlohmann::json_abi_v3_12_0::basic_json<nlohmann::json_abi_v3_12_0::ordered_map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>, bool, long long, unsigned long long, double, std::__1::allocator, nlohmann::json_abi_v3_12_0::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char>>, void> const&)>, void (nlohmann::json_abi_v3_12_0::basic_json<nlohmann::json_abi_v3_12_0::ordered_map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>, bool, long long, unsigned long long, double, std::__1::allocator, nlohmann::json_abi_v3_12_0::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char>>, void> const&)>::operator()(nlohmann::json_abi_v3_12_0::basic_json<nlohmann::json_abi_v3_12_0::ordered_map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>, bool, long long, unsigned long long, double, std::__1::allocator, nlohmann::json_abi_v3_12_0::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char>>, void> const&) function.h:319 #4 0x000100a139f8 in foreach_function(nlohmann::json_abi_v3_12_0::basic_json<nlohmann::json_abi_v3_12_0::ordered_map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>, bool, long long, unsigned long long, double, std::__1::allocator, nlohmann::json_abi_v3_12_0::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char>>, void> const&, std::__1::function<void (nlohmann::json_abi_v3_12_0::basic_json<nlohmann::json_abi_v3_12_0::ordered_map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>, bool, long long, unsigned long long, double, std::__1::allocator, nlohmann::json_abi_v3_12_0::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char>>, void> const&)> const&) chat.cpp:762 #5 0x000100a2a7f4 in std::__1::__function::__func<common_chat_params_init_llama_3_x(minja::chat_template const&, templates_params const&, bool)::$_0, std::__1::allocator<common_chat_params_init_llama_3_x(minja::chat_template const&, templates_params const&, bool)::$_0>, void (common_grammar_builder const&)>::operator()(common_grammar_builder const&) function.h:319 #6 0x000100aa98f4 in build_grammar(std::__1::function<void (common_grammar_builder const&)> const&, common_grammar_options const&) json-schema-to-grammar.cpp:982 #7 0x0001009c9314 in common_chat_params_init_llama_3_x(minja::chat_template const&, templates_params const&, bool) chat.cpp:1110 #8 0x0001009b8afc in common_chat_templates_apply_jinja(common_chat_templates const*, common_chat_templates_inputs const&) chat.cpp:1992 #9 0x0001009b533c in common_chat_templates_apply(common_chat_templates const*, common_chat_templates_inputs const&) chat.cpp:2074 #10 0x000100810120 in llamacpp_apply_chat_template+0x724 (predict_oai-98384e17fb94e863:arm64+0x100090120) ... ==45482==Register values: x[0] = 0x00006020004147f8 x[1] = 0x00006080000013c8 x[2] = 0x0000000000000000 x[3] = 0x0000604006289738 x[4] = 0x0000000000000002 x[5] = 0x0000000000000001 x[6] = 0x04034000004b4000 x[7] = 0x0000000000000001 x[8] = 0xbebebebebebebebe x[9] = 0x17d7d7d7d7d7d7d7 x[10] = 0x00000c04000828ff x[11] = 0x0000000000000001 x[12] = 0x000000002018d383 x[13] = 0x0000000000000000 x[14] = 0xfa0000000000fafa x[15] = 0x000010700001ffff x[16] = 0x000000019dc012c0 x[17] = 0x00000001021284f8 x[18] = 0x0000000000000000 x[19] = 0x00000001700acdc0 x[20] = 0x0000000000000002 x[21] = 0x000000002018d384 x[22] = 0x16dd16fd2e731151 x[23] = 0x0000007000020000 x[24] = 0x0000000100c69c08 x[25] = 0x0000000100c69c20 x[26] = 0x00006080000013c7 x[27] = 0x0000000100c69c00 x[28] = 0x00000001700acd60 fp = 0x00000001700aceb0 lr = 0x0000000100abce30 sp = 0x00000001700acd60 AddressSanitizer can not provide additional info. SUMMARY: AddressSanitizer: SEGV __hash_table:1565 in std::__1::pair<std::__1::__hash_iterator<std::__1::__hash_node<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>, void*>*>, bool> std::__1::__hash_table<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>, std::__1::hash<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>>, std::__1::equal_to<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>>, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>>>::__emplace_unique_key_args<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>> const&>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>> const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>> const&) Thread T5 created by T0 here: #0 0x0001020b99d4 in pthread_create+0x5c (libclang_rt.asan_osx_dynamic.dylib:arm64e+0x359d4) #1 0x000100873910 in std::sys::pal::unix:🧵:Thread:🆕:h77254fdd87a28e05+0x118 (predict_oai-98384e17fb94e863:arm64+0x1000f3910) #2 0x0001007c7a1c in test::run_test::haeb3c2bcd5ed6cf6+0x76c (predict_oai-98384e17fb94e863:arm64+0x100047a1c) #3 0x0001007aedb0 in test::console::run_tests_console::he9d142d704f3a986+0x149c (predict_oai-98384e17fb94e863:arm64+0x10002edb0) #4 0x0001007c5758 in test::test_main::hf86a5e20735245b9+0x118 (predict_oai-98384e17fb94e863:arm64+0x100045758) #5 0x0001007c5da0 in test::test_main_static::h61ee9c8fd30abca0+0x54 (predict_oai-98384e17fb94e863:arm64+0x100045da0) ... ==45482==ABORTING * common : fix reasoning before forced tool call via tool_choice = required (#16264) * common : fix reasoning before forced tool call via tool_choice = required * common : improve reasoning and commentary handling when tool_choice is required (cherry picked from commit c746984956d6882c2de73d53ae2bb3bdf889e475) --------- Co-authored-by: Alde Rojas <hello@alde.dev> * Try fix Jinja template for GLM * Improve Kimi-K2 chat template * Fix "Invalid tool call arguments passed" in a rare case. In a rare case, the model may emit a raw string that begins with a valid JSON string. This commit adds unit tests to cover that scenario and fixes the regression introduced during the Kimi-K2 adaptation. --------- Co-authored-by: shun095 <8069181+shun095@users.noreply.github.com> Co-authored-by: David Ribeiro Alves <davidralves@gmail.com> Co-authored-by: crat0z <11581854+crat0z@users.noreply.github.com> Co-authored-by: Alde Rojas <hello@alde.dev>
271 lines
12 KiB
C++
271 lines
12 KiB
C++
#include <json-partial.h>
|
|
#include "ggml.h"
|
|
#include "log.h"
|
|
#include <string>
|
|
|
|
using json = nlohmann::ordered_json;
|
|
|
|
enum common_json_stack_element_type {
|
|
COMMON_JSON_STACK_ELEMENT_OBJECT,
|
|
COMMON_JSON_STACK_ELEMENT_KEY,
|
|
COMMON_JSON_STACK_ELEMENT_ARRAY,
|
|
};
|
|
|
|
struct common_json_stack_element {
|
|
common_json_stack_element_type type;
|
|
std::string key;
|
|
};
|
|
|
|
bool common_json_parse(
|
|
const std::string & input,
|
|
const std::string & healing_marker,
|
|
common_json & out)
|
|
{
|
|
std::string::const_iterator it = input.begin();
|
|
const auto end = input.end();
|
|
return common_json_parse(it, end, healing_marker, out);
|
|
}
|
|
|
|
bool common_json_parse(
|
|
std::string::const_iterator & it,
|
|
const std::string::const_iterator & end,
|
|
const std::string & healing_marker,
|
|
common_json & out)
|
|
{
|
|
// // https://json.nlohmann.me/features/parsing/sax_interface/
|
|
struct json_error_locator : public nlohmann::json_sax<json> {
|
|
std::size_t position;
|
|
bool found_error;
|
|
std::string last_token;
|
|
std::string exception_message;
|
|
std::vector<common_json_stack_element> stack;
|
|
|
|
json_error_locator() : position(0), found_error(false) {}
|
|
|
|
bool parse_error(std::size_t position, const std::string & last_token, const json::exception & ex) override { // NOLINT
|
|
this->position = position - 1;
|
|
this->found_error = true;
|
|
this->last_token = last_token;
|
|
this->exception_message = ex.what();
|
|
return false;
|
|
}
|
|
void close_value() {
|
|
if (!stack.empty() && (stack.back().type == COMMON_JSON_STACK_ELEMENT_KEY)) {
|
|
stack.pop_back();
|
|
}
|
|
}
|
|
bool null() override { // NOLINT
|
|
close_value();
|
|
return true;
|
|
}
|
|
bool boolean(bool) override { // NOLINT
|
|
close_value();
|
|
return true;
|
|
}
|
|
bool number_integer(number_integer_t) override { // NOLINT
|
|
close_value();
|
|
return true;
|
|
}
|
|
bool number_unsigned(number_unsigned_t) override { // NOLINT
|
|
close_value();
|
|
return true;
|
|
}
|
|
bool number_float(number_float_t, const string_t &) override { // NOLINT
|
|
close_value();
|
|
return true;
|
|
}
|
|
bool string(string_t &) override { // NOLINT
|
|
close_value();
|
|
return true;
|
|
}
|
|
bool binary(binary_t &) override { // NOLINT
|
|
close_value();
|
|
return true;
|
|
}
|
|
bool start_object(std::size_t) override { // NOLINT
|
|
stack.push_back({COMMON_JSON_STACK_ELEMENT_OBJECT, ""});
|
|
return true;
|
|
}
|
|
bool end_object() override {
|
|
GGML_ASSERT(!stack.empty() && stack.back().type == COMMON_JSON_STACK_ELEMENT_OBJECT);
|
|
stack.pop_back();
|
|
close_value();
|
|
return true;
|
|
}
|
|
bool key(string_t & key) override { // NOLINT
|
|
stack.push_back({COMMON_JSON_STACK_ELEMENT_KEY, key});
|
|
return true;
|
|
}
|
|
bool start_array(std::size_t) override { // NOLINT
|
|
stack.push_back({COMMON_JSON_STACK_ELEMENT_ARRAY, ""});
|
|
return true;
|
|
}
|
|
bool end_array() override {
|
|
GGML_ASSERT(!stack.empty() && stack.back().type == COMMON_JSON_STACK_ELEMENT_ARRAY);
|
|
stack.pop_back();
|
|
close_value();
|
|
return true;
|
|
}
|
|
};
|
|
json_error_locator err_loc;
|
|
auto start = it;
|
|
json::sax_parse(it, end, &err_loc);
|
|
|
|
if (err_loc.found_error) {
|
|
it = start;
|
|
auto temptative_end = it + err_loc.position;
|
|
// LOG_DBG("Error at position %zu (is_end = %s): %s\n", err_loc.position, temptative_end == end ? "true" : "false", err_loc.exception_message.c_str());
|
|
|
|
auto input = std::string(it, temptative_end);
|
|
try {
|
|
out.json = json::parse(input);
|
|
// out.json = json::parse(it, temptative_end);
|
|
it = temptative_end;
|
|
return true;
|
|
} catch (const std::exception & ex) {
|
|
// No, needs healing.
|
|
LOG("Failed to parse up to error: %s: <<<%s>>>\n", ex.what(), std::string(it, temptative_end).c_str());
|
|
}
|
|
auto can_parse = [](const std::string & str) {
|
|
try {
|
|
auto _ = json::parse(str); // NOLINT
|
|
return true;
|
|
} catch (const std::exception &) {
|
|
return false;
|
|
}
|
|
};
|
|
if (!healing_marker.empty() && !err_loc.stack.empty()) {
|
|
std::string str(it, temptative_end);
|
|
auto last_non_sp_pos = str.find_last_not_of(" \n\r\t");
|
|
if (last_non_sp_pos == std::string::npos) {
|
|
throw std::runtime_error("Cannot heal a truncated JSON that stopped in an unknown location");
|
|
}
|
|
auto last_non_sp_char = str[last_non_sp_pos];
|
|
// Used to detect stops on a number, which may not be complete.
|
|
auto was_maybe_number = [&]() {
|
|
if (!str.empty() && std::isspace(str.back())) {
|
|
return false;
|
|
}
|
|
return std::isdigit(last_non_sp_char) ||
|
|
last_non_sp_char == '.' ||
|
|
last_non_sp_char == 'e' ||
|
|
last_non_sp_char == 'E' ||
|
|
last_non_sp_char == '-';
|
|
};
|
|
|
|
std::string closing;
|
|
for (size_t i = err_loc.stack.size(); i > 0; i--) {
|
|
auto & el = err_loc.stack[i - 1];
|
|
if (el.type == COMMON_JSON_STACK_ELEMENT_OBJECT) {
|
|
closing += "}";
|
|
} else if (el.type == COMMON_JSON_STACK_ELEMENT_ARRAY) {
|
|
closing += "]";
|
|
} else if (el.type != COMMON_JSON_STACK_ELEMENT_KEY) {
|
|
throw std::runtime_error("Unexpected stack element type");
|
|
}
|
|
}
|
|
|
|
const auto & magic_seed = out.healing_marker.marker = healing_marker;//"$llama.cpp.json$";
|
|
|
|
if (err_loc.stack.back().type == COMMON_JSON_STACK_ELEMENT_KEY) {
|
|
// We're inside an object value
|
|
if (last_non_sp_char == ':' && can_parse(str + "1" + closing)) {
|
|
// Was about to create an object value
|
|
str += (out.healing_marker.json_dump_marker = "\"" + magic_seed) + "\"" + closing;
|
|
} else if (can_parse(str + ": 1" + closing)) {
|
|
str += (out.healing_marker.json_dump_marker = ":\"" + magic_seed) + "\"" + closing;
|
|
} else if (last_non_sp_char == '{' && can_parse(str + closing)) {
|
|
// Was about to create an object
|
|
str += (out.healing_marker.json_dump_marker = "\"" + magic_seed) + "\": 1" + closing;
|
|
} else if (can_parse(str + "\"" + closing)) {
|
|
// Was inside an object value string
|
|
str += (out.healing_marker.json_dump_marker = magic_seed) + "\"" + closing;
|
|
} else if (str[str.length() - 1] == '\\' && can_parse(str + "\\\"" + closing)) {
|
|
// Was inside an object value string after an escape
|
|
str += (out.healing_marker.json_dump_marker = "\\" + magic_seed) + "\"" + closing;
|
|
} else {
|
|
// find last :
|
|
auto last_pos = str.find_last_of(':');
|
|
if (last_pos == std::string::npos) {
|
|
throw std::runtime_error("Cannot heal a truncated JSON that stopped in an unknown location");
|
|
}
|
|
// Cutting back to opening : for object value
|
|
str = str.substr(0, last_pos + 1) + (out.healing_marker.json_dump_marker = "\"" + magic_seed) + "\"" + closing;
|
|
}
|
|
} else if (err_loc.stack.back().type == COMMON_JSON_STACK_ELEMENT_ARRAY) {
|
|
if ((last_non_sp_char == ',' || last_non_sp_char == '[') && can_parse(str + "1" + closing)) {
|
|
// Was about to create an array value
|
|
str += (out.healing_marker.json_dump_marker = "\"" + magic_seed) + "\"" + closing;
|
|
} else if (can_parse(str + "\"" + closing)) {
|
|
// Was inside an array value string
|
|
str += (out.healing_marker.json_dump_marker = magic_seed) + "\"" + closing;
|
|
} else if (str[str.length() - 1] == '\\' && can_parse(str + "\\\"" + closing)) {
|
|
// Was inside an array value string after an escape
|
|
str += (out.healing_marker.json_dump_marker = "\\" + magic_seed) + "\"" + closing;
|
|
} else if (!was_maybe_number() && can_parse(str + ", 1" + closing)) {
|
|
// Had just finished a value
|
|
str += (out.healing_marker.json_dump_marker = ",\"" + magic_seed) + "\"" + closing;
|
|
} else {
|
|
auto last_pos = str.find_last_of("[,");
|
|
if (last_pos == std::string::npos) {
|
|
throw std::runtime_error("Cannot heal a truncated JSON array stopped in an unknown location");
|
|
}
|
|
// Cutting back to last [ or , for array value
|
|
str = str.substr(0, last_pos + 1) + (out.healing_marker.json_dump_marker = "\"" + magic_seed) + "\"" + closing;
|
|
}
|
|
} else if (err_loc.stack.back().type == COMMON_JSON_STACK_ELEMENT_OBJECT) {
|
|
if ((last_non_sp_char == '{' && can_parse(str + closing)) ||
|
|
(last_non_sp_char == ',' && can_parse(str + "\"\": 1" + closing))) {
|
|
// Was about to create an object key+value
|
|
str += (out.healing_marker.json_dump_marker = "\"" + magic_seed) + "\": 1" + closing;
|
|
} else if (!was_maybe_number() && can_parse(str + ",\"\": 1" + closing)) {
|
|
// Was about to create an object key+value
|
|
str += (out.healing_marker.json_dump_marker = ",\"" + magic_seed) + "\": 1" + closing;
|
|
} else if (can_parse(str + "\": 1" + closing)) {
|
|
// Was inside an object key string
|
|
str += (out.healing_marker.json_dump_marker = magic_seed) + "\": 1" + closing;
|
|
} else if (str[str.length() - 1] == '\\' && can_parse(str + "\\\": 1" + closing)) {
|
|
// Was inside an object key string after an escape
|
|
str += (out.healing_marker.json_dump_marker = "\\" + magic_seed) + "\": 1" + closing;
|
|
} else {
|
|
auto last_pos = str.find_last_of(':');
|
|
if (last_pos == std::string::npos) {
|
|
throw std::runtime_error("Cannot heal a truncated JSON object stopped in an unknown location");
|
|
}
|
|
// fprintf(stderr, "Cutting back to last : for object key+value\n");
|
|
str = str.substr(0, last_pos + 1) + (out.healing_marker.json_dump_marker = "\"" + magic_seed) + "\"" + closing;
|
|
}
|
|
} else {
|
|
throw std::runtime_error("Cannot heal a truncated JSON object stopped in an unknown location");
|
|
}
|
|
// fprintf(stderr, "HEALED:\nSTRING <<<\n%s\n>>>\n\nmagic_cut: <<<\n%s\n>>>\n\n", str.c_str(), out.healing_marker.json_dump_marker.c_str());
|
|
out.json = json::parse(str);
|
|
it = temptative_end;
|
|
return true;
|
|
}
|
|
// handle unclosed top-level primitive
|
|
if (err_loc.position != 0 && !healing_marker.empty() && err_loc.stack.empty()) {
|
|
std::string str(it, temptative_end);
|
|
const auto & magic_seed = out.healing_marker.marker = healing_marker;
|
|
if (can_parse(str + "\"")) {
|
|
// Was inside an string
|
|
str += (out.healing_marker.json_dump_marker = magic_seed) + "\"";
|
|
} else if (str[str.length() - 1] == '\\' && can_parse(str + "\\\"")) {
|
|
// Was inside an string after an escape
|
|
str += (out.healing_marker.json_dump_marker = "\\" + magic_seed) + "\"";
|
|
} else {
|
|
// TODO: handle more unclosed top-level primitive if the stack was empty but we got an error (e.g. "tru", "\"", etc...)
|
|
// fprintf(stderr, "Closing: TODO\n");
|
|
return false;
|
|
}
|
|
out.json = json::parse(str);
|
|
it = temptative_end;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
out.json = json::parse(it, end);
|
|
it = end;
|
|
return true;
|
|
}
|