feat: toml arrays and inline tables support

This commit is contained in:
MihailRis 2024-10-11 20:43:37 +03:00
parent b984e363ea
commit 66825d62aa
3 changed files with 150 additions and 24 deletions

View File

@ -52,6 +52,65 @@ class TomlReader : BasicParser {
} while (true);
}
dv::value parseValue() {
char c = peek();
if (is_digit(c)) {
return parseNumber(1);
} else if (c == '-' || c == '+') {
int sign = c == '-' ? -1 : 1;
pos++;
// parse numeric literal
auto value = parseNumber(sign);
if (hasNext() && peekNoJump() == '-') {
// parse timestamp // TODO: implement
throw error("timestamps support is not implemented yet");
}
return value;
} else if (is_identifier_start(c)) {
// parse keywords
std::string keyword = parseName();
if (keyword == "true" || keyword == "false") {
return keyword == "true";
} else if (keyword == "inf") {
return INFINITY;
} else if (keyword == "nan") {
return NAN;
}
throw error("unknown keyword " + util::quote(keyword));
} else if (c == '"' || c == '\'') {
pos++;
return parseString(c);
} else if (c == '[') {
// parse array
pos++;
dv::list_t values;
while (peek() != ']') {
values.push_back(parseValue());
if (peek() != ']') {
expect(',');
}
}
pos++;
return dv::value(std::move(values));
} else if (c == '{') {
// parse inline table
pos++;
auto table = dv::object();
while (peek() != '}') {
auto key = parseName();
expect('=');
table[key] = parseValue();
if (peek() != '}') {
expect(',');
}
}
pos++;
return table;
} else {
throw error("feature is not supported");
}
}
void readSection(const std::string& section, dv::value& map) {
while (hasNext()) {
skipWhitespace();
@ -68,35 +127,13 @@ class TomlReader : BasicParser {
pos--;
std::string name = parseName();
expect('=');
c = peek();
if (is_digit(c)) {
map[name] = parseNumber(1);
} else if (c == '-' || c == '+') {
int sign = c == '-' ? -1 : 1;
pos++;
map[name] = parseNumber(sign);
} else if (is_identifier_start(c)) {
std::string identifier = parseName();
if (identifier == "true" || identifier == "false") {
map[name] = identifier == "true";
} else if (identifier == "inf") {
map[name] = INFINITY;
} else if (identifier == "nan") {
map[name] = NAN;
}
} else if (c == '"' || c == '\'') {
pos++;
map[name] = parseString(c);
} else {
throw error("feature is not supported");
}
map[name] = parseValue();
expectNewLine();
}
}
public:
TomlReader(std::string_view file, std::string_view source)
: BasicParser(file, source) {
root = dv::object();
: BasicParser(file, source), root(dv::object()) {
}
dv::value read() {

View File

@ -196,6 +196,9 @@ namespace dv {
value(std::shared_ptr<objects::Bytes> v) noexcept {
this->operator=(std::move(v));
}
value(list_t values) {
this->operator=(std::make_shared<list_t>(std::move(values)));
}
value(const value& v) noexcept : type(value_type::none) {
this->operator=(v);

86
test/coders/toml.cpp Normal file
View File

@ -0,0 +1,86 @@
#include "coders/toml.hpp"
#include <gtest/gtest.h>
#include "data/dv.hpp"
#include "util/stringutil.hpp"
#include "util/Buffer.hpp"
#include "coders/commons.hpp"
// Example from toml.io
inline std::string SRC_EXAMPLE =
"# This is a TOML document\n"
"\n"
"title = \"TOML Example\"\n"
"\n"
"[owner]\n"
"name = \"Tom Preston-Werner\"\n"
// "dob = 1979-05-27T07:32:00-08:00\n"
"\n"
"[database]\n"
"enabled = true\n"
"ports = [ 8000, 8001, 8002 ]\n"
"data = [ [\"delta\", \"phi\"], [3.14] ]\n"
"temp_targets = { cpu = 79.5, case = 72.0 }\n"
"\n"
"[servers]\n"
"\n"
"[servers.alpha]\n"
"ip = \"10.0.0.1\"\n"
"role = \"frontend\"\n"
"\n"
"[servers.beta]\n"
"ip = \"10.0.0.2\"\n"
"role = \"backend\"";
TEST(TOML, EncodeDecode) {
const std::string name = "TOML-encoder";
const int bytesSize = 20;
const int year = 2019;
const float score = 3.141592;
const bool visible = true;
dv::objects::Bytes srcBytes(bytesSize);
for (int i = 0; i < bytesSize; i ++) {
srcBytes[i] = rand();
}
std::string text;
{
auto object = dv::object();
object["name"] = name;
object["year"] = year;
object["score"] = score;
object["visible"] = visible;
object["data"] = srcBytes;
text = toml::stringify(object, "");
std::cout << text << std::endl;
}
try {
auto object = toml::parse("<string>", text);
EXPECT_EQ(object["name"].asString(), name);
EXPECT_EQ(object["year"].asInteger(), year);
EXPECT_FLOAT_EQ(object["score"].asNumber(), score);
EXPECT_EQ(object["visible"].asBoolean(), visible);
auto b64string = object["data"].asString();
auto bytes = util::base64_decode(b64string);
EXPECT_EQ(bytes.size(), bytesSize);
for (int i = 0; i < bytesSize; i++) {
EXPECT_EQ(bytes[i], srcBytes[i]);
}
} catch (const parsing_error& err) {
std::cerr << err.errorLog() << std::endl;
throw;
}
}
TEST(TOML, ExampleCode) {
try {
auto object = toml::parse("<string>", SRC_EXAMPLE);
std::cout << object << std::endl;
} catch (const parsing_error& err) {
std::cerr << err.errorLog() << std::endl;
throw;
}
}