cql_ruby 0.0.6 → 0.0.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/bin/cql_ruby +27 -5
- data/lib/cql_ruby/abstract_printer.rb +1 -1
- data/lib/cql_ruby/console_printer.rb +2 -2
- data/lib/cql_ruby/crumb_collector.rb +1 -1
- data/lib/cql_ruby/executor.rb +72 -26
- data/lib/cql_ruby/filter_evaluator.rb +82 -4
- data/lib/cql_ruby/filter_reader.rb +23 -7
- data/lib/cql_ruby/pattern_matcher.rb +5 -1
- metadata +1 -1
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: d117740a94a14a7c3f8a6a62f7bb22ae3dbbc450a909de2c9d5c3a9d5eb6fb91
         | 
| 4 | 
            +
              data.tar.gz: 1850b47222e7d5be2caf3b50221f2facececedff355873f4b752d08a9aabf096
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: 37a8dc083b6a7ee31d93ad01e01a1028bf58a0e5cf4ddee3fdb676ac3af606aeab679d6f9e8498865b42797cb159da93fa65479bce432811ead57f1c8827e0d9
         | 
| 7 | 
            +
              data.tar.gz: a78b4f787e661d21e02f914058b6877ef863de7ed21beef8502f1ab278fa61799da32dd0a3956f54f4c0a7a9d3af95f34e7203819090f4f1189879333f0d4b70
         | 
    
        data/bin/cql_ruby
    CHANGED
    
    | @@ -1,12 +1,14 @@ | |
| 1 1 | 
             
            #!/usr/bin/env ruby
         | 
| 2 2 |  | 
| 3 | 
            +
            require 'parser'
         | 
| 3 4 | 
             
            require 'cql_ruby'
         | 
| 4 5 |  | 
| 5 6 | 
             
            def show_help
         | 
| 6 7 | 
             
              puts <<~HELP
         | 
| 7 8 |  | 
| 8 9 | 
             
                \tSYNOPSIS
         | 
| 9 | 
            -
                \t\tcql_ruby  | 
| 10 | 
            +
                \t\tcql_ruby [--token] pattern path options filters ...
         | 
| 11 | 
            +
                \t\tcql_ruby --node    type    path options filters ...
         | 
| 10 12 |  | 
| 11 13 | 
             
                \tDESCRIPTION
         | 
| 12 14 | 
             
                \t\tCQL (Code Query Language) is a semantic search tool for your Ruby source code.
         | 
| @@ -14,8 +16,11 @@ def show_help | |
| 14 16 | 
             
                \tFILTERS
         | 
| 15 17 | 
             
                \t\tParent node type: type:T(,T)* Example: type:def,send,arg
         | 
| 16 18 | 
             
                \t\tNesting under: nest:T(=NAME) Example: nest:def=save_user nest:class=UserManager
         | 
| 19 | 
            +
                \t\tHas child: has:T(=NAME) Example: has:const has:def=valid?
         | 
| 17 20 |  | 
| 18 21 | 
             
                \tOPTIONS
         | 
| 22 | 
            +
                \t\t--include=PATTERN Parses only files whose name matches the pattern.
         | 
| 23 | 
            +
                \t\t--exclude=PATTERN Parses only files whose name does not match the pattern.
         | 
| 19 24 | 
             
                \t\t-lN (N is integer) Add N surrounding line before and after.
         | 
| 20 25 | 
             
                \t\t-nc (--no-color) No color on output.
         | 
| 21 26 | 
             
                \t\t-nf (--no-file) No file names.
         | 
| @@ -23,9 +28,13 @@ def show_help | |
| 23 28 | 
             
                \t\t-nr (--no-recursion) Non-recursive search.
         | 
| 24 29 | 
             
                \t\t-v -vv -vvv Debug output levels.
         | 
| 25 30 |  | 
| 31 | 
            +
                \tALLOWED NODE TYPES
         | 
| 32 | 
            +
                \t\tWhen defining filters only valid AST types can be defined. They are:
         | 
