[gst-debugger] gst-debugger: add filter parser



commit 2dfb687f35c0836c40a031ee5438c8a091684b78
Author: Marcin Kolny <marcin kolny gmail com>
Date:   Tue Sep 15 22:27:45 2015 +0200

    gst-debugger: add filter parser

 src/gst-debugger/Makefile.am                  |    7 ++
 src/gst-debugger/filter-parser/expression.h   |   55 ++++++++++++
 src/gst-debugger/filter-parser/lexer.cpp      |  113 +++++++++++++++++++++++++
 src/gst-debugger/filter-parser/lexer.h        |   34 ++++++++
 src/gst-debugger/filter-parser/parser.cpp     |   10 ++
 src/gst-debugger/filter-parser/parser.h       |   63 ++++++++++++++
 src/gst-debugger/filter-parser/tokens.cpp     |   67 +++++++++++++++
 src/gst-debugger/filter-parser/tokens.h       |  109 ++++++++++++++++++++++++
 src/gst-debugger/main.cpp                     |    2 +
 src/gst-debugger/modules/base_main_module.cpp |   16 +++-
 src/gst-debugger/modules/base_main_module.h   |    5 +-
 src/gst-debugger/modules/log_module.cpp       |   33 +++++++-
 src/gst-debugger/modules/main_module.cpp      |    2 +-
 13 files changed, 507 insertions(+), 9 deletions(-)
---
diff --git a/src/gst-debugger/Makefile.am b/src/gst-debugger/Makefile.am
index ab27b6e..34474ec 100644
--- a/src/gst-debugger/Makefile.am
+++ b/src/gst-debugger/Makefile.am
@@ -29,6 +29,10 @@ gst_debugger_headers =                                       \
        dialogs/enums_dialog.h                          \
        dialogs/factories_dialog.h                      \
        dialogs/remote_data_dialog.h                    \
+       filter-parser/expression.h                      \
+       filter-parser/lexer.h                           \
+       filter-parser/parser.cpp                        \
+       filter-parser/tokens.h                          \
        graphviz-plugin/graphviz-gstdebugger.h          \
        gst-debugger-resources.h                        \
        gvalue-converter/gvalue_base.h                  \
@@ -66,6 +70,9 @@ gst_debugger_ GST_API_VERSION@_SOURCES =              \
        dialogs/enums_dialog.cpp                        \
        dialogs/factories_dialog.cpp                    \
        graphviz-plugin/gvdevice_gstdebugger.c          \
+       filter-parser/lexer.cpp                         \
+       filter-parser/parser.cpp                        \
+       filter-parser/tokens.cpp                        \
        graphviz-plugin/gvplugin_gstdebugger.c          \
        gst-debugger-resources.c                        \
        gvalue-converter/gvalue_base.cpp                \
