viking

webkit based web browser for Enlightenment
Log | Files | Refs | LICENSE

jsmn.c (5545B)


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