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