#pragma once #include #include #include #include namespace DB { /// Visits AST tree in depth, call functions for nodes according to Matcher type data. /// You need to define Data, visit() and needChildVisit() in Matcher class. template class InDepthNodeVisitor { public: using Data = typename Matcher::Data; InDepthNodeVisitor(Data & data_, std::ostream * ostr_ = nullptr) : data(data_), visit_depth(0), ostr(ostr_) {} void visit(ASTPtr & ast) { DumpASTNode dump(*ast, ostr, visit_depth, typeid(Matcher).name()); if constexpr (!_top_to_bottom) visitChildren(ast); Matcher::visit(ast, data); if constexpr (_top_to_bottom) visitChildren(ast); } private: Data & data; size_t visit_depth; std::ostream * ostr; void visitChildren(ASTPtr & ast) { for (auto & child : ast->children) if (Matcher::needChildVisit(ast, child)) visit(child); } }; /// Simple matcher for one node type without complex traversal logic. template class OneTypeMatcher { public: using Data = _Data; using TypeToVisit = typename Data::TypeToVisit; static bool needChildVisit(ASTPtr &, const ASTPtr &) { return _visit_children; } static void visit(ASTPtr & ast, Data & data) { if (auto * t = typeid_cast(ast.get())) data.visit(*t, ast); } }; /// Links two simple matches into resulting one. There's no complex traversal logic: all the children would be visited. template class LinkedMatcher { public: using Data = std::pair; static bool needChildVisit(ASTPtr &, const ASTPtr &) { return true; } static void visit(ASTPtr & ast, Data & data) { First::visit(ast, data.first); Second::visit(ast, data.second); } }; }