diff --git a/src/gst-debugger/filter-parser/expression.h b/src/gst-debugger/filter-parser/expression.h
new file mode 100644
index 0000000..0ef5100
--- /dev/null
+++ b/src/gst-debugger/filter-parser/expression.h
@@ -0,0 +1,55 @@
+/*
+ * expression.h
+ *
+ *  Created on: Sep 3, 2015
+ *      Author: loganek
+ */
+
+#ifndef SRC_GST_DEBUGGER_FILTER_PARSER_EXPRESSION_H_
+#define SRC_GST_DEBUGGER_FILTER_PARSER_EXPRESSION_H_
+
+#include <stdexcept>
+
+enum class ExprOperator
+{
+       EQ,             // ==
+       NEQ,    // !=
+       GE,             // >=
+       LE,             // <=
+       G,              // >
+       L               // <
+};
+
+template<typename T>
+class Expression
+{
+       T left;
+       T right;
+       ExprOperator op;
+
+public:
+       virtual ~Expression() {}
+
+       bool evaluate() const;
+};
+
+template<typename T>
+bool Expression<T>::evaluate() const
+{
+#define APPLY_OPERATOR(OP_ENUM, OP_REAL) \
+       case ExprOperator::OP_ENUM: return left OP_REAL right;
+       switch (op)
+       {
+       APPLY_OPERATOR(EQ, ==)
+       APPLY_OPERATOR(NEQ, !=)
+       APPLY_OPERATOR(GE, >=)
+       APPLY_OPERATOR(LE, <=)
+       APPLY_OPERATOR(G, >)
+       APPLY_OPERATOR(L, <)
+       }
+
+       throw std::runtime_error("unknown operator");
+}
+
+
+#endif /* SRC_GST_DEBUGGER_FILTER_PARSER_EXPRESSION_H_ */
diff --git a/src/gst-debugger/filter-parser/lexer.cpp b/src/gst-debugger/filter-parser/lexer.cpp
new file mode 100644
index 0000000..bc13c9b
--- /dev/null
+++ b/src/gst-debugger/filter-parser/lexer.cpp
@@ -0,0 +1,113 @@
+/*
+ * lexer.cpp
+ *
+ *  Created on: Sep 3, 2015
+ *      Author: loganek
+ */
+
+#include "lexer.h"
+
+#include <algorithm>
+
+enum class State
+{
+       NUMBER,
+       STRING,
+       VARIABLE,
+       OPERATOR
+};
+
+void Lexer::read_number()
+{
+       int tmp = pos--;
+       while (++pos < src_text.length() && isdigit(src_text[pos]));
+       tokens.push_back(std::make_shared<TokenNumber>(std::string(src_text.begin() + tmp, src_text.begin() + 
pos--)));
+}
+
+void Lexer::read_operator()
+{
+       int tmp = pos--;
+       while (++pos < src_text.length() && TokenOperator::is_part_of_op(src_text[pos]));
+       tokens.push_back(std::make_shared<TokenOperator>(std::string(src_text.begin() + tmp, src_text.begin() 
+ pos--)));
+}
+
+void Lexer::read_string()
+{
+       char quote = src_text[pos];
+       bool prev_esc = false;
+       std::string val;
+       char c = -1;
+       while (pos < src_text.length())
+       {
+               c = src_text[++pos];
+               if (prev_esc)
+               {
+                       if (TokenString::is_quote(c) || c == '\\')
+                       {
+                               val += c;
+                               prev_esc = false;
+                       }
+                       else
+                       {
+                               throw std::runtime_error("unknown escape sequence: '" + std::string("\\") + c 
+ "'");
+                       }
+               }
+               else if (c == '\\')
+               {
+                       prev_esc = true;
+               }
+               else if (c == quote)
+               {
+                       break;
+               }
+               else
+               {
+                       val += c;
+               }
+       }
+
+       if (prev_esc)
+       {
+               throw std::runtime_error("incomplete escape sequence");
+       }
+       if (c != quote)
+       {
+               throw std::runtime_error("missing terminating " + std::string{quote} + " character");
+       }
+
+       tokens.push_back(std::make_shared<TokenString>(val));
+}
+
+void Lexer::read_identifier()
+{
+       int tmp = pos;
+
+       while (!isspace(src_text[pos]) &&
+                       !TokenString::is_quote(src_text[pos]) &&
+                       !TokenOperator::is_part_of_op(src_text[pos]) &&
+                       pos < src_text.length())
+       {
+               pos++;
+       }
+
+       tokens.push_back(std::make_shared<TokenIdentifier>(std::string(src_text.begin() + tmp, 
src_text.begin() + pos--)));
+}
+
+void Lexer::tokenize(const std::string &src)
+{
+       src_text = src;
+
+       for (pos = 0; pos < src_text.length(); pos++)
+       {
+               char c = src_text[pos];
+
+               if (isdigit(c))
+                       read_number();
+               else if (TokenOperator::is_part_of_op(c))
+                       read_operator();
+               else if (TokenString::is_quote(c))
+                       read_string();
+               else if (TokenIdentifier::is_id_start(c))
+                       read_identifier();
+       }
+}
diff --git a/src/gst-debugger/filter-parser/lexer.h b/src/gst-debugger/filter-parser/lexer.h
new file mode 100644
index 0000000..4380ee8
--- /dev/null
+++ b/src/gst-debugger/filter-parser/lexer.h
@@ -0,0 +1,34 @@
+/*
+ * lexer.h
+ *
+ *  Created on: Sep 3, 2015
+ *      Author: loganek
+ */
+
+#ifndef SRC_GST_DEBUGGER_FILTER_PARSER_LEXER_H_
+#define SRC_GST_DEBUGGER_FILTER_PARSER_LEXER_H_
+
+#include "tokens.h"
+
+#include <vector>
+#include <memory>
+
+class Lexer
+{
+       std::vector<std::shared_ptr<TokenBase>> tokens;
+       int pos;
+       std::string src_text;
+
+       void read_number();
+       void read_operator();
+       void read_string();
+       void read_identifier();
+
+public:
+       virtual ~Lexer() {}
+       void tokenize(const std::string &src);
+
+       decltype(tokens) get_tokens() const { return tokens; }
+};
+
+#endif /* SRC_GST_DEBUGGER_FILTER_PARSER_LEXER_H_ */
diff --git a/src/gst-debugger/filter-parser/parser.cpp b/src/gst-debugger/filter-parser/parser.cpp
new file mode 100644
index 0000000..f2a4740
--- /dev/null
+++ b/src/gst-debugger/filter-parser/parser.cpp
@@ -0,0 +1,10 @@
+/*
+ * parser.cpp
+ *
+ *  Created on: Sep 13, 2015
+ *      Author: loganek
+ */
+
+#include "parser.h"
+
+
diff --git a/src/gst-debugger/filter-parser/parser.h b/src/gst-debugger/filter-parser/parser.h
new file mode 100644
index 0000000..06a28fa
--- /dev/null
+++ b/src/gst-debugger/filter-parser/parser.h
@@ -0,0 +1,63 @@
+/*
+ * parser.h
+ *
+ *  Created on: Sep 13, 2015
+ *      Author: loganek
+ */
+
+#ifndef SRC_GST_DEBUGGER_FILTER_PARSER_PARSER_H_
+#define SRC_GST_DEBUGGER_FILTER_PARSER_PARSER_H_
+
+#include "lexer.h"
+
+#include <stack>
+
+struct Expression
+{
+       const std::shared_ptr<TokenOperator> op;
+       const std::shared_ptr<TokenBase> left;
+       const std::shared_ptr<TokenBase> right;
+
+       Expression(const std::shared_ptr<TokenOperator> &op,
+                       const std::shared_ptr<TokenBase> &left,
+                       const std::shared_ptr<TokenBase> &right)
+       : op(op), left(left), right(right)
+       {}
+
+       virtual ~Expression() {}
+};
+
+class Parser
+{
+       void check_token(const std::shared_ptr<TokenBase> &token, TokenType expected_type)
+       {
+               if ((token->get_type() & expected_type) == TokenType::INVALID)
+               {
+                       throw std::runtime_error("expected token type " + 
std::to_string(static_cast<int>(expected_type)) +
+                                       " (" + token->to_string() + ")");
+               }
+       }
+
+public:
+       virtual ~Parser() {}
+
+       Expression parse(const std::string &str)
+       {
+               Lexer lexer;
+               lexer.tokenize(str);
+               auto tokens = lexer.get_tokens();
+
+               if (tokens.size() != 3)
+               {
+                       throw std::runtime_error("invalid number of tokens (3 expected)");
+               }
+
+               check_token(tokens[0], TokenType::LITERAL | TokenType::IDENTIFIER);
+               check_token(tokens[1], TokenType::OPERATOR);
+               check_token(tokens[2], TokenType::LITERAL | TokenType::IDENTIFIER);
+
+               return Expression(std::static_pointer_cast<TokenOperator>(tokens[1]), tokens[0], tokens[2]);
+       }
+};
+
+#endif /* SRC_GST_DEBUGGER_FILTER_PARSER_PARSER_H_ */
diff --git a/src/gst-debugger/filter-parser/tokens.cpp b/src/gst-debugger/filter-parser/tokens.cpp
new file mode 100644
index 0000000..cb7a863
--- /dev/null
+++ b/src/gst-debugger/filter-parser/tokens.cpp
@@ -0,0 +1,67 @@
+/*
+ * tokens.cpp
+ *
+ *  Created on: Sep 13, 2015
+ *      Author: loganek
+ */
+
+#include "tokens.h"
+
+#include <stdexcept>
+#include <algorithm>
+
+TokenNumber::TokenNumber(const std::string &number)
+ : Token(TokenType::NUMBER_LITERAL)
+{
+       value = atoi(number.c_str());
+}
+
+TokenString::TokenString(const std::string &str)
+ : Token(TokenType::STRING_LITERAL)
+{
+       value = str;
+}
+
+bool TokenString::is_quote(char c)
+{
+       return c == '"' || c == '\'';
+}
+
+TokenIdentifier::TokenIdentifier(const std::string &identifier)
+ : Token(TokenType::IDENTIFIER)
+{
+       value = identifier;
+}
+
+bool TokenIdentifier::is_id_start(char c)
+{
+       return isalpha(c);
+}
+
+TokenOperator::TokenOperator(const std::string &op)
+ : Token(TokenType::OPERATOR)
+{
+       if (op == "==") value = OpType::EQ;
+       else if (op == "!=") value = OpType::NEQ;
+       else if (op == "||") value = OpType::OR;
+       else if (op == "&&") value = OpType::AND;
+       else throw std::runtime_error ("invalid operator " + op);
+}
+
+bool TokenOperator::is_part_of_op(char c)
+{
+       auto v = {'=', '!', '&', '|'};
+       return std::find(v.begin(), v.end(), c) != v.end();
+}
+
+std::string TokenOperator::to_string() const
+{
+       switch (value)
+       {
+       case OpType::AND: return "&&";
+       case OpType::OR: return "||";
+       case OpType::EQ: return "==";
+       case OpType::NEQ: return "!=";
+       }
+       throw std::runtime_error("unknown operator");
+}
diff --git a/src/gst-debugger/filter-parser/tokens.h b/src/gst-debugger/filter-parser/tokens.h
new file mode 100644
index 0000000..c52108f
--- /dev/null
+++ b/src/gst-debugger/filter-parser/tokens.h
@@ -0,0 +1,109 @@
+/*
+ * tokens.h
+ *
+ *  Created on: Sep 13, 2015
+ *      Author: loganek
+ */
+
+#ifndef SRC_GST_DEBUGGER_FILTER_PARSER_TOKENS_H_
+#define SRC_GST_DEBUGGER_FILTER_PARSER_TOKENS_H_
+
+#include <string>
+
+enum class TokenType : int
+{
+       INVALID = 0x00,
+       IDENTIFIER = 0x01,
+       OPERATOR = 0x02,
+       LITERAL = 0x04,
+       NUMBER_LITERAL = 0x05,
+       STRING_LITERAL = 0x06,
+};
+
+inline TokenType operator|(const TokenType &t1, const TokenType &t2)
+{
+       return static_cast<TokenType>(static_cast<int>(t1) | static_cast<int>(t2));
+}
+
+inline TokenType operator&(const TokenType &t1, const TokenType &t2)
+{
+       return static_cast<TokenType>(static_cast<int>(t1) & static_cast<int>(t2));
+}
+
+class TokenBase
+{
+       TokenType type;
+
+public:
+       virtual ~TokenBase() {}
+       TokenBase(TokenType type) : type(type) {}
+
+       virtual std::string to_string() const = 0;
+
+       TokenType get_type() const { return type; }
+};
+
+template<typename T>
+class Token : public TokenBase
+{
+protected:
+       T value;
+
+public:
+       virtual ~Token() {}
+       Token(TokenType type) : TokenBase(type) {}
+
+       T get_value() const { return value; }
+};
+
+class TokenNumber : public Token<int>
+{
+public:
+       TokenNumber(const std::string &number);
+
+       std::string to_string() const override
+       {
+               return std::to_string(value);
+       }
+
+};
+
+class TokenString : public Token<std::string>
+{
+public:
+       TokenString(const std::string &str);
+
+       std::string to_string() const override { return value; }
+
+       static bool is_quote(char c);
+};
+
+class TokenIdentifier : public Token<std::string>
+{
+public:
+       TokenIdentifier(const std::string &identifier);
+
+       std::string to_string() const override { return value; }
+
+       static bool is_id_start(char c);
+};
+
+enum class OpType
+{
+       EQ,
+       NEQ,
+       OR,
+       AND
+};
+
+class TokenOperator : public Token<OpType>
+{
+public:
+       TokenOperator(const std::string &op);
+
+       std::string to_string() const override;
+
+       static bool is_part_of_op(char c);
+};
+
+#endif /* SRC_GST_DEBUGGER_FILTER_PARSER_TOKENS_H_ */
diff --git a/src/gst-debugger/main.cpp b/src/gst-debugger/main.cpp
index efbcf5b..3d6fc53 100644
--- a/src/gst-debugger/main.cpp
+++ b/src/gst-debugger/main.cpp
@@ -12,6 +12,8 @@
 #include <gtkmm.h>
 #include <gstreamermm.h>
 
