cql_ruby 0.0.8 → 0.0.13

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 17840020ae4f57f60763716ac438b33a68abb4b1add767d5f8e88cfb946439c8
4
- data.tar.gz: 789aa975ced7c62036da026f7e4bd377c09b1502692ff217fb697641d5478d64
3
+ metadata.gz: 91df52709b471c3a213b21380440919736837adc6519b5f64c3b2a2c0d0be8a8
4
+ data.tar.gz: cadae98053971c525d0ae267acd3026bc9d4d0f67c72a278f353514cf189e5eb
5
5
  SHA512:
6
- metadata.gz: ee8c4e5ea2cfde9a8837cc1e19734e0051424af9c15fe02fc5db49b11984052b70a4acb6d196b94c38820ea34bccbb8d78ff431e5bef45ac0627a55548fae324
7
- data.tar.gz: 9e550d59e360717069c3198269bbe815d73aae7cc1d43b9532ed2fc4dee60b8f9decee399051ceedb9b0ae21867e785cce0423170f1f417550b3611c018d4270
6
+ metadata.gz: 68d6f41c76b633d12403370fbb1e47ca63c0a295c0b0b39488567a4e7d3abc4af3d5f39276f271663574ccd8516226501ebc29d8815736b67f96bfee65df732a
7
+ data.tar.gz: 4ea70337068084bb7a17e86bc2ad4769cfd509d9580c32176d88bf6943a0d6d51eabeb115712760bb984966a0bfdaa52fde4bca2ab5ef64a11aa3b9bdc976827
@@ -7,7 +7,8 @@ def show_help
7
7
  puts <<~HELP
8
8
 
9
9
  \tSYNOPSIS
10
- \t\tcql_ruby options pattern path filters ...
10
+ \t\tcql_ruby [--token] pattern path options filters ...
11
+ \t\tcql_ruby --node type path options filters ...
11
12
 
12
13
  \tDESCRIPTION
13
14
  \t\tCQL (Code Query Language) is a semantic search tool for your Ruby source code.
@@ -16,8 +17,11 @@ def show_help
16
17
  \t\tParent node type: type:T(,T)* Example: type:def,send,arg
17
18
  \t\tNesting under: nest:T(=NAME) Example: nest:def=save_user nest:class=UserManager
18
19
  \t\tHas child: has:T(=NAME) Example: has:const has:def=valid?
20
+ \t\tPattern: pattern:(T-)*X(-T)* Example: pattern:class-def-X-block
19
21
 
20
22
  \tOPTIONS
23
+ \t\t--include=PATTERN Parses only files whose name matches the pattern.
24
+ \t\t--exclude=PATTERN Parses only files whose name does not match the pattern.
21
25
  \t\t-lN (N is integer) Add N surrounding line before and after.
22
26
  \t\t-nc (--no-color) No color on output.
23
27
  \t\t-nf (--no-file) No file names.
@@ -45,6 +49,9 @@ def extract_options
45
49
  show_source: true,
46
50
  recursive_search: true,
47
51
  surrounding_lines: 0,
52
+ include_pattern: nil,
53
+ exclude_pattern: nil,
54
+ search_type: :token,
48
55
  }
49
56
 
50
57
  ARGV.delete_if do |arg|
@@ -64,6 +71,14 @@ def extract_options
64
71
  options[:recursive_search] = false
65
72
  elsif arg[0..1] == '-l' && arg[2..].to_i > 0
66
73
  options[:surrounding_lines] = arg[2..].to_i
74
+ elsif arg.start_with?('--include=')
75
+ options[:include_pattern] = arg.split('=')[1]
76
+ elsif arg.start_with?('--exclude=')
77
+ options[:exclude_pattern] = arg.split('=')[1]
78
+ elsif arg == '--node'
79
+ options[:search_type] = :node
80
+ elsif arg == '--token'
81
+ options[:search_type] = :token
67
82
  else
68
83
  raise "Unknown arg #{arg}"
69
84
  end
@@ -91,7 +106,6 @@ begin
91
106
  pattern = ARGV.shift
92
107
  CqlRuby.log "Call pattern: <#{pattern}>" if CqlRuby::Config.debug_level_2?
93
108
 
94
- # TODO Make path patterns universal.
95
109
  path = ARGV.shift
96
110
  CqlRuby.log "Call path: <#{path}>" if CqlRuby::Config.debug_level_2?
97
111
 
@@ -115,8 +129,11 @@ begin
115
129
  path: path,
116
130
  filters: filters,
117
131
  recursive: options[:recursive_search],
132
+ include: options[:include_pattern],
133
+ exclude: options[:exclude_pattern],
134
+ search_type: options[:search_type],
118
135
  ).search_all