| 33 | 
            +
                #{Parser::Meta::NODE_TYPES.to_a.join(' ')}
         | 
| 34 | 
            +
             | 
| 26 35 | 
             
                \tEXAMPLES
         | 
| 27 36 | 
             
                \t\tcql_ruby user ./
         | 
| 28 | 
            -
                \t\tcql_ruby -ns -nr %user_info ./ type:send,arg nest:block nest:class=r/User/i
         | 
| 37 | 
            +
                \t\tcql_ruby -ns -nr %user_info ./ type:send,arg nest:block nest:class=r/User/i has:str=WARNING
         | 
| 29 38 | 
             
              HELP
         | 
| 30 39 |  | 
| 31 40 | 
             
              exit
         | 
| @@ -39,6 +48,9 @@ def extract_options | |
| 39 48 | 
             
                show_source: true,
         | 
| 40 49 | 
             
                recursive_search: true,
         | 
| 41 50 | 
             
                surrounding_lines: 0,
         | 
| 51 | 
            +
                include_pattern: nil,
         | 
| 52 | 
            +
                exclude_pattern: nil,
         | 
| 53 | 
            +
                search_type: :token,
         | 
| 42 54 | 
             
              }
         | 
| 43 55 |  | 
| 44 56 | 
             
              ARGV.delete_if do |arg|
         | 
| @@ -58,6 +70,14 @@ def extract_options | |
| 58 70 | 
             
                    options[:recursive_search] = false
         | 
| 59 71 | 
             
                  elsif arg[0..1] == '-l' && arg[2..].to_i > 0
         | 
| 60 72 | 
             
                    options[:surrounding_lines] = arg[2..].to_i
         | 
| 73 | 
            +
                  elsif arg.start_with?('--include=')
         | 
| 74 | 
            +
                    options[:include_pattern] = arg.split('=')[1]
         | 
| 75 | 
            +
                  elsif arg.start_with?('--exclude=')
         | 
| 76 | 
            +
                    options[:exclude_pattern] = arg.split('=')[1]
         | 
| 77 | 
            +
                  elsif arg == '--node'
         | 
| 78 | 
            +
                    options[:search_type] = :node
         | 
| 79 | 
            +
                  elsif arg == '--token'
         | 
| 80 | 
            +
                    options[:search_type] = :token
         | 
| 61 81 | 
             
                  else
         | 
| 62 82 | 
             
                    raise "Unknown arg #{arg}"
         | 
| 63 83 | 
             
                  end
         | 
| @@ -85,7 +105,6 @@ begin | |
| 85 105 | 
             
              pattern = ARGV.shift
         | 
| 86 106 | 
             
              CqlRuby.log "Call pattern: <#{pattern}>" if CqlRuby::Config.debug_level_2?
         | 
| 87 107 |  | 
| 88 | 
            -
              # TODO Make path patterns universal.
         | 
| 89 108 | 
             
              path = ARGV.shift
         | 
| 90 109 | 
             
              CqlRuby.log "Call path: <#{path}>" if CqlRuby::Config.debug_level_2?
         | 
| 91 110 |  | 
| @@ -109,8 +128,11 @@ begin | |
| 109 128 | 
             
                path: path,
         | 
| 110 129 | 
             
                filters: filters,
         | 
| 111 130 | 
             
                recursive: options[:recursive_search],
         | 
| 131 | 
            +
                include: options[:include_pattern],
         | 
| 132 | 
            +
                exclude: options[:exclude_pattern],
         | 
| 133 | 
            +
                search_type: options[:search_type],
         | 
| 112 134 | 
             
              ).search_all
         | 
| 113 | 
            -
            rescue | 
| 114 | 
            -
              puts "Error: #{ | 
| 135 | 
            +
            rescue
         | 
| 136 | 
            +
              puts "Error: #{$!}"
         | 
| 115 137 | 
             
              show_help
         | 
| 116 138 | 
             
            end
         | 
