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
jsmn.c@0:625bfa0ff591, 2014-01-22 (annotated)
- Committer:
- trisjph
- Date:
- Wed Jan 22 15:21:09 2014 +0000
- Revision:
- 0:625bfa0ff591
Initial commit
Who changed what in which revision?
User | Revision | Line number | New 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 | } |