/* the morphology charts */ /* Group 1 nouns */ ending(n1,"a",noun(nom,sing,_,_,_,_)). ending(n1,"am",noun(acc,sing,_,_,_,_)). ending(n1,"ae",noun(gen,sing,_,_,_,_)). ending(n1,"ae",noun(dat,sing,_,_,_,_)). ending(n1,"a",noun(abl,sing,_,_,_,_)). ending(n1,"ae",noun(nom,plural,_,_,_,_)). ending(n1,"as",noun(acc,plural,_,_,_,_)). ending(n1,"arum",noun(gen,plural,_,_,_,_)). ending(n1,"is",noun(dat,plural,_,_,_,_)). ending(n1,"is",noun(abl,plural,_,_,_,_)). /* Group 2 nouns */ /* type a... */ ending(n2a,"us",noun(nom,sing,_,_,_,_)). ending(n2a,"um",noun(acc,sing,_,_,_,_)). ending(n2a,"i",noun(gen,sing,_,_,_,_)). ending(n2a,"o",noun(dat,sing,_,_,_,_)). ending(n2a,"o",noun(abl,sing,_,_,_,_)). ending(n2a,"i",noun(nom,plural,_,_,_,_)). ending(n2a,"os",noun(acc,plural,_,_,_,_)). ending(n2a,"orum",noun(gen,plural,_,_,_,_)). ending(n2a,"is",noun(dat,plural,_,_,_,_)). ending(n2a,"is",noun(abl,plural,_,_,_,_)). /*...and type b */ ending(n2b,"um",noun(nom,sing,_,_,_,_)). ending(n2b,"um",noun(acc,sing,_,_,_,_)). ending(n2b,"i",noun(gen,sing,_,_,_,_)). ending(n2b,"o",noun(dat,sing,_,_,_,_)). ending(n2b,"o",noun(abl,sing,_,_,_,_)). ending(n2b,"a",noun(nom,plural,_,_,_,_)). ending(n2b,"a",noun(acc,plural,_,_,_,_)). ending(n2b,"orum",noun(gen,plural,_,_,_,_)). ending(n2b,"is",noun(dat,plural,_,_,_,_)). ending(n2b,"is",noun(abl,plural,_,_,_,_)). /* fourth declension nouns */ /* type a... */ ending(n4a,"us",noun(nom,sing,_,_,_,_)). ending(n4a,"um",noun(acc,sing,_,_,_,_)). ending(n4a,"us",noun(gen,sing,_,_,_,_)). ending(n4a,"ui",noun(dat,sing,_,_,_,_)). ending(n4a,"u",noun(abl,sing,_,_,_,_)). ending(n4a,"us",noun(nom,plural,_,_,_,_)). ending(n4a,"us",noun(acc,plural,_,_,_,_)). ending(n4a,"uum",noun(gen,plural,_,_,_,_)). ending(n4a,"ibus",noun(dat,plural,_,_,_,_)). ending(n4a,"ibus",noun(abl,plural,_,_,_,_)). /* and type b */ ending(n4b,"u",noun(nom,sing,_,_,_,_)). ending(n4b,"u",noun(acc,sing,_,_,_,_)). ending(n4b,"us",noun(gen,sing,_,_,_,_)). ending(n4b,"u",noun(dat,sing,_,_,_,_)). ending(n4b,"u",noun(abl,sing,_,_,_,_)). ending(n4b,"ua",noun(nom,plural,_,_,_,_)). ending(n4b,"ua",noun(acc,plural,_,_,_,_)). ending(n4b,"uum",noun(gen,plural,_,_,_,_)). ending(n4b,"ibus",noun(dat,plural,_,_,_,_)). ending(n4b,"ibus",noun(abl,plural,_,_,_,_)). /* 5th declension */ ending(n5,"es",noun(nom,sing,_,_,_,_)). ending(n5,"em",noun(acc,sing,_,_,_,_)). ending(n5,"ei",noun(gen,sing,_,_,_,_)). ending(n5,"ei",noun(dat,sing,_,_,_,_)). ending(n5,"e",noun(abl,sing,_,_,_,_)). ending(n5,"es",noun(nom,plural,_,_,_,_)). ending(n5,"es",noun(acc,plural,_,_,_,_)). ending(n5,"erum",noun(gen,plural,_,_,_,_)). ending(n5,"ebus",noun(dat,plural,_,_,_,_)). ending(n5,"ebus",noun(abl,plural,_,_,_,_)). /* the adjectives, which are a modification of the nouns */ /* feminine adjectives use the same endings as 1st declension nouns */ ending(a1,X,adj(Case,Number,fem,Meaning)):- ending(n1,X,noun(Case,Number,_,_,_,_)). /* masculine adjectives use the same endings as type 2a nouns */ ending(a1,X,adj(Case,Number,masc,Meaning)):- ending(n2a,X,noun(Case,Number,_,_,_,_)). /* neuter adjectives use the same endings as type 2b nouns */ ending(a1,X,adj(Case,Number,neut,Meaning)):- ending(n2b,X,noun(Case,Number,_,_,_,_)). /* 1st conjugation verbs */ /* present tense... */ ending(v1,"o",verb(1,sing,pres,_)). ending(v1,"as",verb(2,sing,pres,_)). ending(v1,"at",verb(3,sing,pres,_)). ending(v1,"amus",verb(1,plural,pres,_)). ending(v1,"atis",verb(2,plural,pres,_)). ending(v1,"ant",verb(3,plural,pres,_)). /* ... and perfect */ ending(v1,"avi",verb(1,sing,perf,_)). ending(v1,"avisti",verb(2,sing,perf,_)). ending(v1,"avit",verb(3,sing,perf,_)). ending(v1,"avimus",verb(1,plural,perf,_)). ending(v1,"avistis",verb(2,plural,perf,_)). ending(v1,"averunt",verb(3,plural,perf,_)). /* 2nd conjugation verbs */ /* present... */ ending(v2,"eo",verb(1,sing,pres,_)). ending(v2,"es",verb(2,sing,pres,_)). ending(v2,"et",verb(3,sing,pres,_)). ending(v2,"emus",verb(1,plural,pres,_)). ending(v2,"etis",verb(2,plural,pres,_)). ending(v2,"ent",verb(3,plural,pres,_)). /* ... and perfect */ ending(v2,"ui",verb(1,sing,perf,_)). ending(v2,"uisti",verb(2,sing,perf,_)). ending(v2,"uit",verb(3,sing,perf,_)). ending(v2,"uimus",verb(1,plural,perf,_)). ending(v2,"uistis",verb(2,plural,perf,_)). ending(v2,"uerunt",verb(3,plural,perf,_)). /* 3rd conjugation verbs */ /* present tense only-- I'm not trying to handle group 3 perfect tense, because there's no regular pattern */ ending(v3,"o",verb(1,sing,pres,_)). ending(v3,"is",verb(2,sing,pres,_)). ending(v3,"it",verb(3,sing,pres,_)). ending(v3,"imus",verb(1,plural,pres,_)). ending(v3,"itis",verb(2,plural,pres,_)). ending(v3,"unt",verb(3,plural,pres,_)). /* 4th conjugation verbs */ /* present... */ ending(v4,"o",verb(1,sing,pres,_)). ending(v4,"s",verb(2,sing,pres,_)). ending(v4,"t",verb(3,sing,pres,_)). ending(v4,"mus",verb(1,plural,pres,_)). ending(v4,"tis",verb(2,plural,pres,_)). ending(v4,"unt",verb(3,plural,pres,_)). /* ...and perfect */ ending(v4,"vi",verb(1,sing,perf,_)). ending(v4,"visti",verb(2,sing,perf,_)). ending(v4,"vit",verb(3,sing,perf,_)). ending(v4,"vimus",verb(1,plural,perf,_)). ending(v4,"vistis",verb(2,plural,perf,_)). ending(v4,"verunt",verb(3,plural,perf,_)). /* the top-level goal */ go:- clearall(s1), % To clear the database. write('Type in your Latin sentence, ending with a full stop'),nl, read_in(Input), % Get input as a list of atoms. knockoff(Input,Sentence),!, internalise(Sentence), parse, check_rep, % to ensure that the parse is valid do_english(Output), write_out(Output). /* internalise--used to convert the list of atoms into internal representation for parsing */ internalise([]). internalise([First|Rest]):- name(First,Chars), parse_word(Chars), internalise(Rest). /* parse_word-- used to determine the grammatical characteristics (case, tense and so on) of a given word (supplied as a list of ascii characters) . Records resultant frame in internal database for ease of access, and so that it doesn't clash with the program. */ parse_word(Word):- lexeme(Stem,Type,Wordframe), append(Stem,Ending,Word), ending(Type,Ending,Wordframe), temprecord(s1,Wordframe,Ref). /* parse--finds a noun in the internal database, and then looks for any adjectives agreeing with it (using bagof). Retracts the noun record in question, and asserts an np structure, carrying the adjectives, in its place. Then, goes round again. When there are none left, second clause applies, causing the goal to succeed. */ parse:- recorded(s1,noun(Case,Number,Lgender,Egender,Properness,Meaning),Ref), bagof(Ameaning,recorded(s1,adj(Case,Number,Lgender,Ameaning),_),Mods), erase(Ref), temprecord(s1,np(Case,Number,Lgender,Egender,Properness,Meaning,Mods),_), parse. parse. /* check_rep --to determine whether the internal representation contains a valid parse of a Latin sentence */ check_rep:- recorded(_,verb(Person,Number,Tense, /* do_english(Output) --Output is the English sentence as a list of atoms */ do_english(Output):- engsent(Output,[]). /* minimal components of an English sentence-- subj np and verb */ engsent-->cased_np(nom),engverb,do_rest. /* cased_np(Case) finds an np record with case argument Case, and generates an English preposition followed by an English np from it */ cased_np(Case)--> { recorded(s1,np(Case,Number,Lgender,Egender,Properness,Nmeaning,Mods),_),!}, prep(Case), engnp(Number,Properness,Nmeaning,Mods). cased_np(Case)-->default(Case). /* Default representatives for cases. Most are just blank, but the subject position must be filled (in English at least), so we simply find an appropriate pronoun to go with the Person and Number specification of the verb when we haven't got any explicit subject. Note that the system doesn't know the difference between transitive and intransitive verbs, so it doesn't know that it should supply a default object for transitive verbs (e.g. 'it'). */ default(dat)-->[]. default(abl)-->[]. default(acc)-->[]. default(gen)-->[]. default(nom)-->{recorded(s1,verb(Person,Number,Tense,Meaning),_)}, engpronoun(Person,Number). /* English Pronouns for use when the system can't find any subject np. engpronoun(Person,Number) rewrites as the subject pronoun appropriate to Person and Number in English */ engpronoun(1,sing)-->[i]. engpronoun(2,sing)-->[you]. engpronoun(3,sing)-->[he]. engpronoun(1,plural)-->[we]. engpronoun(2,plural)-->[you]. engpronoun(3,plural)-->[they]. /* engnp(Number,Properness,Nmeaning,Mods) generates an English np with Head noun Nmeaning and adjectives Mods. Also may attach definite article depending on whether or not head noun is a proper or common one. */ engnp(Number,Properness,Nmeaning,Mods)--> engdet(Properness),engadjs(Mods),engnoun(Number,Nmeaning). /* engdet(Properness) generates a definite article for common nouns, and leaves proper ones alone */ engdet(com)-->[the]. engdet(prop)-->[]. /* engadjs(List) generates adjectives from the list, using a recursive grammar rule. */ engadjs([])--> {!,true}. engadjs([Firstadj|Moreadjs])--> [Firstadj],engadjs(Moreadjs). /* engnoun(Number,Nmeaning) generates an English noun from the Nmeaning and Number arguments, doing any obligatory twiddling for irregular nouns */ /* In the case of irregularities, an obligatory twiddle specifies the form */ engnoun(Number,Nmeaning)--> {irregular(noun(Number,Nmeaning),Irregplural),!}, [Irregplural]. /* for regular singulars (I can't think of any irregular ones, but I suppose there might be some, which is why the above rule for irregulars doesn't specify plural as the Number argument), the word-form is exactly the same as the stem found in the dictionary */ engnoun(sing,Nmeaning)--> [Nmeaning]. /* regular plural nouns are handled by simply adding an "s" to the end */ engnoun(plural,Nmeaning)--> {name(Nmeaning,Singlist), append(Singlist,"s",Plulist), name(Plumeaning,Plulist)}, [Plumeaning]. /* do_rest-- sorts out any other nps which haven't yet been dealt with */ do_rest-->cased_np(acc),cased_np(dat),cased_np(abl),cased_np(gen). /* producing English verbs an overall rule for catching irregularities */ engverb-->{recorded(s1,verb(Person,Number,Tense,Meaning),_), irregular(verb(Person,Number,Tense,Meaning),Output),!}, [Output]. /* the easy case-- if the verb is present tense plural, then the form is just identical to the stem */ engverb-->{recorded(s1,verb(_,plural,pres,Meaning),_)}, [Meaning]. /* for the third person singular present tense, we just stick on an "s" */ engverb-->{recorded(s1,verb(3,sing,pres,Meaning),_), name(Meaning,Chars), append(Chars,"s",Outchrs), name(Output,Outchrs)}, [Output]. /* for any other person of the singular present tense, we don't add anything (in fact, it gets treated just like present tense plural) */ engverb-->{recorded(s1,verb(Person,sing,pres,Meaning),_), Person=\=3}, [Meaning]. /* for regular past tenses, we just stick a 'd' on the end (anything irregular has already been filtered out by the first engverb clause, right up at the top of the engverb section) */ engverb-->{recorded(s1,verb(_,_,perf,Meaning),_), name(Meaning,Chars), append(Chars,"d",Outchrs), name(Output,Outchrs)}, [Output]. /* prepositions for the various cases chosen fairly arbitrarily--see writeup */ prep(dat)-->[to]. prep(gen)-->[of]. prep(nom)-->[]. prep(acc)-->[]. prep(abl)-->[from]. /* obligatory twiddles for sorting out English irregularities take the form: irregular(Input,Output) Input is a description of the problem the system is trying to solve (in morphemes, if you like) and Output is the solution (the realisation of the morphemes) */ irregular(verb(3,sing,pres,go),goes). irregular(verb(3,sing,pres,go),go). irregular(verb(_,_,perf,go),went). irregular(verb(1,sing,pres,be),am). irregular(verb(2,sing,pres,be),are). irregular(verb(3,sing,pres,be),is). irregular(verb(_,plural,pres,be),are). irregular(verb(3,sing,pres,have),has). irregular(verb(_,_,perf,have),had). irregular(verb(_,_,perf,send),sent). irregular(verb(_,_,perf,hear),heard). irregular(verb(_,_,perf,say),said). irregular(verb(_,_,perf,weaken),weakened). irregular(verb(_,_,perf,polish),polished). irregular(verb(_,_,perf,proclaim),proclaimed). irregular(verb(_,_,perf,walk),walked). /* dictionary */ /* format--lexeme(Stem,Type,Frame). Stem is the stem of the word in question; Type is its type, indicating where to look for its endings and Frame is a structure whose functor represents the class of the word (noun, verb &c) and whose arguments represent the information the system can find out about the word (for nouns: Case, Gender (English & Latin), Number, Properness (ie whether a noun is proper or common) and Meaning). Frame got from the dictionary is unified with the one from the endings, instantiating the anon. variables in each. */ lexeme("puell",n1,noun(_,_,fem,fem,girl)). lexeme("serv",n2a,noun(_,_,masc,masc,com,slave)). lexeme("bon",a1,adj(_,_,_,good)). lexeme("mal",a1,adj(_,_,_,bad)). lexeme("par",v1,verb(_,_,_,trans,prepare)). lexeme("am",v1,verb(_,_,_,trans,love)). lexeme("hab",v2,verb(_,_,_,trans,have)). lexeme("mitt",v3,verb(_,_,_,trans,send)). lexeme("aud",v4,verb(_,_,_,trans,hear)). lexeme("dic",v3,verb(_,_,_,say)). lexeme("bacul",n2b,noun(_,_,neut,neut,com,stick)). lexeme("marc",n2a,noun(_,_,masc,masc,prop,marcus)). lexeme("infirm",v1,verb(_,_,_,weaken)). lexeme("stult",a1,adj(_,_,_,stupid)). lexeme("iniust",a1,adj(_,_,_,unjust)). lexeme("domin",n2a,noun(_,_,masc,masc,com,master)). lexeme("magn",a1,adj(_,_,_,big)). lexeme("pol",v4,verb(_,_,_,polish)). lexeme("r",n5,noun(_,_,fem,neut,com,thing)). lexeme("verb",n2b,noun(_,_,neut,neut,com,word)). lexeme("principi",n2b,noun(_,_,neut,neut,com,beginning)). lexeme("ambul",v1,verb(_,_,_,walk)). /* write_out(List) does the reverse of what Read_in does, i.e. it writes out a list of atoms with spaces in between (capitalising the first word as it goes) */ write_out([First|Rest]):- cap_write(First),space,trundle_out(Rest). cap_write(Thing):- name(Thing,[First_Letter|More_Letters]), First_Letter < 123, First_Letter > 96, New_First_Letter is First_Letter - 32, name(New_Thing,[New_First_Letter|More_Letters]), write(New_Thing). trundle_out([Thing]):-write(Thing),write('.'). trundle_out([First|Rest]):- write(First),space, trundle_out(Rest). space:- write(' '). /* clearall(Key) erases everything recorded under the key in question, but also succeeds if there's nothing there in the first place. */ clearall(Key):-not(recorded(Key,Thing,Ref)),!. clearall(Key):- recorded(Key,Thing,Ref), erase(Ref), clearall(Key). /* another useful procedure for recording into the internal database; just like recordz, except that when you backtrack across it, it doesn't just fail, it erases what it recorded first time round, THEN fails */ temprecord(Key,Thing,Ref):- recordz(Key,Thing,Ref). temprecord(Key,Thing,Ref):- recorded(Key,Thing,Ref), erase(Ref),fail.