/
macros.jl
155 lines (122 loc) · 4.3 KB
/
macros.jl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
using Base.Meta: isexpr
# ------------------------------------------------------------------
function getStructTypeAndFields(expr)
if !isexpr(expr, :type)
error("trying to create constructor for non-type expression")
end
#get type name
args = expr.args
if isa(args[2],Symbol) structType = args[2]
elseif isexpr(args[2],:<:) structType = args[2].args[1]
else error("Unable to extract type name!")
end
# loop through all the fields and build an array of types
fields = []
for fieldExpr in args[3].args
if isexpr(fieldExpr, :(::))
push!(fields, (fieldExpr.args[1], fieldExpr.args[2]))
end
end
return structType, fields
end
function makebody(structType, args)
body = :($structType())
append!(body.args, args)
body
end
# create a new bitstype value by converting a UInt8 pointer to a "ftype" pointer, then load from the pointer
function loadFunction(ftype, s)
:(unsafe_load(convert(Ptr{$ftype}, pointer(buf,pos+$s))))
end
# ------------------------------------------------------------------
# this macro takes a type definition expression and creates a custom constructor
# to read in from an IO source that is packed binary data
macro packedStruct(expr)
structType, fields = getStructTypeAndFields(expr)
fieldTypes = [f[2] for f in fields]
# create constructors
defaultConstructorBody = makebody(structType, [:(zero($ft)) for ft in fieldTypes])
readIOConstructorBody = makebody(structType, [:(read(io, $ft)) for ft in fieldTypes])
# create the reinterpret function to construct from a position in a byte array
fsizes = [eval(current_module(), :(sizeof($ft))) for ft in fieldTypes]
spos = vcat(1, cumsum(fsizes)[1:end-1] + 1)
reinterpretBody = makebody(structType, [loadFunction(ft,spos[i]) for (i,ft) in enumerate(fieldTypes)])
# now return the full expression
# note: the whole thing is escaped so that this block is executed in calling scope
# note: "esc(expression)" executes in calling scope, whereas "expression" executes in macro scope
esc(quote
$expr
$structType() = $defaultConstructorBody
$structType(io::IO) = $readIOConstructorBody
Base.zero(::Type{$structType}) = $structType()
Base.read(io::IO, ::Type{$structType}) = $structType(io)
Base.read(mmio::MMapIO, ::Type{$structType}) = $structType(mmio.io)
Base.reinterpret(::Type{$structType}, buf::AbstractVector{UInt8}, pos::Integer) = $reinterpretBody
CTechCommon.getPackedStructSize(::Type{$structType}) = $(sum(fsizes))
end)
end
# ------------------------------------------------------------------
# # create print/show methods, assuming string(typ) is defined
# macro createIOMethods(typ::Symbol)
# esc(quote
# Base.print(io::IO, o::$typ) = print(io, string(o))
# Base.show(io::IO, o::$typ) = print(io, string(o))
# end)
# end
# # ------------------------------------------------------------------
# macro pretty(expr::Expr)
# structType, fields = getStructTypeAndFields(expr)
# # create a string() expression and add "$sep$fieldname: $fieldexpr" for each field
# strExpr = :(string())
# for (i,f) in enumerate(fields)
# fn = f[1]
# sep = i>1 ? ", " : ""
# ss = "$sep$fn="
# push!(strExpr.args, :($ss))
# push!(strExpr.args, :(xxx.$fn))
# end
# # return the original type expression, along with the new methods to be added
# quote
# $(esc(expr))
# Base.string(xxx::$(esc(structType))) = string($(string(structType)), "{", $strExpr, "}")
# Base.print(io::IO, xxx::$(esc(structType))) = print(io, string(xxx))
# Base.show(io::IO, xxx::$(esc(structType))) = print(io, string(xxx))
# end
# end
# recurse
function replace_syms(expr::Expr, names, obj)
for i in eachindex(expr.args)
expr.args[i] = replace_syms(expr.args[i], names, obj)
end
expr
end
# replace?
function replace_syms(s::Symbol, names, obj)
if s in names
:($(obj).$(s))
else
s
end
end
# identity
replace_syms(x, names, obj) = x
export @with
"""
Recursively replace all occurences of a field with dot notation:
```julia
type T
a
b
end
@with t::T a = b + 5
# becomes:
t.a = t.b + 5
```
"""
macro with(typeexpr::Expr, body::Expr)
@assert typeexpr.head == :(::)
@assert length(typeexpr.args) == 2
obj, objtype = typeexpr.args
names = fieldnames(eval(Main, objtype))
esc(replace_syms(body, names, obj))
end