From 05a263d24c6d84d72cf055bce0bf1233226429a4 Mon Sep 17 00:00:00 2001 From: Victor Marsault Date: Fri, 25 Mar 2022 13:57:12 +0100 Subject: [PATCH 1/5] repackage of tp-json --- tp-json/.gitignore | 1 + tp-json/ArrayNode.cpp | 1 + tp-json/ArrayNode.hpp | 117 + tp-json/BooleanLeaf.cpp | 1 + tp-json/BooleanLeaf.hpp | 36 + tp-json/CMakeLists.txt | 6 + tp-json/JsonParser.cpp | 240 + tp-json/JsonParser.hpp | 41 + tp-json/Node.cpp | 6 + tp-json/Node.hpp | 73 + tp-json/NodeKind.hpp | 35 + tp-json/Node_ptr.hpp | 7 + tp-json/NumberLeaf.cpp | 10 + tp-json/NumberLeaf.hpp | 30 + tp-json/ObjectNode.cpp | 1 + tp-json/ObjectNode.hpp | 125 + tp-json/StringLeaf.cpp | 1 + tp-json/StringLeaf.hpp | 37 + .../code_fourni_aux_etudiants/ArrayNode.hpp | 1 + .../code_fourni_aux_etudiants/BooleanNode.hpp | 1 + tp-json/code_fourni_aux_etudiants/Node.hpp | 7 + .../code_fourni_aux_etudiants/NodeKind.hpp | 35 + .../code_fourni_aux_etudiants/Node_ptr.hpp | 3 + .../code_fourni_aux_etudiants/NumberNode.hpp | 1 + .../code_fourni_aux_etudiants/ObjectNode.hpp | 1 + .../code_fourni_aux_etudiants/StringNode.hpp | 1 + .../code_fourni_aux_etudiants/all_nodes.hpp | 9 + tp-json/cpp2022.svg | 498 ++ tp-json/enonce.md | 83 + tp-json/tests/00_basic_node.cpp | 10 + tp-json/tests/01_basic_null.cpp | 14 + tp-json/tests/02_basic_boolean.cpp | 16 + tp-json/tests/03_basic_number.cpp | 13 + tp-json/tests/04_basic_string.cpp | 13 + tp-json/tests/05_basic_array.cpp | 13 + tp-json/tests/06_basic_object.cpp | 13 + tp-json/tests/10_make_ptr.cpp | 20 + tp-json/tests/11_array_add.cpp | 28 + tp-json/tests/12_object_add.cpp | 34 + tp-json/tests/13_height_and_nodecount.cpp | 34 + tp-json/tests/14_object_dico.cpp | 24 + tp-json/tests/21_parser_leaf.cpp | 51 + tp-json/tests/22_parser_array.cpp | 30 + tp-json/tests/23_parser_object.cpp | 31 + tp-json/tests/24_parser_pokedex.cpp | 24 + tp-json/tests/30_output_operator.cpp | 13 + tp-json/tests/31_explicit_cast.cpp | 44 + tp-json/tests/32_explicit_cast_const.cpp | 46 + tp-json/tests/33_children_count.cpp | 13 + tp-json/tests/34_has_child.cpp | 14 + tp-json/tests/35_at.cpp | 15 + tp-json/tests/38_equality.cpp | 50 + tp-json/tests/39_copy.cpp | 34 + tp-json/tests/40_pokedex.cpp | 99 + tp-json/tests/99_dot.cpp | 37 + tp-json/tests/CMakeLists.txt | 24 + tp-json/tests/assert.cpp | 46 + tp-json/tests/json/array_empty.json | 1 + tp-json/tests/json/array_hexadecimal.json | 1 + tp-json/tests/json/array_range10.json | 1 + tp-json/tests/json/boolean_false.json | 1 + tp-json/tests/json/boolean_true.json | 1 + tp-json/tests/json/cpp2022.json | 14 + tp-json/tests/json/number_42.json | 1 + tp-json/tests/json/object_alphabet.json | 1 + tp-json/tests/json/object_empty.json | 1 + tp-json/tests/json/pokedex.json | 4086 +++++++++++++++++ tp-json/tests/json/string_hello.json | 1 + tp-json/tests/old/50_number_node_hardmode.cpp | 19 + tp-json/tests/old/NullNode.hpp | 25 + tp-json/tests/old/array_large_numbers.json | 1 + tp-json/tests/old/basic_node_instances.cpp | 41 + tp-json/tests/old/json.hpp | 9 + 73 files changed, 6415 insertions(+) create mode 100644 tp-json/.gitignore create mode 100644 tp-json/ArrayNode.cpp create mode 100644 tp-json/ArrayNode.hpp create mode 100644 tp-json/BooleanLeaf.cpp create mode 100644 tp-json/BooleanLeaf.hpp create mode 100644 tp-json/CMakeLists.txt create mode 100644 tp-json/JsonParser.cpp create mode 100644 tp-json/JsonParser.hpp create mode 100644 tp-json/Node.cpp create mode 100644 tp-json/Node.hpp create mode 100644 tp-json/NodeKind.hpp create mode 100644 tp-json/Node_ptr.hpp create mode 100644 tp-json/NumberLeaf.cpp create mode 100644 tp-json/NumberLeaf.hpp create mode 100644 tp-json/ObjectNode.cpp create mode 100644 tp-json/ObjectNode.hpp create mode 100644 tp-json/StringLeaf.cpp create mode 100644 tp-json/StringLeaf.hpp create mode 100644 tp-json/code_fourni_aux_etudiants/ArrayNode.hpp create mode 100644 tp-json/code_fourni_aux_etudiants/BooleanNode.hpp create mode 100644 tp-json/code_fourni_aux_etudiants/Node.hpp create mode 100644 tp-json/code_fourni_aux_etudiants/NodeKind.hpp create mode 100644 tp-json/code_fourni_aux_etudiants/Node_ptr.hpp create mode 100644 tp-json/code_fourni_aux_etudiants/NumberNode.hpp create mode 100644 tp-json/code_fourni_aux_etudiants/ObjectNode.hpp create mode 100644 tp-json/code_fourni_aux_etudiants/StringNode.hpp create mode 100644 tp-json/code_fourni_aux_etudiants/all_nodes.hpp create mode 100644 tp-json/cpp2022.svg create mode 100644 tp-json/enonce.md create mode 100644 tp-json/tests/00_basic_node.cpp create mode 100644 tp-json/tests/01_basic_null.cpp create mode 100644 tp-json/tests/02_basic_boolean.cpp create mode 100644 tp-json/tests/03_basic_number.cpp create mode 100644 tp-json/tests/04_basic_string.cpp create mode 100644 tp-json/tests/05_basic_array.cpp create mode 100644 tp-json/tests/06_basic_object.cpp create mode 100644 tp-json/tests/10_make_ptr.cpp create mode 100644 tp-json/tests/11_array_add.cpp create mode 100644 tp-json/tests/12_object_add.cpp create mode 100644 tp-json/tests/13_height_and_nodecount.cpp create mode 100644 tp-json/tests/14_object_dico.cpp create mode 100644 tp-json/tests/21_parser_leaf.cpp create mode 100644 tp-json/tests/22_parser_array.cpp create mode 100644 tp-json/tests/23_parser_object.cpp create mode 100644 tp-json/tests/24_parser_pokedex.cpp create mode 100644 tp-json/tests/30_output_operator.cpp create mode 100644 tp-json/tests/31_explicit_cast.cpp create mode 100644 tp-json/tests/32_explicit_cast_const.cpp create mode 100644 tp-json/tests/33_children_count.cpp create mode 100644 tp-json/tests/34_has_child.cpp create mode 100644 tp-json/tests/35_at.cpp create mode 100644 tp-json/tests/38_equality.cpp create mode 100644 tp-json/tests/39_copy.cpp create mode 100644 tp-json/tests/40_pokedex.cpp create mode 100644 tp-json/tests/99_dot.cpp create mode 100644 tp-json/tests/CMakeLists.txt create mode 100644 tp-json/tests/assert.cpp create mode 100644 tp-json/tests/json/array_empty.json create mode 100644 tp-json/tests/json/array_hexadecimal.json create mode 100644 tp-json/tests/json/array_range10.json create mode 100644 tp-json/tests/json/boolean_false.json create mode 100644 tp-json/tests/json/boolean_true.json create mode 100644 tp-json/tests/json/cpp2022.json create mode 100644 tp-json/tests/json/number_42.json create mode 100644 tp-json/tests/json/object_alphabet.json create mode 100644 tp-json/tests/json/object_empty.json create mode 100644 tp-json/tests/json/pokedex.json create mode 100644 tp-json/tests/json/string_hello.json create mode 100644 tp-json/tests/old/50_number_node_hardmode.cpp create mode 100644 tp-json/tests/old/NullNode.hpp create mode 100644 tp-json/tests/old/array_large_numbers.json create mode 100644 tp-json/tests/old/basic_node_instances.cpp create mode 100644 tp-json/tests/old/json.hpp diff --git a/tp-json/.gitignore b/tp-json/.gitignore new file mode 100644 index 00000000..e35d8850 --- /dev/null +++ b/tp-json/.gitignore @@ -0,0 +1 @@ +_build diff --git a/tp-json/ArrayNode.cpp b/tp-json/ArrayNode.cpp new file mode 100644 index 00000000..3232bf18 --- /dev/null +++ b/tp-json/ArrayNode.cpp @@ -0,0 +1 @@ +#include "ArrayNode.hpp" \ No newline at end of file diff --git a/tp-json/ArrayNode.hpp b/tp-json/ArrayNode.hpp new file mode 100644 index 00000000..d1fd51de --- /dev/null +++ b/tp-json/ArrayNode.hpp @@ -0,0 +1,117 @@ +#pragma once + +#include "Node.hpp" + +#include +#include +#include + +class ArrayNode : public Node +{ +private: + std::vector _data; + +public: + std::vector const& data() const { return _data; } + + std::string print() const override + { + std::string result = "["; + for (unsigned i = 0; i < _data.size(); ++i) + { + if (i > 0) + result += ","; + result += _data[i]->print(); + } + result += ']'; + return result; + } + + void add(Node_ptr node) { _data.push_back(std::move(node)); } + + ArrayNode() + : Node(NodeKind::ARRAY) + {} + + ArrayNode(std::vector data) + : Node { NodeKind::ARRAY } + , _data { std::move(data) } + {} + + static inline std::unique_ptr make_ptr(std::vector data = {}) + { + return std::make_unique(std::move(data)); + } + + size_t children_count() const { return _data.size(); } + + ArrayNode* as_ArrayNode() override { return this; } + const ArrayNode* as_ArrayNode() const override { return this; } + + inline bool operator==(const Node& other) const override + { + if (!(other.is_of_kind(kind()))) + { + std::cerr << kind() << "!=" << other.kind() << std::endl; + return false; + } + ArrayNode const* other_s = other.as_ArrayNode(); + size_t size = children_count(); + if (other_s->children_count() != size) + { + std::cerr << size << " != " << other_s->children_count() << std::endl; + return false; + } + for (unsigned i = 0; i < size; i++) + { + if (*(other_s->_data[i]) != *(_data[i])) + { + std::cerr << *(other_s->_data[i]) << std::endl; + std::cerr << *_data[i] << std::endl; + return false; + } + } + return true; + } + + size_t height() const override + { + return std::accumulate(_data.begin(), _data.end(), 0, + [](size_t i, Node_ptr const& child) + { return std::max(i, 1u + child->height()); }); + } + + virtual size_t node_count() const override + { + return std::accumulate(_data.begin(), _data.end(), 1, + [](size_t i, Node_ptr const& child) { return i + child->node_count(); }); + } + + std::vector::iterator begin() { return _data.begin(); } + std::vector::iterator end() { return _data.end(); } + + std::vector::const_iterator begin() const { return _data.begin(); } + std::vector::const_iterator end() const { return _data.end(); } + + Node_ptr deep_copy() const override + { + auto result = make_ptr(); + for (auto const& elt : this->data()) + result->add(elt->deep_copy()); + return result; + } + + std::string dot_label() const override { return "[...]"; } + + virtual void dot(std::ostream& o) const + { + Node::dot(o); + o << "subgraph cluster_" << dot_id() << " {style=invis;" << std::endl; + for (auto it = _data.rbegin(); it != _data.rend(); ++it) + { + (*it)->dot(o); + o << dot_id() << " -> " << (*it)->dot_id() << " ;" << std::endl; + } + o << "}" << std::endl; + } +}; diff --git a/tp-json/BooleanLeaf.cpp b/tp-json/BooleanLeaf.cpp new file mode 100644 index 00000000..530e217c --- /dev/null +++ b/tp-json/BooleanLeaf.cpp @@ -0,0 +1 @@ +#include "BooleanLeaf.hpp" \ No newline at end of file diff --git a/tp-json/BooleanLeaf.hpp b/tp-json/BooleanLeaf.hpp new file mode 100644 index 00000000..a09bbe63 --- /dev/null +++ b/tp-json/BooleanLeaf.hpp @@ -0,0 +1,36 @@ +#pragma once + +#include "Node.hpp" + +class BooleanLeaf : public Node +{ +private: + bool _data; + +public: + std::string print() const override { return _data ? "true" : "false"; } + BooleanLeaf(bool data) + : Node { NodeKind::BOOLEAN } + , _data { data } + {} + + static std::unique_ptr make_ptr(bool data) { return std::make_unique(data); } + + BooleanLeaf* as_BooleanLeaf() override { return this; } + BooleanLeaf const* as_BooleanLeaf() const override { return this; } + + inline bool operator==(const Node& other) const override + { + if (!(other.is_of_kind(kind()))) + { + return false; + } + return (_data == other.as_BooleanLeaf()->_data); + } + + const bool& data() const { return _data; } + + Node_ptr deep_copy() const override { return make_ptr(data()); } + + std::string dot_label() const override { return _data ? "true" : "false"; } +}; diff --git a/tp-json/CMakeLists.txt b/tp-json/CMakeLists.txt new file mode 100644 index 00000000..42c8862d --- /dev/null +++ b/tp-json/CMakeLists.txt @@ -0,0 +1,6 @@ + +enable_testing() + +file(GLOB TP-JSON-SOURCES *.cpp) + +add_subdirectory(tests) diff --git a/tp-json/JsonParser.cpp b/tp-json/JsonParser.cpp new file mode 100644 index 00000000..a95644ff --- /dev/null +++ b/tp-json/JsonParser.cpp @@ -0,0 +1,240 @@ +#include "JsonParser.hpp" + +#include +#include +#include +#include + +void JsonParser::extract_spaces() +{ + while (!_in.eof() && std::isspace(_in.peek())) + _in.get(); +} + +bool JsonParser::check_next_char_equals(int c, std::string_view v) +{ + int c2 = _in.peek(); + if (c == c2) + { + _in.get(); + return true; + } + std::cerr << "Unexpected character ("; + if (c2 >= 20 && c2 < 127) + std::cerr << (char)c2; + else if (c2 == -1) + std::cerr << "EOF"; + else + std::cerr << '\\' << c2; + if (v != "") + std::cerr << "). Expecting a char in \"" << v << "\"." << std::endl; + else + std::cerr << "). Expecting '" << (char)c << "'." << std::endl; + return false; +} + +Node_ptr JsonParser::parse_Node() +{ + extract_spaces(); + int c {}; + switch (c = _in.peek()) + { + case '{': + return parse_ObjectNode(); + case '[': + return parse_ArrayNode(); + case '"': + return parse_StringLeaf(); + // case 'n': + // return parse_constant("null"); + case 'f': + return parse_constant("false"); + case 't': + return parse_constant("true"); + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case 'e': + case 'E': + case '.': + case '-': + case '+': + return parse_NumberLeaf(); + default: + check_next_char_equals(-123456789, "{[\"nft0123456789eE.-+"); + return nullptr; + } +} + +Node_ptr JsonParser::parse_constant(std::string_view target) +{ + char c[6]; + for (size_t x = 0; x < target.size(); x++) + { + _in >> c[x]; + if (c[x] != target[x]) + { + c[x + 1] = '\0'; + std::cerr << "Expecting JSON value (probably constant " << target << ") at position " + << (_in.tellg() - (long)x) << ". Got string starting with " << c << std::endl; + return nullptr; + } + } + // if (target == "null") + // return NullNode::make_ptr(); + if (target == "true") + return BooleanLeaf::make_ptr(true); + if (target == "false") + return BooleanLeaf::make_ptr(false); + return nullptr; +} + +std::optional JsonParser::extract_string() +{ + extract_spaces(); + check_next_char_equals('"'); + std::string s = ""; + char c = '\0'; + while (((c = _in.get()) != '"') && !_in.eof()) + { + s += c; + if (c == '\\') /* If c is a '\' then the next char is escaped. */ + { + c = _in.get(); + if (_in.eof()) + break; + s += _in.get(); + } + } + + if (_in.eof()) + { + std::cerr << "Found EOF while looking for closing \"." << std::endl; + return std::optional(); + } + return s; +} + +Node_ptr JsonParser::parse_StringLeaf() +{ + auto str = extract_string(); + if (str) + return StringLeaf::make_ptr(std::move(str.value())); + else + return nullptr; +} + +Node_ptr JsonParser::parse_NumberLeaf() +{ + // unsigned starting_pos = _in.tellg(); + + double d; + _in >> d; + // size_t end_pos_double = _in.tellg(); + + return NumberLeaf::make_ptr(d); +} + +Node_ptr JsonParser::parse_ArrayNode() +{ + if (!check_next_char_equals('[')) + return nullptr; + auto arrayNode = ArrayNode::make_ptr(); + extract_spaces(); + if (_in.peek() == ']') + { + _in.get(); + return arrayNode; + } + + do + { + auto child = parse_Node(); + if (child == nullptr) + return nullptr; + else + arrayNode->add(std::move(child)); + extract_spaces(); + } + while (_in.get() == ','); + + _in.unget(); + if (check_next_char_equals(']', ",]")) + return arrayNode; + else + return nullptr; +} + +Node_ptr JsonParser::parse_ObjectNode() +{ + if (!check_next_char_equals('{')) + return nullptr; + auto objectNode = ObjectNode::make_ptr(); + extract_spaces(); + if (_in.peek() == '}') + { + _in.get(); + return objectNode; + } + + do + { + std::optional opt_key = extract_string(); + if (!opt_key) + return nullptr; + extract_spaces(); + if (!check_next_char_equals(':')) + return nullptr; + auto child = parse_Node(); + if (child == nullptr) + return nullptr; + else + objectNode->add(std::move(opt_key.value()), std::move(child)); + extract_spaces(); + } + while (_in.get() == ','); + _in.unget(); + if (check_next_char_equals('}', ",}")) + return objectNode; + else + return nullptr; +} + +Node_ptr JsonParser::run() +{ + return parse_Node(); +} + +Node_ptr JsonParser::parse_from_istream(std::istream& in) +{ + JsonParser parser(in); + Node_ptr parsed_tree = parser.run(); + if (parsed_tree) + return parsed_tree; + else + exit(EXIT_FAILURE); +} + +Node_ptr JsonParser::parse_from_file(std::string const& path) +{ + std::ifstream in(path.c_str(), std::ifstream::in); + if (!in.is_open()) + { + std::cerr << "Could not open file: " << path << std::endl; + exit(EXIT_FAILURE); + } + return parse_from_istream(in); +} + +Node_ptr JsonParser::parse_from_string(std::string const& str) +{ + std::stringstream ss { str }; + return parse_from_istream(ss); +} diff --git a/tp-json/JsonParser.hpp b/tp-json/JsonParser.hpp new file mode 100644 index 00000000..5f0bb89a --- /dev/null +++ b/tp-json/JsonParser.hpp @@ -0,0 +1,41 @@ +#pragma once + +#include "ArrayNode.hpp" +#include "BooleanLeaf.hpp" +#include "Node.hpp" +#include "NodeKind.hpp" +#include "NumberLeaf.hpp" +#include "ObjectNode.hpp" +#include "StringLeaf.hpp" + +#include +#include + +class JsonParser +{ +private: + std::istream& _in; + + void extract_spaces(); + bool check_next_char_equals(int c, std::string_view other_possibilities = ""); + + std::optional extract_string(); + + Node_ptr parse_Node(); + Node_ptr parse_constant(std::string_view target); + Node_ptr parse_StringLeaf(); + Node_ptr parse_NumberLeaf(); + Node_ptr parse_ArrayNode(); + Node_ptr parse_ObjectNode(); + +public: + JsonParser(std::istream& in) + : _in(in) + {} + + Node_ptr run(); + + static Node_ptr parse_from_istream(std::istream& in); + static Node_ptr parse_from_file(std::string const& path); + static Node_ptr parse_from_string(std::string const& str); +}; diff --git a/tp-json/Node.cpp b/tp-json/Node.cpp new file mode 100644 index 00000000..031d7b24 --- /dev/null +++ b/tp-json/Node.cpp @@ -0,0 +1,6 @@ +#include "Node.hpp" + +std::ostream& operator<<(std::ostream& o, const Node& node) +{ + return o << node.print(); +} \ No newline at end of file diff --git a/tp-json/Node.hpp b/tp-json/Node.hpp new file mode 100644 index 00000000..8e7028a1 --- /dev/null +++ b/tp-json/Node.hpp @@ -0,0 +1,73 @@ +#pragma once + +#include "NodeKind.hpp" +#include "Node_ptr.hpp" + +#include +#include +#include +#include +#include + +class NullNode; +class BooleanLeaf; +class NumberLeaf; +class StringLeaf; +class ArrayNode; +class ObjectNode; + +class Node +{ +private: + const NodeKind _kind; + +protected: + Node(NodeKind kind) + : _kind(kind) + {} + +public: + virtual ~Node() = default; + + NodeKind kind() const { return _kind; } + virtual bool is_of_kind(NodeKind kind) const { return _kind == kind; } + + virtual std::string print() const = 0; + + virtual bool operator==(const Node&) const = 0; + inline bool operator!=(const Node& other) const { return !(*this == other); } + + virtual BooleanLeaf* as_BooleanLeaf() { return nullptr; } + virtual NumberLeaf* as_NumberLeaf() { return nullptr; } + virtual StringLeaf* as_StringLeaf() { return nullptr; } + virtual ArrayNode* as_ArrayNode() { return nullptr; } + virtual ObjectNode* as_ObjectNode() { return nullptr; } + + virtual const BooleanLeaf* as_BooleanLeaf() const { return nullptr; } + virtual const NumberLeaf* as_NumberLeaf() const { return nullptr; } + virtual const StringLeaf* as_StringLeaf() const { return nullptr; } + virtual const ArrayNode* as_ArrayNode() const { return nullptr; } + virtual const ObjectNode* as_ObjectNode() const { return nullptr; } + + virtual size_t height() const { return 0u; } + virtual size_t node_count() const { return 1u; } + + virtual Node_ptr deep_copy() const = 0; + + virtual void dot(std::ostream& o) const + { + o << dot_id() << " [label=\"" << dot_label() << "\"];" << std::endl; + } + + inline std::string dot_id() const + { + const void* address = static_cast(this); + std::stringstream ss; + ss << "n"; + ss << address; + return ss.str(); + } + + virtual std::string dot_label() const = 0; +}; +std::ostream& operator<<(std::ostream& o, const Node& node); diff --git a/tp-json/NodeKind.hpp b/tp-json/NodeKind.hpp new file mode 100644 index 00000000..9c1e603b --- /dev/null +++ b/tp-json/NodeKind.hpp @@ -0,0 +1,35 @@ +#pragma once + +#include + +enum class NodeKind +{ + /* NONE is used for NullNode since NULL is reserved */ + // NONE, + BOOLEAN, + NUMBER, + STRING, + ARRAY, + OBJECT +}; + +inline std::ostream& operator<<(std::ostream& o, NodeKind kind) +{ + switch (kind) + { + /* case NodeKind::NONE: + return o << "NONE"; */ + case NodeKind::BOOLEAN: + return o << "BOOLEAN"; + case NodeKind::NUMBER: + return o << "NUMBER"; + case NodeKind::STRING: + return o << "STRING"; + case NodeKind::OBJECT: + return o << "OBJECT"; + case NodeKind::ARRAY: + return o << "ARRAY"; + } + // Never happens + return o; +} diff --git a/tp-json/Node_ptr.hpp b/tp-json/Node_ptr.hpp new file mode 100644 index 00000000..d9f4bdd3 --- /dev/null +++ b/tp-json/Node_ptr.hpp @@ -0,0 +1,7 @@ +#pragma once + +#include + +class Node; + +using Node_ptr = std::unique_ptr; \ No newline at end of file diff --git a/tp-json/NumberLeaf.cpp b/tp-json/NumberLeaf.cpp new file mode 100644 index 00000000..a848804c --- /dev/null +++ b/tp-json/NumberLeaf.cpp @@ -0,0 +1,10 @@ +#include "NumberLeaf.hpp" + +bool NumberLeaf::operator==(const Node& other) const +{ + if (!(other.is_of_kind(kind()))) + { + return false; + } + return (_data == other.as_NumberLeaf()->_data); +} \ No newline at end of file diff --git a/tp-json/NumberLeaf.hpp b/tp-json/NumberLeaf.hpp new file mode 100644 index 00000000..1511541c --- /dev/null +++ b/tp-json/NumberLeaf.hpp @@ -0,0 +1,30 @@ +#pragma once + +#include "Node.hpp" + +class NumberLeaf : public Node +{ +private: + int _data; + +public: + inline const int& data() const { return _data; } + + std::string print() const override { return std::to_string(_data); } + + NumberLeaf(int data) + : Node { NodeKind::NUMBER } + , _data { data } + {} + + static std::unique_ptr make_ptr(int data) { return std::make_unique(data); } + + bool operator==(const Node& other) const override; + + NumberLeaf* as_NumberLeaf() override { return this; } + NumberLeaf const* as_NumberLeaf() const override { return this; } + + Node_ptr deep_copy() const override { return make_ptr(data()); } + + std::string dot_label() const override { return std::to_string(_data); } +}; diff --git a/tp-json/ObjectNode.cpp b/tp-json/ObjectNode.cpp new file mode 100644 index 00000000..1af8b27c --- /dev/null +++ b/tp-json/ObjectNode.cpp @@ -0,0 +1 @@ +#include "ObjectNode.hpp" \ No newline at end of file diff --git a/tp-json/ObjectNode.hpp b/tp-json/ObjectNode.hpp new file mode 100644 index 00000000..d3ce80da --- /dev/null +++ b/tp-json/ObjectNode.hpp @@ -0,0 +1,125 @@ +#pragma once + +#include "Node.hpp" + +#include +#include +#include + +class ObjectNode : public Node +{ +private: + std::map _data; + +public: + const std::map& data() const { return _data; } + + ObjectNode() + : Node(NodeKind::OBJECT) + {} + + ObjectNode(std::map data) + : Node(NodeKind::OBJECT) + , _data(std::move(data)) + {} + + static std::unique_ptr make_ptr(std::map data = {}) + { + return std::make_unique(std::move(data)); + } + + void add(std::string key, Node_ptr value) { _data.emplace(std::move(key), std::move(value)); } + + size_t children_count() const { return _data.size(); } + + std::string print() const override + { + std::string result = "{"; + bool first = true; + for (auto const& el : _data) + { + if (first) + first = false; + else + result += ","; + result += '"' + el.first + "\":"; + result += el.second->print(); + } + result += '}'; + return result; + } + + ObjectNode* as_ObjectNode() override { return this; } + const ObjectNode* as_ObjectNode() const override { return this; } + + inline bool operator==(const Node& other) const override + { + if (!(other.is_of_kind(kind()))) + { + std::cerr << kind() << "!=" << other.kind() << std::endl; + return false; + } + ObjectNode const* other_s = other.as_ObjectNode(); + size_t size = children_count(); + if (other_s->children_count() != size) + { + std::cerr << size << " != " << other_s->children_count() << std::endl; + return false; + } + for (auto& pair : other_s->_data) + { + auto it = _data.find(pair.first); + if (it == _data.end()) + { + std::cerr << pair.first << std::endl; + return false; + } + if (*(it->second) != *(pair.second)) + { + std::cerr << pair.first << std::endl; + std::cerr << *pair.second << std::endl; + std::cerr << *it->second << std::endl; + return false; + } + } + return true; + } + + size_t height() const override + { + return std::accumulate(_data.begin(), _data.end(), 0, + [](size_t i, auto const& pair) + { return std::max(i, 1u + pair.second->height()); }); + } + + virtual size_t node_count() const override + { + return std::accumulate(_data.begin(), _data.end(), 1, + [](size_t i, auto const& pair) { return i + pair.second->node_count(); }); + } + + Node* at(std::string const& key) { return &*_data.at(key); } + const Node* at(std::string const& key) const { return &*_data.at(key); } + + Node_ptr deep_copy() const override + { + auto result = make_ptr(); + for (auto const& [key, child] : this->data()) + result->add(key, child->deep_copy()); + return result; + } + + std::string dot_label() const override { return "{...}"; } + + virtual void dot(std::ostream& o) const + { + o << "subgraph cluster_" << dot_id() << " {style=invis;" << std::endl; + Node::dot(o); + for (auto& [k, v] : _data) + { + v->dot(o); + o << dot_id() << " -> " << v->dot_id() << " [label= \"" << k << "\"];" << std::endl; + } + o << "}" << std::endl; + } +}; diff --git a/tp-json/StringLeaf.cpp b/tp-json/StringLeaf.cpp new file mode 100644 index 00000000..f9d28654 --- /dev/null +++ b/tp-json/StringLeaf.cpp @@ -0,0 +1 @@ +#include "StringLeaf.hpp" \ No newline at end of file diff --git a/tp-json/StringLeaf.hpp b/tp-json/StringLeaf.hpp new file mode 100644 index 00000000..dc5ceec2 --- /dev/null +++ b/tp-json/StringLeaf.hpp @@ -0,0 +1,37 @@ +#pragma once + +#include "Node.hpp" + +class StringLeaf : public Node +{ +private: + std::string _data; + +public: + std::string print() const override { return '"' + _data + '"'; } + StringLeaf(std::string data) + : Node { NodeKind::STRING } + , _data { data } + {} + + static inline std::unique_ptr make_ptr(std::string s) + { + return std::make_unique(std::move(s)); + } + + StringLeaf* as_StringLeaf() override { return this; } + const StringLeaf* as_StringLeaf() const override { return this; } + + inline bool operator==(const Node& other) const override + { + if (!(other.is_of_kind(kind()))) + return false; + return (_data == other.as_StringLeaf()->_data); + } + + inline const std::string& data() const { return _data; } + + Node_ptr deep_copy() const override { return make_ptr(data()); } + + std::string dot_label() const override { return "\\\"" + _data + "\\\""; } +}; diff --git a/tp-json/code_fourni_aux_etudiants/ArrayNode.hpp b/tp-json/code_fourni_aux_etudiants/ArrayNode.hpp new file mode 100644 index 00000000..6f70f09b --- /dev/null +++ b/tp-json/code_fourni_aux_etudiants/ArrayNode.hpp @@ -0,0 +1 @@ +#pragma once diff --git a/tp-json/code_fourni_aux_etudiants/BooleanNode.hpp b/tp-json/code_fourni_aux_etudiants/BooleanNode.hpp new file mode 100644 index 00000000..6f70f09b --- /dev/null +++ b/tp-json/code_fourni_aux_etudiants/BooleanNode.hpp @@ -0,0 +1 @@ +#pragma once diff --git a/tp-json/code_fourni_aux_etudiants/Node.hpp b/tp-json/code_fourni_aux_etudiants/Node.hpp new file mode 100644 index 00000000..7a31b0be --- /dev/null +++ b/tp-json/code_fourni_aux_etudiants/Node.hpp @@ -0,0 +1,7 @@ +#pragma once + +#include "NodeKind.hpp" +#include "Node_ptr.hpp" + +class Node +{}; \ No newline at end of file diff --git a/tp-json/code_fourni_aux_etudiants/NodeKind.hpp b/tp-json/code_fourni_aux_etudiants/NodeKind.hpp new file mode 100644 index 00000000..cf970b14 --- /dev/null +++ b/tp-json/code_fourni_aux_etudiants/NodeKind.hpp @@ -0,0 +1,35 @@ +#pragma once + +#include + +enum class NodeKind +{ + /* NONE is used for NullNode since NULL is reserved */ + NONE, + BOOLEAN, + NUMBER, + STRING, + ARRAY, + OBJECT +}; + +inline std::ostream& operator<<(std::ostream& o, NodeKind kind) +{ + switch (kind) + { + case NodeKind::NONE: + return o << "NONE"; + case NodeKind::BOOLEAN: + return o << "BOOLEAN"; + case NodeKind::NUMBER: + return o << "NUMBER"; + case NodeKind::STRING: + return o << "STRING"; + case NodeKind::OBJECT: + return o << "OBJECT"; + case NodeKind::ARRAY: + return o << "ARRAY"; + } + // Never happens + return o; +} diff --git a/tp-json/code_fourni_aux_etudiants/Node_ptr.hpp b/tp-json/code_fourni_aux_etudiants/Node_ptr.hpp new file mode 100644 index 00000000..50098bea --- /dev/null +++ b/tp-json/code_fourni_aux_etudiants/Node_ptr.hpp @@ -0,0 +1,3 @@ +#pragma once + +using Node_ptr = void /* TODO!! */; \ No newline at end of file diff --git a/tp-json/code_fourni_aux_etudiants/NumberNode.hpp b/tp-json/code_fourni_aux_etudiants/NumberNode.hpp new file mode 100644 index 00000000..6f70f09b --- /dev/null +++ b/tp-json/code_fourni_aux_etudiants/NumberNode.hpp @@ -0,0 +1 @@ +#pragma once diff --git a/tp-json/code_fourni_aux_etudiants/ObjectNode.hpp b/tp-json/code_fourni_aux_etudiants/ObjectNode.hpp new file mode 100644 index 00000000..6f70f09b --- /dev/null +++ b/tp-json/code_fourni_aux_etudiants/ObjectNode.hpp @@ -0,0 +1 @@ +#pragma once diff --git a/tp-json/code_fourni_aux_etudiants/StringNode.hpp b/tp-json/code_fourni_aux_etudiants/StringNode.hpp new file mode 100644 index 00000000..6f70f09b --- /dev/null +++ b/tp-json/code_fourni_aux_etudiants/StringNode.hpp @@ -0,0 +1 @@ +#pragma once diff --git a/tp-json/code_fourni_aux_etudiants/all_nodes.hpp b/tp-json/code_fourni_aux_etudiants/all_nodes.hpp new file mode 100644 index 00000000..d4d973b9 --- /dev/null +++ b/tp-json/code_fourni_aux_etudiants/all_nodes.hpp @@ -0,0 +1,9 @@ +#pragma once + +#include "ArrayNode.hpp" +#include "BooleanNode.hpp" +#include "Node.hpp" +#include "NodeKind.hpp" +#include "NumberNode.hpp" +#include "ObjectNode.hpp" +#include "StringNode.hpp" diff --git a/tp-json/cpp2022.svg b/tp-json/cpp2022.svg new file mode 100644 index 00000000..2d260509 --- /dev/null +++ b/tp-json/cpp2022.svg @@ -0,0 +1,498 @@ + + + + + + +G + + +cluster_n0x5605b4a52f00 + + +cluster_n0x5605b4a55570 + + +cluster_n0x5605b4a55cb0 + + +cluster_n0x5605b4a55d90 + + +cluster_n0x5605b4a55b10 + + +cluster_n0x5605b4a55940 + + +cluster_n0x5605b4a55760 + + +cluster_n0x5605b4a555a0 + + +cluster_n0x5605b4a55370 + + + +n0x5605b4a52f00 + +{...} + + + +n0x5605b4a55270 + +"M1" + + + +n0x5605b4a52f00->n0x5605b4a55270 + + +level + + + +n0x5605b4a55300 + +true + + + +n0x5605b4a52f00->n0x5605b4a55300 + + +mandatory + + + +n0x5605b4a551e0 + +"C++" + + + +n0x5605b4a52f00->n0x5605b4a551e0 + + +name + + + +n0x5605b4a55570 + +[...] + + + +n0x5605b4a52f00->n0x5605b4a55570 + + +sessions + + + +n0x5605b4a55370 + +[...] + + + +n0x5605b4a52f00->n0x5605b4a55370 + + +teachers + + + +n0x5605b4a55440 + +2022 + + + +n0x5605b4a52f00->n0x5605b4a55440 + + +year + + + +n0x5605b4a55cb0 + +{...} + + + +n0x5605b4a55570->n0x5605b4a55cb0 + + + + + +n0x5605b4a55b10 + +{...} + + + +n0x5605b4a55570->n0x5605b4a55b10 + + + + + +n0x5605b4a55940 + +{...} + + + +n0x5605b4a55570->n0x5605b4a55940 + + + + + +n0x5605b4a55760 + +{...} + + + +n0x5605b4a55570->n0x5605b4a55760 + + + + + +n0x5605b4a555a0 + +{...} + + + +n0x5605b4a55570->n0x5605b4a555a0 + + + + + +n0x5605b4a55d00 + +"Project" + + + +n0x5605b4a55cb0->n0x5605b4a55d00 + + +kind + + + +n0x5605b4a55d90 + +[...] + + + +n0x5605b4a55cb0->n0x5605b4a55d90 + + +weeks + + + +n0x5605b4a55f70 + +12 + + + +n0x5605b4a55d90->n0x5605b4a55f70 + + + + + +n0x5605b4a55f50 + +11 + + + +n0x5605b4a55d90->n0x5605b4a55f50 + + + + + +n0x5605b4a55f30 + +10 + + + +n0x5605b4a55d90->n0x5605b4a55f30 + + + + + +n0x5605b4a55ec0 + +9 + + + +n0x5605b4a55d90->n0x5605b4a55ec0 + + + + + +n0x5605b4a55e70 + +8 + + + +n0x5605b4a55d90->n0x5605b4a55e70 + + + + + +n0x5605b4a55e30 + +7 + + + +n0x5605b4a55d90->n0x5605b4a55e30 + + + + + +n0x5605b4a55e50 + +6 + + + +n0x5605b4a55d90->n0x5605b4a55e50 + + + + + +n0x5605b4a55e10 + +5 + + + +n0x5605b4a55d90->n0x5605b4a55e10 + + + + + +n0x5605b4a55b60 + +"TP" + + + +n0x5605b4a55b10->n0x5605b4a55b60 + + +kind + + + +n0x5605b4a55920 + +4 + + + +n0x5605b4a55b10->n0x5605b4a55920 + + +number + + + +n0x5605b4a55c90 + +4 + + + +n0x5605b4a55b10->n0x5605b4a55c90 + + +week + + + +n0x5605b4a55990 + +"TP" + + + +n0x5605b4a55940->n0x5605b4a55990 + + +kind + + + +n0x5605b4a55740 + +3 + + + +n0x5605b4a55940->n0x5605b4a55740 + + +number + + + +n0x5605b4a55ac0 + +3 + + + +n0x5605b4a55940->n0x5605b4a55ac0 + + +week + + + +n0x5605b4a557b0 + +"TP" + + + +n0x5605b4a55760->n0x5605b4a557b0 + + +kind + + + +n0x5605b4a55890 + +2 + + + +n0x5605b4a55760->n0x5605b4a55890 + + +number + + + +n0x5605b4a55900 + +2 + + + +n0x5605b4a55760->n0x5605b4a55900 + + +week + + + +n0x5605b4a555f0 + +"TP" + + + +n0x5605b4a555a0->n0x5605b4a555f0 + + +kind + + + +n0x5605b4a553e0 + +1 + + + +n0x5605b4a555a0->n0x5605b4a553e0 + + +number + + + +n0x5605b4a55720 + +1 + + + +n0x5605b4a555a0->n0x5605b4a55720 + + +week + + + +n0x5605b4a55460 + +"Victor" + + + +n0x5605b4a55370->n0x5605b4a55460 + + + + + +n0x5605b4a55400 + +"Matthias" + + + +n0x5605b4a55370->n0x5605b4a55400 + + + + + +n0x5605b4a553a0 + +"Céline" + + + +n0x5605b4a55370->n0x5605b4a553a0 + + + + + diff --git a/tp-json/enonce.md b/tp-json/enonce.md new file mode 100644 index 00000000..7ccbb9fc --- /dev/null +++ b/tp-json/enonce.md @@ -0,0 +1,83 @@ +# TP4 - Héritage + +Let but de ce TP est d'implémenter les classes permettant de représenter en mémoire un document JSON (JavaScript Object Notation). + + +## Test Driven Development + +Ce TP suit à nouveau en TDD, mais cette fois on utilisera le paradigme `ctest`. Chaque test est un programme indépendant, et il est réussi s'il s'éxecute sans erreur. Les programmes se trouvent dans le répertoire `tests`, et doivent être fait par ordre croissant. + +Pour activer les tests, décommenter la ligne suivante du `CMakeLists.txt` +``` +enable_testing() +``` + +Après reconfiguration, une nouvelle ligne apparait dans la barre en bas de vscode. Vous pouvez lancer la suite de tests en cliquant dessus. +Pour simplifier, chaque fichier + + +## Documents JSON + + +Un document JSON ressemble typiquement à (fichier `tests/json/cpp2022.json`): +```json +{ + "name":"C++", + "level":"M1", + "mandatory":true, + "teachers":["Céline","Matthias","Victor"], + "year": 2022, + "sessions":[ + {"kind":"TP", "number":1, "week":1}, + {"kind":"TP", "number":2, "week":2}, + {"kind":"TP", "number":3, "week":3}, + {"kind":"TP", "number":4, "week":4}, + {"kind":"Project", "weeks":[5,6,7,8,9,10,11,12]} + ] +} +``` + +Une valeur JSON est soit: +1. un *booléen*, par exemple `true`; +2. un *nombre*, par exemple `2022`; +3. une *chaîne de caractère*, par exemple `"C++"`; +4. une *liste* de valeurs JSON entre `[`...`]` et séparées par des virgules, par exemple `["Céline","Matthias","Victor"]`; +5. un *dictionnaire* entre `{`...`}` qui associe des clefs (chaîne de caractère avant le `:`) à des valeurs JSON (après le `:`), par exemple `{"kind":"Project", "weeks":[5,6,7,8,9,10,11,12]}` associe la clef `"kind"` à la valeur `"Project"`, la clef `"weeks"` à la valeur `[5,6,7,8,9,10,11,12]` + +Notez que le document entier est une valeur JSON, usuellement un dictionnaire. D'autres documents json se trouvent dans le dossier `json`. La plupart sont des petits exemples à des fins de tests (**ne les modifiez pas!**). +Au contraire, le document `json/pokedex.json` est représentatif de ce à quoi ressemble un document JSON réel, et sera utilisé dans les tests plus avancés. + + +## Arbres + +Un document peut se voir comme un arbre: +- Les booléens, entier et chaînes de caractères sont des feuilles de l'arbre. +- les listes et les objects sont des noeuds internes et ont pour fils chacune des valeurs à l'intérieur. + +Par exemple, le document donné en début de TP se représente: +![Représentation du document json cpp2022.json](cpp2022.svg) + + +## Code à produire + +Un document JSON sera représenté en mémoire comme un arbre dont les noeuds sont polymorphes: +- La classe `Node` sera la classe principales pour représenter un noeud dont on ne connaît pas le type exact. +- Les classes `BooleanLeaf`, `NumberLeaf`, `StringLeaf`, `ArrayNode`, `ObjectNode` représenterons les différents types de noeuds. +- Le type `Node_ptr` sera utilisée pour faire référence/pointer vers les enfants d'un noeud. Vous devrez choisir le type approprié (dans le fichier `Node_ptr.hpp`) à un certain point du TDD. +- Le type `NodeKind` est fourni, c'est une `enum` listant les différents types de noeuds. Il permet de savoir à l'exécution le type réel d'un `Node`. + +Quand c'est pertinent, on factorisera le code en utilisant l'héritage. +Par exemple, si on remarque que plusieurs classe partagent des fonctionnalités, il faudra les les centraliser dans une classe intermédiaire (entre `Node` et les sous-classes concrètes). + +## Parseur + +Un parseur de JSON est fourni (classe `JsonParser` dans le fichier `JsonParser.cpp`), et normalement vous n'aurez pas besoin de le modifier. +Il pourra éventuellement être utile de regarder ce fichier partir des tests qui utilisent le parser. + + + + + + + +## A vos claviers ! \ No newline at end of file diff --git a/tp-json/tests/00_basic_node.cpp b/tp-json/tests/00_basic_node.cpp new file mode 100644 index 00000000..6025c054 --- /dev/null +++ b/tp-json/tests/00_basic_node.cpp @@ -0,0 +1,10 @@ +/* This files exists to check that the basic files compile. */ +#include "../Node.hpp" +#include "assert.cpp" + +int main() +{ + Node* node1 = nullptr; + Node* node2 = nullptr; + ASSERT_EQUAL(node1, node2); +} diff --git a/tp-json/tests/01_basic_null.cpp b/tp-json/tests/01_basic_null.cpp new file mode 100644 index 00000000..77f2986c --- /dev/null +++ b/tp-json/tests/01_basic_null.cpp @@ -0,0 +1,14 @@ +// TO DELETE +#include "assert.cpp" + +int main() +{ + /* + NullNode p {}; + ASSERT_EQUAL(p.kind(), NodeKind::NONE); + ASSERT_EQUAL(p.print(), "null"); + + Node& r = p; + ASSERT_EQUAL(r.print(), "null"); + ASSERT_EQUAL(r.kind(), NodeKind::NONE);*/ +} diff --git a/tp-json/tests/02_basic_boolean.cpp b/tp-json/tests/02_basic_boolean.cpp new file mode 100644 index 00000000..611482c9 --- /dev/null +++ b/tp-json/tests/02_basic_boolean.cpp @@ -0,0 +1,16 @@ +#include "../BooleanLeaf.hpp" +#include "assert.cpp" + +int main() +{ + BooleanLeaf p { true }; + ASSERT_EQUAL(p.kind(), NodeKind::BOOLEAN); + ASSERT_EQUAL(p.print(), "true"); + + Node& r = p; + ASSERT_EQUAL(r.kind(), NodeKind::BOOLEAN); + ASSERT_EQUAL(r.print(), "true"); + + BooleanLeaf p2 { false }; + ASSERT_EQUAL(p2.print(), "false"); +} diff --git a/tp-json/tests/03_basic_number.cpp b/tp-json/tests/03_basic_number.cpp new file mode 100644 index 00000000..3841d9a4 --- /dev/null +++ b/tp-json/tests/03_basic_number.cpp @@ -0,0 +1,13 @@ +#include "../NumberLeaf.hpp" +#include "assert.cpp" + +int main() +{ + NumberLeaf p { 1 }; + ASSERT_EQUAL(p.kind(), NodeKind::NUMBER); + ASSERT_EQUAL(p.print(), "1"); + + Node& r = p; + ASSERT_EQUAL(r.kind(), NodeKind::NUMBER); + ASSERT_EQUAL(r.print(), "1"); +} diff --git a/tp-json/tests/04_basic_string.cpp b/tp-json/tests/04_basic_string.cpp new file mode 100644 index 00000000..5502b5d8 --- /dev/null +++ b/tp-json/tests/04_basic_string.cpp @@ -0,0 +1,13 @@ +#include "../StringLeaf.hpp" +#include "assert.cpp" + +int main() +{ + StringLeaf p { "Hello world!" }; + ASSERT_EQUAL(p.kind(), NodeKind::STRING); + ASSERT_EQUAL(p.print(), "\"Hello world!\""); + + Node& r = p; + ASSERT_EQUAL(r.kind(), NodeKind::STRING); + ASSERT_EQUAL(r.print(), "\"Hello world!\""); +} diff --git a/tp-json/tests/05_basic_array.cpp b/tp-json/tests/05_basic_array.cpp new file mode 100644 index 00000000..22bf3ca8 --- /dev/null +++ b/tp-json/tests/05_basic_array.cpp @@ -0,0 +1,13 @@ +#include "../ArrayNode.hpp" +#include "assert.cpp" + +int main() +{ + ArrayNode p {}; + ASSERT_EQUAL(p.kind(), NodeKind::ARRAY); + ASSERT_EQUAL(p.print(), "[]"); + + Node& r = p; + ASSERT_EQUAL(r.kind(), NodeKind::ARRAY); + ASSERT_EQUAL(r.print(), "[]"); +} diff --git a/tp-json/tests/06_basic_object.cpp b/tp-json/tests/06_basic_object.cpp new file mode 100644 index 00000000..4099668a --- /dev/null +++ b/tp-json/tests/06_basic_object.cpp @@ -0,0 +1,13 @@ +#include "../ObjectNode.hpp" +#include "assert.cpp" + +int main() +{ + ObjectNode p {}; + ASSERT_EQUAL(p.kind(), NodeKind::OBJECT); + ASSERT_EQUAL(p.print(), "{}"); + + Node& r = p; + ASSERT_EQUAL(r.kind(), NodeKind::OBJECT); + ASSERT_EQUAL(r.print(), "{}"); +} diff --git a/tp-json/tests/10_make_ptr.cpp b/tp-json/tests/10_make_ptr.cpp new file mode 100644 index 00000000..0c4b3923 --- /dev/null +++ b/tp-json/tests/10_make_ptr.cpp @@ -0,0 +1,20 @@ +#include "../ArrayNode.hpp" +#include "../BooleanLeaf.hpp" +#include "../Node.hpp" +#include "../NodeKind.hpp" +#include "../NumberLeaf.hpp" +#include "../ObjectNode.hpp" +#include "../StringLeaf.hpp" +#include "assert.cpp" + +#include + +int main() +{ + // Node_ptr null_node_ptr = NullNode::make_ptr(); + Node_ptr bool_node_ptr = BooleanLeaf::make_ptr(true); + Node_ptr int_node_ptr = NumberLeaf::make_ptr(1); + Node_ptr str_node_ptr = StringLeaf::make_ptr("Hello world"); + Node_ptr arr_node_ptr = ArrayNode::make_ptr(); + Node_ptr obj_node_ptr = ObjectNode::make_ptr(); +} diff --git a/tp-json/tests/11_array_add.cpp b/tp-json/tests/11_array_add.cpp new file mode 100644 index 00000000..14e7b395 --- /dev/null +++ b/tp-json/tests/11_array_add.cpp @@ -0,0 +1,28 @@ +#include "../ArrayNode.hpp" +#include "../BooleanLeaf.hpp" +#include "../Node.hpp" +#include "../NodeKind.hpp" +#include "../NumberLeaf.hpp" +#include "../ObjectNode.hpp" +#include "../StringLeaf.hpp" +#include "assert.cpp" + +int main() +{ + auto array_node_ptr = ArrayNode::make_ptr(); + size_t size = array_node_ptr->children_count(); + ASSERT_EQUAL(size, 0u); + + array_node_ptr->add(StringLeaf::make_ptr("H")); + array_node_ptr->add(NumberLeaf::make_ptr(3110)); + array_node_ptr->add(StringLeaf::make_ptr("!")); + + ASSERT_EQUAL(array_node_ptr->children_count(), 3u); + + // This is a raw literal, go check it out https://en.cppreference.com/w/cpp/language/string_literal */ + std::string target = R"---(["H",3110,"!"])---"; + // ^^^^^^ ^^^^^ + // The parts marked with ^ above are not part of the string; + + ASSERT_EQUAL(array_node_ptr->print(), target); +} diff --git a/tp-json/tests/12_object_add.cpp b/tp-json/tests/12_object_add.cpp new file mode 100644 index 00000000..d4131a41 --- /dev/null +++ b/tp-json/tests/12_object_add.cpp @@ -0,0 +1,34 @@ +#include "../ArrayNode.hpp" +#include "../BooleanLeaf.hpp" +#include "../Node.hpp" +#include "../NodeKind.hpp" +#include "../NumberLeaf.hpp" +#include "../ObjectNode.hpp" +#include "../StringLeaf.hpp" +#include "assert.cpp" + +int main() +{ + auto object_node_ptr = ObjectNode::make_ptr(); + size_t size = object_node_ptr->children_count(); + ASSERT_EQUAL(size, 0u); + + object_node_ptr->add("H", NumberLeaf::make_ptr(3110)); + object_node_ptr->add(" ", BooleanLeaf::make_ptr(true)); + + auto array_node_ptr = ArrayNode::make_ptr(); + array_node_ptr->add(NumberLeaf::make_ptr(0)); + array_node_ptr->add(StringLeaf::make_ptr("rld!")); + array_node_ptr->add(ArrayNode::make_ptr()); + array_node_ptr->add(ObjectNode::make_ptr()); + object_node_ptr->add("W", std::move(array_node_ptr)); + + ASSERT_EQUAL(object_node_ptr->children_count(), 3u); + + // This is a raw literal, go check it out https://en.cppreference.com/w/cpp/language/string_literal */ + std::string target = R"---({" ":true,"H":3110,"W":[0,"rld!",[],{}]})---"; + // ^^^^^^ ^^^^^ + // The parts marked with ^ above are not part of the string; + + ASSERT_EQUAL(object_node_ptr->print(), target); +} \ No newline at end of file diff --git a/tp-json/tests/13_height_and_nodecount.cpp b/tp-json/tests/13_height_and_nodecount.cpp new file mode 100644 index 00000000..ee94322c --- /dev/null +++ b/tp-json/tests/13_height_and_nodecount.cpp @@ -0,0 +1,34 @@ +#include "../ArrayNode.hpp" +#include "../BooleanLeaf.hpp" +#include "../Node.hpp" +#include "../NodeKind.hpp" +#include "../NumberLeaf.hpp" +#include "../ObjectNode.hpp" +#include "../StringLeaf.hpp" +#include "assert.cpp" + +#include + +int main() +{ + // Node_ptr null_node_ptr = NullNode::make_ptr(); + // ASSERT_EQUAL(null_node_ptr->height(), 0u); + // ASSERT_EQUAL(null_node_ptr->node_count(), 1u); + + Node_ptr true_node_ptr = BooleanLeaf::make_ptr(true); + Node_ptr false_node_ptr = BooleanLeaf::make_ptr(false); + auto array_node_ptr = ArrayNode::make_ptr(); + array_node_ptr->add(std::move(true_node_ptr)); + array_node_ptr->add(std::move(false_node_ptr)); + ASSERT_EQUAL(array_node_ptr->height(), 1u); + ASSERT_EQUAL(array_node_ptr->node_count(), 3u); + + auto object_node_ptr = ObjectNode::make_ptr(); + Node_ptr string_node_ptr = StringLeaf::make_ptr("Hello world"); + Node_ptr int_node_ptr = NumberLeaf::make_ptr(1); + object_node_ptr->add("array", std::move(array_node_ptr)); + object_node_ptr->add("string", std::move(string_node_ptr)); + object_node_ptr->add("int", std::move(int_node_ptr)); + ASSERT_EQUAL(object_node_ptr->height(), 2u); + ASSERT_EQUAL(object_node_ptr->node_count(), 6u); +} \ No newline at end of file diff --git a/tp-json/tests/14_object_dico.cpp b/tp-json/tests/14_object_dico.cpp new file mode 100644 index 00000000..2db8b675 --- /dev/null +++ b/tp-json/tests/14_object_dico.cpp @@ -0,0 +1,24 @@ +#include "../ArrayNode.hpp" +#include "../BooleanLeaf.hpp" +#include "../Node.hpp" +#include "../NodeKind.hpp" +#include "../NumberLeaf.hpp" +#include "../ObjectNode.hpp" +#include "../StringLeaf.hpp" +#include "assert.cpp" + +int main() +{ + auto object_node_ptr = ObjectNode::make_ptr(); + size_t size = object_node_ptr->children_count(); + ASSERT_EQUAL(size, 0u); + + object_node_ptr->add("key1", NumberLeaf::make_ptr(42)); + object_node_ptr->add("key1", BooleanLeaf::make_ptr(true)); + object_node_ptr->add("key2", StringLeaf::make_ptr("Hello World!")); + object_node_ptr->add("key2", ArrayNode::make_ptr()); + object_node_ptr->add("key2", StringLeaf::make_ptr("World, hello!")); + + ASSERT_EQUAL(object_node_ptr->height(), 1u); + ASSERT_EQUAL(object_node_ptr->node_count(), 3u); +} \ No newline at end of file diff --git a/tp-json/tests/21_parser_leaf.cpp b/tp-json/tests/21_parser_leaf.cpp new file mode 100644 index 00000000..d07488c8 --- /dev/null +++ b/tp-json/tests/21_parser_leaf.cpp @@ -0,0 +1,51 @@ +#include "../JsonParser.hpp" +#include "assert.cpp" + +#include + +int main(int argc, char** argv) +{ + if (argc < 2) + { + std::cout << "First command-line argument needs to be the path to the json resources."; + exit(EXIT_FAILURE); + } + std::string dir = std::string(argv[1]); + std::string filename; + Node_ptr node; + + filename = dir + "boolean_true.json"; + std::cerr << "Starting test with: " << filename << std::endl; + node = JsonParser::parse_from_file(filename); + ASSERT_EQUAL(node->height(), 0u); + ASSERT_EQUAL(node->node_count(), 1u); + ASSERT_EQUAL(node->kind(), NodeKind::BOOLEAN); + + filename = dir + "number_42.json"; + std::cerr << "Starting test with: " << filename << std::endl; + node = JsonParser::parse_from_file(filename); + ASSERT_EQUAL(node->height(), 0u); + ASSERT_EQUAL(node->node_count(), 1u); + ASSERT_EQUAL(node->kind(), NodeKind::NUMBER); + + filename = dir + "string_hello.json"; + std::cerr << "Starting test with: " << filename << std::endl; + node = JsonParser::parse_from_file(filename); + ASSERT_EQUAL(node->height(), 0u); + ASSERT_EQUAL(node->node_count(), 1u); + ASSERT_EQUAL(node->kind(), NodeKind::STRING); + + filename = dir + "array_empty.json"; + std::cerr << "Starting test with: " << filename << std::endl; + node = JsonParser::parse_from_file(filename); + ASSERT_EQUAL(node->height(), 0u); + ASSERT_EQUAL(node->node_count(), 1u); + ASSERT_EQUAL(node->kind(), NodeKind::ARRAY); + + filename = dir + "object_empty.json"; + std::cerr << "Starting test with: " << filename << std::endl; + node = JsonParser::parse_from_file(filename); + ASSERT_EQUAL(node->height(), 0u); + ASSERT_EQUAL(node->node_count(), 1u); + ASSERT_EQUAL(node->kind(), NodeKind::OBJECT); +} \ No newline at end of file diff --git a/tp-json/tests/22_parser_array.cpp b/tp-json/tests/22_parser_array.cpp new file mode 100644 index 00000000..230ddae1 --- /dev/null +++ b/tp-json/tests/22_parser_array.cpp @@ -0,0 +1,30 @@ +#include "../JsonParser.hpp" +#include "assert.cpp" + +#include + +int main(int argc, char** argv) +{ + if (argc < 2) + { + std::cout << "First command-line argument needs to be the path to the json resources."; + exit(EXIT_FAILURE); + } + std::string dir = std::string(argv[1]); + std::string filename; + Node_ptr node; + + filename = dir + "array_range10.json"; + std::cerr << "Starting test with: " << filename << std::endl; + node = JsonParser::parse_from_file(filename); + ASSERT_EQUAL(node->height(), 1u); + ASSERT_EQUAL(node->node_count(), 11u); + ASSERT_EQUAL(node->kind(), NodeKind::ARRAY); + + filename = dir + "array_hexadecimal.json"; + std::cerr << "Starting test with: " << filename << std::endl; + node = JsonParser::parse_from_file(filename); + ASSERT_EQUAL(node->height(), 4u); + ASSERT_EQUAL(node->node_count(), 31u); + ASSERT_EQUAL(node->kind(), NodeKind::ARRAY); +} \ No newline at end of file diff --git a/tp-json/tests/23_parser_object.cpp b/tp-json/tests/23_parser_object.cpp new file mode 100644 index 00000000..63dbbb95 --- /dev/null +++ b/tp-json/tests/23_parser_object.cpp @@ -0,0 +1,31 @@ +#include "../JsonParser.hpp" +#include "assert.cpp" + +#include + +int main(int argc, char** argv) +{ + if (argc < 2) + { + std::cout << "First command-line argument needs to be the path to the json resources." << std::endl; + exit(EXIT_FAILURE); + } + + std::string dir = std::string(argv[1]); + std::string filename; + Node_ptr node; + + filename = dir + "object_empty.json"; + std::cerr << "Starting test with: " << filename << std::endl; + node = JsonParser::parse_from_file(filename); + ASSERT_EQUAL(node->height(), 0u); + ASSERT_EQUAL(node->node_count(), 1u); + ASSERT_EQUAL(node->kind(), NodeKind::OBJECT); + + filename = dir + "object_alphabet.json"; + std::cerr << "Starting test with: " << filename << std::endl; + node = JsonParser::parse_from_file(filename); + ASSERT_EQUAL(node->height(), 1u); + ASSERT_EQUAL(node->node_count(), 27u); + ASSERT_EQUAL(node->kind(), NodeKind::OBJECT); +} \ No newline at end of file diff --git a/tp-json/tests/24_parser_pokedex.cpp b/tp-json/tests/24_parser_pokedex.cpp new file mode 100644 index 00000000..872285e0 --- /dev/null +++ b/tp-json/tests/24_parser_pokedex.cpp @@ -0,0 +1,24 @@ +#include "../JsonParser.hpp" +#include "assert.cpp" + +#include + +int main(int argc, char** argv) +{ + if (argc < 2) + { + std::cout << "First command-line argument needs to be the path to the json resources."; + exit(EXIT_FAILURE); + } + + std::string dir = std::string(argv[1]); + std::string filename; + Node_ptr node; + + filename = dir + "pokedex.json"; + std::cerr << "Starting test with: " << filename << std::endl; + node = JsonParser::parse_from_file(filename); + ASSERT_EQUAL(node->height(), 5u); + ASSERT_EQUAL(node->node_count(), 3779u); + ASSERT_EQUAL(node->kind(), NodeKind::OBJECT); +} \ No newline at end of file diff --git a/tp-json/tests/30_output_operator.cpp b/tp-json/tests/30_output_operator.cpp new file mode 100644 index 00000000..fc39d9bf --- /dev/null +++ b/tp-json/tests/30_output_operator.cpp @@ -0,0 +1,13 @@ +#include "../ArrayNode.hpp" +#include "assert.cpp" + +#include + +int main() +{ + // This only tests for the existence of the operator<< + // in order to have ASSERT_EQUAL to compile + // We do not impose the format: cf optional questions. + Node_ptr node = ArrayNode::make_ptr(); + std::cout << (*node) << std::endl; +} diff --git a/tp-json/tests/31_explicit_cast.cpp b/tp-json/tests/31_explicit_cast.cpp new file mode 100644 index 00000000..5bd407a9 --- /dev/null +++ b/tp-json/tests/31_explicit_cast.cpp @@ -0,0 +1,44 @@ +#include "../ArrayNode.hpp" +#include "../BooleanLeaf.hpp" +#include "../Node.hpp" +#include "../NodeKind.hpp" +#include "../NumberLeaf.hpp" +#include "../ObjectNode.hpp" +#include "../StringLeaf.hpp" +#include "assert.cpp" + +int main() +{ + // Node_ptr node = NullNode::make_ptr(); + + // ASSERT_UNEQUAL(node->as_NullNode(), nullptr); + + /*ASSERT_EQUAL(node->as_BooleanLeaf(), nullptr); + ASSERT_EQUAL(node->as_NumberNode(), nullptr); + ASSERT_EQUAL(node->as_StringNode(), nullptr); + ASSERT_EQUAL(node->as_ArrayNode(), nullptr); + ASSERT_EQUAL(node->as_ObjectNode(), nullptr);*/ + + Node_ptr node = BooleanLeaf::make_ptr(true); + ASSERT_UNEQUAL(node->as_BooleanLeaf(), nullptr); + ASSERT_EQUAL(node->as_NumberLeaf(), nullptr); + ASSERT_EQUAL(node->as_StringLeaf(), nullptr); + ASSERT_EQUAL(node->as_ArrayNode(), nullptr); + ASSERT_EQUAL(node->as_ObjectNode(), nullptr); + + node = NumberLeaf::make_ptr(1); + ASSERT_UNEQUAL(node->as_NumberLeaf(), nullptr); + ASSERT_EQUAL(node->as_BooleanLeaf(), nullptr); + ASSERT_EQUAL(node->as_StringLeaf(), nullptr); + ASSERT_EQUAL(node->as_ArrayNode(), nullptr); + ASSERT_EQUAL(node->as_ObjectNode(), nullptr); + + node = StringLeaf::make_ptr("Hello world"); + ASSERT_UNEQUAL(node->as_StringLeaf(), nullptr); + + node = ArrayNode::make_ptr(); + ASSERT_UNEQUAL(node->as_ArrayNode(), nullptr); + + node = ObjectNode::make_ptr(); + ASSERT_UNEQUAL(node->as_ObjectNode(), nullptr); +} \ No newline at end of file diff --git a/tp-json/tests/32_explicit_cast_const.cpp b/tp-json/tests/32_explicit_cast_const.cpp new file mode 100644 index 00000000..e3a09df4 --- /dev/null +++ b/tp-json/tests/32_explicit_cast_const.cpp @@ -0,0 +1,46 @@ +#include "../ArrayNode.hpp" +#include "../BooleanLeaf.hpp" +#include "../Node.hpp" +#include "../NodeKind.hpp" +#include "../NumberLeaf.hpp" +#include "../ObjectNode.hpp" +#include "../StringLeaf.hpp" +#include "assert.cpp" + +int main() +{ + Node_ptr node; + { + node = BooleanLeaf::make_ptr(true); + auto const& node_const_ref = *node; + ASSERT_UNEQUAL(node_const_ref.as_BooleanLeaf(), nullptr); + ASSERT_EQUAL(node_const_ref.as_NumberLeaf(), nullptr); + ASSERT_EQUAL(node_const_ref.as_StringLeaf(), nullptr); + ASSERT_EQUAL(node_const_ref.as_ArrayNode(), nullptr); + ASSERT_EQUAL(node_const_ref.as_ObjectNode(), nullptr); + } + + { + node = NumberLeaf::make_ptr(1); + auto const& node_const_ref = *node; + ASSERT_UNEQUAL(node_const_ref.as_NumberLeaf(), nullptr); + } + + { + node = StringLeaf::make_ptr("Hello world"); + auto const& node_const_ref = *node; + ASSERT_UNEQUAL(node_const_ref.as_StringLeaf(), nullptr); + } + + { + node = ArrayNode::make_ptr(); + auto const& node_const_ref = *node; + ASSERT_UNEQUAL(node_const_ref.as_ArrayNode(), nullptr); + } + + { + node = ObjectNode::make_ptr(); + auto const& node_const_ref = *node; + ASSERT_UNEQUAL(node_const_ref.as_ObjectNode(), nullptr); + } +} \ No newline at end of file diff --git a/tp-json/tests/33_children_count.cpp b/tp-json/tests/33_children_count.cpp new file mode 100644 index 00000000..bf71feec --- /dev/null +++ b/tp-json/tests/33_children_count.cpp @@ -0,0 +1,13 @@ +/* TODO */ + +#include "../JsonParser.hpp" +#include "assert.cpp" + +int main(int argc, char**) +{ + if (argc < 1) + { + std::cout << "First command-line argument needs to be where are the json resources."; + exit(EXIT_FAILURE); + } +} \ No newline at end of file diff --git a/tp-json/tests/34_has_child.cpp b/tp-json/tests/34_has_child.cpp new file mode 100644 index 00000000..34e1f15e --- /dev/null +++ b/tp-json/tests/34_has_child.cpp @@ -0,0 +1,14 @@ + +/* TODO */ + +#include "../JsonParser.hpp" +#include "assert.cpp" + +int main(int argc, char**) +{ + if (argc < 1) + { + std::cout << "First command-line argument needs to be where are the json resources."; + exit(EXIT_FAILURE); + } +} \ No newline at end of file diff --git a/tp-json/tests/35_at.cpp b/tp-json/tests/35_at.cpp new file mode 100644 index 00000000..3798f177 --- /dev/null +++ b/tp-json/tests/35_at.cpp @@ -0,0 +1,15 @@ + + +/* TODO */ + +#include "../JsonParser.hpp" +#include "assert.cpp" + +int main(int argc, char**) +{ + if (argc < 1) + { + std::cout << "First command-line argument needs to be where are the json resources."; + exit(EXIT_FAILURE); + } +} \ No newline at end of file diff --git a/tp-json/tests/38_equality.cpp b/tp-json/tests/38_equality.cpp new file mode 100644 index 00000000..51ca00d0 --- /dev/null +++ b/tp-json/tests/38_equality.cpp @@ -0,0 +1,50 @@ +#include "../JsonParser.hpp" +#include "assert.cpp" + +int main(int argc, char** argv) +{ + if (argc < 1) + { + std::cout << "First command-line argument needs to be where are the json resources."; + exit(EXIT_FAILURE); + } + std::string dir = std::string(argv[1]); + std::string filename; + Node_ptr node; + + filename = dir + "boolean_true.json"; + std::cerr << "Starting test with: " << filename << std::endl; + node = JsonParser::parse_from_file(filename); + ASSERT_EQUAL(*node, *BooleanLeaf::make_ptr(true)); + + filename = dir + "number_42.json"; + std::cerr << "Starting test with: " << filename << std::endl; + node = JsonParser::parse_from_file(filename); + ASSERT_EQUAL(*node, *NumberLeaf::make_ptr(42)); + + filename = dir + "string_hello.json"; + std::cerr << "Starting test with: " << filename << std::endl; + node = JsonParser::parse_from_file(filename); + ASSERT_EQUAL(*node, *StringLeaf::make_ptr("Hello")); + + filename = dir + "object_alphabet.json"; + std::cerr << "Starting test with: " << filename << std::endl; + auto target1 = ObjectNode::make_ptr(); + for (char c = 'a'; c <= 'z'; c++) + target1->add(std::string(1, c), NumberLeaf::make_ptr(c)); + node = JsonParser::parse_from_file(filename); + ASSERT_EQUAL(*node, *target1); + + filename = dir + "array_range10.json"; + std::cerr << "Starting test with: " << filename << std::endl; + auto target2 = ArrayNode::make_ptr(); + for (unsigned i = 0; i < 10; i++) + target2->add(NumberLeaf::make_ptr(i)); + node = JsonParser::parse_from_file(filename); + ASSERT_EQUAL(*node, *target2); + + filename = dir + "pokedex.json"; + std::cerr << "Starting test with: " << filename << std::endl; + node = JsonParser::parse_from_file(filename); + ASSERT_EQUAL(*node, *node); +} \ No newline at end of file diff --git a/tp-json/tests/39_copy.cpp b/tp-json/tests/39_copy.cpp new file mode 100644 index 00000000..d32bb043 --- /dev/null +++ b/tp-json/tests/39_copy.cpp @@ -0,0 +1,34 @@ +#include "../JsonParser.hpp" +#include "assert.cpp" + +int main(int argc, char** argv) +{ + if (argc < 1) + { + std::cout << "First command-line argument needs to be where are the json resources."; + exit(EXIT_FAILURE); + } + std::string dir = std::string(argv[1]); + std::string filename; + Node_ptr original; + + filename = dir + "pokedex.json"; + original = JsonParser::parse_from_file(filename); + + // We make the copy multiple times to ensure that deallocation works + for (unsigned i = 0; i < 100; ++i) + { + Node_ptr copy = original->deep_copy(); + ASSERT_EQUAL(copy->height(), 5u); + ASSERT_EQUAL(copy->node_count(), 3779u); + ASSERT_EQUAL(copy->kind(), NodeKind::OBJECT); + + ASSERT_EQUAL(*original, *copy); + + ASSERT_UNEQUAL(&*original, &*copy); + } + + // We browse the original tree to check that it was not deallocated. + ASSERT_EQUAL(original->height(), 5u); + ASSERT_EQUAL(original->node_count(), 3779u); +} \ No newline at end of file diff --git a/tp-json/tests/40_pokedex.cpp b/tp-json/tests/40_pokedex.cpp new file mode 100644 index 00000000..c37d69ee --- /dev/null +++ b/tp-json/tests/40_pokedex.cpp @@ -0,0 +1,99 @@ +#include "../JsonParser.hpp" +#include "assert.cpp" + +#include +#include +#include +#include + +int main(int argc, char** argv) +{ + if (argc < 2) + { + std::cout << "First command-line argument needs to be the path to the json resources."; + exit(EXIT_FAILURE); + } + std::string dir = std::string(argv[1]); + std::string filename; + + { + Node_ptr node; + + filename = dir + "pokedex.json"; + std::cerr << "Starting test with: " << filename << std::endl; + node = JsonParser::parse_from_file(filename); + + ASSERT_EQUAL(node->kind(), NodeKind::OBJECT) + ASSERT_UNEQUAL(node->as_ObjectNode()->at("pokemon"), nullptr) + ASSERT_EQUAL(node->as_ObjectNode()->at("pokemon")->kind(), NodeKind::ARRAY) + + std::unordered_map pokemon_id_by_name; + for (const auto& pokemon_node : *(node->as_ObjectNode()->at("pokemon")->as_ArrayNode())) + { + ASSERT_EQUAL(pokemon_node->kind(), NodeKind::OBJECT) + auto pokemon_obj = pokemon_node->as_ObjectNode(); + + ASSERT_UNEQUAL(pokemon_obj->at("name"), nullptr) + ASSERT_EQUAL(pokemon_obj->at("name")->kind(), NodeKind::STRING) + const std::string& name = pokemon_obj->at("name")->as_StringLeaf()->data(); + + ASSERT_UNEQUAL(pokemon_obj->at("id"), nullptr) + ASSERT_UNEQUAL(pokemon_obj->at("id")->as_NumberLeaf(), nullptr) + unsigned id = pokemon_obj->at("id")->as_NumberLeaf()->data(); + + pokemon_id_by_name[name] = id; + } + + std::unordered_map> pokemon_id_by_type; + for (const auto& pokemon_node : *(node->as_ObjectNode()->at("pokemon")->as_ArrayNode())) + { + ASSERT_EQUAL(pokemon_node->kind(), NodeKind::OBJECT) + ASSERT_UNEQUAL(pokemon_node->as_ObjectNode()->at("type"), nullptr) + ASSERT_EQUAL(pokemon_node->as_ObjectNode()->at("type")->kind(), NodeKind::ARRAY) + for (const auto& type_node : *(pokemon_node->as_ObjectNode()->at("type")->as_ArrayNode())) + { + ASSERT_UNEQUAL(type_node->as_StringLeaf(), nullptr) + std::string const& type = type_node->as_StringLeaf()->data(); + auto it = pokemon_id_by_type.find(type); + if (it == pokemon_id_by_type.end()) + it = pokemon_id_by_type.emplace(type, std::vector()).first; + it->second.emplace_back(pokemon_node->as_ObjectNode()->at("id")->as_NumberLeaf()->data()); + } + } + + for (const auto& pair : pokemon_id_by_type) + { + std::cerr << pair.first << ": ["; + bool b = false; + for (auto i : pair.second) + { + if (b) + std::cerr << ", "; + else + b = true; + std::cerr << i; + } + std::cerr << "]" << std::endl; + } + + std::vector> const tests = { { "Bulbasaur", "Grass" }, + { "Bulbasaur", "Poison" }, + { "Wartortle", "Water" }, + { "Pikachu", "Electric" } }; + for (auto const& [name, type] : tests) + { + std::cerr << "Checking that \"" << name << "\" is of type \"" << type << "\"." << std::endl; + + auto const& list_it = pokemon_id_by_type.find(type); + auto const& elt_it = pokemon_id_by_name.find(name); + + assert(list_it != pokemon_id_by_type.end()); + assert(elt_it != pokemon_id_by_name.end()); + + const std::vector& poke_list = list_it->second; + unsigned poke_elt = elt_it->second; + + assert(std::find(poke_list.begin(), poke_list.end(), poke_elt) != poke_list.end()); + } + } +} \ No newline at end of file diff --git a/tp-json/tests/99_dot.cpp b/tp-json/tests/99_dot.cpp new file mode 100644 index 00000000..6fde7567 --- /dev/null +++ b/tp-json/tests/99_dot.cpp @@ -0,0 +1,37 @@ +#include "../JsonParser.hpp" +#include "assert.cpp" + +#include +#include +#include +#include +#include + +void dot(std::ostream& o, Node_ptr const& node) +{ + o << "digraph G {" << std::endl; + o << "rankdir=LR;" << std::endl; + node->dot(o); + o << "}" << std::endl; +} +int main(int argc, char** argv) +{ + if (argc < 2) + { + std::cout << "First command-line argument needs to be the path to the json resources."; + exit(EXIT_FAILURE); + } + std::string dir = std::string(argv[1]); + std::string filename; + + { + Node_ptr node; + + filename = dir + "cpp2022.json"; + std::cerr << "Starting test with: " << filename << std::endl; + node = JsonParser::parse_from_file(filename); + + std::ofstream out("/tmp/test.gv"); + dot(out, node); + } +} \ No newline at end of file diff --git a/tp-json/tests/CMakeLists.txt b/tp-json/tests/CMakeLists.txt new file mode 100644 index 00000000..7c3100c6 --- /dev/null +++ b/tp-json/tests/CMakeLists.txt @@ -0,0 +1,24 @@ +file(GLOB TESTS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} [0-9]*.cpp) + +SET(OLDVAR 0) +SET(VAR 1) +FOREACH(TEST ${TESTS}) + MESSAGE(STATUS test:${TEST}) + add_executable(tp-json-test-${TEST} EXCLUDE_FROM_ALL ${TP-JSON-SOURCES} "${CMAKE_CURRENT_SOURCE_DIR}/${TEST}" ) + + add_test(NAME build:${TEST} COMMAND "${CMAKE_COMMAND}" --build ${CMAKE_BINARY_DIR} --target tp-json-test-${TEST}) + set_tests_properties(build:${TEST} PROPERTIES FIXTURES_SETUP ${VAR}) + IF(NOT VAR EQUAL 1) + set_tests_properties(build:${TEST} PROPERTIES FIXTURES_REQUIRED ${OLDVAR}) + ENDIF() + + SET(OLDVAR ${VAR}) + MATH(EXPR VAR "${VAR}+1") + + add_test(NAME run:${TEST} COMMAND ./tp-json-test-${TEST} "${CMAKE_CURRENT_SOURCE_DIR}/json/") + set_tests_properties(run:${TEST} PROPERTIES FIXTURES_SETUP ${VAR}) + set_tests_properties(run:${TEST} PROPERTIES FIXTURES_REQUIRED ${OLDVAR}) + + SET(OLDVAR ${VAR}) + MATH(EXPR VAR "${VAR}+1") +ENDFOREACH() diff --git a/tp-json/tests/assert.cpp b/tp-json/tests/assert.cpp new file mode 100644 index 00000000..e3f506b5 --- /dev/null +++ b/tp-json/tests/assert.cpp @@ -0,0 +1,46 @@ +#include +#include + +template +void assert_equal(const T1& left, const T2& right, unsigned line) +{ + if (!(left == right)) + { + std::cerr << "On line " << line << ", assert_equal failed the following tests returns false: " << left + << " == " << right << std::endl; + exit(EXIT_FAILURE); + } +} +#define ASSERT_EQUAL(x, y) assert_equal(x, y, __LINE__); + +template +void assert_unequal(const T1& left, const T2& right, unsigned line) +{ + if (!(left != right)) + { + std::cerr << "On line " << line + << ", assert_unequal failed the following tests returns false: " << left << " != " << right + << std::endl; + exit(EXIT_FAILURE); + } +} +#define ASSERT_UNEQUAL(x, y) assert_unequal(x, y, __LINE__); + +void assert_true(bool b, unsigned line) +{ + if (b) + return; + std::cerr << "On line " << line << ", assert_true failed" << std::endl; + exit(EXIT_FAILURE); +} + +#define ASSERT_TRUE(b) assert_true(b, __LINE__); + +void assert_false(bool b, unsigned line) +{ + if (!b) + return; + std::cerr << "On line " << line << ", assert_false failed" << std::endl; + exit(EXIT_FAILURE); +} +#define ASSERT_FALSE(b) assert_false(b, __LINE__); diff --git a/tp-json/tests/json/array_empty.json b/tp-json/tests/json/array_empty.json new file mode 100644 index 00000000..0637a088 --- /dev/null +++ b/tp-json/tests/json/array_empty.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/tp-json/tests/json/array_hexadecimal.json b/tp-json/tests/json/array_hexadecimal.json new file mode 100644 index 00000000..244cc45a --- /dev/null +++ b/tp-json/tests/json/array_hexadecimal.json @@ -0,0 +1 @@ +[[[[0,1],[2,3]],[[4,5],[6,7]]],[[[8,9],["A","B"]],[["C","D"],["E","F"]]]] \ No newline at end of file diff --git a/tp-json/tests/json/array_range10.json b/tp-json/tests/json/array_range10.json new file mode 100644 index 00000000..0b5c0682 --- /dev/null +++ b/tp-json/tests/json/array_range10.json @@ -0,0 +1 @@ +[0,1,2,3,4,5,6,7,8,9] \ No newline at end of file diff --git a/tp-json/tests/json/boolean_false.json b/tp-json/tests/json/boolean_false.json new file mode 100644 index 00000000..02e4a84d --- /dev/null +++ b/tp-json/tests/json/boolean_false.json @@ -0,0 +1 @@ +false \ No newline at end of file diff --git a/tp-json/tests/json/boolean_true.json b/tp-json/tests/json/boolean_true.json new file mode 100644 index 00000000..f32a5804 --- /dev/null +++ b/tp-json/tests/json/boolean_true.json @@ -0,0 +1 @@ +true \ No newline at end of file diff --git a/tp-json/tests/json/cpp2022.json b/tp-json/tests/json/cpp2022.json new file mode 100644 index 00000000..4c42bc4b --- /dev/null +++ b/tp-json/tests/json/cpp2022.json @@ -0,0 +1,14 @@ +{ + "name":"C++", + "level":"M1", + "mandatory":true, + "teachers":["Céline","Matthias","Victor"], + "year": 2022, + "sessions":[ + {"kind":"TP", "number":1, "week":1}, + {"kind":"TP", "number":2, "week":2}, + {"kind":"TP", "number":3, "week":3}, + {"kind":"TP", "number":4, "week":4}, + {"kind":"Project", "weeks":[5,6,7,8,9,10,11,12]} + ] +} diff --git a/tp-json/tests/json/number_42.json b/tp-json/tests/json/number_42.json new file mode 100644 index 00000000..f70d7bba --- /dev/null +++ b/tp-json/tests/json/number_42.json @@ -0,0 +1 @@ +42 \ No newline at end of file diff --git a/tp-json/tests/json/object_alphabet.json b/tp-json/tests/json/object_alphabet.json new file mode 100644 index 00000000..4f90682e --- /dev/null +++ b/tp-json/tests/json/object_alphabet.json @@ -0,0 +1 @@ +{"a":97,"b":98,"c":99,"d":100,"e":101,"f":102,"g":103,"h":104,"i":105,"j":106,"k":107,"l":108,"m":109,"n":110,"o":111,"p":112,"q":113,"r":114,"s":115,"t":116,"u":117,"v":118,"w":119,"x":120,"y":121,"z":122} \ No newline at end of file diff --git a/tp-json/tests/json/object_empty.json b/tp-json/tests/json/object_empty.json new file mode 100644 index 00000000..9e26dfee --- /dev/null +++ b/tp-json/tests/json/object_empty.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/tp-json/tests/json/pokedex.json b/tp-json/tests/json/pokedex.json new file mode 100644 index 00000000..b9ec6bc9 --- /dev/null +++ b/tp-json/tests/json/pokedex.json @@ -0,0 +1,4086 @@ +{ + "pokemon": [{ + "id": 1, + "num": "001", + "name": "Bulbasaur", + "img": "http://www.serebii.net/pokemongo/pokemon/001.png", + "type": [ + "Grass", + "Poison" + ], + "height": "0.71 m", + "weight": "6.9 kg", + "candy": "Bulbasaur Candy", + "candy_count": 25, + "egg": "2 km", + "spawn_chance": 0.69, + "avg_spawns": 69, + "spawn_time": "20:00", + "multipliers": [1.58], + "weaknesses": [ + "Fire", + "Ice", + "Flying", + "Psychic" + ], + "next_evolution": [{ + "num": "002", + "name": "Ivysaur" + }, { + "num": "003", + "name": "Venusaur" + }] + }, { + "id": 2, + "num": "002", + "name": "Ivysaur", + "img": "http://www.serebii.net/pokemongo/pokemon/002.png", + "type": [ + "Grass", + "Poison" + ], + "height": "0.99 m", + "weight": "13.0 kg", + "candy": "Bulbasaur Candy", + "candy_count": 100, + "egg": "Not in Eggs", + "spawn_chance": 0.042, + "avg_spawns": 4.2, + "spawn_time": "07:00", + "multipliers": [ + 1.2, + 1.6 + ], + "weaknesses": [ + "Fire", + "Ice", + "Flying", + "Psychic" + ], + "prev_evolution": [{ + "num": "001", + "name": "Bulbasaur" + }], + "next_evolution": [{ + "num": "003", + "name": "Venusaur" + }] + }, { + "id": 3, + "num": "003", + "name": "Venusaur", + "img": "http://www.serebii.net/pokemongo/pokemon/003.png", + "type": [ + "Grass", + "Poison" + ], + "height": "2.01 m", + "weight": "100.0 kg", + "candy": "Bulbasaur Candy", + "egg": "Not in Eggs", + "spawn_chance": 0.017, + "avg_spawns": 1.7, + "spawn_time": "11:30", + "multipliers": [], + "weaknesses": [ + "Fire", + "Ice", + "Flying", + "Psychic" + ], + "prev_evolution": [{ + "num": "001", + "name": "Bulbasaur" + }, { + "num": "002", + "name": "Ivysaur" + }] + }, { + "id": 4, + "num": "004", + "name": "Charmander", + "img": "http://www.serebii.net/pokemongo/pokemon/004.png", + "type": [ + "Fire" + ], + "height": "0.61 m", + "weight": "8.5 kg", + "candy": "Charmander Candy", + "candy_count": 25, + "egg": "2 km", + "spawn_chance": 0.253, + "avg_spawns": 25.3, + "spawn_time": "08:45", + "multipliers": [1.65], + "weaknesses": [ + "Water", + "Ground", + "Rock" + ], + "next_evolution": [{ + "num": "005", + "name": "Charmeleon" + }, { + "num": "006", + "name": "Charizard" + }] + }, { + "id": 5, + "num": "005", + "name": "Charmeleon", + "img": "http://www.serebii.net/pokemongo/pokemon/005.png", + "type": [ + "Fire" + ], + "height": "1.09 m", + "weight": "19.0 kg", + "candy": "Charmander Candy", + "candy_count": 100, + "egg": "Not in Eggs", + "spawn_chance": 0.012, + "avg_spawns": 1.2, + "spawn_time": "19:00", + "multipliers": [1.79], + "weaknesses": [ + "Water", + "Ground", + "Rock" + ], + "prev_evolution": [{ + "num": "004", + "name": "Charmander" + }], + "next_evolution": [{ + "num": "006", + "name": "Charizard" + }] + }, { + "id": 6, + "num": "006", + "name": "Charizard", + "img": "http://www.serebii.net/pokemongo/pokemon/006.png", + "type": [ + "Fire", + "Flying" + ], + "height": "1.70 m", + "weight": "90.5 kg", + "candy": "Charmander Candy", + "egg": "Not in Eggs", + "spawn_chance": 0.0031, + "avg_spawns": 0.31, + "spawn_time": "13:34", + "multipliers": [], + "weaknesses": [ + "Water", + "Electric", + "Rock" + ], + "prev_evolution": [{ + "num": "004", + "name": "Charmander" + }, { + "num": "005", + "name": "Charmeleon" + }] + }, { + "id": 7, + "num": "007", + "name": "Squirtle", + "img": "http://www.serebii.net/pokemongo/pokemon/007.png", + "type": [ + "Water" + ], + "height": "0.51 m", + "weight": "9.0 kg", + "candy": "Squirtle Candy", + "candy_count": 25, + "egg": "2 km", + "spawn_chance": 0.58, + "avg_spawns": 58, + "spawn_time": "04:25", + "multipliers": [2.1], + "weaknesses": [ + "Electric", + "Grass" + ], + "next_evolution": [{ + "num": "008", + "name": "Wartortle" + }, { + "num": "009", + "name": "Blastoise" + }] + }, { + "id": 8, + "num": "008", + "name": "Wartortle", + "img": "http://www.serebii.net/pokemongo/pokemon/008.png", + "type": [ + "Water" + ], + "height": "0.99 m", + "weight": "22.5 kg", + "candy": "Squirtle Candy", + "candy_count": 100, + "egg": "Not in Eggs", + "spawn_chance": 0.034, + "avg_spawns": 3.4, + "spawn_time": "07:02", + "multipliers": [1.4], + "weaknesses": [ + "Electric", + "Grass" + ], + "prev_evolution": [{ + "num": "007", + "name": "Squirtle" + }], + "next_evolution": [{ + "num": "009", + "name": "Blastoise" + }] + }, { + "id": 9, + "num": "009", + "name": "Blastoise", + "img": "http://www.serebii.net/pokemongo/pokemon/009.png", + "type": [ + "Water" + ], + "height": "1.60 m", + "weight": "85.5 kg", + "candy": "Squirtle Candy", + "egg": "Not in Eggs", + "spawn_chance": 0.0067, + "avg_spawns": 0.67, + "spawn_time": "00:06", + "multipliers": [], + "weaknesses": [ + "Electric", + "Grass" + ], + "prev_evolution": [{ + "num": "007", + "name": "Squirtle" + }, { + "num": "008", + "name": "Wartortle" + }] + }, { + "id": 10, + "num": "010", + "name": "Caterpie", + "img": "http://www.serebii.net/pokemongo/pokemon/010.png", + "type": [ + "Bug" + ], + "height": "0.30 m", + "weight": "2.9 kg", + "candy": "Caterpie Candy", + "candy_count": 12, + "egg": "2 km", + "spawn_chance": 3.032, + "avg_spawns": 303.2, + "spawn_time": "16:35", + "multipliers": [1.05], + "weaknesses": [ + "Fire", + "Flying", + "Rock" + ], + "next_evolution": [{ + "num": "011", + "name": "Metapod" + }, { + "num": "012", + "name": "Butterfree" + }] + }, { + "id": 11, + "num": "011", + "name": "Metapod", + "img": "http://www.serebii.net/pokemongo/pokemon/011.png", + "type": [ + "Bug" + ], + "height": "0.71 m", + "weight": "9.9 kg", + "candy": "Caterpie Candy", + "candy_count": 50, + "egg": "Not in Eggs", + "spawn_chance": 0.187, + "avg_spawns": 18.7, + "spawn_time": "02:11", + "multipliers": [ + 3.55, + 3.79 + ], + "weaknesses": [ + "Fire", + "Flying", + "Rock" + ], + "prev_evolution": [{ + "num": "010", + "name": "Caterpie" + }], + "next_evolution": [{ + "num": "012", + "name": "Butterfree" + }] + }, { + "id": 12, + "num": "012", + "name": "Butterfree", + "img": "http://www.serebii.net/pokemongo/pokemon/012.png", + "type": [ + "Bug", + "Flying" + ], + "height": "1.09 m", + "weight": "32.0 kg", + "candy": "Caterpie Candy", + "egg": "Not in Eggs", + "spawn_chance": 0.022, + "avg_spawns": 2.2, + "spawn_time": "05:23", + "multipliers": [], + "weaknesses": [ + "Fire", + "Electric", + "Ice", + "Flying", + "Rock" + ], + "prev_evolution": [{ + "num": "010", + "name": "Caterpie" + }, { + "num": "011", + "name": "Metapod" + }] + }, { + "id": 13, + "num": "013", + "name": "Weedle", + "img": "http://www.serebii.net/pokemongo/pokemon/013.png", + "type": [ + "Bug", + "Poison" + ], + "height": "0.30 m", + "weight": "3.2 kg", + "candy": "Weedle Candy", + "candy_count": 12, + "egg": "2 km", + "spawn_chance": 7.12, + "avg_spawns": 712, + "spawn_time": "02:21", + "multipliers": [ + 1.01, + 1.09 + ], + "weaknesses": [ + "Fire", + "Flying", + "Psychic", + "Rock" + ], + "next_evolution": [{ + "num": "014", + "name": "Kakuna" + }, { + "num": "015", + "name": "Beedrill" + }] + }, { + "id": 14, + "num": "014", + "name": "Kakuna", + "img": "http://www.serebii.net/pokemongo/pokemon/014.png", + "type": [ + "Bug", + "Poison" + ], + "height": "0.61 m", + "weight": "10.0 kg", + "candy": "Weedle Candy", + "candy_count": 50, + "egg": "Not in Eggs", + "spawn_chance": 0.44, + "avg_spawns": 44, + "spawn_time": "02:30", + "multipliers": [ + 3.01, + 3.41 + ], + "weaknesses": [ + "Fire", + "Flying", + "Psychic", + "Rock" + ], + "prev_evolution": [{ + "num": "013", + "name": "Weedle" + }], + "next_evolution": [{ + "num": "015", + "name": "Beedrill" + }] + }, { + "id": 15, + "num": "015", + "name": "Beedrill", + "img": "http://www.serebii.net/pokemongo/pokemon/015.png", + "type": [ + "Bug", + "Poison" + ], + "height": "0.99 m", + "weight": "29.5 kg", + "candy": "Weedle Candy", + "egg": "Not in Eggs", + "spawn_chance": 0.051, + "avg_spawns": 5.1, + "spawn_time": "04:50", + "multipliers": [], + "weaknesses": [ + "Fire", + "Flying", + "Psychic", + "Rock" + ], + "prev_evolution": [{ + "num": "013", + "name": "Weedle" + }, { + "num": "014", + "name": "Kakuna" + }] + }, { + "id": 16, + "num": "016", + "name": "Pidgey", + "img": "http://www.serebii.net/pokemongo/pokemon/016.png", + "type": [ + "Normal", + "Flying" + ], + "height": "0.30 m", + "weight": "1.8 kg", + "candy": "Pidgey Candy", + "candy_count": 12, + "egg": "2 km", + "spawn_chance": 15.98, + "avg_spawns": 1.598, + "spawn_time": "01:34", + "multipliers": [ + 1.71, + 1.92 + ], + "weaknesses": [ + "Electric", + "Rock" + ], + "next_evolution": [{ + "num": "017", + "name": "Pidgeotto" + }, { + "num": "018", + "name": "Pidgeot" + }] + }, { + "id": 17, + "num": "017", + "name": "Pidgeotto", + "img": "http://www.serebii.net/pokemongo/pokemon/017.png", + "type": [ + "Normal", + "Flying" + ], + "height": "1.09 m", + "weight": "30.0 kg", + "candy": "Pidgey Candy", + "candy_count": 50, + "egg": "Not in Eggs", + "spawn_chance": 1.02, + "avg_spawns": 102, + "spawn_time": "01:30", + "multipliers": [1.79], + "weaknesses": [ + "Electric", + "Rock" + ], + "prev_evolution": [{ + "num": "016", + "name": "Pidgey" + }], + "next_evolution": [{ + "num": "018", + "name": "Pidgeot" + }] + }, { + "id": 18, + "num": "018", + "name": "Pidgeot", + "img": "http://www.serebii.net/pokemongo/pokemon/018.png", + "type": [ + "Normal", + "Flying" + ], + "height": "1.50 m", + "weight": "39.5 kg", + "candy": "Pidgey Candy", + "egg": "Not in Eggs", + "spawn_chance": 0.13, + "avg_spawns": 13, + "spawn_time": "01:50", + "multipliers": [], + "weaknesses": [ + "Electric", + "Rock" + ], + "prev_evolution": [{ + "num": "016", + "name": "Pidgey" + }, { + "num": "017", + "name": "Pidgeotto" + }] + }, { + "id": 19, + "num": "019", + "name": "Rattata", + "img": "http://www.serebii.net/pokemongo/pokemon/019.png", + "type": [ + "Normal" + ], + "height": "0.30 m", + "weight": "3.5 kg", + "candy": "Rattata Candy", + "candy_count": 25, + "egg": "2 km", + "spawn_chance": 13.05, + "avg_spawns": 1.305, + "spawn_time": "01:55", + "multipliers": [ + 2.55, + 2.73 + ], + "weaknesses": [ + "Fighting" + ], + "next_evolution": [{ + "num": "020", + "name": "Raticate" + }] + }, { + "id": 20, + "num": "020", + "name": "Raticate", + "img": "http://www.serebii.net/pokemongo/pokemon/020.png", + "type": [ + "Normal" + ], + "height": "0.71 m", + "weight": "18.5 kg", + "candy": "Rattata Candy", + "egg": "Not in Eggs", + "spawn_chance": 0.41, + "avg_spawns": 41, + "spawn_time": "01:56", + "multipliers": [], + "weaknesses": [ + "Fighting" + ], + "prev_evolution": [{ + "num": "019", + "name": "Rattata" + }] + }, { + "id": 21, + "num": "021", + "name": "Spearow", + "img": "http://www.serebii.net/pokemongo/pokemon/021.png", + "type": [ + "Normal", + "Flying" + ], + "height": "0.30 m", + "weight": "2.0 kg", + "candy": "Spearow Candy", + "candy_count": 50, + "egg": "2 km", + "spawn_chance": 4.73, + "avg_spawns": 473, + "spawn_time": "12:25", + "multipliers": [ + 2.66, + 2.68 + ], + "weaknesses": [ + "Electric", + "Rock" + ], + "next_evolution": [{ + "num": "022", + "name": "Fearow" + }] + }, { + "id": 22, + "num": "022", + "name": "Fearow", + "img": "http://www.serebii.net/pokemongo/pokemon/022.png", + "type": [ + "Normal", + "Flying" + ], + "height": "1.19 m", + "weight": "38.0 kg", + "candy": "Spearow Candy", + "egg": "Not in Eggs", + "spawn_chance": 0.15, + "avg_spawns": 15, + "spawn_time": "01:11", + "multipliers": [], + "weaknesses": [ + "Electric", + "Rock" + ], + "prev_evolution": [{ + "num": "021", + "name": "Spearow" + }] + }, { + "id": 23, + "num": "023", + "name": "Ekans", + "img": "http://www.serebii.net/pokemongo/pokemon/023.png", + "type": [ + "Poison" + ], + "height": "2.01 m", + "weight": "6.9 kg", + "candy": "Ekans Candy", + "candy_count": 50, + "egg": "5 km", + "spawn_chance": 2.27, + "avg_spawns": 227, + "spawn_time": "12:20", + "multipliers": [ + 2.21, + 2.27 + ], + "weaknesses": [ + "Ground", + "Psychic" + ], + "next_evolution": [{ + "num": "024", + "name": "Arbok" + }] + }, { + "id": 24, + "num": "024", + "name": "Arbok", + "img": "http://www.serebii.net/pokemongo/pokemon/024.png", + "type": [ + "Poison" + ], + "height": "3.51 m", + "weight": "65.0 kg", + "candy": "Ekans Candy", + "egg": "Not in Eggs", + "spawn_chance": 0.072, + "avg_spawns": 7.2, + "spawn_time": "01:50", + "multipliers": [], + "weaknesses": [ + "Ground", + "Psychic" + ], + "prev_evolution": [{ + "num": "023", + "name": "Ekans" + }] + }, { + "id": 25, + "num": "025", + "name": "Pikachu", + "img": "http://www.serebii.net/pokemongo/pokemon/025.png", + "type": [ + "Electric" + ], + "height": "0.41 m", + "weight": "6.0 kg", + "candy": "Pikachu Candy", + "candy_count": 50, + "egg": "2 km", + "spawn_chance": 0.21, + "avg_spawns": 21, + "spawn_time": "04:00", + "multipliers": [2.34], + "weaknesses": [ + "Ground" + ], + "next_evolution": [{ + "num": "026", + "name": "Raichu" + }] + }, { + "id": 26, + "num": "026", + "name": "Raichu", + "img": "http://www.serebii.net/pokemongo/pokemon/026.png", + "type": [ + "Electric" + ], + "height": "0.79 m", + "weight": "30.0 kg", + "candy": "Pikachu Candy", + "egg": "Not in Eggs", + "spawn_chance": 0.0076, + "avg_spawns": 0.76, + "spawn_time": "23:58", + "multipliers": [], + "weaknesses": [ + "Ground" + ], + "prev_evolution": [{ + "num": "025", + "name": "Pikachu" + }] + }, { + "id": 27, + "num": "027", + "name": "Sandshrew", + "img": "http://www.serebii.net/pokemongo/pokemon/027.png", + "type": [ + "Ground" + ], + "height": "0.61 m", + "weight": "12.0 kg", + "candy": "Sandshrew Candy", + "candy_count": 50, + "egg": "5 km", + "spawn_chance": 1.11, + "avg_spawns": 111, + "spawn_time": "01:58", + "multipliers": [2.45], + "weaknesses": [ + "Water", + "Grass", + "Ice" + ], + "next_evolution": [{ + "num": "028", + "name": "Sandslash" + }] + }, { + "id": 28, + "num": "028", + "name": "Sandslash", + "img": "http://www.serebii.net/pokemongo/pokemon/028.png", + "type": [ + "Ground" + ], + "height": "0.99 m", + "weight": "29.5 kg", + "candy": "Sandshrew Candy", + "egg": "Not in Eggs", + "spawn_chance": 0.037, + "avg_spawns": 3.7, + "spawn_time": "12:34", + "multipliers": [], + "weaknesses": [ + "Water", + "Grass", + "Ice" + ], + "prev_evolution": [{ + "num": "027", + "name": "Sandshrew" + }] + }, { + "id": 29, + "num": "029", + "name": "Nidoran ♀ (Female)", + "img": "http://www.serebii.net/pokemongo/pokemon/029.png", + "type": [ + "Poison" + ], + "height": "0.41 m", + "weight": "7.0 kg", + "candy": "Nidoran ♀ (Female) Candy", + "candy_count": 25, + "egg": "5 km", + "spawn_chance": 1.38, + "avg_spawns": 138, + "spawn_time": "01:51", + "multipliers": [ + 1.63, + 2.48 + ], + "weaknesses": [ + "Ground", + "Psychic" + ], + "next_evolution": [{ + "num": "030", + "name": "Nidorina" + }, { + "num": "031", + "name": "Nidoqueen" + }] + }, { + "id": 30, + "num": "030", + "name": "Nidorina", + "img": "http://www.serebii.net/pokemongo/pokemon/030.png", + "type": [ + "Poison" + ], + "height": "0.79 m", + "weight": "20.0 kg", + "candy": "Nidoran ♀ (Female) Candy", + "candy_count": 100, + "egg": "Not in Eggs", + "spawn_chance": 0.088, + "avg_spawns": 8.8, + "spawn_time": "07:22", + "multipliers": [ + 1.83, + 2.48 + ], + "weaknesses": [ + "Ground", + "Psychic" + ], + "prev_evolution": [{ + "num": "029", + "name": "Nidoran(Female)" + }], + "next_evolution": [{ + "num": "031", + "name": "Nidoqueen" + }] + }, { + "id": 31, + "num": "031", + "name": "Nidoqueen", + "img": "http://www.serebii.net/pokemongo/pokemon/031.png", + "type": [ + "Poison", + "Ground" + ], + "height": "1.30 m", + "weight": "60.0 kg", + "candy": "Nidoran ♀ (Female) Candy", + "egg": "Not in Eggs", + "spawn_chance": 0.012, + "avg_spawns": 1.2, + "spawn_time": "12:35", + "multipliers": [], + "weaknesses": [ + "Water", + "Ice", + "Ground", + "Psychic" + ], + "prev_evolution": [{ + "num": "029", + "name": "Nidoran(Female)" + }, { + "num": "030", + "name": "Nidorina" + }] + }, { + "id": 32, + "num": "032", + "name": "Nidoran ♂ (Male)", + "img": "http://www.serebii.net/pokemongo/pokemon/032.png", + "type": [ + "Poison" + ], + "height": "0.51 m", + "weight": "9.0 kg", + "candy": "Nidoran ♂ (Male) Candy", + "candy_count": 25, + "egg": "5 km", + "spawn_chance": 1.31, + "avg_spawns": 131, + "spawn_time": "01:12", + "multipliers": [ + 1.64, + 1.7 + ], + "weaknesses": [ + "Ground", + "Psychic" + ], + "next_evolution": [{ + "num": "033", + "name": "Nidorino" + }, { + "num": "034", + "name": "Nidoking" + }] + }, { + "id": 33, + "num": "033", + "name": "Nidorino", + "img": "http://www.serebii.net/pokemongo/pokemon/033.png", + "type": [ + "Poison" + ], + "height": "0.89 m", + "weight": "19.5 kg", + "candy": "Nidoran ♂ (Male) Candy", + "candy_count": 100, + "egg": "Not in Eggs", + "spawn_chance": 0.083, + "avg_spawns": 8.3, + "spawn_time": "09:02", + "multipliers": [1.83], + "weaknesses": [ + "Ground", + "Psychic" + ], + "prev_evolution": [{ + "num": "032", + "name": "Nidoran(Male)" + }], + "next_evolution": [{ + "num": "034", + "name": "Nidoking" + }] + }, { + "id": 34, + "num": "034", + "name": "Nidoking", + "img": "http://www.serebii.net/pokemongo/pokemon/034.png", + "type": [ + "Poison", + "Ground" + ], + "height": "1.40 m", + "weight": "62.0 kg", + "candy": "Nidoran ♂ (Male) Candy", + "egg": "Not in Eggs", + "spawn_chance": 0.017, + "avg_spawns": 1.7, + "spawn_time": "12:16", + "multipliers": [], + "weaknesses": [ + "Water", + "Ice", + "Ground", + "Psychic" + ], + "prev_evolution": [{ + "num": "032", + "name": "Nidoran(Male)" + }, { + "num": "033", + "name": "Nidorino" + }] + }, { + "id": 35, + "num": "035", + "name": "Clefairy", + "img": "http://www.serebii.net/pokemongo/pokemon/035.png", + "type": [ + "Normal" + ], + "height": "0.61 m", + "weight": "7.5 kg", + "candy": "Clefairy Candy", + "candy_count": 50, + "egg": "2 km", + "spawn_chance": 0.92, + "avg_spawns": 92, + "spawn_time": "03:30", + "multipliers": [ + 2.03, + 2.14 + ], + "weaknesses": [ + "Fighting" + ], + "next_evolution": [{ + "num": "036", + "name": "Clefable" + }] + }, { + "id": 36, + "num": "036", + "name": "Clefable", + "img": "http://www.serebii.net/pokemongo/pokemon/036.png", + "type": [ + "Normal" + ], + "height": "1.30 m", + "weight": "40.0 kg", + "candy": "Clefairy Candy", + "egg": "Not in Eggs", + "spawn_chance": 0.012, + "avg_spawns": 1.2, + "spawn_time": "03:29", + "multipliers": [], + "weaknesses": [ + "Fighting" + ], + "prev_evolution": [{ + "num": "035", + "name": "Clefairy" + }] + }, { + "id": 37, + "num": "037", + "name": "Vulpix", + "img": "http://www.serebii.net/pokemongo/pokemon/037.png", + "type": [ + "Fire" + ], + "height": "0.61 m", + "weight": "9.9 kg", + "candy": "Vulpix Candy", + "candy_count": 50, + "egg": "5 km", + "spawn_chance": 0.22, + "avg_spawns": 22, + "spawn_time": "13:43", + "multipliers": [ + 2.74, + 2.81 + ], + "weaknesses": [ + "Water", + "Ground", + "Rock" + ], + "next_evolution": [{ + "num": "038", + "name": "Ninetales" + }] + }, { + "id": 38, + "num": "038", + "name": "Ninetales", + "img": "http://www.serebii.net/pokemongo/pokemon/038.png", + "type": [ + "Fire" + ], + "height": "1.09 m", + "weight": "19.9 kg", + "candy": "Vulpix Candy", + "egg": "Not in Eggs", + "spawn_chance": 0.0077, + "avg_spawns": 0.77, + "spawn_time": "01:32", + "multipliers": [], + "weaknesses": [ + "Water", + "Ground", + "Rock" + ], + "prev_evolution": [{ + "num": "037", + "name": "Vulpix" + }] + }, { + "id": 39, + "num": "039", + "name": "Jigglypuff", + "img": "http://www.serebii.net/pokemongo/pokemon/039.png", + "type": [ + "Normal" + ], + "height": "0.51 m", + "weight": "5.5 kg", + "candy": "Jigglypuff Candy", + "candy_count": 50, + "egg": "2 km", + "spawn_chance": 0.39, + "avg_spawns": 39, + "spawn_time": "08:46", + "multipliers": [1.85], + "weaknesses": [ + "Fighting" + ], + "next_evolution": [{ + "num": "040", + "name": "Wigglytuff" + }] + }, { + "id": 40, + "num": "040", + "name": "Wigglytuff", + "img": "http://www.serebii.net/pokemongo/pokemon/040.png", + "type": [ + "Normal" + ], + "height": "0.99 m", + "weight": "12.0 kg", + "candy": "Jigglypuff Candy", + "egg": "Not in Eggs", + "spawn_chance": 0.018, + "avg_spawns": 1.8, + "spawn_time": "12:28", + "multipliers": [], + "weaknesses": [ + "Fighting" + ], + "prev_evolution": [{ + "num": "039", + "name": "Jigglypuff" + }] + }, { + "id": 41, + "num": "041", + "name": "Zubat", + "img": "http://www.serebii.net/pokemongo/pokemon/041.png", + "type": [ + "Poison", + "Flying" + ], + "height": "0.79 m", + "weight": "7.5 kg", + "candy": "Zubat Candy", + "candy_count": 50, + "egg": "2 km", + "spawn_chance": 6.52, + "avg_spawns": 652, + "spawn_time": "12:28", + "multipliers": [ + 2.6, + 3.67 + ], + "weaknesses": [ + "Electric", + "Ice", + "Psychic", + "Rock" + ], + "next_evolution": [{ + "num": "042", + "name": "Golbat" + }] + }, { + "id": 42, + "num": "042", + "name": "Golbat", + "img": "http://www.serebii.net/pokemongo/pokemon/042.png", + "type": [ + "Poison", + "Flying" + ], + "height": "1.60 m", + "weight": "55.0 kg", + "candy": "Zubat Candy", + "egg": "Not in Eggs", + "spawn_chance": 0.42, + "avg_spawns": 42, + "spawn_time": "02:15", + "multipliers": [], + "weaknesses": [ + "Electric", + "Ice", + "Psychic", + "Rock" + ], + "prev_evolution": [{ + "num": "041", + "name": "Zubat" + }] + }, { + "id": 43, + "num": "043", + "name": "Oddish", + "img": "http://www.serebii.net/pokemongo/pokemon/043.png", + "type": [ + "Grass", + "Poison" + ], + "height": "0.51 m", + "weight": "5.4 kg", + "candy": "Oddish Candy", + "candy_count": 25, + "egg": "5 km", + "spawn_chance": 1.02, + "avg_spawns": 102, + "spawn_time": "03:58", + "multipliers": [1.5], + "weaknesses": [ + "Fire", + "Ice", + "Flying", + "Psychic" + ], + "next_evolution": [{ + "num": "044", + "name": "Gloom" + }, { + "num": "045", + "name": "Vileplume" + }] + }, { + "id": 44, + "num": "044", + "name": "Gloom", + "img": "http://www.serebii.net/pokemongo/pokemon/044.png", + "type": [ + "Grass", + "Poison" + ], + "height": "0.79 m", + "weight": "8.6 kg", + "candy": "Oddish Candy", + "candy_count": 100, + "egg": "Not in Eggs", + "spawn_chance": 0.064, + "avg_spawns": 6.4, + "spawn_time": "11:33", + "multipliers": [1.49], + "weaknesses": [ + "Fire", + "Ice", + "Flying", + "Psychic" + ], + "prev_evolution": [{ + "num": "043", + "name": "Oddish" + }], + "next_evolution": [{ + "num": "045", + "name": "Vileplume" + }] + }, { + "id": 45, + "num": "045", + "name": "Vileplume", + "img": "http://www.serebii.net/pokemongo/pokemon/045.png", + "type": [ + "Grass", + "Poison" + ], + "height": "1.19 m", + "weight": "18.6 kg", + "candy": "Oddish Candy", + "egg": "Not in Eggs", + "spawn_chance": 0.0097, + "avg_spawns": 0.97, + "spawn_time": "23:58", + "multipliers": [], + "weaknesses": [ + "Fire", + "Ice", + "Flying", + "Psychic" + ], + "prev_evolution": [{ + "num": "043", + "name": "Oddish" + }, { + "num": "044", + "name": "Gloom" + }] + }, { + "id": 46, + "num": "046", + "name": "Paras", + "img": "http://www.serebii.net/pokemongo/pokemon/046.png", + "type": [ + "Bug", + "Grass" + ], + "height": "0.30 m", + "weight": "5.4 kg", + "candy": "Paras Candy", + "candy_count": 50, + "egg": "5 km", + "spawn_chance": 2.36, + "avg_spawns": 236, + "spawn_time": "01:42", + "multipliers": [2.02], + "weaknesses": [ + "Fire", + "Ice", + "Poison", + "Flying", + "Bug", + "Rock" + ], + "next_evolution": [{ + "num": "047", + "name": "Parasect" + }] + }, { + "id": 47, + "num": "047", + "name": "Parasect", + "img": "http://www.serebii.net/pokemongo/pokemon/047.png", + "type": [ + "Bug", + "Grass" + ], + "height": "0.99 m", + "weight": "29.5 kg", + "candy": "Paras Candy", + "egg": "Not in Eggs", + "spawn_chance": 0.074, + "avg_spawns": 7.4, + "spawn_time": "01:22", + "multipliers": [], + "weaknesses": [ + "Fire", + "Ice", + "Poison", + "Flying", + "Bug", + "Rock" + ], + "prev_evolution": [{ + "num": "046", + "name": "Paras" + }] + }, { + "id": 48, + "num": "048", + "name": "Venonat", + "img": "http://www.serebii.net/pokemongo/pokemon/048.png", + "type": [ + "Bug", + "Poison" + ], + "height": "0.99 m", + "weight": "30.0 kg", + "candy": "Venonat Candy", + "candy_count": 50, + "egg": "5 km", + "spawn_chance": 2.28, + "avg_spawns": 228, + "spawn_time": "02:31", + "multipliers": [ + 1.86, + 1.9 + ], + "weaknesses": [ + "Fire", + "Flying", + "Psychic", + "Rock" + ], + "next_evolution": [{ + "num": "049", + "name": "Venomoth" + }] + }, { + "id": 49, + "num": "049", + "name": "Venomoth", + "img": "http://www.serebii.net/pokemongo/pokemon/049.png", + "type": [ + "Bug", + "Poison" + ], + "height": "1.50 m", + "weight": "12.5 kg", + "candy": "Venonat Candy", + "egg": "Not in Eggs", + "spawn_chance": 0.072, + "avg_spawns": 7.2, + "spawn_time": "23:40", + "multipliers": [], + "weaknesses": [ + "Fire", + "Flying", + "Psychic", + "Rock" + ], + "prev_evolution": [{ + "num": "048", + "name": "Venonat" + }] + }, { + "id": 50, + "num": "050", + "name": "Diglett", + "img": "http://www.serebii.net/pokemongo/pokemon/050.png", + "type": [ + "Ground" + ], + "height": "0.20 m", + "weight": "0.8 kg", + "candy": "Diglett Candy", + "candy_count": 50, + "egg": "5 km", + "spawn_chance": 0.40, + "avg_spawns": 40, + "spawn_time": "02:22", + "multipliers": [2.69], + "weaknesses": [ + "Water", + "Grass", + "Ice" + ], + "next_evolution": [{ + "num": "051", + "name": "Dugtrio" + }] + }, { + "id": 51, + "num": "051", + "name": "Dugtrio", + "img": "http://www.serebii.net/pokemongo/pokemon/051.png", + "type": [ + "Ground" + ], + "height": "0.71 m", + "weight": "33.3 kg", + "candy": "Dugtrio", + "egg": "Not in Eggs", + "spawn_chance": 0.014, + "avg_spawns": 1.4, + "spawn_time": "12:37", + "multipliers": [], + "weaknesses": [ + "Water", + "Grass", + "Ice" + ], + "prev_evolution": [{ + "num": "050", + "name": "Diglett" + }] + }, { + "id": 52, + "num": "052", + "name": "Meowth", + "img": "http://www.serebii.net/pokemongo/pokemon/052.png", + "type": [ + "Normal" + ], + "height": "0.41 m", + "weight": "4.2 kg", + "candy": "Meowth Candy", + "candy_count": 50, + "egg": "5 km", + "spawn_chance": 0.86, + "avg_spawns": 86, + "spawn_time": "02:54", + "multipliers": [1.98], + "weaknesses": [ + "Fighting" + ], + "next_evolution": [{ + "num": "053", + "name": "Persian" + }] + }, { + "id": 53, + "num": "053", + "name": "Persian", + "img": "http://www.serebii.net/pokemongo/pokemon/053.png", + "type": [ + "Normal" + ], + "height": "0.99 m", + "weight": "32.0 kg", + "candy": "Meowth Candy", + "egg": "Not in Eggs", + "spawn_chance": 0.022, + "avg_spawns": 2.2, + "spawn_time": "02:44", + "multipliers": [], + "weaknesses": [ + "Fighting" + ], + "prev_evolution": [{ + "num": "052", + "name": "Meowth" + }] + }, { + "id": 54, + "num": "054", + "name": "Psyduck", + "img": "http://www.serebii.net/pokemongo/pokemon/054.png", + "type": [ + "Water" + ], + "height": "0.79 m", + "weight": "19.6 kg", + "candy": "Psyduck Candy", + "candy_count": 50, + "egg": "5 km", + "spawn_chance": 2.54, + "avg_spawns": 254, + "spawn_time": "03:41", + "multipliers": [2.27], + "weaknesses": [ + "Electric", + "Grass" + ], + "next_evolution": [{ + "num": "055", + "name": "Golduck" + }] + }, { + "id": 55, + "num": "055", + "name": "Golduck", + "img": "http://www.serebii.net/pokemongo/pokemon/055.png", + "type": [ + "Water" + ], + "height": "1.70 m", + "weight": "76.6 kg", + "candy": "Psyduck Candy", + "egg": "Not in Eggs", + "spawn_chance": 0.087, + "avg_spawns": 8.7, + "spawn_time": "23:06", + "multipliers": [], + "weaknesses": [ + "Electric", + "Grass" + ], + "prev_evolution": [{ + "num": "054", + "name": "Psyduck" + }] + }, { + "id": 56, + "num": "056", + "name": "Mankey", + "img": "http://www.serebii.net/pokemongo/pokemon/056.png", + "type": [ + "Fighting" + ], + "height": "0.51 m", + "weight": "28.0 kg", + "candy": "Mankey Candy", + "candy_count": 50, + "egg": "5 km", + "spawn_chance": 0.92, + "avg_spawns": 92, + "spawn_time": "12:52", + "multipliers": [ + 2.17, + 2.28 + ], + "weaknesses": [ + "Flying", + "Psychic", + "Fairy" + ], + "next_evolution": [{ + "num": "057", + "name": "Primeape" + }] + }, { + "id": 57, + "num": "057", + "name": "Primeape", + "img": "http://www.serebii.net/pokemongo/pokemon/057.png", + "type": [ + "Fighting" + ], + "height": "0.99 m", + "weight": "32.0 kg", + "candy": "Mankey Candy", + "egg": "Not in Eggs", + "spawn_chance": 0.031, + "avg_spawns": 3.1, + "spawn_time": "12:33", + "multipliers": [], + "weaknesses": [ + "Flying", + "Psychic", + "Fairy" + ], + "prev_evolution": [{ + "num": "056", + "name": "Mankey" + }] + }, { + "id": 58, + "num": "058", + "name": "Growlithe", + "img": "http://www.serebii.net/pokemongo/pokemon/058.png", + "type": [ + "Fire" + ], + "height": "0.71 m", + "weight": "19.0 kg", + "candy": "Growlithe Candy", + "candy_count": 50, + "egg": "5 km", + "spawn_chance": 0.92, + "avg_spawns": 92, + "spawn_time": "03:57", + "multipliers": [ + 2.31, + 2.36 + ], + "weaknesses": [ + "Water", + "Ground", + "Rock" + ], + "next_evolution": [{ + "num": "059", + "name": "Arcanine" + }] + }, { + "id": 59, + "num": "059", + "name": "Arcanine", + "img": "http://www.serebii.net/pokemongo/pokemon/059.png", + "type": [ + "Fire" + ], + "height": "1.91 m", + "weight": "155.0 kg", + "candy": "Growlithe Candy", + "egg": "Not in Eggs", + "spawn_chance": 0.017, + "avg_spawns": 1.7, + "spawn_time": "03:11", + "multipliers": [], + "weaknesses": [ + "Water", + "Ground", + "Rock" + ], + "prev_evolution": [{ + "num": "058", + "name": "Growlithe" + }] + }, { + "id": 60, + "num": "060", + "name": "Poliwag", + "img": "http://www.serebii.net/pokemongo/pokemon/060.png", + "type": [ + "Water" + ], + "height": "0.61 m", + "weight": "12.4 kg", + "candy": "Poliwag Candy", + "candy_count": 25, + "egg": "5 km", + "spawn_chance": 2.19, + "avg_spawns": 219, + "spawn_time": "03:40", + "multipliers": [ + 1.72, + 1.73 + ], + "weaknesses": [ + "Electric", + "Grass" + ], + "next_evolution": [{ + "num": "061", + "name": "Poliwhirl" + }, { + "num": "062", + "name": "Poliwrath" + }] + }, { + "id": 61, + "num": "061", + "name": "Poliwhirl", + "img": "http://www.serebii.net/pokemongo/pokemon/061.png", + "type": [ + "Water" + ], + "height": "0.99 m", + "weight": "20.0 kg", + "candy": "Poliwag Candy", + "candy_count": 100, + "egg": "Not in Eggs", + "spawn_chance": 0.13, + "avg_spawns": 13, + "spawn_time": "09:14", + "multipliers": [1.95], + "weaknesses": [ + "Electric", + "Grass" + ], + "prev_evolution": [{ + "num": "060", + "name": "Poliwag" + }], + "next_evolution": [{ + "num": "062", + "name": "Poliwrath" + }] + }, { + "id": 62, + "num": "062", + "name": "Poliwrath", + "img": "http://www.serebii.net/pokemongo/pokemon/062.png", + "type": [ + "Water", + "Fighting" + ], + "height": "1.30 m", + "weight": "54.0 kg", + "candy": "Poliwag Candy", + "egg": "Not in Eggs", + "spawn_chance": 0.011, + "avg_spawns": 1.1, + "spawn_time": "01:32", + "multipliers": [], + "weaknesses": [ + "Electric", + "Grass", + "Flying", + "Psychic", + "Fairy" + ], + "prev_evolution": [{ + "num": "060", + "name": "Poliwag" + }, { + "num": "061", + "name": "Poliwhirl" + }] + }, { + "id": 63, + "num": "063", + "name": "Abra", + "img": "http://www.serebii.net/pokemongo/pokemon/063.png", + "type": [ + "Psychic" + ], + "height": "0.89 m", + "weight": "19.5 kg", + "candy": "Abra Candy", + "candy_count": 25, + "egg": "5 km", + "spawn_chance": 0.42, + "avg_spawns": 42, + "spawn_time": "04:30", + "multipliers": [ + 1.36, + 1.95 + ], + "weaknesses": [ + "Bug", + "Ghost", + "Dark" + ], + "next_evolution": [{ + "num": "064", + "name": "Kadabra" + }, { + "num": "065", + "name": "Alakazam" + }] + }, { + "id": 64, + "num": "064", + "name": "Kadabra", + "img": "http://www.serebii.net/pokemongo/pokemon/064.png", + "type": [ + "Psychic" + ], + "height": "1.30 m", + "weight": "56.5 kg", + "candy": "Abra Candy", + "candy_count": 100, + "egg": "Not in Eggs", + "spawn_chance": 0.027, + "avg_spawns": 2.7, + "spawn_time": "11:25", + "multipliers": [1.4], + "weaknesses": [ + "Bug", + "Ghost", + "Dark" + ], + "prev_evolution": [{ + "num": "063", + "name": "Abra" + }], + "next_evolution": [{ + "num": "065", + "name": "Alakazam" + }] + }, { + "id": 65, + "num": "065", + "name": "Alakazam", + "img": "http://www.serebii.net/pokemongo/pokemon/065.png", + "type": [ + "Psychic" + ], + "height": "1.50 m", + "weight": "48.0 kg", + "candy": "Abra Candy", + "egg": "Not in Eggs", + "spawn_chance": 0.0073, + "avg_spawns": 0.73, + "spawn_time": "12:33", + "multipliers": [], + "weaknesses": [ + "Bug", + "Ghost", + "Dark" + ], + "prev_evolution": [{ + "num": "063", + "name": "Abra" + }, { + "num": "064", + "name": "Kadabra" + }] + }, { + "id": 66, + "num": "066", + "name": "Machop", + "img": "http://www.serebii.net/pokemongo/pokemon/066.png", + "type": [ + "Fighting" + ], + "height": "0.79 m", + "weight": "19.5 kg", + "candy": "Machop Candy", + "candy_count": 25, + "egg": "5 km", + "spawn_chance": 0.49, + "avg_spawns": 49, + "spawn_time": "01:55", + "multipliers": [ + 1.64, + 1.65 + ], + "weaknesses": [ + "Flying", + "Psychic", + "Fairy" + ], + "next_evolution": [{ + "num": "067", + "name": "Machoke" + }, { + "num": "068", + "name": "Machamp" + }] + }, { + "id": 67, + "num": "067", + "name": "Machoke", + "img": "http://www.serebii.net/pokemongo/pokemon/067.png", + "type": [ + "Fighting" + ], + "height": "1.50 m", + "weight": "70.5 kg", + "candy": "Machop Candy", + "candy_count": 100, + "egg": "Not in Eggs", + "spawn_chance": 0.034, + "avg_spawns": 3.4, + "spawn_time": "10:32", + "multipliers": [1.7], + "weaknesses": [ + "Flying", + "Psychic", + "Fairy" + ], + "prev_evolution": [{ + "num": "066", + "name": "Machop" + }], + "next_evolution": [{ + "num": "068", + "name": "Machamp" + }] + }, { + "id": 68, + "num": "068", + "name": "Machamp", + "img": "http://www.serebii.net/pokemongo/pokemon/068.png", + "type": [ + "Fighting" + ], + "height": "1.60 m", + "weight": "130.0 kg", + "candy": "Machop Candy", + "egg": "Not in Eggs", + "spawn_chance": 0.0068, + "avg_spawns": 0.68, + "spawn_time": "02:55", + "multipliers": [], + "weaknesses": [ + "Flying", + "Psychic", + "Fairy" + ], + "prev_evolution": [{ + "num": "066", + "name": "Machop" + }, { + "num": "067", + "name": "Machoke" + }] + }, { + "id": 69, + "num": "069", + "name": "Bellsprout", + "img": "http://www.serebii.net/pokemongo/pokemon/069.png", + "type": [ + "Grass", + "Poison" + ], + "height": "0.71 m", + "weight": "4.0 kg", + "candy": "Bellsprout Candy", + "candy_count": 25, + "egg": "5 km", + "spawn_chance": 1.15, + "avg_spawns": 115, + "spawn_time": "04:10", + "multipliers": [1.57], + "weaknesses": [ + "Fire", + "Ice", + "Flying", + "Psychic" + ], + "next_evolution": [{ + "num": "070", + "name": "Weepinbell" + }, { + "num": "071", + "name": "Victreebel" + }] + }, { + "id": 70, + "num": "070", + "name": "Weepinbell", + "img": "http://www.serebii.net/pokemongo/pokemon/070.png", + "type": [ + "Grass", + "Poison" + ], + "height": "0.99 m", + "weight": "6.4 kg", + "candy": "Bellsprout Candy", + "candy_count": 100, + "egg": "Not in Eggs", + "spawn_chance": 0.072, + "avg_spawns": 7.2, + "spawn_time": "09:45", + "multipliers": [1.59], + "weaknesses": [ + "Fire", + "Ice", + "Flying", + "Psychic" + ], + "prev_evolution": [{ + "num": "069", + "name": "Bellsprout" + }], + "next_evolution": [{ + "num": "071", + "name": "Victreebel" + }] + }, { + "id": 71, + "num": "071", + "name": "Victreebel", + "img": "http://www.serebii.net/pokemongo/pokemon/071.png", + "type": [ + "Grass", + "Poison" + ], + "height": "1.70 m", + "weight": "15.5 kg", + "candy": "Bellsprout Candy", + "egg": "Not in Eggs", + "spawn_chance": 0.0059, + "avg_spawns": 0.59, + "spawn_time": "12:19", + "multipliers": [], + "weaknesses": [ + "Fire", + "Ice", + "Flying", + "Psychic" + ], + "prev_evolution": [{ + "num": "069", + "name": "Bellsprout" + }, { + "num": "070", + "name": "Weepinbell" + }] + }, { + "id": 72, + "num": "072", + "name": "Tentacool", + "img": "http://www.serebii.net/pokemongo/pokemon/072.png", + "type": [ + "Water", + "Poison" + ], + "height": "0.89 m", + "weight": "45.5 kg", + "candy": "Tentacool Candy", + "candy_count": 50, + "egg": "5 km", + "spawn_chance": 0.81, + "avg_spawns": 81, + "spawn_time": "03:20", + "multipliers": [2.52], + "weaknesses": [ + "Electric", + "Ground", + "Psychic" + ], + "next_evolution": [{ + "num": "073", + "name": "Tentacruel" + }] + }, { + "id": 73, + "num": "073", + "name": "Tentacruel", + "img": "http://www.serebii.net/pokemongo/pokemon/073.png", + "type": [ + "Water", + "Poison" + ], + "height": "1.60 m", + "weight": "55.0 kg", + "candy": "Tentacool Candy", + "egg": "Not in Eggs", + "spawn_chance": 0.082, + "avg_spawns": 8.2, + "spawn_time": "23:36", + "multipliers": [], + "weaknesses": [ + "Electric", + "Ground", + "Psychic" + ], + "prev_evolution": [{ + "num": "072", + "name": "Tentacool" + }] + }, { + "id": 74, + "num": "074", + "name": "Geodude", + "img": "http://www.serebii.net/pokemongo/pokemon/074.png", + "type": [ + "Rock", + "Ground" + ], + "height": "0.41 m", + "weight": "20.0 kg", + "candy": "Geodude Candy", + "candy_count": 25, + "egg": "2 km", + "spawn_chance": 1.19, + "avg_spawns": 119, + "spawn_time": "12:40", + "multipliers": [ + 1.75, + 1.76 + ], + "weaknesses": [ + "Water", + "Grass", + "Ice", + "Fighting", + "Ground", + "Steel" + ], + "next_evolution": [{ + "num": "075", + "name": "Graveler" + }, { + "num": "076", + "name": "Golem" + }] + }, { + "id": 75, + "num": "075", + "name": "Graveler", + "img": "http://www.serebii.net/pokemongo/pokemon/075.png", + "type": [ + "Rock", + "Ground" + ], + "height": "0.99 m", + "weight": "105.0 kg", + "candy": "Geodude Candy", + "candy_count": 100, + "egg": "Not in Eggs", + "spawn_chance": 0.071, + "avg_spawns": 7.1, + "spawn_time": "04:53", + "multipliers": [ + 1.64, + 1.72 + ], + "weaknesses": [ + "Water", + "Grass", + "Ice", + "Fighting", + "Ground", + "Steel" + ], + "prev_evolution": [{ + "num": "074", + "name": "Geodude" + }], + "next_evolution": [{ + "num": "076", + "name": "Golem" + }] + }, { + "id": 76, + "num": "076", + "name": "Golem", + "img": "http://www.serebii.net/pokemongo/pokemon/076.png", + "type": [ + "Rock", + "Ground" + ], + "height": "1.40 m", + "weight": "300.0 kg", + "candy": "Geodude Candy", + "egg": "Not in Eggs", + "spawn_chance": 0.0047, + "avg_spawns": 0.47, + "spawn_time": "12:16", + "multipliers": [], + "weaknesses": [ + "Water", + "Grass", + "Ice", + "Fighting", + "Ground", + "Steel" + ], + "prev_evolution": [{ + "num": "074", + "name": "Geodude" + }, { + "num": "075", + "name": "Graveler" + }] + }, { + "id": 77, + "num": "077", + "name": "Ponyta", + "img": "http://www.serebii.net/pokemongo/pokemon/077.png", + "type": [ + "Fire" + ], + "height": "0.99 m", + "weight": "30.0 kg", + "candy": "Ponyta Candy", + "candy_count": 50, + "egg": "5 km", + "spawn_chance": 0.51, + "avg_spawns": 51, + "spawn_time": "02:50", + "multipliers": [ + 1.48, + 1.5 + ], + "weaknesses": [ + "Water", + "Ground", + "Rock" + ], + "next_evolution": [{ + "num": "078", + "name": "Rapidash" + }] + }, { + "id": 78, + "num": "078", + "name": "Rapidash", + "img": "http://www.serebii.net/pokemongo/pokemon/078.png", + "type": [ + "Fire" + ], + "height": "1.70 m", + "weight": "95.0 kg", + "candy": "Ponyta Candy", + "egg": "Not in Eggs", + "spawn_chance": 0.011, + "avg_spawns": 1.1, + "spawn_time": "04:00", + "multipliers": [], + "weaknesses": [ + "Water", + "Ground", + "Rock" + ], + "prev_evolution": [{ + "num": "077", + "name": "Ponyta" + }] + }, { + "id": 79, + "num": "079", + "name": "Slowpoke", + "img": "http://www.serebii.net/pokemongo/pokemon/079.png", + "type": [ + "Water", + "Psychic" + ], + "height": "1.19 m", + "weight": "36.0 kg", + "candy": "Slowpoke Candy", + "candy_count": 50, + "egg": "5 km", + "spawn_chance": 1.05, + "avg_spawns": 105, + "spawn_time": "07:12", + "multipliers": [2.21], + "weaknesses": [ + "Electric", + "Grass", + "Bug", + "Ghost", + "Dark" + ], + "next_evolution": [{ + "num": "080", + "name": "Slowbro" + }] + }, { + "id": 80, + "num": "080", + "name": "Slowbro", + "img": "http://www.serebii.net/pokemongo/pokemon/080.png", + "type": [ + "Water", + "Psychic" + ], + "height": "1.60 m", + "weight": "78.5 kg", + "candy": "Slowpoke Candy", + "egg": "Not in Eggs", + "spawn_chance": 0.036, + "avg_spawns": 3.6, + "spawn_time": "02:56", + "multipliers": [], + "weaknesses": [ + "Electric", + "Grass", + "Bug", + "Ghost", + "Dark" + ], + "prev_evolution": [{ + "num": "079", + "name": "Slowpoke" + }] + }, { + "id": 81, + "num": "081", + "name": "Magnemite", + "img": "http://www.serebii.net/pokemongo/pokemon/081.png", + "type": [ + "Electric" + ], + "height": "0.30 m", + "weight": "6.0 kg", + "candy": "Magnemite Candy", + "candy_count": 50, + "egg": "5 km", + "spawn_chance": 0.71, + "avg_spawns": 71, + "spawn_time": "04:04", + "multipliers": [ + 2.16, + 2.17 + ], + "weaknesses": [ + "Fire", + "Water", + "Ground" + ], + "next_evolution": [{ + "num": "082", + "name": "Magneton" + }] + }, { + "id": 82, + "num": "082", + "name": "Magneton", + "img": "http://www.serebii.net/pokemongo/pokemon/082.png", + "type": [ + "Electric" + ], + "height": "0.99 m", + "weight": "60.0 kg", + "candy": "Magnemite Candy", + "egg": "Not in Eggs", + "spawn_chance": 0.023, + "avg_spawns": 2.3, + "spawn_time": "15:25", + "multipliers": [], + "weaknesses": [ + "Fire", + "Water", + "Ground" + ], + "prev_evolution": [{ + "num": "081", + "name": "Magnemite" + }] + }, { + "id": 83, + "num": "083", + "name": "Farfetch'd", + "img": "http://www.serebii.net/pokemongo/pokemon/083.png", + "type": [ + "Normal", + "Flying" + ], + "height": "0.79 m", + "weight": "15.0 kg", + "candy": "None", + "egg": "5 km", + "spawn_chance": 0.0212, + "avg_spawns": 2.12, + "spawn_time": "01:09", + "multipliers": [], + "weaknesses": [ + "Electric", + "Rock" + ] + }, { + "id": 84, + "num": "084", + "name": "Doduo", + "img": "http://www.serebii.net/pokemongo/pokemon/084.png", + "type": [ + "Normal", + "Flying" + ], + "height": "1.40 m", + "weight": "39.2 kg", + "candy": "Doduo Candy", + "candy_count": 50, + "egg": "5 km", + "spawn_chance": 0.52, + "avg_spawns": 52, + "spawn_time": "05:10", + "multipliers": [ + 2.19, + 2.24 + ], + "weaknesses": [ + "Electric", + "Rock" + ], + "next_evolution": [{ + "num": "085", + "name": "Dodrio" + }] + }, { + "id": 85, + "num": "085", + "name": "Dodrio", + "img": "http://www.serebii.net/pokemongo/pokemon/085.png", + "type": [ + "Normal", + "Flying" + ], + "height": "1.80 m", + "weight": "85.2 kg", + "candy": "Doduo Candy", + "egg": "Not in Eggs", + "spawn_chance": 0.22, + "avg_spawns": 22, + "spawn_time": "02:12", + "multipliers": [], + "weaknesses": [ + "Electric", + "Rock" + ], + "prev_evolution": [{ + "num": "084", + "name": "Doduo" + }] + }, { + "id": 86, + "num": "086", + "name": "Seel", + "img": "http://www.serebii.net/pokemongo/pokemon/086.png", + "type": [ + "Water" + ], + "height": "1.09 m", + "weight": "90.0 kg", + "candy": "Seel Candy", + "candy_count": 50, + "egg": "5 km", + "spawn_chance": 0.28, + "avg_spawns": 28, + "spawn_time": "06:46", + "multipliers": [ + 1.04, + 1.96 + ], + "weaknesses": [ + "Electric", + "Grass" + ], + "next_evolution": [{ + "num": "087", + "name": "Dewgong" + }] + }, { + "id": 87, + "num": "087", + "name": "Dewgong", + "img": "http://www.serebii.net/pokemongo/pokemon/087.png", + "type": [ + "Water", + "Ice" + ], + "height": "1.70 m", + "weight": "120.0 kg", + "candy": "Seel Candy", + "egg": "Not in Eggs", + "spawn_chance": 0.013, + "avg_spawns": 1.3, + "spawn_time": "06:04", + "multipliers": [], + "weaknesses": [ + "Electric", + "Grass", + "Fighting", + "Rock" + ], + "prev_evolution": [{ + "num": "086", + "name": "Seel" + }] + }, { + "id": 88, + "num": "088", + "name": "Grimer", + "img": "http://www.serebii.net/pokemongo/pokemon/088.png", + "type": [ + "Poison" + ], + "height": "0.89 m", + "weight": "30.0 kg", + "candy": "Grimer Candy", + "candy_count": 50, + "egg": "5 km", + "spawn_chance": 0.052, + "avg_spawns": 5.2, + "spawn_time": "15:11", + "multipliers": [2.44], + "weaknesses": [ + "Ground", + "Psychic" + ], + "next_evolution": [{ + "num": "089", + "name": "Muk" + }] + }, { + "id": 89, + "num": "089", + "name": "Muk", + "img": "http://www.serebii.net/pokemongo/pokemon/089.png", + "type": [ + "Poison" + ], + "height": "1.19 m", + "weight": "30.0 kg", + "candy": "Grimer Candy", + "egg": "Not in Eggs", + "spawn_chance": 0.0031, + "avg_spawns": 0.31, + "spawn_time": "01:28", + "multipliers": [], + "weaknesses": [ + "Ground", + "Psychic" + ], + "prev_evolution": [{ + "num": "088", + "name": "Grimer" + }] + }, { + "id": 90, + "num": "090", + "name": "Shellder", + "img": "http://www.serebii.net/pokemongo/pokemon/090.png", + "type": [ + "Water" + ], + "height": "0.30 m", + "weight": "4.0 kg", + "candy": "Shellder Candy", + "candy_count": 50, + "egg": "5 km", + "spawn_chance": 0.52, + "avg_spawns": 52, + "spawn_time": "07:39", + "multipliers": [2.65], + "weaknesses": [ + "Electric", + "Grass" + ], + "next_evolution": [{ + "num": "091", + "name": "Cloyster" + }] + }, { + "id": 91, + "num": "091", + "name": "Cloyster", + "img": "http://www.serebii.net/pokemongo/pokemon/091.png", + "type": [ + "Water", + "Ice" + ], + "height": "1.50 m", + "weight": "132.5 kg", + "candy": "Shellder Candy", + "egg": "Not in Eggs", + "spawn_chance": 0.015, + "avg_spawns": 1.5, + "spawn_time": "02:33", + "multipliers": [], + "weaknesses": [ + "Electric", + "Grass", + "Fighting", + "Rock" + ], + "prev_evolution": [{ + "num": "090", + "name": "Shellder" + }] + }, { + "id": 92, + "num": "092", + "name": "Gastly", + "img": "http://www.serebii.net/pokemongo/pokemon/092.png", + "type": [ + "Ghost", + "Poison" + ], + "height": "1.30 m", + "weight": "0.1 kg", + "candy": "Gastly Candy", + "candy_count": 25, + "egg": "5 km", + "spawn_chance": 0.79, + "avg_spawns": 79, + "spawn_time": "04:21", + "multipliers": [1.78], + "weaknesses": [ + "Ground", + "Psychic", + "Ghost", + "Dark" + ], + "next_evolution": [{ + "num": "093", + "name": "Haunter" + }, { + "num": "094", + "name": "Gengar" + }] + }, { + "id": 93, + "num": "093", + "name": "Haunter", + "img": "http://www.serebii.net/pokemongo/pokemon/093.png", + "type": [ + "Ghost", + "Poison" + ], + "height": "1.60 m", + "weight": "0.1 kg", + "candy": "Gastly Candy", + "candy_count": 100, + "egg": "Not in Eggs", + "spawn_chance": 0.052, + "avg_spawns": 5.2, + "spawn_time": "00:10", + "multipliers": [ + 1.56, + 1.8 + ], + "weaknesses": [ + "Ground", + "Psychic", + "Ghost", + "Dark" + ], + "prev_evolution": [{ + "num": "092", + "name": "Gastly" + }], + "next_evolution": [{ + "num": "094", + "name": "Gengar" + }] + }, { + "id": 94, + "num": "094", + "name": "Gengar", + "img": "http://www.serebii.net/pokemongo/pokemon/094.png", + "type": [ + "Ghost", + "Poison" + ], + "height": "1.50 m", + "weight": "40.5 kg", + "candy": "Gastly Candy", + "egg": "Not in Eggs", + "spawn_chance": 0.0067, + "avg_spawns": 0.67, + "spawn_time": "03:55", + "multipliers": [], + "weaknesses": [ + "Ground", + "Psychic", + "Ghost", + "Dark" + ], + "prev_evolution": [{ + "num": "092", + "name": "Gastly" + }, { + "num": "093", + "name": "Haunter" + }] + }, { + "id": 95, + "num": "095", + "name": "Onix", + "img": "http://www.serebii.net/pokemongo/pokemon/095.png", + "type": [ + "Rock", + "Ground" + ], + "height": "8.79 m", + "weight": "210.0 kg", + "candy": "None", + "egg": "10 km", + "spawn_chance": 0.10, + "avg_spawns": 10, + "spawn_time": "01:18", + "multipliers": [], + "weaknesses": [ + "Water", + "Grass", + "Ice", + "Fighting", + "Ground", + "Steel" + ] + }, { + "id": 96, + "num": "096", + "name": "Drowzee", + "img": "http://www.serebii.net/pokemongo/pokemon/096.png", + "type": [ + "Psychic" + ], + "height": "0.99 m", + "weight": "32.4 kg", + "candy": "Drowzee Candy", + "candy_count": 50, + "egg": "5 km", + "spawn_chance": 3.21, + "avg_spawns": 321, + "spawn_time": "01:51", + "multipliers": [ + 2.08, + 2.09 + ], + "weaknesses": [ + "Bug", + "Ghost", + "Dark" + ], + "next_evolution": [{ + "num": "097", + "name": "Hypno" + }] + }, { + "id": 97, + "num": "097", + "name": "Hypno", + "img": "http://www.serebii.net/pokemongo/pokemon/097.png", + "type": [ + "Psychic" + ], + "height": "1.60 m", + "weight": "75.6 kg", + "candy": "Drowzee Candy", + "egg": "Not in Eggs", + "spawn_chance": 0.10, + "avg_spawns": 10, + "spawn_time": "02:17", + "multipliers": [], + "weaknesses": [ + "Bug", + "Ghost", + "Dark" + ], + "prev_evolution": [{ + "num": "096", + "name": "Drowzee" + }] + }, { + "id": 98, + "num": "098", + "name": "Krabby", + "img": "http://www.serebii.net/pokemongo/pokemon/098.png", + "type": [ + "Water" + ], + "height": "0.41 m", + "weight": "6.5 kg", + "candy": "Krabby Candy", + "candy_count": 50, + "egg": "5 km", + "spawn_chance": 2.12, + "avg_spawns": 212, + "spawn_time": "03:33", + "multipliers": [ + 2.36, + 2.4 + ], + "weaknesses": [ + "Electric", + "Grass" + ], + "next_evolution": [{ + "num": "099", + "name": "Kingler" + }] + }, { + "id": 99, + "num": "099", + "name": "Kingler", + "img": "http://www.serebii.net/pokemongo/pokemon/099.png", + "type": [ + "Water" + ], + "height": "1.30 m", + "weight": "60.0 kg", + "candy": "Krabby Candy", + "egg": "Not in Eggs", + "spawn_chance": 0.062, + "avg_spawns": 6.2, + "spawn_time": "03:44", + "multipliers": [], + "weaknesses": [ + "Electric", + "Grass" + ], + "prev_evolution": [{ + "num": "098", + "name": "Krabby" + }] + }, { + "id": 100, + "num": "100", + "name": "Voltorb", + "img": "http://www.serebii.net/pokemongo/pokemon/100.png", + "type": [ + "Electric" + ], + "height": "0.51 m", + "weight": "10.4 kg", + "candy": "Voltorb Candy", + "candy_count": 50, + "egg": "5 km", + "spawn_chance": 0.65, + "avg_spawns": 65, + "spawn_time": "04:36", + "multipliers": [ + 2.01, + 2.02 + ], + "weaknesses": [ + "Ground" + ], + "next_evolution": [{ + "num": "101", + "name": "Electrode" + }] + }, { + "id": 101, + "num": "101", + "name": "Electrode", + "img": "http://www.serebii.net/pokemongo/pokemon/101.png", + "type": [ + "Electric" + ], + "height": "1.19 m", + "weight": "66.6 kg", + "candy": "Voltorb Candy", + "egg": "Not in Eggs", + "spawn_chance": 0.02, + "avg_spawns": 2, + "spawn_time": "04:10", + "multipliers": [], + "weaknesses": [ + "Ground" + ], + "prev_evolution": [{ + "num": "100", + "name": "Voltorb" + }] + }, { + "id": 102, + "num": "102", + "name": "Exeggcute", + "img": "http://www.serebii.net/pokemongo/pokemon/102.png", + "type": [ + "Grass", + "Psychic" + ], + "height": "0.41 m", + "weight": "2.5 kg", + "candy": "Exeggcute Candy", + "candy_count": 50, + "egg": "5 km", + "spawn_chance": 0.78, + "avg_spawns": 78, + "spawn_time": "09:09", + "multipliers": [ + 2.7, + 3.18 + ], + "weaknesses": [ + "Fire", + "Ice", + "Poison", + "Flying", + "Bug", + "Ghost", + "Dark" + ], + "next_evolution": [{ + "num": "103", + "name": "Exeggutor" + }] + }, { + "id": 103, + "num": "103", + "name": "Exeggutor", + "img": "http://www.serebii.net/pokemongo/pokemon/103.png", + "type": [ + "Grass", + "Psychic" + ], + "height": "2.01 m", + "weight": "120.0 kg", + "candy": "Exeggcute Candy", + "egg": "Not in Eggs", + "spawn_chance": 0.014, + "avg_spawns": 1.4, + "spawn_time": "12:34", + "multipliers": [], + "weaknesses": [ + "Fire", + "Ice", + "Poison", + "Flying", + "Bug", + "Ghost", + "Dark" + ], + "prev_evolution": [{ + "num": "102", + "name": "Exeggcute" + }] + }, { + "id": 104, + "num": "104", + "name": "Cubone", + "img": "http://www.serebii.net/pokemongo/pokemon/104.png", + "type": [ + "Ground" + ], + "height": "0.41 m", + "weight": "6.5 kg", + "candy": "Cubone Candy", + "candy_count": 50, + "egg": "5 km", + "spawn_chance": 0.61, + "avg_spawns": 61, + "spawn_time": "01:51", + "multipliers": [1.67], + "weaknesses": [ + "Water", + "Grass", + "Ice" + ], + "next_evolution": [{ + "num": "105", + "name": "Marowak" + }] + }, { + "id": 105, + "num": "105", + "name": "Marowak", + "img": "http://www.serebii.net/pokemongo/pokemon/105.png", + "type": [ + "Ground" + ], + "height": "0.99 m", + "weight": "45.0 kg", + "candy": "Cubone Candy", + "egg": "Not in Eggs", + "spawn_chance": 0.02, + "avg_spawns": 2, + "spawn_time": "03:59", + "multipliers": [], + "weaknesses": [ + "Water", + "Grass", + "Ice" + ], + "prev_evolution": [{ + "num": "104", + "name": "Cubone" + }] + }, { + "id": 106, + "num": "106", + "name": "Hitmonlee", + "img": "http://www.serebii.net/pokemongo/pokemon/106.png", + "type": [ + "Fighting" + ], + "height": "1.50 m", + "weight": "49.8 kg", + "candy": "None", + "egg": "10 km", + "spawn_chance": 0.02, + "avg_spawns": 2, + "spawn_time": "03:59", + "multipliers": [], + "weaknesses": [ + "Flying", + "Psychic", + "Fairy" + ] + }, { + "id": 107, + "num": "107", + "name": "Hitmonchan", + "img": "http://www.serebii.net/pokemongo/pokemon/107.png", + "type": [ + "Fighting" + ], + "height": "1.40 m", + "weight": "50.2 kg", + "candy": "None", + "egg": "10 km", + "spawn_chance": 0.022, + "avg_spawns": 2.2, + "spawn_time": "05:58", + "multipliers": [], + "weaknesses": [ + "Flying", + "Psychic", + "Fairy" + ] + }, { + "id": 108, + "num": "108", + "name": "Lickitung", + "img": "http://www.serebii.net/pokemongo/pokemon/108.png", + "type": [ + "Normal" + ], + "height": "1.19 m", + "weight": "65.5 kg", + "candy": "None", + "egg": "5 km", + "spawn_chance": 0.011, + "avg_spawns": 1.1, + "spawn_time": "02:46", + "multipliers": [], + "weaknesses": [ + "Fighting" + ] + }, { + "id": 109, + "num": "109", + "name": "Koffing", + "img": "http://www.serebii.net/pokemongo/pokemon/109.png", + "type": [ + "Poison" + ], + "height": "0.61 m", + "weight": "1.0 kg", + "candy": "Koffing Candy", + "candy_count": 50, + "egg": "5 km", + "spawn_chance": 0.20, + "avg_spawns": 20, + "spawn_time": "08:16", + "multipliers": [1.11], + "weaknesses": [ + "Ground", + "Psychic" + ], + "next_evolution": [{ + "num": "110", + "name": "Weezing" + }] + }, { + "id": 110, + "num": "110", + "name": "Weezing", + "img": "http://www.serebii.net/pokemongo/pokemon/110.png", + "type": [ + "Poison" + ], + "height": "1.19 m", + "weight": "9.5 kg", + "candy": "Koffing Candy", + "egg": "Not in Eggs", + "spawn_chance": 0.016, + "avg_spawns": 1.6, + "spawn_time": "12:17", + "multipliers": [], + "weaknesses": [ + "Ground", + "Psychic" + ], + "prev_evolution": [{ + "num": "109", + "name": "Koffing" + }] + }, { + "id": 111, + "num": "111", + "name": "Rhyhorn", + "img": "http://www.serebii.net/pokemongo/pokemon/111.png", + "type": [ + "Ground", + "Rock" + ], + "height": "0.99 m", + "weight": "115.0 kg", + "candy": "Rhyhorn Candy", + "candy_count": 50, + "egg": "5 km", + "spawn_chance": 0.63, + "avg_spawns": 63, + "spawn_time": "03:21", + "multipliers": [1.91], + "weaknesses": [ + "Water", + "Grass", + "Ice", + "Fighting", + "Ground", + "Steel" + ], + "next_evolution": [{ + "num": "112", + "name": "Rhydon" + }] + }, { + "id": 112, + "num": "112", + "name": "Rhydon", + "img": "http://www.serebii.net/pokemongo/pokemon/112.png", + "type": [ + "Ground", + "Rock" + ], + "height": "1.91 m", + "weight": "120.0 kg", + "candy": "Rhyhorn Candy", + "egg": "Not in Eggs", + "spawn_chance": 0.022, + "avg_spawns": 2.2, + "spawn_time": "05:50", + "multipliers": [], + "weaknesses": [ + "Water", + "Grass", + "Ice", + "Fighting", + "Ground", + "Steel" + ], + "prev_evolution": [{ + "num": "111", + "name": "Rhyhorn" + }] + }, { + "id": 113, + "num": "113", + "name": "Chansey", + "img": "http://www.serebii.net/pokemongo/pokemon/113.png", + "type": [ + "Normal" + ], + "height": "1.09 m", + "weight": "34.6 kg", + "candy": "None", + "egg": "10 km", + "spawn_chance": 0.013, + "avg_spawns": 1.3, + "spawn_time": "04:46", + "multipliers": [], + "weaknesses": [ + "Fighting" + ] + }, { + "id": 114, + "num": "114", + "name": "Tangela", + "img": "http://www.serebii.net/pokemongo/pokemon/114.png", + "type": [ + "Grass" + ], + "height": "0.99 m", + "weight": "35.0 kg", + "candy": "None", + "egg": "5 km", + "spawn_chance": 0.228, + "avg_spawns": 22.8, + "spawn_time": "23:13", + "multipliers": [], + "weaknesses": [ + "Fire", + "Ice", + "Poison", + "Flying", + "Bug" + ] + }, { + "id": 115, + "num": "115", + "name": "Kangaskhan", + "img": "http://www.serebii.net/pokemongo/pokemon/115.png", + "type": [ + "Normal" + ], + "height": "2.21 m", + "weight": "80.0 kg", + "candy": "None", + "egg": "5 km", + "spawn_chance": 0.0086, + "avg_spawns": 0.86, + "spawn_time": "02:40", + "multipliers": [], + "weaknesses": [ + "Fighting" + ] + }, { + "id": 116, + "num": "116", + "name": "Horsea", + "img": "http://www.serebii.net/pokemongo/pokemon/116.png", + "type": [ + "Water" + ], + "height": "0.41 m", + "weight": "8.0 kg", + "candy": "Horsea Candy", + "candy_count": 50, + "egg": "5 km", + "spawn_chance": 1.13, + "avg_spawns": 113, + "spawn_time": "02:53", + "multipliers": [2.23], + "weaknesses": [ + "Electric", + "Grass" + ], + "next_evolution": [{ + "num": "117", + "name": "Seadra" + }] + }, { + "id": 117, + "num": "117", + "name": "Seadra", + "img": "http://www.serebii.net/pokemongo/pokemon/117.png", + "type": [ + "Water" + ], + "height": "1.19 m", + "weight": "25.0 kg", + "candy": "Horsea Candy", + "egg": "Not in Eggs", + "spawn_chance": 0.034, + "avg_spawns": 3.4, + "spawn_time": "03:18", + "multipliers": [], + "weaknesses": [ + "Electric", + "Grass" + ], + "prev_evolution": [{ + "num": "116", + "name": "Horsea" + }] + }, { + "id": 118, + "num": "118", + "name": "Goldeen", + "img": "http://www.serebii.net/pokemongo/pokemon/118.png", + "type": [ + "Water" + ], + "height": "0.61 m", + "weight": "15.0 kg", + "candy": "Goldeen Candy", + "candy_count": 50, + "egg": "5 km", + "spawn_chance": 2.18, + "avg_spawns": 218, + "spawn_time": "03:14", + "multipliers": [ + 2.15, + 2.2 + ], + "weaknesses": [ + "Electric", + "Grass" + ], + "next_evolution": [{ + "num": "119", + "name": "Seaking" + }] + }, { + "id": 119, + "num": "119", + "name": "Seaking", + "img": "http://www.serebii.net/pokemongo/pokemon/119.png", + "type": [ + "Water" + ], + "height": "1.30 m", + "weight": "39.0 kg", + "candy": "Goldeen Candy", + "egg": "Not in Eggs", + "spawn_chance": 0.08, + "avg_spawns": 8, + "spawn_time": "05:21", + "multipliers": [], + "weaknesses": [ + "Electric", + "Grass" + ], + "prev_evolution": [{ + "num": "118", + "name": "Goldeen" + }] + }, { + "id": 120, + "num": "120", + "name": "Staryu", + "img": "http://www.serebii.net/pokemongo/pokemon/120.png", + "type": [ + "Water" + ], + "height": "0.79 m", + "weight": "34.5 kg", + "candy": "Staryu Candy", + "candy_count": 50, + "egg": "5 km", + "spawn_chance": 1.95, + "avg_spawns": 195, + "spawn_time": "22:59", + "multipliers": [ + 2.38, + 2.41 + ], + "weaknesses": [ + "Electric", + "Grass" + ], + "next_evolution": [{ + "num": "121", + "name": "Starmie" + }] + }, { + "id": 121, + "num": "121", + "name": "Starmie", + "img": "http://www.serebii.net/pokemongo/pokemon/121.png", + "type": [ + "Water", + "Psychic" + ], + "height": "1.09 m", + "weight": "80.0 kg", + "candy": "Staryu Candy", + "egg": "Not in Eggs", + "spawn_chance": 0.034, + "avg_spawns": 3.4, + "spawn_time": "06:57", + "multipliers": [], + "weaknesses": [ + "Electric", + "Grass", + "Bug", + "Ghost", + "Dark" + ], + "prev_evolution": [{ + "num": "120", + "name": "Staryu" + }] + }, { + "id": 122, + "num": "122", + "name": "Mr. Mime", + "img": "http://www.serebii.net/pokemongo/pokemon/122.png", + "type": [ + "Psychic" + ], + "height": "1.30 m", + "weight": "54.5 kg", + "candy": "None", + "egg": "10 km", + "spawn_chance": 0.0031, + "avg_spawns": 0.31, + "spawn_time": "01:51", + "multipliers": [], + "weaknesses": [ + "Bug", + "Ghost", + "Dark" + ] + }, { + "id": 123, + "num": "123", + "name": "Scyther", + "img": "http://www.serebii.net/pokemongo/pokemon/123.png", + "type": [ + "Bug", + "Flying" + ], + "height": "1.50 m", + "weight": "56.0 kg", + "candy": "None", + "egg": "10 km", + "spawn_chance": 0.14, + "avg_spawns": 14, + "spawn_time": "05:43", + "multipliers": [], + "weaknesses": [ + "Fire", + "Electric", + "Ice", + "Flying", + "Rock" + ] + }, { + "id": 124, + "num": "124", + "name": "Jynx", + "img": "http://www.serebii.net/pokemongo/pokemon/124.png", + "type": [ + "Ice", + "Psychic" + ], + "height": "1.40 m", + "weight": "40.6 kg", + "candy": "None", + "egg": "10 km", + "spawn_chance": 0.35, + "avg_spawns": 35, + "spawn_time": "05:41", + "multipliers": [], + "weaknesses": [ + "Fire", + "Bug", + "Rock", + "Ghost", + "Dark", + "Steel" + ] + }, { + "id": 125, + "num": "125", + "name": "Electabuzz", + "img": "http://www.serebii.net/pokemongo/pokemon/125.png", + "type": [ + "Electric" + ], + "height": "1.09 m", + "weight": "30.0 kg", + "candy": "None", + "egg": "10 km", + "spawn_chance": 0.074, + "avg_spawns": 7.4, + "spawn_time": "04:28", + "multipliers": [], + "weaknesses": [ + "Ground" + ] + }, { + "id": 126, + "num": "126", + "name": "Magmar", + "img": "http://www.serebii.net/pokemongo/pokemon/126.png", + "type": [ + "Fire" + ], + "height": "1.30 m", + "weight": "44.5 kg", + "candy": "None", + "egg": "10 km", + "spawn_chance": 0.10, + "avg_spawns": 10, + "spawn_time": "20:36", + "multipliers": [], + "weaknesses": [ + "Water", + "Ground", + "Rock" + ] + }, { + "id": 127, + "num": "127", + "name": "Pinsir", + "img": "http://www.serebii.net/pokemongo/pokemon/127.png", + "type": [ + "Bug" + ], + "height": "1.50 m", + "weight": "55.0 kg", + "candy": "None", + "egg": "10 km", + "spawn_chance": 0.99, + "avg_spawns": 99, + "spawn_time": "03:25", + "multipliers": [], + "weaknesses": [ + "Fire", + "Flying", + "Rock" + ] + }, { + "id": 128, + "num": "128", + "name": "Tauros", + "img": "http://www.serebii.net/pokemongo/pokemon/128.png", + "type": [ + "Normal" + ], + "height": "1.40 m", + "weight": "88.4 kg", + "candy": "None", + "egg": "5 km", + "spawn_chance": 0.12, + "avg_spawns": 12, + "spawn_time": "00:37", + "multipliers": [], + "weaknesses": [ + "Fighting" + ] + }, { + "id": 129, + "num": "129", + "name": "Magikarp", + "img": "http://www.serebii.net/pokemongo/pokemon/129.png", + "type": [ + "Water" + ], + "height": "0.89 m", + "weight": "10.0 kg", + "candy": "Magikarp Candy", + "candy_count": 400, + "egg": "2 km", + "spawn_chance": 4.78, + "avg_spawns": 478, + "spawn_time": "14:26", + "multipliers": [ + 10.1, + 11.8 + ], + "weaknesses": [ + "Electric", + "Grass" + ], + "next_evolution": [{ + "num": "130", + "name": "Gyarados" + }] + }, { + "id": 130, + "num": "130", + "name": "Gyarados", + "img": "http://www.serebii.net/pokemongo/pokemon/130.png", + "type": [ + "Water", + "Flying" + ], + "height": "6.50 m", + "weight": "235.0 kg", + "candy": "Magikarp Candy", + "egg": "Not in Eggs", + "spawn_chance": 0.0032, + "avg_spawns": 0.32, + "spawn_time": "02:15", + "multipliers": [], + "weaknesses": [ + "Electric", + "Rock" + ], + "prev_evolution": [{ + "num": "129", + "name": "Magikarp" + }] + }, { + "id": 131, + "num": "131", + "name": "Lapras", + "img": "http://www.serebii.net/pokemongo/pokemon/131.png", + "type": [ + "Water", + "Ice" + ], + "height": "2.49 m", + "weight": "220.0 kg", + "candy": "None", + "egg": "10 km", + "spawn_chance": 0.006, + "avg_spawns": 0.6, + "spawn_time": "08:59", + "multipliers": [], + "weaknesses": [ + "Electric", + "Grass", + "Fighting", + "Rock" + ] + }, { + "id": 132, + "num": "132", + "name": "Ditto", + "img": "http://www.serebii.net/pokemongo/pokemon/132.png", + "type": [ + "Normal" + ], + "height": "0.30 m", + "weight": "4.0 kg", + "candy": "None", + "egg": "Not in Eggs", + "spawn_chance": 0, + "avg_spawns": 0, + "spawn_time": "N/A", + "multipliers": [], + "weaknesses": [ + "Fighting" + ] + }, { + "id": 133, + "num": "133", + "name": "Eevee", + "img": "http://www.serebii.net/pokemongo/pokemon/133.png", + "type": [ + "Normal" + ], + "height": "0.30 m", + "weight": "6.5 kg", + "candy": "Eevee Candy", + "candy_count": 25, + "egg": "10 km", + "spawn_chance": 2.75, + "avg_spawns": 275, + "spawn_time": "05:32", + "multipliers": [ + 2.02, + 2.64 + ], + "weaknesses": [ + "Fighting" + ], + "next_evolution": [{ + "num": "134", + "name": "Vaporeon" + }, { + "num": "135", + "name": "Jolteon" + }, { + "num": "136", + "name": "Flareon" + }] + }, { + "id": 134, + "num": "134", + "name": "Vaporeon", + "img": "http://www.serebii.net/pokemongo/pokemon/134.png", + "type": [ + "Water" + ], + "height": "0.99 m", + "weight": "29.0 kg", + "candy": "Eevee Candy", + "egg": "Not in Eggs", + "spawn_chance": 0.014, + "avg_spawns": 1.4, + "spawn_time": "10:54", + "multipliers": [], + "weaknesses": [ + "Electric", + "Grass" + ], + "prev_evolution": [{ + "num": "133", + "name": "Eevee" + }] + }, { + "id": 135, + "num": "135", + "name": "Jolteon", + "img": "http://www.serebii.net/pokemongo/pokemon/135.png", + "type": [ + "Electric" + ], + "height": "0.79 m", + "weight": "24.5 kg", + "candy": "None", + "egg": "Not in Eggs", + "spawn_chance": 0.012, + "avg_spawns": 1.2, + "spawn_time": "02:30", + "multipliers": [], + "weaknesses": [ + "Ground" + ], + "prev_evolution": [{ + "num": "133", + "name": "Eevee" + }] + }, { + "id": 136, + "num": "136", + "name": "Flareon", + "img": "http://www.serebii.net/pokemongo/pokemon/136.png", + "type": [ + "Fire" + ], + "height": "0.89 m", + "weight": "25.0 kg", + "candy": "Eevee Candy", + "egg": "Not in Eggs", + "spawn_chance": 0.017, + "avg_spawns": 1.7, + "spawn_time": "07:02", + "multipliers": [], + "weaknesses": [ + "Water", + "Ground", + "Rock" + ], + "prev_evolution": [{ + "num": "133", + "name": "Eevee" + }] + }, { + "id": 137, + "num": "137", + "name": "Porygon", + "img": "http://www.serebii.net/pokemongo/pokemon/137.png", + "type": [ + "Normal" + ], + "height": "0.79 m", + "weight": "36.5 kg", + "candy": "None", + "egg": "5 km", + "spawn_chance": 0.012, + "avg_spawns": 1.2, + "spawn_time": "02:49", + "multipliers": [], + "weaknesses": [ + "Fighting" + ] + }, { + "id": 138, + "num": "138", + "name": "Omanyte", + "img": "http://www.serebii.net/pokemongo/pokemon/138.png", + "type": [ + "Rock", + "Water" + ], + "height": "0.41 m", + "weight": "7.5 kg", + "candy": "Omanyte Candy", + "candy_count": 50, + "egg": "10 km", + "spawn_chance": 0.14, + "avg_spawns": 14, + "spawn_time": "10:23", + "multipliers": [2.12], + "weaknesses": [ + "Electric", + "Grass", + "Fighting", + "Ground" + ], + "next_evolution": [{ + "num": "139", + "name": "Omastar" + }] + }, { + "id": 139, + "num": "139", + "name": "Omastar", + "img": "http://www.serebii.net/pokemongo/pokemon/139.png", + "type": [ + "Rock", + "Water" + ], + "height": "0.99 m", + "weight": "35.0 kg", + "candy": "None", + "egg": "Omanyte Candy", + "spawn_chance": 0.0061, + "avg_spawns": 0.61, + "spawn_time": "05:04", + "multipliers": [], + "weaknesses": [ + "Electric", + "Grass", + "Fighting", + "Ground" + ], + "prev_evolution": [{ + "num": "138", + "name": "Omanyte" + }] + }, { + "id": 140, + "num": "140", + "name": "Kabuto", + "img": "http://www.serebii.net/pokemongo/pokemon/140.png", + "type": [ + "Rock", + "Water" + ], + "height": "0.51 m", + "weight": "11.5 kg", + "candy": "Kabuto Candy", + "candy_count": 50, + "egg": "10 km", + "spawn_chance": 0.10, + "avg_spawns": 10, + "spawn_time": "00:05", + "multipliers": [ + 1.97, + 2.37 + ], + "weaknesses": [ + "Electric", + "Grass", + "Fighting", + "Ground" + ], + "next_evolution": [{ + "num": "141", + "name": "Kabutops" + }] + }, { + "id": 141, + "num": "141", + "name": "Kabutops", + "img": "http://www.serebii.net/pokemongo/pokemon/141.png", + "type": [ + "Rock", + "Water" + ], + "height": "1.30 m", + "weight": "40.5 kg", + "candy": "Kabuto Candy", + "egg": "Not in Eggs", + "spawn_chance": 0.0032, + "avg_spawns": 0.32, + "spawn_time": "23:40", + "multipliers": [], + "weaknesses": [ + "Electric", + "Grass", + "Fighting", + "Ground" + ], + "prev_evolution": [{ + "num": "140", + "name": "Kabuto" + }] + }, { + "id": 142, + "num": "142", + "name": "Aerodactyl", + "img": "http://www.serebii.net/pokemongo/pokemon/142.png", + "type": [ + "Rock", + "Flying" + ], + "height": "1.80 m", + "weight": "59.0 kg", + "candy": "None", + "egg": "10 km", + "spawn_chance": 0.018, + "avg_spawns": 1.8, + "spawn_time": "23:40", + "multipliers": [], + "weaknesses": [ + "Water", + "Electric", + "Ice", + "Rock", + "Steel" + ] + }, { + "id": 143, + "num": "143", + "name": "Snorlax", + "img": "http://www.serebii.net/pokemongo/pokemon/143.png", + "type": [ + "Normal" + ], + "height": "2.11 m", + "weight": "460.0 kg", + "candy": "None", + "egg": "10 km", + "spawn_chance": 0.016, + "avg_spawns": 1.6, + "spawn_time": "23:40", + "multipliers": [], + "weaknesses": [ + "Fighting" + ] + }, { + "id": 144, + "num": "144", + "name": "Articuno", + "img": "http://www.serebii.net/pokemongo/pokemon/144.png", + "type": [ + "Ice", + "Flying" + ], + "height": "1.70 m", + "weight": "55.4 kg", + "candy": "None", + "egg": "Not in Eggs", + "spawn_chance": 0, + "avg_spawns": 0, + "spawn_time": "N/A", + "multipliers": [], + "weaknesses": [ + "Fire", + "Electric", + "Rock", + "Steel" + ] + }, { + "id": 145, + "num": "145", + "name": "Zapdos", + "img": "http://www.serebii.net/pokemongo/pokemon/145.png", + "type": [ + "Electric", + "Flying" + ], + "height": "1.60 m", + "weight": "52.6 kg", + "candy": "None", + "egg": "Not in Eggs", + "spawn_chance": 0, + "avg_spawns": 0, + "spawn_time": "N/A", + "multipliers": [], + "weaknesses": [ + "Ice", + "Rock" + ] + }, { + "id": 146, + "num": "146", + "name": "Moltres", + "img": "http://www.serebii.net/pokemongo/pokemon/146.png", + "type": [ + "Fire", + "Flying" + ], + "height": "2.01 m", + "weight": "60.0 kg", + "candy": "None", + "egg": "Not in Eggs", + "spawn_chance": 0, + "avg_spawns": 0, + "spawn_time": "N/A", + "multipliers": [], + "weaknesses": [ + "Water", + "Electric", + "Rock" + ] + }, { + "id": 147, + "num": "147", + "name": "Dratini", + "img": "http://www.serebii.net/pokemongo/pokemon/147.png", + "type": [ + "Dragon" + ], + "height": "1.80 m", + "weight": "3.3 kg", + "candy": "Dratini Candy", + "candy_count": 25, + "egg": "10 km", + "spawn_chance": 0.30, + "avg_spawns": 30, + "spawn_time": "06:41", + "multipliers": [ + 1.83, + 1.84 + ], + "weaknesses": [ + "Ice", + "Dragon", + "Fairy" + ], + "next_evolution": [{ + "num": "148", + "name": "Dragonair" + }, { + "num": "149", + "name": "Dragonite" + }] + }, { + "id": 148, + "num": "148", + "name": "Dragonair", + "img": "http://www.serebii.net/pokemongo/pokemon/148.png", + "type": [ + "Dragon" + ], + "height": "3.99 m", + "weight": "16.5 kg", + "candy": "Dratini Candy", + "candy_count": 100, + "egg": "Not in Eggs", + "spawn_chance": 0.02, + "avg_spawns": 2, + "spawn_time": "11:57", + "multipliers": [2.05], + "weaknesses": [ + "Ice", + "Dragon", + "Fairy" + ], + "prev_evolution": [{ + "num": "147", + "name": "Dratini" + }], + "next_evolution": [{ + "num": "149", + "name": "Dragonite" + }] + }, { + "id": 149, + "num": "149", + "name": "Dragonite", + "img": "http://www.serebii.net/pokemongo/pokemon/149.png", + "type": [ + "Dragon", + "Flying" + ], + "height": "2.21 m", + "weight": "210.0 kg", + "candy": "Dratini Candy", + "egg": "Not in Eggs", + "spawn_chance": 0.0011, + "avg_spawns": 0.11, + "spawn_time": "23:38", + "multipliers": [], + "weaknesses": [ + "Ice", + "Rock", + "Dragon", + "Fairy" + ], + "prev_evolution": [{ + "num": "147", + "name": "Dratini" + }, { + "num": "148", + "name": "Dragonair" + }] + }, { + "id": 150, + "num": "150", + "name": "Mewtwo", + "img": "http://www.serebii.net/pokemongo/pokemon/150.png", + "type": [ + "Psychic" + ], + "height": "2.01 m", + "weight": "122.0 kg", + "candy": "None", + "egg": "Not in Eggs", + "spawn_chance": 0, + "avg_spawns": 0, + "spawn_time": "N/A", + "multipliers": [], + "weaknesses": [ + "Bug", + "Ghost", + "Dark" + ] + }, { + "id": 151, + "num": "151", + "name": "Mew", + "img": "http://www.serebii.net/pokemongo/pokemon/151.png", + "type": [ + "Psychic" + ], + "height": "0.41 m", + "weight": "4.0 kg", + "candy": "None", + "egg": "Not in Eggs", + "spawn_chance": 0, + "avg_spawns": 0, + "spawn_time": "N/A", + "multipliers": [], + "weaknesses": [ + "Bug", + "Ghost", + "Dark" + ] + }] +} diff --git a/tp-json/tests/json/string_hello.json b/tp-json/tests/json/string_hello.json new file mode 100644 index 00000000..5638d8ba --- /dev/null +++ b/tp-json/tests/json/string_hello.json @@ -0,0 +1 @@ +"Hello" \ No newline at end of file diff --git a/tp-json/tests/old/50_number_node_hardmode.cpp b/tp-json/tests/old/50_number_node_hardmode.cpp new file mode 100644 index 00000000..46c8b9fe --- /dev/null +++ b/tp-json/tests/old/50_number_node_hardmode.cpp @@ -0,0 +1,19 @@ +#include "../JsonParser.hpp" +#include "assert.cpp" + +int main(int argc, char** argv) +{ + if (argc < 1) + { + std::cout << "First command-line argument needs to be where are the json resources."; + exit(EXIT_FAILURE); + } + std::string dir = std::string(argv[1]); + std::string filename; + Node_ptr node; + + filename = dir + "array_large_numbers.json"; + node = JsonParser::parse_from_file(filename); + auto const& array_node = node->as_ArrayNode(); + for (unsigned i = 0; i < node->children_count()) +} \ No newline at end of file diff --git a/tp-json/tests/old/NullNode.hpp b/tp-json/tests/old/NullNode.hpp new file mode 100644 index 00000000..f7fa6e2d --- /dev/null +++ b/tp-json/tests/old/NullNode.hpp @@ -0,0 +1,25 @@ +#pragma once + +#include "Node.hpp" + +#include + +class NullNode : public Node +{ +public: + std::string print() const override { return "null"; } + NullNode() + : Node { NodeKind::NONE } + {} + + static std::unique_ptr make_ptr() { return std::make_unique(); } + + inline bool operator==(const Node& node) const override { return (node.is_of_kind(kind())); } + + NullNode* as_NullNode() override { return this; } + NullNode const* as_NullNode() const override { return this; } + + Node_ptr deep_copy() const override { return make_ptr(); } + + std::string dot_label() const override { return "null"; } +}; diff --git a/tp-json/tests/old/array_large_numbers.json b/tp-json/tests/old/array_large_numbers.json new file mode 100644 index 00000000..97f5c995 --- /dev/null +++ b/tp-json/tests/old/array_large_numbers.json @@ -0,0 +1 @@ +[9007199254740992,9007199254740992.0,9007199254740993] \ No newline at end of file diff --git a/tp-json/tests/old/basic_node_instances.cpp b/tp-json/tests/old/basic_node_instances.cpp new file mode 100644 index 00000000..25ec4018 --- /dev/null +++ b/tp-json/tests/old/basic_node_instances.cpp @@ -0,0 +1,41 @@ +#pragma once + +#include "../json.hpp" + +namespace example { +/* This complicated declaration is to let the implementer choose the return type of ObjectNode::make_ptr() */ +auto make_ArrayNode_example2() -> decltype(ArrayNode::make_ptr()) +{ + auto node = ArrayNode::make_ptr(); + + return node; +} + +/* This complicated declaration is to let the implementer choose the return type of ObjectNode::make_ptr() */ +auto make_ObjectNode_example2() -> decltype(ObjectNode::make_ptr()) +{ + auto node = ObjectNode::make_ptr(); + node->add("premier", StringLeaf::make_ptr("Child 1")); + auto child2 = ArrayNode::make_ptr(); + child2->add(NullNode::make_ptr()); + child2->add(StringLeaf::make_ptr("Grandchild with null siblings")); + child2->add(NullNode::make_ptr()); + node->add("enfant fecond", std::move(child2)); + node->add("enfant vide", ArrayNode::make_ptr()); + node->add("le petit dernier", StringLeaf::make_ptr("Benjamin")); + return node; +} +std::vector const instances { NullNode::make_ptr(), NullNode::make_ptr(), + NumberNode::make_ptr(0), NumberNode::make_ptr(42), + StringLeaf::make_ptr(""), StringLeaf::make_ptr("Hello world!"), + ArrayNode::make_ptr(), make_ArrayNode_example2(), + ObjectNode::make_ptr(), make_ObjectNode_example2() }; + +auto& null_node_ptr1 = instances[1]; +auto& null_node_ptr2 = instances[2]; +auto& int_node_ptr2 = instances[3]; +auto& string_node_ptr2 = instances[4]; +auto& array_node_ptr1 = instances[5]; +auto& object_node_ptr1 = instances[6]; + +} // namespace example diff --git a/tp-json/tests/old/json.hpp b/tp-json/tests/old/json.hpp new file mode 100644 index 00000000..f544f474 --- /dev/null +++ b/tp-json/tests/old/json.hpp @@ -0,0 +1,9 @@ +#pragma once + +#include "../ArrayNode.hpp" +#include "../BooleanLeaf.hpp" +#include "../Node.hpp" +#include "../NodeKind.hpp" +#include "../NumberLeaf.hpp" +#include "../ObjectNode.hpp" +#include "../StringLeaf.hpp" From 96070e53e7e4a1eebaaa3a3591c88a818fd7d2aa Mon Sep 17 00:00:00 2001 From: Victor Marsault Date: Fri, 25 Mar 2022 14:11:17 +0100 Subject: [PATCH 2/5] Updated code for students --- tp-json/NodeKind.hpp | 2 - .../code_fourni_aux_etudiants/ArrayNode.cpp | 1 + .../code_fourni_aux_etudiants/BooleanLeaf.cpp | 1 + .../{StringNode.hpp => BooleanLeaf.hpp} | 2 + .../code_fourni_aux_etudiants/JsonParser.cpp | 240 ++++++++++++++++++ .../code_fourni_aux_etudiants/JsonParser.hpp | 41 +++ tp-json/code_fourni_aux_etudiants/Node.cpp | 1 + tp-json/code_fourni_aux_etudiants/Node.hpp | 6 - .../code_fourni_aux_etudiants/NodeKind.hpp | 4 - .../code_fourni_aux_etudiants/Node_ptr.hpp | 4 +- .../code_fourni_aux_etudiants/NumberLeaf.cpp | 1 + .../{BooleanNode.hpp => NumberLeaf.hpp} | 0 .../code_fourni_aux_etudiants/ObjectNode.cpp | 1 + .../code_fourni_aux_etudiants/StringLeaf.cpp | 1 + .../{NumberNode.hpp => StringLeaf.hpp} | 0 .../code_fourni_aux_etudiants/all_nodes.hpp | 9 - 16 files changed, 292 insertions(+), 22 deletions(-) create mode 100644 tp-json/code_fourni_aux_etudiants/ArrayNode.cpp create mode 100644 tp-json/code_fourni_aux_etudiants/BooleanLeaf.cpp rename tp-json/code_fourni_aux_etudiants/{StringNode.hpp => BooleanLeaf.hpp} (50%) create mode 100644 tp-json/code_fourni_aux_etudiants/JsonParser.cpp create mode 100644 tp-json/code_fourni_aux_etudiants/JsonParser.hpp create mode 100644 tp-json/code_fourni_aux_etudiants/Node.cpp create mode 100644 tp-json/code_fourni_aux_etudiants/NumberLeaf.cpp rename tp-json/code_fourni_aux_etudiants/{BooleanNode.hpp => NumberLeaf.hpp} (100%) create mode 100644 tp-json/code_fourni_aux_etudiants/ObjectNode.cpp create mode 100644 tp-json/code_fourni_aux_etudiants/StringLeaf.cpp rename tp-json/code_fourni_aux_etudiants/{NumberNode.hpp => StringLeaf.hpp} (100%) delete mode 100644 tp-json/code_fourni_aux_etudiants/all_nodes.hpp diff --git a/tp-json/NodeKind.hpp b/tp-json/NodeKind.hpp index 9c1e603b..ddb63953 100644 --- a/tp-json/NodeKind.hpp +++ b/tp-json/NodeKind.hpp @@ -17,8 +17,6 @@ inline std::ostream& operator<<(std::ostream& o, NodeKind kind) { switch (kind) { - /* case NodeKind::NONE: - return o << "NONE"; */ case NodeKind::BOOLEAN: return o << "BOOLEAN"; case NodeKind::NUMBER: diff --git a/tp-json/code_fourni_aux_etudiants/ArrayNode.cpp b/tp-json/code_fourni_aux_etudiants/ArrayNode.cpp new file mode 100644 index 00000000..3232bf18 --- /dev/null +++ b/tp-json/code_fourni_aux_etudiants/ArrayNode.cpp @@ -0,0 +1 @@ +#include "ArrayNode.hpp" \ No newline at end of file diff --git a/tp-json/code_fourni_aux_etudiants/BooleanLeaf.cpp b/tp-json/code_fourni_aux_etudiants/BooleanLeaf.cpp new file mode 100644 index 00000000..530e217c --- /dev/null +++ b/tp-json/code_fourni_aux_etudiants/BooleanLeaf.cpp @@ -0,0 +1 @@ +#include "BooleanLeaf.hpp" \ No newline at end of file diff --git a/tp-json/code_fourni_aux_etudiants/StringNode.hpp b/tp-json/code_fourni_aux_etudiants/BooleanLeaf.hpp similarity index 50% rename from tp-json/code_fourni_aux_etudiants/StringNode.hpp rename to tp-json/code_fourni_aux_etudiants/BooleanLeaf.hpp index 6f70f09b..9b1559cc 100644 --- a/tp-json/code_fourni_aux_etudiants/StringNode.hpp +++ b/tp-json/code_fourni_aux_etudiants/BooleanLeaf.hpp @@ -1 +1,3 @@ #pragma once + +#pragma once \ No newline at end of file diff --git a/tp-json/code_fourni_aux_etudiants/JsonParser.cpp b/tp-json/code_fourni_aux_etudiants/JsonParser.cpp new file mode 100644 index 00000000..a95644ff --- /dev/null +++ b/tp-json/code_fourni_aux_etudiants/JsonParser.cpp @@ -0,0 +1,240 @@ +#include "JsonParser.hpp" + +#include +#include +#include +#include + +void JsonParser::extract_spaces() +{ + while (!_in.eof() && std::isspace(_in.peek())) + _in.get(); +} + +bool JsonParser::check_next_char_equals(int c, std::string_view v) +{ + int c2 = _in.peek(); + if (c == c2) + { + _in.get(); + return true; + } + std::cerr << "Unexpected character ("; + if (c2 >= 20 && c2 < 127) + std::cerr << (char)c2; + else if (c2 == -1) + std::cerr << "EOF"; + else + std::cerr << '\\' << c2; + if (v != "") + std::cerr << "). Expecting a char in \"" << v << "\"." << std::endl; + else + std::cerr << "). Expecting '" << (char)c << "'." << std::endl; + return false; +} + +Node_ptr JsonParser::parse_Node() +{ + extract_spaces(); + int c {}; + switch (c = _in.peek()) + { + case '{': + return parse_ObjectNode(); + case '[': + return parse_ArrayNode(); + case '"': + return parse_StringLeaf(); + // case 'n': + // return parse_constant("null"); + case 'f': + return parse_constant("false"); + case 't': + return parse_constant("true"); + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case 'e': + case 'E': + case '.': + case '-': + case '+': + return parse_NumberLeaf(); + default: + check_next_char_equals(-123456789, "{[\"nft0123456789eE.-+"); + return nullptr; + } +} + +Node_ptr JsonParser::parse_constant(std::string_view target) +{ + char c[6]; + for (size_t x = 0; x < target.size(); x++) + { + _in >> c[x]; + if (c[x] != target[x]) + { + c[x + 1] = '\0'; + std::cerr << "Expecting JSON value (probably constant " << target << ") at position " + << (_in.tellg() - (long)x) << ". Got string starting with " << c << std::endl; + return nullptr; + } + } + // if (target == "null") + // return NullNode::make_ptr(); + if (target == "true") + return BooleanLeaf::make_ptr(true); + if (target == "false") + return BooleanLeaf::make_ptr(false); + return nullptr; +} + +std::optional JsonParser::extract_string() +{ + extract_spaces(); + check_next_char_equals('"'); + std::string s = ""; + char c = '\0'; + while (((c = _in.get()) != '"') && !_in.eof()) + { + s += c; + if (c == '\\') /* If c is a '\' then the next char is escaped. */ + { + c = _in.get(); + if (_in.eof()) + break; + s += _in.get(); + } + } + + if (_in.eof()) + { + std::cerr << "Found EOF while looking for closing \"." << std::endl; + return std::optional(); + } + return s; +} + +Node_ptr JsonParser::parse_StringLeaf() +{ + auto str = extract_string(); + if (str) + return StringLeaf::make_ptr(std::move(str.value())); + else + return nullptr; +} + +Node_ptr JsonParser::parse_NumberLeaf() +{ + // unsigned starting_pos = _in.tellg(); + + double d; + _in >> d; + // size_t end_pos_double = _in.tellg(); + + return NumberLeaf::make_ptr(d); +} + +Node_ptr JsonParser::parse_ArrayNode() +{ + if (!check_next_char_equals('[')) + return nullptr; + auto arrayNode = ArrayNode::make_ptr(); + extract_spaces(); + if (_in.peek() == ']') + { + _in.get(); + return arrayNode; + } + + do + { + auto child = parse_Node(); + if (child == nullptr) + return nullptr; + else + arrayNode->add(std::move(child)); + extract_spaces(); + } + while (_in.get() == ','); + + _in.unget(); + if (check_next_char_equals(']', ",]")) + return arrayNode; + else + return nullptr; +} + +Node_ptr JsonParser::parse_ObjectNode() +{ + if (!check_next_char_equals('{')) + return nullptr; + auto objectNode = ObjectNode::make_ptr(); + extract_spaces(); + if (_in.peek() == '}') + { + _in.get(); + return objectNode; + } + + do + { + std::optional opt_key = extract_string(); + if (!opt_key) + return nullptr; + extract_spaces(); + if (!check_next_char_equals(':')) + return nullptr; + auto child = parse_Node(); + if (child == nullptr) + return nullptr; + else + objectNode->add(std::move(opt_key.value()), std::move(child)); + extract_spaces(); + } + while (_in.get() == ','); + _in.unget(); + if (check_next_char_equals('}', ",}")) + return objectNode; + else + return nullptr; +} + +Node_ptr JsonParser::run() +{ + return parse_Node(); +} + +Node_ptr JsonParser::parse_from_istream(std::istream& in) +{ + JsonParser parser(in); + Node_ptr parsed_tree = parser.run(); + if (parsed_tree) + return parsed_tree; + else + exit(EXIT_FAILURE); +} + +Node_ptr JsonParser::parse_from_file(std::string const& path) +{ + std::ifstream in(path.c_str(), std::ifstream::in); + if (!in.is_open()) + { + std::cerr << "Could not open file: " << path << std::endl; + exit(EXIT_FAILURE); + } + return parse_from_istream(in); +} + +Node_ptr JsonParser::parse_from_string(std::string const& str) +{ + std::stringstream ss { str }; + return parse_from_istream(ss); +} diff --git a/tp-json/code_fourni_aux_etudiants/JsonParser.hpp b/tp-json/code_fourni_aux_etudiants/JsonParser.hpp new file mode 100644 index 00000000..5f0bb89a --- /dev/null +++ b/tp-json/code_fourni_aux_etudiants/JsonParser.hpp @@ -0,0 +1,41 @@ +#pragma once + +#include "ArrayNode.hpp" +#include "BooleanLeaf.hpp" +#include "Node.hpp" +#include "NodeKind.hpp" +#include "NumberLeaf.hpp" +#include "ObjectNode.hpp" +#include "StringLeaf.hpp" + +#include +#include + +class JsonParser +{ +private: + std::istream& _in; + + void extract_spaces(); + bool check_next_char_equals(int c, std::string_view other_possibilities = ""); + + std::optional extract_string(); + + Node_ptr parse_Node(); + Node_ptr parse_constant(std::string_view target); + Node_ptr parse_StringLeaf(); + Node_ptr parse_NumberLeaf(); + Node_ptr parse_ArrayNode(); + Node_ptr parse_ObjectNode(); + +public: + JsonParser(std::istream& in) + : _in(in) + {} + + Node_ptr run(); + + static Node_ptr parse_from_istream(std::istream& in); + static Node_ptr parse_from_file(std::string const& path); + static Node_ptr parse_from_string(std::string const& str); +}; diff --git a/tp-json/code_fourni_aux_etudiants/Node.cpp b/tp-json/code_fourni_aux_etudiants/Node.cpp new file mode 100644 index 00000000..d4c476f4 --- /dev/null +++ b/tp-json/code_fourni_aux_etudiants/Node.cpp @@ -0,0 +1 @@ +#include "Node.hpp" diff --git a/tp-json/code_fourni_aux_etudiants/Node.hpp b/tp-json/code_fourni_aux_etudiants/Node.hpp index 7a31b0be..6f70f09b 100644 --- a/tp-json/code_fourni_aux_etudiants/Node.hpp +++ b/tp-json/code_fourni_aux_etudiants/Node.hpp @@ -1,7 +1 @@ #pragma once - -#include "NodeKind.hpp" -#include "Node_ptr.hpp" - -class Node -{}; \ No newline at end of file diff --git a/tp-json/code_fourni_aux_etudiants/NodeKind.hpp b/tp-json/code_fourni_aux_etudiants/NodeKind.hpp index cf970b14..b436f326 100644 --- a/tp-json/code_fourni_aux_etudiants/NodeKind.hpp +++ b/tp-json/code_fourni_aux_etudiants/NodeKind.hpp @@ -4,8 +4,6 @@ enum class NodeKind { - /* NONE is used for NullNode since NULL is reserved */ - NONE, BOOLEAN, NUMBER, STRING, @@ -17,8 +15,6 @@ inline std::ostream& operator<<(std::ostream& o, NodeKind kind) { switch (kind) { - case NodeKind::NONE: - return o << "NONE"; case NodeKind::BOOLEAN: return o << "BOOLEAN"; case NodeKind::NUMBER: diff --git a/tp-json/code_fourni_aux_etudiants/Node_ptr.hpp b/tp-json/code_fourni_aux_etudiants/Node_ptr.hpp index 50098bea..636a7adb 100644 --- a/tp-json/code_fourni_aux_etudiants/Node_ptr.hpp +++ b/tp-json/code_fourni_aux_etudiants/Node_ptr.hpp @@ -1,3 +1,5 @@ #pragma once -using Node_ptr = void /* TODO!! */; \ No newline at end of file +class Node; + +using Node_ptr = void /* todo */; \ No newline at end of file diff --git a/tp-json/code_fourni_aux_etudiants/NumberLeaf.cpp b/tp-json/code_fourni_aux_etudiants/NumberLeaf.cpp new file mode 100644 index 00000000..ccc70860 --- /dev/null +++ b/tp-json/code_fourni_aux_etudiants/NumberLeaf.cpp @@ -0,0 +1 @@ +#include "NumberLeaf.hpp" diff --git a/tp-json/code_fourni_aux_etudiants/BooleanNode.hpp b/tp-json/code_fourni_aux_etudiants/NumberLeaf.hpp similarity index 100% rename from tp-json/code_fourni_aux_etudiants/BooleanNode.hpp rename to tp-json/code_fourni_aux_etudiants/NumberLeaf.hpp diff --git a/tp-json/code_fourni_aux_etudiants/ObjectNode.cpp b/tp-json/code_fourni_aux_etudiants/ObjectNode.cpp new file mode 100644 index 00000000..1af8b27c --- /dev/null +++ b/tp-json/code_fourni_aux_etudiants/ObjectNode.cpp @@ -0,0 +1 @@ +#include "ObjectNode.hpp" \ No newline at end of file diff --git a/tp-json/code_fourni_aux_etudiants/StringLeaf.cpp b/tp-json/code_fourni_aux_etudiants/StringLeaf.cpp new file mode 100644 index 00000000..f9d28654 --- /dev/null +++ b/tp-json/code_fourni_aux_etudiants/StringLeaf.cpp @@ -0,0 +1 @@ +#include "StringLeaf.hpp" \ No newline at end of file diff --git a/tp-json/code_fourni_aux_etudiants/NumberNode.hpp b/tp-json/code_fourni_aux_etudiants/StringLeaf.hpp similarity index 100% rename from tp-json/code_fourni_aux_etudiants/NumberNode.hpp rename to tp-json/code_fourni_aux_etudiants/StringLeaf.hpp diff --git a/tp-json/code_fourni_aux_etudiants/all_nodes.hpp b/tp-json/code_fourni_aux_etudiants/all_nodes.hpp deleted file mode 100644 index d4d973b9..00000000 --- a/tp-json/code_fourni_aux_etudiants/all_nodes.hpp +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once - -#include "ArrayNode.hpp" -#include "BooleanNode.hpp" -#include "Node.hpp" -#include "NodeKind.hpp" -#include "NumberNode.hpp" -#include "ObjectNode.hpp" -#include "StringNode.hpp" From fc0e9463bec35a52591e53ab85cd926b6940a1ed Mon Sep 17 00:00:00 2001 From: Victor Marsault Date: Mon, 28 Mar 2022 08:44:39 +0200 Subject: [PATCH 3/5] Updated according to Laefy's comments --- .gitignore | 1 + tp-json/.gitignore | 1 - tp-json/JsonParser.cpp | 15 ++++++- .../code_fourni_aux_etudiants/BooleanLeaf.hpp | 2 - .../code_fourni_aux_etudiants/JsonParser.cpp | 21 ++++++---- tp-json/enonce.md | 6 +-- .../{00_basic_node.cpp => 01_basic_node.cpp} | 4 +- tp-json/tests/01_basic_null.cpp | 14 ------- tp-json/tests/02_basic_boolean.cpp | 2 +- tp-json/tests/03_basic_number.cpp | 2 +- tp-json/tests/04_basic_string.cpp | 2 +- tp-json/tests/05_basic_array.cpp | 2 +- tp-json/tests/06_basic_object.cpp | 2 +- tp-json/tests/10_make_ptr.cpp | 2 +- tp-json/tests/11_array_add.cpp | 2 +- tp-json/tests/12_object_add.cpp | 2 +- tp-json/tests/13_height_and_nodecount.cpp | 2 +- tp-json/tests/14_object_dico.cpp | 2 +- tp-json/tests/21_parser_leaf.cpp | 2 +- tp-json/tests/22_parser_array.cpp | 2 +- tp-json/tests/23_parser_object.cpp | 2 +- tp-json/tests/24_parser_pokedex.cpp | 2 +- tp-json/tests/30_output_operator.cpp | 2 +- tp-json/tests/31_explicit_cast.cpp | 11 +---- tp-json/tests/32_explicit_cast_const.cpp | 13 +++--- tp-json/tests/33_children_count.cpp | 2 +- tp-json/tests/34_has_child.cpp | 2 +- tp-json/tests/35_at.cpp | 2 +- tp-json/tests/38_equality.cpp | 2 +- tp-json/tests/39_copy.cpp | 2 +- tp-json/tests/40_pokedex.cpp | 2 +- tp-json/tests/99_dot.cpp | 2 +- .../tests/{assert.cpp => custom_assert.hpp} | 4 +- tp-json/tests/old/50_number_node_hardmode.cpp | 19 --------- tp-json/tests/old/NullNode.hpp | 25 ----------- tp-json/tests/old/array_large_numbers.json | 1 - tp-json/tests/old/basic_node_instances.cpp | 41 ------------------- tp-json/tests/old/json.hpp | 9 ---- 38 files changed, 64 insertions(+), 167 deletions(-) delete mode 100644 tp-json/.gitignore rename tp-json/tests/{00_basic_node.cpp => 01_basic_node.cpp} (59%) delete mode 100644 tp-json/tests/01_basic_null.cpp rename tp-json/tests/{assert.cpp => custom_assert.hpp} (92%) delete mode 100644 tp-json/tests/old/50_number_node_hardmode.cpp delete mode 100644 tp-json/tests/old/NullNode.hpp delete mode 100644 tp-json/tests/old/array_large_numbers.json delete mode 100644 tp-json/tests/old/basic_node_instances.cpp delete mode 100644 tp-json/tests/old/json.hpp diff --git a/.gitignore b/.gitignore index 173bd29c..10aef982 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ .vscode/ build/ solutions/ +_build diff --git a/tp-json/.gitignore b/tp-json/.gitignore deleted file mode 100644 index e35d8850..00000000 --- a/tp-json/.gitignore +++ /dev/null @@ -1 +0,0 @@ -_build diff --git a/tp-json/JsonParser.cpp b/tp-json/JsonParser.cpp index a95644ff..2b96ad8e 100644 --- a/tp-json/JsonParser.cpp +++ b/tp-json/JsonParser.cpp @@ -21,23 +21,34 @@ bool JsonParser::check_next_char_equals(int c, std::string_view v) } std::cerr << "Unexpected character ("; if (c2 >= 20 && c2 < 127) + { std::cerr << (char)c2; + } else if (c2 == -1) + { std::cerr << "EOF"; + } else + { std::cerr << '\\' << c2; + } + if (v != "") + { std::cerr << "). Expecting a char in \"" << v << "\"." << std::endl; + } else + { std::cerr << "). Expecting '" << (char)c << "'." << std::endl; + } return false; } Node_ptr JsonParser::parse_Node() { extract_spaces(); - int c {}; - switch (c = _in.peek()) + const int c = _in.peek(); + switch (c) { case '{': return parse_ObjectNode(); diff --git a/tp-json/code_fourni_aux_etudiants/BooleanLeaf.hpp b/tp-json/code_fourni_aux_etudiants/BooleanLeaf.hpp index 9b1559cc..6f70f09b 100644 --- a/tp-json/code_fourni_aux_etudiants/BooleanLeaf.hpp +++ b/tp-json/code_fourni_aux_etudiants/BooleanLeaf.hpp @@ -1,3 +1 @@ #pragma once - -#pragma once \ No newline at end of file diff --git a/tp-json/code_fourni_aux_etudiants/JsonParser.cpp b/tp-json/code_fourni_aux_etudiants/JsonParser.cpp index a95644ff..3a15dde4 100644 --- a/tp-json/code_fourni_aux_etudiants/JsonParser.cpp +++ b/tp-json/code_fourni_aux_etudiants/JsonParser.cpp @@ -20,24 +20,31 @@ bool JsonParser::check_next_char_equals(int c, std::string_view v) return true; } std::cerr << "Unexpected character ("; - if (c2 >= 20 && c2 < 127) + if (c2 >= 20 && c2 < 127) { std::cerr << (char)c2; - else if (c2 == -1) + } + else if (c2 == -1) { std::cerr << "EOF"; - else + } + else { std::cerr << '\\' << c2; - if (v != "") + } + + + if (v != ""){ std::cerr << "). Expecting a char in \"" << v << "\"." << std::endl; - else + } + else{ std::cerr << "). Expecting '" << (char)c << "'." << std::endl; + } return false; } Node_ptr JsonParser::parse_Node() { extract_spaces(); - int c {}; - switch (c = _in.peek()) + int c = _in.peek() + switch (c) { case '{': return parse_ObjectNode(); diff --git a/tp-json/enonce.md b/tp-json/enonce.md index 7ccbb9fc..b49b9e73 100644 --- a/tp-json/enonce.md +++ b/tp-json/enonce.md @@ -1,6 +1,6 @@ # TP4 - Héritage -Let but de ce TP est d'implémenter les classes permettant de représenter en mémoire un document JSON (JavaScript Object Notation). +Le but de ce TP est d'implémenter les classes permettant de représenter en mémoire un document JSON (JavaScript Object Notation). ## Test Driven Development @@ -72,7 +72,7 @@ Par exemple, si on remarque que plusieurs classe partagent des fonctionnalités, ## Parseur Un parseur de JSON est fourni (classe `JsonParser` dans le fichier `JsonParser.cpp`), et normalement vous n'aurez pas besoin de le modifier. -Il pourra éventuellement être utile de regarder ce fichier partir des tests qui utilisent le parser. +Il pourra éventuellement être utile de regarder ce fichier partir des tests qui l'utilisent. @@ -80,4 +80,4 @@ Il pourra éventuellement être utile de regarder ce fichier partir des tests qu -## A vos claviers ! \ No newline at end of file +## A vos claviers ! diff --git a/tp-json/tests/00_basic_node.cpp b/tp-json/tests/01_basic_node.cpp similarity index 59% rename from tp-json/tests/00_basic_node.cpp rename to tp-json/tests/01_basic_node.cpp index 6025c054..c3e824ba 100644 --- a/tp-json/tests/00_basic_node.cpp +++ b/tp-json/tests/01_basic_node.cpp @@ -1,6 +1,6 @@ -/* This files exists to check that the basic files compile. */ +/* This files exists to check that the files compile. */ #include "../Node.hpp" -#include "assert.cpp" +#include "custom_assert.hpp" int main() { diff --git a/tp-json/tests/01_basic_null.cpp b/tp-json/tests/01_basic_null.cpp deleted file mode 100644 index 77f2986c..00000000 --- a/tp-json/tests/01_basic_null.cpp +++ /dev/null @@ -1,14 +0,0 @@ -// TO DELETE -#include "assert.cpp" - -int main() -{ - /* - NullNode p {}; - ASSERT_EQUAL(p.kind(), NodeKind::NONE); - ASSERT_EQUAL(p.print(), "null"); - - Node& r = p; - ASSERT_EQUAL(r.print(), "null"); - ASSERT_EQUAL(r.kind(), NodeKind::NONE);*/ -} diff --git a/tp-json/tests/02_basic_boolean.cpp b/tp-json/tests/02_basic_boolean.cpp index 611482c9..8a7fe5b9 100644 --- a/tp-json/tests/02_basic_boolean.cpp +++ b/tp-json/tests/02_basic_boolean.cpp @@ -1,5 +1,5 @@ #include "../BooleanLeaf.hpp" -#include "assert.cpp" +#include "custom_assert.hpp" int main() { diff --git a/tp-json/tests/03_basic_number.cpp b/tp-json/tests/03_basic_number.cpp index 3841d9a4..8162cc37 100644 --- a/tp-json/tests/03_basic_number.cpp +++ b/tp-json/tests/03_basic_number.cpp @@ -1,5 +1,5 @@ #include "../NumberLeaf.hpp" -#include "assert.cpp" +#include "custom_assert.hpp" int main() { diff --git a/tp-json/tests/04_basic_string.cpp b/tp-json/tests/04_basic_string.cpp index 5502b5d8..f3bf7000 100644 --- a/tp-json/tests/04_basic_string.cpp +++ b/tp-json/tests/04_basic_string.cpp @@ -1,5 +1,5 @@ #include "../StringLeaf.hpp" -#include "assert.cpp" +#include "custom_assert.hpp" int main() { diff --git a/tp-json/tests/05_basic_array.cpp b/tp-json/tests/05_basic_array.cpp index 22bf3ca8..1b18db71 100644 --- a/tp-json/tests/05_basic_array.cpp +++ b/tp-json/tests/05_basic_array.cpp @@ -1,5 +1,5 @@ #include "../ArrayNode.hpp" -#include "assert.cpp" +#include "custom_assert.hpp" int main() { diff --git a/tp-json/tests/06_basic_object.cpp b/tp-json/tests/06_basic_object.cpp index 4099668a..0c36e351 100644 --- a/tp-json/tests/06_basic_object.cpp +++ b/tp-json/tests/06_basic_object.cpp @@ -1,5 +1,5 @@ #include "../ObjectNode.hpp" -#include "assert.cpp" +#include "custom_assert.hpp" int main() { diff --git a/tp-json/tests/10_make_ptr.cpp b/tp-json/tests/10_make_ptr.cpp index 0c4b3923..bab7472b 100644 --- a/tp-json/tests/10_make_ptr.cpp +++ b/tp-json/tests/10_make_ptr.cpp @@ -5,7 +5,7 @@ #include "../NumberLeaf.hpp" #include "../ObjectNode.hpp" #include "../StringLeaf.hpp" -#include "assert.cpp" +#include "custom_assert.hpp" #include diff --git a/tp-json/tests/11_array_add.cpp b/tp-json/tests/11_array_add.cpp index 14e7b395..52b5c6ce 100644 --- a/tp-json/tests/11_array_add.cpp +++ b/tp-json/tests/11_array_add.cpp @@ -5,7 +5,7 @@ #include "../NumberLeaf.hpp" #include "../ObjectNode.hpp" #include "../StringLeaf.hpp" -#include "assert.cpp" +#include "custom_assert.hpp" int main() { diff --git a/tp-json/tests/12_object_add.cpp b/tp-json/tests/12_object_add.cpp index d4131a41..0568b2a7 100644 --- a/tp-json/tests/12_object_add.cpp +++ b/tp-json/tests/12_object_add.cpp @@ -5,7 +5,7 @@ #include "../NumberLeaf.hpp" #include "../ObjectNode.hpp" #include "../StringLeaf.hpp" -#include "assert.cpp" +#include "custom_assert.hpp" int main() { diff --git a/tp-json/tests/13_height_and_nodecount.cpp b/tp-json/tests/13_height_and_nodecount.cpp index ee94322c..e4af9dde 100644 --- a/tp-json/tests/13_height_and_nodecount.cpp +++ b/tp-json/tests/13_height_and_nodecount.cpp @@ -5,7 +5,7 @@ #include "../NumberLeaf.hpp" #include "../ObjectNode.hpp" #include "../StringLeaf.hpp" -#include "assert.cpp" +#include "custom_assert.hpp" #include diff --git a/tp-json/tests/14_object_dico.cpp b/tp-json/tests/14_object_dico.cpp index 2db8b675..fa492381 100644 --- a/tp-json/tests/14_object_dico.cpp +++ b/tp-json/tests/14_object_dico.cpp @@ -5,7 +5,7 @@ #include "../NumberLeaf.hpp" #include "../ObjectNode.hpp" #include "../StringLeaf.hpp" -#include "assert.cpp" +#include "custom_assert.hpp" int main() { diff --git a/tp-json/tests/21_parser_leaf.cpp b/tp-json/tests/21_parser_leaf.cpp index d07488c8..17db0482 100644 --- a/tp-json/tests/21_parser_leaf.cpp +++ b/tp-json/tests/21_parser_leaf.cpp @@ -1,5 +1,5 @@ #include "../JsonParser.hpp" -#include "assert.cpp" +#include "custom_assert.hpp" #include diff --git a/tp-json/tests/22_parser_array.cpp b/tp-json/tests/22_parser_array.cpp index 230ddae1..f2495325 100644 --- a/tp-json/tests/22_parser_array.cpp +++ b/tp-json/tests/22_parser_array.cpp @@ -1,5 +1,5 @@ #include "../JsonParser.hpp" -#include "assert.cpp" +#include "custom_assert.hpp" #include diff --git a/tp-json/tests/23_parser_object.cpp b/tp-json/tests/23_parser_object.cpp index 63dbbb95..bc9b6749 100644 --- a/tp-json/tests/23_parser_object.cpp +++ b/tp-json/tests/23_parser_object.cpp @@ -1,5 +1,5 @@ #include "../JsonParser.hpp" -#include "assert.cpp" +#include "custom_assert.hpp" #include diff --git a/tp-json/tests/24_parser_pokedex.cpp b/tp-json/tests/24_parser_pokedex.cpp index 872285e0..92e27cc1 100644 --- a/tp-json/tests/24_parser_pokedex.cpp +++ b/tp-json/tests/24_parser_pokedex.cpp @@ -1,5 +1,5 @@ #include "../JsonParser.hpp" -#include "assert.cpp" +#include "custom_assert.hpp" #include diff --git a/tp-json/tests/30_output_operator.cpp b/tp-json/tests/30_output_operator.cpp index fc39d9bf..a72f053b 100644 --- a/tp-json/tests/30_output_operator.cpp +++ b/tp-json/tests/30_output_operator.cpp @@ -1,5 +1,5 @@ #include "../ArrayNode.hpp" -#include "assert.cpp" +#include "custom_assert.hpp" #include diff --git a/tp-json/tests/31_explicit_cast.cpp b/tp-json/tests/31_explicit_cast.cpp index 5bd407a9..1d97d367 100644 --- a/tp-json/tests/31_explicit_cast.cpp +++ b/tp-json/tests/31_explicit_cast.cpp @@ -5,19 +5,10 @@ #include "../NumberLeaf.hpp" #include "../ObjectNode.hpp" #include "../StringLeaf.hpp" -#include "assert.cpp" +#include "custom_assert.hpp" int main() { - // Node_ptr node = NullNode::make_ptr(); - - // ASSERT_UNEQUAL(node->as_NullNode(), nullptr); - - /*ASSERT_EQUAL(node->as_BooleanLeaf(), nullptr); - ASSERT_EQUAL(node->as_NumberNode(), nullptr); - ASSERT_EQUAL(node->as_StringNode(), nullptr); - ASSERT_EQUAL(node->as_ArrayNode(), nullptr); - ASSERT_EQUAL(node->as_ObjectNode(), nullptr);*/ Node_ptr node = BooleanLeaf::make_ptr(true); ASSERT_UNEQUAL(node->as_BooleanLeaf(), nullptr); diff --git a/tp-json/tests/32_explicit_cast_const.cpp b/tp-json/tests/32_explicit_cast_const.cpp index e3a09df4..84733471 100644 --- a/tp-json/tests/32_explicit_cast_const.cpp +++ b/tp-json/tests/32_explicit_cast_const.cpp @@ -5,13 +5,12 @@ #include "../NumberLeaf.hpp" #include "../ObjectNode.hpp" #include "../StringLeaf.hpp" -#include "assert.cpp" +#include "custom_assert.hpp" int main() { - Node_ptr node; { - node = BooleanLeaf::make_ptr(true); + Node_ptr node = BooleanLeaf::make_ptr(true); auto const& node_const_ref = *node; ASSERT_UNEQUAL(node_const_ref.as_BooleanLeaf(), nullptr); ASSERT_EQUAL(node_const_ref.as_NumberLeaf(), nullptr); @@ -21,25 +20,25 @@ int main() } { - node = NumberLeaf::make_ptr(1); + Node_ptr node = NumberLeaf::make_ptr(1); auto const& node_const_ref = *node; ASSERT_UNEQUAL(node_const_ref.as_NumberLeaf(), nullptr); } { - node = StringLeaf::make_ptr("Hello world"); + Node_ptr node = StringLeaf::make_ptr("Hello world"); auto const& node_const_ref = *node; ASSERT_UNEQUAL(node_const_ref.as_StringLeaf(), nullptr); } { - node = ArrayNode::make_ptr(); + Node_ptr node = ArrayNode::make_ptr(); auto const& node_const_ref = *node; ASSERT_UNEQUAL(node_const_ref.as_ArrayNode(), nullptr); } { - node = ObjectNode::make_ptr(); + Node_ptr node = ObjectNode::make_ptr(); auto const& node_const_ref = *node; ASSERT_UNEQUAL(node_const_ref.as_ObjectNode(), nullptr); } diff --git a/tp-json/tests/33_children_count.cpp b/tp-json/tests/33_children_count.cpp index bf71feec..9ae867fd 100644 --- a/tp-json/tests/33_children_count.cpp +++ b/tp-json/tests/33_children_count.cpp @@ -1,7 +1,7 @@ /* TODO */ #include "../JsonParser.hpp" -#include "assert.cpp" +#include "custom_assert.hpp" int main(int argc, char**) { diff --git a/tp-json/tests/34_has_child.cpp b/tp-json/tests/34_has_child.cpp index 34e1f15e..6e3b182a 100644 --- a/tp-json/tests/34_has_child.cpp +++ b/tp-json/tests/34_has_child.cpp @@ -2,7 +2,7 @@ /* TODO */ #include "../JsonParser.hpp" -#include "assert.cpp" +#include "custom_assert.hpp" int main(int argc, char**) { diff --git a/tp-json/tests/35_at.cpp b/tp-json/tests/35_at.cpp index 3798f177..1f226bf1 100644 --- a/tp-json/tests/35_at.cpp +++ b/tp-json/tests/35_at.cpp @@ -3,7 +3,7 @@ /* TODO */ #include "../JsonParser.hpp" -#include "assert.cpp" +#include "custom_assert.hpp" int main(int argc, char**) { diff --git a/tp-json/tests/38_equality.cpp b/tp-json/tests/38_equality.cpp index 51ca00d0..94004f99 100644 --- a/tp-json/tests/38_equality.cpp +++ b/tp-json/tests/38_equality.cpp @@ -1,5 +1,5 @@ #include "../JsonParser.hpp" -#include "assert.cpp" +#include "custom_assert.hpp" int main(int argc, char** argv) { diff --git a/tp-json/tests/39_copy.cpp b/tp-json/tests/39_copy.cpp index d32bb043..800e2593 100644 --- a/tp-json/tests/39_copy.cpp +++ b/tp-json/tests/39_copy.cpp @@ -1,5 +1,5 @@ #include "../JsonParser.hpp" -#include "assert.cpp" +#include "custom_assert.hpp" int main(int argc, char** argv) { diff --git a/tp-json/tests/40_pokedex.cpp b/tp-json/tests/40_pokedex.cpp index c37d69ee..c00b0abf 100644 --- a/tp-json/tests/40_pokedex.cpp +++ b/tp-json/tests/40_pokedex.cpp @@ -1,5 +1,5 @@ #include "../JsonParser.hpp" -#include "assert.cpp" +#include "custom_assert.hpp" #include #include diff --git a/tp-json/tests/99_dot.cpp b/tp-json/tests/99_dot.cpp index 6fde7567..d6bce83e 100644 --- a/tp-json/tests/99_dot.cpp +++ b/tp-json/tests/99_dot.cpp @@ -1,5 +1,5 @@ #include "../JsonParser.hpp" -#include "assert.cpp" +#include "custom_assert.hpp" #include #include diff --git a/tp-json/tests/assert.cpp b/tp-json/tests/custom_assert.hpp similarity index 92% rename from tp-json/tests/assert.cpp rename to tp-json/tests/custom_assert.hpp index e3f506b5..79a381c4 100644 --- a/tp-json/tests/assert.cpp +++ b/tp-json/tests/custom_assert.hpp @@ -26,7 +26,7 @@ void assert_unequal(const T1& left, const T2& right, unsigned line) } #define ASSERT_UNEQUAL(x, y) assert_unequal(x, y, __LINE__); -void assert_true(bool b, unsigned line) +inline void assert_true(bool b, unsigned line) { if (b) return; @@ -36,7 +36,7 @@ void assert_true(bool b, unsigned line) #define ASSERT_TRUE(b) assert_true(b, __LINE__); -void assert_false(bool b, unsigned line) +inline void assert_false(bool b, unsigned line) { if (!b) return; diff --git a/tp-json/tests/old/50_number_node_hardmode.cpp b/tp-json/tests/old/50_number_node_hardmode.cpp deleted file mode 100644 index 46c8b9fe..00000000 --- a/tp-json/tests/old/50_number_node_hardmode.cpp +++ /dev/null @@ -1,19 +0,0 @@ -#include "../JsonParser.hpp" -#include "assert.cpp" - -int main(int argc, char** argv) -{ - if (argc < 1) - { - std::cout << "First command-line argument needs to be where are the json resources."; - exit(EXIT_FAILURE); - } - std::string dir = std::string(argv[1]); - std::string filename; - Node_ptr node; - - filename = dir + "array_large_numbers.json"; - node = JsonParser::parse_from_file(filename); - auto const& array_node = node->as_ArrayNode(); - for (unsigned i = 0; i < node->children_count()) -} \ No newline at end of file diff --git a/tp-json/tests/old/NullNode.hpp b/tp-json/tests/old/NullNode.hpp deleted file mode 100644 index f7fa6e2d..00000000 --- a/tp-json/tests/old/NullNode.hpp +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once - -#include "Node.hpp" - -#include - -class NullNode : public Node -{ -public: - std::string print() const override { return "null"; } - NullNode() - : Node { NodeKind::NONE } - {} - - static std::unique_ptr make_ptr() { return std::make_unique(); } - - inline bool operator==(const Node& node) const override { return (node.is_of_kind(kind())); } - - NullNode* as_NullNode() override { return this; } - NullNode const* as_NullNode() const override { return this; } - - Node_ptr deep_copy() const override { return make_ptr(); } - - std::string dot_label() const override { return "null"; } -}; diff --git a/tp-json/tests/old/array_large_numbers.json b/tp-json/tests/old/array_large_numbers.json deleted file mode 100644 index 97f5c995..00000000 --- a/tp-json/tests/old/array_large_numbers.json +++ /dev/null @@ -1 +0,0 @@ -[9007199254740992,9007199254740992.0,9007199254740993] \ No newline at end of file diff --git a/tp-json/tests/old/basic_node_instances.cpp b/tp-json/tests/old/basic_node_instances.cpp deleted file mode 100644 index 25ec4018..00000000 --- a/tp-json/tests/old/basic_node_instances.cpp +++ /dev/null @@ -1,41 +0,0 @@ -#pragma once - -#include "../json.hpp" - -namespace example { -/* This complicated declaration is to let the implementer choose the return type of ObjectNode::make_ptr() */ -auto make_ArrayNode_example2() -> decltype(ArrayNode::make_ptr()) -{ - auto node = ArrayNode::make_ptr(); - - return node; -} - -/* This complicated declaration is to let the implementer choose the return type of ObjectNode::make_ptr() */ -auto make_ObjectNode_example2() -> decltype(ObjectNode::make_ptr()) -{ - auto node = ObjectNode::make_ptr(); - node->add("premier", StringLeaf::make_ptr("Child 1")); - auto child2 = ArrayNode::make_ptr(); - child2->add(NullNode::make_ptr()); - child2->add(StringLeaf::make_ptr("Grandchild with null siblings")); - child2->add(NullNode::make_ptr()); - node->add("enfant fecond", std::move(child2)); - node->add("enfant vide", ArrayNode::make_ptr()); - node->add("le petit dernier", StringLeaf::make_ptr("Benjamin")); - return node; -} -std::vector const instances { NullNode::make_ptr(), NullNode::make_ptr(), - NumberNode::make_ptr(0), NumberNode::make_ptr(42), - StringLeaf::make_ptr(""), StringLeaf::make_ptr("Hello world!"), - ArrayNode::make_ptr(), make_ArrayNode_example2(), - ObjectNode::make_ptr(), make_ObjectNode_example2() }; - -auto& null_node_ptr1 = instances[1]; -auto& null_node_ptr2 = instances[2]; -auto& int_node_ptr2 = instances[3]; -auto& string_node_ptr2 = instances[4]; -auto& array_node_ptr1 = instances[5]; -auto& object_node_ptr1 = instances[6]; - -} // namespace example diff --git a/tp-json/tests/old/json.hpp b/tp-json/tests/old/json.hpp deleted file mode 100644 index f544f474..00000000 --- a/tp-json/tests/old/json.hpp +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once - -#include "../ArrayNode.hpp" -#include "../BooleanLeaf.hpp" -#include "../Node.hpp" -#include "../NodeKind.hpp" -#include "../NumberLeaf.hpp" -#include "../ObjectNode.hpp" -#include "../StringLeaf.hpp" From e860c61fc830befa5a7b60eca4266f261ca35299 Mon Sep 17 00:00:00 2001 From: Victor Marsault Date: Fri, 1 Apr 2022 16:13:18 +0200 Subject: [PATCH 4/5] Added navifation tests and implemented some of the changes discussed --- tp-json/ArrayNode.hpp | 23 ++++- tp-json/JsonParser.cpp | 62 ++++++------ tp-json/Node.hpp | 7 ++ tp-json/ObjectNode.hpp | 26 +++-- tp-json/enonce.md | 14 +-- ...p2022.svg => example_cpp2022_as_image.svg} | 0 tp-json/tests/10_make_ptr.cpp | 2 +- tp-json/tests/11_array_add.cpp | 4 +- tp-json/tests/12_object_add.cpp | 4 +- tp-json/tests/13_height_and_nodecount.cpp | 4 - tp-json/tests/14_object_dico.cpp | 2 +- tp-json/tests/21_parser_leaf.cpp | 79 ++++++++------- tp-json/tests/22_parser_array.cpp | 37 ++++--- tp-json/tests/23_parser_object.cpp | 38 ++++--- tp-json/tests/24_parser_pokedex.cpp | 25 ++--- tp-json/tests/31_equality.cpp | 50 ++++++++++ tp-json/tests/31_explicit_cast.cpp | 35 ------- tp-json/tests/32_explicit_cast.cpp | 45 +++++++++ tp-json/tests/33_children_count.cpp | 13 --- ...t_const.cpp => 33_explicit_cast_const.cpp} | 0 tp-json/tests/34_has_child.cpp | 14 --- tp-json/tests/34_navigation_array.cpp | 16 +++ tp-json/tests/35_at.cpp | 15 --- tp-json/tests/35_navigation_object.cpp | 18 ++++ tp-json/tests/36_navigation_full.cpp | 88 +++++++++++++++++ tp-json/tests/38_equality.cpp | 50 ---------- tp-json/tests/40_modification.cpp | 24 +++++ tp-json/tests/40_pokedex.cpp | 99 ------------------- tp-json/tests/{39_copy.cpp => 41_copy.cpp} | 15 +-- tp-json/tests/99_dot.cpp | 25 ++--- tp-json/tests/CMakeLists.txt | 4 +- 31 files changed, 434 insertions(+), 404 deletions(-) rename tp-json/{cpp2022.svg => example_cpp2022_as_image.svg} (100%) create mode 100644 tp-json/tests/31_equality.cpp delete mode 100644 tp-json/tests/31_explicit_cast.cpp create mode 100644 tp-json/tests/32_explicit_cast.cpp delete mode 100644 tp-json/tests/33_children_count.cpp rename tp-json/tests/{32_explicit_cast_const.cpp => 33_explicit_cast_const.cpp} (100%) delete mode 100644 tp-json/tests/34_has_child.cpp create mode 100644 tp-json/tests/34_navigation_array.cpp delete mode 100644 tp-json/tests/35_at.cpp create mode 100644 tp-json/tests/35_navigation_object.cpp create mode 100644 tp-json/tests/36_navigation_full.cpp delete mode 100644 tp-json/tests/38_equality.cpp create mode 100644 tp-json/tests/40_modification.cpp delete mode 100644 tp-json/tests/40_pokedex.cpp rename tp-json/tests/{39_copy.cpp => 41_copy.cpp} (62%) diff --git a/tp-json/ArrayNode.hpp b/tp-json/ArrayNode.hpp index d1fd51de..3fb88675 100644 --- a/tp-json/ArrayNode.hpp +++ b/tp-json/ArrayNode.hpp @@ -43,7 +43,7 @@ class ArrayNode : public Node return std::make_unique(std::move(data)); } - size_t children_count() const { return _data.size(); } + size_t child_count() const override { return _data.size(); } ArrayNode* as_ArrayNode() override { return this; } const ArrayNode* as_ArrayNode() const override { return this; } @@ -56,10 +56,10 @@ class ArrayNode : public Node return false; } ArrayNode const* other_s = other.as_ArrayNode(); - size_t size = children_count(); - if (other_s->children_count() != size) + size_t size = child_count(); + if (other_s->child_count() != size) { - std::cerr << size << " != " << other_s->children_count() << std::endl; + std::cerr << size << " != " << other_s->child_count() << std::endl; return false; } for (unsigned i = 0; i < size; i++) @@ -114,4 +114,19 @@ class ArrayNode : public Node } o << "}" << std::endl; } + + Node* at(size_t index) override + { + if (index < child_count()) + return _data.at(index).get(); + else + return nullptr; + } + const Node* at(size_t index) const override + { + if (index < child_count()) + return _data.at(index).get(); + else + return nullptr; + } }; diff --git a/tp-json/JsonParser.cpp b/tp-json/JsonParser.cpp index 2b96ad8e..d55c2139 100644 --- a/tp-json/JsonParser.cpp +++ b/tp-json/JsonParser.cpp @@ -11,35 +11,35 @@ void JsonParser::extract_spaces() _in.get(); } -bool JsonParser::check_next_char_equals(int c, std::string_view v) +bool JsonParser::check_next_char_equals(int expected_char, std::string_view alternatives) { - int c2 = _in.peek(); - if (c == c2) + int next_char = _in.peek(); + if (expected_char == next_char) { _in.get(); return true; } std::cerr << "Unexpected character ("; - if (c2 >= 20 && c2 < 127) + if (next_char >= 20 && next_char < 127) { - std::cerr << (char)c2; + std::cerr << static_cast(expected_char); } - else if (c2 == -1) + else if (next_char == -1) { std::cerr << "EOF"; } else { - std::cerr << '\\' << c2; + std::cerr << '\\' << next_char; } - if (v != "") + if (alternatives != "") { - std::cerr << "). Expecting a char in \"" << v << "\"." << std::endl; + std::cerr << "). Expecting a char in \"" << alternatives << "\"." << std::endl; } else { - std::cerr << "). Expecting '" << (char)c << "'." << std::endl; + std::cerr << "). Expecting '" << static_cast(expected_char) << "'." << std::endl; } return false; } @@ -56,8 +56,6 @@ Node_ptr JsonParser::parse_Node() return parse_ArrayNode(); case '"': return parse_StringLeaf(); - // case 'n': - // return parse_constant("null"); case 'f': return parse_constant("false"); case 't': @@ -144,13 +142,17 @@ Node_ptr JsonParser::parse_StringLeaf() Node_ptr JsonParser::parse_NumberLeaf() { - // unsigned starting_pos = _in.tellg(); + unsigned starting_pos = _in.tellg(); double d; _in >> d; - // size_t end_pos_double = _in.tellg(); - return NumberLeaf::make_ptr(d); + size_t end_pos = _in.tellg(); + + if (starting_pos == end_pos) + return nullptr; + else + return NumberLeaf::make_ptr(d); } Node_ptr JsonParser::parse_ArrayNode() @@ -164,23 +166,25 @@ Node_ptr JsonParser::parse_ArrayNode() _in.get(); return arrayNode; } - - do + else { - auto child = parse_Node(); - if (child == nullptr) - return nullptr; + do + { + auto child = parse_Node(); + if (child == nullptr) + return nullptr; + else + arrayNode->add(std::move(child)); + extract_spaces(); + } + while (_in.get() == ','); + + _in.unget(); + if (check_next_char_equals(']', ",]")) + return arrayNode; else - arrayNode->add(std::move(child)); - extract_spaces(); + return nullptr; } - while (_in.get() == ','); - - _in.unget(); - if (check_next_char_equals(']', ",]")) - return arrayNode; - else - return nullptr; } Node_ptr JsonParser::parse_ObjectNode() diff --git a/tp-json/Node.hpp b/tp-json/Node.hpp index 8e7028a1..53ed7787 100644 --- a/tp-json/Node.hpp +++ b/tp-json/Node.hpp @@ -69,5 +69,12 @@ class Node } virtual std::string dot_label() const = 0; + + virtual size_t child_count() const { return 0; } + + virtual Node* at(std::string const&) { return nullptr; } + virtual const Node* at(std::string const&) const { return nullptr; } + virtual const Node* at(size_t) const { return nullptr; } + virtual Node* at(size_t) { return nullptr; } }; std::ostream& operator<<(std::ostream& o, const Node& node); diff --git a/tp-json/ObjectNode.hpp b/tp-json/ObjectNode.hpp index d3ce80da..f94ae241 100644 --- a/tp-json/ObjectNode.hpp +++ b/tp-json/ObjectNode.hpp @@ -30,7 +30,7 @@ class ObjectNode : public Node void add(std::string key, Node_ptr value) { _data.emplace(std::move(key), std::move(value)); } - size_t children_count() const { return _data.size(); } + size_t child_count() const override { return _data.size(); } std::string print() const override { @@ -60,10 +60,10 @@ class ObjectNode : public Node return false; } ObjectNode const* other_s = other.as_ObjectNode(); - size_t size = children_count(); - if (other_s->children_count() != size) + size_t size = child_count(); + if (other_s->child_count() != size) { - std::cerr << size << " != " << other_s->children_count() << std::endl; + std::cerr << size << " != " << other_s->child_count() << std::endl; return false; } for (auto& pair : other_s->_data) @@ -98,8 +98,20 @@ class ObjectNode : public Node [](size_t i, auto const& pair) { return i + pair.second->node_count(); }); } - Node* at(std::string const& key) { return &*_data.at(key); } - const Node* at(std::string const& key) const { return &*_data.at(key); } + Node* at(std::string const& key) override + { + if (has_child(key)) + return _data.at(key).get(); + else + return nullptr; + } + const Node* at(std::string const& key) const override + { + if (has_child(key)) + return _data.at(key).get(); + else + return nullptr; + } Node_ptr deep_copy() const override { @@ -122,4 +134,6 @@ class ObjectNode : public Node } o << "}" << std::endl; } + + bool has_child(const std::string& key) const { return _data.count(key); } }; diff --git a/tp-json/enonce.md b/tp-json/enonce.md index b49b9e73..281d9089 100644 --- a/tp-json/enonce.md +++ b/tp-json/enonce.md @@ -41,10 +41,12 @@ Une valeur JSON est soit: 1. un *booléen*, par exemple `true`; 2. un *nombre*, par exemple `2022`; 3. une *chaîne de caractère*, par exemple `"C++"`; -4. une *liste* de valeurs JSON entre `[`...`]` et séparées par des virgules, par exemple `["Céline","Matthias","Victor"]`; +4. une *liste* de valeurs JSON entre `[`...`]` et séparées par des virgules, par exemple `["Céline","Matthias","Victor"]`; 5. un *dictionnaire* entre `{`...`}` qui associe des clefs (chaîne de caractère avant le `:`) à des valeurs JSON (après le `:`), par exemple `{"kind":"Project", "weeks":[5,6,7,8,9,10,11,12]}` associe la clef `"kind"` à la valeur `"Project"`, la clef `"weeks"` à la valeur `[5,6,7,8,9,10,11,12]` -Notez que le document entier est une valeur JSON, usuellement un dictionnaire. D'autres documents json se trouvent dans le dossier `json`. La plupart sont des petits exemples à des fins de tests (**ne les modifiez pas!**). +Notez qu'on ne suppose aucune cohérence particulière de typage. Par exemple les valeurs JSON dans une liste peuvent être de types différents, par exemple `[1, "2i",{"imag":3, "real":4}]` pourrait représenter la liste des trois nombres complexes $1$, $2i$ et $3i+4$. + +Un document JSON est simplement un fichier qui contient une valeur JSON, usuellement un dictionnaire. D'autres documents json se trouvent dans le dossier `json`. La plupart sont des petits exemples à des fins de tests (**ne les modifiez pas!**). Au contraire, le document `json/pokedex.json` est représentatif de ce à quoi ressemble un document JSON réel, et sera utilisé dans les tests plus avancés. @@ -52,18 +54,18 @@ Au contraire, le document `json/pokedex.json` est représentatif de ce à quoi r Un document peut se voir comme un arbre: - Les booléens, entier et chaînes de caractères sont des feuilles de l'arbre. -- les listes et les objects sont des noeuds internes et ont pour fils chacune des valeurs à l'intérieur. +- les listes et les dictionnaires sont des noeuds internes et ont pour fils chacune des valeurs à l'intérieur. Par exemple, le document donné en début de TP se représente: -![Représentation du document json cpp2022.json](cpp2022.svg) +![Représentation du document json cpp2022.json](example_cpp2022_as_image.svg) ## Code à produire Un document JSON sera représenté en mémoire comme un arbre dont les noeuds sont polymorphes: -- La classe `Node` sera la classe principales pour représenter un noeud dont on ne connaît pas le type exact. +- La classe `Node` sera la classe principale pour représenter un noeud dont on ne connaît pas le type exact. - Les classes `BooleanLeaf`, `NumberLeaf`, `StringLeaf`, `ArrayNode`, `ObjectNode` représenterons les différents types de noeuds. -- Le type `Node_ptr` sera utilisée pour faire référence/pointer vers les enfants d'un noeud. Vous devrez choisir le type approprié (dans le fichier `Node_ptr.hpp`) à un certain point du TDD. +- Le type `Node_ptr` sera utilisée pour faire référence/pointer vers les enfants d'un noeud. Vous devrez choisir le type approprié à un certain point du TDD. - Le type `NodeKind` est fourni, c'est une `enum` listant les différents types de noeuds. Il permet de savoir à l'exécution le type réel d'un `Node`. Quand c'est pertinent, on factorisera le code en utilisant l'héritage. diff --git a/tp-json/cpp2022.svg b/tp-json/example_cpp2022_as_image.svg similarity index 100% rename from tp-json/cpp2022.svg rename to tp-json/example_cpp2022_as_image.svg diff --git a/tp-json/tests/10_make_ptr.cpp b/tp-json/tests/10_make_ptr.cpp index bab7472b..7ffd684f 100644 --- a/tp-json/tests/10_make_ptr.cpp +++ b/tp-json/tests/10_make_ptr.cpp @@ -11,7 +11,7 @@ int main() { - // Node_ptr null_node_ptr = NullNode::make_ptr(); + Node_ptr null_node_ptr = nullptr; Node_ptr bool_node_ptr = BooleanLeaf::make_ptr(true); Node_ptr int_node_ptr = NumberLeaf::make_ptr(1); Node_ptr str_node_ptr = StringLeaf::make_ptr("Hello world"); diff --git a/tp-json/tests/11_array_add.cpp b/tp-json/tests/11_array_add.cpp index 52b5c6ce..399c144a 100644 --- a/tp-json/tests/11_array_add.cpp +++ b/tp-json/tests/11_array_add.cpp @@ -10,14 +10,14 @@ int main() { auto array_node_ptr = ArrayNode::make_ptr(); - size_t size = array_node_ptr->children_count(); + size_t size = array_node_ptr->child_count(); ASSERT_EQUAL(size, 0u); array_node_ptr->add(StringLeaf::make_ptr("H")); array_node_ptr->add(NumberLeaf::make_ptr(3110)); array_node_ptr->add(StringLeaf::make_ptr("!")); - ASSERT_EQUAL(array_node_ptr->children_count(), 3u); + ASSERT_EQUAL(array_node_ptr->child_count(), 3u); // This is a raw literal, go check it out https://en.cppreference.com/w/cpp/language/string_literal */ std::string target = R"---(["H",3110,"!"])---"; diff --git a/tp-json/tests/12_object_add.cpp b/tp-json/tests/12_object_add.cpp index 0568b2a7..705914ab 100644 --- a/tp-json/tests/12_object_add.cpp +++ b/tp-json/tests/12_object_add.cpp @@ -10,7 +10,7 @@ int main() { auto object_node_ptr = ObjectNode::make_ptr(); - size_t size = object_node_ptr->children_count(); + size_t size = object_node_ptr->child_count(); ASSERT_EQUAL(size, 0u); object_node_ptr->add("H", NumberLeaf::make_ptr(3110)); @@ -23,7 +23,7 @@ int main() array_node_ptr->add(ObjectNode::make_ptr()); object_node_ptr->add("W", std::move(array_node_ptr)); - ASSERT_EQUAL(object_node_ptr->children_count(), 3u); + ASSERT_EQUAL(object_node_ptr->child_count(), 3u); // This is a raw literal, go check it out https://en.cppreference.com/w/cpp/language/string_literal */ std::string target = R"---({" ":true,"H":3110,"W":[0,"rld!",[],{}]})---"; diff --git a/tp-json/tests/13_height_and_nodecount.cpp b/tp-json/tests/13_height_and_nodecount.cpp index e4af9dde..f88104aa 100644 --- a/tp-json/tests/13_height_and_nodecount.cpp +++ b/tp-json/tests/13_height_and_nodecount.cpp @@ -11,10 +11,6 @@ int main() { - // Node_ptr null_node_ptr = NullNode::make_ptr(); - // ASSERT_EQUAL(null_node_ptr->height(), 0u); - // ASSERT_EQUAL(null_node_ptr->node_count(), 1u); - Node_ptr true_node_ptr = BooleanLeaf::make_ptr(true); Node_ptr false_node_ptr = BooleanLeaf::make_ptr(false); auto array_node_ptr = ArrayNode::make_ptr(); diff --git a/tp-json/tests/14_object_dico.cpp b/tp-json/tests/14_object_dico.cpp index fa492381..b6f419de 100644 --- a/tp-json/tests/14_object_dico.cpp +++ b/tp-json/tests/14_object_dico.cpp @@ -10,7 +10,7 @@ int main() { auto object_node_ptr = ObjectNode::make_ptr(); - size_t size = object_node_ptr->children_count(); + size_t size = object_node_ptr->child_count(); ASSERT_EQUAL(size, 0u); object_node_ptr->add("key1", NumberLeaf::make_ptr(42)); diff --git a/tp-json/tests/21_parser_leaf.cpp b/tp-json/tests/21_parser_leaf.cpp index 17db0482..933b4f44 100644 --- a/tp-json/tests/21_parser_leaf.cpp +++ b/tp-json/tests/21_parser_leaf.cpp @@ -3,49 +3,52 @@ #include -int main(int argc, char** argv) +int main() { - if (argc < 2) + std::string json_dir = "json/"; + { - std::cout << "First command-line argument needs to be the path to the json resources."; - exit(EXIT_FAILURE); + std::string filename = json_dir + "boolean_true.json"; + std::cerr << "Starting test with: " << filename << std::endl; + Node_ptr node = JsonParser::parse_from_file(filename); + ASSERT_EQUAL(node->height(), 0u); + ASSERT_EQUAL(node->node_count(), 1u); + ASSERT_EQUAL(node->kind(), NodeKind::BOOLEAN); } - std::string dir = std::string(argv[1]); - std::string filename; - Node_ptr node; - - filename = dir + "boolean_true.json"; - std::cerr << "Starting test with: " << filename << std::endl; - node = JsonParser::parse_from_file(filename); - ASSERT_EQUAL(node->height(), 0u); - ASSERT_EQUAL(node->node_count(), 1u); - ASSERT_EQUAL(node->kind(), NodeKind::BOOLEAN); - filename = dir + "number_42.json"; - std::cerr << "Starting test with: " << filename << std::endl; - node = JsonParser::parse_from_file(filename); - ASSERT_EQUAL(node->height(), 0u); - ASSERT_EQUAL(node->node_count(), 1u); - ASSERT_EQUAL(node->kind(), NodeKind::NUMBER); + { + std::string filename = json_dir + "number_42.json"; + std::cerr << "Starting test with: " << filename << std::endl; + Node_ptr node = JsonParser::parse_from_file(filename); + ASSERT_EQUAL(node->height(), 0u); + ASSERT_EQUAL(node->node_count(), 1u); + ASSERT_EQUAL(node->kind(), NodeKind::NUMBER); + } - filename = dir + "string_hello.json"; - std::cerr << "Starting test with: " << filename << std::endl; - node = JsonParser::parse_from_file(filename); - ASSERT_EQUAL(node->height(), 0u); - ASSERT_EQUAL(node->node_count(), 1u); - ASSERT_EQUAL(node->kind(), NodeKind::STRING); + { + std::string filename = json_dir + "string_hello.json"; + std::cerr << "Starting test with: " << filename << std::endl; + Node_ptr node = JsonParser::parse_from_file(filename); + ASSERT_EQUAL(node->height(), 0u); + ASSERT_EQUAL(node->node_count(), 1u); + ASSERT_EQUAL(node->kind(), NodeKind::STRING); + } - filename = dir + "array_empty.json"; - std::cerr << "Starting test with: " << filename << std::endl; - node = JsonParser::parse_from_file(filename); - ASSERT_EQUAL(node->height(), 0u); - ASSERT_EQUAL(node->node_count(), 1u); - ASSERT_EQUAL(node->kind(), NodeKind::ARRAY); + { + std::string filename = json_dir + "array_empty.json"; + std::cerr << "Starting test with: " << filename << std::endl; + Node_ptr node = JsonParser::parse_from_file(filename); + ASSERT_EQUAL(node->height(), 0u); + ASSERT_EQUAL(node->node_count(), 1u); + ASSERT_EQUAL(node->kind(), NodeKind::ARRAY); + } - filename = dir + "object_empty.json"; - std::cerr << "Starting test with: " << filename << std::endl; - node = JsonParser::parse_from_file(filename); - ASSERT_EQUAL(node->height(), 0u); - ASSERT_EQUAL(node->node_count(), 1u); - ASSERT_EQUAL(node->kind(), NodeKind::OBJECT); + { + std::string filename = json_dir + "object_empty.json"; + std::cerr << "Starting test with: " << filename << std::endl; + Node_ptr node = JsonParser::parse_from_file(filename); + ASSERT_EQUAL(node->height(), 0u); + ASSERT_EQUAL(node->node_count(), 1u); + ASSERT_EQUAL(node->kind(), NodeKind::OBJECT); + } } \ No newline at end of file diff --git a/tp-json/tests/22_parser_array.cpp b/tp-json/tests/22_parser_array.cpp index f2495325..c57419b9 100644 --- a/tp-json/tests/22_parser_array.cpp +++ b/tp-json/tests/22_parser_array.cpp @@ -3,28 +3,25 @@ #include -int main(int argc, char** argv) +int main() { - if (argc < 2) + std::string json_dir = "json/"; + { - std::cout << "First command-line argument needs to be the path to the json resources."; - exit(EXIT_FAILURE); + std::string filename = json_dir + "array_range10.json"; + std::cerr << "Starting test with: " << filename << std::endl; + Node_ptr node = JsonParser::parse_from_file(filename); + ASSERT_EQUAL(node->height(), 1u); + ASSERT_EQUAL(node->node_count(), 11u); + ASSERT_EQUAL(node->kind(), NodeKind::ARRAY); } - std::string dir = std::string(argv[1]); - std::string filename; - Node_ptr node; - - filename = dir + "array_range10.json"; - std::cerr << "Starting test with: " << filename << std::endl; - node = JsonParser::parse_from_file(filename); - ASSERT_EQUAL(node->height(), 1u); - ASSERT_EQUAL(node->node_count(), 11u); - ASSERT_EQUAL(node->kind(), NodeKind::ARRAY); - filename = dir + "array_hexadecimal.json"; - std::cerr << "Starting test with: " << filename << std::endl; - node = JsonParser::parse_from_file(filename); - ASSERT_EQUAL(node->height(), 4u); - ASSERT_EQUAL(node->node_count(), 31u); - ASSERT_EQUAL(node->kind(), NodeKind::ARRAY); + { + std::string filename = json_dir + "array_hexadecimal.json"; + std::cerr << "Starting test with: " << filename << std::endl; + Node_ptr node = JsonParser::parse_from_file(filename); + ASSERT_EQUAL(node->height(), 4u); + ASSERT_EQUAL(node->node_count(), 31u); + ASSERT_EQUAL(node->kind(), NodeKind::ARRAY); + } } \ No newline at end of file diff --git a/tp-json/tests/23_parser_object.cpp b/tp-json/tests/23_parser_object.cpp index bc9b6749..b5f807b5 100644 --- a/tp-json/tests/23_parser_object.cpp +++ b/tp-json/tests/23_parser_object.cpp @@ -3,29 +3,25 @@ #include -int main(int argc, char** argv) +int main() { - if (argc < 2) + std::string json_dir = "json/"; + { - std::cout << "First command-line argument needs to be the path to the json resources." << std::endl; - exit(EXIT_FAILURE); + std::string filename = json_dir + "object_empty.json"; + std::cerr << "Starting test with: " << filename << std::endl; + Node_ptr node = JsonParser::parse_from_file(filename); + ASSERT_EQUAL(node->height(), 0u); + ASSERT_EQUAL(node->node_count(), 1u); + ASSERT_EQUAL(node->kind(), NodeKind::OBJECT); } - std::string dir = std::string(argv[1]); - std::string filename; - Node_ptr node; - - filename = dir + "object_empty.json"; - std::cerr << "Starting test with: " << filename << std::endl; - node = JsonParser::parse_from_file(filename); - ASSERT_EQUAL(node->height(), 0u); - ASSERT_EQUAL(node->node_count(), 1u); - ASSERT_EQUAL(node->kind(), NodeKind::OBJECT); - - filename = dir + "object_alphabet.json"; - std::cerr << "Starting test with: " << filename << std::endl; - node = JsonParser::parse_from_file(filename); - ASSERT_EQUAL(node->height(), 1u); - ASSERT_EQUAL(node->node_count(), 27u); - ASSERT_EQUAL(node->kind(), NodeKind::OBJECT); + { + std::string filename = json_dir + "object_alphabet.json"; + std::cerr << "Starting test with: " << filename << std::endl; + Node_ptr node = JsonParser::parse_from_file(filename); + ASSERT_EQUAL(node->height(), 1u); + ASSERT_EQUAL(node->node_count(), 27u); + ASSERT_EQUAL(node->kind(), NodeKind::OBJECT); + } } \ No newline at end of file diff --git a/tp-json/tests/24_parser_pokedex.cpp b/tp-json/tests/24_parser_pokedex.cpp index 92e27cc1..66e20503 100644 --- a/tp-json/tests/24_parser_pokedex.cpp +++ b/tp-json/tests/24_parser_pokedex.cpp @@ -3,22 +3,17 @@ #include -int main(int argc, char** argv) +int main() { - if (argc < 2) + std::string json_dir = "json/"; + { - std::cout << "First command-line argument needs to be the path to the json resources."; - exit(EXIT_FAILURE); + std::string filename = json_dir + "pokedex.json"; + std::cerr << "Starting test with: " << filename << std::endl; + Node_ptr node = JsonParser::parse_from_file(filename); + ASSERT_EQUAL(node->child_count(), 1u); + ASSERT_EQUAL(node->height(), 5u); + ASSERT_EQUAL(node->node_count(), 3779u); + ASSERT_EQUAL(node->kind(), NodeKind::OBJECT); } - - std::string dir = std::string(argv[1]); - std::string filename; - Node_ptr node; - - filename = dir + "pokedex.json"; - std::cerr << "Starting test with: " << filename << std::endl; - node = JsonParser::parse_from_file(filename); - ASSERT_EQUAL(node->height(), 5u); - ASSERT_EQUAL(node->node_count(), 3779u); - ASSERT_EQUAL(node->kind(), NodeKind::OBJECT); } \ No newline at end of file diff --git a/tp-json/tests/31_equality.cpp b/tp-json/tests/31_equality.cpp new file mode 100644 index 00000000..f3254f93 --- /dev/null +++ b/tp-json/tests/31_equality.cpp @@ -0,0 +1,50 @@ +#include "../JsonParser.hpp" +#include "custom_assert.hpp" + +int main() +{ + std::string json_dir = "json/"; + + { + std::string filename = json_dir + "boolean_true.json"; + std::cerr << "Starting test with: " << filename << std::endl; + Node_ptr node = JsonParser::parse_from_file(filename); + ASSERT_EQUAL(*node, *BooleanLeaf::make_ptr(true)); + } + { + std::string filename = json_dir + "number_42.json"; + std::cerr << "Starting test with: " << filename << std::endl; + Node_ptr node = JsonParser::parse_from_file(filename); + ASSERT_EQUAL(*node, *NumberLeaf::make_ptr(42)); + } + { + std::string filename = json_dir + "string_hello.json"; + std::cerr << "Starting test with: " << filename << std::endl; + Node_ptr node = JsonParser::parse_from_file(filename); + ASSERT_EQUAL(*node, *StringLeaf::make_ptr("Hello")); + } + { + std::string filename = json_dir + "object_alphabet.json"; + std::cerr << "Starting test with: " << filename << std::endl; + Node_ptr node = JsonParser::parse_from_file(filename); + auto target = ObjectNode::make_ptr(); + for (char c = 'a'; c <= 'z'; c++) + target->add(std::string(1, c), NumberLeaf::make_ptr(c)); + ASSERT_EQUAL(*node, *target); + } + { + std::string filename = json_dir + "array_range10.json"; + std::cerr << "Starting test with: " << filename << std::endl; + Node_ptr node = JsonParser::parse_from_file(filename); + auto target = ArrayNode::make_ptr(); + for (unsigned i = 0; i < 10; i++) + target->add(NumberLeaf::make_ptr(i)); + ASSERT_EQUAL(*node, *target); + } + { + std::string filename = json_dir + "pokedex.json"; + std::cerr << "Starting test with: " << filename << std::endl; + Node_ptr node = JsonParser::parse_from_file(filename); + ASSERT_EQUAL(*node, *node); + } +} \ No newline at end of file diff --git a/tp-json/tests/31_explicit_cast.cpp b/tp-json/tests/31_explicit_cast.cpp deleted file mode 100644 index 1d97d367..00000000 --- a/tp-json/tests/31_explicit_cast.cpp +++ /dev/null @@ -1,35 +0,0 @@ -#include "../ArrayNode.hpp" -#include "../BooleanLeaf.hpp" -#include "../Node.hpp" -#include "../NodeKind.hpp" -#include "../NumberLeaf.hpp" -#include "../ObjectNode.hpp" -#include "../StringLeaf.hpp" -#include "custom_assert.hpp" - -int main() -{ - - Node_ptr node = BooleanLeaf::make_ptr(true); - ASSERT_UNEQUAL(node->as_BooleanLeaf(), nullptr); - ASSERT_EQUAL(node->as_NumberLeaf(), nullptr); - ASSERT_EQUAL(node->as_StringLeaf(), nullptr); - ASSERT_EQUAL(node->as_ArrayNode(), nullptr); - ASSERT_EQUAL(node->as_ObjectNode(), nullptr); - - node = NumberLeaf::make_ptr(1); - ASSERT_UNEQUAL(node->as_NumberLeaf(), nullptr); - ASSERT_EQUAL(node->as_BooleanLeaf(), nullptr); - ASSERT_EQUAL(node->as_StringLeaf(), nullptr); - ASSERT_EQUAL(node->as_ArrayNode(), nullptr); - ASSERT_EQUAL(node->as_ObjectNode(), nullptr); - - node = StringLeaf::make_ptr("Hello world"); - ASSERT_UNEQUAL(node->as_StringLeaf(), nullptr); - - node = ArrayNode::make_ptr(); - ASSERT_UNEQUAL(node->as_ArrayNode(), nullptr); - - node = ObjectNode::make_ptr(); - ASSERT_UNEQUAL(node->as_ObjectNode(), nullptr); -} \ No newline at end of file diff --git a/tp-json/tests/32_explicit_cast.cpp b/tp-json/tests/32_explicit_cast.cpp new file mode 100644 index 00000000..3cb04d10 --- /dev/null +++ b/tp-json/tests/32_explicit_cast.cpp @@ -0,0 +1,45 @@ +#include "../ArrayNode.hpp" +#include "../BooleanLeaf.hpp" +#include "../Node.hpp" +#include "../NodeKind.hpp" +#include "../NumberLeaf.hpp" +#include "../ObjectNode.hpp" +#include "../StringLeaf.hpp" +#include "custom_assert.hpp" + +int main() +{ + + { + Node_ptr node = BooleanLeaf::make_ptr(true); + ASSERT_UNEQUAL(node->as_BooleanLeaf(), nullptr); + ASSERT_EQUAL(node->as_NumberLeaf(), nullptr); + ASSERT_EQUAL(node->as_StringLeaf(), nullptr); + ASSERT_EQUAL(node->as_ArrayNode(), nullptr); + ASSERT_EQUAL(node->as_ObjectNode(), nullptr); + } + + { + Node_ptr node = NumberLeaf::make_ptr(1); + ASSERT_UNEQUAL(node->as_NumberLeaf(), nullptr); + ASSERT_EQUAL(node->as_BooleanLeaf(), nullptr); + ASSERT_EQUAL(node->as_StringLeaf(), nullptr); + ASSERT_EQUAL(node->as_ArrayNode(), nullptr); + ASSERT_EQUAL(node->as_ObjectNode(), nullptr); + } + + { + Node_ptr node = StringLeaf::make_ptr("Hello world"); + ASSERT_UNEQUAL(node->as_StringLeaf(), nullptr); + } + + { + Node_ptr node = ArrayNode::make_ptr(); + ASSERT_UNEQUAL(node->as_ArrayNode(), nullptr); + } + + { + Node_ptr node = ObjectNode::make_ptr(); + ASSERT_UNEQUAL(node->as_ObjectNode(), nullptr); + } +} \ No newline at end of file diff --git a/tp-json/tests/33_children_count.cpp b/tp-json/tests/33_children_count.cpp deleted file mode 100644 index 9ae867fd..00000000 --- a/tp-json/tests/33_children_count.cpp +++ /dev/null @@ -1,13 +0,0 @@ -/* TODO */ - -#include "../JsonParser.hpp" -#include "custom_assert.hpp" - -int main(int argc, char**) -{ - if (argc < 1) - { - std::cout << "First command-line argument needs to be where are the json resources."; - exit(EXIT_FAILURE); - } -} \ No newline at end of file diff --git a/tp-json/tests/32_explicit_cast_const.cpp b/tp-json/tests/33_explicit_cast_const.cpp similarity index 100% rename from tp-json/tests/32_explicit_cast_const.cpp rename to tp-json/tests/33_explicit_cast_const.cpp diff --git a/tp-json/tests/34_has_child.cpp b/tp-json/tests/34_has_child.cpp deleted file mode 100644 index 6e3b182a..00000000 --- a/tp-json/tests/34_has_child.cpp +++ /dev/null @@ -1,14 +0,0 @@ - -/* TODO */ - -#include "../JsonParser.hpp" -#include "custom_assert.hpp" - -int main(int argc, char**) -{ - if (argc < 1) - { - std::cout << "First command-line argument needs to be where are the json resources."; - exit(EXIT_FAILURE); - } -} \ No newline at end of file diff --git a/tp-json/tests/34_navigation_array.cpp b/tp-json/tests/34_navigation_array.cpp new file mode 100644 index 00000000..617a0f6e --- /dev/null +++ b/tp-json/tests/34_navigation_array.cpp @@ -0,0 +1,16 @@ +#include "../JsonParser.hpp" +#include "custom_assert.hpp" + +int main() +{ + std::string json_dir = "json/"; + + std::string filename = json_dir + "pokedex.json"; + auto node = JsonParser::parse_from_file(filename); + auto pokemon_node = node->at("pokemon"); + ASSERT_EQUAL(pokemon_node->kind(), NodeKind::ARRAY); + ASSERT_EQUAL(pokemon_node->child_count(), 151u); + + /* Pikachu pokedex number is 25 */ + ASSERT_EQUAL(pokemon_node->at(24)->at("name")->as_StringLeaf()->data(), "Pikachu"); +} \ No newline at end of file diff --git a/tp-json/tests/35_at.cpp b/tp-json/tests/35_at.cpp deleted file mode 100644 index 1f226bf1..00000000 --- a/tp-json/tests/35_at.cpp +++ /dev/null @@ -1,15 +0,0 @@ - - -/* TODO */ - -#include "../JsonParser.hpp" -#include "custom_assert.hpp" - -int main(int argc, char**) -{ - if (argc < 1) - { - std::cout << "First command-line argument needs to be where are the json resources."; - exit(EXIT_FAILURE); - } -} \ No newline at end of file diff --git a/tp-json/tests/35_navigation_object.cpp b/tp-json/tests/35_navigation_object.cpp new file mode 100644 index 00000000..3ab3377d --- /dev/null +++ b/tp-json/tests/35_navigation_object.cpp @@ -0,0 +1,18 @@ +#include "../JsonParser.hpp" +#include "custom_assert.hpp" + +int main() +{ + std::string json_dir = "json/"; + + std::string filename = json_dir + "pokedex.json"; + auto node = JsonParser::parse_from_file(filename); + const auto& const_node = node->as_ObjectNode(); + + ASSERT_TRUE(const_node->has_child("pokemon")); + ASSERT_FALSE(const_node->has_child("digimon")); + + auto pokemon_node = const_node->at("pokemon"); + ASSERT_UNEQUAL(pokemon_node, nullptr); + ASSERT_EQUAL(const_node->at("digimon"), nullptr); +} \ No newline at end of file diff --git a/tp-json/tests/36_navigation_full.cpp b/tp-json/tests/36_navigation_full.cpp new file mode 100644 index 00000000..a347929a --- /dev/null +++ b/tp-json/tests/36_navigation_full.cpp @@ -0,0 +1,88 @@ +#include "../JsonParser.hpp" +#include "custom_assert.hpp" + +#include +#include +#include +#include + +int main() +{ + std::string json_dir = "json/"; + + std::string filename = json_dir + "pokedex.json"; + Node_ptr node = JsonParser::parse_from_file(filename); + + ASSERT_EQUAL(node->kind(), NodeKind::OBJECT) + ASSERT_UNEQUAL(node->as_ObjectNode()->at("pokemon"), nullptr) + ASSERT_EQUAL(node->as_ObjectNode()->at("pokemon")->kind(), NodeKind::ARRAY) + + std::unordered_map pokemon_id_by_name; + for (const auto& pokemon_node : *(node->as_ObjectNode()->at("pokemon")->as_ArrayNode())) + { + ASSERT_EQUAL(pokemon_node->kind(), NodeKind::OBJECT) + auto pokemon_obj = pokemon_node->as_ObjectNode(); + + ASSERT_UNEQUAL(pokemon_obj->at("name"), nullptr) + ASSERT_EQUAL(pokemon_obj->at("name")->kind(), NodeKind::STRING) + const std::string& name = pokemon_obj->at("name")->as_StringLeaf()->data(); + + ASSERT_UNEQUAL(pokemon_obj->at("id"), nullptr) + ASSERT_UNEQUAL(pokemon_obj->at("id")->as_NumberLeaf(), nullptr) + unsigned id = pokemon_obj->at("id")->as_NumberLeaf()->data(); + + pokemon_id_by_name[name] = id; + } + + std::unordered_map> pokemon_id_by_type; + for (const auto& pokemon_node : *(node->as_ObjectNode()->at("pokemon")->as_ArrayNode())) + { + ASSERT_EQUAL(pokemon_node->kind(), NodeKind::OBJECT) + ASSERT_UNEQUAL(pokemon_node->as_ObjectNode()->at("type"), nullptr) + ASSERT_EQUAL(pokemon_node->as_ObjectNode()->at("type")->kind(), NodeKind::ARRAY) + for (const auto& type_node : *(pokemon_node->as_ObjectNode()->at("type")->as_ArrayNode())) + { + ASSERT_UNEQUAL(type_node->as_StringLeaf(), nullptr) + const std::string& type = type_node->as_StringLeaf()->data(); + auto it = pokemon_id_by_type.find(type); + if (it == pokemon_id_by_type.end()) + it = pokemon_id_by_type.emplace(type, std::vector()).first; + it->second.emplace_back(pokemon_node->as_ObjectNode()->at("id")->as_NumberLeaf()->data()); + } + } + + for (const auto& pair : pokemon_id_by_type) + { + std::cerr << pair.first << ": ["; + bool postfirst = false; + for (auto i : pair.second) + { + if (postfirst) + std::cerr << ", "; + else + postfirst = true; + std::cerr << i; + } + std::cerr << "]" << std::endl; + } + + std::vector> const tests = { { "Bulbasaur", "Grass" }, + { "Bulbasaur", "Poison" }, + { "Wartortle", "Water" }, + { "Pikachu", "Electric" } }; + for (auto const& [name, type] : tests) + { + std::cerr << "Checking that \"" << name << "\" is of type \"" << type << "\"." << std::endl; + + const auto& list_it = pokemon_id_by_type.find(type); + const auto& elt_it = pokemon_id_by_name.find(name); + + assert(list_it != pokemon_id_by_type.end()); + assert(elt_it != pokemon_id_by_name.end()); + + const std::vector& poke_list = list_it->second; + unsigned poke_elt = elt_it->second; + + assert(std::find(poke_list.begin(), poke_list.end(), poke_elt) != poke_list.end()); + } +} \ No newline at end of file diff --git a/tp-json/tests/38_equality.cpp b/tp-json/tests/38_equality.cpp deleted file mode 100644 index 94004f99..00000000 --- a/tp-json/tests/38_equality.cpp +++ /dev/null @@ -1,50 +0,0 @@ -#include "../JsonParser.hpp" -#include "custom_assert.hpp" - -int main(int argc, char** argv) -{ - if (argc < 1) - { - std::cout << "First command-line argument needs to be where are the json resources."; - exit(EXIT_FAILURE); - } - std::string dir = std::string(argv[1]); - std::string filename; - Node_ptr node; - - filename = dir + "boolean_true.json"; - std::cerr << "Starting test with: " << filename << std::endl; - node = JsonParser::parse_from_file(filename); - ASSERT_EQUAL(*node, *BooleanLeaf::make_ptr(true)); - - filename = dir + "number_42.json"; - std::cerr << "Starting test with: " << filename << std::endl; - node = JsonParser::parse_from_file(filename); - ASSERT_EQUAL(*node, *NumberLeaf::make_ptr(42)); - - filename = dir + "string_hello.json"; - std::cerr << "Starting test with: " << filename << std::endl; - node = JsonParser::parse_from_file(filename); - ASSERT_EQUAL(*node, *StringLeaf::make_ptr("Hello")); - - filename = dir + "object_alphabet.json"; - std::cerr << "Starting test with: " << filename << std::endl; - auto target1 = ObjectNode::make_ptr(); - for (char c = 'a'; c <= 'z'; c++) - target1->add(std::string(1, c), NumberLeaf::make_ptr(c)); - node = JsonParser::parse_from_file(filename); - ASSERT_EQUAL(*node, *target1); - - filename = dir + "array_range10.json"; - std::cerr << "Starting test with: " << filename << std::endl; - auto target2 = ArrayNode::make_ptr(); - for (unsigned i = 0; i < 10; i++) - target2->add(NumberLeaf::make_ptr(i)); - node = JsonParser::parse_from_file(filename); - ASSERT_EQUAL(*node, *target2); - - filename = dir + "pokedex.json"; - std::cerr << "Starting test with: " << filename << std::endl; - node = JsonParser::parse_from_file(filename); - ASSERT_EQUAL(*node, *node); -} \ No newline at end of file diff --git a/tp-json/tests/40_modification.cpp b/tp-json/tests/40_modification.cpp new file mode 100644 index 00000000..120c3269 --- /dev/null +++ b/tp-json/tests/40_modification.cpp @@ -0,0 +1,24 @@ +#include "../JsonParser.hpp" +#include "custom_assert.hpp" + +int main() +{ + std::string json_dir = "json/"; + + std::string filename = json_dir + "pokedex.json"; + auto node = JsonParser::parse_from_file(filename); + auto pokemon_node = node->at("pokemon")->as_ArrayNode(); + + auto chikorita = ObjectNode::make_ptr(); + chikorita->add("id", NumberLeaf::make_ptr(152)); + chikorita->add("name", StringLeaf::make_ptr("Chikorita")); + chikorita->add("num", StringLeaf::make_ptr("152")); + chikorita->add("type", ArrayNode::make_ptr()); + chikorita->at("type")->as_ArrayNode()->add(StringLeaf::make_ptr("Grass")); + + pokemon_node->add(std::move(chikorita)); + + ASSERT_EQUAL(pokemon_node->child_count(), 152u); + ASSERT_EQUAL(node->height(), 5u); + ASSERT_EQUAL(node->node_count(), 3779u + 6u); +} \ No newline at end of file diff --git a/tp-json/tests/40_pokedex.cpp b/tp-json/tests/40_pokedex.cpp deleted file mode 100644 index c00b0abf..00000000 --- a/tp-json/tests/40_pokedex.cpp +++ /dev/null @@ -1,99 +0,0 @@ -#include "../JsonParser.hpp" -#include "custom_assert.hpp" - -#include -#include -#include -#include - -int main(int argc, char** argv) -{ - if (argc < 2) - { - std::cout << "First command-line argument needs to be the path to the json resources."; - exit(EXIT_FAILURE); - } - std::string dir = std::string(argv[1]); - std::string filename; - - { - Node_ptr node; - - filename = dir + "pokedex.json"; - std::cerr << "Starting test with: " << filename << std::endl; - node = JsonParser::parse_from_file(filename); - - ASSERT_EQUAL(node->kind(), NodeKind::OBJECT) - ASSERT_UNEQUAL(node->as_ObjectNode()->at("pokemon"), nullptr) - ASSERT_EQUAL(node->as_ObjectNode()->at("pokemon")->kind(), NodeKind::ARRAY) - - std::unordered_map pokemon_id_by_name; - for (const auto& pokemon_node : *(node->as_ObjectNode()->at("pokemon")->as_ArrayNode())) - { - ASSERT_EQUAL(pokemon_node->kind(), NodeKind::OBJECT) - auto pokemon_obj = pokemon_node->as_ObjectNode(); - - ASSERT_UNEQUAL(pokemon_obj->at("name"), nullptr) - ASSERT_EQUAL(pokemon_obj->at("name")->kind(), NodeKind::STRING) - const std::string& name = pokemon_obj->at("name")->as_StringLeaf()->data(); - - ASSERT_UNEQUAL(pokemon_obj->at("id"), nullptr) - ASSERT_UNEQUAL(pokemon_obj->at("id")->as_NumberLeaf(), nullptr) - unsigned id = pokemon_obj->at("id")->as_NumberLeaf()->data(); - - pokemon_id_by_name[name] = id; - } - - std::unordered_map> pokemon_id_by_type; - for (const auto& pokemon_node : *(node->as_ObjectNode()->at("pokemon")->as_ArrayNode())) - { - ASSERT_EQUAL(pokemon_node->kind(), NodeKind::OBJECT) - ASSERT_UNEQUAL(pokemon_node->as_ObjectNode()->at("type"), nullptr) - ASSERT_EQUAL(pokemon_node->as_ObjectNode()->at("type")->kind(), NodeKind::ARRAY) - for (const auto& type_node : *(pokemon_node->as_ObjectNode()->at("type")->as_ArrayNode())) - { - ASSERT_UNEQUAL(type_node->as_StringLeaf(), nullptr) - std::string const& type = type_node->as_StringLeaf()->data(); - auto it = pokemon_id_by_type.find(type); - if (it == pokemon_id_by_type.end()) - it = pokemon_id_by_type.emplace(type, std::vector()).first; - it->second.emplace_back(pokemon_node->as_ObjectNode()->at("id")->as_NumberLeaf()->data()); - } - } - - for (const auto& pair : pokemon_id_by_type) - { - std::cerr << pair.first << ": ["; - bool b = false; - for (auto i : pair.second) - { - if (b) - std::cerr << ", "; - else - b = true; - std::cerr << i; - } - std::cerr << "]" << std::endl; - } - - std::vector> const tests = { { "Bulbasaur", "Grass" }, - { "Bulbasaur", "Poison" }, - { "Wartortle", "Water" }, - { "Pikachu", "Electric" } }; - for (auto const& [name, type] : tests) - { - std::cerr << "Checking that \"" << name << "\" is of type \"" << type << "\"." << std::endl; - - auto const& list_it = pokemon_id_by_type.find(type); - auto const& elt_it = pokemon_id_by_name.find(name); - - assert(list_it != pokemon_id_by_type.end()); - assert(elt_it != pokemon_id_by_name.end()); - - const std::vector& poke_list = list_it->second; - unsigned poke_elt = elt_it->second; - - assert(std::find(poke_list.begin(), poke_list.end(), poke_elt) != poke_list.end()); - } - } -} \ No newline at end of file diff --git a/tp-json/tests/39_copy.cpp b/tp-json/tests/41_copy.cpp similarity index 62% rename from tp-json/tests/39_copy.cpp rename to tp-json/tests/41_copy.cpp index 800e2593..f7c6ca0e 100644 --- a/tp-json/tests/39_copy.cpp +++ b/tp-json/tests/41_copy.cpp @@ -1,19 +1,12 @@ #include "../JsonParser.hpp" #include "custom_assert.hpp" -int main(int argc, char** argv) +int main() { - if (argc < 1) - { - std::cout << "First command-line argument needs to be where are the json resources."; - exit(EXIT_FAILURE); - } - std::string dir = std::string(argv[1]); - std::string filename; - Node_ptr original; - filename = dir + "pokedex.json"; - original = JsonParser::parse_from_file(filename); + std::string json_dir = "json/"; + std::string filename = json_dir + "pokedex.json"; + Node_ptr original = JsonParser::parse_from_file(filename); // We make the copy multiple times to ensure that deallocation works for (unsigned i = 0; i < 100; ++i) diff --git a/tp-json/tests/99_dot.cpp b/tp-json/tests/99_dot.cpp index d6bce83e..47460494 100644 --- a/tp-json/tests/99_dot.cpp +++ b/tp-json/tests/99_dot.cpp @@ -14,24 +14,15 @@ void dot(std::ostream& o, Node_ptr const& node) node->dot(o); o << "}" << std::endl; } -int main(int argc, char** argv) -{ - if (argc < 2) - { - std::cout << "First command-line argument needs to be the path to the json resources."; - exit(EXIT_FAILURE); - } - std::string dir = std::string(argv[1]); - std::string filename; - { - Node_ptr node; +int main() +{ + std::string dir = "json/"; - filename = dir + "cpp2022.json"; - std::cerr << "Starting test with: " << filename << std::endl; - node = JsonParser::parse_from_file(filename); + std::string filename = dir + "cpp2022.json"; + std::cerr << "Starting test with: " << filename << std::endl; + Node_ptr node = JsonParser::parse_from_file(filename); - std::ofstream out("/tmp/test.gv"); - dot(out, node); - } + std::ofstream out("/tmp/test.gv"); + dot(out, node); } \ No newline at end of file diff --git a/tp-json/tests/CMakeLists.txt b/tp-json/tests/CMakeLists.txt index 7c3100c6..da1fad61 100644 --- a/tp-json/tests/CMakeLists.txt +++ b/tp-json/tests/CMakeLists.txt @@ -1,4 +1,6 @@ file(GLOB TESTS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} [0-9]*.cpp) +file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/json DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) + SET(OLDVAR 0) SET(VAR 1) @@ -15,7 +17,7 @@ FOREACH(TEST ${TESTS}) SET(OLDVAR ${VAR}) MATH(EXPR VAR "${VAR}+1") - add_test(NAME run:${TEST} COMMAND ./tp-json-test-${TEST} "${CMAKE_CURRENT_SOURCE_DIR}/json/") + add_test(NAME run:${TEST} COMMAND ./tp-json-test-${TEST}) set_tests_properties(run:${TEST} PROPERTIES FIXTURES_SETUP ${VAR}) set_tests_properties(run:${TEST} PROPERTIES FIXTURES_REQUIRED ${OLDVAR}) From 515a8fc0a9d55eefd55f13f7f7faf18c1ac66c47 Mon Sep 17 00:00:00 2001 From: Victor Marsault Date: Fri, 1 Apr 2022 20:00:02 +0200 Subject: [PATCH 5/5] Applied several suggestions --- CMakeLists.txt | 4 ++ tp-json/CMakeLists.txt | 3 -- tp-json/JsonParser.cpp | 51 +++++++++++-------- tp-json/JsonParser.hpp | 2 +- tp-json/Node.hpp | 5 +- tp-json/NodeKind.hpp | 2 - tp-json/Node_ptr.hpp | 7 --- tp-json/StringLeaf.hpp | 16 +++++- .../code_fourni_aux_etudiants/Node_ptr.hpp | 5 -- tp-json/enonce.md | 30 +++++------ .../{01_basic_node.cpp => 01_compile.cpp} | 0 tp-json/tests/32_printparse_consistency.cpp | 19 +++++++ ...explicit_cast.cpp => 40_explicit_cast.cpp} | 4 -- ...t_const.cpp => 41_explicit_cast_const.cpp} | 0 ...on_object.cpp => 42_navigation_object.cpp} | 0 ...tion_array.cpp => 43_navigation_array.cpp} | 0 ...gation_full.cpp => 44_navigation_full.cpp} | 0 ...0_modification.cpp => 50_modification.cpp} | 0 tp-json/tests/{41_copy.cpp => 51_copy.cpp} | 0 tp-json/tests/json/string_nasty.json | 1 + 20 files changed, 87 insertions(+), 62 deletions(-) delete mode 100644 tp-json/Node_ptr.hpp delete mode 100644 tp-json/code_fourni_aux_etudiants/Node_ptr.hpp rename tp-json/tests/{01_basic_node.cpp => 01_compile.cpp} (100%) create mode 100644 tp-json/tests/32_printparse_consistency.cpp rename tp-json/tests/{32_explicit_cast.cpp => 40_explicit_cast.cpp} (99%) rename tp-json/tests/{33_explicit_cast_const.cpp => 41_explicit_cast_const.cpp} (100%) rename tp-json/tests/{35_navigation_object.cpp => 42_navigation_object.cpp} (100%) rename tp-json/tests/{34_navigation_array.cpp => 43_navigation_array.cpp} (100%) rename tp-json/tests/{36_navigation_full.cpp => 44_navigation_full.cpp} (100%) rename tp-json/tests/{40_modification.cpp => 50_modification.cpp} (100%) rename tp-json/tests/{41_copy.cpp => 51_copy.cpp} (100%) create mode 100644 tp-json/tests/json/string_nasty.json diff --git a/CMakeLists.txt b/CMakeLists.txt index a29df37e..82ab4008 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -23,3 +23,7 @@ file(GLOB tp_folders tp-*) foreach(tp ${tp_folders}) add_subdirectory(${tp}) endforeach() + + +# To uncomment to start the TP about JSON +enable_testing() diff --git a/tp-json/CMakeLists.txt b/tp-json/CMakeLists.txt index 42c8862d..dda6a6a2 100644 --- a/tp-json/CMakeLists.txt +++ b/tp-json/CMakeLists.txt @@ -1,6 +1,3 @@ - -enable_testing() - file(GLOB TP-JSON-SOURCES *.cpp) add_subdirectory(tests) diff --git a/tp-json/JsonParser.cpp b/tp-json/JsonParser.cpp index d55c2139..a3847093 100644 --- a/tp-json/JsonParser.cpp +++ b/tp-json/JsonParser.cpp @@ -1,5 +1,7 @@ #include "JsonParser.hpp" +#include +#include #include #include #include @@ -11,26 +13,27 @@ void JsonParser::extract_spaces() _in.get(); } -bool JsonParser::check_next_char_equals(int expected_char, std::string_view alternatives) +bool JsonParser::check_next_char_equals(char expected_char, std::string_view alternatives) { - int next_char = _in.peek(); - if (expected_char == next_char) + int expected_char_as_int = expected_char; + int next_char = _in.peek(); + if (expected_char_as_int == next_char) { _in.get(); return true; } std::cerr << "Unexpected character ("; - if (next_char >= 20 && next_char < 127) + if (std::isprint(next_char)) { - std::cerr << static_cast(expected_char); + std::cerr << static_cast(next_char); } - else if (next_char == -1) + else if (next_char == std::char_traits::eof()) { std::cerr << "EOF"; } else { - std::cerr << '\\' << next_char; + std::cerr << "code: " << next_char; } if (alternatives != "") @@ -39,7 +42,7 @@ bool JsonParser::check_next_char_equals(int expected_char, std::string_view alte } else { - std::cerr << "). Expecting '" << static_cast(expected_char) << "'." << std::endl; + std::cerr << "). Expecting '" << expected_char << "'." << std::endl; } return false; } @@ -77,27 +80,29 @@ Node_ptr JsonParser::parse_Node() case '+': return parse_NumberLeaf(); default: - check_next_char_equals(-123456789, "{[\"nft0123456789eE.-+"); + check_next_char_equals('{', "{[\"ft0123456789eE.-+"); return nullptr; } } Node_ptr JsonParser::parse_constant(std::string_view target) { - char c[6]; - for (size_t x = 0; x < target.size(); x++) + assert(target == "true" || target == "false"); + std::string actual_string; + actual_string.reserve(target.size()); + for (char expected_char : target) { - _in >> c[x]; - if (c[x] != target[x]) + char actual_char; + _in >> actual_char; + actual_string += actual_char; + if (actual_char != expected_char) { - c[x + 1] = '\0'; std::cerr << "Expecting JSON value (probably constant " << target << ") at position " - << (_in.tellg() - (long)x) << ". Got string starting with " << c << std::endl; + << (_in.tellg() - (long)actual_string.size()) << ". Got string starting with " + << actual_string << std::endl; return nullptr; } } - // if (target == "null") - // return NullNode::make_ptr(); if (target == "true") return BooleanLeaf::make_ptr(true); if (target == "false") @@ -109,18 +114,17 @@ std::optional JsonParser::extract_string() { extract_spaces(); check_next_char_equals('"'); - std::string s = ""; + std::string s; char c = '\0'; while (((c = _in.get()) != '"') && !_in.eof()) { - s += c; if (c == '\\') /* If c is a '\' then the next char is escaped. */ { c = _in.get(); if (_in.eof()) break; - s += _in.get(); } + s += c; } if (_in.eof()) @@ -232,7 +236,12 @@ Node_ptr JsonParser::parse_from_istream(std::istream& in) JsonParser parser(in); Node_ptr parsed_tree = parser.run(); if (parsed_tree) + { + parser.extract_spaces(); + if (in.good() && (in.get() != std::char_traits::eof())) + std::cerr << "[Warning] Ended parsing before reaching EOF." << std::endl; return parsed_tree; + } else exit(EXIT_FAILURE); } @@ -242,7 +251,7 @@ Node_ptr JsonParser::parse_from_file(std::string const& path) std::ifstream in(path.c_str(), std::ifstream::in); if (!in.is_open()) { - std::cerr << "Could not open file: " << path << std::endl; + std::cerr << "Could not open file: \"" << path << '"' << std::endl; exit(EXIT_FAILURE); } return parse_from_istream(in); diff --git a/tp-json/JsonParser.hpp b/tp-json/JsonParser.hpp index 5f0bb89a..cd3b4b72 100644 --- a/tp-json/JsonParser.hpp +++ b/tp-json/JsonParser.hpp @@ -17,7 +17,7 @@ class JsonParser std::istream& _in; void extract_spaces(); - bool check_next_char_equals(int c, std::string_view other_possibilities = ""); + bool check_next_char_equals(char expected_char, std::string_view alternatives = ""); std::optional extract_string(); diff --git a/tp-json/Node.hpp b/tp-json/Node.hpp index 53ed7787..3e321b6b 100644 --- a/tp-json/Node.hpp +++ b/tp-json/Node.hpp @@ -1,7 +1,6 @@ #pragma once #include "NodeKind.hpp" -#include "Node_ptr.hpp" #include #include @@ -16,6 +15,10 @@ class StringLeaf; class ArrayNode; class ObjectNode; +class Node; + +using Node_ptr = std::unique_ptr; + class Node { private: diff --git a/tp-json/NodeKind.hpp b/tp-json/NodeKind.hpp index ddb63953..b436f326 100644 --- a/tp-json/NodeKind.hpp +++ b/tp-json/NodeKind.hpp @@ -4,8 +4,6 @@ enum class NodeKind { - /* NONE is used for NullNode since NULL is reserved */ - // NONE, BOOLEAN, NUMBER, STRING, diff --git a/tp-json/Node_ptr.hpp b/tp-json/Node_ptr.hpp deleted file mode 100644 index d9f4bdd3..00000000 --- a/tp-json/Node_ptr.hpp +++ /dev/null @@ -1,7 +0,0 @@ -#pragma once - -#include - -class Node; - -using Node_ptr = std::unique_ptr; \ No newline at end of file diff --git a/tp-json/StringLeaf.hpp b/tp-json/StringLeaf.hpp index dc5ceec2..30156457 100644 --- a/tp-json/StringLeaf.hpp +++ b/tp-json/StringLeaf.hpp @@ -8,7 +8,21 @@ class StringLeaf : public Node std::string _data; public: - std::string print() const override { return '"' + _data + '"'; } + std::string print() const override + { + std::string result; + result.reserve(_data.size() + 2); + result += '"'; + for (char c : _data) + { + if (c == '\\' || c == '"') + result += '\\'; + result += c; + } + result += '"'; + return result; + } + StringLeaf(std::string data) : Node { NodeKind::STRING } , _data { data } diff --git a/tp-json/code_fourni_aux_etudiants/Node_ptr.hpp b/tp-json/code_fourni_aux_etudiants/Node_ptr.hpp deleted file mode 100644 index 636a7adb..00000000 --- a/tp-json/code_fourni_aux_etudiants/Node_ptr.hpp +++ /dev/null @@ -1,5 +0,0 @@ -#pragma once - -class Node; - -using Node_ptr = void /* todo */; \ No newline at end of file diff --git a/tp-json/enonce.md b/tp-json/enonce.md index 281d9089..8c4d1b66 100644 --- a/tp-json/enonce.md +++ b/tp-json/enonce.md @@ -5,15 +5,19 @@ Le but de ce TP est d'implémenter les classes permettant de représenter en mé ## Test Driven Development -Ce TP suit à nouveau en TDD, mais cette fois on utilisera le paradigme `ctest`. Chaque test est un programme indépendant, et il est réussi s'il s'éxecute sans erreur. Les programmes se trouvent dans le répertoire `tests`, et doivent être fait par ordre croissant. +Ce TP suit à nouveau en TDD, mais cette fois on utilisera des `ctest`. Chaque test est un programme indépendant, et il est réussi s'il s'éxecute sans erreur. Les programmes se trouvent dans le répertoire `tests`, et doivent être fait par ordre croissant. -Pour activer les tests, décommenter la ligne suivante du `CMakeLists.txt` +Pour activer les tests, décommenter la ligne suivante du `CMakeLists.txt` à la racine du répertoire git. ``` enable_testing() ``` -Après reconfiguration, une nouvelle ligne apparait dans la barre en bas de vscode. Vous pouvez lancer la suite de tests en cliquant dessus. -Pour simplifier, chaque fichier +Après reconfiguration, une nouvelle ligne apparait dans la barre en bas de vscode. Vous pouvez lancer la suite de tests en cliquant dessus. Vous pouvez aussi utiliser les boutons usuels (*Build*, *Run* et *Debug*) pour compiler ou éxécuter un test en particulier. + + +Un `ctest` est un fichier qui contient un petit programme indépendant (par exemple `tests/01_compile.cpp`), qui correspond à deux tests: +- Un test de compilation (par exemple `build:01_compile.cpp`): il est réussi si le compilateur arrive a compiler le fichier. +- Un text d'éxecution (par exemple `run:01_compile.cpp`): le test est réussi si le programme s'éxecute sans erreur. ## Documents JSON @@ -53,7 +57,7 @@ Au contraire, le document `json/pokedex.json` est représentatif de ce à quoi r ## Arbres Un document peut se voir comme un arbre: -- Les booléens, entier et chaînes de caractères sont des feuilles de l'arbre. +- Les booléens, les entiers et chaînes de caractères sont des feuilles de l'arbre. - les listes et les dictionnaires sont des noeuds internes et ont pour fils chacune des valeurs à l'intérieur. Par exemple, le document donné en début de TP se représente: @@ -65,21 +69,13 @@ Par exemple, le document donné en début de TP se représente: Un document JSON sera représenté en mémoire comme un arbre dont les noeuds sont polymorphes: - La classe `Node` sera la classe principale pour représenter un noeud dont on ne connaît pas le type exact. - Les classes `BooleanLeaf`, `NumberLeaf`, `StringLeaf`, `ArrayNode`, `ObjectNode` représenterons les différents types de noeuds. -- Le type `Node_ptr` sera utilisée pour faire référence/pointer vers les enfants d'un noeud. Vous devrez choisir le type approprié à un certain point du TDD. -- Le type `NodeKind` est fourni, c'est une `enum` listant les différents types de noeuds. Il permet de savoir à l'exécution le type réel d'un `Node`. +- Le type `Node_ptr` sera utilisée pour faire référence/pointer vers les enfants d'un noeud. Vous devrez choisir le type approprié. +- Le type `NodeKind` est fourni, c'est une `enum` listant les différents types de noeuds. A l'éxecution, il sera utilisé par les `Node`'s pour indiquer leur type réel. Quand c'est pertinent, on factorisera le code en utilisant l'héritage. -Par exemple, si on remarque que plusieurs classe partagent des fonctionnalités, il faudra les les centraliser dans une classe intermédiaire (entre `Node` et les sous-classes concrètes). +Par exemple, si on remarque que plusieurs classes partagent des fonctionnalités, il faudra les centraliser dans une classe intermédiaire (entre `Node` et les sous-classes concrètes). ## Parseur -Un parseur de JSON est fourni (classe `JsonParser` dans le fichier `JsonParser.cpp`), et normalement vous n'aurez pas besoin de le modifier. -Il pourra éventuellement être utile de regarder ce fichier partir des tests qui l'utilisent. - - - - - - +Un parseur de JSON est fourni (classe `JsonParser` dans le fichier `JsonParser.cpp`), et normalement vous n'aurez pas besoin de le modifier. Il pourra éventuellement être utile de regarder ce fichier à partir des tests qui l'utilisent. -## A vos claviers ! diff --git a/tp-json/tests/01_basic_node.cpp b/tp-json/tests/01_compile.cpp similarity index 100% rename from tp-json/tests/01_basic_node.cpp rename to tp-json/tests/01_compile.cpp diff --git a/tp-json/tests/32_printparse_consistency.cpp b/tp-json/tests/32_printparse_consistency.cpp new file mode 100644 index 00000000..b5bfae73 --- /dev/null +++ b/tp-json/tests/32_printparse_consistency.cpp @@ -0,0 +1,19 @@ +#include "../JsonParser.hpp" +#include "custom_assert.hpp" + +int main() +{ + std::string json_dir = "json/"; + + std::vector names = { "cpp2022.json", "pokedex.json", "string_nasty.json" }; + + for (const std::string& name : names) + { + std::string filename = json_dir + name; + std::cerr << "Starting test with: \"" << filename << '"' << std::endl; + Node_ptr node1 = JsonParser::parse_from_file(filename); + std::string node1_as_string = node1->print(); + Node_ptr node2 = JsonParser::parse_from_string(node1_as_string); + ASSERT_EQUAL(*node1, *node2); + } +} \ No newline at end of file diff --git a/tp-json/tests/32_explicit_cast.cpp b/tp-json/tests/40_explicit_cast.cpp similarity index 99% rename from tp-json/tests/32_explicit_cast.cpp rename to tp-json/tests/40_explicit_cast.cpp index 3cb04d10..5ee7f0fb 100644 --- a/tp-json/tests/32_explicit_cast.cpp +++ b/tp-json/tests/40_explicit_cast.cpp @@ -18,7 +18,6 @@ int main() ASSERT_EQUAL(node->as_ArrayNode(), nullptr); ASSERT_EQUAL(node->as_ObjectNode(), nullptr); } - { Node_ptr node = NumberLeaf::make_ptr(1); ASSERT_UNEQUAL(node->as_NumberLeaf(), nullptr); @@ -27,17 +26,14 @@ int main() ASSERT_EQUAL(node->as_ArrayNode(), nullptr); ASSERT_EQUAL(node->as_ObjectNode(), nullptr); } - { Node_ptr node = StringLeaf::make_ptr("Hello world"); ASSERT_UNEQUAL(node->as_StringLeaf(), nullptr); } - { Node_ptr node = ArrayNode::make_ptr(); ASSERT_UNEQUAL(node->as_ArrayNode(), nullptr); } - { Node_ptr node = ObjectNode::make_ptr(); ASSERT_UNEQUAL(node->as_ObjectNode(), nullptr); diff --git a/tp-json/tests/33_explicit_cast_const.cpp b/tp-json/tests/41_explicit_cast_const.cpp similarity index 100% rename from tp-json/tests/33_explicit_cast_const.cpp rename to tp-json/tests/41_explicit_cast_const.cpp diff --git a/tp-json/tests/35_navigation_object.cpp b/tp-json/tests/42_navigation_object.cpp similarity index 100% rename from tp-json/tests/35_navigation_object.cpp rename to tp-json/tests/42_navigation_object.cpp diff --git a/tp-json/tests/34_navigation_array.cpp b/tp-json/tests/43_navigation_array.cpp similarity index 100% rename from tp-json/tests/34_navigation_array.cpp rename to tp-json/tests/43_navigation_array.cpp diff --git a/tp-json/tests/36_navigation_full.cpp b/tp-json/tests/44_navigation_full.cpp similarity index 100% rename from tp-json/tests/36_navigation_full.cpp rename to tp-json/tests/44_navigation_full.cpp diff --git a/tp-json/tests/40_modification.cpp b/tp-json/tests/50_modification.cpp similarity index 100% rename from tp-json/tests/40_modification.cpp rename to tp-json/tests/50_modification.cpp diff --git a/tp-json/tests/41_copy.cpp b/tp-json/tests/51_copy.cpp similarity index 100% rename from tp-json/tests/41_copy.cpp rename to tp-json/tests/51_copy.cpp diff --git a/tp-json/tests/json/string_nasty.json b/tp-json/tests/json/string_nasty.json new file mode 100644 index 00000000..36dc22db --- /dev/null +++ b/tp-json/tests/json/string_nasty.json @@ -0,0 +1 @@ +"{ \"truefalse\" : [ \",\\,\" ] }" \ No newline at end of file