In the preceding section, we built the core logic for event-driven GUIs.
In this section, we finally extend the core to a real application.
No one would actually write a commercial web server in gawk
, but
it is instructive to see that it is feasible in principle.
The application is ELIZA, the famous program by Joseph Weizenbaum that mimics the behavior of a professional psychotherapist when talking to you. Weizenbaum would certainly object to this description, but this is part of the legend around ELIZA. Take the site-independent core logic and append the following code:
function SetUpServer() { SetUpEliza() TopHeader = \ "<HTML><title>An HTTP-based System with GAWK</title>\ <HEAD><META HTTP-EQUIV=\"Content-Type\"\ CONTENT=\"text/html; charset=iso-8859-1\"></HEAD>\ <BODY BGCOLOR=\"#ffffff\" TEXT=\"#000000\"\ LINK=\"#0000ff\" VLINK=\"#0000ff\"\ ALINK=\"#0000ff\"> <A NAME=\"top\">" TopDoc = "\ <h2>Please choose one of the following actions:</h2>\ <UL>\ <LI>\ <A HREF=" MyPrefix "/AboutServer>About this server</A>\ </LI><LI>\ <A HREF=" MyPrefix "/AboutELIZA>About Eliza</A></LI>\ <LI>\ <A HREF=" MyPrefix \ "/StartELIZA>Start talking to Eliza</A></LI></UL>" TopFooter = "</BODY></HTML>" }
SetUpServer()
is similar to the previous example,
except for calling another function, SetUpEliza()
.
This approach can be used to implement other kinds of servers.
The only changes needed to do so are hidden in the functions
SetUpServer()
and HandleGET()
. Perhaps it might be necessary to
implement other HTTP methods.
The igawk
program that comes with gawk
may be useful for this process.
When extending this example to a complete application, the first
thing to do is to implement the function SetUpServer()
to
initialize the HTML pages and some variables. These initializations
determine the way your HTML pages look (colors, titles, menu
items, etc.).
The function HandleGET()
is a nested case selection that decides
which page the user wants to see next. Each nesting level refers to a menu
level of the GUI. Each case implements a certain action of the menu. At the
deepest level of case selection, the handler essentially knows what the
user wants and stores the answer into the variable that holds the HTML
page contents:
function HandleGET() { # A real HTTP server would treat some parts of the URI as a file name. # We take parts of the URI as menu choices and go on accordingly. if (MENU[2] == "AboutServer") { Document = "This is not a CGI script.\ This is an httpd, an HTML file, and a CGI script all \ in one GAWK script. It needs no separate www-server, \ no installation, and no root privileges.\ <p>To run it, do this:</p><ul>\ <li> start this script with \"gawk -f httpserver.awk\",</li>\ <li> and on the same host let your www browser open location\ \"http://localhost:8080\"</li>\ </ul>\<p>\ Details of HTTP come from:</p><ul>\ <li>Hethmon: Illustrated Guide to HTTP</p>\ <li>RFC 2068</li></ul><p>JK 14.9.1997</p>" } else if (MENU[2] == "AboutELIZA") { Document = "This is an implementation of the famous ELIZA\ program by Joseph Weizenbaum. It is written in GAWK and\ uses an HTML GUI." } else if (MENU[2] == "StartELIZA") { gsub(/\+/, " ", GETARG["YouSay"]) # Here we also have to substitute coded special characters Document = "<form method=GET>" \ "<h3>" ElizaSays(GETARG["YouSay"]) "</h3>\ <p><input type=text name=YouSay value=\"\" size=60>\ <br><input type=submit value=\"Tell her about it\"></p></form>" } }
Now we are down to the heart of ELIZA, so you can see how it works. Initially the user does not say anything; then ELIZA resets its money counter and asks the user to tell what comes to mind open-heartedly. The subsequent answers are converted to uppercase characters and stored for later comparison. ELIZA presents the bill when being confronted with a sentence that contains the phrase “shut up.” Otherwise, it looks for keywords in the sentence, conjugates the rest of the sentence, remembers the keyword for later use, and finally selects an answer from the set of possible answers:
function ElizaSays(YouSay) { if (YouSay == "") { cost = 0 answer = "HI, IM ELIZA, TELL ME YOUR PROBLEM" } else { q = toupper(YouSay) gsub("'", "", q) if (q == qold) { answer = "PLEASE DONT REPEAT YOURSELF !" } else { if (index(q, "SHUT UP") > 0) { answer = "WELL, PLEASE PAY YOUR BILL. ITS EXACTLY ... $"\ int(100*rand()+30+cost/100) } else { qold = q w = "-" # no keyword recognized yet for (i in k) { # search for keywords if (index(q, i) > 0) { w = i break } } if (w == "-") { # no keyword, take old subject w = wold subj = subjold } else { # find subject subj = substr(q, index(q, w) + length(w)+1) wold = w subjold = subj # remember keyword and subject } for (i in conj) gsub(i, conj[i], q) # conjugation # from all answers to this keyword, select one randomly answer = r[indices[int(split(k[w], indices) * rand()) + 1]] # insert subject into answer gsub("_", subj, answer) } } } cost += length(answer) # for later payment : 1 cent per character return answer }
In the long but simple function SetUpEliza()
, you can see tables
for conjugation, keywords, and answers.11 The associative array k
contains indices into the array of answers r
. To choose an
answer, ELIZA just picks an index randomly:
function SetUpEliza() { srand() wold = "-" subjold = " " # table for conjugation conj[" ARE " ] = " AM " conj["WERE " ] = "WAS " conj[" YOU " ] = " I " conj["YOUR " ] = "MY " conj[" IVE " ] =\ conj[" I HAVE " ] = " YOU HAVE " conj[" YOUVE " ] =\ conj[" YOU HAVE "] = " I HAVE " conj[" IM " ] =\ conj[" I AM " ] = " YOU ARE " conj[" YOURE " ] =\ conj[" YOU ARE " ] = " I AM " # table of all answers r[1] = "DONT YOU BELIEVE THAT I CAN _" r[2] = "PERHAPS YOU WOULD LIKE TO BE ABLE TO _ ?" …
# table for looking up answers that # fit to a certain keyword k["CAN YOU"] = "1 2 3" k["CAN I"] = "4 5" k["YOU ARE"] =\ k["YOURE"] = "6 7 8 9" …
}
Some interesting remarks and details (including the original source code of ELIZA) are found on Mark Humphrys’s home page How my program passed the Turing Test. Wikipedia provides much background information about ELIZA, including the original design of the software and its early implementations.
The version shown
here is abbreviated. The full version comes with the gawk
distribution.