eliza.bush

# File : eliza.bush

    #!/usr/local/bin/bush

    -- Eliza
    --
    -- Original author: Joseph Weizenbaum

    procedure eliza is

    -- Keywords
    --
    -- Keywords and responses are loaded into the keywords
    -- variable.  Keywords are prefixed with the keyword_tag
    -- character, responses with the response_tag character.

    delimiter  : constant character := '~';
    nokey_marker : constant string := delimiter & "NOKEYFOUND";

    -- keyword lists are pairs of a keyword and first reponse field
    -- reponses are lists of responses terminated with an empty field

    single_keywords : string := "";
    num_single      : natural := 0;
    multi_keywords  : string := "";
    num_multi       : natural := 0;
    responses       : string := "";
    num_responses   : natural := 0;

    -- User Input
    --
    -- i is the what the user types.  previous is the
    -- previous value of i, used to check if the user
    -- repeats his/herself.

    i : string := "";
    previous : string := "";

    begin

    -- initialization

    put_line( "Eliza" );
    new_line;

    declare
      type data_type is ( keyword, replies );
      data_file : file_type;
      current_type : data_type := keyword;
      s : string;
      terminate_response_list : boolean := false;
    begin
      put_line( "Please wait...loading keywords/responses..." );
      open( data_file, in_file, "eliza.data" );
      while not end_of_file( data_file ) loop
           s := get_line( data_file );
           if s = "." then
              current_type := keyword;
           elsif s = "!" then
              current_type := replies;
           elsif current_type = keyword then
              if terminate_response_list then
                 responses := @ & delimiter;
                 num_responses := @+1;
                 terminate_response_list := false;
              end if;
              if strings.index( s, ' ' ) = 0 then
                 single_keywords := @ & s & delimiter;
                 single_keywords := @ & strings.image( num_responses+1 ) & delimiter;
                 num_single := @+2;
              else
                 multi_keywords := @ & s & delimiter;
                 multi_keywords := @ & strings.image( num_responses+1 ) & delimiter;
                 num_multi := @+2;
              end if;
           else
              responses := @ & s & delimiter;
              num_responses := @+1;
              terminate_response_list := true;
           end if;
           put( "." );
      end loop;
      responses := @ & delimiter;
      close( data_file );
      new_line;
    end;

    put_line( "HI!  I'M ELIZA.  LET'S TALK.  TYPE `BYE' TO END THIS SESSION." );

    -- get next line of input

    loop

    -- read user's input

    i := "";
    while i = "" loop
      put( ">" );
      i := get_line;
    end loop;
    i := ' ' & i & ' ';

    -- clean up input

    declare
      c : character;
      new_i : string := "";
      last_was_space : boolean := false;
    begin
      for l in 1..strings.length( i ) loop
        c := strings.element( i, l );
        if c = ' ' then
           if last_was_space then
              null;
           else
              new_i := @ & c;
              last_was_space := true;
           end if;
        else
           last_was_space := false;
           if c >= "a" and c <= "z" then
              new_i := @ & strings.val(  numerics.pos( c ) - 32 );
           elsif c >= '0' and c <= '9' then
              new_i := @ & c;
           elsif c >= "A" and c <= 'Z' then
              new_i := @ & c;
           end if;
        end if;
      end loop;
      i := new_i;
    end;

    -- test for the basics

    if i = previous then
       put_line( "PLEASE DON'T REPEAT YOURSELF!" );
    elsif i = " BYE " or i = " LOGOUT " then
       put_line( "TALK TO YOU LATER!  BYE!" );
       exit;
    else
       previous := i;

       -- look for keyword(s)

       declare
          looking_for_start : constant positive := 99999;
          remains : string := "";

          eliza_reply : string := "";
          word_start  : positive := 1;
          testword    : string := "";
          first_pos   : natural := 0;
          response_pos: natural := 0;
          reading_word: boolean := false;
          ch : character;
       begin

          -- look for single keywords

          declare
            k : positive; -- keyword in the user's input
            keyword : string;
          begin
            k := 2; -- skip first null "field"
            loop
              testword := strings.field( i, k, ' ' );
              exit when testword = "";
              -- not 100% since doesn't take into account leading delimiter
              -- since first single keyword has no leading delimiter
              if strings.index( single_keywords, testword & delimiter ) > 0 then
                 for sk in 1..num_single loop
                     keyword := strings.field( single_keywords, sk, delimiter );
                     if keyword = testword then
                        response_pos := numerics.value( strings.field( single_keywords, sk+1, delimiter ) );
                        exit;
                     end if;
                 end loop;
              end if;
              exit when response_pos > 0;
              k := @+1;
            end loop;
          end;

          -- no match? look for multiple keywords

          if response_pos = 0 then
             declare
               k  : positive;
             begin
               k := 1;
               while k < positive( num_multi ) loop
                   testword := strings.field( multi_keywords, k, delimiter );
                   if strings.index( i, ' ' & testword & ' ' ) > 0 then
                      response_pos := numerics.value( strings.field( multi_keywords, k+1, delimiter ) );
                      exit;
                   end if;
                   k := @+1;
               end loop;
             end;
          end if;

          -- still no match? use fallback responses else get remainder
          -- of user input after the keyword(s)

          if response_pos = 0 then
             testword := nokey_marker;
             response_pos := numerics.value( strings.field( single_keywords, num_single, delimiter ) );
          else
             remains := strings.slice( i, positive( strings.index( i, testword ) +
                strings.length( testword ) ), strings.length( i ) );
          end if;

          -- rewrite the remainder of the input

          declare
            c : natural;
            p : positive;
          begin
            c := strings.index( remains, " ARE " );
            if c > 0 then
               p := positive( c );
               remains := strings.replace_slice( remains, p, c+4, " AM+ " );
            end if;
            c := strings.index( remains, " AM " );
            if c > 0 then
               p := positive( c );
               remains := strings.replace_slice( remains, p, c+3, " ARE+ " );
            end if;
            c := strings.index( remains, " WERE " );
            if c > 0 then
               p := positive( c );
               remains := strings.replace_slice( remains, p, c+5, " WAS+ " );
            end if;
            c := strings.index( remains, " WAS " );
            if c > 0 then
               p := positive( c );
               remains := strings.replace_slice( remains, p, c+4, " WERE+ " );
            end if;
            c := strings.index( remains, " YOU " );
            if c > 0 then
               p := positive( c );
               remains := strings.replace_slice( remains, p, c+4, " I+ " );
            end if;
            c := strings.index( remains, " I " );
            if c > 0 then
               p := positive( c );
               remains := strings.replace_slice( remains, p, c+2, " YOU+ " );
            end if;
            c := strings.index( remains, " YOUR " );
            if c > 0 then
               p := positive( c );
               remains := strings.replace_slice( remains, p, c+5, " MY+ " );
            end if;
            c := strings.index( remains, " MY " );
            if c > 0 then
               p := positive( c );
               remains := strings.replace_slice( remains, p, c+3, " YOUR+ " );
            end if;
            c := strings.index( remains, " IVE " );
            if c > 0 then
               p := positive( c );
               remains := strings.replace_slice( remains, p, c+4, " YOUVE+ " );
            end if;
            c := strings.index( remains, " YOUVE " );
            if c > 0 then
               p := positive( c );
               remains := strings.replace_slice( remains, p, c+6, " IVE+ " );
            end if;
            c := strings.index( remains, " IM " );
            if c > 0 then
               p := positive( c );
               remains := strings.replace_slice( remains, p, c+3, " YOURE+ " );
            end if;
            c := strings.index( remains, " ME " );
            if c > 0 then
               p := positive( c );
               remains := strings.replace_slice( remains, p, c+3, " YOU+ " );
            end if;
            c := strings.index( remains, " US " );
            if c > 0 then
               p := positive( c );
               remains := strings.replace_slice( remains, p, c+3, " YOU+ " );
            end if;
            c := strings.index( remains, " WE " );
            if c > 0 then
               p := positive( c );
               remains := strings.replace_slice( remains, p, c+3, " YOU+ " );
            end if;
            loop
               c := strings.index( remains, "+" );
               exit when c = 0;
               remains := strings.delete( remains, positive( c ), c );
            end loop;
            if strings.tail( remains, 3 ) = " I " then
               remains := strings.slice( remains, 1,
                  strings.length( remains ) - 2 )
                  & "ME ";
            end if;
          end;
    --put_line( "Remains (after conjugation): " & remains );

             -- attach reply

          declare
            last_pos    : natural := 0;
            reply_cnt   : natural := 0;
            reply       : natural := 0;
            ch          : character;
            response    : string;
          begin

            -- count replies

            last_pos := response_pos+1;
            loop
               response := strings.field( responses, last_pos, delimiter );
               exit when response = "";
               last_pos := @+1;
            end loop;
            reply_cnt := last_pos - response_pos;

            reply := numerics.truncation(numerics.random * float(reply_cnt) );
            eliza_reply := strings.field( responses, response_pos+reply,
              delimiter );

            ch := strings.element( eliza_reply, positive( strings.length( eliza_reply ) ) );
            if ch = '*' then
               eliza_reply := strings.head( eliza_reply,
                 strings.length( eliza_reply )-1 ) & remains;
            end if;
          end;
          put_line( eliza_reply );
       end;
    end if;

    end loop;

    end eliza;