--------------------------------------------------------------------------------

-- Procedure  Load_exception_file loads the exception words contained in an "ex-
-- ception file" into Exception_pool and Exception_table.   This  procedure  may
-- not  be  called  more than once to load in more than one exception file.  The
-- exception file is assumed to be open on entry and is not closed on return.

With Data_structures, Text_IO;
Use  Data_structures, Text_IO;

Procedure Load_exception_file ( Exception_file: in File_type ) is

   Length: Natural;
   Line: String (1..32767);

   Exception_table_entry_count: Natural := 0;
Procedure Append_letter_to_Exception_pool ( Char: in Letter ) is

   Exception_pool_has_overflowed: Exception;

begin

   If Exception_pool_last = Exception_pool'last then
      Raise Exception_pool_has_overflowed;
   end if;

   Exception_pool_last := Exception_pool_last + 1;
   Exception_pool(Exception_pool_last) := Char;

end Append_letter_to_Exception_pool;

-- A procedure to swap two integers...

Procedure Swap ( A, B: in out Integer ) is
   T: Integer;
begin
   T := A; A := B; B := T;
end Swap;

-- Function Compare compares two exception words that  are  specified  by  their
-- descriptions  in  Exception_pool, and returns the result of the comparison as
-- an integer.  Exception words are ordered primarily by length and  secondarily
-- by lexicographic ordering.

Function Compare ( I, J: in Exception_pool_index ) return Integer is

   D: Integer;

begin

   D := Exception_pool(I) - Exception_pool(J);
   If D /= 0 then Return D; end if;

   For K in 1 .. Exception_pool(I) loop
      D := Exception_pool(I+K) - Exception_pool(J+K);
      If D /= 0 then Return D; end if;
   end loop;

   Return 0;

end Compare;

--------------------------------------------------------------------------------

Procedure Hash_exception_word_into_Exception_table ( W_arg: in
   Exception_pool_index ) is

   Exception_file_contains_duplicate_word: Exception;
   Exception_table_has_overflowed: Exception;
   J: Natural;
   W: Exception_pool_index := W_arg;

begin

   -- To keep our algorithm (and subroutine Hyphenate's corresponding algorithm)
   -- from entering an infinite loop, we insure that there is  always  at  least
   -- one zero entry in Exception_table.


   If Exception_table_entry_count = Exception_table'length - 1 then
      Raise Exception_table_has_overflowed;
   end if;
   Exception_table_entry_count := Exception_table_entry_count + 1;

   -- We  use  a "staggered sum" of the exception word's letters as a hash func-
   -- tion.  A staggered sum is a sum which is shifted left  one  bit  prior  to
   -- each addition.  To avoid overflow we "mod" after each addition, instead of
   -- waiting to do it at the end.  The result, of course, is the same.

   J := 0;
   For I in 1 .. Exception_pool(W) loop
      J := ( 2*J + Exception_pool(W+I) ) mod Exception_table'length;
   end loop;

   While Exception_table(J) /= 0 loop

      -- If  the  current  exception  word (Exception_table(J)) is less than the
      -- word to be inserted (W), we swap the two, and continue looping  to  in-
      -- sert  the lesser word.  Note that such a swap cannot disrupt any exist-
      -- ing search paths in the hash table since  we've  replaced  an  existing
      -- entry  with  a  greater  entry (and search paths terminate only on zero
      -- entries and on lesser entries).

      If    Compare( Exception_table(J), W ) < 0 then
         Swap( Exception_table(J), W );
      elsif Compare( Exception_table(J), W ) = 0 then
         Raise Exception_file_contains_duplicate_word;
      end if;

      If J = 0 then
         J := Exception_table'last;
      else
         J := J - 1;
      end if;

   end loop;

   Exception_table(J) := W;

end Hash_exception_word_into_Exception_table;

--------------------------------------------------------------------------------

--------------------------------------------------------------------------------

-- Small, utility procedures.  We use the following procedure to add a letter to
-- the end of Exception_pool, which corresponds to adding a letter to the end of
-- the current exception word's description.

Procedure Load_exception ( S: in String ) is

   Hyphen_at_position: array (0..255) of Boolean := ( others=>False );
   J: Natural := 0;
   W: Exception_pool_index;

   Exception_word_contains_illegal_hyphen: Exception;
   Exception_word_contains_null_character: Exception;
   Exception_word_has_zero_length: Exception;
   Exception_word_is_longer_than_255_characters: Exception;

begin

   -- We start by allocating a letter in Exception_pool to  hold  the  exception
   -- word's  length;  this  letter will be filled in when we're done loading in
   -- the word's characters.  We use W to identify the first letter of  the  ex-
   -- ception word's description.

   Append_letter_to_Exception_pool( 0 ); W := Exception_pool_last;

   For I in S'range loop
      If S(I) = '-' then Hyphen_at_position(J) := True;
      else

         If J = 255 then Raise Exception_word_is_longer_than_255_characters;
         else
            J := J + 1;
         end if;

         If S(I) = ASCII.NUL then
            Raise Exception_word_contains_null_character;
         end if;

         Append_letter_to_Exception_pool( Character'pos( S(I) ) );

      end if;
   end loop;

   If J = 0 then Raise Exception_word_has_zero_length; end if;
   Exception_pool(W) := J;

   -- While loading the hyphen positions into Exception_pool we check that there
   -- are no meaningless hyphens at positions 0 and J, which are outside the ex-
   -- ception word.

   If Hyphen_at_position(0) or Hyphen_at_position(J) then
      Raise Exception_word_contains_illegal_hyphen;
   end if;

   For I in 1 .. J-1 loop
      If Hyphen_at_position(I) then
         Append_letter_to_Exception_pool( I );
      end if;
   end loop;

   Append_letter_to_Exception_pool( 0 );

   -- Finally, we hash the exception word and add an entry into Exception_table:

   Hash_exception_word_into_Exception_table( W );

end Load_exception;

--------------------------------------------------------------------------------

--------------------------------------------------------------------------------

-- Procedure Hash_exception_word_into_Exception_table hashes an  exception  word
-- and places an appropriate entry in Exception_table.  The word is specified by
-- its description in Exception_pool.



begin

   While not End_of_file( Exception_file ) loop

      Get_line( Exception_file, Line, Length );
      If Length > 0 then
         Load_exception( Line (1..Length) );
      end if;

   end loop;

end Load_exception_file;

--------------------------------------------------------------------------------

--------------------------------------------------------------------------------

-- Procedure  Load_exception  loads  a  single exception word into the exception
-- data structures.  An exception word in an exception file is a string  of  one
-- or  more  characters;  the characters may be any characters but ASCII.NUL and
-- '-'.  A string of one or more '-' characters between two exception word char-
-- acters indicates a legal hyphenation point.

