summaryrefslogtreecommitdiff
path: root/src/json_ui.cc
diff options
context:
space:
mode:
authorFrank LENORMAND <lenormf@gmail.com>2019-11-13 09:54:17 +0100
committerFrank LENORMAND <lenormf@gmail.com>2019-11-17 09:27:46 +0100
commit7cdbe1d3d24c1cc13bd7cbc3fe252f1e88747ffb (patch)
tree3d1bc2cb81cb4c9623bd18a0dcc9fc607fa8db53 /src/json_ui.cc
parenta7d3976a1002a80c4e5a17c989921025ded89450 (diff)
src: Move JSON parsing code to its own file
The `json_ui.cc` file contained both data-parsing and UI-related code. This commit moves the JSON parsing code to its own `json.cc` file, to separate concerns, make compilation faster when changes are made to either UI or parsing code, and make the parsing code more accessible to fuzzers. The signature of the following function: ``` auto parse_json(StringView json); ``` was changed to: ``` JsonResult parse_json(StringView json); ``` to avoid `auto` deduction issues at compile-time.
Diffstat (limited to 'src/json_ui.cc')
-rw-r--r--src/json_ui.cc187
1 files changed, 1 insertions, 186 deletions
diff --git a/src/json_ui.cc b/src/json_ui.cc
index 51fde617..0f1d6f3b 100644
--- a/src/json_ui.cc
+++ b/src/json_ui.cc
@@ -4,13 +4,11 @@
#include "event_manager.hh"
#include "exception.hh"
#include "file.hh"
+#include "json.hh"
#include "keys.hh"
#include "ranges.hh"
#include "string_utils.hh"
-#include "unit_tests.hh"
-#include "value.hh"
-#include <cstdio>
#include <utility>
#include <unistd.h>
@@ -23,50 +21,6 @@ struct invalid_rpc_request : runtime_error {
: runtime_error(format("invalid json rpc request ({})", message)) {}
};
-template<typename T>
-String to_json(ArrayView<const T> array)
-{
- return "[" + join(array | transform([](auto&& elem) { return to_json(elem); }), ", ") + "]";
-}
-
-template<typename T, MemoryDomain D>
-String to_json(const Vector<T, D>& vec) { return to_json(ArrayView<const T>{vec}); }
-
-template<typename K, typename V, MemoryDomain D>
-String to_json(const HashMap<K, V, D>& map)
-{
- return "{" + join(map | transform([](auto&& i) { return format("{}: {}", to_json(i.key), to_json(i.value)); }),
- ',', false) + "}";
-}
-
-String to_json(int i) { return to_string(i); }
-String to_json(bool b) { return b ? "true" : "false"; }
-String to_json(StringView str)
-{
- String res;
- res.reserve(str.length() + 4);
- res += '"';
- for (auto it = str.begin(), end = str.end(); it != end; )
- {
- auto next = std::find_if(it, end, [](char c) {
- return c == '\\' or c == '"' or (c >= 0 and c <= 0x1F);
- });
-
- res += StringView{it, next};
- if (next == end)
- break;
-
- char buf[7] = {'\\', *next, 0};
- if (*next >= 0 and *next <= 0x1F)
- sprintf(buf, "\\u%04x", *next);
-
- res += buf;
- it = next+1;
- }
- res += '"';
- return res;
-}
-
String to_json(Color color)
{
if (color.color == Kakoune::Color::RGB)
@@ -254,120 +208,6 @@ void JsonUI::set_on_key(OnKeyCallback callback)
m_on_key = std::move(callback);
}
-using JsonArray = Vector<Value>;
-using JsonObject = HashMap<String, Value>;
-
-static bool is_digit(char c) { return c >= '0' and c <= '9'; }
-
-struct JsonResult { Value value; const char* new_pos; };
-
-JsonResult parse_json(const char* pos, const char* end)
-{
- if (not skip_while(pos, end, is_blank))
- return {};
-
- if (is_digit(*pos) or *pos == '-')
- {
- auto digit_end = pos + 1;
- skip_while(digit_end, end, is_digit);
- return { Value{str_to_int({pos, digit_end})}, digit_end };
- }
- if (end - pos > 4 and StringView{pos, pos+4} == "true")
- return { Value{true}, pos+4 };
- if (end - pos > 5 and StringView{pos, pos+5} == "false")
- return { Value{false}, pos+5 };
- if (*pos == '"')
- {
- String value;
- bool escaped = false;
- ++pos;
- for (auto string_end = pos; string_end != end; ++string_end)
- {
- if (escaped)
- {
- escaped = false;
- value += StringView{pos, string_end};
- value.back() = *string_end;
- pos = string_end+1;
- continue;
- }
- if (*string_end == '\\')
- escaped = true;
- if (*string_end == '"')
- {
- value += StringView{pos, string_end};
- return {std::move(value), string_end+1};
- }
- }
- return {};
- }
- if (*pos == '[')
- {
- JsonArray array;
- if (++pos == end)
- throw runtime_error("unable to parse array");
- if (*pos == ']')
- return {std::move(array), pos+1};
-
- while (true)
- {
- auto [element, new_pos] = parse_json(pos, end);
- if (not element)
- return {};
- pos = new_pos;
- array.push_back(std::move(element));
- if (not skip_while(pos, end, is_blank))
- return {};
-
- if (*pos == ',')
- ++pos;
- else if (*pos == ']')
- return {std::move(array), pos+1};
- else
- throw runtime_error("unable to parse array, expected ',' or ']'");
- }
- }
- if (*pos == '{')
- {
- if (++pos == end)
- throw runtime_error("unable to parse object");
- JsonObject object;
- if (*pos == '}')
- return {std::move(object), pos+1};
-
- while (true)
- {
- auto [name_value, name_end] = parse_json(pos, end);
- if (not name_value)
- return {};
- pos = name_end;
- String& name = name_value.as<String>();
- if (not skip_while(pos, end, is_blank))
- return {};
- if (*pos++ != ':')
- throw runtime_error("expected :");
-
- auto [element, element_end] = parse_json(pos, end);
- if (not element)
- return {};
- pos = element_end;
- object.insert({ std::move(name), std::move(element) });
- if (not skip_while(pos, end, is_blank))
- return {};
-
- if (*pos == ',')
- ++pos;
- else if (*pos == '}')
- return {std::move(object), pos+1};
- else
- throw runtime_error("unable to parse object, expected ',' or '}'");
- }
- }
- throw runtime_error("unable to parse json");
-}
-
-auto parse_json(StringView json) { return parse_json(json.begin(), json.end()); }
-
void JsonUI::eval_json(const Value& json)
{
if (not json.is_a<JsonObject>())
@@ -510,29 +350,4 @@ void JsonUI::parse_requests(EventMode mode)
}
}
-UnitTest test_json_parser{[]()
-{
- {
- auto value = parse_json(R"({ "jsonrpc": "2.0", "method": "keys", "params": [ "b", "l", "a", "h" ] })").value;
- kak_assert(value);
- }
-
- {
- auto value = parse_json("[10,20]").value;
- kak_assert(value and value.is_a<JsonArray>());
- kak_assert(value.as<JsonArray>().at(1).as<int>() == 20);
- }
-
- {
- auto value = parse_json("-1").value;
- kak_assert(value.as<int>() == -1);
- }
-
- {
- auto value = parse_json("{}").value;
- kak_assert(value and value.is_a<JsonObject>());
- kak_assert(value.as<JsonObject>().empty());
- }
-}};
-
}