On 6/29/24 19:05, Maxime Devos wrote:
> Instead of run-time reflection on values, I think an IDE implementing
> jump to definition should use source location and binding information
> from syntax objects. That's how DrRacket does it.
> ...
> Since syntax objects from the expander encode all the details of scope,
> this works mostly automatically. DrRacket makes this functionality
> available as a library, so navigation features can also be used from
> Emacs with racket-xp-mode [2] or other editors with the Language Server
> Protocol [3].
I suppose this could work, but it seems the wrong layer of abstraction
and a bit round-about to me, it also only works on languages that use
Scheme-style syntax. Instead, I’d propose compiling the relevant code to
Tree-IL (without optimisations, should work with most languages). (For
Scheme, this is essentially macro expansion, but the resulting object
won’t technically be a syntax object.)
In Tree-IL, all syntax has been expanded (in tree form), which
eliminates a lot of complications. Yet, source location information
remains available (albeit not documented ...)! And the original variable
names remain available (together with an unshadow-ified version, so for
local variables you can move upwards to find the corresponding
definition and in particular its source location.
A large upside is that this should work for most languages (not only
Scheme) and is relatively straightforward to implement,
I hadn't realized that other languages in Guile might compile to Tree-IL
directly instead of generating syntax objects. Is that common and/or
encouraged? It seems like it would require the new language's compiler
to do a lot of work that could otherwise be delegated to Guile, and it
would make it difficult to implement parts of the new language using macros.
a small downside
is that information on definitions like (let-syntax ((f [...])) ...)
aren’t available – you only see the return of (f stuff), not ‘f’ itself.
(But this downside applies to the ‘syntax’ route as well, unless you are
doing complicated (but possible!) DIY partial macro expansion shenanigans.)
This and other features, like correctly tracking the binding of `else`
in `cond`, require cooperation from the macro expander. The explanation
of origin tracking in
<https://docs.racket-lang.org/reference/stxprops.html> might be one
place to start reading about how Racket supports this (but I am not an
expert!). Similar expander features can also enable other tools, like
DrRacket's macro stepper:
<https://docs.racket-lang.org/macro-debugger/index.html>
Philip