summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJo-Philipp Wich <jow@openwrt.org>2013-12-28 21:25:20 +0000
committerJo-Philipp Wich <jow@openwrt.org>2013-12-28 21:25:20 +0000
commitba3e89199b78c33fc5b0dce6a4456096c71c2e19 (patch)
treed66abbdd0a78e5b08a15db0d120602e89f6fabec
downloadjsonpath-ba3e89199b78c33fc5b0dce6a4456096c71c2e19.tar.gz
Initial commit
-rw-r--r--CMakeLists.txt47
-rw-r--r--lexer.h336
-rw-r--r--lexer.l172
-rw-r--r--main.c215
-rw-r--r--matcher.c267
-rw-r--r--matcher.h34
-rw-r--r--parser.h116
-rw-r--r--parser.y231
8 files changed, 1418 insertions, 0 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000..9b2fd50
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,47 @@
+cmake_minimum_required(VERSION 2.6)
+
+PROJECT(jsonpath C)
+ADD_DEFINITIONS(-Os -Wall -Werror --std=gnu99 -Wmissing-declarations)
+
+SET(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "")
+
+IF(APPLE)
+ INCLUDE_DIRECTORIES(/opt/local/include)
+ LINK_DIRECTORIES(/opt/local/lib)
+ENDIF()
+
+find_library(json NAMES json-c json)
+
+IF(DEBUG)
+ ADD_DEFINITIONS(-DDEBUG -g3)
+ENDIF()
+
+INCLUDE(FindPkgConfig)
+PKG_CHECK_MODULES(JSONC json-c json)
+IF(JSONC_FOUND)
+ ADD_DEFINITIONS(-DJSONC)
+ INCLUDE_DIRECTORIES(${JSONC_INCLUDE_DIRS})
+ENDIF()
+
+FIND_PACKAGE(BISON REQUIRED)
+IF(BISON_FOUND)
+ ADD_CUSTOM_COMMAND(
+ OUTPUT parser.c
+ COMMAND ${BISON_EXECUTABLE} parser.y
+ COMMENT "Generating parser.c"
+ )
+ENDIF()
+
+FIND_PACKAGE(FLEX REQUIRED)
+IF(FLEX_FOUND)
+ ADD_CUSTOM_COMMAND(
+ OUTPUT lexer.c
+ COMMAND ${FLEX_EXECUTABLE} lexer.l
+ COMMENT "Generating lexer.c"
+ )
+ENDIF()
+
+ADD_EXECUTABLE(jsonpath main.c lexer.c parser.c matcher.c)
+TARGET_LINK_LIBRARIES(jsonpath ubox ${json})
+
+INSTALL(TARGETS jsonpath RUNTIME DESTINATION bin)
diff --git a/lexer.h b/lexer.h
new file mode 100644
index 0000000..c8b5bed
--- /dev/null
+++ b/lexer.h
@@ -0,0 +1,336 @@
+#ifndef yyHEADER_H
+#define yyHEADER_H 1
+#define yyIN_HEADER 1
+
+#line 6 "lexer.h"
+
+#line 8 "lexer.h"
+
+#define YY_INT_ALIGNED short int
+
+/* A lexical scanner generated by flex */
+
+#define FLEX_SCANNER
+#define YY_FLEX_MAJOR_VERSION 2
+#define YY_FLEX_MINOR_VERSION 5
+#define YY_FLEX_SUBMINOR_VERSION 35
+#if YY_FLEX_SUBMINOR_VERSION > 0
+#define FLEX_BETA
+#endif
+
+/* First, we deal with platform-specific or compiler-specific issues. */
+
+/* begin standard C headers. */
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+
+/* end standard C headers. */
+
+/* flex integer type definitions */
+
+#ifndef FLEXINT_H
+#define FLEXINT_H
+
+/* C99 systems have <inttypes.h>. Non-C99 systems may or may not. */
+
+#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
+
+/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h,
+ * if you want the limit (max/min) macros for int types.
+ */
+#ifndef __STDC_LIMIT_MACROS
+#define __STDC_LIMIT_MACROS 1
+#endif
+
+#include <inttypes.h>
+typedef int8_t flex_int8_t;
+typedef uint8_t flex_uint8_t;
+typedef int16_t flex_int16_t;
+typedef uint16_t flex_uint16_t;
+typedef int32_t flex_int32_t;
+typedef uint32_t flex_uint32_t;
+#else
+typedef signed char flex_int8_t;
+typedef short int flex_int16_t;
+typedef int flex_int32_t;
+typedef unsigned char flex_uint8_t;
+typedef unsigned short int flex_uint16_t;
+typedef unsigned int flex_uint32_t;
+
+/* Limits of integral types. */
+#ifndef INT8_MIN
+#define INT8_MIN (-128)
+#endif
+#ifndef INT16_MIN
+#define INT16_MIN (-32767-1)
+#endif
+#ifndef INT32_MIN
+#define INT32_MIN (-2147483647-1)
+#endif
+#ifndef INT8_MAX
+#define INT8_MAX (127)
+#endif
+#ifndef INT16_MAX
+#define INT16_MAX (32767)
+#endif
+#ifndef INT32_MAX
+#define INT32_MAX (2147483647)
+#endif
+#ifndef UINT8_MAX
+#define UINT8_MAX (255U)
+#endif
+#ifndef UINT16_MAX
+#define UINT16_MAX (65535U)
+#endif
+#ifndef UINT32_MAX
+#define UINT32_MAX (4294967295U)
+#endif
+
+#endif /* ! C99 */
+
+#endif /* ! FLEXINT_H */
+
+#ifdef __cplusplus
+
+/* The "const" storage-class-modifier is valid. */
+#define YY_USE_CONST
+
+#else /* ! __cplusplus */
+
+/* C99 requires __STDC__ to be defined as 1. */
+#if defined (__STDC__)
+
+#define YY_USE_CONST
+
+#endif /* defined (__STDC__) */
+#endif /* ! __cplusplus */
+
+#ifdef YY_USE_CONST
+#define yyconst const
+#else
+#define yyconst
+#endif
+
+/* Size of default input buffer. */
+#ifndef YY_BUF_SIZE
+#ifdef __ia64__
+/* On IA-64, the buffer size is 16k, not 8k.
+ * Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case.
+ * Ditto for the __ia64__ case accordingly.
+ */
+#define YY_BUF_SIZE 32768
+#else
+#define YY_BUF_SIZE 16384
+#endif /* __ia64__ */
+#endif
+
+#ifndef YY_TYPEDEF_YY_BUFFER_STATE
+#define YY_TYPEDEF_YY_BUFFER_STATE
+typedef struct yy_buffer_state *YY_BUFFER_STATE;
+#endif
+
+extern int yyleng;
+
+extern FILE *yyin, *yyout;
+
+#ifndef YY_TYPEDEF_YY_SIZE_T
+#define YY_TYPEDEF_YY_SIZE_T
+typedef size_t yy_size_t;
+#endif
+
+#ifndef YY_STRUCT_YY_BUFFER_STATE
+#define YY_STRUCT_YY_BUFFER_STATE
+struct yy_buffer_state
+ {
+ FILE *yy_input_file;
+
+ char *yy_ch_buf; /* input buffer */
+ char *yy_buf_pos; /* current position in input buffer */
+
+ /* Size of input buffer in bytes, not including room for EOB
+ * characters.
+ */
+ yy_size_t yy_buf_size;
+
+ /* Number of characters read into yy_ch_buf, not including EOB
+ * characters.
+ */
+ int yy_n_chars;
+
+ /* Whether we "own" the buffer - i.e., we know we created it,
+ * and can realloc() it to grow it, and should free() it to
+ * delete it.
+ */
+ int yy_is_our_buffer;
+
+ /* Whether this is an "interactive" input source; if so, and
+ * if we're using stdio for input, then we want to use getc()
+ * instead of fread(), to make sure we stop fetching input after
+ * each newline.
+ */
+ int yy_is_interactive;
+
+ /* Whether we're considered to be at the beginning of a line.
+ * If so, '^' rules will be active on the next match, otherwise
+ * not.
+ */
+ int yy_at_bol;
+
+ int yy_bs_lineno; /**< The line count. */
+ int yy_bs_column; /**< The column count. */
+
+ /* Whether to try to fill the input buffer when we reach the
+ * end of it.
+ */
+ int yy_fill_buffer;
+
+ int yy_buffer_status;
+
+ };
+#endif /* !YY_STRUCT_YY_BUFFER_STATE */
+
+void yyrestart (FILE *input_file );
+void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer );
+YY_BUFFER_STATE yy_create_buffer (FILE *file,int size );
+void yy_delete_buffer (YY_BUFFER_STATE b );
+void yy_flush_buffer (YY_BUFFER_STATE b );
+void yypush_buffer_state (YY_BUFFER_STATE new_buffer );
+void yypop_buffer_state (void );
+
+YY_BUFFER_STATE yy_scan_buffer (char *base,yy_size_t size );
+YY_BUFFER_STATE yy_scan_string (yyconst char *yy_str );
+YY_BUFFER_STATE yy_scan_bytes (yyconst char *bytes,int len );
+
+void *yyalloc (yy_size_t );
+void *yyrealloc (void *,yy_size_t );
+void yyfree (void * );
+
+/* Begin user sect3 */
+
+#define yywrap(n) 1
+#define YY_SKIP_YYWRAP
+
+extern int yylineno;
+
+extern char *yytext;
+#define yytext_ptr yytext
+
+#ifdef YY_HEADER_EXPORT_START_CONDITIONS
+#define INITIAL 0
+#define STRING 1
+
+#endif
+
+#ifndef YY_NO_UNISTD_H
+/* Special case for "unistd.h", since it is non-ANSI. We include it way
+ * down here because we want the user's section 1 to have been scanned first.
+ * The user has a chance to override it with an option.
+ */
+#include <unistd.h>
+#endif
+
+#ifndef YY_EXTRA_TYPE
+#define YY_EXTRA_TYPE void *
+#endif
+
+/* Accessor methods to globals.
+ These are made visible to non-reentrant scanners for convenience. */
+
+int yylex_destroy (void );
+
+int yyget_debug (void );
+
+void yyset_debug (int debug_flag );
+
+YY_EXTRA_TYPE yyget_extra (void );
+
+void yyset_extra (YY_EXTRA_TYPE user_defined );
+
+FILE *yyget_in (void );
+
+void yyset_in (FILE * in_str );
+
+FILE *yyget_out (void );
+
+void yyset_out (FILE * out_str );
+
+int yyget_leng (void );
+
+char *yyget_text (void );
+
+int yyget_lineno (void );
+
+void yyset_lineno (int line_number );
+
+/* Macros after this point can all be overridden by user definitions in
+ * section 1.
+ */
+
+#ifndef YY_SKIP_YYWRAP
+#ifdef __cplusplus
+extern "C" int yywrap (void );
+#else
+extern int yywrap (void );
+#endif
+#endif
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy (char *,yyconst char *,int );
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen (yyconst char * );
+#endif
+
+#ifndef YY_NO_INPUT
+
+#endif
+
+/* Amount of stuff to slurp up with each read. */
+#ifndef YY_READ_BUF_SIZE
+#ifdef __ia64__
+/* On IA-64, the buffer size is 16k, not 8k */
+#define YY_READ_BUF_SIZE 16384
+#else
+#define YY_READ_BUF_SIZE 8192
+#endif /* __ia64__ */
+#endif
+
+/* Number of entries by which start-condition stack grows. */
+#ifndef YY_START_STACK_INCR
+#define YY_START_STACK_INCR 25
+#endif
+
+/* Default declaration of generated scanner - a define so the user can
+ * easily add parameters.
+ */
+#ifndef YY_DECL
+#define YY_DECL_IS_OURS 1
+
+extern int yylex (void);
+
+#define YY_DECL int yylex (void)
+#endif /* !YY_DECL */
+
+/* yy_get_previous_state - get the state just before the EOB char was reached */
+
+#undef YY_NEW_FILE
+#undef YY_FLUSH_BUFFER
+#undef yy_set_bol
+#undef yy_new_buffer
+#undef yy_set_interactive
+#undef YY_DO_BEFORE_ACTION
+
+#ifdef YY_DECL_IS_OURS
+#undef YY_DECL_IS_OURS
+#undef YY_DECL
+#endif
+
+#line 172 "lexer.l"
+
+
+#line 335 "lexer.h"
+#undef yyIN_HEADER
+#endif /* yyHEADER_H */
diff --git a/lexer.l b/lexer.l
new file mode 100644
index 0000000..e3467bc
--- /dev/null
+++ b/lexer.l
@@ -0,0 +1,172 @@
+%{
+/*
+ * Copyright (C) 2013 Jo-Philipp Wich <jow@openwrt.org>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <ctype.h>
+
+#include "parser.h"
+
+static char *str_ptr;
+static char str_buf[128];
+
+static void
+str_put(char c)
+{
+ if ((str_ptr - str_buf + 1) < sizeof(str_buf))
+ *str_ptr++ = c;
+}
+
+static void
+str_decode(const char *input, int base)
+{
+ int code;
+ char *end;
+
+ code = strtoul(input, &end, base);
+
+ if (end == input || *end)
+ return;
+
+ if (code > 0 && code <= 0x7F)
+ {
+ str_put(code);
+ }
+ else if (code > 0 && code <= 0x7FF)
+ {
+ str_put(((code >> 6) & 0x1F) | 0xC0);
+ str_put(( code & 0x3F) | 0x80);
+ }
+ else if (code > 0 && code <= 0xFFFF)
+ {
+ str_put(((code >> 12) & 0x0F) | 0xE0);
+ str_put(((code >> 6) & 0x3F) | 0x80);
+ str_put(( code & 0x3F) | 0x80);
+ }
+ else if (code > 0 && code <= 0x10FFFF)
+ {
+ str_put(((code >> 18) & 0x07) | 0xF0);
+ str_put(((code >> 12) & 0x3F) | 0x80);
+ str_put(((code >> 6) & 0x3F) | 0x80);
+ str_put(( code & 0x3F) | 0x80);
+ }
+}
+
+%}
+
+%option outfile="lexer.c" header-file="lexer.h"
+%option noyywrap nounput noinput
+
+DOT "."
+LABEL [a-zA-Z_][a-zA-Z0-9_]*
+
+BROPEN "["
+BRCLOSE "]"
+POPEN "("
+PCLOSE ")"
+
+ROOT "$"
+THIS "@"
+
+LT "<"
+LE "<="
+GT ">"
+GE ">="
+NE "!="
+EQ "="
+NOT "!"
+AND "&&"
+OR "||"
+
+NUMBER -?[0-9]+
+WILDCARD "*"
+BOOL (true|false)
+
+WS [ \t\n]*
+
+%x STRING
+
+%%
+
+\" {
+ str_ptr = str_buf;
+ memset(str_buf, 0, sizeof(str_buf));
+ BEGIN(STRING);
+}
+
+<STRING>{
+ \" {
+ BEGIN(INITIAL);
+ yylval.op = jp_alloc_op(T_STRING, 0, str_buf);
+ return T_STRING;
+ }
+
+ \\([0-3][0-7]{1,2}|[0-7]{0,2}) { str_decode(yytext + 1, 8); }
+ \\x[A-Fa-f0-9]{2} { str_decode(yytext + 2, 16); }
+ \\u[A-Fa-f0-9]{4} { str_decode(yytext + 2, 16); }
+ \\a { str_put('\a'); }
+ \\b { str_put('\b'); }
+ \\e { str_put('\e'); }
+ \\f { str_put('\f'); }
+ \\n { str_put('\n'); }
+ \\r { str_put('\r'); }
+ \\t { str_put('\t'); }
+ \\v { str_put('\v'); }
+ \\. { str_put(*yytext); }
+ [^\\"]+ { while (*yytext) str_put(*yytext++); }
+}
+
+{BOOL} {
+ yylval.op = jp_alloc_op(T_BOOL, (*yytext == 't'), NULL);
+ return T_BOOL;
+}
+
+{NUMBER} {
+ yylval.op = jp_alloc_op(T_NUMBER, atoi(yytext), NULL);
+ return T_NUMBER;
+}
+
+{LABEL} {
+ yylval.op = jp_alloc_op(T_LABEL, 0, yytext);
+ return T_LABEL;
+}
+
+{WILDCARD} {
+ yylval.op = jp_alloc_op(T_WILDCARD, 0, NULL);
+ return T_WILDCARD;
+}
+
+{DOT} { return T_DOT; }
+{BROPEN} { return T_BROPEN; }
+{BRCLOSE} { return T_BRCLOSE; }
+{POPEN} { return T_POPEN; }
+{PCLOSE} { return T_PCLOSE; }
+
+{ROOT} { return T_ROOT; }
+{THIS} { return T_THIS; }
+
+{LT} { return T_LT; }
+{LE} { return T_LE; }
+{GT} { return T_GT; }
+{GE} { return T_GE; }
+{EQ} { return T_EQ; }
+{NE} { return T_NE; }
+{NOT} { return T_NOT; }
+{AND} { return T_AND; }
+{OR} { return T_OR; }
+
+{WS} { }
+
+%%
diff --git a/main.c b/main.c
new file mode 100644
index 0000000..7a19091
--- /dev/null
+++ b/main.c
@@ -0,0 +1,215 @@
+/*
+ * Copyright (C) 2013 Jo-Philipp Wich <jow@openwrt.org>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+
+#ifdef JSONC
+ #include <json.h>
+#else
+ #include <json-c/json.h>
+#endif
+
+#include "lexer.h"
+#include "parser.h"
+#include "matcher.h"
+
+static struct json_object *
+parse_json(FILE *fd)
+{
+ int len;
+ char buf[256];
+ struct json_object *obj = NULL;
+ struct json_tokener *tok = json_tokener_new();
+ enum json_tokener_error err = json_tokener_continue;
+
+ if (!tok)
+ return NULL;
+
+ while ((len = fread(buf, 1, sizeof(buf), fd)) > 0)
+ {
+ obj = json_tokener_parse_ex(tok, buf, len);
+ err = json_tokener_get_error(tok);
+
+ if (!err || err != json_tokener_continue)
+ break;
+ }
+
+ json_tokener_free(tok);
+
+ return err ? NULL : obj;
+}
+
+static void
+print_string(const char *s)
+{
+ const char *p;
+
+ printf("'");
+
+ for (p = s; *p; p++)
+ {
+ if (*p == '\'')
+ printf("'\"'\"'");
+ else
+ printf("%c", *p);
+ }
+
+ printf("'");
+}
+
+static void
+export_json(struct json_object *jsobj, char *expr)
+{
+ bool first;
+ struct jp_opcode *tree;
+ struct json_object *res;
+ const char *error, *prefix;
+
+ tree = jp_parse(expr, &error);
+
+ if (error)
+ {
+ fprintf(stderr, "In expression '%s': %s\n", expr, error);
+ return;
+ }
+
+ res = jp_match(tree, jsobj);
+
+ if (tree->type == T_LABEL)
+ {
+ prefix = tree->str;
+
+ switch (json_object_get_type(res))
+ {
+ case json_type_object:
+ printf("export %s_TYPE=object; ", prefix);
+
+ first = true;
+ printf("export %s_KEYS=", prefix);
+ json_object_object_foreach(res, key, val)
+ {
+ if (!val)
+ continue;
+
+ if (!first)
+ printf("\\ ");
+
+ print_string(key);
+ first = false;
+ }
+ printf("; ");
+
+ //printf("export %s=", prefix);
+ //print_string(json_object_to_json_string(res));
+ //printf("; ");
+
+ break;
+
+ case json_type_array:
+ printf("export %s_TYPE=array; ", prefix);
+ printf("export %s_LENGTH=%d; ",
+ prefix, json_object_array_length(res));
+
+ //printf("export %s=", prefix);
+ //print_string(json_object_to_json_string(res));
+ //printf("; ");
+ break;
+
+ case json_type_boolean:
+ printf("export %s_TYPE=bool; ", prefix);
+ printf("export %s=%d; ", prefix, json_object_get_boolean(res));
+ break;
+
+ case json_type_int:
+ printf("export %s_TYPE=int; ", prefix);
+ printf("export %s=%d; ", prefix, json_object_get_int(res));
+ break;
+
+ case json_type_double:
+ printf("export %s_TYPE=double; ", prefix);
+ printf("export %s=%f; ", prefix, json_object_get_double(res));
+ break;
+
+ case json_type_string:
+ printf("export %s_TYPE=string; ", prefix);
+ printf("export %s=", prefix);
+ print_string(json_object_get_string(res));
+ printf("; ");
+ break;
+
+ case json_type_null:
+ printf("unset %s %s_TYPE %s_LENGTH %s_KEYS; ",
+ prefix, prefix, prefix, prefix);
+ break;
+ }
+ }
+ else
+ {
+ printf("%s\n", json_object_to_json_string(res));
+ }
+
+ jp_free();
+}
+
+int main(int argc, char **argv)
+{
+ int opt;
+ FILE *input = stdin;
+ struct json_object *jsobj = NULL;
+
+ while ((opt = getopt(argc, argv, "i:e:")) != -1)
+ {
+ switch (opt)
+ {
+ case 'i':
+ input = fopen(optarg, "r");
+
+ if (!input)
+ {
+ fprintf(stderr, "Failed to open %s: %s\n",
+ optarg, strerror(errno));
+
+ exit(1);
+ }
+
+ break;
+
+ case 'e':
+ if (!jsobj)
+ {
+ jsobj = parse_json(input);
+
+ if (!jsobj)
+ {
+ fprintf(stderr, "Failed to parse json data\n");
+ exit(2);
+ }
+ }
+
+ export_json(jsobj, optarg);
+ break;
+ }
+ }
+
+ if (jsobj)
+ json_object_put(jsobj);
+
+ fclose(input);
+
+ return 0;
+}
diff --git a/matcher.c b/matcher.c
new file mode 100644
index 0000000..1a4a57d
--- /dev/null
+++ b/matcher.c
@@ -0,0 +1,267 @@
+/*
+ * Copyright (C) 2013 Jo-Philipp Wich <jow@openwrt.org>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "matcher.h"
+
+static struct json_object *
+jp_match_next(struct jp_opcode *ptr,
+ struct json_object *root, struct json_object *cur);
+
+static bool
+jp_json_to_op(struct json_object *obj, struct jp_opcode *op)
+{
+ switch (json_object_get_type(obj))
+ {
+ case json_type_boolean:
+ op->type = T_BOOL;
+ op->num = json_object_get_boolean(obj);
+ return true;
+
+ case json_type_int:
+ op->type = T_NUMBER;
+ op->num = json_object_get_int(obj);
+ return true;
+
+ case json_type_string:
+ op->type = T_STRING;
+ op->str = (char *)json_object_get_string(obj);
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+static bool
+jp_resolve(struct json_object *root, struct json_object *cur,
+ struct jp_opcode *op, struct jp_opcode *res)
+{
+ struct json_object *val;
+
+ switch (op->type)
+ {
+ case T_THIS:
+ val = jp_match(op, cur);
+
+ if (val)
+ return jp_json_to_op(val, res);
+
+ return false;
+
+ case T_ROOT:
+ val = jp_match(op, root);
+
+ if (val)
+ return jp_json_to_op(val, res);
+
+ return false;
+
+ default:
+ *res = *op;
+ return true;
+ }
+}
+
+static bool
+jp_cmp(struct jp_opcode *op, struct json_object *root, struct json_object *cur)
+{
+ int delta;
+ struct jp_opcode left, right;
+
+ if (!jp_resolve(root, cur, op->down, &left) ||
+ !jp_resolve(root, cur, op->down->sibling, &right))
+ return false;
+
+ if (left.type != right.type)
+ return false;
+
+ switch (left.type)
+ {
+ case T_BOOL:
+ case T_NUMBER:
+ delta = left.num - right.num;
+ break;
+
+ case T_STRING:
+ delta = strcmp(left.str, right.str);
+ break;
+
+ default:
+ return false;
+ }
+
+ switch (op->type)
+ {
+ case T_EQ:
+ return (delta == 0);
+
+ case T_LT:
+ return (delta < 0);
+
+ case T_LE:
+ return (delta <= 0);
+
+ case T_GT:
+ return (delta > 0);
+
+ case T_GE:
+ return (delta >= 0);
+
+ case T_NE:
+ return (delta != 0);
+
+ default:
+ return false;
+ }
+}
+
+static bool
+jp_expr(struct jp_opcode *op, struct json_object *root, struct json_object *cur)
+{
+ struct jp_opcode *sop;
+
+ switch (op->type)
+ {
+ case T_WILDCARD:
+ return true;
+
+ case T_EQ:
+ case T_NE:
+ case T_LT:
+ case T_LE:
+ case T_GT:
+ case T_GE:
+ return jp_cmp(op, root, cur);
+
+ case T_ROOT:
+ return !!jp_match(op, root);
+
+ case T_THIS:
+ return !!jp_match(op, cur);
+
+ case T_NOT:
+ return !jp_expr(op->down, root, cur);
+
+ case T_AND:
+ for (sop = op->down; sop; sop = sop->sibling)
+ if (!jp_expr(sop, root, cur))
+ return false;
+ return true;
+
+ case T_OR:
+ for (sop = op->down; sop; sop = sop->sibling)
+ if (jp_expr(sop, root, cur))
+ return true;
+ return false;
+
+ default:
+ return false;
+ }
+}
+
+static struct json_object *
+jp_match_expr(struct jp_opcode *ptr,
+ struct json_object *root, struct json_object *cur)
+{
+ int idx, len;
+ struct json_object *tmp, *res = NULL;
+
+ switch (json_object_get_type(cur))
+ {
+ case json_type_object:
+ ; /* a label can only be part of a statement and a declaration is not a statement */
+ json_object_object_foreach(cur, key, val)
+ {
+ if (!key)
+ continue;
+
+ if (jp_expr(ptr, root, val))
+ {
+ tmp = jp_match_next(ptr->sibling, root, val);
+
+ if (tmp && !res)
+ res = tmp;
+ }
+ }
+
+ break;
+
+ case json_type_array:
+ len = json_object_array_length(cur);
+
+ for (idx = 0; idx < len; idx++)
+ {
+ tmp = json_object_array_get_idx(cur, idx);
+
+ if (jp_expr(ptr, root, tmp))
+ {
+ tmp = jp_match_next(ptr->sibling, root, tmp);
+
+ if (tmp && !res)
+ res = tmp;
+ }
+ }
+
+ break;
+
+ default:
+ break;
+ }
+
+ return res;
+}
+
+static struct json_object *
+jp_match_next(struct jp_opcode *ptr,
+ struct json_object *root, struct json_object *cur)
+{
+ struct json_object *next;
+
+ if (!ptr)
+ return cur;
+
+ switch (ptr->type)
+ {
+ case T_STRING:
+ case T_LABEL:
+ if (json_object_object_get_ex(cur, ptr->str, &next))
+ return jp_match_next(ptr->sibling, root, next);
+
+ break;
+
+ case T_NUMBER:
+ next = json_object_array_get_idx(cur, ptr->num);
+
+ if (next)
+ return jp_match_next(ptr->sibling, root, next);
+
+ break;
+
+ default:
+ return jp_match_expr(ptr, root, cur);
+ }
+
+ return NULL;
+}
+
+struct json_object *
+jp_match(struct jp_opcode *path, json_object *jsobj)
+{
+ if (path->type == T_LABEL)
+ path = path->down;
+
+ return jp_match_next(path->down, jsobj, jsobj);
+}
diff --git a/matcher.h b/matcher.h
new file mode 100644
index 0000000..4187241
--- /dev/null
+++ b/matcher.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2013 Jo-Philipp Wich <jow@openwrt.org>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef __MATCHER_H_
+#define __MATCHER_H_
+
+#include <string.h>
+#include <stdbool.h>
+
+#ifdef JSONC
+ #include <json.h>
+#else
+ #include <json-c/json.h>
+#endif
+
+#include "parser.h"
+
+struct json_object *
+jp_match(struct jp_opcode *path, struct json_object *jsobj);
+
+#endif
diff --git a/parser.h b/parser.h
new file mode 100644
index 0000000..a50bd50
--- /dev/null
+++ b/parser.h
@@ -0,0 +1,116 @@
+/* A Bison parser, made by GNU Bison 2.5. */
+
+/* Bison interface for Yacc-like parsers in C
+
+ Copyright (C) 1984, 1989-1990, 2000-2011 Free Software Foundation, Inc.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+/* As a special exception, you may create a larger work that contains
+ part or all of the Bison parser skeleton and distribute that work
+ under terms of your choice, so long as that work isn't itself a
+ parser generator using the skeleton or a modified version thereof
+ as a parser skeleton. Alternatively, if you modify or redistribute
+ the parser skeleton itself, you may (at your option) remove this
+ special exception, which will cause the skeleton and the resulting
+ Bison output files to be licensed under the GNU General Public
+ License without this special exception.
+
+ This special exception was added by the Free Software Foundation in
+ version 2.2 of Bison. */
+
+
+/* Tokens. */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+ /* Put the tokens into the symbol table, so that GDB and other debuggers
+ know about them. */
+ enum yytokentype {
+ T_ROOT = 258,
+ T_THIS = 259,
+ T_DOT = 260,
+ T_BROPEN = 261,
+ T_BRCLOSE = 262,
+ T_OR = 263,
+ T_AND = 264,
+ T_LT = 265,
+ T_LE = 266,
+ T_GT = 267,
+ T_GE = 268,
+ T_EQ = 269,
+ T_NE = 270,
+ T_POPEN = 271,
+ T_PCLOSE = 272,
+ T_NOT = 273,
+ T_BOOL = 274,
+ T_NUMBER = 275,
+ T_STRING = 276,
+ T_LABEL = 277,
+ T_WILDCARD = 278
+ };
+#endif
+
+
+
+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
+typedef union YYSTYPE
+{
+
+/* Line 2068 of yacc.c */
+#line 60 "parser.y"
+
+ struct jp_opcode *op;
+
+
+
+/* Line 2068 of yacc.c */
+#line 79 "parser.h"
+} YYSTYPE;
+# define YYSTYPE_IS_TRIVIAL 1
+# define yystype YYSTYPE /* obsolescent; will be withdrawn */
+# define YYSTYPE_IS_DECLARED 1
+#endif
+
+extern YYSTYPE yylval;
+
+
+/* "%code provides" blocks. */
+
+/* Line 2068 of yacc.c */
+#line 38 "parser.y"
+
+
+#ifndef JP_OPCODE
+# define JP_OPCODE
+ struct jp_opcode {
+ int type;
+ struct jp_opcode *next;
+ struct jp_opcode *down;
+ struct jp_opcode *sibling;
+ char *str;
+ int num;
+ };
+#endif
+
+struct jp_opcode *_jp_alloc_op(int type, int num, char *str, ...);
+#define jp_alloc_op(type, num, str, ...) _jp_alloc_op(type, num, str, ##__VA_ARGS__, NULL)
+
+struct jp_opcode *jp_parse(const char *expr, const char **error);
+void jp_free(void);
+
+
+
+
+/* Line 2068 of yacc.c */
+#line 117 "parser.h"
diff --git a/parser.y b/parser.y
new file mode 100644
index 0000000..40b25f5
--- /dev/null
+++ b/parser.y
@@ -0,0 +1,231 @@
+%{
+/*
+ * Copyright (C) 2013 Jo-Philipp Wich <jow@openwrt.org>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stdarg.h>
+#include <libubox/utils.h>
+
+#include "lexer.h"
+#include "parser.h"
+
+static struct jp_opcode *op_pool = NULL;
+static struct jp_opcode *append_op(struct jp_opcode *a, struct jp_opcode *b);
+
+int yyparse(struct jp_opcode **tree, const char **error);
+void yyerror(struct jp_opcode **expr, const char **error, const char *msg);
+
+%}
+
+%output "parser.c"
+%defines "parser.h"
+
+%parse-param { struct jp_opcode **expr }
+%parse-param { const char **error }
+
+%code provides {
+
+#ifndef JP_OPCODE
+# define JP_OPCODE
+ struct jp_opcode {
+ int type;
+ struct jp_opcode *next;
+ struct jp_opcode *down;
+ struct jp_opcode *sibling;
+ char *str;
+ int num;
+ };
+#endif
+
+struct jp_opcode *_jp_alloc_op(int type, int num, char *str, ...);
+#define jp_alloc_op(type, num, str, ...) _jp_alloc_op(type, num, str, ##__VA_ARGS__, NULL)
+
+struct jp_opcode *jp_parse(const char *expr, const char **error);
+void jp_free(void);
+
+}
+
+%union {
+ struct jp_opcode *op;
+}
+
+
+%token T_ROOT T_THIS T_DOT T_BROPEN T_BRCLOSE
+%token T_OR T_AND T_LT T_LE T_GT T_GE T_EQ T_NE T_POPEN T_PCLOSE T_NOT
+
+%token <op> T_BOOL T_NUMBER T_STRING T_LABEL T_WILDCARD
+
+%type <op> expr path segments segment or_exps or_exp and_exps and_exp cmp_exp unary_exp
+
+%error-verbose
+
+%%
+
+input
+ : expr { *expr = $1; }
+ ;
+
+expr
+ : T_LABEL T_EQ path { $1->down = $3; $$ = $1; }
+ | path { $$ = $1; }
+ ;
+
+path
+ : T_ROOT segments { $$ = jp_alloc_op(T_ROOT, 0, NULL, $2); }
+ | T_THIS segments { $$ = jp_alloc_op(T_THIS, 0, NULL, $2); }
+ ;
+
+segments
+ : segments segment { $$ = append_op($1, $2); }
+ | segment { $$ = $1; }
+ ;
+
+segment
+ : T_DOT T_LABEL { $$ = $2; }
+ | T_DOT T_WILDCARD { $$ = $2; }
+ | T_BROPEN or_exps T_BRCLOSE { $$ = $2; }
+ ;
+
+or_exps
+ : or_exp { $$ = $1->sibling ? jp_alloc_op(T_OR, 0, NULL, $1) : $1; }
+ ;
+
+or_exp
+ : or_exp T_OR and_exps { $$ = append_op($1, $3); }
+ | and_exps { $$ = $1; }
+ ;
+
+and_exps
+ : and_exp { $$ = $1->sibling ? jp_alloc_op(T_AND, 0, NULL, $1) : $1; }
+ ;
+
+and_exp
+ : and_exp T_AND cmp_exp { $$ = append_op($1, $3); }
+ | cmp_exp { $$ = $1; }
+ ;
+
+cmp_exp
+ : unary_exp T_LT unary_exp { $$ = jp_alloc_op(T_LT, 0, NULL, $1, $3); }
+ | unary_exp T_LE unary_exp { $$ = jp_alloc_op(T_LE, 0, NULL, $1, $3); }
+ | unary_exp T_GT unary_exp { $$ = jp_alloc_op(T_GT, 0, NULL, $1, $3); }
+ | unary_exp T_GE unary_exp { $$ = jp_alloc_op(T_GE, 0, NULL, $1, $3); }
+ | unary_exp T_EQ unary_exp { $$ = jp_alloc_op(T_EQ, 0, NULL, $1, $3); }
+ | unary_exp T_NE unary_exp { $$ = jp_alloc_op(T_NE, 0, NULL, $1, $3); }
+ | unary_exp { $$ = $1; }
+ ;
+
+unary_exp
+ : T_BOOL { $$ = $1; }
+ | T_NUMBER { $$ = $1; }
+ | T_STRING { $$ = $1; }
+ | T_WILDCARD { $$ = $1; }
+ | T_POPEN or_exps T_PCLOSE { $$ = $2; }
+ | T_NOT unary_exp { $$ = jp_alloc_op(T_NOT, 0, NULL, $2); }
+ | path { $$ = $1; }
+ ;
+
+%%
+
+void
+yyerror(struct jp_opcode **expr, const char **error, const char *msg)
+{
+ *error = msg;
+ jp_free();
+}
+
+static struct jp_opcode *
+append_op(struct jp_opcode *a, struct jp_opcode *b)
+{
+ struct jp_opcode *tail = a;
+
+ while (tail->sibling)
+ tail = tail->sibling;
+
+ tail->sibling = b;
+
+ return a;
+}
+
+struct jp_opcode *
+_jp_alloc_op(int type, int num, char *str, ...)
+{
+ va_list ap;
+ char *ptr;
+ struct jp_opcode *newop, *child;
+
+ newop = calloc_a(sizeof(*newop),
+ str ? &ptr : NULL, str ? strlen(str) + 1 : 0);
+
+ if (!newop)
+ {
+ fprintf(stderr, "Out of memory\n");
+ exit(1);
+ }
+
+ newop->type = type;
+ newop->num = num;
+
+ if (str)
+ newop->str = strcpy(ptr, str);
+
+ va_start(ap, str);
+
+ while ((child = va_arg(ap, void *)) != NULL)
+ if (!newop->down)
+ newop->down = child;
+ else
+ append_op(newop->down, child);
+
+ va_end(ap);
+
+ newop->next = op_pool;
+ op_pool = newop;
+
+ return newop;
+}
+
+struct jp_opcode *
+jp_parse(const char *expr, const char **error)
+{
+ void *buf;
+ struct jp_opcode *tree;
+
+ buf = yy_scan_string(expr);
+
+ if (yyparse(&tree, error))
+ tree = NULL;
+ else
+ *error = NULL;
+
+ yy_delete_buffer(buf);
+ yylex_destroy();
+
+ return tree;
+}
+
+void
+jp_free(void)
+{
+ struct jp_opcode *op, *tmp;
+
+ for (op = op_pool; op;)
+ {
+ tmp = op->next;
+ free(op);
+ op = tmp;
+ }
+
+ op_pool = NULL;
+}