Skip to content

Commit 9110cbc

Browse files
authored
Commuting Macros (#16)
1 parent d44ed60 commit 9110cbc

3 files changed

Lines changed: 61 additions & 3 deletions

File tree

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name = "ConcreteStructs"
22
uuid = "2569d6c7-a4a2-43d3-a901-331e8e4be471"
33
authors = ["Jonnie Diegelman <jonniediegelman@gmail.com> and contributors"]
4-
version = "0.2.4"
4+
version = "0.2.5"
55

66
[compat]
77
julia = "1.1"

src/ConcreteStructs.jl

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,8 +102,8 @@ end
102102

103103
# Parse whole struct definition for the @concrete macro
104104
function _concretize(expr)
105-
expr isa Expr && expr.head == :struct || error("Invalid usage of @concrete")
106-
105+
original_expr, expr = _find_struct_def(expr)
106+
107107
is_mutable = expr.args[1]
108108
struct_name, type_params, super = _parse_head(expr.args[2])
109109
line_tuples = _parse_line.(expr.args[3].args)
@@ -121,6 +121,8 @@ function _concretize(expr)
121121
body = Expr(:block, lines..., constructor_expr)
122122
struct_expr = Expr(:struct, is_mutable, head, body)
123123

124+
struct_expr = _apply_original_expr(original_expr, struct_expr)
125+
124126
return struct_expr, struct_name, type_params
125127
end
126128

@@ -239,5 +241,27 @@ function _parse_line(line::Symbol)
239241
return (:($line::$T), T)
240242
end
241243

244+
# find the struct def in case its encapsuled in other macros
245+
# returns full_original_expression, struct_definition_expr
246+
function _find_struct_def(expr; tl_expr = expr)
247+
if expr.head == :struct
248+
return tl_expr, expr
249+
elseif expr.head == :macrocall
250+
return _find_struct_def(expr.args[3]; tl_expr = tl_expr)
251+
end
252+
return nerror("Invalid usage of @concrete.")
253+
end
254+
255+
# finds where the struct is defined in original_expr and plugs in struct_expr
256+
function _apply_original_expr(original_expr, struct_expr)
257+
if original_expr.head == :struct
258+
original_expr.args = struct_expr.args
259+
elseif original_expr.head == :macrocall
260+
original_expr.args[3] = _apply_original_expr(original_expr.args[3], struct_expr)
261+
else
262+
error("Unsupported expression.")
263+
end
264+
return original_expr
265+
end
242266

243267
end

test/runtests.jl

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,3 +201,37 @@ end
201201

202202
@test typeof(foo) === Foo{Int, Float64, Adjoint{Int, Vector{Int}}}
203203
end
204+
205+
206+
@testset "Commutation with other Macros" begin
207+
# this has been working for a while already
208+
Base.@kwdef @concrete struct Foo2{T<:Real}
209+
x::T = 1.0
210+
y = true
211+
z = 42
212+
end
213+
214+
foo = Foo2()
215+
@test typeof(foo) === Foo2{Float64, Bool, Int64}
216+
217+
# this is new
218+
@concrete Base.@kwdef struct Foo3{T<:Real}
219+
x::T = 1.0
220+
y = true
221+
z = 42
222+
end
223+
224+
foo = Foo3()
225+
@test typeof(foo) === Foo3{Float64, Bool, Int64}
226+
227+
foo = Foo3(z=[1, 2, 3])
228+
@test typeof(foo) === Foo3{Float64, Bool, Vector{Int64}}
229+
230+
# terse test case, unfortunately doesn't work the other way around. But also never did.
231+
@concrete terse Base.@kwdef struct TerseKWDef
232+
a = 1.0
233+
b = true
234+
end
235+
terse_kw_def = TerseKWDef(b = false)
236+
@test typeof(terse_kw_def) === TerseKWDef{Float64, Bool}
237+
end

0 commit comments

Comments
 (0)