jsonsax.c
changeset 1 ef7abb48573b
parent 0 d187e7fc9970
--- /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