cql_ruby 0.0.11 → 0.0.12

Sign up to get free protection for your applications and to get access to all the features.
@@ -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