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,37 @@
1
+ module Morpher
2
+ class Evaluator
3
+ class Transformer
4
+
5
+ # Identity transformer which always returns +input+
6
+ class Input < self
7
+ include Nullary, Transitive
8
+
9
+ register :input
10
+
11
+ # Call evaluator with input
12
+ #
13
+ # @param [Object] input
14
+ #
15
+ # @return [Object]
16
+ # always returns input
17
+ #
18
+ # @api private
19
+ #
20
+ def call(input)
21
+ input
22
+ end
23
+
24
+ # Return inverse evaluator
25
+ #
26
+ # @return [Evaluator::Transformer]
27
+ #
28
+ # @api private
29
+ #
30
+ def inverse
31
+ self
32
+ end
33
+
34
+ end # Input
35
+ end # Transformer
36
+ end # Evaluator
37
+ end # Morpher
@@ -0,0 +1,86 @@
1
+ module Morpher
2
+ class Evaluator
3
+ class Transformer
4
+ # Abstract namespace class for evaluators operating on hash keys
5
+ class Key < self
6
+ include AbstractType, Nullary::Parameterized, Intransitive
7
+
8
+ # Evaluator for dumping hash keys
9
+ class Dump < self
10
+
11
+ register :key_dump
12
+
13
+ # Call evaluator
14
+ #
15
+ # @param [Object] object
16
+ #
17
+ # @return [Array]
18
+ #
19
+ # @api private
20
+ #
21
+ def call(object)
22
+ [param, object]
23
+ end
24
+
25
+ # Return inverse evaluator
26
+ #
27
+ # @return [Fetch]
28
+ #
29
+ # @api private
30
+ #
31
+ def inverse
32
+ Fetch.new(param)
33
+ end
34
+
35
+ end # Dump
36
+
37
+ # Evaluator to fetch a specific hash key
38
+ class Fetch < self
39
+
40
+ register :key_fetch
41
+
42
+ # Call evaluator
43
+ #
44
+ # @param [Hash] object
45
+ #
46
+ # @return [Object]
47
+ #
48
+ # @api private
49
+ #
50
+ def call(object)
51
+ object.fetch(param) do
52
+ fail TransformError.new(self, object)
53
+ end
54
+ end
55
+
56
+ # Return evaluation
57
+ #
58
+ # @param [Object] input
59
+ #
60
+ # @return [Evaluation]
61
+ #
62
+ # @api private
63
+ #
64
+ def evaluation(input)
65
+ output = input.fetch(param) do
66
+ return evaluation_error(input)
67
+ end
68
+
69
+ evaluation_success(input, output)
70
+ end
71
+
72
+ # Return inverse evaluator
73
+ #
74
+ # @return [Dump]
75
+ #
76
+ # @api private
77
+ #
78
+ def inverse
79
+ Dump.new(param)
80
+ end
81
+
82
+ end # Fetch
83
+ end # Key
84
+ end # Transformer
85
+ end # Evaluator
86
+ end # Morpher
@@ -0,0 +1,100 @@
1
+ module Morpher
2
+ class Evaluator
3
+ class Transformer
4
+
5
+ # Transformer over each element in an enumerable
6
+ class Map < self
7
+ include Unary
8
+
9
+ register :map
10
+
11
+ # Test if evaluator is transitive
12
+ #
13
+ # @return [true]
14
+ # if evaluator is transitive
15
+ #
16
+ # @return [false]
17
+ # otherwise
18
+ #
19
+ # @api private
20
+ #
21
+ def transitive?
22
+ operand.transitive?
23
+ end
24
+
25
+ # Call evaluator
26
+ #
27
+ # @param [Enumerable#map] input
28
+ #
29
+ # @return [Enumerable]
30
+ # if input evaluates true under predicate
31
+ #
32
+ # @raise [TransformError]
33
+ # otherwise
34
+ #
35
+ # @api private
36
+ #
37
+ def call(input)
38
+ input.map(&operand.method(:call))
39
+ end
40
+
41
+ # Return evaluation
42
+ #
43
+ # @param [Enumerable#map] input
44
+ #
45
+ # @return [Evaluation]
46
+ #
47
+ # @api private
48
+ #
49
+ # rubocop:disable MethodLength
50
+ #
51
+ def evaluation(input)
52
+ evaluations = input.each_with_object([]) do |item, aggregate|
53
+ evaluation = operand.evaluation(item)
54
+ aggregate << evaluation
55
+ unless evaluation.success?
56
+ return evaluation_error(input, aggregate)
57
+ end
58
+ end
59
+
60
+ Evaluation::Nary.success(
61
+ evaluator: self,
62
+ input: input,
63
+ output: evaluations.map(&:output),
64
+ evaluations: evaluations
65
+ )
66
+ end
67
+
68
+ # Return inverse evaluator
69
+ #
70
+ # @return [Evaluator::Transformer]
71
+ #
72
+ # @api private
73
+ #
74
+ def inverse
75
+ self.class.new(operand.inverse)
76
+ end
77
+
78
+ private
79
+
80
+ # Return evaluation error
81
+ #
82
+ # @param [Object] input
83
+ # @param [Array<Evaluation>] evaluations
84
+ #
85
+ # @return [Evaluation]
86
+ #
87
+ # @api private
88
+ #
89
+ def evaluation_error(input, evaluations)
90
+ Evaluation::Nary.error(
91
+ evaluator: self,
92
+ input: input,
93
+ evaluations: evaluations
94
+ )
95
+ end
96
+
97
+ end # Guard
98
+ end # Transformer
99
+ end # Evaluator
100
+ end # Morpher
@@ -0,0 +1,25 @@
1
+ module Morpher
2
+ class Evaluator
3
+ class Transformer
4
+ # Transformer to merge input into defaults
5
+ class Merge < self
6
+ include Intransitive, Nullary::Parameterized
7
+
8
+ register :merge
9
+
10
+ # Call evaluator for input
11
+ #
12
+ # @param [Object] input
13
+ #
14
+ # @return [Object] output
15
+ #
16
+ # @api private
17
+ #
18
+ def call(input)
19
+ param.merge(input)
20
+ end
21
+
22
+ end # Merge
23
+ end # Transformer
24
+ end # Evaluator
25
+ end # Morpher
@@ -0,0 +1,27 @@
1
+ module Morpher
2
+ class Evaluator
3
+ class Transformer
4
+
5
+ # Transformer that always returns the passed +param+
6
+ class Static < self
7
+ include Nullary::Parameterized, Intransitive
8
+
9
+ register :static
10
+
11
+ # Call evaluator with input
12
+ #
13
+ # @param [Object] _input
14
+ #
15
+ # @return [Object]
16
+ # alwasys returns the param
17
+ #
18
+ # @api private
19
+ #
20
+ def call(_input)
21
+ param
22
+ end
23
+
24
+ end # Static
25
+ end # Transformer
26
+ end # Evaluator
27
+ end # Morpher
@@ -0,0 +1,79 @@
1
+ module Morpher
2
+ class Evaluator
3
+
4
+ # Mixin for unary evaluators
5
+ module Unary
6
+ CONCORD = Concord::Public.new(:operand)
7
+
8
+ PRINTER = lambda do |_|
9
+ name
10
+ indent do
11
+ visit(:operand)
12
+ end
13
+ end
14
+
15
+ # Return node
16
+ #
17
+ # @return [AST::Node]
18
+ #
19
+ # @api private
20
+ #
21
+ def node
22
+ s(type, operand.node)
23
+ end
24
+
25
+ private
26
+
27
+ # Return success evaluation for input
28
+ #
29
+ # @param [Object] input
30
+ #
31
+ # @return [Evalation::Unary]
32
+ #
33
+ # @api private
34
+ #
35
+ def evaluation_success(input, operand_evaluation, output)
36
+ Evaluation::Unary.success(
37
+ evaluator: self,
38
+ input: input,
39
+ operand_evaluation: operand_evaluation,
40
+ output: output
41
+ )
42
+ end
43
+
44
+ # Return error evaluation for input
45
+ #
46
+ # @param [Object] input
47
+ #
48
+ # @return [Evalation::Unary]
49
+ #
50
+ # @api private
51
+ #
52
+ def evaluation_error(input, operand_evaluation)
53
+ Evaluation::Unary.error(
54
+ evaluator: self,
55
+ input: input,
56
+ operand_evaluation: operand_evaluation
57
+ )
58
+ end
59
+
60
+ private
61
+
62
+ # Hook called when module gets included
63
+ #
64
+ # @return [undefined]
65
+ #
66
+ # @api private
67
+ #
68
+ def self.included(descendant)
69
+ descendant.class_eval do
70
+ include CONCORD
71
+ printer(&PRINTER)
72
+ end
73
+ end
74
+ private_class_method :included
75
+
76
+ end # Unary
77
+
78
+ end # Evaluator
79
+ end # Morpher
@@ -0,0 +1,19 @@
1
+ module Morpher
2
+ # Node helpers
3
+ module NodeHelpers
4
+
5
+ # Build node
6
+ #
7
+ # @param [Symbol] type
8
+ #
9
+ # @return [Parser::AST::Node]
10
+ #
11
+ # @api private
12
+ #
13
+ def s(type, *children)
14
+ AST::Node.new(type, children)
15
+ end
16
+ module_function :s
17
+
18
+ end # NodeHelpers
19
+ end # Morpher
@@ -0,0 +1,233 @@
1
+ module Morpher
2
+
3
+ # Evaluation and Evaluator pretty printer
4
+ class Printer
5
+ include Adamantium::Flat, Concord.new(:object, :output, :indent_level)
6
+
7
+ INDENT = ' '.freeze
8
+
9
+ REGISTRY = {}
10
+
11
+ # Run pretty printer on object
12
+ #
13
+ # @param [Object] object
14
+ # the object to be pretty printed
15
+ # @param [IO] output
16
+ # the output to write to
17
+ # @param [Fixnum] indent_level
18
+ # the current indentation level
19
+ #
20
+ # @return [self]
21
+ #
22
+ # @api private
23
+ #
24
+ def self.run(object, output, indent_level = 0)
25
+ printer = new(object, output, indent_level)
26
+ block = lookup(object)
27
+ printer.instance_eval(&block)
28
+ end
29
+
30
+ # Perform type lookup
31
+ #
32
+ # FIXME: Instanciate type lookup once and allow caching.
33
+ #
34
+ # @param [Evaluation, Evaluator] object
35
+ #
36
+ # @return [Proc]
37
+ # if found
38
+ #
39
+ # @raise [PrinterMissingException]
40
+ # otherwise
41
+ #
42
+ # @api private
43
+ #
44
+ def self.lookup(object)
45
+ TypeLookup.new(REGISTRY).call(object)
46
+ end
47
+
48
+ private
49
+
50
+ # Visit a child
51
+ #
52
+ # @param [Node] child
53
+ #
54
+ # @api private
55
+ #
56
+ # @return [undefined]
57
+ #
58
+ def visit_child(child)
59
+ self.class.run(child, output, indent_level.succ)
60
+ end
61
+
62
+ # Visit a child by name
63
+ #
64
+ # @param [Symbol] name
65
+ # the attribute name of the child to visit
66
+ #
67
+ # @return [undefined]
68
+ #
69
+ # @api private
70
+ #
71
+ def visit(name)
72
+ child = object.public_send(name)
73
+ child_label(name)
74
+ visit_child(child)
75
+ end
76
+
77
+ # Visit many children
78
+ #
79
+ # @param [Symbol] name
80
+ # the name of the collection attribute with children to visit
81
+ #
82
+ # @return [undefined]
83
+ #
84
+ # @api private
85
+ #
86
+ def visit_many(name)
87
+ children = object.public_send(name)
88
+ child_label(name)
89
+ children.each do |child|
90
+ visit_child(child)
91
+ end
92
+ end
93
+
94
+ # Print attribute class
95
+ #
96
+ # @param [Symbol] name
97
+ #
98
+ # @return [undefined]
99
+ #
100
+ # @api private
101
+ #
102
+ def attribute_class(name)
103
+ label_value(name, object.public_send(name).class)
104
+ end
105
+
106
+ # Print inspected attribute value with label
107
+ #
108
+ # @return [undefined]
109
+ #
110
+ # @api private
111
+ #
112
+ def attribute(name)
113
+ label_value(name, object.public_send(name))
114
+ end
115
+
116
+ # Print attributes of object
117
+ #
118
+ # @return [undefined]
119
+ #
120
+ # @api private
121
+ #
122
+ def attributes(*names)
123
+ names.each do |name|
124
+ attribute(name)
125
+ end
126
+ end
127
+
128
+ # Print name of object
129
+ #
130
+ # @return [undefined]
131
+ #
132
+ # @api private
133
+ #
134
+ def name
135
+ puts(object.class.name)
136
+ end
137
+
138
+ # Return string indented with current level
139
+ #
140
+ # @param [String] content
141
+ #
142
+ # @return [String]
143
+ #
144
+ # @api private
145
+ #
146
+ def indented(content)
147
+ "#{indentation_prefix}#{content}"
148
+ end
149
+
150
+ # Return indentation prefix
151
+ #
152
+ # @return [String]
153
+ #
154
+ # @api private
155
+ #
156
+ def indentation_prefix
157
+ INDENT * indent_level
158
+ end
159
+ memoize :indentation_prefix
160
+
161
+ # Add content to output at current indentation and close line
162
+ #
163
+ # @param [String] content
164
+ #
165
+ # @return [undefined]
166
+ #
167
+ # @api private
168
+ #
169
+ def puts(string)
170
+ output.puts(indented(string))
171
+ end
172
+
173
+ # Write content to output at current indentation
174
+ #
175
+ # @param [String] content
176
+ #
177
+ # @return [undefined]
178
+ #
179
+ # @api private
180
+ #
181
+ def write(string)
182
+ output.write(indented(string))
183
+ end
184
+
185
+ # Write child label to output at current indentation
186
+ #
187
+ # @param [String] label
188
+ #
189
+ # @return [undefined]
190
+ #
191
+ # @api private
192
+ #
193
+ def child_label(string)
194
+ puts("#{string}:")
195
+ end
196
+
197
+ # Call block inside indented context
198
+ #
199
+ # @return [undefined]
200
+ #
201
+ # @api private
202
+ #
203
+ def indent(&block)
204
+ printer = new(object, output, indent_level.succ)
205
+ printer.instance_eval(&block)
206
+ end
207
+
208
+ # Return new printer
209
+ #
210
+ # @return [Printer]
211
+ #
212
+ # @api private
213
+ #
214
+ def new(*arguments)
215
+ self.class.new(*arguments)
216
+ end
217
+
218
+ # Print label with value
219
+ #
220
+ # @param [String] label
221
+ # @param [Object] value
222
+ #
223
+ # @return [undefined]
224
+ #
225
+ # @api private
226
+ #
227
+ def label_value(label, value)
228
+ write("#{label}: ")
229
+ output.puts(value.inspect)
230
+ end
231
+
232
+ end # Printer
233
+ end # Morpher