1275970Scy#include <stdlib.h> 2275970Scy 3275970Scy#include "jsmn.h" 4275970Scy 5275970Scy/** 6275970Scy * Allocates a fresh unused token from the token pull. 7275970Scy */ 8285169Scystatic jsmntok_t *jsmn_alloc_token(jsmn_parser *parser, 9275970Scy jsmntok_t *tokens, size_t num_tokens) { 10275970Scy jsmntok_t *tok; 11285169Scy if (parser->toknext >= num_tokens) { 12275970Scy return NULL; 13275970Scy } 14275970Scy tok = &tokens[parser->toknext++]; 15275970Scy tok->start = tok->end = -1; 16275970Scy tok->size = 0; 17275970Scy#ifdef JSMN_PARENT_LINKS 18275970Scy tok->parent = -1; 19275970Scy#endif 20275970Scy return tok; 21275970Scy} 22275970Scy 23275970Scy/** 24275970Scy * Fills token type and boundaries. 25275970Scy */ 26285169Scystatic void jsmn_fill_token(jsmntok_t *token, jsmntype_t type, 27275970Scy int start, int end) { 28275970Scy token->type = type; 29275970Scy token->start = start; 30275970Scy token->end = end; 31275970Scy token->size = 0; 32275970Scy} 33275970Scy 34275970Scy/** 35275970Scy * Fills next available token with JSON primitive. 36275970Scy */ 37275970Scystatic jsmnerr_t jsmn_parse_primitive(jsmn_parser *parser, const char *js, 38285169Scy size_t len, jsmntok_t *tokens, size_t num_tokens) { 39275970Scy jsmntok_t *token; 40275970Scy int start; 41275970Scy 42275970Scy start = parser->pos; 43275970Scy 44285169Scy for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { 45275970Scy switch (js[parser->pos]) { 46275970Scy#ifndef JSMN_STRICT 47275970Scy /* In strict mode primitive must be followed by "," or "}" or "]" */ 48275970Scy case ':': 49275970Scy#endif 50275970Scy case '\t' : case '\r' : case '\n' : case ' ' : 51275970Scy case ',' : case ']' : case '}' : 52275970Scy goto found; 53275970Scy } 54275970Scy if (js[parser->pos] < 32 || js[parser->pos] >= 127) { 55275970Scy parser->pos = start; 56275970Scy return JSMN_ERROR_INVAL; 57275970Scy } 58275970Scy } 59275970Scy#ifdef JSMN_STRICT 60275970Scy /* In strict mode primitive must be followed by a comma/object/array */ 61275970Scy parser->pos = start; 62275970Scy return JSMN_ERROR_PART; 63275970Scy#endif 64275970Scy 65275970Scyfound: 66285169Scy if (tokens == NULL) { 67285169Scy parser->pos--; 68285169Scy return 0; 69285169Scy } 70275970Scy token = jsmn_alloc_token(parser, tokens, num_tokens); 71275970Scy if (token == NULL) { 72275970Scy parser->pos = start; 73275970Scy return JSMN_ERROR_NOMEM; 74275970Scy } 75275970Scy jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos); 76275970Scy#ifdef JSMN_PARENT_LINKS 77275970Scy token->parent = parser->toksuper; 78275970Scy#endif 79275970Scy parser->pos--; 80285169Scy return 0; 81275970Scy} 82275970Scy 83275970Scy/** 84275970Scy * Filsl next token with JSON string. 85275970Scy */ 86275970Scystatic jsmnerr_t jsmn_parse_string(jsmn_parser *parser, const char *js, 87285169Scy size_t len, jsmntok_t *tokens, size_t num_tokens) { 88275970Scy jsmntok_t *token; 89275970Scy 90275970Scy int start = parser->pos; 91275970Scy 92275970Scy parser->pos++; 93275970Scy 94275970Scy /* Skip starting quote */ 95285169Scy for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { 96275970Scy char c = js[parser->pos]; 97275970Scy 98275970Scy /* Quote: end of string */ 99275970Scy if (c == '\"') { 100285169Scy if (tokens == NULL) { 101285169Scy return 0; 102285169Scy } 103275970Scy token = jsmn_alloc_token(parser, tokens, num_tokens); 104275970Scy if (token == NULL) { 105275970Scy parser->pos = start; 106275970Scy return JSMN_ERROR_NOMEM; 107275970Scy } 108275970Scy jsmn_fill_token(token, JSMN_STRING, start+1, parser->pos); 109275970Scy#ifdef JSMN_PARENT_LINKS 110275970Scy token->parent = parser->toksuper; 111275970Scy#endif 112285169Scy return 0; 113275970Scy } 114275970Scy 115275970Scy /* Backslash: Quoted symbol expected */ 116285169Scy if (c == '\\' && parser->pos + 1 < len) { 117285169Scy int i; 118275970Scy parser->pos++; 119275970Scy switch (js[parser->pos]) { 120275970Scy /* Allowed escaped symbols */ 121275970Scy case '\"': case '/' : case '\\' : case 'b' : 122275970Scy case 'f' : case 'r' : case 'n' : case 't' : 123275970Scy break; 124275970Scy /* Allows escaped symbol \uXXXX */ 125275970Scy case 'u': 126275970Scy parser->pos++; 127285169Scy for(i = 0; i < 4 && parser->pos < len && js[parser->pos] != '\0'; i++) { 128275970Scy /* If it isn't a hex character we have an error */ 129275970Scy if(!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */ 130275970Scy (js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */ 131275970Scy (js[parser->pos] >= 97 && js[parser->pos] <= 102))) { /* a-f */ 132275970Scy parser->pos = start; 133275970Scy return JSMN_ERROR_INVAL; 134275970Scy } 135275970Scy parser->pos++; 136275970Scy } 137275970Scy parser->pos--; 138275970Scy break; 139275970Scy /* Unexpected symbol */ 140275970Scy default: 141275970Scy parser->pos = start; 142275970Scy return JSMN_ERROR_INVAL; 143275970Scy } 144275970Scy } 145275970Scy } 146275970Scy parser->pos = start; 147275970Scy return JSMN_ERROR_PART; 148275970Scy} 149275970Scy 150275970Scy/** 151275970Scy * Parse JSON string and fill tokens. 152275970Scy */ 153285169Scyjsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, size_t len, 154285169Scy jsmntok_t *tokens, unsigned int num_tokens) { 155275970Scy jsmnerr_t r; 156275970Scy int i; 157275970Scy jsmntok_t *token; 158285169Scy int count = 0; 159275970Scy 160285169Scy for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { 161275970Scy char c; 162275970Scy jsmntype_t type; 163275970Scy 164275970Scy c = js[parser->pos]; 165275970Scy switch (c) { 166275970Scy case '{': case '[': 167285169Scy count++; 168285169Scy if (tokens == NULL) { 169285169Scy break; 170285169Scy } 171275970Scy token = jsmn_alloc_token(parser, tokens, num_tokens); 172275970Scy if (token == NULL) 173275970Scy return JSMN_ERROR_NOMEM; 174275970Scy if (parser->toksuper != -1) { 175275970Scy tokens[parser->toksuper].size++; 176275970Scy#ifdef JSMN_PARENT_LINKS 177275970Scy token->parent = parser->toksuper; 178275970Scy#endif 179275970Scy } 180275970Scy token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY); 181275970Scy token->start = parser->pos; 182275970Scy parser->toksuper = parser->toknext - 1; 183275970Scy break; 184275970Scy case '}': case ']': 185285169Scy if (tokens == NULL) 186285169Scy break; 187275970Scy type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY); 188275970Scy#ifdef JSMN_PARENT_LINKS 189275970Scy if (parser->toknext < 1) { 190275970Scy return JSMN_ERROR_INVAL; 191275970Scy } 192275970Scy token = &tokens[parser->toknext - 1]; 193275970Scy for (;;) { 194275970Scy if (token->start != -1 && token->end == -1) { 195275970Scy if (token->type != type) { 196275970Scy return JSMN_ERROR_INVAL; 197275970Scy } 198275970Scy token->end = parser->pos + 1; 199275970Scy parser->toksuper = token->parent; 200275970Scy break; 201275970Scy } 202275970Scy if (token->parent == -1) { 203275970Scy break; 204275970Scy } 205275970Scy token = &tokens[token->parent]; 206275970Scy } 207275970Scy#else 208275970Scy for (i = parser->toknext - 1; i >= 0; i--) { 209275970Scy token = &tokens[i]; 210275970Scy if (token->start != -1 && token->end == -1) { 211275970Scy if (token->type != type) { 212275970Scy return JSMN_ERROR_INVAL; 213275970Scy } 214275970Scy parser->toksuper = -1; 215275970Scy token->end = parser->pos + 1; 216275970Scy break; 217275970Scy } 218275970Scy } 219275970Scy /* Error if unmatched closing bracket */ 220275970Scy if (i == -1) return JSMN_ERROR_INVAL; 221275970Scy for (; i >= 0; i--) { 222275970Scy token = &tokens[i]; 223275970Scy if (token->start != -1 && token->end == -1) { 224275970Scy parser->toksuper = i; 225275970Scy break; 226275970Scy } 227275970Scy } 228275970Scy#endif 229275970Scy break; 230275970Scy case '\"': 231285169Scy r = jsmn_parse_string(parser, js, len, tokens, num_tokens); 232275970Scy if (r < 0) return r; 233285169Scy count++; 234285169Scy if (parser->toksuper != -1 && tokens != NULL) 235275970Scy tokens[parser->toksuper].size++; 236275970Scy break; 237285169Scy case '\t' : case '\r' : case '\n' : case ' ': 238275970Scy break; 239285169Scy case ':': 240285169Scy parser->toksuper = parser->toknext - 1; 241285169Scy break; 242285169Scy case ',': 243285169Scy if (tokens != NULL && 244285169Scy tokens[parser->toksuper].type != JSMN_ARRAY && 245285169Scy tokens[parser->toksuper].type != JSMN_OBJECT) { 246285169Scy#ifdef JSMN_PARENT_LINKS 247285169Scy parser->toksuper = tokens[parser->toksuper].parent; 248285169Scy#else 249285169Scy for (i = parser->toknext - 1; i >= 0; i--) { 250285169Scy if (tokens[i].type == JSMN_ARRAY || tokens[i].type == JSMN_OBJECT) { 251285169Scy if (tokens[i].start != -1 && tokens[i].end == -1) { 252285169Scy parser->toksuper = i; 253285169Scy break; 254285169Scy } 255285169Scy } 256285169Scy } 257285169Scy#endif 258285169Scy } 259285169Scy break; 260275970Scy#ifdef JSMN_STRICT 261275970Scy /* In strict mode primitives are: numbers and booleans */ 262275970Scy case '-': case '0': case '1' : case '2': case '3' : case '4': 263275970Scy case '5': case '6': case '7' : case '8': case '9': 264275970Scy case 't': case 'f': case 'n' : 265285169Scy /* And they must not be keys of the object */ 266285169Scy if (tokens != NULL) { 267285169Scy jsmntok_t *t = &tokens[parser->toksuper]; 268285169Scy if (t->type == JSMN_OBJECT || 269285169Scy (t->type == JSMN_STRING && t->size != 0)) { 270285169Scy return JSMN_ERROR_INVAL; 271285169Scy } 272285169Scy } 273275970Scy#else 274275970Scy /* In non-strict mode every unquoted value is a primitive */ 275275970Scy default: 276275970Scy#endif 277285169Scy r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens); 278275970Scy if (r < 0) return r; 279285169Scy count++; 280285169Scy if (parser->toksuper != -1 && tokens != NULL) 281275970Scy tokens[parser->toksuper].size++; 282275970Scy break; 283275970Scy 284275970Scy#ifdef JSMN_STRICT 285275970Scy /* Unexpected char in strict mode */ 286275970Scy default: 287275970Scy return JSMN_ERROR_INVAL; 288275970Scy#endif 289275970Scy } 290275970Scy } 291285169Scy if (tokens != NULL) { 292285169Scy for (i = parser->toknext - 1; i >= 0; i--) { 293285169Scy /* Unmatched opened object or array */ 294285169Scy if (tokens[i].start != -1 && tokens[i].end == -1) { 295285169Scy return JSMN_ERROR_PART; 296285169Scy } 297275970Scy } 298275970Scy } 299275970Scy 300285169Scy return count; 301275970Scy} 302275970Scy 303275970Scy/** 304285169Scy * Creates a new parser based over a given buffer with an array of tokens 305275970Scy * available. 306275970Scy */ 307275970Scyvoid jsmn_init(jsmn_parser *parser) { 308275970Scy parser->pos = 0; 309275970Scy parser->toknext = 0; 310275970Scy parser->toksuper = -1; 311275970Scy} 312275970Scy 313