Question

This answer: Very basic dcg prolog syntax helped me a little but [X] only gets the next character, I want the whole enchilada, read on!

I am using GNU Prolog to write a command option parser and I am stuck on a DCG point. I have this grammar rule which looks for "foo --as=json" for example, and I just cannot work out how to get my hands on the result of "anything", the code:

as_opt --> "--as=", anything, { c( as_opt )}, !.
anything --> [], {c(anything_match)}.

And the gprolog expansion of that is:

as_opt([45, 45, 97, 115, 61|A], B) :-
        anything(A, C),
        c(as_opt), !,
        C = B.

anything(A, B) :-
        c(anything_match), !,
        A = B.

The "c()" predicate is simple and is just used to track that the rule executed with a format() to stdout so I could see what's going on as it runs. If I had written the code by hand I would do:

%% for completeness here!
c(Msg) :- format("Processed ~w~n", [Msg]).

as_opt([45, 45, 97, 115, 61|A], B) :-
        anything(A, C),
        c(as_opt), !,
        C = B,
        { g_assign( gvValue, B )}. %% just for example

Going back to the original DCG:

as_opt --> "--as=", anything, { c( as_opt ), gassign( gvValue, ??? )}, !.

So what goes where the "???" is. Is it possible...it must be. I am going to re-read the gprolog rules on how it expands DCG rules again in case I am about to (facepalm) myself but in the meantime any assistance would be most welcome.

Thanks, Sean.

Was it helpful?

Solution

You are asking for one of the simplest DCG non-terminals, which describes any list:

list --> [].
list --> [_], list.

To actually access the list that is being described, you introduce an argument:

list([])    --> [].
list([L|Ls) --> [L], list(Ls)

You can use it like this:

as_opt(Option) --> "--as=", list(Option).

OTHER TIPS

There's a better solution than the one described by mat that is faster and avoids spurious choice points but it requires support for the call//1 built-in non-terminal as found on e.g. SWI-Prolog, GNU Prolog, and other Prolog compilers. Also on Logtalk. Consider:

as_opt(Option) --> "--as=", list(Option).

list([L|Ls]) --> [L], list(Ls).
list([]) --> [].

as_opt2(Option) --> "--as=", call(rest(Option)).

rest(Rest, Rest, _).

Using SWI-Prolog to better illustrate the differences:

?- phrase(as_opt(Option), "--as=json").
Option = [106, 115, 111, 110] ;
false.

?- phrase(as_opt2(Option), "--as=json").
Option = [106, 115, 111, 110].

?- time(phrase(as_opt(Option), "--as=json")).
% 9 inferences, 0.000 CPU in 0.000 seconds (57% CPU, 562500 Lips)
Option = [106, 115, 111, 110] ;
% 2 inferences, 0.000 CPU in 0.000 seconds (63% CPU, 133333 Lips)
false.

?- time(phrase(as_opt2(Option), "--as=json")).
% 6 inferences, 0.000 CPU in 0.000 seconds (51% CPU, 285714 Lips)
Option = [106, 115, 111, 110].

The spurious choice-point comes from the definition of the list//1 non-terminal. The performance difference is that, while call//1 allows us to simply access the implicit list argument, the list//1 non-terminal is doing a list copy element by element of that implicit argument. As a consequence, the list//1 version performance is linear on the characters following the --as= prefix while the call//1 performance is constant, independent of the number of characters after the prefix:

?- time(phrase(as_opt(Option), "--as=jsonjsonjsonjsonjsonjsonjsonjsonjsonjsonjsonjson")).
% 54 inferences, 0.000 CPU in 0.000 seconds (83% CPU, 2700000 Lips)
Option = [106, 115, 111, 110, 106, 115, 111, 110, 106|...] ;
% 4 inferences, 0.000 CPU in 0.000 seconds (69% CPU, 137931 Lips)
false.

?- time(phrase(as_opt2(Option), "--as=jsonjsonjsonjsonjsonjsonjsonjsonjsonjsonjsonjson")).
% 6 inferences, 0.000 CPU in 0.000 seconds (79% CPU, 333333 Lips)
Option = [106, 115, 111, 110, 106, 115, 111, 110, 106|...].

I think you should not use global variables. Instead, add argument(s) to DCG clauses, to get back the values:

anything([C|Cs]) --> [C], anything(Cs).
anything([]) --> [].

otherwise, the remainder is available as last argument of phrase/3, after rules succeded, 'consuming' the heading match.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top