Skip to content

Commit 83475db

Browse files
committed
fix(py), feat(sugar): class & methods are exported if top-level
1 parent 1e3dca7 commit 83475db

File tree

4 files changed

+62
-24
lines changed

4 files changed

+62
-24
lines changed

src/pylib/pysugar/class.nim

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import ./stmt/class
33
export class.new
44
import ./parserWithCfg
55

6-
macro class*(obj, body: untyped): untyped =
6+
macro classAux(obj, body: untyped, topLevel: static[bool]): untyped =
77
## wrapper of `classImpl proc<./stmt/tonim.html>`_
88
runnableExamples:
99
class O:
@@ -31,4 +31,12 @@ macro class*(obj, body: untyped): untyped =
3131
return super().f(b)
3232
assert CC().f(2) == 3
3333
var parser = parserWithDefCfg()
34-
parser.classImpl(obj, body)
34+
parser.classImpl(obj, body, topLevel)
35+
36+
template class*(obj, body) =
37+
bind classAux
38+
classAux(obj, body,
39+
instantiationInfo().column == 0
40+
# cannot handle `when`
41+
)
42+

src/pylib/pysugar/stmt/class.nim

Lines changed: 48 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11

22
import std/macros
3-
import ./frame, ./funcSignature, ./decorator, ./tonim, ./types
3+
import ./frame, ./funcSignature, ./decorator, ./types
44
import ./pydef
55

66
template emptyn: NimNode = newEmptyNode()
@@ -159,6 +159,9 @@ func remove1[T](s: var seq[T], x: T) =
159159
return
160160
s.delete idx
161161

162+
proc rmBase(n: var seq[NimNode]) =
163+
n.remove1 ident"base"
164+
162165
proc tryPreClsBltinDecorater(mparser: var PyAsgnRewriter,
163166
args: var seq[NimNode], procType: var NimNodeKind,
164167
pragmas: var seq[NimNode], methKind: var MethKind
@@ -208,7 +211,7 @@ then you will find it compile but `O.f()` gives `0` instead of `3`
208211
]#
209212
warning "There may be a error like: " &
210213
"`Error: cannot instantiate: '_`gensymXXX:type'`"
211-
pragmas.remove1 ident"base"
214+
pragmas.rmBase
212215
template clsType: NimNode =
213216
nnkBracketExpr.newTree(ident"typedesc", curClass())
214217

@@ -228,13 +231,29 @@ then you will find it compile but `O.f()` gives `0` instead of `3`
228231
withType(nnkProcDef)
229232
return true
230233

231-
template mkPragma(pragmas: seq[NimNode]): NimNode =
234+
template mkPragma(pragmas: openArray[NimNode]): NimNode =
232235
if pragmas.len == 0: emptyn
233236
else: nnkPragma.newNimNode.add pragmas
234237

235-
proc genNewCls(classId, generics: NimNode, initArgs, initPragmas: seq[NimNode]): NimNode =
236-
## returns decl of `newXxx`
237-
let classNewName = ident("new" & classId.strVal)
238+
proc newMethAsProc(name, generics: NimNode, params: openArray[NimNode], body: NimNode, procType = nnkProcDef, pragmas: openArray[NimNode]): NimNode =
239+
let procType = (if procType == nnkMethodDef: nnkProcDef else: procType)
240+
var pragmas = @pragmas
241+
pragmas.rmBase
242+
newProc(name, generics, params, body, procType, pragmas.mkPragma)
243+
244+
proc mkExport(n: NimNode): NimNode = n.postfix"*"
245+
246+
proc newMethProc(topLevel: bool, name, generics: NimNode, params: openArray[NimNode], body: NimNode, procType = nnkProcDef, pragmas: openArray[NimNode]): NimNode =
247+
if topLevel:
248+
var pragmas = @pragmas
249+
pragmas.rmBase
250+
result = newProc(name.mkExport, generics, params, body, procType, pragmas.mkPragma)
251+
else:
252+
result = newMethAsProc(name, generics, params, body, procType, pragmas)
253+
result.addPragma ident"used"
254+
255+
proc genNewCls(topLevel: bool, classNewProcName, classId, generics: NimNode, initArgs, initPragmas: openArray[NimNode]): NimNode =
256+
## returns decl of proc
238257
var newArgs = @[classId]
239258
var body = newStmtList()
240259
let resId = ident"result"
@@ -253,11 +272,11 @@ proc genNewCls(classId, generics: NimNode, initArgs, initPragmas: seq[NimNode]):
253272
body.add newAssignment(resId, nnkObjConstr.newTree(callNew))
254273
if defInit:
255274
body.add callInit
256-
var pragmas = initPragmas
257-
pragmas.remove1 ident"base"
258-
newProc(classNewName, generics, newArgs, body, pragmas=pragmas.mkPragma)
275+
var pragmas = @initPragmas
276+
pragmas.rmBase
277+
newMethProc(topLevel, classNewProcName, generics, newArgs, body, pragmas=pragmas)
259278

