morpher 0.2.6
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.
Potentially problematic release.
This version of morpher might be problematic. Click here for more details.
- checksums.yaml +7 -0
- data/.circle.yml +6 -0
- data/.gitignore +5 -0
- data/.rspec +4 -0
- data/.rubocop.yml +8 -0
- data/Changelog.md +60 -0
- data/Gemfile +3 -0
- data/LICENSE +20 -0
- data/README.md +56 -0
- data/Rakefile +95 -0
- data/circle.yml +7 -0
- data/config/devtools.yml +2 -0
- data/config/flay.yml +3 -0
- data/config/flog.yml +2 -0
- data/config/heckle.yml +3 -0
- data/config/mutant.yml +8 -0
- data/config/reek.yml +109 -0
- data/config/rubocop.yml +138 -0
- data/config/yardstick.yml +2 -0
- data/examples/README.md +13 -0
- data/examples/a.rb +25 -0
- data/examples/b.rb +35 -0
- data/lib/morpher.rb +111 -0
- data/lib/morpher/compiler.rb +17 -0
- data/lib/morpher/compiler/emitter.rb +82 -0
- data/lib/morpher/compiler/error.rb +84 -0
- data/lib/morpher/compiler/evaluator.rb +63 -0
- data/lib/morpher/compiler/evaluator/emitter.rb +224 -0
- data/lib/morpher/compiler/preprocessor.rb +29 -0
- data/lib/morpher/compiler/preprocessor/emitter.rb +54 -0
- data/lib/morpher/compiler/preprocessor/emitter/anima.rb +69 -0
- data/lib/morpher/compiler/preprocessor/emitter/boolean.rb +31 -0
- data/lib/morpher/compiler/preprocessor/emitter/key.rb +87 -0
- data/lib/morpher/compiler/preprocessor/emitter/noop.rb +45 -0
- data/lib/morpher/compiler/preprocessor/emitter/param.rb +50 -0
- data/lib/morpher/evaluation.rb +118 -0
- data/lib/morpher/evaluator.rb +40 -0
- data/lib/morpher/evaluator/binary.rb +46 -0
- data/lib/morpher/evaluator/nary.rb +97 -0
- data/lib/morpher/evaluator/nullary.rb +92 -0
- data/lib/morpher/evaluator/nullary/parameterized.rb +48 -0
- data/lib/morpher/evaluator/predicate.rb +22 -0
- data/lib/morpher/evaluator/predicate/boolean.rb +76 -0
- data/lib/morpher/evaluator/predicate/contradiction.rb +36 -0
- data/lib/morpher/evaluator/predicate/eql.rb +50 -0
- data/lib/morpher/evaluator/predicate/negation.rb +52 -0
- data/lib/morpher/evaluator/predicate/primitive.rb +49 -0
- data/lib/morpher/evaluator/predicate/tautology.rb +36 -0
- data/lib/morpher/evaluator/transformer.rb +75 -0
- data/lib/morpher/evaluator/transformer/attribute.rb +25 -0
- data/lib/morpher/evaluator/transformer/block.rb +81 -0
- data/lib/morpher/evaluator/transformer/coerce.rb +166 -0
- data/lib/morpher/evaluator/transformer/custom.rb +34 -0
- data/lib/morpher/evaluator/transformer/domain.rb +86 -0
- data/lib/morpher/evaluator/transformer/domain/attribute_accessors.rb +60 -0
- data/lib/morpher/evaluator/transformer/domain/attribute_hash.rb +52 -0
- data/lib/morpher/evaluator/transformer/domain/instance_variables.rb +60 -0
- data/lib/morpher/evaluator/transformer/domain/param.rb +54 -0
- data/lib/morpher/evaluator/transformer/guard.rb +62 -0
- data/lib/morpher/evaluator/transformer/hash_transform.rb +149 -0
- data/lib/morpher/evaluator/transformer/input.rb +37 -0
- data/lib/morpher/evaluator/transformer/key.rb +86 -0
- data/lib/morpher/evaluator/transformer/map.rb +100 -0
- data/lib/morpher/evaluator/transformer/merge.rb +25 -0
- data/lib/morpher/evaluator/transformer/static.rb +27 -0
- data/lib/morpher/evaluator/unary.rb +79 -0
- data/lib/morpher/node_helpers.rb +19 -0
- data/lib/morpher/printer.rb +233 -0
- data/lib/morpher/printer/mixin.rb +58 -0
- data/lib/morpher/registry.rb +51 -0
- data/lib/morpher/type_lookup.rb +51 -0
- data/morpher.gemspec +29 -0
- data/spec/integration_spec.rb +184 -0
- data/spec/rcov.opts +7 -0
- data/spec/shared/evaluator_behavior.rb +155 -0
- data/spec/spec_helper.rb +36 -0
- data/spec/support/ice_nine_config.rb +8 -0
- data/spec/support/let_mock_helper.rb +8 -0
- data/spec/support/strip_helper.rb +12 -0
- data/spec/unit/morpher/compiler/preprocessor_spec.rb +46 -0
- data/spec/unit/morpher/evaluator/nullary/parameterized_spec.rb +25 -0
- data/spec/unit/morpher/evaluator/predicate/boolean/and_spec.rb +11 -0
- data/spec/unit/morpher/evaluator/predicate/boolean/or_spec.rb +26 -0
- data/spec/unit/morpher/evaluator/predicate/boolean/xor_spec.rb +26 -0
- data/spec/unit/morpher/evaluator/predicate/contrandiction_spec.rb +7 -0
- data/spec/unit/morpher/evaluator/predicate/eql_spec.rb +11 -0
- data/spec/unit/morpher/evaluator/predicate/negation_spec.rb +10 -0
- data/spec/unit/morpher/evaluator/predicate/primitive_spec.rb +17 -0
- data/spec/unit/morpher/evaluator/predicate/tautology_spec.rb +7 -0
- data/spec/unit/morpher/evaluator/transformer/attribute_spec.rb +9 -0
- data/spec/unit/morpher/evaluator/transformer/block_spec.rb +92 -0
- data/spec/unit/morpher/evaluator/transformer/coerce/parse_int_spec.rb +23 -0
- data/spec/unit/morpher/evaluator/transformer/custom_spec.rb +13 -0
- data/spec/unit/morpher/evaluator/transformer/domain/attribute_accessors_spec.rb +48 -0
- data/spec/unit/morpher/evaluator/transformer/domain/attribute_hash_spec.rb +40 -0
- data/spec/unit/morpher/evaluator/transformer/domain/instance_variables_spec.rb +47 -0
- data/spec/unit/morpher/evaluator/transformer/guard_spec.rb +12 -0
- data/spec/unit/morpher/evaluator/transformer/hash_transform_spec.rb +47 -0
- data/spec/unit/morpher/evaluator/transformer/input_spec.rb +11 -0
- data/spec/unit/morpher/evaluator/transformer/map_spec.rb +25 -0
- data/spec/unit/morpher/evaluator/transformer/static_spec.rb +10 -0
- data/spec/unit/morpher/evaluator_spec.rb +15 -0
- data/spec/unit/morpher/printer_spec.rb +21 -0
- data/spec/unit/morpher/registry_spec.rb +11 -0
- data/spec/unit/morpher_spec.rb +53 -0
- metadata +302 -0
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'devtools/spec_helper'
|
2
|
+
require 'morpher'
|
3
|
+
require 'mutant' # for the node helpers
|
4
|
+
|
5
|
+
# Monkeypatch to mutant all specs per mutation.
|
6
|
+
#
|
7
|
+
# TODO: Use master once it supports configurable implicit coverage.
|
8
|
+
#
|
9
|
+
# Morpher predicates are needed to finally make this configurable in mutant.
|
10
|
+
#
|
11
|
+
module Mutant
|
12
|
+
|
13
|
+
module Rspec
|
14
|
+
class Killer
|
15
|
+
|
16
|
+
# Return all example groups
|
17
|
+
#
|
18
|
+
# @return [Enumerable<RSpec::Example>]
|
19
|
+
#
|
20
|
+
# @api private
|
21
|
+
#
|
22
|
+
def example_groups
|
23
|
+
strategy.example_groups
|
24
|
+
end
|
25
|
+
|
26
|
+
end # Rspec
|
27
|
+
end # Killer
|
28
|
+
end # Mutant
|
29
|
+
|
30
|
+
RSpec.configure do |config|
|
31
|
+
config.include(StripHelper)
|
32
|
+
config.include(Morpher::NodeHelpers)
|
33
|
+
config.expect_with :rspec do |rspec|
|
34
|
+
rspec.syntax = %i[expect should]
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
describe Morpher::Compiler::Preprocessor do
|
2
|
+
subject { Morpher::Compiler::Preprocessor::DEFAULT.call(input) }
|
3
|
+
|
4
|
+
shared_examples_for 'a preprocessor' do
|
5
|
+
it { should eql(expected) }
|
6
|
+
|
7
|
+
it 'is able to compile output' do
|
8
|
+
Morpher.compile(subject)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
context 'with s(:boolean)' do
|
13
|
+
|
14
|
+
it_should_behave_like 'a preprocessor' do
|
15
|
+
let(:input) { s(:boolean) }
|
16
|
+
let(:expected) { s(:xor, s(:primitive, TrueClass), s(:primitive, FalseClass)) }
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
class Example
|
22
|
+
include Anima.new(:foo, :bar)
|
23
|
+
end
|
24
|
+
|
25
|
+
let(:param) do
|
26
|
+
Morpher::Evaluator::Transformer::Domain::Param.new(Example, %i[foo bar])
|
27
|
+
end
|
28
|
+
|
29
|
+
context 'with s(:anima_load)' do
|
30
|
+
|
31
|
+
it_should_behave_like 'a preprocessor' do
|
32
|
+
let(:input) { s(:anima_load, Example) }
|
33
|
+
let(:expected) { s(:load_attribute_hash, param) }
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
context 'with s(:anima_dump)' do
|
39
|
+
|
40
|
+
it_should_behave_like 'a preprocessor' do
|
41
|
+
let(:input) { s(:anima_dump, Example) }
|
42
|
+
let(:expected) { s(:dump_attribute_hash, param) }
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
describe Morpher::Evaluator::Nullary::Parameterized do
|
2
|
+
|
3
|
+
describe '.include' do
|
4
|
+
let(:object) do
|
5
|
+
Class.new(Morpher::Evaluator) do
|
6
|
+
include Morpher::Evaluator::Nullary::Parameterized
|
7
|
+
|
8
|
+
def self.name
|
9
|
+
'Foo'
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
let(:instance) do
|
15
|
+
object.new('foo')
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'sets up printer' do
|
19
|
+
instance.description.should eql(strip(<<-'TEXT'))
|
20
|
+
Foo
|
21
|
+
param: "foo"
|
22
|
+
TEXT
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
describe Morpher::Evaluator::Predicate::Boolean::And do
|
2
|
+
let(:object) { described_class.new([body_a, body_b]) }
|
3
|
+
|
4
|
+
let(:body_a) { Morpher.compile(s(:primitive, String)) }
|
5
|
+
let(:body_b) { Morpher.compile(s(:eql, s(:attribute, :length), s(:static, 1))) }
|
6
|
+
|
7
|
+
let(:negative_input) { '' }
|
8
|
+
let(:positive_input) { 'a' }
|
9
|
+
|
10
|
+
include_examples 'predicate evaluator'
|
11
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
describe Morpher::Evaluator::Predicate::Boolean::Or do
|
2
|
+
let(:object) { described_class.new([body_a, body_b]) }
|
3
|
+
|
4
|
+
let(:body_a) { Morpher.compile(s(:primitive, String)) }
|
5
|
+
let(:body_b) { Morpher.compile(s(:primitive, Symbol)) }
|
6
|
+
|
7
|
+
let(:negative_input) { Object.new }
|
8
|
+
let(:positive_input) { '' }
|
9
|
+
|
10
|
+
include_examples 'predicate evaluator'
|
11
|
+
|
12
|
+
context '#evalaution' do
|
13
|
+
|
14
|
+
context 'on positive input' do
|
15
|
+
subject { object.evaluation(positive_input) }
|
16
|
+
|
17
|
+
its(:evaluations) { should eql([body_a.evaluation(positive_input)]) }
|
18
|
+
end
|
19
|
+
|
20
|
+
context 'on negative input' do
|
21
|
+
subject { object.evaluation(negative_input) }
|
22
|
+
|
23
|
+
its(:evaluations) { should eql(object.body.map { |node| node.evaluation(negative_input) }) }
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
describe Morpher::Evaluator::Predicate::Boolean::Xor do
|
2
|
+
let(:object) { described_class.new([body_a, body_b]) }
|
3
|
+
|
4
|
+
let(:body_a) { Morpher.compile(s(:primitive, TrueClass)) }
|
5
|
+
let(:body_b) { Morpher.compile(s(:primitive, FalseClass)) }
|
6
|
+
|
7
|
+
let(:negative_input) { nil }
|
8
|
+
let(:positive_input) { true }
|
9
|
+
|
10
|
+
include_examples 'predicate evaluator'
|
11
|
+
|
12
|
+
context '#evalaution' do
|
13
|
+
|
14
|
+
context 'on positive input' do
|
15
|
+
subject { object.evaluation(positive_input) }
|
16
|
+
|
17
|
+
its(:evaluations) { should eql([body_a.evaluation(positive_input)]) }
|
18
|
+
end
|
19
|
+
|
20
|
+
context 'on negative input' do
|
21
|
+
subject { object.evaluation(negative_input) }
|
22
|
+
|
23
|
+
its(:evaluations) { should eql(object.body.map { |node| node.evaluation(negative_input) }) }
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
describe Morpher::Evaluator::Predicate::EQL do
|
2
|
+
let(:object) { described_class.new(left, right) }
|
3
|
+
|
4
|
+
let(:left) { Morpher.compile(s(:static, 1.0)) }
|
5
|
+
let(:right) { Morpher.compile(s(:input)) }
|
6
|
+
|
7
|
+
let(:positive_input) { 1.0 }
|
8
|
+
let(:negative_input) { 1 }
|
9
|
+
|
10
|
+
include_examples 'predicate evaluator'
|
11
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
describe Morpher::Evaluator::Predicate::Negation do
|
2
|
+
let(:object) { described_class.new(operand) }
|
3
|
+
|
4
|
+
let(:operand) { Morpher.compile(s(:input)) }
|
5
|
+
|
6
|
+
let(:positive_input) { false }
|
7
|
+
let(:negative_input) { true }
|
8
|
+
|
9
|
+
include_examples 'predicate evaluator'
|
10
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
describe Morpher::Evaluator::Predicate::Primitive::Exact do
|
2
|
+
let(:object) { described_class.new(Morpher::Evaluator) }
|
3
|
+
|
4
|
+
let(:positive_input) { Morpher::Evaluator.allocate }
|
5
|
+
let(:negative_input) { Morpher::Evaluator::Predicate.allocate }
|
6
|
+
|
7
|
+
include_examples 'predicate evaluator'
|
8
|
+
end
|
9
|
+
|
10
|
+
describe Morpher::Evaluator::Predicate::Primitive::Permissive do
|
11
|
+
let(:object) { described_class.new(Morpher::Evaluator) }
|
12
|
+
|
13
|
+
let(:positive_input) { Morpher::Evaluator::Predicate.allocate }
|
14
|
+
let(:negative_input) { '' }
|
15
|
+
|
16
|
+
include_examples 'predicate evaluator'
|
17
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
describe Morpher::Evaluator::Transformer::Attribute do
|
2
|
+
let(:object) { described_class.new(:length) }
|
3
|
+
|
4
|
+
include_examples 'transforming evaluator on valid input'
|
5
|
+
include_examples 'intransitive evaluator'
|
6
|
+
|
7
|
+
let(:valid_input) { 'foo' }
|
8
|
+
let(:expected_output) { 3 }
|
9
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
describe Morpher::Evaluator::Transformer::Block do
|
2
|
+
|
3
|
+
let(:ast) do
|
4
|
+
s(:block, body_a, body_b)
|
5
|
+
end
|
6
|
+
|
7
|
+
let(:object) do
|
8
|
+
Morpher.compile(ast)
|
9
|
+
end
|
10
|
+
|
11
|
+
let(:evaluator_a) do
|
12
|
+
Morpher.compile(body_a)
|
13
|
+
end
|
14
|
+
|
15
|
+
let(:evaluator_b) do
|
16
|
+
Morpher.compile(body_b)
|
17
|
+
end
|
18
|
+
|
19
|
+
context 'transitive' do
|
20
|
+
|
21
|
+
let(:body_a) do
|
22
|
+
s(:guard, s(:primitive, String))
|
23
|
+
end
|
24
|
+
|
25
|
+
let(:body_b) do
|
26
|
+
s(:guard, s(:primitive, String))
|
27
|
+
end
|
28
|
+
|
29
|
+
let(:valid_input) { 'foo' }
|
30
|
+
let(:expected_output) { 'foo' }
|
31
|
+
let(:invalid_input) { :foo }
|
32
|
+
|
33
|
+
let(:expected_exception) do
|
34
|
+
Morpher::Evaluator::Transformer::TransformError.new(object.body.first, invalid_input)
|
35
|
+
end
|
36
|
+
|
37
|
+
include_examples 'transitive evaluator'
|
38
|
+
|
39
|
+
context 'with invalid input' do
|
40
|
+
specify '#evaluation' do
|
41
|
+
evaluation = object.evaluation(invalid_input)
|
42
|
+
expect(evaluation.evaluations.length).to eql(1)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
context 'intransitive' do
|
48
|
+
|
49
|
+
let(:valid_input) { { 'foo' => 'bar' } }
|
50
|
+
let(:invalid_input) { {} }
|
51
|
+
let(:expected_output) { true }
|
52
|
+
|
53
|
+
let(:body_a) do
|
54
|
+
s(:key_fetch, 'foo')
|
55
|
+
end
|
56
|
+
|
57
|
+
let(:body_b) do
|
58
|
+
s(:primitive, String)
|
59
|
+
end
|
60
|
+
|
61
|
+
let(:expected_exception) do
|
62
|
+
Morpher::Evaluator::Transformer::TransformError.new(object.body.first, invalid_input)
|
63
|
+
end
|
64
|
+
|
65
|
+
include_examples 'intransitive evaluator'
|
66
|
+
|
67
|
+
context '#evaluation' do
|
68
|
+
subject { object.evaluation(valid_input) }
|
69
|
+
|
70
|
+
let(:evaluations) do
|
71
|
+
[
|
72
|
+
evaluator_a.evaluation(valid_input),
|
73
|
+
evaluator_b.evaluation('bar')
|
74
|
+
]
|
75
|
+
end
|
76
|
+
|
77
|
+
context 'with valid input' do
|
78
|
+
it 'returns evaluation' do
|
79
|
+
should eql(
|
80
|
+
Morpher::Evaluation::Nary.new(
|
81
|
+
input: valid_input,
|
82
|
+
evaluator: object,
|
83
|
+
evaluations: evaluations,
|
84
|
+
output: expected_output,
|
85
|
+
success: true
|
86
|
+
)
|
87
|
+
)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
describe Morpher::Evaluator::Transformer::Coerce::ParseInt do
|
2
|
+
let(:object) { described_class.new(base) }
|
3
|
+
|
4
|
+
context 'base 16' do
|
5
|
+
let(:base) { 16 }
|
6
|
+
|
7
|
+
include_examples 'transitive evaluator'
|
8
|
+
|
9
|
+
let(:valid_input) { 'ff' }
|
10
|
+
let(:invalid_input) { '0xfg' }
|
11
|
+
let(:expected_output) { 255 }
|
12
|
+
end
|
13
|
+
|
14
|
+
context 'base 10' do
|
15
|
+
let(:base) { 10 }
|
16
|
+
|
17
|
+
include_examples 'transitive evaluator'
|
18
|
+
|
19
|
+
let(:valid_input) { '100' }
|
20
|
+
let(:invalid_input) { '100.0' }
|
21
|
+
let(:expected_output) { 100 }
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
describe Morpher::Evaluator::Transformer::Custom do
|
2
|
+
let(:object) { described_class.new(param) }
|
3
|
+
|
4
|
+
let(:param) { [a, b] }
|
5
|
+
|
6
|
+
let(:a) { ->(_input) { :a } }
|
7
|
+
let(:b) { ->(_input) { :b } }
|
8
|
+
|
9
|
+
let(:valid_input) { double('Input') }
|
10
|
+
let(:expected_output) { :a }
|
11
|
+
|
12
|
+
include_examples 'transforming evaluator on valid input'
|
13
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
describe Morpher::Evaluator::Transformer::Domain::AttributeAccessors do
|
2
|
+
|
3
|
+
let(:model) do
|
4
|
+
Class.new do
|
5
|
+
include Equalizer.new(:foo, :bar)
|
6
|
+
|
7
|
+
attr_accessor :foo, :bar
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
let(:param) do
|
12
|
+
described_class::Param.new(model, %i[foo bar])
|
13
|
+
end
|
14
|
+
|
15
|
+
describe Morpher::Evaluator::Transformer::Domain::AttributeAccessors::Dump do
|
16
|
+
|
17
|
+
let(:object) { described_class::Dump.new(param) }
|
18
|
+
|
19
|
+
let(:expected_output) { { foo: :foo, bar: :bar } }
|
20
|
+
|
21
|
+
let(:valid_input) do
|
22
|
+
object = model.allocate
|
23
|
+
object.foo = :foo
|
24
|
+
object.bar = :bar
|
25
|
+
object
|
26
|
+
end
|
27
|
+
|
28
|
+
include_examples 'transforming evaluator on valid input'
|
29
|
+
include_examples 'transitive evaluator'
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
describe Morpher::Evaluator::Transformer::Domain::AttributeAccessors::Load do
|
34
|
+
let(:object) { described_class::Load.new(param) }
|
35
|
+
|
36
|
+
let(:valid_input) { { foo: :foo, bar: :bar } }
|
37
|
+
|
38
|
+
let(:expected_output) do
|
39
|
+
object = model.allocate
|
40
|
+
object.foo = :foo
|
41
|
+
object.bar = :bar
|
42
|
+
object
|
43
|
+
end
|
44
|
+
|
45
|
+
include_examples 'transforming evaluator on valid input'
|
46
|
+
include_examples 'transitive evaluator'
|
47
|
+
end
|
48
|
+
end
|