-
Notifications
You must be signed in to change notification settings - Fork 9
Macro expansion for old-style Expr macros
#33
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
Conversation
ca5ed38 to
65368b6
Compare
|
In fixing the tests I've encountered issues with To make this systematic, I've introduced the new form A few special forms have a kind of "deferred top level evaluation"
For example, in To fix this problem, introduce the new |
65368b6 to
7b56524
Compare
|
The deferred toplevel eval stuff is split out into #36 now, which can merge before this. |
7b56524 to
c5ed1b5
Compare
In the case where neither new nor old matching macro-methods exist, we still get a MethodError. In the case where an old macro exists without a new one, I think it would be fair to assume that a new macro and an old macro available in the same module defined with the same name and number of (explicit) arguments should have the same functionality. We currently enforce this by being the only ones to define new-style macros; as long as we don't make it easy to define a mix of both, there shouldn't be a case where we accidentally call an old macro where we wanted a new one. |
I'd be in favour of bringing back the elseif k == K"escape"
@chk numchildren(ex) >= 1 "`escape` requires an argument"
if isnothing(ctx.current_layer.parent)
throw(LoweringError(ex, "`escape` node in outer context"))
end
outer = ctx.current_layer.parent
newctx = MacroExpansionContext(ctx.graph, ctx.bindings, ctx.scope_layers, outer)
return expand_forms_1(newctx, ex[1])
elseif k == K"hygienic_scope"
@chk numchildren(ex) >= 2 && ex[2].value isa Module "`hygienic_scope` requires an AST and a module"
new_layer = ScopeLayer(length(ctx.scope_layers)+1, ex[2].value, ctx.current_layer, true) # param 3 is parent scope
push!(ctx.scope_layers, new_layer)
newctx = MacroExpansionContext(ctx.graph, ctx.bindings, ctx.scope_layers, new_layer)
return expand_forms_1(newctx, ex[1]) |
|
A small fix you may want to include in this PR is a special case for macros returning I implemented this for Expr here: mlechu@80ba20a. Feel free to take that change and anything else you want from that branch (only some of it is JETLS hacking; other parts I intend to upstream eventually) |
|
Thanks for the hygenic scope support and extra tests! I thought testing was a bit light but tbh I couldn't think of extra cases to cover. By the way if you find it more convenient to suggest big chunks of code as a commit I'm happy to cherry pick from any branch of yours which is based on this one - just lmk which commit to pull. Looks like I have minor things to fix with the new code you added there so I'll fix those up. I'll fiddle with After that, I think we're good to go with this once I've sorted out the static parameter stuff in the other PR this depends on. |
61e55bc to
7700894
Compare
Implements mixed macro expansion for old-style macros (those written to expect an `Expr` data structure) and new-style macros (those written to expect `SyntaxTree`). The main difficulty here is managing hygiene correctly. We choose to represent new-style scoped identifiers passed to old macros using `Expr(:scope_layer, name, layer_id)` where necessary. But only where necessary - in most contexts, old-style macros will see unadorned identifiers just as they currently do. The only time the new `Expr` construct is visible is when new macros interpolate an expression into a call to an old-style macro in the returned code. Previously, such macro-calling-macro situations would result in the inner macro call seeing `Expr(:escape, ...)` but they now see `Expr(:scope_layer)`. However, and it's rare for old-style macros to de- and re-construct escaped expressions correctly so this should be a minor issue for compatibility. Old-style macros may still return `Expr(:escape)` expressions resulting from manual escaping. When consuming the output of old macros, we process these manual escapes by escaping up the macro expansion stack in the same way we currently do. Also add `parent_layer` id to `ScopeLayer` to preserve the macro expansion stack there for use by JETLS. Co-authored-by: Em Chu <[email protected]>
7700894 to
5a718b4
Compare
|
I put a bit of extra effort into attempting something sensible with the I really don't love using In the future we could, perhaps, emit metadata from lowering which marks a macro as purely new-style or purely old-style and use that for dispatch rather than |
Yea, it's true that using |
The main difficulty here is managing hygiene correctly. We choose to
represent new-style scoped identifiers passed to old macros using
Expr(:scope_layer, name, layer_id)where necessary. But only wherenecessary - in most contexts, old-style macros will see unadorned
identifiers just as they currently do. The only time the new
Exprconstruct is visible is when new macros interpolate an expression into a
call to an old-style macro in the returned code. Previously, such
macro-calling-macro situations would result in the inner macro call
seeing
Expr(:escape, ...)and it's rare for old-style macros to handlethis correctly.
Old-style macros may still return
Expr(:escape)expressions resultingfrom manual escaping. When consuming the output of old macros, we
process these manual escapes by escaping up the macro expansion stack in
the same way we currently do.
TODO:
Expr->SyntaxTreeconversion #22 mergeshygienic-scopeform?hasmethod()for better error messages (what if the user calls a new-style macro with the incorrect number of arguments? How do we make it so that they get a comprehensible error message in that case?)K"deferred_toplevel_eval"