{------------------------------------------------------------------------------
                                 EXPERT SYSTEM

This prototype expert system program uses the modules `result.g', `table.g',
`knowledge.g', `match.g' and `search.g'. The main program reads in the file
`animals', treats the first line as the main goal to be solved, and converts
the remaining lines into the table of definitions representing the permanent
knowledge about the problem area. The program then solves the main goal and
displays the questions and solutions to the user, using the answers to the
questions to continue the search for solutions. Each answer should be `yes' or
`no'. After each solution, the user is asked whether the solution is adequate
or whether the search should be continued for alternative solutions.
------------------------------------------------------------------------------}

module Main where
import Result
import Table
import Knowledge
import Match
import Search

-- The `main' function reads in the data file before interacting with user.
-- The `process' function takes the contents of the file and the input from the
-- user and produces the output. It builds an initial goal and a definition
-- table from the file contents, and an information table from the user's
-- input, and calls the `solve' function. The list of questions and solutions
-- from this call is stripped to remove duplicate questions, and displayed as
-- output.  The questions are also extracted and used to help build the
-- information table which contains question-and-answer pairs.

main rs =
   GetProgName : GetArgs :
   let (r0:r1:rrs) = rs in
   case r1 of
      StrList [filename] -> getData filename rrs
      StrList [] -> getData "animals" rrs
      StrList args -> case r0 of
         Str prog -> [AppendChan stderr ("Usage: " ++ prog ++ " datafile\n")]
         Failure _ -> []

getData filename rs =
   ReadFile filename :
   let (r:rrs) = rs in
   case r of
      Failure ioerr -> [AppendChan stderr
         ("Unable to read file " ++ filename ++ "\n")]
      Str contents -> interact (process contents) rrs

process contents input =
   "Solving: " ++ showPhrase problem ++ "\n" ++
   display results (vars problem) replies
   where
   problem = goal (words (head (lines contents)))
   defs = definitions (tail (lines contents))
   info = enterList newTable [(q,a) | (Question q, a) <- zip results replies]
   replies = [words l /= ["no"] | l <- lines input]
   db = (defs,info)
   newsoln = Soln newTable ['X' : show n | n<-[0..]]
   results = strip [] (solve db newsoln problem)

-- The `strip' function takes the list of questions and solutions from the main
-- call to `solve' and removes all but the first occurrence of each question,
-- to make sure that the user is not asked the same question twice. The first
-- argument is a list of the questions seen so far.

strip qs [] = []
strip qs (Question q : rs) =
   if elem q qs then strip qs rs else
   Question q : strip (q:qs) rs
strip qs (soln:rs) = soln : strip qs rs

-- The display function displays a list of questions and solutions as a
-- character stream. It also takes the list of variable names in the original
-- goal to interpret solution environments using `showVars', and the list of
-- answers from the user to determine whether to continue displaying more
-- solutions.

display [] xs as = "No (more) solutions\n"
display (Question q : rs) xs as =
   "Is it true that " ++ q ++ "?\n" ++ display rs xs (tail as)
display (Soln env vs : rs) xs as =
   "Solution: " ++ sol ++ ". More?\n" ++ etc  where
   sol = showVars env xs
   etc = if as == [] || head as == False then "" else display rs xs (tail as)

showVars env vs =
   foldr1 join (map showVar vs) where
   join x y = x ++ "; " ++ y
   showVar v = v ++ " = " ++ showPhrase (subst env (Var v))
