Przeglądaj źródła

[bugfix] Fix #2 Better error reporting
[add] buffer input in a circular buffer to print the lasts char read

isundil 9 lat temu
rodzic
commit
a490631af2

+ 1 - 0
.gitignore

@@ -36,6 +36,7 @@
 /Testing
 /bin
 /test/json_test
+/test/wrapped_test
 /cmake_install.cmake
 /Makefile
 .fuse_hidden*

+ 3 - 0
CMakeLists.txt

@@ -2,6 +2,7 @@ cmake_minimum_required(VERSION 2.8)
 
 add_executable(jsonstroll src/main.cpp src/jsonContainer.cpp src/params.cpp src/curseOutput.cpp src/streamConsumer.cpp src/jsonArray.cpp src/jsonObjectEntry.cpp src/jsonObject.cpp src/jsonElement.cpp src/jsonPrimitive.cpp src/jsonException.cpp)
 add_executable(json_test test/src/main.cpp src/jsonContainer.cpp src/params.cpp src/streamConsumer.cpp src/jsonArray.cpp src/jsonObjectEntry.cpp src/jsonObject.cpp src/jsonElement.cpp src/jsonPrimitive.cpp src/jsonException.cpp)
+add_executable(wrapped_test test/src/wrapped.cpp)
 
 set_property(TARGET jsonstroll PROPERTY RUNTIME_OUTPUT_DIRECTORY bin)
 
@@ -17,4 +18,6 @@ include_directories(include ${CURSES_INCLUDE_DIRS})
 enable_testing()
 set_property(TARGET json_test PROPERTY RUNTIME_OUTPUT_DIRECTORY test)
 add_test(json_test test/json_test)
+set_property(TARGET wrapped_test PROPERTY RUNTIME_OUTPUT_DIRECTORY test)
+add_test(wrapped_test test/wrapped_test)
 

+ 27 - 10
include/jsonException.hh

@@ -1,32 +1,49 @@
 #pragma once
 
 #include <exception>
+#include "wrappedBuffer.hpp"
 
-class EofException: std::exception
+class EofException: public std::exception
 { };
 
-class JsonException: std::exception
+class JsonException: public std::exception
 {
     public:
-        JsonException(unsigned long long offset);
+        JsonException(const std::string &what, unsigned long long offset, WrappedBuffer<char> &buf);
+
+        std::string getHistory() const;
+        const char *what() const noexcept;
 
     protected:
         const unsigned long long offset;
+        const WrappedBuffer<char> history;
+        const std::string _what;
 };
 
-class JsonFormatException: JsonException
+class JsonNotJsonException: public JsonException
 {
     public:
-        JsonFormatException(char character, unsigned long long offset);
-        const char *what() const noexcept;
+        JsonNotJsonException(unsigned long long offet, WrappedBuffer<char> &h);
+};
 
-    protected:
-        const char c;
+class JsonUnexpectedException: public JsonException
+{
+    public:
+        JsonUnexpectedException(const char expected, unsigned long long offset, WrappedBuffer<char> &h);
 };
 
-class JsonEscapedException: JsonFormatException
+class JsonFormatException: public JsonException
 {
     public:
-        JsonEscapedException(char character, unsigned long long offset);
+        JsonFormatException(unsigned long long offset, WrappedBuffer<char> &h);
+};
+
+class JsonEscapedException: public JsonException
+{
+    public:
+        JsonEscapedException(char c, unsigned long long offset, WrappedBuffer<char> &h);
+
+    protected:
+        const char c;
 };
 

+ 13 - 0
include/jsonObject.hh

@@ -2,6 +2,7 @@
 
 #include "jsonContainer.hh"
 #include "jsonObjectEntry.hh"
+#include "jsonException.hh"
 
 class JSonObject: public JSonContainer
 {
@@ -19,5 +20,17 @@ class JSonObject: public JSonContainer
         const JSonElement* get(const std::string &) const;
 
         virtual std::string stringify() const;
+
+    class DoubleKeyException: public JsonException
+    {
+        public:
+            DoubleKeyException(unsigned long long offset, const std::string &key, WrappedBuffer<char> &buf);
+    };
+
+    class NotAKeyException: public JsonException
+    {
+        public:
+            NotAKeyException(unsigned long long offset, WrappedBuffer<char> &buf);
+    };
 };
 

