Skip to content

Commit 9f05cc3

Browse files
committed
Evaluate init and freeze immediately
1 parent cb8d7ec commit 9f05cc3

File tree

3 files changed

+134
-119
lines changed

3 files changed

+134
-119
lines changed

src/APITools.jl

Lines changed: 106 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -26,37 +26,49 @@ macro def(name, definition)
2626
end
2727
end
2828

29-
struct TMP_API
30-
base::Vector{Symbol}
31-
public::Vector{Symbol}
32-
develop::Vector{Symbol}
33-
define_public::Vector{Symbol}
34-
define_develop::Vector{Symbol}
35-
define_module::Vector{Symbol}
36-
TMP_API() = new(Symbol[], Symbol[], Symbol[], Symbol[], Symbol[], Symbol[])
29+
const SymSet = Set{Symbol}
30+
31+
abstract type AbstractAPI end
32+
33+
struct TMP_API <: AbstractAPI
34+
mod::Module
35+
base::SymSet
36+
public::SymSet
37+
develop::SymSet
38+
define_public::SymSet
39+
define_develop::SymSet
40+
define_module::SymSet
41+
42+
TMP_API(mod::Module) = new(mod, SymSet(), SymSet(), SymSet(), SymSet(), SymSet(), SymSet())
3743
end
3844

3945
const SymList = Tuple{Vararg{Symbol}}
4046

41-
struct API
47+
struct API <: AbstractAPI
4248
mod::Module
4349
base::SymList
4450
public::SymList
4551
develop::SymList
4652
define_public::SymList
4753
define_develop::SymList
4854
define_module::SymList
49-
50-
API(mod, api::TMP_API) =
51-
new(mod,
52-
SymList(api.base), SymList(api.public), SymList(api.develop),
53-
SymList(api.define_public), SymList(api.define_develop), SymList(api.define_module))
5455
end
5556

56-
const APIList = Tuple{Vararg{API}}
57+
API(api::TMP_API) =
58+
API(api.mod, SymList(api.base), SymList(api.public),
59+
SymList(api.develop), SymList(api.define_public),
60+
SymList(api.define_develop), SymList(api.define_module))
5761

58-
"""Expression to get current module"""
59-
const _cur_mod = V6_COMPAT ? :( current_module() ) : :( @__MODULE__ )
62+
function Base.show(io::IO, api::AbstractAPI)
63+
println(io, "APITools.API: ", api.mod)
64+
for fld in (:base, :public, :develop, :define_public, :define_develop, :define_module)
65+
syms = getfield(api, fld)
66+
isempty(syms) || println(fld, ": ", syms)
67+
end
68+
end
69+
70+
"""Get current module"""
71+
cur_mod() = ccall(:jl_get_current_module, Ref{Module}, ())
6072

6173
"""
6274
@api <cmd> [<symbols>...]
@@ -79,44 +91,42 @@ const _cur_mod = V6_COMPAT ? :( current_module() ) : :( @__MODULE__ )
7991
* @api define_module <names...> # Add submodule names that are part of the API
8092
"""
8193
macro api(cmd::Symbol)
82-
cmd == :init && return _api_init()
83-
cmd == :freeze && return esc(_api_freeze())
84-
cmd == :list && return _api_list()
94+
mod = @static V6_COMPAT ? current_module() : __module__
95+
#=
96+
@static if V6_COMPAT
97+
println("api($mod, $cmd)")
98+
else
99+
println("api($__source__, $mod, $cmd)")
100+
end
101+
=#
102+
cmd == :list ? _api_list(mod) :
103+
cmd == :init ? _api_init(mod) :
104+
cmd == :freeze ? _api_freeze(mod) :
85105
error("@api unrecognized command: $cmd")
86106
end
87107

88-
function _api_display(api, chain)
108+
function _api_display(api::AbstractAPI)
89109
show(api)
90110
println()
91-
show(chain)
92-
println()
93111
end
94112

