Search code examples
prologdcgmeta-predicate

Custom DCG Operators


Suppose I want to write a custom operator for composing DCG rules in a way that would otherwise be repetitive.

For example, suppose I had I a DCG, ws such that:

ws --> [].
ws --> " ", ws.

to match zero or more spaces. Obviously, if I want optional whitespace between each token in my grammar, it's obnoxious to have to put , ws everywhere.

I could define a new operator to replace ,/2.

:- op(1000, xfy, [ -- ]).
:- meta_predicate --(*,*,?,?).
--(L,R) --> ({callable(L)} -> call(L); L), ws, ({callable(R)} -> call(R); R).

This joines the left and right hand of --/2 via the optional whitespace rule. This mostly works fine, however certain things trip it up:

rule --> "foo" -- ("bar"; "quux").

If I try and execute this rule, I get an error saying that ;/4 is not defined. I have a vague idea of the problem here, but basically the question is: is there a way to define new operators for DCGs that work with the same generality as ,/2?


Solution

  • Yes, this is possible.

    Currently, the main problem is:

    ?- callable("foo").
    true.
    

    So, I suggest this simpler definition:

    --(L, R) --> L, ws, R.
    

    In addition, I suggest:

    :- set_prolog_flag(double_quotes, chars).
    

    Sample query:

    ?- phrase(rule, Ls).
    Ls = [f, o, o, b, a, r] ;
    Ls = [f, o, o, q, u, u, x] ;
    Ls = [f, o, o, ' ', b, a, r] ;
    Ls = [f, o, o, ' ', q, u, u, x] ;
    Ls = [f, o, o, ' ', ' ', b, a, r] .
    

    As another example, let us use this rule instead:

    rule --> "foo" -- ("bar" | "quux") -- "test".
    

    Now we get for example:

    ?- length(Ls, _), phrase(rule, Ls).
    Ls = [f, o, o, b, a, r, t, e, s, t] ;
    Ls = [f, o, o, b, a, r, ' ', t, e, s, t] ;
    Ls = [f, o, o, q, u, u, x, t, e, s, t] ;
    Ls = [f, o, o, ' ', b, a, r, t, e, s, t] ;
    Ls = [f, o, o, b, a, r, ' ', ' ', t, e, s, t] ;
    Ls = [f, o, o, q, u, u, x, ' ', t, e, s, t] ;
    Ls = [f, o, o, ' ', b, a, r, ' ', t, e, s, t] ;
    Ls = [f, o, o, ' ', q, u, u, x, t, e, s, t] ;
    Ls = [f, o, o, ' ', ' ', b, a, r, t, e, s, t] ;
    Ls = [f, o, o, b, a, r, ' ', ' ', ' ', t, e, s, t] .
    

    Note how iterative deepening is used for fair enumeration.