Skip to content
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

added @macroexpand #18660

Merged
merged 8 commits into from
Sep 30, 2016
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions base/exports.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1052,6 +1052,7 @@ export
expand,
gensym,
macroexpand,
@macroexpand,
parse,

# help and reflection
Expand Down
10 changes: 10 additions & 0 deletions base/expr.jl
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,16 @@ Takes the expression `x` and returns an equivalent expression with all macros re
"""
macroexpand(x::ANY) = ccall(:jl_macroexpand, Any, (Any,), x)

"""
@macroexpand
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing indent, and would have to be added to rst if this gets merged before #18588

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

where do I have to add it to rst?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done


Return equivalent expression with all macros removed (expanded).
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should be documented that the expansion happens in the module where the @macroexpand macro is expanded (i.e. the same module if the expression is used directly) and not the current module when the returned code runs (which is the module if you call macroexpand) The difference is demonstrated below

julia> module M
       macro macroexpand(code)
           QuoteNode(macroexpand(code))
       end
       macro m()
           1
       end
       function f()
           (@macroexpand(@m), macroexpand(:(@m)))
       end
       end
M

julia> macro m()
           2
       end
@m (macro with 1 method)

julia> M.f()
(1,2)

This makes no difference when used in the REPL and as I said the current @macroexpand might be slightly better but this should still be documented.

Copy link
Contributor Author

@jw3126 jw3126 Sep 26, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you give a usecase this would actually make a difference? And why is @macroexpand potentially better for interactive use?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you give a usecase this would actually make a difference?

That's exactly what the code above is showing.

And why is @macroexpand potentially better for interactive use?

I said

This makes no difference when used in the REPL

"""
macro macroexpand(code)
code_expanded = macroexpand(code)
Expr(:quote, code_expanded)
end

## misc syntax ##

"""
Expand Down
2 changes: 1 addition & 1 deletion test/choosetests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ function choosetests(choices = [])
"markdown", "base64", "serialize", "misc", "threads",
"enums", "cmdlineargs", "i18n", "workspace", "libdl", "int",
"checked", "intset", "floatfuncs", "compile", "parallel", "inline",
"boundscheck", "error", "ambiguous", "cartesian", "asmvariant"
"boundscheck", "error", "ambiguous", "cartesian", "asmvariant", "expr"
]

if Base.USE_GPL_LIBS
Expand Down
18 changes: 18 additions & 0 deletions test/expr.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this in a new file? This can probably go to parse or replutil or reflection.

# @macroexpand tests
macro seven_dollar(ex)
# simonbyrne example 18240
isa(ex,Expr) && ex.head == :$ ? 7 : ex
end

let
@test (@macroexpand @macroexpand x) == macroexpand(:(@macroexpand x))
@test (@macroexpand :(1+$y) ) == macroexpand(:( :(1+ $y)))
@test (@macroexpand @fastmath 1+2 ) == :(Base.FastMath.add_fast(1,2))
@test (@macroexpand @fastmath + ) == :(Base.FastMath.add_fast)
@test (@macroexpand @fastmath min(1) ) == :(Base.FastMath.min_fast(1))
@test (@macroexpand @doc "" f() = @x) == Expr(:error, UndefVarError(Symbol("@x")))
@test (@macroexpand @seven_dollar $bar) == 7
x = 2
@test (@macroexpand @seven_dollar 1+$x) == :(1 + 2)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this actually the correct behaviour? Shouldn't it throw an error?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right!

macro macroexpand(code)
    code_expanded = macroexpand(code)
    Expr(:quote, code_expanded)
end

code_expanded looks good the problem is the eval call on Expr(:quote, code_expanded) which does $ interpolation.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So would the solution be to search and quote the dollars expressions in code_expanded?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm honestly not sure. This stuff always makes my head hurt.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think correct behaviour would be not to throw an error but return something like
1 + $(Expr(:$, :x))

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah my head hurts, too.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe it would be better to print the result, rather than return it, i.e. do:

macro macroprint(code)
    print(macroexpand(code))
end

After all, that's probably what you want in 99% of cases.

Copy link
Contributor

@yuyichao yuyichao Sep 24, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👎 to print it.

macro macroexpand(code)
    QuoteNode(macroexpand(code))
end

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is eval(QuoteNode(ex)) == ex for all expressions ex?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should be.

end