+ 2 - 0
include/params.hh

@@ -16,6 +16,8 @@ class Params
 
         static void usage(const std::string &) noexcept;
 
+        const std::string &getProgName() const;
+
     private:
         std::basic_istream<char> *input;
         const std::string progName;

+ 3 - 0
include/streamConsumer.hh

@@ -4,6 +4,7 @@
 #include "jsonObject.hh"
 #include "jsonArray.hh"
 #include "jsonPrimitive.hh"
+#include "wrappedBuffer.hpp"
 
 class StreamConsumer
 {
@@ -27,5 +28,7 @@ class StreamConsumer
 
         std::istream &stream;
         JSonElement *root;
+
+        WrappedBuffer<char> history;
 };
 

+ 106 - 0
include/wrappedBuffer.hpp

@@ -0,0 +1,106 @@
+#pragma once
+
+#include <string>
+
+template <typename T, int SIZE =10>
+class WrappedBuffer
+{
+    public:
+        WrappedBuffer();
+        virtual ~WrappedBuffer();
+
+        void put(T item);
+        void pop_back();
+
+        unsigned int size() const;
+        std::basic_string<T> toString() const;
+        T* toArray(T arr[SIZE]) const;
+
+    protected:
+        T buffer[SIZE];
+        int curR;
+        int curW;
+};
+
+template<typename T, int SIZE>
+WrappedBuffer<T, SIZE>::WrappedBuffer(): curR(0), curW(-1)
+{ }
+
+template<typename T, int SIZE>
+WrappedBuffer<T, SIZE>::~WrappedBuffer()
+{ }
+
+template<typename T, int SIZE>
+void WrappedBuffer<T, SIZE>::put(T item)
+{
+    if (curW +1 == SIZE)
+    {
+        curR = 1;
+        curW = 0;
+        buffer[0] = item;
+    }
+    else if (curW == -1)
+    {
+        curR = SIZE;
+        buffer[curW = 0] = item;
+    }
+    else
+    {
+        buffer[++curW] = item;
+        if (curR == curW)
+        {
+            if (++curR > SIZE)
+                curR = 0;
+        }
+    }
+}
+
+template<typename T, int SIZE>
+void WrappedBuffer<T, SIZE>::pop_back()
+{
+    unsigned int oldSize = size();
+    if (oldSize == 0)
+        return;
+    else if (oldSize == 1)
+    {
+        curW = -1;
+        curR = 0;
+    }
+    else if (--curW < 0)
+    {
+        curW += SIZE;
+    }
+}
+
+template<typename T, int SIZE>
+unsigned int WrappedBuffer<T, SIZE>::size() const
+{
+    if (curW == -1)
+        return 0;
+    return (curR > curW) ? (SIZE - curR + curW +1) : (curW - curR +1);
+}
+
+template<typename T, int SIZE>
+std::basic_string<T> WrappedBuffer<T, SIZE>::toString() const
+{
+    std::basic_string<T> result(size(), (T) 0);
+    const unsigned int size = this->size();
+    int from = (curR == SIZE) ? 0 : curR;
+    unsigned int j = 0;
+
+    for (int i = from; (curW > curR && i <= curW) || (curW <= curR && i < SIZE); ++i)
+        result[j++] = buffer[i];
+    for (int i = 0; j < size; ++i)
+        result[j++] = buffer[i];
+    return result;
+}
+
+template<typename T, int SIZE>
+T* WrappedBuffer<T, SIZE>::toArray(T arr[SIZE]) const
+{
+    if (!arr)
+        arr = new T[SIZE]();
+    //TODO
+    return arr;
+}
+

+ 19 - 7
src/jsonException.cpp

@@ -1,19 +1,31 @@
 #include <string>
 #include "jsonException.hh"
 
-JsonException::JsonException(unsigned long long pos): offset(pos)
+JsonException::JsonException(const std::string &wh, unsigned long long pos, WrappedBuffer<char> &h): offset(pos), history(h), _what(wh)
 { }
 
