Printing PPX'd source with ReScript

Before v11, I used reason + refmt to print generated code to debug PPXs. Since Reason support is dropped, is there a way to debug generated code somehow?

1 Like

AFACIT there’s no way to print generated code from rescript sources, so I came up with the following workflow.

Context
I have a few ppx’s to update to v11. I didn’t touch them for years, so I’m rusty on both - ppx themselves and OCaml in general.

Workflow
ReScript ships with bsc binary that can do a lot of stuff. Don’t read its pubic --help, go directly to its source code and inspect all the flags.

One of the things that bsc can do is to print a readable representation of AST with the -dparsetree flag. So when you run:

node_modules/rescript/bsc -dparsetree src/playground.res

It would give you an AST representation of what’s inside src/playground.res.

This is how I inspect how new things, such as uncurried functions, are currently represented in ReScript’s AST.


To inspect an AST generated by ppx, I pass ppx binary via -ppx flag:

node_modules/rescript/bsc \
    -ppx _build/default/ppx/bin/bin.exe \
    -dparsetree \
    src/playground.res

With this, I can write the resulting code by hand and print its AST. Then print what’s generated by ppx. Put them side by side and see what’s missing/wrong in the ppx’ed version → then fix it in ppx source.


Another issue is that I don’t remember what the resulting code looks like :sweat_smile: So, in the project workspace, I added an older version of the ReScript compiler that supports Reason. It ships with refmt which can print generated by ppx code from the Reason source.

cat path/to/ReasonModule.re \
| <PATH_TO_RESCRIPT_V9>/bsrefmt --parse re --print binary \
| _build/default/ppx/bin/bin.exe /dev/stdin /dev/stdout \
| <PATH_TO_RESCRIPT_V9>/bsrefmt --parse binary --interface false

I can take this Reason code (thankfully, I have lots of test cases written in Reason), convert it to ReScript, and then print its AST as explained above.


It’s also possible to use this approach for automated testing (to validate that compilation works, errors snapshots, etc.). The test case succeeds if bsc exits with 0. But to make it work, bsc needs dependencies available. You can pass them with -I flag.

node_modules/rescript/bsc \
    -I ./node_modules/@rescript/react/lib/ocaml \
    -I ./node_modules/my-lib/lib/ocaml \
    -ppx _build/default/ppx/bin/bin.exe \
    -w "+A" \          # enable all warnings
    -warn-error "+A" \ # warnings would be treated like errors
    -bs-cmi-only \.    # no need to produce js, just make sure it compiles
    src/playground.res

bsb had -install flag that builds these interfaces, but it doesn’t exist in rescript. The easiest way to build these interfaces is to compile some project in the workspace that has these dependencies.


Can’t say I’m having fun here haha, but it should get the job done.

8 Likes

This is super useful! Thanks for all the pointers.

2 Likes

Yeah great write up @alex.fedoseev ! Ideally the rescript bin should also be able to print the PPXed code. No idea if that’d be hard to implement though.

1 Like