@@ -27,7 +27,13 @@ proc parseDeclWithType(def: NimNode): tuple[name, typ, val: NimNode] =
27
27
28
28
# proc mapSuper(nCall: NimNode): NimNode = discard
29
29
30
- proc replaceSuperCall (n: var NimNode , defSupCls: NimNode ): bool =
30
+ type
31
+ MethKind = enum
32
+ mkNorm
33
+ mkCls
34
+ mkStatic
35
+
36
+ proc replaceSuperCall (n: var NimNode , defSupCls: NimNode , methKind: MethKind ): bool =
31
37
template nret (cond): untyped =
32
38
if not cond:
33
39
return
@@ -37,24 +43,51 @@ proc replaceSuperCall(n: var NimNode, defSupCls: NimNode): bool =
37
43
nret callSup.kind == nnkCall
38
44
nret callSup[0 ].eqIdent " super"
39
45
40
- var supCls = defSupCls
41
- let cLen = callSup.len
42
- if cLen > 1 :
43
- expectIdent callSup[2 ], " self"
44
- if callSup.len > 2 :
45
- if cLen > 3 : error " super(<SupCls>, self) expected, but got too many args"
46
- supCls = callSup[1 ]
46
+ var cLen = callSup.len
47
+ let supCls =
48
+ if cLen > 2 :
49
+ if cLen > 3 : error " super([<cls>[, self]]) expected, but got too many args"
50
+ callSup[1 ]
51
+ else :
52
+ if cLen == 1 :
53
+ callSup.add defSupCls
54
+ cLen.inc
55
+ defSupCls
56
+
57
+ if methKind == mkCls:
58
+ if cLen == 3 :
59
+ expectIdent callSup[2 ], " cls"
60
+ else :
61
+ callSup.add ident " cls"
62
+ cLen.inc
63
+ n = newDotExpr (supCls, n[0 ][1 ]).newCall (callSup[2 ])
64
+ elif methKind == mkNorm:
65
+ if cLen > 1 :
66
+ expectIdent callSup[2 ], " self"
67
+ n[0 ][0 ] = newCall (supCls, ident " self" )
47
68
48
- n[0 ][0 ] = newCall (supCls, ident " self" )
69
+ # let meth = n[0][1]
70
+ # let args = n[1..^1]
49
71
50
- # let meth = n[0][1]
51
- # let args = n[1..^1]
72
+ n = newCall (ident " procCall" , n)
52
73
53
- n = newCall (ident " procCall" , n)
54
74
result = true
55
75
56
- proc recReplaceSuperCall * (n: NimNode , defSupCls: NimNode , start= 0 ): NimNode =
57
- ##[ Recursively maps `super(...).f(...)`
76
+ template new * [T; R: RootRef ](_: typedesc [R], cls: typedesc [T],
77
+ _: varargs [untyped ]): typedesc [T] =
78
+ # # py's `object.__new__`
79
+ cls
80
+
81
+ proc init_subclass * [T](cls: typedesc [ref T]): ref T =
82
+ # # py's `object.__init_subclass__`
83
+ discard
84
+
85
+ proc recReplaceSuperCall * (n: NimNode , defSupCls: NimNode , start= 0 , methKind= mkNorm): NimNode =
86
+ ##[ Recursively maps
87
+
88
+ ### 1.
89
+
90
+ `super(...).f(...)`
58
91
to `procCall(<SuperClass>(self).f(...))`
59
92
60
93
The AST map:
@@ -66,7 +99,7 @@ The AST map:
66
99
[<SuperClass>]
67
100
[Ident "self"]
68
101
Ident "f"
69
- <args>
102
+ <args>
70
103
71
104
|
72
105
|
@@ -80,7 +113,13 @@ The AST map:
80
113
<SuperClass>
81
114
Ident "self"
82
115
Ident "f"
83
- ``` ]##
116
+ <args>
117
+ ```
118
+
119
+ ### 2
120
+ `super(...).new/init_subclass(...)`
121
+ to `<SuperClass>.new/init_subclass(...)`
122
+ ]##
84
123
runnableExamples:
85
124
import std/ [macros, strutils]
86
125
macro checkSupSub (resStr: static string ; b) =
@@ -104,9 +143,9 @@ The AST map:
104
143
result = n.copy ()
105
144
var i = start
106
145
while i< result .len:
107
- discard replaceSuperCall (result , defSupCls)
146
+ discard replaceSuperCall (result , defSupCls, methKind = methKind )
108
147
if result [i].len != 0 :
109
- result [i] = recReplaceSuperCall (result [i], defSupCls, 0 )
148
+ result [i] = recReplaceSuperCall (result [i], defSupCls, 0 , methKind = methKind )
110
149
i.inc
111
150
112
151
func remove1 [T](s: var seq [T], x: T) =
@@ -122,7 +161,7 @@ func remove1[T](s: var seq[T], x: T) =
122
161
123
162
proc tryPreClsBltinDecorater (mparser: var PyAsgnRewriter ,
124
163
args: var seq [NimNode ], procType: var NimNodeKind ,
125
- pragmas: var seq [NimNode ],
164
+ pragmas: var seq [NimNode ], methKind: var MethKind
126
165
): bool =
127
166
#[
128
167
@staticmethod
@@ -177,9 +216,11 @@ then you will find it compile but `O.f()` gives `0` instead of `3`
177
216
return false
178
217
case $ decor.name
179
218
of " staticmethod" :
219
+ methKind = mkStatic
180
220
purgeBase ()
181
221
args.insert (newIdentDefs (ident " _" , clsType), 1 )
182
222
of " classmethod" :
223
+ methKind = mkCls
183
224
purgeBase ()
184
225
args[1 ][1 ] = clsType
185
226
else :
@@ -191,6 +232,31 @@ template mkPragma(pragmas: seq[NimNode]): NimNode =
191
232
if pragmas.len == 0 : emptyn
192
233
else : nnkPragma.newNimNode.add pragmas
193
234
235
+ proc genNewCls (classId, generics: NimNode , initArgs, initPragmas: seq [NimNode ]): NimNode =
236
+ # # returns decl of `newXxx`
237
+ let classNewName = ident (" new" & classId.strVal)
238
+ var newArgs = @ [classId]
239
+ var body = newStmtList ()
240
+ let resId = ident " result"
241
+ var callNew = newCall (newDotExpr (classId, ident " new" ), newCall (" typeof" , classId))
242
+ let defInit = initArgs.len > 0
243
+ var callInit: NimNode
244
+ if defInit:
245
+ # var init = newCall(ident"init", resId)
246
+ callInit = newCall (resId.newDotExpr (ident " init" ))
247
+ for i in 2 ..< initArgs.len: # skip `resType` and `self: Cls`
248
+ let argDef = initArgs[i].copyNimTree # we cannot use the old, as it's a symbol
249
+ # or there will be a error: positional param was already given as named param
250
+ newArgs.add argDef
251
+ callNew.add argDef[0 ]
252
+ callInit.add argDef[0 ]
253
+ body.add newAssignment (resId, nnkObjConstr.newTree (callNew))
254
+ if defInit:
255
+ body.add callInit
256
+ var pragmas = initPragmas
257
+ pragmas.remove1 ident " base"
258
+ newProc (classNewName, generics, newArgs, body, pragmas= pragmas.mkPragma)
259
+
194
260
proc classImpl * (parser: var PySyntaxProcesser ; obj, body: NimNode ): NimNode =
195
261
##[ minic Python's `class`.
196
262
@@ -292,8 +358,25 @@ so if wantting the attr inherited from SupCls, just write it as-is (e.g. `self.a
292
358
template addAttr (name; typ= emptyn, defVal= emptyn) =
293
359
typDefLs.add nnkIdentDefs.newTree (name, typ, defVal)
294
360
var defs = newStmtList ()
361
+ var decls = newStmtList ()
362
+ template addMeth (def: NimNode ) = defs.add def
363
+ #[ return type 'auto' cannot be used in forward declarations
364
+ template addMethWithDecl(def: NimNode) =
365
+ defs.add def
366
+ var decl = def.copyNimNode
367
+ let lastI = def.len - 1
368
+ for i in 0..<lastI:
369
+ decl.add def[i].copyNimTree
370
+ decl.add newEmptyNode()
371
+ decls.add decl
372
+ ]#
373
+ var noNew = true
374
+ var
375
+ initArgs: seq [NimNode ]
376
+ initGenerics = newNimNode nnkGenericParams
377
+ initPragmas: seq [NimNode ]
295
378
for def in items (body):
296
- var pragmas = defPragmas # will be set as empty if `isConstruct ` or not base
379
+ var pragmas = defPragmas # will be set as empty if `isConstructor ` or not base
297
380
case def.kind
298
381
of nnkCall:
299
382
# - attr define, e.g. a: int / a: int = 1
@@ -317,51 +400,68 @@ so if wantting the attr inherited from SupCls, just write it as-is (e.g. `self.a
317
400
let tup = parser.parseSignatureMayGenerics (generics_cpy, signature, deftype)
318
401
var procName = tup.name
319
402
dunderDirVal.add newLit procName.strVal
320
- let isConstructor = procName.eqIdent " init"
321
- if isConstructor:
322
- procName = newIdentNode (" new" & className)
403
+ let
404
+ isConstructor = procName.eqIdent " init"
405
+ isNew = procName.eqIdent " new"
406
+ var methKind: MethKind
407
+ if isNew or procName.eqIdent " init_subclass" :
408
+ methKind = mkCls
409
+ if isNew:
410
+ noNew = false
323
411
pragmas = @ []
324
412
# First argument is the return type of the procedure
325
413
var args = tup.params
326
414
# push a new stack frame
327
415
parser.push ()
328
- # Statements which will occur before proc body
329
- var beforeBody = newStmtList ()
416
+ var body = newStmtList ()
417
+ template markSelfType =
418
+ args[1 ][1 ] = genericsClassId
419
+ template chk1ArgCls =
420
+ if not args[1 ][0 ].eqIdent " cls" :
421
+ warning " the 1st arg of __new__ is not cls, but " & args[1 ][0 ].repr,
422
+ args[1 ]
423
+ args[1 ][1 ] = classId
330
424
if isConstructor:
425
+ if args[0 ].strVal in [" None" , " auto" ]:
426
+ args[0 ] = newEmptyNode ()
331
427
expectIdent args[1 ][0 ], " self"
332
- args.delete 1
333
- args[0 ] = genericsClassId
334
- template construct (): untyped {.dirty .} =
335
- var self: type (result )
336
- new (self)
337
- beforeBody.add getAst (construct ())
428
+ markSelfType
429
+ initArgs = args
430
+ elif methKind == mkCls:
431
+ if isNew:
432
+ args[0 ] = nnkBracketExpr.newTree (bindSym " typedesc" , genericsClassId)
433
+ chk1ArgCls
434
+ elif methKind == mkStatic:
435
+ discard
338
436
else :
339
437
if args.len > 1 and args[1 ][0 ].eqIdent " self" :
340
- args[ 1 ][ 1 ] = genericsClassId
438
+ markSelfType
341
439
# Function body
342
440
var docNode: NimNode
343
- var parsedbody = recReplaceSuperCall (parser.parsePyBodyWithDoc (def[2 ], docNode), supCls)
344
- # If we're generating a constructor proc - we need to return self
345
- # after we've created it
346
- if isConstructor:
347
- parsedbody.add nnkReturnStmt.newTree ident " self"
348
- # Add statement which will occur before function body
349
- beforeBody.add parsedBody
441
+ var parsedbody = recReplaceSuperCall (
442
+ parser.parsePyBodyWithDoc (def[2 ], docNode), supCls,
443
+ methKind= methKind)
350
444
if docNode.len != 0 :
351
- beforeBody.insert (0 , docNode)
445
+ body.insert (0 , docNode)
446
+ # Add statement which will occur before function body
447
+ body.add parsedBody
352
448
parser.pop ()
353
449
354
450
# Finally create a procedure and add it to result!
355
451
var procType: NimNodeKind
356
452
discard parser.tryPreClsBltinDecorater (
357
- args, procType, pragmas= pragmas
453
+ args, procType, pragmas= pragmas, methKind = methKind
358
454
)
359
- if isConstructor: procType = nnkProcDef
455
+ if methKind != mkNorm:
456
+ procType = nnkProcDef
457
+ elif isConstructor:
458
+ initPragmas = pragmas
459
+ initGenerics = generics_cpy
360
460
let nDef = parser.consumeDecorator (
361
- newProc (procName, generics_cpy, args, beforeBody ,
461
+ newProc (procName, generics_cpy, args, body ,
362
462
procType, pragmas= pragmas.mkPragma)
363
463
)
364
- defs. add nDef
464
+ addMeth nDef
365
465
366
466
of nnkStrLit, nnkRStrLit, nnkTripleStrLit:
367
467
result .add newCommentStmtNode $ def
@@ -370,6 +470,9 @@ so if wantting the attr inherited from SupCls, just write it as-is (e.g. `self.a
370
470
result .add def
371
471
else :
372
472
result .add def # AS-IS
473
+
474
+ addMeth genNewCls (classId, initGenerics, initArgs, initPragmas)
475
+
373
476
let ty = nnkRefTy.newTree nnkObjectTy.newTree (emptyn, supClsNode, typDefLs)
374
477
let typDef = nnkTypeSection.newTree nnkTypeDef.newTree (classId, generics, ty)
375
478
result .add:
@@ -383,6 +486,8 @@ so if wantting the attr inherited from SupCls, just write it as-is (e.g. `self.a
383
486
# result.add quote do:
384
487
# when not declaredInScope `classId`:
385
488
# `typDef`
489
+
490
+ result .add decls
386
491
result .add defs
387
492
388
493
result .add newConstStmt (
0 commit comments