class Dentaku::AST::Arithmetic

def cast(val, prefer_integer = true)

def cast(val, prefer_integer = true)
  validate_value(val)
  numeric(val, prefer_integer)
end

def initialize(*)

def initialize(*)
  super
  unless valid_left?
    raise NodeError.new(:numeric, left.type, :left),
          "#{self.class} requires numeric operands"
  end
  unless valid_right?
    raise NodeError.new(:numeric, right.type, :right),
          "#{self.class} requires numeric operands"
  end
end

def numeric(val, prefer_integer)

def numeric(val, prefer_integer)
  v = BigDecimal(val, Float::DIG + 1)
  v = v.to_i if prefer_integer && v.frac.zero?
  v
rescue ::TypeError
  # If we got a TypeError BigDecimal or to_i failed;
  # let value through so ruby things like Time - integer work
  val
end

def operator

def operator
  raise NotImplementedError
end

def type

def type
  :numeric
end

def valid_left?

def valid_left?
  valid_node?(left) || left.type == :datetime
end

def valid_node?(node)

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

def valid_right?

def valid_right?
  valid_node?(right) || right.type == :duration || right.type == :datetime
end

def validate_format(string)

def validate_format(string)
  unless string =~ /\A-?\d*(\.\d+)?\z/ && !string.empty?
    raise Dentaku::ArgumentError.for(:invalid_value, value: string, for: BigDecimal),
          "String input '#{string}' is not coercible to numeric"
  end
end

def validate_operation(val)

def validate_operation(val)
  unless val.respond_to?(operator)
    raise Dentaku::ArgumentError.for(:invalid_operator, operation: self.class, operator: operator),
          "#{ self.class } requires operands that respond to #{operator}"
  end
end

def validate_value(val)

def validate_value(val)
  if val.is_a?(::String)
    validate_format(val)
  else
    validate_operation(val)
  end
end

def value(context = {})

def value(context = {})
  l = cast(left.value(context))
  r = cast(right.value(context))
  begin
    l.public_send(operator, r)
  rescue ::TypeError => e
    # Right cannot be converted to a suitable type for left. e.g. [] + 1
    raise Dentaku::ArgumentError.for(:incompatible_type, value: r, for: l.class), e.message
  end
end