119
- rescue => e
120
- puts "Error: #{e}"
136
+ rescue
137
+ puts "Error: #{$!}"
121
138
  show_help
122
139
  end
@@ -6,10 +6,4 @@ module CqlRuby;
6
6
  end
7
7
  end
8
8
 
9
- require 'cql_ruby/executor'
10
- require 'cql_ruby/crumb_collector'
11
- require 'cql_ruby/abstract_printer'
12
- require 'cql_ruby/console_printer'
13
- require 'cql_ruby/filter_reader'
14
- require 'cql_ruby/filter_evaluator'
15
- require 'cql_ruby/pattern_matcher'
9
+ Dir.glob(File.dirname(__FILE__) + '/cql_ruby/*.rb').each { |source| require source }
@@ -2,7 +2,7 @@
2
2
 
3
3
  module CqlRuby
4
4
  #
5
- # Printing Cqlruby::Crumb-s.
5
+ # Printing CqlRuby::Crumb-s.
6
6
  #
7
7
  class AbstractPrinter
8
8
  def print(_crumb)
@@ -21,7 +21,7 @@ module CqlRuby
21
21
  end
22
22
 
23
23
  #
24
- # @param crumb [Cqlruby::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 [Cqlruby::Crumb] crumb
74
+ # @param [CqlRuby::Crumb] crumb
75
75
  # @return [String]
76
76
  def decorate_source_line(crumb)
77
77
  source = crumb.source
@@ -3,7 +3,7 @@
3
3
  module CqlRuby
4
4
  class CrumbCollector
5
5
  #
6
- # @param printer [Cqlruby::AbstractPrinter]
6
+ # @param printer [CqlRuby::AbstractPrinter]
7
7
  #
8
8
  def initialize(printer)
9
9
  super()
@@ -0,0 +1,3 @@
1
+ module CqlRuby
2
+ MATCH_ANYTHING = '*'
3
+ end
@@ -20,7 +20,7 @@ end
20
20
  #
21
21
  # Executes search and dumps results into the collector.
22
22
  #
23
- # @param collector [Cqlruby::CrumbCollector]
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 => e
62
- CqlRuby.log "File #{file} cannot be parsed: #{e}"
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, node, ancestors)
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::Crumb = Struct.new(:full_name, :ancestors, :source_reader) do
103
- def line_no
104
- ancestors.last.location.expression.line
105
- end
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
- def line_col_no
108
- ancestors.last.location.expression.column
109
- end
134
+ def line_no
135
+ anchor.location.expression.line
136
+ end
110
137
 
111
- def source
112
- source_reader.source_line(line_no)
113
- end
138
+ def line_col_no
139
+ anchor.location.expression.column
140
+ end
114
141
 
115
- def surrounding_line(offset)
116
- source_reader.source_line(line_no + offset)
117
- end
142
+ def source
143
+ source_reader.source_line(line_no)
144
+ end
118
145
 
119
- def file_name
120
- source_reader.file
121
- end
146
+ def surrounding_line(offset)
147
+ source_reader.source_line(line_no + offset)
148
+ end
122
149
 
123
- def expression_size
124
- ancestors.last.location.expression.size
125
- end
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?(Parser::AST::Node)
166
+ node
167
+ else
168
+ ancestors.last
169
+ end
170
+ end
126
171
 
127
- def type
128
- ancestors.last.type
172
+ attr_reader :node
173
+ attr_reader :ancestors
174
+ attr_reader :source_reader
129
175
  end
130
176
  end
131
177
 
@@ -3,18 +3,19 @@
3
3
  module CqlRuby
4
4
  class FilterEvaluator
5
5
  class << self
6
- def pass?(filter_reader, node, ancestors)
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, node, ancestors),
10
+ pass_has?(filter_reader, ancestors, node),
11
+ pass_pattern?(filter_reader, ancestors, node),
11
12
  ].all?
12
13
  end
13
14
 
14
15
  private
15
16
 
16
17
  #
17
- # @param [Cqlruby::FilterReader] filter_reader
18
+ # @param [CqlRuby::FilterReader] filter_reader
18
19
  # @param [Array<Parser::AST::Node>] ancestors
19
20
  #
20
21
  # @return [Boolean]
@@ -39,7 +40,6 @@ module CqlRuby
39
40
  next false unless ancestor.type.to_s == nest_rule.type
40
41
  next true unless nest_rule.restrict_name?
41
42
 
42
- # TODO Make a proper matcher class.
43
43
  if %w[class module].include?(nest_rule.type)
44
44
  CqlRuby::PatternMatcher.match?(nest_rule.name, ancestor.children[0].children[1])
45
45
  elsif %[def].include?(nest_rule.type)
@@ -53,20 +53,69 @@ module CqlRuby
53
53
 
54
54
  #
