Browse Source

Reading (non-empty) objects

isundil 9 years ago
parent
commit
a3d8ed9d04

+ 1 - 1
CMakeLists.txt

@@ -1,6 +1,6 @@
 cmake_minimum_required(VERSION 2.8)
 
-add_executable(jsonstroll src/main.cpp src/params.cpp)
+add_executable(jsonstroll src/main.cpp src/params.cpp src/streamConsumer.cpp src/jsonObject.cpp src/jsonElement.cpp src/jsonPrimitive.cpp src/jsonException.cpp)
 
 set_property(TARGET jsonstroll PROPERTY RUNTIME_OUTPUT_DIRECTORY bin)
 

+ 7 - 0
include/jsonArray.hh

@@ -0,0 +1,7 @@
+#pragma once
+
+#include "jsonElement.hh"
+
+class JSonArray: public JSonElement
+{
+};

+ 8 - 0
include/jsonElement.hh

@@ -0,0 +1,8 @@
+#pragma once
+
+class JSonElement
+{
+    public:
+        virtual ~JSonElement();
+};
+

+ 32 - 0
include/jsonException.hh

@@ -0,0 +1,32 @@
+#pragma once
+
+#include <exception>
+
+class EofException: std::exception
+{ };
+
+class JsonException: std::exception
+{
+    public:
+        JsonException(unsigned long long offset);
+
+    protected:
+        const unsigned long long offset;
+};
+
+class JsonFormatException: JsonException
+{
+    public:
+        JsonFormatException(char character, unsigned long long offset);
+        const char *what() const noexcept;
+
+    protected:
+        const char c;
+};
+
+class JsonEscapedException: JsonFormatException
+{
+    public:
+        JsonEscapedException(char character, unsigned long long offset);
+};
+

+ 19 - 0
include/jsonObject.hh

@@ -0,0 +1,19 @@
+#pragma once
+
+#include <map>
+#include "jsonElement.hh"
+
+template<typename T> class JSonPrimitive;
+
+class JSonObject: public JSonElement
+{
+    public:
+        JSonObject();
+        void push(const JSonPrimitive<std::string> &key, JSonElement *child);
+
+        bool contains(const JSonPrimitive<std::string> &) const;
+
+    protected:
+        std::map<JSonPrimitive<std::string>, JSonElement *> *children;
+};
+

+ 55 - 0
include/jsonPrimitive.hh

@@ -0,0 +1,55 @@
+#pragma once
+
+#include <string>
+#include "jsonElement.hh"
+
+template <typename T>
+class JSonPrimitive: public JSonElement
+{
+    public:
+        JSonPrimitive(T const &v);
+        virtual ~JSonPrimitive();
+
+    protected:
+        const T value;
+};
+
+template<> class JSonPrimitive<float>: public JSonElement
+{
+    public:
+        JSonPrimitive(const std::string &v);
+
+    protected:
+        const float value;
+};
+
+template<> class JSonPrimitive<long long int>: public JSonElement
+{
+    public:
+        JSonPrimitive(const std::string &v);
+
+    protected:
+        const bool value;
+};
+
+template<> class JSonPrimitive<std::string>: public JSonElement
+{
+    public:
+        JSonPrimitive(const std::string &v);
+
+        virtual bool operator<(const JSonPrimitive<std::string> &other) const;
+
+    protected:
+        //TODO
+        const std::string value;
+};
+
+template<> class JSonPrimitive<bool>: public JSonElement
+{
+    public:
+        JSonPrimitive(bool v);
+
+    protected:
+        const bool value;
+};
+

+ 1 - 0
include/params.hpp → include/params.hh

@@ -11,6 +11,7 @@ class Params
         Params(int ac, char **av);
 
         std::basic_istream<char> &getInput() const;
+        bool isValid() const;
 
     private:
         std::basic_istream<char> *input;

+ 29 - 0
include/streamConsumer.hh

