4
\$\begingroup\$

I have reworked the code from my previous (linked) post.

For reference:

I have a string which contains JSON-like object flattened (stringify()ed). I need to JSON-ify it back. It's a custom format, so I wrote function to JSON-ify the string.

Please review it. Also one particular thing that I don't like is previousIsClosingBracket variable. I have added it for handling correct formatting of the last example. Is there a better way to detect such cases and have more beautiful solution?

#include <string> #include <unordered_set> #include <vector> #include <iostream> struct WordAtLevel { std::string word; std::size_t level; }; using tWordLevels = std::vector<WordAtLevel>; class Formatter { public: explicit Formatter(std::string tclString); std::string formatted() const; private: tWordLevels parse() const; static std::string build(tWordLevels wordAtLevels); std::string readUntilDelimiter() const; static bool isClosingBrackets(const std::string &str); static std::string indentation(const std::size_t tabCount); std::string m_tclString; std::size_t m_strLen = 0; mutable std::size_t m_idx = 0; static constexpr int tabWidth = 4; mutable bool m_bPrevWordReserved = false; const std::unordered_set<std::string> m_reservedWords{ "coords", "comment" }; }; Formatter::Formatter(std::string tclString) : m_tclString(std::move(tclString)) , m_strLen(m_tclString.length()) { } std::string Formatter::formatted() const { const tWordLevels wordLevels = parse(); return build(wordLevels); } tWordLevels Formatter::parse() const { tWordLevels wordAtLevels; int level = 0; for (m_idx = 0; m_idx < m_strLen; ++m_idx) { switch (auto ch = m_tclString[m_idx]) { case '}': { if (m_bPrevWordReserved) m_bPrevWordReserved = false; else --level; wordAtLevels.emplace_back("}", level); break; } case '{': wordAtLevels.emplace_back("{", level); if (!m_bPrevWordReserved) ++level; [[fallthrough]]; default: { std::string word = readUntilDelimiter(); if (!word.empty()) { if (m_reservedWords.contains(word)) m_bPrevWordReserved = true; wordAtLevels.emplace_back(word, level); } break; } } } return wordAtLevels; } std::string Formatter::build(tWordLevels wordAtLevels) { std::string result; const auto wordCount = wordAtLevels.size(); bool previousIsClosingBracket = false; for (std::size_t idx = 0; idx < wordCount; ++idx) { const auto &[word, level] = wordAtLevels[idx]; result += indentation(level); result += word; previousIsClosingBracket = isClosingBrackets(word); while (++idx < wordCount) { const auto &[nextWord, nextLevel] = wordAtLevels[idx]; if (nextLevel != level) break; if (previousIsClosingBracket && !isClosingBrackets(nextWord)) break; result += ' '; result += nextWord; } result += '\n'; --idx; } return result; } std::string Formatter::readUntilDelimiter() const { std::string word; ++m_idx; while (m_idx < m_strLen) { const auto ch = m_tclString[m_idx]; if (ch == ' ' || ch == '{' || ch == '}') { --m_idx; break; } ++m_idx; word += ch; } return word; } bool Formatter::isClosingBrackets(const std::string& str) { return str == std::string_view("}"); } std::string Formatter::indentation(const std::size_t tabCount) { return std::string(tabCount * tabWidth, ' '); } int main() { const Formatter f1("{data {a {obj {coords {10 10} comment {} radius 260 type circle}}}}"); std::cout << f1.formatted() << '\n'; const Formatter f2("{data {b {obj {coords {-95 -85 -70 -85 -75 -95} comment {abc} type polygon}}}}"); std::cout << f2.formatted() << '\n'; const Formatter f3("{data {c {obj {coords {-55 -65 -70 -65 -25 -64} comment {abc def} type polygon}}}}"); std::cout << f3.formatted() << '\n'; const Formatter f4("{data {d {obj {coords {59 60} comment {} type point}} e {obj {coords {928 324} comment {} type point}}}}"); std::cout << f4.formatted() << '\n'; } 
\$\endgroup\$

    0

    Start asking to get answers

    Find the answer to your question by asking.

    Ask question

    Explore related questions

    See similar questions with these tags.