jsmn (pronounced like 'jasmine') is a minimalistic JSON parser in C. It can be easily integrated into resource-limited or embedded projects. For more information visit http://zserge.bitbucket.org/jsmn.html

Committer:
trisjph
Date:
Wed Jan 22 15:21:09 2014 +0000
Revision:
0:625bfa0ff591
Initial commit

Who changed what in which revision?

UserRevisionLine numberNew contents of line
trisjph 0:625bfa0ff591 1 #include <stdlib.h>
trisjph 0:625bfa0ff591 2
trisjph 0:625bfa0ff591 3 #include "jsmn.h"
trisjph 0:625bfa0ff591 4
trisjph 0:625bfa0ff591 5 /**
trisjph 0:625bfa0ff591 6 * Allocates a fresh unused token from the token pull.
trisjph 0:625bfa0ff591 7 */
trisjph 0:625bfa0ff591 8 static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser,
trisjph 0:625bfa0ff591 9 jsmntok_t *tokens, size_t num_tokens) {
trisjph 0:625bfa0ff591 10 jsmntok_t *tok;
trisjph 0:625bfa0ff591 11 if (parser->toknext >= num_tokens) {
trisjph 0:625bfa0ff591 12 return NULL;
trisjph 0:625bfa0ff591 13 }
trisjph 0:625bfa0ff591 14 tok = &tokens[parser->toknext++];
trisjph 0:625bfa0ff591 15 tok->start = tok->end = -1;
trisjph 0:625bfa0ff591 16 tok->size = 0;
trisjph 0:625bfa0ff591 17 #ifdef JSMN_PARENT_LINKS
trisjph 0:625bfa0ff591 18 tok->parent = -1;
trisjph 0:625bfa0ff591 19 #endif
trisjph 0:625bfa0ff591 20 return tok;
trisjph 0:625bfa0ff591 21 }
trisjph 0:625bfa0ff591 22
trisjph 0:625bfa0ff591 23 /**
trisjph 0:625bfa0ff591 24 * Fills token type and boundaries.
trisjph 0:625bfa0ff591 25 */
trisjph 0:625bfa0ff591 26 static void jsmn_fill_token(jsmntok_t *token, jsmntype_t type,
trisjph 0:625bfa0ff591 27 int start, int end) {
trisjph 0:625bfa0ff591 28 token->type = type;
trisjph 0:625bfa0ff591 29 token->start = start;
trisjph 0:625bfa0ff591 30 token->end = end;
trisjph 0:625bfa0ff591 31 token->size = 0;
trisjph 0:625bfa0ff591 32 }
trisjph 0:625bfa0ff591 33
trisjph 0:625bfa0ff591 34 /**
trisjph 0:625bfa0ff591 35 * Fills next available token with JSON primitive.
trisjph 0:625bfa0ff591 36 */
trisjph 0:625bfa0ff591 37 static jsmnerr_t jsmn_parse_primitive(jsmn_parser *parser, char *js,
trisjph 0:625bfa0ff591 38 jsmntok_t *tokens, size_t num_tokens) {
trisjph 0:625bfa0ff591 39 jsmntok_t *token;
trisjph 0:625bfa0ff591 40 int start;
trisjph 0:625bfa0ff591 41
trisjph 0:625bfa0ff591 42 start = parser->pos;
trisjph 0:625bfa0ff591 43
trisjph 0:625bfa0ff591 44 for (; js[parser->pos] != '\0'; parser->pos++) {
trisjph 0:625bfa0ff591 45 switch (js[parser->pos]) {
trisjph 0:625bfa0ff591 46 #ifndef JSMN_STRICT
trisjph 0:625bfa0ff591 47 /* In strict mode primitive must be followed by "," or "}" or "]" */
trisjph 0:625bfa0ff591 48 case ':':
trisjph 0:625bfa0ff591 49 #endif
trisjph 0:625bfa0ff591 50 case '\t' : case '\r' : case '\n' : case ' ' :
trisjph 0:625bfa0ff591 51 case ',' : case ']' : case '}' :
trisjph 0:625bfa0ff591 52 goto found;
trisjph 0:625bfa0ff591 53 }
trisjph 0:625bfa0ff591 54 if (js[parser->pos] < 32 || js[parser->pos] >= 127) {
trisjph 0:625bfa0ff591 55 parser->pos = start;
trisjph 0:625bfa0ff591 56 return JSMN_ERROR_INVAL;
trisjph 0:625bfa0ff591 57 }
trisjph 0:625bfa0ff591 58 }
trisjph 0:625bfa0ff591 59 #ifdef JSMN_STRICT
trisjph 0:625bfa0ff591 60 /* In strict mode primitive must be followed by a comma/object/array */
trisjph 0:625bfa0ff591 61 parser->pos = start;
trisjph 0:625bfa0ff591 62 return JSMN_ERROR_PART;
trisjph 0:625bfa0ff591 63 #endif
trisjph 0:625bfa0ff591 64
trisjph 0:625bfa0ff591 65 found:
trisjph 0:625bfa0ff591 66 token = jsmn_alloc_token(parser, tokens, num_tokens);
trisjph 0:625bfa0ff591 67 if (token == NULL) {
trisjph 0:625bfa0ff591 68 parser->pos = start;
trisjph 0:625bfa0ff591 69 return JSMN_ERROR_NOMEM;
trisjph 0:625bfa0ff591 70 }
trisjph 0:625bfa0ff591 71 jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos);
trisjph 0:625bfa0ff591 72 #ifdef JSMN_PARENT_LINKS
trisjph 0:625bfa0ff591 73 token->parent = parser->toksuper;
trisjph 0:625bfa0ff591 74 #endif
trisjph 0:625bfa0ff591 75 parser->pos--;
trisjph 0:625bfa0ff591 76 return JSMN_SUCCESS;
trisjph 0:625bfa0ff591 77 }
trisjph 0:625bfa0ff591 78
trisjph 0:625bfa0ff591 79 /**
trisjph 0:625bfa0ff591 80 * Filsl next token with JSON string.
trisjph 0:625bfa0ff591 81 */
trisjph 0:625bfa0ff591 82 static jsmnerr_t jsmn_parse_string(jsmn_parser *parser, const char *js,
trisjph 0:625bfa0ff591 83 jsmntok_t *tokens, size_t num_tokens) {
trisjph 0:625bfa0ff591 84 jsmntok_t *token;
trisjph 0:625bfa0ff591 85
trisjph 0:625bfa0ff591 86 int start = parser->pos;
trisjph 0:625bfa0ff591 87
trisjph 0:625bfa0ff591 88 parser->pos++;
trisjph 0:625bfa0ff591 89
trisjph 0:625bfa0ff591 90 /* Skip starting quote */
trisjph 0:625bfa0ff591 91 for (; js[parser->pos] != '\0'; parser->pos++) {
trisjph 0:625bfa0ff591 92 char c = js[parser->pos];
trisjph 0:625bfa0ff591 93
trisjph 0:625bfa0ff591 94 /* Quote: end of string */
trisjph 0:625bfa0ff591 95 if (c == '\"') {
trisjph 0:625bfa0ff591 96 token = jsmn_alloc_token(parser, tokens, num_tokens);
trisjph 0:625bfa0ff591 97 if (token == NULL) {
trisjph 0:625bfa0ff591 98 parser->pos = start;
trisjph 0:625bfa0ff591 99 return JSMN_ERROR_NOMEM;
trisjph 0:625bfa0ff591 100 }
trisjph 0:625bfa0ff591 101 jsmn_fill_token(token, JSMN_STRING, start+1, parser->pos);
trisjph 0:625bfa0ff591 102 #ifdef JSMN_PARENT_LINKS
trisjph 0:625bfa0ff591 103 token->parent = parser->toksuper;
trisjph 0:625bfa0ff591 104 #endif
trisjph 0:625bfa0ff591 105 return JSMN_SUCCESS;
trisjph 0:625bfa0ff591 106 }
trisjph 0:625bfa0ff591 107
trisjph 0:625bfa0ff591 108 /* Backslash: Quoted symbol expected */
trisjph 0:625bfa0ff591 109 if (c == '\\') {
trisjph 0:625bfa0ff591 110 parser->pos++;
trisjph 0:625bfa0ff591 111 switch (js[parser->pos]) {
trisjph 0:625bfa0ff591 112 /* Allowed escaped symbols */
trisjph 0:625bfa0ff591 113 case '\"': case '/' : case '\\' : case 'b' :
trisjph 0:625bfa0ff591 114 case 'f' : case 'r' : case 'n' : case 't' :
trisjph 0:625bfa0ff591 115 break;
trisjph 0:625bfa0ff591 116 /* Allows escaped symbol \uXXXX */
trisjph 0:625bfa0ff591 117 case 'u':
trisjph 0:625bfa0ff591 118 /* TODO */
trisjph 0:625bfa0ff591 119 break;
trisjph 0:625bfa0ff591 120 /* Unexpected symbol */
trisjph 0:625bfa0ff591 121 default:
trisjph 0:625bfa0ff591 122 parser->pos = start;
trisjph 0:625bfa0ff591 123 return JSMN_ERROR_INVAL;
trisjph 0:625bfa0ff591 124 }
trisjph 0:625bfa0ff591 125 }
trisjph 0:625bfa0ff591 126 }
trisjph 0:625bfa0ff591 127 parser->pos = start;
trisjph 0:625bfa0ff591 128 return JSMN_ERROR_PART;
trisjph 0:625bfa0ff591 129 }
trisjph 0:625bfa0ff591 130
trisjph 0:625bfa0ff591 131 /**
trisjph 0:625bfa0ff591 132 * Parse JSON string and fill tokens.
trisjph 0:625bfa0ff591 133 */
trisjph 0:625bfa0ff591 134 jsmnerr_t jsmn_parse(jsmn_parser *parser, char *js, jsmntok_t *tokens,
trisjph 0:625bfa0ff591 135 unsigned int num_tokens) {
trisjph 0:625bfa0ff591 136 jsmnerr_t r;
trisjph 0:625bfa0ff591 137 int i;
trisjph 0:625bfa0ff591 138 jsmntok_t *token;
trisjph 0:625bfa0ff591 139
trisjph 0:625bfa0ff591 140 for (; js[parser->pos] != '\0'; parser->pos++) {
trisjph 0:625bfa0ff591 141 char c;
trisjph 0:625bfa0ff591 142 jsmntype_t type;
trisjph 0:625bfa0ff591 143
trisjph 0:625bfa0ff591 144 c = js[parser->pos];
trisjph 0:625bfa0ff591 145 switch (c) {
trisjph 0:625bfa0ff591 146 case '{': case '[':
trisjph 0:625bfa0ff591 147 token = jsmn_alloc_token(parser, tokens, num_tokens);
trisjph 0:625bfa0ff591 148 if (token == NULL)
trisjph 0:625bfa0ff591 149 return JSMN_ERROR_NOMEM;
trisjph 0:625bfa0ff591 150 if (parser->toksuper != -1) {
trisjph 0:625bfa0ff591 151 tokens[parser->toksuper].size++;
trisjph 0:625bfa0ff591 152 #ifdef JSMN_PARENT_LINKS
trisjph 0:625bfa0ff591 153 token->parent = parser->toksuper;
trisjph 0:625bfa0ff591 154 #endif
trisjph 0:625bfa0ff591 155 }
trisjph 0:625bfa0ff591 156 token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY);
trisjph 0:625bfa0ff591 157 token->start = parser->pos;
trisjph 0:625bfa0ff591 158 parser->toksuper = parser->toknext - 1;
trisjph 0:625bfa0ff591 159 break;
trisjph 0:625bfa0ff591 160 case '}': case ']':
trisjph 0:625bfa0ff591 161 type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY);
trisjph 0:625bfa0ff591 162 #ifdef JSMN_PARENT_LINKS
trisjph 0:625bfa0ff591 163 if (parser->toknext < 1) {
trisjph 0:625bfa0ff591 164 return JSMN_ERROR_INVAL;
trisjph 0:625bfa0ff591 165 }
trisjph 0:625bfa0ff591 166 token = &tokens[parser->toknext - 1];
trisjph 0:625bfa0ff591 167 for (;;) {
trisjph 0:625bfa0ff591 168 if (token->start != -1 && token->end == -1) {
trisjph 0:625bfa0ff591 169 if (token->type != type) {
trisjph 0:625bfa0ff591 170 return JSMN_ERROR_INVAL;
trisjph 0:625bfa0ff591 171 }
trisjph 0:625bfa0ff591 172 token->end = parser->pos + 1;
trisjph 0:625bfa0ff591 173 parser->toksuper = token->parent;
trisjph 0:625bfa0ff591 174 break;
trisjph 0:625bfa0ff591 175 }
trisjph 0:625bfa0ff591 176 if (token->parent == -1) {
trisjph 0:625bfa0ff591 177 break;
trisjph 0:625bfa0ff591 178 }
trisjph 0:625bfa0ff591 179 token = &tokens[token->parent];
trisjph 0:625bfa0ff591 180 }
trisjph 0:625bfa0ff591 181 #else
trisjph 0:625bfa0ff591 182 for (i = parser->toknext - 1; i >= 0; i--) {
trisjph 0:625bfa0ff591 183 token = &tokens[i];
trisjph 0:625bfa0ff591 184 if (token->start != -1 && token->end == -1) {
trisjph 0:625bfa0ff591 185 if (token->type != type) {
trisjph 0:625bfa0ff591 186 return JSMN_ERROR_INVAL;
trisjph 0:625bfa0ff591 187 }
trisjph 0:625bfa0ff591 188 parser->toksuper = -1;
trisjph 0:625bfa0ff591 189 token->end = parser->pos + 1;
trisjph 0:625bfa0ff591 190 break;
trisjph 0:625bfa0ff591 191 }
trisjph 0:625bfa0ff591 192 }
trisjph 0:625bfa0ff591 193 /* Error if unmatched closing bracket */
trisjph 0:625bfa0ff591 194 if (i == -1) return JSMN_ERROR_INVAL;
trisjph 0:625bfa0ff591 195 for (; i >= 0; i--) {
trisjph 0:625bfa0ff591 196 token = &tokens[i];
trisjph 0:625bfa0ff591 197 if (token->start != -1 && token->end == -1) {
trisjph 0:625bfa0ff591 198 parser->toksuper = i;
trisjph 0:625bfa0ff591 199 break;
trisjph 0:625bfa0ff591 200 }
trisjph 0:625bfa0ff591 201 }
trisjph 0:625bfa0ff591 202 #endif
trisjph 0:625bfa0ff591 203 break;
trisjph 0:625bfa0ff591 204 case '\"':
trisjph 0:625bfa0ff591 205 r = jsmn_parse_string(parser, js, tokens, num_tokens);
trisjph 0:625bfa0ff591 206 if (r < 0) return r;
trisjph 0:625bfa0ff591 207 if (parser->toksuper != -1)
trisjph 0:625bfa0ff591 208 tokens[parser->toksuper].size++;
trisjph 0:625bfa0ff591 209 break;
trisjph 0:625bfa0ff591 210 case '\t' : case '\r' : case '\n' : case ':' : case ',': case ' ':
trisjph 0:625bfa0ff591 211 break;
trisjph 0:625bfa0ff591 212 #ifdef JSMN_STRICT
trisjph 0:625bfa0ff591 213 /* In strict mode primitives are: numbers and booleans */
trisjph 0:625bfa0ff591 214 case '-': case '0': case '1' : case '2': case '3' : case '4':
trisjph 0:625bfa0ff591 215 case '5': case '6': case '7' : case '8': case '9':
trisjph 0:625bfa0ff591 216 case 't': case 'f': case 'n' :
trisjph 0:625bfa0ff591 217 #else
trisjph 0:625bfa0ff591 218 /* In non-strict mode every unquoted value is a primitive */
trisjph 0:625bfa0ff591 219 default:
trisjph 0:625bfa0ff591 220 #endif
trisjph 0:625bfa0ff591 221 r = jsmn_parse_primitive(parser, js, tokens, num_tokens);
trisjph 0:625bfa0ff591 222 if (r < 0) return r;
trisjph 0:625bfa0ff591 223 if (parser->toksuper != -1)
trisjph 0:625bfa0ff591 224 tokens[parser->toksuper].size++;
trisjph 0:625bfa0ff591 225 break;
trisjph 0:625bfa0ff591 226
trisjph 0:625bfa0ff591 227 #ifdef JSMN_STRICT
trisjph 0:625bfa0ff591 228 /* Unexpected char in strict mode */
trisjph 0:625bfa0ff591 229 default:
trisjph 0:625bfa0ff591 230 return JSMN_ERROR_INVAL;
trisjph 0:625bfa0ff591 231 #endif
trisjph 0:625bfa0ff591 232
trisjph 0:625bfa0ff591 233 }
trisjph 0:625bfa0ff591 234 }
trisjph 0:625bfa0ff591 235
trisjph 0:625bfa0ff591 236 for (i = parser->toknext - 1; i >= 0; i--) {
trisjph 0:625bfa0ff591 237 /* Unmatched opened object or array */
trisjph 0:625bfa0ff591 238 if (tokens[i].start != -1 && tokens[i].end == -1) {
trisjph 0:625bfa0ff591 239 return JSMN_ERROR_PART;
trisjph 0:625bfa0ff591 240 }
trisjph 0:625bfa0ff591 241 }
trisjph 0:625bfa0ff591 242
trisjph 0:625bfa0ff591 243 return JSMN_SUCCESS;
trisjph 0:625bfa0ff591 244 }
trisjph 0:625bfa0ff591 245
trisjph 0:625bfa0ff591 246 /**
trisjph 0:625bfa0ff591 247 * Creates a new parser based over a given buffer with an array of tokens
trisjph 0:625bfa0ff591 248 * available.
trisjph 0:625bfa0ff591 249 */
trisjph 0:625bfa0ff591 250 void jsmn_init(jsmn_parser *parser) {
trisjph 0:625bfa0ff591 251 parser->pos = 0;
trisjph 0:625bfa0ff591 252 parser->toknext = 0;
trisjph 0:625bfa0ff591 253 parser->toksuper = -1;
trisjph 0:625bfa0ff591 254 }