user1812457
user1812457

Reputation:

A DCG that matches the rest of the input

This is the predicate that does what it should, namely, collect whatever is left on input when part of a DCG:

rest([H|T], [H|T], []).
rest([], [], []).

but I am struggling to define this as a DCG... Or is it at all doable?

This of course is not the same (although it does the same when used in the same manner):

rest([H|T]) --> [H], !, rest(T).
rest([]) --> [].

The reason I think I need this is that the rest//1 is part of a set of DCG rules that I need to parse the input. I could do phrase(foo(T), Input, Rest), but then I would have to call another phrase(bar(T1), Rest).

Say I know that all I have left on input is a string of digits that I want as an integer:

phrase(stuff_n(Stuff, N), `some other stuff, 1324`).

stuff_n(Stuff, N) -->
    stuff(Stuff),
    rest(Rest),
    {   number_codes(N, Rest),
        integer(N)
    }.

Upvotes: 2

Views: 624

Answers (3)

Paulo Moura
Paulo Moura

Reputation: 18663

An alternative (that doesn't leave a choice point behind) is to use the call//1 built-in non-terminal with a lambda expression. Using Logtalk's lambda expression syntax to illustrate:

rest(Rest) --> call({Rest}/[Rest,_]>>true).

This solution is a bit nasty, however, as it uses a variable with a dual role in the lambda expression (which triggers a warning with the Logtalk compiler). An usage example:

:- object(rest).

    :- public(test/2).
    test(Input, Rest) :-
        phrase(input(Rest), Input).

    input(Rest) --> [a,b,c], rest(Rest).

    rest(Rest) --> call({Rest}/[Rest,_]>>true).
%   rest([C|Cs]) --> [C|Cs].    % Carlo's solution

:- end_object.

Assuming the above object is saved in a dcg_rest.lgt source file:

$ swilgt
...

?- {dcg_rest}.
*     Variable A have dual role in lambda expression: {A}/[A,B]>>true
*       in file /Users/pmoura/Desktop/dcg_rest.lgt between lines 13-14
*       while compiling object rest
% [ /Users/pmoura/Desktop/dcg_rest.lgt loaded ]
% 1 compilation warning
true.

?- rest::test([a,b,c,d,e], Rest).
Rest = [d, e].

You should be able to get the same results using other lambda expressions implementation such as Ulrich's lambda library.

Upvotes: 1

user1812457
user1812457

Reputation:

Answering my own silly question:

@CapelliC gave a solution that works (+1). It does something I don't understand :-(, but the real issue was that I did not understand the problem I was trying to solve. The real problem was:

Problem

You have as input a code list that you need to parse. The result should be a term. You know quite close to the beginning of this list of codes what the rest looks like. In other words, it begins with a "keyword" that defines the contents. In some cases, after some point in the input, the rest of the contents do not need to be parsed: instead, they are collected in the resulting term as a code list.

Solution

One possible solution is to break up the parsing in two calls to phrase/3 (because there is no reason not to?):

  1. Read the keyword (first call to phrase/3) and make it an atom;
  2. Look up in a table what the rest is supposed to look like;
  3. Parse only what needs to be parsed (second call to phrase/3).

Code

So, using an approach from (O'Keefe 1990) and taking advantage of library(dcg/basics) available in SWI-Prolog, with a file rest.pl:

:- use_module(library(dcg/basics)).

codes_term(Codes, Term) :-
    phrase(dcg_basics:nonblanks(Word), Codes, Codes_rest),
    atom_codes(Keyword, Word),
    kw(Keyword, Content, Rest, Term),
    phrase(items(Content), Codes_rest, Rest).

kw(foo, [space, integer(N), space, integer(M)], [], foo(N, M)).
kw(bar, [], Text, bar(Text)).
kw(baz, [space, integer(N), space], Rest, baz(N, Rest)).

items([I|Is]) -->
    item(I),
    items(Is).
items([]) --> [].

item(space) --> " ".
item(integer(N)) --> dcg_basics:integer(N).

It is important that here, the "rest" does not need to be handled by a DCG rule at all.

Example use

This solution is nice because it is deterministic, and very easy to expand: just add clauses to the kw/4 table and item//1 rules. (Note the use of the --traditional flag when starting SWI-Prolog, for double-quote delimited code lists)

$ swipl --traditional --quiet
?- [rest].
true.

?- codes_term("foo 22 7", T).
T = foo(22, 7).

?- codes_term("bar 22 7", T).
T = bar([32, 50, 50, 32, 55]).

?- codes_term("baz 22 7", T).
T = baz(22, [55]).

Upvotes: 2

CapelliC
CapelliC

Reputation: 60014

could be

rest([C|Cs]) --> [C|Cs] .

at least in SWI-Prolog, it seems to run (I used library(dcg/basics) to get the number)

line(I,R) --> integer(I), rest(R).
?- phrase(line(N,R), `6546 okok`).
N = 6546,
R = [32, 111, 107, 111, 107] 

Upvotes: 1

Related Questions