From 60509e2850a1f4ac8324acc282bc307378d4980d Mon Sep 17 00:00:00 2001 From: "Serge A. Zaitsev" Date: Tue, 16 Nov 2010 15:41:49 +0200 Subject: [PATCH] Design: rewritten using parser structure --- Makefile | 2 +- demo.c | 11 ++- jsmn.c | 226 ++++++++++++++++++++++++------------------------------- jsmn.h | 27 +++++-- 4 files changed, 130 insertions(+), 136 deletions(-) diff --git a/Makefile b/Makefile index cda64f5..dc90eab 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -CFLAGS=-Wall -std=c89 +CFLAGS=-Wall -std=c89 -g OBJS=jsmn.o demo.o diff --git a/demo.c b/demo.c index a9dfa41..0c2d18b 100644 --- a/demo.c +++ b/demo.c @@ -50,7 +50,6 @@ static void jsmn_dump_obj(jsontok_t *obj, const char *js) { int main(int argc, char *argv[]) { int i; int r; - int errpos; jsontok_t tokens[NUM_TOKENS]; FILE *f; int filesize = 0; @@ -85,14 +84,18 @@ int main(int argc, char *argv[]) { fclose(f); - r = jsmn_parse((unsigned char *) js, tokens, NUM_TOKENS, &errpos); + jsmn_parser parser; + + jsmn_init_parser(&parser, js, tokens, NUM_TOKENS); + + r = jsmn_parse(&parser); if (r < 0) { - printf("error %d at pos %d: %s\n", r, errpos, &js[errpos]); + printf("error %d at pos %d: %s\n", r, parser.pos, &js[parser.pos]); exit(EXIT_FAILURE); } for (i = 0; istart] != '\"') { - return -1; - } - - /* Skip starting quote */ - token->start++; - - for (p = &js[token->start]; *p != '\0'; p++) { - /* Quote: end of string */ - if (*p == '\"') { - token->end = p - js; - return 0; - } - - /* Backslash: Quoted symbol expected */ - if (*p == '\\') { - p++; - switch (*p) { - /* Allowed escaped symbols */ - case '\"': case '/' : case '\\' : case 'b' : - case 'f' : case 'r' : case 'n' : case 't' : - break; - /* Allows escaped symbol \uXXXX */ - case 'u': - /* TODO */ - break; - /* Unexpected symbol */ - default: - return -1; - } - } - } - return -1; -} +void jsmn_init_parser(jsmn_parser *parser, const char *js, + jsontok_t *tokens, size_t num_tokens) { + unsigned int i; -static int jsmn_parse_primitive(const unsigned char *js, jsontok_t *token) { - const unsigned char *p; + parser->js = js; + parser->pos = 0; + parser->tokens = tokens; + parser->num_tokens = num_tokens; - for (p = &js[token->start]; *p != '\0'; p++) { - switch (*p) { - case '\t' : case '\r' : case '\n' : case ' ' : - case ',' : case ']' : case '}' : - token->end = p - js; - return 0; - } - if (*p < 32 || *p >= 127) { - return -1; - } + for (i = 0; i < parser->num_tokens; i++) { + parser->tokens[i].start = -1; + parser->tokens[i].end = -1; + parser->tokens[i].type = JSON_OTHER; } - return -1; } -static jsontok_t *jsmn_token_start(struct jsmn_params *params, jsontype_t type, int pos) { +jsontok_t *jsmn_start_token(jsmn_parser *parser, jsontype_t type) { unsigned int i; - jsontok_t *tokens = params->tokens; - for (i = 0; inum_tokens; i++) { + jsontok_t *tokens = parser->tokens; + for (i = 0; inum_tokens; i++) { if (tokens[i].start == -1 && tokens[i].end == -1) { - tokens[i].start = pos; + tokens[i].start = parser->pos; tokens[i].type = type; return &tokens[i]; } @@ -80,98 +31,121 @@ static jsontok_t *jsmn_token_start(struct jsmn_params *params, jsontype_t type, return NULL; } -static jsontok_t *jsmn_token_end(struct jsmn_params *params, jsontype_t type, int pos) { +jsontok_t *jsmn_end_token(jsmn_parser *parser, jsontype_t type) { int i; - jsontok_t *tokens = params->tokens; - for (i = params->num_tokens - 1; i>= 0; i--) { + jsontok_t *tokens = parser->tokens; + for (i = parser->num_tokens - 1; i>= 0; i--) { if (tokens[i].type == type && tokens[i].start != -1 && tokens[i].end == -1) { - tokens[i].end = pos; + tokens[i].end = parser->pos; return &tokens[i]; } } return NULL; } -int jsmn_parse(const unsigned char *js, jsontok_t *tokens, size_t num_tokens, int *errpos) { +static int jsmn_parse_primitive(jsmn_parser *parser) { + const char *js; + jsontok_t *token; -#define jsmn_return(error) \ - do { \ - if ((errpos) != NULL) { \ - *(errpos) = (p - js); \ - } \ - return (error); \ - } while (0) + js = parser->js; -#define jsmn_assert(cond, error) \ - if (!(cond)) { \ - jsmn_return(error); \ + token = jsmn_start_token(parser, JSON_NUMBER); + + for (; js[parser->pos] != '\0'; parser->pos++) { + switch (js[parser->pos]) { + case '\t' : case '\r' : case '\n' : case ' ' : + case ',' : case ']' : case '}' : + token->end = parser->pos; + parser->pos--; + return JSMN_SUCCESS; + } + if (js[parser->pos] < 32 || js[parser->pos] >= 127) { + return JSMN_ERROR_INVAL; + } } + return JSMN_ERROR_PART; +} - struct jsmn_params params; - int r; - unsigned int i; - const unsigned char *p; - jsontype_t type; - jsontok_t *cur_token; +static int jsmn_parse_string(jsmn_parser *parser) { + const char *js; + jsontok_t *token; + + js = parser->js; - params.num_tokens = num_tokens; - params.tokens = tokens; - params.errpos = errpos; + /* Check if string begins from a quote */ + if (js[parser->pos] != '\"') { + return JSMN_ERROR_INVAL; + } - for (i = 0; ipos++; + + token = jsmn_start_token(parser, JSON_STRING); + /* Skip starting quote */ + for (; js[parser->pos] != '\0'; parser->pos++) { + char c = js[parser->pos]; + + /* Quote: end of string */ + if (c == '\"') { + token->end = parser->pos; + return JSMN_SUCCESS; + } + + /* Backslash: Quoted symbol expected */ + if (c == '\\') { + parser->pos++; + switch (js[parser->pos]) { + /* Allowed escaped symbols */ + case '\"': case '/' : case '\\' : case 'b' : + case 'f' : case 'r' : case 'n' : case 't' : + break; + /* Allows escaped symbol \uXXXX */ + case 'u': + /* TODO */ + break; + /* Unexpected symbol */ + default: + return JSMN_ERROR_INVAL; + } + } } + return JSMN_ERROR_PART; +} + - for (p = js; *p != '\0'; ) { - switch (*p) { +jsmnerr_t jsmn_parse(jsmn_parser *parser) { + const char *js; + jsontype_t type; + jsontok_t *token; + + js = parser->js; + + for (; js[parser->pos] != '\0'; parser->pos++) { + char c; + c = js[parser->pos]; + switch (c) { case '{': case '[': - type = (*p == '{' ? JSON_OBJECT : JSON_ARRAY); - cur_token = jsmn_token_start(¶ms, type, p - js); - jsmn_assert(cur_token != NULL, JSMN_ERROR_NOMEM); + type = (c == '{' ? JSON_OBJECT : JSON_ARRAY); + token = jsmn_start_token(parser, type); break; - case '}' : case ']': - type = (*p == '}' ? JSON_OBJECT : JSON_ARRAY); - cur_token = jsmn_token_end(¶ms, type, p - js + 1); - jsmn_assert(cur_token != NULL, JSMN_ERROR_PART); + case '}': case ']': + type = (c == '}' ? JSON_OBJECT : JSON_ARRAY); + token = jsmn_end_token(parser, type); break; - case '-': case '0': case '1' : case '2': case '3' : case '4': case '5': case '6': case '7' : case '8': case '9': - cur_token = jsmn_token_start(¶ms, JSON_NUMBER, p - js); - jsmn_assert(cur_token != NULL, JSMN_ERROR_NOMEM); - r = jsmn_parse_primitive(js, cur_token); - jsmn_assert(r == 0, JSMN_ERROR_INVAL); - p = &js[cur_token->end] - 1; - break; case 't': case 'f': case 'n' : - cur_token = jsmn_token_start(¶ms, JSON_OTHER, p - js); - jsmn_assert(cur_token != NULL, JSMN_ERROR_NOMEM); - r = jsmn_parse_primitive(js, cur_token); - jsmn_assert(r == 0, JSMN_ERROR_INVAL); - p = &js[cur_token->end] - 1; + jsmn_parse_primitive(parser); break; - case '\"': - cur_token = jsmn_token_start(¶ms, JSON_STRING, p - js); - jsmn_assert(cur_token != NULL, JSMN_ERROR_NOMEM); - r = jsmn_parse_string(js, cur_token); - jsmn_assert(r == 0, JSMN_ERROR_INVAL); - p = &js[cur_token->end]; + jsmn_parse_string(parser); break; - case '\t' : case '\r' : case '\n' : case ':' : case ',': case ' ': break; - default: - jsmn_return(JSMN_ERROR_INVAL); + return JSMN_ERROR_INVAL; } - p++; } - if (errpos != NULL) *errpos = 0; - return 0; -#undef jsmn_return -#undef jsmn_assert + return JSMN_SUCCESS; } diff --git a/jsmn.h b/jsmn.h index 408c538..f045a08 100644 --- a/jsmn.h +++ b/jsmn.h @@ -11,10 +11,10 @@ */ typedef enum { JSON_OTHER = 0, - JSON_OBJECT, - JSON_ARRAY, - JSON_STRING, - JSON_NUMBER + JSON_OBJECT = 1, + JSON_ARRAY = 2, + JSON_STRING = 3, + JSON_NUMBER = 4 } jsontype_t; typedef enum { @@ -36,10 +36,27 @@ typedef struct { int end; } jsontok_t; +/** + * JSON parser. Contains an array of token blocks available. Also stores + * the string being parsed now and current position in that string + */ +typedef struct { + const char *js; + unsigned int pos; + size_t num_tokens; + jsontok_t *tokens; +} jsmn_parser; + +/** + * Create JSON parser over an array of tokens + */ +void jsmn_init_parser(jsmn_parser *parser, const char *js, + jsontok_t *tokens, size_t num_tokens); + /** * Run JSON parser. It parses a JSON data string into and array of tokens, each describing * a single JSON object. */ -int jsmn_parse(const unsigned char *js, jsontok_t *tokens, size_t num_tokens, int *errpos); +jsmnerr_t jsmn_parse(jsmn_parser *parser); #endif /* __JSMN_H_ */