#include "yamal.hpp" using namespace yamal_ns; bool yamal::isScalar() const { return mode == SCALAR; } bool yamal::isVector() const { return mode == VECTOR; } bool yamal::isMap() const { return mode == MAP; } void yamal::clearToScalar() { mode = SCALAR; vec_data.clear(); map_data.clear(); } void yamal::clearToVector() { mode = VECTOR; value.clear(); map_data.clear(); } void yamal::clearToMap() { mode = MAP; value.clear(); vec_data.clear(); } yamal& yamal::operator=(const std::string& v) { clearToScalar(); value = v; return *this; } yamal& yamal::operator=(int v) { clearToScalar(); value = std::to_string(v); return *this; } yamal& yamal::operator=(float v) { clearToScalar(); value = std::to_string(v); return *this; } yamal& yamal::operator=(bool b) { clearToScalar(); value = b ? "true" : "false"; return *this; } std::string yamal::asString() const { return value; } int yamal::asInt() const { return std::stoi(value); } float yamal::asFloat() const { return std::stof(value); } bool yamal::asBool() const { std::string s = value; std::transform(s.begin(), s.end(), s.begin(), ::tolower); if (s == "true") return true; if (s == "false") return false; try { size_t idx; float f = std::stof(s, &idx); return (idx != 0 && f != 0.0f); } catch (...) { return false; } } const std::string& yamal::getComment() const { return pre_comment; } yamal& yamal::setComment(const std::string& c) { pre_comment = c; return *this; } yamal& yamal::appendComment(const std::string& c) { if (!pre_comment.empty()) pre_comment += "\n"; pre_comment += c; return *this; } yamal& yamal::setQuoted(bool q) { quoted = q; return *this; } yamal& yamal::setInline(bool in) { inline_map = in; return *this; } void yamal::push_back(const yamal& item) { clearToVector(); vec_data.push_back(item); } yamal& yamal::operator[](size_t i) { clearToVector(); return vec_data[i]; } yamal& yamal::operator[](const std::string& key) { clearToMap(); return map_data[key]; } std::vector> yamal::attributes() const { return map_data.items(); } bool yamal::isBoolLiteral() const { std::string s = value; std::transform(s.begin(), s.end(), s.begin(), ::tolower); return s == "true" || s == "false"; } bool yamal::isNumericLiteral() const { if (value.empty()) return false; char* end = nullptr; std::strtod(value.c_str(), &end); return end != value.c_str() && *end == '\0'; } bool yamal::isSafeUnquotedString() const { if (value.empty()) return false; if (std::isdigit(value[0])) return false; for (char c : value) { if (!std::isalnum(c) && c != '_' && c != '-') return false; } return true; } // --- Parsing helpers --- static std::vector splitLines(const std::string& text) { std::vector lines; std::istringstream stream(text); std::string line; while (std::getline(stream, line)) lines.push_back(line); return lines; } static std::string trim(const std::string& s) { size_t start = s.find_first_not_of(" \t"); size_t end = s.find_last_not_of(" \t"); return (start == std::string::npos) ? "" : s.substr(start, end - start + 1); } static std::string removeWhitespace(const std::string& s) { size_t start = s.find_first_not_of(" \t\n\r"); size_t end = s.find_last_not_of(" \t\n\r"); return (start == std::string::npos) ? "" : s.substr(start, end - start + 1); } static int countIndent(const std::string& line) { int count = 0; for (char c : line) if (c == ' ') ++count; else break; return count; } static std::string joinCommentLines(const std::vector& lines) { std::ostringstream oss; for (const auto& line : lines) oss << line << "\n"; return trim(oss.str()); } static yamal parseInlineSequence(const std::string& text) { yamal node; node.clearToVector(); node.setInline(true); std::string inner = text.substr(1, text.size() - 2); std::istringstream ss(inner); std::string item; while (std::getline(ss, item, ',')) { yamal child; child.deserialize(trim(item)); node.push_back(child); } return node; } static yamal parseInlineMap(const std::string& text) { yamal node; node.clearToMap(); node.setInline(true); std::string inner = text.substr(1, text.size() - 2); std::istringstream ss(inner); std::string pair; while (std::getline(ss, pair, ',')) { auto pos = pair.find(':'); if (pos != std::string::npos) { std::string key = trim(pair.substr(0, pos)); std::string val = trim(pair.substr(pos + 1)); yamal& child = node[key]; child.deserialize(val); } } return node; } static yamal parseBlock(const std::vector& lines, size_t& i, int indent) { yamal node; bool is_sequence = false; std::vector pending_comment_lines; while (i < lines.size()) { const std::string& line = lines[i]; std::string trimmed = trim(line); if (trimmed.empty()) { pending_comment_lines.push_back(""); ++i; continue; } if (trimmed[0] == '#') { pending_comment_lines.push_back(trimmed.substr(1)); ++i; continue; } int currentIndent = countIndent(line); if (currentIndent < indent) break; std::string content = trimmed; std::string inline_comment; size_t hash_pos = content.find('#'); if (hash_pos != std::string::npos && hash_pos > 0) { inline_comment = trim(content.substr(hash_pos + 1)); content = trim(content.substr(0, hash_pos)); } if (content[0] == '-') { if (!is_sequence) { node.clearToVector(); is_sequence = true; } std::string item = trim(content.substr(1)); yamal child; if (!pending_comment_lines.empty()) { child.pre_comment = joinCommentLines(pending_comment_lines); pending_comment_lines.clear(); } if (!inline_comment.empty()) child.inline_comment = inline_comment; ++i; if (item.empty()) child = parseBlock(lines, i, indent + 2); else if (item[0] == '[') child = parseInlineSequence(item); else if (item[0] == '{') child = parseInlineMap(item); else child.deserialize(item); node.push_back(child); } else if (auto pos = content.find(':'); pos != std::string::npos) { node.clearToMap(); std::string key = trim(content.substr(0, pos)); std::string val = trim(content.substr(pos + 1)); yamal& child = node[key]; if (!pending_comment_lines.empty()) { child.pre_comment = joinCommentLines(pending_comment_lines); pending_comment_lines.clear(); } if (!inline_comment.empty()) child.inline_comment = inline_comment; ++i; if (val.empty()) child = parseBlock(lines, i, indent + 2); else if (val[0] == '[') child = parseInlineSequence(val); else if (val[0] == '{') child = parseInlineMap(val); else { child.clearToScalar(); if (val.size() >= 2 && val.front() == '"' && val.back() == '"') { child.value = val.substr(1, val.size() - 2); child.quoted = true; } else { child.value = val; } } } else { ++i; } } return node; } void yamal::deserialize(const std::string& yamlText) { auto lines = splitLines(yamlText); clearToMap(); size_t index = 0; yamal top = parseBlock(lines, index, 0); for (const auto& [key, val] : top.attributes()) { (*this)[key] = val; } } std::string yamal::serialize(int indent) const { std::ostringstream out; std::string pad(indent, ' '); std::string trimmed;// = removeWhitespace(pre_comment); /*if (!trimmed.empty()) { std::istringstream ss(pre_comment); std::string line; while (std::getline(ss, line)) { out << pad << "# " << line << "\n"; } }*/ if (isScalar()) { if (isBoolLiteral()) { out << pad << value; } else if (quoted) { out << pad << "\"" << value << "\""; } else if (isNumericLiteral() || isSafeUnquotedString()) { out << pad << value; } else { out << pad << "\"" << value << "\""; } trimmed = removeWhitespace(inline_comment); if (!trimmed.empty()) { out << " # " << inline_comment; } return out.str(); } if (isVector()) { if (inline_map) { out << pad << "["; for (size_t i = 0; i < vec_data.size(); ++i) { out << vec_data[i].serialize(0); if (i + 1 < vec_data.size()) out << ", "; } out << "]"; trimmed = removeWhitespace(inline_comment); if (!trimmed.empty()) out << " # " << inline_comment; out << "\n"; return out.str(); } for (const auto& item : vec_data) { trimmed = removeWhitespace(item.pre_comment); if (!trimmed.empty()) { std::istringstream ss(item.pre_comment); std::string line; while (std::getline(ss, line)) { out << pad << "# " << line << "\n"; } } out << pad << "-"; if (item.isScalar()) { out << " " << item.serialize(0); trimmed = removeWhitespace(item.inline_comment); if (!trimmed.empty()) { out << " # " << item.inline_comment; } out << "\n"; } else { out << "\n" << item.serialize(indent + 2); } } return out.str(); } if (isMap()) { if (inline_map) { out << pad << "{"; auto items = map_data.items(); for (size_t i = 0; i < items.size(); ++i) { const auto& [key, val] = items[i]; out << key << ": " << val.serialize(0); if (i + 1 < items.size()) out << ", "; } out << "}"; trimmed = removeWhitespace(inline_comment); if (!trimmed.empty()) out << " # " << inline_comment; out << "\n"; return out.str(); } for (const auto& [key, val] : map_data.items()) { trimmed = removeWhitespace(val.pre_comment); if (!trimmed.empty()) { std::istringstream ss(val.pre_comment); std::string line; while (std::getline(ss, line)) { out << pad << "# " << line << "\n"; } } out << pad << key << ":"; if (val.isScalar()) { out << " " << val.serialize(0); trimmed = removeWhitespace(val.inline_comment); if (!trimmed.empty()) { out << " # " << val.inline_comment; } out << "\n"; } else { out << "\n" << val.serialize(indent + 2); } } return out.str(); } return ""; }