Design: rewritten using parser structure

master
Serge A. Zaitsev 14 years ago
parent a2755a7595
commit 60509e2850

@ -1,4 +1,4 @@
CFLAGS=-Wall -std=c89 CFLAGS=-Wall -std=c89 -g
OBJS=jsmn.o demo.o OBJS=jsmn.o demo.o

@ -50,7 +50,6 @@ static void jsmn_dump_obj(jsontok_t *obj, const char *js) {
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
int i; int i;
int r; int r;
int errpos;
jsontok_t tokens[NUM_TOKENS]; jsontok_t tokens[NUM_TOKENS];
FILE *f; FILE *f;
int filesize = 0; int filesize = 0;
@ -85,14 +84,18 @@ int main(int argc, char *argv[]) {
fclose(f); 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) { 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); exit(EXIT_FAILURE);
} }
for (i = 0; i<NUM_TOKENS; i++) { for (i = 0; i<NUM_TOKENS; i++) {
jsmn_dump_obj(&tokens[i], js); jsmn_dump_obj(&parser.tokens[i], js);
} }
free(js); free(js);

226
jsmn.c

@ -2,77 +2,28 @@
#include "jsmn.h" #include "jsmn.h"
struct jsmn_params { void jsmn_init_parser(jsmn_parser *parser, const char *js,
jsontok_t *tokens; jsontok_t *tokens, size_t num_tokens) {
size_t num_tokens; unsigned int i;
int *errpos;
};
/**
* Read the string from JSON data. Store string
*/
static int jsmn_parse_string(const unsigned char *js, jsontok_t *token) {
const unsigned char *p;
/* Check if string begins from a quote */
if (js[token->start] != '\"') {
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;
}
static int jsmn_parse_primitive(const unsigned char *js, jsontok_t *token) { parser->js = js;
const unsigned char *p; parser->pos = 0;
parser->tokens = tokens;
parser->num_tokens = num_tokens;
for (p = &js[token->start]; *p != '\0'; p++) { for (i = 0; i < parser->num_tokens; i++) {
switch (*p) { parser->tokens[i].start = -1;
case '\t' : case '\r' : case '\n' : case ' ' : parser->tokens[i].end = -1;
case ',' : case ']' : case '}' : parser->tokens[i].type = JSON_OTHER;
token->end = p - js;
return 0;
}
if (*p < 32 || *p >= 127) {
return -1;
}
} }
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; unsigned int i;
jsontok_t *tokens = params->tokens; jsontok_t *tokens = parser->tokens;
for (i = 0; i<params->num_tokens; i++) { for (i = 0; i<parser->num_tokens; i++) {
if (tokens[i].start == -1 && tokens[i].end == -1) { if (tokens[i].start == -1 && tokens[i].end == -1) {
tokens[i].start = pos; tokens[i].start = parser->pos;
tokens[i].type = type; tokens[i].type = type;
return &tokens[i]; return &tokens[i];
} }
@ -80,98 +31,121 @@ static jsontok_t *jsmn_token_start(struct jsmn_params *params, jsontype_t type,
return NULL; 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; int i;
jsontok_t *tokens = params->tokens; jsontok_t *tokens = parser->tokens;
for (i = params->num_tokens - 1; i>= 0; i--) { for (i = parser->num_tokens - 1; i>= 0; i--) {
if (tokens[i].type == type && tokens[i].start != -1 && tokens[i].end == -1) { 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 &tokens[i];
} }
} }
return NULL; 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) \ js = parser->js;
do { \
if ((errpos) != NULL) { \
*(errpos) = (p - js); \
} \
return (error); \
} while (0)
#define jsmn_assert(cond, error) \ token = jsmn_start_token(parser, JSON_NUMBER);
if (!(cond)) { \
jsmn_return(error); \ 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; static int jsmn_parse_string(jsmn_parser *parser) {
unsigned int i; const char *js;
const unsigned char *p; jsontok_t *token;
jsontype_t type;
jsontok_t *cur_token; js = parser->js;
params.num_tokens = num_tokens; /* Check if string begins from a quote */
params.tokens = tokens; if (js[parser->pos] != '\"') {
params.errpos = errpos; return JSMN_ERROR_INVAL;
}
for (i = 0; i<num_tokens; i++) { parser->pos++;
tokens[i].start = tokens[i].end = -1;
tokens[i].type = JSON_OTHER; 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'; ) { jsmnerr_t jsmn_parse(jsmn_parser *parser) {
switch (*p) { 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 '[': case '{': case '[':
type = (*p == '{' ? JSON_OBJECT : JSON_ARRAY); type = (c == '{' ? JSON_OBJECT : JSON_ARRAY);
cur_token = jsmn_token_start(&params, type, p - js); token = jsmn_start_token(parser, type);
jsmn_assert(cur_token != NULL, JSMN_ERROR_NOMEM);
break; break;
case '}' : case ']': case '}': case ']':
type = (*p == '}' ? JSON_OBJECT : JSON_ARRAY); type = (c == '}' ? JSON_OBJECT : JSON_ARRAY);
cur_token = jsmn_token_end(&params, type, p - js + 1); token = jsmn_end_token(parser, type);
jsmn_assert(cur_token != NULL, JSMN_ERROR_PART);
break; break;
case '-': case '0': case '1' : case '2': case '3' : case '4': case '-': case '0': case '1' : case '2': case '3' : case '4':
case '5': case '6': case '7' : case '8': case '9': case '5': case '6': case '7' : case '8': case '9':
cur_token = jsmn_token_start(&params, 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' : case 't': case 'f': case 'n' :
cur_token = jsmn_token_start(&params, JSON_OTHER, p - js); jsmn_parse_primitive(parser);
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; break;
case '\"': case '\"':
cur_token = jsmn_token_start(&params, JSON_STRING, p - js); jsmn_parse_string(parser);
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];
break; break;
case '\t' : case '\r' : case '\n' : case ':' : case ',': case ' ': case '\t' : case '\r' : case '\n' : case ':' : case ',': case ' ':
break; break;
default: default:
jsmn_return(JSMN_ERROR_INVAL); return JSMN_ERROR_INVAL;
} }
p++;
} }
if (errpos != NULL) *errpos = 0; return JSMN_SUCCESS;
return 0;
#undef jsmn_return
#undef jsmn_assert
} }

@ -11,10 +11,10 @@
*/ */
typedef enum { typedef enum {
JSON_OTHER = 0, JSON_OTHER = 0,
JSON_OBJECT, JSON_OBJECT = 1,
JSON_ARRAY, JSON_ARRAY = 2,
JSON_STRING, JSON_STRING = 3,
JSON_NUMBER JSON_NUMBER = 4
} jsontype_t; } jsontype_t;
typedef enum { typedef enum {
@ -36,10 +36,27 @@ typedef struct {
int end; int end;
} jsontok_t; } 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 * Run JSON parser. It parses a JSON data string into and array of tokens, each describing
* a single JSON object. * 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_ */ #endif /* __JSMN_H_ */

Loading…
Cancel
Save