next up previous contents
Next: Mixing Tabled and Prolog Up: Grammars Previous: An Expression Grammar

Representing the Input String as Facts

In actuality, this way to process grammars with tabling is not as efficient as it might be. The reason is that the arguments to the tabled nonterminals consist of lists, and so each time a call is copied into the table, a long list may have to be copied. Also answers consist of tails of the list corresponding to the input string, and these may be long as well. We can use a slightly different representation for the input string to avoid this inefficiency.

Instead of representing the input string as a list, we will store it in the database, representing it by a set of facts. We will think of each word in the input sentence as being numbered, starting from 1. Then we will store the string as a set of facts of the form, word(n,word), where word is the nth word in the input string. For example, the string used in the example above, [1,+,2,*,3,*,'(',4,+,5,')'], would be represented by the following facts:

word(1,1).
word(2,+).
word(3,2).
word(4,*).
word(5,3).
word(6,*).
word(7,'(').
word(8,4).
word(9,+).
word(10,5).
word(11,')').
Recall that we said that the DCG translation translates lists in the body of DCG rules to calls to the predicate 'C'/3, which is defined to process the list input. But we can redefine this predicate to look at the word/2 predicate as follows:
'C'(I,W,I1) :- word(I,W), I1 is I+1.
(We could alternatively use word/3 facts and explicitly store the two consecutive integers, so no computation would be involved.) With this definition of 'C'/3, we can use the same DCG for parsing but now, rather than using lists to represent positions in the input, the system uses integers.
% grammar.P
:- table expr/3, term/3.
'C'(I,W,I1) :- word(I,W), I1 is I+1.

eval_string(String,Val) :- 
    retractall(word(_,_)),
    assert_words(String,1,N),
    abolish_all_tables,
    expr(Val,1,N).

assert_words([],N,N).
assert_words([Word|Words],N,M) :- 
        assert(word(N,Word)), N1 is N+1, assert_words(Words,N1,M).

expr(Val) --> expr(Eval), [+], term(Tval), {Val is Eval+Tval}.
expr(Val) --> term(Val).
term(Val) --> term(Tval), [*], primary(Fval), {Val is Tval*Fval}.
term(Val) --> primary(Val).
primary(Val) --> ['('], expr(Val), [')'].
primary(Int) --> [Int], {integer(Int)}.
Here we've defined a predicate eval_string to take an input string as a list, assert it into the database as a set of word/2 facts and then to call expr to parse and evaluate it. Notice that we need both to retract any facts previously stored for word/2 and to abolish any tables that were created during previous evaluations of strings. This is because old tables are no longer valid, since the new input string changes the meanings of the integers that represent positions in the input string.

We can compile and call this predicate as follows:

| ?- [grammar].
[Compiling ./grammar]
++Warning: Redefining the standard predicate: C / 3
[grammar compiled, cpu time used: 1.069 seconds]
[grammar loaded]

yes
| ?- eval_string([1,+,2,*,3,*,'(',4,+,5,')'],V).

V = 55;

no
| ?-
The warning is to alert the user to the fact that a standard predicate is being redefined. In this case, that is exactly what we want to do, and so we can safely ignore the warning.


next up previous contents
Next: Mixing Tabled and Prolog Up: Grammars Previous: An Expression Grammar
David S. Warren
1999-07-31