@@ -10,6 +10,8 @@ Licensed under MIT License, see LICENSE.md
1010"""
1111module APITools
1212
13+ const debug = Ref(false )
14+
1315const V6_COMPAT = VERSION < v" 0.7.0-DEV"
1416const 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())
4345end
@@ -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
5557end
5658
5759API(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
6263function 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"""
9293macro 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 " )
9898end
@@ -108,12 +108,6 @@ function _api_list(mod::Module)
108108 nothing
109109end
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-
117111function _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
123117const _cmduse = (:use, :test, :extend, :export, :list)
124118const _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
138140end
139141
140142""" Add symbols"""
141143function _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
178218end
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+
180231function _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)
253266end
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
0 commit comments