| @@ -21,7 +21,7 @@ module CqlRuby | |
| 21 21 | 
             
                end
         | 
| 22 22 |  | 
| 23 23 | 
             
                #
         | 
| 24 | 
            -
                # @param crumb [ | 
| 24 | 
            +
                # @param crumb [CqlRuby::Crumb]
         | 
| 25 25 | 
             
                #
         | 
| 26 26 | 
             
                def print(crumb)
         | 
| 27 27 | 
             
                  parts = "##{color(97)}#{@counter}#{decor_reset}"
         | 
| @@ -71,7 +71,7 @@ module CqlRuby | |
| 71 71 | 
             
                  end
         | 
| 72 72 | 
             
                end
         | 
| 73 73 |  | 
| 74 | 
            -
                # @param [ | 
| 74 | 
            +
                # @param [CqlRuby::Crumb] crumb
         | 
| 75 75 | 
             
                # @return [String]
         | 
| 76 76 | 
             
                def decorate_source_line(crumb)
         | 
| 77 77 | 
             
                  source = crumb.source
         | 
    
        data/lib/cql_ruby/executor.rb
    CHANGED
    
    | @@ -20,7 +20,7 @@ end | |
| 20 20 | 
             
            #
         | 
| 21 21 | 
             
            # Executes search and dumps results into the collector.
         | 
| 22 22 | 
             
            #
         | 
| 23 | 
            -
            # @param collector [ | 
| 23 | 
            +
            # @param collector [CqlRuby::CrumbCollector]
         | 
| 24 24 | 
             
            # @param pattern [String]
         | 
| 25 25 | 
             
            # @param path [String]
         | 
| 26 26 | 
             
            # @param filters [Array<String>]
         | 
| @@ -33,7 +33,10 @@ module CqlRuby | |
| 33 33 | 
             
                  pattern:,
         | 
| 34 34 | 
             
                  path:,
         | 
| 35 35 | 
             
                  filters: [],
         | 
| 36 | 
            -
                  recursive: true
         | 
| 36 | 
            +
                  recursive: true,
         | 
| 37 | 
            +
                  include: nil,
         | 
| 38 | 
            +
                  exclude: nil,
         | 
| 39 | 
            +
                  search_type: :token
         | 
| 37 40 | 
             
                )
         | 
| 38 41 | 
             
                  @collector = collector
         | 
| 39 42 | 
             
                  @filter_reader = filter_reader
         | 
| @@ -41,10 +44,16 @@ module CqlRuby | |
| 41 44 | 
             
                  @path = path
         | 
| 42 45 | 
             
                  @filters = filters
         | 
| 43 46 | 
             
                  @recursive = recursive
         | 
| 47 | 
            +
                  @include = include
         | 
| 48 | 
            +
                  @exclude = exclude
         | 
| 49 | 
            +
                  @search_type = search_type
         | 
| 44 50 | 
             
                end
         | 
| 45 51 |  | 
| 46 52 | 
             
                def search_all
         | 
| 47 53 | 
             
                  files.flat_map do |file|
         | 
| 54 | 
            +
                    next if !@exclude.nil? && CqlRuby::PatternMatcher.match?(@exclude, file)
         | 
| 55 | 
            +
                    next unless @include.nil? || CqlRuby::PatternMatcher.match?(@include, file)
         | 
| 56 | 
            +
             | 
| 48 57 | 
             
                    CqlRuby.log "File check: #{file}" if CqlRuby::Config.debug_level_3?
         | 
| 49 58 | 
             
                    search(file)
         | 
| 50 59 | 
             
                  end
         | 
| @@ -58,17 +67,24 @@ module CqlRuby | |
| 58 67 | 
             
                  walk(ast, [], source_reader)
         | 
| 59 68 |  | 
| 60 69 | 
             
                  nil
         | 
| 61 | 
            -
                rescue | 
| 62 | 
            -
                  CqlRuby.log "File #{file} cannot be parsed | 
| 70 | 
            +
                rescue
         | 
