Ивайло обнови решението на 13.11.2012 11:13 (преди почти 13 години)
+
+module Interpetator
+
+  # ======================
+  # Environment
+  # ======================
+
+  module Core                   #; end
+  end
+
+  class Number                  #; end
+  end
+  class Variable                #; end
+  end
+
+  class Expression              #; end
+  end
+  class Unary     < Expression  #; end
+  end
+  class Binary    < Expression  #; end
+  end
+
+  # ======================
+  # Parse data
+  # ======================
+
+  module PARSE
+    TYPES     = {
+      :number     => Interpetator::Number    ,
+      :variable   => Interpetator::Variable,
+    }
+
+    OPERATORS = {
+      :unary      => {
+        :+        => Interpetator::Unary,
+        :-        => Interpetator::Unary,
+        :sin      => Interpetator::Unary,
+        :cos      => Interpetator::Unary,
+      },
+
+      :binary     => {
+        :+        => Interpetator::Binary,
+        :-        => Interpetator::Binary,
+        :*        => Interpetator::Binary,
+      }
+    }
+  end
+
+  # ======================
+  # Other data
+  # ======================
+
+  LENGTH_TO_OPERATOR_TYPE = {
+    2 => :unary ,
+    3 => :binary,
+  }
+
+  # ======================
+  # Builder
+  # ======================
+
+  def self.build(syntax_tree)
+    if PARSE::TYPES.has_key? syntax_tree.first
+      type  = PARSE::TYPES[syntax_tree.first]
+      value = syntax_tree.last
+
+      return type.new(value)
+    end
+
+    operation           = syntax_tree                   .fetch 0
+    operation_type      = LENGTH_TO_OPERATOR_TYPE       .fetch syntax_tree.length
+    operation_type_list = PARSE::OPERATORS              .fetch operation_type
+    operation_class     = operation_type_list           .fetch operation
+
+    operands = syntax_tree.slice(1..-1).map {|operand| build operand}
+
+    operation_class.new operation, *operands
+  end
+
+  # ======================
+  # Core
+  # ======================
+
+  # common functionality for all the atoms ...
+
+  module Core
+    def evaluate(scope = {})
+      calculate(scope).value
+    end
+
+    def calculate(scope = {})
+      self
+    end
+
+    def simplify
+      self
+    end
+  end
+
+  # ======================
+  # Operators
+  # ======================
+
+  module BaseOperators
+    def -@
+      self.class.new self.value.send :-@
+    end
+
+    def +@
+      self.class.new self.value.send :+@
+    end
+
+    def +(other)
+      self.class.new self.value + other.value
+    end
+
+    def -(other)
+      self.class.new self.value - other.value
+    end
+
+    def *(other)
+      self.class.new self.value * other.value
+    end
+  end
+
+  module MathOperators
+    def sin
+      self.class.new Math.sin @value
+    end
+
+    def cos
+      self.class.new Math.cos @value
+    end
+  end
+
+  # ======================
+  # Variables
+  # ======================
+
+  class Variable
+    include Core
+
+    attr_reader :name
+
+    def initialize(name)
+      @name = name
+    end
+
+    def calculate(scope = {})
+      unless scope[@name]
+        throw 'Undefined variable "' + @name.to_s + '"'
+      end
+
+      Number.new scope[@name]
+    end
+
+    def derive(var)
+      Number.new var == @name ? 1 : 0
+    end
+
+    def ==(other)
+      other.class == self.class and
+      other.name  == self.name
+    end
+
+    def to_s
+      @name.to_s
+    end
+  end
+
+  # ======================
+  # Types
+  # ======================
+
+  class Number
+    include Core
+    include BaseOperators
+    include MathOperators
+
+    attr_reader :value
+
+    def initialize(value)
+      @value = value
+    end
+
+    def derive(var)
+      self.class.new 0
+    end
+
+    def ==(other)
+      other.class == self.class and
+      other.value == self.value
+    end
+
+    def to_s
+      @value.to_s
+    end
+  end
+
+  # ======================
+  # Expressions
+  # ======================
+
+  class Expression
+    include Core
+  end
+
+  class Unary < Expression
+    attr_reader :operator, :operand
+
+    OPERATORS_MAP = {
+      :+ => :+@,
+      :- => :-@,
+    }
+
+    DERIVE_RULES = {
+      :+@ => lambda do |operand, var|
+        Unary.new(:+@, operand.derive(var)).simplify
+      end,
+
+      :-@ => lambda do |operand, var|
+        Unary.new(:-@, operand.derive(var)).simplify
+      end,
+
+      :sin => lambda do |operand, var|
+        Binary.new(:+, operand.derive(var), Unary.new(:cos, operand)).simplify
+      end,
+
+      :cos => lambda do |operator, var|
+        Binary.new(:+, operand.derive(var), Unary.new(:sin, operand)).simplify
+      end,
+    }
+
+    SIMPLIFICATIONS = {
+      :number => lambda do |expression|
+        return expression.calculate if expression.operand.is_a? Number
+      end,
+
+      :+      => lambda do |expression|
+        return expression.operand if expression.operator == :+
+      end,
+    }
+
+    def initialize(operator, operand)
+      @operator = OPERATORS_MAP.fetch operator, operator
+      @operand  = operand
+    end
+
+    def ==(other)
+      other.class   == self.class    and
+
+      other.operator== self.operator and
+      other.operand == self.operand
+    end
+
+    def calculate(scope = {})
+      @operand.calculate(scope).send @operator
+    end
+
+    def simplify
+      SIMPLIFICATIONS.each do |name, try_to_simplify|
+        simplified = try_to_simplify.call(self)
+
+        return simplified.simplify if simplified
+      end
+
+      self.class.new @operator, @operand.simplify
+    end
+
+    def derive(var)
+      DERIVE_RULES[operator].call operand, var
+    end
+
+    def to_s
+      "#{operator.to_s}(#{operand.to_s})"
+    end
+  end
+
+  class Binary < Expression
+    attr_reader :operator, :left_operand, :right_operand
+
+    DERIVE_RULES = {
+      :+ => lambda do |left_operand, right_operand, var|
+        Binary.new(:+, left_operand.derive(var), right_operand.derive(var)).simplify
+      end,
+
+      :- => lambda do |left_operand, right_operand, var|
+        DERIVE_RULES[:+].call left_operand, Unary.new(:-, right_operand)
+      end,
+
+      :* => lambda do |left_operand, right_operand, var|
+        Binary.new(
+          :+,
+          Binary.new(:*, left_operand.derive(var), right_operand),
+          Binary.new(:*, left_operand, right_operand.derive(var)),
+        ).simplify
+      end,
+    }
+
+    SIMPLIFICATIONS = {
+      :numbers          => lambda do |expression|
+        return expression.calculate if
+          expression.left_operand .is_a?  Number and
+          expression.right_operand.is_a?  Number
+      end,
+
+      :variables          => lambda do |expression|
+        return Number.new(0) if
+          expression.left_operand .is_a?  Variable and
+          expression.right_operand.is_a?  Variable and
+          expression.operator == :-
+      end,
+
+      :number_expression  => lambda do |expression|
+        test_num = Number.new 13
+        left  = expression.left_operand
+        right = expression.right_operand
+
+        unless left.is_a? Number and not right.is_a? Number
+          return
+        end
+
+        return case Binary.new( expression.operator, left, test_num ).calculate
+          when test_num then right
+          when left     then left
+        end
+      end,
+
+      :expression_number  => lambda do |expression|
+        return SIMPLIFICATIONS[:number_expression].call Binary.new(
+          expression.operator, expression.right_operand, expression.left_operand
+        )
+      end,
+    }
+
+    def initialize(operator, left_operand, right_operand)
+      @operator       = operator
+      @left_operand   = left_operand
+      @right_operand  = right_operand
+    end
+
+    def ==(other)
+      other.class         == self.class         and
+
+      other.operator      == self.operator      and
+      other.left_operand  == self.left_operand  and
+      other.right_operand == self.right_operand
+    end
+
+    def calculate(scope = {})
+      @left_operand.calculate(scope).send @operator, @right_operand.calculate(scope)
+    end
+
+    def simplify
+      SIMPLIFICATIONS.each do |name, try_to_simplify|
+        simplified = try_to_simplify.call(self)
+
+        return simplified.simplify if simplified
+      end
+
+      simplified = self.class.new(
+        @operator,
+        @left_operand .simplify,
+        @right_operand.simplify,
+      )
+      if simplified == self then simplified else simplified.simplify end
+    end
+
+    def derive(var)
+      DERIVE_RULES[operator].call(left_operand, right_operand, var).simplify
+    end
+
+    def to_s
+      "(#{left_operand.to_s} #{operator.to_s} #{right_operand.to_s})"
+    end
+  end
+end
+
+Expr = Interpetator
