This is highly implementation-specific, but SWI-Prolog has a source_location/2
predicate that, called inside a term_expansion/2
rule, gives you the file name and line number of the clause being expanded.
So you can use something like the following:
term_expansion(Head --> Body, EnhancedHead --> Body) :-
source_location(File, Line),
format('~w --> ~w at ~w:~w~n', [Head, Body, File, Line]),
Head =.. [Functor, Arg1 | Args],
Arg1 =.. [ArgFunctor | ArgArgs],
EnhancedArg1 =.. [ArgFunctor, File:Line | ArgArgs],
EnhancedHead =.. [Functor, EnhancedArg1 | Args].
hello -->
[world].
sentence(sentence(Subject, Verb, Object)) -->
[Subject, Verb, Object].
Note that this term_expansion/2
will print the log message for every -->/2
rule in the program:
hello --> [world] at /home/isabelle/hello.pl:9
sentence(sentence(_2976,_2978,_2980)) --> [_2976,_2978,_2980] at /home/isabelle/hello.pl:12
But it will then fail if the rule's head doesn't have at least one argument, and the first argument doesn't have at least one argument of its own. This is fine, failure just means "don't rewrite this term":
?- listing(hello).
hello([world|A], A).
true.
?- phrase(hello, Hello).
Hello = [world].
But sentence//1
will be rewritten:
?- listing(sentence).
sentence(sentence('/home/isabelle/hello.pl':12, A, B, C), [A, B, C|D], D).
true.
?- phrase(sentence(sentence(Position, S, V, O)), [isabelle, likes, prolog]).
Position = '/home/isabelle/hello.pl':12,
S = isabelle,
V = likes,
O = prolog.
You could build on this, maybe with a separate operator --->
to mark only those rules you really want rewritten. I think having this extra implicit argument is a recipe for lots of unexpected failures when you try to unify something with the actual underlying term, not the term as it appears in the source code.
So maybe a better approach would be something like this:
sentence(sentence(@position, Subject, Verb, Object)) -->
[Subject, Verb, Object].
and a corresponding term_expansion/2
rule that looks for these @position
terms and replaces them accordingly.