In ppx rewriters: Producing the fully-qualified module-name of an annotated binding

I’m attempting to write some tracing-support ppxes. These basically wrap functions in debugging information.

Short of annotating every module declaration in the file with an extension-attribute, is there a way for me to derive the fully qualified module-name of a given let that has been annotated?

i.e. given

module Sth = struct
   module Widget = struct
      let%foo func arg arg2 = (* ... *)

can I reasonably expect to produce the string "My_file.Sth.Widget.func" without resorting to non-context-free APIs?

(A subset is fine, this doesn’t need to be absolutely perfect — I just need more debugging info than (file_name, binding_name) when a value in a large file is named something like make, haha.)

For similar purposes I have made ppx rewriters expand to code involving Stdlib.__FUNCTION__, which seems to at least approximately fit the bill.

Look here: GitHub - ocaml-ppx/ppx_deriving: Type-driven code generation for OCaml and search for “with_path”. I don’t know how they do it (you can look in the code of their “show” deriver to see it) b/c I merely implemented that same function in my Camlp5-based verison of the “show” deriver. My guess is they did it the same way I did: carry a context that is inherited down the AST, and update the “path” component of that context as you traverse module-definitions.

(Mind linking your repository, if it’s public?)

Yeah, that’s definitely one approach — unfortunately, it’ll require a non-context-free impl. Which I’m afraid may be inevitable, but still, thought someone may have some ideas …

After all, to quote the ppxlib manual,

In fact, even if, at first sight, it seems like your transformation isn’t context-free, it’s likely that you can find a more suitable abstraction with which it becomes context-free. Whenever that’s the case, go for context-free!

Here is one example. Please feel free to ask whatever, that code isn’t documented very well.

Fantastic! That’s definitely a promising approach, I didn’t even realize that particular constant existed. Yum.

Also, lol:

This diff is a semantic no-op: it renames the ppx_trace package to ppx_dbg …

… maybe I should think up a better name for my package. :sweat_smile:

It would seem that in order to achieve your end, somebody somewhere has to be context dependent. It might not be your code, but it will be code that calls your code or is called by your code or is called after your code. One way or another something is going to be context sensitive.

“Context-free” transformations still have access to some “non context-free” information: see the ctxt argument of the expand function.

This context contains in particular a code_path component, that allows you to retrieve the information listed here. I hope that helps achieve what you want!