cql_ruby 0.0.11 → 0.0.12

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.
@@ -1,18 +1,18 @@
1
- # frozen_string_literal: true
2
-
3
- module CqlRuby
4
- class CrumbCollector
5
- #
6
- # @param printer [CqlRuby::AbstractPrinter]
7
- #
8
- def initialize(printer)
9
- super()
10
-
11
- @printer = printer
12
- end
13
-
14
- def add(crumb)
15
- @printer.print(crumb)
16
- end
17
- end
18
- end
1
+ # frozen_string_literal: true
2
+
3
+ module CqlRuby
4
+ class CrumbCollector
5
+ #
6
+ # @param printer [CqlRuby::AbstractPrinter]
7
+ #
8
+ def initialize(printer)
9
+ super()
10
+
11
+ @printer = printer
12
+ end
13
+
14
+ def add(crumb)
15
+ @printer.print(crumb)
16
+ end
17
+ end
18
+ end
@@ -1,194 +1,194 @@
1
- # frozen_string_literal: true
2
-
3
- require 'parser/current'
4
- require 'pathname'
5
-
6
- module CqlRuby
7
- class Config
8
- @@debug_level = 0
9
-
10
- class << self
11
- def debug_level=(lvl); @@debug_level = lvl; end
12
- def debug_level; @@debug_level; end
13
- def debug_level_1?; @@debug_level >= 1; end
14
- def debug_level_2?; @@debug_level >= 2; end
15
- def debug_level_3?; @@debug_level >= 3; end
16
- end
17
- end
18
- end
19
-
20
- #
21
- # Executes search and dumps results into the collector.
22
- #
23
- # @param collector [CqlRuby::CrumbCollector]
24
- # @param pattern [String]
25
- # @param path [String]
26
- # @param filters [Array<String>]
27
- #
28
- module CqlRuby
29
- class Executor
30
- def initialize(
31
- collector:,
32
- filter_reader:,
33
- pattern:,
34
- path:,
35
- filters: [],
36
- recursive: true,
37
- include: nil,
38
- exclude: nil,
39
- search_type: :token
40
- )
41
- @collector = collector
42
- @filter_reader = filter_reader
43
- @pattern = pattern
44
- @path = path
45
- @filters = filters
46
- @recursive = recursive
47
- @include = include
48
- @exclude = exclude
49
- @search_type = search_type
50
- end
51
-
52
- def search_all
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
-
57
- CqlRuby.log "File check: #{file}" if CqlRuby::Config.debug_level_3?
58
- search(file)
59
- end
60
- end
61
-
62
- private
63
-
64
- def search(file)
65
- ast = Parser::CurrentRuby.parse(File.read(file))
66
- source_reader = CqlRuby::SourceReader.new(file)
67
- walk(ast, [], source_reader)
68
-
69
- nil
70
- rescue
71
- CqlRuby.log "File #{file} cannot be parsed"
72
- CqlRuby.log "Reason: #{$!}" if CqlRuby::Config.debug_level_1?
73
- end
74
-
75
- def walk(node, ancestors, source_reader)
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
-
83
- node.children.flat_map do |child|
84
- walk(child, ancestors.dup + [node], source_reader)
85
- end
86
- else
87
- if search_for_token? && match?(node) && CqlRuby::FilterEvaluator.pass?(filter_reader, ancestors, node)
88
- collector.add(CqlRuby::Crumb.new(node, ancestors, source_reader))
89
- end
90
- end
91
-
92
- nil
93
- end
94
-
95
- def match?(target)
96
- CqlRuby::PatternMatcher.match?(pattern, target)
97
- end
98
-
99
- def files
100
- return [path] if File.file?(path)
101
-
102
- clean_path = Pathname(path).cleanpath.to_s
103
- clean_path += '/**' if recursive
104
- clean_path += '/*.rb'
105
-
106
- Dir.glob(clean_path)
107
- end
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
-
117
- attr_reader :collector
118
- attr_reader :filter_reader
119
- attr_reader :pattern
120
- attr_reader :path
121
- attr_reader :filters
122
- attr_reader :recursive
123
- end
124
- end
125
-
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
133
-
134
- def line_no
135
- anchor.location.expression.line
136
- end
137
-
138
- def line_col_no
139
- anchor.location.expression.column
140
- end
141
-
142
- def source
143
- source_reader.source_line(line_no)
144
- end
145
-
146
- def surrounding_line(offset)
147
- source_reader.source_line(line_no + offset)
148
- end
149
-
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
171
-
172
- attr_reader :node
173
- attr_reader :ancestors
174
- attr_reader :source_reader
175
- end
176
- end
177
-
178
- CqlRuby::SourceReader = Struct.new(:file) do
179
- def initialize(*args)
180
- super
181
- end
182
-
183
- def source_line(n)
184
- return nil unless lines.size >= n
185
-
186
- lines[n - 1].chop
187
- end
188
-
189
- private
190
-
191
- def lines
192
- @lines ||= IO.readlines(file)
193
- end
194
- end
1
+ # frozen_string_literal: true
2
+
3
+ require 'parser/current'
4
+ require 'pathname'
5
+
6
+ module CqlRuby
7
+ class Config
8
+ @@debug_level = 0
9
+
10
+ class << self
11
+ def debug_level=(lvl); @@debug_level = lvl; end
12
+ def debug_level; @@debug_level; end
13
+ def debug_level_1?; @@debug_level >= 1; end
14
+ def debug_level_2?; @@debug_level >= 2; end
15
+ def debug_level_3?; @@debug_level >= 3; end
16
+ end
17
+ end
18
+ end
19
+
20
+ #
21
+ # Executes search and dumps results into the collector.
22
+ #
23
+ # @param collector [CqlRuby::CrumbCollector]
24
+ # @param pattern [String]
25
+ # @param path [String]
26
+ # @param filters [Array<String>]
27
+ #
28
+ module CqlRuby
29
+ class Executor
30
+ def initialize(
31
+ collector:,
32
+ filter_reader:,
33
+ pattern:,
34
+ path:,
35
+ filters: [],
36
+ recursive: true,
37
+ include: nil,
38
+ exclude: nil,
39
+ search_type: :token
40
+ )
41
+ @collector = collector
42
+ @filter_reader = filter_reader
43
+ @pattern = pattern
44
+ @path = path
45
+ @filters = filters
46
+ @recursive = recursive
47
+ @include = include
48
+ @exclude = exclude
49
+ @search_type = search_type
50
+ end
51
+
52
+ def search_all
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
+
57
+ CqlRuby.log "File check: #{file}" if CqlRuby::Config.debug_level_3?
58
+ search(file)
59
+ end
60
+ end
61
+
62
+ private
63
+
64
+ def search(file)
65
+ ast = Parser::CurrentRuby.parse(File.read(file))
66
+ source_reader = CqlRuby::SourceReader.new(file)
67
+ walk(ast, [], source_reader)
68
+
69
+ nil
70
+ rescue
71
+ CqlRuby.log "File #{file} cannot be parsed"
72
+ CqlRuby.log "Reason: #{$!}" if CqlRuby::Config.debug_level_1?
73
+ end
74
+
75
+ def walk(node, ancestors, source_reader)
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
+
83
+ node.children.flat_map do |child|
84
+ walk(child, ancestors.dup + [node], source_reader)
85
+ end
86
+ else
87
+ if search_for_token? && match?(node) && CqlRuby::FilterEvaluator.pass?(filter_reader, ancestors, node)
88
+ collector.add(CqlRuby::Crumb.new(node, ancestors, source_reader))
89
+ end
90
+ end
91
+
92
+ nil
93
+ end
94
+
95
+ def match?(target)
96
+ CqlRuby::PatternMatcher.match?(pattern, target)
97
+ end
98
+
99
+ def files
100
+ return [path] if File.file?(path)
101
+
102
+ clean_path = Pathname(path).cleanpath.to_s
103
+ clean_path += '/**' if recursive
104
+ clean_path += '/*.rb'
105
+
106
+ Dir.glob(clean_path)
107
+ end
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
+
117
+ attr_reader :collector
118
+ attr_reader :filter_reader
119
+ attr_reader :pattern
120
+ attr_reader :path
121
+ attr_reader :filters
122
+ attr_reader :recursive
123
+ end
124
+ end
125
+
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
133
+
134
+ def line_no
135
+ anchor.location.expression.line
136
+ end
137
+
138
+ def line_col_no
139
+ anchor.location.expression.column
140
+ end
141
+
142
+ def source
143
+ source_reader.source_line(line_no)
144
+ end
145
+
146
+ def surrounding_line(offset)
147
+ source_reader.source_line(line_no + offset)
148
+ end
149
+
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
171
+
172
+ attr_reader :node
173
+ attr_reader :ancestors
174
+ attr_reader :source_reader
175
+ end
176
+ end
177
+
178
+ CqlRuby::SourceReader = Struct.new(:file) do
179
+ def initialize(*args)
180
+ super
181
+ end
182
+
183
+ def source_line(n)
184
+ return nil unless lines.size >= n
185
+
186
+ lines[n - 1].chop
187
+ end
188
+
189
+ private
190
+
191
+ def lines
192
+ @lines ||= IO.readlines(file)
193
+ end
194
+ end
@@ -1,132 +1,178 @@
1
- # frozen_string_literal: true
2
-
3
- module CqlRuby
4
- class FilterEvaluator
5
- class << self
6
- def pass?(filter_reader, ancestors, node)
7
- [
8
- pass_type?(filter_reader, ancestors),
9
- pass_nesting?(filter_reader, ancestors),
10
- pass_has?(filter_reader, ancestors, node),
11
- ].all?
12
- end
13
-
14
- private
15
-
16
- #
17
- # @param [CqlRuby::FilterReader] filter_reader
18
- # @param [Array<Parser::AST::Node>] ancestors
19
- #
20
- # @return [Boolean]
21
- #
22
- def pass_type?(filter_reader, ancestors)
23
- return true unless filter_reader.restrict_types?
24
-
25
- filter_reader.allowed_types.include?(ancestors.last.type)
26
- end
27
-
28
- #
29
- # @param [CqlRuby::FilterReader] filter_reader
30
- # @param [Array<Parser::AST::Node>] ancestors
31
- #
32
- # @return [Boolean]
33
- #
34
- def pass_nesting?(filter_reader, ancestors)
35
- return true unless filter_reader.restrict_nesting?
36
-
37
- filter_reader.nest_under.all? do |nest_rule|
38
- ancestors.reverse.any? do |ancestor|
39
- next false unless ancestor.type.to_s == nest_rule.type
40
- next true unless nest_rule.restrict_name?
41
-
42
- if %w[class module].include?(nest_rule.type)
43
- CqlRuby::PatternMatcher.match?(nest_rule.name, ancestor.children[0].children[1])
44
- elsif %[def].include?(nest_rule.type)
45
- CqlRuby::PatternMatcher.match?(nest_rule.name, ancestor.children[0])
46
- else
47
- raise 'Unknown type.'
48
- end
49
- end
50
- end
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
130
- end
131
- end
132
- end
1
+ # frozen_string_literal: true
2
+
3
+ module CqlRuby
4
+ class FilterEvaluator
5
+ class << self
6
+ def pass?(filter_reader, ancestors, node)
7
+ [
8
+ pass_type?(filter_reader, ancestors),
9
+ pass_nesting?(filter_reader, ancestors),
10
+ pass_has?(filter_reader, ancestors, node),
11
+ pass_pattern?(filter_reader, ancestors, node),
12
+ ].all?
13
+ end
14
+
15
+ private
16
+
17
+ #
18
+ # @param [CqlRuby::FilterReader] filter_reader
19
+ # @param [Array<Parser::AST::Node>] ancestors
20
+ #
21
+ # @return [Boolean]
22
+ #
23
+ def pass_type?(filter_reader, ancestors)
24
+ return true unless filter_reader.restrict_types?
25
+
26
+ filter_reader.allowed_types.include?(ancestors.last.type)
27
+ end
28
+
29
+ #
30
+ # @param [CqlRuby::FilterReader] filter_reader
31
+ # @param [Array<Parser::AST::Node>] ancestors
32
+ #
33
+ # @return [Boolean]
34
+ #
35
+ def pass_nesting?(filter_reader, ancestors)
36
+ return true unless filter_reader.restrict_nesting?
37
+
38
+ filter_reader.nest_under.all? do |nest_rule|
39
+ ancestors.reverse.any? do |ancestor|
40
+ next false unless ancestor.type.to_s == nest_rule.type
41
+ next true unless nest_rule.restrict_name?
42
+
43
+ if %w[class module].include?(nest_rule.type)
44
+ CqlRuby::PatternMatcher.match?(nest_rule.name, ancestor.children[0].children[1])
45
+ elsif %[def].include?(nest_rule.type)
46
+ CqlRuby::PatternMatcher.match?(nest_rule.name, ancestor.children[0])
47
+ else
48
+ raise 'Unknown type.'
49
+ end
50
+ end
51
+ end
52
+ end
53
+
54
+ #
55
+ # @param [CqlRuby::FilterReader] filter_reader
56
+ # @param [Array<Parser::AST::Node>] ancestors
57
+ # @param [Any<Parser::AST::Node, Symbol>] node
58
+ #
59
+ def pass_has?(filter_reader, ancestors, node)
60
+ return true unless filter_reader.restrict_children?
61
+
62
+ filter_reader.has_leaves.all? do |has_rule|
63
+ anchor_node = if node.is_a?(Symbol)
64
+ # TODO: Expand this to other wrappers (loops, conditions, etc).
65
+ try_get_class(ancestors) || try_get_module(ancestors) || try_get_def(ancestors)
66
+ else
67
+ node
68
+ end
69
+ next false unless anchor_node
70
+
71
+ has_node_with_name?(anchor_node, has_rule)
72
+ end
73
+ end
74
+
75
+ #
76
+ # @param [CqlRuby::FilterReader] filter_reader
77
+ # @param [Array<Parser::AST::Node>] ancestors
78
+ # @param [Any<Parser::AST::Node, Symbol>] node
79
+ #
80
+ def pass_pattern?(filter_reader, ancestors, node)
81
+ return true unless filter_reader.restrict_pattern?
82
+
83
+ filter_reader.patterns.all? do |pattern|
84
+ pattern_ancestors = pattern.ancestors.dup
85
+ ancestor_idx = ancestors.size - 1
86
+
87
+ while !pattern_ancestors.empty? && ancestor_idx >= 0
88
+ if CqlRuby::PatternMatcher.match?(pattern_ancestors.last, ancestors[ancestor_idx].type)
89
+ pattern_ancestors.pop
90
+ end
91
+
92
+ ancestor_idx -= 1
93
+ end
94
+ return false unless pattern_ancestors.empty?
95
+
96
+ pattern_descendants = pattern.descendants.dup
97
+ match_descendant_pattern?(pattern_descendants, node)
98
+ end
99
+ end
100
+
101
+ #
102
+ # @param [Array<String>] pattern_descendants
103
+ # @param [Parser::AST::Node] node
104
+ #
105
+ def match_descendant_pattern?(pattern_descendants, node)
106
+ return true if pattern_descendants.empty?
107
+ # If we're at the end and we're still expecting a type - no match.
108
+ return false unless node.is_a?(Parser::AST::Node)
109
+
110
+ node.children.any? do |child|
111
+ next false unless child.is_a?(Parser::AST::Node)
112
+ if CqlRuby::PatternMatcher.match?(pattern_descendants.first, child.type)
113
+ match_descendant_pattern?(pattern_descendants[1..], child)
114
+ else
115
+ match_descendant_pattern?(pattern_descendants.dup, child)
116
+ end
117
+ end
118
+ end
119
+
120
+ #
121
+ # @param [Array<Parser::AST::Node>] ancestors
122
+ #
123
+ def try_get_class(ancestors)
124
+ return nil unless ancestors.size >= 2
125
+ return nil unless ancestors[-1].type == :const
126
+ return nil unless ancestors[-2].type == :class
127
+
128
+ ancestors[-2].children[2]
129
+ end
130
+
131
+ #
132
+ # @param [Array<Parser::AST::Node>] ancestors
133
+ #
134
+ def try_get_module(ancestors)
135
+ return nil unless ancestors.size >= 2
136
+ return nil unless ancestors[-1].type == :const
137
+ return nil unless ancestors[-2].type == :module
138
+
139
+ ancestors[-2].children[1]
140
+ end
141
+
142
+ #
143
+ # @param [Array<Parser::AST::Node>] ancestors
144
+ #
145
+ def try_get_def(ancestors)
146
+ return nil unless ancestors.size >= 1
147
+ return nil unless ancestors[-1].type == :def
148
+
149
+ ancestors[-1].children[2]
150
+ end
151
+
152
+ #
153
+ # @param [Parser::AST::Node] anchor_node
154
+ # @param [CqlRuby::NodeSpec]
155
+ #
156
+ def has_node_with_name?(anchor_node, has_rule)
157
+ return false unless anchor_node.is_a?(Parser::AST::Node)
158
+
159
+ fn_children_with_type = ->(node) { node.children.map { |child| [child, node.type] } }
160
+ to_visit = fn_children_with_type.call(anchor_node)
161
+
162
+ until to_visit.empty?
163
+ current_node, current_type = to_visit.shift
164
+
165
+ if current_node.is_a?(Parser::AST::Node)
166
+ to_visit += fn_children_with_type.call(current_node)
167
+ else
168
+ if current_type == has_rule.type.to_sym && CqlRuby::PatternMatcher.match?(has_rule.name, current_node)
169
+ return true
170
+ end
171
+ end
172
+ end
173
+
174
+ false
175
+ end
176
+ end
177
+ end
178
+ end