From d14548cff8cfcf3659ac19d07e7884894edb1e6f Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 12 Oct 2024 00:23:52 +0300 Subject: [PATCH] feat: toml lvalues support --- src/coders/commons.cpp | 16 ++++++++--- src/coders/commons.hpp | 13 +++++++-- src/coders/json.cpp | 3 +- src/coders/toml.cpp | 64 ++++++++++++++++++++---------------------- src/data/dv.cpp | 2 +- src/engine.cpp | 8 +++++- src/window/Events.cpp | 35 ++++++++++++----------- 7 files changed, 83 insertions(+), 58 deletions(-) diff --git a/src/coders/commons.cpp b/src/coders/commons.cpp index e915b6a6..8f2ca6cd 100644 --- a/src/coders/commons.cpp +++ b/src/coders/commons.cpp @@ -221,10 +221,6 @@ std::string_view BasicParser::readUntilEOL() { std::string BasicParser::parseName() { char c = peek(); if (!is_identifier_start(c)) { - if (c == '"') { - pos++; - return parseString(c); - } throw error("identifier expected"); } int start = pos; @@ -234,6 +230,18 @@ std::string BasicParser::parseName() { return std::string(source.substr(start, pos - start)); } +std::string BasicParser::parseXmlName() { + char c = peek(); + if (!is_json_identifier_start(c)) { + throw error("identifier expected"); + } + int start = pos; + while (hasNext() && is_json_identifier_part(source[pos])) { + pos++; + } + return std::string(source.substr(start, pos - start)); +} + int64_t BasicParser::parseSimpleInt(int base) { char c = peek(); int index = hexchar2int(c); diff --git a/src/coders/commons.hpp b/src/coders/commons.hpp index e5bb3bc1..952ea316 100644 --- a/src/coders/commons.hpp +++ b/src/coders/commons.hpp @@ -30,14 +30,22 @@ inline bool is_whitespace(int c) { } inline bool is_identifier_start(int c) { - return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c == '_' || - c == '.'; + return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c == '_'; } inline bool is_identifier_part(int c) { return is_identifier_start(c) || is_digit(c) || c == '-'; } +inline bool is_json_identifier_start(int c) { + return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c == '_' || + c == '.'; +} + +inline bool is_json_identifier_part(int c) { + return is_json_identifier_start(c) || is_digit(c) || c == '-'; +} + inline int hexchar2int(int c) { if (c >= '0' && c <= '9') { return c - '0'; @@ -99,6 +107,7 @@ public: std::string_view readUntil(char c); std::string_view readUntilEOL(); std::string parseName(); + std::string parseXmlName(); bool hasNext(); char peek(); char peekInLine(); diff --git a/src/coders/json.cpp b/src/coders/json.cpp index 8fb6a1e2..9c189998 100644 --- a/src/coders/json.cpp +++ b/src/coders/json.cpp @@ -178,7 +178,8 @@ dv::value Parser::parseObject() { skipLine(); continue; } - std::string key = parseName(); + expect('"'); + std::string key = parseString('"'); char next = peek(); if (next != ':') { throw error("':' expected"); diff --git a/src/coders/toml.cpp b/src/coders/toml.cpp index b8dc1ea3..1bbb52aa 100644 --- a/src/coders/toml.cpp +++ b/src/coders/toml.cpp @@ -26,32 +26,6 @@ class TomlReader : BasicParser { } } - dv::value& getSection(const std::string& section) { - if (section.empty()) { - return root; - } - size_t offset = 0; - auto rootMap = &root; - do { - size_t index = section.find('.', offset); - if (index == std::string::npos) { - auto map = rootMap->at(section); - if (!map) { - return rootMap->object(section); - } - return *map; - } - auto subsection = section.substr(offset, index); - auto map = rootMap->at(subsection); - if (!map) { - rootMap = &rootMap->object(subsection); - } else { - rootMap = &*map; - } - offset = index + 1; - } while (true); - } - dv::value parseValue() { char c = peek(); if (is_digit(c)) { @@ -111,7 +85,30 @@ class TomlReader : BasicParser { } } - void readSection(const std::string& section, dv::value& map) { + dv::value& parseLValue(dv::value& root) { + dv::value* lvalue = &root; + while (hasNext()) { + char c = peek(); + std::string name; + if (c == '\'' || c == '"') { + pos++; + name = parseString(c); + } else { + name = parseName(); + } + if (lvalue->getType() == dv::value_type::none) { + *lvalue = dv::object(); + } + lvalue = &(*lvalue)[name]; + if (peek() != '.') { + break; + } + pos++; + } + return *lvalue; + } + + void readSection(dv::value& map, dv::value& root) { while (hasNext()) { skipWhitespace(); if (!hasNext()) { @@ -119,15 +116,15 @@ class TomlReader : BasicParser { } char c = nextChar(); if (c == '[') { - std::string name = parseName(); - pos++; - readSection(name, getSection(name)); + dv::value& section = parseLValue(root); + expect(']'); + readSection(section, root); return; } pos--; - std::string name = parseName(); + dv::value& lvalue = parseLValue(map); expect('='); - map[name] = parseValue(); + lvalue = parseValue(); expectNewLine(); } } @@ -141,7 +138,7 @@ public: if (!hasNext()) { return std::move(root); } - readSection("", root); + readSection(root, root); return std::move(root); } }; @@ -150,6 +147,7 @@ void toml::parse( SettingsHandler& handler, std::string_view file, std::string_view source ) { auto map = parse(file, source); + for (const auto& [sectionName, sectionMap] : map.asObject()) { if (!sectionMap.isObject()) { continue; diff --git a/src/data/dv.cpp b/src/data/dv.cpp index 5709fd84..16502c04 100644 --- a/src/data/dv.cpp +++ b/src/data/dv.cpp @@ -157,5 +157,5 @@ namespace dv { #include "coders/json.hpp" std::ostream& operator<<(std::ostream& stream, const dv::value& value) { - return stream << json::stringify(value, false); + return stream << json::stringify(value, true); } diff --git a/src/engine.cpp b/src/engine.cpp index 787a257b..8a3b3147 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -9,6 +9,7 @@ #include "coders/imageio.hpp" #include "coders/json.hpp" #include "coders/toml.hpp" +#include "coders/commons.hpp" #include "content/Content.hpp" #include "content/ContentBuilder.hpp" #include "content/ContentLoader.hpp" @@ -125,7 +126,12 @@ void Engine::loadSettings() { if (fs::is_regular_file(settings_file)) { logger.info() << "loading settings"; std::string text = files::read_string(settings_file); - toml::parse(settingsHandler, settings_file.string(), text); + try { + toml::parse(settingsHandler, settings_file.string(), text); + } catch (const parsing_error& err) { + logger.error() << err.errorLog(); + throw; + } } } diff --git a/src/window/Events.cpp b/src/window/Events.cpp index 67c305cf..092e4a0d 100644 --- a/src/window/Events.cpp +++ b/src/window/Events.cpp @@ -196,22 +196,25 @@ void Events::loadBindings( const std::string& filename, const std::string& source ) { auto map = toml::parse(filename, source); - for (auto& [key, value] : map.asObject()) { - auto [prefix, codename] = util::split_at(value.asString(), ':'); - inputtype type; - int code; - if (prefix == "key") { - type = inputtype::keyboard; - code = static_cast(input_util::keycode_from(codename)); - } else if (prefix == "mouse") { - type = inputtype::mouse; - code = static_cast(input_util::mousecode_from(codename)); - } else { - logger.error() - << "unknown input type: " << prefix << " (binding " - << util::quote(key) << ")"; - continue; + for (auto& [sectionName, section] : map.asObject()) { + for (auto& [name, value] : section.asObject()) { + auto key = sectionName + "." + name; + auto [prefix, codename] = util::split_at(value.asString(), ':'); + inputtype type; + int code; + if (prefix == "key") { + type = inputtype::keyboard; + code = static_cast(input_util::keycode_from(codename)); + } else if (prefix == "mouse") { + type = inputtype::mouse; + code = static_cast(input_util::mousecode_from(codename)); + } else { + logger.error() + << "unknown input type: " << prefix << " (binding " + << util::quote(key) << ")"; + continue; + } + Events::bind(key, type, code); } - Events::bind(key, type, code); } }