@@ -0,0 +1,29 @@
+#pragma once
+
+#include <istream>
+#include "jsonObject.hh"
+#include "jsonArray.hh"
+#include "jsonPrimitive.hh"
+
+class StreamConsumer
+{
+    public:
+        static StreamConsumer *read(std::istream &stream);
+        JSonElement * const getRoot() const;
+
+    private:
+        StreamConsumer(std::istream &stream);
+        /**
+         * @return true on success
+        **/
+        JSonElement *consumeToken(std::string &buf);
+        JSonElement *readNext();
+
+        JSonObject *readObject();
+        JSonArray *readArray();
+        bool ignoreChar(char c) const noexcept;
+
+        std::istream &stream;
+        JSonElement *root;
+};
+

+ 5 - 0
src/jsonElement.cpp

@@ -0,0 +1,5 @@
+#include "jsonElement.hh"
+
+JSonElement::~JSonElement()
+{ }
+

+ 19 - 0
src/jsonException.cpp

@@ -0,0 +1,19 @@
+#include <string>
+#include "jsonException.hh"
+
+JsonException::JsonException(unsigned long long pos): offset(pos)
+{ }
+
+JsonFormatException::JsonFormatException(char character, unsigned long long pos): JsonException(pos), c(character)
+{ }
+
+JsonEscapedException::JsonEscapedException(char character, unsigned long long pos): JsonFormatException(character, pos)
+{ }
+
+const char *JsonFormatException::what() const noexcept
+{
+    std::string res = "Error: unexpected escaped char '";
+    res += c +"' at offset " +offset;
+    return res.c_str();
+}
+

+ 18 - 0
src/jsonObject.cpp

@@ -0,0 +1,18 @@
+#include "jsonObject.hh"
+#include "jsonPrimitive.hh"
+
+JSonObject::JSonObject()
+{
+    children = new std::map<JSonPrimitive<std::string>, JSonElement *>();
+}
+
+void JSonObject::push(const JSonPrimitive<std::string> &key, JSonElement *child)
+{
+    (*children)[key] = child;
+}
+
+bool JSonObject::contains(const JSonPrimitive<std::string> &key) const
+{
+    return children->find(key) != children->end();
+}
+

+ 19 - 0
src/jsonPrimitive.cpp

@@ -0,0 +1,19 @@
+#include "jsonPrimitive.hh"
+
+JSonPrimitive<std::string>::JSonPrimitive(std::string const &v): value(v)
+{ }
+
+bool JSonPrimitive<std::string>::operator<(const JSonPrimitive<std::string> &other) const
+{
+    return value < other.value;
+}
+
+JSonPrimitive<bool>::JSonPrimitive(bool v): value(v)
+{ }
+
+JSonPrimitive<float>::JSonPrimitive(const std::string &v): value(std::stof(v))
+{ }
+
+JSonPrimitive<long long int>::JSonPrimitive(const std::string &v): value(std::stol(v))
+{ }
+

+ 6 - 7
src/main.cpp

@@ -1,16 +1,15 @@
 
 #include <iostream>
-#include "params.hpp"
+#include "streamConsumer.hh"
+#include "params.hh"
 
 int main(int ac, char **av)
 {
     Params *params = new Params(ac, av);
+    JSonElement *rootNode;
 
-    std::basic_istream<char> &ss = params->getInput();
-    while (ss.good())
-    {
-        std::cout << "[" << (char) ss.get() << "]";
-    }
-    std::cout << std::endl;
+    if (!params->isValid())
+        return 0;
+    rootNode = StreamConsumer::read(params->getInput())->getRoot();
 }
 

+ 6 - 1
src/params.cpp

@@ -1,7 +1,7 @@
 
 #include <iostream>
 #include <sstream>
-#include "params.hpp"
+#include "params.hh"
 
 Params::Params(int ac, char **av) :progName(*av), params(std::list<std::string>(ac -1))
 {
@@ -36,3 +36,8 @@ std::basic_istream<char> &Params::getInput() const
     return input == nullptr ? std::cin : *input;
 }
 
+bool Params::isValid() const
+{
+    return true;
+}
+

+ 175 - 0
src/streamConsumer.cpp

