Article View: comp.lang.prolog
Article #4086re: definite clause grammar
From: Thom Fruhwirth
Date: Fri, 09 Jun 1989 22:18
Date: Fri, 09 Jun 1989 22:18
201 lines
5913 bytes
5913 bytes
/*
This is a comment on the posting from ken@aiai.UUCP of Fri Jun 2 12:49:17 1989.
Ken Johnson wrote a definite clause translator "for a short course in Prolog
to illustrate how the mechanism works".
In my opinion, a Prolog program for educational purposes should be
simple, consistent, and specification-like (avoiding built-ins including cut),
so that the main principles can be seen without having to deal with optimized
implementation code.
Therefore I modified Ken Johnsons Prolog code according to these guidelines.
I claim that the following modified version is simpler and shorter than
the original and therefore more suited for a short course in Prolog.
For those, who have access to the original version, I describe how I modified
the definite clause translator program:
- Because split(A,B,C) is equivalent to append(A,C,B), I replaced split/3 by
the more familiar append/3.
- Because the second and last clause of transform_2/3 dealing with the empty
list can also be handled by split/3 and append/3 respectively, I merged
the two clauses of transform_2/3 into one simpler clause.
- I split trim_null_output/4 into two different predicates, namely
simplify_clause/3 and simplify_conjunction/3, because making a fact out of
a rule with the body true and removing true's in conjunctions are two
different things.
- Finally I merged the predicates transform_1 and transform_2 into transform,
so simplifying the code and getting rid of two superfluos predicates.
- I also changed some names of variables and predicates slightly.
The DCG program translates/rewrites/transforms/ *compiles* a DCG rule into
a Prolog clause. Consequently, a DCG *interpreter* must exist. Actually,
the interpreter is very similar to the compiler, its code is given after
that of the compiler. As an interpreter is usually simpler to understand
than a compiler, instruction should maybe start with the interpreter.
Note that I did not take the time to document the code properly.
*/
%----------------------- The DCG Translator -----------------------------------
%
% To load a grammar, call `load_grammar(File)'
% To use it, call `my_phrase(Struct_name,Text)'
% To rewrite it, call `rewrite(Rule,Rewritten)'
%
% Ken Johnson, 2 June 1989, modified Thom Fruehwirth 8 June 1989
% If necessary, define the following operators used in DCGs
% :- op(1200,xfx,'-->').
% :- op(901,fx,'{').
% :- op(900,xf,'}').
% The following is only slightly modified
load_grammar(File) :- % Load the file by
seeing(S), % repeatedly reading terms
see(File), % and transforming them
repeat,
read(Term),
(
Term == end_of_file
;
process_grammar_rule(Term),
fail
),
!,
seen,
see(S).
process_grammar_rule(Rule) :- % Attempt to rewrite; complain if
rewrite(Rule,Rewritten), % can't rewrite a term whose principal
!, % functor is -->
assert(Rewritten).
process_grammar_rule(Term) :-
write('Cannot rewrite: '),
write(Term),
nl.
my_phrase(Term,Text) :-
add_two_args(Term,New_term,Text,[]),
call(New_term). % wrap by call/1 for clarity
rewrite((Term-->Sequence),Clause) :-
add_two_args(Term,New_term,A,B),
transform(Sequence,A,B,Transformed),
simplify_clause(New_term,Transformed,Clause). % no cut needed here
% Here the main modifications appear
% transform/4 does the translation from dcg into Prolog clauses
transform((P,Q),A,C,W) :-
!,
transform(P,A,B,U),
transform(Q,B,C,V),
simplify_conjunction(U,V,W).
transform({Goal},A,A,Goal) :-
!.
transform(Terminals,A,B,true):-
append(Terminals,B,A), % append/3 also tests for lists,
!. % so do not move cut before it.
transform(T,A,B,G) :-
add_two_args(T,G,A,B).
% Add two arguments to a compound term
add_two_args(T,New_term,A,B) :-
functor(T,F,N),
N1 is N + 1,
N2 is N + 2,
functor(New_term,F,N2),
copy_args(N,T,New_term),
arg(N1,New_term,A),
arg(N2,New_term,B).
copy_args(0,_,_).
copy_args(N,T,U) :-
N>0, % test avoids cut
arg(N,T,A),
arg(N,U,A),
M is N - 1,
copy_args(M,T,U).
% There is always a need for good ol' append/3
append([],L,L).
append([X|L1],L2,[X|L3]):-
append(L1,L2,L3).
% remove true from conjunction
simplify_conjunction(true,B,B):-!.
simplify_conjunction(A,true,A):-!.
simplify_conjunction(A,B,(A,B)).
% rules with body true are facts
simplify_clause(H,true,H):-!.
simplify_clause(H,B,(H:-B)).
%----------------------- The DCG Interpreter ---------------------------------
%
% To load a grammar, call `load_file(File)'
% To use it, call `parse(Struct_name,Text)'
%
% Thom Fruehwirth 8 June 1989, based on the ken Johnsons DCG translator
% load_file/1 is like consult/1, but consult/1 might automatically transform
% DCG rules into Prolog clauses (Quintus Prolog does).
load_file(File) :- % Load the file by
seeing(S), % repeatedly asserting clauses
see(File),
repeat,
read(Clause),
(
Clause == end_of_file
;
assert(Clause),
fail
),
!,
seen,
see(S).
% Here is the parser. Note the similarity to transform/4 and my_phrase/2.
parse(Term,Text):-
parse(Term,Text,[]).
parse((P,Q),A,C) :-
!,
parse(P,A,B),
parse(Q,B,C).
parse({Goal},A,A) :-
!,
call(Goal).
parse(Terminals,A,B) :-
append(Terminals,B,C),
!,
A=C. % take care when using a cut
parse(T,A,B) :-
(T-->S), % access DCG rule
parse(S,A,B).
% code for append/3 see DCG translator
% the end
Technical Details
Message-ID:
<2970@sbcs.sunysb.edu>Path:
arc1-ca.pugleaf.net!utzoo!utgpu!jarvis.csri.toronto.edu!rutgers!sunybcs!sbcs!thomShow Full Headers
Complete Article Headers
Path: arc1-ca.pugleaf.net!utzoo!utgpu!jarvis.csri.toronto.edu!rutgers!sunybcs!sbcs!thom From: thom@sbcs.sunysb.edu (Thom Fruhwirth) Newsgroups: comp.lang.prolog Subject: re: definite clause grammar Keywords: simple, interpreter, compiler Message-ID: <2970@sbcs.sunysb.edu> Date: 9 Jun 89 22:18:18 GMT Organization: State University of New York at Stony Brook Lines: 201