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
| @@ -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
         |