From 2aea19febdc6588c6e5a47564bdf1ea7f93085e7 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 23 Mar 2025 08:49:09 +0300 Subject: [PATCH 1/5] add YAML parser --- src/coders/BasicParser.hpp | 6 +- src/coders/BasicParser.inl | 45 +++++- src/coders/json.cpp | 9 +- src/coders/toml.cpp | 11 +- src/coders/yaml.cpp | 318 +++++++++++++++++++++++++++++++++++++ src/coders/yaml.hpp | 10 ++ src/frontend/locale.cpp | 10 +- test/coders/yaml.cpp | 22 +++ 8 files changed, 406 insertions(+), 25 deletions(-) create mode 100644 src/coders/yaml.cpp create mode 100644 src/coders/yaml.hpp create mode 100644 test/coders/yaml.cpp diff --git a/src/coders/BasicParser.hpp b/src/coders/BasicParser.hpp index e3a3dda8..ba1cf5a8 100644 --- a/src/coders/BasicParser.hpp +++ b/src/coders/BasicParser.hpp @@ -7,16 +7,20 @@ template class BasicParser { using StringT = std::basic_string; using StringViewT = std::basic_string_view; + + void skipWhitespaceHashComment(bool newline = true); protected: std::string_view filename; StringViewT source; uint pos = 0; uint line = 1; uint linestart = 0; + bool hashComment = false; - virtual void skipWhitespace(); + void skipWhitespace(bool newline = true); void skip(size_t n); void skipLine(); + void skipEmptyLines(); bool skipTo(const StringT& substring); void expect(CharT expected); void expect(const StringT& substring); diff --git a/src/coders/BasicParser.inl b/src/coders/BasicParser.inl index 2d15d8e9..72f644ba 100644 --- a/src/coders/BasicParser.inl +++ b/src/coders/BasicParser.inl @@ -31,10 +31,17 @@ namespace { } template -void BasicParser::skipWhitespace() { +void BasicParser::skipWhitespace(bool newline) { + if (hashComment) { + skipWhitespaceHashComment(newline); + return; + } while (hasNext()) { char next = source[pos]; if (next == '\n') { + if (!newline) { + break; + } line++; linestart = ++pos; continue; @@ -47,6 +54,36 @@ void BasicParser::skipWhitespace() { } } +template +void BasicParser::skipWhitespaceHashComment(bool newline) { + while (hasNext()) { + char next = source[pos]; + if (next == '\n') { + if (!newline) { + break; + } + line++; + linestart = ++pos; + continue; + } + if (is_whitespace(next)) { + pos++; + } else { + break; + } + } + if (hasNext() && source[pos] == '#') { + if (!newline) { + readUntilEOL(); + return; + } + skipLine(); + if (hasNext() && (is_whitespace(source[pos]) || source[pos] == '#')) { + skipWhitespaceHashComment(newline); + } + } +} + template void BasicParser::skip(size_t n) { n = std::min(n, source.length() - pos); @@ -73,6 +110,12 @@ void BasicParser::skipLine() { } } +template +void BasicParser::skipEmptyLines() { + skipWhitespace(); + pos = linestart; +} + template bool BasicParser::skipTo(const std::basic_string& substring) { size_t idx = source.find(substring, pos); diff --git a/src/coders/json.cpp b/src/coders/json.cpp index 19110f90..bbc56360 100644 --- a/src/coders/json.cpp +++ b/src/coders/json.cpp @@ -13,13 +13,14 @@ using namespace json; namespace { class Parser : BasicParser { - dv::value parseList(); - dv::value parseObject(); - dv::value parseValue(); - public: + public: Parser(std::string_view filename, std::string_view source); dv::value parse(); + private: + dv::value parseList(); + dv::value parseObject(); + dv::value parseValue(); }; } diff --git a/src/coders/toml.cpp b/src/coders/toml.cpp index 8fe05391..1652745a 100644 --- a/src/coders/toml.cpp +++ b/src/coders/toml.cpp @@ -16,16 +16,6 @@ using namespace toml; class TomlReader : BasicParser { dv::value root; - void skipWhitespace() override { - BasicParser::skipWhitespace(); - if (hasNext() && source[pos] == '#') { - skipLine(); - if (hasNext() && is_whitespace(peek())) { - skipWhitespace(); - } - } - } - // modified version of BaseParser.parseString // todo: extract common part std::string parseMultilineString() { @@ -214,6 +204,7 @@ class TomlReader : BasicParser { public: TomlReader(std::string_view file, std::string_view source) : BasicParser(file, source), root(dv::object()) { + hashComment = true; } dv::value read() { diff --git a/src/coders/yaml.cpp b/src/coders/yaml.cpp new file mode 100644 index 00000000..a7a1101f --- /dev/null +++ b/src/coders/yaml.cpp @@ -0,0 +1,318 @@ +#include "yaml.hpp" +#include "BasicParser.hpp" + +using namespace yaml; + +namespace { + enum Chomping { + CLIP, STRIP, KEEP + }; + + class Parser : BasicParser { + public: + Parser(std::string_view filename, std::string_view source); + + dv::value parseValue(); + dv::value parseFullValue(int indent); + dv::value parseArray(int indent = 0); + dv::value parseObject(dv::value&& object, int indent = 0); + dv::value parseInlineArray(); + dv::value parseInlineObject(); + private: + int countIndent(); + bool expectIndent(int indent); + std::string_view readYamlIdentifier(); + std::string readMultilineString(int indent, bool eols, Chomping chomp); + }; +} + +inline bool is_yaml_identifier_char(int c) { + return c > 20 && c != ':' && c != ' ' && c != '\n' && c != '\r' && + c != '\t' && c != '\f' && c != '\v'; +} + +static dv::value perform_literal(std::string_view literal) { + if (literal == "true" || literal == "True" || + literal == "false" || literal == "False") { + return literal[0] == 't'; + } + if (literal == "null" || literal == "Null") { + return nullptr; + } + return std::string(literal); +} + +Parser::Parser(std::string_view filename, std::string_view source) + : BasicParser(filename, source) { + hashComment = true; +} + +bool Parser::expectIndent(int required) { + int indent = 0; + while (hasNext() && source[pos] == ' ' && indent < required) { + indent++; + pos++; + } + return indent >= required; +} + +std::string Parser::readMultilineString(int indent, bool eols, Chomping chomp) { + int next_indent = countIndent(); + if (next_indent <= indent) { + throw error("indentation error"); + } + std::stringstream ss; + ss << readUntilEOL(); + if (hasNext()) { + skip(1); + } + int trailingEmpties = 0; + while (true) { + while (expectIndent(next_indent)) { + trailingEmpties = 0; + ss << (eols ? '\n' : ' '); + ss << readUntilEOL(); + if (hasNext()) { + skip(1); + } + } + while (true) { + skipWhitespace(false); + if (!hasNext() || source[pos] != '\n') { + break; + } + skip(1); + trailingEmpties++; + } + if (!expectIndent(next_indent)) { + break; + } + pos = linestart; + } + if (chomp == KEEP) { + for (int i = 0; i < trailingEmpties - 1; i++) { + ss << (eols ? '\n' : ' '); + } + } + ss << '\n'; + + pos = linestart; + + auto string = ss.str(); + if (chomp == STRIP) { + util::trim(string); + } + return string; +} + +std::string_view Parser::readYamlIdentifier() { + char c = peek(); + if (!is_yaml_identifier_char(c)) { + throw error("identifier expected"); + } + int start = pos; + while (hasNext() && is_yaml_identifier_char(source[pos])) { + pos++; + } + return source.substr(start, pos - start); +} + +int Parser::countIndent() { + int indent = 0; + while (hasNext() && source[pos] == ' ') { + indent++; + pos++; + } + return indent; +} + +dv::value Parser::parseValue() { + char c = peek(); + if (is_digit(c)) { + return parseNumber(1); + } else if (c == '-' || c == '+') { + skip(1); + return parseNumber(c == '-' ? -1 : 1); + } else if (c == '"' || c == '\'') { + skip(1); + return parseString(c, true); + } else if (c == '[') { + return parseInlineArray(); + } else if (c == '{') { + return parseInlineObject(); + } else { + return perform_literal(readUntilEOL()); + } + throw error("unexpected character"); +} + +dv::value Parser::parseInlineArray() { + expect('['); + auto list = dv::list(); + while (peek() != ']') { + if (peek() == '#') { + skipLine(); + continue; + } + list.add(parseValue()); + + char next = peek(); + if (next == ',') { + pos++; + } else if (next == ']') { + break; + } else { + throw error("',' expected"); + } + } + pos++; + return list; +} + +dv::value Parser::parseInlineObject() { + expect('{'); + dv::value object = dv::object(); + while (peek() != '}') { + if (peek() == '#') { + skipLine(); + continue; + } + auto name = readYamlIdentifier(); + expect(':'); + object[std::string(name)] = parseValue(); + + char next = peek(); + if (next == ',') { + pos++; + } else if (next == '}') { + break; + } else { + throw error("',' expected"); + } + } + pos++; + return object; +} + +dv::value Parser::parseFullValue(int indent) { + dv::value value; + char c = source[pos]; + if (c == '\n') { + skip(1); + skipEmptyLines(); + int init_pos = pos; + int next_indent = countIndent(); + if (next_indent < indent) { + throw error("indentation error"); + } + if (source[pos] == '-') { + pos = init_pos; + return parseArray(next_indent); + } else { + pos = init_pos; + return parseObject(dv::object(), next_indent); + } + } else if (is_digit(c)) { + return parseNumber(1); + } else if (c == '-' || c == '+') { + skip(1); + return parseNumber(c == '-' ? -1 : 1); + } else if (c == '"' || c == '\'') { + skip(1); + return parseString(c, true); + } else if (c == '[') { + return parseInlineArray(); + } else if (c == '{') { + return parseInlineObject(); + } else if (c == '|' || c == '>') { + skip(1); + Chomping chomp = CLIP; + if (source[pos] == '-' || source[pos] == '+') { + chomp = source[pos] == '-' ? STRIP : KEEP; + skip(1); + } + skipWhitespace(false); + expectNewLine(); + return readMultilineString(indent, c == '|', chomp); + } else { + return perform_literal(readUntilEOL()); + } +} + +dv::value Parser::parseArray(int indent) { + dv::value list = dv::list(); + + while (hasNext()) { + skipEmptyLines(); + int next_indent = countIndent(); + if (next_indent < indent) { + pos = linestart; + break; + } + expect('-'); + skipWhitespace(); + size_t nlpos = source.find('\n', pos); + size_t colonpos = source.find(':', pos); + if (nlpos == std::string::npos && colonpos == std::string::npos) { + list.add(perform_literal(readUntilEOL())); + break; + } + if (nlpos < colonpos) { + list.add(parseFullValue(next_indent)); + skipLine(); + } else { + auto name = readYamlIdentifier(); + expect(':'); + skipWhitespace(false); + dv::value object = dv::object(); + object[std::string(name)] = parseFullValue(next_indent); + skipEmptyLines(); + next_indent = countIndent(); + if (next_indent > indent) { + pos = linestart; + object = parseObject(std::move(object), next_indent); + } else { + pos = linestart; + } + list.add(std::move(object)); + } + } + return list; +} + +dv::value Parser::parseObject(dv::value&& object, int indent) { + skipEmptyLines(); + while (hasNext()) { + size_t prev_pos = pos; + int next_indent = countIndent(); + if (source[pos] == '\n') { + skip(1); + continue; + } + if (next_indent < indent) { + pos = prev_pos; + break; + } + char c = peek(); + if (!is_yaml_identifier_char(c)) { + if (!is_whitespace(c)) { + throw error("invalid character"); + } + continue; + } + auto name = readYamlIdentifier(); + expect(':'); + skipWhitespace(false); + object[std::string(name)] = parseFullValue(indent); + skipEmptyLines(); + } + return object; +} + +dv::value yaml::parse(std::string_view filename, std::string_view source) { + return Parser(filename, source).parseObject(dv::object()); +} + +dv::value yaml::parse(std::string_view source) { + return parse("[string]", source); +} diff --git a/src/coders/yaml.hpp b/src/coders/yaml.hpp new file mode 100644 index 00000000..2802e523 --- /dev/null +++ b/src/coders/yaml.hpp @@ -0,0 +1,10 @@ +#pragma once + +#include + +#include "data/dv.hpp" + +namespace yaml { + dv::value parse(std::string_view filename, std::string_view source); + dv::value parse(std::string_view source); +} diff --git a/src/frontend/locale.cpp b/src/frontend/locale.cpp index 669d0b4c..4e1812dd 100644 --- a/src/frontend/locale.cpp +++ b/src/frontend/locale.cpp @@ -40,18 +40,10 @@ const std::string& langs::Lang::getId() const { /// @brief Language key-value txt files parser namespace { class Reader : BasicParser { - void skipWhitespace() override { - BasicParser::skipWhitespace(); - if (hasNext() && source[pos] == '#') { - skipLine(); - if (hasNext() && is_whitespace(peek())) { - skipWhitespace(); - } - } - } public: Reader(std::string_view file, std::string_view source) : BasicParser(file, source) { + hashComment = true; } void read(langs::Lang& lang, const std::string &prefix) { diff --git a/test/coders/yaml.cpp b/test/coders/yaml.cpp new file mode 100644 index 00000000..272b3b0e --- /dev/null +++ b/test/coders/yaml.cpp @@ -0,0 +1,22 @@ +#include + +#include "coders/yaml.hpp" +#include "coders/json.hpp" +#include "coders/commons.hpp" + +#include "io/io.hpp" +#include "io/devices/StdfsDevice.hpp" + +namespace fs = std::filesystem; + +TEST(YAML, EncodeDecode) { + io::set_device("root", std::make_shared(fs::u8path("../../"))); + auto filename = "root:.github/workflows/windows-clang.yml"; + try { + auto value = yaml::parse(io::read_string(filename)); + std::cout << json::stringify(value, true) << std::endl; + } catch (const parsing_error& error) { + std::cerr << error.errorLog() << std::endl; + throw error; + } +} From 396fab02b32ac75eaabf77c0b9d38e09cd2a816d Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 24 Mar 2025 05:30:56 +0300 Subject: [PATCH 2/5] add yaml::stringify --- src/coders/yaml.cpp | 141 ++++++++++++++++++++++++++++++++++++++++ src/coders/yaml.hpp | 2 + src/util/stringutil.hpp | 25 +++++++ test/coders/yaml.cpp | 2 +- 4 files changed, 169 insertions(+), 1 deletion(-) diff --git a/src/coders/yaml.cpp b/src/coders/yaml.cpp index a7a1101f..a9944ea3 100644 --- a/src/coders/yaml.cpp +++ b/src/coders/yaml.cpp @@ -1,6 +1,8 @@ #include "yaml.hpp" #include "BasicParser.hpp" +#include + using namespace yaml; namespace { @@ -316,3 +318,142 @@ dv::value yaml::parse(std::string_view filename, std::string_view source) { dv::value yaml::parse(std::string_view source) { return parse("[string]", source); } + +static void add_indent(std::stringstream& ss, int indent) { + for (int i = 0; i < indent; i++) { + ss << " "; + } +} + +static void insert_string( + std::stringstream& ss, const std::string& string, int indent +) { + bool has_spec_chars = false; + bool multiline = false; + for (char c : string) { + if (c < ' ' || c == '"' || c == '\'') { + has_spec_chars = true; + if (multiline) { + break; + } + } + if (c == '\n') { + multiline = true; + break; + } + } + if (multiline) { + ss << "|-\n"; + size_t offset = 0; + size_t newoffset = 0; + + do { + offset = newoffset; + if (offset == string.length() - 1 && string[offset] == '\n') { + break; + } + add_indent(ss, indent); + newoffset = string.find('\n', offset + 1); + if (newoffset == std::string::npos) { + ss << string.substr(offset); + break; + } else { + ss << string.substr(offset + 1, newoffset - offset - 1); + } + ss << '\n'; + } while (true); + } else { + if (has_spec_chars || string.empty()) { + ss << util::escape(string, false); + } else { + ss << string; + } + } +} + +static void to_string( + std::stringstream& ss, + const dv::value& value, + int indent, + bool eliminateIndent = false +) { + using dv::value_type; + + switch (value.getType()) { + case value_type::string: + insert_string(ss, value.asString(), indent); + break; + case value_type::number: + ss << std::setprecision(15) << value.asNumber(); + break; + case value_type::integer: + ss << value.asInteger(); + break; + case value_type::boolean: + ss << (value.asBoolean() ? "true" : "false"); + break; + case value_type::none: + ss << "null"; + break; + case value_type::object: { + if (value.empty()) { + ss << "{}"; + break; + } + bool first = true; + for (const auto& [key, elem] : value.asObject()) { + if (!first) { + ss << '\n'; + } + if (!eliminateIndent) { + add_indent(ss, indent); + } else { + eliminateIndent = false; + } + ss << key << ": "; + if ((elem.isObject() || elem.isList()) && !elem.empty()) { + ss << "\n"; + to_string(ss, elem, indent + 1); + } else { + to_string(ss, elem, indent + 1); + } + first = false; + } + break; + } + case value_type::list: { + if (value.empty()) { + ss << "[]"; + break; + } + bool first = true; + for (const auto& elem : value) { + if (!first) { + ss << '\n'; + } + if (!eliminateIndent) { + add_indent(ss, indent); + } else { + eliminateIndent = false; + } + ss << "- "; + to_string(ss, elem, indent + 1, true); + first = false; + } + break; + } + case value_type::bytes: { + const auto& bytes = value.asBytes(); + auto b64 = util::base64_encode(bytes.data(), bytes.size()); + b64 = util::join(util::split_by_n(b64, 64), '\n'); + insert_string(ss, b64, indent); + break; + } + } +} + +std::string yaml::stringify(const dv::value& value) { + std::stringstream ss; + to_string(ss, value, 0); + return ss.str(); +} diff --git a/src/coders/yaml.hpp b/src/coders/yaml.hpp index 2802e523..3d871855 100644 --- a/src/coders/yaml.hpp +++ b/src/coders/yaml.hpp @@ -7,4 +7,6 @@ namespace yaml { dv::value parse(std::string_view filename, std::string_view source); dv::value parse(std::string_view source); + + std::string stringify(const dv::value& value); } diff --git a/src/util/stringutil.hpp b/src/util/stringutil.hpp index d85f93cb..3d8bf16e 100644 --- a/src/util/stringutil.hpp +++ b/src/util/stringutil.hpp @@ -115,4 +115,29 @@ namespace util { std::string format_data_size(size_t size); std::pair split_at(std::string_view view, char c); + + template + std::vector> split_by_n( + const std::basic_string& str, size_t n + ) { + std::vector> result; + for (size_t i = 0; i < str.length(); i += n) { + result.push_back(str.substr(i, n)); + } + return result; + } + + template + std::basic_string join( + const std::vector>& strings, CharT delimiter + ) { + std::basic_stringstream ss; + for (size_t i = 0; i < strings.size(); ++i) { + ss << strings[i]; + if (i != strings.size() - 1) { + ss << delimiter; + } + } + return ss.str(); + } } diff --git a/test/coders/yaml.cpp b/test/coders/yaml.cpp index 272b3b0e..0401bb19 100644 --- a/test/coders/yaml.cpp +++ b/test/coders/yaml.cpp @@ -14,7 +14,7 @@ TEST(YAML, EncodeDecode) { auto filename = "root:.github/workflows/windows-clang.yml"; try { auto value = yaml::parse(io::read_string(filename)); - std::cout << json::stringify(value, true) << std::endl; + std::cout << yaml::stringify(value) << std::endl; } catch (const parsing_error& error) { std::cerr << error.errorLog() << std::endl; throw error; From c28bfae679d267c68b0de06d6cfe35afddc53a31 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 24 Mar 2025 07:43:41 +0300 Subject: [PATCH 3/5] add yaml serialization library --- doc/en/scripting.md | 2 +- doc/en/scripting/filesystem.md | 16 ++++++++++++++++ doc/ru/scripting.md | 2 +- doc/ru/scripting/filesystem.md | 16 ++++++++++++++++ src/logic/scripting/lua/libs/api_lua.hpp | 3 ++- src/logic/scripting/lua/libs/libjson.cpp | 3 ++- src/logic/scripting/lua/libs/libyaml.cpp | 20 ++++++++++++++++++++ src/logic/scripting/lua/lua_engine.cpp | 1 + 8 files changed, 59 insertions(+), 4 deletions(-) create mode 100644 src/logic/scripting/lua/libs/libyaml.cpp diff --git a/doc/en/scripting.md b/doc/en/scripting.md index 471bdb15..f205a237 100644 --- a/doc/en/scripting.md +++ b/doc/en/scripting.md @@ -11,7 +11,7 @@ Subsections: - [Libraries](#) - [app](scripting/builtins/libapp.md) - [base64](scripting/builtins/libbase64.md) - - [bjson, json, toml](scripting/filesystem.md) + - [bjson, json, toml, yaml](scripting/filesystem.md) - [block](scripting/builtins/libblock.md) - [byteutil](scripting/builtins/libbyteutil.md) - [cameras](scripting/builtins/libcameras.md) diff --git a/doc/en/scripting/filesystem.md b/doc/en/scripting/filesystem.md index b37c799f..8341e966 100644 --- a/doc/en/scripting/filesystem.md +++ b/doc/en/scripting/filesystem.md @@ -36,6 +36,22 @@ toml.parse(code: str) -> table Parses a TOML string into a table. +## *yaml* library + +The library contains functions for serializing and deserializing tables: + +```python +yaml.tostring(object: table) -> str +``` + +Serializes an object into a YAML string. + +```python +yaml.parse(code: str) -> table +``` + +Parses a YAML string into a table. + ## *bjson* library The library contains functions for working with the binary data exchange format [vcbjson](../../specs/binary_json_spec.md). diff --git a/doc/ru/scripting.md b/doc/ru/scripting.md index 942241b6..79e53450 100644 --- a/doc/ru/scripting.md +++ b/doc/ru/scripting.md @@ -11,7 +11,7 @@ - [Библиотеки](#) - [app](scripting/builtins/libapp.md) - [base64](scripting/builtins/libbase64.md) - - [bjson, json, toml](scripting/filesystem.md) + - [bjson, json, toml, yaml](scripting/filesystem.md) - [block](scripting/builtins/libblock.md) - [byteutil](scripting/builtins/libbyteutil.md) - [cameras](scripting/builtins/libcameras.md) diff --git a/doc/ru/scripting/filesystem.md b/doc/ru/scripting/filesystem.md index faaaf9b7..10d82c60 100644 --- a/doc/ru/scripting/filesystem.md +++ b/doc/ru/scripting/filesystem.md @@ -36,6 +36,22 @@ toml.parse(code: str) -> table Парсит TOML строку в таблицу. +## Библиотека yaml + +Библиотека содержит функции для сериализации и десериализации таблиц: + +```python +yaml.tostring(object: table) -> str +``` + +Сериализует объект в YAML строку. + +```python +yaml.parse(code: str) -> table +``` + +Парсит YAML строку в таблицу. + ## Библиотека bjson Библиотека содержит функции для работы с двоичным форматом обмена данными [vcbjson](../../specs/binary_json_spec.md). diff --git a/src/logic/scripting/lua/libs/api_lua.hpp b/src/logic/scripting/lua/libs/api_lua.hpp index 818b9c30..39d09241 100644 --- a/src/logic/scripting/lua/libs/api_lua.hpp +++ b/src/logic/scripting/lua/libs/api_lua.hpp @@ -14,6 +14,7 @@ /// int l_function_name(lua::State* L); // Libraries +extern const luaL_Reg applib[]; extern const luaL_Reg audiolib[]; extern const luaL_Reg base64lib[]; extern const luaL_Reg bjsonlib[]; @@ -38,7 +39,6 @@ extern const luaL_Reg packlib[]; extern const luaL_Reg particleslib[]; // gfx.particles extern const luaL_Reg playerlib[]; extern const luaL_Reg quatlib[]; -extern const luaL_Reg applib[]; extern const luaL_Reg text3dlib[]; // gfx.text3d extern const luaL_Reg timelib[]; extern const luaL_Reg tomllib[]; @@ -48,6 +48,7 @@ extern const luaL_Reg vec3lib[]; // vecn.cpp extern const luaL_Reg vec4lib[]; // vecn.cpp extern const luaL_Reg weatherlib[]; extern const luaL_Reg worldlib[]; +extern const luaL_Reg yamllib[]; // Components extern const luaL_Reg skeletonlib[]; diff --git a/src/logic/scripting/lua/libs/libjson.cpp b/src/logic/scripting/lua/libs/libjson.cpp index 503173bd..ed929d3d 100644 --- a/src/logic/scripting/lua/libs/libjson.cpp +++ b/src/logic/scripting/lua/libs/libjson.cpp @@ -19,4 +19,5 @@ static int l_json_parse(lua::State* L) { const luaL_Reg jsonlib[] = { {"tostring", lua::wrap}, {"parse", lua::wrap}, - {NULL, NULL}}; + {NULL, NULL} +}; diff --git a/src/logic/scripting/lua/libs/libyaml.cpp b/src/logic/scripting/lua/libs/libyaml.cpp new file mode 100644 index 00000000..3b625ddb --- /dev/null +++ b/src/logic/scripting/lua/libs/libyaml.cpp @@ -0,0 +1,20 @@ +#include "coders/yaml.hpp" +#include "api_lua.hpp" + +static int l_stringify(lua::State* L) { + auto value = lua::tovalue(L, 1); + auto string = yaml::stringify(value); + return lua::pushstring(L, string); +} + +static int l_parse(lua::State* L) { + auto string = lua::require_string(L, 1); + auto element = yaml::parse("[string]", string); + return lua::pushvalue(L, element); +} + +const luaL_Reg yamllib[] = { + {"tostring", lua::wrap}, + {"parse", lua::wrap}, + {NULL, NULL} +}; diff --git a/src/logic/scripting/lua/lua_engine.cpp b/src/logic/scripting/lua/lua_engine.cpp index ffeaf360..1ae35d39 100644 --- a/src/logic/scripting/lua/lua_engine.cpp +++ b/src/logic/scripting/lua/lua_engine.cpp @@ -56,6 +56,7 @@ static void create_libs(State* L, StateType stateType) { openlib(L, "vec2", vec2lib); openlib(L, "vec3", vec3lib); openlib(L, "vec4", vec4lib); + openlib(L, "yaml", yamllib); if (stateType == StateType::SCRIPT) { openlib(L, "app", applib); From ccccf2254104b3ccd1555de705aa06cf50cdf32d Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 24 Mar 2025 07:57:19 +0300 Subject: [PATCH 4/5] trigger checks From aaf8053b87bb9238b258ee64e17664e3db2589ed Mon Sep 17 00:00:00 2001 From: MihailRis Date: Mon, 24 Mar 2025 08:28:06 +0300 Subject: [PATCH 5/5] fix BasicParser::readUntilEOL --- src/coders/BasicParser.inl | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/coders/BasicParser.inl b/src/coders/BasicParser.inl index 72f644ba..3616b4a1 100644 --- a/src/coders/BasicParser.inl +++ b/src/coders/BasicParser.inl @@ -283,9 +283,12 @@ std::basic_string_view BasicParser::readUntilWhitespace() { template std::basic_string_view BasicParser::readUntilEOL() { int start = pos; - while (hasNext() && source[pos] != '\r' && source[pos] != '\n') { + while (hasNext() && source[pos] != '\n') { pos++; } + if (pos > start && source[pos - 1] == '\r') { + return source.substr(start, pos - start - 1); + } return source.substr(start, pos - start); }