├── .gitignore ├── lexer.h ├── matcher.h ├── CMakeLists.txt ├── ast.h ├── ast.c ├── parser.y ├── matcher.c ├── lexer.c ├── main.c └── contrib └── lempar.c /.gitignore: -------------------------------------------------------------------------------- 1 | Makefile 2 | CMakeCache.txt 3 | CMakeFiles 4 | *.cmake 5 | install_manifest.txt 6 | jsonpath 7 | parser.[ch] 8 | -------------------------------------------------------------------------------- /lexer.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013-2014 Jo-Philipp Wich 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 | #ifndef __LEXER_H_ 18 | #define __LEXER_H_ 19 | 20 | #include "ast.h" 21 | 22 | extern const char *tokennames[25]; 23 | 24 | struct jp_opcode * 25 | jp_get_token(struct jp_state *s, const char *input, int *mlen); 26 | 27 | #endif /* __LEXER_H_ */ 28 | -------------------------------------------------------------------------------- /matcher.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013-2014 Jo-Philipp Wich 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 | #ifndef __MATCHER_H_ 18 | #define __MATCHER_H_ 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #ifdef JSONC 26 | #include 27 | #else 28 | #include 29 | #endif 30 | 31 | #include "ast.h" 32 | 33 | typedef void (*jp_match_cb_t)(struct json_object *res, void *priv); 34 | 35 | struct json_object * 36 | jp_match(struct jp_opcode *path, struct json_object *jsobj, 37 | jp_match_cb_t cb, void *priv); 38 | 39 | #endif 40 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | 3 | PROJECT(jsonpath C) 4 | ADD_DEFINITIONS(-Os -Wall -Werror --std=gnu99 -Wmissing-declarations -Wno-error=unused-variable -ffunction-sections -D_GNU_SOURCE) 5 | 6 | IF(NOT APPLE) 7 | SET(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "-Wl,--gc-sections") 8 | ENDIF() 9 | 10 | IF(DEBUG) 11 | ADD_DEFINITIONS(-DDEBUG -g3) 12 | ENDIF() 13 | 14 | INCLUDE(FindPkgConfig) 15 | PKG_CHECK_MODULES(JSONC json-c) 16 | IF(JSONC_FOUND) 17 | ADD_DEFINITIONS(-DJSONC) 18 | INCLUDE_DIRECTORIES(${JSONC_INCLUDE_DIRS}) 19 | ENDIF() 20 | 21 | ADD_CUSTOM_COMMAND( 22 | OUTPUT contrib/lemon 23 | DEPENDS contrib/lemon.c contrib/lempar.c 24 | COMMAND gcc -std=gnu17 -o contrib/lemon contrib/lemon.c 25 | COMMENT "Generating lemon parser generator" 26 | ) 27 | 28 | ADD_CUSTOM_COMMAND( 29 | OUTPUT parser.c 30 | DEPENDS parser.y contrib/lemon 31 | COMMAND ./contrib/lemon parser.y 32 | COMMENT "Generating parser.c" 33 | ) 34 | 35 | FIND_PATH(ubox_include_dir libubox/list.h) 36 | INCLUDE_DIRECTORIES(${ubox_include_dir}) 37 | 38 | SET_PROPERTY(DIRECTORY APPEND PROPERTY ADDITIONAL_MAKE_CLEAN_FILES "lemon;parser.h;parser.out") 39 | SET_SOURCE_FILES_PROPERTIES("parser.c" PROPERTIES GENERATED TRUE) 40 | ADD_EXECUTABLE(jsonpath main.c ast.c lexer.c parser.c matcher.c) 41 | find_library(json NAMES json-c) 42 | TARGET_LINK_LIBRARIES(jsonpath ubox ${json}) 43 | 44 | INSTALL(TARGETS jsonpath RUNTIME DESTINATION bin) 45 | -------------------------------------------------------------------------------- /ast.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013-2014 Jo-Philipp Wich 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 | #ifndef __AST_H_ 18 | #define __AST_H_ 19 | 20 | #include 21 | 22 | struct jp_opcode { 23 | int type; 24 | struct jp_opcode *next; 25 | struct jp_opcode *down; 26 | struct jp_opcode *sibling; 27 | char *str; 28 | int num; 29 | }; 30 | 31 | struct jp_state { 32 | struct jp_opcode *pool; 33 | struct jp_opcode *path; 34 | int error_pos; 35 | int error_code; 36 | int off; 37 | }; 38 | 39 | static inline struct jp_opcode * 40 | append_op(struct jp_opcode *a, struct jp_opcode *b) 41 | { 42 | struct jp_opcode *tail = a; 43 | 44 | while (tail->sibling) 45 | tail = tail->sibling; 46 | 47 | tail->sibling = b; 48 | 49 | return a; 50 | } 51 | 52 | struct jp_opcode *jp_alloc_op(struct jp_state *s, int type, int num, char *str, ...); 53 | struct jp_state *jp_parse(const char *expr); 54 | void jp_free(struct jp_state *s); 55 | 56 | void *ParseAlloc(void *(*mfunc)(size_t)); 57 | void Parse(void *pParser, int type, struct jp_opcode *op, struct jp_state *s); 58 | void ParseFree(void *pParser, void (*ffunc)(void *)); 59 | 60 | #endif /* __AST_H_ */ 61 | -------------------------------------------------------------------------------- /ast.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013-2014 Jo-Philipp Wich 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 "ast.h" 18 | #include "lexer.h" 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | struct jp_opcode * 27 | jp_alloc_op(struct jp_state *s, int type, int num, char *str, ...) 28 | { 29 | va_list ap; 30 | char *ptr; 31 | struct jp_opcode *newop, *child; 32 | 33 | newop = calloc_a(sizeof(*newop), 34 | str ? &ptr : NULL, str ? strlen(str) + 1 : 0); 35 | 36 | if (!newop) 37 | { 38 | fprintf(stderr, "Out of memory\n"); 39 | exit(127); 40 | } 41 | 42 | newop->type = type; 43 | newop->num = num; 44 | 45 | if (str) 46 | newop->str = strcpy(ptr, str); 47 | 48 | va_start(ap, str); 49 | 50 | while ((child = va_arg(ap, void *)) != NULL) 51 | if (!newop->down) 52 | newop->down = child; 53 | else 54 | append_op(newop->down, child); 55 | 56 | va_end(ap); 57 | 58 | newop->next = s->pool; 59 | s->pool = newop; 60 | 61 | return newop; 62 | } 63 | 64 | void 65 | jp_free(struct jp_state *s) 66 | { 67 | struct jp_opcode *op, *tmp; 68 | 69 | for (op = s->pool; op;) 70 | { 71 | tmp = op->next; 72 | free(op); 73 | op = tmp; 74 | } 75 | 76 | free(s); 77 | } 78 | 79 | struct jp_state * 80 | jp_parse(const char *expr) 81 | { 82 | struct jp_state *s; 83 | struct jp_opcode *op; 84 | const char *ptr = expr; 85 | void *pParser; 86 | int len = strlen(expr); 87 | int mlen = 0; 88 | 89 | s = calloc(1, sizeof(*s)); 90 | 91 | if (!s) 92 | return NULL; 93 | 94 | pParser = ParseAlloc(malloc); 95 | 96 | if (!pParser) 97 | return NULL; 98 | 99 | while (len > 0) 100 | { 101 | op = jp_get_token(s, ptr, &mlen); 102 | 103 | if (mlen < 0) 104 | { 105 | s->error_code = mlen; 106 | goto out; 107 | } 108 | 109 | if (op) 110 | Parse(pParser, op->type, op, s); 111 | 112 | len -= mlen; 113 | ptr += mlen; 114 | 115 | s->off += mlen; 116 | } 117 | 118 | Parse(pParser, 0, NULL, s); 119 | 120 | out: 121 | ParseFree(pParser, free); 122 | 123 | return s; 124 | } 125 | -------------------------------------------------------------------------------- /parser.y: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013-2014 Jo-Philipp Wich 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 | %token_type {struct jp_opcode *} 18 | %extra_argument {struct jp_state *s} 19 | 20 | %left T_AND. 21 | %left T_OR. 22 | %left T_UNION. 23 | %nonassoc T_EQ T_NE T_GT T_GE T_LT T_LE T_MATCH. 24 | %right T_NOT. 25 | 26 | %include { 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #include "ast.h" 33 | #include "lexer.h" 34 | #include "parser.h" 35 | 36 | #define alloc_op(type, num, str, ...) \ 37 | jp_alloc_op(s, type, num, str, ##__VA_ARGS__, NULL) 38 | 39 | } 40 | 41 | %syntax_error { 42 | int i; 43 | 44 | for (i = 0; i < sizeof(tokennames) / sizeof(tokennames[0]); i++) 45 | if (yy_find_shift_action(yypParser, (YYCODETYPE)i) < YYNSTATE + YYNRULE) 46 | s->error_code |= (1 << i); 47 | 48 | s->error_pos = s->off; 49 | } 50 | 51 | 52 | input ::= expr(A). { s->path = A; } 53 | 54 | expr(A) ::= T_LABEL(B) T_EQ path(C). { A = B; B->down = C; } 55 | expr(A) ::= path(B). { A = B; } 56 | 57 | path(A) ::= T_ROOT segments(B). { A = alloc_op(T_ROOT, 0, NULL, B); } 58 | path(A) ::= T_THIS segments(B). { A = alloc_op(T_THIS, 0, NULL, B); } 59 | path(A) ::= T_ROOT(B). { A = B; } 60 | path(A) ::= T_THIS(B). { A = B; } 61 | 62 | segments(A) ::= segments(B) segment(C). { A = append_op(B, C); } 63 | segments(A) ::= segment(B). { A = B; } 64 | 65 | segment(A) ::= T_DOT T_LABEL(B). { A = B; } 66 | segment(A) ::= T_DOT T_WILDCARD(B). { A = B; } 67 | segment(A) ::= T_BROPEN union_exps(B) T_BRCLOSE. { A = B; } 68 | 69 | union_exps(A) ::= union_exp(B). { A = B->sibling ? alloc_op(T_UNION, 0, NULL, B) : B; } 70 | 71 | union_exp(A) ::= union_exp(B) T_UNION or_exps(C). { A = append_op(B, C); } 72 | union_exp(A) ::= or_exps(B). { A = B; } 73 | 74 | or_exps(A) ::= or_exp(B). { A = B->sibling ? alloc_op(T_OR, 0, NULL, B) : B; } 75 | 76 | or_exp(A) ::= or_exp(B) T_OR and_exps(C). { A = append_op(B, C); } 77 | or_exp(A) ::= and_exps(B). { A = B; } 78 | 79 | and_exps(A) ::= and_exp(B). { A = B->sibling ? alloc_op(T_AND, 0, NULL, B) : B; } 80 | 81 | and_exp(A) ::= and_exp(B) T_AND cmp_exp(C). { A = append_op(B, C); } 82 | and_exp(A) ::= cmp_exp(B). { A = B; } 83 | 84 | cmp_exp(A) ::= unary_exp(B) T_LT unary_exp(C). { A = alloc_op(T_LT, 0, NULL, B, C); } 85 | cmp_exp(A) ::= unary_exp(B) T_LE unary_exp(C). { A = alloc_op(T_LE, 0, NULL, B, C); } 86 | cmp_exp(A) ::= unary_exp(B) T_GT unary_exp(C). { A = alloc_op(T_GT, 0, NULL, B, C); } 87 | cmp_exp(A) ::= unary_exp(B) T_GE unary_exp(C). { A = alloc_op(T_GE, 0, NULL, B, C); } 88 | cmp_exp(A) ::= unary_exp(B) T_EQ unary_exp(C). { A = alloc_op(T_EQ, 0, NULL, B, C); } 89 | cmp_exp(A) ::= unary_exp(B) T_NE unary_exp(C). { A = alloc_op(T_NE, 0, NULL, B, C); } 90 | cmp_exp(A) ::= unary_exp(B) T_MATCH unary_exp(C). { A = alloc_op(T_MATCH, 0, NULL, B, C); } 91 | cmp_exp(A) ::= unary_exp(B). { A = B; } 92 | 93 | unary_exp(A) ::= T_BOOL(B). { A = B; } 94 | unary_exp(A) ::= T_NUMBER(B). { A = B; } 95 | unary_exp(A) ::= T_STRING(B). { A = B; } 96 | unary_exp(A) ::= T_REGEXP(B). { A = B; } 97 | unary_exp(A) ::= T_WILDCARD(B). { A = B; } 98 | unary_exp(A) ::= T_POPEN or_exps(B) T_PCLOSE. { A = B; } 99 | unary_exp(A) ::= T_NOT unary_exp(B). { A = alloc_op(T_NOT, 0, NULL, B); } 100 | unary_exp(A) ::= path(B). { A = B; } 101 | -------------------------------------------------------------------------------- /matcher.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013-2014 Jo-Philipp Wich 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 "parser.h" 18 | #include "matcher.h" 19 | 20 | 21 | static struct json_object * 22 | jp_match_next(struct jp_opcode *ptr, 23 | struct json_object *root, struct json_object *cur, 24 | jp_match_cb_t cb, void *priv); 25 | 26 | static bool 27 | jp_json_to_op(struct json_object *obj, struct jp_opcode *op) 28 | { 29 | switch (json_object_get_type(obj)) 30 | { 31 | case json_type_boolean: 32 | op->type = T_BOOL; 33 | op->num = json_object_get_boolean(obj); 34 | return true; 35 | 36 | case json_type_int: 37 | op->type = T_NUMBER; 38 | op->num = json_object_get_int(obj); 39 | return true; 40 | 41 | case json_type_string: 42 | op->type = T_STRING; 43 | op->str = (char *)json_object_get_string(obj); 44 | return true; 45 | 46 | default: 47 | return false; 48 | } 49 | } 50 | 51 | static bool 52 | jp_resolve(struct json_object *root, struct json_object *cur, 53 | struct jp_opcode *op, struct jp_opcode *res) 54 | { 55 | struct json_object *val; 56 | 57 | switch (op->type) 58 | { 59 | case T_THIS: 60 | val = jp_match(op, cur, NULL, NULL); 61 | 62 | if (val) 63 | return jp_json_to_op(val, res); 64 | 65 | return false; 66 | 67 | case T_ROOT: 68 | val = jp_match(op, root, NULL, NULL); 69 | 70 | if (val) 71 | return jp_json_to_op(val, res); 72 | 73 | return false; 74 | 75 | default: 76 | *res = *op; 77 | return true; 78 | } 79 | } 80 | 81 | static bool 82 | jp_cmp(struct jp_opcode *op, struct json_object *root, struct json_object *cur) 83 | { 84 | int delta; 85 | struct jp_opcode left, right; 86 | 87 | if (!jp_resolve(root, cur, op->down, &left) || 88 | !jp_resolve(root, cur, op->down->sibling, &right)) 89 | return false; 90 | 91 | if (left.type != right.type) 92 | return false; 93 | 94 | switch (left.type) 95 | { 96 | case T_BOOL: 97 | case T_NUMBER: 98 | delta = left.num - right.num; 99 | break; 100 | 101 | case T_STRING: 102 | delta = strcmp(left.str, right.str); 103 | break; 104 | 105 | default: 106 | return false; 107 | } 108 | 109 | switch (op->type) 110 | { 111 | case T_EQ: 112 | return (delta == 0); 113 | 114 | case T_LT: 115 | return (delta < 0); 116 | 117 | case T_LE: 118 | return (delta <= 0); 119 | 120 | case T_GT: 121 | return (delta > 0); 122 | 123 | case T_GE: 124 | return (delta >= 0); 125 | 126 | case T_NE: 127 | return (delta != 0); 128 | 129 | default: 130 | return false; 131 | } 132 | } 133 | 134 | static bool 135 | jp_regmatch(struct jp_opcode *op, struct json_object *root, struct json_object *cur) 136 | { 137 | struct jp_opcode left, right; 138 | char lbuf[22], rbuf[22], *lval, *rval; 139 | int err, rflags = REG_NOSUB | REG_NEWLINE; 140 | regex_t preg; 141 | 142 | 143 | if (!jp_resolve(root, cur, op->down, &left) || 144 | !jp_resolve(root, cur, op->down->sibling, &right)) 145 | return false; 146 | 147 | if (left.type == T_REGEXP) 148 | { 149 | switch (right.type) 150 | { 151 | case T_BOOL: 152 | lval = right.num ? "true" : "false"; 153 | break; 154 | 155 | case T_NUMBER: 156 | snprintf(lbuf, sizeof(lbuf), "%d", right.num); 157 | lval = lbuf; 158 | break; 159 | 160 | case T_STRING: 161 | lval = right.str; 162 | break; 163 | 164 | default: 165 | return false; 166 | } 167 | 168 | rval = left.str; 169 | rflags = left.num; 170 | } 171 | else 172 | { 173 | switch (left.type) 174 | { 175 | case T_BOOL: 176 | lval = left.num ? "true" : "false"; 177 | break; 178 | 179 | case T_NUMBER: 180 | snprintf(lbuf, sizeof(lbuf), "%d", left.num); 181 | lval = lbuf; 182 | break; 183 | 184 | case T_STRING: 185 | lval = left.str; 186 | break; 187 | 188 | default: 189 | return false; 190 | } 191 | 192 | switch (right.type) 193 | { 194 | case T_BOOL: 195 | rval = right.num ? "true" : "false"; 196 | break; 197 | 198 | case T_NUMBER: 199 | snprintf(rbuf, sizeof(rbuf), "%d", right.num); 200 | rval = rbuf; 201 | break; 202 | 203 | case T_STRING: 204 | rval = right.str; 205 | break; 206 | 207 | case T_REGEXP: 208 | rval = right.str; 209 | rflags = right.num; 210 | break; 211 | 212 | default: 213 | return false; 214 | } 215 | } 216 | 217 | if (regcomp(&preg, rval, rflags)) 218 | return false; 219 | 220 | err = regexec(&preg, lval, 0, NULL, 0); 221 | 222 | regfree(&preg); 223 | 224 | return err ? false : true; 225 | } 226 | 227 | static bool 228 | jp_expr(struct jp_opcode *op, struct json_object *root, struct json_object *cur, 229 | int idx, const char *key, jp_match_cb_t cb, void *priv) 230 | { 231 | struct jp_opcode *sop; 232 | 233 | switch (op->type) 234 | { 235 | case T_WILDCARD: 236 | return true; 237 | 238 | case T_EQ: 239 | case T_NE: 240 | case T_LT: 241 | case T_LE: 242 | case T_GT: 243 | case T_GE: 244 | return jp_cmp(op, root, cur); 245 | 246 | case T_MATCH: 247 | return jp_regmatch(op, root, cur); 248 | 249 | case T_ROOT: 250 | return !!jp_match(op, root, NULL, NULL); 251 | 252 | case T_THIS: 253 | return !!jp_match(op, cur, NULL, NULL); 254 | 255 | case T_NOT: 256 | return !jp_expr(op->down, root, cur, idx, key, cb, priv); 257 | 258 | case T_AND: 259 | for (sop = op->down; sop; sop = sop->sibling) 260 | if (!jp_expr(sop, root, cur, idx, key, cb, priv)) 261 | return false; 262 | return true; 263 | 264 | case T_OR: 265 | case T_UNION: 266 | for (sop = op->down; sop; sop = sop->sibling) 267 | if (jp_expr(sop, root, cur, idx, key, cb, priv)) 268 | return true; 269 | return false; 270 | 271 | case T_STRING: 272 | return (key && !strcmp(op->str, key)); 273 | 274 | case T_NUMBER: 275 | return (idx == op->num); 276 | 277 | default: 278 | return false; 279 | } 280 | } 281 | 282 | static struct json_object * 283 | jp_match_expr(struct jp_opcode *ptr, 284 | struct json_object *root, struct json_object *cur, 285 | jp_match_cb_t cb, void *priv) 286 | { 287 | int idx, len; 288 | struct json_object *tmp, *res = NULL; 289 | 290 | switch (json_object_get_type(cur)) 291 | { 292 | case json_type_object: 293 | ; /* a label can only be part of a statement and a declaration is not a statement */ 294 | json_object_object_foreach(cur, key, val) 295 | { 296 | if (jp_expr(ptr, root, val, -1, key, cb, priv)) 297 | { 298 | tmp = jp_match_next(ptr->sibling, root, val, cb, priv); 299 | 300 | if (tmp && !res) 301 | res = tmp; 302 | } 303 | } 304 | 305 | break; 306 | 307 | case json_type_array: 308 | len = json_object_array_length(cur); 309 | 310 | for (idx = 0; idx < len; idx++) 311 | { 312 | tmp = json_object_array_get_idx(cur, idx); 313 | 314 | if (jp_expr(ptr, root, tmp, idx, NULL, cb, priv)) 315 | { 316 | tmp = jp_match_next(ptr->sibling, root, tmp, cb, priv); 317 | 318 | if (tmp && !res) 319 | res = tmp; 320 | } 321 | } 322 | 323 | break; 324 | 325 | default: 326 | break; 327 | } 328 | 329 | return res; 330 | } 331 | 332 | static struct json_object * 333 | jp_match_next(struct jp_opcode *ptr, 334 | struct json_object *root, struct json_object *cur, 335 | jp_match_cb_t cb, void *priv) 336 | { 337 | int idx; 338 | struct json_object *next = NULL; 339 | 340 | if (!ptr) 341 | { 342 | if (cb) 343 | cb(cur, priv); 344 | 345 | return cur; 346 | } 347 | 348 | switch (ptr->type) 349 | { 350 | case T_STRING: 351 | case T_LABEL: 352 | if (json_object_object_get_ex(cur, ptr->str, &next)) 353 | return jp_match_next(ptr->sibling, root, next, cb, priv); 354 | 355 | break; 356 | 357 | case T_NUMBER: 358 | if (json_object_get_type(cur) == json_type_array) 359 | { 360 | idx = ptr->num; 361 | 362 | if (idx < 0) 363 | idx += json_object_array_length(cur); 364 | 365 | if (idx >= 0) 366 | next = json_object_array_get_idx(cur, idx); 367 | 368 | if (next) 369 | return jp_match_next(ptr->sibling, root, next, cb, priv); 370 | } 371 | 372 | break; 373 | 374 | default: 375 | return jp_match_expr(ptr, root, cur, cb, priv); 376 | } 377 | 378 | return NULL; 379 | } 380 | 381 | struct json_object * 382 | jp_match(struct jp_opcode *path, json_object *jsobj, 383 | jp_match_cb_t cb, void *priv) 384 | { 385 | if (path->type == T_LABEL) 386 | path = path->down; 387 | 388 | return jp_match_next(path->down, jsobj, jsobj, cb, priv); 389 | } 390 | -------------------------------------------------------------------------------- /lexer.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013-2014 Jo-Philipp Wich 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 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #include "ast.h" 24 | #include "lexer.h" 25 | #include "parser.h" 26 | 27 | 28 | struct token { 29 | int type; 30 | const char *pat; 31 | int plen; 32 | int (*parse)(const char *buf, struct jp_opcode *op, struct jp_state *s); 33 | }; 34 | 35 | #define dec(o) \ 36 | ((o) - '0') 37 | 38 | #define hex(x) \ 39 | (((x) >= 'a') ? (10 + (x) - 'a') : \ 40 | (((x) >= 'A') ? (10 + (x) - 'A') : dec(x))) 41 | 42 | /* 43 | * Stores the given codepoint as a utf8 multibyte sequence into the given 44 | * output buffer and substracts the required amount of bytes from the given 45 | * length pointer. 46 | * 47 | * Returns false if the multibyte sequence would not fit into the buffer, 48 | * otherwise true. 49 | */ 50 | 51 | static bool 52 | utf8enc(char **out, int *rem, int code) 53 | { 54 | if (code > 0 && code <= 0x7F) 55 | { 56 | if (*rem < 1) 57 | return false; 58 | 59 | *(*out)++ = code; (*rem)--; 60 | return true; 61 | } 62 | else if (code > 0 && code <= 0x7FF) 63 | { 64 | if (*rem < 2) 65 | return false; 66 | 67 | *(*out)++ = ((code >> 6) & 0x1F) | 0xC0; (*rem)--; 68 | *(*out)++ = ( code & 0x3F) | 0x80; (*rem)--; 69 | return true; 70 | } 71 | else if (code > 0 && code <= 0xFFFF) 72 | { 73 | if (*rem < 3) 74 | return false; 75 | 76 | *(*out)++ = ((code >> 12) & 0x0F) | 0xE0; (*rem)--; 77 | *(*out)++ = ((code >> 6) & 0x3F) | 0x80; (*rem)--; 78 | *(*out)++ = ( code & 0x3F) | 0x80; (*rem)--; 79 | return true; 80 | } 81 | else if (code > 0 && code <= 0x10FFFF) 82 | { 83 | if (*rem < 4) 84 | return false; 85 | 86 | *(*out)++ = ((code >> 18) & 0x07) | 0xF0; (*rem)--; 87 | *(*out)++ = ((code >> 12) & 0x3F) | 0x80; (*rem)--; 88 | *(*out)++ = ((code >> 6) & 0x3F) | 0x80; (*rem)--; 89 | *(*out)++ = ( code & 0x3F) | 0x80; (*rem)--; 90 | return true; 91 | } 92 | 93 | return true; 94 | } 95 | 96 | 97 | /* 98 | * Parses a string literal from the given buffer. 99 | * 100 | * Returns a negative value on error, otherwise the amount of consumed 101 | * characters from the given buffer. 102 | * 103 | * Error values: 104 | * -1 Unterminated string 105 | * -2 Invalid escape sequence 106 | * -3 String literal too long 107 | */ 108 | 109 | static int 110 | parse_string(const char *buf, struct jp_opcode *op, struct jp_state *s) 111 | { 112 | char q = *(buf++); 113 | char str[128] = { 0 }; 114 | char *out = str; 115 | const char *in = buf; 116 | bool esc = false; 117 | int rem = sizeof(str) - 1; 118 | int code; 119 | 120 | while (*in) 121 | { 122 | /* continuation of escape sequence */ 123 | if (esc) 124 | { 125 | /* \uFFFF */ 126 | if (in[0] == 'u') 127 | { 128 | if (isxdigit(in[1]) && isxdigit(in[2]) && 129 | isxdigit(in[3]) && isxdigit(in[4])) 130 | { 131 | if (!utf8enc(&out, &rem, 132 | hex(in[1]) * 16 * 16 * 16 + 133 | hex(in[2]) * 16 * 16 + 134 | hex(in[3]) * 16 + 135 | hex(in[4]))) 136 | { 137 | s->error_pos = s->off + (in - buf); 138 | return -3; 139 | } 140 | 141 | in += 5; 142 | } 143 | else 144 | { 145 | s->error_pos = s->off + (in - buf); 146 | return -2; 147 | } 148 | } 149 | 150 | /* \xFF */ 151 | else if (in[0] == 'x') 152 | { 153 | if (isxdigit(in[1]) && isxdigit(in[2])) 154 | { 155 | if (!utf8enc(&out, &rem, hex(in[1]) * 16 + hex(in[2]))) 156 | { 157 | s->error_pos = s->off + (in - buf); 158 | return -3; 159 | } 160 | 161 | in += 3; 162 | } 163 | else 164 | { 165 | s->error_pos = s->off + (in - buf); 166 | return -2; 167 | } 168 | } 169 | 170 | /* \377, \77 or \7 */ 171 | else if (in[0] >= '0' && in[0] <= '7') 172 | { 173 | /* \377 */ 174 | if (in[1] >= '0' && in[1] <= '7' && 175 | in[2] >= '0' && in[2] <= '7') 176 | { 177 | code = dec(in[0]) * 8 * 8 + 178 | dec(in[1]) * 8 + 179 | dec(in[2]); 180 | 181 | if (code > 255) 182 | { 183 | s->error_pos = s->off + (in - buf); 184 | return -2; 185 | } 186 | 187 | if (!utf8enc(&out, &rem, code)) 188 | { 189 | s->error_pos = s->off + (in - buf); 190 | return -3; 191 | } 192 | 193 | in += 3; 194 | } 195 | 196 | /* \77 */ 197 | else if (in[1] >= '0' && in[1] <= '7') 198 | { 199 | if (!utf8enc(&out, &rem, dec(in[0]) * 8 + dec(in[1]))) 200 | { 201 | s->error_pos = s->off + (in - buf); 202 | return -3; 203 | } 204 | 205 | in += 2; 206 | } 207 | 208 | /* \7 */ 209 | else 210 | { 211 | if (!utf8enc(&out, &rem, dec(in[0]))) 212 | { 213 | s->error_pos = s->off + (in - buf); 214 | return -3; 215 | } 216 | 217 | in += 1; 218 | } 219 | } 220 | 221 | /* single character escape */ 222 | else 223 | { 224 | if (rem-- < 1) 225 | { 226 | s->error_pos = s->off + (in - buf); 227 | return -3; 228 | } 229 | 230 | switch (in[0]) 231 | { 232 | case 'a': *out = '\a'; break; 233 | case 'b': *out = '\b'; break; 234 | case 'e': *out = '\e'; break; 235 | case 'f': *out = '\f'; break; 236 | case 'n': *out = '\n'; break; 237 | case 'r': *out = '\r'; break; 238 | case 't': *out = '\t'; break; 239 | case 'v': *out = '\v'; break; 240 | default: 241 | /* in regexp mode, retain backslash */ 242 | if (q == '/') 243 | { 244 | if (rem-- < 1) 245 | { 246 | s->error_pos = s->off + (in - buf); 247 | return -3; 248 | } 249 | 250 | *out++ = '\\'; 251 | } 252 | 253 | *out = *in; 254 | break; 255 | } 256 | 257 | in++; 258 | out++; 259 | } 260 | 261 | esc = false; 262 | } 263 | 264 | /* begin of escape sequence */ 265 | else if (*in == '\\') 266 | { 267 | in++; 268 | esc = true; 269 | } 270 | 271 | /* terminating quote */ 272 | else if (*in == q) 273 | { 274 | op->str = strdup(str); 275 | return (in - buf) + 2; 276 | } 277 | 278 | /* ordinary char */ 279 | else 280 | { 281 | if (rem-- < 1) 282 | { 283 | s->error_pos = s->off + (in - buf); 284 | return -3; 285 | } 286 | 287 | *out++ = *in++; 288 | } 289 | } 290 | 291 | return -1; 292 | } 293 | 294 | 295 | /* 296 | * Parses a regexp literal from the given buffer. 297 | * 298 | * Returns a negative value on error, otherwise the amount of consumed 299 | * characters from the given buffer. 300 | * 301 | * Error values: 302 | * -1 Unterminated regexp 303 | * -2 Invalid escape sequence 304 | * -3 Regexp literal too long 305 | */ 306 | 307 | static int 308 | parse_regexp(const char *buf, struct jp_opcode *op, struct jp_state *s) 309 | { 310 | int len = parse_string(buf, op, s); 311 | const char *p; 312 | 313 | if (len >= 2) 314 | { 315 | op->num = REG_NOSUB | REG_NEWLINE; 316 | 317 | for (p = buf + len; p; p++) 318 | { 319 | switch (*p) 320 | { 321 | case 'e': 322 | op->num |= REG_EXTENDED; 323 | len++; 324 | break; 325 | 326 | case 'i': 327 | op->num |= REG_ICASE; 328 | len++; 329 | break; 330 | 331 | case 's': 332 | op->num &= ~REG_NEWLINE; 333 | len++; 334 | break; 335 | 336 | default: 337 | return len; 338 | } 339 | } 340 | 341 | } 342 | 343 | return len; 344 | } 345 | 346 | 347 | /* 348 | * Parses a label from the given buffer. 349 | * 350 | * Returns a negative value on error, otherwise the amount of consumed 351 | * characters from the given buffer. 352 | * 353 | * Error values: 354 | * -3 Label too long 355 | */ 356 | 357 | static int 358 | parse_label(const char *buf, struct jp_opcode *op, struct jp_state *s) 359 | { 360 | char str[128] = { 0 }; 361 | char *out = str; 362 | const char *in = buf; 363 | int rem = sizeof(str) - 1; 364 | 365 | while (*in == '_' || isalnum(*in)) 366 | { 367 | if (rem-- < 1) 368 | { 369 | s->error_pos = s->off + (in - buf); 370 | return -3; 371 | } 372 | 373 | *out++ = *in++; 374 | } 375 | 376 | if (!strcmp(str, "true") || !strcmp(str, "false")) 377 | { 378 | op->num = (str[0] == 't'); 379 | op->type = T_BOOL; 380 | } 381 | else 382 | { 383 | op->str = strdup(str); 384 | } 385 | 386 | return (in - buf); 387 | } 388 | 389 | 390 | /* 391 | * Parses a number literal from the given buffer. 392 | * 393 | * Returns a negative value on error, otherwise the amount of consumed 394 | * characters from the given buffer. 395 | * 396 | * Error values: 397 | * -2 Invalid number character 398 | */ 399 | 400 | static int 401 | parse_number(const char *buf, struct jp_opcode *op, struct jp_state *s) 402 | { 403 | char *e; 404 | int n = strtol(buf, &e, 10); 405 | 406 | if (e == buf) 407 | { 408 | s->error_pos = s->off; 409 | return -2; 410 | } 411 | 412 | op->num = n; 413 | 414 | return (e - buf); 415 | } 416 | 417 | static const struct token tokens[] = { 418 | { 0, " ", 1 }, 419 | { 0, "\t", 1 }, 420 | { 0, "\n", 1 }, 421 | { T_LE, "<=", 2 }, 422 | { T_GE, ">=", 2 }, 423 | { T_NE, "!=", 2 }, 424 | { T_AND, "&&", 2 }, 425 | { T_OR, "||", 2 }, 426 | { T_DOT, ".", 1 }, 427 | { T_BROPEN, "[", 1 }, 428 | { T_BRCLOSE, "]", 1 }, 429 | { T_POPEN, "(", 1 }, 430 | { T_PCLOSE, ")", 1 }, 431 | { T_UNION, ",", 1 }, 432 | { T_ROOT, "$", 1 }, 433 | { T_THIS, "@", 1 }, 434 | { T_LT, "<", 1 }, 435 | { T_GT, ">", 1 }, 436 | { T_EQ, "=", 1 }, 437 | { T_MATCH, "~", 1 }, 438 | { T_NOT, "!", 1 }, 439 | { T_WILDCARD, "*", 1 }, 440 | { T_REGEXP, "/", 1, parse_regexp }, 441 | { T_STRING, "'", 1, parse_string }, 442 | { T_STRING, "\"", 1, parse_string }, 443 | { T_LABEL, "_", 1, parse_label }, 444 | { T_LABEL, "az", 0, parse_label }, 445 | { T_LABEL, "AZ", 0, parse_label }, 446 | { T_NUMBER, "-", 1, parse_number }, 447 | { T_NUMBER, "09", 0, parse_number }, 448 | }; 449 | 450 | const char *tokennames[25] = { 451 | [0] = "End of file", 452 | [T_AND] = "'&&'", 453 | [T_OR] = "'||'", 454 | [T_UNION] = "','", 455 | [T_EQ] = "'='", 456 | [T_NE] = "'!='", 457 | [T_GT] = "'>'", 458 | [T_GE] = "'>='", 459 | [T_LT] = "'<'", 460 | [T_LE] = "'<='", 461 | [T_MATCH] = "'~'", 462 | [T_NOT] = "'!'", 463 | [T_LABEL] = "Label", 464 | [T_ROOT] = "'$'", 465 | [T_THIS] = "'@'", 466 | [T_DOT] = "'.'", 467 | [T_WILDCARD] = "'*'", 468 | [T_REGEXP] = "/.../", 469 | [T_BROPEN] = "'['", 470 | [T_BRCLOSE] = "']'", 471 | [T_BOOL] = "Bool", 472 | [T_NUMBER] = "Number", 473 | [T_STRING] = "String", 474 | [T_POPEN] = "'('", 475 | [T_PCLOSE] = "')'", 476 | }; 477 | 478 | 479 | static int 480 | match_token(const char *ptr, struct jp_opcode *op, struct jp_state *s) 481 | { 482 | int i; 483 | const struct token *tok; 484 | 485 | for (i = 0, tok = &tokens[0]; 486 | i < sizeof(tokens) / sizeof(tokens[0]); 487 | i++, tok = &tokens[i]) 488 | { 489 | if ((tok->plen > 0 && !strncmp(ptr, tok->pat, tok->plen)) || 490 | (tok->plen == 0 && *ptr >= tok->pat[0] && *ptr <= tok->pat[1])) 491 | { 492 | op->type = tok->type; 493 | 494 | if (tok->parse) 495 | return tok->parse(ptr, op, s); 496 | 497 | return tok->plen; 498 | } 499 | } 500 | 501 | s->error_pos = s->off; 502 | return -4; 503 | } 504 | 505 | struct jp_opcode * 506 | jp_get_token(struct jp_state *s, const char *input, int *mlen) 507 | { 508 | struct jp_opcode op = { 0 }; 509 | 510 | *mlen = match_token(input, &op, s); 511 | 512 | if (*mlen < 0) 513 | { 514 | s->error_code = *mlen; 515 | return NULL; 516 | } 517 | else if (op.type == 0) 518 | { 519 | return NULL; 520 | } 521 | 522 | return jp_alloc_op(s, op.type, op.num, op.str, NULL); 523 | } 524 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013-2014 Jo-Philipp Wich 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 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #ifdef JSONC 24 | #include 25 | #else 26 | #include 27 | #endif 28 | 29 | #include 30 | 31 | #include "lexer.h" 32 | #include "parser.h" 33 | #include "matcher.h" 34 | 35 | 36 | struct match_item { 37 | struct json_object *jsobj; 38 | struct list_head list; 39 | }; 40 | 41 | static void 42 | print_usage(char *app) 43 | { 44 | printf( 45 | "== Usage ==\n\n" 46 | " # %s [-a] [-i | -s \"json...\"] {-t | -e }\n" 47 | " -q Quiet, no errors are printed\n" 48 | " -h, --help Print this help\n" 49 | " -a Implicitely treat input as array, useful for JSON logs\n" 50 | " -i path Specify a JSON file to parse\n" 51 | " -s \"json\" Specify a JSON string to parse\n" 52 | " -l limit Specify max number of results to show\n" 53 | " -F separator Specify a field separator when using export\n" 54 | " -t Print the type of values matched by pattern\n" 55 | " -e Print the values matched by pattern\n" 56 | " -e VAR= Serialize matched value for shell \"eval\"\n\n" 57 | "== Patterns ==\n\n" 58 | " Patterns are JsonPath: http://goessner.net/articles/JsonPath/\n" 59 | " This tool implements $, @, [], * and the union operator ','\n" 60 | " plus the usual expressions and literals.\n" 61 | " It does not support the recursive child search operator '..' or\n" 62 | " the '?()' and '()' filter expressions as those would require a\n" 63 | " complete JavaScript engine to support them.\n\n" 64 | "== Examples ==\n\n" 65 | " Display the first IPv4 address on lan:\n" 66 | " # ifstatus lan | %s -e '@[\"ipv4-address\"][0].address'\n\n" 67 | " Extract the release string from the board information:\n" 68 | " # ubus call system board | %s -e '@.release.description'\n\n" 69 | " Find all interfaces which are up:\n" 70 | " # ubus call network.interface dump | \\\n" 71 | " %s -e '@.interface[@.up=true].interface'\n\n" 72 | " Export br-lan traffic counters for shell eval:\n" 73 | " # devstatus br-lan | %s -e 'RX=@.statistics.rx_bytes' \\\n" 74 | " -e 'TX=@.statistics.tx_bytes'\n", 75 | app, app, app, app, app); 76 | } 77 | 78 | static struct json_object * 79 | parse_json_chunk(struct json_tokener *tok, struct json_object *array, 80 | const char *buf, size_t len, enum json_tokener_error *err) 81 | { 82 | struct json_object *obj = NULL; 83 | 84 | while (len) 85 | { 86 | obj = json_tokener_parse_ex(tok, buf, len); 87 | *err = json_tokener_get_error(tok); 88 | 89 | if (*err == json_tokener_success) 90 | { 91 | if (array) 92 | { 93 | json_object_array_add(array, obj); 94 | } 95 | else 96 | { 97 | break; 98 | } 99 | } 100 | else if (*err != json_tokener_continue) 101 | { 102 | break; 103 | } 104 | 105 | buf += tok->char_offset; 106 | len -= tok->char_offset; 107 | } 108 | 109 | return obj; 110 | } 111 | 112 | static struct json_object * 113 | parse_json(FILE *fd, const char *source, const char **error, bool array_mode) 114 | { 115 | size_t len; 116 | char buf[256]; 117 | struct json_object *obj = NULL, *array = NULL; 118 | struct json_tokener *tok = json_tokener_new(); 119 | enum json_tokener_error err = json_tokener_continue; 120 | 121 | if (!tok) 122 | { 123 | *error = "Out of memory"; 124 | return NULL; 125 | } 126 | 127 | if (array_mode) 128 | { 129 | array = json_object_new_array(); 130 | 131 | if (!array) 132 | { 133 | json_tokener_free(tok); 134 | *error = "Out of memory"; 135 | return NULL; 136 | } 137 | } 138 | 139 | if (source) 140 | { 141 | obj = parse_json_chunk(tok, array, source, strlen(source), &err); 142 | } 143 | else 144 | { 145 | while ((len = fread(buf, 1, sizeof(buf), fd)) > 0) 146 | { 147 | obj = parse_json_chunk(tok, array, buf, len, &err); 148 | 149 | if ((err == json_tokener_success && array_mode == false) || 150 | (err != json_tokener_continue && err != json_tokener_success)) 151 | break; 152 | } 153 | } 154 | 155 | json_tokener_free(tok); 156 | 157 | if (err) 158 | { 159 | if (err == json_tokener_continue) 160 | err = json_tokener_error_parse_eof; 161 | 162 | *error = json_tokener_error_desc(err); 163 | return NULL; 164 | } 165 | 166 | return array ? array : obj; 167 | } 168 | 169 | static void 170 | print_string(const char *s) 171 | { 172 | const char *p; 173 | 174 | printf("'"); 175 | 176 | for (p = s; *p; p++) 177 | { 178 | if (*p == '\'') 179 | printf("'\"'\"'"); 180 | else 181 | printf("%c", *p); 182 | } 183 | 184 | printf("'"); 185 | } 186 | 187 | static void 188 | print_separator(const char *sep, int *sc, int sl) 189 | { 190 | if (*sc > 0) 191 | { 192 | switch (sep[(*sc - 1) % sl]) 193 | { 194 | case '"': 195 | printf("'\"'"); 196 | break; 197 | 198 | case '\'': 199 | printf("\"'\""); 200 | break; 201 | 202 | case ' ': 203 | printf("\\ "); 204 | break; 205 | 206 | default: 207 | printf("%c", sep[(*sc - 1) % sl]); 208 | } 209 | } 210 | 211 | (*sc)++; 212 | } 213 | 214 | static void 215 | export_value(struct list_head *matches, const char *prefix, const char *sep, 216 | int limit) 217 | { 218 | int n, len; 219 | int sc = 0, sl = strlen(sep); 220 | struct match_item *item; 221 | 222 | if (list_empty(matches)) 223 | return; 224 | 225 | if (prefix) 226 | { 227 | printf("export %s=", prefix); 228 | 229 | list_for_each_entry(item, matches, list) 230 | { 231 | if (limit-- <= 0) 232 | break; 233 | 234 | switch (json_object_get_type(item->jsobj)) 235 | { 236 | case json_type_object: 237 | ; /* a label can only be part of a statement */ 238 | json_object_object_foreach(item->jsobj, key, val) 239 | { 240 | if (!val) 241 | continue; 242 | 243 | print_separator(sep, &sc, sl); 244 | print_string(key); 245 | } 246 | break; 247 | 248 | case json_type_array: 249 | for (n = 0, len = json_object_array_length(item->jsobj); 250 | n < len; n++) 251 | { 252 | print_separator(sep, &sc, sl); 253 | printf("%d", n); 254 | } 255 | break; 256 | 257 | case json_type_boolean: 258 | print_separator(sep, &sc, sl); 259 | printf("%d", json_object_get_boolean(item->jsobj)); 260 | break; 261 | 262 | case json_type_int: 263 | print_separator(sep, &sc, sl); 264 | printf("%" PRId64, json_object_get_int64(item->jsobj)); 265 | break; 266 | 267 | case json_type_double: 268 | print_separator(sep, &sc, sl); 269 | printf("%f", json_object_get_double(item->jsobj)); 270 | break; 271 | 272 | case json_type_string: 273 | print_separator(sep, &sc, sl); 274 | print_string(json_object_get_string(item->jsobj)); 275 | break; 276 | 277 | case json_type_null: 278 | break; 279 | } 280 | } 281 | 282 | printf("; "); 283 | } 284 | else 285 | { 286 | list_for_each_entry(item, matches, list) 287 | { 288 | if (limit-- <= 0) 289 | break; 290 | 291 | switch (json_object_get_type(item->jsobj)) 292 | { 293 | case json_type_object: 294 | case json_type_array: 295 | case json_type_boolean: 296 | case json_type_int: 297 | case json_type_double: 298 | printf("%s\n", json_object_to_json_string(item->jsobj)); 299 | break; 300 | 301 | case json_type_string: 302 | printf("%s\n", json_object_get_string(item->jsobj)); 303 | break; 304 | 305 | case json_type_null: 306 | break; 307 | } 308 | } 309 | } 310 | } 311 | 312 | static void 313 | export_type(struct list_head *matches, const char *prefix, int limit) 314 | { 315 | bool first = true; 316 | struct match_item *item; 317 | const char *types[] = { 318 | "null", 319 | "boolean", 320 | "double", 321 | "int", 322 | "object", 323 | "array", 324 | "string" 325 | }; 326 | 327 | if (list_empty(matches)) 328 | return; 329 | 330 | if (prefix) 331 | printf("export %s=", prefix); 332 | 333 | list_for_each_entry(item, matches, list) 334 | { 335 | if (!first) 336 | printf("\\ "); 337 | 338 | if (limit-- <= 0) 339 | break; 340 | 341 | printf("%s", types[json_object_get_type(item->jsobj)]); 342 | first = false; 343 | } 344 | 345 | if (prefix) 346 | printf("; "); 347 | else 348 | printf("\n"); 349 | } 350 | 351 | static void 352 | match_cb(struct json_object *res, void *priv) 353 | { 354 | struct list_head *h = priv; 355 | struct match_item *i = calloc(1, sizeof(*i)); 356 | 357 | if (i) 358 | { 359 | i->jsobj = res; 360 | list_add_tail(&i->list, h); 361 | } 362 | } 363 | 364 | static void 365 | print_error(struct jp_state *state, char *expr) 366 | { 367 | int i; 368 | bool first = true; 369 | 370 | fprintf(stderr, "Syntax error: "); 371 | 372 | switch (state->error_code) 373 | { 374 | case -4: 375 | fprintf(stderr, "Unexpected character\n"); 376 | break; 377 | 378 | case -3: 379 | fprintf(stderr, "String or label literal too long\n"); 380 | break; 381 | 382 | case -2: 383 | fprintf(stderr, "Invalid escape sequence\n"); 384 | break; 385 | 386 | case -1: 387 | fprintf(stderr, "Unterminated string\n"); 388 | break; 389 | 390 | default: 391 | for (i = 0; i < sizeof(state->error_code) * 8; i++) 392 | { 393 | if (state->error_code & (1 << i)) 394 | { 395 | fprintf(stderr, 396 | first ? "Expecting %s" : " or %s", tokennames[i]); 397 | 398 | first = false; 399 | } 400 | } 401 | 402 | fprintf(stderr, "\n"); 403 | break; 404 | } 405 | 406 | fprintf(stderr, "In expression %s\n", expr); 407 | fprintf(stderr, "Near here ----"); 408 | 409 | for (i = 0; i < state->error_pos; i++) 410 | fprintf(stderr, "-"); 411 | 412 | fprintf(stderr, "^\n"); 413 | } 414 | 415 | static bool 416 | filter_json(int opt, struct json_object *jsobj, char *expr, const char *sep, 417 | int limit) 418 | { 419 | struct jp_state *state; 420 | const char *prefix = NULL; 421 | struct list_head matches; 422 | struct match_item *item, *tmp; 423 | struct json_object *res = NULL; 424 | 425 | state = jp_parse(expr); 426 | 427 | if (!state) 428 | { 429 | fprintf(stderr, "Out of memory\n"); 430 | goto out; 431 | } 432 | else if (state->error_code) 433 | { 434 | print_error(state, expr); 435 | goto out; 436 | } 437 | 438 | INIT_LIST_HEAD(&matches); 439 | 440 | res = jp_match(state->path, jsobj, match_cb, &matches); 441 | prefix = (state->path->type == T_LABEL) ? state->path->str : NULL; 442 | 443 | switch (opt) 444 | { 445 | case 't': 446 | export_type(&matches, prefix, limit); 447 | break; 448 | 449 | default: 450 | export_value(&matches, prefix, sep, limit); 451 | break; 452 | } 453 | 454 | list_for_each_entry_safe(item, tmp, &matches, list) 455 | free(item); 456 | 457 | out: 458 | if (state) 459 | jp_free(state); 460 | 461 | return !!res; 462 | } 463 | 464 | int main(int argc, char **argv) 465 | { 466 | bool array_mode = false; 467 | int opt, rv = 0, limit = 0x7FFFFFFF; 468 | FILE *input = stdin; 469 | struct json_object *jsobj = NULL; 470 | const char *jserr = NULL, *source = NULL, *separator = " "; 471 | 472 | if (argc == 1) 473 | { 474 | print_usage(argv[0]); 475 | goto out; 476 | } 477 | 478 | while ((opt = getopt(argc, argv, "ahi:s:e:t:F:l:q")) != -1) 479 | { 480 | switch (opt) 481 | { 482 | case 'a': 483 | array_mode = true; 484 | break; 485 | 486 | case 'h': 487 | print_usage(argv[0]); 488 | goto out; 489 | 490 | case 'i': 491 | input = fopen(optarg, "r"); 492 | 493 | if (!input) 494 | { 495 | fprintf(stderr, "Failed to open %s: %s\n", 496 | optarg, strerror(errno)); 497 | 498 | rv = 125; 499 | goto out; 500 | } 501 | 502 | break; 503 | 504 | case 's': 505 | source = optarg; 506 | break; 507 | 508 | case 'F': 509 | if (optarg && *optarg) 510 | separator = optarg; 511 | break; 512 | 513 | case 'l': 514 | limit = atoi(optarg); 515 | break; 516 | 517 | case 't': 518 | case 'e': 519 | if (!jsobj) 520 | { 521 | jsobj = parse_json(input, source, &jserr, array_mode); 522 | 523 | if (!jsobj) 524 | { 525 | fprintf(stderr, "Failed to parse json data: %s\n", 526 | jserr); 527 | 528 | rv = 126; 529 | goto out; 530 | } 531 | } 532 | 533 | if (!filter_json(opt, jsobj, optarg, separator, limit)) 534 | rv = 1; 535 | 536 | break; 537 | 538 | case 'q': 539 | fclose(stderr); 540 | break; 541 | } 542 | } 543 | 544 | out: 545 | if (jsobj) 546 | json_object_put(jsobj); 547 | 548 | if (input && input != stdin) 549 | fclose(input); 550 | 551 | return rv; 552 | } 553 | -------------------------------------------------------------------------------- /contrib/lempar.c: -------------------------------------------------------------------------------- 1 | /* Driver template for the LEMON parser generator. 2 | ** The author disclaims copyright to this source code. 3 | */ 4 | /* First off, code is included that follows the "include" declaration 5 | ** in the input grammar file. */ 6 | #include 7 | %% 8 | /* Next is all token values, in a form suitable for use by makeheaders. 9 | ** This section will be null unless lemon is run with the -m switch. 10 | */ 11 | /* 12 | ** These constants (all generated automatically by the parser generator) 13 | ** specify the various kinds of tokens (terminals) that the parser 14 | ** understands. 15 | ** 16 | ** Each symbol here is a terminal symbol in the grammar. 17 | */ 18 | %% 19 | /* Make sure the INTERFACE macro is defined. 20 | */ 21 | #ifndef INTERFACE 22 | # define INTERFACE 1 23 | #endif 24 | /* The next thing included is series of defines which control 25 | ** various aspects of the generated parser. 26 | ** YYCODETYPE is the data type used for storing terminal 27 | ** and nonterminal numbers. "unsigned char" is 28 | ** used if there are fewer than 250 terminals 29 | ** and nonterminals. "int" is used otherwise. 30 | ** YYNOCODE is a number of type YYCODETYPE which corresponds 31 | ** to no legal terminal or nonterminal number. This 32 | ** number is used to fill in empty slots of the hash 33 | ** table. 34 | ** YYFALLBACK If defined, this indicates that one or more tokens 35 | ** have fall-back values which should be used if the 36 | ** original value of the token will not parse. 37 | ** YYACTIONTYPE is the data type used for storing terminal 38 | ** and nonterminal numbers. "unsigned char" is 39 | ** used if there are fewer than 250 rules and 40 | ** states combined. "int" is used otherwise. 41 | ** ParseTOKENTYPE is the data type used for minor tokens given 42 | ** directly to the parser from the tokenizer. 43 | ** YYMINORTYPE is the data type used for all minor tokens. 44 | ** This is typically a union of many types, one of 45 | ** which is ParseTOKENTYPE. The entry in the union 46 | ** for base tokens is called "yy0". 47 | ** YYSTACKDEPTH is the maximum depth of the parser's stack. If 48 | ** zero the stack is dynamically sized using realloc() 49 | ** ParseARG_SDECL A static variable declaration for the %extra_argument 50 | ** ParseARG_PDECL A parameter declaration for the %extra_argument 51 | ** ParseARG_STORE Code to store %extra_argument into yypParser 52 | ** ParseARG_FETCH Code to extract %extra_argument from yypParser 53 | ** YYNSTATE the combined number of states. 54 | ** YYNRULE the number of rules in the grammar 55 | ** YYERRORSYMBOL is the code number of the error symbol. If not 56 | ** defined, then do no error processing. 57 | */ 58 | %% 59 | #define YY_NO_ACTION (YYNSTATE+YYNRULE+2) 60 | #define YY_ACCEPT_ACTION (YYNSTATE+YYNRULE+1) 61 | #define YY_ERROR_ACTION (YYNSTATE+YYNRULE) 62 | 63 | /* The yyzerominor constant is used to initialize instances of 64 | ** YYMINORTYPE objects to zero. */ 65 | static const YYMINORTYPE yyzerominor = { 0 }; 66 | 67 | /* Define the yytestcase() macro to be a no-op if is not already defined 68 | ** otherwise. 69 | ** 70 | ** Applications can choose to define yytestcase() in the %include section 71 | ** to a macro that can assist in verifying code coverage. For production 72 | ** code the yytestcase() macro should be turned off. But it is useful 73 | ** for testing. 74 | */ 75 | #ifndef yytestcase 76 | # define yytestcase(X) 77 | #endif 78 | 79 | 80 | /* Next are the tables used to determine what action to take based on the 81 | ** current state and lookahead token. These tables are used to implement 82 | ** functions that take a state number and lookahead value and return an 83 | ** action integer. 84 | ** 85 | ** Suppose the action integer is N. Then the action is determined as 86 | ** follows 87 | ** 88 | ** 0 <= N < YYNSTATE Shift N. That is, push the lookahead 89 | ** token onto the stack and goto state N. 90 | ** 91 | ** YYNSTATE <= N < YYNSTATE+YYNRULE Reduce by rule N-YYNSTATE. 92 | ** 93 | ** N == YYNSTATE+YYNRULE A syntax error has occurred. 94 | ** 95 | ** N == YYNSTATE+YYNRULE+1 The parser accepts its input. 96 | ** 97 | ** N == YYNSTATE+YYNRULE+2 No such action. Denotes unused 98 | ** slots in the yy_action[] table. 99 | ** 100 | ** The action table is constructed as a single large table named yy_action[]. 101 | ** Given state S and lookahead X, the action is computed as 102 | ** 103 | ** yy_action[ yy_shift_ofst[S] + X ] 104 | ** 105 | ** If the index value yy_shift_ofst[S]+X is out of range or if the value 106 | ** yy_lookahead[yy_shift_ofst[S]+X] is not equal to X or if yy_shift_ofst[S] 107 | ** is equal to YY_SHIFT_USE_DFLT, it means that the action is not in the table 108 | ** and that yy_default[S] should be used instead. 109 | ** 110 | ** The formula above is for computing the action when the lookahead is 111 | ** a terminal symbol. If the lookahead is a non-terminal (as occurs after 112 | ** a reduce action) then the yy_reduce_ofst[] array is used in place of 113 | ** the yy_shift_ofst[] array and YY_REDUCE_USE_DFLT is used in place of 114 | ** YY_SHIFT_USE_DFLT. 115 | ** 116 | ** The following are the tables generated in this section: 117 | ** 118 | ** yy_action[] A single table containing all actions. 119 | ** yy_lookahead[] A table containing the lookahead for each entry in 120 | ** yy_action. Used to detect hash collisions. 121 | ** yy_shift_ofst[] For each state, the offset into yy_action for 122 | ** shifting terminals. 123 | ** yy_reduce_ofst[] For each state, the offset into yy_action for 124 | ** shifting non-terminals after a reduce. 125 | ** yy_default[] Default action for each state. 126 | */ 127 | %% 128 | 129 | /* The next table maps tokens into fallback tokens. If a construct 130 | ** like the following: 131 | ** 132 | ** %fallback ID X Y Z. 133 | ** 134 | ** appears in the grammar, then ID becomes a fallback token for X, Y, 135 | ** and Z. Whenever one of the tokens X, Y, or Z is input to the parser 136 | ** but it does not parse, the type of the token is changed to ID and 137 | ** the parse is retried before an error is thrown. 138 | */ 139 | #ifdef YYFALLBACK 140 | static const YYCODETYPE yyFallback[] = { 141 | %% 142 | }; 143 | #endif /* YYFALLBACK */ 144 | 145 | /* The following structure represents a single element of the 146 | ** parser's stack. Information stored includes: 147 | ** 148 | ** + The state number for the parser at this level of the stack. 149 | ** 150 | ** + The value of the token stored at this level of the stack. 151 | ** (In other words, the "major" token.) 152 | ** 153 | ** + The semantic value stored at this level of the stack. This is 154 | ** the information used by the action routines in the grammar. 155 | ** It is sometimes called the "minor" token. 156 | */ 157 | struct yyStackEntry { 158 | YYACTIONTYPE stateno; /* The state-number */ 159 | YYCODETYPE major; /* The major token value. This is the code 160 | ** number for the token at this stack level */ 161 | YYMINORTYPE minor; /* The user-supplied minor token value. This 162 | ** is the value of the token */ 163 | }; 164 | typedef struct yyStackEntry yyStackEntry; 165 | 166 | /* The state of the parser is completely contained in an instance of 167 | ** the following structure */ 168 | struct yyParser { 169 | int yyidx; /* Index of top element in stack */ 170 | #ifdef YYTRACKMAXSTACKDEPTH 171 | int yyidxMax; /* Maximum value of yyidx */ 172 | #endif 173 | int yyerrcnt; /* Shifts left before out of the error */ 174 | ParseARG_SDECL /* A place to hold %extra_argument */ 175 | #if YYSTACKDEPTH<=0 176 | int yystksz; /* Current side of the stack */ 177 | yyStackEntry *yystack; /* The parser's stack */ 178 | #else 179 | yyStackEntry yystack[YYSTACKDEPTH]; /* The parser's stack */ 180 | #endif 181 | }; 182 | typedef struct yyParser yyParser; 183 | 184 | #ifndef NDEBUG 185 | #include 186 | static FILE *yyTraceFILE = 0; 187 | static char *yyTracePrompt = 0; 188 | #endif /* NDEBUG */ 189 | 190 | #ifndef NDEBUG 191 | /* 192 | ** Turn parser tracing on by giving a stream to which to write the trace 193 | ** and a prompt to preface each trace message. Tracing is turned off 194 | ** by making either argument NULL 195 | ** 196 | ** Inputs: 197 | **
    198 | **
  • A FILE* to which trace output should be written. 199 | ** If NULL, then tracing is turned off. 200 | **
  • A prefix string written at the beginning of every 201 | ** line of trace output. If NULL, then tracing is 202 | ** turned off. 203 | **
