{------------------------------------------------------------------------------ KNOWLEDGE Knowledge, in the form of sentences and phrases with variables in them, is represented using a tree structure. Simple parsers are provided for rules, goals, relations and nouns. Functions are provided for converting a text file into a table of definitions and for accessing the table. ------------------------------------------------------------------------------} module Knowledge where import Result import Table import List(nub)--1.3 -- The type `Phrase' is a tree-like data structure for storing sentences and -- phrases. A phrase is usually a term consisting of a word with a list of -- subphrases. Variables are picked out separately as they have a special role, -- and a function is provided for extracting a duplicate-free list of the names -- of the variables in a phrase. Variable names start with capital letters. A -- single type is used rather than separate types for rules, goals, relations -- and so on to make it easier to write the matching and searching modules. data Phrase = Term String [Phrase] | Var String vars p = nub (names p) where names (Var x) = [x] names (Term x ps) = concat [names p | p <- ps] -- The display function `showPhrase' assumes that the only phrases are -- variables, nouns, and pairs of subphrases with joining words between them. showPhrase (Var x) = x showPhrase (Term x []) = x showPhrase (Term op [p1,p2]) = showPhrase p1 ++ " " ++ op ++ " " ++ showPhrase p2 -- Each parser takes a list of words and returns a Phrase. The parsers for -- rules, goals and relations involve finding the joining word and the two -- lists of words on either side with `split', and then parsing the two lists. -- A rule is a relation and a goal joined by `if'. A goal is a collection of -- relations joined by `and' and `or', with `and' binding tighter. A relation -- is two nouns joined by a verb, and a noun is a word, perhaps preceded by -- `a', `an' or `the' for readability. These parsers are neither very general -- (no brackets, for instance) nor very efficient, nor do they detect errors. rule ws = split ws relation "if" goal goal ws | elem "or" ws = split ws goal "or" goal | elem "and" ws = split ws goal "and" goal | otherwise = relation ws relation ws = split ws noun verb noun where verb = head [w | w<-ws, elem w verbs] verbs = ["is","describes","has","can","eats"] noun [a,x] | elem a ["a","an","the"] = noun [a++" "++x] noun [x] | ('A' <= head x) && (head x <= 'Z') = Var x noun [x] = Term x [] split ws f op g = Term op [f lhs, g rhs] where lhs = takeWhile (/=op) ws rhs = tail (dropWhile (/=op) ws) -- The `definitions' function takes a list of text lines and converts it into a -- table of definitions. Each entry is a verb, together with the rules which -- are define that verb. Each verb is either completely defined in the table -- (eg `is', `describes') or is completely undefined so that the user has to be -- asked (eg `has', `eats'). The `relevant' function extracts from the table -- the list of rules which are relevant to a given relation. definitions ls = updateList newTable [(v, def v) | v<-verbs] where def v = [r | r<-rs, verb r == v] verbs = nub [verb r | r<-rs] verb (Term "if" [Term v ns, g]) = v rs = [rule (words l) | l<-ls] relevant defs (Term v ns) = if fails lookup then [] else answer lookup where lookup = find defs v