| 71 | 
            +
                  CqlRuby.log "File #{file} cannot be parsed"
         | 
| 72 | 
            +
                  CqlRuby.log "Reason: #{$!}" if CqlRuby::Config.debug_level_1?
         | 
| 63 73 | 
             
                end
         | 
| 64 74 |  | 
| 65 75 | 
             
                def walk(node, ancestors, source_reader)
         | 
| 66 76 | 
             
                  if node.is_a?(Parser::AST::Node)
         | 
| 77 | 
            +
                    if search_for_node?
         | 
| 78 | 
            +
                      if match?(node.type) && CqlRuby::FilterEvaluator.pass?(filter_reader, ancestors, node)
         | 
| 79 | 
            +
                        collector.add(CqlRuby::Crumb.new(node, ancestors, source_reader))
         | 
| 80 | 
            +
                      end
         | 
| 81 | 
            +
                    end
         | 
| 82 | 
            +
             | 
| 67 83 | 
             
                    node.children.flat_map do |child|
         | 
| 68 84 | 
             
                      walk(child, ancestors.dup + [node], source_reader)
         | 
| 69 85 | 
             
                    end
         | 
| 70 86 | 
             
                  else
         | 
| 71 | 
            -
                    if match?(node) && CqlRuby::FilterEvaluator.pass?(filter_reader,  | 
| 87 | 
            +
                    if search_for_token? && match?(node) && CqlRuby::FilterEvaluator.pass?(filter_reader, ancestors, node)
         | 
| 72 88 | 
             
                      collector.add(CqlRuby::Crumb.new(node, ancestors, source_reader))
         | 
| 73 89 | 
             
                    end
         | 
| 74 90 | 
             
                  end
         | 
| @@ -90,6 +106,14 @@ module CqlRuby | |
| 90 106 | 
             
                  Dir.glob(clean_path)
         | 
| 91 107 | 
             
                end
         | 
| 92 108 |  | 
| 109 | 
            +
                def search_for_token?
         | 
| 110 | 
            +
                  @search_type == :token
         | 
| 111 | 
            +
                end
         | 
| 112 | 
            +
             | 
| 113 | 
            +
                def search_for_node?
         | 
| 114 | 
            +
                  @search_type == :node
         | 
| 115 | 
            +
                end
         | 
| 116 | 
            +
             | 
| 93 117 | 
             
                attr_reader :collector
         | 
| 94 118 | 
             
                attr_reader :filter_reader
         | 
| 95 119 | 
             
                attr_reader :pattern
         | 
| @@ -99,33 +123,55 @@ module CqlRuby | |
| 99 123 | 
             
              end
         | 
| 100 124 | 
             
            end
         | 
| 101 125 |  | 
| 102 | 
            -
            CqlRuby | 
| 103 | 
            -
               | 
| 104 | 
            -
                ancestors | 
| 105 | 
            -
             | 
| 126 | 
            +
            module CqlRuby
         | 
| 127 | 
            +
              class Crumb
         | 
| 128 | 
            +
                def initialize(node, ancestors, source_reader)
         | 
| 129 | 
            +
                  @node = node
         | 
| 130 | 
            +
                  @ancestors = ancestors
         | 
| 131 | 
            +
                  @source_reader = source_reader
         | 
| 132 | 
            +
                end
         | 
| 106 133 |  | 
| 107 | 
            -
             | 
| 108 | 
            -
             | 
| 109 | 
            -
             | 
| 134 | 
            +
                def line_no
         | 
| 135 | 
            +
                  anchor.location.expression.line
         | 
| 136 | 
            +
                end
         | 
| 110 137 |  | 
| 111 | 
            -
             | 
| 112 | 
            -
             | 
| 113 | 
            -
             | 
| 138 | 
            +
                def line_col_no
         | 
| 139 | 
            +
                  anchor.location.expression.column
         | 
| 140 | 
            +
                end
         | 
| 114 141 |  | 
| 115 | 
            -
             | 
| 116 | 
            -
             | 
| 117 | 
            -
             | 
| 142 | 
            +
                def source
         | 
| 143 | 
            +
                  source_reader.source_line(line_no)
         | 
| 144 | 
            +
                end
         | 
| 118 145 |  | 
| 119 | 
            -
             | 
| 120 | 
            -
             | 
| 121 | 
            -
             | 
| 146 | 
            +
                def surrounding_line(offset)
         | 
| 147 | 
            +
                  source_reader.source_line(line_no + offset)
         | 
| 148 | 
            +
                end
         | 
| 122 149 |  | 
| 123 | 
            -
             | 
| 124 | 
            -
             | 
| 125 | 
            -
             | 
| 150 | 
            +
                def file_name
         | 
| 151 | 
            +
                  source_reader.file
         | 
| 152 | 
            +
                end
         | 
| 153 | 
            +
             | 
| 154 | 
            +
                def expression_size
         | 
| 155 | 
            +
                  anchor.location.expression.size
         | 
| 156 | 
            +
                end
         | 
| 157 | 
            +
             | 
| 158 | 
            +
                def type
         | 
| 159 | 
            +
                  anchor.type
         | 
| 160 | 
            +
                end
         | 
| 161 | 
            +
             | 
| 162 | 
            +
                private
         | 
| 163 | 
            +
             | 
| 164 | 
            +
                def anchor
         | 
| 165 | 
            +
                  if node.is_a?(Symbol)
         | 
| 166 | 
            +
                    ancestors.last
         | 
| 167 | 
            +
                  else
         | 
| 168 | 
            +
                    node
         | 
| 169 | 
            +
                  end
         | 
| 170 | 
            +
                end
         | 
| 126 171 |  | 
| 127 | 
            -
             | 
| 128 | 
            -
                ancestors | 
| 172 | 
            +
                attr_reader :node
         | 
| 173 | 
            +
                attr_reader :ancestors
         | 
| 174 | 
            +
                attr_reader :source_reader
         | 
| 129 175 | 
             
              end
         | 
| 130 176 | 
             
            end
         | 
| 131 177 |  | 
| @@ -3,17 +3,18 @@ | |
| 3 3 | 
             
            module CqlRuby
         | 
| 4 4 | 
             
              class FilterEvaluator
         | 
| 5 5 | 
             
                class << self
         | 
| 6 | 
            -
                  def pass?(filter_reader,  | 
| 6 | 
            +
                  def pass?(filter_reader, ancestors, node)
         | 
| 7 7 | 
             
                    [
         | 
| 8 8 | 
             
                      pass_type?(filter_reader, ancestors),
         | 
| 9 9 | 
             
                      pass_nesting?(filter_reader, ancestors),
         | 
| 10 | 
            +
                      pass_has?(filter_reader, ancestors, node),
         | 
| 10 11 | 
             
                    ].all?
         | 
| 11 12 | 
             
                  end
         | 
| 12 13 |  | 
| 13 14 | 
             
                  private
         | 
| 14 15 |  | 
| 15 16 | 
             
                  #
         | 
| 16 | 
            -
                  # @param [ | 
| 17 | 
            +
                  # @param [CqlRuby::FilterReader] filter_reader
         | 
| 17 18 | 
             
                  # @param [Array<Parser::AST::Node>] ancestors
         | 
| 18 19 | 
             
                  #
         | 
| 19 20 | 
             
                  # @return [Boolean]
         | 
| @@ -25,7 +26,7 @@ module CqlRuby | |
| 25 26 | 
             
                  end
         | 
| 26 27 |  | 
| 27 28 | 
             
                  #
         | 
| 28 | 
            -
                  # @param [ | 
| 29 | 
            +
                  # @param [CqlRuby::FilterReader] filter_reader
         | 
| 29 30 | 
             
                  # @param [Array<Parser::AST::Node>] ancestors
         | 
| 30 31 | 
             
                  #
         | 
| 31 32 | 
             
                  # @return [Boolean]
         | 
| @@ -38,7 +39,6 @@ module CqlRuby | |
| 38 39 | 
             
                        next false unless ancestor.type.to_s == nest_rule.type
         | 
| 39 40 | 
             
                        next true unless nest_rule.restrict_name?
         | 
| 40 41 |  | 
| 41 | 
            -
                        # TODO Make a proper matcher class.
         | 
| 42 42 | 
             
                        if %w[class module].include?(nest_rule.type)
         | 
| 43 43 | 
             
                          CqlRuby::PatternMatcher.match?(nest_rule.name, ancestor.children[0].children[1])
         | 
| 44 44 | 
             
                        elsif %[def].include?(nest_rule.type)
         | 
| @@ -49,6 +49,84 @@ module CqlRuby | |
| 49 49 | 
             
                      end
         | 
| 50 50 | 
             
                    end
         | 
| 51 51 | 
             
                  end
         | 
| 52 | 
            +
             | 
| 53 | 
            +
                  #
         | 
| 54 | 
            +
                  # @param [CqlRuby::FilterReader] filter_reader
         | 
| 55 | 
            +
                  # @param [Array<Parser::AST::Node>] ancestors
         | 
| 56 | 
            +
                  # @param [Any<Parser::AST::Node, Symbol>] node
         | 
| 57 | 
            +
                  #
         | 
| 58 | 
            +
                  def pass_has?(filter_reader, ancestors, node)
         | 
| 59 | 
            +
                    return true unless filter_reader.restrict_children?
         | 
| 60 | 
            +
             | 
| 61 | 
            +
                    filter_reader.has_leaves.all? do |has_rule|
         | 
| 62 | 
            +
                      anchor_node = if node.is_a?(Symbol)
         | 
| 63 | 
            +
                        # TODO: Expand this to other wrappers (loops, conditions, etc).
         | 
| 64 | 
            +
                        try_get_class(ancestors) || try_get_module(ancestors) || try_get_def(ancestors)
         | 
| 65 | 
            +
                      else
         | 
| 66 | 
            +
                        node
         | 
| 67 | 
            +
                      end
         | 
| 68 | 
            +
                      next false unless anchor_node
         | 
| 69 | 
            +
             | 
| 70 | 
            +
                      has_node_with_name?(anchor_node, has_rule)
         | 
| 71 | 
            +
                    end
         | 
| 72 | 
            +
                  end
         | 
| 73 | 
            +
             | 
| 74 | 
            +
                  #
         | 
| 75 | 
            +
                  # @param [Array<Parser::AST::Node>] ancestors
         | 
| 76 | 
            +
                  #
         | 
| 77 | 
            +
                  def try_get_class(ancestors)
         | 
| 78 | 
            +
                    return nil unless ancestors.size >= 2
         | 
| 79 | 
            +
                    return nil unless ancestors[-1].type == :const
         | 
| 80 | 
            +
                    return nil unless ancestors[-2].type == :class
         | 
| 81 | 
            +
             | 
| 82 | 
            +
                    ancestors[-2].children[2]
         | 
| 83 | 
            +
                  end
         | 
| 84 | 
            +
             | 
| 85 | 
            +
                  #
         | 
| 86 | 
            +
                  # @param [Array<Parser::AST::Node>] ancestors
         | 
| 87 | 
            +
                  #
         | 
| 88 | 
            +
                  def try_get_module(ancestors)
         | 
| 89 | 
            +
                    return nil unless ancestors.size >= 2
         | 
| 90 | 
            +
                    return nil unless ancestors[-1].type == :const
         | 
| 91 | 
            +
                    return nil unless ancestors[-2].type == :module
         | 
| 92 | 
            +
             | 
| 93 | 
            +
                    ancestors[-2].children[1]
         | 
| 94 | 
            +
                  end
         | 
| 95 | 
            +
             | 
| 96 | 
            +
                  #
         | 
| 97 | 
            +
                  # @param [Array<Parser::AST::Node>] ancestors
         | 
| 98 | 
            +
                  #
         | 
| 99 | 
            +
                  def try_get_def(ancestors)
         | 
| 100 | 
            +
                    return nil unless ancestors.size >= 1
         | 
| 101 | 
            +
                    return nil unless ancestors[-1].type == :def
         | 
| 102 | 
            +
             | 
| 103 | 
            +
                    ancestors[-1].children[2]
         | 
| 104 | 
            +
                  end
         | 
| 105 | 
            +
             | 
| 106 | 
            +
                  #
         | 
| 107 | 
            +
                  # @param [Parser::AST::Node] anchor_node
         | 
| 108 | 
            +
                  # @param [CqlRuby::NodeSpec]
         | 
| 109 | 
            +
                  #
         | 
| 110 | 
            +
                  def has_node_with_name?(anchor_node, has_rule)
         | 
| 111 | 
            +
                    return false unless anchor_node.is_a?(Parser::AST::Node)
         | 
| 112 | 
            +
             | 
| 113 | 
            +
                    fn_children_with_type = ->(node) { node.children.map { |child| [child, node.type] } }
         | 
| 114 | 
            +
                    to_visit = fn_children_with_type.call(anchor_node)
         | 
| 115 | 
            +
             | 
| 116 | 
            +
                    until to_visit.empty?
         | 
| 117 | 
            +
                      current_node, current_type = to_visit.shift
         | 
| 118 | 
            +
             | 
| 119 | 
            +
                      if current_node.is_a?(Parser::AST::Node)
         | 
| 120 | 
            +
                        to_visit += fn_children_with_type.call(current_node)
         | 
| 121 | 
            +
                      else
         | 
| 122 | 
            +
                        if current_type == has_rule.type.to_sym && CqlRuby::PatternMatcher.match?(has_rule.name, current_node)
         | 
| 123 | 
            +
                          return true
         | 
| 124 | 
            +
                        end
         | 
| 125 | 
            +
                      end
         | 
| 126 | 
            +
                    end
         | 
| 127 | 
            +
             | 
| 128 | 
            +
                    false
         | 
| 129 | 
            +
                  end
         | 
| 52 130 | 
             
                end
         | 
| 53 131 | 
             
              end
         | 
| 54 132 | 
             
            end
         | 
| @@ -1,9 +1,11 @@ | |
| 1 1 | 
             
            # frozen_string_literal: true
         | 
| 2 2 |  | 
| 3 | 
            +
            # TODO: Have convenience filters for type:_ such as: isclass, ismodule, isdef ...
         | 
| 4 | 
            +
             | 
| 3 5 | 
             
            module CqlRuby
         | 
| 4 | 
            -
              class  | 
| 6 | 
            +
              class NodeSpec < Struct.new(:type, :name)
         | 
| 7 | 
            +
                # Make this non duplicated.
         | 
| 5 8 | 
             
                NAME_ANY = '*'
         | 
| 6 | 
            -
                ALLOWED_TYPE = %w[class module def block].freeze
         | 
| 7 9 |  | 
| 8 10 | 
             
                class << self
         | 
| 9 11 | 
             
                  #
         | 
| @@ -15,8 +17,7 @@ module CqlRuby | |
| 15 17 | 
             
                    type, name = raw_value.split('=')
         | 
| 16 18 | 
             
                    name ||= NAME_ANY
         | 
| 17 19 |  | 
| 18 | 
            -
                    raise " | 
| 19 | 
            -
                    raise "Type #{type} cannot have a name." if %w[block].include?(type) && name != NAME_ANY
         | 
| 20 | 
            +
                    raise "Type '#{type}' is not recognized. See 'cql_ruby --help' for allowed types." unless Parser::Meta::NODE_TYPES.member?(type.to_sym)
         | 
| 20 21 |  | 
| 21 22 | 
             
                    new(type, name)
         | 
| 22 23 | 
             
                  end
         | 
| @@ -38,16 +39,21 @@ module CqlRuby | |
| 38 39 | 
             
              # example: type:def,send
         | 
| 39 40 | 
             
              #
         | 
| 40 41 | 
             
              class FilterReader
         | 
| 41 | 
            -
                 | 
| 42 | 
            +
                NESTING_ALLOWED_TYPES = %w[class module def block].freeze
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                # @attribute [Array<Symbol>] allowed_types
         | 
| 42 45 | 
             
                attr_reader :allowed_types
         | 
| 43 | 
            -
                # @attribute [Array< | 
| 46 | 
            +
                # @attribute [Array<CqlRuby::NodeSpec>] nest_under
         | 
| 44 47 | 
             
                attr_reader :nest_under
         | 
| 48 | 
            +
                # @attribute [Array<CqlRuby::NodeSpec>] has_leaves
         | 
| 49 | 
            +
                attr_reader :has_leaves
         | 
| 45 50 |  | 
| 46 51 | 
             
                def initialize(raw_filters)
         | 
| 47 52 | 
             
                  super()
         | 
| 48 53 |  | 
| 49 54 | 
             
                  @allowed_types = []
         | 
| 50 55 | 
             
                  @nest_under = []
         | 
| 56 | 
            +
                  @has_leaves = []
         | 
| 51 57 |  | 
| 52 58 | 
             
                  parse_raw_filters(raw_filters)
         | 
| 53 59 | 
             
                end
         | 
| @@ -60,6 +66,10 @@ module CqlRuby | |
| 60 66 | 
             
                  !@nest_under.empty?
         | 
| 61 67 | 
             
                end
         | 
| 62 68 |  | 
| 69 | 
            +
                def restrict_children?
         | 
| 70 | 
            +
                  !@has_leaves.empty?
         | 
| 71 | 
            +
                end
         | 
| 72 | 
            +
             | 
| 63 73 | 
             
                private
         | 
| 64 74 |  | 
| 65 75 | 
             
                # @param [Array<String>] raw_filters
         | 
| @@ -71,7 +81,13 @@ module CqlRuby | |
| 71 81 | 
             
                    if %w[type t].include?(name)
         | 
| 72 82 | 
             
                      @allowed_types += value.split(',').map(&:to_sym)
         | 
| 73 83 | 
             
                    elsif %w[nest n].include?(name)
         | 
| 74 | 
            -
                       | 
| 84 | 
            +
                      spec = NodeSpec.from(value)
         | 
| 85 | 
            +
                      raise "Unknown type for nesting: '#{spec.type}' from '#{raw_filter}'. Allowed: #{NESTING_ALLOWED_TYPES}" unless NESTING_ALLOWED_TYPES.include?(spec.type)
         | 
| 86 | 
            +
                      raise "Type #{spec.type} cannot have a name." if %w[block].include?(spec.type) && spec.restrict_name?
         | 
| 87 | 
            +
             | 
| 88 | 
            +
                      @nest_under << spec
         | 
| 89 | 
            +
                    elsif %w[has h].include?(name)
         | 
| 90 | 
            +
                      @has_leaves << NodeSpec.from(value)
         | 
| 75 91 | 
             
                    end
         | 
| 76 92 | 
             
                  end
         | 
| 77 93 |  | 
| @@ -1,9 +1,13 @@ | |
| 1 1 | 
             
            # frozen_string_literal: true
         | 
| 2 2 | 
             
            module CqlRuby
         | 
| 3 3 | 
             
              module PatternMatcher
         | 
| 4 | 
            +
                MATCH_ANY = '*'
         | 
| 5 | 
            +
             | 
| 4 6 | 
             
                def self.match?(pattern, subject)
         | 
| 5 | 
            -
                  subject = subject.to_s
         | 
| 6 7 | 
             
                  pattern = pattern.to_s
         | 
| 8 | 
            +
                  return true if pattern == MATCH_ANY
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                  subject = subject.to_s
         | 
| 7 11 |  | 
| 8 12 | 
             
                  if regex?(pattern)
         | 
| 9 13 | 
             
                    regex_match?(pattern, subject)
         |