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.
- checksums.yaml +4 -4
- data/Changelog.md +7 -0
- data/config/flay.yml +1 -1
- data/config/reek.yml +1 -0
- data/lib/mutant.rb +10 -3
- data/lib/mutant/actor.rb +2 -5
- data/lib/mutant/actor/env.rb +1 -3
- data/lib/mutant/actor/mailbox.rb +2 -4
- data/lib/mutant/actor/receiver.rb +0 -2
- data/lib/mutant/actor/sender.rb +0 -1
- data/lib/mutant/ast.rb +22 -28
- data/lib/mutant/ast/meta.rb +8 -88
- data/lib/mutant/ast/named_children.rb +1 -8
- data/lib/mutant/ast/sexp.rb +0 -2
- data/lib/mutant/cache.rb +1 -3
- data/lib/mutant/cli.rb +9 -19
- data/lib/mutant/color.rb +0 -3
- data/lib/mutant/config.rb +6 -2
- data/lib/mutant/context.rb +2 -4
- data/lib/mutant/context/scope.rb +10 -16
- data/lib/mutant/delegator.rb +0 -3
- data/lib/mutant/diff.rb +8 -17
- data/lib/mutant/env.rb +3 -5
- data/lib/mutant/env/bootstrap.rb +32 -39
- data/lib/mutant/expression.rb +14 -132
- data/lib/mutant/expression/method.rb +25 -42
- data/lib/mutant/expression/methods.rb +17 -29
- data/lib/mutant/expression/namespace.rb +33 -28
- data/lib/mutant/expression/parser.rb +71 -0
- data/lib/mutant/integration.rb +17 -16
- data/lib/mutant/isolation.rb +14 -14
- data/lib/mutant/loader.rb +2 -4
- data/lib/mutant/matcher.rb +1 -11
- data/lib/mutant/matcher/chain.rb +0 -1
- data/lib/mutant/matcher/compiler.rb +19 -52
- data/lib/mutant/matcher/config.rb +65 -5
- data/lib/mutant/matcher/filter.rb +11 -1
- data/lib/mutant/matcher/method.rb +11 -21
- data/lib/mutant/matcher/method/instance.rb +2 -16
- data/lib/mutant/matcher/method/singleton.rb +3 -20
- data/lib/mutant/matcher/methods.rb +11 -21
- data/lib/mutant/matcher/namespace.rb +0 -4
- data/lib/mutant/matcher/null.rb +0 -1
- data/lib/mutant/matcher/scope.rb +0 -12
- data/lib/mutant/meta.rb +0 -1
- data/lib/mutant/meta/example.rb +12 -26
- data/lib/mutant/meta/example/dsl.rb +1 -8
- data/lib/mutant/mutation.rb +6 -14
- data/lib/mutant/mutator.rb +2 -14
- data/lib/mutant/mutator/node.rb +6 -33
- data/lib/mutant/mutator/node/and_asgn.rb +0 -1
- data/lib/mutant/mutator/node/argument.rb +0 -5
- data/lib/mutant/mutator/node/arguments.rb +1 -7
- data/lib/mutant/mutator/node/begin.rb +0 -2
- data/lib/mutant/mutator/node/binary.rb +0 -3
- data/lib/mutant/mutator/node/block.rb +0 -2
- data/lib/mutant/mutator/node/break.rb +0 -1
- data/lib/mutant/mutator/node/case.rb +0 -3
- data/lib/mutant/mutator/node/conditional_loop.rb +0 -1
- data/lib/mutant/mutator/node/const.rb +0 -1
- data/lib/mutant/mutator/node/define.rb +0 -1
- data/lib/mutant/mutator/node/defined.rb +0 -1
- data/lib/mutant/mutator/node/dstr.rb +0 -1
- data/lib/mutant/mutator/node/dsym.rb +0 -1
- data/lib/mutant/mutator/node/generic.rb +0 -1
- data/lib/mutant/mutator/node/if.rb +0 -4
- data/lib/mutant/mutator/node/kwbegin.rb +0 -1
- data/lib/mutant/mutator/node/literal/array.rb +0 -2
- data/lib/mutant/mutator/node/literal/boolean.rb +0 -1
- data/lib/mutant/mutator/node/literal/fixnum.rb +2 -5
- data/lib/mutant/mutator/node/literal/float.rb +1 -4
- data/lib/mutant/mutator/node/literal/hash.rb +0 -3
- data/lib/mutant/mutator/node/literal/nil.rb +0 -1
- data/lib/mutant/mutator/node/literal/range.rb +1 -5
- data/lib/mutant/mutator/node/literal/regex.rb +1 -3
- data/lib/mutant/mutator/node/literal/string.rb +0 -1
- data/lib/mutant/mutator/node/literal/symbol.rb +0 -1
- data/lib/mutant/mutator/node/masgn.rb +0 -1
- data/lib/mutant/mutator/node/match_current_line.rb +0 -1
- data/lib/mutant/mutator/node/mlhs.rb +0 -1
- data/lib/mutant/mutator/node/named_value/access.rb +0 -1
- data/lib/mutant/mutator/node/named_value/constant_assignment.rb +0 -2
- data/lib/mutant/mutator/node/named_value/variable_assignment.rb +0 -2
- data/lib/mutant/mutator/node/next.rb +0 -1
- data/lib/mutant/mutator/node/noop.rb +0 -1
- data/lib/mutant/mutator/node/nthref.rb +0 -1
- data/lib/mutant/mutator/node/op_asgn.rb +0 -1
- data/lib/mutant/mutator/node/or_asgn.rb +1 -2
- data/lib/mutant/mutator/node/resbody.rb +0 -2
- data/lib/mutant/mutator/node/rescue.rb +1 -6
- data/lib/mutant/mutator/node/return.rb +0 -1
- data/lib/mutant/mutator/node/send.rb +16 -17
- data/lib/mutant/mutator/node/send/attribute_assignment.rb +0 -3
- data/lib/mutant/mutator/node/send/binary.rb +0 -1
- data/lib/mutant/mutator/node/send/index.rb +0 -3
- data/lib/mutant/mutator/node/splat.rb +0 -1
- data/lib/mutant/mutator/node/super.rb +0 -1
- data/lib/mutant/mutator/node/when.rb +2 -7
- data/lib/mutant/mutator/node/yield.rb +0 -1
- data/lib/mutant/mutator/node/zsuper.rb +0 -1
- data/lib/mutant/mutator/registry.rb +1 -4
- data/lib/mutant/mutator/util.rb +0 -2
- data/lib/mutant/mutator/util/array.rb +0 -3
- data/lib/mutant/mutator/util/symbol.rb +0 -1
- data/lib/mutant/parallel.rb +3 -9
- data/lib/mutant/parallel/master.rb +7 -17
- data/lib/mutant/parallel/source.rb +2 -7
- data/lib/mutant/parallel/worker.rb +1 -5
- data/lib/mutant/reporter.rb +0 -4
- data/lib/mutant/reporter/cli.rb +1 -8
- data/lib/mutant/reporter/cli/format.rb +7 -18
- data/lib/mutant/reporter/cli/printer.rb +2 -12
- data/lib/mutant/reporter/cli/printer/config.rb +1 -4
- data/lib/mutant/reporter/cli/printer/env_progress.rb +3 -7
- data/lib/mutant/reporter/cli/printer/env_result.rb +0 -1
- data/lib/mutant/reporter/cli/printer/mutation_progress_result.rb +0 -2
- data/lib/mutant/reporter/cli/printer/mutation_result.rb +3 -11
- data/lib/mutant/reporter/cli/printer/status.rb +1 -4
- data/lib/mutant/reporter/cli/printer/status_progressive.rb +1 -3
- data/lib/mutant/reporter/cli/printer/subject_progress.rb +0 -5
- data/lib/mutant/reporter/cli/printer/subject_result.rb +0 -1
- data/lib/mutant/reporter/cli/printer/test_result.rb +0 -1
- data/lib/mutant/reporter/cli/tput.rb +4 -1
- data/lib/mutant/reporter/trace.rb +2 -4
- data/lib/mutant/repository.rb +88 -0
- data/lib/mutant/require_highjack.rb +0 -1
- data/lib/mutant/result.rb +25 -48
- data/lib/mutant/runner.rb +7 -16
- data/lib/mutant/runner/sink.rb +3 -11
- data/lib/mutant/selector.rb +1 -2
- data/lib/mutant/selector/expression.rb +1 -2
- data/lib/mutant/subject.rb +10 -21
- data/lib/mutant/subject/method.rb +11 -12
- data/lib/mutant/subject/method/instance.rb +1 -5
- data/lib/mutant/subject/method/singleton.rb +1 -3
- data/lib/mutant/test.rb +1 -2
- data/lib/mutant/version.rb +2 -2
- data/lib/mutant/warning_filter.rb +2 -7
- data/lib/mutant/zombifier.rb +6 -10
- data/meta/send.rb +8 -0
- data/spec/spec_helper.rb +5 -1
- data/spec/support/corpus.rb +5 -9
- data/spec/support/rb_bug.rb +0 -1
- data/spec/support/rspec.rb +0 -1
- data/spec/support/ruby_vm.rb +0 -2
- data/spec/unit/mutant/ast/meta/send_spec.rb +42 -0
- data/spec/unit/mutant/ast/named_children_spec.rb +51 -0
- data/spec/unit/mutant/ast/sexp_spec.rb +36 -0
- data/spec/unit/mutant/ast_spec.rb +8 -0
- data/spec/unit/mutant/cli_spec.rb +34 -19
- data/spec/unit/mutant/context/scope_spec.rb +11 -0
- data/spec/unit/mutant/env/boostrap_spec.rb +5 -2
- data/spec/unit/mutant/env_spec.rb +14 -15
- data/spec/unit/mutant/expression/method_spec.rb +10 -14
- data/spec/unit/mutant/expression/methods_spec.rb +24 -11
- data/spec/unit/mutant/expression/namespace/flat_spec.rb +5 -6
- data/spec/unit/mutant/expression/namespace/recursive_spec.rb +16 -10
- data/spec/unit/mutant/expression/parser_spec.rb +67 -0
- data/spec/unit/mutant/expression_spec.rb +24 -57
- data/spec/unit/mutant/integration/rspec_spec.rb +7 -7
- data/spec/unit/mutant/integration_spec.rb +2 -2
- data/spec/unit/mutant/isolation_spec.rb +31 -29
- data/spec/unit/mutant/matcher/compiler/subject_prefix_spec.rb +2 -2
- data/spec/unit/mutant/matcher/compiler_spec.rb +27 -58
- data/spec/unit/mutant/matcher/config_spec.rb +45 -0
- data/spec/unit/mutant/matcher/filter_spec.rb +12 -5
- data/spec/unit/mutant/matcher/method/instance_spec.rb +0 -1
- data/spec/unit/mutant/matcher/method/singleton_spec.rb +0 -1
- data/spec/unit/mutant/matcher/namespace_spec.rb +4 -4
- data/spec/unit/mutant/parallel/worker_spec.rb +1 -1
- data/spec/unit/mutant/reporter/cli/printer/config_spec.rb +4 -4
- data/spec/unit/mutant/reporter/cli/printer/env_progress_spec.rb +7 -7
- data/spec/unit/mutant/reporter/cli/printer/env_result_spec.rb +2 -2
- data/spec/unit/mutant/reporter/cli/printer/status_progressive_spec.rb +2 -2
- data/spec/unit/mutant/reporter/cli/printer/status_spec.rb +12 -12
- data/spec/unit/mutant/reporter/cli/printer/subject_progress_spec.rb +1 -1
- data/spec/unit/mutant/reporter/cli_spec.rb +9 -10
- data/spec/unit/mutant/repository/diff_spec.rb +80 -0
- data/spec/unit/mutant/repository/subject_filter_spec.rb +28 -0
- data/spec/unit/mutant/result/env_spec.rb +1 -1
- data/spec/unit/mutant/runner_spec.rb +0 -1
- data/spec/unit/mutant/selector/expression_spec.rb +14 -14
- data/spec/unit/mutant/subject/method/instance_spec.rb +2 -2
- data/spec/unit/mutant/subject/method/singleton_spec.rb +2 -2
- data/spec/unit/mutant/subject_spec.rb +5 -2
- metadata +20 -3
- data/spec/support/mutation_verifier.rb +0 -96
data/lib/mutant/env.rb
CHANGED
@@ -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
|
-
|
64
|
+
integration.call(tests)
|
67
65
|
end
|
68
66
|
rescue Isolation::Error => error
|
69
67
|
Result::Test.new(
|
data/lib/mutant/env/bootstrap.rb
CHANGED
@@ -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
|
-
#
|
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
|
-
#
|
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
|
-
#
|
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
|
-
|
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
|
-
#
|
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
|
-
|
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
|
data/lib/mutant/expression.rb
CHANGED
@@ -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
|
5
|
+
include AbstractType, Adamantium::Flat
|
6
6
|
|
7
|
-
|
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
|
-
|
11
|
+
private_constant(*constants(false))
|
10
12
|
|
11
|
-
|
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
|
-
#
|
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
|
-
#
|
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
|
-
#
|
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
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
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
|
-
|
13
|
-
|
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
|
-
|
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
|
-
|
21
|
+
REGEXP = /\A#{SCOPE_NAME_PATTERN}#{SCOPE_SYMBOL_PATTERN}#{METHOD_NAME_PATTERN}\z/.freeze
|
33
22
|
|
34
|
-
#
|
23
|
+
# Syntax of expression
|
35
24
|
#
|
36
25
|
# @return [String]
|
37
26
|
#
|
38
27
|
# @api private
|
39
|
-
|
40
|
-
|
41
|
-
match[__method__]
|
28
|
+
def syntax
|
29
|
+
[scope_name, scope_symbol, method_name].join
|
42
30
|
end
|
31
|
+
memoize :syntax
|
43
32
|
|
44
|
-
#
|
45
|
-
#
|
46
|
-
# @return [Class, Method]
|
47
|
-
#
|
48
|
-
# @api private
|
33
|
+
# Matcher for expression
|
49
34
|
#
|
50
|
-
|
51
|
-
Object.const_get(scope_name)
|
52
|
-
end
|
53
|
-
|
54
|
-
# Return method name
|
35
|
+
# @param [Env] env
|
55
36
|
#
|
56
|
-
# @return [
|
37
|
+
# @return [Matcher]
|
57
38
|
#
|
58
39
|
# @api private
|
59
|
-
|
60
|
-
|
61
|
-
|
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
|
-
|
46
|
+
private
|
47
|
+
|
48
|
+
# Scope object
|
65
49
|
#
|
66
|
-
# @return [
|
50
|
+
# @return [Class, Method]
|
67
51
|
#
|
68
52
|
# @api private
|
69
|
-
|
70
|
-
|
71
|
-
match[__method__]
|
53
|
+
def scope
|
54
|
+
Object.const_get(scope_name)
|
72
55
|
end
|
73
56
|
|
74
57
|
end # Method
|