95-
function _api_display(mod)
96-
isdefined(mod, :__api__) &&
97-
_api_display(eval(mod, :__api__), eval(mod, :__chain__))
98-
isdefined(mod, :__tmp_api__) &&
99-
_api_display(eval(mod, :__tmp_api__), eval(mod, :__tmp_chain__))
113+
function _api_list(mod::Module)
114+
isdefined(mod, :__api__) && _api_display(eval(mod, :__api__))
115+
isdefined(mod, :__tmp_api__) && _api_display(eval(mod, :__tmp_api__))
100116
nothing
101117
end
102118

103-
_api_freeze(mod, api, chain) = APIList(push!(chain, API(mod, api)))
104-
105-
_api_init() =
106-
quote
107-
export @api, APITools
108-
global __tmp_api__ = APITools.TMP_API()
109-
global __tmp_chain__ = APITools.API[]
110-
end
111-
112-
_api_freeze() =
113-
quote
114-
global const __chain__ = APITools._api_freeze($_cur_mod, __tmp_api__, __tmp_chain__)
115-
global const __api__ = __chain__[end]
116-
__tmp_chain__ = __tmp_api__ = nothing
117-
end
119+
function _api_init(mod::Module)
120+
ex = :( export @api, APITools ; global __tmp_api__ = APITools.TMP_API($mod) )
121+
isdefined(mod, :__tmp_api__) || eval(mod, ex)
122+
nothing
123+
end
118124

119-
_api_list(mod = _cur_mod) = :( APITools._api_display($mod) )
125+
function _api_freeze(mod::Module)
126+
ex = :( global const __api__ = APITools.API(__tmp_api__) ; __tmp_api__ = nothing )
127+
isdefined(mod, :__tmp_api__) && eval(mod, :( __tmp_api__ !== nothing ) ) && eval(mod, ex)
128+
nothing
129+
end
120130

121131
const _cmduse = (:use, :test, :extend, :export)
122132
const _cmdadd =
@@ -125,102 +135,96 @@ const _cmdadd =
125135
@static V6_COMPAT && (const _ff = findfirst)
126136
@static V6_COMPAT || (_ff(lst, val) = coalesce(findfirst(isequal(val), lst), 0))
127137

128-
function _add_def!(deflst, implst, explst, sym)
138+
function _add_def!(curmod, sym)
129139
if isdefined(Base, sym)
130-
push!(implst, sym)
140+
eval(curmod, :(push!(__tmp_api__.base, $(QuoteNode(sym)))))
141+
eval(curmod, :(import Base.$sym ))
131142
else
132-
push!(deflst, sym)
133-
push!(explst, esc(:(function $sym end)))
143+
eval(curmod, :(push!(__tmp_api__.public, $(QuoteNode(sym)))))
144+
eval(curmod, :(function $sym end))
134145
end
135146
end
136147

