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