Skip to content

Commit eed6d9c

Browse files
committed
feat(augassign): impl a.__iXX__(b) fallback to a=a.__XX__(b)
1 parent b431380 commit eed6d9c

File tree

2 files changed

+74
-20
lines changed

2 files changed

+74
-20
lines changed

Objects/pyobject.nim

Lines changed: 39 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,13 @@ export pyobjectBase
2121
template getMagic*(obj: PyObject, methodName): untyped =
2222
obj.pyType.magicMethods.methodName
2323

24-
template getFun*(obj: PyObject, methodName: untyped, handleExcp=false): untyped =
25-
if obj.pyType.isNil:
26-
unreachable("Py type not set")
27-
let fun = getMagic(obj, methodName)
28-
if fun.isNil:
24+
25+
template checkTypeNotNil(obj) =
26+
when not defined(release):
27+
if obj.pyType.isNil:
28+
unreachable("Py type not set")
29+
30+
template handleNilFunOfGetFun(obj, methodName, handleExcp) =
2931
let objTypeStr = $obj.pyType.name
3032
let methodStr = astToStr(methodName)
3133
let msg = "No " & methodStr & " method for " & objTypeStr & " defined"
@@ -34,33 +36,56 @@ template getFun*(obj: PyObject, methodName: untyped, handleExcp=false): untyped
3436
handleException(excp)
3537
else:
3638
return excp
39+
40+
template getFun*(obj: PyObject, methodName: untyped, handleExcp=false): untyped =
41+
bind checkTypeNotNil, handleNilFunOfGetFun
42+
obj.checkTypeNotNil
43+
let fun = getMagic(obj, methodName)
44+
if fun.isNil:
45+
handleNilFunOfGetFun(obj, methodName, handleExcp)
3746
fun
3847

3948

4049
# XXX: `obj` is used twice so it better be a simple identity
4150
# if it's a function then the function is called twice!
4251

43-
# is there any ways to reduce the repetition? simple template won't work
44-
template callMagic*(obj: PyObject, methodName: untyped, handleExcp=false): PyObject =
45-
let fun = obj.getFun(methodName, handleExcp)
46-
let res = fun(obj)
52+
template checkExcAndRet[T](res: T, handleExcp): T =
4753
when handleExcp:
4854
if res.isThrownException:
4955
handleException(res)
5056
res
5157
else:
5258
res
5359

60+
# is there any ways to reduce the repetition? simple template won't work
61+
template callMagic*(obj: PyObject, methodName: untyped, handleExcp=false): PyObject =
62+
let fun = obj.getFun(methodName, handleExcp)
63+
let res = fun(obj)
64+
bind checkExcAndRet
65+
res.checkExcAndRet handleExcp
66+
5467

5568
template callMagic*(obj: PyObject, methodName: untyped, arg1: PyObject, handleExcp=false): PyObject =
5669
let fun = obj.getFun(methodName, handleExcp)
5770
let res = fun(obj, arg1)
58-
when handleExcp:
59-
if res.isThrownException:
60-
handleException(res)
61-
res
71+
bind checkExcAndRet
72+
res.checkExcAndRet handleExcp
73+
74+
75+
template callInplaceMagic*(obj: PyObject, methodName1: untyped,
76+
arg1: PyObject, handleExcp=false): PyObject =
77+
bind checkTypeNotNil, handleNilFunOfGetFun
78+
bind checkExcAndRet
79+
obj.checkTypeNotNil
80+
var fun = obj.getMagic(methodName1)
81+
if fun.isNil:
82+
pyNotImplemented
6283
else:
63-
res
84+
if fun.isNil:
85+
handleNilFunOfGetFun(obj, methodName1, handleExcp)
86+
else:
87+
let res = fun(obj, arg1)
88+
res.checkExcAndRet handleExcp
6489

6590
template callMagic*(obj: PyObject, methodName: untyped,
6691
arg1, arg2: PyObject, handleExcp=false): PyObject =

Python/neval.nim

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import builtindict
1010
import traceback
1111
import ../Objects/[pyobject, baseBundle, tupleobject, listobject, dictobject,
1212
sliceobject, codeobject, frameobject, funcobject, cellobject,
13-
setobject,
13+
setobject, notimplementedobject,
1414
exceptionsImpl, moduleobject, methodobject]
1515
import ../Utils/utils
1616

@@ -46,17 +46,46 @@ template doUnary(opName: untyped) =
4646
let res = top.callMagic(opName, handleExcp=true)
4747
sSetTop res
4848

49-
macro callInplaceMagic(op1, instr, op2): untyped =
50-
let iMagic = ident 'i' & instr.strVal
49+
macro tryCallInplaceMagic(op1, opName, op2): untyped =
50+
let iMagic = ident 'i' & opName.strVal
5151
quote do:
52-
`op1`.callMagic(`iMagic`, `op2`, handleExcp=true)
52+
`op1`.callInplaceMagic(`iMagic`, `op2`, handleExcp=false)
5353

5454
template doInplace(opName: untyped) =
5555
bind callInplaceMagic
5656
let op2 = sPop()
5757
let op1 = sTop()
58-
let res = op1.callInplaceMagic(opName, op2)
59-
sSetTop res
58+
let res = op1.tryCallInplaceMagic(opName, op2)
59+
if res.isNotImplemented:
60+
var nres = op1.callMagic(opName, op2, handleExcp=true)
61+
if nres.isNotImplemented:
62+
let
63+
opStr{.inject.} = "i" & astToStr(opName)
64+
# PY-DIFF: not iadd, but +=
65+
typ1{.inject.} = op1.pyType.name
66+
typ2{.inject.} = op2.pyType.name
67+
nres = newTypeError(
68+
&"unsupported operand type(s) for '{opStr}': '{typ1}' and '{typ2}'"
69+
)
70+
sSetTop nres
71+
else:
72+
sSetTop nres
73+
let stIdx = lastI - 2
74+
let (opCode, opArg) = f.code.code[stIdx]
75+
var st: OpCode
76+
case opCode
77+
of OpCode.LoadFast: st=StoreFast
78+
of OpCode.LoadGlobal: st=StoreGlobal
79+
of OpCode.LoadAttr: st=StoreAttr
80+
of OpCode.LoadName: st=StoreName
81+
of OpCode.LoadDeref: st=StoreDeref
82+
of {OpCode.LoadClosure, LoadMethod, LoadClassDeref}:
83+
raiseAssert(
84+
&"augumented assignment for {opCode} is not implemented yet")
85+
else: unreachable()
86+
f.code.code.insert (st, opArg), lastI+1
87+
else:
88+
sSetTop res
6089

6190
template doBinary(opName: untyped) =
6291
let op2 = sPop()

0 commit comments

Comments
 (0)