Files: cec6448afd2f0c42739c510e602283f56518510c / jsmn.c
7174 bytesRaw
1 | /** |
2 | * jsmn |
3 | * Copyright (c) 2010 Serge A. Zaitsev |
4 | * |
5 | * Permission is hereby granted, free of charge, to any person obtaining a copy |
6 | * of this software and associated documentation files (the "Software"), to deal |
7 | * in the Software without restriction, including without limitation the rights |
8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
9 | * copies of the Software, and to permit persons to whom the Software is |
10 | * furnished to do so, subject to the following conditions: |
11 | * |
12 | * The above copyright notice and this permission notice shall be included in |
13 | * all copies or substantial portions of the Software. |
14 | * |
15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
21 | * THE SOFTWARE. |
22 | */ |
23 | |
24 | |
25 | |
26 | |
27 | |
28 | /** |
29 | * Allocates a fresh unused token from the token pull. |
30 | */ |
31 | static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser, |
32 | jsmntok_t *tokens, size_t num_tokens) { |
33 | jsmntok_t *tok; |
34 | if (parser->toknext >= (int)num_tokens) { |
35 | return NULL; |
36 | } |
37 | tok = &tokens[parser->toknext++]; |
38 | tok->start = tok->end = -1; |
39 | tok->size = 0; |
40 | |
41 | tok->parent = -1; |
42 | |
43 | return tok; |
44 | } |
45 | |
46 | /** |
47 | * Fills token type and boundaries. |
48 | */ |
49 | static void jsmn_fill_token(jsmntok_t *token, jsmntype_t type, |
50 | int start, int end) { |
51 | token->type = type; |
52 | token->start = start; |
53 | token->end = end; |
54 | token->size = 0; |
55 | } |
56 | |
57 | /** |
58 | * Fills next available token with JSON primitive. |
59 | */ |
60 | static jsmnerr_t jsmn_parse_primitive(jsmn_parser *parser, const char *js, |
61 | jsmntok_t *tokens, size_t num_tokens) { |
62 | jsmntok_t *token; |
63 | int start; |
64 | |
65 | start = parser->pos; |
66 | |
67 | for (; js[parser->pos] != '\0'; parser->pos++) { |
68 | switch (js[parser->pos]) { |
69 | |
70 | /* In strict mode primitive must be followed by "," or "}" or "]" */ |
71 | case ':': |
72 | |
73 | case '\t' : case '\r' : case '\n' : case ' ' : |
74 | case ',' : case ']' : case '}' : |
75 | goto found; |
76 | } |
77 | if (js[parser->pos] < 32 || js[parser->pos] >= 127) { |
78 | parser->pos = start; |
79 | return JSMN_ERROR_INVAL; |
80 | } |
81 | } |
82 | |
83 | /* In strict mode primitive must be followed by a comma/object/array */ |
84 | parser->pos = start; |
85 | return JSMN_ERROR_PART; |
86 | |
87 | |
88 | found: |
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_PRIMITIVE, start, parser->pos); |
95 | |
96 | token->parent = parser->toksuper; |
97 | |
98 | parser->pos--; |
99 | return JSMN_SUCCESS; |
100 | } |
101 | |
102 | /** |
103 | * Filsl next token with JSON string. |
104 | */ |
105 | static jsmnerr_t jsmn_parse_string(jsmn_parser *parser, const char *js, |
106 | jsmntok_t *tokens, size_t num_tokens) { |
107 | jsmntok_t *token; |
108 | |
109 | int start = parser->pos; |
110 | |
111 | parser->pos++; |
112 | |
113 | /* Skip starting quote */ |
114 | for (; js[parser->pos] != '\0'; parser->pos++) { |
115 | char c = js[parser->pos]; |
116 | |
117 | /* Quote: end of string */ |
118 | if (c == '\"') { |
119 | token = jsmn_alloc_token(parser, tokens, num_tokens); |
120 | if (token == NULL) { |
121 | parser->pos = start; |
122 | return JSMN_ERROR_NOMEM; |
123 | } |
124 | jsmn_fill_token(token, JSMN_STRING, start+1, parser->pos); |
125 | |
126 | token->parent = parser->toksuper; |
127 | |
128 | return JSMN_SUCCESS; |
129 | } |
130 | |
131 | /* Backslash: Quoted symbol expected */ |
132 | if (c == '\\') { |
133 | parser->pos++; |
134 | switch (js[parser->pos]) { |
135 | /* Allowed escaped symbols */ |
136 | case '\"': case '/' : case '\\' : case 'b' : |
137 | case 'f' : case 'r' : case 'n' : case 't' : |
138 | break; |
139 | /* Allows escaped symbol \uXXXX */ |
140 | case 'u': |
141 | /* TODO */ |
142 | break; |
143 | /* Unexpected symbol */ |
144 | default: |
145 | parser->pos = start; |
146 | return JSMN_ERROR_INVAL; |
147 | } |
148 | } |
149 | } |
150 | parser->pos = start; |
151 | return JSMN_ERROR_PART; |
152 | } |
153 | |
154 | /** |
155 | * Parse JSON string and fill tokens. |
156 | */ |
157 | jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, jsmntok_t *tokens, |
158 | unsigned int num_tokens) { |
159 | jsmnerr_t r; |
160 | int i; |
161 | jsmntok_t *token; |
162 | |
163 | for (; js[parser->pos] != '\0'; parser->pos++) { |
164 | char c; |
165 | jsmntype_t type; |
166 | |
167 | c = js[parser->pos]; |
168 | switch (c) { |
169 | case '{': case '[': |
170 | token = jsmn_alloc_token(parser, tokens, num_tokens); |
171 | if (token == NULL) |
172 | return JSMN_ERROR_NOMEM; |
173 | if (parser->toksuper != -1) { |
174 | tokens[parser->toksuper].size++; |
175 | |
176 | token->parent = parser->toksuper; |
177 | |
178 | } |
179 | token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY); |
180 | token->start = parser->pos; |
181 | parser->toksuper = parser->toknext - 1; |
182 | break; |
183 | case '}': case ']': |
184 | type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY); |
185 | |
186 | if (parser->toknext < 1) { |
187 | return JSMN_ERROR_INVAL; |
188 | } |
189 | token = &tokens[parser->toknext - 1]; |
190 | for (;;) { |
191 | if (token->start != -1 && token->end == -1) { |
192 | if (token->type != type) { |
193 | return JSMN_ERROR_INVAL; |
194 | } |
195 | token->end = parser->pos + 1; |
196 | parser->toksuper = token->parent; |
197 | break; |
198 | } |
199 | if (token->parent == -1) { |
200 | break; |
201 | } |
202 | token = &tokens[token->parent]; |
203 | } |
204 | |
205 | for (i = parser->toknext - 1; i >= 0; i--) { |
206 | token = &tokens[i]; |
207 | if (token->start != -1 && token->end == -1) { |
208 | if (token->type != type) { |
209 | return JSMN_ERROR_INVAL; |
210 | } |
211 | parser->toksuper = -1; |
212 | token->end = parser->pos + 1; |
213 | break; |
214 | } |
215 | } |
216 | /* Error if unmatched closing bracket */ |
217 | if (i == -1) return JSMN_ERROR_INVAL; |
218 | for (; i >= 0; i--) { |
219 | token = &tokens[i]; |
220 | if (token->start != -1 && token->end == -1) { |
221 | parser->toksuper = i; |
222 | break; |
223 | } |
224 | } |
225 | |
226 | break; |
227 | case '\"': |
228 | r = jsmn_parse_string(parser, js, tokens, num_tokens); |
229 | if (r < 0) return r; |
230 | if (parser->toksuper != -1) |
231 | tokens[parser->toksuper].size++; |
232 | break; |
233 | case '\t' : case '\r' : case '\n' : case ':' : case ',': case ' ': |
234 | break; |
235 | |
236 | /* In strict mode primitives are: numbers and booleans */ |
237 | case '-': case '0': case '1' : case '2': case '3' : case '4': |
238 | case '5': case '6': case '7' : case '8': case '9': |
239 | case 't': case 'f': case 'n' : |
240 | |
241 | /* In non-strict mode every unquoted value is a primitive */ |
242 | default: |
243 | |
244 | r = jsmn_parse_primitive(parser, js, tokens, num_tokens); |
245 | if (r < 0) return r; |
246 | if (parser->toksuper != -1) |
247 | tokens[parser->toksuper].size++; |
248 | break; |
249 | |
250 | |
251 | /* Unexpected char in strict mode */ |
252 | default: |
253 | return JSMN_ERROR_INVAL; |
254 | |
255 | |
256 | } |
257 | } |
258 | |
259 | for (i = parser->toknext - 1; i >= 0; i--) { |
260 | /* Unmatched opened object or array */ |
261 | if (tokens[i].start != -1 && tokens[i].end == -1) { |
262 | return JSMN_ERROR_PART; |
263 | } |
264 | } |
265 | |
266 | return JSMN_SUCCESS; |
267 | } |
268 | |
269 | /** |
270 | * Creates a new parser based over a given buffer with an array of tokens |
271 | * available. |
272 | */ |
273 | void jsmn_init(jsmn_parser *parser) { |
274 | parser->pos = 0; |
275 | parser->toknext = 0; |
276 | parser->toksuper = -1; |
277 | } |
278 | |
279 |
Built with git-ssb-web