--          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.
--
class MINI_BUFFER
   --
   -- This mini buffer is used to parse information inside MANIFEST_STRING 
   -- objects (used for example to parse external feature definition). This 
   -- singleton object is shared via the GLOBALS.`mini_buffer' once function.

inherit GLOBALS

feature -- A simple ITERATOR like interface:

   start_with(ms: MANIFEST_STRING) is
	 -- Getting started with `ms', the `item' is set with the 
	 -- first non-blank character.
      require
	 ms /= Void
      do
	 manifest_string := ms
	 to_string := manifest_string.to_string
	 count := to_string.count
	 idx := 1
	 skip_separators
      ensure
	 manifest_string = ms
	 not is_off implies not item.is_separator
      end

   item: CHARACTER is
	 -- The one at current position.
      require
	 not is_off
      do
	 Result := to_string.item(idx)
      end

   is_off: BOOLEAN is
      do
	 Result := idx > count
      end

   next is
      require
	 not is_off
      do
	 idx := idx + 1
      end

feature

   skip_separators is
	 -- (Use `is_separator' of class CHARACTER.)
      do
	 from 
	 until
	    idx > count or else not to_string.item(idx).is_separator
	 loop
	    idx := idx + 1
	 end
      ensure
	 not is_off implies not item.is_separator
      end

   a_keyword(keyword: STRING): BOOLEAN is
	 -- Look for a `keyword' beginning strictly at current position, then,
	 -- `skip_separators' is automatically called. A keyword is never followed 
	 -- by a character of this set: {'A'..'Z','a'..'z','0'..'9','_'}.
      require
	 not item.is_separator
      local
	 idx_memory, i: INTEGER; stop: BOOLEAN
      do
	 idx_memory := idx
	 from
	    i := 1
	 until
	    stop
	 loop
	    if i > keyword.count then
	       stop := true
	    elseif idx > count then
	       stop := true
	    elseif to_string.item(idx) = keyword.item(i) then
	       i := i + 1
	       idx := idx + 1
	    else
	       stop := true
	    end
	 end
	 if i > keyword.count then
	    skip_separators
	    Result := true
	 else
	    idx := idx_memory
	 end
      ensure
	 is_off or else not item.is_separator
      end
   
   a_word(keyword: STRING): BOOLEAN is
	 -- Is it a single stand-alone `keyword'?
      require
	 not item.is_separator
      local
	 idx_memory: INTEGER
      do
	 idx_memory := idx
	 if a_keyword(keyword) then
	    if idx > count then
	       Result := true
	    else
	       idx := idx_memory
	    end
	 end
      ensure
	 Result implies is_off
      end

   a_include: STRING is
	 -- Always return some file name wich may looks like a file 
	 -- to include: "....foo.h" or <...foo.h>. When the file name 
	 -- is not properly brace with "..." or <...>, the standard 
	 -- "..." are automatically added.
      require
	 not item.is_separator
      local
	 stop: BOOLEAN
      do
	 from
	    Result := once ".... local unique buffer ...."
	    Result.clear
	    Result.extend(item)
	    next
	 until
	    stop
	 loop
	    if is_off then
	       stop := true
	    elseif item.is_separator then
	       stop := true
	    elseif item = ',' then
	       stop := true
	    else
	       Result.extend(item)
	       next
	    end
	 end
	 inspect
	    Result.first
	 when '%"', '<' then
	 else
	    Result.add_first('%"')
	    Result.add_last('%"')
	 end
	 Result := Result.twin
      ensure
	 is_off or else not item.is_separator
      end

   a_field_or_variable_name: STRING is
      do
	 Result := a_type_cast
      ensure
	 is_off or else not item.is_separator
      end

   a_type_cast: STRING is
	 -- Read an expected C cast.
      require
	 not item.is_separator      
      local
	 stop: BOOLEAN
      do
	 from
	    Result := once ".... local unique buffer ...."
	    Result.clear
	    Result.extend(item)
	    next
	 until
	    stop
	 loop
	    if is_off then
	       stop := true
	    elseif item = ',' then
	       stop := true
	    elseif item.is_separator then
	       stop := true;	       
	    else
	       Result.extend(item)
	       next
	       if Result.last = ')' then
		  if Result.occurrences('(') < Result.occurrences(')') then
		     idx := idx - 1
		     Result.remove_last(1)
		     stop := true
		  end
	       end
	    end
	 end
	 Result := Result.twin
	 skip_separators
      ensure
	 is_off or else not item.is_separator
      end

   a_signature: FIXED_ARRAY[STRING] is
	 -- Read the expected external signature.
      require
	 not item.is_separator      
      local
	 stop: BOOLEAN; external_cast: STRING
      do
	 if item /= '(' then
	    error_handler.append("Bad external signature (missing opening %"(%" %
		      %delimiter.")
	    error_handler.print_as_fatal_error
	 end
	 from
	    next; skip_separators
	    create Result.with_capacity(8)
	    Result.add_last(Void)
	 until
	    stop
	 loop
	    if is_off then
	       error_handler.append("Bad external signature (missing %")%" %
                         %delimiter.")
	       error_handler.print_as_fatal_error
	    elseif item = ',' then
	       next; skip_separators
	    elseif item = ')' then
	       stop := true
	       next; skip_separators
	    else
	       external_cast := a_type_cast
	       Result.add_last(external_cast)
	    end
	 end
      ensure
         Result.count > 1
	 is_off or else not item.is_separator
      end

feature {NONE}

   manifest_string: MANIFEST_STRING
	 -- The one currently parsed.

   to_string: STRING
	 -- To cache `manifest_string.to_string'.

   count: INTEGER
	 -- To cache `to_string.count'.

   idx: INTEGER
	 -- The current position in `to_string'.

   singleton_memory: MINI_BUFFER is
      once
	 Result := Current
      end

invariant

   is_real_singleton: Current = singleton_memory

end -- MINI_BUFFER