-JsonFormatException::JsonFormatException(char character, unsigned long long pos): JsonException(pos), c(character)
+JsonFormatException::JsonFormatException(unsigned long long pos, WrappedBuffer<char> &h):
+    JsonException("invalid value", pos, h)
 { }
 
-JsonEscapedException::JsonEscapedException(char character, unsigned long long pos): JsonFormatException(character, pos)
+JsonNotJsonException::JsonNotJsonException(unsigned long long pos, WrappedBuffer<char> &h):
+    JsonException("expected json entry, got token", pos, h)
 { }
 
-const char *JsonFormatException::what() const noexcept
+JsonEscapedException::JsonEscapedException(char ch, unsigned long long pos, WrappedBuffer<char> &h):
+    JsonException("unexpected escaped char " +c, pos, h), c(ch)
+{ }
+
+JsonUnexpectedException::JsonUnexpectedException(const char expected, unsigned long long offset, WrappedBuffer<char> &h): JsonException("expected " +expected, offset, h)
+{ }
+
+std::string JsonException::getHistory() const
+{
+    return history.toString();
+}
+
+const char *JsonException::what() const noexcept
 {
-    std::string res = "Error: unexpected escaped char '";
-    res += c +"' at offset " +offset;
-    return res.c_str();
+    return _what.c_str();
 }
 

+ 9 - 0
src/jsonObject.cpp

@@ -62,3 +62,12 @@ std::string JSonObject::stringify() const
     return "{ }";
 }
 
+JSonObject::DoubleKeyException::DoubleKeyException(unsigned long long pos, const std::string &key, WrappedBuffer<char> &buf):
+    JsonException("Unexpected double key " +key +" for object", pos, buf)
+{
+}
+
+JSonObject::NotAKeyException::NotAKeyException(unsigned long long pos, WrappedBuffer<char> &buf):
+    JsonException("expected string key for object", pos, buf)
+{ }
+

+ 22 - 6
src/main.cpp

@@ -1,7 +1,9 @@
 #include <iostream>
+#include <typeinfo>
 #include "streamConsumer.hh"
 #include "curseOutput.hh"
 #include "params.hh"
+#include "jsonException.hh"
 
 void run(Params *params)
 {
@@ -9,14 +11,28 @@ void run(Params *params)
     CurseOutput *out;
     JSonElement *root;
 
-    stream = StreamConsumer::read(params->getInput());
-    root = stream->getRoot();
-    if (root)
+    try
     {
-        out = new CurseOutput(root);
-        out->run();
-        delete out;
+        stream = StreamConsumer::read(params->getInput());
+        root = stream->getRoot();
+        if (!root)
+            throw EofException();
     }
+    catch (EofException &e)
+    {
+        std::cerr << params->getProgName() << ": " << typeid(e).name() << " ("  << e.what() << ") error while reading" << std::endl;
+        return;
+    }
+    catch (JsonException &e)
+    {
+        std::cerr << params->getProgName() << ": [" << typeid(e).name() << "] ("  << e.what() << ") error while reading" << std::endl;
+        std::string buffer = e.getHistory();
+        std::cerr << buffer << std::endl << std::string(buffer.size() -1, '~') << '^' << std::endl;
+        return;
+    }
+    out = new CurseOutput(root);
+    out->run();
+    delete out;
     delete stream;
 }
 

+ 3 - 0
src/params.cpp

@@ -70,3 +70,6 @@ void Params::usage(const std::string &progName) noexcept
     std::cout << "Usage: " << progName << " -- [INPUT] (read input from argument line)" << std::endl;
 }
 
+const std::string &Params::getProgName() const
+{ return progName; }
+

+ 13 - 9
src/streamConsumer.cpp

@@ -58,13 +58,13 @@ JSonObject *StreamConsumer::readObject(JSonContainer *parent)
             return new JSonObject(parent);
         JSonPrimitive<std::string> *key = dynamic_cast<JSonPrimitive<std::string> *>(keyObj);
         if (key == nullptr)
-            throw JsonException(stream.tellg());
+            throw JSonObject::NotAKeyException(stream.tellg(), history);
         if (consumeToken(parent, buf) != nullptr || buf != ":")
-            throw JsonException(stream.tellg());
+            throw JsonUnexpectedException(':', stream.tellg(), history);
         if (result == nullptr)
             result = new JSonObject(parent);
         else if (result->contains(key->getValue()))
