cql_ruby 0.0.9 → 0.0.14

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 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