55
55
  # @param [CqlRuby::FilterReader] filter_reader
56
- # @param [Parser::AST::Node] node
57
56
  # @param [Array<Parser::AST::Node>] ancestors
57
+ # @param [Any<Parser::AST::Node, Symbol>] node
58
58
  #
59
- def pass_has?(filter_reader, node, ancestors)
59
+ def pass_has?(filter_reader, ancestors, node)
60
60
  return true unless filter_reader.restrict_children?
61
61
 
62
62
  filter_reader.has_leaves.all? do |has_rule|
63
- anchor_node = try_get_class(ancestors) || try_get_module(ancestors) || try_get_def(ancestors)
63
+ anchor_node = if node.is_a?(Symbol)
64
+ try_get_class(ancestors) || try_get_module(ancestors) || try_get_def(ancestors)
65
+ else
66
+ node
67
+ end
64
68
  next false unless anchor_node
65
69
 
66
70
  has_node_with_name?(anchor_node, has_rule)
67
71
  end
68
72
  end
69
73
 
74
+ #
75
+ # @param [CqlRuby::FilterReader] filter_reader
76
+ # @param [Array<Parser::AST::Node>] ancestors
77
+ # @param [Any<Parser::AST::Node, Symbol>] node
78
+ #
79
+ def pass_pattern?(filter_reader, ancestors, node)
80
+ return true unless filter_reader.restrict_pattern?
81
+
82
+ filter_reader.patterns.all? do |pattern|
83
+ pattern_ancestors = pattern.ancestors.dup
84
+ ancestor_idx = ancestors.size - 1
85
+
86
+ while !pattern_ancestors.empty? && ancestor_idx >= 0
87
+ if CqlRuby::PatternMatcher.match?(pattern_ancestors.last, ancestors[ancestor_idx].type)
88
+ pattern_ancestors.pop
89
+ end
90
+
91
+ ancestor_idx -= 1
92
+ end
93
+ return false unless pattern_ancestors.empty?
94
+
95
+ pattern_descendants = pattern.descendants.dup
96
+ match_descendant_pattern?(pattern_descendants, node)
97
+ end
98
+ end
99
+
100
+ #
101
+ # @param [Array<String>] pattern_descendants
102
+ # @param [Parser::AST::Node] node
103
+ #
104
+ def match_descendant_pattern?(pattern_descendants, node)
105
+ return true if pattern_descendants.empty?
106
+ # If we're at the end and we're still expecting a type - no match.
107
+ return false unless node.is_a?(Parser::AST::Node)
108
+
109
+ node.children.any? do |child|
110
+ next false unless child.is_a?(Parser::AST::Node)
111
+ if CqlRuby::PatternMatcher.match?(pattern_descendants.first, child.type)
112
+ match_descendant_pattern?(pattern_descendants[1..], child)
113
+ else
114
+ match_descendant_pattern?(pattern_descendants.dup, child)
115
+ end
116
+ end
117
+ end
118
+
70
119
  #
71
120
  # @param [Array<Parser::AST::Node>] ancestors
72
121
  #
@@ -101,7 +150,7 @@ module CqlRuby
101
150
 
102
151
  #
103
152
  # @param [Parser::AST::Node] anchor_node
104
- # @param [CqlRuby::NodeSpec]
153
+ # @param [CqlRuby::FilterReader::NodeSpec]
105
154
  #
106
155
  def has_node_with_name?(anchor_node, has_rule)
107
156
  return false unless anchor_node.is_a?(Parser::AST::Node)
@@ -1,33 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # TODO: Have convenience filters for type:_ such as: isclass, ismodule, isdef ...
4
-
5
3
  module CqlRuby
6
- class NodeSpec < Struct.new(:type, :name)
7
- # Make this non duplicated.
8
- NAME_ANY = '*'
9
-
10
- class << self
11
- #
12
- # @param [String] raw_value
13
- # Format: TYPE(=NAME|=*)
14
- # Accepted types: class, module, def, block
15
- #
16
- def from(raw_value)
17
- type, name = raw_value.split('=')
18
- name ||= NAME_ANY
19
-
20
- raise "Type '#{type}' is not recognized. See 'cql_ruby --help' for allowed types." unless Parser::Meta::NODE_TYPES.member?(type.to_sym)
21
-
22
- new(type, name)
23
- end
24
- end
25
-
26
- def restrict_name?
27
- name != NAME_ANY
28
- end
29
- end
30
-
31
4
  #
32
5
  # Reads and provides filters.
33
6
  #
@@ -39,21 +12,69 @@ module CqlRuby
39
12
  # example: type:def,send
40
13
  #
41
14
  class FilterReader
