Initial commit
authorJo-Philipp Wich <jow@openwrt.org>
Sat, 28 Dec 2013 21:25:20 +0000 (21:25 +0000)
committerJo-Philipp Wich <jow@openwrt.org>
Sat, 28 Dec 2013 21:25:20 +0000 (21:25 +0000)
CMakeLists.txt [new file with mode: 0644]
lexer.h [new file with mode: 0644]
lexer.l [new file with mode: 0644]
main.c [new file with mode: 0644]
matcher.c [new file with mode: 0644]
matcher.h [new file with mode: 0644]
parser.h [new file with mode: 0644]
parser.y [new file with mode: 0644]

diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644 (file)
index 0000000..9b2fd50
--- /dev/null
@@ -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 (file)
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 (file)
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 (file)
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 (file)
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 (file)
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 (file)
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 (file)
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;
+}