Skip to content

Commit 0757f6b

Browse files
committed
Update
1 parent 4ec036b commit 0757f6b

File tree

3 files changed

+101
-86
lines changed

3 files changed

+101
-86
lines changed

src/APITools.jl

+97-82
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ Licensed under MIT License, see LICENSE.md
1010
"""
1111
module APITools
1212

13+
const debug = Ref(false)
14+
1315
const V6_COMPAT = VERSION < v"0.7.0-DEV"
1416
const BIG_ENDIAN = (ENDIAN_BOM == 0x01020304)
1517

@@ -35,9 +37,9 @@ struct TMP_API <: AbstractAPI
3537
base::SymSet
3638
public::SymSet
3739
develop::SymSet
38-
define_public::SymSet
39-
define_develop::SymSet
40-
define_module::SymSet
40+
public!::SymSet
41+
develop!::SymSet
42+
modules::SymSet
4143

4244
TMP_API(mod::Module) = new(mod, SymSet(), SymSet(), SymSet(), SymSet(), SymSet(), SymSet())
4345
end
@@ -49,19 +51,18 @@ struct API <: AbstractAPI
4951
base::SymList
5052
public::SymList
5153
develop::SymList
52-
define_public::SymList
53-
define_develop::SymList
54-
define_module::SymList
54+
public!::SymList
55+
develop!::SymList
56+
modules::SymList
5557
end
5658

5759
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))
60+
API(api.mod, SymList(api.base), SymList(api.public), SymList(api.develop),
61+
SymList(api.public!), SymList(api.develop!), SymList(api.modules))
6162

6263
function Base.show(io::IO, api::AbstractAPI)
6364
println(io, "APITools.API: ", api.mod)
64-
for fld in (:base, :public, :develop, :define_public, :define_develop, :define_module)
65+
for fld in (:base, :public, :develop, :public!, :develop!, :modules)
6566
syms = getfield(api, fld)
6667
isempty(syms) || println(fld, ": ", syms)
6768
end
@@ -77,22 +78,21 @@ cur_mod() = ccall(:jl_get_current_module, Ref{Module}, ())
7778
7879
* @api list <modules>... # list API(s) of given modules (or current if none given)
7980
80-
* @api use <modules>... # use for normal use
81-
* @api test <modules>... # using api and dev, for testing purposes
81+
* @api use <modules>... # use, without importing (i.e. can't extend)
82+
* @api test <modules>... # using public and develop APIs, for testing purposes
8283
* @api extend <modules>... # for development, imports api & dev, use api & dev definitions
83-
* @api export <modules>... # export api symbols
84+
* @api export <modules>... # export public definitions
8485
8586
* @api base <names...> # Add functions from Base that are part of the API
87+
* @api public! <names...> # Add other symbols that are part of the public API (structs, consts)
88+
* @api develop! <names...> # Add other symbols that are part of the development API
8689
* @api public <names...> # Add functions that are part of the public API
8790
* @api develop <names...> # Add functions that are part of the development API
88-
* @api define_public <names...> # Add other symbols that are part of the public API (structs, consts)
89-
* @api define_develop <names...> # Add other symbols that are part of the development API
90-
* @api define_module <names...> # Add submodule names that are part of the API
91+
* @api modules <names...> # Add submodule names that are part of the API
9192
"""
9293
macro api(cmd::Symbol)
9394
mod = @static V6_COMPAT ? current_module() : __module__
9495
cmd == :list ? _api_list(mod) :
95-
cmd == :init ? _api_init(mod) :
9696
cmd == :freeze ? _api_freeze(mod) :
9797
error("@api unrecognized command: $cmd")
9898
end
@@ -108,12 +108,6 @@ function _api_list(mod::Module)
108108
nothing
109109
end
110110

111-
function _api_init(mod::Module)
112-
ex = :( export @api, APITools ; global __tmp_api__ = APITools.TMP_API($mod) )
113-
isdefined(mod, :__tmp_api__) || eval(mod, ex)
114-
nothing
115-
end
116-
117111
function _api_freeze(mod::Module)
118112
ex = :( global const __api__ = APITools.API(__tmp_api__) ; __tmp_api__ = nothing )
119113
isdefined(mod, :__tmp_api__) && eval(mod, :( __tmp_api__ !== nothing ) ) && eval(mod, ex)
@@ -122,36 +116,46 @@ end
122116

123117
const _cmduse = (:use, :test, :extend, :export, :list)
124118
const _cmdadd =
125-
(:define_module, :define_public, :define_develop, :public, :develop, :base, :maybe_public)
119+
(:modules, :public, :develop, :public!, :develop!, :base, :base!)
126120

127121
@static V6_COMPAT && (const _ff = findfirst)
128122
@static V6_COMPAT || (_ff(lst, val) = coalesce(findfirst(isequal(val), lst), 0))
129123