15
+ class HierarchyPattern
16
+ SELF_MARKER = 'X'
17
+
18
+ def self.from(raw_value)
19
+ parts = raw_value.split('-')
20
+ self_marker_idx = parts.index(SELF_MARKER)
21
+ raise "Missing self marker '#{SELF_MARKER}' in hierarchy pattern." if self_marker_idx.nil?
22
+
23
+ ancestors = parts[0...self_marker_idx]
24
+ descendants = parts[self_marker_idx + 1..]
25
+
26
+ new(ancestors, descendants)
27
+ end
28
+
29
+ attr_reader :ancestors
30
+ attr_reader :descendants
31
+
32
+ def initialize(ancestors, descendants)
33
+ @ancestors = ancestors
34
+ @descendants = descendants
35
+ end
36
+ end
37
+
38
+ class NodeSpec < Struct.new(:type, :name)
39
+ class << self
40
+ #
41
+ # @param [String] raw_value
42
+ # Format: TYPE(=NAME|=*)
43
+ # Accepted types: class, module, def, block
44
+ #
45
+ def from(raw_value)
46
+ type, name = raw_value.split('=')
47
+ name ||= CqlRuby::MATCH_ANYTHING
48
+
49
+ raise "Type '#{type}' is not recognized. See 'cql_ruby --help' for allowed types." unless Parser::Meta::NODE_TYPES.member?(type.to_sym)
50
+
51
+ new(type, name)
52
+ end
53
+ end
54
+
55
+ def restrict_name?
56
+ name != CqlRuby::MATCH_ANYTHING
57
+ end
58
+ end
59
+
42
60
  NESTING_ALLOWED_TYPES = %w[class module def block].freeze
43
61
 
44
62
  # @attribute [Array<Symbol>] allowed_types
45
63
  attr_reader :allowed_types
46
- # @attribute [Array<CqlRuby::NodeSpec>] nest_under
64
+ # @attribute [Array<CqlRuby::FilterReader::NodeSpec>] nest_under
47
65
  attr_reader :nest_under
48
- # @attribute [Array<CqlRuby::NodeSpec>] has_leaves
66
+ # @attribute [Array<CqlRuby::FilterReader::NodeSpec>] has_leaves
49
67
  attr_reader :has_leaves
68
+ # @attribute [Array<CqlRuby::FilterReader::HierarchyPattern>] patterns
69
+ attr_reader :patterns
50
70
 
51
- def initialize(raw_filters)
71
+ def initialize(raw_filters = [])
52
72
  super()
53
73
 
54
74
  @allowed_types = []
55
75
  @nest_under = []
56
76
  @has_leaves = []
77
+ @patterns = []
57
78
 
58
79
  parse_raw_filters(raw_filters)
59
80
  end
@@ -70,6 +91,10 @@ module CqlRuby
70
91
  !@has_leaves.empty?
71
92
  end
72
93
 
94
+ def restrict_pattern?
95
+ !@patterns.empty?
96
+ end
97
+
73
98
  private
74
99
 
75
100
  # @param [Array<String>] raw_filters
@@ -88,6 +113,8 @@ module CqlRuby
88
113
  @nest_under << spec
89
114
  elsif %w[has h].include?(name)
90
115
  @has_leaves << NodeSpec.from(value)
116
+ elsif %w[pattern p].include?(name)
117
+ @patterns << HierarchyPattern.from(value)
91
118
  end
92
119
  end
93
120
 
@@ -1,11 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
  module CqlRuby
3
3
  module PatternMatcher
4
- MATCH_ANY = '*'
5
-
6
4
  def self.match?(pattern, subject)
7
5
  pattern = pattern.to_s
8
- return true if pattern == MATCH_ANY
6
+ return true if pattern == CqlRuby::MATCH_ANYTHING
9
7
 
10
8
  subject = subject.to_s
11
9
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cql_ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.8
4
+ version: 0.0.13
5
5
  platform: ruby
6
6
  authors:
7
7
  - itarato
@@ -30,6 +30,20 @@ dependencies:
30
30
  - - ">="
31
31
  - !ruby/object:Gem::Version
32
32
  version: 2.7.1
33
+ - !ruby/object:Gem::Dependency
34
+ name: rspec
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '0'
40
+ type: :development
41
+ prerelease: false
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: '0'
33
47
  description:
34
48
  email: it.arato@gmail.com
35
49
  executables:
@@ -42,6 +56,7 @@ files:
42
56
  - lib/cql_ruby/abstract_printer.rb
43
57
  - lib/cql_ruby/console_printer.rb
44
58
  - lib/cql_ruby/crumb_collector.rb
59
+ - lib/cql_ruby/defs.rb
45
60
  - lib/cql_ruby/executor.rb
46
61
  - lib/cql_ruby/filter_evaluator.rb
47
62
  - lib/cql_ruby/filter_reader.rb