lib/ridl/expression.rb



#--------------------------------------------------------------------
# expression.rb - IDL Expression classes
#
# Author: Martin Corino
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the RIDL LICENSE which is
# included with this program.
#
# Copyright (c) Remedy IT Expertise BV
#--------------------------------------------------------------------
require 'ridl/node'

module IDL
  class Expression
    attr_reader :idltype, :value

    def typename
      @idltype.typename
    end

    def is_template?
      false
    end

    def instantiate(_)
      self
    end

    class Value < Expression
      def initialize(type, val)
        @idltype = type
        @value = @idltype.narrow(val)
      end
    end

    class ScopedName < Expression
      attr_reader :node

      def initialize(node)
        if $DEBUG
          unless IDL::AST::Const === node || (IDL::AST::TemplateParam === node && node.idltype.is_a?(IDL::Type::Const))
            raise "#{node.scoped_name} must be constant: #{node.class.name}."
          end
        end
        @node = node
        @idltype = node.idltype
        @value = @idltype.narrow(node.value) unless node.is_template?
      end

      def is_template?
        @node.is_template?
      end

      def instantiate(instantiation_context)
        if self.is_template?
          cp = IDL::AST::TemplateParam.concrete_param(instantiation_context, @node)
          cp.is_a?(Expression) ? cp : ScopedName.new(cp)
        else
          self
        end
      end

      def is_node?(node_class)
        @node.is_a?(node_class)
      end

      def resolved_node
        @node
      end
    end

    class Enumerator < Expression
      attr_reader :node

      def initialize(node)
        if $DEBUG
          unless IDL::AST::Enumerator === node
            raise "#{node.scoped_name} must be enumerator: #{node.class.name}."
          end
        end
        @node = node
        @idltype = node.idltype
        @value = node.value
      end
    end

    class Operation < Expression
      NUMBER_OF_OPERANDS = nil

      attr_reader :operands

      def initialize(*_operands)
        n = self.class::NUMBER_OF_OPERANDS

        if _operands.size != n
          raise format("%s must receive %d operand%s.",
            self.typename, n, if (n > 1) then "s" else "" end)
        end

        unless _operands.any? { |o| o.is_template? }
          @idltype = self.class.suite_type(*(_operands.collect { |o| o.idltype.resolved_type }))
          @value = calculate(*(_operands.collect { |o| o.value }))
        else
          @idltype = nil
          @value = nil
        end
        @operands = _operands
        self.set_type
      end

      def is_template?
        @operands.any? { |o| o.is_template? }
      end

      def instantiate(instantiation_context)
        self.is_template? ? self.class.new(*@operands.collect { |o| o.instantiate(instantiation_context) }) : self
      end

      def Operation.suite_type(*types)
        types.each do |t|
          unless self::Applicable.include? t.class
            raise "#{self.name} cannot be applicable for #{t.typename}"
          end
        end

        ret = nil
        types = types.collect { |t| t.class }
        self::Applicable.each do |t|
          if types.include? t
            ret = t
            break
          end
        end
        ret
      end

      def set_type; end

      class Unary < Operation
        NUMBER_OF_OPERANDS = 1
        Applicable = nil
      end # of class Unary

      class Integer2 < Operation
        NUMBER_OF_OPERANDS = 2
        Applicable = [
          IDL::Type::LongLong, IDL::Type::ULongLong,
          IDL::Type::Long, IDL::Type::ULong,
          IDL::Type::Short, IDL::Type::UShort,
          IDL::Type::Octet
        ]

        def Integer2.suite_sign(_t, _v)
          [[IDL::Type::LongLong, IDL::Type::ULongLong],
            [IDL::Type::Long,     IDL::Type::ULong],
            [IDL::Type::Short,    IDL::Type::UShort]
].each do |t|
            next unless t.include? _t

            return (if _v.negative? then t[0] else t[1] end)
          end
        end

        def set_type
          if Integer2::Applicable.include? @idltype
            @idltype = self.class.suite_sign(@idltype, @value)
          end
        end
      end

      class Boolean2 < Integer2
        Applicable = [
          IDL::Type::Boolean
        ] + Integer2::Applicable

        def Boolean2.checktype(t1, t2)
          superclass.checktype(*types)

          t = IDL::Type::Boolean
          if (t1 == t && t2 != t) or (t1 != t && t2 == t)
            raise "#{self.name} about #{t1.typename} and #{t2.typename} is illegal."
          end
        end
      end

      class Float2 < Integer2
        Applicable = [
          IDL::Type::LongDouble, IDL::Type::Double, IDL::Type::Float,
          IDL::Type::Fixed
        ] + Integer2::Applicable

        def Float2.checktype(t1, t2)
          superclass.checktype(*types)

          # it's expected that Double, LongDouble is a Float.
          s1 = IDL::Type::Float
          s2 = IDL::Type::Fixed
          if (t1 === s1 && t2 === s2) or (t1 === s2 && t2 === s1)
            raise "#{self.name} about #{t1.typename} and #{t2.typename} is illegal."
          end
        end
      end

      class UnaryPlus < Unary
        Applicable = Float2::Applicable
        def calculate(op)
          op
        end
      end

      class UnaryMinus < Unary
        Applicable = Float2::Applicable
        def calculate(op)
          -op
        end

        def set_type
          @idltype = Integer2.suite_sign(@idltype, @value)
        end
      end

      class UnaryNot < Unary
        Applicable = Integer2::Applicable
        def calculate(op)
          if @idltype.is_unsigned?
            (2**@idltype.bits - 1) - op
          else
            ~op
          end
        end
      end

      class Or < Boolean2
        def calculate(lop, rop)
          lop | rop
        end
      end

      class And < Boolean2
        def calculate(lop, rop)
          lop & rop
        end
      end

      class Xor < Boolean2
        def calculate(lop, rop)
          lop ^ rop
        end
      end

      class Shift < Integer2
      protected
        def check_rop(rop)
          unless (0...64) === rop
            raise "right operand for shift must be in the range 0 <= right operand < 64: #{rop}."
          end
        end
      end

      class LShift < Shift
        def calculate(lop, rop)
          check_rop(rop)
          lop << rop
        end
      end

      class RShift < Shift
        def calculate(lop, rop)
          check_rop(rop)
          lop >> rop
        end
      end

      class Add < Float2
        def calculate(lop, rop)
          lop + rop
        end
      end

      class Minus < Float2
        def calculate(lop, rop)
          lop - rop
        end
      end

      class Mult < Float2
        def calculate(lop, rop)
          lop * rop
        end
      end

      class Div < Float2
        def calculate(lop, rop)
          lop / rop
        end
      end

      class Mod < Integer2
        def calculate(lop, rop)
          lop % rop
        end
      end
    end # of class Operation
  end # of class Expression
end