137148
"""Add symbols"""
138-
function _add_symbols(grp, exprs)
139-
print("_add_symbols($grp, $exprs)")
140-
outlst = Expr[:(isdefined($_cur_mod, :__tmp_api__) || APITools._api_init())]
141-
println(" => ", outlst)
142-
outlst = Expr[]
149+
function _add_symbols(curmod, grp, exprs)
150+
#print("_add_symbols($curmod, $grp, $exprs)", isdefined(curmod, :__tmp_api__))
151+
_api_init(curmod)
143152
if grp == :maybe_public
144-
implst = Symbol[]
145-
deflst = Symbol[]
146153
for ex in exprs
147154
if isa(ex, Expr) && ex.head == :tuple
148155
for sym in ex.args
149156
isa(sym, Symbol) || error("@api $grp: $sym not a Symbol")
150-
_add_def!(deflst, implst, outlst, sym)
157+
_add_def!(curmod, sym)
151158
end
152159
elseif isa(ex, Symbol)
153-
_add_def!(deflst, implst, outlst, ex)
160+
_add_def!(curmod, ex)
154161
else
155162
error("@api $grp: syntax error $ex")
156163
end
157164
end
158-
isempty(deflst) ||
159-
push!(outlst, Expr(:call, :push!,
160-
Expr(:., :__tmp_api__, QuoteNode(:public)),
161-
QuoteNode.(deflst)...))
162-
exprs = implst
163-
grp = :base
164-
end
165-
symbols = Symbol[]
166-
for ex in exprs
167-
if isa(ex, Expr) && ex.head == :tuple
168-
append!(symbols, ex.args)
169-
elseif isa(ex, Symbol)
170-
push!(symbols, ex)
171-
else
172-
error("@api $grp: syntax error $ex")
165+
else
166+
symbols = SymSet()
167+
for ex in exprs
168+
if isa(ex, Expr) && ex.head == :tuple
169+
push!(symbols, ex.args...)
170+
elseif isa(ex, Symbol)
171+
push!(symbols, ex)
172+
else
173+
error("@api $grp: syntax error $ex")
174+
end
175+
end
176+
#println("symbols: ", symbols)
177+
if grp == :base
178+
for sym in symbols
179+
eval(curmod, :( import Base.$sym ))
180+
end
181+
end
182+
for sym in symbols
183+
eval(curmod, :( push!(__tmp_api__.$grp, $(QuoteNode(sym)) )))
173184
end
174185
end
175-
println("symbols: ", symbols)
176-
if grp == :base
177-
syms = SymList(symbols)
178-
expr = "APITools._make_list($(QuoteNode(:import)), $(QuoteNode(:Base)), $syms)"
179-
push!(outlst, esc(:(eval($_cur_mod, $(Meta.parse(expr))))))
180-
end
181-
push!(outlst, Expr(:call, :push!,
182-
Expr(:., :__tmp_api__, QuoteNode(grp)), QuoteNode.(symbols)...))
183-
println(outlst)
184-
outlst
186+
nothing
185187
end
186188

187-
function _make_modules(cmd, exprs)
188-
uselst = Expr[]
189-
modlst = Symbol[]
189+
function _make_modules(curmod, cmd, exprs)
190+
modlst = SymSet()
190191
for ex in exprs
191192
if isa(ex, Expr) && ex.head == :tuple
192-
append!(modlst, ex.args)
193-
for sym in ex.args ; push!(uselst, :(import $sym)) ; end
193+
push!(modlst, ex.args...)
194+
for sym in ex.args ; eval(curmod, :(import $sym)) ; end
194195
elseif isa(ex, Symbol)
195196
push!(modlst, ex)
196-
push!(uselst, :(import $ex))
197+
eval(curmod, :(import $ex))
197198
else
198199
error("@api $cmd: syntax error $ex")
199200
end
200201
end
201-
uselst, modlst
202+
modlst
202203
end
203204

204-
function _api(cmd, exprs)
205+
function _api(curmod::Module, cmd::Symbol, exprs)
206+
#println("api($curmod, $cmd, $exprs)")
205207
ind = _ff(_cmdadd, cmd)
206-
ind == 0 || return esc(Expr(:toplevel, _add_symbols(cmd, exprs), nothing))
208+
ind == 0 || return _add_symbols(curmod, cmd, exprs)
207209

208210
ind = _ff(_cmduse, cmd)
209211

210-
lst, modules = _make_modules(cmd, exprs)
212+
modules = _make_modules(curmod, cmd, exprs)
211213