@@ -0,0 +1,175 @@
+#include <iostream>
+#include "jsonException.hh"
+#include "jsonElement.hh"
+#include "streamConsumer.hh"
+
+StreamConsumer::StreamConsumer(std::istream &s): stream(s)
+{ }
+
+StreamConsumer *StreamConsumer::read(std::istream &stream)
+{
+    StreamConsumer *inst = new StreamConsumer(stream);
+    inst->root = inst->readNext();
+    return inst;
+}
+
+JSonElement *StreamConsumer::readNext()
+{
+    std::string buf;
+    JSonElement *root = consumeToken(buf);
+
+    if (root == nullptr)
+    {
+        if (buf == "{")
+        {
+            return readObject();
+        }
+        else if (buf == "[")
+        {
+            return readArray();
+        }
+        else
+            return nullptr;
+    }
+    return root;
+}
+
+JSonElement * const StreamConsumer::getRoot() const
+{
+    return root;
+}
+
+JSonObject *StreamConsumer::readObject()
+{
+    JSonObject *result = new JSonObject();
+    std::string buf;
+    JSonElement *keyObj;
+
+    do
+    {
+        keyObj = consumeToken(buf);
+        //TODO empty object case
+        JSonPrimitive<std::string> *key = dynamic_cast<JSonPrimitive<std::string> *>(keyObj);
+        if (key == nullptr)
+            throw new JsonException(stream.tellg());
+        if (consumeToken(buf) != nullptr || buf != ":")
+            throw new JsonException(stream.tellg());
+        JSonElement *child = readNext();
+        if (result->contains(*key))
+            throw new JsonException(stream.tellg()); //Double key
+        result->push(*key, child);
+        delete keyObj;
+        keyObj = consumeToken(buf);
+    } while (!keyObj && buf != "}");
+    return result;
+}
+
+JSonArray *StreamConsumer::readArray()
+{
+    //TODO
+    return nullptr;
+}
+
+JSonElement *StreamConsumer::consumeToken(std::string &buf)
+{
+    bool escaped = false;
+    bool inString = false;
+    bool inBool = false;
+    bool inNumber = false;
+    bool numberIsFloat = false;
+
+    while (stream.good())
+    {
+        char c = stream.get();
+
+        if (inString)
+        {
+            if (!escaped)
+            {
+                if (c == '"')
+                    return new JSonPrimitive<std::string>(buf);
+                else if (c == '\\')
+                    escaped = true;
+                else
+                    buf += c;
+            }
+            else
+            {
+                if (c == '\\' || c == '"')
+                    buf += c;
+                else
+                    throw new JsonEscapedException(c, stream.tellg());
+            }
+        }
+        else if (inBool)
+        {
+            if (c == 'a' || c == 'e' || c == 'l' || c == 'r' || c == 's' || c == 'u')
+                buf += c;
+            else if (buf == "true")
+            {
+                stream.unget();
+                return new JSonPrimitive<bool>(true);
+            }
+            else if (buf == "false")
+            {
+                stream.unget();
+                return new JSonPrimitive<bool>(false);
+            }
+            else if (ignoreChar(c))
+                ;
+            else
+                throw new JsonFormatException(c, stream.tellg());
+        }
+        else if (inNumber)
+        {
+            if (c >= '0' && c <= '9')
+                buf += c;
+            else if (c == '.' && !numberIsFloat)
+            {
+                numberIsFloat = true;
+                buf += c;
+            }
+            else
+            {
+                stream.unget();
+                if (numberIsFloat)
+                    return new JSonPrimitive<float>(buf);
+                return new JSonPrimitive<long long int>(buf);
+            }
+        }
+        else
+        {
+            //!InString, !inbool
+            if (c == '"')
+            {
+                buf = "";
+                inString = true;
+            }
+            else if (c == 't' || c == 'f')
+            {
+                buf = c;
+                inBool = true;
+            }
+            else if (c == '{' || c == '[' || c == '}' || c == ']' || c == ':' || c == ',')
+            {
+                buf = c;
+                return nullptr;
+            }
+            else if ((c >= '0' && c <= '9') || c == '.')
+            {
+                buf = c;
+                inNumber = true;
+            }
+            else if (!ignoreChar(c))
+                throw new JsonFormatException(c, stream.tellg());
+        }
+    }
+    buf = "";
+    return nullptr;
+}
+
+bool StreamConsumer::ignoreChar(char c) const noexcept
+{
+    return (c <= 32 || c >= 127);
+}
+