204 | ** 205 | ** Outputs: 206 | ** None. 207 | */ 208 | void ParseTrace(FILE *TraceFILE, char *zTracePrompt); 209 | void ParseTrace(FILE *TraceFILE, char *zTracePrompt){ 210 | yyTraceFILE = TraceFILE; 211 | yyTracePrompt = zTracePrompt; 212 | if( yyTraceFILE==0 ) yyTracePrompt = 0; 213 | else if( yyTracePrompt==0 ) yyTraceFILE = 0; 214 | } 215 | #endif /* NDEBUG */ 216 | 217 | #ifndef NDEBUG 218 | /* For tracing shifts, the names of all terminals and nonterminals 219 | ** are required. The following table supplies these names */ 220 | static const char *const yyTokenName[] = { 221 | %% 222 | }; 223 | #endif /* NDEBUG */ 224 | 225 | #ifndef NDEBUG 226 | /* For tracing reduce actions, the names of all rules are required. 227 | */ 228 | static const char *const yyRuleName[] = { 229 | %% 230 | }; 231 | #endif /* NDEBUG */ 232 | 233 | 234 | #if YYSTACKDEPTH<=0 235 | /* 236 | ** Try to increase the size of the parser stack. 237 | */ 238 | static void yyGrowStack(yyParser *p){ 239 | int newSize; 240 | yyStackEntry *pNew; 241 | 242 | newSize = p->yystksz*2 + 100; 243 | pNew = realloc(p->yystack, newSize*sizeof(pNew[0])); 244 | if( pNew ){ 245 | p->yystack = pNew; 246 | p->yystksz = newSize; 247 | #ifndef NDEBUG 248 | if( yyTraceFILE ){ 249 | fprintf(yyTraceFILE,"%sStack grows to %d entries!\n", 250 | yyTracePrompt, p->yystksz); 251 | } 252 | #endif 253 | } 254 | } 255 | #endif 256 | 257 | /* 258 | ** This function allocates a new parser. 259 | ** The only argument is a pointer to a function which works like 260 | ** malloc. 261 | ** 262 | ** Inputs: 263 | ** A pointer to the function used to allocate memory. 264 | ** 265 | ** Outputs: 266 | ** A pointer to a parser. This pointer is used in subsequent calls 267 | ** to Parse and ParseFree. 268 | */ 269 | void *ParseAlloc(void *(*mallocProc)(size_t)){ 270 | yyParser *pParser; 271 | pParser = (yyParser*)(*mallocProc)( (size_t)sizeof(yyParser) ); 272 | if( pParser ){ 273 | pParser->yyidx = -1; 274 | #ifdef YYTRACKMAXSTACKDEPTH 275 | pParser->yyidxMax = 0; 276 | #endif 277 | #if YYSTACKDEPTH<=0 278 | pParser->yystack = NULL; 279 | pParser->yystksz = 0; 280 | yyGrowStack(pParser); 281 | #endif 282 | } 283 | return pParser; 284 | } 285 | 286 | /* The following function deletes the value associated with a 287 | ** symbol. The symbol can be either a terminal or nonterminal. 288 | ** "yymajor" is the symbol code, and "yypminor" is a pointer to 289 | ** the value. 290 | */ 291 | static void yy_destructor( 292 | yyParser *yypParser, /* The parser */ 293 | YYCODETYPE yymajor, /* Type code for object to destroy */ 294 | YYMINORTYPE *yypminor /* The object to be destroyed */ 295 | ){ 296 | ParseARG_FETCH; 297 | switch( yymajor ){ 298 | /* Here is inserted the actions which take place when a 299 | ** terminal or non-terminal is destroyed. This can happen 300 | ** when the symbol is popped from the stack during a 301 | ** reduce or during error processing or when a parser is 302 | ** being destroyed before it is finished parsing. 303 | ** 304 | ** Note: during a reduce, the only symbols destroyed are those 305 | ** which appear on the RHS of the rule, but which are not used 306 | ** inside the C code. 307 | */ 308 | %% 309 | default: break; /* If no destructor action specified: do nothing */ 310 | } 311 | } 312 | 313 | /* 314 | ** Pop the parser's stack once. 315 | ** 316 | ** If there is a destructor routine associated with the token which 317 | ** is popped from the stack, then call it. 318 | ** 319 | ** Return the major token number for the symbol popped. 320 | */ 321 | static int yy_pop_parser_stack(yyParser *pParser){ 322 | YYCODETYPE yymajor; 323 | yyStackEntry *yytos = &pParser->yystack[pParser->yyidx]; 324 | 325 | if( pParser->yyidx<0 ) return 0; 326 | #ifndef NDEBUG 327 | if( yyTraceFILE && pParser->yyidx>=0 ){ 328 | fprintf(yyTraceFILE,"%sPopping %s\n", 329 | yyTracePrompt, 330 | yyTokenName[yytos->major]); 331 | } 332 | #endif 333 | yymajor = yytos->major; 334 | yy_destructor(pParser, yymajor, &yytos->minor); 335 | pParser->yyidx--; 336 | return yymajor; 337 | } 338 | 339 | /* 340 | ** Deallocate and destroy a parser. Destructors are all called for 341 | ** all stack elements before shutting the parser down. 342 | ** 343 | ** Inputs: 344 | **
    345 | **
  • A pointer to the parser. This should be a pointer 346 | ** obtained from ParseAlloc. 347 | **
  • A pointer to a function used to reclaim memory obtained 348 | ** from malloc. 349 | **
