mutant 0.8.0 → 0.8.1

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.
Files changed (187) hide show
  1. checksums.yaml +4 -4
  2. data/Changelog.md +7 -0
  3. data/config/flay.yml +1 -1
  4. data/config/reek.yml +1 -0
  5. data/lib/mutant.rb +10 -3
  6. data/lib/mutant/actor.rb +2 -5
  7. data/lib/mutant/actor/env.rb +1 -3
  8. data/lib/mutant/actor/mailbox.rb +2 -4
  9. data/lib/mutant/actor/receiver.rb +0 -2
  10. data/lib/mutant/actor/sender.rb +0 -1
  11. data/lib/mutant/ast.rb +22 -28
  12. data/lib/mutant/ast/meta.rb +8 -88
  13. data/lib/mutant/ast/named_children.rb +1 -8
  14. data/lib/mutant/ast/sexp.rb +0 -2
  15. data/lib/mutant/cache.rb +1 -3
  16. data/lib/mutant/cli.rb +9 -19
  17. data/lib/mutant/color.rb +0 -3
  18. data/lib/mutant/config.rb +6 -2
  19. data/lib/mutant/context.rb +2 -4
  20. data/lib/mutant/context/scope.rb +10 -16
  21. data/lib/mutant/delegator.rb +0 -3
  22. data/lib/mutant/diff.rb +8 -17
  23. data/lib/mutant/env.rb +3 -5
  24. data/lib/mutant/env/bootstrap.rb +32 -39
  25. data/lib/mutant/expression.rb +14 -132
  26. data/lib/mutant/expression/method.rb +25 -42
  27. data/lib/mutant/expression/methods.rb +17 -29
  28. data/lib/mutant/expression/namespace.rb +33 -28
  29. data/lib/mutant/expression/parser.rb +71 -0
  30. data/lib/mutant/integration.rb +17 -16
  31. data/lib/mutant/isolation.rb +14 -14
  32. data/lib/mutant/loader.rb +2 -4
  33. data/lib/mutant/matcher.rb +1 -11
  34. data/lib/mutant/matcher/chain.rb +0 -1
  35. data/lib/mutant/matcher/compiler.rb +19 -52
  36. data/lib/mutant/matcher/config.rb +65 -5
  37. data/lib/mutant/matcher/filter.rb +11 -1
  38. data/lib/mutant/matcher/method.rb +11 -21
  39. data/lib/mutant/matcher/method/instance.rb +2 -16
  40. data/lib/mutant/matcher/method/singleton.rb +3 -20
  41. data/lib/mutant/matcher/methods.rb +11 -21
  42. data/lib/mutant/matcher/namespace.rb +0 -4
  43. data/lib/mutant/matcher/null.rb +0 -1
  44. data/lib/mutant/matcher/scope.rb +0 -12
  45. data/lib/mutant/meta.rb +0 -1
  46. data/lib/mutant/meta/example.rb +12 -26
  47. data/lib/mutant/meta/example/dsl.rb +1 -8
  48. data/lib/mutant/mutation.rb +6 -14
  49. data/lib/mutant/mutator.rb +2 -14
  50. data/lib/mutant/mutator/node.rb +6 -33
  51. data/lib/mutant/mutator/node/and_asgn.rb +0 -1
  52. data/lib/mutant/mutator/node/argument.rb +0 -5
  53. data/lib/mutant/mutator/node/arguments.rb +1 -7
  54. data/lib/mutant/mutator/node/begin.rb +0 -2
  55. data/lib/mutant/mutator/node/binary.rb +0 -3
  56. data/lib/mutant/mutator/node/block.rb +0 -2
  57. data/lib/mutant/mutator/node/break.rb +0 -1
  58. data/lib/mutant/mutator/node/case.rb +0 -3
  59. data/lib/mutant/mutator/node/conditional_loop.rb +0 -1
  60. data/lib/mutant/mutator/node/const.rb +0 -1
  61. data/lib/mutant/mutator/node/define.rb +0 -1
  62. data/lib/mutant/mutator/node/defined.rb +0 -1
  63. data/lib/mutant/mutator/node/dstr.rb +0 -1
  64. data/lib/mutant/mutator/node/dsym.rb +0 -1
  65. data/lib/mutant/mutator/node/generic.rb +0 -1
  66. data/lib/mutant/mutator/node/if.rb +0 -4
  67. data/lib/mutant/mutator/node/kwbegin.rb +0 -1
  68. data/lib/mutant/mutator/node/literal/array.rb +0 -2
  69. data/lib/mutant/mutator/node/literal/boolean.rb +0 -1
  70. data/lib/mutant/mutator/node/literal/fixnum.rb +2 -5
  71. data/lib/mutant/mutator/node/literal/float.rb +1 -4
  72. data/lib/mutant/mutator/node/literal/hash.rb +0 -3
  73. data/lib/mutant/mutator/node/literal/nil.rb +0 -1
  74. data/lib/mutant/mutator/node/literal/range.rb +1 -5
  75. data/lib/mutant/mutator/node/literal/regex.rb +1 -3
  76. data/lib/mutant/mutator/node/literal/string.rb +0 -1
  77. data/lib/mutant/mutator/node/literal/symbol.rb +0 -1
  78. data/lib/mutant/mutator/node/masgn.rb +0 -1
  79. data/lib/mutant/mutator/node/match_current_line.rb +0 -1
  80. data/lib/mutant/mutator/node/mlhs.rb +0 -1
  81. data/lib/mutant/mutator/node/named_value/access.rb +0 -1
  82. data/lib/mutant/mutator/node/named_value/constant_assignment.rb +0 -2
  83. data/lib/mutant/mutator/node/named_value/variable_assignment.rb +0 -2
  84. data/lib/mutant/mutator/node/next.rb +0 -1
  85. data/lib/mutant/mutator/node/noop.rb +0 -1
  86. data/lib/mutant/mutator/node/nthref.rb +0 -1
  87. data/lib/mutant/mutator/node/op_asgn.rb +0 -1
  88. data/lib/mutant/mutator/node/or_asgn.rb +1 -2
  89. data/lib/mutant/mutator/node/resbody.rb +0 -2
  90. data/lib/mutant/mutator/node/rescue.rb +1 -6
  91. data/lib/mutant/mutator/node/return.rb +0 -1
  92. data/lib/mutant/mutator/node/send.rb +16 -17
  93. data/lib/mutant/mutator/node/send/attribute_assignment.rb +0 -3
  94. data/lib/mutant/mutator/node/send/binary.rb +0 -1
  95. data/lib/mutant/mutator/node/send/index.rb +0 -3
  96. data/lib/mutant/mutator/node/splat.rb +0 -1
  97. data/lib/mutant/mutator/node/super.rb +0 -1
  98. data/lib/mutant/mutator/node/when.rb +2 -7
  99. data/lib/mutant/mutator/node/yield.rb +0 -1
  100. data/lib/mutant/mutator/node/zsuper.rb +0 -1
  101. data/lib/mutant/mutator/registry.rb +1 -4
  102. data/lib/mutant/mutator/util.rb +0 -2
  103. data/lib/mutant/mutator/util/array.rb +0 -3
  104. data/lib/mutant/mutator/util/symbol.rb +0 -1
  105. data/lib/mutant/parallel.rb +3 -9
  106. data/lib/mutant/parallel/master.rb +7 -17
  107. data/lib/mutant/parallel/source.rb +2 -7
  108. data/lib/mutant/parallel/worker.rb +1 -5
  109. data/lib/mutant/reporter.rb +0 -4
  110. data/lib/mutant/reporter/cli.rb +1 -8
  111. data/lib/mutant/reporter/cli/format.rb +7 -18
  112. data/lib/mutant/reporter/cli/printer.rb +2 -12
  113. data/lib/mutant/reporter/cli/printer/config.rb +1 -4
  114. data/lib/mutant/reporter/cli/printer/env_progress.rb +3 -7
  115. data/lib/mutant/reporter/cli/printer/env_result.rb +0 -1
  116. data/lib/mutant/reporter/cli/printer/mutation_progress_result.rb +0 -2
  117. data/lib/mutant/reporter/cli/printer/mutation_result.rb +3 -11
  118. data/lib/mutant/reporter/cli/printer/status.rb +1 -4
  119. data/lib/mutant/reporter/cli/printer/status_progressive.rb +1 -3
  120. data/lib/mutant/reporter/cli/printer/subject_progress.rb +0 -5
  121. data/lib/mutant/reporter/cli/printer/subject_result.rb +0 -1
  122. data/lib/mutant/reporter/cli/printer/test_result.rb +0 -1
  123. data/lib/mutant/reporter/cli/tput.rb +4 -1
  124. data/lib/mutant/reporter/trace.rb +2 -4
  125. data/lib/mutant/repository.rb +88 -0
  126. data/lib/mutant/require_highjack.rb +0 -1
  127. data/lib/mutant/result.rb +25 -48
  128. data/lib/mutant/runner.rb +7 -16
  129. data/lib/mutant/runner/sink.rb +3 -11
  130. data/lib/mutant/selector.rb +1 -2
  131. data/lib/mutant/selector/expression.rb +1 -2
  132. data/lib/mutant/subject.rb +10 -21
  133. data/lib/mutant/subject/method.rb +11 -12
  134. data/lib/mutant/subject/method/instance.rb +1 -5
  135. data/lib/mutant/subject/method/singleton.rb +1 -3
  136. data/lib/mutant/test.rb +1 -2
  137. data/lib/mutant/version.rb +2 -2
  138. data/lib/mutant/warning_filter.rb +2 -7
  139. data/lib/mutant/zombifier.rb +6 -10
  140. data/meta/send.rb +8 -0
  141. data/spec/spec_helper.rb +5 -1
  142. data/spec/support/corpus.rb +5 -9
  143. data/spec/support/rb_bug.rb +0 -1
  144. data/spec/support/rspec.rb +0 -1
  145. data/spec/support/ruby_vm.rb +0 -2
  146. data/spec/unit/mutant/ast/meta/send_spec.rb +42 -0
  147. data/spec/unit/mutant/ast/named_children_spec.rb +51 -0
  148. data/spec/unit/mutant/ast/sexp_spec.rb +36 -0
  149. data/spec/unit/mutant/ast_spec.rb +8 -0
  150. data/spec/unit/mutant/cli_spec.rb +34 -19
  151. data/spec/unit/mutant/context/scope_spec.rb +11 -0
  152. data/spec/unit/mutant/env/boostrap_spec.rb +5 -2
  153. data/spec/unit/mutant/env_spec.rb +14 -15
  154. data/spec/unit/mutant/expression/method_spec.rb +10 -14
  155. data/spec/unit/mutant/expression/methods_spec.rb +24 -11
  156. data/spec/unit/mutant/expression/namespace/flat_spec.rb +5 -6
  157. data/spec/unit/mutant/expression/namespace/recursive_spec.rb +16 -10
  158. data/spec/unit/mutant/expression/parser_spec.rb +67 -0
  159. data/spec/unit/mutant/expression_spec.rb +24 -57
  160. data/spec/unit/mutant/integration/rspec_spec.rb +7 -7
  161. data/spec/unit/mutant/integration_spec.rb +2 -2
  162. data/spec/unit/mutant/isolation_spec.rb +31 -29
  163. data/spec/unit/mutant/matcher/compiler/subject_prefix_spec.rb +2 -2
  164. data/spec/unit/mutant/matcher/compiler_spec.rb +27 -58
  165. data/spec/unit/mutant/matcher/config_spec.rb +45 -0
  166. data/spec/unit/mutant/matcher/filter_spec.rb +12 -5
  167. data/spec/unit/mutant/matcher/method/instance_spec.rb +0 -1
  168. data/spec/unit/mutant/matcher/method/singleton_spec.rb +0 -1
  169. data/spec/unit/mutant/matcher/namespace_spec.rb +4 -4
  170. data/spec/unit/mutant/parallel/worker_spec.rb +1 -1
  171. data/spec/unit/mutant/reporter/cli/printer/config_spec.rb +4 -4
  172. data/spec/unit/mutant/reporter/cli/printer/env_progress_spec.rb +7 -7
  173. data/spec/unit/mutant/reporter/cli/printer/env_result_spec.rb +2 -2
  174. data/spec/unit/mutant/reporter/cli/printer/status_progressive_spec.rb +2 -2
  175. data/spec/unit/mutant/reporter/cli/printer/status_spec.rb +12 -12
  176. data/spec/unit/mutant/reporter/cli/printer/subject_progress_spec.rb +1 -1
  177. data/spec/unit/mutant/reporter/cli_spec.rb +9 -10
  178. data/spec/unit/mutant/repository/diff_spec.rb +80 -0
  179. data/spec/unit/mutant/repository/subject_filter_spec.rb +28 -0
  180. data/spec/unit/mutant/result/env_spec.rb +1 -1
  181. data/spec/unit/mutant/runner_spec.rb +0 -1
  182. data/spec/unit/mutant/selector/expression_spec.rb +14 -14
  183. data/spec/unit/mutant/subject/method/instance_spec.rb +2 -2
  184. data/spec/unit/mutant/subject/method/singleton_spec.rb +2 -2
  185. data/spec/unit/mutant/subject_spec.rb +5 -2
  186. metadata +20 -3
  187. data/spec/support/mutation_verifier.rb +0 -96
