1
- macro connector (name:: Symbol , body)
2
- esc (connector_macro (__module__, name, body))
3
- end
4
-
5
1
struct Model{F, S}
6
2
f:: F
7
3
structure:: S
8
4
end
9
5
(m:: Model )(args... ; kw... ) = m. f (args... ; kw... )
10
6
11
- using MLStyle
7
+ for f in (:connector , :model )
8
+ @eval begin
9
+ macro $f (name:: Symbol , body)
10
+ esc ($ (Symbol (f, :_macro ))(__module__, name, body))
11
+ end
12
+ end
13
+ end
14
+
15
+ @inline is_kwarg (:: Symbol ) = false
16
+ @inline is_kwarg (e:: Expr ) = (e. head == :parameters )
12
17
13
- function connector_macro (mod, name, body)
18
+ function connector_macro (mod, name, body; arglist = Set ([]), kwargs = Set ([]) )
14
19
if ! Meta. isexpr (body, :block )
15
20
err = """
16
21
connector body must be a block! It should be in the form of
@@ -23,16 +28,18 @@ function connector_macro(mod, name, body)
23
28
"""
24
29
error (err)
25
30
end
26
- vs = Num []
31
+ vs = []
27
32
icon = Ref {Union{String, URI}} ()
28
33
dict = Dict {Symbol, Any} ()
34
+ dict[:kwargs ] = Dict {Symbol, Any} ()
35
+ expr = Expr (:block )
29
36
for arg in body. args
30
37
arg isa LineNumberNode && continue
31
38
if arg. head == :macrocall && arg. args[1 ] == Symbol (" @icon" )
32
39
parse_icon! (icon, dict, dict, arg. args[end ])
33
40
continue
34
41
end
35
- push! ( vs, Num ( parse_variable_def! ( dict, mod, arg, :variables )) )
42
+ parse_variable_arg! (expr, vs, dict, mod, arg, :variables , kwargs )
36
43
end
37
44
iv = get (dict, :independent_variable , nothing )
38
45
if iv === nothing
@@ -41,31 +48,50 @@ function connector_macro(mod, name, body)
41
48
gui_metadata = isassigned (icon) ? GUIMetadata (GlobalRef (mod, name), icon[]) :
42
49
nothing
43
50
quote
44
- $ name = $ Model ((; name) -> begin
45
- var"#___sys___" = $ ODESystem ($ (Equation[]), $ iv, $ vs, $ ([]);
51
+ $ name = $ Model (($ (arglist... ); name, $ (kwargs... )) -> begin
52
+ $ expr
53
+ var"#___sys___" = $ ODESystem ($ (Equation[]), $ iv, [$ (vs... )], $ ([]);
46
54
name, gui_metadata = $ gui_metadata)
47
55
$ Setfield. @set! (var"#___sys___" . connector_type= $ connector_type (var"#___sys___" ))
48
56
end , $ dict)
49
57
end
50
58
end
51
59
52
- function parse_variable_def! (dict, mod, arg, varclass)
60
+ function parse_variable_def! (dict, mod, arg, varclass, kwargs, def = nothing )
61
+ arg isa LineNumberNode && return
53
62
MLStyle. @match arg begin
54
- :: Symbol => generate_var! (dict, arg, varclass)
55
- Expr (:call , a, b) => generate_var! (dict, a, b, varclass)
63
+ a:: Symbol => begin
64
+ push! (kwargs, Expr (:kw , a, nothing ))
65
+ var = generate_var! (dict, a, varclass)
66
+ dict[:kwargs ][getname (var)] = def
67
+ (var, nothing )
68
+ end
69
+ Expr (:call , a, b) => begin
70
+ push! (kwargs, Expr (:kw , a, nothing ))
71
+ var = generate_var! (dict, a, b, varclass)
72
+ dict[:kwargs ][getname (var)] = def
73
+ (var, nothing )
74
+ end
56
75
Expr (:(= ), a, b) => begin
57
- var = parse_variable_def! (dict, mod, a, varclass)
58
- def = parse_default (mod, b)
76
+ Base. remove_linenums! (b)
77
+ def, meta = parse_default (mod, b)
78
+ var, _ = parse_variable_def! (dict, mod, a, varclass, kwargs, def)
59
79
dict[varclass][getname (var)][:default ] = def
60
- setdefault (var, def)
80
+ if ! isnothing (meta)
81
+ if (ct = get (meta, VariableConnectType, nothing )) != = nothing
82
+ dict[varclass][getname (var)][:connection_type ] = nameof (ct)
83
+ end
84
+ var = set_var_metadata (var, meta)
85
+ end
86
+ (var, def)
61
87
end
62
88
Expr (:tuple , a, b) => begin
63
- var = parse_variable_def! (dict, mod, a, varclass)
89
+ var, def = parse_variable_def! (dict, mod, a, varclass, kwargs )
64
90
meta = parse_metadata (mod, b)
65
91
if (ct = get (meta, VariableConnectType, nothing )) != = nothing
66
92
dict[varclass][getname (var)][:connection_type ] = nameof (ct)
67
93
end
68
- set_var_metadata (var, meta)
94
+ ( set_var_metadata (var, meta), def )
69
95
end
70
96
_ => error (" $arg cannot be parsed" )
71
97
end
@@ -78,14 +104,17 @@ function generate_var(a, varclass)
78
104
end
79
105
var
80
106
end
107
+
81
108
function generate_var! (dict, a, varclass)
109
+ # var = generate_var(Symbol("#", a), varclass)
82
110
var = generate_var (a, varclass)
83
111
vd = get! (dict, varclass) do
84
112
Dict {Symbol, Dict{Symbol, Any}} ()
85
113
end
86
114
vd[a] = Dict {Symbol, Any} ()
87
115
var
88
116
end
117
+
89
118
function generate_var! (dict, a, b, varclass)
90
119
iv = generate_var (b, :variables )
91
120
prev_iv = get! (dict, :independent_variable ) do
@@ -102,77 +131,101 @@ function generate_var!(dict, a, b, varclass)
102
131
end
103
132
var
104
133
end
134
+
105
135
function parse_default (mod, a)
106
136
a = Base. remove_linenums! (deepcopy (a))
107
137
MLStyle. @match a begin
108
- Expr (:block , a) => get_var (mod, a)
109
- :: Symbol => get_var (mod, a)
110
- :: Number => a
138
+ Expr (:block , x) => parse_default (mod, x)
139
+ Expr (:tuple , x, y) => begin
140
+ def, _ = parse_default (mod, x)
141
+ meta = parse_metadata (mod, y)
142
+ (def, meta)
143
+ end
144
+ :: Symbol || :: Number => (a, nothing )
145
+ Expr (:call , a... ) => begin
146
+ def = parse_default .(Ref (mod), a)
147
+ expr = Expr (:call )
148
+ for (d, _) in def
149
+ push! (expr. args, d)
150
+ end
151
+ (expr, nothing )
152
+ end
111
153
_ => error (" Cannot parse default $a " )
112
154
end
113
155
end
156
+
114
157
function parse_metadata (mod, a)
115
158
MLStyle. @match a begin
116
159
Expr (:vect , eles... ) => Dict (parse_metadata (mod, e) for e in eles)
117
160
Expr (:(= ), a, b) => Symbolics. option_to_metadata_type (Val (a)) => get_var (mod, b)
118
161
_ => error (" Cannot parse metadata $a " )
119
162
end
120
163
end
164
+
121
165
function set_var_metadata (a, ms)
122
166
for (m, v) in ms
123
167
a = setmetadata (a, m, v)
124
168
end
125
169
a
126
170
end
171
+
127
172
function get_var (mod:: Module , b)
128
173
b isa Symbol ? getproperty (mod, b) : b
129
174
end
130
175
131
- macro model (name:: Symbol , expr)
132
- esc (model_macro (__module__, name, expr))
133
- end
134
-
135
- function model_macro (mod, name, expr)
176
+ function model_macro (mod, name, expr; arglist = Set ([]), kwargs = Set ([]))
136
177
exprs = Expr (:block )
137
178
dict = Dict {Symbol, Any} ()
179
+ dict[:kwargs ] = Dict {Symbol, Any} ()
138
180
comps = Symbol[]
139
181
ext = Ref {Any} (nothing )
140
- vs = Symbol[]
141
- ps = Symbol[]
142
182
eqs = Expr[]
143
183
icon = Ref {Union{String, URI}} ()
184
+ vs = []
185
+ ps = []
186
+
144
187
for arg in expr. args
145
188
arg isa LineNumberNode && continue
146
- arg. head == :macrocall || error (" $arg is not valid syntax. Expected a macro call." )
147
- parse_model! (exprs. args, comps, ext, eqs, vs, ps, icon, dict, mod, arg)
189
+ if arg. head == :macrocall
190
+ parse_model! (exprs. args, comps, ext, eqs, icon, vs, ps,
191
+ dict, mod, arg, kwargs)
192
+ elseif arg. head == :block
193
+ push! (exprs. args, arg)
194
+ else
195
+ error (" $arg is not valid syntax. Expected a macro call." )
196
+ end
148
197
end
149
198
iv = get (dict, :independent_variable , nothing )
150
199
if iv === nothing
151
200
iv = dict[:independent_variable ] = variable (:t )
152
201
end
202
+
153
203
gui_metadata = isassigned (icon) > 0 ? GUIMetadata (GlobalRef (mod, name), icon[]) :
154
204
nothing
205
+
155
206
sys = :($ ODESystem ($ Equation[$ (eqs... )], $ iv, [$ (vs... )], [$ (ps... )];
156
- systems = [$ (comps... )], name, gui_metadata = $ gui_metadata))
207
+ systems = [$ (comps... )], name, gui_metadata = $ gui_metadata)) # , defaults = $defaults))
157
208
if ext[] === nothing
158
209
push! (exprs. args, sys)
159
210
else
160
211
push! (exprs. args, :($ extend ($ sys, $ (ext[]))))
161
212
end
162
- :($ name = $ Model ((; name) -> $ exprs, $ dict))
213
+
214
+ :($ name = $ Model (($ (arglist... ); name, $ (kwargs... )) -> $ exprs, $ dict))
163
215
end
164
216
165
- function parse_model! (exprs, comps, ext, eqs, vs, ps, icon, dict, mod, arg)
217
+ function parse_model! (exprs, comps, ext, eqs, icon, vs, ps, dict,
218
+ mod, arg, kwargs)
166
219
mname = arg. args[1 ]
167
220
body = arg. args[end ]
168
221
if mname == Symbol (" @components" )
169
- parse_components! (exprs, comps, dict, body)
222
+ parse_components! (exprs, comps, dict, body, kwargs )
170
223
elseif mname == Symbol (" @extend" )
171
224
parse_extend! (exprs, ext, dict, body)
172
225
elseif mname == Symbol (" @variables" )
173
- parse_variables! (exprs, vs, dict, mod, body, :variables )
226
+ parse_variables! (exprs, vs, dict, mod, body, :variables , kwargs )
174
227
elseif mname == Symbol (" @parameters" )
175
- parse_variables! (exprs, ps, dict, mod, body, :parameters )
228
+ parse_variables! (exprs, ps, dict, mod, body, :parameters , kwargs )
176
229
elseif mname == Symbol (" @equations" )
177
230
parse_equations! (exprs, eqs, dict, body)
178
231
elseif mname == Symbol (" @icon" )
@@ -182,7 +235,7 @@ function parse_model!(exprs, comps, ext, eqs, vs, ps, icon, dict, mod, arg)
182
235
end
183
236
end
184
237
185
- function parse_components! (exprs, cs, dict, body)
238
+ function parse_components! (exprs, cs, dict, body, kwargs )
186
239
expr = Expr (:block )
187
240
push! (exprs, expr)
188
241
comps = Vector{String}[]
@@ -194,6 +247,9 @@ function parse_components!(exprs, cs, dict, body)
194
247
push! (comps, [String (a), String (b. args[1 ])])
195
248
arg = deepcopy (arg)
196
249
b = deepcopy (arg. args[2 ])
250
+
251
+ component_args! (a, b, expr, kwargs)
252
+
197
253
push! (b. args, Expr (:kw , :name , Meta. quot (a)))
198
254
arg. args[2 ] = b
199
255
push! (expr. args, arg)
@@ -204,6 +260,46 @@ function parse_components!(exprs, cs, dict, body)
204
260
dict[:components ] = comps
205
261
end
206
262
263
+ function _rename (compname, varname)
264
+ compname = Symbol (compname, :__ , varname)
265
+ end
266
+
267
+ function component_args! (a, b, expr, kwargs)
268
+ # Whenever `b` is a function call, skip the first arg aka the function name.
269
+ # Whenver it is a kwargs list, include it.
270
+ start = b. head == :call ? 2 : 1
271
+ for i in start: lastindex (b. args)
272
+ arg = b. args[i]
273
+ arg isa LineNumberNode && continue
274
+ MLStyle. @match arg begin
275
+ :: Symbol => begin
276
+ _v = _rename (a, arg)
277
+ push! (kwargs, _v)
278
+ b. args[i] = Expr (:kw , arg, _v)
279
+ end
280
+ Expr (:parameters , x... ) => begin
281
+ component_args! (a, arg, expr, kwargs)
282
+ end
283
+ Expr (:kw , x) => begin
284
+ _v = _rename (a, x)
285
+ b. args[i] = Expr (:kw , x, _v)
286
+ push! (kwargs, _v)
287
+ end
288
+ Expr (:kw , x, y:: Number ) => begin
289
+ _v = _rename (a, x)
290
+ b. args[i] = Expr (:kw , x, _v)
291
+ push! (kwargs, Expr (:kw , _v, y))
292
+ end
293
+ Expr (:kw , x, y) => begin
294
+ _v = _rename (a, x)
295
+ push! (expr. args, :($ y = $ _v))
296
+ push! (kwargs, Expr (:kw , _v, y))
297
+ end
298
+ _ => error (" Could not parse $arg of component $a " )
299
+ end
300
+ end
301
+ end
302
+
207
303
function parse_extend! (exprs, ext, dict, body)
208
304
expr = Expr (:block )
209
305
push! (exprs, expr)
@@ -231,16 +327,21 @@ function parse_extend!(exprs, ext, dict, body)
231
327
end
232
328
end
233
329
234
- function parse_variables! (exprs, vs, dict, mod, body, varclass)
330
+ function parse_variable_arg! (expr, vs, dict, mod, arg, varclass, kwargs)
331
+ vv, def = parse_variable_def! (dict, mod, arg, varclass, kwargs)
332
+ v = Num (vv)
333
+ name = getname (v)
334
+ push! (vs, name)
335
+ push! (expr. args,
336
+ :($ name = $ name === nothing ? $ setdefault ($ vv, $ def) : $ setdefault ($ vv, $ name)))
337
+ end
338
+
339
+ function parse_variables! (exprs, vs, dict, mod, body, varclass, kwargs)
235
340
expr = Expr (:block )
236
341
push! (exprs, expr)
237
342
for arg in body. args
238
343
arg isa LineNumberNode && continue
239
- vv = parse_variable_def! (dict, mod, arg, varclass)
240
- v = Num (vv)
241
- name = getname (v)
242
- push! (vs, name)
243
- push! (expr. args, :($ name = $ v))
344
+ parse_variable_arg! (expr, vs, dict, mod, arg, varclass, kwargs)
244
345
end
245
346
end
246
347
0 commit comments