First version, c++.
authorFabien Ninoles <fabien@tzone.org>
Sat, 06 Oct 2012 21:08:55 -0400
changeset 0 d187e7fc9970
child 1 ef7abb48573b
First version, c++.
Makefile
jsonsax.cpp
jsonsax.hpp
tests.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Makefile	Sat Oct 06 21:08:55 2012 -0400
@@ -0,0 +1,8 @@
+all: tests
+
+CC=g++
+
+tests: tests.o jsonsax.o
+
+runtests: tests
+	./tests
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jsonsax.cpp	Sat Oct 06 21:08:55 2012 -0400
@@ -0,0 +1,276 @@
+#include "jsonsax.hpp"
+
+#include <assert.h>
+
+#define CASE_IS_SPACE \
+	case ' ':		  \
+case '\n':			  \
+case '\t':			  \
+case '\r'
+
+#define CASE_IS_DIGIT \
+	case '0':		  \
+case '1':			  \
+case '2':			  \
+case '3':			  \
+case '4':			  \
+case '5':			  \
+case '6':			  \
+case '7':			  \
+case '8':			  \
+case '9'
+
+JSLXParser::JSLXParser(IJSLXBackend* pBackend)
+    : m_pBackend(pBackend)
+{}
+bool JSLXParser::Parse()
+{
+    return GetNextChar() && ParseValue();
+}
+
+bool JSLXParser::_GetNextChar()
+{
+    if (!m_pBackend->Eof())
+    {
+        m_lastChar = m_pBackend->GetChar();
+        return true;
+    }
+    return false;
+}
+
+bool JSLXParser::TrimWhiteSpace()
+{
+    do
+    {
+        switch(m_lastChar)
+        {
+        CASE_IS_SPACE:
+            break;
+        default:
+            return true;
+        }
+    }
+    while(_GetNextChar());
+    return false;
+}
+
+bool JSLXParser::GetNextChar(bool trim /* = true */)
+{
+    if (trim)
+    {
+        return _GetNextChar() && TrimWhiteSpace();
+    }
+    return _GetNextChar();
+}
+
+bool JSLXParser::ParseValue()
+{
+    switch(m_lastChar)
+    {
+    case '[':
+        return ParseArray();
+    case '{':
+        return ParseObject();
+    case '"':
+        return ParseString();
+    case '-':
+    CASE_IS_DIGIT:
+        return ParseNumber();
+    case 'n':
+        return ParseNull();
+    case 't':
+        return ParseTrue();
+    case 'f':
+        return ParseFalse();
+    default:
+        return false;
+    }
+}
+
+bool JSLXParser::ParseObject()
+{
+    assert(m_lastChar == '{');
+    m_pBackend->OnStartObject();
+    GetNextChar();
+    while(ParsePair())
+    {
+        switch(m_lastChar)
+        {
+        case ',':
+            GetNextChar();
+            break;
+        case '}':
+            m_pBackend->OnStopObject();
+            GetNextChar();
+            return true;
+        default:
+            m_pBackend->OnError("ParseObject: invalid char");
+            return false;
+        }
+    }
+    if (m_lastChar != '}')
+    {
+        m_pBackend->OnError("ParseObject: invalid char");
+        return false;
+    }
+    m_pBackend->OnStopObject();
+    GetNextChar();
+    return true;
+}
+
+bool JSLXParser::ParsePair()
+{
+    if (m_lastChar != '"')
+    {
+        m_pBackend->OnError("ParsePair: Invalid Key");
+        return false;
+    }
+    if (!ParseString())
+    {
+        m_pBackend->OnError("ParsePair: Invalid Key");
+        return false;
+    }
+    if (m_lastChar != ':')
+    {
+        m_pBackend->OnError("ParsePair: Missing pair separator");
+        return false;
+    }
+    if (!GetNextChar())
+    {
+        m_pBackend->OnError("ParsePair: Missing value");
+        return false;
+    }
+    return ParseValue();
+}
+
+bool JSLXParser::ParseArray()
+{
+    assert(m_lastChar == '[');
+    if (!GetNextChar()) return false;
+    while(ParseValue())
+    {
+        switch(m_lastChar)
+        {
+        case ',':
+            GetNextChar();
+            break;
+        case ']':
+            m_pBackend->OnStopArray();
+            GetNextChar();
+            return true;
+        default:
+            m_pBackend->OnError("ParseArray: Missing seperator in array");
+            return false;
+        }
+    }
+    if (m_lastChar != ']')
+    {
+        m_pBackend->OnError("ParseArray: Bad value in array");
+        return false;
+    }
+    m_pBackend->OnStopArray();
+    GetNextChar();
+    return true;
+}
+
+bool JSLXParser::ParseString()
+{
+    assert(m_lastChar == '"');
+    m_pBackend->OnStartString();
+    while(GetNextChar())
+    {
+        switch(m_lastChar)
+        {
+        case '\\':
+            GetNextChar();
+            break;
+        case '"':
+            m_pBackend->OnStopString();
+            GetNextChar();
+            return true;
+        }
+    }
+    return false;
+}
+
+bool JSLXParser::ParseNumber()
+{
+    assert(m_lastChar == '-' ||
+        m_lastChar == '0' ||
+        m_lastChar == '1' ||
+        m_lastChar == '2' ||
+        m_lastChar == '3' ||
+        m_lastChar == '4' ||
+        m_lastChar == '5' ||
+        m_lastChar == '6' ||
+        m_lastChar == '7' ||
+        m_lastChar == '8' ||
+        m_lastChar == '9');
+
+    m_pBackend->OnStartNumber();
+    while(GetNextChar())
+    {
+        switch(m_lastChar)
+        {
+            // TODO: lookup for correct number format.
+        case 'e':
+        case '-':
+        case '.':
+        CASE_IS_DIGIT:
+            break;
+        default:
+            m_pBackend->OnStopNumber();
+            return true;
+        }
+    }
+    return true;
+}
+
+bool JSLXParser::ParseNull()
+{
+    if (ParseConstant("null"))
+    {
+        m_pBackend->OnNull();
+        return true;
+    }
+    return false;
+}
+
+bool JSLXParser::ParseTrue()
+{
+    if (ParseConstant("true"))
+    {
+        m_pBackend->OnTrue();
+        return true;
+    }
+    return false;
+}
+
+bool JSLXParser::ParseFalse()
+{
+    if (ParseConstant("false"))
+    {
+        m_pBackend->OnFalse();
+        return true;
+    }
+    return false;
+}
+
+bool JSLXParser::ParseConstant(const char* constant)
+{
+    assert(m_lastChar == constant[0]);
+    ++constant;
+    while(GetNextChar())
+    {
+        if (*constant == '\0')
+            return true;
+        if (*constant != m_lastChar)
+            return false;
+        constant++;
+    }
+    return *constant == '\0';
+}
+
+#undef CASE_IS_DIGIT
+#undef CASE_IS_SPACE
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jsonsax.hpp	Sat Oct 06 21:08:55 2012 -0400
@@ -0,0 +1,84 @@
+#ifndef _JSONSAX_HPP_
+#define _JSONSAX_HPP_
+
+class IJSLXBackend
+{
+public:
+    virtual bool Eof() = 0;
+    virtual char GetChar() = 0;
+
+    virtual bool OnStartObject()
+    {
+        return true;
+    }
+    virtual bool OnStopObject()
+    {
+        return true;
+    }
+    virtual bool OnStartArray()
+    {
+        return true;
+    }
+    virtual bool OnStopArray()
+    {
+        return true;
+    }
+    virtual bool OnStartNumber()
+    {
+        return true;
+    }
+    virtual bool OnStopNumber()
+    {
+        return true;
+    }
+    virtual bool OnStartString()
+    {
+        return true;
+    }
+    virtual bool OnStopString()
+    {
+        return true;
+    }
+    virtual bool OnNull()
+    {
+        return true;
+    }
+    virtual bool OnTrue()
+    {
+        return true;
+    }
+    virtual bool OnFalse()
+    {
+        return true;
+    }
+    virtual void OnError(const char* err)
+    {
+        (void)err;
+    }
+};
+
+class JSLXParser
+{
+public:
+    JSLXParser(IJSLXBackend* pBackend);
+    bool Parse();
+protected:
+    bool _GetNextChar();
+    bool TrimWhiteSpace();
+    bool GetNextChar(bool trim = true);
+    bool ParseValue();
+    bool ParseObject();
+    bool ParsePair();
+    bool ParseArray();
+    bool ParseString();
+    bool ParseNumber();
+    bool ParseNull();
+    bool ParseTrue();
+    bool ParseFalse();
+    bool ParseConstant(const char* constant);
+private:
+    IJSLXBackend* m_pBackend;
+    char          m_lastChar;
+};
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests.cpp	Sat Oct 06 21:08:55 2012 -0400
@@ -0,0 +1,138 @@
+#include "jsonsax.hpp"
+
+#include <iostream>
+#include <assert.h>
+
+// #define DEBUGTEST
+class JSLXTester : public IJSLXBackend
+{
+public:
+	explicit JSLXTester(const char* json)
+	: m_json(json)
+	{}
+
+	void debug(const char* data)
+	{
+#ifdef DEBUGTEST
+		std::cerr << std::endl << "-*- " << data << " -*-" << std::endl;
+#endif
+	}
+
+	virtual bool Eof()
+	{
+		return *m_json == '\0';
+	}
+
+	virtual char GetChar()
+	{
+		assert(!Eof());
+#ifdef DEBUGTEST
+		std::cerr.put(*m_json == ' ' ? '_' : *m_json);
+#endif
+		return *m_json++;
+	}
+
+	virtual bool OnStartObject()
+	{
+		assert(m_json[-1] == '{');
+		debug("OnStartObject");
+		return true;
+	}
+
+	virtual bool OnStopObject()
+	{
+		assert(m_json[-1] == '}');
+		debug("OnStopObject");
+		return true;
+	}
+
+	virtual bool OnStartArray()
+	{
+		assert(m_json[-1] == '[');
+		debug("OnStartArray");
+		return true;
+	}
+
+	virtual bool OnStopArray()
+	{
+		assert(m_json[-1] == ']');
+		debug("OnStopArray");
+		return true;
+	}
+
+	virtual bool OnStartNumber()
+	{
+		// assert(m_json[-1] == ']');
+		debug("OnStartNumber");
+		return true;
+	}
+
+	virtual bool OnStopNumber()
+	{
+		// assert(m_json[-1] == ']');
+		debug("OnStopNumber");
+		return true;
+	}
+
+	virtual bool OnStartString()
+	{
+		assert(m_json[-1] == '"');
+		debug("OnStartString");
+		return true;
+	}
+
+	virtual bool OnStopString()
+	{
+		assert(m_json[-1] == '"');
+		debug("OnStopString");
+		return true;
+	}
+
+	virtual bool OnNull()
+	{
+		// assert(m_json[-1] == ']');
+		debug("OnNull");
+		return true;
+	}
+
+	virtual bool OnTrue()
+	{
+		// assert(m_json[-1] == ']');
+		debug("OnTrue");
+		return true;
+	}
+
+	virtual bool OnFalse()
+	{
+		// assert(m_json[-1] == ']');
+		debug("OnFalse");
+		return true;
+	}
+
+	virtual void OnError(const char* err)
+	{
+		debug("Error");
+		debug(err);
+	}
+
+	bool Check()
+	{
+		return Eof();
+	}
+
+private:
+	const char* m_json;
+};
+
+int main()
+{
+	const char json[] =
+		"{ \"hello\" : [ 11, 12 ,1"
+		"3 ], \n  \"world\" : -3.45e2 }";
+	JSLXTester tester(json);
+	JSLXParser parser(&tester);
+	assert(parser.Parse());
+	assert(tester.Check());
+
+	return 0;
+}