%%% Grammar E1, a trivial fragment of English s --> np, vp. % A sentence (s) is a noun phrase (np) % plus a verb phrase np --> det, n. % A noun phrase is a determiner plus a noun np --> n. % ... or just a noun. vp --> v, np. % A verb phrase is a verb and its direct % object, which is an np vp --> v. % ... or just the verb (for intransitives). n --> [mary]. % 'mary', 'john', 'woman, 'man', 'apple' % are nouns. n --> [john]. n --> [woman]. n --> [man]. n --> [apple]. det --> [the]. % 'the' is a determiner v --> [loves]. % 'loves', 'eats', and 'sings' are verbs. v --> [eats]. v --> [sings].This grammar generates sentences like “John loves Mary” and “The man sings” and “The woman eats the apple”, as well as the somewhat less plausible “The apple sings the Mary.”
?- listing([s,np,vp,n,det,v]). s(A, B) :- np(A, C), vp(C, B). np(A, B) :- det(A, C), n(C, B). np(A, B) :- n(A, B). vp(A, B) :- v(A, C), np(C, B). vp(A, B) :- v(A, B). n([mary|A], A). n([john|A], A). n([woman|A], A). n([man|A], A). n([apple|A], A). det([the|A], A). v([loves|A], A). v([eats|A], A). v([sings|A], A). Yes ?-
?- s([john,loves,mary],[]). Yes ?- s([the,woman,eats,the,apple],[]). Yes ?- s([the,man,sings],[]). Yes ?- s([apple,eats,woman,the],[]). No ?- s([apple,sings,the,mary],[]). Yes ?-
?- s([the,woman,eats,the,apple],Remainder). Remainder = [] ; Remainder = [the, apple] ; No ?-In one parse, the entire input is parsed as a sentence, and the remainder is the empty list. An alternative parse, however, is just to take [the,woman,eats] as a sentence, leaving [the,apple] as the remainder.
%%% E2: a trivial attribute grammar for a fragment of %%% English, with a synthesized attribute for structural %%% description and enforcement of number agreement. s(s(S,P)) --> np(S,Num), vp(P,Num). np(np(D,N),Num) --> det(D,Num), n(N,Num). np(np(N),pl) --> n(N,pl). np(np(N),sg) --> pn(N,sg). vp(vp(V,O),Num) --> v(V,Num), np(O,_). vp(vp(V),Num) --> v(V,Num). n(n(L),Num) --> [L], { lex(L,n,Num) }. pn(pn(L),Num) --> [L], { lex(L,pn,Num) }. v(v(L),Num) --> [L], { lex(L,v,Num) }. det(det(L),Num) --> [L], { lex(L,det,Num) }. lex(mary,pn,sg). lex(john,pn,sg). lex(woman,n,sg). lex(women,n,pl). lex(man,n,sg). lex(men,n,pl). lex(apple,n,sg). lex(apples,n,pl). lex(the,det,_). lex(some,det,pl). lex(one,det,sg). lex(loves,v,sg). lex(love,v,pl). lex(eats,v,sg). lex(eat,v,pl). lex(sings,v,sg). lex(sing,v,pl).
s( np( pn(john) ), vp( v(loves), np( pn(mary) ) ) )Here, the grammatical structure for each non-terminal on the left-hand side of a grammar rule is synthesized from the grammatical structures of the non-terminals on the right-hand side of the rule. So it's a synthetic attribute.
n(n(L),Num) --> [L], { lex(L,n,Num)}.These Prolog clauses express constraints on the grammatical construction which are not themselves grammatical constituents. They are sometimes called ‘guards’ (e.g. by [Brown/Blair 1990]); they correspond directly to the ‘semantic conditions’ which are a constituent part of attribute grammars as described by [Alblas 1991]. With the guard, this rule can be paraphrased as saying “A noun (an n), has the structure n(L) and the grammatical number Num, if (a) L (for ‘lexical form’) is a single item in the input list [L], and (b) the relation lex contains the triple (L,n,Num).”
?- s(Struc,[john,loves,mary],[]). Struc = s(np(pn(john)), vp(v(loves), np(pn(mary)))) Yes ?- s(S,[the,woman,eats,the,apple],[]). S = s(np(det(the), n(woman)), vp(v(eats), np(det(the), n(apple)))) Yes ?- s(S,[the,man,sings],[]). S = s(np(det(the), n(man)), vp(v(sings))) Yes ?- s(S,[apple,sings,the,mary],[]). No ?- s(S,[the,apple,sings,mary],[]). S = s(np(det(the), n(apple)), vp(v(sings), np(pn(mary)))) Yes ?-
/* A DCG version of the attribute grammar AG1 presented in * [Alblas 1991]. */ /* 1. The grammar */ assignment --> variable(Vtype), gets, expr(Etype), { (Vtype = int, Etype = real) -> error('Type of right hand side not convertible to type of left-hand side') ; true }.Continued in <Expressions 2>, <Terms 3>, <Simple terms 4>, <Factors 5>, <Addition operators 6>, <Multiplication operators 7>, <Punctuation 8>, <Variables 9>, <Error messages 10>, <Test data 11>
expr(T) --> term(T1), addop(_Op), expr(T2), { T1 = real -> T = real ; T = T2 }. expr(T) --> term(T).
term(T) --> factor(T1), mulop(Op), term(T2), { (Op = mul) -> (T1 = real -> T = real ; T = T2) ; (Op = div) -> T = real ; (Op = mod) -> T = int, (T1 = real -> error('left operand should be of type integer') ; true), (T2 = real -> error('right operand should be of type integer') ; true) }.
factor(T) --> variable(T). factor(T) --> lparen, expr(T), rparen.
addop(add) --> ['+']. addop(sub) --> ['-'].
mulop(mul) --> ['*']. mulop(mul) --> ['times']. mulop(mul) --> ['×']. mulop(div) --> ['/']. mulop(div) --> ['÷']. mulop(mod) --> ['mod'].
gets --> [':=']. lparen --> ['(']. rparen --> [')'].
variable(int) --> [i]. variable(int) --> [j]. variable(int) --> [k]. variable(real) --> [x]. variable(real) --> [y]. variable(real) --> [z]. variable(int) --> [a]. variable(int) --> [b]. variable(int) --> [c].
/* 2. Miscellaneous utilities (for something like verisimilitude) */ error(E) :- nl, write('!! '), write_error_message(E), write(' !!'), nl. /* if we want to fail on an error, add !, fail after the nl. */ write_error_message(E) :- atom(E), write(E). write_error_message([H|T]) :- write_error_message(H), write_error_message(T). write_error_message([]).
/* Tests */ /* to walk through these one by one, type * * testdata(Test), assignment(Node,Test,[]), write(Test), nl * * (or the equivalent) at the Prolog prompt, and then keep hitting ; to see * new test data. */ testdata([a, :=, b]). testdata([a, :=, y]). testdata([z, :=, y]). testdata([z, :=, a]). testdata([z, :=, a, /, '(', b, +, y, ')' ]). testdata([z, :=, a, mod, '(', b, +, y,')' ]). testdata([z, :=, a, mod, '(', b, +, c,')' ]). testdata([z, :=, z, mod, '(', b, +, c,')' ]). testdata([z, :=, z, *, '(', y, +, z,')' ]). testdata([a, :=, a, mod, '(', b, +, c,')' ]).
bit ::= '0'. bit ::= '1'. bitstring ::= '' /* nothing */. bitstring ::= bit, bitstring. number ::= bitstring, fraction. fraction ::= '.', bitstring. fraction ::= ''.We might wish to calculate the length and the (unsigned base-two) value of the bitstring as attributes. Using a yacc-like notation that might look like this. Notice that scale is a top-down attribute and value and fractionalvalue are bottom-up attributes.
bit ::= '0' { $0.bitvalue = 0; }. bit ::= '1' { $0.bitvalue = power(2,$0.scale); }. bitstring ::= '' { $0.value = 0; $0.length = 0; /* scale doesn't matter here */ }. bitstring ::= bit, bitstring { $0.length = $2.length + 1; $1.scale = $0.scale; $2.scale = $0.scale - 1; $0.value = $1.value + $2.value; }. number ::= bitstring, fraction { $1.scale = $1.length - 1; $0.value = $1.value + $2.fractionalvalue; }. fraction ::= '.', bitstring { $2.scale = -1; $0.fractionalvalue = $2.value; }. fraction ::= '' { $0.fractionalvalue = 0; }.
bit ::= [0] <:> bitval(0,_). bit ::= [1] <:> bitval(V,Scale) ::- V is **(2,Scale). bitstring ::= [] <:> length(0) && value(0,_). bitstring ::= bit^^B, bitstring^^B1 <:> length(Length) ::- B1 ^^ length(Length1), Length is Length1 + 1 && value(Value,ScaleB) ::- B ^^ bitval(VB,ScaleB), S1 is ScaleB - 1, B1 ^^ value(V1,S1), Value is VB + V1. number ::= bitstring ^^ B, fraction ^^ F <:> value(V) ::- B ^^ length(Length), S is Length-1, B ^^ value(VB,S), F ^^ fractional_value(VF), V is VB + VF. fraction ::= ['.'], bitstring ^^ B <:> fractional_value(V) ::- S is -1, B ^^ value(V,S). fraction ::= [] <:> fractional_value(0).
grammar ::= (clause | directive | rule)* rule ::= lhs '::=' rhs ('<:>' att-spec ('&&' att-spec)*)? lhs ::= term rhs ::= term (',' term)* attspec ::= compound-term ('::-' goal (',' goal)*)? compound-term ::= ATOM '(' term (',' term)* ')' clause ::= head ':-' body
%%% E1 (trivial context-free grammar for a fragment of English) %%% in DCTG notation. s ::= np, vp. np ::= det, n. np ::= n. vp ::= v, np. vp ::= v. n ::= [mary]. n ::= [john]. n ::= [woman]. n ::= [man]. n ::= [apple]. det ::= [the]. v ::= [loves]. v ::= [eats]. v ::= [sings].
?- dctg_reconsult('ks81dctg.pl'). Yes ?- listing([s,np,vp,n,det,v]). :- dynamic s/3. s(node(s, [A, B], []), C, D) :- np(A, C, E), vp(B, E, D). :- dynamic np/3. np(node(np, [A, B], []), C, D) :- det(A, C, E), n(B, E, D). np(node(np, [A], []), B, C) :- n(A, B, C). :- dynamic vp/3. vp(node(vp, [A, B], []), C, D) :- v(A, C, E), np(B, E, D). vp(node(vp, [A], []), B, C) :- v(A, B, C). :- dynamic n/3. n(node(n, [[mary]], []), A, B) :- c(A, mary, B). n(node(n, [[john]], []), A, B) :- c(A, john, B). n(node(n, [[woman]], []), A, B) :- c(A, woman, B). n(node(n, [[man]], []), A, B) :- c(A, man, B). n(node(n, [[apple]], []), A, B) :- c(A, apple, B). :- dynamic det/3. det(node(det, [[the]], []), A, B) :- c(A, the, B). :- dynamic v/3. v(node(v, [[loves]], []), A, B) :- c(A, loves, B). v(node(v, [[eats]], []), A, B) :- c(A, eats, B). v(node(v, [[sings]], []), A, B) :- c(A, sings, B). Yes ?-The predicate dctg_reconsult(File) is used to translate a DCTG grammar into Prolog clauses and load them; it is provided by [Abramson/Dahl/Paine 1990] and is available from a variety of sources on the net.[5]
?- s(S,[john,loves,mary],[]), write(S). node(s, [node(np, [node(n, [[john]], [])], []), node(vp, [node(v, [[loves]], []), node(np, [node(n, [[mary]], [])], [])], [])], []) S = node(s, [node(np, [node(n, [[john]], [])], []), node(vp, [node(v, [[loves]], []), node(np, [node(n, [...], [])], [])], [])], []) Yes ?- s(S,[the,woman,eats,the,apple],[]), write(S). node(s, [node(np, [node(det, [[the]], []), node(n, [[woman]], [])], []), node(vp, [node(v, [[eats]], []), node(np, [node(det, [[the]], []), node(n, [[apple]], [])], [])], [])], []) S = node(s, [node(np, [node(det, [[the]], []), node(n, [[woman]], [])], []), node(vp, [node(v, [[eats]], []), node(np, [node(det, [...], []), node(..., ..., ...)], [])], [])], []) Yes ?- s(S,[the,man,sings],[]), write(S). node(s, [node(np, [node(det, [[the]], []), node(n, [[man]], [])], []), node(vp, [node(v, [[sings]], [])], [])], []) S = node(s, [node(np, [node(det, [[the]], []), node(n, [[man]], [])], []), node(vp, [node(v, [[sings]], [])], [])], []) Yes ?-
s ::= np^^S, vp^^P, { S^^number(Num), P^^number(Num) } <:> {Attributes for non-terminal s 13}Continued in <NP rules 14>, <VP rules 19>, <Pre-terminal rules 22>, <Lexicon 23>
structure(s(Sstr,Pstr)) ::- S^^structure(Sstr), P^^structure(Pstr).
np ::= det^^D, n^^N, { D^^number(Num), N^^number(Num) } <:> {NP structure attribute (for DET+N) 15} && {NP number attribute (for DET+N) 16}Continued in <NP rules, cont'd 17>, <NP rules, cont'd 18>
structure(np(Dstr,Nstr)) ::- D^^structure(Dstr), N^^structure(Nstr)
np ::= n^^N, { N^^number(pl) } <:> structure(np(Nstr)) ::- N^^structure(Nstr) && number(pl).
np ::= pn^^N, { N^^number(sg) } <:> structure(np(Nstr)) ::- N^^structure(Nstr) && number(sg).
vp ::= v^^V, np^^O <:> structure(vp(Vs,Os)) ::- V^^structure(Vs), O^^structure(Os) && {Number attribute for VP 21}Continued in <VP rules, cont'd 20>
vp ::= v^^V <:> structure(vp(Vs)) ::- V^^structure(Vs) && {Number attribute for VP 21}
number(Num) ::- V^^number(Num).
n ::= [L], { lex(L,n,Num) } <:> structure(n(L)) && number(Num). pn ::= [L], { lex(L,pn,Num) } <:> structure(pn(L)) && number(Num). v ::= [L], { lex(L,v,Num) } <:> structure(v(L)) && number(Num). det ::= [L], { lex(L,det,Num) } <:> structure(det(L)) && number(Num).
lex(mary,pn,sg). lex(john,pn,sg). lex(woman,n,sg). lex(women,n,pl). lex(man,n,sg). lex(men,n,pl). lex(apple,n,sg). lex(apples,n,pl). lex(the,det,_). lex(some,det,pl). lex(one,det,sg). lex(loves,v,sg). lex(love,v,pl). lex(eats,v,sg). lex(eat,v,pl). lex(sings,v,sg). lex(sing,v,pl).
?- s(S,[john,loves,mary],[]), write(S). node(s, [node(np, [node(pn, [[john]], [structure(pn(john)), (number(sg)::-lex(john, pn, sg))])], [ (structure(np(_G292))::- node(pn, [[john]], [structure(pn(john)), (number(sg)::-lex(john, pn, sg))])^^structure(_G292)), number(sg)]), node(vp, [node(v, [[loves]], [structure(v(loves)), (number(sg)::-lex(loves, v, sg))]), node(np, [node(pn, [[mary]], [structure(pn(mary)), (number(sg)::-lex(mary, pn, sg))])], [(structure(np(_G424))::- node(pn, [[mary]], [structure(pn(mary)), (number(sg)::-lex(mary, pn, sg))]) ^^structure(_G424)), number(sg)])], [ (structure(vp(_G351, _G352))::- node(v, [[loves]], [structure(v(loves)), (number(sg)::-lex(loves, v, sg))])^^structure(_G351), node(np, [node(pn, [[mary]], [structure(pn(mary)), (number(sg)::-lex(mary, pn, sg))])], [(structure(np(_G424))::- node(pn, [[mary]], [structure(pn(mary)), (number(sg)::-lex(mary, pn, sg))]) ^^structure(_G424)), number(sg)])^^structure(_G352)), (number(_G373)::- node(v, [[loves]], [structure(v(loves)), (number(sg)::-lex(loves, v, sg))])^^number(_G373))])], [(structure(s(_G261, _G262))::- node(np, [node(pn, [[john]], [structure(pn(john)), (number(sg)::-lex(john, pn, sg))])], [(structure(np(_G292))::- node(pn, [[john]], [structure(pn(john)), (number(sg)::-lex(john, pn, sg))])^^structure(_G292)), number(sg)])^^structure(_G261), node(vp, [node(v, [[loves]], [structure(v(loves)), (number(sg)::-lex(loves, v, sg))]), node(np, [node(pn, [[mary]], [structure(pn(mary)), (number(sg)::-lex(mary, pn, sg))])], [(structure(np(_G424))::- node(pn, [[mary]], [structure(pn(mary)), (number(sg)::-lex(mary, pn, sg))]) ^^structure(_G424)), number(sg)])], [(structure(vp(_G351, _G352))::- node(v, [[loves]], [structure(v(loves)), (number(sg)::-lex(loves, v, sg))]) ^^structure(_G351), node(np, [node(pn, [[mary]], [structure(pn(mary)), (number(sg)::-lex(mary, pn, sg))])], [(structure(np(_G424))::- node(pn, [[mary]], [structure(pn(mary)), (number(sg)::-lex(mary, pn, sg))]) ^^structure(_G424)), number(sg)])^^structure(_G352)), (number(_G373)::- node(v, [[loves]], [structure(v(loves)), (number(sg)::-lex(loves, v, sg))]) ^^number(_G373))]) ^^structure(_G262))])
?- s(Sentence,[john,loves,mary],[]), | Sentence ^^ structure(Str).This binds the variable Str to the appropriate value, namely (np(pn(john)), vp(v(loves), np(pn(mary)))).
?- s(Sentence, [john, loves, mary], []), | Sentence ^^ number(Num). No ?-
man(john). man(bill). man(chuck).and some attribute a is defined by the rule a(X) ::- man(X), then if we ask for the a attribute of some node N, the result is much as one might expect. In an interactive session, it might look like this:[10]
?- ... $N ^^ a(X). X = john ; X = bill ; X = chuck ; No ?-
course(complexity,monday,9,11,david,harel,feinberg,a).This represents the fact that a lecture course on complexity is given on Monday from 9 to 11 by David Harel in room A of the Feinberg building. We could also use a list of attributes to represent the same information:
course(complexity, [ (name(complexity)), (day(monday)), (start_time(9)), (end_time(11)), (lecturer(david,harel)), (building(feinberg)), (room(a)) ]).
?- course(complexity,CourseAtts), | CourseAtts^^start_time(ST), | CourseAtts^^end_time(ET), | Duration is ET - ST. CourseAtts = [name(complexity), day(monday), start_time(9), end_time(11), lecturer(david, harel), building(feinberg), room(a)] ST = 9 ET = 11 Duration = 2 Yes ?-
lecturer(Course,GivenName,FamilyName) :- course(Course,_,_,_,GivenName,FamilyName,_,_).It will also often be somewhat slower than structures with fixed arity, since the list structure gives the Prolog compiler very little help in finding the right rule, and extraction of attributes immediately degenerates into a linear search of the attribute list.
Abramson, Harvey. 1984. “Definite Clause Translation Grammars”. Proceedings of the 1984 International Symposium on Logic Programming, Atlantic City, New Jersey, February 6-9, 1984, pp. 233-240. (IEEE-CS 1984, ISBN 0-8186-0522-7)
Abramson, Harvey, and Veronica Dahl. 1989. Logic Grammars. Symbolic Computation AI Series. Springer-Verlag, 1989.
Abramson, Harvey, and Veronica Dahl, rev. Jocelyn Paine. 1990. DCTG: Prolog definite clause translation grammar translator. (Prolog code for translating from DCTG notation to standard Prolog. Note says syntax extended slightly by Jocelyn Paine to accept && between specifications of grammatical attributes, to minimize need for parentheses. Available from numerous AI/NLP software repositotries, including http://www-2.cs.cmu.edu/afs/cs/project/ai-repository/ai/lang/prolog/code/syntax/dctg/0.html, http://www.ims.uni-stuttgart.de/ftp/pub/languages/prolog/libraries/imperial_college/dctg.tar.gz, and http://www.ifs.org.uk/~popx/prolog/dctg/.)
Alblas, Henk. 1991. “Introduction to attribute grammars”. Attribute grammars, applications and systems: International Summer School SAGA, Prague, Czechoslovakia, June 4-13, 1991, Proceedings, pp. 1-15. Berlin: Springer, 1991. Lecture Notes in Computer Science, 545.
Bratko, Ivan. 1990. Prolog programming for artificial intelligence. Second edition. Wokingham: Addison-Wesley. xxi, 597 pp.
Brown, Allen L., Jr., and Howard A. Blair. 1990. “A logic grammar foundation for document representation and layout”. In EP90: Proceedings of the International Conference on Electronic Publishing, Document Manipulation and Typography, ed. Richard Furuta. Cambridge: Cambridge University Press, 1990, pp. 47-64.
Brown, Allen L., Jr., Toshiro Wakayama, and Howard A. Blair. 1992. “A reconstruction of context-dependent document processing in SGML”. In EP92: Proceedings of Electronic Publishing, 1992, ed. C. Vanoirbeek and G. Coray. Cambridge: Cambridge University Press, 1992. Pages 1-25.
Clocksin, W. F., and C. S. Mellish. Programming in Prolog. Second edition. Berlin: Springer, 1984.
Cohen, Jacques, and Timothy J. Hickey. “Parsing and compiling using Prolog”. ACM Transactions on Programming Languages and Systems 9.2 (1987): 125-163.
Gal, Annie, Guy Lapalme, Patrick Saint-Dizier, and Harold Somers. 1991. Prolog for natural language processing. Chichester: Wiley, 1991. xiii, 306 pp.
Gazdar, Gerald, and Chris Mellish. 1989. Natural language processing in PROLOG: An introduction to computational linguistics. Wokingham: Addison-Wesley, 1989. xv, 504 pp.
Grune, Dick, and Ceriel J. H. Jacobs. 1990. Parsing techniques: a practical guide. New York, London: Ellis Horwood, 1990. Postscript of the book is available from the first author's Web site at http://www.cs.vu.nl/~dick/PTAPG.html
Knuth, D. E. 1968. “Semantics of context-free languages”. Mathematical Systems Theory 2: 127-145.
König, Esther, and Roland Seiffert. Grundkurs PROLOG für Linguisten. Tübingen: Francke, 1989. [= Uni-Taschenbücher 1525]
Pereira, Fernando C. N., and Stuart M. Shieber, Prolog and natural-language analysis. CSLI lecture notes 10. Stanford: Center for the study of language and information, 1987.
Stepney, Susan. High-integrity compilation. Prentice-Hall. Available from http://www-users.cs.york.ac.uk/~susan/bib/ss/hic/index.htm. Chapter 3 (Using Prolog) provides a terse introduction to DCTG notation and use. The rest of the book (available in Postscript form) provides a serious example of their application in a compiler.
Sterling, Leon, and Ehud Shapiro. 1994. The Art of Prolog: Advanced Programming Techniques. Cambridge, Mass.: MIT Press.
Wielemaker, Jan. 2003. “SWI-Prolog 5.2.4 Reference Manual”. http://www.swi-prolog.org