diff -r d187e7fc9970 -r ef7abb48573b jsonsax.c --- /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 +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 + +#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