+#include "filter-parser/parser.h"
+
 int main(int argc, char** argv)
 {
        Gst::init(argc, argv);
diff --git a/src/gst-debugger/modules/base_main_module.cpp b/src/gst-debugger/modules/base_main_module.cpp
index a0bd7e4..896433b 100644
--- a/src/gst-debugger/modules/base_main_module.cpp
+++ b/src/gst-debugger/modules/base_main_module.cpp
@@ -32,10 +32,20 @@ void BaseMainModule::load_details(Gtk::TreeView *view, const Gtk::TreeModel::Pat
        view->set_model(details_model);
 }
 
-void BaseMainModule::update_filter_string(const std::string &filter_text)
+void BaseMainModule::update_filter_expression(const std::string &expr)
 {
-       this->filter_text = filter_text;
-       filter->refilter();
+       Parser p;
+       auto prev = filter_expression;
+       try
+       {
+               filter_expression = std::make_shared<Expression>(p.parse(expr));
+       }
+       catch (...) { filter_expression = std::shared_ptr<Expression>(); }
+
+       if (prev != filter_expression)
+       {
+               filter->refilter();
+       }
 }
 
 void BaseMainModule::configure_details_view(Gtk::TreeView *view)
diff --git a/src/gst-debugger/modules/base_main_module.h b/src/gst-debugger/modules/base_main_module.h
index 60734be..6912f56 100644
--- a/src/gst-debugger/modules/base_main_module.h
+++ b/src/gst-debugger/modules/base_main_module.h
@@ -10,6 +10,7 @@
 
 #include "controller/iview.h"
 #include "common_model_columns.h"
+#include "filter-parser/parser.h"
 
 #include <gtkmm/liststore.h>
 #include <gtkmm/treeview.h>
@@ -18,7 +19,7 @@
 class BaseMainModule : public IBaseView
 {
 protected:
-       std::string filter_text;
+       std::shared_ptr<Expression> filter_expression;
 
        static DetailsModelColumns detail_columns;
 
@@ -41,7 +42,7 @@ public:
 
        static void configure_details_view(Gtk::TreeView *view);
 
-       void update_filter_string(const std::string &filter_text);
+       void update_filter_expression(const std::string &expr);
 
        Glib::RefPtr<Gtk::ListStore> get_model() const { return model; }
 };
diff --git a/src/gst-debugger/modules/log_module.cpp b/src/gst-debugger/modules/log_module.cpp
index 6b87838..a6b101e 100644
--- a/src/gst-debugger/modules/log_module.cpp
+++ b/src/gst-debugger/modules/log_module.cpp
@@ -66,7 +66,7 @@ void LogModule::log_received_()
 
 bool LogModule::filter_function(const Gtk::TreeModel::const_iterator& it)
 {
-       if (filter_text.empty())
+       if (!filter_expression)
                return true;
 
        auto log = it->get_value(columns.log);
@@ -74,9 +74,36 @@ bool LogModule::filter_function(const Gtk::TreeModel::const_iterator& it)
        if (log == nullptr)
                return true;
 
-       int line = atoi(filter_text.c_str());
+       std::shared_ptr<TokenIdentifier> ident;
+       std::shared_ptr<TokenBase> value;
 
-       return log->line() == line;
+       if (filter_expression->left->get_type() == TokenType::IDENTIFIER)
+       {
+               ident = std::static_pointer_cast<TokenIdentifier>(filter_expression->left);
+               value = filter_expression->right;
+       }
+       else
+       {
+               ident = std::static_pointer_cast<TokenIdentifier>(filter_expression->right);
+               value = filter_expression->left;
+       }
+
+#define MAKE_FIELD_FILTER(FIELD, GETTER, TOKEN_CLASS) \
+       do { \
+               if (ident->get_value() == FIELD) \
+                       return filter_expression->op->get_value() == OpType::EQ ? (log->GETTER() == 
std::static_pointer_cast<TOKEN_CLASS>(value)->get_value()) : \
+                                       (log->GETTER() != 
std::static_pointer_cast<TOKEN_CLASS>(value)->get_value()); \
+       } while (false);
+
+       MAKE_FIELD_FILTER("line", line, TokenNumber);
+       MAKE_FIELD_FILTER("level", level, TokenNumber);
+       MAKE_FIELD_FILTER("category", category_name, TokenString);
+       MAKE_FIELD_FILTER("file", file, TokenString);
+       MAKE_FIELD_FILTER("function", function, TokenString);
+       MAKE_FIELD_FILTER("object_path", object_path, TokenString);
+       MAKE_FIELD_FILTER("message", message, TokenString);
+
+       return true;
 }
 
 LogControlModule::LogControlModule()
diff --git a/src/gst-debugger/modules/main_module.cpp b/src/gst-debugger/modules/main_module.cpp
index b62e2e8..93a1901 100644
--- a/src/gst-debugger/modules/main_module.cpp
+++ b/src/gst-debugger/modules/main_module.cpp
@@ -28,7 +28,7 @@ MainModule::MainModule(const Glib::RefPtr<Gtk::Builder> &builder)
 
        builder->get_widget("dataFilterEntry", data_filter_entry);
        data_filter_entry->signal_activate().connect([this]{
-               current_module->update_filter_string(data_filter_entry->get_text());
+               current_module->update_filter_expression(data_filter_entry->get_text());
        });
 
        load_submodules(builder);


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]