diff --git a/src/coders/commons.cpp b/src/coders/commons.cpp index 8f2ca6cd..5c05626b 100644 --- a/src/coders/commons.cpp +++ b/src/coders/commons.cpp @@ -108,6 +108,10 @@ bool BasicParser::hasNext() { return pos < source.length(); } +size_t BasicParser::remain() const { + return source.length() - pos; +} + bool BasicParser::isNext(const std::string& substring) { if (source.length() - pos < substring.length()) { return false; @@ -357,36 +361,16 @@ std::string BasicParser::parseString(char quote, bool closeRequired) { continue; } switch (c) { - case 'n': - ss << '\n'; - break; - case 'r': - ss << '\r'; - break; - case 'b': - ss << '\b'; - break; - case 't': - ss << '\t'; - break; - case 'f': - ss << '\f'; - break; - case '\'': - ss << '\\'; - break; - case '"': - ss << '"'; - break; - case '\\': - ss << '\\'; - break; - case '/': - ss << '/'; - break; - case '\n': - pos++; - continue; + case 'n': ss << '\n'; break; + case 'r': ss << '\r'; break; + case 'b': ss << '\b'; break; + case 't': ss << '\t'; break; + case 'f': ss << '\f'; break; + case '\'': ss << '\\'; break; + case '"': ss << '"'; break; + case '\\': ss << '\\'; break; + case '/': ss << '/'; break; + case '\n': pos++; continue; default: throw error( "'\\" + std::string({c}) + "' is an illegal escape" diff --git a/src/coders/commons.hpp b/src/coders/commons.hpp index 952ea316..abd4c01a 100644 --- a/src/coders/commons.hpp +++ b/src/coders/commons.hpp @@ -109,6 +109,7 @@ public: std::string parseName(); std::string parseXmlName(); bool hasNext(); + size_t remain() const; char peek(); char peekInLine(); char peekNoJump(); diff --git a/src/coders/toml.cpp b/src/coders/toml.cpp index 1bbb52aa..08e65720 100644 --- a/src/coders/toml.cpp +++ b/src/coders/toml.cpp @@ -26,6 +26,51 @@ class TomlReader : BasicParser { } } + std::string parseMultilineString() { + pos += 2; + char next = peek(); + + std::stringstream ss; + while (hasNext()) { + char c = source[pos]; + if (c == '"' && remain() >= 2 && + source[pos+1] == '"' && + source[pos+2] == '"') { + pos += 3; + return ss.str(); + } + if (c == '\\') { + pos++; + c = nextChar(); + if (c >= '0' && c <= '7') { + pos--; + ss << (char)parseSimpleInt(8); + continue; + } + switch (c) { + case 'n': ss << '\n'; break; + case 'r': ss << '\r'; break; + case 'b': ss << '\b'; break; + case 't': ss << '\t'; break; + case 'f': ss << '\f'; break; + case '\'': ss << '\\'; break; + case '"': ss << '"'; break; + case '\\': ss << '\\'; break; + case '/': ss << '/'; break; + case '\n': pos++; continue; + default: + throw error( + "'\\" + std::string({c}) + "' is an illegal escape" + ); + } + continue; + } + ss << c; + pos++; + } + throw error("unexpected end"); + } + dv::value parseValue() { char c = peek(); if (is_digit(c)) { @@ -53,6 +98,12 @@ class TomlReader : BasicParser { throw error("unknown keyword " + util::quote(keyword)); } else if (c == '"' || c == '\'') { pos++; + if (remain() >= 2 && + c == '"' && + source[pos] == '"' && + source[pos+1] == '"') { + return parseMultilineString(); + } return parseString(c); } else if (c == '[') { // parse array @@ -125,7 +176,7 @@ class TomlReader : BasicParser { dv::value& lvalue = parseLValue(map); expect('='); lvalue = parseValue(); - expectNewLine(); + skipWhitespace(); } } public: diff --git a/test/coders/toml.cpp b/test/coders/toml.cpp index 369a20cf..f9c189db 100644 --- a/test/coders/toml.cpp +++ b/test/coders/toml.cpp @@ -7,32 +7,6 @@ #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; @@ -75,8 +49,35 @@ TEST(TOML, EncodeDecode) { } } +// Modified 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, ExampleCode) { try { + std::cout << SRC_EXAMPLE << std::endl; auto object = toml::parse("", SRC_EXAMPLE); std::cout << object << std::endl; } catch (const parsing_error& err) {