// -*- Mode: Lisp; Syntax: Common-Lisp; -*- // Code from Paradigms of AI Programming // Copyright (c) 1991 Peter Norvig // File lexicon.lisp: Macros and functions to support the entry of // words into the lexicon. define variable *abbrevs* = make(); // LTD: No macros. #"abbrev"; define method clear-abbrevs () size(*abbrevs*) := 0; end method clear-abbrevs; define method get-abbrev (symbol) *abbrevs*[symbol]; end method get-abbrev; // ============================== define variable *words* = make(
, size: 500); // LTD: No macros. #"word"; define method add-word (word, cat, #rest info) // Put word, with category and other info, into lexicon. push!(pair(cat, map(expand-abbrevs-and-variables, info)), *words*[word]); word; end method add-word; define method kwote (x) list(#"quote", x); end method kwote; // ============================== define method expand-abbrevs-and-variables (exp) // Replace all variables in exp with vars, and expand abbrevs. let bindings = #f; local method expand (exp) let _that = #f; if (_that := lookup(exp, bindings)) _that; elseif (exp == #"?") ?(); elseif (variable-p(exp)) begin let var = ?(); push!(pair(exp, var), bindings); var; end; elseif (instance?(exp, )) reuse-cons(expand(first(exp)), expand(tail(exp)), exp); else let (expansion, found?) = get-abbrev(exp); if (found?) expand-abbrevs-and-variables(expansion); else exp; end if; end if; end method expand; expand(exp); end method expand-abbrevs-and-variables; // ============================== define method word/n (word, cat, cont, #rest info) // Retrieve a word from the lexicon. if (~ unbound-var-p(deref(word))) let old-trail = size(*trail*); for (old-entry in *words*[word]) let entry = deref-copy(old-entry); if (instance?(entry, ) & unify!(cat, first(entry)) & unify!(info, tail(entry))) cont(); end if; undo-bindings!(old-trail); end for; end if; end method word/n; // ============================== define method word/2 (w, cat, cont) word/n(w, cat, cont); end method word/2; define method word/3 (w, cat, a, cont) word/n(w, cat, cont, a); end method word/3; define method word/4 (w, cat, a, b, cont) word/n(w, cat, cont, a, b); end method word/4; define method word/5 (w, cat, a, b, c, cont) word/n(w, cat, cont, a, b, c); end method word/5; define method word/6 (w, cat, a, b, c, d, cont) word/n(w, cat, cont, a, b, c, d); end method word/6; // ============================== // LTD: No macros. #"noun"; define method add-noun-form (base, #key plural = symbol(base, #"s"), sem = base, #rest slots) if (plural == #"*") add-word(base, #"noun", #"?", slots, sem); else add-word(base, #"noun", #"3sing", slots, sem); add-word(plural, #"noun", #"3plur", slots, sem); end if; end method add-noun-form; // LTD: No macros. #"verb"; define method add-verb (senses, base, #key past = symbol(strip-vowel(base), #"ed"), past-part = past, pres-part = symbol(strip-vowel(base), #"ing"), plural = symbol(base, #"s")) // Enter a verb into the lexicon. add-word(base, #"verb", #"nonfinite", senses); add-word(base, #"verb", #(#"finite", #"~3sing", #"present"), senses); add-word(past, #"verb", #(#"finite", #"?", #"past"), senses); add-word(past-part, #"verb", #"-en", senses); add-word(pres-part, #"verb", #"-ing", senses); add-word(plural, #"verb", #(#"finite", #"3sing", #"present"), senses); add-word(past-part, #"verb", #"passive", map(passivize-sense, expand-abbrevs-and-variables(senses))); end method add-verb; // ============================== define method strip-vowel (word) // Strip off a trailing vowel from a string. let str = as(, word); let end = size(str) - 1; if (vowel-p(str[end])) copy-sequence(str, 0, end); else str; end if; end method strip-vowel; define method vowel-p (char) cl-find(char, "aeiou", test: char-equal?); end method vowel-p; // ============================== define method passivize-sense (sense) // The first element of sense is the semantics; rest are slots pair(first(sense), apply(concatenate!, map(passivize-subcat, tail(sense)))); end method passivize-sense; define method passivize-subcat (slots) // Return a list of passivizations of this subcat frame. // Whenever the 1 slot is of the form (?any 1 (NP ?)), // demote the 1 to a (3), and promote any 2 to a 1. if (slot-number(first(slots)) == 1 & starts-with(third(first(slots)), #"np")) let old-1 = pair(first(first(slots)), #(#(3), #(#"pp", #"by", #"?"))); let _acc = make(); for (slot in slots) if (slot-number(slot) == 2) push-last(_acc, pair(list(first(slot), 1, third(slot)), concatenate(remove(tail(slots), slot), list(old-1)))); end if; finally _acc; end for; end if; end method passivize-subcat; define method slot-number (slot) first-or-self(second(slot)); end method slot-number; // ============================== define method copula (senses, entries) // Copula entries are both aux and main verb. // They also are used in passive verb phrases and aux-inv-S for (entry in entries) add-word(first(entry), #"aux", second(entry), third(entry)); add-word(first(entry), #"verb", second(entry), senses); add-word(first(entry), #"aux", second(entry), #"passive"); add-word(first(entry), #"be"); end for; end method copula; // ============================== define method clear-lexicon () size(*words*) := 0; clear-abbrevs(); end method clear-lexicon; define method clear-grammar () clear-examples(); clear-db(); end method clear-grammar; // ============================== // LTD: No macros. #"try"; define method try-dcg (#key cat, words) // Tries to parse WORDS as a constituent of category CAT. // With no words, runs all the :ex examples for category. // With no cat, runs all the examples. if (empty?(words)) run-examples(cat); else let args = apply(list, #(#"gap", #()), #(#"gap", #()), #"?sem", words, #(#"()")); begin do(test-unknown-word, words); words; end; top-level-prove(select (cat) #"np" => list(apply(list, #"np", #"?", #"?", #"?wh", #"?x", args)); #"vp" => list(apply(list, #"vp", #"?infl", #"?x", #"?sl", #"?v", args)); #"pp" => list(apply(list, #"pp", #"?prep", #"?role", #"?wh", #"?x", args)); #"xp" => list(apply(list, #"xp", #"?slot", #"?constituent", #"?wh", #"?x", args)); #"s" => list(apply(list, #"s", #"?", #"?sem", words, #(#"()"))); #"rel-clause" => list(apply(list, #"rel-clause", #"?", #"?x", #"?sem", words, #(#"()"))); #"clause" => list(apply(list, #"clause", #"?infl", #"?x", #"?int-subj", #"?v", #"?g1", #"?g2", #"?sem", words, #(#"()"))); end select); end if; end method try-dcg; define method test-unknown-word (word) // Print a warning message if this is an unknown word. if (~ (*words*[word] | instance?(word, ))) format-out("~&Unknown word: ~a", word); end if; end method test-unknown-word; // ============================== "eof";