-- This file is part of SmartEiffel The GNU Eiffel Compiler Tools and Libraries
--
-- SmartEiffel is  free software;  you can redistribute it and/or  modify it
-- under  the terms of the  GNU General Public License, as published by  the
-- Free Software Foundation; either version 2, or (at your option) any later
-- version.
-- SmartEiffel is distributed in the hope that it will be useful but WITHOUT 
-- ANY WARRANTY;  without  even the implied warranty  of MERCHANTABILITY  or
-- FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
-- more details.  You should have received a copy of  the GNU General Public
-- License along with SmartEiffel;  see the file COPYING.  If not,  write to
-- the Free Software Foundation,  Inc., 59 Temple Place - Suite 330,  Boston, 
-- MA 02111-1307, USA.
--
-- Copyright(C) 1994-2002: INRIA - LORIA (INRIA Lorraine) - ESIAL U.H.P.
--			   - University of Nancy 1 - FRANCE
-- Copyright(C) 2003:      INRIA - LORIA (INRIA Lorraine) - I.U.T. Charlemagne
--			   - University of Nancy 2 - FRANCE
--
--		 Dominique COLNET, Suzanne COLLIN, Olivier ZENDRA,
--			   Philippe RIBET, Cyril ADRIAN
--
-- http://SmartEiffel.loria.fr - SmartEiffel@loria.fr
--
class E_WHEN
   --
   -- To store a when clause of an inspect instruction.
   --

inherit GLOBALS

creation {EIFFEL_PARSER} make

creation {E_WHEN, WHEN_LIST} from_e_when

feature

   start_position: POSITION
         -- Of the first character of keyword "when".

   header_comment: COMMENT
         -- Of the when clause.

   compound: COMPOUND
         -- Of the when clause if any.

   when_list: WHEN_LIST
         -- Corresponding container when checked.

   use_current: BOOLEAN is
      do
         if compound /= Void then
            Result := compound.use_current
         end
      end

   stupid_switch(run_time_set: RUN_TIME_SET): BOOLEAN is
      do
         Result := (compound = Void 
		    or else 
		    compound.stupid_switch(run_time_set))
      end

   e_inspect: E_INSPECT is
      do
         Result := when_list.e_inspect
      end

feature {WHEN_LIST}

   simplify_2(cc: CHARACTER_CONSTANT; ic: INTEGER_CONSTANT) is
      local
         i: INTEGER
      do
         from
            i := list.lower
         until
            i > list.upper
         loop
            list.item(i).simplify_2(i, cc, ic)
            i := i + 1
         end
	 if compound /= Void then
	    compound := compound.simplify_2
	 end
      end
   
   verify_scoop(allowed: FORMAL_ARG_LIST) is
      local
         i: INTEGER
      do
         if compound /= Void then
            compound.verify_scoop(allowed)
         end
         from
            i := list.upper
         until
            i < list.lower
         loop
            list.item(i).verify_scoop(allowed)
            i := i - 1
         end
      end

feature {E_WHEN_VISITOR}

   accept(visitor: E_WHEN_VISITOR) is
      do
         visitor.visit_e_when(Current)
      end

feature {EIFFEL_PARSER}

   make(sp: like start_position; hc: like header_comment) is
      require
         not sp.is_unknown
      do
         start_position := sp
         header_comment := hc
      ensure
         start_position = sp
      end

feature {WHEN_LIST}

   afd_check is
      do
         if compound /= Void then
            compound.afd_check
         end
      end

   safety_check is
      do
         if compound /= Void then
            compound.safety_check
         end
      end

