By using backtrackable setarg/3 and logical global variables (lval/3) we can implement easily a superset of DCG grammars practically equivalent with Peter VanRoy's Extended DCGs. We call them Assumption Grammars as no preprocessor is involved in their implementation. It turns out that the technique has the advantage of `meta-programming for free' (without the expensive phrase/3), allows source level debugging and can be made more space and time efficient than the usual preprocessing based implementation. On real examples, their best use is for writing a Prolog compiler (they can contribute to the writing of compact and efficient code with very little programming effort) and for for large natural language processing systems.
Basically, they work as follows:
% tools begin_dcg(Name,Xs):-lval(dcg,Name,Xs-Xs). end_dcg(Name,Xs):-lval(dcg,Name,Xs-[]). w(Word,Name):- lval(dcg,Name,State), State=_-[Word|Xs2], setarg(2,State,Xs2). begin_dcg(Xs):-begin_dcg(default,Xs). end_dcg(Xs):-end_dcg(default,Xs). w(Word):-w(Word,default). % grammar x:-ng,v. ng:-a,n. a:-w(the). a:-w(a). n:-w(cat). n:-w(dog). v:-w(walks). v:-w(sleeps). % test go:-begin_dcg(Xs),x,end_dcg(Ys),write(Ys),nl,fail. ?- go. [the,cat,walks] [the,cat,sleeps] [the,dog,walks] [the,dog,sleeps] [a,cat,walks] [a,cat,sleeps] [a,dog,walks] [a,dog,sleeps]
The program can be found in progs/setarg_dcg.pl. For reasons of efficiency (i.e. to equal or beat preprocessor based DCGs in terms of both space and time) BinProlog's `Assumption Grammars' have been implemented in C and are accessible through the following set of builtins:
dcg_connect/1 % works like 'C'/3 with 2 hidden arguments dcg_def/1 % sets the first hidden DCG argument dcg_val/1 % retrieves the current state of the DCG stream dcg_tell/1 % focus on a given DCG stream (from 0 to 255) dcg_telling/1 % returns the number of the current DCGs stream % INVISIBLE DCG connect operation: normally macro-expanded '#'(Word):-dcg_connect(Word). % example: ?-dcg_phrase(1,(#a,#b,#c),X). dcg_phrase(DcgStream,Axiom,Phrase):- dcg_telling(X),dcg_tell(DcgStream), dcg_def(Phrase), Axiom, dcg_val([]), dcg_tell(X).
Starting with BinProlog 3.33 dcg_tell/1 is backtrackable, mostly like lval/3. Admissible values for X in dcg_tell(X) are ranging from 1 to 255, which gives (I hope) enough distinct DCG streams for any application.
Invisible grammars consume no heap, as they are based on backtrackable destructive assignment implemented with a technique called value trailing like setarg/3. They consume trail only when a nondeterministic situation (choice-point) arises.
If you try the following program:
% grammar ax:-ng,v. ng:-a,n. a:-dcg_connect(the). a:-dcg_connect(a). n:-dcg_connect(cat). n:-dcg_connect(dog). v:-dcg_connect(walks). v:-dcg_connect(sleeps). go:- st(H,T), dcg_def(Xs),ax,dcg_val([]), pp(Xs,H,T),nl, fail. st(H,T):-statistics(global_stack,[H,_]),statistics(trail,[T,_]). pp(O,H0,T0):- st(H1,T1),T is T1-T0,H is H1-H0, write([heap=H,trail=T]),write('==>'),write(O),nl,fail. pp(_,_,_).
it will print out something like:
[heap = 308,trail = 44]==>[the,cat,walks] [heap = 308,trail = 32]==>[the,cat,sleeps] [heap = 308,trail = 32]==>[the,dog,walks] [heap = 308,trail = 20]==>[the,dog,sleeps] [heap = 308,trail = 44]==>[a,cat,walks] [heap = 308,trail = 32]==>[a,cat,sleeps] [heap = 308,trail = 32]==>[a,dog,walks] [heap = 308,trail = 20]==>[a,dog,sleeps]