Hi Luc,
I've asked exactly the same question many moons ago...  Someone responded that it was a super trivial exercise you would give to students (ca. 10 lines of code!).  Unfortunately, the 10 lines of code were not included back then.
Most operators can be implemented by m*n, where m is the min number of consecutive rule applications and n is the max number of consecutive rule applications.  n may be inf.  You also want the number of times the rule is applied back, ideally.  It should non-deterministically generate all strings.  It has to work for parsing and generating.  It has to be able to take arguments that are given or returned in a list.
The core API would look like this:
    'm*n'//3, % ?Low, ?High, -Count, :Dcg_0
    'm*n'//4, % ?Low, ?High, -Count, :Dcg_1, -Args1
    'm*n'//5, % ?Low, ?High, -Count, :Dcg_2, -Args1, -Args2
You can then define m*, *n, ?, +, #, *.
Implementing this in a naive way, my DCGs still look crappy when compared to the ABNF definition:
    [ABNF]   field-body = *(continuation 1*character)
    [DCG]    field_body(S) --> parsing, continuation, +(character, Cs), {string_codes(S, Cs)}.
Here I am at the end of my PhD, still scratching my head as to how to do this properly :-)