From 759ac10965ce037145bf84047140ec491b362f83 Mon Sep 17 00:00:00 2001 From: jw3126 Date: Fri, 30 Sep 2016 14:06:49 +0200 Subject: [PATCH] added `at-macroexpand` (#18660) Fix #18240 --- base/exports.jl | 1 + base/expr.jl | 36 ++++++++++++++++++++++++++++++++++++ doc/stdlib/base.rst | 30 ++++++++++++++++++++++++++++++ doc/stdlib/io-network.rst | 1 + test/replutil.jl | 19 +++++++++++++++++++ 5 files changed, 87 insertions(+) diff --git a/base/exports.jl b/base/exports.jl index e30494ed191a2..43986e7d5d9b2 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -1052,6 +1052,7 @@ export expand, gensym, macroexpand, + @macroexpand, parse, # help and reflection diff --git a/base/expr.jl b/base/expr.jl index 6fc8a69239fb4..391ce27fe4a74 100644 --- a/base/expr.jl +++ b/base/expr.jl @@ -61,6 +61,42 @@ Takes the expression `x` and returns an equivalent expression with all macros re """ macroexpand(x::ANY) = ccall(:jl_macroexpand, Any, (Any,), x) +""" + @macroexpand + +Return equivalent expression with all macros removed (expanded). + +There is a subtle difference between `@macroexpand` and `macroexpand` in that expansion takes place in +different contexts. This is best seen in the following example: + +```jldoctest +julia> module M + 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) +``` +With `@macroexpand` the expression expands where `@macroexpand` appears in the code (module `M`). +With `macroexpand` the expressions expands in the current module where the code was finally called (REPL). +Note that when calling `macroexpand` or `@macroexpand` directly from the REPL, both of these contexts coincide, hence there is no difference. +""" +macro macroexpand(code) + code_expanded = macroexpand(code) + QuoteNode(code_expanded) +end + ## misc syntax ## """ diff --git a/doc/stdlib/base.rst b/doc/stdlib/base.rst index 472fde947af30..b2385c9ac1933 100644 --- a/doc/stdlib/base.rst +++ b/doc/stdlib/base.rst @@ -1517,6 +1517,36 @@ Internals Takes the expression ``x`` and returns an equivalent expression with all macros removed (expanded). +.. function:: @macroexpand + + .. Docstring generated from Julia source + + Return equivalent expression with all macros removed (expanded). + + There is a subtle difference between ``@macroexpand`` and ``macroexpand`` in that expansion takes place in different contexts. This is best seen in the following example: + + .. doctest:: + + julia> module M + 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) + + With @macroexpand the expression expands where @macroexpand appears in the code (module M). With macroexpand the expressions expands in the current module where the code was finally called. Note that when calling macroexpand or @macroexpand directly from the REPL, both of these contexts coincide, hence there is no difference. + .. function:: expand(x) .. Docstring generated from Julia source diff --git a/doc/stdlib/io-network.rst b/doc/stdlib/io-network.rst index 6c82c0a052eb0..76450b2d1cfb5 100644 --- a/doc/stdlib/io-network.rst +++ b/doc/stdlib/io-network.rst @@ -1078,3 +1078,4 @@ Network I/O .. Docstring generated from Julia source The 32-bit byte-order-mark indicates the native byte order of the host machine. Little-endian machines will contain the value ``0x04030201``\ . Big-endian machines will contain the value ``0x01020304``\ . + diff --git a/test/replutil.jl b/test/replutil.jl index 642d8bfe39ff6..9fbb928a577e0 100644 --- a/test/replutil.jl +++ b/test/replutil.jl @@ -447,3 +447,22 @@ let d = Dict(1 => 2, 3 => 45) @test contains(replace(result, " ", ""), string(el)) end end + + +# @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 + $(Expr(:$, :x))) +end