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.

Files changed (106) hide show
  1. checksums.yaml +7 -0
  2. data/.circle.yml +6 -0
  3. data/.gitignore +5 -0
  4. data/.rspec +4 -0
  5. data/.rubocop.yml +8 -0
  6. data/Changelog.md +60 -0
  7. data/Gemfile +3 -0
  8. data/LICENSE +20 -0
  9. data/README.md +56 -0
  10. data/Rakefile +95 -0
  11. data/circle.yml +7 -0
  12. data/config/devtools.yml +2 -0
  13. data/config/flay.yml +3 -0
  14. data/config/flog.yml +2 -0
  15. data/config/heckle.yml +3 -0
  16. data/config/mutant.yml +8 -0
  17. data/config/reek.yml +109 -0
  18. data/config/rubocop.yml +138 -0
  19. data/config/yardstick.yml +2 -0
  20. data/examples/README.md +13 -0
  21. data/examples/a.rb +25 -0
  22. data/examples/b.rb +35 -0
  23. data/lib/morpher.rb +111 -0
  24. data/lib/morpher/compiler.rb +17 -0
  25. data/lib/morpher/compiler/emitter.rb +82 -0
  26. data/lib/morpher/compiler/error.rb +84 -0
  27. data/lib/morpher/compiler/evaluator.rb +63 -0
  28. data/lib/morpher/compiler/evaluator/emitter.rb +224 -0
  29. data/lib/morpher/compiler/preprocessor.rb +29 -0
  30. data/lib/morpher/compiler/preprocessor/emitter.rb +54 -0
  31. data/lib/morpher/compiler/preprocessor/emitter/anima.rb +69 -0
  32. data/lib/morpher/compiler/preprocessor/emitter/boolean.rb +31 -0
  33. data/lib/morpher/compiler/preprocessor/emitter/key.rb +87 -0
  34. data/lib/morpher/compiler/preprocessor/emitter/noop.rb +45 -0
  35. data/lib/morpher/compiler/preprocessor/emitter/param.rb +50 -0
  36. data/lib/morpher/evaluation.rb +118 -0
  37. data/lib/morpher/evaluator.rb +40 -0
  38. data/lib/morpher/evaluator/binary.rb +46 -0
  39. data/lib/morpher/evaluator/nary.rb +97 -0
  40. data/lib/morpher/evaluator/nullary.rb +92 -0
  41. data/lib/morpher/evaluator/nullary/parameterized.rb +48 -0
  42. data/lib/morpher/evaluator/predicate.rb +22 -0
  43. data/lib/morpher/evaluator/predicate/boolean.rb +76 -0
  44. data/lib/morpher/evaluator/predicate/contradiction.rb +36 -0
  45. data/lib/morpher/evaluator/predicate/eql.rb +50 -0
  46. data/lib/morpher/evaluator/predicate/negation.rb +52 -0
  47. data/lib/morpher/evaluator/predicate/primitive.rb +49 -0
  48. data/lib/morpher/evaluator/predicate/tautology.rb +36 -0
  49. data/lib/morpher/evaluator/transformer.rb +75 -0
  50. data/lib/morpher/evaluator/transformer/attribute.rb +25 -0
  51. data/lib/morpher/evaluator/transformer/block.rb +81 -0
  52. data/lib/morpher/evaluator/transformer/coerce.rb +166 -0
  53. data/lib/morpher/evaluator/transformer/custom.rb +34 -0
  54. data/lib/morpher/evaluator/transformer/domain.rb +86 -0
  55. data/lib/morpher/evaluator/transformer/domain/attribute_accessors.rb +60 -0
  56. data/lib/morpher/evaluator/transformer/domain/attribute_hash.rb +52 -0
  57. data/lib/morpher/evaluator/transformer/domain/instance_variables.rb +60 -0
  58. data/lib/morpher/evaluator/transformer/domain/param.rb +54 -0
  59. data/lib/morpher/evaluator/transformer/guard.rb +62 -0
  60. data/lib/morpher/evaluator/transformer/hash_transform.rb +149 -0
  61. data/lib/morpher/evaluator/transformer/input.rb +37 -0
  62. data/lib/morpher/evaluator/transformer/key.rb +86 -0
  63. data/lib/morpher/evaluator/transformer/map.rb +100 -0
  64. data/lib/morpher/evaluator/transformer/merge.rb +25 -0
  65. data/lib/morpher/evaluator/transformer/static.rb +27 -0
  66. data/lib/morpher/evaluator/unary.rb +79 -0
  67. data/lib/morpher/node_helpers.rb +19 -0
  68. data/lib/morpher/printer.rb +233 -0
  69. data/lib/morpher/printer/mixin.rb +58 -0
  70. data/lib/morpher/registry.rb +51 -0
  71. data/lib/morpher/type_lookup.rb +51 -0
  72. data/morpher.gemspec +29 -0
  73. data/spec/integration_spec.rb +184 -0
  74. data/spec/rcov.opts +7 -0
  75. data/spec/shared/evaluator_behavior.rb +155 -0
  76. data/spec/spec_helper.rb +36 -0
  77. data/spec/support/ice_nine_config.rb +8 -0
  78. data/spec/support/let_mock_helper.rb +8 -0
  79. data/spec/support/strip_helper.rb +12 -0
  80. data/spec/unit/morpher/compiler/preprocessor_spec.rb +46 -0
  81. data/spec/unit/morpher/evaluator/nullary/parameterized_spec.rb +25 -0
  82. data/spec/unit/morpher/evaluator/predicate/boolean/and_spec.rb +11 -0
  83. data/spec/unit/morpher/evaluator/predicate/boolean/or_spec.rb +26 -0
  84. data/spec/unit/morpher/evaluator/predicate/boolean/xor_spec.rb +26 -0
  85. data/spec/unit/morpher/evaluator/predicate/contrandiction_spec.rb +7 -0
  86. data/spec/unit/morpher/evaluator/predicate/eql_spec.rb +11 -0
  87. data/spec/unit/morpher/evaluator/predicate/negation_spec.rb +10 -0
  88. data/spec/unit/morpher/evaluator/predicate/primitive_spec.rb +17 -0
  89. data/spec/unit/morpher/evaluator/predicate/tautology_spec.rb +7 -0
  90. data/spec/unit/morpher/evaluator/transformer/attribute_spec.rb +9 -0
  91. data/spec/unit/morpher/evaluator/transformer/block_spec.rb +92 -0
  92. data/spec/unit/morpher/evaluator/transformer/coerce/parse_int_spec.rb +23 -0
  93. data/spec/unit/morpher/evaluator/transformer/custom_spec.rb +13 -0
  94. data/spec/unit/morpher/evaluator/transformer/domain/attribute_accessors_spec.rb +48 -0
  95. data/spec/unit/morpher/evaluator/transformer/domain/attribute_hash_spec.rb +40 -0
  96. data/spec/unit/morpher/evaluator/transformer/domain/instance_variables_spec.rb +47 -0
  97. data/spec/unit/morpher/evaluator/transformer/guard_spec.rb +12 -0
  98. data/spec/unit/morpher/evaluator/transformer/hash_transform_spec.rb +47 -0
  99. data/spec/unit/morpher/evaluator/transformer/input_spec.rb +11 -0
  100. data/spec/unit/morpher/evaluator/transformer/map_spec.rb +25 -0
  101. data/spec/unit/morpher/evaluator/transformer/static_spec.rb +10 -0
  102. data/spec/unit/morpher/evaluator_spec.rb +15 -0
  103. data/spec/unit/morpher/printer_spec.rb +21 -0
  104. data/spec/unit/morpher/registry_spec.rb +11 -0
  105. data/spec/unit/morpher_spec.rb +53 -0
  106. metadata +302 -0
