mutant 0.8.0 → 0.8.1

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