/* * MAIN.PRO, 1989. * SYNOPSIS: Natural Language Query Parser. * GPL(C) Mohsin Ahmed, http://www.cs.albany.edu/~mosh */ main1 :- retractall( endofsession ), nl, write(' INLQP ' ), nl, repeat, retractall( model(_) ), main2, endofsession. main2 :- repeat, write('> '), readin( S ), analyse( S ), modelcomplete, main3. modelcomplete :- endofsession. modelcomplete :- model( select( [], _ , _ ) ), !, fail. modelcomplete :- model( select( _ , [], _ ) ), !, fail. modelcomplete :- model( select( _ , _ , _ ) ). main3 :- endofsession. main3 :- showmodel, okattrib, oktable, okwhere. /* *********************************************************************** */ /* Top Level Sentence Analyser */ /* Check if user needs help, and provide it */ analyse( S ) :- member( help, S ), !, help( S ). analyse( [new|_] ) :- !, retractall( model(_) ). /* A sentence beginning with END word portends user wants to finish off */ analyse( [B|S] ) :- syn( end, EndL ), member( B, EndL ), !, asserta( endofsession ). /* If S contains a SHOW(select) word, call anashow( S ) */ analyse( S ) :- synreplace( S, select, So), member( select, So ), !, anashow( So ). analyse( S ) :- write('Cannot Comprehend'), nl. /* Analyse S containing the word SHOW */ /* show the DataDictionary if asked */ anashow( S ) :- inter( [dict,dictionary], S ), showdatadict. anashow( S ) :- inter( [table,tables], S ), anashowtab( S ). /* Analyse S containing the words SHOW TABLE(S) and take action */ /* anashowtab( Si ) show tables mentioned in S */ anashowtab( S ) :- table( T, _ ), tableandor( S, T ), showtable( T ), fail. anashowtab( _ ). /* If no model yet start building a model */ anashow( S ) :- not( model( select(_,_,_) ) ), asserta( model( select([],[],[]) ) ), ! , anasel( S ). anashow( S ) :- anasel( S ). /* Analyse sentence to make a query: anasel( Si) */ /* Remove sugar words and standardize synonyms */ anasel( S1 ) :- effacelist( S1, [me,us,the], S2 ), synreplace( S2, min, S3 ), synreplace( S3, max, S4 ), synreplace( S4, average, S5 ), synreplace( S5, count, S6 ), synreplace( S6, phone, S7 ), synreplace( S7, address, S8 ), synreplace( S8, insert, S9 ), synreplace( S9, delete, S10 ), tail( select, S10, S11 ), anasel2( S11 ). anasel2( S ) :- anaattrib1( S ), !. anasel2( S ) :- syn( that_who, WT ), syn( of, OF ), cutlist( S, OF, WT, S2 ), !, anatab2( S2 ), headlist( OF, S, S3 ), anaattrib3( S3 ), taillist( WT, S, S4 ), anawhere( S4 ). /* if no where clause ( cutlist will fail above ) */ anasel2( S ) :- syn( of, OF ), taillist( OF, S, S2 ), anatab2( S2 ), headlist( OF, S, S3 ), anaattrib3( S3 ). anaattrib3( S ) :- retract( model( select(_,T,W) ) ), fandor( S, T, F ), asserta( model( select(F,T,W) ) ). /* Analyse sentence to make a query: anaattrib( Si): S is stdzd by anasel */ /* User wants all attributes */ anaattrib1( [all,I|S1] ) :- member( I, [info,information] ), anaattrib2( S2 ). anaattrib1( [everything|S1] ) :- anaattrib2( S2 ). /* User wanted all attributes (Select * from .. ) */ anaattrib2( S1 ) :- effacelist( S1, [on,about], S2 ), retract( model( select( _ , T, C ) ) ), asserta( model( select( [*] , T, C ) ) ), anatab1( S2 ). /* Find the Table Name */ anatab1( [T1,T2|S] ) :- tableandor( [T1,T2], Table ), retract( model( select( A , _ , C ) ) ), asserta( model( select( A , Table, C ) ) ). anatab2( S ) :- tableandor( S, Table ), retract( model( select( A , _ , C ) ) ), asserta( model( select( A , Table, C ) ) ). /* Analyse the condition, in and/or format */ anawhere( [] ). anawhere( [_] ). anawhere( [_,_] ). anawhere( S ) :- member( and, S ), head( and, S, S1 ), anawhereor( S1 ), tail( and, S, S2 ), anawhere( S2 ). anawhere( S ) :- anawhereor( S ). anawhereor( S ) :- anawhereor2( S, S1 ), retract( model( select( A, T, W ) ) ), asserta( model( select( A, T, [S1|W] ) ) ). /* Analyse 'or' conditions: anawhereor2( Si, So ), So = C1 or C2 or ... */ anawhereor2( S, SF ) :- member( or, S ), ! , head( or, S, S1 ), anawhereor3( S1, S2 ), tail( or, S, S3 ), anawhereor2( S3, S4 ), append( S2, [or|S4], SF ). anawhereor2( S, SF ) :- anawhereor3( S, SF ). /* Analyse a single condition: anawhereor3( Si, So ) */ anawhereor3( S, [F,Op,V] ) :- head( is, S, S1 ), model( select(_,T,_) ), fandor( S1, T, [F|_] ), taillist( [than,to], S, [V|_] ), operator( S, Op ). operator( S, '=' ) :- member( equal, S ), ! . operator( S, '<' ) :- member( less , S ), ! . operator( S, '>' ) :- member( more , S ), ! . operator( S, '>' ) :- member( greater , S ), ! . topspy :- spy( analyse ), spy( anasel ), spy( anashow ), spy( anashowtab ), spy( anasel2 ), spy( anaattrib1 ), spy( anaattrib2 ), spy( anaattrib3 ), spy( anatab1 ), spy( anatab2 ). /* SHOW.PRO */ showdatadict :- table( Table, AndOrWordList ), write('Table: '), write( Table ), nl, showtable( Table ), fail. showdatadict. showtable( Table ) :- field( Table, Field, FieldType, RelatedWords ), write(' '), write( Field ), write(' '), write( FieldType ), nl, fail. showtable( Table ). showmodel :- !, model( select( Attrib, Tables, Conditions ) ), write(' select '), writelist( Attrib ), nl, write(' from '), writelist( Tables ), writecond( Conditions ). /* write a list with comma as separator */ writelist( [] ) :- !. writelist( [A] ) :- !, write( A ). writelist( [A|B] ) :- !, write( A ), write(', '), writelist( B ). writelist( Term ) :- write( Term ). writecond( [] ) :- !, nl. writecond( L ) :- nl, write(' where '), nl, writecond1( L ). /* write a list with 'and'/nl as separator */ writecond1( [A] ) :- !, write( ' ( '), writecond2( A ), write(' )'), nl . writecond1( [A|B] ) :- write( ' ( '), writecond2( A ), write(' )'), write(' and '), nl, writecond1( B ). /* write a list with blanks as separators */ writecond2( [A] ) :- !, write( A ). writecond2( [A|B] ) :- write( A ), write(' '), writecond2( B ). /* SENT.PRO */ /* S Sentence, L list, W word, C Char, LC LookaheadChar, i in, o out */ /* readin( S i ) reads in a sentence ( a list of words ) */ readin( [W|Ws] ) :- get0(C), readword(C,W,C1), readsent(W,C1,Ws). /* readsent( Wi, LCi, So ) Read in a Sent following word W */ readsent( W, _, [] ) :- lastword( W ), !. readsent( W, C, [W1|Ws] ) :- readword( C, W1, C1 ), readsent( W1, C1, Ws ). /* readword( LCi, Wo, LCo ) Read in a complete word W */ readword( C, W, C1 ) :- singlechar( C ), !, name( W, [C] ), get0( C1 ). readword( C, W, C2 ) :- inword( C, NewC ), !, get0( C1 ), restword( C1, Cs, C2 ), name( W, [NewC|Cs] ). /* Remove filler chars */ readword( C, W, C2 ) :- get0( C1 ), readword( C1, W, C2 ). /* restword( LCi, Wo, LCo ) Read in rest of word until nonword char */ restword( C, [NewC|Cs], C2 ) :- inword( C, NewC ), !, get0( C1 ), restword( C1, Cs, C2 ). restword( C, [], C ). /* singlechar( Co ) if C is a word by itself */ singlechar(44). /* , */ singlechar(59). /* ; */ singlechar(58). /* : */ singlechar(63). /* ? */ singlechar(33). /* ! */ singlechar(46). /* . */ singlechar(39). /* ' */ /* inword( Ci, Co ) if C can be part of a word */ /* a to z */ /* A to Z converted to Small char Cs */ /* 0 to 9 */ /* Minus sign - */ inword( C, C ) :- C > 96, C < 123. inword( C, Cs ) :- C > 64, C < 91, Cs is C + 32. inword( C, C ) :- C > 47, C < 58. inword( 45, 45 ). /* lastword( Ci ) if C is EndofSentence marker */ lastword( '.' ). lastword( '!' ). lastword( '?' ). /* LIST.PRO */ /* T term, L list, F functor, i in, o out */ /* member( Ti, Li ) if T is a member of List */ member( A, [A|_]) :- !. member( A, [_|C]) :- member(A,C). /* subset( L1i, L2i ) if L1 is a subset of L2 */ subset( [] , _ ) :- !. subset( [A|B],L ) :- member( A, L ), subset( B, L ). /* tail( Ti, L1i, L2o ) L2 is tail part of L1 cut at term T */ tail( X, [X|B], B ):- !. tail( X, [_|B], C ):- tail( X, B, C ). /* head( Ti, L1i, L2o ) L2 is head part of L1 cut at term T */ head( X, [X|_], [] ):- !. head( X, [A|B], [A|HB] ):- head( X, B, HB ). /* taillist( Li, L1i, L2o ) L2 is tail part of L1 cut at term T in L */ taillist( LX, [X|B], B ):- member( X, LX ), !. taillist( LX, [_|B], C ):- taillist( LX, B, C ). /* headlist( Li, L1i, L2o ) L2 is head part of L1 cut at term T in L */ headlist( LX, [X|B], [] ):- member( X, LX ), !. headlist( LX, [A|B], [A|TB] ):- headlist( LX, B, TB ). /* last( Li, To ) T is the last element of T */ last( [A], A ) :- !. last( [_|B], C ) :- last( B, C ). /* append( L1i, L2i, L3o ) */ append( [], A, A ) :- !. append( [A|B], C, [A|D] ) :- append( B, C, D ). /* reverse( L1i, L2o ) L2 is reverse of list L1 */ reverse( [], [] ) :- !. reverse( [A|B], D ) :- reverse( B,C ), append( C, [A], D ). /* inter( L1i, L2i, L3o ) L3 is L1 intersection L2 */ inter( [], C, [] ) :- !. inter( [A|B], C, E ) :- member( A, C ), !, inter( B, C, D ), append( [A], D, E ). inter( [_|B], C, E ) :- inter( B, C, E ). /* inter( L1i, L2i ) if L1 and L2 have a common element */ inter( [], _ ) :- !, fail. inter( [A|B], C ) :- member( A, C ), !. inter( [_|B], C ) :- inter( B, C ). /* add( Ti, Li, Lo ) add term T to Li(if not present in Li) to get Lo */ add( X, L, L ) :- member( X, L ), !. add( X, L, [X|L] ). /* sublist( L1i, L2i ) if L1 is a sublist of L2 */ sublist( [], _ ):- !. sublist( [A|B], [A|C] ) :- sublistmatch( B, C ), !. sublist( A, [_|C] ) :- sublist( A, C ). sublistmatch( [], _ ) :- !. sublistmatch( [A|B], [A|C] ):- sublistmatch( B, C ), !. /* efface( L1i, Ti, L2o ) remove all T from L1 to get L2 */ efface( [], X , [] ) :- !. efface( [X|B], X, C ) :- !, efface( B, X, C ). efface( [A|B], X, [A|C] ) :- efface( B, X, C ). /* effacelist( L1i, LTi, L2o ) remove all T of LT from L1 to get L2 */ effacelist( [], L , [] ) :- !. effacelist( [A|B], L, C ) :- member(A, L), !, effacelist( B, L, C ). effacelist( [A|B], L, [A|C] ) :- effacelist( B, L, C ). /* subst( L1i, T1i, T2i, L2o ) substitute all T1 by T2 in L1 to get L2 */ subst( [], _ , _ , [] ) :- !. subst( [X|B], X, Y, [Y|C] ) :- !, subst( B, X, Y, C ). subst( [A|B], X, Y, [A|C] ) :- subst( B, X, Y, C ). /* match( L1i, L2i ) if L1 is a subsequence of L2 */ match( [], _ ) :- !. match( [A|B], L ) :- tail( A, L , M ), match( B, M ). /* cut( L1i, T1i, T2i, L2o ) L2 = max sublist in L1 delimited by T1,T2 */ cut( L, X, Y, P ) :- tail( X, L, M ), reverse( M, N ), tail( Y, N, O ), reverse( O, P ). /* cutlist(L1i,LT1i,LT2i,L2o) L2 = max sublist in L1 delimited by any T1 of LT1, any T2 of LT2 */ cutlist( L, LX, LY, P ) :- taillist( LX, L, M ), reverse( M, N ), taillist( LY, N, O ), reverse( O, P ). /* uni( Li, Lo ) Lo is Li without duplicate elements */ uniq( [], [] ) :- !. uniq( [A|B], BU ) :- member( A, B ), !, uniq( B, BU ). uniq( [A|B], [A|BU] ) :- uniq( B, BU ). /* oddeven(L1i,LOo,LEo) LO is 1st,3rd,..elems of L1, LE even elems of L1 */ oddeven( [], [], [] ) :- !. oddeven( [O,E|L], [O|LO], [E|LE] ) :- oddeven( L, LO, LE ). /* SYN.PRO */ /* syn( Word i, SynonymsList o ) */ syn( select, [ select, choose, print, display, give, show, list, type ] ). syn( name, [ name, called, titled ] ). syn( max, [ max, maximum, largest, oldest, best, costliest, most, greatest ] ). syn( min, [ min, minimum, smallest, youngest, worst, cheapest, least ] ). syn( average, [ average, mean, medium, median ] ). syn( count, [ count, number, total, many ] ). syn( phone, [ phone, ring, telephone ] ). syn( address, [ address, place, location, located, district ] ). syn( insert, [ insert, add ] ). syn( delete, [ delete, del, remove, zap ] ). syn( end, [ stop, exit, end, quit, bye, finish, done, over, finish ] ). syn( sugar_word, [ a, an, the, so, me, i, am, very ] ). syn( who_word, [ who, where, why, when, which, how ] ). syn( that_word, [ that, whose ] ). syn( that_who, [ that, whose, who, where, why, when, which, how ] ). syn( of, [ of, on, about ] ). /* help( KeywordList i , HelpSentence o ) */ help( [ stop, exit, end, quit, bye, finish, finish, over ], 'Bye ends the program' ). help( [ select, query ], 'See SQL plus manual, Select Attributes from Table where Conditions' ). help( [ sql, sqlplus ], 'See Oracle SQL plus manual' ). help( [ update, modify, delete ], 'See Oracle SQL plus manual' ). help( [ tables, table, from ], 'The data tables of the database, use Show Data Dictionary to see it' ). help( [ where, conditions ], 'Rows satisfying the conditions will be selected by the query'). help( [ rows, attributes, columns ], 'The fields of the database table' ). help( [ helpless ], 'See INLPQ and SQLplus manuals, type exit to end' ). /* Synonym Replacement Routines */ /* synreplace( L i, W i, L o ) replace all syns of W in Li to get Lo */ synreplace( Si, W, So ) :- syn( W, SL ), !, replace( SL, W, Si, So ). synreplace( S, _ , S ). /* replace( Li, Wi, L1i, L2o ) replace any of Li in L1i by Wi to get W2o */ /* W is a word, Li is a list of synonyms for W */ /* L1 is the input sentence, all synonyms of W in it are replaced by W */ /* to give sentence L2. */ replace( _ , _ , [], [] ). replace( WL, W, [A|Si], [W|So] ) :- member( A, WL ), !, replace( WL, W, Si, So ). replace( WL, W, [A|Si], [A|So] ) :- replace( WL, W, Si, So ). /* help( Sentence i ) Help on subject in S or general help */ help( S ) :- help( WL, HS ), inter( S, HS ), !, write( HS ), nl. help( S ) :- help( [helpless], HS ), write( HS ), nl. /* DICT.PRO */ /* table( table name, [ and/or related word list ] ) */ table( emp, [ [emp], [employee], [employees], [workers] ] ). table( dept, [ [dept], [department], [section] ] ). /* field( table name, field, field type, [ and/or related word list ] ) */ field( emp, empno, number(4), [ [employee,number], [empno] ] ). field( emp, ename, char(10), [ [employee,name], [ename], [name] ] ). field( emp, job, char(9), [ [job], [work], [does], [duty] ] ). field( emp, mrg, number(4), [ [manager,number] ] ). field( emp, hiredate, date, [ [hire,date], [hiredate], [start], [begin], [time] ] ). field( emp, sal, number(7,2), [ [salary], [sal], [income] ] ). field( emp, comm, number(7,2), [ [commission], [comm] ] ). field( emp, deptno, number(2), [ [department,number], [deptno] ] ). field( dept, deptno, number(2), [ [department,number], [deptno] ] ). field( dept, dname, char(14), [ [department,name], [dname] ] ). field( dept, loc, char(13), [ [location], [place], [loc], [located], [department,at] ] ). /* Data Related routines */ /* tableandor( WL i, Table o ) */ tableandor( S, T ) :- table( T, AOWL ), !, /* one table is enough */ andor( S, AOWL ). /* fandor( WL i, Table i, FieldList o ) gives all fields in WL */ /* First findall and store as field( Name ) */ fandor( S, T, _ ) :- asserta( field([mark]) ), /* stack mark is [mark] */ field( T, F, _ , AOWL ), andor( S, AOWL ), asserta( field(F) ), fail. /* Finally collectall field( Names ) */ fandor( _ , _, F ) :- fcollect( [], M ), !, M = F . /* fcollect( Li, Lo ) collects all field( Names ) into Lo */ fcollect( S, L ) :- fget( F ), !, fcollect( [F|S], L ). fcollect( L, L ). /* fget( F o ) get a field( F ) until F is [mark] */ fget( F ) :- retract( field( F ) ), !, F \== [mark] . /* andor( WL i, AOWL i ) if WL is in any of AOWL */ andor( S, [H|T] ) :- subset( H, S ), !. andor( S, [H|T] ) :- andor( S, T ).