@@ -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,8 @@
1
+ require 'ice_nine'
2
+
3
+ module IceNine
4
+ class Freezer
5
+ class RSpec < NoFreeze
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,8 @@
1
+ module LetMockHelper
2
+ def let_mock(name, &block)
3
+ let(name) do
4
+ stubs = block ? instance_exec(double, &block) : {}
5
+ double(name.to_s.capitalize, stubs)
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,12 @@
1
+ module StripHelper
2
+ def strip(text)
3
+ return text if text.empty?
4
+ lines = text.lines
5
+ match = /\A[ ]*/.match(lines.first)
6
+ range = match[0].length..-1
7
+ source = lines.map do |line|
8
+ line[range]
9
+ end.join
10
+ source.chomp << "\n"
11
+ end
12
+ 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,7 @@
1
+ describe Morpher::Evaluator::Predicate::Contradiction do
2
+ let(:object) { described_class.new }
3
+ let(:valid_input) { double('Input') }
4
+ let(:expected_output) { false }
5
+
6
+ include_examples 'transforming evaluator on valid input'
7
+ 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,7 @@
1
+ describe Morpher::Evaluator::Predicate::Tautology do
2
+ let(:object) { described_class.new }
3
+ let(:valid_input) { double('Input') }
4
+ let(:expected_output) { true }
5
+
6
+ include_examples 'transforming evaluator on valid input'
7
+ 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