feature {WHEN_LIST}

   compile_to_jvm(else_position: POSITION; remainder: INTEGER) is
         -- Where `remainder' is the number of E_WHEN after Current.
      local
         point3, point4, bi: INTEGER; must_test: BOOLEAN
	 ca: like code_attribute
      do
         ca := code_attribute
         if remainder > 0 then
            must_test := True
         elseif ace.no_check then
            must_test := True
         else -- boost :
            must_test := not else_position.is_unknown
         end
         points1.clear
         if must_test then
            from
               bi := values.lower
            until
               bi > values.upper
            loop
               if values.item(bi) = values.item(bi+1) then
                  ca.opcode_dup
                  ca.opcode_push_integer(values.item(bi))
                  points1.add_last(ca.opcode_if_icmpeq)
               else
                  ca.opcode_dup
                  ca.opcode_push_integer(values.item(bi))
                  point3 := ca.opcode_if_icmplt
                  ca.opcode_dup
                  ca.opcode_push_integer(values.item(bi+1))
                  points1.add_last(ca.opcode_if_icmple)
                  ca.resolve_u2_branch(point3)
               end
               bi := bi + 2
            end
            point4 := ca.opcode_goto
         end
         ca.resolve_with(points1)
         if compound /= Void then
            compound.compile_to_jvm
         end
         point2 := ca.opcode_goto
         if must_test then
            ca.resolve_u2_branch(point4)
         end
      end

   compile_to_jvm_resolve_branch is
      do
         code_attribute.resolve_u2_branch(point2)
      end

   compile_to_c is
      local
         bi: INTEGER
      do
         cpp.put_string(once "%Nif(")
         from
            bi := values.lower
         until
            bi > values.upper
         loop
            cpp.put_character('(')
            if values.item(bi) = values.item(bi+1) then
               cpp.put_integer(values.item(bi))
               cpp.put_string(once "==")
               cpp.put_inspect
            else
               cpp.put_character('(')
               cpp.put_integer(values.item(bi))
               cpp.put_string(once "<=")
               cpp.put_inspect
               cpp.put_string(fz_39)
               cpp.put_inspect
               cpp.put_string(once "<=")
               cpp.put_integer(values.item(bi+1))
               cpp.put_character(')')
            end
            cpp.put_character(')')
            --
            bi := bi + 2
            if bi < values.upper then
               cpp.put_string(once "||")
            end
         end
         cpp.put_string(once "){%N")
         if compound /= Void then
            compound.compile_to_c
         end
         cpp.put_string(fz_12)
      end

   compile_to_c_switch is
      local
         bi, v: INTEGER
      do
         from
            bi := values.lower
         until
            bi > values.upper
         loop
            from
               v := values.item(bi)
            until
               v > values.item(bi+1)
            loop
               cpp.put_string(once "case ")
               cpp.put_integer(v)
               cpp.put_string(once ":%N")
               v := v + 1
            end
            bi := bi + 2
         end
         if compound /= Void then
            compound.compile_to_c
         end
         cpp.put_string(once "break;%N")
      end

   pretty_print is
      local
         i: INTEGER
      do
         pretty_printer.keyword(fz_when)
         pretty_printer.indent_level_increment
         if header_comment /= Void then
            header_comment.pretty_print
         end
         if list /= Void then
            from
               i := list.lower
            until
               i > list.upper
            loop
               list.item(i).pretty_print
               i := i + 1
               if i <= list.upper then
                  pretty_printer.put_character(',')
		  if not pretty_printer.zen_mode then
		     pretty_printer.put_character(' ')
		  end
               end
            end
         end
         pretty_printer.indent_level_decrement
         pretty_printer.keyword(fz_then)
         if compound /= Void then
            compound.pretty_print
         end
      end

   includes_integer(v: INTEGER): BOOLEAN is
      local
         i: INTEGER
      do
         if
            values /= Void
               and then
            v >= values.item(values.lower)
               and then
            v <= values.item(values.upper)
         then
            from
               i := values.lower
            until
               Result or else i > values.upper
            loop
               Result := values.item(i) <= v and v <= values.item(i+1)
               i := i + 2
            end
         end
      end

   includes_integer_between(l,u: INTEGER): BOOLEAN is
      require
         invalid_range: l < u
      local
         i: INTEGER
      do
         if
            values /= Void
               and then
            u >= values.item(values.lower)
               and then
            l <= values.item(values.upper)
         then
            from
               i := values.lower
            until
               Result or else i > values.upper
            loop
               Result := values.item(i) <= u and l <= values.item(i+1)
               i := i + 2
            end
         end
      end

   largest_range_of_values: INTEGER is
         -- The largest number of consecutive values - returns 0 if none
      local
         i, range: INTEGER
      do
         if values /= Void then
            from
               i := values.lower
            until
               i > values.upper
            loop
               range := values.item(i+1) - values.item(i) + 1
               if range > Result then
                  Result := range
               end
               i := i + 2
            end
         end
      end

feature {WHEN_LIST, E_WHEN}

   to_runnable_integer(wl: like when_list): like Current is
      require
         wl /= Void
      local
         ct: E_TYPE; ne, i: INTEGER; when_item: WHEN_ITEM
      do
         if when_list = Void then
            ne := nb_errors
            when_list := wl
            if list = Void then
               error_handler.add_position(e_inspect.start_position)
               error(start_position,em2)
            else
               from
                  i := list.lower
               until
                  i > list.upper or else nb_errors - ne > 0
               loop
                  when_item := list.item(i).to_runnable_integer(Current)
                  if when_item = Void then
                     error(start_position,em1)
                  else
                     list.put(when_item,i)
                  end
                  i := i + 1
               end
            end
            if compound /= Void then
               ct := smart_eiffel.top_rf.current_type
               compound := compound.to_runnable(ct)
               if compound = Void then
                  error(start_position,em1)
               end
            end
            Result := Current
         else
            !!Result.from_e_when(Current)
            Result := Result.to_runnable_integer(wl)
         end
      ensure
         Result.when_list = wl
      end

   to_runnable_character(wl: like when_list): like Current is
      require
         wl /= Void
      local
         ct: E_TYPE; ne, i: INTEGER; when_item: WHEN_ITEM
      do
         if when_list = Void then
            ne := nb_errors
            when_list := wl
            if list = Void then
               error_handler.add_position(e_inspect.start_position)
               error(start_position,em2)
            else
               from
                  i := list.lower
               until
                  i > list.upper or else nb_errors - ne > 0
               loop
                  when_item := list.item(i).to_runnable_character(Current)
                  if when_item = Void then
                     error(start_position,em1)
                  else
                     list.put(when_item,i)
                  end
                  i := i + 1
               end
            end
            if compound /= Void then
               ct := smart_eiffel.top_rf.current_type
               compound := compound.to_runnable(ct)
               if compound = Void then
                  error(start_position,em1)
               end
            end
            Result := Current
         else
            !!Result.from_e_when(Current)
            Result := Result.to_runnable_character(wl)
         end
      ensure
         Result.when_list = wl
      end

   to_runnable_string(wl: like when_list): like Current is
      require
         wl /= Void;
      local
         ct: E_TYPE;
         ne, i: INTEGER
         when_item: WHEN_ITEM;
      do
         if when_list = Void then
            ne := nb_errors;
            when_list := wl;
            if list = Void then
               error_handler.add_position(e_inspect.start_position);
               error(start_position,em2);
            else
               from
                  i := list.lower;
               until
                  i > list.upper or else nb_errors - ne > 0
               loop
                  when_item := list.item(i).to_runnable_string(Current);
                  if when_item = Void then
                     error(start_position,em1);
                  else
                     list.put(when_item,i);
                  end;
                  i := i + 1;
               end;
            end;
            if compound /= Void then
               ct := smart_eiffel.top_rf.current_type;
               compound := compound.to_runnable(ct);
               if compound = Void then
                  error(start_position,em1);
               end;
            end;
            Result := Current;
         else
            !!Result.from_e_when(Current);
            Result := Result.to_runnable_string(wl);
         end;
      ensure
         Result.when_list = wl
      end;

feature {WHEN_ITEM_1, WHEN_ITEM_2}

   always_selected(index: INTEGER) is
      do
	 if compound /= Void then
	    compound := compound.simplify_2
	 end
	 when_list.always_selected(compound)
      end

feature {WHEN_ITEM_1}

   add_when_item_1(wi1: WHEN_ITEM_1) is
      require
         wi1 /= Void
      local
         i, v: INTEGER
      do
         v := wi1.expression_value
         if e_inspect.includes(v) then
            err_occ(v,wi1.start_position)
         elseif values = Void then
            !!values.make(501,502)
            values.put(v,values.lower)
            values.put(v,values.upper)
         else
            i := locate_in_values(v)
            if i = values.lower then  -- v is lower than lowest value
               if v = values.item(i) - 1 then  -- just change lower
                  values.put(v,i)
               else
                  values.resize(values.lower-2, values.upper)
                  values.put(v,values.lower)
                  values.put(v,values.lower+1)
               end
            elseif i > values.upper then  -- v is higher than highest value
               if v = values.item(i-1) + 1 then  -- just change upper
                  values.put(v,i-1)
               else
                  values.resize(values.lower, values.upper+2)
                  values.put(v,values.upper-1)
                  values.put(v,values.upper)
               end
            else
               if v = values.item(i-1) + 1 and v = values.item(i) - 1 then
                  values.put(values.item(i+1),i-1)
                  from until i > values.upper-2 loop
                     values.put(values.item(i+2),i)
                     values.put(values.item(i+3),i+1)
                     i := i + 2
                  end
                  values.resize(values.lower, values.upper-2)
               elseif v = values.item(i-1) + 1 then  -- just change upper
                  values.put(v,i-1)
               elseif v = values.item(i) - 1 then  -- just change lower
                  values.put(v,i)
               else
                  values.resize(values.lower, values.upper+2)
                  from
                     i := values.upper - 1
                  until
                     v > values.item(i-1)
                  loop
                     values.put(values.item(i-2),i)
                     values.put(values.item(i-1),i+1)
                     i := i - 2
                  end
                  values.put(v,i)
                  values.put(v,i+1)
               end
            end
         end
      ensure
         e_inspect.includes(wi1.expression_value)
      end

feature {WHEN_ITEM_2}

   add_when_item_2(wi2: WHEN_ITEM_2) is
      require
         wi2 /= Void
      local
         l, u, i: INTEGER
      do
         l := wi2.lower_value
         u := wi2.upper_value
         if l >= u then
            error(wi2.start_position,"Not a good slice.")
         elseif e_inspect.includes_between(l,u) then
            from
               i := l
            until
               e_inspect.includes(i)  -- try to locate the exact problem
            loop
                if i = l then
                   l := l + 1
                   i := u
                else
                   u := u - 1
                   i := l
                end
            end
            err_occ(i,wi2.start_position)
         elseif values = Void then
            !!values.make(501,502)
            values.put(l,values.lower)
            values.put(u,values.upper)
         else
            i := locate_in_values(l)
            if i = values.lower then  -- l and u are lower than lowest value
               if u = values.item(i) - 1 then  -- just change lower
                  values.put(l,i)
               else
                  values.resize(values.lower-2, values.upper)
                  values.put(l,values.lower)
                  values.put(u,values.lower+1)
               end
            elseif i > values.upper then  -- l and u are higher than highest
               if l = values.item(i-1) + 1 then  -- just change upper
                  values.put(u,i-1)
               else
                  values.resize(values.lower, values.upper+2)
                  values.put(l,values.upper-1)
                  values.put(u,values.upper)
               end
            else
               if l = values.item(i-1) + 1 and u = values.item(i) - 1 then
                  values.put(values.item(i+1),i-1)
                  from until i > values.upper-2 loop
                     values.put(values.item(i+2),i)
                     values.put(values.item(i+3),i+1)
                     i := i + 2
                  end
                  values.resize(values.lower, values.upper-2)
               elseif l = values.item(i-1) + 1 then  -- just change upper
                  values.put(u,i-1)
               elseif u = values.item(i) - 1 then  -- just change lower
                  values.put(l,i)
               else
                  values.resize(values.lower, values.upper+2)
                  from
                     i := values.upper - 1
                  until
                     l > values.item(i-1)
                  loop
                     values.put(values.item(i-2),i)
                     values.put(values.item(i-1),i+1)
                     i := i - 2
                  end
                  values.put(l,i)
                  values.put(u,i+1)
               end
            end
         end
      end

feature {EIFFEL_PARSER}

   add_value(v: EXPRESSION) is
      require
         v /= Void
      local
         element: WHEN_ITEM
      do
         !WHEN_ITEM_1!element.make(v)
         if list = Void then
            !!list.with_capacity(4,1)
	 end
	 list.add_last(element)
      end

   add_slice(min, max: EXPRESSION) is
      require
         min /= Void
         max /= Void
      local
         element: WHEN_ITEM
      do
         if min.is_manifest_string then
            error_handler.add_position(min.start_position)
            fatal_error(once "Cannot use '..' with manifest strings")
         elseif max.is_manifest_string then
            error_handler.add_position(max.start_position)
            fatal_error(once "Cannot use '..' with manifest strings")
         else
            !WHEN_ITEM_2!element.make(min,max)
            if list = Void then
               !!list.with_capacity(4,1)
            end
            list.add_last(element)
         end
      end

   set_compound(c: like compound) is
      do
         compound := c
      ensure
         compound = c
      end

feature {E_WHEN, MANIFEST_STRING_INSPECTOR}

   list: ARRAY[WHEN_ITEM]

feature {NONE}

   values: ARRAY[INTEGER]
	 -- To store pairs of range values.

   points1: FAST_ARRAY[INTEGER] is
         -- To reach the `compound'.
      once
         !!Result.with_capacity(12)
      end

   point2: INTEGER
         -- To go outside the E_INSPECT.

   from_e_when(other: like Current) is
      local
         i: INTEGER
         when_item: WHEN_ITEM
      do
         start_position := other.start_position
         list := other.list.twin
         from
            i := list.lower
         until
            i > list.upper
         loop
            when_item := list.item(i).twin
            when_item.clear_e_when
            list.put(when_item,i)
            i := i + 1
         end
         header_comment := other.header_comment
         compound := other.compound
      end

   err_occ(v: INTEGER; p: POSITION) is
      do
         error_handler.add_position(e_inspect.start_position)
         error_handler.append("Second occurrence for this value (")
         error_handler.append(v.to_string)
         error(p,") in the same inspect.")
      end

   locate_in_values(v: INTEGER): INTEGER is
        -- returns index in values table where v would be inserted
      require
         values /= Void
      do
         from
            Result := values.lower
         until
            Result > values.upper
               or else
            v < values.item(Result)
         loop
            Result := Result + 2
         end
      ensure
         (Result - values.lower) \\ 2 = 0
         Result < values.upper implies v < values.item(Result)
         Result > values.upper implies v > values.item(Result-1)
      end

   em1: STRING is "Bad when clause."
   em2: STRING is "Empty when clause in inspect."

end -- E_WHEN