260-
proc classImpl*(parser: var PySyntaxProcesser; obj, body: NimNode): NimNode =
279+
proc classImpl*(parser: var PySyntaxProcesser; obj, body: NimNode, topLevel = true): NimNode =
261280
##[ minic Python's `class`.
262281
263282
support `def` for method with nested `def/class` supported
@@ -298,7 +317,6 @@ so if wantting the attr inherited from SupCls, just write it as-is (e.g. `self.a
298317
var
299318
classId = obj
300319
supCls = ident"RootObj"
301-
supClsNode = nnkOfInherit.newTree supCls
302320
defPragmas = @[ident"base"]
303321
generics = emptyn
304322
template parseGenerics(n: NimNode) =
@@ -333,13 +351,16 @@ so if wantting the attr inherited from SupCls, just write it as-is (e.g. `self.a
333351
elif supLen == 1:
334352
# class O(SupCls)
335353
supCls = supClses[0]
336-
if supCls.kind != nnkObjectTy: # not `class O(object)`
337-
supClsNode = nnkOfInherit.newTree supCls
338-
defPragmas.remove1 ident"base"
354+
if not supCls.eqIdent"RootObj": # not `class O(object)`
355+
if not topLevel:
356+
error "non-topLevel class cannot inherit currently " &
357+
"(as Nim's method must be top-level)", obj[1]
358+
defPragmas.rmBase
339359
of nnkBracketExpr:
340360
parseGenerics obj
341361
else:
342362
error "unexpected class syntax, got: ", obj
363+
let supClsNode = nnkOfInherit.newTree supCls
343364

344365
initSubClassArgs[0] = classId
345366
let className = $classId
@@ -349,7 +370,10 @@ so if wantting the attr inherited from SupCls, just write it as-is (e.g. `self.a
349370
for i in generics.items():
350371
let g = i[0] # g.kind == nnkIdentDef
351372
genericsClassId.add g
352-
373+
template exportIfTop(n: NimNode): NimNode =
374+
if topLevel: n.mkExport
375+
else:
376+
nnkPragmaExpr.newTree(n, nnkPragma.newTree ident"used")
353377
result = newStmtList()
354378
parser.classes.add classId
355379
let dunderDirId = ident className & ".dunder.dict.keys()" # `classId.__dict__.keys()`
@@ -391,7 +415,10 @@ so if wantting the attr inherited from SupCls, just write it as-is (e.g. `self.a
391415
of nnkCommand: # TODO: support async
392416
# def a(b, c=1).
393417
# Other stuff than defines: comments, etc
394-
if not def[0].eqIdent "def":
418+
if def[0].eqIdent "class":
419+
result.add parser.classImpl(def[1], def[2], topLevel=false)
420+
continue
421+
elif not def[0].eqIdent "def":
395422
result.add def
396423
continue
397424
let signature = def[1]
@@ -458,8 +485,8 @@ so if wantting the attr inherited from SupCls, just write it as-is (e.g. `self.a
458485
initPragmas = pragmas
459486
initGenerics = generics_cpy
460487
let nDef = parser.consumeDecorator(
461-
newProc(procName, generics_cpy, args, body,
462-
procType, pragmas=pragmas.mkPragma)
488+
newMethProc(topLevel, procName, generics_cpy, args, body,
489+
procType, pragmas=pragmas)
463490
)
464491
addMeth nDef
465492

@@ -471,10 +498,10 @@ so if wantting the attr inherited from SupCls, just write it as-is (e.g. `self.a
471498
else:
472499
result.add def # AS-IS
473500

474-
addMeth genNewCls(classId, initGenerics, initArgs, initPragmas)
501+
addMeth genNewCls(topLevel, ident("new" & classId.strVal), classId, initGenerics, initArgs, initPragmas)
475502

476503
let ty = nnkRefTy.newTree nnkObjectTy.newTree(emptyn, supClsNode, typDefLs)
477-
let typDef = nnkTypeSection.newTree nnkTypeDef.newTree(classId, generics, ty)
504+
let typDef = nnkTypeSection.newTree nnkTypeDef.newTree(classId.exportIfTop, generics, ty)
478505
result.add:
479506
nnkWhenStmt.newTree(
480507
nnkElifBranch.newTree(
@@ -491,7 +518,7 @@ so if wantting the attr inherited from SupCls, just write it as-is (e.g. `self.a
491518
result.add defs
492519

493520
result.add newConstStmt(
494-
dunderDirId.postfix"*", dunderDirVal
521+
dunderDirId.exportIfTop, dunderDirVal
495522
)
496523
let initSub = newCall("init_subclass").add initSubClassArgs
497524
result.add nnkWhenStmt.newTree(

src/pylib/pysugar/stmt/tonim.nim

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,13 @@
2525

2626
import std/macros
2727
import ./pyraise, ./frame, ./pydef, ./unpack, ./decorator, ./exprRewrite
28+
import ./class
2829
import ../../private/inspect_cleandoc
2930

3031
using mparser: var PyAsgnRewriter
3132
proc parsePyBody*(mparser; body: NimNode): NimNode # front decl
3233
proc parsePyBodyWithDoc*(mparser; body: NimNode): NimNode # front decl
34+
proc parsePyBodyWithDoc*(mparser; body: NimNode, docNode: var NimNode): NimNode # front decl
3335

3436
proc tryHandleDocStr(res: var NimNode; n: NimNode, dedent=false): bool =
3537
if n.kind in nnkStrLit..nnkTripleStrLit:
@@ -116,7 +118,7 @@ proc parsePyStmt*(mparser; statement: NimNode): NimNode =
116118
defRe = asyncImpl(statement[1], statement[2], parser=mparser)
117119
result.add mparser.consumeDecorator(defRe)
118120
of "class":
119-
error "class in def is not supported yet"
121+
result.add mparser.classImpl(statement[1], statement[2], topLevel=false)
120122
# TODO: impl by define such class in global but mangling its name
121123
# It has to be global as class's def is implemented via `method`,
122124
# which is only allowed at global scope

src/pylib/pysugar/stmt/types.nim

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,6 @@ type
44
## `PyAsgnRewriter` in ./frame
55
## while its parsePyBody is in ./tonim
66
parsePyBodyWithDoc(self, NimNode) is NimNode
7+
parsePyBodyWithDoc(self, NimNode, var NimNode) is NimNode
78
self.supportGenerics is bool
89
self.dedentDoc is bool

0 commit comments

Comments
 (0)