summaryrefslogtreecommitdiff
path: root/deployment_scripts/puppet/files/embedded/lib/ruby/gems/2.3.0/gems/dentaku-2.0.9/lib/dentaku/ast/arithmetic.rb
blob: 74ac0641949d5d6ffcc87b7e2ae6f939e4a3c85b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
require_relative './operation'
require 'bigdecimal'
require 'bigdecimal/util'

module Dentaku
  module AST
    class Arithmetic < Operation
      def initialize(*)
        super
        unless valid_node?(left) && valid_node?(right)
          fail ParseError, "#{ self.class } requires numeric operands"
        end
      end

      def type
        :numeric
      end

      def value(context={})
        l = cast(left.value(context))
        r = cast(right.value(context))
        l.public_send(operator, r)
      end

      private

      def cast(value, prefer_integer=true)
        validate_numeric(value)
        v = BigDecimal.new(value, Float::DIG+1)
        v = v.to_i if prefer_integer && v.frac.zero?
        v
      end

      def valid_node?(node)
        node && (node.dependencies.any? || node.type == :numeric)
      end

      def validate_numeric(value)
        Float(value)
      rescue ::ArgumentError, ::TypeError
        fail Dentaku::ArgumentError, "#{ self.class } requires numeric operands"
      end
    end

    class Addition < Arithmetic
      def operator
        :+
      end

      def self.precedence
        10
      end
    end

    class Subtraction < Arithmetic
      def operator
        :-
      end

      def self.precedence
        10
      end
    end

    class Multiplication < Arithmetic
      def operator
        :*
      end

      def self.precedence
        20
      end
    end

    class Division < Arithmetic
      def value(context={})
        r = cast(right.value(context), false)
        raise Dentaku::ZeroDivisionError if r.zero?

        cast(cast(left.value(context)) / r)
      end

      def self.precedence
        20
      end
    end

    class Modulo < Arithmetic
      def initialize(left, right)
        @left  = left
        @right = right

        unless (valid_node?(left) || left.nil?) && valid_node?(right)
          fail ParseError, "#{ self.class } requires numeric operands"
        end
      end

      def percent?
        left.nil?
      end

      def value(context={})
        if percent?
          cast(right.value(context)) * 0.01
        else
          super
        end
      end

      def operator
        :%
      end

      def self.precedence
        20
      end
    end

    class Exponentiation < Arithmetic
      def operator
        :**
      end

      def self.precedence
        30
      end
    end
  end
end