-            throw JsonException(stream.tellg()); //Double key
+            throw JSonObject::DoubleKeyException(stream.tellg(), key->getValue(), history); //Double key
         JSonElement *child = readNext(result);
         result->push(key->getValue(), child);
         delete keyObj;
@@ -88,18 +88,18 @@ JSonArray *StreamConsumer::readArray(JSonContainer *parent)
         if (child == nullptr && buf == "{")
             child = readObject(nullptr);
         if (child == nullptr)
-            throw JsonException(stream.tellg());
+            throw JsonNotJsonException(stream.tellg(), history);
         if (result == nullptr)
             result = new JSonArray(parent);
         child->setParent(result);
         result->push_back(child);
         child = consumeToken(result, buf);
         if (child != nullptr)
-            throw JsonException(stream.tellg());
+            throw JsonUnexpectedException(']', stream.tellg(), history);
         else if (buf == "]")
             break;
         else if (buf != ",")
-            throw JsonException(stream.tellg());
+            throw JsonUnexpectedException(']', stream.tellg(), history);
         child = consumeToken(result, buf);
     } while (true);
     return result;
@@ -116,6 +116,7 @@ JSonElement *StreamConsumer::consumeToken(JSonContainer *parent, std::string &bu
     while (stream.good())
     {
         char c = stream.get();
+        history.put(c);
 
         if (inString)
         {
@@ -136,7 +137,7 @@ JSonElement *StreamConsumer::consumeToken(JSonContainer *parent, std::string &bu
                     escaped = false;
                 }
                 else
-                    throw JsonEscapedException(c, stream.tellg());
+                    throw JsonEscapedException(c, stream.tellg(), history);
             }
         }
         else if (inBool)
@@ -145,18 +146,20 @@ JSonElement *StreamConsumer::consumeToken(JSonContainer *parent, std::string &bu
                 buf += c;
             else if (buf == "true")
             {
+                history.pop_back();
                 stream.unget();
                 return new JSonPrimitive<bool>(parent, true);
             }
             else if (buf == "false")
             {
+                history.pop_back();
                 stream.unget();
                 return new JSonPrimitive<bool>(parent, false);
             }
             else if (ignoreChar(c))
                 ;
             else
-                throw JsonFormatException(c, stream.tellg());
+                throw JsonFormatException(stream.tellg(), history);
         }
         else if (inNumber)
         {
@@ -169,6 +172,7 @@ JSonElement *StreamConsumer::consumeToken(JSonContainer *parent, std::string &bu
             }
             else
             {
+                history.pop_back();
                 stream.unget();
                 if (numberIsFloat)
                     return new JSonPrimitive<float>(parent, std::stof(buf));
@@ -206,7 +210,7 @@ JSonElement *StreamConsumer::consumeToken(JSonContainer *parent, std::string &bu
                 inNumber = true;
             }
             else if (!ignoreChar(c))
-                throw JsonFormatException(c, stream.tellg());
+                throw JsonFormatException(stream.tellg(), history);
         }
     }
     buf = "";

+ 155 - 0
test/src/wrapped.cpp

@@ -0,0 +1,155 @@
+#include <iostream>
+#include "wrappedBuffer.hpp"
+
+#define FAILED(got, op, expt) {std::cout << __FILE__ << ":" << __LINE__ << ": failed asserting " << got << " " << op << " expected " << expt << std::endl; return false; }
+
+bool simpleTest()
+{
+    WrappedBuffer<char, 5> test;
+    if (test.toString().size() != 0)
+        FAILED(test.toString().size(), "!=", 0);
+    if (test.size() != 0)
+        FAILED(test.size(), "!=", 0);
+    test.put('a');
+    if (test.size() != 1)
+        FAILED(test.size(), "!=", 1);
+    if (test.toString().size() != 1)
+        FAILED(test.toString().size(), "!=", 1);
+    if (test.toString() != "a")
+        FAILED(test.toString(), "!=", "a");
+
+    test.put('b');
+    if (test.size() != 2)
+        FAILED(test.size(), "!=", 2);
+    if (test.toString().size() != 2)
+        FAILED(test.toString().size(), "!=", 2);
+    if (test.toString() != "ab")
+        FAILED(test.toString(), "!=", "ab");
+
+    test.put('c');
+    if (test.size() != 3)
+        FAILED(test.size(), "!=", 3);
+    if (test.toString().size() != 3)
+        FAILED(test.toString().size(), "!=", 3);
+    if (test.toString() != "abc")
+        FAILED(test.toString(), "!=", "abc");
+
+    test.pop_back();
+    if (test.size() != 2)
+        FAILED(test.size(), "!=", 2);
+    if (test.toString().size() != 2)
+        FAILED(test.toString().size(), "!=", 2);
+    if (test.toString() != "ab")
+        FAILED(test.toString(), "!=", "ab");
+    return true;
+}
+
+bool _testBorder(WrappedBuffer<char, 3> &test)
+{
+    test.put('a');
+    test.put('b');
+    test.put('c');
+
+    if (test.size() != 3)
+        FAILED(test.size(), "!=", 3);
+    if (test.toString() != "abc")
+        FAILED(test.toString(), "!=", "abc");
+
+    test.put('d');
+    if (test.size() != 3)
+        FAILED(test.size(), "!=", 3);
+    if (test.toString() != "bcd")
+        FAILED(test.toString(), "!=", "bcd");
+
+    test.put('e');
+    if (test.size() != 3)
+        FAILED(test.size(), "!=", 3);
+    if (test.toString() != "cde")
+        FAILED(test.toString(), "!=", "cde");
+
+    test.put('f');
+    if (test.size() != 3)
+        FAILED(test.size(), "!=", 3);
+    if (test.toString() != "def")
+        FAILED(test.toString(), "!=", "def");
+
+    test.put('g');
+    if (test.size() != 3)
+        FAILED(test.size(), "!=", 3);
+    if (test.toString() != "efg")
+        FAILED(test.toString(), "!=", "efg");
+
+    test.pop_back();
+    if (test.size() != 2)
+        FAILED(test.size(), "!=", 2);
+    if (test.toString() != "ef")
+        FAILED(test.toString(), "!=", "ef");
+
+    test.put('g');
+    if (test.size() != 3)
+        FAILED(test.size(), "!=", 3);
+    if (test.toString() != "efg")
+        FAILED(test.toString(), "!=", "efg");
+    test.put('h');
+    if (test.size() != 3)
+        FAILED(test.size(), "!=", 3);
+    if (test.toString() != "fgh")
+        FAILED(test.toString(), "!=", "fgh");
+
+    test.pop_back();
+    if (test.size() != 2)
+        FAILED(test.size(), "!=", 2);
+    if (test.toString() != "fg")
+        FAILED(test.toString(), "!=", "fg");
+
+    test.put('h');
+    if (test.size() != 3)
+        FAILED(test.size(), "!=", 3);
+    if (test.toString() != "fgh")
+        FAILED(test.toString(), "!=", "fgh");
+    test.put('i');
+    if (test.size() != 3)
+        FAILED(test.size(), "!=", 3);
+    if (test.toString() != "ghi")
+        FAILED(test.toString(), "!=", "ghi");
+
+    test.pop_back();
+    if (test.size() != 2)
+        FAILED(test.size(), "!=", 2);
+    if (test.toString() != "gh")
+        FAILED(test.toString(), "!=", "gh");
+
+    test.pop_back();
+    if (test.size() != 1)
+        FAILED(test.size(), "!=", 1);
+    if (test.toString() != "g")
+        FAILED(test.toString(), "!=", "g");
+
+    test.pop_back();
+    if (test.size() != 0)
+        FAILED(test.size(), "!=", 0);
+    test.pop_back();
+    if (test.size() != 0)
+        FAILED(test.size(), "!=", 0);
+
+    return true;
+}
+
+bool testBorder()
+{
+    WrappedBuffer<char, 3> test;
+
+    if (!_testBorder(test))
+        return false;
+    return _testBorder(test);
+}
+
+int main()
+{
+    if (!simpleTest())
+        exit(EXIT_FAILURE);
+    if (!testBorder())
+        exit(EXIT_FAILURE);
+    exit(EXIT_SUCCESS);
+}
+