diff --git a/main.cpp b/main.cpp index 2c348c8..ba961ce 100644 --- a/main.cpp +++ b/main.cpp @@ -38,6 +38,20 @@ TEST(TestCase, testParseStr) { EXPECT_EQ(parser.get_root().obj_get("key").as_string(), "1"); } +TEST(TestCase, testParseNumber) { + std::string json_str = "{\"key\":1}"; + simpleJson::JsonParser parser(json_str); + EXPECT_EQ(parser.get_root().obj_get("key").as_int(), 1); +} + +TEST(TestCase, testParseList) { + std::string json_str = "{\"key\":[1,2,\"2\"]}"; + simpleJson::JsonParser parser(json_str); + EXPECT_EQ(parser.get_root().obj_get("key").list_get(0).as_int(), 1); + EXPECT_EQ(parser.get_root().obj_get("key").list_get(1).as_int(), 2); + EXPECT_EQ(parser.get_root().obj_get("key").list_get(2).as_string(), "2"); +} + int main(int argc, char** argv) { testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); diff --git a/src/exception.h b/src/exception.h index fe6f73f..239b01c 100644 --- a/src/exception.h +++ b/src/exception.h @@ -13,6 +13,7 @@ namespace simpleJson { const std::string_view SYNTAX_ERROR = "Syntax Error"; const std::string_view INVALID_TYPE = "Invalid Type"; + const std::string_view FILE_NOT_FOUND = "File Not Found"; class JsonException : public std::exception { protected: @@ -23,6 +24,18 @@ namespace simpleJson { virtual ~JsonException() = default; }; + class FilePathNotValid : public JsonException { + public: + FilePathNotValid(std::string_view str) : JsonException{str} {} + + const char *what() { + std::stringstream ss; + ss << "[JsonFileNotFound]File path\"" << message << "\" is not valid"; + return ss.str().c_str(); + } + }; + + class JsonTypeError : public JsonException { protected: std::string function_name; @@ -83,6 +96,18 @@ namespace simpleJson { return ss.str().c_str(); } }; + + class JsonInvalidUnicodeSym : public JsonInvalidSym { + public: + JsonInvalidUnicodeSym(int line, int column, char detail) : JsonInvalidSym{line, column, detail} {} + + const char *what() { + std::stringstream ss; + ss << "[JsonParseError]" << message << " :invalid unicode symbol " << detail << " at line " << line + << " column " << column << " detail " << detail; + return ss.str().c_str(); + } + }; } #endif //SIMPLEJSON_EXCEPTION_H diff --git a/src/json.cpp b/src/json.cpp index e2d14f2..e71b87e 100644 --- a/src/json.cpp +++ b/src/json.cpp @@ -4,6 +4,7 @@ #include "json.h" #include "exception.h" +#include namespace simpleJson { @@ -793,7 +794,20 @@ namespace simpleJson { ch == '\t') { str += ch; } else if (ch == 'u') { - //unicode TODO + std::string unicode_str; + for (int i = 0; i < 4; i ++) { + reader.get_char(); + ch = reader.curr_char(); + if (ch >= '0' && ch <= '9' || ch >= 'a' && ch <= 'f' || ch >= 'A' && ch <= 'F') { + unicode_str += ch; + } else { + throw JsonInvalidUnicodeSym(reader.get_curr_line(), reader.get_curr_col(), ch); + } + } + long unicode = std::stol(unicode_str.data(), nullptr, 16); + std::string utf8_str; + encode_utf8(unicode, utf8_str); + str += utf8_str; } else { throw JsonInvalidEscapedSym(reader.get_curr_line(), reader.get_curr_col(), ch); } @@ -809,7 +823,125 @@ namespace simpleJson { node->set_value(str); } - void JsonParser::parseNumber(simpleJson::JsonReader &reader, simpleJson::JsonNodePtr &node) {} + void JsonParser::parseNumber(simpleJson::JsonReader &reader, simpleJson::JsonNodePtr &node) { + node->setValueType(JSON_TYPE_DOUBLE); + bool sign = 1; + double value = 0; + bool exp_sign = 1; + int exp_val = 0; + int scale = 0, sub_scale = 0; + + char ch = reader.curr_char(); + if (ch != '-' && !isdigit(ch)) { + throw JsonInvalidSym(reader.get_curr_line(), reader.get_curr_col(), ch); + } + if (ch == '-') { + sign = 0; + reader.get_char(); + ch = reader.curr_char(); + } + if (isdigit(ch) && ch != '0') { + while(isdigit(ch)) { + value = value * 10 + (ch - '0'); + reader.get_char(); + ch = reader.curr_char(); + } + } else if (ch == '0') { + reader.get_char(); + ch = reader.curr_char(); + } + if (ch == '.') { + reader.get_char(); + ch = reader.curr_char(); + if (!isdigit(ch)) { + throw JsonInvalidSym(reader.get_curr_line(), reader.get_curr_col(), ch); + } + while(isdigit(ch)) { + sub_scale += 1; + value = value + double(ch-'0')*std::pow(0.1, double(sub_scale)); + reader.get_char(); + ch = reader.curr_char(); + } + } + + if (ch == 'e' || ch == 'E') { + reader.get_char(); + ch = reader.curr_char(); + + if (ch == '-') { + exp_sign = 0; + reader.get_char(); + ch = reader.curr_char(); + } else if (ch == '+') { + reader.get_char(); + ch = reader.curr_char(); + } + + if (!isdigit(ch)) { + throw JsonInvalidSym(reader.get_curr_line(), reader.get_curr_col(), ch); + } + while(isdigit(ch)) { + exp_val = exp_val * 10 + (ch - '0'); + reader.get_char(); + ch = reader.curr_char(); + } + + exp_val *= exp_sign ? 1 : -1; + } + + value = (value * (sign ? 1 : -1)) * std::pow(10, exp_val); + node->set_value(value); + } + + + void JsonParser::to_json_string(std::string &_dst_str, bool _one_line) const { + _dst_str.clear(); + root->to_json_string(_dst_str, false, 0, _one_line); + } + + bool JsonParser::save_to_file(const std::string &_file_path, bool _one_line) const { + std::ofstream output_file_stream(_file_path); + if (!output_file_stream) { + throw FilePathNotValid(_file_path); + } + std::string json_str; + to_json_string(json_str, _one_line); + output_file_stream << json_str; + output_file_stream.close(); + return true; + } + /****************************************************************************************************************/ + + void encode_utf8(long _unicode, std::string& _out) + { + if (_unicode < 0 || _unicode > 0x10FFFF) + return; + + _out.clear(); + if (_unicode <= 0x7F) + { + _out += static_cast(_unicode); + } + else if (_unicode >= 0x80 && _unicode <= 0x7FF) + { + _out += static_cast((_unicode >> 6) | 0xC0); + _out += static_cast((_unicode & 0x3F) | 0x80); + } + else if (_unicode >= 0x800 && _unicode <= 0xFFFF) + { + _out += static_cast((_unicode >> 12) | 0xE0); + _out += static_cast(((_unicode >> 6) & 0x3F) | 0x80); + _out += static_cast((_unicode & 0x3F) | 0x80); + } + else if (_unicode >= 0x10000 && _unicode <= 0x10FFFF) + { + _out += static_cast((_unicode >> 18) | 0xF0); + _out += static_cast(((_unicode >> 12) & 0x3F) | 0x80); + _out += static_cast(((_unicode >> 6) & 0x3F) | 0x80); + _out += static_cast((_unicode & 0x3F) | 0x80); + } + + } } \ No newline at end of file diff --git a/src/json.h b/src/json.h index 70fbe61..f5aef37 100644 --- a/src/json.h +++ b/src/json.h @@ -194,6 +194,9 @@ namespace simpleJson { bool save_to_file(const std::string &_file_path, bool _one_line = 0) const; }; + + + void encode_utf8(long _unicode, std::string &_out); } #endif //SIMPLEJSON_JSON_H