Convert to C api. default tip
authorFabien Ninoles <fabien@tzone.org>
Sun, 07 Oct 2012 14:30:52 -0400
changeset 1 ef7abb48573b
parent 0 d187e7fc9970
Convert to C api.
Makefile
jsonsax.c
jsonsax.cpp
jsonsax.h
tests.c
tests.cpp
--- a/Makefile	Sat Oct 06 21:08:55 2012 -0400
+++ b/Makefile	Sun Oct 07 14:30:52 2012 -0400
@@ -1,6 +1,7 @@
 all: tests
 
-CC=g++
+clean:
+	rm -f *.o tests
 
 tests: tests.o jsonsax.o
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jsonsax.c	Sun Oct 07 14:30:52 2012 -0400
@@ -0,0 +1,341 @@
+/*
+Copyright (c) Fabien Ninoles <fabien@tzone.org>
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+3. Neither the name of the University nor the names of its contributors
+   may be used to endorse or promote products derived from this software
+   without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+*/
+
+#include "jsonsax.h"
+
+#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'
+
+
+struct _jsonsax_state
+{
+  struct jsonsax_backend  m_backend;
+  char                    m_lastchar;
+  void*                   m_data;
+};
+
+#define JSONSAX_GETCHAR(state) \
+  (state)->m_backend.m_getchar((state)->m_data)
+#define JSONSAX_ONERROR(state, err) \
+  (state)->m_backend.m_onerror((state)->m_data, err)
+#define JSONSAX_ONSTART(state,type) \
+  (state)->m_backend.m_onstart((state)->m_data, type)
+#define JSONSAX_ONSTOP(state,type) \
+  (state)->m_backend.m_onstop((state)->m_data, type)
+
+static int jsonsax_parsevalue(struct _jsonsax_state* state);
+
+static int _jsonsax_getc(struct _jsonsax_state* state)
+{
+  int c = JSONSAX_GETCHAR(state);
+  if (c > 0)
+  {
+    state->m_lastchar = c;
+    return 0;
+  };
+  return -1;
+}
+
+static int _jsonsax_trim(struct _jsonsax_state* state)
+{
+  do
+    {
+      switch(state->m_lastchar)
+        {
+        CASE_IS_SPACE:
+          break;
+        default:
+          return 0;
+        }
+    }
+  while(_jsonsax_getc(state) == 0);
+  return -1;
+}
+
+static int jsonsax_getc(struct _jsonsax_state* state)
+{
+  int err = _jsonsax_getc(state);
+  if (err == 0)
+  {
+    return _jsonsax_trim(state);
+  }
+  return err;
+}
+
+int jsonsax_parsearray(struct _jsonsax_state* state)
+{
+    assert(state->m_lastchar == '[');
+    int err = jsonsax_getc(state);
+    if (err != 0) return err;
+    while(jsonsax_parsevalue(state))
+    {
+        switch(state->m_lastchar)
+        {
+        case ',':
+          jsonsax_getc(state);
+          break;
+        case ']':
+          JSONSAX_ONSTOP(state, EJSONSAX_ARRAY);
+          jsonsax_getc(state);
+          return 0;
+        default:
+          JSONSAX_ONERROR(state, -1);
+          return -1;
+        }
+    }
+    if (state->m_lastchar != ']')
+    {
+      JSONSAX_ONERROR(state, -1);
+      return -1;
+    }
+    JSONSAX_ONSTOP(state, EJSONSAX_ARRAY);
+    jsonsax_getc(state);
+    return 0;
+}
+
+int jsonsax_parsestring(struct _jsonsax_state* state)
+{
+    assert(state->m_lastchar == '"');
+    JSONSAX_ONSTART(state, EJSONSAX_STRING);
+    while(jsonsax_getc(state) == 0)
+    {
+        switch(state->m_lastchar)
+        {
+        case '\\':
+          jsonsax_getc(state);
+          break;
+        case '"':
+          JSONSAX_ONSTOP(state, EJSONSAX_STRING);
+          jsonsax_getc(state);
+          return 0;
+        }
+    }
+    return -1;
+}
+
+int jsonsax_parsenumber(struct _jsonsax_state* state)
+{
+    assert(state->m_lastchar == '-' ||
+           state->m_lastchar == '0' ||
+           state->m_lastchar == '1' ||
+           state->m_lastchar == '2' ||
+           state->m_lastchar == '3' ||
+           state->m_lastchar == '4' ||
+           state->m_lastchar == '5' ||
+           state->m_lastchar == '6' ||
+           state->m_lastchar == '7' ||
+           state->m_lastchar == '8' ||
+           state->m_lastchar == '9');
+
+    JSONSAX_ONSTART(state, EJSONSAX_NUMBER);
+    while(jsonsax_getc(state) == 0)
+    {
+        switch(state->m_lastchar)
+        {
+            // TODO: lookup for correct number format.
+        case 'e':
+        case '-':
+        case '.':
+        CASE_IS_DIGIT:
+            break;
+        default:
+          JSONSAX_ONSTOP(state, EJSONSAX_NUMBER);
+          return -1;
+        }
+    }
+    return 0;
+}
+
+int jsonsax_parseconstant(struct _jsonsax_state* state,
+                          const char* constant,
+                          enum EJSONSAX_TYPE etype)
+{
+    assert(state->m_lastchar == constant[0]);
+    ++constant;
+    while(jsonsax_getc(state) == 0)
+    {
+      if (*constant == '\0') break;
+      if (*constant != state->m_lastchar) break;
+      constant++;
+    }
+
+    if (*constant == '\0')
+    {
+      JSONSAX_ONSTOP(state, etype);
+      return 0;
+    }
+    else
+    {
+      JSONSAX_ONERROR(state, -1);
+      return -1;
+    }
+}
+
+int jsonsax_parsenull(struct _jsonsax_state* state)
+{
+  return jsonsax_parseconstant(state, "null", EJSONSAX_NULL);
+}
+
+int jsonsax_parsetrue(struct _jsonsax_state* state)
+{
+  return jsonsax_parseconstant(state, "true", EJSONSAX_TRUE);
+}
+
+int jsonsax_parsefalse(struct _jsonsax_state* state)
+{
+  return jsonsax_parseconstant(state, "false", EJSONSAX_FALSE);
+}
+
+int jsonsax_parsepair(struct _jsonsax_state* state)
+{
+    if (state->m_lastchar != '"')
+    {
+      JSONSAX_ONERROR(state, -1);
+      return -1;
+    }
+    if (jsonsax_parsestring(state) != 0)
+    {
+      JSONSAX_ONERROR(state, -1);
+      return -1;
+    }
+    if (state->m_lastchar != ':')
+    {
+      JSONSAX_ONERROR(state, -1);
+      return -1;
+    }
+    if (jsonsax_getc(state) != 0)
+    {
+      JSONSAX_ONERROR(state, -1);
+      return -1;
+    }
+    return jsonsax_parsevalue(state);
+}
+
+static int jsonsax_parseobject(struct _jsonsax_state* state)
+{
+    assert(state->m_lastchar == '{');
+    JSONSAX_ONSTART(state, EJSONSAX_OBJECT);
+    jsonsax_getc(state);
+    while(jsonsax_parsepair(state) == 0)
+    {
+        switch(state->m_lastchar)
+        {
+        case ',':
+          jsonsax_getc(state);
+          break;
+        case '}':
+          JSONSAX_ONSTOP(state, EJSONSAX_OBJECT);
+          jsonsax_getc(state);
+          return 0;
+        default:
+          JSONSAX_ONERROR(state, -1);
+          return -1;
+        }
+    }
+    if (state->m_lastchar != '}')
+    {
+      JSONSAX_ONERROR(state, -1);
+      return -1;
+    }
+    JSONSAX_ONSTOP(state, EJSONSAX_OBJECT);
+    jsonsax_getc(state);
+    return 0;
+}
+
+static int jsonsax_parsevalue(struct _jsonsax_state* state)
+{
+    switch(state->m_lastchar)
+    {
+    case '[':
+        return jsonsax_parsearray(state);
+    case '{':
+        return jsonsax_parseobject(state);
+    case '"':
+        return jsonsax_parsestring(state);
+    case '-':
+    CASE_IS_DIGIT:
+        return jsonsax_parsenumber(state);
+    case 'n':
+        return jsonsax_parsenull(state);
+    case 't':
+        return jsonsax_parsetrue(state);
+    case 'f':
+        return jsonsax_parsefalse(state);
+    default:
+        return -1;
+    }
+}
+
+static int default_onevent(void* data, enum EJSONSAX_TYPE type)
+{
+  return 0;
+}
+
+static void default_onerror(void* data, int error)
+{
+  (void)data;
+  (void)error;
+}
+
+int jsonsax_parse(struct jsonsax_backend* backend, void* data)
+{
+  assert(backend != 0);
+  assert(backend->m_getchar != 0);
+  struct _jsonsax_state state;
+  state.m_backend.m_getchar = backend->m_getchar;
+  state.m_backend.m_onstart = backend->m_onstart ? backend->m_onstart : default_onevent;
+  state.m_backend.m_onstop  = backend->m_onstop  ? backend->m_onstop  : default_onevent;
+  state.m_backend.m_onerror = backend->m_onerror ? backend->m_onerror : default_onerror;
+  state.m_data = data;
+  state.m_lastchar = '\0';
+  int err = jsonsax_getc(&state);
+  if (err != 0) return err;
+  err = jsonsax_parsevalue(&state);
+  return err;
+}
+
+#undef CASE_IS_DIGIT
+#undef CASE_IS_SPACE
--- a/jsonsax.cpp	Sat Oct 06 21:08:55 2012 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,276 +0,0 @@
-#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.h	Sun Oct 07 14:30:52 2012 -0400
@@ -0,0 +1,65 @@
+/*
+Copyright (c) Fabien Ninoles <fabien@tzone.org>
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+3. Neither the name of the University nor the names of its contributors
+   may be used to endorse or promote products derived from this software
+   without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+*/
+
+#ifndef _JSONSAX_H_
+#define _JSONSAX_H_
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+enum EJSONSAX_TYPE {
+  EJSONSAX_OBJECT,
+  EJSONSAX_ARRAY,
+  EJSONSAX_STRING,
+  EJSONSAX_NUMBER,
+  EJSONSAX_NULL,
+  EJSONSAX_TRUE,
+  EJSONSAX_FALSE
+};
+
+struct jsonsax_backend
+{
+  int  (*m_getchar)(void* data);
+  int  (*m_onstart)(void* data, enum EJSONSAX_TYPE type);
+  int  (*m_onstop)(void* data, enum EJSONSAX_TYPE type);
+  void (*m_onerror)(void* data, int error);
+};
+
+// Note: the structure members would be copied before
+// being used.  You cannot change the functions called after
+// it.
+int jsonsax_parse(struct jsonsax_backend* backend, void* data);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests.c	Sun Oct 07 14:30:52 2012 -0400
@@ -0,0 +1,127 @@
+#include "jsonsax.h"
+
+#include <stdio.h>
+#include <assert.h>
+#include <string.h>
+#include <stdarg.h>
+
+#define DEBUGTEST
+
+void debug(const char* msg, ...)
+{
+#ifdef DEBUGTEST
+    fprintf(stderr, "\n-*- ");
+    va_list ap;
+    va_start(ap, msg);
+    vfprintf(stderr, msg, ap);
+    va_end(ap);
+    fprintf(stderr, " -*-\n");
+#else
+    (void)msg;
+#endif
+}
+
+const char* TEST_JSONTYPE_STR[] = {
+    "EJSONSAX_OBJECT",
+    "EJSONSAX_ARRAY",
+    "EJSONSAX_STRING",
+    "EJSONSAX_NUMBER",
+    "EJSONSAX_NULL",
+    "EJSONSAX_TRUE",
+    "EJSONSAX_FALSE"
+};
+
+struct test_data
+{
+    const char* m_str;
+};
+
+int test_getchar(void* data)
+{
+    assert(data);
+    struct test_data* t = (struct test_data*)(data);
+    if (t->m_str[0] == 0) return -1;
+#ifdef DEBUGTEST
+    char c = t->m_str[0];
+    fprintf(stderr, "%c", c == ' ' ? '_' : c);
+#endif
+    return *(t->m_str++);
+}
+
+int test_on_start(void* data, enum EJSONSAX_TYPE etype)
+{
+    assert(data);
+    struct test_data* t = (struct test_data*)(data);
+    debug("start %s", TEST_JSONTYPE_STR[etype]);
+
+    switch (etype)
+    {
+case EJSONSAX_OBJECT:
+    assert(t->m_str[-1] == '{');
+    break;
+case EJSONSAX_ARRAY:
+    assert(t->m_str[-1] == '[');
+    break;
+case EJSONSAX_STRING:
+    assert(t->m_str[-1] == '"');
+    break;
+case EJSONSAX_NUMBER:
+    break;
+case EJSONSAX_NULL:
+case EJSONSAX_TRUE:
+case EJSONSAX_FALSE:
+    assert(0);
+}
+    return 0;
+}
+
+int test_on_stop(void* data, enum EJSONSAX_TYPE etype)
+{
+    assert(data);
+    struct test_data* t = (struct test_data*)(data);
+    debug("stop %s", TEST_JSONTYPE_STR[etype]);
+
+    switch (etype)
+    {
+case EJSONSAX_OBJECT:
+    assert(t->m_str[-1] == '}');
+    break;
+case EJSONSAX_ARRAY:
+    assert(t->m_str[-1] == ']');
+    break;
+case EJSONSAX_STRING:
+    assert(t->m_str[-1] == '"');
+    break;
+case EJSONSAX_NUMBER:
+case EJSONSAX_NULL:
+case EJSONSAX_TRUE:
+case EJSONSAX_FALSE:
+    break;
+}
+    return 0;
+}
+
+void test_on_error(void* data, int error)
+{
+    assert(data);
+    debug("Error %d", error);
+}
+
+int main()
+{
+    const char json[] =
+        "{ \"hello\" : [ 11, 12 ,1"
+        "3 ], \n  \"world\" : -3.45e2 }";
+    struct test_data data;
+    data.m_str = json;
+    struct jsonsax_backend tester;
+    memset(&tester, 0, sizeof(tester));
+    tester.m_getchar = &test_getchar;
+    tester.m_onstart = &test_on_start;
+    tester.m_onstop  = &test_on_stop;
+
+    int err = jsonsax_parse(&tester, &data);
+    assert(err == 0);
+
+    return err;
+};
--- a/tests.cpp	Sat Oct 06 21:08:55 2012 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,138 +0,0 @@
-#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;
-}