Skip to content

Commit 4ed6e98

Browse files
committed
splitstructdef
1 parent fe3ce38 commit 4ed6e98

File tree

4 files changed

+114
-0
lines changed

4 files changed

+114
-0
lines changed

src/MacroTools.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ include("types.jl")
1010
include("union.jl")
1111
include("macro.jl")
1212
include("utils.jl")
13+
include("structdef.jl")
1314

1415
include("examples/destruct.jl")
1516
include("examples/threading.jl")

src/structdef.jl

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
const STRUCTSYMBOL = VERSION < v"0.7-" ? :type : :struct
2+
isstructdef(ex) = Meta.isexpr(ex, STRUCTSYMBOL)
3+
4+
function splitstructdef(ex)
5+
ex = MacroTools.striplines(ex)
6+
ex = MacroTools.flatten(ex)
7+
d = Dict{Symbol, Any}()
8+
if @capture(ex, struct header_ body__ end)
9+
d[:mutable] = false
10+
elseif @capture(ex, mutable struct header_ body__ end)
11+
d[:mutable] = true
12+
else
13+
parse_error(ex)
14+
end
15+
16+
if @capture header nameparam_ <: super_
17+
nothing
18+
elseif @capture header nameparam_
19+
super = :Any
20+
else
21+
parse_error(ex)
22+
end
23+
d[:supertype] = super
24+
if @capture nameparam name_{param__}
25+
nothing
26+
elseif @capture nameparam name_
27+
param = []
28+
else
29+
parse_error(ex)
30+
end
31+
d[:name] = name
32+
d[:params] = param
33+
d[:fields] = []
34+
d[:constructors] = []
35+
for item in body
36+
if @capture item field_::T_
37+
push!(d[:fields], (field, T))
38+
elseif item isa Symbol
39+
push!(d[:fields], (item, Any))
40+
else
41+
push!(d[:constructors], item)
42+
end
43+
end
44+
d
45+
end
46+
47+
function combinestructdef(d)::Expr
48+
name = d[:name]
49+
parameters = d[:params]
50+
nameparam = isempty(parameters) ? name : :($name{$(parameters...)})
51+
header = :($nameparam <: $(d[:supertype]))
52+
fields = map(d[:fields]) do field
53+
fieldname, typ = field
54+
:($fieldname::$typ)
55+
end
56+
body = quote
57+
$(fields...)
58+
$(d[:constructors]...)
59+
end
60+
61+
Expr(STRUCTSYMBOL, d[:mutable], header, body)
62+
end
63+
64+
function combinefield(x)
65+
fieldname, T = x
66+
:($fieldname::$T)
67+
end

test/runtests.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,3 +150,4 @@ let
150150
end
151151

152152
include("destruct.jl")
153+
include("structdef.jl")

test/structdef.jl

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
using MacroTools: splitstructdef, combinestructdef
2+
3+
@testset "combinestructdef, splitstructdef" begin
4+
ex = :(struct S end)
5+
@test ex |> splitstructdef |> combinestructdef |> Base.remove_linenums! ==
6+
:(struct S <: Any end)
7+
8+
@test splitstructdef(ex) == Dict(
9+
:constructors => Any[],
10+
:mutable => false,
11+
:params => Any[],
12+
:name => :S,
13+
:fields => Any[],
14+
:supertype => :Any)
15+
16+
ex = :(mutable struct T end)
17+
@test splitstructdef(ex)[:mutable] === true
18+
@test ex |> splitstructdef |> combinestructdef |> Base.remove_linenums! ==
19+
:(mutable struct T <: Any end)
20+
21+
ex = :(struct S{A,B} <: AbstractS{B}
22+
a::A
23+
end)
24+
@test splitstructdef(ex) == Dict(
25+
:constructors => Any[],
26+
:mutable => false,
27+
:params => Any[:A, :B],
28+
:name => :S,
29+
:fields => Any[(:a, :A)],
30+
:supertype => :(AbstractS{B}),)
31+
32+
@test ex |> splitstructdef |> combinestructdef |> Base.remove_linenums! ==
33+
ex |> Base.remove_linenums!
34+
35+
ex = :(struct S{A} <: Foo; S(a::A) where {A} = new{A}() end)
36+
@test ex |> splitstructdef |> combinestructdef |>
37+
Base.remove_linenums! |> MacroTools.flatten ==
38+
ex |> Base.remove_linenums! |> MacroTools.flatten
39+
40+
constructors = splitstructdef(ex)[:constructors]
41+
@test length(constructors) == 1
42+
@test first(constructors) ==
43+
:((S(a::A) where A) = new{A}()) |> MacroTools.flatten
44+
45+
end

0 commit comments

Comments
 (0)