@@ -7,6 +7,7 @@ module Mutant
7
7
  :cache,
8
8
  :subjects,
9
9
  :matchable_scopes,
10
+ :integration,
10
11
  :selector,
11
12
  :mutations
12
13
  )
@@ -22,7 +23,6 @@ module Mutant
22
23
  # @return [self]
23
24
  #
24
25
  # @api private
25
- #
26
26
  def warn(message)
27
27
  config.reporter.warn(message)
28
28
  self
@@ -35,7 +35,6 @@ module Mutant
35
35
  # @return [Result::Mutation]
36
36
  #
37
37
  # @api private
38
- #
39
38
  def kill(mutation)
40
39
  test_result = run_mutation_tests(mutation)
41
40
  Result::Mutation.new(
@@ -53,17 +52,16 @@ module Mutant
53
52
  #
54
53
  # @return [Result::Test]
55
54
  #
56
- # @api private
57
- #
58
55
  # rubocop:disable MethodLength
59
56
  #
57
+ # @api private
60
58
  def run_mutation_tests(mutation)
61
59
  start = Time.now
62
60
  tests = selector.call(mutation.subject)
63
61
 
64
62
  config.isolation.call do
65
63
  mutation.insert
66
- config.integration.call(tests)
64
+ integration.call(tests)
67
65
  end
68
66
  rescue Isolation::Error => error
69
67
  Result::Test.new(
@@ -8,20 +8,18 @@ module Mutant
8
8
  "Fix your lib to follow normal ruby semantics!\n" \
9
9
  '{Module,Class}#name should return resolvable constant name as String or nil'.freeze
10
10
 
11
- # Return scopes that are eligible for mnatching
11
+ # Scopes that are eligible for matching
12
12
  #
13
13
  # @return [Enumerable<Matcher::Scope>]
14
14
  #
15
15
  # @api private
16
- #
17
16
  attr_reader :matchable_scopes
18
17
 
19
- # Return new bootstrap env
18
+ # New bootstrap env
20
19
  #
21
20
  # @return [Env]
22
21
  #
23
22
  # @api private
24
- #
25
23
  def self.new(_config, _cache = Cache.new)
26
24
  super
27
25
  end
@@ -31,7 +29,6 @@ module Mutant
31
29
  # @return [Object]
32
30
  #
33
31
  # @api private
34
- #
35
32
  def initialize(*)
36
33
  super
37
34
  infect
@@ -45,35 +42,35 @@ module Mutant
45
42
  # @return [self]
46
43
  #
47
44
  # @api private
48
- #
49
45
  def warn(message)
50
46
  config.reporter.warn(message)
51
47
  self
52
48
  end
53
49
 
54
- # Return environment after bootstraping
50
+ # Environment after bootstraping
55
51
  #
56
52
  # @return [Env]
57
53
  #
58
54
  # @api private
55
+ # rubocop:disable MethodLength
59
56
  #
60
57
  def env
61
58
  subjects = matched_subjects
62
-
63
59
  Env.new(
64
60
  actor_env: Actor::Env.new(Thread),
65
61
  config: config,
66
62
  cache: cache,
67
63
  subjects: subjects,
68
64
  matchable_scopes: matchable_scopes,
69
- selector: Selector::Expression.new(config.integration),
65
+ integration: @integration,
66
+ selector: Selector::Expression.new(@integration),
70
67
  mutations: subjects.flat_map(&:mutations)
71
68
  )
72
69
  end
73
70
 
74
71
  private
75
72
 
76
- # Return scope name
73
+ # Scope name from scopeing object
77
74
  #
78
75
  # @param [Class, Module] scope
79
76
  #
@@ -84,9 +81,6 @@ module Mutant
84
81
  # otherwise
85
82
  #
86
83
  # @api private
87
- #
88
- # rubocop:disable LineLength
89
- #
90
84
  def scope_name(scope)
91
85
  scope.name
92
86
  rescue => exception
@@ -99,10 +93,33 @@ module Mutant
99
93
  # @return [undefined]
100
94
  #
101
95
  # @api private
102
- #
103
96
  def infect
104
97
  config.includes.each(&$LOAD_PATH.method(:<<))
105
98
  config.requires.each(&method(:require))
99
+ @integration = config.integration.new(config).setup
100
+ end
101
+
102
+ # Matched subjects
103
+ #
104
+ # @return [Enumerable<Subject>]
105
+ #
106
+ # @api private
107
+ def matched_subjects
108
+ Matcher::Compiler.call(self, config.matcher).to_a
109
+ end
110
+
111
+ # Initialize matchable scopes
112
+ #
113
+ # @return [undefined]
114
+ #
115
+ # @api private
116
+ def initialize_matchable_scopes
117
+ scopes = ObjectSpace.each_object(Module).each_with_object([]) do |scope, aggregate|
118
+ expression = expression(scope)
119
+ aggregate << Matcher::Scope.new(self, scope, expression) if expression
120
+ end
121
+
122
+ @matchable_scopes = scopes.sort_by { |scope| scope.expression.syntax }
106
123
  end
107
124
 
108
125
  # Try to turn scope into expression
@@ -116,7 +133,6 @@ module Mutant
116
133
  # otherwise
117
134
  #
118
135
  # @api private
119
- #
120
136
  def expression(scope)
121
137
  name = scope_name(scope) or return
122
138
 
@@ -125,30 +141,7 @@ module Mutant
125
141
  return
126
142
  end
127
143
 
128
- Expression.try_parse(name)
129
- end
130
-
131
- # Return matched subjects
132
- #
133
- # @return [Enumerable<Subject>]
134
- #
135
- # @api private
136
- #
137
- def matched_subjects
138
- Matcher::Compiler.call(self, config.matcher).to_a
139
- end
140
-
141
- # Initialize matchable scopes
142
- #
143
- # @return [undefined]
144
- #
145
- # @api private
146
- #
147
- def initialize_matchable_scopes
148
- @matchable_scopes = ObjectSpace.each_object(Module).each_with_object([]) do |scope, aggregate|
149
- expression = expression(scope)
150
- aggregate << Matcher::Scope.new(self, scope, expression) if expression
151
- end.sort_by(&:identification)
144
+ config.expression_parser.try_parse(name)
152
145
  end
153
146
  end # Boostrap
154
147
  end # Env
@@ -2,92 +2,28 @@ module Mutant
2
2
 
3
3
  # Abstract base class for match expression
4
4
  class Expression
5
- include AbstractType, Adamantium::Flat, Concord::Public.new(:match)
5
+ include AbstractType, Adamantium::Flat
6
6
 
7
- include Equalizer.new(:syntax)
7
+ fragment = /[A-Za-z][A-Za-z\d_]*/.freeze
8
+ SCOPE_NAME_PATTERN = /(?<scope_name>#{fragment}(?:#{SCOPE_OPERATOR}#{fragment})*)/.freeze
9
+ SCOPE_SYMBOL_PATTERN = '(?<scope_symbol>[.#])'.freeze
8
10
 
9
- SCOPE_NAME_PATTERN = /[A-Za-z][A-Za-z\d_]*/.freeze
11
+ private_constant(*constants(false))
10
12
 
11
- METHOD_NAME_PATTERN = Regexp.union(
12
- /[A-Za-z_][A-Za-z\d_]*[!?=]?/,
13
- *AST::Types::OPERATOR_METHODS.map(&:to_s)
14
- ).freeze
15
-
16
- INSPECT_FORMAT = '<Mutant::Expression: %s>'.freeze
17
-
18
- SCOPE_PATTERN = /#{SCOPE_NAME_PATTERN}(?:#{SCOPE_OPERATOR}#{SCOPE_NAME_PATTERN})*/.freeze
19
-
20
- REGISTRY = {}
21
-
22
- # Error raised on invalid expressions
23
- class InvalidExpressionError < RuntimeError; end
24
-
25
- # Error raised on ambiguous expressions
26
- class AmbiguousExpressionError < RuntimeError; end
27
-
28
- # Initialize expression
29
- #
30
- # @param [MatchData] match
31
- #
32
- # @api private
33
- #
34
- def initialize(*)
35
- super
36
- @syntax = match.to_s
37
- @inspect = format(INSPECT_FORMAT, syntax)
38
- end
39
-
40
- # Return marshallable representation
41
- #
42
- # FIXME: Remove the need for this.
43
- #
44
- # Refactoring Expression objects not to reference a MatchData instance.
45
- # This will make this hack unneeded.
46
- #
47
- # @return [String]
48
- #
49
- # @api private
50
- #
51
- def _dump(_level)
52
- syntax
53
- end
54
-
55
- # Load serializable representation
56
- #
57
- # @return [String]
58
- #
59
- # @return [Expression]
60
- #
61
- # @api private
62
- #
63
- def self._load(syntax)
64
- parse(syntax)
65
- end
66
-
67
- # Return inspection
68
- #
69
- # @return [String]
70
- #
71
- # @api private
72
- #
73
- attr_reader :inspect
74
-
75
- # Return syntax
13
+ # Syntax of expression
76
14
  #
77
15
  # @return [String]
78
16
  #
79
17
  # @api private
80
- #
81
- attr_reader :syntax
18
+ abstract_method :syntax
82
19
 
83
- # Return match length for expression
20
+ # Match length with other expression
84
21
  #
85
22
  # @param [Expression] other
86
23
  #
87
24
  # @return [Fixnum]
88
25
  #
89
26
  # @api private
90
- #
91
27
  def match_length(other)
92
28
  if eql?(other)
93
29
  syntax.length
@@ -103,81 +39,27 @@ module Mutant
103
39
  # @return [Boolean]
104
40
  #
105
41
  # @api private
106
- #
107
42
  def prefix?(other)
108
43
  !match_length(other).zero?
109
44
  end
110
45
 
111
- # Register expression
112
- #
113
- # @return [undefined]
114
- #
115
- # @api private
116
- #
117
- def self.register(regexp)
118
- REGISTRY[regexp] = self
119
- end
120
- private_class_method :register
121
-
122
- # Parse input into expression or raise
123
- #
124
- # @param [String] syntax
125
- #
126
- # @return [Expression]
127
- # if expression is valid
128
- #
129
- # @raise [RuntimeError]
130
- # otherwise
131
- #
132
- # @api private
133
- #
134
- def self.parse(input)
135
- try_parse(input) or fail InvalidExpressionError, "Expression: #{input.inspect} is not valid"
136
- end
137
-
138
- # Parse input into expression
46
+ # Try to parse input into expression of receiver class
139
47
  #
140
48
  # @param [String] input
141
49
  #
142
50
  # @return [Expression]
143
- # if expression is valid
51
+ # when successful
144
52
  #
145
53
  # @return [nil]
146
54
  # otherwise
147
55
  #
148
56
  # @api private
149
- #
150
57
  def self.try_parse(input)
151
- expressions = expressions(input)
152
- case expressions.length
153
- when 0
154
- when 1
155
- expressions.first
156
- else
157
- fail AmbiguousExpressionError, "Ambiguous expression: #{input.inspect}"
158
- end
159
- end
160
-
161
- # Return expressions for input
162
- #
163
- # @param [String] input
164
- #
165
- # @return [Classifier]
166
- # if classifier can be found
167
- #
168
- # @return [nil]
169
- # otherwise
170
- #
171
- # @api private
172
- #
173
- def self.expressions(input)
174
- REGISTRY.each_with_object([]) do |(regexp, klass), expressions|
175
- match = regexp.match(input)
176
- next unless match
177
- expressions << klass.new(match)
178
- end
58
+ match = self::REGEXP.match(input)
59
+ return unless match
60
+ names = anima.attribute_names
61
+ new(Hash[names.zip(names.map(&match.method(:[])))])
179
62
  end
180
- private_class_method :expressions
181
63
 
182
64
  end # Expression
183
65
  end # Mutant
@@ -3,72 +3,55 @@ module Mutant
3
3
 
4
4
  # Explicit method expression
5
5
  class Method < self
6
+ include Anima.new(:scope_name, :scope_symbol, :method_name)
7
+ private(*anima.attribute_names)
6
8
 
7
9
  MATCHERS = IceNine.deep_freeze(
8
10
  '.' => Matcher::Methods::Singleton,
9
11
  '#' => Matcher::Methods::Instance
10
12
  )
11
13
 
12
- register(
13
- /\A(?<scope_name>#{SCOPE_PATTERN})(?<scope_symbol>[.#])(?<method_name>#{METHOD_NAME_PATTERN})\z/
14
- )
14
+ METHOD_NAME_PATTERN = Regexp.union(
15
+ /(?<method_name>[A-Za-z_][A-Za-z\d_]*[!?=]?)/,
16
+ *AST::Types::OPERATOR_METHODS.map(&:to_s)
17
+ ).freeze
15
18
 
16
- # Return method matcher
17
- #
18
- # @param [Env] env
19
- #
20
- # @return [Matcher::Method]
21
- #
22
- # @api private
23
- #
24
- def matcher(env)
25
- methods_matcher = MATCHERS.fetch(scope_symbol).new(env, scope)
26
- method = methods_matcher.methods.detect do |meth|
27
- meth.name.equal?(method_name)
28
- end or fail NameError, "Cannot find method #{method_name}"
29
- methods_matcher.matcher.build(env, scope, method)
30
- end
19
+ private_constant(*constants(false))
31
20
 
32
- private
21
+ REGEXP = /\A#{SCOPE_NAME_PATTERN}#{SCOPE_SYMBOL_PATTERN}#{METHOD_NAME_PATTERN}\z/.freeze
33
22
 
34
- # Return scope name
23
+ # Syntax of expression
35
24
  #
36
25
  # @return [String]
37
26
  #
38
27
  # @api private
39
- #
40
- def scope_name
41
- match[__method__]
28
+ def syntax
29
+ [scope_name, scope_symbol, method_name].join
42
30
  end
31
+ memoize :syntax
43
32
 
44
- # Return scope
45
- #
46
- # @return [Class, Method]
47
- #
48
- # @api private
33
+ # Matcher for expression
49
34
  #
50
- def scope
51
- Object.const_get(scope_name)
52
- end
53
-
54
- # Return method name
35
+ # @param [Env] env
55
36
  #
56
- # @return [String]
37
+ # @return [Matcher]
57
38
  #
58
39
  # @api private
59
- #
60
- def method_name
61
- match[__method__].to_sym
40
+ def matcher(env)
41
+ methods_matcher = MATCHERS.fetch(scope_symbol).new(env, scope)
42
+
43
+ Matcher::Filter.build(methods_matcher) { |subject| subject.expression.eql?(self) }
62
44
  end
63
45
 
64
- # Return scope symbol
46
+ private
47
+
48
+ # Scope object
65
49
  #
66
- # @return [Symbol]
50
+ # @return [Class, Method]
67
51
  #
68
52
  # @api private
69
- #
70
- def scope_symbol
71
- match[__method__]
53
+ def scope
54
+ Object.const_get(scope_name)
72
55
  end
73
56
 
74
57
  end # Method