130-
function _add_def!(curmod, sym)
124+
function _add_def!(curmod, grp, exp)
125+
debug[] && print("_add_def!($curmod, $grp, $exp::$(typeof(exp))")
126+
if isa(exp, Symbol)
127+
sym = exp
128+
elseif isa(exp, AbstractString)
129+
sym = Symbol(exp)
130+
else
131+
error("@api $grp: syntax error $exp")
132+
end
131133
if isdefined(Base, sym)
132-
eval(curmod, :(push!(__tmp_api__.base, $(QuoteNode(sym)))))
133134
eval(curmod, :(import Base.$sym ))
135+
eval(curmod, :(push!(__tmp_api__.base, $(QuoteNode(sym)))))
134136
else
135-
eval(curmod, :(push!(__tmp_api__.public, $(QuoteNode(sym)))))
136137
eval(curmod, :(function $sym end))
138+
eval(curmod, :(push!(__tmp_api__.public!, $(QuoteNode(sym)))))
137139
end
138140
end
139141

140142
"""Add symbols"""
141143
function _add_symbols(curmod, grp, exprs)
142-
#print("_add_symbols($curmod, $grp, $exprs)", isdefined(curmod, :__tmp_api__))
143-
_api_init(curmod)
144-
if grp == :maybe_public
144+
if debug[]
145+
print("_add_symbols($curmod, $grp, $exprs)")
146+
isdefined(curmod, :__tmp_api__) && print(" => ", eval(curmod, :__tmp_api__))
147+
println()
148+
end
149+
ex = :( export @api, APITools ; global __tmp_api__ = APITools.TMP_API($curmod) )
150+
isdefined(curmod, :__tmp_api__) || eval(curmod, ex)
151+
if grp == :base!
145152
for ex in exprs
146153
if isa(ex, Expr) && ex.head == :tuple
147154
for sym in ex.args
148-
isa(sym, Symbol) || error("@api $grp: $sym not a Symbol")
149-
_add_def!(curmod, sym)
155+
_add_def!(curmod, grp, sym)
150156
end
151-
elseif isa(ex, Symbol)
152-
_add_def!(curmod, ex)
153157
else
154-
error("@api $grp: syntax error $ex")
158+
_add_def!(curmod, grp, ex)
155159
end
156160
end
157161
else
@@ -161,6 +165,8 @@ function _add_symbols(curmod, grp, exprs)
161165
push!(symbols, ex.args...)
162166
elseif isa(ex, Symbol)
163167
push!(symbols, ex)
168+
elseif isa(ex, AbstractString)
169+
push!(symbols, Symbol(ex))
164170
else
165171
error("@api $grp: syntax error $ex")
166172
end
@@ -174,15 +180,61 @@ function _add_symbols(curmod, grp, exprs)
174180
eval(curmod, :( push!(__tmp_api__.$grp, $(QuoteNode(sym)) )))
175181
end
176182
end
183+
debug[] && println("after add symbols: ", eval(curmod, :__tmp_api__))
184+
nothing
185+
end
186+
187+
function _api_extend(curmod, modules)
188+
imp = :import
189+
use = :using
190+
191+
for nam in modules
192+
mod = eval(curmod, nam)
193+
if isdefined(mod, :__api__)
194+
api = eval(mod, :__api__)
195+
_do_list(curmod, imp, api, :Base, :base)
196+
_do_list(curmod, imp, api, nam, :public!)
197+
_do_list(curmod, imp, api, nam, :develop!)
198+
_do_list(curmod, use, api, nam, :public)
199+
_do_list(curmod, use, api, nam, :develop)
200+
else
201+
println("API not found for module: $mod")
202+
end
203+
end
204+
205+
nothing
206+
end
207+
208+
function _api_use(curmod, modules)
209+
for nam in modules
210+
mod = eval(curmod, nam)
211+
if isdefined(mod, :__api__)
212+
api = eval(mod, :__api__)
213+
_do_list(curmod, :using, api, nam, :public)
214+
_do_list(curmod, :using, api, nam, :public!)
215+
end
216+
end
177217
nothing
178218
end
179219

220+
_api_export(curmod, modules) =
221+
esc(Expr(:toplevel,
222+
[:(eval(Expr( :export, $mod.__api__.$grp... )))
223+
for mod in modules, grp in (:modules, :public, :public!)]...,
224+
nothing))
225+
226+
_api_list(curmod, modules) =
227+
Expr(:toplevel,
228+
[:(eval(APITools._api_display($mod))) for mod in modules]...,
229+
nothing)
230+
180231
function _api(curmod::Module, cmd::Symbol, exprs)
181232
ind = _ff(_cmdadd, cmd)
182233
ind == 0 || return _add_symbols(curmod, cmd, exprs)
183234

