--          This file is part of SmartEiffel The GNU Eiffel Compiler.
--       Copyright (C) 1994-2002 LORIA - INRIA - U.H.P. Nancy 1 - FRANCE
--          Dominique COLNET and Suzanne COLLIN - SmartEiffel@loria.fr
--                       http://SmartEiffel.loria.fr
-- 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.
--
expanded class POSITION
   --
   -- A position in some source file. Actually the source file can be
   -- some Eiffel source file, some -cecil file or the ACE file.
   --

inherit GLOBALS

feature

   base_class: BASE_CLASS
         -- The corresponding one which may be Void for example when
         -- `is_unknown' or when parsing a -cecil file or the ACE file.

   line: INTEGER is
         -- The corresponding `line' number in the source file or 0
         -- when `is_unknown'.
      local
         bit: BIT Integer_bits
      do
         if mangling.last then
            bit := mangling |>> 1
            bit := bit and 111111111111111B
         else
            bit := mangling |>> 8
            bit := bit and 1111111111111B
         end
         Result := bit.to_integer
      ensure
         not is_unknown implies Result >= 0
      end

   column: INTEGER is
         -- The `column' number in the source file or 0 when `is_unknown' or
         -- when there is not enough space in `mangling' for the `column'.
      local
         bit: BIT Integer_bits
      do
         if mangling.last then
            -- Result is 0 because `column' is not memorized.
         else
            bit := mangling |>> 1; -- To drop the flag.
            bit := bit and 1111111B
            Result := bit.to_integer
         end
      ensure
         Result >= 0
      end

   base_class_name: CLASS_NAME is
         -- The corresponding one when already loaded by the `eiffel_parser'.
      local
         bc: BASE_CLASS
      do
         bc := base_class
         if bc /= Void then
            Result := bc.name
         end
      end

   path: STRING is
         -- The corresponding file `path' or Void when `is_unknown'.
      local
         bc: BASE_CLASS
         id_value: INTEGER
      do
         bc := base_class
         if bc /= Void then
            Result := bc.path
         end
         if Result = Void then
            -- Looking for the path of the -cecil file or for the
            -- path of some ACE file.
            id_value := id
            if id /= 0 then
               Result := id_provider.alias_of(id)
	    else
	       Result := ace.file_path
	    end
         end
      ensure
         Result /= Void implies string_aliaser.item(Result) = Result
      end

   is_unknown: BOOLEAN is
         -- True when the `eiffel_parser' as called `set'.
      do
         Result := mangling.to_integer = 0
      end

   before(other: like Current): BOOLEAN is
         -- Is `Current' position strictly before `other' (which is in the
         -- same source text file).
      require
         path = other.path
      local
         li, other_li: INTEGER
      do
         li := line
         other_li := other.line
         if li < other_li then
            Result := true
         elseif li = other_li then
            Result := column < other.column
         end
      end

   sedb_trace: BOOLEAN is
	 -- Is the run-time trace mechanism activated for this position?
      local
	 bc: BASE_CLASS
      do
	 bc := base_class
	 if bc /= Void then
	    Result := bc.sedb_trace
	 end
      end

   show is
      require
	 not smart_eiffel.no_file_for(base_class_name.to_string)
      local
         li, co, nb: INTEGER; bc: BASE_CLASS; bcn: CLASS_NAME
         name, file_path, the_line: STRING
      do
         li := line
         co := column
         bc := base_class
         if bc /= Void then
            check id = bc.id end
            bcn := base_class_name
         end
         if bcn /= Void then
            name := bcn.to_string
            file_path := bc.path
         end
	 if file_path = Void and then id = 0 then
	    file_path := ace.file_path
	 end
         echo.w_put_string("Line ")
         echo.w_put_integer(li)
         if co > 0 then
            echo.w_put_string(" column ")
            echo.w_put_integer(co)
         end
         echo.w_put_string(" in ")
         if name /= Void then
            echo.w_put_string(name)
         end
         if file_path /= Void then
            echo.w_put_string(" (")
            echo.w_put_string(file_path)
            echo.w_put_character(')')
         end
         echo.w_put_string(" :%N")
         if file_path /= Void then
            the_line := get_line(file_path,li)
            if li > 0 and then the_line /= Void then
               echo.w_put_string(the_line)
               echo.w_put_character('%N')
               if co > 0 then
                  from
                     nb := 1
                  until
                     nb >= co
                  loop
                     if the_line.item(nb) = '%T' then
                        echo.w_put_character('%T')
                     else
                        echo.w_put_character(' ')
                     end
                     nb := nb + 1
                  end
                  echo.w_put_string("^%N")
               end
            end
         end
      end

   append_in(buffer: STRING) is
      require
         buffer /= Void
      local
         li, co: INTEGER; bc: BASE_CLASS; bcn: CLASS_NAME; place: STRING
      do
         li := line
         co := column
         buffer.append("Line ")
         li.append_in(buffer)
         if co > 0 then
            buffer.append(" column ")
            co.append_in(buffer)
         end
         bc := base_class
         if bc /= Void then
            place := bc.path
            if place = Void then
               bcn := bc.name
               if bcn /= Void then
                  place := bcn.to_string
               end
            end
            if place /= Void then
               buffer.append(" in %"")
               buffer.append(place)
               buffer.append(fz_03)
            end
         end
      end

   set_in(bc: like base_class) is
	 -- Assign `Current' in `bc'. This routine is used to create implicit
	 -- Eiffel code. As an example, this is used to define pseudo
	 -- code of class TUPLE which is not truly written.
      require
	 smart_eiffel.no_file_for(bc.name.to_string)
      do
	 eiffel_file(1,1,bc.id,bc)
      end

feature {EIFFEL_PARSER}

   eiffel_file(li, co, class_id: INTEGER; bc: like base_class) is
	 -- The position is in some Eiffel file.
      require
         li >= 1
         co >= 1
         class_id >= 0
      do
         base_class := bc
         check Integer_bits >= 32 end
         if class_id <= 2047 and then li <= 8191 and then co <= 127 then
            mangling := (class_id.to_bit |<< 21);       -- 11 bits for `id'
            mangling := mangling or (li.to_bit |<< 8);  -- 13 bits for `line'
            mangling := mangling or (co.to_bit |<< 1);  -- 7  bits for `column'
         else
            -- The `column' is not memorized.
            mangling := (class_id.to_bit |<< 17);       -- 15 bits for `id'
            mangling := mangling or (li.to_bit |<< 1);  -- 16 bits for `line'
            mangling := mangling or (1).to_bit;         -- forget `column'.
         end
      ensure
         line = li
         id = class_id
         column = 0 or else column = co
      end

feature {ACE}

   ace_file(li, co: INTEGER) is
	 -- The position is in the ACE file.
      require
         li >= 1; co >= 1
      do
         eiffel_file(li,co,0,Void)
      ensure
         line = li
         id = 0
         column = 0 or else column = co
      end


feature {C_PRETTY_PRINTER,E_AGENT}

   mangling: BIT Integer_bits
         -- In order to save memory (there are a lot of objects like `Current'),
         -- the `id' of the class, the `line' and the `column' are saved in this
         -- BIT sequence. Two mangling are used, and the `column' may be
         -- dropped (not memorized, see `set'). This implementation assume
         -- that `Integer_bits' is greater or equal to 32.

feature {NONE}

   id: INTEGER is
      do
         if mangling.last then
            Result := (mangling |>> 17).to_integer
         else
            Result := (mangling |>> 21).to_integer
         end
      ensure
         Result >= 0
      end

   get_line(file_path: STRING; li: INTEGER): STRING is
      require
         not file_path.is_empty
         li > 0
      local
         i: INTEGER
      do
         echo.tfr_connect(tmp_file_read,file_path)
         if tmp_file_read.is_connected then
            from until tmp_file_read.end_of_input or else i = li
            loop
               tmp_file_read.read_line
               i := i + 1
            end
            if not tmp_file_read.end_of_input then
               Result := tmp_file_read.last_string
            end
            tmp_file_read.disconnect
         end
      end

end -- POSITION