# HG changeset patch # User Fabien Ninoles # Date 1349572135 14400 # Node ID d187e7fc99707187dc40e1ba9ccedc3b5f6daf5a First version, c++. diff -r 000000000000 -r d187e7fc9970 Makefile --- /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 diff -r 000000000000 -r d187e7fc9970 jsonsax.cpp --- /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 + +#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 + diff -r 000000000000 -r d187e7fc9970 jsonsax.hpp --- /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 diff -r 000000000000 -r d187e7fc9970 tests.cpp --- /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 +#include + +// #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; +}