212214
cmd == :export &&
213-
return esc(Expr(:toplevel, lst...,
214-
[:(eval($_cur_mod, Expr( :export, $mod.__api__.$grp... )))
215+
return esc(Expr(:toplevel,
216+
[:(eval(Expr( :export, $mod.__api__.$grp... )))
215217
for mod in modules, grp in (:define_module, :define_public, :public)]...,
216218
nothing))
217219
cmd == :list &&
218220
return Expr(:toplevel,
219-
[:(eval($_cur_mod, APITools._api_display($mod))) for mod in modules]...,
221+
[:(eval(APITools._api_display($mod))) for mod in modules]...,
220222
nothing)
221223

224+
lst = Expr[]
222225
for mod in modules
223-
push!(lst, _make_module_exprs(mod))
226+
exp = "APITools._make_module_list($(QuoteNode(mod)), $mod.__api__.define_module)"
227+
push!(lst, :(eval($(Meta.parse(exp)))))
224228
end
225229

226230
if cmd == :use
@@ -230,11 +234,6 @@ function _api(cmd, exprs)
230234
push!(lst, V6_COMPAT ? :(using Base.Test) : :(using Test))
231235
elseif cmd == :extend
232236
grplst = (:define_public, :define_develop)
233-
# should add unique modules to __tmp_chain__
234-
for mod in modules
235-
push!(lst,
236-
esc(:(in($mod.__api__, __tmp_chain__) || push!(__tmp_chain__, $mod.__api__))))
237-
end
238237
for mod in modules, grp in (:base, :public, :develop)
239238
push!(lst, _make_exprs(:import, mod, grp))
240239
end
@@ -247,19 +246,18 @@ function _api(cmd, exprs)
247246
esc(Expr(:toplevel, lst..., nothing))
248247
end
249248

250-
macro api(cmd::Symbol, exprs...) ; _api(cmd, exprs) ; end
251-
252-
# We need Expr(:toplevel, (Expr($cmd, $mod, $sym) for sym in $mod.__api__.$grp)...)
249+
@static if V6_COMPAT
250+
macro api(cmd::Symbol, exprs...) ; _api(current_module(), cmd, exprs) ; end
251+
else
252+
macro api(cmd::Symbol, exprs...) ; _api(__module__, cmd, exprs) ; end
253+
end
253254

254255
function _make_module_list(mod, lst)
255256
isempty(lst) && return nothing
256257
length(lst) == 1 ? :(import $mod.$(lst[1])) :
257258
Expr(:toplevel, [:(import $mod.$nam) for nam in lst]..., nothing)
258259
end
259260

260-
_make_module_exprs(mod) =
261-
:(eval($_cur_mod, $(Meta.parse("APITools._make_module_list($(QuoteNode(mod)), $mod.__api__.define_module)"))))
262-
263261
function _make_list(cmd, mod, lst)
264262
isempty(lst) && return nothing
265263
@static if VERSION < v"0.7.0-DEV"
@@ -272,8 +270,7 @@ end
272270

273271
function _make_exprs(cmd, mod, grp)
274272
from = QuoteNode(grp == :base ? :Base : mod)
275-
:(eval($_cur_mod,
276-
$(Meta.parse("APITools._make_list($(QuoteNode(cmd)), $from, $mod.__api__.$grp)"))))
273+
:(eval($(Meta.parse("APITools._make_list($(QuoteNode(cmd)), $from, $mod.__api__.$grp)"))))
277274
end
278275

279276
end # module APITools

test/APITest.jl

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,33 @@
11
module APITest
22
using APITools
33

4+
_printapi(C) = (print(C) ; isdefined(APITest, :__tmp_api__) ? (show(__tmp_api__) ; println()) : println("Not defined"))
5+
macro printapi() ; _printapi("C: ") ; :( _printapi("R: ") ) ; end
6+
_printsym(C) = println(C, names(APITest, true, true))
7+
macro printsym() ; _printsym("C: ") ; :( _printsym("R: ") ) ; end
8+
9+
#@printsym
10+
11+
#@printapi
12+
13+
#println(macroexpand( :( @api init ) ))
14+
415
@api init
516

17+
#@printapi
18+
19+
#println(macroexpand( :( @api base nextind, getindex, setindex! ) ) )
20+
621
@api base nextind, getindex, setindex!
722

8-
println(__tmp_api__)
23+
#@printapi
24+
25+
#println(macroexpand( :( @api public myfunc ) ) )
926

1027
@api public myfunc
28+
29+
#println(macroexpand( :( @api define_public Foo )))
30+
1131
@api define_public Foo
1232

1333
struct Foo end
@@ -16,10 +36,12 @@ function myfunc end
1636
myfunc(::Integer) = 1
1737
myfunc(::String) = 2
1838

19-
#println(__tmp_api__)
39+
#@printapi()
2040

2141
#println(macroexpand( :( @api freeze ) ))
2242

2343
@api freeze
2444

45+
#@printsym
46+
2547
end # module APITest

0 commit comments

Comments
 (0)