--          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 NATIVE_C
   --
   -- For the external "C ..." definition.
   --

inherit NATIVE redefine make end

creation make

feature

   use_current(er: EXTERNAL_ROUTINE): BOOLEAN is do end

   stupid_switch_function(run_time_set: RUN_TIME_SET; name: STRING)
      : BOOLEAN is
      do
         Result := true
      end

   stupid_switch_procedure(run_time_set: RUN_TIME_SET; name: STRING)
      : BOOLEAN is
      do
         Result := true
      end

   notify_external_assignments(args: FORMAL_ARG_LIST; rt: E_TYPE) is
      do
	 assignment_handler.from_external(start_position, args, rt)
      end
   
   c_define_function(rf8: RUN_FEATURE_8; bcn, name: STRING) is
      do
	 parse_external_tag
	 if ace.no_check then
	    if need_prototype then 
	       rf8.external_c_prototype_from(start_position)
	    end
	    rf8.c_opening
	    c_mapping(rf8.base_feature,true,true,rf8.arg_count)
	    rf8.c_closing
	 end
      end

   c_mapping_function(rf8: RUN_FEATURE_8; bcn, name: STRING) is
      do
	 parse_external_tag
         if ace.no_check then
            rf8.default_mapping_function
         else
	    if need_prototype then 
	       rf8.external_c_prototype_from(start_position)
	    end
            c_mapping(rf8.base_feature,false,true,rf8.arg_count)
         end
      end

   c_define_procedure(rf7: RUN_FEATURE_7; bcn, name: STRING) is
      do
	 parse_external_tag
	 if ace.no_check then
	    if need_prototype then 
	       rf7.external_c_prototype_from(start_position)
	    end
	    rf7.c_opening
	    c_mapping(rf7.base_feature,true,false,rf7.arg_count)
	    rf7.c_closing
	 end
      end

   c_mapping_procedure(rf7: RUN_FEATURE_7; bcn, name: STRING) is
      do
	 parse_external_tag
         if ace.no_check then
            rf7.default_mapping_procedure
         else
	    if need_prototype then 
	       rf7.external_c_prototype_from(start_position)
	    end
            c_mapping(rf7.base_feature,false,false,rf7.arg_count)
         end
      end

   jvm_add_method_for_function(rf8: RUN_FEATURE_8; bcn, name: STRING) is
      do
      end

   jvm_define_function(rf8: RUN_FEATURE_8; bcn, name: STRING) is
      do
         fe_c2jvm(rf8)
      end

   jvm_mapping_function(rf8: RUN_FEATURE_8; bcn, name: STRING) is
      do
         fe_c2jvm(rf8)
      end

   jvm_add_method_for_procedure(rf7: RUN_FEATURE_7; bcn, name: STRING) is
      do
      end

   jvm_define_procedure(rf7: RUN_FEATURE_7; bcn, name: STRING) is
      do
         fe_c2jvm(rf7)
      end

   jvm_mapping_procedure(rf7: RUN_FEATURE_7; bcn, name: STRING) is
      do
         fe_c2jvm(rf7)
      end

