/* # 076 Free License Copyright (c) テクニカル諏訪子 Permission is hereby granted to any person obtaining a copy of the software LibJson (the "Software") to use, modify, merge, copy, publish, distribute, sublicense, and/or sell copies of the Software, subject to the following conditions: 1. **Origin Attribution**: - You must not misrepresent the origin of the Software; you must not claim you created the original Software. 2. **Notice Preservation**: - This license and the above copyright notice must remain intact in all copies of the source code. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include #include #include #include #include namespace json { ParseResult Parser::make_error(Error err) const { return { std::make_unique(), err, m_Pos }; } void Parser::skip_whitespace() { while (m_Pos < m_Text.size()) { char c = m_Text[m_Pos]; if (c != ' ' && c != '\t' && c != '\n' && c != '\r') break; ++m_Pos; } } char Parser::peek() const { return m_Pos < m_Text.size() ? m_Text[m_Pos] : '\0'; } char Parser::consume() { return m_Pos < m_Text.size() ? m_Text[m_Pos++] : '\0'; } bool Parser::expect(char c) { if (peek() == c) { consume(); return true; } return false; } ParseResult Parser::parse_value() { skip_whitespace(); if (m_Pos >= m_Text.size()) return make_error(Error::UnexpectedEnd); char c = peek(); if (c == '{') return parse_object(); if (c == '[') return parse_array(); if (c == '"') { auto str = parse_string(); if (!str.ok()) return str; return str; } if (m_Text.substr(m_Pos, 4) == "true") { m_Pos += 4; return { std::make_unique(true) }; } if (m_Text.substr(m_Pos, 5) == "false") { m_Pos += 5; return { std::make_unique(false) }; } if (m_Text.substr(m_Pos, 4) == "null") { m_Pos += 4; return { std::make_unique(nullptr) }; } if ((c >= '0' && c <= '9') || c == '-' || c == '+') { auto num = parse_number(); if (num) return { std::make_unique(*num) }; return make_error(Error::InvalidNumber); } return make_error(Error::InvalidValue); } ParseResult Parser::parse_object() { if (!expect('{')) return make_error(Error::SyntaxError); Object obj; skip_whitespace(); if (peek() == '}') { consume(); return { std::make_unique(std::move(obj)) }; } while (true) { skip_whitespace(); auto keyres = parse_string(); if (!keyres.ok()) return keyres; skip_whitespace(); if (!expect(':')) return make_error(Error::SyntaxError); auto valres = parse_value(); if (!valres.ok()) return valres; obj.insert( std::move(keyres.value->as_string()), std::move(*valres.value) ); skip_whitespace(); if (peek() == '}') { consume(); break; } if (!expect(',')) return make_error(Error::SyntaxError); } return { std::make_unique(std::move(obj)) }; } ParseResult Parser::parse_array() { if (!expect('[')) return make_error(Error::SyntaxError); Value::Array arr; skip_whitespace(); if (peek() == ']') { consume(); return { std::make_unique(std::move(arr)) }; } while (true) { auto valres = parse_value(); if (!valres.ok()) return valres; arr.push_back(std::move(*valres.value)); skip_whitespace(); if (peek() == ']') { consume(); break; } if (!expect(',')) return make_error(Error::SyntaxError); } return { std::make_unique(std::move(arr)) }; } ParseResult Parser::parse_string() { if (!expect('"')) return make_error(Error::SyntaxError); string res; res.reserve(32); while (m_Pos < m_Text.size()) { char c = consume(); if (c == '"') return { std::make_unique(std::move(res)) }; if (c == '\\') { if (m_Pos >= m_Text.size()) make_error(Error::UnterminatedString); char esc = consume(); switch (esc) { case '"': res += '"'; break; case '\\': res += '\\'; break; case '/': res += '/'; break; case 'b': res += '\b'; break; case 'f': res += '\f'; break; case 'n': res += '\n'; break; case 'r': res += '\r'; break; case 't': res += '\t'; break; case 'u': { for (int i = 0; i < 4; ++i) { if (m_Pos >= m_Text.size()) return make_error(Error::UnterminatedString); char h = consume(); if (!std::isxdigit((unsigned char)h)) return make_error(Error::SyntaxError); } break; } default: return make_error(Error::SyntaxError); } } else { res += c; } } return make_error(Error::UnterminatedString); } std::optional Parser::parse_number() { cstr start = m_Text.data() + m_Pos; f64 num = 0.0; char *end = nullptr; num = std::strtod(start, &end); if (end == start) return std::nullopt; m_Pos += (end - start); return num; } // 公開API ParseResult Value::parse(std::string_view jsonText) { Parser p(jsonText); return p.parse(); } ParseResult Parser::parse() { skip_whitespace(); ParseResult res = parse_value(); if (!res.ok()) return res; skip_whitespace(); if (m_Pos < m_Text.size()) { assert(false && "JSON値の後でデータがある"); } return res; } string Value::serialize(const SerializeOptions &opts) const { return serialize_impl(opts, 0); } string Value::serialize_impl(const SerializeOptions &opts, size_t depth) const { if (is_null()) return "null"; if (is_bool()) return as_bool() ? "true" : "false"; if (is_number()) { std::ostringstream oss; oss << as_number(); return oss.str(); } if (is_string()) return serialize_string(as_string()); if (is_array()) { const auto &arr = as_array(); string out = "["; if (opts.pretty && !arr.empty()) out += "\n"; for (size_t i = 0; i < arr.size(); ++i) { if (opts.pretty) out += make_indent(depth + 1, opts.indent); out += arr[i].serialize_impl(opts, depth + 1); if (i + 1 < arr.size()) out += ","; if (opts.pretty) out += "\n"; } if (opts.pretty && !arr.empty()) out += make_indent(depth, opts.indent); out += "]"; return out; } if (is_object()) { const auto &obj = as_object(); string out = "{"; if (opts.pretty && !obj.empty()) out += "\n"; size_t i = 0; for (const auto &[key, val] : obj) { if (opts.pretty) out += make_indent(depth + 1, opts.indent); out += serialize_string(key); out += opts.pretty ? ": " : ":"; out += val->serialize_impl(opts, depth + 1); if (++i < obj.size()) out += ","; if (opts.pretty) out += "\n"; } if (opts.pretty && !obj.empty()) out += make_indent(depth, opts.indent); out += "}"; return out; } return "null"; } string Value::make_indent(size_t depth, size_t width) { return string(depth * width, ' '); } string Value::serialize_string(const string &s) { string out = "\""; for (char c : s) { switch (c) { case '"': out += "\\\""; break; case '\\': out += "\\\\"; break; case '\b': out += "\\b"; break; case '\f': out += "\\f"; break; case '\n': out += "\\n"; break; case '\r': out += "\\r"; break; case '\t': out += "\\t"; break; default: out += c; break; } } out += "\""; return out; } void Object::insert(string key, Value value) { auto ptr = std::make_unique(std::move(value)); auto it = m_Index.find(key); if (it != m_Index.end()) { m_Data[it->second].second = std::move(ptr); } else { m_Index[key] = m_Data.size(); m_Data.emplace_back(std::move(key), std::move(ptr)); } } Value *Object::get(std::string_view key) { auto it = m_Index.find(key); return it != m_Index.end() ? m_Data[it->second].second.get() : nullptr; } const Value *Object::get(std::string_view key) const { auto it = m_Index.find(key); return it != m_Index.end() ? m_Data[it->second].second.get() : nullptr; } bool Object::contains(std::string_view key) const { return m_Index.contains(key); } ////////////// std::optional Value::get_bool() const { if (auto *b = std::get_if(&m_Value)) return *b; return std::nullopt; } std::optional Value::get_number() const { if (auto *b = std::get_if(&m_Value)) return *b; return std::nullopt; } std::optional Value::get_string() const { if (auto *b = std::get_if(&m_Value)) return *b; return std::nullopt; } } // namespace json