--- /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