A DCG that matches the rest of the input

喜你入骨 提交于 2019-12-24 04:41:25

问题


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)
    }.

回答1:


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]).



回答2:


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.




回答3:


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] 


来源:https://stackoverflow.com/questions/25757393/a-dcg-that-matches-the-rest-of-the-input

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!