Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add predicates for using library(debug) with DCGs #1128

Open
wants to merge 4 commits into
base: master
Choose a base branch
from

Conversation

webstrand
Copy link

Enables easy tracing of DCGs:

natural(N)   --> digit(D), $natural(D,N).
natural(A,N) --> digit(D), {A1 is A * 10 + D}, $natural(A1,N).
natural(N,N) --> [].

@webstrand
Copy link
Author

I couldn't find any way to make ${ ... } work, i.e.

natural(A,N) --> digit(D), ${A1 is A * 10 + D}, natural(A1,N).

I think it's excluded by the grammar?

@triska
Copy link
Contributor

triska commented Dec 2, 2021

It is definitely a good idea to extend these constructs to grammars, thank you a lot for working on this!

One thing I noticed: With the current definition, the nonterminal (*)//1 is equivalent to the terminal [], i.e., epsilon, so "generalizing away" a nonterminal with * may actually make the definition more specific. I think (*)//1 should be interpreted as: At this position, any sequence at all, and so it should be defined for example as:

*(_) --> ... .

Please consider using --> to define nonterminals, since the particular expansion method depends on the Prolog system and may also change. If you want access to the implicit arguments, use the nonterminal call//1: call(Goal_2) invokes Goal with two arguments appended, and you can use this to refer to the arguments, using portable mechanisms.

One small detail: Please consider using brackets for the meta_predicate declarations, such as :- meta_predicate(...)., because meta_predicate is not a standard operator. Thank you a lot!

@UWN
Copy link

UWN commented Dec 2, 2021

Use in stead of call(G_0, A, B) rather phrase(G__0, Xs0,Xs).

Also note that G_0 means a goal that does not lack any argument (zero). So this should be callable like G_0 whereas in the situation here, we expect a non-terminal.

?- phrase({true},Xs0,Xs).
   Xs0 = Xs.

G__0 refers to a non-terminal that does not lack an argument (as a non-terminal).

@UWN
Copy link

UWN commented Dec 2, 2021

*(_) --> ... .

This definition only makes sense in the absence of semicontext.

@webstrand
Copy link
Author

I've removed (*)/3, I'm not sure there's a semantically equivalent DCG rule. Users would be better served to choose between [] and ... depending on the situation, right?

Should I report portray_clause(call:phrase(G_2, Xs0, Xs)) instead of portray_clause(call:G_2=(Xs0, Xs))? I personally favor some variant of the latter.

I've used library(lambda) instead of call/3 otherwise I'd need to define another predicate to actually use Xs0 and Xs. If this is not desirable, can you let me know why?

Is there any way to define a meta_predicate for DCG rules independent of the compilation technique?

@webstrand
Copy link
Author

I see that SWI's meta_predicate has // as a meta argument specifier. I'm not sure how to use it, though.

@webstrand
Copy link
Author

webstrand commented Dec 2, 2021

I may have misunderstood

use the nonterminal call//1

are you saying that some other DCG compiler may interpret call specially? In which case I should wrap the lambdas like call(\Xs0^Xs^(...)) instead of leaving them bare like I have.

I was specifically wondering whether it's okay for me to use a lambda there, or if I should define a predicate like trace_body/3 and call that instead.

Copy link

@UWN UWN left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please note that \ renames variables. So what is the purpose of it in this context?

@webstrand
Copy link
Author

Please note that \ renames variables. So what is the purpose of it in this context?

I need to get Xs0 and Xs somehow so that I can report them along with the state of the DCG rule.

$-G_2 --> call(\Xs0^Xs^
   catch(phrase(G_2, Xs0, Xs), Ex, (
      portray_clause(exception:Ex:G_2=(Xs0, Xs)), throw(Ex)
   ))).

without a lambda:

$-G_2 --> call(catch_exception_body(G_2)).

% can't think of a better name for this:
catch_exception_body(G_2, Xs0, Xs) :- 
   catch(phrase(G_2, Xs0, Xs), Ex, (
      portray_clause(exception:Ex:G_2=(Xs0, Xs)), throw(Ex)
   )).

Is there some other trick to getting those variables? Or are you suggesting that Xs0 and Xs not be reported at all?

@UWN
Copy link

UWN commented Dec 4, 2021

You are renaming the variables in G_2. Thus, any bindings are removed. Think of

?- phrase({X = 2}, []).
   X = 2.
?- phrase($-{X = 2}, []).
   true.  % unexpected

Either declare G_2 as global, using G_2+\... or just note that λ consists of two parts, the \ and the ^. Renaming and parameter passing. And these mechanisms are pretty independent of each other. You only need parameter passing alone. So remove the \.

However, there is a further problem here. DCGs and regular predicates share the same name space. So whenever we add a new non-terminal NT//A this translates into a predicate NT/A+2. And thus that name is taken too. And in this particular context, the $ and family would thus also be taken to debug higher-order constructs. Like this which was somehow skipped.

$(C_1,V1) :-
   $call(C_1,V1).
$(C_2,V1,V2) :-
   $call(C_2,V1,V2).
$(C_3,V1,V2,V3) :-
   $call(C_3,V1,V2,V3).
$(C_4,V1,V2,V3,V4) :-
   $call(C_4,V1,V2,V3,V4).
$(C_5,V1,V2,V3,V4,V5) :-
   $call(C_5,V1,V2,V3,V4,V5).
$(C_6,V1,V2,V3,V4,V5,V6) :-
   $call(C_6,V1,V2,V3,V4,V5,V6).
$(C_7,V1,V2,V3,V4,V5,V6,V7) :-
   $call(C_7,V1,V2,V3,V4,V5,V6,V7).

Note that this code does not repeat anything unnecessarily. There is just one place that is responsible for printing.

So for debugging DCGs the best is to use another operator, say $$ in analogy to / and //.

@UWN
Copy link

UWN commented Dec 4, 2021

Users would be better served to choose between [] and ... depending on the situation, right?

Users would choose between ... //0 and, er, it could be defined as

anything -->
   call(_^_^true).

(In the expansion that is equivalent to anything(_,_).)

... //0 is an accurate generalization, if the grammar-part of the DCG sans arguments is a context free language. And anything//0 if full type 0 power is used via semicontext.

force-pushed the master branch 2 times, most recently from 2a7a1b7 to bb624cc Compare January 8, 2023 19:13
force-pushed the master branch 3 times, most recently from cf96173 to bb420e9 Compare July 11, 2023 19:43
force-pushed the master branch 6 times, most recently from a769b09 to 0666b56 Compare October 15, 2023 23:11
@hurufu
Copy link
Contributor

hurufu commented Dec 15, 2024

If someone will find this ticket and is interested in tracing DCGs, I usually just add {$true} to the suspicious places in DCG or even {$(*Var)} or any other term that guarantees that my variable isn't touched, but will be traced to the console. This approach is "good enough" for many cases.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants