lexer, parser, matcher: extend grammar to allow comma separated keys/indexes as more...
[project/jsonpath.git] / matcher.c
1 /*
2 * Copyright (C) 2013 Jo-Philipp Wich <jow@openwrt.org>
3 *
4 * Permission to use, copy, modify, and/or distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17 #include "matcher.h"
18
19 static struct json_object *
20 jp_match_next(struct jp_opcode *ptr,
21 struct json_object *root, struct json_object *cur,
22 jp_match_cb_t cb, void *priv);
23
24 static bool
25 jp_json_to_op(struct json_object *obj, struct jp_opcode *op)
26 {
27 switch (json_object_get_type(obj))
28 {
29 case json_type_boolean:
30 op->type = T_BOOL;
31 op->num = json_object_get_boolean(obj);
32 return true;
33
34 case json_type_int:
35 op->type = T_NUMBER;
36 op->num = json_object_get_int(obj);
37 return true;
38
39 case json_type_string:
40 op->type = T_STRING;
41 op->str = (char *)json_object_get_string(obj);
42 return true;
43
44 default:
45 return false;
46 }
47 }
48
49 static bool
50 jp_resolve(struct json_object *root, struct json_object *cur,
51 struct jp_opcode *op, struct jp_opcode *res)
52 {
53 struct json_object *val;
54
55 switch (op->type)
56 {
57 case T_THIS:
58 val = jp_match(op, cur, NULL, NULL);
59
60 if (val)
61 return jp_json_to_op(val, res);
62
63 return false;
64
65 case T_ROOT:
66 val = jp_match(op, root, NULL, NULL);
67
68 if (val)
69 return jp_json_to_op(val, res);
70
71 return false;
72
73 default:
74 *res = *op;
75 return true;
76 }
77 }
78
79 static bool
80 jp_cmp(struct jp_opcode *op, struct json_object *root, struct json_object *cur)
81 {
82 int delta;
83 struct jp_opcode left, right;
84
85 if (!jp_resolve(root, cur, op->down, &left) ||
86 !jp_resolve(root, cur, op->down->sibling, &right))
87 return false;
88
89 if (left.type != right.type)
90 return false;
91
92 switch (left.type)
93 {
94 case T_BOOL:
95 case T_NUMBER:
96 delta = left.num - right.num;
97 break;
98
99 case T_STRING:
100 delta = strcmp(left.str, right.str);
101 break;
102
103 default:
104 return false;
105 }
106
107 switch (op->type)
108 {
109 case T_EQ:
110 return (delta == 0);
111
112 case T_LT:
113 return (delta < 0);
114
115 case T_LE:
116 return (delta <= 0);
117
118 case T_GT:
119 return (delta > 0);
120
121 case T_GE:
122 return (delta >= 0);
123
124 case T_NE:
125 return (delta != 0);
126
127 default:
128 return false;
129 }
130 }
131
132 static bool
133 jp_expr(struct jp_opcode *op, struct json_object *root, struct json_object *cur,
134 int idx, const char *key, jp_match_cb_t cb, void *priv)
135 {
136 struct jp_opcode *sop;
137
138 switch (op->type)
139 {
140 case T_WILDCARD:
141 return true;
142
143 case T_EQ:
144 case T_NE:
145 case T_LT:
146 case T_LE:
147 case T_GT:
148 case T_GE:
149 return jp_cmp(op, root, cur);
150
151 case T_ROOT:
152 return !!jp_match(op, root, NULL, NULL);
153
154 case T_THIS:
155 return !!jp_match(op, cur, NULL, NULL);
156
157 case T_NOT:
158 return !jp_expr(op->down, root, cur, idx, key, cb, priv);
159
160 case T_AND:
161 for (sop = op->down; sop; sop = sop->sibling)
162 if (!jp_expr(sop, root, cur, idx, key, cb, priv))
163 return false;
164 return true;
165
166 case T_OR:
167 case T_UNION:
168 for (sop = op->down; sop; sop = sop->sibling)
169 if (jp_expr(sop, root, cur, idx, key, cb, priv))
170 return true;
171 return false;
172
173 case T_STRING:
174 return (key && !strcmp(op->str, key));
175
176 case T_NUMBER:
177 return (idx == op->num);
178
179 default:
180 return false;
181 }
182 }
183
184 static struct json_object *
185 jp_match_expr(struct jp_opcode *ptr,
186 struct json_object *root, struct json_object *cur,
187 jp_match_cb_t cb, void *priv)
188 {
189 int idx, len;
190 struct json_object *tmp, *res = NULL;
191
192 switch (json_object_get_type(cur))
193 {
194 case json_type_object:
195 ; /* a label can only be part of a statement and a declaration is not a statement */
196 json_object_object_foreach(cur, key, val)
197 {
198 if (jp_expr(ptr, root, val, -1, key, cb, priv))
199 {
200 tmp = jp_match_next(ptr->sibling, root, val, cb, priv);
201
202 if (tmp && !res)
203 res = tmp;
204 }
205 }
206
207 break;
208
209 case json_type_array:
210 len = json_object_array_length(cur);
211
212 for (idx = 0; idx < len; idx++)
213 {
214 tmp = json_object_array_get_idx(cur, idx);
215
216 if (jp_expr(ptr, root, tmp, idx, NULL, cb, priv))
217 {
218 tmp = jp_match_next(ptr->sibling, root, tmp, cb, priv);
219
220 if (tmp && !res)
221 res = tmp;
222 }
223 }
224
225 break;
226
227 default:
228 break;
229 }
230
231 return res;
232 }
233
234 static struct json_object *
235 jp_match_next(struct jp_opcode *ptr,
236 struct json_object *root, struct json_object *cur,
237 jp_match_cb_t cb, void *priv)
238 {
239 struct json_object *next;
240
241 if (!ptr)
242 {
243 if (cb)
244 cb(cur, priv);
245
246 return cur;
247 }
248
249 switch (ptr->type)
250 {
251 case T_STRING:
252 case T_LABEL:
253 if (json_object_object_get_ex(cur, ptr->str, &next))
254 return jp_match_next(ptr->sibling, root, next, cb, priv);
255
256 break;
257
258 case T_NUMBER:
259 next = json_object_array_get_idx(cur, ptr->num);
260
261 if (next)
262 return jp_match_next(ptr->sibling, root, next, cb, priv);
263
264 break;
265
266 default:
267 return jp_match_expr(ptr, root, cur, cb, priv);
268 }
269
270 return NULL;
271 }
272
273 struct json_object *
274 jp_match(struct jp_opcode *path, json_object *jsobj,
275 jp_match_cb_t cb, void *priv)
276 {
277 if (path->type == T_LABEL)
278 path = path->down;
279
280 return jp_match_next(path->down, jsobj, jsobj, cb, priv);
281 }