cql_ruby 0.0.9 → 0.0.14

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: 4e459067630e91a0d52a1839445f881647e9c0271c052d973ccfafd6040f21b6
4
- data.tar.gz: 39379431fb2d817930aff070a8335c02b62822d6bba0129956f6691cb5ea99b8
3
+ metadata.gz: a2d4b179a2563722c3dc2ef101ec10f817b1f79b3ce7d81a9ca6550fd088f39d
4
+ data.tar.gz: 3cace84921a72b38150aa52d60d1a48a48775b20234a3f4094e58939cf1c6058
5
5
  SHA512:
6
- metadata.gz: 1de5455822e96348fad5fbda540e66ebf830723a4579927f8d4b1a2a9a4c9dd1c6d4763a514674eebff6974c8d61958e53af9fae74397fd643bb8a036f0a725d
7
- data.tar.gz: 94c05fd4d565b1db7e793137495bd97456b1e459f9a58c6faba8c008c772a4898929ce5dc02d4064dec2ef0ee2ef0a7a2b68a2f9915430c681aca2a1a2bbb6f9
6
+ metadata.gz: e4c5f5f1e37f8520a7e366d6dc1bb07792ba8062230cd41a068cb846516e76497639131d0f93a0b60c360586bc886743d0a02aac4d3c3660281e1c8a18021f26
7
+ data.tar.gz: 65b4991554e454c0ca76f1077ae19665b3b4b27fc48243d301fb873e36af0da6652dcb6d741a249f529f72263866c4a5d2104b9fd5c958c0ed6c307218f829eb
@@ -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,6 +17,7 @@ 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
21
23
  \t\t--include=PATTERN Parses only files whose name matches the pattern.
@@ -49,6 +51,7 @@ def extract_options
49
51
  surrounding_lines: 0,
50
52
  include_pattern: nil,
51
53
  exclude_pattern: nil,
54
+ search_type: :token,
52
55
  }
53
56
 
54
57
  ARGV.delete_if do |arg|
@@ -72,6 +75,10 @@ def extract_options
72
75
  options[:include_pattern] = arg.split('=')[1]
73
76
  elsif arg.start_with?('--exclude=')
74
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
75
82
  else
76
83
  raise "Unknown arg #{arg}"
77
84
  end
@@ -124,8 +131,9 @@ begin
124
131
  recursive: options[:recursive_search],
125
132
  include: options[:include_pattern],
126
133
  exclude: options[:exclude_pattern],
134
+ search_type: options[:search_type],
127
135
  ).search_all
128
- rescue => e
129
- puts "Error: #{e}"
136
+ rescue
137
+ puts "Error: #{$!}"
130
138
  show_help
131
139
  end
@@ -6,6 +6,7 @@ module CqlRuby;
6
6
  end
7
7
  end
8
8
 
9
+ require 'cql_ruby/defs'
9
10
  require 'cql_ruby/executor'
10
11
  require 'cql_ruby/crumb_collector'
11
12
  require 'cql_ruby/abstract_printer'
@@ -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>]
@@ -35,7 +35,8 @@ module CqlRuby
35
35
  filters: [],
36
36
  recursive: true,
37
37
  include: nil,
38
- exclude: nil
38
+ exclude: nil,
39
+ search_type: :token
39
40
  )
40
41
  @collector = collector
41
42
  @filter_reader = filter_reader
@@ -45,6 +46,7 @@ module CqlRuby
45
46
  @recursive = recursive
46
47
  @include = include
47
48
  @exclude = exclude
49
+ @search_type = search_type
48
50
  end
49
51
 
50
52
  def search_all
@@ -65,18 +67,24 @@ module CqlRuby
65
67
  walk(ast, [], source_reader)
66
68
 
67
69
  nil
68
- rescue => e
70
+ rescue
69
71
  CqlRuby.log "File #{file} cannot be parsed"
70
- CqlRuby.log "Reason: #{e}" if CqlRuby::Config.debug_level_1?
72
+ CqlRuby.log "Reason: #{$!}" if CqlRuby::Config.debug_level_1?
71
73
  end
72
74
 
73
75
  def walk(node, ancestors, source_reader)
74
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
+
75
83
  node.children.flat_map do |child|
76
84
  walk(child, ancestors.dup + [node], source_reader)
77
85
  end
78
86
  else
79
- if match?(node) && CqlRuby::FilterEvaluator.pass?(filter_reader, node, ancestors)
87
+ if search_for_token? && match?(node) && CqlRuby::FilterEvaluator.pass?(filter_reader, ancestors, node)
80
88
  collector.add(CqlRuby::Crumb.new(node, ancestors, source_reader))
81
89
  end
82
90
  end
@@ -98,6 +106,14 @@ module CqlRuby
98
106
  Dir.glob(clean_path)
99
107
  end
100
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
+
101
117
  attr_reader :collector
102
118
  attr_reader :filter_reader
103
119
  attr_reader :pattern
@@ -107,33 +123,55 @@ module CqlRuby
107
123
  end
108
124
  end
109
125
 
110
- CqlRuby::Crumb = Struct.new(:full_name, :ancestors, :source_reader) do
111
- def line_no
112
- ancestors.last.location.expression.line
113
- 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
114
133
 
115
- def line_col_no
116
- ancestors.last.location.expression.column
117
- end
134
+ def line_no
135
+ anchor.location.expression.line
136
+ end
118
137
 
119
- def source
120
- source_reader.source_line(line_no)
121
- end
138
+ def line_col_no
139
+ anchor.location.expression.column
140
+ end
122
141
 
123
- def surrounding_line(offset)
124
- source_reader.source_line(line_no + offset)
125
- end
142
+ def source
143
+ source_reader.source_line(line_no)
144
+ end
126
145
 
127
- def file_name
128
- source_reader.file
129
- end
146
+ def surrounding_line(offset)
147
+ source_reader.source_line(line_no + offset)
148
+ end
130
149
 
131
- def expression_size
132
- ancestors.last.location.expression.size
133
- 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
134
171
 
135
- def type
136
- ancestors.last.type
172
+ attr_reader :node
173
+ attr_reader :ancestors
174
+ attr_reader :source_reader
137
175
  end
138
176
  end
139
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.9
4
+ version: 0.0.14
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