feature {NONE}

   parsing_done, need_prototype: BOOLEAN

   macro: BOOLEAN
	 -- Is the "macro" keyword used?

   struct: STRING
	 -- Non Void when the "struct" keyword is used. The value is then 
	 -- the appropriate C cast to access the corresponding C struct.

   get, set: STRING
	 -- Non Void if we have to access this `struct' name field.

   signature: FIXED_ARRAY[STRING]
	 -- Non Void when some external `signature' is used.

   c_inline: BOOLEAN
	 -- When the C inline is used.

   parse_external_tag is
      local
	 file_name: STRING
      do
	 if not parsing_done then
	    parsing_done := true
	    from
	       need_prototype := true
	       mini_buffer.start_with(external_tag)
	       mini_buffer.next
	       mini_buffer.skip_separators
	    until
	       mini_buffer.is_off
	    loop
	       if mini_buffer.a_keyword(fz_macro) then
		  macro := true
		  need_prototype := false
	       elseif mini_buffer.a_keyword(fz_struct) then
		  need_prototype := false
		  struct := mini_buffer.a_type_cast
		  if mini_buffer.a_keyword(fz_set) then
		     set := mini_buffer.a_field_or_variable_name
		     get := set
		  elseif mini_buffer.a_keyword(fz_get) then
		     get := mini_buffer.a_field_or_variable_name
		  else
		     bad_external("%"set%" or %"get%" keyword expected.")
		  end
	       elseif mini_buffer.a_keyword(fz_use) then
	       elseif mini_buffer.a_keyword(fz_inline) then
		  c_inline := true
		  need_prototype := false
	       elseif mini_buffer.a_keyword(fz_signature) then
                  signature := mini_buffer.a_signature
	       elseif mini_buffer.item = '(' then
                  signature := mini_buffer.a_signature
	       else
		  inspect
		     mini_buffer.item
		  when ',', '|' then
		     mini_buffer.next
		     mini_buffer.skip_separators
		  else
		     file_name := mini_buffer.a_include
		     need_prototype := false
		     cpp.include_register(start_position,file_name)
		  end
	       end
	    end
	 end
      end

   bad_external(msg: STRING) is
      do
	 error_handler.add_position(start_position)
	 error_handler.append(msg)
	 error_handler.print_as_fatal_error
      end

   c_mapping(er: EXTERNAL_ROUTINE; wrapped, is_function: BOOLEAN
	     arg_count: INTEGER) is
	 -- Where `wrapped' means that the code is wrapped inside 
	 -- some function (-no_check mode).
      local
         tcbd, stop: BOOLEAN; p: POSITION; c_code, arg: STRING
	 cc: CHARACTER; i, arg_idx: INTEGER
      do
	 p := start_position
	 cpp.put_position_comment(p)
	 if signature /= Void and then signature.upper /= arg_count then
	    bad_external("Bad number of arguments of external signature.")
	 end
	 if wrapped then
	    if ace.no_check then
	       cpp.put_trace_or_sedb_instruction(p)
	    end
	    if is_function then cpp.put_string(fz_26); end
	 else
	    tcbd := cpp.target_cannot_be_dropped
	    if tcbd then cpp.put_character(',') end
	 end
         if struct /= Void then
	    if set /= Void then
	       if is_function or else arg_count /= 2 then
		  error_handler.add_position(er.start_position)
		  bad_external("Bad prototype for C struture set external.")
	       end
	    else
	       check get /= Void end
	       if (not is_function) or else arg_count /= 1 then
		  error_handler.add_position(er.start_position)
		  bad_external("Bad prototype for C struture get external.")
	       end
	    end
	    cpp.put_string(fz_79)
	    cpp.put_string(struct)
	    cpp.put_string(once "*)")
	    if wrapped then
	       cpp.put_string(as_a1)
	    else
	       cpp.put_ith_argument(1)
	    end
	    cpp.put_string(fz_72)
	    cpp.put_string(get)
	    cpp.put_character(')')
	    if set /= Void then
	       cpp.put_string(fz_47)
	       if wrapped then
		  cpp.put_string(once "a2")
	       else
		  cpp.put_ith_argument(2)
	       end
	       cpp.put_character(')')
	    end
	 elseif c_inline then
	    from
	       c_code := er.external_name
	       i := 1
	    until
	       i > c_code.count
	    loop
	       cc := c_code.item(i)
	       if cc /= '$' then
		  cpp.put_character(cc)
	       else
		  from
		     i := i + 1
		     arg := once "local buffer..."
		     arg.clear
		     stop := i > c_code.count
		  until
		     stop
		  loop
		     cc := c_code.item(i)
		     inspect
			cc
		     when 'a'..'z', 'A'..'Z', '0'..'9', '_' then
			arg.extend(cc)
			i := i + 1
		     else
			stop := true
			i := i - 1
		     end
		  end
		  arg := string_aliaser.item(arg)
		  arg_idx := er.argument_rank_of(arg)
		  if arg_idx > 0 then
		     if wrapped then
			cpp.put_character('a')
			cpp.put_integer(arg_idx)
		     else
			cpp.put_ith_argument(arg_idx)
		     end
		  else
		     error_handler.add_position(er.start_position)
		     error_handler.add_position(start_position)
		     error_handler.append("Unknown %"$")
		     error_handler.append(arg)
		     error_handler.append("%" argument in external C inline definition.")
		     error_handler.print_as_fatal_error
		  end
	       end
	       i := i + 1
	    end
	 else
	    cpp.put_string(er.external_name)
	    if arg_count > 0 then
	       cpp.put_character('(')
	       from
		  i := 1
	       until
		  i > arg_count
	       loop
		  if signature /= Void then
		     cpp.put_character('(')
		     cpp.put_character('(')
		     cpp.put_string(signature.item(i))
		     cpp.put_character(')')
		  end
		  if wrapped then
		     cpp.put_character('a')
		     cpp.put_integer(i)
		  else
		     cpp.put_ith_argument(i)
		  end
		  if signature /= Void then
		     cpp.put_character(')')
		  end
		  i := i + 1
		  if i <= arg_count then cpp.put_character(',') end
	       end
	       cpp.put_character(')')
	    elseif not macro then
	       cpp.put_string(fz_c_no_args_function)
	    end
	 end
	 if wrapped then
	    if is_function then
	       cpp.put_character(')')
	    end
	    cpp.put_character(';')
	 else
	    if tcbd then cpp.put_character(')') end
	    if not is_function then cpp.put_character(';') end
	 end
	 cpp.put_character('%N')
	 cpp.put_position_comment(p)
      end

   make(et: like external_tag) is
	 -- To handle old external obsolete notation.
      local
	 oldies, new_notation: STRING; obsolete_warning: BOOLEAN
      do
	 Precursor(et)
	 oldies := et.to_string
	 if (once "C_WithoutCurrent").is_equal(oldies) then
	    obsolete_warning := true
	    new_notation := "C"
	 elseif (once "C_WithCurrent").is_equal(oldies) then
	    obsolete_warning := true
	 elseif (once "C_InlineWithoutCurrent").is_equal(oldies) then
	    obsolete_warning := true
	    new_notation := "C macro"
	 elseif (once "C_InlineWithCurrent").is_equal(oldies) then
	    obsolete_warning := true
	 end
	 if obsolete_warning then
	    error_handler.add_position(start_position)
	    error_handler.append("Since release -0.74 this external C definition is %
		      %obsolete. You should update your code with the new %
		      %ETL external specification (see SmartEiffel/tutorial/%
		      %external/C directory for examples).")
	    if new_notation /= Void then
	       et.to_string.copy(new_notation)
	       error_handler.append("Obsolete notation replaced with %"")
	       error_handler.append(new_notation)
	       error_handler.append("%". Update your code before the next release.")
	       error_handler.print_as_warning
	    else
	       error_handler.print_as_error
	    end
	 end
      end

end -- NATIVE_C