350 | */ 351 | void ParseFree( 352 | void *p, /* The parser to be deleted */ 353 | void (*freeProc)(void*) /* Function used to reclaim memory */ 354 | ){ 355 | yyParser *pParser = (yyParser*)p; 356 | if( pParser==0 ) return; 357 | while( pParser->yyidx>=0 ) yy_pop_parser_stack(pParser); 358 | #if YYSTACKDEPTH<=0 359 | free(pParser->yystack); 360 | #endif 361 | (*freeProc)((void*)pParser); 362 | } 363 | 364 | /* 365 | ** Return the peak depth of the stack for a parser. 366 | */ 367 | #ifdef YYTRACKMAXSTACKDEPTH 368 | int ParseStackPeak(void *p){ 369 | yyParser *pParser = (yyParser*)p; 370 | return pParser->yyidxMax; 371 | } 372 | #endif 373 | 374 | /* 375 | ** Find the appropriate action for a parser given the terminal 376 | ** look-ahead token iLookAhead. 377 | ** 378 | ** If the look-ahead token is YYNOCODE, then check to see if the action is 379 | ** independent of the look-ahead. If it is, return the action, otherwise 380 | ** return YY_NO_ACTION. 381 | */ 382 | static int yy_find_shift_action( 383 | yyParser *pParser, /* The parser */ 384 | YYCODETYPE iLookAhead /* The look-ahead token */ 385 | ){ 386 | int i; 387 | int stateno = pParser->yystack[pParser->yyidx].stateno; 388 | 389 | if( stateno>YY_SHIFT_COUNT 390 | || (i = yy_shift_ofst[stateno])==YY_SHIFT_USE_DFLT ){ 391 | return yy_default[stateno]; 392 | } 393 | assert( iLookAhead!=YYNOCODE ); 394 | i += iLookAhead; 395 | if( i<0 || i>=YY_ACTTAB_COUNT || yy_lookahead[i]!=iLookAhead ){ 396 | if( iLookAhead>0 ){ 397 | #ifdef YYFALLBACK 398 | YYCODETYPE iFallback; /* Fallback token */ 399 | if( iLookAhead %s\n", 404 | yyTracePrompt, yyTokenName[iLookAhead], yyTokenName[iFallback]); 405 | } 406 | #endif 407 | return yy_find_shift_action(pParser, iFallback); 408 | } 409 | #endif 410 | #ifdef YYWILDCARD 411 | { 412 | int j = i - iLookAhead + YYWILDCARD; 413 | if( 414 | #if YY_SHIFT_MIN+YYWILDCARD<0 415 | j>=0 && 416 | #endif 417 | #if YY_SHIFT_MAX+YYWILDCARD>=YY_ACTTAB_COUNT 418 | j %s\n", 425 | yyTracePrompt, yyTokenName[iLookAhead], yyTokenName[YYWILDCARD]); 426 | } 427 | #endif /* NDEBUG */ 428 | return yy_action[j]; 429 | } 430 | } 431 | #endif /* YYWILDCARD */ 432 | } 433 | return yy_default[stateno]; 434 | }else{ 435 | return yy_action[i]; 436 | } 437 | } 438 | 439 | /* 440 | ** Find the appropriate action for a parser given the non-terminal 441 | ** look-ahead token iLookAhead. 442 | ** 443 | ** If the look-ahead token is YYNOCODE, then check to see if the action is 444 | ** independent of the look-ahead. If it is, return the action, otherwise 445 | ** return YY_NO_ACTION. 446 | */ 447 | static int yy_find_reduce_action( 448 | int stateno, /* Current state number */ 449 | YYCODETYPE iLookAhead /* The look-ahead token */ 450 | ){ 451 | int i; 452 | #ifdef YYERRORSYMBOL 453 | if( stateno>YY_REDUCE_COUNT ){ 454 | return yy_default[stateno]; 455 | } 456 | #else 457 | assert( stateno<=YY_REDUCE_COUNT ); 458 | #endif 459 | i = yy_reduce_ofst[stateno]; 460 | assert( i!=YY_REDUCE_USE_DFLT ); 461 | assert( iLookAhead!=YYNOCODE ); 462 | i += iLookAhead; 463 | #ifdef YYERRORSYMBOL 464 | if( i<0 || i>=YY_ACTTAB_COUNT || yy_lookahead[i]!=iLookAhead ){ 465 | return yy_default[stateno]; 466 | } 467 | #else 468 | assert( i>=0 && iyyidx--; 480 | #ifndef NDEBUG 481 | if( yyTraceFILE ){ 482 | fprintf(yyTraceFILE,"%sStack Overflow!\n",yyTracePrompt); 483 | } 484 | #endif 485 | while( yypParser->yyidx>=0 ) yy_pop_parser_stack(yypParser); 486 | /* Here code is inserted which will execute if the parser 487 | ** stack every overflows */ 488 | %% 489 | ParseARG_STORE; /* Suppress warning about unused %extra_argument var */ 490 | } 491 | 492 | /* 493 | ** Perform a shift action. 494 | */ 495 | static void yy_shift( 496 | yyParser *yypParser, /* The parser to be shifted */ 497 | int yyNewState, /* The new state to shift in */ 498 | int yyMajor, /* The major token to shift in */ 499 | YYMINORTYPE *yypMinor /* Pointer to the minor token to shift in */ 500 | ){ 501 | yyStackEntry *yytos; 502 | yypParser->yyidx++; 503 | #ifdef YYTRACKMAXSTACKDEPTH 504 | if( yypParser->yyidx>yypParser->yyidxMax ){ 505 | yypParser->yyidxMax = yypParser->yyidx; 506 | } 507 | #endif 508 | #if YYSTACKDEPTH>0 509 | if( yypParser->yyidx>=YYSTACKDEPTH ){ 510 | yyStackOverflow(yypParser, yypMinor); 511 | return; 512 | } 513 | #else 514 | if( yypParser->yyidx>=yypParser->yystksz ){ 515 | yyGrowStack(yypParser); 516 | if( yypParser->yyidx>=yypParser->yystksz ){ 517 | yyStackOverflow(yypParser, yypMinor); 518 | return; 519 | } 520 | } 521 | #endif 522 | yytos = &yypParser->yystack[yypParser->yyidx]; 523 | yytos->stateno = (YYACTIONTYPE)yyNewState; 524 | yytos->major = (YYCODETYPE)yyMajor; 525 | yytos->minor = *yypMinor; 526 | #ifndef NDEBUG 527 | if( yyTraceFILE && yypParser->yyidx>0 ){ 528 | int i; 529 | fprintf(yyTraceFILE,"%sShift %d\n",yyTracePrompt,yyNewState); 530 | fprintf(yyTraceFILE,"%sStack:",yyTracePrompt); 531 | for(i=1; i<=yypParser->yyidx; i++) 532 | fprintf(yyTraceFILE," %s",yyTokenName[yypParser->yystack[i].major]); 533 | fprintf(yyTraceFILE,"\n"); 534 | } 535 | #endif 536 | } 537 | 538 | /* The following table contains information about every rule that 539 | ** is used during the reduce. 540 | */ 541 | static const struct { 542 | YYCODETYPE lhs; /* Symbol on the left-hand side of the rule */ 543 | unsigned char nrhs; /* Number of right-hand side symbols in the rule */ 544 | } yyRuleInfo[] = { 545 | %% 546 | }; 547 | 548 | static void yy_accept(yyParser*); /* Forward Declaration */ 549 | 550 | /* 551 | ** Perform a reduce action and the shift that must immediately 552 | ** follow the reduce. 553 | */ 554 | static void yy_reduce( 555 | yyParser *yypParser, /* The parser */ 556 | int yyruleno /* Number of the rule by which to reduce */ 557 | ){ 558 | int yygoto; /* The next state */ 559 | int yyact; /* The next action */ 560 | YYMINORTYPE yygotominor; /* The LHS of the rule reduced */ 561 | yyStackEntry *yymsp; /* The top of the parser's stack */ 562 | int yysize; /* Amount to pop the stack */ 563 | ParseARG_FETCH; 564 | yymsp = &yypParser->yystack[yypParser->yyidx]; 565 | #ifndef NDEBUG 566 | if( yyTraceFILE && yyruleno>=0 567 | && yyruleno<(int)(sizeof(yyRuleName)/sizeof(yyRuleName[0])) ){ 568 | fprintf(yyTraceFILE, "%sReduce [%s].\n", yyTracePrompt, 569 | yyRuleName[yyruleno]); 570 | } 571 | #endif /* NDEBUG */ 572 | 573 | /* Silence complaints from purify about yygotominor being uninitialized 574 | ** in some cases when it is copied into the stack after the following 575 | ** switch. yygotominor is uninitialized when a rule reduces that does 576 | ** not set the value of its left-hand side nonterminal. Leaving the 577 | ** value of the nonterminal uninitialized is utterly harmless as long 578 | ** as the value is never used. So really the only thing this code 579 | ** accomplishes is to quieten purify. 580 | ** 581 | ** 2007-01-16: The wireshark project (www.wireshark.org) reports that 582 | ** without this code, their parser segfaults. I'm not sure what there 583 | ** parser is doing to make this happen. This is the second bug report 584 | ** from wireshark this week. Clearly they are stressing Lemon in ways 585 | ** that it has not been previously stressed... (SQLite ticket #2172) 586 | */ 587 | /*memset(&yygotominor, 0, sizeof(yygotominor));*/ 588 | yygotominor = yyzerominor; 589 | 590 | 591 | switch( yyruleno ){ 592 | /* Beginning here are the reduction cases. A typical example 593 | ** follows: 594 | ** case 0: 595 | ** #line 596 | ** { ... } // User supplied code 597 | ** #line 598 | ** break; 599 | */ 600 | %% 601 | }; 602 | yygoto = yyRuleInfo[yyruleno].lhs; 603 | yysize = yyRuleInfo[yyruleno].nrhs; 604 | yypParser->yyidx -= yysize; 605 | yyact = yy_find_reduce_action(yymsp[-yysize].stateno,(YYCODETYPE)yygoto); 606 | if( yyact < YYNSTATE ){ 607 | #ifdef NDEBUG 608 | /* If we are not debugging and the reduce action popped at least 609 | ** one element off the stack, then we can push the new element back 610 | ** onto the stack here, and skip the stack overflow test in yy_shift(). 611 | ** That gives a significant speed improvement. */ 612 | if( yysize ){ 613 | yypParser->yyidx++; 614 | yymsp -= yysize-1; 615 | yymsp->stateno = (YYACTIONTYPE)yyact; 616 | yymsp->major = (YYCODETYPE)yygoto; 617 | yymsp->minor = yygotominor; 618 | }else 619 | #endif 620 | { 621 | yy_shift(yypParser,yyact,yygoto,&yygotominor); 622 | } 623 | }else{ 624 | assert( yyact == YYNSTATE + YYNRULE + 1 ); 625 | yy_accept(yypParser); 626 | } 627 | } 628 | 629 | /* 630 | ** The following code executes when the parse fails 631 | */ 632 | #ifndef YYNOERRORRECOVERY 633 | static void yy_parse_failed( 634 | yyParser *yypParser /* The parser */ 635 | ){ 636 | ParseARG_FETCH; 637 | #ifndef NDEBUG 638 | if( yyTraceFILE ){ 639 | fprintf(yyTraceFILE,"%sFail!\n",yyTracePrompt); 640 | } 641 | #endif 642 | while( yypParser->yyidx>=0 ) yy_pop_parser_stack(yypParser); 643 | /* Here code is inserted which will be executed whenever the 644 | ** parser fails */ 645 | %% 646 | ParseARG_STORE; /* Suppress warning about unused %extra_argument variable */ 647 | } 648 | #endif /* YYNOERRORRECOVERY */ 649 | 650 | /* 651 | ** The following code executes when a syntax error first occurs. 652 | */ 653 | static void yy_syntax_error( 654 | yyParser *yypParser, /* The parser */ 655 | int yymajor, /* The major type of the error token */ 656 | YYMINORTYPE yyminor /* The minor type of the error token */ 657 | ){ 658 | ParseARG_FETCH; 659 | #define TOKEN (yyminor.yy0) 660 | %% 661 | ParseARG_STORE; /* Suppress warning about unused %extra_argument variable */ 662 | } 663 | 664 | /* 665 | ** The following is executed when the parser accepts 666 | */ 667 | static void yy_accept( 668 | yyParser *yypParser /* The parser */ 669 | ){ 670 | ParseARG_FETCH; 671 | #ifndef NDEBUG 672 | if( yyTraceFILE ){ 673 | fprintf(yyTraceFILE,"%sAccept!\n",yyTracePrompt); 674 | } 675 | #endif 676 | while( yypParser->yyidx>=0 ) yy_pop_parser_stack(yypParser); 677 | /* Here code is inserted which will be executed whenever the 678 | ** parser accepts */ 679 | %% 680 | ParseARG_STORE; /* Suppress warning about unused %extra_argument variable */ 681 | } 682 | 683 | /* The main parser program. 684 | ** The first argument is a pointer to a structure obtained from 685 | ** "ParseAlloc" which describes the current state of the parser. 686 | ** The second argument is the major token number. The third is 687 | ** the minor token. The fourth optional argument is whatever the 688 | ** user wants (and specified in the grammar) and is available for 689 | ** use by the action routines. 690 | ** 691 | ** Inputs: 692 | **
    693 | **
  • A pointer to the parser (an opaque structure.) 694 | **
  • The major token number. 695 | **
  • The minor token number. 696 | **
  • An option argument of a grammar-specified type. 697 | **
698 | ** 699 | ** Outputs: 700 | ** None. 701 | */ 702 | void Parse( 703 | void *yyp, /* The parser */ 704 | int yymajor, /* The major token code number */ 705 | ParseTOKENTYPE yyminor /* The value for the token */ 706 | ParseARG_PDECL /* Optional %extra_argument parameter */ 707 | ){ 708 | YYMINORTYPE yyminorunion; 709 | int yyact; /* The parser action. */ 710 | int yyendofinput; /* True if we are at the end of input */ 711 | #ifdef YYERRORSYMBOL 712 | int yyerrorhit = 0; /* True if yymajor has invoked an error */ 713 | #endif 714 | yyParser *yypParser; /* The parser */ 715 | 716 | /* (re)initialize the parser, if necessary */ 717 | yypParser = (yyParser*)yyp; 718 | if( yypParser->yyidx<0 ){ 719 | #if YYSTACKDEPTH<=0 720 | if( yypParser->yystksz <=0 ){ 721 | /*memset(&yyminorunion, 0, sizeof(yyminorunion));*/ 722 | yyminorunion = yyzerominor; 723 | yyStackOverflow(yypParser, &yyminorunion); 724 | return; 725 | } 726 | #endif 727 | yypParser->yyidx = 0; 728 | yypParser->yyerrcnt = -1; 729 | yypParser->yystack[0].stateno = 0; 730 | yypParser->yystack[0].major = 0; 731 | } 732 | yyminorunion.yy0 = yyminor; 733 | yyendofinput = (yymajor==0); 734 | ParseARG_STORE; 735 | 736 | #ifndef NDEBUG 737 | if( yyTraceFILE ){ 738 | fprintf(yyTraceFILE,"%sInput %s\n",yyTracePrompt,yyTokenName[yymajor]); 739 | } 740 | #endif 741 | 742 | do{ 743 | yyact = yy_find_shift_action(yypParser,(YYCODETYPE)yymajor); 744 | if( yyactyyerrcnt--; 748 | yymajor = YYNOCODE; 749 | }else if( yyact < YYNSTATE + YYNRULE ){ 750 | yy_reduce(yypParser,yyact-YYNSTATE); 751 | }else{ 752 | assert( yyact == YY_ERROR_ACTION ); 753 | #ifdef YYERRORSYMBOL 754 | int yymx; 755 | #endif 756 | #ifndef NDEBUG 757 | if( yyTraceFILE ){ 758 | fprintf(yyTraceFILE,"%sSyntax Error!\n",yyTracePrompt); 759 | } 760 | #endif 761 | #ifdef YYERRORSYMBOL 762 | /* A syntax error has occurred. 763 | ** The response to an error depends upon whether or not the 764 | ** grammar defines an error token "ERROR". 765 | ** 766 | ** This is what we do if the grammar does define ERROR: 767 | ** 768 | ** * Call the %syntax_error function. 769 | ** 770 | ** * Begin popping the stack until we enter a state where 771 | ** it is legal to shift the error symbol, then shift 772 | ** the error symbol. 773 | ** 774 | ** * Set the error count to three. 775 | ** 776 | ** * Begin accepting and shifting new tokens. No new error 777 | ** processing will occur until three tokens have been 778 | ** shifted successfully. 779 | ** 780 | */ 781 | if( yypParser->yyerrcnt<0 ){ 782 | yy_syntax_error(yypParser,yymajor,yyminorunion); 783 | } 784 | yymx = yypParser->yystack[yypParser->yyidx].major; 785 | if( yymx==YYERRORSYMBOL || yyerrorhit ){ 786 | #ifndef NDEBUG 787 | if( yyTraceFILE ){ 788 | fprintf(yyTraceFILE,"%sDiscard input token %s\n", 789 | yyTracePrompt,yyTokenName[yymajor]); 790 | } 791 | #endif 792 | yy_destructor(yypParser, (YYCODETYPE)yymajor,&yyminorunion); 793 | yymajor = YYNOCODE; 794 | }else{ 795 | while( 796 | yypParser->yyidx >= 0 && 797 | yymx != YYERRORSYMBOL && 798 | (yyact = yy_find_reduce_action( 799 | yypParser->yystack[yypParser->yyidx].stateno, 800 | YYERRORSYMBOL)) >= YYNSTATE 801 | ){ 802 | yy_pop_parser_stack(yypParser); 803 | } 804 | if( yypParser->yyidx < 0 || yymajor==0 ){ 805 | yy_destructor(yypParser,(YYCODETYPE)yymajor,&yyminorunion); 806 | yy_parse_failed(yypParser); 807 | yymajor = YYNOCODE; 808 | }else if( yymx!=YYERRORSYMBOL ){ 809 | YYMINORTYPE u2; 810 | u2.YYERRSYMDT = 0; 811 | yy_shift(yypParser,yyact,YYERRORSYMBOL,&u2); 812 | } 813 | } 814 | yypParser->yyerrcnt = 3; 815 | yyerrorhit = 1; 816 | #elif defined(YYNOERRORRECOVERY) 817 | /* If the YYNOERRORRECOVERY macro is defined, then do not attempt to 818 | ** do any kind of error recovery. Instead, simply invoke the syntax 819 | ** error routine and continue going as if nothing had happened. 820 | ** 821 | ** Applications can set this macro (for example inside %include) if 822 | ** they intend to abandon the parse upon the first syntax error seen. 823 | */ 824 | yy_syntax_error(yypParser,yymajor,yyminorunion); 825 | yy_destructor(yypParser,(YYCODETYPE)yymajor,&yyminorunion); 826 | yymajor = YYNOCODE; 827 | 828 | #else /* YYERRORSYMBOL is not defined */ 829 | /* This is what we do if the grammar does not define ERROR: 830 | ** 831 | ** * Report an error message, and throw away the input token. 832 | ** 833 | ** * If the input token is $, then fail the parse. 834 | ** 835 | ** As before, subsequent error messages are suppressed until 836 | ** three input tokens have been successfully shifted. 837 | */ 838 | if( yypParser->yyerrcnt<=0 ){ 839 | yy_syntax_error(yypParser,yymajor,yyminorunion); 840 | } 841 | yypParser->yyerrcnt = 3; 842 | yy_destructor(yypParser,(YYCODETYPE)yymajor,&yyminorunion); 843 | if( yyendofinput ){ 844 | yy_parse_failed(yypParser); 845 | } 846 | yymajor = YYNOCODE; 847 | #endif 848 | } 849 | }while( yymajor!=YYNOCODE && yypParser->yyidx>=0 ); 850 | return; 851 | } 852 | --------------------------------------------------------------------------------