First version, c++.
--- /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;
+}