1 {------------------------------------------------------------------------------
    2                                    KNOWLEDGE
    3 
    4 Knowledge, in the form of sentences and phrases with variables in them, is
    5 represented using a tree structure. Simple parsers are provided for rules,
    6 goals, relations and nouns. Functions are provided for converting a text file
    7 into a table of definitions and for accessing the table.
    8 ------------------------------------------------------------------------------}
    9 
   10 module Knowledge where
   11 import Result
   12 import Table
   13 import List(nub)--1.3
   14 
   15 -- The type `Phrase' is a tree-like data structure for storing sentences and
   16 -- phrases. A phrase is usually a term consisting of a word with a list of
   17 -- subphrases. Variables are picked out separately as they have a special role,
   18 -- and a function is provided for extracting a duplicate-free list of the names
   19 -- of the variables in a phrase. Variable names start with capital letters. A
   20 -- single type is used rather than separate types for rules, goals, relations
   21 -- and so on to make it easier to write the matching and searching modules.
   22 
   23 data Phrase = Term String [Phrase] | Var String
   24 
   25 vars p = nub (names p)  where
   26    names (Var x) = [x]
   27    names (Term x ps) = concat [names p | p <- ps]
   28 
   29 -- The display function `showPhrase' assumes that the only phrases are
   30 -- variables, nouns, and pairs of subphrases with joining words between them.
   31 
   32 showPhrase (Var x) = x
   33 showPhrase (Term x []) = x
   34 showPhrase (Term op [p1,p2]) =
   35    showPhrase p1 ++ " " ++ op ++ " " ++ showPhrase p2
   36 
   37 -- Each parser takes a list of words and returns a Phrase. The parsers for
   38 -- rules, goals and relations involve finding the joining word and the two
   39 -- lists of words on either side with `split', and then parsing the two lists.
   40 -- A rule is a relation and a goal joined by `if'. A goal is a collection of
   41 -- relations joined by `and' and `or', with `and' binding tighter. A relation
   42 -- is two nouns joined by a verb, and a noun is a word, perhaps preceded by
   43 -- `a', `an' or `the' for readability. These parsers are neither very general
   44 -- (no brackets, for instance) nor very efficient, nor do they detect errors.
   45 
   46 rule ws = split ws relation "if" goal
   47 
   48 goal ws
   49    | elem "or" ws  = split ws goal "or" goal
   50    | elem "and" ws = split ws goal "and" goal
   51    | otherwise     = relation ws
   52 
   53 relation ws =
   54    split ws noun verb noun  where
   55    verb = head [w | w<-ws, elem w verbs]
   56    verbs = ["is","describes","has","can","eats"]
   57 
   58 noun [a,x] | elem a ["a","an","the"]  =  noun [a++" "++x]
   59 noun [x] | ('A' <= head x) && (head x <= 'Z') = Var x
   60 noun [x]  =  Term x []
   61 
   62 split ws f op g =
   63    Term op [f lhs, g rhs]
   64    where
   65    lhs = takeWhile (/=op) ws
   66    rhs = tail (dropWhile (/=op) ws)
   67 
   68 -- The `definitions' function takes a list of text lines and converts it into a
   69 -- table of definitions. Each entry is a verb, together with the rules which
   70 -- are define that verb. Each verb is either completely defined in the table
   71 -- (eg `is', `describes') or is completely undefined so that the user has to be
   72 -- asked (eg `has', `eats'). The `relevant' function extracts from the table
   73 -- the list of rules which are relevant to a given relation.
   74 
   75 definitions ls =
   76    updateList newTable [(v, def v) | v<-verbs] where
   77    def v = [r | r<-rs, verb r == v]
   78    verbs = nub [verb r | r<-rs]
   79    verb (Term "if" [Term v ns, g]) = v
   80    rs = [rule (words l) | l<-ls]
   81 
   82 relevant defs (Term v ns) =
   83    if fails lookup then [] else answer lookup where
   84    lookup = find defs v