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;