Tristan Hughes / jsmn
Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers jsmn.c Source File

jsmn.c

00001 #include <stdlib.h>
00002 
00003 #include "jsmn.h"
00004 
00005 /**
00006  * Allocates a fresh unused token from the token pull.
00007  */
00008 static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser, 
00009         jsmntok_t *tokens, size_t num_tokens) {
00010     jsmntok_t *tok;
00011     if (parser->toknext >= num_tokens) {
00012         return NULL;
00013     }
00014     tok = &tokens[parser->toknext++];
00015     tok->start = tok->end = -1;
00016     tok->size = 0;
00017 #ifdef JSMN_PARENT_LINKS
00018     tok->parent = -1;
00019 #endif
00020     return tok;
00021 }
00022 
00023 /**
00024  * Fills token type and boundaries.
00025  */
00026 static void jsmn_fill_token(jsmntok_t *token, jsmntype_t type, 
00027                             int start, int end) {
00028     token->type = type;
00029     token->start = start;
00030     token->end = end;
00031     token->size = 0;
00032 }
00033 
00034 /**
00035  * Fills next available token with JSON primitive.
00036  */
00037 static jsmnerr_t jsmn_parse_primitive(jsmn_parser *parser, char *js,
00038         jsmntok_t *tokens, size_t num_tokens) {
00039     jsmntok_t *token;
00040     int start;
00041 
00042     start = parser->pos;
00043 
00044     for (; js[parser->pos] != '\0'; parser->pos++) {
00045         switch (js[parser->pos]) {
00046 #ifndef JSMN_STRICT
00047             /* In strict mode primitive must be followed by "," or "}" or "]" */
00048             case ':':
00049 #endif
00050             case '\t' : case '\r' : case '\n' : case ' ' :
00051             case ','  : case ']'  : case '}' :
00052                 goto found;
00053         }
00054         if (js[parser->pos] < 32 || js[parser->pos] >= 127) {
00055             parser->pos = start;
00056             return JSMN_ERROR_INVAL;
00057         }
00058     }
00059 #ifdef JSMN_STRICT
00060     /* In strict mode primitive must be followed by a comma/object/array */
00061     parser->pos = start;
00062     return JSMN_ERROR_PART;
00063 #endif
00064 
00065 found:
00066     token = jsmn_alloc_token(parser, tokens, num_tokens);
00067     if (token == NULL) {
00068         parser->pos = start;
00069         return JSMN_ERROR_NOMEM;
00070     }
00071     jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos);
00072 #ifdef JSMN_PARENT_LINKS
00073     token->parent = parser->toksuper;
00074 #endif
00075     parser->pos--;
00076     return JSMN_SUCCESS;
00077 }
00078 
00079 /**
00080  * Filsl next token with JSON string.
00081  */
00082 static jsmnerr_t jsmn_parse_string(jsmn_parser *parser, const char *js,
00083         jsmntok_t *tokens, size_t num_tokens) {
00084     jsmntok_t *token;
00085 
00086     int start = parser->pos;
00087 
00088     parser->pos++;
00089 
00090     /* Skip starting quote */
00091     for (; js[parser->pos] != '\0'; parser->pos++) {
00092         char c = js[parser->pos];
00093 
00094         /* Quote: end of string */
00095         if (c == '\"') {
00096             token = jsmn_alloc_token(parser, tokens, num_tokens);
00097             if (token == NULL) {
00098                 parser->pos = start;
00099                 return JSMN_ERROR_NOMEM;
00100             }
00101             jsmn_fill_token(token, JSMN_STRING, start+1, parser->pos);
00102 #ifdef JSMN_PARENT_LINKS
00103             token->parent = parser->toksuper;
00104 #endif
00105             return JSMN_SUCCESS;
00106         }
00107 
00108         /* Backslash: Quoted symbol expected */
00109         if (c == '\\') {
00110             parser->pos++;
00111             switch (js[parser->pos]) {
00112                 /* Allowed escaped symbols */
00113                 case '\"': case '/' : case '\\' : case 'b' :
00114                 case 'f' : case 'r' : case 'n'  : case 't' :
00115                     break;
00116                 /* Allows escaped symbol \uXXXX */
00117                 case 'u':
00118                     /* TODO */
00119                     break;
00120                 /* Unexpected symbol */
00121                 default:
00122                     parser->pos = start;
00123                     return JSMN_ERROR_INVAL;
00124             }
00125         }
00126     }
00127     parser->pos = start;
00128     return JSMN_ERROR_PART;
00129 }
00130 
00131 /**
00132  * Parse JSON string and fill tokens.
00133  */
00134 jsmnerr_t jsmn_parse(jsmn_parser *parser, char *js, jsmntok_t *tokens, 
00135         unsigned int num_tokens) {
00136     jsmnerr_t r;
00137     int i;
00138     jsmntok_t *token;
00139 
00140     for (; js[parser->pos] != '\0'; parser->pos++) {
00141         char c;
00142         jsmntype_t type;
00143 
00144         c = js[parser->pos];
00145         switch (c) {
00146             case '{': case '[':
00147                 token = jsmn_alloc_token(parser, tokens, num_tokens);
00148                 if (token == NULL)
00149                     return JSMN_ERROR_NOMEM;
00150                 if (parser->toksuper != -1) {
00151                     tokens[parser->toksuper].size++;
00152 #ifdef JSMN_PARENT_LINKS
00153                     token->parent = parser->toksuper;
00154 #endif
00155                 }
00156                 token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY);
00157                 token->start = parser->pos;
00158                 parser->toksuper = parser->toknext - 1;
00159                 break;
00160             case '}': case ']':
00161                 type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY);
00162 #ifdef JSMN_PARENT_LINKS
00163                 if (parser->toknext < 1) {
00164                     return JSMN_ERROR_INVAL;
00165                 }
00166                 token = &tokens[parser->toknext - 1];
00167                 for (;;) {
00168                     if (token->start != -1 && token->end == -1) {
00169                         if (token->type != type) {
00170                             return JSMN_ERROR_INVAL;
00171                         }
00172                         token->end = parser->pos + 1;
00173                         parser->toksuper = token->parent;
00174                         break;
00175                     }
00176                     if (token->parent == -1) {
00177                         break;
00178                     }
00179                     token = &tokens[token->parent];
00180                 }
00181 #else
00182                 for (i = parser->toknext - 1; i >= 0; i--) {
00183                     token = &tokens[i];
00184                     if (token->start != -1 && token->end == -1) {
00185                         if (token->type != type) {
00186                             return JSMN_ERROR_INVAL;
00187                         }
00188                         parser->toksuper = -1;
00189                         token->end = parser->pos + 1;
00190                         break;
00191                     }
00192                 }
00193                 /* Error if unmatched closing bracket */
00194                 if (i == -1) return JSMN_ERROR_INVAL;
00195                 for (; i >= 0; i--) {
00196                     token = &tokens[i];
00197                     if (token->start != -1 && token->end == -1) {
00198                         parser->toksuper = i;
00199                         break;
00200                     }
00201                 }
00202 #endif
00203                 break;
00204             case '\"':
00205                 r = jsmn_parse_string(parser, js, tokens, num_tokens);
00206                 if (r < 0) return r;
00207                 if (parser->toksuper != -1)
00208                     tokens[parser->toksuper].size++;
00209                 break;
00210             case '\t' : case '\r' : case '\n' : case ':' : case ',': case ' ': 
00211                 break;
00212 #ifdef JSMN_STRICT
00213             /* In strict mode primitives are: numbers and booleans */
00214             case '-': case '0': case '1' : case '2': case '3' : case '4':
00215             case '5': case '6': case '7' : case '8': case '9':
00216             case 't': case 'f': case 'n' :
00217 #else
00218             /* In non-strict mode every unquoted value is a primitive */
00219             default:
00220 #endif
00221                 r = jsmn_parse_primitive(parser, js, tokens, num_tokens);
00222                 if (r < 0) return r;
00223                 if (parser->toksuper != -1)
00224                     tokens[parser->toksuper].size++;
00225                 break;
00226 
00227 #ifdef JSMN_STRICT
00228             /* Unexpected char in strict mode */
00229             default:
00230                 return JSMN_ERROR_INVAL;
00231 #endif
00232 
00233         }
00234     }
00235 
00236     for (i = parser->toknext - 1; i >= 0; i--) {
00237         /* Unmatched opened object or array */
00238         if (tokens[i].start != -1 && tokens[i].end == -1) {
00239             return JSMN_ERROR_PART;
00240         }
00241     }
00242 
00243     return JSMN_SUCCESS;
00244 }
00245 
00246 /**
00247  * Creates a new parser based over a given  buffer with an array of tokens 
00248  * available.
00249  */
00250 void jsmn_init(jsmn_parser *parser) {
00251     parser->pos = 0;
00252     parser->toknext = 0;
00253     parser->toksuper = -1;
00254 }