--          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 EIFFEL_PARSER
   --
   -- Singleton object in charge of Eiffel parsing.
   -- This singleton is shared via the GLOBALS.`eiffel_parser' once function.
   --

inherit PARSER

feature

   case_insensitive: BOOLEAN
	 -- When flag "-case_insensitive" is on.

   no_style_warning: BOOLEAN
	 -- When flag "-no_style_warning" is on.

   is_running: BOOLEAN
	 -- True when the parser is running (i.e. parsing of the current 
	 -- class is not finished).

feature {SMART_EIFFEL}

   analyse_class(class_name: CLASS_NAME): BASE_CLASS is
      require
	 not is_running
	 not smart_eiffel.is_ready
	 class_name.to_string /= Void
	 parser_buffer.is_ready
      local
	 old_nbe, old_nbw: INTEGER; path: STRING
      do
	 current_id := id_provider.item(class_name.to_string)
	 path := parser_buffer.path
	 if nb_errors > 0 then
	    error_handler.append("Correct previous error(s) first.")
	    error_handler.print_as_fatal_error
	 end
	 debug
	    if smart_eiffel.is_ready then
	       error_handler.append(once "Tried to load class ")
	       error_handler.append(path)
	       error_handler.append(once " while smart_eiffel `is_ready'.")
	       error_handler.print_as_warning
	    end
	 end
	 echo.put_integer(smart_eiffel.base_class_count + 1)
	 echo.put_character('%T')
	 echo.put_string(path)
	 echo.put_character('%N')
	 old_nbe := nb_errors
	 old_nbw := nb_warnings
	 is_running := true
	 inside_function := false
	 inside_once_function := false
	 in_ensure := false
	 last_comment := Void
	 line := 1
	 column := 1
	 current_line := parser_buffer.item(line)
	 if current_line.count = 0 then
	    cc := '%N'
	 else
	    cc := current_line.first
	 end
	 !!last_base_class.make(path,class_name.to_string,
				parser_buffer.cluster,current_id)
	 skip_comments
	 a_class_declaration
	 is_running := false
	 parser_buffer.release
	 Result := last_base_class
	 if nb_errors - old_nbe > 0 then
	    show_nb_errors
	    echo.w_put_string("Load class %"")
	    echo.w_put_string(path)
	    echo.w_put_string("%" aborted.%N")
	    Result := Void
	 elseif nb_warnings - old_nbw > 0 then
	    show_nb_warnings
	    check
	       Result /= Void
	    end
	 end
	 if Result /= Void then
	    Result.get_started
	 end
      ensure
	 not parser_buffer.is_ready
      end

   analyse_buffer: BASE_CLASS is
	 -- Scan the header of the `parser_buffer' in order to find the name
	 -- of the class in order to launch `analyse_class' with the
	 -- appropriate argument. (This is used for to handle the "include"
	 -- option of ACE files.)
      require
	 not is_running
	 not smart_eiffel.is_ready
	 parser_buffer.is_ready
      local
	 stop: BOOLEAN; n: STRING; cn: CLASS_NAME
      do
	 last_comment := Void
	 line := 1
	 column := 1
	 current_line := parser_buffer.item(line)
	 if current_line.count = 0 then
	    cc := '%N'
	 else
	    cc := current_line.first
	 end
	 from
	    skip_comments
	 until
	    stop
	 loop
	    if cc = end_of_text then
	       stop := true
	    elseif a_keyword(fz_class) then
	       stop := true
	       from
		  !!n.make(32)
	       until
		  (once " %T[%/0/%N").has(cc)
	       loop
		  n.extend(cc)
		  next_char
	       end
	       !!cn.unknown_position(string_aliaser.item(n))
	    else
	       from until (once " %T%/0/%N").has(cc)
	       loop
		  next_char
	       end
	       skip_comments
	    end
	 end
	 if cn = Void then
	    error_handler.append("Unable to find a class definition in %"")
	    error_handler.append(parser_buffer.path)
	    error_handler.append("%".")
	    error_handler.print_as_fatal_error
	 end
	 Result := analyse_class(cn)
      end

feature {ACE,COMMAND_LINE_TOOLS}

   set_case_insensitive is
      do
	 case_insensitive := true
      end

   set_no_style_warning is
      do
	 no_style_warning := true
      end

feature {CECIL_FILE}

   connect_to_cecil(a_path: STRING): STRING is
	 -- Return the cecil file user's include path (first information).
      require
	 not is_running
	 nb_errors = 0
	 a_path /= Void
      local
	 path: STRING
      do
	 echo.put_string(once "Parsing Cecil File: %"")
	 echo.put_string(a_path)
	 echo.put_string(fz_18)
	 parser_buffer.load_file(a_path)
	 if not parser_buffer.is_ready then
	    error_handler.append(
	       "Cannot open Cecil file (use -verbose flag for details).")
	    error_handler.print_as_fatal_error
	 end
	 path := parser_buffer.path
	 current_id := id_provider.item(path)
	 is_running := true
	 formal_generic_list := Void
	 inside_function := false
	 inside_once_function := false
	 in_ensure := false
	 last_comment := Void
	 line := 1
	 column := 1
	 current_line := parser_buffer.item(line)
	 last_base_class := Void
	 if current_line.count = 0 then
	    cc := '%N'
	 else
	    cc := current_line.first
	 end
	 skip_comments
	 from
	    !!Result.make(32)
	 until
	    cc = '%N' or else cc = end_of_text
	 loop
	    Result.extend(cc)
	    next_char
	 end
	 skip_comments
	 if cc = end_of_text then
	    error_handler.append(
               "Empty Cecil file (use -verbose flag for details).")
	    error_handler.print_as_fatal_error
	 end
      end

   end_of_input: BOOLEAN is
      do
	 Result := cc = end_of_text
      end

   parse_c_name: STRING is
      do
	 from
	    !!Result.make(32)
	 until
	    cc.is_separator
	 loop
	    Result.extend(cc)
	    next_char
	 end
	 skip_comments
      end

   parse_run_type: E_TYPE is
      do
	 if a_class_type then
	    Result := last_class_type
	 else
	    fcp(em16)
	 end
      ensure
	 nb_errors = 0
      end

   parse_feature_name: FEATURE_NAME is
      do
	 if a_feature_name then
	    Result := last_feature_name
	 else
	    fcp(em2)
	 end
      ensure
	 nb_errors = 0
      end

   disconnect is
      do
	 is_running := false
	 parser_buffer.release
      end

feature {SMART_EIFFEL}

   show_nb_warnings is
      local
	 do_it: BOOLEAN
      do
	 if echo.verbose then
	    do_it := true
	 elseif error_handler.no_warning then
	 else
	    do_it := true
	 end
	 if do_it then
	    show_nb(nb_warnings,once " warning(s).%N")
	 end
      end

   show_nb_errors is
      do
	 show_nb(nb_errors,once " error(s).%N")
      end

feature {COMPILE_TO_C,COMPILE_TO_JVM}

   set_drop_comments is
      do
	 drop_comments := true
      end

feature {TMP_FEATURE}

   ecp(msg: STRING) is
	 -- Error at current position.
      do
	 error(current_position,msg)
      end

feature {NONE}

   current_id: INTEGER
	 -- This is the `id' of the `last_base_class' or the `id' of the ACE file or of
	 -- some cecil file path.

   last_base_class: BASE_CLASS
	 -- The one beeing parsed.

   inside_function: BOOLEAN
	 -- True when a function (once or non-once) is parsed.

   inside_once_function: BOOLEAN
	 -- True when a once function is parsed.

   formal_generic_list: FORMAL_GENERIC_LIST
	 -- Void or not empty list of formal generic arguments.

   in_ensure: BOOLEAN
	 -- True during the parsing of a ensure clause.

   in_rescue: BOOLEAN
	 -- True during the parsing of a rescue clause.

   arguments: FORMAL_ARG_LIST
	 -- Void or actual formal arguments list.

   local_vars: LOCAL_VAR_LIST
	 -- Void or actual local variables list.

   ok: BOOLEAN
	 -- Dummy variable to call functions.

   last_base_type: E_TYPE
   last_bit_constant: BIT_CONSTANT
   last_boolean_constant: BOOLEAN_CONSTANT
   last_character_or_integer: BASE_TYPE_CONSTANT
   last_character_constant: CHARACTER_CONSTANT
   last_class_name: CLASS_NAME
   last_class_type: E_TYPE
   last_expression: EXPRESSION
   last_feature_declaration: E_FEATURE
   last_feature_name: FEATURE_NAME
   last_feature_name_list: FEATURE_NAME_LIST
   last_type_formal_generic: TYPE_FORMAL_GENERIC
   last_integer_constant: INTEGER_CONSTANT
   last_instruction: INSTRUCTION
   last_index_value: EXPRESSION
   last_manifest_constant: EXPRESSION
   last_manifest_string: MANIFEST_STRING
   last_parent: PARENT
   last_real_constant: REAL_CONSTANT
   last_type: E_TYPE
   last_tag_mark: TAG_NAME

   a_argument: BOOLEAN is
      local
	 rank: INTEGER
      do
	 if arguments /= Void then
	    rank := arguments.rank_of(tmp_name.aliased_string)
	    if rank > 0 then
	       last_expression := tmp_name.to_argument_name2(arguments,rank)
	       Result := true
	    end
	 end
      end

   a_current: BOOLEAN is
      do
	 if tmp_name.is_current then
	    !WRITTEN_CURRENT!last_expression.make(tmp_name.start_position)
	    Result := true
	 end
      end

   a_formal_arg_list is
	 --++ formal_arg_list -> ["(" {declaration_group ";" ...} ")"]
	 --++ declaration_group -> {identifier "," ...}+ ":" type
      local
	 name: ARGUMENT_NAME1; name_list: ARRAY[ARGUMENT_NAME1]
	 declaration: DECLARATION; list: ARRAY[DECLARATION]
	 state: INTEGER
      do
	 arguments := Void
	 if skip1('(') then
	    from
	    until
	       state > 4
	    loop
	       inspect
		  state
	       when 0 then -- Waiting for the first name of a group.
		  if a_identifier then
		     name := tmp_name.to_argument_name1
		     state := 1
		  elseif skip1(')') then
		     state := 5
		  else
		     state := 6
		  end
	       when 1 then -- Waiting "," or ":".
		  if skip1(':') then
		     if name_list /= Void then
			name_list.add_last(name)
			name := Void
		     end
		     state := 3
		  else
		     ok := skip1(',')
		     if name_list = Void then
			!!name_list.with_capacity(2,1)
		     end
		     name_list.add_last(name)
		     name := Void
		     state := 2
		  end
	       when 2 then -- Waiting for a name (not the first).
		  if a_identifier then
		     name := tmp_name.to_argument_name1
		     state := 1
		  elseif cc = ',' or else cc = ';' then
		     wcp(em13)
		     ok := skip1(',') or else skip1(';')
		  else
		     state := 6
		  end
	       when 3 then -- Waiting for type mark.
		  if a_type then
		     if name_list /= Void then
			!DECLARATION_GROUP!declaration.make(name_list,
			                                    last_type)
			name_list := Void
		     else
			!DECLARATION_1!declaration.make(name,last_type)
			name := Void
		     end
		     if list = Void then
			!!list.with_capacity(2,1)
		     end
		     list.add_last(declaration)
		     declaration := Void
		     state := 4
		  else
		     state := 6
		  end
	       when 4 then -- Waiting ";" or ")".
		  if skip1(')') then
		     state := 5
		  elseif cc = ',' then
		     wcp(once "Substitute with %";%".")
		     ok := skip1(',')
		     state := 0
		  else
		     ok := skip1(';')
		     state := 0
		  end
	       end
	    end
	    if state = 6 then
	       fcp("Bad formal arguments list.")
	    elseif list = Void then
	       wcp(once "Empty formal argument list (deleted).")
	    else
	       !!arguments.make(list)
	       tmp_feature.set_arguments(arguments)
	    end
	 end
      end

   a_local_var_list is
	 --++ local_var_list -> [{declaration_group ";" ...}]
	 --++ declaration_group -> {identifier "," ...}+ ":" type
      local
	 name: LOCAL_NAME1; name_list: ARRAY[LOCAL_NAME1]
	 declaration: DECLARATION; list: ARRAY[DECLARATION]
	 rank, state: INTEGER
      do
	 from
	 until
	    state > 4
	 loop
	    inspect
	       state
	    when 0 then -- Waiting for the first name of a group.
	       if a_identifier then
		  name := tmp_name.to_local_name1
		  state := 1
		  if arguments /= Void then
		     rank := arguments.rank_of(name.to_string)
		     if rank > 0 then
			error_handler.add_position(name.start_position)
			error(arguments.name(rank).start_position,
			      em26)
		     end
		  end
	       elseif cc = ',' or else cc = ';' then
		  wcp(em13)
		  ok := skip1(',') or else skip1(';')
	       else
		  state := 5
	       end
	    when 1 then -- Waiting "," or ":".
	       if skip1(':') then
		  if name_list /= Void then
		     name_list.add_last(name)
		     name := Void
		  end
		  state := 3
	       else
		  if cc = ';' then
		     wcp(once "Substitute with %",%".")
		     ok := skip1(';')
		  else
		     ok := skip1(',')
		  end
		  if name_list = Void then
		     !!name_list.with_capacity(2,1)
		  end
		  name_list.add_last(name)
		  name := Void
		  state := 2
	       end
	    when 2 then -- Waiting for a name (not the first).
	       if a_identifier then
		  name := tmp_name.to_local_name1
		  state := 1
		  if arguments /= Void then
		     rank := arguments.rank_of(name.to_string)
		     if rank > 0 then
			error_handler.add_position(name.start_position)
			error_handler.add_position(arguments.name(rank).start_position)
			error_handler.append(em26)
			error_handler.print_as_error
		     end
		  end
	       elseif cc = ',' or else cc = ';' then
		  wcp(em13)
		  ok := skip1(',') or else skip1(';')
	       else
		  state := 6
	       end
	    when 3 then -- Waiting for type mark.
	       if a_type then
		  if name_list /= Void then
		     !DECLARATION_GROUP!declaration.make(name_list,last_type)
		     name_list := Void
		  else
		     !DECLARATION_1!declaration.make(name,last_type)
		     name := Void
		  end
		  if list = Void then
		     !!list.with_capacity(2,1)
		  end
		  list.add_last(declaration)
		  state := 4
	       else
		  state := 6
	       end
	    when 4 then -- Waiting ";".
	       if cc = ',' then
		  wcp(once "Substitute with %";%".")
		  ok := skip1(',')
		  state := 0
	       else
		  ok := skip1(';')
		  state := 0
	       end
	    end
	 end
	 if state = 6 then
	    fcp("Bad local variable list.")
	 elseif list /= Void then
	    !!local_vars.make(list)
	    tmp_feature.set_local_vars(local_vars)
	 end
      end

   a_local_variable: BOOLEAN is
      local
	 rank: INTEGER
      do
	 if local_vars /= Void then
	    rank := local_vars.rank_of(tmp_name.aliased_string)
	    if rank > 0 then
	       last_expression := tmp_name.to_local_name2(local_vars,rank)
	       Result := true
	    end
	 end
      end

   a_result: BOOLEAN is
      do
	 if tmp_name.is_result then
	    last_expression := last_result
	    Result := true
	 end
      end

   a_void: BOOLEAN is
      do
	 if tmp_name.is_void then
	    last_expression := tmp_name.to_e_void
	    Result := true
	 end
      end

   get_comment: COMMENT is
      do
	 Result := last_comment
	 last_comment := Void
      end

   go_back_at(l, c: INTEGER) is
	 -- Go back to some existing line `l', and column `c'.
      require
	 l >= 1; c >= 1
      local
	 sp: POSITION
      do
	 line := l
	 column := c
	 current_line := parser_buffer.item(l)
	 cc := current_line.item(c)
	 if last_comment /= Void then
	    sp := last_comment.start_position
	    if l < sp.line then
	       last_comment := Void
	    elseif l = sp.line then
	       if c < sp.column then
		  last_comment := Void
	       end
	    end
	 end
      end

   skip2(c1, c2: CHARACTER): BOOLEAN is
      do
	 if c1 = cc then
	    start_line := line
	    start_column := column
	    next_char
	    if c2 = cc then
	       Result := true
	       next_char
	       skip_comments
	    else
	       cc := c1
	       column := start_column
	    end
	 end
      end

   skip1unless2(c1, c2: CHARACTER): BOOLEAN is
      do
	 start_line := line
	 start_column := column
	 if cc = c1 then
	    next_char
	    if cc = c2 then
	       cc := c1
	       column := start_column
	    else
	       Result := true
	       skip_comments
	    end
	 end
      end

   a_bit_constant: BOOLEAN is
      local
	 l, c: INTEGER; stop: BOOLEAN
      do
	 if cc = '0' or else cc = '1' then
	    from
	       l := line; c := column
	       buffer.clear
	       buffer.extend(cc)
	    until
	       stop
	    loop
	       next_char
	       inspect
		  cc
	       when '0','1' then
		  buffer.extend(cc)
	       when 'b','B' then
		  !!last_bit_constant.make(pos(l,c),buffer.twin)
		  next_char
		  skip_comments
		  stop := true
		  Result := true
	       else
		  go_back_at(l,c)
		  stop := true
	       end
	    end
	 end
      end

   a_character_constant: BOOLEAN is
      local
	 sp: POSITION; state, printing_mode: INTEGER
	 value: CHARACTER
	 ascii_code, ascii_digit_counter: INTEGER
      do
	 if cc = '%'' then
	    from
	       sp := pos(line,column)
	       Result := true
	    until
	       state > 3
	    loop
	       next_char
	       inspect
		  state
	       when 0 then -- First '%'' read.
		  inspect
		     cc
		  when '%%' then
		     state := 1
		  when '%'' then
		     fcp(em10)
		     state := 2
		  else
		     value := cc
		     printing_mode := 0
		     state := 2
		  end
	       when 1 then -- First '%%' read.
		  printing_mode := 1
		  state := 2
		  inspect
		     cc
		  when 'A' then value := '%A'
		  when 'B' then value := '%B'
		  when 'C' then value := '%C'
		  when 'D' then value := '%D'
		  when 'F' then value := '%F'
		  when 'H' then value := '%H'
		  when 'L' then value := '%L'
		  when 'N' then value := '%N'
		  when 'Q' then value := '%Q'
		  when 'R' then value := '%R'
		  when 'S' then value := '%S'
		  when 'T' then value := '%T'
		  when 'U' then value := '%U'
		  when 'V' then value := '%V'
		  when '%%' then value := '%%'
		  when '%'' then value := '%''
		  when '%"' then value := '%"'
		  when '(' then value := '%('
		  when ')' then value := '%)'
		  when '<' then value := '%<'
		  when '>' then value := '%>'
		  when '/' then
		     state := 3
		     printing_mode := 2
		  else
		     fcp(em37)
		  end
	       when 2 then -- Wait for second '%''.
		  inspect
		     cc
		  when '%'' then
		     state := 4
		  else
		     fcp(em10)
		  end
		  next_char
		  skip_comments
	       when 3 then -- Inside ascii code. After opening '/'.
		  inspect
		     cc
		  when '0' .. '9' then
		     ascii_digit_counter := ascii_digit_counter + 1
		     ascii_code := ascii_code * 10 + cc.decimal_value
		  when '/' then
		     value := ascii_code.to_character
		     state := 2
		     if ascii_digit_counter = 0 then
			fcp(em39)
		     elseif ascii_digit_counter > 3 then
			fcp(em40)
		     end
		  else
		     fcp(em38)
		  end
	       end
	    end
	    create last_character_constant.make(sp,value,printing_mode)
	 end
      end

   a_constant: BOOLEAN is
	 -- Only true for constant allowed in "when of inspect".
      local
	 implicit_current: IMPLICIT_CURRENT; sfn: FEATURE_NAME
      do
	 if a_identifier then
	    Result := true
	    sfn := tmp_name.to_simple_feature_name
	    !!implicit_current.make(sfn.start_position)
	    !CALL_0_C!last_expression.make(implicit_current,sfn)
	 elseif a_character_constant then
	    Result := true
	    last_expression := last_character_constant
	 elseif a_integer_constant then
	    Result := true
	    last_expression := last_integer_constant
	 else
	    fcp(fz_iinaiv)
	 end
      end

   a_base_class_name: BOOLEAN is
      local
	 c: INTEGER; stop, do_warning: BOOLEAN
      do
	 if cc.is_letter then
	    from
	       if cc >= 'a' then
		  do_warning := true
		  cc := cc.to_upper
	       end
	       c := column
	       tmp_name.reset(pos(line,c))
	       tmp_name.extend(cc)
	    until
	       stop
	    loop
	       next_char
	       inspect
		  cc
	       when 'A'..'Z','0'..'9','_' then
		  tmp_name.extend(cc)
	       when 'a'..'z' then
		  do_warning := true
		  tmp_name.extend(cc.to_upper)
	       else
		  stop := true
	       end
	    end
	    if tmp_name.isa_keyword then
	       cc := tmp_name.buffer.first
	       column := c
	    else
	       Result := true
	       skip_comments
	       if do_warning then
		  warning(tmp_name.start_position,em14)
	       end
	       last_class_name := tmp_name.to_class_name
	    end
	 end
      end

   a_base_class_name1 is
	 -- Read the current base class name.
      local
	 cn: CLASS_NAME
	 bc: BASE_CLASS
      do
	 bc := last_base_class
	 cn := bc.name
	 cn.set_accurate_position(pos(line,column))
	 if a_base_class_name then
	    if last_class_name.to_string /= cn.to_string then
	       error_handler.add_position(last_class_name.start_position)
	       error_handler.append(fz_01)
	       error_handler.append(bc.path)
	       error_handler.append("%" does not contain class %"")
	       error_handler.append(cn.to_string)
	       error_handler.append(fz_03)
	       error_handler.print_as_fatal_error
	    end
	 else
	    fcp(em15)
	 end
	 if as_none = cn.to_string then
	    error_handler.add_position(cn.start_position)
	    error_handler.append("Cannot write such a class.")
	    error_handler.print_as_fatal_error
	 end
      end

   a_type_formal_generic: BOOLEAN is
      local
	 fga: FORMAL_GENERIC_ARG
	 cn: CLASS_NAME
	 rank: INTEGER
      do
	 if formal_generic_list /= Void then
	    from
	       rank := 1
	    until
	       Result or else rank > formal_generic_list.count
	    loop
	       fga := formal_generic_list.item(rank)
	       if a_keyword(fga.name.to_string) then
		  !!cn.make(fga.name.to_string,pos(start_line,start_column))
		  !!last_type_formal_generic.make(cn,fga,rank)
		  Result := true
	       end
	       rank := rank + 1
	    end
	 end
      end

   a_free_operator_definition(prefix_flag: BOOLEAN): BOOLEAN is
	 -- A free operator name definition (the one which comes after the 
	 -- "infix" keyword or the "prefix" keyword at the definition 
	 -- place). A free operator must start and finish with one of the 
	 -- following character:  +-*/\=<>@#|&
      local
	 stop: BOOLEAN; l, c: INTEGER
      do
	 inspect
	    cc
	 when '+','-','*','/','\','=','<','>','@','#','|','&' then
	    l := line; c := column; buffer.clear
	    Result := true
	    from
	       buffer.extend(cc)
	       next_char
	    until
	       stop
	    loop
	       inspect
		  cc
	       when '.','?','{','}' then
		  Result := False
		  buffer.extend(cc)
		  next_char
	       when '+','-','*','/','\','=','<','>','@','#','|','&','^' then
		  Result := True
		  buffer.extend(cc)
		  next_char
	       else
		  stop := True
	       end
	    end
	    if not Result then
	       error_handler.add_position(pos(line,column))
	       error_handler.append(em43)
	       error_handler.print_as_fatal_error
	    end
	    if buffer.count = 1 and then buffer.first = '=' then
	       error_handler.add_position(pos(l,c))
	       error_handler.append(
		"The basic = operator cannot be redefined. (This is a %
		%hard-coded builtin that we must trust.)")
	       error_handler.print_as_fatal_error
	    end
	    create_infix_prefix(prefix_flag, l, c)
	 else
	 end
      end
   
   a_free_operator_usage(prefix_flag: BOOLEAN): BOOLEAN is
	 -- Syntactically, a free operator must start and finish with one 
	 -- of the following set of characters:  +-*/\=<>@#|&
	 -- Because of priority, traditional operators are not handled here.
      local
	 stop: BOOLEAN; l, c: INTEGER
      do
	 inspect
	    cc
	 when '+','-','*','/','\','=','<','>','@','#','|','&' then
	    l := line; c := column; buffer.clear
	    from
	       Result := True
	       buffer.extend(cc)
	       next_char
	    until
	       stop
	    loop
	       inspect
		  cc
	       when '.','?','{','}' then
		  Result := False
		  buffer.extend(cc)
		  next_char
	       when '+','-','*','/','\','=','<','>','@','#','|','&','^' then
		  Result := True
		  buffer.extend(cc)
		  next_char
	       else
		  stop := True
	       end
	    end
	    -- Elimination of traditional operators which are handled 
	    -- by specific class as well as some other usage in order  
	    -- to provide backward compatibility:
	    if Result then
	       inspect
		  buffer.count
	       when 1 then
		  inspect
		     buffer.first
		  when '+', '-', '*', '/', '=', '<', '>' then
		     Result := False
		  else
		  end
	       when 2 then
		  inspect
		     buffer.first
		  when '>' then
		     inspect
			buffer.last
		     when '=', '>' then
			Result := False
		     else
		     end
		  when '/', '\', '<' then
		     inspect
			buffer.last
		     when '=', '\', '/' then
			Result := False
		     else
		     end
		  else
		  end
	       else
		  Result := buffer.occurrences('>') /= buffer.count
	       end
	    end
	    if Result then
	       skip_comments
	       create_infix_prefix(prefix_flag, l, c)
	    else
	       go_back_at(l, c)
	    end
	 else
	 end
      end
   
   a_identifier: BOOLEAN is
      do
	 if case_insensitive then
	    Result := a_identifier1
	 else
	    Result := a_identifier2
	 end
      end

   a_integer(sign: INTEGER_8): BOOLEAN is
      require
	 sign = -1 or sign = 0
      local
	 state, l, c: INTEGER; value: INTEGER_64
      do
	 -- ***Should detect that constant like 92233720368547758085 is too big
	 if cc.is_digit then
	    from
	       l := line; c := column
	       value := -cc.value
	    until
	       state > 7
	    loop
	       next_char
	       inspect
		  state
	       when 0 then -- 1st digit read (no '_' encountered).
		  inspect
		     cc
		  when '0'..'9' then
		     value := value * 10 - cc.value
		     state := 1
		  when '_' then
		     state := 4
		  else
		     state := 8
		  end
	       when 1 then -- 2nd digit read (no '_' encountered).
		  inspect
		     cc
		  when '0'..'9' then
		     value := value * 10 - cc.value
		     state := 2
		  when '_' then
		     state := 4
		  else
		     state := 8
		  end
	       when 2 then -- 3rd digit read (no '_' encountered).
		  inspect
		     cc
		  when '0'..'9' then
		     value := value * 10 - cc.value
		     state := 3
		  when '_' then
		     state := 4
		  else
		     state := 8
		  end
	       when 3 then -- More than 3 digit read (no '_' encountered).
		  inspect
		     cc
		  when '0'..'9' then
		     value := value * 10 - cc.value
		  when '_' then
		     fcp(em9)
		  else
		     state := 8
		  end
	       when 4 then -- After '_'.
		  inspect
		     cc
		  when '0'..'9' then
		     value := value * 10 - cc.value
		     state := 5
		  else
		     fcp(em9)
		  end
	       when 5 then -- After '_' and 1 digit read.
		  inspect
		     cc
		  when '0'..'9' then
		     value := value * 10 - cc.value
		     state := 6
		  else
		     fcp(em9)
		  end
	       when 6 then -- After '_' and 2 digit read.
		  inspect
		     cc
		  when '0'..'9' then
		     value := value * 10 - cc.value
		     state := 7
		  else
		     fcp(em9)
		  end
	       when 7 then -- After '_' and 3 digit read.
		  inspect
		     cc
		  when '0'..'9' then
		     fcp(em9)
		  when '_' then
		     state := 4
		  else
		     state := 8
		  end
	       end
	    end -- loop
	    if cc.is_letter then
	       go_back_at(l, c)
	    else
	       Result := true
	       skip_comments
	       create last_integer_constant.make(sign, value, pos(l,c))
	    end
	 end
      end

   a_manifest_string: BOOLEAN is
      local
	 state, l, c: INTEGER; is_once, stop: BOOLEAN
	 ascii_code, ascii_digit_counter: INTEGER
	 source_view: FIXED_ARRAY[STRING]; line_view: STRING
	 multi_line_end: STRING; left_alignment, current_alignment: INTEGER
      do
	 l := line; c := column
	 if a_keyword(fz_once) then
	    is_once := true
	 end
	 if cc = '%"' then
	    Result := true
	    if smart_eiffel.short_flag or else smart_eiffel.pretty_flag then
	       create source_view.with_capacity(4)
	       create line_view.make(32)
	       source_view.add_last(line_view)
	    end
	    from
	       buffer.clear
	    until
	       state > 7
	    loop
	       if source_view /= Void then
		  line_view.extend(cc)
		  if cc = '%N' then
		     create line_view.make(32)
		     source_view.add_last(line_view)
		  end
	       end
	       next_char
	       inspect
		  state
	       when 0 then -- Somewhere inside the manifest string.
		  inspect
		     cc
		  when '%N' then
		     from until	multi_line_end /= Void
		     loop
			if buffer.is_empty then
			   fcp(em8)
			else
			   inspect
			      buffer.last
			   when ' ', '%T' then
			      buffer.remove_last(1)
			      wcp(em42)
			   when '{', '[' then
			      multi_line_end := once "]foo%""
			      multi_line_end.clear
			      if buffer.last = '{' then
				 multi_line_end.extend('}')
				 left_alignment := 1
				 state := 6
			      else
				 multi_line_end.extend(']')
				 state := 5
			      end
			      multi_line_end.append(buffer)
			      current_alignment := 1
			      multi_line_end.remove_last(1)
			      multi_line_end.extend('%"')
			      buffer.clear
			   else
			      fcp(em8)
			   end
			end
		     end
		  when '%"' then
		     state := 9
		  when '%%' then
		     state := 1
		  else
		     buffer.extend(cc)
		  end
	       when 1 then -- Just after a '%%'.
		  state := 0
		  inspect
		     cc
		  when '%N' then
		     state := 3
		  when 'A' then buffer.extend('%A')
		  when 'B' then buffer.extend('%B')
		  when 'C' then buffer.extend('%C')
		  when 'D' then buffer.extend('%D')
		  when 'F' then buffer.extend('%F')
		  when 'H' then buffer.extend('%H')
		  when 'L' then buffer.extend('%L')
		  when 'N' then buffer.extend('%N')
		  when 'Q' then buffer.extend('%Q')
		  when 'R' then buffer.extend('%R')
		  when 'S' then buffer.extend('%S')
		  when 'T' then buffer.extend('%T')
		  when 'U' then buffer.extend('%U')
		  when 'V' then buffer.extend('%V')
		  when '%%' then buffer.extend('%%')
		  when '%'' then buffer.extend('%'')
		  when '%"' then buffer.extend('%"')
		  when '(' then buffer.extend('%(')
		  when ')' then buffer.extend('%)')
		  when '<' then buffer.extend('%<')
		  when '>' then buffer.extend('%>')
		  when '/' then
		     ascii_code := 0
		     ascii_digit_counter := 0
		     state := 4
		  when ' ','%T' then
		     state := 2
		  else
		     fcp(em37)
		     state := 0
		  end
	       when 2 then -- Extended form. At end of line.
		  inspect
		     cc
		  when '%N' then
		     state := 3
		  when ' ','%T' then
		     wcp(em42)
		  else
		     fcp("In extended form of manifest string.%
			 %Bad character after '%%'.")
		  end
	       when 3 then  -- Extended form. At beginning of line.
		  inspect
		     cc
		  when ' ','%T' then
		  when '%%' then
		     state := 0
		  when '%N' then
		     fcp(em8)
		  else
		     fcp("In extended form of manifest string.%
			 % Bad character before '%%'.")
		  end
	       when 4 then  -- Inside ascii code. After opening '/'.
		  inspect
		     cc
		  when '0' .. '9' then
		     ascii_digit_counter := ascii_digit_counter + 1
		     ascii_code := ascii_code * 10 + cc.decimal_value
		  when '/' then
		     buffer.extend(ascii_code.to_character)
		     state := 0
		     if ascii_digit_counter = 0 then
			fcp(em39)
		     elseif ascii_digit_counter > 3 then
			fcp(em40)
		     end
		  else
		     fcp(em38)
		  end
	       when 5 then -- Just after the multi-line opener, in left
		  -- alignment mode.
		  check left_alignment = 0 end
		  inspect
		     cc
		  when ' ', '%T' then
		     current_alignment := current_alignment + 1
		  when '%N' then
		     buffer.extend('%N')
		     current_alignment := 1
		  when end_of_text then
		     error_handler.add_position(pos(l,c))
		     fcp(em41)
		  else
		     buffer.extend(cc)
		     if left_alignment = 0 then
			left_alignment := current_alignment
		     end
		     state := 6
		  end
	       when 6 then -- Inside multi-line manifest string.
		  current_alignment := current_alignment + 1
		  inspect
		     cc
		  when ' ', '%T' then
		     if current_alignment >= left_alignment then
			buffer.extend(cc)
		     end
		  when '%N' then
		     buffer.extend(cc)
		     current_alignment := 0
		  when end_of_text then
		     error_handler.add_position(pos(l,c))
		     fcp(em41)
		  when '%"' then
		     buffer.extend(cc)
		     if buffer.has_suffix(multi_line_end) then
			state := 8
		     end
		  else
		     if current_alignment < left_alignment then
			if cc = multi_line_end.first then
			   buffer.extend(cc)
			   state := 7
			else
			   error_handler.add_position(pos(l,c))
			   fcp(em41)
			end
		     else
			buffer.extend(cc)
		     end
		  end
	       when 7 then -- Inside multi-line closer.
		  inspect
		     cc
		  when end_of_text then
		     error_handler.add_position(pos(l,c))
		     fcp(em41)
		  when '%"' then
		     buffer.extend(cc)
		     if buffer.has_suffix(multi_line_end) then
			state := 8
		     end
		  else
		     buffer.extend(cc)
		  end
	       end
	    end
	    if state = 8 then -- Good verbatim string.
	       buffer.remove_suffix(multi_line_end)
	       from
	       until stop
	       loop
		  if buffer.is_empty then
		     stop := true
		  else
		     stop := buffer.last = '%N'
		     buffer.remove_last(1)
		  end
	       end
	    end
	    if source_view /= Void then line_view.extend('%"') end
	    create last_manifest_string.make(pos(l,c),is_once,buffer)
	    last_manifest_string.set_source_view(source_view)
	    next_char
	    skip_comments
	 elseif is_once then
	    go_back_at(l,c)
	 end
      end

   a_real(sign: INTEGER_8): BOOLEAN is
      require
	 sign = -1 or sign = 0
      local
	 state, l, c: INTEGER
      do
	 if cc.is_digit or else cc = '.' then
	    from
	       l := line; c := column
	       buffer.clear
	       if sign < 0 then
		  buffer.extend('-')
	       end
	       if cc = '.' then
		  buffer.append(fz_59)
		  state := 5
	       else
		  buffer.extend(cc)
	       end
	    until
	       state > 11
	    loop
	       next_char
	       inspect
		  state
	       when 0 then -- Reading integral part.
		  inspect
		     cc
		  when '0' .. '9' then
		     buffer.extend(cc)
		  when '.' then
		     buffer.extend('.')
		     state := 4
		  else
		     state := 13
		  end
	       when 1 then -- Reading integral part after '_'.
		  inspect
		     cc
		  when '0'..'9' then
		     buffer.extend(cc)
		     state := 2
		  else
		     fcp(em9)
		  end
	       when 2 then -- Reading integral part after '_' and 1 digit.
		  inspect
		     cc
		  when '0'..'9' then
		     buffer.extend(cc)
		     state := 3
		  else
		     fcp(em9)
		  end
	       when 3 then -- Reading integral part after '_' and 2 digits.
		  inspect
		     cc
		  when '0'..'9' then
		     buffer.extend(cc)
		     state := 0
		  else
		     fcp(em9)
		  end
	       when 4 then -- The '.' is read and non empty integral_part.
		  inspect
		     cc
		  when '0'..'9' then
		     buffer.extend(cc)
		     state := 6
		  when 'E','e' then
		     buffer.extend('E')
		     state := 10
		  else
		     state := 12
		  end
	       when 5 then -- The '.' is read and empty integral_part.
		  inspect
		     cc
		  when '0'..'9' then
		     buffer.extend(cc)
		     state := 6
		  else
		     state := 13
		  end
	       when 6 then -- Reading frac_part.
		  inspect
		     cc
		  when '0'..'9' then
		     buffer.extend(cc)
		  when 'E','e' then
		     buffer.extend('E')
		     state := 10
		  when '_' then
		     state := 7
		  else
		     state := 12
		  end
	       when 7 then -- Reading frac_part after '_'.
		  inspect
		     cc
		  when '0'..'9' then
		     buffer.extend(cc)
		     state := 8
		  else
		     fcp(em1)
		  end
	       when 8 then -- Reading frac_part after '_' and 1 digit.
		  inspect
		     cc
		  when '0'..'9' then
		     buffer.extend(cc)
		     state := 9
		  else
		     fcp(em1)
		  end
	       when 9 then -- Reading frac_part after '_' and 2 digits.
		  inspect
		     cc
		  when '0'..'9' then
		     buffer.extend(cc)
		     state := 6
		  else
		     fcp(em1)
		  end
	       when 10 then -- 'E' or 'e' read.
		  inspect
		     cc
		  when '+' then
		     state := 11
		  when '-' then
		     buffer.extend('-')
		     state := 11
		  when '0'..'9' then
		     buffer.extend(cc)
		     state := 11
		  else
		     fcp("Exponent of a real value expected.")
		     state := 13
		  end
	       else -- Reading exponent.
		  inspect
		     cc
		  when '0'..'9' then
		     buffer.extend(cc)
		  else
		     state := 12
		  end
	       end
	    end
	    if state = 12 then
	       create last_real_constant.make(pos(l,c),buffer.twin)
	       Result := true
	       skip_comments
	    else
	       go_back_at(l,c)
	    end
	 end
      end

   a_retry: BOOLEAN is
      do
	 if a_keyword(fz_retry) then
	    if not in_rescue then
	       error(pos(start_line,start_column),
		     "%"retry%" cannot be outside of a rescue clause.")
	    end
	    !E_RETRY!last_instruction.make(pos(start_line,start_column))
	    Result := true
	 end
      end

   a_actual: BOOLEAN is
	 --++ actual -> expression | "$" identifier | "?"
	 --++
      local
	 l,c: INTEGER; type: E_TYPE; sfn: FEATURE_NAME
      do
	 if skip1('%D') then
	    if a_identifier then
	       if a_result or else a_void or else a_current then
		  error_handler.add_position(last_expression.start_position)
		  error_handler.append(fz_vuar4)
		  error_handler.print_as_fatal_error
	       else
		  sfn := tmp_name.to_simple_feature_name
		  !ADDRESS_OF!last_expression.make(sfn)
		  Result:= true
	       end
	    else
	       fcp(fz_vuar4)
	    end
	 elseif skip1('?') then
	    Result := true
	    l := start_line; c := start_column
	    create {OPEN_OPERAND} last_expression.make(pos(l,c))
	 elseif skip1('{') then
	    l := start_line; c := start_column
	    if not a_type then fcp(em32) end
	    type := last_type
	    if not skip1('}') then fcp(em36) end
	    if a_keyword(as_precursor) then
	       go_back_at(l,c)
	    else
	       Result := true
	       create {OPEN_OPERAND} last_expression.with(pos(l,c),type)
	    end
	 elseif a_expression then
	    Result := true
	 end
      end

   a_actuals: EFFECTIVE_ARG_LIST is
	 --++ actuals -> "(" {actual "," ...} ")"
	 --++
      local
	 first_one: EXPRESSION; remainder: FIXED_ARRAY[EXPRESSION]
      do
	 if skip1('(') then
	    from
	    until
	       not a_actual
	    loop
	       if first_one = Void then
		  first_one := last_expression
	       else
		  if remainder = Void then
		     !!remainder.with_capacity(4)
		  end
		  remainder.add_last(last_expression)
	       end
	       if not skip1(',') and then cc /= ')' then
	          wcp(em5)
	       end
	    end
	    if first_one = Void then
	       wcp(once "Empty argument list (deleted).")
	    else
	       !!Result.make_n(first_one,remainder)
	    end
	    if not skip1(')') then
	       fcp("')' expected to end arguments list.")
	    end
	 end
      end

   a_after_a_dot(do_instruction: BOOLEAN; target: EXPRESSION) is
	 --++ after_a_dot -> identifier [actuals] ["." after_a_dot]
	 --++
      require
	 target /= Void
      local
	 sfn: FEATURE_NAME; eal: EFFECTIVE_ARG_LIST
      do
	 if a_identifier then
	    if a_result or else a_void or else a_current then
	       error(last_expression.start_position,
		     "This name must not appear after a dot.")
	    end
	    sfn := tmp_name.to_simple_feature_name
	    eal := a_actuals
	    a_r10(do_instruction,target,sfn,eal,false)
	 else
	    fcp("Identifier expected after a dot.")
	 end
      end

   a_assignment_or_call: BOOLEAN is
	 --++ assignment_or_call -> "(" expression ")" r10 |
	 --++                       precursor_call |
	 --++                       "Current" r10 |
	 --++                       "Result" r10 |
	 --++                       local_variable r10 |
	 --++                       formal_argument r10 |
	 --++                       writable ":=" expression |
	 --++                       writable "?=" expression |
	 --++                       identifier procedure_call
	 --++
      do
	 if skip1('(') and then a_expression then
	    Result := true
	    if skip1(')') then
	       a_r10(true,last_expression,Void,Void,false)
	    else
	       fcp("')' expected.")
	    end
	 elseif a_precursor(true) then
	    Result := true
	 elseif a_identifier then
	    Result := true
	    if skip2(':','=') then
	       a_assignment_aux(true)
	    elseif skip2('?','=') then
	       a_assignment_aux(false)
            elseif a_argument then
	       a_r10(true,last_expression,Void,Void,true)
	    elseif a_current or else a_result or else a_local_variable then
	       a_r10(true,last_expression,Void,Void,false)
	    else
	       a_procedure_call
	    end
	 end
      end

   a_assignment_aux(regular: BOOLEAN) is
      local
	 writable, rhs: EXPRESSION
      do
	 if a_current then
	    error_handler.add_position(last_expression.start_position)
	    error_handler.append("Must not affect `Current'.")
	    error_handler.print_as_fatal_error
	 elseif a_void then
	    error_handler.add_position(tmp_name.start_position)
	    error_handler.append("Must not affect `Void'.")
	    error_handler.print_as_fatal_error
	 elseif a_argument then
	    error_handler.add_position(last_expression.start_position)
	    error_handler.append("Must not affect a formal argument.")
	    error_handler.print_as_fatal_error
	 else
	    if tmp_name.is_result then
	       writable := last_result
	    elseif a_local_variable then
	       writable := last_expression
	    else
	       writable := tmp_name.to_simple_feature_name
	    end
	    if a_expression then
	       rhs := last_expression
	       if regular then
		  !ASSIGNMENT!last_instruction.make(writable,rhs)
	       else
		  !ASSIGNMENT_ATTEMPT!last_instruction.make(writable,rhs)
	       end
	    else
	       fcp("Right hand side expression expected for assignment.")
	    end
	 end
      end

   a_assertion_buffer: ARRAY[ASSERTION] is
	 -- Used only inside `a_assertion'.
      once
	 !!Result.with_capacity(32,1)
      end

   a_assertion: ARRAY[ASSERTION] is
	 --++ assertion -> {assertion_clause ";" ...}
	 --++ assertion_clause -> [identifier ":"] [expression] [comment]
	 --++
      local
	 tag: like last_tag_mark; expression: like last_expression
	 assertion: ASSERTION; state: INTEGER
      do
	 from
	    a_assertion_buffer.clear
	 until
	    state > 3
	 loop
	    inspect
	       state
	    when 0 then -- Nothing read.
	       if cc = ';' then
		  wcp(em24)
		  ok := skip1(';')
	       elseif last_comment /= Void then
		  !!assertion.make(Void,Void,get_comment)
		  a_assertion_buffer.add_last(assertion)
	       elseif a_tag_mark then
		  tag := last_tag_mark
		  state := 1
	       elseif a_expression then
		  expression := last_expression
		  state := 2
	       else
		  state := 4
	       end
	    when 1 then -- Read a `tag'.
	       if skip1(';') then
		  !!assertion.make(tag,Void,get_comment)
		  a_assertion_buffer.add_last(assertion)
		  state := 0
	       elseif a_tag_mark then
		  !!assertion.make(tag,Void,get_comment)
		  a_assertion_buffer.add_last(assertion)
		  tag := last_tag_mark
	       elseif a_expression then
		  expression := last_expression
		  state := 3
	       else
		  !!assertion.make(tag,Void,get_comment)
		  a_assertion_buffer.add_last(assertion)
		  state := 4
	       end
	    when 2 then -- Read an `expression'.
	       if skip1(';') then
		  !!assertion.make(Void,expression,get_comment)
		  a_assertion_buffer.add_last(assertion)
		  state := 0
	       elseif a_tag_mark then
		  !!assertion.make(Void,expression,get_comment)
		  a_assertion_buffer.add_last(assertion)
		  tag := last_tag_mark
		  state := 1
	       elseif a_expression then
		  !!assertion.make(Void,expression,get_comment)
		  a_assertion_buffer.add_last(assertion)
		  expression := last_expression
		  state := 2
	       else
		  !!assertion.make(Void,expression,get_comment)
		  a_assertion_buffer.add_last(assertion)
		  state := 4
	       end
	    else -- Read a `tag' and an `expression'.
	       if skip1(';') then
		  !!assertion.make(tag,expression,get_comment)
		  a_assertion_buffer.add_last(assertion)
		  state := 0
	       elseif a_tag_mark then
		  !!assertion.make(tag,expression,get_comment)
		  a_assertion_buffer.add_last(assertion)
		  tag := last_tag_mark
		  state := 1
	       elseif a_expression then
		  !!assertion.make(tag,expression,get_comment)
		  a_assertion_buffer.add_last(assertion)
		  expression := last_expression
		  state := 2
	       else
		  !!assertion.make(tag,expression,get_comment)
		  a_assertion_buffer.add_last(assertion)
		  state := 4
	       end
	    end
	 end
	 if not a_assertion_buffer.is_empty then
	    Result := a_assertion_buffer.twin
	 end
      end

   a_base_type: BOOLEAN is
	 --++ base_type -> "ANY" | ARRAY "[" type "]" | "BOOLEAN" |
	 --++         "CHARACTER" | "DOUBLE" | "INTEGER" | "NONE" |
	 --++         "POINTER" | "REAL" | "STRING" | "TUPLE" |
	 --++         "ROUTINE" | "PROCEDURE" | "FUNCTION" | "PREDICATE"
	 --++
      local
	 sp: POSITION; types: ARRAY[E_TYPE]
	 type1, type2, type3: E_TYPE; tuple: TYPE_TUPLE
      do
	 Result := true
	 if a_keyword(as_any) then
	    create {TYPE_ANY}
	       last_base_type.make(pos(start_line,start_column))
	 elseif a_keyword(as_array) then
	    sp := pos(start_line,start_column)
	    if skip1('[') and then a_type and then skip1(']') then
	       check
		  last_type /= Void
	       end
	       create {TYPE_ARRAY}
	          last_base_type.make(sp,last_type)
	    else
	       fcp("Bad use of predefined type ARRAY.")
	    end
	 elseif a_keyword(as_native_array) then
	    sp := pos(start_line,start_column)
	    if skip1('[') and then a_type and then skip1(']') then
	       check
		  last_type /= Void
	       end
	       create {TYPE_NATIVE_ARRAY}
	          last_base_type.make(sp,last_type)
	    else
	       fcp("Bad use of predefined type NATIVE_ARRAY.")
	    end
	 elseif a_keyword(as_boolean) then
	    create {TYPE_BOOLEAN}
	       last_base_type.make(pos(start_line,start_column))
	 elseif a_keyword(as_character) then
	    create {TYPE_CHARACTER}
	       last_base_type.make(pos(start_line,start_column))
	 elseif a_keyword(as_double) then
	    create {TYPE_DOUBLE}
	       last_base_type.make(pos(start_line,start_column))
	 elseif a_keyword(as_pointer_ref) then
	    old_ref_style_compatibility(
	       create {TYPE_POINTER}.make(pos(start_line,start_column)))
	 elseif a_keyword(as_boolean_ref) then
	    old_ref_style_compatibility(
	       create {TYPE_BOOLEAN}.make(pos(start_line,start_column)))
	 elseif a_keyword(as_character_ref) then
	    old_ref_style_compatibility(
	       create {TYPE_CHARACTER}.make(pos(start_line,start_column)))
	 elseif a_keyword(as_integer_ref) then
	    old_ref_style_compatibility(
	       create {TYPE_INTEGER}.integer(pos(start_line,start_column)))
	 elseif a_keyword(as_real_ref) then
	    old_ref_style_compatibility(
	       create {TYPE_REAL}.make(pos(start_line,start_column)))
	 elseif a_keyword(as_double_ref) then
	    old_ref_style_compatibility(
	       create {TYPE_DOUBLE}.make(pos(start_line,start_column)))
	 elseif a_keyword(as_integer) then
	    create {TYPE_INTEGER}
	       last_base_type.integer(pos(start_line,start_column))
	 elseif a_keyword(as_integer_8) then
	    create {TYPE_INTEGER}
	       last_base_type.integer_8(pos(start_line,start_column))
	 elseif a_keyword(as_integer_16) then
	    create {TYPE_INTEGER}
	       last_base_type.integer_16(pos(start_line,start_column))
	 elseif a_keyword(as_integer_32) then
	    create {TYPE_INTEGER}
	       last_base_type.integer_32(pos(start_line,start_column))
	 elseif a_keyword(as_integer_64) then
	    create {TYPE_INTEGER}
	       last_base_type.integer_64(pos(start_line,start_column))
	 elseif a_keyword(as_integer_general) then
	    create {TYPE_INTEGER}
	       last_base_type.integer_general(pos(start_line,start_column))
	 elseif a_keyword(as_none) then
	    create {TYPE_NONE}
	       last_base_type.make(pos(start_line,start_column))
	 elseif a_keyword(as_pointer) then
	    create {TYPE_POINTER}
	       last_base_type.make(pos(start_line,start_column))
	 elseif a_keyword(as_real) then
	    create {TYPE_REAL}
	       last_base_type.make(pos(start_line,start_column))
	 elseif a_keyword(as_string) then
	    create {TYPE_STRING}
	       last_base_type.make(pos(start_line,start_column))
	 elseif a_keyword(as_tuple) then
	    sp := pos(start_line,start_column)
	    if skip1('[') then
	       if cc = ']' then
		  wcp(em35)
		  if skip1(']') then end
	       else
		  from
		     !!types.with_capacity(4,1)
		  until
		     skip1(']')
		  loop
		     if a_type then
			types.add_last(last_type)
			if not skip1(',') then
			   if cc /= ']' then
			      wcp(em5)
			   end
			end
		     else
			error_handler.add_position(current_position)
			error_handler.append("Incorrect TUPLE (type expected).")
			error_handler.print_as_fatal_error
		     end
		  end
	       end
	    end
	    create {TYPE_TUPLE} last_base_type.make(sp,types)
	 elseif a_keyword(as_routine) then
	    sp := pos(start_line,start_column)
	    if not skip1('[') then fcp(em30); end
	    if not a_type then fcp(em32) end
	    type1 := last_type
	    if not skip1(',') then wcp(em5); end
	    if not a_type then fcp(em32) end
	    type2 := last_type
	    tuple ?= type2
	    if tuple = Void then
	       error_handler.add_position(type2.start_position)
	       error_handler.append(em33)
	       error_handler.print_as_fatal_error
	    end
	    if not skip1(']') then fcp(em31); end
	    create {TYPE_ROUTINE} last_base_type.make(sp,type1,tuple)
	 elseif a_keyword(as_procedure) then
	    sp := pos(start_line,start_column)
	    if not skip1('[') then fcp(em30); end
	    if not a_type then fcp(em32) end
	    type1 := last_type
	    if not skip1(',') then wcp(em5); end
	    if not a_type then fcp(em32) end
	    type2 := last_type
	    tuple ?= type2
	    if tuple = Void then
	       error_handler.add_position(type2.start_position)
	       error_handler.append(em33)
	       error_handler.print_as_fatal_error
	    end
	    if not skip1(']') then fcp(em31); end
	    create {TYPE_PROCEDURE} last_base_type.make(sp,type1,tuple)
	 elseif a_keyword(as_predicate) then
	    sp := pos(start_line,start_column)
	    if not skip1('[') then fcp(em30); end
	    if not a_type then fcp(em32) end
	    type1 := last_type
	    if not skip1(',') then wcp(em5); end
	    if not a_type then fcp(em32) end
	    type2 := last_type
	    tuple ?= type2
	    if tuple = Void then
	       error_handler.add_position(type2.start_position)
	       error_handler.append(em33)
	       error_handler.print_as_fatal_error
	    end
	    if not skip1(']') then fcp(em31); end
	    create {TYPE_FUNCTION} last_base_type.make(sp,type1,tuple,Void)
	 elseif a_keyword(as_function) then
	    sp := pos(start_line,start_column)
	    if not skip1('[') then fcp(em30); end
	    if not a_type then fcp(em32) end
	    type1 := last_type
	    if not skip1(',') then wcp(em5); end
	    if not a_type then fcp(em32) end
	    type2 := last_type
	    tuple ?= type2
	    if tuple = Void then
	       error_handler.add_position(type2.start_position)
	       error_handler.append(em33)
	       error_handler.print_as_fatal_error
	    end
	    if not skip1(',') then wcp(em5); end
	    if not a_type then fcp(em32) end
	    type3 := last_type
	    if not skip1(']') then fcp(em31); end
	    create {TYPE_FUNCTION} last_base_type.make(sp,type1,tuple,type3)
	 else
	    Result := false
	 end
      end

   a_binary(sp: POSITION): BOOLEAN is
	 --++ binary -> "<=" | ">=" | "//" | "\\" |
	 --++           "+" | "-" | "*" | "/" | "<" | ">" | "^" |
	 --++           xor" | "implies" | "and then" | "and" |
	 --++           "or else" | "or"
	 --++
      do
	 Result := true
	 if skip2('<','=') then
	    create last_feature_name.infix_name(as_le,sp)
	 elseif skip2('>','=') then
	    create last_feature_name.infix_name(as_ge,sp)
	 elseif skip2('/','/') then
	    create last_feature_name.infix_name(as_slash_slash,sp)
	 elseif skip2('\','\') then
	    create last_feature_name.infix_name(as_backslash_backslash,sp)
	 elseif skip1('+') then
	    create last_feature_name.infix_name(as_plus,sp)
	 elseif skip1('-') then
	    create last_feature_name.infix_name(as_minus,sp)
	 elseif skip1('*') then
	    create last_feature_name.infix_name(as_muls,sp)
	 elseif skip1('/') then
	    create last_feature_name.infix_name(as_slash,sp)
	 elseif skip1('>') then
	    create last_feature_name.infix_name(as_gt,sp)
	 elseif skip1('<') then
	    create last_feature_name.infix_name(as_lt,sp)
	 elseif skip1('^') then
	    create last_feature_name.infix_name(as_pow,sp)
	 elseif a_keyword(as_xor) then
	    create last_feature_name.infix_name(as_xor,sp)
	 elseif a_keyword(as_implies) then
	    create last_feature_name.infix_name(as_implies,sp)
	 elseif a_keyword(as_and) then
	    if a_keyword(fz_then) then
	       create last_feature_name.infix_name(as_and_then,sp)
	    else
	       create last_feature_name.infix_name(as_and,sp)
	    end
	 elseif a_keyword(as_or) then
	    if a_keyword(fz_else) then
	       create last_feature_name.infix_name(as_or_else,sp)
	    else
	       create last_feature_name.infix_name(as_or,sp)
	    end
	 else
	    last_feature_name := Void
	    Result := false
	 end
      end

   a_boolean_constant: BOOLEAN is
	 --++ boolean_constant -> "true" | "false"
	 --++
      do
	 if a_keyword(fz_true) then
	    !E_TRUE!last_boolean_constant.make(pos(start_line,start_column))
	    Result := true
	 elseif a_keyword(fz_false) then
	    !E_FALSE!last_boolean_constant.make(pos(start_line,start_column))
	    Result := true
	 end
      end

   a_character_or_integer: BOOLEAN is
	 --++ character_or_integer -> character_constant |
	 --++                         integer_constant
	 --++
      do
	 if a_character_constant then
	    last_character_or_integer := last_character_constant
	    Result := true
	 elseif a_integer_constant then
	    last_character_or_integer := last_integer_constant
	    Result := true
	 else
	    fcp(fz_iinaiv)
	 end
      end

   a_check: BOOLEAN is
	 --++ check -> "check" assertion "end"
	 --++
      local
	 sp: POSITION
	 hc: COMMENT
	 al: ARRAY[ASSERTION]
      do
	 if a_keyword(fz_check) then
	    sp := pos(start_line,start_column)
	    hc := get_comment
	    al := a_assertion
	    if not a_keyword(fz_end) then
	       error_handler.add_position(sp)
	       fcp("Keyword %"end%" expected at the end of check clause.")
	    end
	    if hc /= Void or else al /= Void then
	       !E_CHECK!last_instruction.make(sp,hc,al)
	       Result := true
	    elseif skip1(';') then
	    end
	 end
      end

   a_class_declaration is
	 --++ class_declaration -> [indexing]
	 --++                      ["expanded" | "deferred" | "separate"]
	 --++                      "class" base_class_name
	 --++                      ["[" formal_generic_list "]"]
	 --++                      [comment]
	 --++                      ["obsolete" manifest_string]
	 --++                      ["inherit" parent_list]
	 --++                      {"creation" creation_clause ...}
	 --++                      {"create" creation_clause ...}
	 --++                      {"feature" feature_clause ...}
	 --++                      ["invariant" assertion]
	 --++                      "end"
	 --++
      local
	 sp: POSITION
	 hc: COMMENT
	 al: ARRAY[ASSERTION]
	 drop_comments_save, end_of_class_flag: BOOLEAN
         prefixword: BOOLEAN
      do
	 a_indexing
         from
            prefixword := true
         until
            not prefixword
         loop
            if a_keyword(fz_deferred) then
               last_base_class.set_is_deferred
            elseif a_keyword(fz_expanded) then
               last_base_class.set_is_expanded
            elseif a_keyword(fz_separate) then
	       error_handler.add_position(pos(start_line,start_column))
	       error_handler.append("Compiler limitation: separate classes are not %
				     %yet supported. Use separate entities instead.")
	       error_handler.print_as_fatal_error
               last_base_class.set_is_separate
            else
               prefixword := false
            end
         end
	 last_base_class.set_heading_comment1(get_comment)
	 if not a_keyword(fz_class) then
	    fcp("Keyword %"class%" expected.")
	 end
	 a_base_class_name1
	 a_formal_generic_list
	 if a_keyword(fz_obsolete) then
	    if a_manifest_string then
	       last_manifest_string.set_is_once(true)
	       last_base_class.set_obsolete_type_string(last_manifest_string)
	    else
	       fcp("Manifest string expected for %"obsolete%" clause.")
	    end
	 end
	 last_base_class.set_heading_comment2(get_comment)
	 if a_keyword(fz_inherit) then
	    end_of_class_flag := true
	    a_parent_list(pos(start_line,start_column),get_comment)
	 end
	 from
	 until
	    not (a_keyword(fz_creation) or else a_keyword(fz_create))
	 loop
	    a_creation_clause(pos(start_line,start_column))
	    end_of_class_flag := false
	 end
	 from
	 until
	    not a_keyword(fz_feature)
	 loop
	    a_feature_clause
	    end_of_class_flag := false
	 end
	 drop_comments_save := drop_comments
	 drop_comments := false
	 if a_keyword(fz_invariant) then
	    end_of_class_flag := false
	    sp := pos(start_line,start_column)
	    hc := get_comment
	    al := a_assertion
	    last_base_class.set_invariant(sp,hc,al)
	 end
	 if a_keyword(fz_end) or else end_of_class_flag then
	    if cc = ';' then
	       wcp(em24)
	       ok := skip1(';')
	    end
	    last_base_class.set_end_comment(get_comment)
	    if cc /= end_of_text then
	       fcp("End of text expected.")
	    end
	 else
	    fcp("Keyword %"end%" expected at the end of a class.")
	 end
	 drop_comments := drop_comments_save
      end

   a_class_type: BOOLEAN is
	 --++ class_type -> base_type |
	 --++               base_class_name ["[" {type "," ...} "]"]
	 --++
      local
	 state: INTEGER; base_class_name: CLASS_NAME
	 generic_list: ARRAY[E_TYPE]
      do
	 if a_base_type then
	    last_class_type := last_base_type
	    Result := true
	 elseif a_base_class_name then
	    from
	       Result := true
	       base_class_name := last_class_name
	    until
	       state > 2
	    loop
	       inspect
		  state
	       when 0 then  -- `base_class_name' read.
		  if skip1('[') then
		     state := 1
		  else
		     !TYPE_CLASS!last_class_type.make(base_class_name)
		     state := 3
		  end
	       when 1 then -- Waiting next generic argument.
		  if a_type then
		     if generic_list = Void then
			!!generic_list.with_capacity(2,1)
		     end
		     generic_list.add_last(last_type)
		     state := 2
		  elseif cc = ',' then
		     wcp(em12)
		     ok := skip1(',')
		  elseif cc = ']' then
		     state := 2
		  else
		     fcp(em16)
		     state := 2
		  end
	       when 2 then -- Waiting ',' or ']'.
		  if skip1(',') then
		     state := 1
		  elseif cc = ']' then
		     if generic_list = Void then
			wcp(em35)
			!TYPE_CLASS!last_class_type.make(base_class_name)
		     else
			!TYPE_GENERIC!last_class_type.make(base_class_name,generic_list)
		     end
		     ok := skip1(']')
		     state := 3
		  elseif a_type then
		     if generic_list = Void then
			!!generic_list.with_capacity(2,1)
		     end
		     generic_list.add_last(last_type)
		     warning(last_type.start_position,em5)
		  else
		     fcp("Bad generic list.")
		     state := 3
		  end
	       end
	    end
	 end
      end

   a_clients: CLIENT_LIST is
	 --++ clients -> "{" { base_class_name "," ... } "}"
	 --++
      local
	 sp: POSITION; list: CLASS_NAME_LIST; state: INTEGER
      do
	 if skip1('{') then
	    from
	       sp := pos(start_line,start_column)
	    until
	       state > 3
	    loop
	       inspect
		  state
	       when 0 then -- Waiting a base_class_name or "}" if empty list.
		  if a_base_class_name then
		     !!list.make_1(last_class_name)
		     state := 2
		  elseif skip1('}') then
		     state := 4
		  elseif cc = ',' then
		     wcp(em7)
		     ok := skip1(',')
		  else
		     state := 3
		  end
	       when 1 then -- Waiting a base_class_name after a ",".
		  if a_base_class_name then
		     list.add_last(last_class_name)
		     state := 2
		  elseif cc = ',' then
		     wcp(em7)
		     ok := skip1(',')
		  elseif cc = '}' then
		     wcp(once "Unexpected bracket.")
		     ok := skip1('}')
		     state := 4
		  else
		     state := 3
		  end
	       when 2 then -- Waiting "," or "}" to end list.
		  if skip1(',') then
		     state := 1
		  elseif skip1('}') then
		     state := 4
		  elseif a_base_class_name then
		     warning(last_class_name.start_position,em5)
		     list.add_last(last_class_name)
		  else
		     state := 3
		  end
	       when 3 then -- Error.
		  fcp(em11)
		  state := 4
	       end
	    end
	    !!Result.make(sp,list)
	 else
	    Result := omitted_client_list
	 end
      ensure
	 Result /= Void
      end

   a_compound1: COMPOUND is
	 --++ compound -> {instruction ";" ...}
	 --++
      local
	 hc: COMMENT
	 instruction: INSTRUCTION
      do
	 from
	    hc := get_comment
	    from
	    until
	       cc /= ';'
	    loop
	       wcp(em24)
	       ok := skip1(';')
	    end
	 until
	    not a_instruction or else nb_errors > 0
	 loop
	    instruction := last_instruction
	    check
	       instruction /= Void
	    end
	    if cc = '(' then
	       wcp(em6)
	    end
	    from
	       ok := skip1(';')
	    until
	       cc /= ';'
	    loop
	       wcp(em24)
	       ok := skip1(';')
	    end
	    if nb_errors = 0 then
	       instruction := instruction_with_comment(instruction)
	       if Result = Void then
		  !!Result.make_1(hc,instruction)
	       else
		  Result.add_last(instruction)
	       end
	    end
	 end
	 if hc /= Void and then Result = Void then
	    !!Result.make_0(hc)
	 end
      end

   a_compound2(compound_of, terminator: STRING): COMPOUND is
	 -- Similar to `a_compound1' but stop when `terminator' is encountered.
      local
	 hc: COMMENT
	 instruction: INSTRUCTION
      do
	 from
	    hc := get_comment
	    from
	    until
	       cc /= ';'
	    loop
	       wcp(em24)
	       ok := skip1(';')
	    end
	 until
	    not a_instruction or else nb_errors > 0
	 loop
	    instruction := last_instruction
	    check
	       instruction /= Void
	    end
	    if cc = '(' then
	       wcp(em6)
	    end
	    from
	       ok := skip1(';')
	    until
	       cc /= ';'
	    loop
	       wcp(em24)
	       ok := skip1(';')
	    end
	    if nb_errors = 0 then
	       instruction := instruction_with_comment(instruction)
	       if Result = Void then
		  !!Result.make_1(hc,instruction)
	       else
		  Result.add_last(instruction)
	       end
	    end
	 end
	 if not a_keyword(terminator) then
	    error_handler.append("In compound (")
	    error_handler.append(compound_of)
	    error_handler.append("). Instruction or keyword %"")
	    error_handler.append(terminator)
	    fcp("%" expected.")
	 end
	 if hc /= Void and then Result = Void then
	    !!Result.make_0(hc)
	 end
      end

   a_conditional: BOOLEAN is
	 --++ conditional -> "if" then_part_list ["else" compound] "end"
	 --++
      local
	 ifthenelse: IFTHENELSE
      do
	 if a_keyword(fz_if) then
	    Result := true
	    !!ifthenelse.make(pos(start_line,start_column))
	    a_then_part_list(ifthenelse)
	    if a_keyword(fz_else) then
	       ifthenelse.set_else_compound(a_compound2(once "else part",
							fz_end))
	    else
	       if not a_keyword(fz_end) then
		  wcp("Keyword %"end%" added.")
	       end
	    end
	    last_instruction := ifthenelse
	 end
      end

   a_old_creation: BOOLEAN is
	 --++ old_creation -> "!"[type]"!" writable
         --++                 ["." procedure_name [actuals]]
	 --++
      local
	 sp: POSITION; type: E_TYPE; writable: EXPRESSION
	 procedure_name: FEATURE_NAME; call: PROC_CALL
      do
	 if skip1('!') then
	    Result := true
	    sp := pos(start_line,start_column)
	    if a_type then
	       type := last_type
	       if type.is_anchored then
		  warning(type.start_position,em20)
	       end
	       if not skip1('!') then
		  fcp("Bad creation instruction ('!' expected).")
	       end
	    elseif skip1('!') then
	    else
	       fcp("Bad creation instruction (type or '!' expected).")
	    end
	    writable := mandatory_writable
	    if skip1('.') then
	       if a_identifier then
		  procedure_name := tmp_name.to_simple_feature_name
		  if cc = '(' then
		     call := to_proc_call(writable,procedure_name,a_actuals)
		  else
		     !PROC_CALL_0!call.make(writable,procedure_name)
		  end
	       else
		  fcp(em23)
	       end
	    end
	    !OLD_CREATION!last_instruction.make(sp,type,writable,call)
	 end
      end

   a_create_instruction: BOOLEAN is
	 --++ create_instruction -> "create" ["{" type "}"] writable
         --++                       ["." procedure_name [actuals]]
	 --++
      local
	 sp: POSITION; type: E_TYPE; writable: EXPRESSION
	 procedure_name: FEATURE_NAME; call: PROC_CALL
      do
	 if a_keyword(fz_create) then
	    Result := true
	    sp := pos(start_line,start_column)
	    if skip1('{') then
	       if a_type then
		  type := last_type
		  if type.is_anchored then
		     warning(type.start_position,em20)
		  end
		  if not skip1('}') then
		     fcp(em36)
		  end
	       else
		  fcp("Bad create instruction (type expected).")
	       end
	    end
	    writable := mandatory_writable
	    if skip1('.') then
	       if a_identifier then
		  procedure_name := tmp_name.to_simple_feature_name
		  if cc = '(' then
		     call := to_proc_call(writable,procedure_name,a_actuals)
		  else
		     !PROC_CALL_0!call.make(writable,procedure_name)
		  end
	       end
	    end
	    !CREATE_INSTRUCTION!last_instruction.make(sp,type,writable,call)
	 end
      end

   a_create_expression: BOOLEAN is
	 --++ create_expression -> "create" "{" type "}" ["." procedure_name [actuals]]
	 --++
      local
	 sp: POSITION; type: E_TYPE; procedure_name: FEATURE_NAME
	 call: PROC_CALL; face_target: FACE_TARGET
      do
	 if a_keyword(fz_create) then
	    Result := true
	    sp := pos(start_line,start_column)
	    if not skip1('{') then
	       fcp("Bad create expression ('{' expected).")
	    end
	    if not a_type then
	       fcp("Bad create instruction (type expected).")
	    end
	    type := last_type
	    if type.is_anchored then
	       warning(type.start_position,em20)
	    end
	    if not skip1('}') then
	       fcp("Bad create expression ('}' expected).")
	    end
	    if skip1('.') then
	       if a_identifier then
		  procedure_name := tmp_name.to_simple_feature_name
		  !!face_target.make(type)
		  if cc = '(' then
		     call := to_proc_call(face_target,procedure_name,a_actuals)
		  else
		     !PROC_CALL_0!call.make(face_target,procedure_name)
		  end
	       end
	    end
	    !CREATE_EXPRESSION!last_expression.make(sp,type,call)
	 end
      end

   a_agent: BOOLEAN is
	 --++ agent -> "agent" ...
	 --++
      local
	 sp: POSITION; base: E_TYPE; e: EXPRESSION; call: CALL
      do
	 if a_keyword(as_agent) then
	    sp := pos(start_line,start_column)
	    Result := true
	    if skip1('{') then
	       if not a_type then fcp("Bad agent (BASE type expected).") end
	       base := last_type
	       if not skip1('}') then fcp("Bad agent (%"}%" expected).") end
	       if not skip1('.') then fcp("Bad agent (%".%" expected).") end
	    end
	    if not a_identifier then
	       fcp(em34)
	    end
            if a_argument then
	       a_r10(false,last_expression,Void,Void,true)
	    elseif a_result or else a_current or else a_void or else
	       a_local_variable then
	       a_r10(false,last_expression,Void,Void,false)
	    else
	       a_function_call
	    end
	    e := last_expression
	    call ?= e
	    if call = Void then
	       if e /= Void then
		  error_handler.add_position(e.start_position)
	       end
	       error_handler.append(em34)
	       error_handler.print_as_fatal_error
	    end
	    create {E_AGENT} last_expression.make(sp,base,call)
	 end
      end

   a_creation_clause(sp: POSITION) is
	 --++ creation_clause -> [clients] [comment] feature_list
	 --++
      local
	 clients: CLIENT_LIST; comments: COMMENT
	 creation_clause: CREATION_CLAUSE
      do
	 clients := a_clients
	 comments := get_comment
	 if a_feature_name_list then end
	 !!creation_clause.make(sp,clients,comments,last_feature_name_list)
	 last_base_class.add_creation_clause(creation_clause)
      end

   a_debug: BOOLEAN is
	 --++ debug -> "debug" "(" {manifest_string "," ...} ")"
	 --++                  compound "end"
	 --++
      local
	 sp: POSITION
	 list: FIXED_ARRAY[MANIFEST_STRING]
	 e_debug: E_DEBUG
      do
	 if a_keyword(fz_debug) then
	    sp := pos(start_line,start_column)
	    if skip1('(') then
	       from
		  !!list.with_capacity(4)
	       until
		  not a_manifest_string
	       loop
		  last_manifest_string.set_is_once(true)
		  list.add_last(last_manifest_string)
		  ok := skip1(',')
	       end
	       if list.is_empty then
		  wcp("Empty debug key list (deleted).")
		  list := Void
	       end
	       if not skip1(')') then
		  fcp("%")%" expected to end debug string list.")
	       end
	    end
	    Result := true
	    !!e_debug.make(sp,list,a_compound2(once "debug",fz_end))
	    last_instruction := e_debug
	    end
	 end

   a_expression: BOOLEAN is
	 --++ expression -> "<<" {Expression "," ...} ">>" |
         --++               "[" {Expression "," ...} "]" |
	 --++               Void |
	 --++               e0
	 --++
      local
	 sp: POSITION; list: FIXED_ARRAY[EXPRESSION]
      do
	 if skip2('<','<') then
	    from
	       Result := true
	       sp := pos(start_line,start_column)
	    until
	       not a_expression
	    loop
	       if list = Void then
		  !!list.with_capacity(4)
	       end
	       list.add_last(last_expression)
	       ok := skip1(',')
	    end
	    if not skip2('>','>') then
	       fcp("End of manifest array expected.")
	    end
	    !MANIFEST_ARRAY!last_expression.make(sp,list)
	 elseif skip1('[') then
	    from
	       Result := true
	       sp := pos(start_line,start_column)
	    until
	       not a_expression
	    loop
	       if list = Void then
		  !!list.with_capacity(4)
	       end
	       list.add_last(last_expression)
	       ok := skip1(',')
	    end
	    if not skip1(']') then
	       fcp("End of TUPLE expression expected.")
	    end
	    !TUPLE_EXPRESSION!last_expression.make(sp,list)
	 else
	    Result := a_e0
	 end
      end

   a_e0: BOOLEAN is
	 --++ e0 -> e1 r1
	 --++
      do
	 Result := a_e1
	 if Result then
	    a_r1(last_expression)
	 end
      end

   a_e1: BOOLEAN is
	 --++ e1 -> e2 r2
	 --++
      do
	 Result := a_e2
	 if Result then
	    a_r2(last_expression)
	 end
      end

   a_e2: BOOLEAN is
	 --++ e2 -> e3 r3
	 --++
      do
	 Result := a_e3
	 if Result then
	    a_r3(last_expression)
	 end
      end

   a_e3: BOOLEAN is
	 --++ e3 -> e4 r4
	 --++
      do
	 Result := a_e4
	 if Result then
	    a_r4(last_expression)
	 end
      end

   a_e4: BOOLEAN is
	 --++ e4 -> e5 r5
	 --++
      do
	 Result := a_e5
	 if Result then
	    a_r5(last_expression)
	 end
      end

   a_e5: BOOLEAN is
	 --++ e5 -> e6 r6
	 --++
      do
	 Result := a_e6
	 if Result then
	    a_r6(last_expression)
	 end
      end

   a_e6: BOOLEAN is
	 --++ e6 -> e7 r7
	 --++
      do
	 Result := a_e7
	 if Result then
	    a_r7(last_expression)
	 end
      end

   a_e7: BOOLEAN is
	 --++ e7 -> e8 r8
	 --++
      do
	 Result := a_e8
	 if Result then
	    a_r8(last_expression)
	 end
      end

   a_e8: BOOLEAN is
	 --++ e8 -> "not" e8 |
	 --++       "+" e8 |
	 --++       "-" e8 |
	 --++       free_operator e8 !
	 --++       e9
	 --++
      local
	 op: FEATURE_NAME; prefix_freeop: CALL_PREFIX_FREEOP; sp: POSITION
	 l, c: INTEGER
      do
	 if a_keyword(as_not) then
	    sp := pos(start_line,start_column)
	    if a_e8 then
	       create {CALL_PREFIX_NOT}
	          last_expression.make(sp,last_expression)
	       Result := true
	    else
	       err_exp(sp, True, as_not)
	    end
	 elseif skip1('+') then
	    l := start_line; c := start_column
	    if a_real(0) then
	       last_expression := last_real_constant
	       Result := true
	    elseif a_integer(0) then
	       last_expression := last_integer_constant
	       Result := true
	    else
	       sp := pos(l, c)
	       if a_e8 then
		  create {CALL_PREFIX_PLUS}
	             last_expression.make(sp,last_expression)
		  Result := true
	       else
		  err_exp(sp, True, as_plus)
	       end
	    end
	 elseif skip1('-') then
	    l := start_line; c := start_column
	    if a_real(-1) then
	       last_expression := last_real_constant
	       Result := true
	    elseif a_integer(-1) then
	       last_expression := last_integer_constant
	       Result := true
	    else
	       sp := pos(l, c)
	       if a_e8 then
		  create {CALL_PREFIX_MINUS}
	             last_expression.make(sp,last_expression)
	          Result := true
	       else
		  err_exp(sp, True, as_minus)
	       end
	    end
	 elseif a_free_operator_usage(True) then
	    op := last_feature_name
	    if a_e8 then
	       create prefix_freeop.make(last_expression,op)
	       last_expression := prefix_freeop
	       Result := true
	    else
	       err_exp(op.start_position, True, op.to_string)
	    end
	 else
	    Result := a_e9
	 end
      end

   a_e9: BOOLEAN is
	 --++ e9 -> e10 |
	 --++       "old" e10
	 --++
      do
	 if a_keyword(fz_old) then
	    if not in_ensure then
	       error(pos(start_line,start_column),
		     "Expression %"old%" can be used in ensure clause %
				       %only (VAOL.1).")
	    end
	    if a_e10 then
	       !E_OLD!last_expression.make(last_expression)
	       Result := true
	    else
	       fcp("Expression expected after %"old%".")
	    end
	 else
	    Result := a_e10
	 end
      end

   a_e10: BOOLEAN is
	 --++ e10 -> strip |
	 --++       "(" expression ")" r10 |
	 --++       manifest_constant |
	 --++       precursor_call |
	 --++       "Result" r10 |
	 --++       "Current" r10 |
	 --++       "Void" r10 |
	 --++       local_variable r10 |
	 --++       argument r10 |
	 --++       function_call r10 |
	 --++
      do
	 if a_strip then
	    Result := true
	 elseif skip1('(') then
	    Result := true
	    if a_expression then
	       if skip1(')') then
		  a_r10(false,last_expression,Void,Void,false)
	       else
		  fcp("')' expected in expression.")
	       end
	    else
	       fcp("Expression expected.")
	    end
	 elseif a_manifest_constant then
	    last_expression := last_manifest_constant
	    Result := true
	    if skip1('.') then
	       wcp(once "Added brackets for manifest constant before dot.")
	       a_after_a_dot(false,last_expression)
	    end
	 elseif a_precursor(false) then
	    Result := true
	 elseif a_create_expression then
	    Result := true
	 elseif a_agent then
	    Result := true
	 elseif a_identifier then
	    Result := true
            if a_argument then
	       a_r10(false,last_expression,Void,Void,true)
            elseif a_current or else a_result or else a_void or else a_local_variable then
	       a_r10(false,last_expression,Void,Void,false)
	    else
	       a_function_call
	    end
	 end
      end

   a_external: E_ROUTINE is
	 --++ external -> "<external-specification>" external_name
	 --++ external_name -> ["alias" manifest_string]
	 --++
      local
	 alias_tag: STRING; l: NATIVE; external_tag: MANIFEST_STRING
      do
	 if not a_manifest_string then
	    fcp("Bad external clause (manifest string expected).")
	 end
	 unused_once_warning_check
	 external_tag := last_manifest_string
	 external_tag.set_is_once(false)
	 mini_buffer.start_with(external_tag)
	 if mini_buffer.is_off then
	    unknown_external_language(external_tag)
	 elseif mini_buffer.a_word(fz_smarteiffel) then
	    create {NATIVE_SMART_EIFFEL} l.make(external_tag)
	 elseif mini_buffer.a_word(once "SmallEiffel") then
	    error_handler.add_position(external_tag.start_position)
	    error_handler.append(
	              "Obsolete %"SmallEiffel%" keyword replaced % 
		      %with %"SmartEiffel%" (update your code).")
	    error_handler.print_as_warning
	    create {NATIVE_SMART_EIFFEL} l.make(external_tag)
	 elseif mini_buffer.a_keyword(once "C++") then
	    create {NATIVE_C_PLUS_PLUS} l.make(external_tag)
	 elseif mini_buffer.a_keyword(once "C") then
	    create {NATIVE_C} l.make(external_tag)
	 elseif mini_buffer.a_keyword(once "Java") then
	    create {NATIVE_JAVA} l.make(external_tag)
	 else
	    unknown_external_language(external_tag)
	 end
	 if a_keyword(fz_alias) then
	    if not a_manifest_string then
	       fcp("Bad external alias clause.")
	    end
	    unused_once_warning_check
	    alias_tag := last_manifest_string.to_string
	 end
	 Result := tmp_feature.to_external_routine(l,alias_tag)
      end

   a_feature_name_list: BOOLEAN is
	 --++ feature_name_list -> {feature_name "," ...}
	 --++
	 --
	 -- Gives true when list is not empty.
      local
	 state: INTEGER
      do
	 from
	    last_feature_name_list := Void
	 until
	    state >= 3
	 loop
	    inspect
	       state
	    when 0 then -- Nothing read.
	       if a_feature_name then
		  !!last_feature_name_list.make_1(last_feature_name)
		  Result := true
		  state := 1
	       elseif cc = ',' then
		  wcp(em7)
		  ok := skip1(',')
	       else
		  state := 3
	       end
	    when 1 then -- Feature name read.
	       if cc = ',' then
		  ok := skip1(',')
		  state := 2
	       elseif a_feature_name then
		  warning(last_feature_name.start_position,em5)
		  last_feature_name_list.add_last(last_feature_name)
	       else
		  state := 3
	       end
	    when 2 then -- Separator read.
	       if a_feature_name then
		  last_feature_name_list.add_last(last_feature_name)
		  state := 1
	       elseif cc = ',' then
		  wcp(em12)
		  ok := skip1(',')
	       else
		  ecp(em2)
		  state := 3
	       end
	    end
	 end
      end

   a_feature_name: BOOLEAN is
	 --++ feature_name -> prefix |
	 --++                 infix |
	 --++                 simple_feature_name
	 --++
      do
	 if a_prefix then
	    Result := true
	 elseif a_infix then
	    Result := true
	 elseif a_identifier then
	    last_feature_name := tmp_name.to_simple_feature_name
	    Result := true
	 end
      end

   a_feature_clause is
	 --++ feature_clause -> [clients] [comment] feature_declaration_list
	 --++
      local
	 feature_clause: FEATURE_CLAUSE; clients: CLIENT_LIST
	 comment: COMMENT
      do
	 from
	    clients := a_clients
	    comment := get_comment
	    faof.clear
	 until
	    not a_feature_declaration
	 loop
	    ok := skip1(';')
	    if last_feature_declaration /= Void then
	       faof.add_last(last_feature_declaration)
	       last_feature_declaration.set_header_comment(get_comment)
	    end
	 end
	 if not faof.is_empty then
	    !!feature_clause.make(clients,comment,faof.twin)
	    last_base_class.add_feature_clause(feature_clause)
	 elseif comment /= Void then
	    !!feature_clause.make(clients,comment,Void)
	    last_base_class.add_feature_clause(feature_clause)
	 end
      end

   a_feature_declaration: BOOLEAN is
	 --++ feature_declaration -> {["frozen"] feature_name "," ...}+
	 --++                        formal_arg_list
	 --++                        [":" type]
	 --++                        ["is" "unique" |
	 --++                         "is" manifest_constant |
	 --++                         "is" routine]
	 --++
      do
	 from
	    tmp_feature.initialize
	    if a_keyword(fz_frozen) then
	       if a_feature_name then
		  Result := true
		  to_frozen_feature_name
		  tmp_feature.add_synonym(last_feature_name)
	       else
		  fcp(em2)
	       end
	    elseif a_feature_name then
	       Result := true
	       tmp_feature.add_synonym(last_feature_name)
	    end
	 until
	    not skip1(',')
	 loop
	    if a_keyword(fz_frozen) then
	       if a_feature_name then
		  to_frozen_feature_name
		  tmp_feature.add_synonym(last_feature_name)
	       else
		  fcp("Frozen feature name synonym expected.")
	       end
	    elseif a_feature_name then
	       tmp_feature.add_synonym(last_feature_name)
	    else
	       ecp("Synonym feature name expected.")
	    end
	 end
	 if Result then
	    a_formal_arg_list
	    if skip1(':') then
	       if a_type then
		  inside_function := true
		  tmp_feature.set_type(last_type)
	       else
		  fcp(em16)
	       end
	    else
	       inside_function := false
	    end
	    if a_keyword(fz_is) then
	       if a_keyword(fz_unique) then
		  last_feature_declaration := tmp_feature.to_cst_att_unique
	       elseif a_boolean_constant then
		  last_feature_declaration :=
		     tmp_feature.to_cst_att_boolean(last_boolean_constant)
	       elseif a_character_constant then
		  last_feature_declaration :=
		     tmp_feature.to_cst_att_character(last_character_constant)
	       elseif a_manifest_string then
		  unused_once_warning_check
		  last_manifest_string.set_is_once(true)
		  last_feature_declaration :=
		     tmp_feature.to_cst_att_string(last_manifest_string)
	       elseif a_bit_constant then
		  last_feature_declaration :=
		     tmp_feature.to_cst_att_bit(last_bit_constant)
	       elseif a_real_constant then
		  last_feature_declaration :=
		     tmp_feature.to_cst_att_real(last_real_constant)
	       elseif a_integer_constant then
		  last_feature_declaration :=
		     tmp_feature.to_cst_att_integer(last_integer_constant)
	       else
		  last_feature_declaration := a_routine
	       end
	    else
	       last_feature_declaration := tmp_feature.to_writable_attribute
	    end
	    inside_function := false
	    inside_once_function := false
	    arguments := Void
	 end
      end

   a_formal_generic_list is
	 --++ formal_generic_list -> ["[" {formal_generic "," ...} "]"]
	 --++ formal_generic -> base_class_name ["->" class_type]
	 --++
      local
	 fga: FORMAL_GENERIC_ARG; formal_generic_name: CLASS_NAME
	 constraint: E_TYPE; state: INTEGER
      do
	 formal_generic_list := Void
	 if skip1('[') then
	    from
	       !!formal_generic_list.make(pos(start_line,start_column))
	       last_base_class.set_formal_generic_list(formal_generic_list)
	    until
	       state > 3
	    loop
	       inspect
		  state
	       when 0 then -- Waiting for a `formal_generic_name'.
		  if a_base_class_name then
		     formal_generic_name := last_class_name
		     state := 1
		  else
		     state := 5
		  end
	       when 1 then -- Waiting for "->" or "," or "]".
		  if skip2('-','>') then
		     state := 3
		  elseif cc = ',' or else cc = ']' then
		     !!fga.make(formal_generic_name,constraint)
		     constraint := Void
		     formal_generic_list.add_last(fga)
		     inspect
			cc
		     when ',' then state := 0
		     when ']' then state := 4
		     end
		     ok := skip1(cc)
		  else
		     state := 5
		  end
	       when 2 then -- Waiting for "," or "]".
		  if cc = ',' or else cc = ']' then
		     !!fga.make(formal_generic_name,constraint)
		     constraint := Void
		     formal_generic_list.add_last(fga)
		     inspect
			cc
		     when ',' then state := 0
		     when ']' then state := 4
		     end
		     ok := skip1(cc)
		  else
		     state := 5
		  end
	       when 3 then -- Waiting for a `constraint' type.
		  if a_type_formal_generic then
		     constraint := last_type_formal_generic
		     state := 2
		  elseif a_class_type then
		     constraint := last_class_type
		     state := 2
		  else
		     fcp("Constraint Class name expected.")
		     state := 5
		  end
	       end
	    end
	    inspect
	       state
	    when 4 then
	       if formal_generic_list.count = 0 then
		  error_handler.add_position(formal_generic_list.start_position)
		  error_handler.append("Empty formal generic list (deleted).")
		  error_handler.print_as_warning
		  formal_generic_list := Void
		  last_base_class.set_formal_generic_list(Void)
	       end
	    when 5 then
	       check nb_errors > 0 end
	    end
	 end
      end

   a_function_call is
	 --++ function_call -> [actuals] r10 |
	 --++                   ^
	 --++
      local
	 sfn: FEATURE_NAME; implicit_current: IMPLICIT_CURRENT
      do
	 sfn := tmp_name.to_simple_feature_name
	 !!implicit_current.make(sfn.start_position)
	 a_r10(false,implicit_current,sfn,a_actuals,true)
      end

   a_index_clause: BOOLEAN is
	 --++ index_clause -> [identifier ":"] {index_value "," ...}+
	 --++
      local
	 index_clause: INDEX_CLAUSE
      do
	 if a_identifier then
	    Result := true
	    if skip1(':') then
	       !!index_clause.with_tag(tmp_name.aliased_string)
	       if a_index_value then
		  index_clause.add_last(last_index_value)
	       else
		  fcp(em3)
	       end
	    else
	       !!index_clause.without_tag(tmp_name.to_simple_feature_name)
	    end
	 elseif a_manifest_constant then
	    Result := true
	    !!index_clause.without_tag(last_manifest_constant)
	 end
	 if Result then
	    from
	    until
	       not skip1(',')
	    loop
	       if a_index_value then
		  index_clause.add_last(last_index_value)
	       else
		  fcp(em3)
	       end
	    end
	    last_base_class.add_index_clause(index_clause)
	 end
      end

   a_index_value: BOOLEAN is
	 --++ index_value -> identifier | manifest_constant
	 --++
      do
	 if a_identifier then
	    last_index_value := tmp_name.to_simple_feature_name
	    Result := true
	 elseif a_manifest_constant then
	    last_index_value := last_manifest_constant
	    Result := true
	 end
      end

   a_indexing is
	 --++ indexing -> "indexing" {index_clause ";" ...}
	 --++
      do
	 if a_keyword(fz_indexing) then
	    from
	    until
	       not a_index_clause
	    loop
	       ok := skip1(';')
	    end
	 end
      end

   a_infix: BOOLEAN is
	 --++ infix -> "infix" "%"" binary "%""
	 --++          "infix" "%"" free_operator "%""
	 --++
      local
	 sp: POSITION
      do
	 if a_keyword(fz_infix) then
	    Result := true
	    sp := pos(start_line,start_column)
	    if cc = '%"' then
	       next_char
	    else
	       wcp("Character '%%%"' inserted after %"infix%".")
	    end
	    if a_binary(sp) then
	    elseif a_free_operator_definition(False) then
	    else
	       fcp("Infix operator name expected.")
	    end
	    if not skip1('%"') then
	       wcp("Character '%%%"' inserted.")
	    end
	 end
      end

   a_inspect: BOOLEAN is
	 --++ inspect -> "inspect" expression
	 --++            {when_part ...}
	 --++            ["else" compound]
	 --++            "end"
	 --++
      local
	 sp, spec: POSITION
	 i: E_INSPECT
	 ec: COMPOUND
      do
	 if a_keyword(fz_inspect) then
	    Result := true
	    sp := pos(start_line,start_column)
	    if a_expression then
	       last_expression := expression_with_comment(last_expression)
	    else
	       fcp("Expression expected (%"inspect ... %").")
	    end
	    from
	       !!i.make(sp,last_expression)
	    until
	       not a_when_part(i)
	    loop
	    end
	    if a_keyword(fz_else) then
	       spec := pos(start_line,start_column)
	       ec := a_compound2(once "else of inspect",fz_end)
	       i.set_else_compound(spec,ec)
	    elseif not a_keyword(fz_end) then
	       wcp("Added %"end%" for inspect instruction.")
	    end
	    last_instruction := i
	 end
      end

   a_instruction: BOOLEAN is
	 --++ instruction -> check | debug | conditionnal | retry |
	 --++                inspect | loop | old_creation |
	 --++                create_instruction | assignment_or_call
	 --++
      do
	 Result := true
	 if a_check then
	 elseif a_debug then
	 elseif a_conditional then
	 elseif a_retry then
	 elseif a_inspect then
	 elseif a_loop then
	 elseif a_create_instruction then
	 elseif a_assignment_or_call then
	 elseif a_old_creation then
	 else
	    Result := false
	 end
      end

   a_integer_constant: BOOLEAN is
	 --++ integer_constant -> ["+" | "-"] integer
	 --++
      local
	 l, c: INTEGER
      do
	 l := line; c := column
	 if skip1('+') then
	    if a_integer(0) then
	       Result := true
	    else
	       go_back_at(l,c)
	    end
	 elseif skip1('-') then
	    if a_integer(-1) then
	       Result := true
	    else
	       go_back_at(l,c)
	    end
	 elseif a_integer(0) then
	    Result := true
	 end
      end

   a_loop: BOOLEAN is
	 --++ loop -> "from" compound
	 --++         ["invariant"] assertion
	 --++         ["variant" [identifier ":"] expression]
	 --++         "until" expression
	 --++         "loop" compound
	 --++         "end"
	 --++
      local
	 l1, c1, l2, c2: INTEGER
	 e_loop: E_LOOP
	 i: COMPOUND
	 ic: LOOP_INVARIANT
	 vc: LOOP_VARIANT
	 ue: EXPRESSION
	 lb: COMPOUND
	 hc, vc1, vc2: COMMENT
	 al: ARRAY[ASSERTION]
	 tag_name: TAG_NAME
      do
	 if a_keyword(fz_from) then
	    Result := true
	    l1 := start_line
	    c1 := start_column
	    i := a_compound1
	    if a_keyword(fz_invariant) then
	       l2 := start_line
	       c2 := start_column
	       hc := get_comment
	       al := a_assertion
	       if hc /= Void or else al /= Void then
		  !!ic.make(pos(l2,c2),hc,al)
	       end
	    end
	    if a_keyword(fz_variant) then
	       vc1 := get_comment
	       if a_tag_mark then
		  tag_name := last_tag_mark
	       end
	       vc2 := get_comment
	       if a_expression then
		  !!vc.make(vc1,tag_name,vc2,
			    expression_with_comment(last_expression))
	       else
		  wcp("Variant (INTEGER) Expression Expected.")
	       end
	    end
	    if a_keyword(fz_until) then
	       if a_expression then
		  ue := expression_with_comment(last_expression)
	       else
		  fcp("Boolean expression expected (until).")
		  ue := last_expression
	       end
	    else
	       fcp("Keyword %"until%" expected (in a loop).")
	       ue := last_expression
	    end
	    if cc = ';' then
	       wcp(em24)
	       ok := skip1(';')
	    end
	    if not a_keyword(fz_loop) then
	       wcp("Keyword %"loop%" expected (in a loop).")
	    end
	    lb := a_compound2(once "loop body",fz_end)
	    !!e_loop.make(pos(l1,c1),i,ic,vc,ue,lb)
	    last_instruction := e_loop
	 end
      end

   a_manifest_constant: BOOLEAN is
	 --++ manifest_constant -> boolean_constant | character_constant |
	 --++                      real_constant | integer_constant |
	 --++                      manifest_string | bit_constant
	 --++
      do
	 if a_boolean_constant then
	    last_manifest_constant := last_boolean_constant
	    Result := true
	 elseif a_character_constant then
	    last_manifest_constant := last_character_constant
	    Result := true
	 elseif a_manifest_string then
	    last_manifest_constant := last_manifest_string
	    Result := true
	 elseif a_bit_constant then
	    last_manifest_constant := last_bit_constant
	    Result := true
	 elseif a_real_constant then
	    last_manifest_constant := last_real_constant
	    Result := true
	 elseif a_integer_constant then
	    last_manifest_constant := last_integer_constant
	    Result := true
	 end
      end

   a_new_export_list is
	 --++ new_export_list -> ["export" {new_export_item ";" ...}]
	 --++ new_export_item -> clients "all" |
	 --++                    clients feature_list
	 --++
      local
	 export_list: EXPORT_LIST; sp: POSITION; clients: CLIENT_LIST
	 items: ARRAY[EXPORT_ITEM]; new_export_item: EXPORT_ITEM
	 state: INTEGER
      do
	 if a_keyword(fz_export) then
	    from
	       sp := pos(start_line,start_column)
	    until
	       state > 3
	    loop
	       inspect
		  state
	       when 0 then -- Waiting for a `clients'.
		  if cc = '{' then
		     clients := a_clients
		     state := 1
		  elseif cc = ';' then
		     wcp(em24)
		     ok := skip1(';')
		  else
		     if items /= Void then
			!!export_list.make(sp,items)
			last_parent.set_export(export_list)
		     end
		     state := 4
		  end
	       when 1 then -- `clients' read.
		  if a_keyword(fz_all) then
		     !!new_export_item.make_all(clients)
		     if items = Void then
			!!items.with_capacity(2,1)
		     end
		     items.add_last(new_export_item)
		     state := 2
		  else
		     if a_feature_name_list then
			!!new_export_item.make(clients,last_feature_name_list)
			if items = Void then
			   !!items.with_capacity(2,1)
			end
			items.add_last(new_export_item)
			state := 2
		     else
			state := 3
		     end
		  end
	       when 2 then -- Waiting ";" before next one.
		  if skip1(';') then
		     state := 0
		  elseif cc = '{' then
		     wcp(em6)
		     state := 0
		  else
		     if items /= Void then
			!!export_list.make(sp,items)
			last_parent.set_export(export_list)
		     end
		     state := 4
		  end
	       when 3 then -- Error.
		  fcp(em11)
		  state := 4
	       end
	    end
	 end
      end

   a_parent_list(sp: POSITION; hc: COMMENT) is
	 --++ parent_list -> {parent ";" ...}
	 --++
      local
	 list: FIXED_ARRAY[PARENT]
      do
	 from until
	    not a_parent
	 loop
	    if list = Void then
	       create list.with_capacity(4)
	    end
	    list.add_last(last_parent)
	    ok := skip1(';')
	    last_parent.set_comment(get_comment)
	 end
	 if hc /= Void or else list /= Void then
	    if list = Void then
	       if last_base_class.heading_comment2 = Void then
		  last_base_class.set_heading_comment2(hc)
	       else
		  last_base_class.heading_comment2.append(hc)
	       end
	    else
	       last_base_class.set_parent_list(sp,hc,list)
	    end
	 end
      end

   a_parent: BOOLEAN is
	 --++ parent -> class_type
	 --++           ["rename" rename_list]
	 --++           new_export_list
	 --++           ["undefine" feature_name_list]
	 --++           ["redefine" feature_name_list]
	 --++           ["select" feature_name_list]
	 --++           ["end"]
	 --++
      do
	 if a_keyword(fz_expanded) then
	    error_handler.add_position(pos(start_line,start_column))
	    error_handler.append(
               "This form of inheritance (expanded inheritance) is %
               %not yet implemented.")
	    error_handler.print_as_error
	 elseif a_keyword(fz_reference) then
	    error_handler.add_position(pos(start_line,start_column))
	    error_handler.append("Unexpected %"reference%" keyword.")
	    error_handler.print_as_error
	 end
	 if a_class_type then
	    Result := true
	    create last_parent.make(last_class_type)
	    if a_keyword(fz_rename) then
	       a_rename_list
	       if cc = ';' then
		  wcp("Unexpected %";%" to end rename list.")
		  ok := skip1(';')
	       end
	    end
	    a_new_export_list
	    if a_keyword(fz_undefine) then
	       if a_feature_name_list then
		  last_parent.set_undefine(last_feature_name_list)
	       end
	    end
	    if a_keyword(fz_redefine) then
	       if a_feature_name_list then
		  last_parent.set_redefine(last_feature_name_list)
	       end
	    end
	    if a_keyword(fz_select) then
	       if a_feature_name_list then
		  last_parent.set_select(last_feature_name_list)
	       end
	    end
	    if a_keyword(fz_rename) or else
	       a_keyword(fz_export) or else
	       a_keyword(fz_undefine) or else
	       a_keyword(fz_redefine) or else
	       a_keyword(fz_select) then
	       error_handler.add_position(pos(start_line,start_column))
	       error_handler.append(
                  "Inheritance option not at a correct place. %
                  %The correct order is: %"rename... export... %
                  %undefine... redefine... select...%".")
	       error_handler.print_as_fatal_error
	    end
	    ok := a_keyword(fz_end)
	 end
      end

   a_prefix: BOOLEAN is
	 --++ prefix -> "prefix" "%"" unary "%""
	 --++           "prefix" "%"" free_operator "%""
	 --++
      do
	 if a_keyword(fz_prefix) then
	    Result := true
	    if cc = '%"' then
	       next_char
	    else
	       wcp("Character '%%%"' inserted after %"prefix%".")
	    end
	    if a_unary then
	    elseif a_free_operator_definition(True) then
	    else
	       fcp("Prefix operator name expected.")
	    end
	    if not skip1('%"') then
	       wcp("Character '%%%"' inserted.")
	    end
	 end
      end

   a_precursor(do_instruction: BOOLEAN): BOOLEAN is
	 --++ precursor -> ["{" class_name "}"] "Precursor" [actuals]  -- obsolete
         --++              "Precursor" ["{" class_name "}"] [actuals]  -- ETL3
	 --++
      local
	 l, c: INTEGER; parent: CLASS_NAME; args: EFFECTIVE_ARG_LIST
	 p: POSITION
         old_syntax: BOOLEAN
      do
	 if skip1('{') then
	    Result := true
	    l := start_line; c := start_column
	    if skip1('{') then
	       warning(pos(start_line,start_column),
	          "One single opening '{' is correct too here.")
	    end
	    if a_base_class_name then
	       parent := last_class_name
	    end
	    if not skip1('}')  then
	       fcp("Closing '}' expected to end Precursor's parent %
                   %qualification.")
	    end
	    if skip1('}') then
	       warning(pos(start_line,start_column),
		       "One single closing '}' is correct too here.")
	    end
            old_syntax := true
	 end
	 if a_keyword(as_precursor) then
	    Result := true
	    if l = 0 then
	       l := start_line
	       c := start_column
	    end
	 elseif l > 0 then
	    fcp("Precursor keyword expected here.")
	 end
	 if Result then
	    p := pos(l,c)

            if old_syntax then
               warning(pos(start_line, start_column),
                       "Obsolete syntax: the parent should now be written %
                       %after the `precursor' keyword.")
            else
               -- new ETL3 syntax
               if skip1('{') then
                  if skip1('{') then
                     warning(pos(start_line,start_column),
                             "One single opening '{' is correct too here.")
                  end
                  if a_base_class_name then
                     parent := last_class_name
                  end
                  if not skip1('}')  then
                     fcp("Closing '}' expected to end Precursor's parent %
                    %qualification.")
                  end
                  if skip1('}') then
                     warning(pos(start_line,start_column),
                             "One single closing '}' is correct too here.")
                  end
               end
            end
	    args := a_actuals

	    if skip1('.') then
	       !PRECURSOR_EXPRESSION!last_expression.make(p,parent,args)
	       a_after_a_dot(do_instruction,last_expression)
	    elseif do_instruction then
	       !PRECURSOR_INSTRUCTION!last_instruction.make(p,parent,args)
	    else
	       !PRECURSOR_EXPRESSION!last_expression.make(p,parent,args)
	    end
	 end
      end

   a_procedure_call is
	 --++ procedure_call -> [actuals] r10 |
	 --++                   ^
	 --++
      local
	 sfn: FEATURE_NAME; implicit_current: IMPLICIT_CURRENT
      do
	 sfn := tmp_name.to_simple_feature_name
	 !!implicit_current.make(sfn.start_position)
	 a_r10(true,implicit_current,sfn,a_actuals,true)
      end

   a_real_constant: BOOLEAN is
	 --++ real_constant -> ["+" | "-"] real
	 --++
      local
	 l, c: INTEGER
      do
	 l := line; c := column
	 if skip1('+') then
	    if a_real(0) then
	       Result := true
	    else
	       go_back_at(l,c)
	    end
	 elseif skip1('-') then
	    if a_real(-1) then
	       Result := true
	    else
	       go_back_at(l,c)
	    end
	 else
	    Result := a_real(0)
	 end
      end

   a_rename_list is
	 --++ rename_list -> {rename_pair "," ...}
	 --++
      do
	 from until
	    not a_rename_pair
	 loop
	    ok := skip1(',')
	 end
      end

   a_rename_pair: BOOLEAN is
	 --++ rename_pair -> identifier "as" identifier
	 --++
      local
	 name1: FEATURE_NAME; rename_pair: RENAME_PAIR; l, c: INTEGER
      do
	 l := line; c := column
	 if a_feature_name then
	    name1 := last_feature_name
	    if a_keyword(fz_as) then
	       if a_feature_name then
		  Result := true
		  !!rename_pair.make(name1,last_feature_name)
		  last_parent.add_rename(rename_pair)
	       else
		  fcp("Second identifier of a %"rename%" pair expected.")
	       end
	    else
	       go_back_at(l,c)
	    end
	 end
      end

   a_routine: E_ROUTINE is
	 --++ routine -> ["obsolete" manifest_string]
	 --++            ["require" ["else"] assertion]
	 --++            ["local" entity_declaration_list]
	 --++            routine_body
	 --++            ["ensure" ["then"] assertion]
	 --++            ["rescue" compound]
	 --++            "end"
	 --++
      local
	 sp: POSITION; hc: COMMENT; al: ARRAY[ASSERTION]; ea: E_ENSURE
      do
	 if a_keyword(fz_obsolete) then
	    if a_manifest_string then
	       last_manifest_string.set_is_once(true)
	       tmp_feature.set_obsolete_mark(last_manifest_string)
	    else
	       fcp("Obsolete manifest string expected.")
	    end
	 end
	 tmp_feature.set_header_comment(get_comment)
	 if a_keyword(fz_require) then
	    sp := pos(start_line,start_column)
	    if a_keyword(fz_else) then
	       hc := get_comment
	       tmp_feature.set_require_else(sp,hc,a_assertion)
	    else
	       hc := get_comment
	       tmp_feature.set_require(sp,hc,a_assertion)
	    end
	 end
	 if a_keyword(fz_local) then
	    a_local_var_list
	 end
	 Result := a_routine_body
	 if a_keyword(fz_ensure) then
	    sp := pos(start_line,start_column)
	    in_ensure := true
	    if a_keyword(fz_then) then
	       hc := get_comment
	       al := a_assertion
	       if hc /= Void or else al /= Void then
		  !!ea.make(sp,hc,al)
		  ea.set_ensure_then
	       end
	       Result.set_ensure_assertion(ea)
	    else
	       hc := get_comment
	       al := a_assertion
	       if hc /= Void or else al /= Void then
		  !!ea.make(sp,hc,al)
	       end
	       Result.set_ensure_assertion(ea)
	    end
	    in_ensure := false
	 end
	 if a_keyword(fz_rescue) then
	    in_rescue := true
	    Result.set_rescue_compound(a_compound2(fz_rescue,fz_end))
	    in_rescue := false
	 elseif a_keyword(fz_end) then
	    if ace.sedb then
	       sp := pos(start_line,start_column)
	       Result.set_sedb_trace_before_exit(sp)
	    end
	 else
	    wcp("A routine must be ended with %"end%".")
	 end
	 local_vars := Void
      end

   a_routine_body: E_ROUTINE is
	 --++ routine_body -> "deferred" |
	 --++                 "external" external |
	 --++                 "do" compound |
	 --++                 "once" compound
	 --++
      do
	 if a_keyword(fz_deferred) then
	    last_base_class.set_is_deferred
	    Result := tmp_feature.to_deferred_routine
	 elseif a_keyword(fz_external) then
	    Result := a_external
	 elseif a_keyword(fz_do) then
	    tmp_feature.set_routine_body(a_compound1)
	    Result := tmp_feature.to_procedure_or_function
	 elseif a_keyword(fz_once) then
	    inside_once_function := true
	    tmp_feature.set_routine_body(a_compound1)
	    Result := tmp_feature.to_once_routine
	 else
	    fcp("Routine body expected.")
	 end
      end

   a_r1(left_part: like last_expression) is
	 --++ r1 -> "implies" e1 r1 |
	 --++       ^
	 --++
      local
	 infix_implies: CALL_INFIX_IMPLIES
	 sp: POSITION
      do
	 if a_keyword(as_implies) then
	    sp := pos(start_line,start_column)
	    if a_e1 then
	       !!infix_implies.make(left_part,sp,last_expression)
	       a_r1(infix_implies)
	    else
	       error(sp,"Expression expected after 'implies'.")
	    end
	 else
	    last_expression := left_part
	 end
      end

   a_r2(left_part: like last_expression) is
	 --++ r2 -> "or else" e2 r2 |
	 --++       "or" e2 r2 |
	 --++       "xor" e2 r2 |
	 --++       ^
	 --++
      local
	 infix_or_else: CALL_INFIX_OR_ELSE
	 infix_or: CALL_INFIX_OR
	 infix_xor: CALL_INFIX_XOR
	 sp: POSITION
      do
	 if a_keyword(as_or) then
	    sp := pos(start_line,start_column)
	    if a_keyword(fz_else) then
	       if a_e2 then
		  !!infix_or_else.make(left_part,sp,last_expression)
		  a_r2(infix_or_else)
	       else
		  err_exp(sp, False, as_or_else)
	       end
	    else
	       if a_e2 then
		  !!infix_or.make(left_part,sp,last_expression)
		  a_r2(infix_or)
	       else
		  err_exp(sp, False, as_or)
	       end
	    end
	 elseif a_keyword(as_xor) then
	    sp := pos(start_line,start_column)
	    if a_e2 then
	       !!infix_xor.make(left_part,sp,last_expression)
	       a_r2(infix_xor)
	    else
	       err_exp(sp, False, as_xor)
	    end
	 else
	    last_expression := left_part
	 end
      end

   a_r3(left_part: like last_expression) is
	 --++ r3 -> "and then" e3 r3 |
	 --++       "and" e3 r3 |
	 --++       ^
	 --++
      local
	 infix_and_then: CALL_INFIX_AND_THEN
	 infix_and: CALL_INFIX_AND
	 sp: POSITION
      do
	 if a_keyword(as_and) then
	    sp := pos(start_line,start_column)
	    if a_keyword(fz_then) then
	       if a_e3 then
		  !!infix_and_then.make(left_part,sp,last_expression)
		  a_r3(infix_and_then)
	       else
		  err_exp(sp, False, as_and_then)
	       end
	    else
	       if a_e3 then
		  !!infix_and.make(left_part,sp,last_expression)
		  a_r3(infix_and)
	       else
		  err_exp(sp, False, as_and)
	       end
	    end
	 else
	    last_expression := left_part
	 end
      end

   a_r4(left_part: like last_expression) is
	 --++ r4 -> "=" e4 r4 |
	 --++       "/=" e4 r4 |
	 --++       "<=" e4 r4 |
	 --++       "<" e4 r4 |
	 --++       ">=" e4 r4 |
	 --++       ">" e4 r4 |
	 --++       ^
	 --++
      local
	 call_infix: CALL_INFIX; sp: POSITION
      do
	 if skip1('=') then
	    sp := pos(start_line,start_column)
	    if a_e4 then
	       !CALL_INFIX_EQ!call_infix.make(left_part,sp,last_expression)
	       a_r4(call_infix)
	    else
	       err_exp(sp, False, as_eq)
	    end
	 elseif skip2('/','=') then
	    sp := pos(start_line,start_column)
	    if a_e4 then
	       !CALL_INFIX_NEQ!call_infix.make(left_part,sp,last_expression)
	       a_r4(call_infix)
	    else
	       err_exp(sp, False, as_neq)
	    end
	 elseif skip2('<','=') then
	    sp := pos(start_line,start_column)
	    if a_e4 then
	       !CALL_INFIX_LE!call_infix.make(left_part,sp,last_expression)
	       a_r4(call_infix)
	    else
	       err_exp(sp, False, as_le)
	    end
	 elseif skip2('>','=') then
	    sp := pos(start_line,start_column)
	    if a_e4 then
	       !CALL_INFIX_GE!call_infix.make(left_part,sp,last_expression)
	       a_r4(call_infix)
	    else
	       err_exp(sp, False, as_ge)
	    end
	 elseif skip1('<') then
	    sp := pos(start_line,start_column)
	    if a_e4 then
	       !CALL_INFIX_LT!call_infix.make(left_part,sp,last_expression)
	       a_r4(call_infix)
	    else
	       err_exp(sp, False, as_lt)
	    end
	 elseif skip1unless2('>','>') then
	    sp := pos(start_line,start_column)
	    if a_e4 then
	       !CALL_INFIX_GT!call_infix.make(left_part,sp,last_expression)
	       a_r4(call_infix)
	    else
	       err_exp(sp, False, as_gt)
	    end
	 else
	    last_expression := left_part
	 end
      end

   a_r5(left_part: like last_expression) is
	 --++ r5 -> "+" e5 r5 |
	 --++       "-" e5 r5 |
	 --++       ^
	 --++
      local
	 infix_plus: CALL_INFIX_PLUS; infix_minus: CALL_INFIX_MINUS
	 sp: POSITION
      do
	 if skip1('+') then
	    sp := pos(start_line,start_column)
	    if a_e5 then
	       !!infix_plus.make(left_part,sp,last_expression)
	       a_r5(infix_plus)
	    else
	       err_exp(sp, False, as_plus)
	    end
	 elseif skip1('-') then
	    sp := pos(start_line,start_column)
	    if a_e5 then
	       !!infix_minus.make(left_part,sp,last_expression)
	       a_r5(infix_minus)
	    else
	       err_exp(sp, False, as_minus)
	    end
	 else
	    last_expression := left_part
	 end
      end

   a_r6(left_part: like last_expression) is
	 --++ r6 -> "*" e6 r6 |
	 --++       "//" e6 r6 |
	 --++       "\\" e6 r6 |
	 --++       "/" e6 r6 |
	 --++       ^
	 --++
      local
	 infix_times: CALL_INFIX_TIMES
	 infix_int_div: CALL_INFIX_INT_DIV
	 infix_int_rem: CALL_INFIX_INT_REM
	 infix_div: CALL_INFIX_DIV
	 sp: POSITION
      do
	 if skip1('*') then
	    sp := pos(start_line,start_column)
	    if a_e6 then
	       !!infix_times.make(left_part,sp,last_expression)
	       a_r6(infix_times)
	    else
	       err_exp(sp, False, as_muls)
	    end
	 elseif skip2('/','/') then
	    sp := pos(start_line,start_column)
	    if a_e6 then
	       !!infix_int_div.make(left_part,sp,last_expression)
	       a_r6(infix_int_div)
	    else
	       err_exp(sp, False, as_slash_slash)
	    end
	 elseif skip2('\','\') then
	    sp := pos(start_line,start_column)
	    if a_e6 then
	       !!infix_int_rem.make(left_part,sp,last_expression)
	       a_r6(infix_int_rem)
	    else
	       err_exp(sp, False, as_backslash_backslash)
	    end
	 elseif skip1unless2('/','=') then
	    sp := pos(start_line,start_column)
	    if a_e6 then
	       !!infix_div.make(left_part,sp,last_expression)
	       a_r6(infix_div)
	    else
	       err_exp(sp, False, as_slash)
	    end
	 else
	    last_expression := left_part
	 end
      end

   a_r7(left_part: like last_expression) is
	 --++ r7 -> "^" e7 r7 |
	 --++       ^
	 --++
      local
         sp: POSITION
      do
         if skip1('^') then
            sp := pos(start_line,start_column)
            if a_e7 then
               a_r7(last_expression)
               !CALL_INFIX_POWER!last_expression.make(left_part,sp,
						      last_expression)
            else
               err_exp(sp, False, as_pow)
            end
         else
            last_expression := left_part
         end
      end

   a_r8(left_part: like last_expression) is
	 --++ r8 -> free_operator e8 r8 |
	 --++       ^
	 --++
      local
	 infix_name: FEATURE_NAME; infix_freeop: CALL_INFIX_FREEOP
      do
	 if a_free_operator_usage(False) then
	    infix_name := last_feature_name
	    if a_e8 then
	       !!infix_freeop.make(left_part,infix_name,last_expression)
	       a_r8(infix_freeop)
	    else
	       err_exp(infix_name.start_position, False,
		       infix_name.to_string)
	    end
	 else
	    last_expression := left_part
	 end
      end

   a_r10(do_instruction: BOOLEAN; t: EXPRESSION; fn: FEATURE_NAME
	 eal: EFFECTIVE_ARG_LIST
         separate_allowed: BOOLEAN) is
	 --++ r10 -> "." after_a_dot |
	 --++        ^
	 --++
      do
	 if skip1('.') then
	    if t /= Void and then t.is_void then
	       error_handler.add_position(t.start_position)
	       error_handler.append("Void is not a valid target.")
	       error_handler.print_as_fatal_error
	    end
	    a_after_a_dot(do_instruction,to_call(t,fn,eal))
	 else
	    if do_instruction then
	       last_instruction := to_proc_call(t,fn,eal)
	       last_expression := Void
	    else
	       last_expression := to_call(t,fn,eal)
	       last_instruction := Void
	    end
	 end
      end

   a_strip: BOOLEAN is
	 --++ a_strip -> "strip" "(" {identifier "," ...} ")"
      local
	 sp: POSITION
      do
	 if a_keyword(fz_strip) then
	    sp := pos(start_line,start_column)
	    if skip1('(') then
	       ok := a_feature_name_list
	       !E_STRIP!last_expression.make(sp,last_feature_name_list)
	       if not skip1(')') then
		  fcp("')' expected to end a strip expression.")
	       end
	       Result := true
	    else
	       fcp("'(' expected to begin a strip list.")
	    end
	 end
      end

   a_tag_mark: BOOLEAN is
	 --++ tag_mark -> identifier ":"
	 --++
      local
	 l, c: INTEGER
      do
	 l := line; c := column
	 if a_identifier then
	    if skip1(':') then
	       Result := true
	       last_tag_mark := tmp_name.to_tag_name
	    else
	       go_back_at(l,c)
	    end
	 end
      end

   a_then_part_list(ifthenelse: IFTHENELSE) is
	 --++ then_part_list -> {then_part "elseif"}+
	 --++
      do
	 from
	    if not a_then_part(ifthenelse) then
	       fcp("In %"if ... then ...%".")
	    end
	 until
	    not a_keyword(fz_elseif)
	 loop
	    if not a_then_part(ifthenelse) then
	       fcp("In %"elseif ... then ...%".")
	    end
	 end
      end

   a_then_part(ifthenelse: IFTHENELSE): BOOLEAN is
	 --++ then_part -> expression "then"
	 --++
      local
	 expression: EXPRESSION
      do
	 if a_expression then
	    Result := true
	    expression := expression_with_comment(last_expression)
	    if not a_keyword(fz_then) then
	       wcp("Added %"then%".")
	    end
	    ifthenelse.add_if_then(expression,a_compound1)
	 end
      end

   a_type: BOOLEAN is
	 --++ type -> "like" <anchor> | "expanded" class_type | BIT <constant>
         --++         "separate" class_type
	 --++         type_formal_generic | class_type
	 --++
      local
	 sp: POSITION
	 argument_name2: ARGUMENT_NAME2
      do
	 Result := true
	 if a_keyword(fz_like) then
	    sp := pos(start_line,start_column)
	    if a_infix then
	       !TYPE_LIKE_FEATURE!last_type.make(sp,last_feature_name)
	    elseif a_prefix then
	       !TYPE_LIKE_FEATURE!last_type.make(sp,last_feature_name)
	    elseif a_identifier then
	       if a_current then
		  !TYPE_LIKE_CURRENT!last_type.make(sp)
	       elseif a_argument then
		  argument_name2 ?= last_expression
		  !TYPE_LIKE_ARGUMENT!last_type.make(sp,argument_name2)
	       else
		  !TYPE_LIKE_FEATURE!last_type.make(sp,
				           tmp_name.to_simple_feature_name)
	       end
	    else
	       fcp("Anchor expected. An anchor could be `Current', %
		   %a feature name or an argument name.")
	    end
	 elseif a_keyword(fz_expanded) then
	    sp := pos(start_line,start_column)
	    if a_class_type then
	       !TYPE_EXPANDED!last_type.make(sp,last_class_type)
	    else
	       fcp("Must find a class type after %"expanded%".")
	    end
	 elseif a_keyword(fz_reference) then
	    sp := pos(start_line,start_column)
	    if a_class_type then
	       create {TYPE_REFERENCE} last_type.make(sp,last_class_type)
	    else
	       fcp("Must find a class type after %"reference%".")
	    end
         elseif a_keyword(fz_separate) then
	    sp := pos(start_line,start_column)
	    if a_class_type then
	       last_type := separate_tools.create_type_separate(sp,
							last_class_type)
	    else
	       fcp("Must find a class type after %"separate%".")
	    end
	 elseif a_keyword(as_bit) then
	    sp := pos(start_line,start_column)
	    if a_integer(0) then
	       !TYPE_BIT_1!last_type.make(sp,last_integer_constant)
	    elseif a_identifier then
	       !TYPE_BIT_2!last_type.make(sp,tmp_name.to_simple_feature_name)
	    else
	       fcp("Expected constant for BIT_N type declaration.")
	    end
	 elseif a_type_formal_generic then
	    last_type := last_type_formal_generic
	 elseif a_class_type then
	    last_type := last_class_type
	 else
	    Result := false
	 end
      end

   a_unary: BOOLEAN is
	 --++ unary -> "not" | "+" | "-"
	 --++
      do
	 if a_keyword(as_not) then
	    create
	       last_feature_name.prefix_name(as_not,
					     pos(start_line,start_column))
	    Result := true
	 elseif skip1('+') then
	    create
	       last_feature_name.prefix_name(as_plus,
					     pos(start_line,start_column))
	    Result := true
	 elseif skip1('-') then
	    create
	       last_feature_name.prefix_name(as_minus,
					     pos(start_line,start_column))
	    Result := true
	 end
      end

   a_when_part(i: E_INSPECT): BOOLEAN is
	 --++ when_part -> "when" {when_part_item "," ...} then compound
	 --++
	 --++ when_part_item -> constant ".." constant |
	 --++                   constant
	 --++
	 --++ constant -> character_constant | integer_constant | identifier
	 --++
      local
	 state: INTEGER; e_when: E_WHEN; constant: EXPRESSION
      do
	 if a_keyword(fz_when) then
	    from
	       Result := true
	       !!e_when.make(pos(start_line,start_column),get_comment)
	    until
	       state > 3
	    loop
	       inspect
		  state
	       when 0 then  -- Sepator read, waiting a constant or "then".
		  if a_constant then
		     constant := last_expression
		     state := 1
		  elseif a_keyword(fz_then) then
		     if constant /= Void then
			e_when.add_value(constant)
		     end
		     e_when.set_compound(a_compound1)
		     i.add_when(e_when)
		     state := 4
		  elseif cc = ',' then
		     wcp(em7)
		     ok := skip1(',')
		  else
		     fcp(em4)
		     state := 4
		  end
	       when 1 then -- First constant read.
		  if a_keyword(fz_then) then
		     if constant /= Void then
			e_when.add_value(constant)
		     end
		     e_when.set_compound(a_compound1)
		     i.add_when(e_when)
		     state := 4
		  elseif skip2('.','.') then
		     state := 2
		  elseif skip1(',') then
		     e_when.add_value(constant)
		     constant := Void
		     state := 0
		  else
		     fcp(em4)
		     state := 4
		  end
	       when 2 then -- ".." read.
		  if a_constant then
		     e_when.add_slice(constant,last_expression)
		     constant := Void
		     state := 3
		  else
		     fcp(em4)
		     state := 4
		  end
	       when 3 then -- Slice read.
		  if skip1(',') then
		     state := 0
		  elseif a_keyword(fz_then) then
		     e_when.set_compound(a_compound1)
		     i.add_when(e_when)
		     state := 4
		  elseif a_constant then
		     constant := last_expression
		     warning(tmp_name.start_position,em5)
		     state := 1
		  else
		     fcp(em4)
		     state := 4
		  end
	       end
	    end
	 end
      end

   mandatory_writable: EXPRESSION is
	 -- Skip and return the writable which is mandatory here.
      do
	 if a_identifier then
	    if a_current then
	       error_handler.add_position(last_expression.start_position)
	       error_handler.append("Current is not a writable variable.")
	       error_handler.print_as_fatal_error
	    elseif a_argument then
	       error_handler.add_position(last_expression.start_position)
	       error_handler.append(em21)
	       error_handler.print_as_fatal_error
	    elseif a_result or else a_local_variable then
	       Result := last_expression
	    else
	       Result := tmp_name.to_simple_feature_name
	    end
	 else
	    fcp(em22)
	 end
      ensure
	 Result /= Void
      end

   to_call(t: EXPRESSION; fn: FEATURE_NAME
	   eal: EFFECTIVE_ARG_LIST): EXPRESSION is
      require
	 t /= Void
      do
	 if fn = Void then
	    check
	       eal = Void
	    end
	    Result := t
	 elseif eal = Void then
	    !CALL_0_C!Result.make(t,fn)
	 elseif eal.count = 1 then
	    !CALL_1_C!Result.make(t,fn,eal)
	 else
	    !CALL_N!Result.make(t,fn,eal)
	 end
      end

   to_proc_call(t: EXPRESSION; fn: FEATURE_NAME
		eal: EFFECTIVE_ARG_LIST): PROC_CALL is
      do
	 if fn = Void then
	    fcp("An expression has a result value. %
		%This is not an instruction.")
	 elseif eal = Void then
	    !PROC_CALL_0!Result.make(t,fn)
	 elseif eal.count = 1 then
	    !PROC_CALL_1!Result.make(t,fn,eal)
	 else
	    !PROC_CALL_N!Result.make(t,fn,eal)
	 end
      end

   buffer: STRING is
      once
	 create Result.make(80)
      end

   a_identifier1: BOOLEAN is
	 -- Case Insensitive (option -case_insensitive).
      require
	 case_insensitive
      local
	 backward_column: INTEGER; stop: BOOLEAN
      do
	 if cc.is_letter then
	    from
	       backward_column := column
	       tmp_name.reset(pos(line,backward_column))
	       tmp_name.extend(cc.to_lower)
	    until
	       stop
	    loop
	       next_char
	       inspect
		  cc
	       when 'a'..'z' then
		  tmp_name.extend(cc)
	       when '0'..'9','_' then
		  tmp_name.extend(cc)
	       when 'A'..'Z' then
		  tmp_name.extend(cc.to_lower)
	       else
		  stop := true
	       end
	    end
	    if tmp_name.isa_keyword then
	       cc := tmp_name.buffer.first
	       column := backward_column
	    else
	       Result := true
	       skip_comments
	    end
	 end
      end

   a_identifier2: BOOLEAN is
	 -- Case Sensitivity for identifiers (default).
      require
	 not case_insensitive
      local
	 backward_column: INTEGER
	 stop, may_be_a_keyword, style_warning: BOOLEAN
      do
	 if cc.is_letter then
	    from
	       backward_column := column
	       may_be_a_keyword := true
	       tmp_name.reset(pos(line,backward_column))
	       tmp_name.extend(cc)
	    until
	       stop
	    loop
	       next_char
	       inspect
		  cc
	       when 'a'..'z' then
		  tmp_name.extend(cc)
	       when 'A'..'Z' then
		  style_warning := true
		  tmp_name.extend(cc)
	       when '0'..'9','_' then
		  may_be_a_keyword := false
		  tmp_name.extend(cc)
	       else
		  stop := true
	       end
	    end
	    if may_be_a_keyword and then tmp_name.isa_keyword then
	       cc := tmp_name.buffer.first
	       column := backward_column
	    else
	       Result := true
	       skip_comments
	       if style_warning then
		  if not no_style_warning then
		     warning(tmp_name.start_position,em25)
		  end
	       end
	    end
	 end
      end

   show_nb(nb: INTEGER; tail: STRING) is
      do
	 if nb > 0 then
	    echo.w_put_string(fz_error_stars)
	    echo.w_put_integer(nb)
	    echo.w_put_string(tail)
	 end
      end

   tmp_feature: TMP_FEATURE

   to_frozen_feature_name is
      do
	 create last_feature_name.frozen_name(last_feature_name)
      end

   last_result: ABSTRACT_RESULT is
      local
	 sp: POSITION
      do
	 sp := tmp_name.start_position
	 if inside_function then
	    if inside_once_function then
	       !ONCE_RESULT!Result.make(sp)
	    else
	       !ORDINARY_RESULT!Result.make(sp)
	    end
	 else
	    error_handler.add_position(sp)
	    error_handler.append(
               "`Result' must only be used inside a function.")
	    error_handler.print_as_fatal_error
	 end
      ensure
	 Result /= Void
      end

   faof: FIXED_ARRAY[E_FEATURE] is
      once
	 !!Result.with_capacity(256)
      end

   err_exp(sp: POSITION; prefix_flag: BOOLEAN; operator: STRING) is
	 -- When an error occurs in the right hand side of some `operator'.
      local
	 msg: STRING
      do
	 msg := "Right hand side expression of "
	 if prefix_flag then
	    msg.append(fz_prefix)
	 else
	    msg.append(fz_infix)
	 end
	 msg.append(" %"")
	 msg.append(operator)
	 msg.append("%" expected.")
	 error_handler.add_position(sp)
	 error_handler.append(msg)
	 error_handler.print_as_fatal_error
      end

   pos(l, c: INTEGER): POSITION is
      do
	 Result.eiffel_file(l,c,current_id,last_base_class)
      end

   instruction_with_comment(i: INSTRUCTION): INSTRUCTION is
	 -- There is some following comment, `i' may be wrapped
	 -- inside some INSTRUCTION_WITH_COMMENT object.
      local
	 c: COMMENT
      do
	 c := get_comment
	 if c = Void or else c.count = 0 then
	    Result := i
	 else
	    Result := i.add_comment(c)
	 end
      end

   expression_with_comment(e: EXPRESSION): EXPRESSION is
	 -- There is some following comment, `e' may be wrapped
	 -- inside some EXPRESSION_WITH_COMMENT object.
      local
	 c: COMMENT
      do
	 c := get_comment
	 if c = Void or else c.count = 0 then
	    Result := e
	 else
	    !EXPRESSION_WITH_COMMENT!Result.make(e,c)
	 end
      end

   unknown_external_language(external_tag: MANIFEST_STRING) is
      do
	 error_handler.add_position(external_tag.start_position)
	 error_handler.append("Unkown external language specification.")
	 error_handler.print_as_fatal_error
      end

   unused_once_warning_check is
      do
	 if last_manifest_string.is_once then
	    error_handler.add_position(last_manifest_string.start_position)
	    error_handler.append(em18)
	    error_handler.print_as_warning
	 end
      end

   create_infix_prefix(prefix_flag: BOOLEAN; l, c: INTEGER) is
      local
	 operator: STRING
      do
	 operator := string_aliaser.item(buffer)
	 if prefix_flag then
	    create last_feature_name.prefix_name(operator, pos(l,c))
	 else
	    create last_feature_name.infix_name(operator, pos(l,c))
	 end
      end
   
   old_ref_style_compatibility(bt: E_TYPE) is
	 -- To obsolete all *_REF type, just ad the warning here.
      require
	 bt.is_basic_eiffel_expanded
      do
	 create {TYPE_REFERENCE}
	    last_base_type.make(bt.start_position,bt)
      end
   
   singleton_memory: EIFFEL_PARSER is
      once
	 Result := Current
      end

invariant

   is_real_singleton: Current = singleton_memory

end -- EIFFEL_PARSER