From 43fc3eec75bb0b06b58551ebb497a900e57ebf88 Mon Sep 17 00:00:00 2001 From: Karel Zak Date: Thu, 20 Jul 2023 23:08:07 +0200 Subject: [PATCH] libsmartcols: introduce basic files for filter implementation Signed-off-by: Karel Zak --- configure.ac | 1 + libsmartcols/src/Makemodule.am | 14 +- libsmartcols/src/filter-nodes.c | 211 ++++++++++++++++++++++++++++++ libsmartcols/src/filter-parser.y | 100 ++++++++++++++ libsmartcols/src/filter-scanner.l | 58 ++++++++ libsmartcols/src/filter.c | 55 ++++++++ libsmartcols/src/smartcolsP.h | 83 ++++++++++++ 7 files changed, 520 insertions(+), 2 deletions(-) create mode 100644 libsmartcols/src/filter-nodes.c create mode 100644 libsmartcols/src/filter-parser.y create mode 100644 libsmartcols/src/filter-scanner.l create mode 100644 libsmartcols/src/filter.c diff --git a/configure.ac b/configure.ac index d7c5155c9c..cc4af86c18 100644 --- a/configure.ac +++ b/configure.ac @@ -124,6 +124,7 @@ AC_PROG_CC AM_PROG_CC_C_O AC_PROG_MKDIR_P AC_PROG_YACC +AM_PROG_LEX AC_CANONICAL_HOST AC_C_CONST AC_C_VOLATILE diff --git a/libsmartcols/src/Makemodule.am b/libsmartcols/src/Makemodule.am index 2bb19fdbde..51458cbfc2 100644 --- a/libsmartcols/src/Makemodule.am +++ b/libsmartcols/src/Makemodule.am @@ -1,5 +1,4 @@ - # smartcols.h is generated, so it's stored in builddir! smartcolsincdir = $(includedir)/libsmartcols nodist_smartcolsinc_HEADERS = libsmartcols/src/libsmartcols.h @@ -21,7 +20,18 @@ libsmartcols_la_SOURCES= \ libsmartcols/src/calculate.c \ libsmartcols/src/grouping.c \ libsmartcols/src/walk.c \ - libsmartcols/src/init.c + libsmartcols/src/init.c \ + \ + libsmartcols/src/filter-nodes.c \ + libsmartcols/src/filter-parser.y \ + libsmartcols/src/filter-scanner.l \ + libsmartcols/src/filter.c + +BUILT_SOURCES = libsmartcols/src/filter-scanner.c \ + libsmartcols/src/filter-scanner.h \ + libsmartcols/src/filter-parser.h \ + libsmartcols/src/filter-parser.c +AM_YFLAGS = -d libsmartcols_la_LIBADD = $(LDADD) libcommon.la diff --git a/libsmartcols/src/filter-nodes.c b/libsmartcols/src/filter-nodes.c new file mode 100644 index 0000000000..fadb364a57 --- /dev/null +++ b/libsmartcols/src/filter-nodes.c @@ -0,0 +1,211 @@ + +#include "smartcolsP.h" + +/* This is generic allocater for a new node, always use the node type specific + * functions (e.g. filter_new_param() */ +static struct filter_node *new_node(enum filter_ntype type, size_t sz) +{ + void *x = calloc(1, sz); + struct filter_node *n = (struct filter_node *) x; + + if (!x) + return NULL; + + n->type = type; + n->refcount = 1; + return n; +} + +struct filter_node *filter_new_param(struct libscols_filter *fltr __attribute__((__unused__)), + enum filter_ptype type, + void *data) +{ + struct filter_param *n = (struct filter_param *) new_node( + F_NODE_PARAM, + sizeof(struct filter_param)); + n->type = type; + + switch (type) { + case F_PARAM_NAME: + case F_PARAM_STRING: + n->val.str = strdup((char *) data); + break; + case F_PARAM_NUMBER: + n->val.num = *((unsigned long long *) data); + break; + case F_PARAM_FLOAT: + n->val.fnum = *((long double *) data); + break; + case F_PARAM_BOOLEAN: + n->val.boolean = *((bool *) data) == 0 ? 0 : 1; + break; + } + return (struct filter_node *) n; +} + +static void free_param(struct filter_param *n) +{ + if (n->type == F_PARAM_NAME || n->type == F_PARAM_STRING) + free(n->val.str); + free(n); +} + +#define plus_indent(i) ((i) + 5) +#define indent(f, i) fprintf(f, "%*s", (i), ""); +#define indent_inside(f, i) indent(f, plus_indent(i)) + +static void dump_param(FILE *out, int i __attribute__((__unused__)), struct filter_param *n) +{ + fprintf(out, "param { "); + + switch (n->type) { + case F_PARAM_NAME: + fprintf(out, "name: '%s'", n->val.str); + break; + case F_PARAM_STRING: + fprintf(out, "string: '%s'", n->val.str); + break; + case F_PARAM_NUMBER: + fprintf(out, "number: %llu", n->val.num); + break; + case F_PARAM_FLOAT: + fprintf(out, "float: %Lg", n->val.fnum); + break; + case F_PARAM_BOOLEAN: + fprintf(out, "bool: %s", n->val.boolean ? "true" : "false"); + break; + } + fprintf(out, " }\n"); +} + +struct filter_node *filter_new_expr(struct libscols_filter *fltr __attribute__((__unused__)), + enum filter_etype type, + struct filter_node *left, + struct filter_node *right) +{ + struct filter_expr *n = (struct filter_expr *) new_node( + F_NODE_EXPR, sizeof(struct filter_expr)); + + n->type = type; + switch (type) { + case F_EXPR_AND: + case F_EXPR_OR: + case F_EXPR_EQ: + case F_EXPR_NE: + case F_EXPR_LE: + case F_EXPR_LT: + case F_EXPR_GE: + case F_EXPR_GT: + case F_EXPR_REG: + case F_EXPR_NREG: + n->left = left; + n->right = right; + break; + case F_EXPR_NEG: + n->right = right; + break; + + } + return (struct filter_node *) n; +} + +static void free_expr(struct filter_expr *n) +{ + filter_unref_node(n->left); + filter_unref_node(n->right); + free(n); +} + +static void dump_expr(FILE *out, int i, struct filter_expr *n) +{ + fprintf(out, "expr {\n"); + + indent_inside(out, i); + fprintf(out, "type: "); + + switch (n->type) { + case F_EXPR_AND: + fprintf(out, "AND"); + break; + case F_EXPR_OR: + fprintf(out, "OR"); + break; + case F_EXPR_EQ: + fprintf(out, "EQ"); + break; + case F_EXPR_NE: + fprintf(out, "NE"); + break; + case F_EXPR_LE: + fprintf(out, "LE"); + break; + case F_EXPR_LT: + fprintf(out, "LT"); + break; + case F_EXPR_GE: + fprintf(out, "GE"); + break; + case F_EXPR_GT: + fprintf(out, "GT"); + break; + case F_EXPR_REG: + fprintf(out, "REG"); + break; + case F_EXPR_NREG: + fprintf(out, "NREG"); + break; + case F_EXPR_NEG: + fprintf(out, "NOT"); + break; + } + + fprintf(out, "\n"); + + if (n->left) { + indent_inside(out, i); + fprintf(out, "left: "); + filter_dump_node(out, plus_indent(i), n->left); + } + + if (n->right) { + indent_inside(out, i); + fprintf(out, "right: "); + filter_dump_node(out, plus_indent(i), n->right); + } + + indent(out, i); + fprintf(out, "}\n"); +} + + +void filter_unref_node(struct filter_node *n) +{ + if (!n || --n->refcount > 0) + return; + + switch (n->type) { + case F_NODE_EXPR: + free_expr((struct filter_expr *) n); + break; + case F_NODE_PARAM: + free_param((struct filter_param *) n); + break; + } +} + +void filter_dump_node(FILE *out, int i, struct filter_node *n) +{ + if (!n) + return; + + switch (n->type) { + case F_NODE_EXPR: + dump_expr(out, i, (struct filter_expr *) n); + break; + case F_NODE_PARAM: + dump_param(out, i, (struct filter_param *) n); + break; + } + if (i == 0) + fprintf(out, "\n"); +} diff --git a/libsmartcols/src/filter-parser.y b/libsmartcols/src/filter-parser.y new file mode 100644 index 0000000000..4c93a00961 --- /dev/null +++ b/libsmartcols/src/filter-parser.y @@ -0,0 +1,100 @@ +%{ +#include +#include "smartcolsP.h" + +#include "filter-parser.h" +#include "filter-scanner.h" + +void yyerror(yyscan_t *locp, struct libscols_filter *fltr, char const *msg); +%} + +%define api.pure full + +%lex-param {void *scanner} +%parse-param {void *scanner}{struct libscols_filter *fltr} + +%define parse.trace +%define parse.error verbose + +%header "filter-parser.h" + +%code requires +{ + #include "smartcolsP.h" +} + +/* Elegant way, but not compatible with biron -y (autotools): +%define api.value.type union +%token param_number +%token param_string +%token param_name +%token param_float +%type param +%type expr +*/ + +%union { + unsigned long long param_number; + const char* param_string; + const char* param_name; + long double param_float; + struct filter_node *param; + struct filter_node *expr; +} +%token param_number +%token param_string +%token param_name +%token param_float +%type param expr + +%token T_OR T_AND T_EQ T_NE T_LT T_LE T_GT T_GE T_REG T_NREG T_TRUE T_FALSE T_NEG +%left T_OR T_AND +%left T_EQ T_NE T_LT T_LE T_GT T_GE T_REG T_NREG T_TRUE T_FALSE T_NEG + +%% + +%start filter; + +filter: + expr { fltr->root = $1; } +; + +expr: + param { $$ = $1; } + | '(' expr ')' { $$ = $2; } + | expr T_AND expr { $$ = filter_new_expr(fltr, F_EXPR_AND, $1, $3); } + | expr T_OR expr { $$ = filter_new_expr(fltr, F_EXPR_OR, $1, $3); } + | T_NEG expr { $$ = filter_new_expr(fltr, F_EXPR_NEG, NULL, $2); } + | expr T_EQ expr { $$ = filter_new_expr(fltr, F_EXPR_EQ, $1, $3); } + | expr T_NE expr { $$ = filter_new_expr(fltr, F_EXPR_NE, $1, $3); } + | expr T_LE expr { $$ = filter_new_expr(fltr, F_EXPR_LE, $1, $3); } + | expr T_LT expr { $$ = filter_new_expr(fltr, F_EXPR_LT, $1, $3); } + | expr T_GE expr { $$ = filter_new_expr(fltr, F_EXPR_GE, $1, $3); } + | expr T_GT expr { $$ = filter_new_expr(fltr, F_EXPR_GT, $1, $3); } + | expr T_REG expr { $$ = filter_new_expr(fltr, F_EXPR_REG, $1, $3); } + | expr T_NREG expr { $$ = filter_new_expr(fltr, F_EXPR_NREG, $1, $3); } +; + +param: + param_number { $$ = filter_new_param(fltr, F_PARAM_NUMBER, (void *) (&$1)); } + | param_float { $$ = filter_new_param(fltr, F_PARAM_FLOAT, (void *) (&$1)); } + | param_name { $$ = filter_new_param(fltr, F_PARAM_NAME, (void *) $1); } + | param_string { $$ = filter_new_param(fltr, F_PARAM_STRING, (void *) $1); } + | T_TRUE { + bool x = true; + $$ = filter_new_param(fltr, F_PARAM_BOOLEAN, (void *) &x); + } + | T_FALSE { + bool x = false; + $$ = filter_new_param(fltr, F_PARAM_BOOLEAN, (void *) &x); + } + +; + + +%% + +void yyerror (yyscan_t *locp, struct libscols_filter *fltr, char const *msg) +{ + fprintf(stderr, "--> %s\n", msg); +} diff --git a/libsmartcols/src/filter-scanner.l b/libsmartcols/src/filter-scanner.l new file mode 100644 index 0000000000..71ce3d185e --- /dev/null +++ b/libsmartcols/src/filter-scanner.l @@ -0,0 +1,58 @@ +%{ +#include "filter-parser.h" /* define tokens (T_*) */ +%} + +%option reentrant bison-bridge noyywrap noinput nounput + +id [a-zA-Z][a-zA-Z_0-9]* +int [0-9]+ +blank [ \t] +string \"[^\"\n]*\" + +%% + +{blank}+ ; /* ignore */ +[\n]+ ; /* ignore */ + +"(" return '('; +")" return ')'; +"'" return '\''; + +false|FALSE return T_FALSE; +true|TRUE return T_TRUE; + +{int}+\.{int}+ { + yylval->param_float = strtold(yytext, NULL); + return param_float; +} + +{int}+ { + yylval->param_number = (int64_t) strtoumax(yytext, NULL, 10); + return param_number; +} + +{id} { + yylval->param_name = yytext; + return param_name; +} + +{string} { + yylval->param_string = yytext; + return param_string; +} + +and|AND|"&&" return T_AND; +or|OR|"||" return T_OR; +"!"|not|NOT return T_NEG; + +eq|"==" return T_EQ; +ne|"!=" return T_NE; + +le|"<=" return T_LE; +lt|"<" return T_LT; + +ge|">=" return T_GE; +gt|">" return T_GT; + +"=~" return T_REG; +"!~" return T_NREG; diff --git a/libsmartcols/src/filter.c b/libsmartcols/src/filter.c new file mode 100644 index 0000000000..74501501a6 --- /dev/null +++ b/libsmartcols/src/filter.c @@ -0,0 +1,55 @@ +#include +#include +#include +#include + +#include "smartcolsP.h" + +#include "filter-parser.h" +#include "filter-scanner.h" + +struct libscols_filter *scols_new_filter() +{ + struct libscols_filter *fltr = calloc(1, sizeof(*fltr)); + + return fltr; +} + +static void reset_filter(struct libscols_filter *fltr) +{ + if (!fltr) + return; + filter_unref_node(fltr->root); + fltr->root = NULL; + + if (fltr->src) + fclose(fltr->src); + fltr->src = NULL; +} + +int scols_filter_parse_string(struct libscols_filter *fltr, const char *string) +{ + yyscan_t sc; + int rc; + + reset_filter(fltr); + + fltr->src = fmemopen((void *) string, strlen(string) + 1, "r"); + if (!fltr->src) + return -errno; + + yylex_init(&sc); + yyset_in(fltr->src, sc); + + rc = yyparse(sc, fltr); + yylex_destroy(sc); + + return rc; +} + +/* TODO: + +scols_dump_filter() +scols_line_apply_filter() +scols_table_apply_filter() + */ diff --git a/libsmartcols/src/smartcolsP.h b/libsmartcols/src/smartcolsP.h index afb0c4b77d..1b476f8599 100644 --- a/libsmartcols/src/smartcolsP.h +++ b/libsmartcols/src/smartcolsP.h @@ -19,6 +19,8 @@ #include "debug.h" #include "buffer.h" +#include + #include "libsmartcols.h" /* @@ -475,4 +477,85 @@ static inline int has_group_children(struct libscols_line *ln) return ln && ln->group && !list_empty(&ln->group->gr_children); } +/* + * Filter stuff + */ + +/* node types */ +enum filter_ntype { + F_NODE_PARAM, + F_NODE_EXPR +}; + +/* param types */ +enum filter_ptype { + F_PARAM_NUMBER, + F_PARAM_FLOAT, + F_PARAM_NAME, + F_PARAM_STRING, + F_PARAM_BOOLEAN +}; + +/* expresion types */ +enum filter_etype { + F_EXPR_AND, + F_EXPR_OR, + F_EXPR_NEG, + + F_EXPR_EQ, + F_EXPR_NE, + + F_EXPR_LT, + F_EXPR_LE, + F_EXPR_GT, + F_EXPR_GE, + + F_EXPR_REG, + F_EXPR_NREG, +}; + +struct filter_node { + enum filter_ntype type; + int refcount; +}; + +#define filter_node_get_type(n) (((struct filter_node *)(n))->type) + +struct filter_param { + struct filter_node node; + enum filter_ptype type; + + union { + char *str; + unsigned long long num; + long double fnum; + bool boolean; + } val; +}; + +struct filter_expr { + struct filter_node node; + enum filter_etype type; + + struct filter_node *left; + struct filter_node *right; +}; + +struct libscols_filter { + struct filter_node *root; + FILE *src; +}; + +void filter_unref_node(struct filter_node *n); +void filter_dump_node(FILE *out, int i, struct filter_node *n); + +/* reuiqred by parser */ +struct filter_node *filter_new_param(struct libscols_filter *filter, + enum filter_ptype type, + void *data); +struct filter_node *filter_new_expr(struct libscols_filter *filter, + enum filter_etype type, + struct filter_node *left, + struct filter_node *right); + #endif /* _LIBSMARTCOLS_PRIVATE_H */ -- 2.47.3