184235
_ff(_cmduse, cmd) == 0 && error("Syntax error: @api $cmd $exprs")
185236

237+
debug[] && print("_api($curmod, $cmd, $exprs)")
186238
modules = SymSet()
187239
for ex in exprs
188240
if isa(ex, Expr) && ex.head == :tuple
@@ -195,61 +247,22 @@ function _api(curmod::Module, cmd::Symbol, exprs)
195247
error("@api $cmd: syntax error $ex")
196248
end
197249
end
250+
debug[] && println(" => $modules")
198251

199-
cmd == :export &&
200-
return esc(Expr(:toplevel,
201-
[:(eval(Expr( :export, $mod.__api__.$grp... )))
202-
for mod in modules, grp in (:define_module, :define_public, :public)]...,
203-
nothing))
204-
cmd == :list &&
205-
return Expr(:toplevel,
206-
[:(eval(APITools._api_display($mod))) for mod in modules]...,
207-
nothing)
252+
cmd == :export && return _api_export(curmod, modules)
253+
cmd == :list && return _api_list(curmod, modules)
208254

209255
for nam in modules
210256
mod = eval(curmod, nam)
211-
for sym in getfield(eval(mod, :__api__), :define_module)
257+
for sym in getfield(eval(mod, :__api__), :modules)
212258
eval(curmod, :(using $nam.$sym))
213259
end
214260
end
215261

216-
imp = :import
217-
use = :using
218-
219-
if cmd == :extend
220-
for nam in modules
221-
mod = eval(curmod, nam)
222-
if isdefined(mod, :__api__)
223-
api = eval(mod, :__api__)
224-
_do_list(curmod, imp, api, :Base, :base)
225-
_do_list(curmod, imp, api, nam, :public)
226-
_do_list(curmod, imp, api, nam, :develop)
227-
_do_list(curmod, use, api, nam, :define_public)
228-
_do_list(curmod, use, api, nam, :define_develop)
229-
else
230-
println("API not found for module: $mod")
231-
end
232-
end
233-
return nothing
234-
end
235-
236262
# Be nice and set up standard Test
237263
cmd == :test && eval(curmod, V6_COMPAT ? :(using Base.Test) : :(using Test))
238264

239-
for nam in modules
240-
mod = eval(curmod, nam)
241-
if isdefined(mod, :__api__)
242-
api = eval(mod, :__api__)
243-
_do_list(curmod, use, api, nam, :public)
244-
_do_list(curmod, use, api, nam, :define_public)
245-
if cmd == :test
246-
_do_list(curmod, use, api, nam, :public)
247-
_do_list(curmod, use, api, nam, :define_public)
248-
end
249-
end
250-
end
251-
252-
nothing
265+
cmd == :use ? _api_use(curmod, modules) : _api_extend(curmod, modules)
253266
end
254267

255268
@static V6_COMPAT || (_dot_name(nam) = Expr(:., nam))
@@ -258,16 +271,18 @@ function _do_list(curmod, cmd, api, mod, grp)
258271
lst = getfield(api, grp)
259272
isempty(lst) && return
260273
@static if V6_COMPAT
261-
length(lst) == 1 && return eval(curmod, Expr(cmd, mod, lst[1]))
262274
for nam in lst
263-
eval(curmod, Expr(cmd, mod, nam))
275+
exp = Expr(cmd, mod, nam)
276+
debug[] && println("V6: $cmd, $mod, $mod, $exp")
277+
eval(curmod, exp)
264278
end
265279
else
266280
exp = Expr(cmd, Expr(:(:), _dot_name(mod), _dot_name.(lst)...))
267-
println(exp)
281+
debug[] && println("V7: $cmd, $mod, $mod, $exp")
268282
try
269283
eval(curmod, exp)
270284
catch ex
285+
println("APITools: Error evaluating $exp")
271286
dump(exp)
272287
println(sprint(showerror, ex, catch_backtrace()))
273288
end

test/APITest.jl

+2-2
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@ using APITools
33

44
@api base nextind, getindex, setindex!
55

6-
@api public myfunc
6+
@api public! myfunc
77

8-
@api define_public Foo
8+
@api public Foo
99

1010
struct Foo end
1111

test/runtests.jl

+2-2
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,6 @@ myfunc(::AbstractFloat) = 3
2929
@test myfunc(2.0) == 3
3030
@test APITest.__api__.mod == APITest
3131
@test Set(APITest.__api__.base) == Set([:nextind, :getindex, :setindex!])
32-
@test Set(APITest.__api__.public) == Set([:myfunc])
33-
@test Set(APITest.__api__.define_public) == Set([:Foo])
32+
@test Set(APITest.__api__.public) == Set([:Foo])
33+
@test Set(APITest.__api__.public!) == Set([:myfunc])
3434
end

0 commit comments

Comments
 (0)