├── Python ├── karaxpython.nim.cfg ├── pyimport.nim ├── python.nim ├── getargs.nim ├── pylifecycle │ ├── exit.nim │ ├── pyruntime_singleton.nim │ ├── utils.nim │ └── builtins.nim ├── bltinmodule │ ├── utils.nim │ ├── compile_eval_exec.nim │ └── unarys.nim ├── builtindict.nim ├── sysmodule_instance.nim ├── main │ └── utils.nim ├── getargs │ ├── paramsMeta.nim │ ├── tovalsBase.nim │ ├── nokw.nim │ ├── optionstr.nim │ ├── tovals.nim │ └── tovalUtils.nim ├── errors.nim ├── sysmodule │ ├── decl.nim │ ├── io.nim │ ├── int_max_str_digits.nim │ ├── initUtils.nim │ ├── attrs.nim │ ├── audit.nim │ ├── hooks.nim │ └── init.nim ├── intrinsics.nim ├── versionInfo.nim ├── neval_helpers.nim ├── jspython.nim ├── coreconfig.nim ├── pythonrun │ ├── pyerr_display.nim │ ├── compile.nim │ ├── pyerr.nim │ └── utils.nim ├── getcopyright.nim ├── bltinmodule_mod.nim ├── getversion.nim ├── sysmodule.nim ├── call.nim ├── pystrtod.nim ├── traceback.nim ├── pyimport │ └── main.nim └── neval_frame.nim ├── tests ├── benchmark │ ├── pass.py │ ├── leak.py │ ├── spin.py │ ├── f_spin.py │ ├── mem.py │ └── fib.py ├── basics │ ├── setitem.py │ ├── loop.py │ ├── import.py │ ├── basic.py │ ├── calc.py │ ├── ifelse.py │ ├── closure.py │ ├── function.py │ └── everything.py ├── asserts │ ├── import.py │ ├── none.py │ ├── descriptors.py │ ├── int.py │ ├── for.py │ ├── ifelse.py │ ├── simpleclass.py │ ├── assert.py │ ├── xfail.py │ ├── factorial.py │ ├── in.py │ ├── fib.py │ ├── comprehension.py │ ├── slice.py │ ├── and.py │ ├── bigint.py │ ├── unpack.py │ ├── function.py │ ├── insertsort.py │ ├── property.py │ ├── raise.py │ ├── tuple.py │ ├── decorators.py │ ├── list.py │ ├── simpleclass2.py │ ├── magic.py │ ├── closure.py │ ├── breakcontinue.py │ ├── quicksort.py │ └── tryexcept.py ├── README.md └── run.sh ├── Objects ├── exceptions │ ├── common_h.nim │ ├── ioerror.nim │ ├── basetok.nim │ ├── utils.nim │ └── sub.nim ├── numobjects │ ├── floatobject_decl.nim │ ├── intobject │ │ ├── bytes_h.nim │ │ ├── README.md │ │ ├── bit_length_util.nim │ │ ├── signbit.nim │ │ ├── bit_length.nim │ │ ├── idxHelpers.nim │ │ ├── floatinfos.nim │ │ ├── shift.nim │ │ ├── utils.nim │ │ ├── fromStrUtils.nim │ │ ├── ops_imp_warn.nim │ │ ├── fromx.nim │ │ └── magicNew.nim │ ├── intobject.nim │ ├── floatobject │ │ ├── decl.nim │ │ └── toval.nim │ ├── helpers.nim │ ├── numobjects_comm.nim │ └── intobject_decl.nim ├── pyobject_apis.nim ├── exceptions.nim ├── bltcommon.nim ├── pyobject_apis │ ├── gc.nim │ ├── typeCheck.nim │ ├── attrsUtils.nim │ ├── strings.nim │ └── io.nim ├── stringobject │ ├── common_h.nim │ ├── internal.nim │ ├── utf8apis.nim │ ├── unicodeapis.nim │ └── codec.nim ├── abstract_without_call.nim ├── abstract.nim ├── baseBundle.nim ├── noneobjectImpl.nim ├── stringobjectImpl.nim ├── typeobject │ ├── apis │ │ ├── common.nim │ │ ├── subtype.nim │ │ └── attrs.nim │ ├── decl.nim │ ├── utils.nim │ ├── default_generics.nim │ ├── getters.nim │ └── wraps.nim ├── cellobject.nim ├── numobjects.nim ├── dictobject │ ├── decl.nim │ └── helpers.nim ├── listobjectImpl.nim ├── noneobject.nim ├── listobject │ ├── bltin_sort.nim │ └── sort.nim ├── bundle.nim ├── boolobject.nim ├── funcobjectImpl.nim ├── notimplementedobject.nim ├── abstract │ ├── call.nim │ ├── dunder.nim │ ├── helpers.nim │ ├── sequence │ │ └── list.nim │ ├── args.nim │ ├── number.nim │ ├── iter.nim │ ├── sequence.nim │ └── op_helpers.nim ├── dictproxyobject.nim ├── moduledefs.nim ├── typeobjectImpl.nim ├── frameobject.nim ├── tupleobject │ └── decl.nim ├── moduleobjectImpl.nim ├── hash_def.nim ├── funcobject.nim ├── tupleobject.nim ├── boolobjectImpl.nim ├── methodobject.nim ├── moduleobject.nim └── descrobjectImpl.nim ├── Modules ├── posixmodule.nim ├── unicodedata │ ├── common_h.nim │ ├── rune_decl.nim │ ├── space.nim │ ├── private │ │ └── consts.nim │ ├── decimalAndSpace.nim │ ├── test_decimalAndSpace.nim │ └── decimal.nim ├── posixmodule │ └── utils.nim ├── getbuildinfo.nim └── os_findExe_patch.nim ├── Include ├── internal │ ├── defines_gil.nim │ ├── str.nim │ ├── pycore_ceval_rec.nim │ ├── defines.nim │ ├── pycore_global_strings.nim │ ├── pycore_initconfig.nim │ ├── pycore_int.nim │ └── pycore_object.nim ├── README.md ├── ceval.nim ├── pymacro.nim ├── cpython │ ├── pyerrors.nim │ ├── critical_section.nim │ └── compile.nim ├── modsupport.nim └── descrobject.nim ├── Utils ├── optres.nim ├── addr0.nim ├── getfuncname.nim ├── castChar.nim ├── rangeLen.nim ├── getplatform.nim ├── destroyPatch.nim ├── clib.nim ├── rtarrays.nim ├── trans_imp.nim ├── macroutils.nim ├── sequtils2.nim ├── rtarrays │ ├── utils.nim │ └── jsArrays.nim ├── npointer_js.nim ├── npointer.nim ├── utils.nim ├── compat_io_os.nim ├── jsdispatch.nim └── sequtils.nim ├── .gitignore ├── .github └── workflows │ ├── ci.yml │ └── playground.yml ├── LICENSE ├── Parser ├── lexerTypes.nim ├── string_parser.nim └── lexer_utils.nim └── npython.nimble /Python/karaxpython.nim.cfg: -------------------------------------------------------------------------------- 1 | -d:karax 2 | -d:js -------------------------------------------------------------------------------- /tests/benchmark/pass.py: -------------------------------------------------------------------------------- 1 | while True: 2 | pass 3 | -------------------------------------------------------------------------------- /tests/basics/setitem.py: -------------------------------------------------------------------------------- 1 | l = [1,2,3] 2 | l[1] = True 3 | print(l[1]) 4 | -------------------------------------------------------------------------------- /Objects/exceptions/common_h.nim: -------------------------------------------------------------------------------- 1 | # to be included 2 | import ../tupleobject/decl -------------------------------------------------------------------------------- /tests/asserts/import.py: -------------------------------------------------------------------------------- 1 | import fib 2 | 3 | assert 4181 == fib.fib(19) 4 | 5 | -------------------------------------------------------------------------------- /tests/basics/loop.py: -------------------------------------------------------------------------------- 1 | i = 1 2 | while i < 10: 3 | print(i) 4 | i = i + 1 5 | -------------------------------------------------------------------------------- /Python/pyimport.nim: -------------------------------------------------------------------------------- 1 | 2 | import ../Utils/trans_imp 3 | impExp pyimport, 4 | main 5 | -------------------------------------------------------------------------------- /tests/basics/import.py: -------------------------------------------------------------------------------- 1 | import function 2 | 3 | 4 | print(function.foobar(1,2)) 5 | -------------------------------------------------------------------------------- /tests/benchmark/leak.py: -------------------------------------------------------------------------------- 1 | while True: 2 | i = 1 3 | b = "sdf" 4 | c = [i, b] 5 | -------------------------------------------------------------------------------- /Modules/posixmodule.nim: -------------------------------------------------------------------------------- 1 | 2 | import ../Utils/trans_imp 3 | impExp posixmodule, 4 | utils 5 | -------------------------------------------------------------------------------- /Objects/numobjects/floatobject_decl.nim: -------------------------------------------------------------------------------- 1 | 2 | import ./floatobject/decl 3 | export decl 4 | 5 | -------------------------------------------------------------------------------- /Python/python.nim: -------------------------------------------------------------------------------- 1 | when defined(js): 2 | include jspython 3 | else: 4 | include cpython 5 | -------------------------------------------------------------------------------- /tests/asserts/none.py: -------------------------------------------------------------------------------- 1 | assert None == None 2 | assert None != 1 3 | assert None == print("ok") 4 | -------------------------------------------------------------------------------- /Include/internal/defines_gil.nim: -------------------------------------------------------------------------------- 1 | 2 | const SingleThread* = defined(js) or not compileOption"threads" 3 | -------------------------------------------------------------------------------- /Objects/numobjects/intobject/bytes_h.nim: -------------------------------------------------------------------------------- 1 | # to be included 2 | import ../../[ 3 | byteobjects 4 | ] 5 | -------------------------------------------------------------------------------- /Objects/pyobject_apis.nim: -------------------------------------------------------------------------------- 1 | 2 | import ../Utils/trans_imp 3 | impExp pyobject_apis, 4 | attrs, strings 5 | -------------------------------------------------------------------------------- /tests/asserts/descriptors.py: -------------------------------------------------------------------------------- 1 | assert list(list.__iter__.__get__([1, 2])())[0] == 1 2 | 3 | print("ok") 4 | -------------------------------------------------------------------------------- /Objects/exceptions.nim: -------------------------------------------------------------------------------- 1 | 2 | import ../Utils/trans_imp 3 | 4 | impExp exceptions, 5 | base, sub, baseapi 6 | -------------------------------------------------------------------------------- /Python/getargs.nim: -------------------------------------------------------------------------------- 1 | 2 | 3 | import ../Utils/trans_imp 4 | impExp getargs, 5 | nokw, vargs 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /tests/asserts/int.py: -------------------------------------------------------------------------------- 1 | assert int(1) == 1 2 | assert int("0") == 0 3 | assert int(-14.5) == -14 4 | print("ok") 5 | -------------------------------------------------------------------------------- /tests/benchmark/spin.py: -------------------------------------------------------------------------------- 1 | def foo(): 2 | i = 0 3 | while i < 10000000: 4 | i = i + 1 5 | foo() 6 | -------------------------------------------------------------------------------- /Objects/bltcommon.nim: -------------------------------------------------------------------------------- 1 | import ../Python/getargs/nokw 2 | import ./dictobject/decl 3 | export nokw, PyDictObject 4 | -------------------------------------------------------------------------------- /tests/basics/basic.py: -------------------------------------------------------------------------------- 1 | # basic sanity check without fancy stuff 2 | a = 2 3 | 4 | b = 3 ** a - 1 5 | print(a + b) 6 | -------------------------------------------------------------------------------- /Modules/unicodedata/common_h.nim: -------------------------------------------------------------------------------- 1 | # to be included 2 | import ../../Objects/[ 3 | pyobjectBase, 4 | stringobject, 5 | ] -------------------------------------------------------------------------------- /Objects/numobjects/intobject/README.md: -------------------------------------------------------------------------------- 1 | We split ./frexp to break cyclic `import` as intobject_decl requires `toFloat` 2 | -------------------------------------------------------------------------------- /Objects/pyobject_apis/gc.nim: -------------------------------------------------------------------------------- 1 | 2 | template Py_SetImmortal*(x) = discard 3 | ## `_Py_SetImmortal`, just placeholder now -------------------------------------------------------------------------------- /Objects/stringobject/common_h.nim: -------------------------------------------------------------------------------- 1 | # to be included 2 | import ../[ 3 | pyobjectBase, exceptions, 4 | stringobject, 5 | ] -------------------------------------------------------------------------------- /Modules/unicodedata/rune_decl.nim: -------------------------------------------------------------------------------- 1 | ## used if no need to `include ../common_h` 2 | from std/unicode import Rune 3 | export Rune -------------------------------------------------------------------------------- /tests/benchmark/f_spin.py: -------------------------------------------------------------------------------- 1 | def foo(): 2 | f = 0.0 3 | while f < 10000000.0: 4 | f = f + 1.0 5 | 6 | foo() 7 | -------------------------------------------------------------------------------- /Python/pylifecycle/exit.nim: -------------------------------------------------------------------------------- 1 | 2 | proc Py_Exit*(sts: int) {.noReturn.}= 3 | var sts = sts 4 | #TODO:Py_Finalize 5 | quit sts 6 | -------------------------------------------------------------------------------- /Objects/abstract_without_call.nim: -------------------------------------------------------------------------------- 1 | 2 | import ../Utils/trans_imp 3 | impExp abstract, 4 | args, 5 | iter, number, sequence, dunder 6 | -------------------------------------------------------------------------------- /tests/asserts/for.py: -------------------------------------------------------------------------------- 1 | j = 0 2 | for i in [1,2,3]: 3 | j = j + i 4 | 5 | 6 | assert j == 6 7 | print("ok") 8 | 9 | 10 | -------------------------------------------------------------------------------- /tests/asserts/ifelse.py: -------------------------------------------------------------------------------- 1 | a = 1 2 | 3 | 4 | if a: 5 | assert 5 6 | else: 7 | assert False 8 | 9 | 10 | print("ok") 11 | -------------------------------------------------------------------------------- /tests/asserts/simpleclass.py: -------------------------------------------------------------------------------- 1 | A = type("A", (), {"x":1}) 2 | a = A() 3 | assert a.x == 1 4 | a.y = 2 5 | assert a.y == 2 6 | print("ok") 7 | -------------------------------------------------------------------------------- /Objects/numobjects/intobject.nim: -------------------------------------------------------------------------------- 1 | 2 | import ../../Utils/trans_imp 3 | impExp intobject, 4 | decl, ops_imp_warn, idxHelpers, bit_length, fromx 5 | -------------------------------------------------------------------------------- /Objects/stringobject/internal.nim: -------------------------------------------------------------------------------- 1 | 2 | import ../stringobject 3 | 4 | proc PyUnicode_InternMortal*(s: PyStrObject) = discard #TODO:str-internal 5 | -------------------------------------------------------------------------------- /Python/pylifecycle/pyruntime_singleton.nim: -------------------------------------------------------------------------------- 1 | 2 | var PyRuntime*{.threadVar.}: tuple[signals: 3 | tuple[unhandled_keyboard_interrupt: int]] 4 | -------------------------------------------------------------------------------- /tests/basics/calc.py: -------------------------------------------------------------------------------- 1 | # basic calclation without fancy stuff 2 | a = 1 + 2 - 3 * 4 / 5 3 | b = a ** 2 % 4 4 | c = a // b 5 | 6 | print(a, b, c) 7 | -------------------------------------------------------------------------------- /Utils/optres.nim: -------------------------------------------------------------------------------- 1 | 2 | type GetItemRes*{.pure.} = enum 3 | ## order value is the same as Python's 4 | Error = -1 5 | Missing = 0 6 | Get = 1 7 | -------------------------------------------------------------------------------- /Objects/abstract.nim: -------------------------------------------------------------------------------- 1 | 2 | import ./abstract_without_call 3 | export abstract_without_call 4 | 5 | import ./abstract/call as callLib 6 | export callLib 7 | -------------------------------------------------------------------------------- /tests/asserts/assert.py: -------------------------------------------------------------------------------- 1 | import xfail 2 | 3 | assert True 4 | def foo(): 5 | assert False 6 | xfail.xfail(foo, AssertionError) 7 | 8 | print("ok") 9 | -------------------------------------------------------------------------------- /tests/benchmark/mem.py: -------------------------------------------------------------------------------- 1 | i = 0 2 | l = [] 3 | while i < 4000000: 4 | i = i + 1 5 | l.append(i+0.0) 6 | 7 | while True: 8 | pass 9 | 10 | -------------------------------------------------------------------------------- /Objects/exceptions/ioerror.nim: -------------------------------------------------------------------------------- 1 | 2 | import ../stringobject 3 | import ./sub 4 | proc newIOError*(e: ref IOError): PyIOErrorObject = 5 | newIOError newPyAscii e.msg 6 | -------------------------------------------------------------------------------- /tests/asserts/xfail.py: -------------------------------------------------------------------------------- 1 | def xfail(fun, excp): 2 | flag = False 3 | try: 4 | fun() 5 | except excp: 6 | flag = True 7 | 8 | assert flag 9 | -------------------------------------------------------------------------------- /Include/internal/str.nim: -------------------------------------------------------------------------------- 1 | 2 | type 3 | Str* = string 4 | Char* = typeof (var s: Str; s[0]) 5 | 6 | const StrEmpty* = default Str 7 | proc isEmpty*(s: Str): bool = s=="" 8 | -------------------------------------------------------------------------------- /Objects/baseBundle.nim: -------------------------------------------------------------------------------- 1 | import numobjects, exceptions, noneobject, stringobject, boolobject, bltcommon 2 | export numobjects, exceptions, noneobject, stringobject, boolobject, bltcommon 3 | -------------------------------------------------------------------------------- /tests/asserts/factorial.py: -------------------------------------------------------------------------------- 1 | def factorial(x): 2 | if x == 0: 3 | return 1 4 | return x * factorial(x-1) 5 | 6 | 7 | assert factorial(10) == 3628800 8 | print("ok") 9 | -------------------------------------------------------------------------------- /tests/benchmark/fib.py: -------------------------------------------------------------------------------- 1 | def fib(x): 2 | if x == 0: 3 | return 0 4 | if x == 1: 5 | return 1 6 | return fib(x-1) + fib(x-2) 7 | 8 | 9 | fib(28) 10 | -------------------------------------------------------------------------------- /Utils/addr0.nim: -------------------------------------------------------------------------------- 1 | 2 | proc addr0*[T](x: T): ptr{.inline.} = 3 | # in case s.items is empty, XXX:assuming cap is alloc 4 | {.push boundChecks: off.} 5 | return x[0].addr 6 | {.pop.} 7 | -------------------------------------------------------------------------------- /Objects/pyobject_apis/typeCheck.nim: -------------------------------------------------------------------------------- 1 | 2 | 3 | import ../pyobject 4 | proc ofPyCallable*(x: PyObject): bool = 5 | ## PyCallable_Check 6 | if x.isNil: return 7 | not x.getMagic(call).isNil 8 | -------------------------------------------------------------------------------- /tests/asserts/in.py: -------------------------------------------------------------------------------- 1 | l = [1,2,3] 2 | assert 1 in l 3 | assert 5 not in l 4 | 5 | d = {1:2} 6 | assert 1 in d 7 | assert 2 not in d 8 | d[2] = d 9 | assert 2 in d 10 | 11 | print("ok") 12 | -------------------------------------------------------------------------------- /Python/bltinmodule/utils.nim: -------------------------------------------------------------------------------- 1 | 2 | template regfunc*(f) = 3 | registerBltinFunction astToStr(f), `builtin f` 4 | 5 | template regobj*(f) = 6 | registerBltinObject astToStr(f), `py f ObjectType` 7 | -------------------------------------------------------------------------------- /tests/basics/ifelse.py: -------------------------------------------------------------------------------- 1 | a = 0 2 | if a: 3 | x = 2 4 | else: 5 | x = 1 6 | 7 | print(x) 8 | 9 | 10 | if not False: 11 | print("hello") 12 | 13 | if 1 <= 1: 14 | print("world") 15 | -------------------------------------------------------------------------------- /tests/basics/closure.py: -------------------------------------------------------------------------------- 1 | def outer(x): 2 | def inner(y): 3 | return x + y 4 | return inner 5 | 6 | f = outer(10) 7 | assert f(21) == 31 8 | 9 | g = outer(50) 10 | assert g(31) == 81 11 | -------------------------------------------------------------------------------- /Include/README.md: -------------------------------------------------------------------------------- 1 | In spite of this directory being named `Include` 2 | (which is only to keep consistent with CPython's structure), 3 | `*.nim` here are not for `include`, but regular modules to be `import`-ed. 4 | -------------------------------------------------------------------------------- /tests/README.md: -------------------------------------------------------------------------------- 1 | # NPython test sets 2 | 3 | * `asserts`, tests containing asserts. Use `run.sh` to run all tests. 4 | * `basics`, basic sanity check, run by hand 5 | * `benchmark`, some primitive benchmark scripts 6 | -------------------------------------------------------------------------------- /tests/asserts/fib.py: -------------------------------------------------------------------------------- 1 | def fib(x): 2 | if x == 0: 3 | return 0 4 | if x == 1: 5 | return 1 6 | return fib(x-1) + fib(x-2) 7 | 8 | 9 | assert fib(20) == 6765 10 | print("ok") 11 | -------------------------------------------------------------------------------- /Objects/noneobjectImpl.nim: -------------------------------------------------------------------------------- 1 | {.used.} 2 | import ./noneobject 3 | import ./[pyobject, exceptions] 4 | import ./bltcommon; export bltcommon 5 | 6 | methodMacroTmpl(None) 7 | 8 | implNoneMagic New(_: PyObject): pyNone 9 | -------------------------------------------------------------------------------- /Objects/stringobjectImpl.nim: -------------------------------------------------------------------------------- 1 | 2 | import ./stringobject 3 | import ../Utils/trans_imp 4 | export stringobject 5 | impExp stringobject, 6 | utf8apis, internal, meth, unicodeapis 7 | # XXX: import codec causes rec-dep 8 | 9 | -------------------------------------------------------------------------------- /tests/asserts/comprehension.py: -------------------------------------------------------------------------------- 1 | import xfail 2 | l = [i for i in range(10)] 3 | def foo(): 4 | print(i) 5 | 6 | xfail.xfail(foo, NameError) 7 | 8 | for i in range(10): 9 | assert l[i] == i 10 | print("ok") 11 | -------------------------------------------------------------------------------- /Utils/getfuncname.nim: -------------------------------------------------------------------------------- 1 | 2 | template instantiationFuncname*: cstring = 3 | ## `__func__` 4 | when defined(js): 5 | var res{.importjs: "arguments.callee.name".}: cstring 6 | res 7 | else: 8 | getFrame().filename 9 | -------------------------------------------------------------------------------- /Objects/typeobject/apis/common.nim: -------------------------------------------------------------------------------- 1 | 2 | import ../../[ 3 | pyobjectBase, 4 | tupleobjectImpl, 5 | exceptions, stringobject, 6 | ] 7 | 8 | export pyobjectBase, 9 | tupleobjectImpl, 10 | exceptions, stringobject 11 | 12 | -------------------------------------------------------------------------------- /tests/asserts/slice.py: -------------------------------------------------------------------------------- 1 | l = list(range(10)) 2 | 3 | assert l[::-1][0] == 9 4 | assert l[1:5:2][1] == 3 5 | for i in range(10): 6 | assert l[:][i] == i 7 | assert id(l) != id(l[:]) 8 | assert len(l[:9:-1]) == 0 9 | print("ok") 10 | -------------------------------------------------------------------------------- /tests/run.sh: -------------------------------------------------------------------------------- 1 | echo "tryexcept.py is expected to fail" 2 | echo "fib.py and import.py (imports fib.py) are expected to be slow" 3 | PYTHON=../bin/npython 4 | for fname in ./asserts/*.py; do 5 | echo $fname 6 | $PYTHON $fname 7 | done 8 | -------------------------------------------------------------------------------- /Objects/cellobject.nim: -------------------------------------------------------------------------------- 1 | import pyobject 2 | 3 | 4 | declarePyType Cell(tpToken): 5 | refObj: PyObject # might be nil 6 | 7 | proc newPyCell*(content: PyObject): PyCellObject = 8 | result = newPyCellSimple() 9 | result.refObj = content 10 | -------------------------------------------------------------------------------- /Python/builtindict.nim: -------------------------------------------------------------------------------- 1 | import ../Objects/dictobject 2 | 3 | let bltinDict* = newPyDict() ## inner 4 | 5 | proc PyEval_GetBuiltins*: PyDictObject = 6 | ## `ceval:_PyEval_GetBuiltins` 7 | ## 8 | ## returns builtins dict 9 | bltinDict 10 | -------------------------------------------------------------------------------- /tests/asserts/and.py: -------------------------------------------------------------------------------- 1 | a=1 2 | b=2 3 | c=3 4 | d=True 5 | flag = False 6 | if a and b and c and d: 7 | flag = True 8 | assert flag 9 | 10 | flag = False 11 | if (0 and False) or a: 12 | flag = True 13 | assert flag 14 | 15 | print("ok") 16 | -------------------------------------------------------------------------------- /tests/basics/function.py: -------------------------------------------------------------------------------- 1 | 2 | def foo(x): 3 | print(-x) 4 | 5 | def bar(): 6 | foo(2) 7 | 8 | def foobar(x,y): 9 | z = 2 10 | return x+y+2 / z 11 | 12 | foo(1) 13 | # some ccomment 14 | bar() 15 | 16 | print(foobar(1, 4)) 17 | -------------------------------------------------------------------------------- /Objects/numobjects.nim: -------------------------------------------------------------------------------- 1 | 2 | import ./numobjects/[ 3 | intobject, intobjectImpl, floatobject, numobjects_comm_with_warn, 4 | ] 5 | export intobject, intobjectImpl, floatobject, 6 | numobjects_comm.newPyFloat, PyNumber_Index 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Python/sysmodule_instance.nim: -------------------------------------------------------------------------------- 1 | ## workaround, currently assume only one `sys.modules` exists 2 | 3 | 4 | import ./sysmodule/decl 5 | export decl 6 | 7 | var sys*: PySysModuleObject ## unstable 8 | # will be init via PySys_Create in pyInit 9 | 10 | -------------------------------------------------------------------------------- /Objects/dictobject/decl.nim: -------------------------------------------------------------------------------- 1 | 2 | import std/tables 3 | import ../pyobject 4 | # currently not ordered 5 | # nim ordered table has O(n) delete time 6 | # todo: implement an ordered dict 7 | declarePyType Dict(tpToken, reprLock, mutable): 8 | table: Table[PyObject, PyObject] 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | trash/ 2 | 3 | .gdb_history 4 | *.txt 5 | # nimpretty backup file 6 | *.backup 7 | 8 | # vim session 9 | /.session 10 | 11 | nimcache/ 12 | 13 | ./Objects/test.nim 14 | 15 | __pycache__/ 16 | /bin/ 17 | /app.html 18 | /app.js 19 | /npython.html 20 | /npython.js -------------------------------------------------------------------------------- /Include/internal/pycore_ceval_rec.nim: -------------------------------------------------------------------------------- 1 | 2 | template withNoRecusiveCallOrRetE*(where: cstring; body) = body 3 | #TODO:rec 4 | #[ 5 | bind Py_EnterRecursiveCall, Py_LeaveRecursiveCall, retIfExc 6 | retIfExc Py_EnterRecursiveCall(where) 7 | body 8 | Py_LeaveRecursiveCall() 9 | ]# -------------------------------------------------------------------------------- /Objects/typeobject/decl.nim: -------------------------------------------------------------------------------- 1 | 2 | import ../pyobjectBase 3 | 4 | let pyTypeObjectType* = newPyType[PyTypeObject]("type") 5 | # NOTE: 6 | #[ 7 | type.__base__ is object 8 | type(type) is type 9 | object.__base__ is None 10 | ]# 11 | pyTypeObjectType.kind = PyTypeToken.Type 12 | -------------------------------------------------------------------------------- /Objects/listobjectImpl.nim: -------------------------------------------------------------------------------- 1 | 2 | import ./[ 3 | pyobject, 4 | listobject, 5 | ] 6 | export listobject 7 | 8 | import ../Utils/trans_imp 9 | impExp listobject, 10 | bltin_sort 11 | 12 | methodMacroTmpl(List) 13 | registerBltinMethod pyListObjectType, "sort", builtin_sort 14 | -------------------------------------------------------------------------------- /Utils/castChar.nim: -------------------------------------------------------------------------------- 1 | 2 | from std/unicode import Rune 3 | when defined(js): 4 | template castChar*(i: SomeInteger): char = cast[char](i and 255) 5 | template castChar*(i: Rune): char = char cast[uint8](i) 6 | else: 7 | template castChar*(i: SomeInteger|Rune): char = cast[char](i) 8 | -------------------------------------------------------------------------------- /tests/asserts/bigint.py: -------------------------------------------------------------------------------- 1 | assert 100000000000 ** 10 == 100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 2 | 3 | a = 1000000000000 4 | assert str(a) == "1000000000000" 5 | assert str(a * a) == "1000000000000000000000000" 6 | 7 | print("ok") 8 | -------------------------------------------------------------------------------- /Include/ceval.nim: -------------------------------------------------------------------------------- 1 | 2 | import ./internal/[ 3 | defines_gil, #pycore_pystate, #TODO:tstate 4 | ] 5 | 6 | 7 | when SingleThread: 8 | template pyAllowThreads*(body) = body 9 | else: 10 | template pyAllowThreads*(body) = 11 | #Py_BEGIN_ALLOW_THREADS 12 | body 13 | #Py_END_ALLOW_THREADS 14 | 15 | -------------------------------------------------------------------------------- /Python/main/utils.nim: -------------------------------------------------------------------------------- 1 | 2 | 3 | import ../getversion 4 | import ../../Utils/[getplatform, ] 5 | proc getVersionString*(verbose=false): string = 6 | result = "NPython " 7 | if not verbose: 8 | result.add Version 9 | return 10 | result.add Py_GetVersion() 11 | result.add " on " 12 | result.add PLATFORM 13 | -------------------------------------------------------------------------------- /Include/internal/defines.nim: -------------------------------------------------------------------------------- 1 | import ./defines_gil 2 | const 3 | Py_BUILD_CORE* = appType != "lib" 4 | Py_BUILD_CORE_MODULE* = Py_BUILD_CORE #TODO:defined right? 5 | 6 | Py_DEBUG* = defined(debug) 7 | 8 | Py_GIL_DISABLED* = not SingleThread ## CPython's no gil means lock is needed 9 | 10 | MS_WINDOWS* = defined(windows) 11 | -------------------------------------------------------------------------------- /tests/asserts/unpack.py: -------------------------------------------------------------------------------- 1 | import xfail 2 | 3 | x, y = 1, 2 4 | 5 | assert x == 1 6 | 7 | def foo(): 8 | return True, False 9 | 10 | t, f = foo() 11 | 12 | assert t 13 | assert not f 14 | 15 | a, b, c, d = range(4) 16 | 17 | assert a < d 18 | 19 | def fail(): 20 | x,y = range(1) 21 | 22 | xfail.xfail(fail, ValueError) 23 | 24 | print("ok") 25 | -------------------------------------------------------------------------------- /Objects/noneobject.nim: -------------------------------------------------------------------------------- 1 | import pyobject 2 | import ./stringobject 3 | 4 | declarePyType None(tpToken): 5 | discard 6 | 7 | let pyNone* = newPyNoneSimple() ## singleton 8 | 9 | proc isPyNone*(o: PyObject): bool = o == pyNone 10 | 11 | const sNone = "None" 12 | method `$`*(_: PyNoneObject): string = sNone 13 | 14 | implNoneMagic repr: newPyAscii sNone 15 | 16 | -------------------------------------------------------------------------------- /tests/asserts/function.py: -------------------------------------------------------------------------------- 1 | import xfail 2 | 3 | 4 | def cmp(a, b): 5 | return a < b 6 | 7 | 8 | assert cmp(1, 2) 9 | assert not cmp(5, 1) 10 | 11 | 12 | def more_arg(): 13 | cmp(2, 1, 3) 14 | 15 | 16 | xfail.xfail(more_arg, TypeError) 17 | 18 | 19 | def less_arg(): 20 | cmp() 21 | 22 | 23 | xfail.xfail(less_arg, TypeError) 24 | 25 | print("ok") 26 | -------------------------------------------------------------------------------- /Objects/numobjects/intobject/bit_length_util.nim: -------------------------------------------------------------------------------- 1 | 2 | import std/bitops 3 | export popcount 4 | const BitPerByte = 8 5 | proc bit_length*(self: SomeInteger): int = 6 | when defined(noUndefinedBitOpts): 7 | sizeof(x) * BitPerByte bitops.countLeadingZeroBits x 8 | else: 9 | 1 + fastLog2( 10 | when self is SomeSignedInt: abs(self) 11 | else: self 12 | ) 13 | -------------------------------------------------------------------------------- /Objects/numobjects/floatobject/decl.nim: -------------------------------------------------------------------------------- 1 | import ../../pyobject 2 | declarePyType Float(tpToken): 3 | v: float 4 | 5 | method `$`*(f: PyFloatObject): string{.raises: [].} = 6 | $f.v 7 | 8 | 9 | proc newPyFloat*(v: float): PyFloatObject = 10 | ## `PyFloat_FromDouble` 11 | result = newPyFloatSimple() 12 | result.v = v 13 | 14 | proc newPyFloat*(v: PyFloatObject): PyFloatObject = v 15 | -------------------------------------------------------------------------------- /Python/getargs/paramsMeta.nim: -------------------------------------------------------------------------------- 1 | ## params' metadata 2 | template convertVia*(f: typed) {.pragma.} ## `converter` is reserved word in Nim 3 | template startKwOnly* {.pragma.} ## Python's `*` in param list, e.g. 4 | ## for Python's `a: int, *, b=1`, 5 | ## in npython you shall write as `a: int, b{.startKwOnly.}=1` 6 | 7 | template AsPyParam*(s: string) {.pragma.} ## Python's `as` in clinic input 8 | -------------------------------------------------------------------------------- /Python/errors.nim: -------------------------------------------------------------------------------- 1 | 2 | import ../Objects/[ 3 | #pyobjectBase, 4 | exceptions, stringobject, 5 | ] 6 | import ../Utils/compat 7 | 8 | proc PyErr_BadArgument*: PyTypeErrorObject = 9 | newTypeError newPyAscii"bad argument type for built-in operation" 10 | 11 | proc PyErr_FormatUnraisable*(msg: PyStrObject){.raises: [].} = 12 | #TODO:sys.unraisablehook 13 | errEchoCompatNoRaise $msg 14 | 15 | -------------------------------------------------------------------------------- /Utils/rangeLen.nim: -------------------------------------------------------------------------------- 1 | # nimpylib src/pylib/builtins/private/mathutils.nim 2 | # @aca732bcf09561a6ef3a6d4957af55ec854b5045 3 | func iPosCeil*[I: SomeInteger](x: float): I = 4 | ## I(ceil(x)) if x > 0 else 0 5 | if x > 0: 6 | let more = (x - float(I(x)) > 0) 7 | I(x) + I(more) 8 | else: I(0) 9 | 10 | func rangeLen*[I](start, stop, step: I): I = 11 | iPosCeil[I]((stop - start) / step) 12 | -------------------------------------------------------------------------------- /tests/asserts/insertsort.py: -------------------------------------------------------------------------------- 1 | array = [13, 5, 6, 12, 1, 14, 3, 2, -2, 11, 8, 0, 9, 4, 10, -1] 2 | 3 | 4 | for i in range(1, len(array)): 5 | j = i 6 | var = array[j] 7 | while j != 0 and var < array[j-1]: 8 | array[j] = array[j-1] 9 | j = j - 1 10 | array[j] = var 11 | 12 | for i in range(1, len(array)): 13 | assert array[i-1] < array[i] 14 | 15 | print("ok") 16 | -------------------------------------------------------------------------------- /tests/asserts/property.py: -------------------------------------------------------------------------------- 1 | import xfail 2 | 3 | 4 | class A: 5 | def __init__(self): 6 | self.x = 1 7 | 8 | @property 9 | def y(self): 10 | return self.x 11 | 12 | 13 | a = A() 14 | assert a.x == 1 15 | assert a.y == 1 16 | 17 | 18 | def foo(): 19 | A.x 20 | 21 | 22 | xfail.xfail(foo, AttributeError) 23 | 24 | 25 | assert A.y.__get__(a) == 1 26 | 27 | 28 | print("ok") 29 | -------------------------------------------------------------------------------- /Python/sysmodule/decl.nim: -------------------------------------------------------------------------------- 1 | 2 | 3 | import ../../Objects/[ 4 | pyobject, 5 | moduleobject, 6 | dictobject, 7 | listobject, 8 | ] 9 | export name, `name=` 10 | declarePyType SysModule(base(Module)): 11 | modules{.member.}: PyDictObject # dict[str, Module] 12 | path{.member.}: PyListObject # list[str] 13 | argv{.member.}: PyListObject # list[str] 14 | orig_argv{.member.}: PyListObject # list[str] 15 | 16 | -------------------------------------------------------------------------------- /tests/asserts/raise.py: -------------------------------------------------------------------------------- 1 | import xfail 2 | 3 | 4 | def raise_name_error(): 5 | raise NameError 6 | 7 | 8 | def empty_raise(): 9 | raise 10 | 11 | 12 | def reraise(): 13 | try: 14 | 1//0 15 | except ZeroDivisionError: 16 | raise 17 | 18 | 19 | xfail.xfail(raise_name_error, NameError) 20 | xfail.xfail(empty_raise, RuntimeError) 21 | xfail.xfail(reraise, ZeroDivisionError) 22 | 23 | 24 | print("ok") 25 | -------------------------------------------------------------------------------- /tests/asserts/tuple.py: -------------------------------------------------------------------------------- 1 | a = True 2 | t = (1, 2, 4, a) 3 | 4 | assert t[1] == 2 5 | assert len(t) == 4 6 | assert t[::2][-1] == 4 7 | assert len(()) == 0 8 | 9 | t = 1, 10 | assert t[0] == 1 11 | 12 | t = 3, 2 13 | assert t[0] == 3 14 | assert t[1] == 2 15 | 16 | 17 | def foo(): 18 | return 1, 2 19 | 20 | 21 | assert foo() == (1, 2) 22 | 23 | assert (1, 2) != [1, 2] 24 | 25 | assert tuple([3, 4]) == (3, 4) 26 | 27 | print("ok") 28 | -------------------------------------------------------------------------------- /Python/sysmodule/io.nim: -------------------------------------------------------------------------------- 1 | 2 | import ../../Objects/[pyobjectBase, stringobject,] 3 | import ../../Utils/compat 4 | import ./attrs 5 | 6 | proc PySys_EchoStderr*(s: string) = 7 | #TODO:sys.stderr 8 | assert: 9 | var unused: PyObject 10 | var exists: bool 11 | discard PySys_GetOptionalAttr(newPyAscii"stderr", unused, exists) 12 | not exists 13 | do: "sys.stderr shall be not set currently" 14 | 15 | errEchoCompatNoRaise s 16 | -------------------------------------------------------------------------------- /Python/intrinsics.nim: -------------------------------------------------------------------------------- 1 | 2 | import ./sysmodule/attrs 3 | import ./call 4 | import ../Objects/[ 5 | pyobject, exceptions, 6 | ] 7 | import ../Include/internal/pycore_global_strings 8 | 9 | # ******* Unary functions ******* 10 | proc print_expr*(value: PyObject; res: var PyObject): PyBaseErrorObject = 11 | var hook: PyObject 12 | retIfExc PySys_GetAttr(pyID(displayhook), hook) 13 | res = call(hook, value) 14 | retIfExc res 15 | res = nil 16 | -------------------------------------------------------------------------------- /Objects/dictobject/helpers.nim: -------------------------------------------------------------------------------- 1 | 2 | import ../[pyobject, exceptions] 3 | 4 | template keyError*(other: PyObject): PyBaseErrorObject = 5 | let repr = other.pyType.magicMethods.repr(other) 6 | if repr.isThrownException: 7 | PyBaseErrorObject repr 8 | else: 9 | PyBaseErrorObject newKeyError PyStrObject(repr) 10 | 11 | template handleBadHash*(res; body){.dirty.} = 12 | template setRes(e) = res = e 13 | handleHashExc setRes: 14 | body 15 | -------------------------------------------------------------------------------- /Objects/listobject/bltin_sort.nim: -------------------------------------------------------------------------------- 1 | 2 | import ../[ 3 | pyobject, 4 | noneobject, 5 | exceptions, 6 | listobject, 7 | ] 8 | import ./sort 9 | import ../../Python/getargs/[dispatch, paramsMeta,] 10 | 11 | 12 | proc list_sort_impl(self: PyObject; keyfunc{.startKwOnly.}: PyObject = pyNone, reversed = false): PyObject{. clinicGenMeth(builtin_sort, true) .}= 13 | let self = PyListObject self 14 | retIfExc self.sort(keyfunc, reversed) 15 | pyNone 16 | -------------------------------------------------------------------------------- /Utils/getplatform.nim: -------------------------------------------------------------------------------- 1 | 2 | const PLATFORM* = 3 | when defined(js): 4 | # compiling with `-d:nodejs` means being not runnable on others 5 | when defined(nodejs): "nodejs" 6 | elif defined(deno): "deno" 7 | else: "js" 8 | elif defined(windows): "win32" 9 | elif defined(macosx): "darwin" # hostOS is macosx 10 | else: hostOS ## XXX: not right on all platform. PY-DIFF 11 | ## see nimpylib/nimpylib src/pylib/Lib/sys_impl/genplatform.nim 12 | -------------------------------------------------------------------------------- /tests/asserts/decorators.py: -------------------------------------------------------------------------------- 1 | def foo(f): 2 | def bar(): 3 | return 1 4 | return bar 5 | 6 | 7 | @foo 8 | @foo 9 | def baz(): 10 | return 2 11 | 12 | 13 | assert baz() == 1 14 | 15 | 16 | def foobar(i): 17 | def foo(f): 18 | def bar(): 19 | return i 20 | return bar 21 | return foo 22 | 23 | 24 | @foobar(10) 25 | def baz(): 26 | return 2 27 | 28 | 29 | assert baz() == 10 30 | 31 | print("ok") 32 | -------------------------------------------------------------------------------- /Include/pymacro.nim: -------------------------------------------------------------------------------- 1 | 2 | when declared(WITH_DOC_STRINGS): 3 | template PyDoc_STR(str): untyped = cstring str 4 | else: 5 | template PyDoc_STR(str): untyped = cstring "" 6 | 7 | template PyDoc_STRVAR*(name; str) = 8 | bind PyDoc_STR 9 | const name = PyDoc_STR(str) 10 | 11 | template Py_SIZE_ROUND_UP*(n, a): uint = 12 | ## `_Py_SIZE_ROUND_UP` 13 | ## Round up size "n" to be a multiple of "a". 14 | (uint(n) + uint(a - 1)) and 15 | not uint(a - 1) 16 | 17 | -------------------------------------------------------------------------------- /Python/pylifecycle/utils.nim: -------------------------------------------------------------------------------- 1 | 2 | import ../../Utils/[ 3 | fileio, 4 | ] 5 | 6 | import ../../Objects/[ 7 | stringobject, 8 | ] 9 | import ../coreconfig 10 | 11 | proc Py_FdIsInteractive*(fp: fileio.File, filename: PyStrObject): bool = 12 | if isatty(fileno(fp)): 13 | return true 14 | if not Py_GetConfig().interactive: 15 | return false 16 | return (filename.isNil or 17 | eqAscii(filename, "") or 18 | eqAscii(filename, "???")) 19 | 20 | -------------------------------------------------------------------------------- /Modules/unicodedata/space.nim: -------------------------------------------------------------------------------- 1 | #XXX: 2 | #[As of 2.3.1, isSpace in `std/unicode` checks against 25 kinds of whitespaces, 3 | in which: 4 | 5 | * 0x001C: FILE SEPARATOR 6 | * 0x001D: GROUP SEPARATOR 7 | * 0x001E: RECORD SEPARATOR 8 | * 0x001F: UNIT SEPARATOR 9 | 10 | are not regarded as whitespaces, while CPython thinks they are. 11 | 12 | And std/unicode lacks isSpace for Rune 13 | ]# 14 | import ./private/consts 15 | import ./rune_decl 16 | proc isspace*(r: Rune): bool = r.int in spaces 17 | -------------------------------------------------------------------------------- /tests/asserts/list.py: -------------------------------------------------------------------------------- 1 | l = [1,3.01,2,3,4,5, 1] 2 | 3 | 4 | assert len(l) == 7 5 | 6 | 7 | l.append(False) 8 | 9 | 10 | assert not l[-1] 11 | 12 | 13 | assert l.count(1) == 2 14 | 15 | 16 | assert l.index(3) == 3 17 | 18 | 19 | l.insert(0, 0) 20 | 21 | 22 | assert l[0] == 0 23 | 24 | 25 | l.pop() 26 | 27 | 28 | assert l.pop() == 1 29 | 30 | l[-1] = 100 31 | 32 | 33 | assert l.pop() == 100 34 | 35 | ls2 = [1, 2, 2] 36 | ls2.remove(2) 37 | assert ls2[-1] == 2 38 | 39 | print("ok") 40 | -------------------------------------------------------------------------------- /Python/versionInfo.nim: -------------------------------------------------------------------------------- 1 | 2 | const 3 | Major* = 0 4 | Minor* = 1 5 | Patch* = 1 6 | 7 | PyMajor*{.intdefine.} = 3 8 | PyMinor*{.intdefine.} = 13 9 | PyPatch*{.intdefine.} = 0 10 | 11 | PyReleaseLevel*{.intdefine.} = 0xA 12 | PyReleaseLevelStr*{.strdefine.} = "alpha" 13 | PY_RELEASE_SERIAL*{.intdefine.} = 0 14 | 15 | const sep = '.' 16 | template asVersion(major, minor, patch: int): string = 17 | $major & sep & $minor & sep & $patch 18 | 19 | const 20 | Version* = asVersion(Major, Minor, Patch) 21 | -------------------------------------------------------------------------------- /Python/neval_helpers.nim: -------------------------------------------------------------------------------- 1 | 2 | import ./pythonrun/pyerr 3 | import ../Objects/exceptions 4 | 5 | template orPrintTb*(retRes: PyBaseErrorObject): bool{.dirty.} = 6 | bind PyErr_Print, PyExceptionObject 7 | if retRes.isNil: true 8 | else: 9 | PyErr_Print PyExceptionObject(retRes) 10 | false 11 | 12 | template orPrintTb*(retRes): bool{.dirty.} = 13 | bind PyErr_Print, PyExceptionObject 14 | if retRes.isThrownException: 15 | PyErr_Print PyExceptionObject(retRes) 16 | false 17 | else: 18 | true 19 | -------------------------------------------------------------------------------- /Utils/destroyPatch.nim: -------------------------------------------------------------------------------- 1 | 2 | template sinceNim(ma, mi, pa): bool = 3 | (NimMajor, NimMinor, NimPatch) >= (ma, mi, pa) 4 | const Js = defined(js) 5 | when not Js and sinceNim(2,1,1) or 6 | Js and sinceNim(2,3,2): 7 | template defdestroy*(self; Self; body){.dirty.} = 8 | proc `=destroy`*(self: Self) = body 9 | else: 10 | template defdestroy*(self; Self; body){.dirty.} = 11 | proc `=destroy`*(self: var Self) = body 12 | 13 | 14 | template defdestroy*(Self; body){.dirty.} = 15 | defdestroy self, Self, body 16 | 17 | -------------------------------------------------------------------------------- /tests/asserts/simpleclass2.py: -------------------------------------------------------------------------------- 1 | class A: 2 | def __init__(self): 3 | self.x = 1 4 | 5 | def foo(self, b): 6 | return self.x + b 7 | 8 | 9 | a = A() 10 | assert a.x == 1 11 | assert a.foo(1) == 2 12 | 13 | 14 | def foo(): 15 | x = 1 16 | 17 | class B: 18 | def __init__(self): 19 | self.x = x 20 | return B 21 | 22 | 23 | assert foo()().x == 1 24 | 25 | 26 | class C: 27 | def __init__(self, x): 28 | self.x = x 29 | 30 | c = C(True) 31 | assert c.x 32 | print("ok") 33 | -------------------------------------------------------------------------------- /Objects/bundle.nim: -------------------------------------------------------------------------------- 1 | import baseBundle 2 | import codeobject, dictobject, frameobject, boolobjectImpl, 3 | listobject, moduleobject, methodobject, funcobject, 4 | pyobject, stringobjectImpl, rangeobject, exceptionsImpl, 5 | sliceobject, tupleobjectImpl, cellobject, setobject 6 | export baseBundle 7 | export codeobject, dictobject, frameobject, boolobjectImpl, 8 | listobject, moduleobject, methodobject, funcobject, 9 | pyobject, stringobjectImpl, rangeobject, exceptionsImpl, 10 | sliceobject, tupleobjectImpl, cellobject, setobject 11 | -------------------------------------------------------------------------------- /Python/jspython.nim: -------------------------------------------------------------------------------- 1 | 2 | import ./cpython 3 | import ../Utils/[compat, compat_io_os] 4 | 5 | import std/jsffi except require 6 | 7 | template isMain(b): bool = isMainModule and b 8 | const 9 | nodejs = defined(nodejs) 10 | deno = defined(deno) 11 | dKarax = defined(karax) 12 | when isMain(nodejs or deno): 13 | mayWaitFor main(commandLineParamsCompat()) 14 | else: 15 | when isMain(dKarax): 16 | import ./karaxpython 17 | elif isMainModule: 18 | import ./lifecycle 19 | pyInit(@[]) 20 | mayWaitFor interactiveShell() 21 | -------------------------------------------------------------------------------- /Objects/typeobject/utils.nim: -------------------------------------------------------------------------------- 1 | 2 | import ../[ 3 | pyobjectBase, 4 | stringobject, 5 | noneobject, 6 | ] 7 | import ../../Python/[ 8 | errors, 9 | ] 10 | proc handleDelRes*(call: PyObject){.cdecl, raises: [].} = 11 | ## inner 12 | ## logic in `slot_tp_finalize` 13 | let res = call 14 | if res.isPyNone: return 15 | PyErr_FormatUnraisable newPyAscii"Exception ignored while calling deallocator " #TODO:stack 16 | 17 | var magicNameStrs*: seq[PyStrObject] # inner 18 | for name in magicNames: 19 | magicNameStrs.add newPyStr(name) 20 | -------------------------------------------------------------------------------- /tests/asserts/magic.py: -------------------------------------------------------------------------------- 1 | class A: 2 | 3 | def __init__(self, x): 4 | self.x = x 5 | 6 | def __add__(self, y): 7 | return self.x + y 8 | 9 | def __len__(self): 10 | return 10 11 | 12 | def __iter__(self): 13 | return iter(range(10)) 14 | 15 | def __str__(self): 16 | return "bla" 17 | 18 | 19 | a = A(3) 20 | 21 | assert a + 1 == 4 22 | assert len(a) == 10 23 | 24 | i = 0 25 | for j in a: 26 | assert i == j 27 | i = i + 1 28 | 29 | assert str(a) == "bla" 30 | 31 | print("ok") 32 | -------------------------------------------------------------------------------- /Objects/boolobject.nim: -------------------------------------------------------------------------------- 1 | import pyobject 2 | import ./numobjects/intobject/decl 3 | 4 | declarePyType Bool(tpToken, base(Int)): 5 | b: bool 6 | 7 | proc newPyBoolInner(b: bool): PyBoolObject = 8 | result = newPyBoolSimple() 9 | result.b = b 10 | 11 | 12 | let pyTrueObj* = newPyBoolInner(true) ## singleton 13 | let pyFalseObj* = newPyBoolInner(false) ## singleton 14 | 15 | proc newPyBool*(b: bool): PyBoolObject = 16 | if b: pyTrueObj 17 | else: pyFalseObj 18 | 19 | proc isPyTrue*(self: PyBoolObject): bool = system.`==`(self, pyTrueObj) 20 | -------------------------------------------------------------------------------- /tests/asserts/closure.py: -------------------------------------------------------------------------------- 1 | def get_add(n): 2 | def add(x): 3 | return x + n 4 | return add 5 | 6 | 7 | myadd = get_add(1) 8 | 9 | assert 2 == myadd(1) 10 | 11 | 12 | def foo(): 13 | x = 1 14 | def bar(y): 15 | def baz(): 16 | z = 1 17 | return x + y + z 18 | return baz 19 | return bar(1) 20 | 21 | 22 | assert 3 == foo()() 23 | 24 | 25 | def change(): 26 | x = 1 27 | def bar(): 28 | assert x == 2 29 | x = 2 30 | bar() 31 | 32 | change() 33 | 34 | print("ok") 35 | -------------------------------------------------------------------------------- /Python/getargs/tovalsBase.nim: -------------------------------------------------------------------------------- 1 | 2 | 3 | import ../../Objects/[ 4 | pyobject, 5 | exceptions, 6 | ] 7 | template toval*(a: PyObject, val: var PyObject): PyBaseErrorObject = 8 | val = a 9 | PyBaseErrorObject nil 10 | 11 | template genToVal*(T; fun){.dirty.} = 12 | template toval*(obj; val: var T): PyBaseErrorObject = 13 | bind fun 14 | fun(obj, val) 15 | 16 | template genToValGeneric*(T; CT; GenericT){.dirty.} = 17 | genToVal T, `Py GenericT As CT` 18 | 19 | template genToValGeneric*(T; GenericT){.dirty.} = 20 | genToValGeneric(T, T, GenericT) 21 | -------------------------------------------------------------------------------- /Utils/clib.nim: -------------------------------------------------------------------------------- 1 | 2 | const Js = defined(js) 3 | when Js: 4 | when defined(nodejs): 5 | proc abort*(){.noReturn, importc: "process.abort()".} 6 | elif defined(deno): 7 | proc abort*(){.noReturn, importc: "Deno.abort()".} 8 | else: 9 | proc abort*(){.noReturn, importc: """( 10 | process?process.abort():( 11 | Deno?Deno.abort():( 12 | window.abort() 13 | )) 14 | )""".} 15 | else: 16 | proc abort*(){.noReturn, importc, header: "".} 17 | 18 | when defined(windows): 19 | proc DebugBreak*(){.imporc, header: "".} 20 | -------------------------------------------------------------------------------- /Utils/rtarrays.nim: -------------------------------------------------------------------------------- 1 | 2 | const Js = defined(js) 3 | 4 | when Js: 5 | import ./rtarrays/jsArrays 6 | type RtArray*[T] = JsArray[T] 7 | export jsArrays except JsArray, newJsArray, add 8 | proc initRtArray*[T](x: int): RtArray[T] = newJsArray[T](x) 9 | proc initRtArray*[T](x: openArray[T]): RtArray[T] = newJsArray[T](x) 10 | 11 | else: 12 | import ./rtarrays/rawMem 13 | export rawMem 14 | 15 | when isMainModule: 16 | var arr = initRtArray [ 17 | 1, 2, 3 18 | ] 19 | echo $arr 20 | echo arr[2] 21 | echo arr == initRtArray [1,2,2] 22 | echo @arr == @[1,2,3] 23 | -------------------------------------------------------------------------------- /tests/asserts/breakcontinue.py: -------------------------------------------------------------------------------- 1 | def single(): 2 | for i in range(10): 3 | if i == 5: 4 | break 5 | assert i == 5 6 | 7 | 8 | def double(): 9 | for i in range(10): 10 | for j in range(10): 11 | if i == j and j == 5: 12 | break 13 | if i == j: 14 | assert j == 5 or j == 9 15 | while 1: 16 | break 17 | assert i == 9 18 | 19 | while 0 < i: 20 | i = i - 1 21 | continue 22 | assert i == 0 23 | 24 | 25 | single() 26 | 27 | 28 | double() 29 | 30 | 31 | print("ok") 32 | -------------------------------------------------------------------------------- /Utils/trans_imp.nim: -------------------------------------------------------------------------------- 1 | ## NIMPYLIB: nimpylib/private/trans_imp 2 | ## import & export helper 3 | import std/macros 4 | 5 | macro impExp*(pre; mods: varargs[untyped]) = 6 | ## gen: `import ./pre/[...mods]; export ...mods` 7 | result = newStmtList() 8 | var imp = newNimNode nnkImportStmt 9 | var modsList = newNimNode nnkBracket 10 | for m in mods: modsList.add m 11 | 12 | var impMods = infix(prefix(pre, "./"), "/", modsList) 13 | 14 | imp.add impMods 15 | result.add imp 16 | 17 | var exp = newNimNode nnkExportStmt 18 | for m in mods: 19 | exp.add m 20 | result.add exp 21 | 22 | -------------------------------------------------------------------------------- /Python/sysmodule/int_max_str_digits.nim: -------------------------------------------------------------------------------- 1 | 2 | import ../../Objects/[exceptions, stringobject,] 3 | import ../../Include/internal/pycore_int 4 | 5 | 6 | proc PySys_SetIntMaxStrDigits*(maxdigits: int): PyBaseErrorObject = 7 | if maxdigits != 0 and maxdigits < PY_INT_MAX_STR_DIGITS_THRESHOLD: 8 | return newValueError newPyAscii( 9 | "maxdigits must be >= " & $PY_INT_MAX_STR_DIGITS_THRESHOLD & " or 0 for unlimited" 10 | ) 11 | PyInterpreterState_GET_long_state().max_str_digits = maxdigits 12 | 13 | proc PySys_GetIntMaxStrDigits*: int = PyInterpreterState_GET_long_state().max_str_digits 14 | 15 | -------------------------------------------------------------------------------- /Python/coreconfig.nim: -------------------------------------------------------------------------------- 1 | 2 | import ../Include/internal/str 3 | 4 | type 5 | PyConfig* = object 6 | run_command*, 7 | run_module*, 8 | run_filename*: string 9 | filename*: string 10 | quiet*, verbose*: bool 11 | executable*: string 12 | program_name*: string 13 | module_search_paths*, 14 | argv*, orig_argv* 15 | :seq[Str] 16 | optimization_level*: int 17 | interactive*: bool 18 | site_import*: bool ## if `import site` (no `-S` given) 19 | inspect*: bool 20 | 21 | var pyConfig* = PyConfig(site_import: true) 22 | 23 | proc Py_GetConfig*: PyConfig = pyConfig 24 | -------------------------------------------------------------------------------- /Objects/stringobject/utf8apis.nim: -------------------------------------------------------------------------------- 1 | 2 | include ./common_h 3 | from ../../Python/errors import PyErr_BadArgument 4 | 5 | proc PyUnicode_fromStringAndSize*(u: string, size: int): PyObject = 6 | if size < 0: return newSystemError newPyAscii"Negative size passed to PyUnicode_FromStringAndSize" 7 | if size == 0: return newPyAscii() 8 | #TODO:PyUnicode_DecodeUTF8Stateful 9 | newPyStr u[0.. 0` 13 | if not kw.isNil: return newTypeError newPyStr(funcname)&newPyStr"() takes no keyword arguments" 14 | 15 | template PyArg_NoKw*(funcname){.dirty.} = 16 | bind PyArg_NoKw 17 | PyArg_NoKw funcname, kwargs 18 | -------------------------------------------------------------------------------- /Include/internal/pycore_global_strings.nim: -------------------------------------------------------------------------------- 1 | 2 | import ../../Objects/stringobject 3 | 4 | #TODO:str-internal 5 | template pyIdImpl(s: string): PyStrObject = 6 | bind newPyAscii 7 | newPyAscii s 8 | template pyId*(id): PyStrObject = 9 | ## `_Py_ID` in CPython 10 | bind pyIdImpl 11 | pyIdImpl astToStr id 12 | 13 | const DU = "__" 14 | template pyDUId*(id): PyStrObject = 15 | ## dunder(double underline) Py_ID 16 | bind pyIdImpl, DU 17 | pyIdImpl(DU & astToStr(id) & DU) 18 | 19 | template Py_DECLARE_STR*(name; str: static string) = 20 | bind newPyAscii 21 | when not declared(name): 22 | let name = newPyAscii str 23 | template Py_STR*(name): PyStrObject = name 24 | -------------------------------------------------------------------------------- /Python/getcopyright.nim: -------------------------------------------------------------------------------- 1 | 2 | import std/os 3 | const cprtFp = currentSourcePath() /../ "" /../ "LICENSE" 4 | import std/strutils 5 | proc Py_GetCopyrightImpl(): string = 6 | let all = readFile $cprtFp 7 | var start, stop = false 8 | template addl(L) = 9 | result.add L 10 | for line in all.splitLines(keepEol=true): 11 | if line == "\n": 12 | if start: stop = true 13 | else: start = true 14 | continue 15 | if stop: break 16 | if start: 17 | addl line 18 | result.stripLineEnd 19 | 20 | proc Py_GetCopyright*(): string{.compileTime.} = 21 | Py_GetCopyrightImpl() 22 | #except IOError: 23 | 24 | when isMainModule: 25 | static: echo Py_GetCopyright() 26 | -------------------------------------------------------------------------------- /Python/getargs/optionstr.nim: -------------------------------------------------------------------------------- 1 | 2 | import ../../Objects/[ 3 | pyobjectBase, stringobject, exceptions, noneobject, 4 | ] 5 | import ../../Objects/stringobject/strformat 6 | 7 | proc getOptionalStr*(argname: string; sobj: var PyObject; res: var string): PyBaseErrorObject = 8 | ## to unpack keyword arg, for Optional[str] 9 | ## `res` won't be overwritten if `sobj` is not given as a str object 10 | if sobj.isPyNone: sobj = nil 11 | if not sobj.isNil: 12 | if not sobj.ofPyStrObject: 13 | let s = newPyStr&"{argname} must be None or a string, not {sobj.typeName:.200s}" 14 | retIfExc s 15 | return newTypeError PyStrObject s 16 | (res, _) = sobj.PyStrObject.asUTF8AndSize() 17 | 18 | -------------------------------------------------------------------------------- /Objects/numobjects/intobject/signbit.nim: -------------------------------------------------------------------------------- 1 | 2 | import ./decl 3 | proc setSignNegative*(self: PyIntObject){.inline.} = 4 | self.sign = Negative 5 | 6 | proc negative*(intObj: PyIntObject): bool {. inline .} = 7 | intObj.sign == Negative 8 | 9 | proc zero*(intObj: PyIntObject): bool {. inline .} = 10 | intObj.sign == Zero 11 | 12 | proc positive*(intObj: PyIntObject): bool {. inline .} = 13 | intObj.sign == Positive 14 | 15 | proc flipSign*(intObj: PyIntObject) = 16 | ## `_PyLong_FlipSign` 17 | ## inner 18 | intObj.sign = IntSign(-int(intObj.sign)) 19 | 20 | proc negate*(self: PyIntObject){.inline.} = 21 | ## currently the same as `flipSign`_ as we didn't have small int 22 | self.flipSign 23 | -------------------------------------------------------------------------------- /Python/getargs/tovals.nim: -------------------------------------------------------------------------------- 1 | 2 | import std/strformat 3 | import ./tovalsBase 4 | export tovalsBase 5 | import ../../Objects/[ 6 | pyobject, 7 | exceptions, 8 | numobjects, 9 | stringobject, 10 | boolobjectImpl, 11 | ] 12 | 13 | genToValGeneric(int, Ssize_t, Number) 14 | genToValGeneric(float, double, Float) 15 | 16 | proc converterr(expected: string, arg: PyObject): string = 17 | fmt"must be {expected:.50s}, not {arg.typeName:.50s}" 18 | 19 | proc `handle %s`(x: PyObject, res: var string): PyBaseErrorObject = 20 | if x.ofPyStrObject: 21 | res = PyStrObject(x).asUTF8 22 | return 23 | newTypeError newPyAscii converterr("str", x) 24 | genToVal string, `handle %s` 25 | 26 | genToVal bool, PyObject_IsTrue 27 | -------------------------------------------------------------------------------- /Objects/abstract/call.nim: -------------------------------------------------------------------------------- 1 | 2 | import ../[pyobjectBase, stringobject] 3 | 4 | import ../../Python/call 5 | 6 | proc callMethod*(self: PyObject, name: PyStrObject): PyObject = 7 | ## `PyObject_CallMethodNoArg` 8 | vectorcallMethod(name, [self]) 9 | 10 | proc callMethod*(self: PyObject, name: PyStrObject, arg: PyObject): PyObject = 11 | ## `PyObject_CallMethodOneArg` 12 | assert not arg.isNil 13 | vectorcallMethod(name, [self, arg]) 14 | 15 | proc callMethodArgs*(self: PyObject, name: PyStrObject, args: varargs[PyObject]): PyObject = 16 | ## `PyObject_CallMethodObjArgs` 17 | var s = newSeq[PyObject](args.len+1) 18 | s[0] = self 19 | for i in 1..args.len: 20 | s[i] = args[i-1] 21 | vectorcallMethod(name, s) 22 | -------------------------------------------------------------------------------- /Python/bltinmodule_mod.nim: -------------------------------------------------------------------------------- 1 | 2 | import ./[ 3 | bltinmodule, 4 | builtindict, 5 | coreconfig, 6 | ] 7 | 8 | import ../Objects/[ 9 | pyobject, exceptions, 10 | dictobject, 11 | moduleobjectImpl, 12 | stringobject, 13 | ] 14 | import ../Objects/numobjects/intobject_decl 15 | 16 | declarePyType BuiltinsModule(base(Module)): 17 | discard 18 | 19 | 20 | proc PyBuiltin_Init*(config: PyConfig): PyObject = 21 | let moduObj = PyModule_CreateInitialized(builtins) 22 | retIfExc moduObj 23 | let modu = PyBuiltinsModuleObject moduObj 24 | let dict = PyEval_GetBuiltins() 25 | modu.dict = dict 26 | let debug = newPyInt int config.optimization_level == 0 27 | dict[newPyAscii"__debug__"] = debug 28 | modu 29 | 30 | 31 | -------------------------------------------------------------------------------- /Include/internal/pycore_initconfig.nim: -------------------------------------------------------------------------------- 1 | 2 | import ../../Objects/[pyobjectBase, 3 | listobject, stringobject, 4 | ] 5 | when newPyStr("") is_not PyStrObject: 6 | import ../../Objects/exceptions 7 | import ./str 8 | 9 | type 10 | PyConfigInitEnum* = enum 11 | ## Py_Initialize() API: backward compatibility with Python 3.6 and 3.7 12 | PyConfig_INIT_COMPAT = 1.cint, 13 | PyConfig_INIT_PYTHON = 2, 14 | PyConfig_INIT_ISOLATED = 3 15 | 16 | proc asList*(list: openArray[Str]): PyObject = 17 | ## `_PyWideStringList_AsList` 18 | var pylist = newPyList(list.len) 19 | for i, v in list: 20 | let item = newPyStr(v) 21 | when item is_not PyStrObject: 22 | retIfExc item 23 | pylist[i] = item 24 | pylist 25 | 26 | 27 | -------------------------------------------------------------------------------- /Modules/unicodedata/private/consts.nim: -------------------------------------------------------------------------------- 1 | # Generated by test_decimalAndSpace 2 | const unidata_version* = "15.1.0" 3 | const maxunicode* = 1114111 4 | const spaces* = [9, 10, 11, 12, 13, 28, 29, 30, 31, 32, 133, 160, 5760, 8192, 8193, 8194, 8195, 8196, 8197, 8198, 8199, 8200, 8201, 8202, 8232, 8233, 8239, 8287, 12288] 5 | const allZeros* = [48, 1632, 1776, 1984, 2406, 2534, 2662, 2790, 2918, 3046, 3174, 3302, 3430, 3558, 3664, 3792, 3872, 4160, 4240, 6112, 6160, 6470, 6608, 6784, 6800, 6992, 7088, 7232, 7248, 42528, 43216, 43264, 43472, 43504, 43600, 44016, 65296, 66720, 68912, 69734, 69872, 69942, 70096, 70384, 70736, 70864, 71248, 71360, 71472, 71904, 72016, 72784, 73040, 73120, 73552, 92768, 92864, 93008, 120782, 120792, 120802, 120812, 120822, 123200, 123632, 124144, 125264, 130032] -------------------------------------------------------------------------------- /Objects/dictproxyobject.nim: -------------------------------------------------------------------------------- 1 | import pyobject 2 | import baseBundle 3 | 4 | 5 | # read only dict used for `__dict__` of types 6 | declarePyType DictProxy(): 7 | dict: PyObject 8 | 9 | implDictProxyMagic repr: 10 | # todo: add "dictproxy" or "mappingproxy" after string methods are implemented 11 | self.dict.callMagic(repr) 12 | 13 | implDictProxyMagic str: 14 | self.dict.callMagic(str) 15 | 16 | implDictProxyMagic getitem: 17 | self.dict.callMagic(getitem, other) 18 | 19 | implDictProxyMagic len: 20 | self.dict.callMagic(len) 21 | 22 | implDictProxyMagic init(mapping: PyObject): 23 | self.dict = mapping 24 | pyNone 25 | 26 | proc newPyDictProxy*(mapping: PyObject): PyObject {. cdecl .} = 27 | let d = newPyDictProxySimple() 28 | d.dict = mapping 29 | d 30 | 31 | -------------------------------------------------------------------------------- /Objects/numobjects/helpers.nim: -------------------------------------------------------------------------------- 1 | 2 | import ./numobjects_comm_with_warn 3 | 4 | template PyNumber_FloatOrIntImpl*(o: PyObject, resObj: typed; nameId; doWithIndexRes){.dirty.} = 5 | bind PyNumber_Xxx_Wrap, PyNumber_Index 6 | if o.isNil: return null_error() 7 | if o.`ofExactPy nameId Object`: 8 | resObj = `Py nameId Object` o 9 | return 10 | PyNumber_Xxx_Wrap o, nameId, nameId, fmt"{o.typeName:.50s}.", 50, resObj 11 | if not o.getMagic(index).isNil: 12 | var res: PyIntObject 13 | retIfExc PyNumber_Index(o, res) 14 | doWithIndexRes 15 | 16 | template genNumberVariant*(Name, T){.dirty.} = 17 | proc `PyNumber Name`*(v: PyObject): PyObject {.pyCFuncPragma.} = 18 | var res: `Py T Object` 19 | result = `PyNumber Name`(v, res) 20 | if result.isNil: result = res 21 | -------------------------------------------------------------------------------- /Python/sysmodule/initUtils.nim: -------------------------------------------------------------------------------- 1 | 2 | import ../../Objects/[ 3 | pyobjectBase, 4 | exceptions, 5 | stringobject, 6 | dictobject, 7 | listobject, 8 | ] 9 | import ../../Objects/numobjects/intobject 10 | export pyobjectBase, exceptions, stringobject, dictobject, listobject, intobject 11 | 12 | template SET_SYSimpl(key; v) = 13 | retIfExc sysdict.setItem(newPyAscii key, v) 14 | template SET_SYS*(key; value: PyIntObject|PyListObject|PyDictObject) = SET_SYSimpl(key, value) 15 | template `!`*(value: PyObject): PyObject = 16 | bind retIfExc 17 | let v = value 18 | retIfExc v 19 | v 20 | template SET_SYS*(key; value: PyObject) = 21 | bind `!` 22 | SET_SYSimpl(key, !value) 23 | template SET_SYS*(key; value: string) = 24 | ## SET_SYS_FROM_STRING 25 | SET_SYSimpl(key, newPyStr(value)) 26 | -------------------------------------------------------------------------------- /Objects/numobjects/numobjects_comm.nim: -------------------------------------------------------------------------------- 1 | 2 | import ../../Include/internal/pycore_object 3 | export Py_CheckSlotResult 4 | import ./[ 5 | intobject_decl, floatobject_decl, 6 | ] 7 | import ../[ 8 | pyobject, exceptions, stringobject, boolobject, notimplementedobject, 9 | ] 10 | 11 | import ../abstract/helpers 12 | export null_error 13 | 14 | export intobject_decl, floatobject_decl, 15 | pyobject, exceptions, boolobject, stringobject, notimplementedobject 16 | 17 | let pyIntZero* = newPyInt(0) 18 | let pyIntOne* = newPyInt(1) 19 | let pyIntTen* = newPyInt(10) 20 | 21 | let divZeroError = newPyAscii"division by zero" 22 | template retZeroDiv* = 23 | return newZeroDivisionError divZeroError 24 | 25 | proc newPyFloat*(pyInt: PyIntObject): PyFloatObject = 26 | result = newPyFloat(pyInt.toFloat) 27 | -------------------------------------------------------------------------------- /Objects/pyobject_apis/attrsUtils.nim: -------------------------------------------------------------------------------- 1 | 2 | import std/strformat 3 | import ../[ 4 | stringobject, pyobjectBase, exceptions, 5 | ] 6 | template asAttrNameOrSetExc*(name: PyObject, exc: PyObject): PyStrObject = 7 | bind ofPyStrObject, typeName, newTypeError, newPyStr, PyStrObject 8 | bind formatValue, fmt 9 | if not ofPyStrObject(name): 10 | let n{.inject.} = typeName(name) 11 | exc = newTypeError newPyStr( 12 | fmt"attribute name must be string, not '{n:.200s}'",) 13 | return 14 | PyStrObject name 15 | 16 | template asAttrNameOrRetE*(name: PyObject): PyStrObject = 17 | bind ofPyStrObject, typeName, newTypeError, newPyStr, PyStrObject 18 | asAttrNameOrSetExc(name, result) 19 | 20 | template nameAsStr*{.dirty.} = 21 | when name is_not PyStrObject: 22 | let name = name.asAttrNameOrRetE 23 | 24 | -------------------------------------------------------------------------------- /Objects/stringobject/unicodeapis.nim: -------------------------------------------------------------------------------- 1 | 2 | include /common_h 3 | proc unicode_char(c: uint32): PyObject = 4 | assert c <= MAX_UNICODE 5 | newPyStr if c < 256: newAsciiUnicodeVariant cast[char](c) 6 | else: newUnicodeUnicodeVariant @[cast[Rune](c)] 7 | 8 | template retChrOutOfRange = 9 | return newValueError newPyAscii"chr() arg not in range(0x110000)" 10 | template chkUpperBound = 11 | if ordinal > MAX_UNICODE: retChrOutOfRange 12 | proc PyUnicode_FromOrdinal*(ordinal: uint32): PyObject = 13 | chkUpperBound 14 | unicode_char ordinal 15 | 16 | proc PyUnicode_FromOrdinal*(ordinal: int32): PyObject = 17 | if ordinal < 0: retChrOutOfRange 18 | PyUnicode_FromOrdinal cast[uint32](ordinal) 19 | 20 | proc PyUnicode_FromOrdinal*(ordinal: int): PyObject = 21 | chkUpperBound 22 | unicode_char cast[uint32](ordinal) 23 | -------------------------------------------------------------------------------- /Objects/abstract/dunder.nim: -------------------------------------------------------------------------------- 1 | 2 | 3 | import ./helpers 4 | import ../[ 5 | pyobject, 6 | ] 7 | proc PyObject_GetItem*(o: PyObject; key: PyObject): PyObject = 8 | if o.isNil or key.isNil: 9 | return null_error() 10 | let f = o.getMagic(getitem) 11 | if not f.isNil: 12 | return f(o, key) 13 | #TODO:class_getitem 14 | #[ 15 | if o.ofPyTypeObject: 16 | let tp = PyTypeObject(o) 17 | if tp == pyTypeObjectType: 18 | return 19 | ]# 20 | type_errorn("'$#' object is not subscriptable", o) 21 | 22 | proc PyObject_SetItem*(o: PyObject; key, value: PyObject): PyObject = 23 | if o.isNil or key.isNil or value.isNil: 24 | return null_error() 25 | let f = o.getMagic(setitem) 26 | if not f.isNil: 27 | return f(o, key, value) 28 | type_errorn("'$#' object does not support item assignment", o) 29 | -------------------------------------------------------------------------------- /Objects/numobjects/intobject/bit_length.nim: -------------------------------------------------------------------------------- 1 | 2 | import ./bit_length_util 3 | import ./decl 4 | proc digitCount*(v: PyIntObject): int{.inline.} = v.digits.len ## `_PyLong_DigitCount` 5 | proc numbits*(v: PyIntObject): int64 = 6 | ## `_PyLong_NumBits` 7 | assert not v.isNil 8 | let ndigits = v.digitCount 9 | #assert ndigits == 0 or 10 | if ndigits > 0: 11 | let ndigits1 = int64 ndigits-1 12 | let msd = v.digits[ndigits1] 13 | result = ndigits1 * digitBits 14 | result += bit_length(msd) 15 | 16 | proc bit_length*(self: PyIntObject): PyIntObject = 17 | ## int_bit_length_impl 18 | let nbits = self.numbits 19 | assert nbits >= 0 20 | return newPyInt nbits 21 | 22 | proc bit_count*(self: PyIntObject): PyIntObject = 23 | var res = int64 0 24 | for d in self.digits: 25 | res += popcount(d) 26 | newPyInt res 27 | -------------------------------------------------------------------------------- /tests/asserts/quicksort.py: -------------------------------------------------------------------------------- 1 | array = [13, 5, 6, 12, 1, 14, 3, 2, -2, 11, 8, 0, 9, 4, 10, -1] 2 | 3 | 4 | def quicksort(array, start, end): 5 | if end - start < 1: 6 | return 7 | pivot = array[start] 8 | l = start 9 | r = end - 1 10 | while l < r: 11 | while l < r and pivot < array[r]: 12 | r = r - 1 13 | if l < r: 14 | array[l], array[r] = array[r], array[l] 15 | l = l + 1 16 | while l < r and array[l] < pivot: 17 | l = l + 1 18 | if l < r: 19 | array[l], array[r] = array[r], array[l] 20 | r = r - 1 21 | quicksort(array, start, l) 22 | quicksort(array, l+1, end) 23 | 24 | 25 | quicksort(array, 0, len(array)) 26 | 27 | for i in range(1, len(array)): 28 | assert array[i-1] < array[i] 29 | 30 | print("ok") 31 | -------------------------------------------------------------------------------- /Objects/abstract/helpers.nim: -------------------------------------------------------------------------------- 1 | 2 | import std/strutils 3 | import ../[ 4 | pyobjectBase, 5 | stringobject, 6 | exceptions, 7 | ] 8 | import ../../Include/internal/pycore_object 9 | export Py_CheckSlotResult 10 | 11 | proc null_error*(): PyBaseErrorObject = 12 | newSystemError newPyAscii"null argument to internal routine" 13 | 14 | proc type_error*(s: string): PyBaseErrorObject = 15 | return newTypeError newPyStr s 16 | 17 | proc type_errorn*(nstyleS: string; o: PyObject): PyBaseErrorObject{.raises: [].} = 18 | ## nim-style type_error, using `$n` based format of std/strutils 19 | var s = o.typeName.substr(0, 199) # %.200s 20 | try: s = nstyleS % s 21 | except ValueError: 22 | return newSystemError newPyStr("invalid format string:" & nstyleS) 23 | #doAssert false, "bad PyUnicode_FromFormat call. msg: " & e.msg 24 | type_error s 25 | -------------------------------------------------------------------------------- /Objects/stringobject/codec.nim: -------------------------------------------------------------------------------- 1 | 2 | import ../[pyobjectBase, 3 | stringobject, exceptions, 4 | byteobjects, 5 | ] 6 | import ./meth 7 | import ../../Modules/posixmodule/utils 8 | 9 | proc PyUnicode_DecodeFSDefault*(s: string): PyStrObject = 10 | #TODO:decode 11 | newPyStr s 12 | 13 | proc PyUnicode_FSDecoder*(arg: PyObject, val: var PyStrObject): PyBaseErrorObject = 14 | let path = PyOS_FSPath(arg) 15 | retIfExc path 16 | let output = if path.ofPyStrObject: PyStrObject path 17 | elif path.ofPyBytesObject: 18 | PyUnicode_DecodeFSDefault PyBytesObject(path).asString 19 | else: 20 | return newTypeError newPyStr( 21 | "path should be string, bytes, or os.PathLike, not " & arg.typeName.substr(0, 199)) 22 | 23 | if output.find('\0') > 0: 24 | return newValueError newPyAscii"embedded null character" 25 | val = output 26 | -------------------------------------------------------------------------------- /Objects/numobjects/intobject/idxHelpers.nim: -------------------------------------------------------------------------------- 1 | ## for external use 2 | 3 | import ./[decl, ops] 4 | # used in list and tuple 5 | template getIndex*(obj: PyIntObject, size: int, includeSize: static[bool] = false): int = 6 | var idx = toIntOrRetOF(obj) 7 | if idx < 0: 8 | idx = size + idx 9 | if (idx < 0) or (when includeSize: (size < idx) else: size <= idx): 10 | let msg = "index out of range. idx: " & $idx & ", len: " & $size 11 | return newIndexError newPyAscii(msg) 12 | idx 13 | 14 | proc getClampedIndex*(idx: int, size: int): int = 15 | result = idx 16 | if result < 0: 17 | result += size 18 | if result < 0: result = 0 19 | elif size <= result: result = size 20 | 21 | 22 | template getClampedIndex*(obj: PyIntObject, size: int): int = 23 | ## like `getIndex`_ but clamping result in `0..= ... ]# 25 | Py_PACK_FULL_VERSION( 26 | PY_MAJOR, 27 | PY_MINOR, 28 | PyPatch, 29 | PY_RELEASE_LEVEL, 30 | PY_RELEASE_SERIAL) 31 | -------------------------------------------------------------------------------- /tests/basics/everything.py: -------------------------------------------------------------------------------- 1 | l = [] 2 | 3 | 4 | d = dict() 5 | def foo(): 6 | for i in [1,2,3,4,5]: 7 | l.append(i ** i / (i + 2 * (i - 10))) 8 | 9 | for j in range(2 * 5): 10 | l.append(j * i) 11 | d[j] = i 12 | 13 | 14 | 15 | def bar(): 16 | i = 1 17 | while i != 10: 18 | i = i + 1 19 | foo() 20 | 21 | 22 | if True and 1 and 3: 23 | bar() 24 | 25 | print(d) 26 | 27 | if 0 or False: 28 | l.clear() 29 | 30 | 31 | print(l) 32 | print(list(range(-1, -14, -2))) 33 | 34 | 35 | b = [] 36 | b.append(b) 37 | print(b) 38 | 39 | import function 40 | 41 | print(function.foobar(1,2)) 42 | 43 | def stress(): 44 | ll = [] 45 | sz = 10 ** 4 46 | for i in range(sz): 47 | ll.append(i) 48 | print(len(ll) + 1) 49 | for j in range(sz): 50 | ll[j] = j * j 51 | return ll 52 | 53 | 54 | 55 | print(stress()[100]) 56 | 57 | -------------------------------------------------------------------------------- /Objects/numobjects/intobject/floatinfos.nim: -------------------------------------------------------------------------------- 1 | ## Used by ./frexp.nim 2 | 3 | import std/fenv 4 | const 5 | DBL_MAX_EXP* = float.maxExponent ## inner, for PyLong_AsDouble 6 | DBL_MAX* = float.maximumPositiveValue 7 | DBL_MAX_10_EXP* = float.max10Exponent 8 | DBL_MIN* = float.minimumPositiveValue 9 | DBL_MIN_EXP* = float.minExponent 10 | DBL_MIN_10_EXP* = float.min10Exponent 11 | DBL_DIG* = float.digits 12 | DBL_MANT_DIG* = float.mantissaDigits 13 | DBL_EPSILON* = float.epsilon 14 | FLT_RADIX* = fpRadix() 15 | #FLT_ROUNDS* = 16 | const weirdTarget = defined(js) or defined(nimscript) 17 | when not weirdTarget: 18 | let fiRound = fegetround().int 19 | template FLT_ROUNDS*: int = 20 | ## not available when nimscript 21 | bind fiRound 22 | fiRound 23 | else: 24 | template FLT_ROUNDS*: int = 25 | #{.error: "not available for nimscript/JavaScript/compile-time".} 26 | -1 27 | -------------------------------------------------------------------------------- /Objects/moduledefs.nim: -------------------------------------------------------------------------------- 1 | 2 | import ./[ 3 | pyobject, 4 | ] 5 | export rtarrays 6 | 7 | 8 | type 9 | PyModuleDef_SlotKind* = enum 10 | Py_mod_unknown ## invalid, or nim will complain object variant not start from 0 11 | Py_mod_create = 1 12 | Py_mod_exec 13 | PyModuleDef_Slot* = object 14 | case slot: PyModuleDef_SlotKind 15 | of Py_mod_unknown: discard # just make sure 16 | of Py_mod_create: 17 | create: proc (spec: PyObject, def: PyModuleDef): PyObject{.pyCFuncPragma.} 18 | of Py_mod_exec: 19 | exec: proc (m: PyObject#[PyModuleObject]#): PyObject{.pyCFuncPragma.} 20 | PyModuleDef_Slots* = RtArray[PyModuleDef_Slot] 21 | PyModuleDef* = ref object 22 | m_name*: string 23 | typ*: PyTypeObject 24 | m_slots*: PyModuleDef_Slots 25 | 26 | proc newPyModuleDef*(name: string, typ: PyTypeObject): PyModuleDef = 27 | PyModuleDef( 28 | m_name: name, 29 | typ: typ, 30 | ) 31 | -------------------------------------------------------------------------------- /Objects/typeobjectImpl.nim: -------------------------------------------------------------------------------- 1 | 2 | import std/strformat 3 | import ./[ 4 | pyobject, 5 | exceptions, 6 | typeobject, 7 | dictobject, 8 | tupleobjectImpl, 9 | stringobject, 10 | ] 11 | export typeobject 12 | 13 | import ./classobject 14 | 15 | 16 | methodMacroTmpl(Type) 17 | 18 | # type_new_init:type_new_alloc 19 | 20 | implTypeMagic New(metaType: PyTypeObject, name: PyStrObject, 21 | bases: PyTupleObject, dict: PyDictObject): 22 | assert metaType == pyTypeObjectType 23 | assert bases.len == 0 24 | type Cls = PyInstanceObject 25 | let tp = newPyType[Cls]($name.str) 26 | tp.pyType = metaType 27 | tp.kind = PyTypeToken.Type 28 | tp.tp_dealloc = subtype_dealloc[Cls] 29 | tp.magicMethods.New = tpMagic(Instance, new) 30 | updateSlots(tp, dict) 31 | tp.dict = PyDictObject(tpMethod(Dict, copy)(dict)) 32 | tp.typeReady true 33 | # XXX: CPython's `object` doesn't contains `__del__` 34 | tp 35 | -------------------------------------------------------------------------------- /Python/pylifecycle/builtins.nim: -------------------------------------------------------------------------------- 1 | 2 | import ../../Objects/[ 3 | pyobject, dictobject, 4 | exceptions, 5 | ] 6 | import ../[ 7 | bltinmodule_mod, 8 | coreconfig, 9 | sysmodule_instance, 10 | ] 11 | proc pycore_init_builtins*(): PyBaseErrorObject = 12 | let bimod = PyBuiltin_Init(pyConfig) 13 | let modules = sys.modules 14 | 15 | retIfExc bimod 16 | 17 | let builtins = PyBuiltinsModuleObject bimod 18 | #XXX: maybe this shall go to pyimport/targets/core.nim... 19 | # I cannot found where does CPython does these 20 | # (probably in _importlib.boostrap.py), 21 | # Currently I'd like to write here 22 | template addModule(m) = 23 | modules[m.name] = m 24 | addModule sys 25 | addModule builtins 26 | 27 | #retIfExc PyImport_FixupBuiltin(tstate, bimod, "builtins", modules) 28 | #TODO:_PyImport_FixupBuiltin pyimport/builtins 29 | 30 | #TODO:interp.callable_cache 31 | #TODO:interp.common_consts 32 | 33 | -------------------------------------------------------------------------------- /Utils/macroutils.nim: -------------------------------------------------------------------------------- 1 | 2 | import std/macros 3 | proc deepCopy*(s: seq[NimNode]): seq[NimNode] = 4 | ##[To get rid of compile-error caused by Nim's backward non-compatibility change: 5 | 6 | Error: illegal capture 'selfNoCast' because 'addPyIntObjectMagic' has the calling convention: 7 | 8 | Bacause for all procs generated by `implMethod`, whose 1st param is `selfNoCast` ( 9 | take it for example, so for other params), 10 | 11 | if not `deepCopy`, 12 | their `selfNoCast` symbol was shared (there's only one instance), 13 | so its loc would be updated each time `implMethod` is called. 14 | 15 | Finally `selfNoCast` would be considered to be placed at the place 16 | where the last `implMethod` was called. 17 | So for all `implMethod`-ed procs except the last, 18 | their `selfNoCast` were mistakely considered as `captured` 19 | 20 | Old Nim compiler didn't behave so. 21 | ]## 22 | result = newSeq[NimNode](s.len) 23 | for i, e in s: 24 | result[i] = s[i].copyNimTree 25 | -------------------------------------------------------------------------------- /Objects/pyobject_apis/strings.nim: -------------------------------------------------------------------------------- 1 | 2 | import std/strformat 3 | import ../[pyobject, 4 | stringobject, exceptions, 5 | ] 6 | 7 | proc reprDefault*(self: PyObject): PyObject {. cdecl .} = 8 | newPyString(fmt"<{self.typeName} object at {self.idStr}>") 9 | proc PyObject_ReprNonNil*(obj: PyObject): PyObject = 10 | let fun = obj.getMagic(repr) 11 | if fun.isNil: 12 | return reprDefault obj 13 | result = fun(obj) 14 | retIfExc result 15 | result.errorIfNotString "__repr__" 16 | 17 | template nullOr(obj; elseCall): PyObject = 18 | if obj.isNil: newPyAscii"" 19 | else: elseCall obj 20 | 21 | proc PyObject_Repr*(obj: PyObject): PyObject = obj.nullOr PyObject_ReprNonNil 22 | 23 | proc PyObject_StrNonNil*(obj: PyObject): PyObject = 24 | let fun = obj.getMagic(str) 25 | if fun.isNil: return PyObject_ReprNonNil(obj) 26 | result = fun(obj) 27 | retIfExc result 28 | result.errorIfNotString "__str__" 29 | 30 | proc PyObject_Str*(obj: PyObject): PyObject = obj.nullOr PyObject_StrNonNil 31 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: test C & JS 2 | on: 3 | push: 4 | branches-ignore: 5 | - 'feat-*' 6 | paths: 7 | - '**/*.nim' 8 | - './*.nimble' 9 | - 'tests/**' 10 | - '.github/workflows/ci.yml' 11 | pull_request: 12 | workflow_dispatch: 13 | env: 14 | nim-version: 'stable' 15 | jobs: 16 | ci: 17 | runs-on: ubuntu-latest 18 | steps: 19 | - uses: actions/checkout@v4 20 | - name: Cache nimble 21 | id: cache-nimble 22 | uses: actions/cache@v4 23 | with: 24 | path: ~/.nimble 25 | key: ${{ runner.os }}-nimble 26 | - uses: jiro4989/setup-nim-action@v1 27 | with: 28 | nim-version: ${{ env.nim-version }} 29 | 30 | - name: BuildC 31 | run: nimble build --warning[Spacing]:off 32 | - name: Test C backend 33 | run: nimble test 34 | 35 | - name: BuildJs 36 | run: nimble buildJs -d:nodejs --warning[Spacing]:off 37 | - name: Test NodeJS backend 38 | run: nimble testNodeJs 39 | 40 | -------------------------------------------------------------------------------- /Objects/typeobject/default_generics.nim: -------------------------------------------------------------------------------- 1 | 2 | import ../[ 3 | pyobject, 4 | exceptions, 5 | stringobject, 6 | hash, 7 | boolobject, 8 | ] 9 | import ../numobjects/intobject_decl 10 | 11 | # some generic behaviors that every type should obey 12 | proc leDefault*(o1, o2: PyObject): PyObject {. pyCFuncPragma .} = 13 | let lt = o1.callMagic(lt, o2) 14 | let eq = o1.callMagic(eq, o2) 15 | lt.callMagic(Or, eq) 16 | 17 | proc neDefault*(o1, o2: PyObject): PyObject {. pyCFuncPragma .} = 18 | let eq = o1.callMagic(eq, o2) 19 | eq.callMagic(Not) 20 | 21 | proc geDefault*(o1, o2: PyObject): PyObject {. pyCFuncPragma .} = 22 | let gt = o1.callMagic(gt, o2) 23 | let eq = o1.callMagic(eq, o2) 24 | gt.callMagic(Or, eq) 25 | 26 | proc hashDefault*(self: PyObject): PyObject {. pyCFuncPragma .} = 27 | let res = cast[BiggestInt](rawHash(self)) # CPython does so 28 | newPyInt(res) 29 | 30 | proc eqDefault*(o1, o2: PyObject): PyObject {. pyCFuncPragma .} = 31 | if rawEq(o1, o2): pyTrueObj 32 | else: pyFalseObj 33 | -------------------------------------------------------------------------------- /Objects/numobjects/floatobject/toval.nim: -------------------------------------------------------------------------------- 1 | 2 | import ./decl 3 | import ../numobjects_comm_with_warn 4 | import ../../[ 5 | pyobjectBase, 6 | exceptions, 7 | ] 8 | template asDouble*(op: PyFloatObject): float = op.v 9 | template asDouble*(op: PyFloatObject; v: var float): PyBaseErrorObject = 10 | ## `PyFloat_AS_DOUBLE` 11 | v = op.asDouble 12 | PyBaseErrorObject nil 13 | proc PyFloat_AsDouble*(op: PyObject; v: var float): PyBaseErrorObject = 14 | if op.ofPyFloatObject: 15 | return op.PyFloatObject.asDouble v 16 | var fun = op.pyType.magicMethods.float 17 | if fun.isNil: 18 | var res: PyIntObject 19 | let exc = PyNumber_Index(op, res) 20 | if exc.isNil: 21 | retIfExc res.toFloat v 22 | else: 23 | let res = fun(op) 24 | errorIfNot Float, "float", res, (op.typeName & ".__float__") 25 | return res.PyFloatObject.asDouble v 26 | 27 | proc PyFloat_AsFloat*(op: PyObject; v: var float32): PyBaseErrorObject = 28 | ## EXT. 29 | var df: float 30 | result = PyFloat_AsDouble(op, df) 31 | if result.isNil: 32 | v = float32 df 33 | -------------------------------------------------------------------------------- /Utils/sequtils2.nim: -------------------------------------------------------------------------------- 1 | 2 | ## called sequtils2 as having more apis over `./sequtils` 3 | 4 | {.push hint[ConvFromXtoItselfNotNeeded]: off.} 5 | proc rfind*[A; B: not string and not seq](key: typedesc; s: openArray[A], sub: B, start=0, stop=s.len): int = 6 | for i in countdown(stop-1, start): 7 | if key(s[i]) == key(sub): return i 8 | return -1 9 | 10 | proc rfind*[A, B](key: typedesc; s: openArray[A], sub: openArray[B], start=0, stop=s.len): int = 11 | ## assert `start.. s.len - start: 15 | return -1 16 | result = 0 17 | for i in countdown(stop - sub.len, start): 18 | for j in 0..sub.len-1: 19 | result = i 20 | if key(sub[j]) != key(s[i+j]): 21 | result = -1 22 | break 23 | if result != -1: return 24 | return -1 25 | 26 | proc rfind*[T](s: openArray[T], sub: T, start=0, stop=s.len): int = T.rfind(s, sub, start, stop) 27 | proc rfind*[T](s: openArray[T], sub: openArray[T], start=0, stop=s.len): int = T.rfind(s, sub, start, stop) 28 | {.pop.} 29 | -------------------------------------------------------------------------------- /Utils/rtarrays/utils.nim: -------------------------------------------------------------------------------- 1 | 2 | import ../addr0 3 | export addr0 4 | template selfAsAccessor(p, self) = 5 | template p: untyped = self 6 | template dollarImpl*(self: typed; getAccessor=selfAsAccessor){.dirty.} = 7 | result.add '[' 8 | let L = self.len 9 | if L > 0: 10 | getAccessor(p, self) 11 | result.add $p[0] 12 | for i in 1..int_max_str_digits` 7 | static:assert PY_INT_DEFAULT_MAX_STR_DIGITS >= PY_INT_MAX_STR_DIGITS_THRESHOLD 8 | 9 | type Py_long_state = object 10 | max_str_digits*: int 11 | 12 | var state{.threadVar.}: Py_long_state 13 | 14 | proc PyInterpreterState_GET_long_state*(): var Py_long_state{.inline.} = 15 | ## `_PyInterpreterState_GET->long_state` 16 | state 17 | 18 | template pre = result.add "Exceeds the limit (" & $cfg_max & " digits) for integer string conversion" 19 | template suf = result.add "; use sys.set_int_max_str_digits() to increase the limit" 20 | proc MAX_STR_DIGITS_errMsg_to_int*(cfg_max, d: int): string = 21 | pre 22 | result.add ": value has " & $d & " digits" 23 | suf 24 | 25 | proc MAX_STR_DIGITS_errMsg_to_str*(cfg_max: int): string = pre; suf 26 | -------------------------------------------------------------------------------- /Objects/numobjects/intobject/shift.nim: -------------------------------------------------------------------------------- 1 | 2 | import ./decl 3 | proc vLShift*(z: var openArray[Digit], a: openArray[Digit], m: int, d: int): Digit = 4 | ## Shift digit vector `a[0:m]` left by `d` bits, with 0 <= d < digitBits. 5 | ## Put the result in `z[0:m]`, and return the `d` bits shifted out of the top. 6 | assert d >= 0 and d < digitBits 7 | var carry: Digit = 0 8 | for i in 0..= 0 and d < digitBits 18 | var carry: Digit = 0 19 | let mask = (Digit(1) shl d) - 1 20 | 21 | for i in countdown(m - 1, 0): 22 | let acc = (TwoDigits(carry) shl digitBits) or TwoDigits(a[i]) 23 | carry = Digit(acc and mask) 24 | z[i] = Digit(acc shr d) 25 | 26 | return carry 27 | -------------------------------------------------------------------------------- /Utils/npointer_js.nim: -------------------------------------------------------------------------------- 1 | 2 | import std/jsffi 3 | type 4 | ArrayBuffer{.importjs.} = JsObject 5 | DataView{.importjs.} = JsObject 6 | NPointer* = DataView ## XXX: impl is unstable, later maybe `ref object` 7 | 8 | template genNew(T, A1){.dirty.} = 9 | proc `new T`(a: A1): T{.importjs: "new " & $T & "(#)".} 10 | genNew DataView, ArrayBuffer 11 | using n: int 12 | genNew ArrayBuffer, int 13 | proc alloc0*(n): NPointer = newDataView newArrayBuffer(n) 14 | template a0alias(name){.dirty.} = 15 | proc name*(n): NPointer = alloc0 n 16 | a0alias alloc 17 | # JS is single-threaded 18 | a0alias allocShared 19 | a0alias allocShared0 20 | using p: NPointer 21 | proc dealloc*(p) = discard 22 | proc deallocShared*(p) = discard 23 | 24 | proc repr*(d: DataView): string = "[object DataView]" 25 | #proc repr*(d: NPointer): string = "[object]" 26 | const defLittleEndian* = false # either is ok, we currently use bigEndian as in network such is used 27 | template genGS*(g, s, T, n){.dirty.} = 28 | bind DataView 29 | proc g*(d: DataView; i: int; isLittleEndian = defLittleEndian): T{.importcpp.} 30 | proc s*(d: DataView; i: int; v: T; isLittleEndian = defLittleEndian){.importcpp.} 31 | 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018-2019 wtli 4 | Copyright (c) 2025-2025 litlighilit 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | -------------------------------------------------------------------------------- /Include/modsupport.nim: -------------------------------------------------------------------------------- 1 | 2 | type ApiVersion* = distinct int 3 | proc `==`*(a, b: ApiVersion): bool{.borrow.} 4 | proc `$`*(a: ApiVersion): string{.borrow.} 5 | 6 | converter toInt*(a: ApiVersion): int = int(a) 7 | 8 | const 9 | NPYTHON_API_VERSION* = ApiVersion 0 10 | ##[ 11 | .. hint:: this is not `PYTHON_API_VERSION` as NPython API currently isn't compatible with CPython 12 | 13 | The API version is maintained (independently from the Python version) 14 | so we can detect mismatches between the interpreter and dynamically 15 | loaded modules. These are diagnosed by an error message but 16 | the module is still loaded (because the mismatch can only be tested 17 | after loading the module). The error message is intended to 18 | explain the core dump a few seconds later. 19 | 20 | The symbol PYTHON_API_STRING defines the same value as a string 21 | literal. *** PLEASE MAKE SURE THE DEFINITIONS MATCH. *** 22 | 23 | Please add a line or two to the top of this log for each API 24 | version change: 25 | 26 | ]## 27 | NPYTHON_API_STRING* = $NPYTHON_API_VERSION 28 | 29 | PYTHON_ABI_VERSION* = ApiVersion 3 30 | PYTHON_ABI_STRING* = $PYTHON_ABI_VERSION 31 | -------------------------------------------------------------------------------- /tests/asserts/tryexcept.py: -------------------------------------------------------------------------------- 1 | def main(): 2 | try: 3 | assert False 4 | except: 5 | pass 6 | 7 | 8 | main() 9 | 10 | 11 | def catch(): 12 | try: 13 | assert False 14 | except AssertionError: 15 | pass 16 | 17 | catch() 18 | 19 | 20 | def nocatch(): 21 | try: 22 | a 23 | except AssertionError: 24 | pass 25 | 26 | 27 | 28 | def get_name_error(fun): 29 | flag = False 30 | try: 31 | fun() 32 | except NameError: 33 | flag = True 34 | 35 | assert flag 36 | 37 | 38 | get_name_error(nocatch) 39 | 40 | 41 | def multiple_except(): 42 | flag = False 43 | try: 44 | multiple 45 | except ValueError: 46 | pass 47 | except NameError: 48 | flag = True 49 | except AssertionError: 50 | a = 1+2 51 | 52 | assert flag 53 | 54 | 55 | multiple_except() 56 | 57 | print("nested exception with A B C means ok") 58 | 59 | 60 | def nested(): 61 | try: 62 | a 63 | except: 64 | try: 65 | b 66 | except: 67 | c 68 | 69 | try: 70 | nested() 71 | except NameError as e: 72 | assert str(e) == "name 'c' is not defined" 73 | 74 | -------------------------------------------------------------------------------- /Objects/numobjects/intobject_decl.nim: -------------------------------------------------------------------------------- 1 | 2 | import ./intobject/[decl, frexp, signbit] 3 | export decl, frexp, signbit 4 | 5 | import ../[ 6 | pyobjectBase, exceptions, 7 | ] 8 | import ../../Utils/utils 9 | const HasLdExp = declared(ldexp) 10 | when not HasLdExp: 11 | import std/strutils 12 | 13 | proc toFloat*(pyInt: PyIntObject; overflow: var PyOverflowErrorObject): float{.pyCFuncPragma.} = 14 | ## `PyLong_AsDouble` 15 | overflow = nil 16 | when not HasLdexp: 17 | ValueError!parseFloat($pyInt) #TODO:long-opt 18 | else: 19 | var exponent: int64 20 | let x = frexp(pyInt, exponent) 21 | assert exponent >= 0 22 | if exponent > DBL_MAX_EXP: 23 | overflow = newOverflowError newPyAscii"int too large to convert to float" 24 | return -1.0 25 | ldexp(x, cint exponent) 26 | 27 | 28 | proc toFloat*(pyInt: PyIntObject): float{.pyCFuncPragma.} = 29 | ## `PyLong_AsDouble` but never OverflowError, just returns `+-Inf` 30 | var ovf: PyOverflowErrorObject 31 | result = pyInt.toFloat ovf 32 | if ovf.isNil: return 33 | result = if pyInt.negative: NegInf 34 | else: Inf 35 | 36 | proc toFloat*(pyInt: PyIntObject; res: var float): PyOverflowErrorObject{.pyCFuncPragma.} = 37 | res = pyInt.toFloat result 38 | -------------------------------------------------------------------------------- /Objects/typeobject/apis/subtype.nim: -------------------------------------------------------------------------------- 1 | 2 | import ./common #[ 3 | pyobject, 4 | tupleobjectImpl, 5 | ] 6 | ]# 7 | 8 | proc type_is_subtype_base_chain(a, b: PyTypeObject): bool = 9 | var a = a 10 | while true: 11 | if isType(a, b): 12 | return true 13 | a = a.base 14 | if a.isNil: break 15 | return b == pyObjectType 16 | 17 | const HasMro = compiles((var a: PyObject; a.mro)) 18 | when HasMro: 19 | proc is_subtype_with_mro(a_mro: PyTupleObject, a, b: PyTypeObject): bool = 20 | if not a_mro.isNil: 21 | # Deal with multiple inheritance without recursion by walking the MRO tuple 22 | for i in a_mro: 23 | if i == b: 24 | return true 25 | else: 26 | return type_is_subtype_base_chain(a, b) 27 | 28 | proc PyType_IsSubtype*(a, b: PyTypeObject): bool = 29 | when HasMro: 30 | is_subtype_with_mro(a.mro, a, b) 31 | else: 32 | type_is_subtype_base_chain(a, b) 33 | 34 | proc isSubtype*(a, b: PyTypeObject): bool{.inline.} = PyType_IsSubtype(a, b) 35 | 36 | proc PyObject_TypeCheck*(obj: PyObject, tp: PyTypeObject): bool = 37 | let objtp = obj.pyType 38 | objtp.isType(tp) or isSubtype(objtp, tp) 39 | 40 | proc typeCheck*(ob: PyObject, tp: PyTypeObject): bool{.inline.} = PyObject_TypeCheck ob, tp 41 | -------------------------------------------------------------------------------- /Objects/frameobject.nim: -------------------------------------------------------------------------------- 1 | 2 | 3 | import pyobject 4 | 5 | import codeobject 6 | import dictobject 7 | import cellobject 8 | 9 | 10 | declarePyType Frame(): 11 | # currently not used? 12 | back{.member"f_back", readonly.}: PyFrameObject 13 | code{.member"f_code", readonly.}: PyCodeObject 14 | # dicts and sequences for variable lookup 15 | # locals not used for now 16 | # locals*: PyDictObject 17 | globals{.member"f_globals", readonly.}: PyDictObject 18 | # builtins: PyDictObject 19 | fastLocals: seq[PyObject] 20 | cellVars: seq[PyCellObject] 21 | 22 | 23 | # initialized in neval.nim 24 | proc newPyFrame*: PyFrameObject = 25 | newPyFrameSimple() 26 | 27 | proc toPyDict*(f: PyFrameObject): PyDictObject {. cdecl .} = 28 | result = newPyDict() 29 | let c = f.code 30 | for idx, v in f.fastLocals: 31 | if v.isNil: 32 | continue 33 | result[c.localVars[idx]] = v 34 | let n = c.cellVars.len 35 | for idx, cell in f.cellVars[0..= 0: 17 | let modu = PyUnicode_fromStringAndSize(typ.name, idx) 18 | retIfExc modu 19 | let m = PyStrObject modu 20 | PyUnicode_InternMortal(m) 21 | m 22 | else: 23 | pyId builtins 24 | 25 | 26 | proc getFullyQualifiedName*(typ; sep: char): PyObject = 27 | ## `_PyType_GetFullyQualifiedName` 28 | #TODO:tp_flags 29 | let qualname = type_qualname(typ) 30 | retIfExc qualname 31 | let squal = PyStrObject qualname 32 | let module = type_module(typ) 33 | retIfExc module 34 | var smod: PyStrObject 35 | if module.ofPyStrObject and (smod = PyStrObject module; smod) != pyId builtins and 36 | smod != pyDUId main: 37 | result = smod & sep & squal 38 | else: 39 | result = qualname 40 | 41 | proc getFullyQualifiedName*(typ): PyObject = 42 | ## PyType_GetFullyQualifiedName 43 | typ.getFullyQualifiedName '.' 44 | 45 | -------------------------------------------------------------------------------- /Objects/typeobject/wraps.nim: -------------------------------------------------------------------------------- 1 | 2 | import ../[ 3 | pyobject, 4 | exceptions, 5 | stringobject, 6 | ] 7 | 8 | import ../numobjects/intobject/[decl, ops_imp_warn] 9 | 10 | proc has_sq_length*(o: PyObject): bool = 11 | #TODO:mro 12 | not o.getMagic(len).isNil 13 | 14 | proc sq_length*(o: PyObject, resi: var int): PyBaseErrorObject = 15 | ## typeobject.c:slot_sq_length 16 | 17 | template ret(e: PyBaseErrorObject) = 18 | return e 19 | #TODO:mro vectorcall_method 20 | let mlen = o.getMagic(len) 21 | if mlen.isNil: 22 | ret newTypeError newPyAscii"no __len__ found" #TODO: dive vectorcall_method's errmsg 23 | # this msg is just what I made at random 24 | var resObj = mlen(o) 25 | retIfExc resObj 26 | resObj = privatePyNumber_Index(resObj) 27 | retIfExc resObj 28 | assert resObj.ofPyIntObject 29 | let res = PyIntObject resObj 30 | if res.negative: 31 | return newValueError newPyAscii"__len__() should return >= 0" 32 | result = PyLong_AsSsize_t(res, resi) 33 | assert resi >= 0 or result.ofPyOverflowErrorObject 34 | 35 | 36 | proc has_sq_item*(self: PyObject): bool = not self.getMagic(getitem).isNil 37 | proc sq_item*(self: PyObject, i: int): PyObject = 38 | ## slot_sq_item 39 | let ival = newPyInt(i) 40 | #TODO: vectorcall_method(&_Py_ID(__getitem__), stack, 2); 41 | self.getMagic(getitem)(self, ival) 42 | -------------------------------------------------------------------------------- /Objects/tupleobject/decl.nim: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | import std/hashes 5 | from std/sugar import collect 6 | import ../[ 7 | pyobject, 8 | ] 9 | declarePyType Tuple(reprLock, tpToken): 10 | items: seq[PyObject] 11 | setHash: bool 12 | privateHash: Hash 13 | 14 | proc newPyTuple*(): PyTupleObject{.inline.} = 15 | ## inner, used by `__mul__` method 16 | result = newPyTupleSimple() 17 | 18 | proc newPyTuple*(items: seq[PyObject]): PyTupleObject = 19 | result = newPyTuple() 20 | # shallow copy 21 | result.items = items 22 | 23 | template PyTuple_Collect*(body): PyTupleObject = 24 | ## EXT. use as std/sugar's collect. 25 | ## this exists as we cannot define `PyTuple_New`(which accepts int as len) 26 | bind collect, newPyTuple 27 | newPyTuple collect body 28 | 29 | proc newPyTuple*[T: PyObject](items: openArray[T]): PyTupleObject{.inline.} = 30 | newPyTuple @items 31 | 32 | template toPyObject(x: PyObject): PyObject = x 33 | proc collectVarargsToPyObjectArr(args: NimNode): NimNode = 34 | result = newNimNode(nnkBracket, args) 35 | for i in args: result.add newCall(bindSym"toPyObject", i) 36 | 37 | macro PyTuple_Pack*(args: varargs[typed]): PyTupleObject{.inline.} = 38 | ## mainly used for arguments with different types 39 | runnableExamples: 40 | let i = newPyTuple() 41 | discard PyTuple_Pack(i, PyObject i) 42 | newCall(bindSym"newPyTuple", collectVarargsToPyObjectArr(args)) 43 | -------------------------------------------------------------------------------- /Objects/abstract/sequence/list.nim: -------------------------------------------------------------------------------- 1 | 2 | ##XXX: rec-dep: ../sequence shall not import this, otherwise rec-dep 3 | import ../../[ 4 | pyobjectBase, 5 | exceptionsImpl, 6 | listobject, 7 | tupleobject, 8 | ] 9 | import ../iter 10 | 11 | proc PySequence_List*(v: PyObject): PyObject = 12 | let res = newPyList() 13 | retIfExc res.extend v 14 | return res 15 | 16 | proc PySequence_Fast*(v: PyObject, errMsg: string): PyObject = 17 | if v.ofPyListObject or v.ofPyTupleObject: 18 | return v 19 | let it = PyObject_GetIter v 20 | if it.isThrownException: 21 | if it.ofPyTypeErrorObject: 22 | PyTypeErrorObject(it).setString errMsg 23 | return it 24 | PySequence_List it 25 | 26 | template getCommonPySequence_Fast(s: PyObject, attr): untyped = 27 | bind ofPyListObject, PyListObject, PyTupleObject 28 | if s.ofPyListObject: PyListObject(s).attr 29 | else: PyTupleObject(s).attr 30 | 31 | template PySequence_FAST_ITEMS*(s: PyObject): openArray[PyObject] = 32 | bind getCommonPySequence_Fast 33 | getCommonPySequence_Fast s, items 34 | 35 | template PySequence_Fast_GET_SIZE*(s: PyObject): int = 36 | bind getCommonPySequence_Fast 37 | getCommonPySequence_Fast s, len 38 | 39 | proc PySequence_Fast_GET_ITEM*(s: PyListObject; i: int): PyObject = s[i] 40 | proc PySequence_Fast_GET_ITEM*(s: PyObject; i: int): PyObject = 41 | if s.ofPyListObject: PyListObject(s)[i] 42 | else: PyTupleObject(s)[i] 43 | -------------------------------------------------------------------------------- /Python/sysmodule/attrs.nim: -------------------------------------------------------------------------------- 1 | 2 | 3 | import ../sysmodule_instance 4 | import ../../Objects/[ 5 | pyobject, 6 | stringobject, exceptions, 7 | moduleobject, dictobject, 8 | ] 9 | template sysdict: PyDictObject = sys.getDict() 10 | export sysdict 11 | 12 | template opOr(sysd: PyDictObject, op; elseDo) = 13 | if sysd.isNil: elseDo 14 | else: 15 | op 16 | 17 | proc PySys_GetOptionalAttr*(name: PyStrObject, value: var PyObject, exists: var bool): PyBaseErrorObject = 18 | sysdict.opOr: 19 | exists = sysdict.getItemRef(name, value) 20 | do: value=nil 21 | proc PySys_GetOptionalAttr*(name: PyStrObject, value: var PyObject): PyBaseErrorObject = 22 | var unused: bool 23 | PySys_GetOptionalAttr(name, value, unused) 24 | 25 | proc no_sys_module: PyBaseErrorObject = newRuntimeError(newPyAscii"no sys module") 26 | proc PySys_GetAttr*(name: PyStrObject, value: var PyObject): PyBaseErrorObject = 27 | sysdict.opOr: 28 | var exists = sysdict.getItemRef(name, value) 29 | if not exists: 30 | return newRuntimeError(newPyAscii"lost sys." & name) 31 | do: return no_sys_module() 32 | proc PySys_GetAttr*(name: PyStrObject): PyObject = 33 | let exc = PySys_GetAttr(name, result) 34 | retIfExc exc 35 | 36 | proc PySys_SetAttrNonNil*(name: PyStrObject, valueNonNil: PyObject): PyBaseErrorObject = 37 | sysdict.opOr: 38 | return sysdict.setItem(name, valueNonNil) 39 | do: return no_sys_module() 40 | 41 | -------------------------------------------------------------------------------- /Python/call.nim: -------------------------------------------------------------------------------- 1 | 2 | 3 | import ../Objects/[pyobject, methodobject, funcobject, 4 | stringobject, exceptions, 5 | pyobject_apis, 6 | dictobject, 7 | ] 8 | 9 | using kwnames: PyDictObject 10 | 11 | proc fastCall*(callable: PyObject, args: openArray[PyObject]; kwnames: PyDictObject = nil): PyObject {. cdecl .} = 12 | if callable.ofPyNimFuncObject: 13 | return tpMagic(NimFunc, call)(callable, @args, kwnames) 14 | elif callable.ofPyFunctionObject: 15 | # XXX:rec-dep: 16 | #return tpMagic(PyFunction, call)(callable, @args, kwnames) 17 | return callable.getMagic(call)(callable, @args, kwnames) 18 | else: 19 | let fun = getFun(callable, call) 20 | return fun(callable, @args, kwnames) 21 | 22 | proc vectorcallMethod*(name: PyStrObject, args: openArray[PyObject] 23 | #[, nargsf: NArgsFlag]#, kwnames: PyDictObject = nil): PyObject = 24 | ## `PyObject_VectorcallMethod` 25 | ## 26 | ## .. note:: PY-DIFF this differs CPython's vectorcall as kwnames is not a tuple, but a dict 27 | assert not name.isNil 28 | assert args.len >= 1 29 | # Use args[0] as "self" argument 30 | let self = args[0] 31 | let callable = PyObject_GetAttr(self, name) 32 | retIfExc callable 33 | fastCall(callable, args, kwnames) 34 | 35 | proc call*(fun: PyObject): PyObject = 36 | ## `_PyObject_CallNoArgs` 37 | fun.fastCall([]) 38 | 39 | proc call*(fun, arg: PyObject): PyObject = 40 | ## `_PyObject_CallOneArg` 41 | fun.fastCall([arg]) 42 | -------------------------------------------------------------------------------- /Utils/npointer.nim: -------------------------------------------------------------------------------- 1 | 2 | when defined(js): 3 | import npointer_js 4 | export npointer_js except genGS 5 | else: 6 | import std/endians 7 | type NPointer* = pointer 8 | 9 | const defLittleEndian* = cpuEndian == littleEndian 10 | template swapEndian8(a, b) = discard 11 | template swapIfNeed(n, o, i; elseDo) = 12 | if defLittleEndian != isLittleEndian: 13 | `swapEndian n` o.addr, i.addr 14 | else: elseDo 15 | using d: NPointer 16 | template genGS*(g, s, T; n: int){.dirty.} = 17 | proc g*(d; i: int): T = 18 | (cast[ptr T](cast[int](d) + i))[] 19 | 20 | proc s*(d; i: int; v: T) = 21 | (cast[ptr T](cast[int](d) + i))[] = v 22 | 23 | proc g*(d; i: int; isLittleEndian: bool): T = 24 | let res = d.g i 25 | swapIfNeed n, result, res: 26 | result = res 27 | 28 | proc s*(d; i: int; v: T; isLittleEndian: bool) = 29 | var vv: T 30 | swapIfNeed n, vv, v: 31 | vv = v 32 | d.s(i, vv) 33 | template genIGS*(n){.dirty.} = 34 | genGS `getUint n`, `setUint n`, `uint n`, n 35 | genGS `getInt n`, `setInt n`, `int n`, n 36 | template genFGS*(n){.dirty.} = 37 | genGS `getFloat n`, `setFloat n`, `float n`, n 38 | genIGS 8 39 | genIGS 16 40 | genIGS 32 41 | genIGS 64 42 | 43 | genFGS 32 44 | genFGS 64 45 | 46 | when isMainModule: 47 | var p = alloc0(4) 48 | echo p.repr 49 | echo p.getint8 0 50 | p.setUint16 2, 1 51 | echo p.getint16(2) 52 | dealloc p 53 | p = nil 54 | 55 | -------------------------------------------------------------------------------- /Modules/posixmodule/utils.nim: -------------------------------------------------------------------------------- 1 | 2 | import std/strformat 3 | import ../../Objects/[ 4 | pyobjectBase, 5 | exceptions, 6 | stringobject, 7 | byteobjects, 8 | noneobject, 9 | ] 10 | import ../../Objects/typeobject/apis/attrs 11 | import ../../Include/internal/pycore_global_strings 12 | import ../../Python/call 13 | 14 | 15 | proc PyOS_FSPath*(path: PyObject): PyObject = 16 | ##[ 17 | Return the file system path representation of the object. 18 | 19 | If the object is str or bytes, then allow it to pass through with 20 | an incremented refcount. If the object defines __fspath__(), then 21 | return the result of that method. All other types raise a TypeError. 22 | ]## 23 | #[ For error message reasons, this function is manually inlined in 24 | path_converter(). ]# 25 | 26 | if path.ofPyStrObject or path.ofPyBytesObject: 27 | return path 28 | 29 | var fun = PyObject_LookupSpecial(path, pyDUId(fspath)) 30 | if fun.isNil or fun.isPyNone: 31 | return newTypeError newPyStr( 32 | fmt"expected str, bytes or os.PathLike object, not {path.typeName:.200s}") 33 | 34 | let path_repr = call(fun) 35 | if path_repr.isThrownException: 36 | return path_repr 37 | 38 | if not (path_repr.ofPyStrObject or path_repr.ofPyBytesObject): 39 | return newTypeError newPyStr( 40 | fmt"expected {path.typeName:.200s}.__fspath__() to return str or bytes, "& 41 | fmt"not {path.typeName:.200s}") 42 | 43 | return path_repr 44 | -------------------------------------------------------------------------------- /Modules/unicodedata/decimalAndSpace.nim: -------------------------------------------------------------------------------- 1 | 2 | 3 | from std/unicode import `<%` 4 | include ./common_h 5 | import ./[rune_decl, decimal, space] 6 | import ../../Utils/castChar 7 | 8 | const AsciiDigits = "0123456789" 9 | proc transformDecimalAndSpaceToASCII*(unicodeStr: openArray[Rune]): string = 10 | result = (when declared(newStringUninit): newStringUninit else: newString)(unicodeStr.len) 11 | for i, rune in unicodeStr: 12 | template st(val) = result[i] = val 13 | if rune <% Rune(127): st castChar(rune) 14 | elif rune.isspace(): st ' ' 15 | else: 16 | decimalItOr(rune): 17 | st AsciiDigits[it] 18 | do: 19 | st '?' 20 | 21 | proc transformDecimalAndSpaceToASCII*(unicode: PyStrObject): string = 22 | if unicode.isAscii: unicode.str.asciiStr 23 | else: unicode.str.unicodeStr.transformDecimalAndSpaceToASCII 24 | 25 | proc PyUnicode_TransformDecimalAndSpaceToASCII*(unicode: PyStrObject): PyObject = 26 | ##[ `_PyUnicode_TransformDecimalAndSpaceToASCII` 27 | 28 | Converts a Unicode object holding a decimal value to an ASCII string 29 | for using in int, float and complex parsers. 30 | Transforms code points that have decimal digit property to the 31 | corresponding ASCII digit code points. Transforms spaces to ASCII. 32 | Transforms code points starting from the first non-ASCII code point that 33 | is neither a decimal digit nor a space to the end into '?'. 34 | ]## 35 | if unicode.isAscii: unicode 36 | else: newPyAscii unicode.transformDecimalAndSpaceToASCII 37 | -------------------------------------------------------------------------------- /Objects/moduleobjectImpl.nim: -------------------------------------------------------------------------------- 1 | 2 | import std/strformat 3 | import ./[pyobjectBase, 4 | moduleobject, 5 | exceptions, 6 | ] 7 | export moduleobject 8 | 9 | import ../Python/warnings 10 | import ../Include/modsupport 11 | 12 | proc check_api_version(name: string, module_api_version: ApiVersion): PyBaseErrorObject = 13 | ##[ Check API/ABI version 14 | Issues a warning on mismatch, which is usually not fatal. 15 | Returns 0 if an exception is raised. 16 | ]## 17 | if module_api_version != NPYTHON_API_VERSION and module_api_version != PYTHON_ABI_VERSION: 18 | retIfExc warnEx(pyRuntimeWarningObjectType, 19 | fmt("Python C API version mismatch for module {name:.100s}: " & 20 | "This Python has API version {NPYTHON_API_VERSION}, module {name:.100s} has version {module_api_version}.") 21 | ) 22 | 23 | 24 | 25 | template PyModule_CreateInitialized(T: typedesc[PyObject]; module: PyModuleDef, module_api_version: ApiVersion): PyObject = 26 | ## `_PyModule_CreateInitialized` 27 | bind newPyModuleImpl 28 | (proc (): PyObject = 29 | let tname = module.m_name 30 | retIfExc check_api_version(tname, module_api_version) 31 | newPyModuleImpl(T, module.typ, tname) 32 | )() 33 | 34 | template PyModule_CreateInitialized*(nameId: untyped; module_api_version=NPYTHON_API_VERSION): PyObject = 35 | bind newPyModuleDef 36 | PyModule_CreateInitialized(`Py nameId ModuleObject`, 37 | newPyModuleDef(astToStr(nameId), `py nameId ModuleObjectType`), 38 | module_api_version 39 | ) 40 | -------------------------------------------------------------------------------- /Objects/listobject/sort.nim: -------------------------------------------------------------------------------- 1 | 2 | import std/algorithm 3 | import ../[ 4 | pyobject, 5 | noneobject, 6 | exceptions, 7 | listobject, 8 | ] 9 | import ../pyobject_apis/compare 10 | import ../../Python/call 11 | export listobject 12 | 13 | using self: PyListObject 14 | type SortCmpError = object of CatchableError 15 | template genCmp(exc; key){.dirty.} = 16 | proc cmp(a, b: PyObject): int = 17 | var res: bool 18 | exc = PyObject_RichCompareBool(key a, key b, Py_LT, res) 19 | if not exc.isNil: 20 | raise new SortCmpError 21 | if res: -1 22 | else: 1 23 | template catchAsRet(key; body): untyped = 24 | var exc: PyBaseErrorObject 25 | genCmp exc, key 26 | try: body 27 | except SortCmpError: return exc 28 | 29 | template asIs(x): untyped = x 30 | template sortImpl(self): untyped{.dirty.} = 31 | self.items.sort(cmp=cmp, order=if reversed: Descending else: Ascending) 32 | 33 | proc sort*(self; reversed=false): PyBaseErrorObject = 34 | catchAsRet asIs, self.sortImpl 35 | 36 | proc sort*(self; keyfunc: PyObject, reversed=false): PyBaseErrorObject = 37 | template keyTmpl(x): untyped = 38 | keyfunc.call(x) 39 | if keyfunc.isNil or keyfunc.isPyNone: 40 | return self.sort(reversed) 41 | catchAsRet keyTmpl, self.sortImpl 42 | 43 | 44 | when isMainModule: 45 | import ../numobjects 46 | var ls = newPyList() 47 | ls.add newPyFloat 1.0 48 | ls.add newPyInt 0 49 | let exc = ls.sort 50 | if not exc.isNil: 51 | echo "Error:" 52 | echo exc 53 | else: 54 | echo ls 55 | 56 | -------------------------------------------------------------------------------- /Python/bltinmodule/compile_eval_exec.nim: -------------------------------------------------------------------------------- 1 | 2 | 3 | import ../../Objects/[ 4 | stringobject/codec, 5 | exceptions, 6 | ] 7 | import ../pythonrun/compile 8 | import ../getargs/[ 9 | dispatch, paramsMeta, 10 | ] 11 | 12 | proc compile*( 13 | source: PyObject, filename{.convertVia(PyUnicode_FSDecoder).}: PyStrObject, 14 | mode: string, flags: int = 0, 15 | dont_inherit=false, optimize = -1, 16 | feature_version{.startKwOnly, AsPyParam"_feature_version".} = -1): PyObject{.bltin_clinicGen.} = 17 | #const start = Mode.toSeq 18 | var cf = initPyCompilerFlags() 19 | let flags = typeof(cf.flags)(flags) 20 | cf.flags = flags | PyCF.SOURCE_IS_UTF8 21 | if feature_version >= 0 and (flags & PyCF.ONLY_AST): 22 | cf.feature_version = feature_version 23 | 24 | if optimize < -1 or optimize > 2: 25 | return newValueError newPyAscii"compile(): invalid optimize value" 26 | 27 | var emode: Mode 28 | if not parseModeEnum($mode, emode): 29 | #TODO:_ast "compile() mode 'func_type' requires flag PyCF_ONLY_AST" 30 | return newValueError newPyAscii("compile() mode must be 'exec', 'eval' or 'single'") 31 | 32 | #let is_ast = source.ofPyASTObject #TODO:_ast 33 | var source_copy: PyObject 34 | var str: string 35 | retIfExc Py_SourceAsString(source, "compile", "string, bytes or AST", cf, source_copy, str) 36 | 37 | Py_CompileStringObject(str, filename, emode, cf, optimize) 38 | 39 | template reg(f) = 40 | registerBltinFunction astToStr(f), `builtin f` 41 | template register_compile_eval_exec* = 42 | reg compile 43 | -------------------------------------------------------------------------------- /Objects/abstract/args.nim: -------------------------------------------------------------------------------- 1 | 2 | import ../pyobject 3 | from ../numobjects/intobject/decl import PyIntObject 4 | from ../numobjects/intobject/ops_imp_warn import PyNumber_AsSsize_t, PyNumber_Index 5 | from ../numobjects/intobject/idxHelpers import getClampedIndex 6 | template optionalTLikeArg[T](args; i: int, def: T; mapper): T = 7 | if args.len > i: mapper args[i] 8 | else: def 9 | 10 | template numAsIntOrRetE*(x: PyObject): int = 11 | ## interpret int or int-able object `x` to `system.int` 12 | bind PyNumber_AsSsize_t 13 | var res: int 14 | let e = x.PyNumber_AsSsize_t res 15 | if not e.isNil: 16 | return e 17 | res 18 | 19 | template numAsClampedIndexOrRetE*(x: PyObject; size: int): int = 20 | ## interpret int or int-able object `x` to `system.int`, clamping result in `0..] = None` 30 | bind optionalTLikeArg, numAsIntOrRetE 31 | optionalTLikeArg(args, i, def, numAsIntOrRetE) 32 | 33 | template clampedIndexOptArgAt*(args: openArray[PyObject]; i: int, def: int, size: int): int = 34 | ## parse arg `x: Optional[] = None`, clamped result in `0.. (2,3,1): 38 | iterator extraAttrs*(tok: ExceptionToken): NimNode = 39 | ExcAttrs.withValue(tok, value): 40 | for n in value.split(','): 41 | yield ident n 42 | else: 43 | # nim-lang/Nim#25162 44 | iterator extraAttrs*(tok: ExceptionToken): NimNode = 45 | let value = ExcAttrs.getOrDefault(tok, "") 46 | if value != "": 47 | for n in value.split(','): 48 | yield ident n 49 | 50 | 51 | type BaseExceptionToken*{.pure.} = enum 52 | ## subclasses of `BaseException` except `Exception` and `BaseExceptionGroup` 53 | BaseException = 0 54 | SystemExit GeneratorExit KeyboardInterrupt 55 | 56 | iterator extraAttrs*(tok: BaseExceptionToken): NimNode = 57 | # only SystemExit has a attr: code 58 | if tok == SystemExit: yield ident"code" 59 | -------------------------------------------------------------------------------- /Parser/lexerTypes.nim: -------------------------------------------------------------------------------- 1 | 2 | import token 3 | import ../Utils/[utils] 4 | 5 | type 6 | Mode* {.pure.} = enum 7 | Single = "single" 8 | File = "exec" 9 | Eval = "eval" 10 | #[ 11 | LexerState* = enum 12 | E_OK = 10 13 | E_EOF = 11 14 | E_DONE = 16 15 | E_TABSPACE = (18, "Inconsistent mixing of tabs and spaces") 16 | E_LINECONT = (25, "Unexpected characters after a line continuation") 17 | E_EOFS = (23, "EOF in triple-quoted string") 18 | ]# 19 | LexerEscaper* = proc (s: string): string{.raises: [SyntaxError].} 20 | Lexer* = ref object 21 | ## For CPython 3.13, this is roughly equal to `tok_state*` 22 | indentStack: seq[int] # Stack to track indentation levels 23 | lineNo: int 24 | tokenNodes*: seq[TokenNode] # might be consumed by parser 25 | fileName*: string 26 | 27 | tripleStr*: tuple[ 28 | within: bool, 29 | val: string, 30 | quote: char, # ' or " 31 | tokenKind: Token, # String or Bytes 32 | escape: LexerEscaper, 33 | start: tuple[ 34 | lineNo, colNo: int, 35 | ] 36 | ] ## is handling triple string (multiline string) 37 | 38 | proc parseModeEnum*(s: string, res: var Mode): bool = 39 | template ret(x) = 40 | res = Mode.x 41 | return true 42 | case s 43 | of "single": ret Single 44 | of "exec": ret File 45 | of "eval": ret Eval 46 | else: return false 47 | 48 | proc cont*(lexer: Lexer): bool{.inline.} = lexer.tripleStr.within 49 | 50 | proc lineNo*(lexer: Lexer): var int{.inline.} = lexer.lineNo 51 | 52 | proc indentStack*(lexer: Lexer): var seq[int]{.inline.} = lexer.indentStack 53 | -------------------------------------------------------------------------------- /Objects/funcobject.nim: -------------------------------------------------------------------------------- 1 | import pyobject 2 | import codeobject 3 | import baseBundle 4 | import dictobject 5 | import tupleobject 6 | 7 | declarePyType Function(tpToken): 8 | name{.dunder_member,readonly.}: PyStrObject 9 | code{.dunder_member,readonly.}: PyCodeObject 10 | globals{.dunder_member,readonly.}: PyDictObject 11 | closure{.dunder_member,readonly.}: PyTupleObject # could be nil 12 | 13 | # forward declaretion 14 | declarePyType BoundMethod(tpToken): 15 | fun: PyFunctionObject 16 | self: PyObject 17 | 18 | 19 | proc newPyFunc*(name: PyStrObject, 20 | code: PyCodeObject, 21 | globals: PyDictObject, 22 | closure: PyObject = nil): PyFunctionObject = 23 | result = newPyFunctionSimple() 24 | result.name = name 25 | result.code = code 26 | result.globals = globals 27 | if not closure.isNil: 28 | assert closure.ofPyTupleObject() 29 | result.closure = PyTupleObject(closure) 30 | 31 | 32 | proc newBoundMethod*(fun: PyFunctionObject, self: PyObject): PyBoundMethodObject = 33 | result = newPyBoundMethodSimple() 34 | result.fun = fun 35 | result.self = self 36 | 37 | 38 | implFunctionMagic get: 39 | newBoundMethod(self, other) 40 | 41 | 42 | implBoundMethodMagic get: 43 | self 44 | 45 | declarePyType StaticMethod(): 46 | callable: PyObject 47 | 48 | implStaticMethodMagic get: 49 | self.callable 50 | 51 | implStaticMethodMagic init(callable: PyObject): 52 | self.callable = callable 53 | pyNone 54 | 55 | proc newPyStaticMethod*(callable: PyObject): PyStaticMethodObject = 56 | result = newPyStaticMethodSimple() 57 | result.callable = callable 58 | -------------------------------------------------------------------------------- /Objects/abstract/number.nim: -------------------------------------------------------------------------------- 1 | 2 | 3 | import ../[ 4 | pyobject, 5 | exceptions, 6 | stringobject, 7 | numobjects, 8 | ] 9 | import ../../Include/cpython/pyerrors 10 | export PyNumber_Index, PyNumber_AsSsize_t, PyNumber_AsClampedSsize_t 11 | import ./op_helpers 12 | 13 | proc PyNumber_ToBase*(n: PyObject, base: uint8): PyObject = 14 | if base not_in {2u8, 8, 10, 16}: 15 | return newSystemError newPyAscii"PyNumber_ToBase: base must be 2, 8, 10 or 16" 16 | let index = privatePyNumber_Index(n) 17 | retIfExc index 18 | var s: string 19 | retIfExc PyIntObject(index).format(base, s) 20 | newPyAscii s 21 | 22 | proc PyLong_AsLongAndOverflow*(vv: PyObject, overflow: var IntSign, res: var int): PyBaseErrorObject = 23 | if vv.isNil: return PyErr_BadInternalCall() 24 | res = PyIntObject( 25 | if vv.ofPyIntObject: vv 26 | else: 27 | let ret = PyNumber_Index(vv) 28 | retIfExc ret 29 | ret 30 | ).toInt overflow 31 | 32 | template genBOp(op; opname){.dirty.} = 33 | binary_func(`PyNumber op`, op, opname) 34 | template genUOp(op; pyopname){.dirty.} = 35 | proc `PyNumber pyopname`*(v: PyObject): PyObject = v.callMagic(op) 36 | 37 | genBOp add, "+" 38 | genBOp sub, "-" 39 | genBOp mul, "*" 40 | genBOp truediv, "/" ## PyNumber_TrueDivide 41 | genBOp floorDiv, "//" ## PyNumber_FloorDivide 42 | genBOp pow, "** or pow()" ## PyNumber_PowerNoMod 43 | genBOp Mod, "%" ## PyNumber_Remainder 44 | 45 | 46 | genBOp lshift, "<<" 47 | genBOp rshift, ">>" 48 | genBOp And, "&" 49 | genBOp Or, "|" 50 | genBOp Xor, "^" 51 | 52 | genUOp abs, Absolute 53 | genUOp negative, Negative 54 | genUOp positive, Positive 55 | genUOp invert, Invert 56 | -------------------------------------------------------------------------------- /Include/cpython/critical_section.nim: -------------------------------------------------------------------------------- 1 | 2 | 3 | import ../../Objects/pyobjectBase 4 | 5 | #[ 6 | const SingleThread = not compileOption("threads") 7 | 8 | when SingleThread: 9 | template withPyCriticalSection*(_; body) = block: body 10 | else: 11 | type 12 | PyCriticalSection = object 13 | ##[NOTE: the contents of this struct are private and may change betweeen 14 | Python releases without a deprecation period. 15 | ]## 16 | prev: uint 17 | mutex: PyMutex 18 | PyCriticalSection2 = object of PyCriticalSection 19 | ##[A critical section protected by two mutexes. Use 20 | Py_BEGIN_CRITICAL_SECTION2 and Py_END_CRITICAL_SECTION2. 21 | NOTE: the contents of this struct are private and may change betweeen 22 | Python releases without a deprecation period.]## 23 | mutex2: PyMutex 24 | 25 | template withPyCriticalSection*(op; body) = 26 | block: 27 | var pycs: PyCriticalSection 28 | 29 | body 30 | using c: PyCriticalSection 31 | proc beginMutex(c; m: PyMutex) = 32 | 33 | 34 | proc PyCriticalSection_Begin(c; op: PyObject) = 35 | beginMutex(c, op.mutex) 36 | ]# 37 | 38 | #TODO:mutex 39 | template criticalRead*(self: PyObject; body) = 40 | if self.writeLock: 41 | return newLockError newPyAscii"Read failed because object is been written." 42 | inc self.readNum 43 | try: body 44 | finally: 45 | dec self.readNum 46 | 47 | template criticalWrite*(self: PyObject; body) = 48 | if 0 < self.readNum or self.writeLock: 49 | return newLockError newPyAscii"Write failed because object is been read or written." 50 | self.writeLock = true 51 | try: body 52 | finally: 53 | self.writeLock = false 54 | -------------------------------------------------------------------------------- /Python/pythonrun/compile.nim: -------------------------------------------------------------------------------- 1 | 2 | 3 | import std/strformat 4 | import ../../Include/cpython/compile as compile_h 5 | import ../../Objects/[pyobjectBase, 6 | stringobjectImpl, exceptions, 7 | byteobjects, 8 | ] 9 | import ../../Parser/[ 10 | lexerTypes, apis, 11 | ] 12 | import ../../Python/[asdl, compile] 13 | 14 | export lexerTypes, compile_h, PyObject, stringobjectImpl 15 | 16 | 17 | proc Py_SourceAsString*(cmd: PyObject, funcname, what: string, cf: PyCompilerFlags, cmd_copy: var PyObject; res: var string): PyBaseErrorObject = 18 | ## `_Py_SourceAsString` 19 | cmd_copy = nil 20 | res = if cmd.ofPyStrObject: 21 | cf.flags = cf.flags | PyCF.IGNORE_COOKIE 22 | PyStrObject(cmd).asUTF8 23 | elif cmd.ofPyBytesObject: 24 | PyBytesObject(cmd).asString 25 | elif cmd.ofPyByteArrayObject: 26 | PyByteArrayObject(cmd).asString 27 | else: 28 | #TODO:buffer 29 | return newTypeError newPyStr fmt"{funcname}() arg 1 must be a {what} object" 30 | if '\0' in res: 31 | return newSyntaxError newPyAscii"source code string cannot contain null bytes" 32 | 33 | 34 | proc Py_CompileStringObject*(str: string, filename: PyStrObject, mode: Mode; flags=initPyCompilerFlags(), optimize = -1): PyObject = 35 | var modu: Asdlmodl 36 | 37 | retIfExc PyParser_ASTFromString(str, filename, mode, flags, modu) 38 | if not flags.isNil and ((flags.flags.cint and PyCF.ONLY_AST.cint) == PyCF.ONLY_AST.cint): 39 | # unoptiomized AST 40 | #let syntax_check_only = flags.flags & PyCF.OPTIMIZED_AST 41 | result = newNotImplementedError newPyAscii"TODO:ast not impl" 42 | #result = PyAST_mod2obj modu 43 | return 44 | compile(modu, filename, flags, optimize) 45 | 46 | -------------------------------------------------------------------------------- /Modules/unicodedata/test_decimalAndSpace.nim: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | ## this file is also runnable via Python 3 | ## and it should be run via python for one time when it's time to test to update 4 | 5 | """\""".`!=`("").assert 6 | import ./rune_decl 7 | import ./[decimal, space] 8 | import ./private/consts 9 | when true: 10 | template range(a, b: int): untyped = a.. 1: 4 | from std/paths import `/../`, Path, parentDir 5 | template `/../`(a, b: string): untyped = string(Path(a) /../ Path b) 6 | template parentDir(a: string): untyped = string(Path(a).parentDir) 7 | else: 8 | from std/os import `/../`, parentDir 9 | import ./os_findExe_patch 10 | from std/strutils import stripLineEnd 11 | 12 | ## see CPython/configure.ac 13 | 14 | const gitExe{.strdefine: "git".} = findExe("git") 15 | const git = (exe: gitExe) 16 | when git.exe == "": 17 | template exec(git; sub: string): string = "" 18 | else: 19 | const srcdir_git = currentSourcePath().parentDir /../ ".git" 20 | template exec(git: typeof(git); sub: string): string = 21 | bind srcdir_git 22 | let res = gorgeEx(git.exe & " --git-dir " & srcdir_git & " " & sub) 23 | assert res.exitCode == 0, res.output 24 | var outp = res.output 25 | outp.stripLineEnd 26 | outp 27 | 28 | const 29 | version = git.exec"rev-parse --short HEAD" 30 | tag = git.exec"describe --all --always --dirty" 31 | branch = git.exec"name-rev --name-only HEAD" 32 | 33 | proc gitversion*: string = version ## Py_gitversion 34 | proc gitidentifier*: string = 35 | result = tag 36 | if result != "" and result != "undefined": 37 | return 38 | result = branch 39 | 40 | 41 | proc getBuildInfo: string{.compileTime.} = 42 | let revision = version 43 | result = gitidentifier() 44 | if revision != "": 45 | result.add ':' 46 | result.add revision 47 | 48 | result.add ", " 49 | 50 | #result.add &"{CompileDate:.20s}, {CompileTime:.9s}" 51 | result.add CompileDate.substr(0, 19) 52 | result.add ", " 53 | result.add CompileTime.substr(0, 8) 54 | 55 | const buildinfo = getBuildInfo() 56 | proc Py_GetBuildInfo*: string = buildinfo 57 | -------------------------------------------------------------------------------- /Python/sysmodule/audit.nim: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | import std/macros 5 | 6 | import ../[ 7 | modsupport, 8 | call, 9 | ] 10 | import ../../Objects/[ 11 | pyobject, 12 | stringobject, 13 | tupleobject, 14 | exceptions, 15 | ] 16 | type Py_AuditHookFunction* = 17 | proc(event: cstring, eventArg: PyTupleObject, userData: pointer 18 | ): PyBaseErrorObject{.raises: [].} ## XXX: CPython's returns `cint`, 19 | ## but we use `PyBaseErrorObject` to avoid global Exception. 20 | 21 | var 22 | audit_hooks: seq[PyObject] ## PyInterpreterState.audit_hooks 23 | rt_audit_hooks: seq[ 24 | tuple[ 25 | hookCFunction: Py_AuditHookFunction, 26 | userData: pointer 27 | ] 28 | ] ## PyRuntimeState.audit_hooks 29 | 30 | proc auditTupleImpl(event: cstring, eventStr: PyStrObject, args: PyTupleObject): PyBaseErrorObject = 31 | for i in rt_audit_hooks: 32 | retIfExc i.hookCFunction(event, args, i.userData) 33 | for i in audit_hooks: 34 | retIfExc fastCall(i, [eventStr, args]) 35 | proc auditTuple*(event: cstring, args: PyTupleObject): PyBaseErrorObject = 36 | auditTupleImpl event, newPyAscii(event), args 37 | 38 | macro auditImpl(event: cstring, args: typed): PyBaseErrorObject = 39 | let tup = Py_VaBuildTuple(args) 40 | newCall(bindSym("auditTuple"), event, tup) 41 | 42 | template audit*(event: cstring, args: varargs[typed]): PyBaseErrorObject = auditImpl(event, args) 43 | 44 | #proc sys_audit*(event: PyStrObject, *args): PyObject = auditImpl(event, args) 45 | 46 | proc addaudithook*(hook: Py_AuditHookFunction, userData: pointer = nil): PyBaseErrorObject = 47 | ## `PySys_AddAuditHook` 48 | retIfExc audit"sys.addaudithook" 49 | rt_audit_hooks.add (hook, userData) 50 | 51 | 52 | proc addaudithook*(hook: PyObject): PyBaseErrorObject = 53 | retIfExc audit"sys.addaudithook" 54 | audit_hooks.add hook 55 | -------------------------------------------------------------------------------- /Python/sysmodule/hooks.nim: -------------------------------------------------------------------------------- 1 | 2 | import ../../Objects/[ 3 | pyobjectBase, exceptions, 4 | noneobject, 5 | stringobject, 6 | ] 7 | import ../../Objects/pyobject_apis/[attrs, io] 8 | import ../pythonrun/pyerr_display 9 | import ../pyimport/utils 10 | import ../../Include/internal/pycore_global_strings 11 | import ../../Utils/[fileio,] 12 | 13 | proc excepthook*(exctype: PyObject, value: PyBaseErrorObject, traceback: PyObject) = 14 | PyErr_Display(nil, value, traceback) 15 | 16 | proc displayhook_impl*(o: PyObject): PyBaseErrorObject = 17 | var builtins: PyObject 18 | retIfExc PyImport_GetModule(pyId(builtins), builtins) 19 | if builtins.isNil: 20 | return newRuntimeError newPyAscii"lost builtins module" 21 | retIfExc builtins 22 | 23 | # Print value except if None 24 | # After printing, also assign to '_' 25 | # Before, set '_' to None to avoid recursion 26 | if o.isPyNone: return 27 | retIfExc PyObject_SetAttr(builtins, newPyAscii('_'), pyNone) 28 | 29 | #TODO:sys.stdout 30 | #TODO:encoding 31 | #[ 32 | var outf: PyObject 33 | retIfExc PySys_GetAttr(pyId(stdout), outf) 34 | if outf.isPyNone: 35 | return newRuntimeError newPyAscii"lost sys.stdout" 36 | let res = PyFile_WriteObject(o, outf) 37 | if res.ofPyUnicodeEncodeErrorObject: 38 | #[/* repr(o) is not encodable to sys.stdout.encoding with 39 | * sys.stdout.errors error handler (which is probably 'strict') */]# 40 | retIfExc sys_displayhook_unencodable(outf, o) 41 | retIfExc res 42 | retIfExc PyFile_WriteObject(newPyAscii('\n'), outf, Py_PRINT_RAW) 43 | ]# 44 | 45 | 46 | let res = PyObject_Println(o, fileio.stdout) 47 | retIfExc res 48 | 49 | retIfExc PyObject_SetAttr(builtins, newPyAscii('_'), o) 50 | 51 | proc displayhook*(o: PyObject): PyObject = 52 | retIfExc displayhook_impl(o) 53 | return pyNone 54 | -------------------------------------------------------------------------------- /Objects/pyobject_apis/io.nim: -------------------------------------------------------------------------------- 1 | 2 | import ../../Include/internal/pycore_ceval_rec 3 | import ../../Include/ceval 4 | import ../../Utils/fileio 5 | import ../../Utils/[intflags,] 6 | export intflags 7 | import ../[ 8 | pyobjectBase, 9 | exceptions, 10 | stringobject, 11 | ] 12 | import ../exceptions/ioerror 13 | import ./strings 14 | 15 | 16 | # Flag bits for printing: 17 | declareIntFlag PyPrintFlags: 18 | Py_PRINT_REPR = 0 19 | Py_PRINT_RAW = 1 ## No string quotes etc. (using str over repr) 20 | 21 | template PyObject_WriteImpl(writePrc) = 22 | ## variant of PyObject_Print that also appends an '\n' 23 | #TODO:PyErr_CheckSignals 24 | when declared(PyErr_CheckSignals): 25 | retIfExc PyErr_CheckSignals() #TODO:signal 26 | withNoRecusiveCallOrRetE " printing an object": 27 | #clearerr(fp) 28 | template writeImpl = 29 | let s = if flags & Py_PRINT_RAW: 30 | PyObject_StrNonNil(op) 31 | else: 32 | PyObject_ReprNonNil(op) 33 | retIfExc s 34 | assert s.ofPyStrObject 35 | let t = PyStrObject(s).asUTF8() 36 | writePrc fp, t 37 | try: 38 | if op.isNiL: 39 | pyAllowThreads: 40 | writePrc fp, "" 41 | else: 42 | when compiles(Py_REFCNT(op)): 43 | let cnt = Py_REFCNT(op) 44 | if cnt <= 0: 45 | pyAllowThreads: 46 | writePrc fp, "" 47 | else: writeImpl 48 | else: writeImpl 49 | except IOError as e: 50 | result = newIOError e #TODO:OSError PyErr_SetFromErrno(pyOSErrorObjectType) 51 | #clearerr(fp) 52 | 53 | proc PyObject_Println*(op: PyObject, fp: fileio.File, flags: IntFlag[PyPrintFlags] = Py_PRINT_REPR): PyBaseErrorObject = 54 | ## variant of PyObject_Print that also appends an '\n' 55 | PyObject_WriteImpl writeLine 56 | -------------------------------------------------------------------------------- /Utils/compat_io_os.nim: -------------------------------------------------------------------------------- 1 | 2 | const Js = defined(js) 3 | 4 | template jsOr(a, b): untyped = 5 | when Js: a else: b 6 | 7 | when Js: 8 | import std/jsffi 9 | import ./jsdispatch 10 | proc bufferAsString(buf: JsObject): string = 11 | let n = buf.length.to int 12 | when declared(newStringUninit): 13 | result = newStringUninit(n) 14 | for i in 0.. integer conversion. 25 | '0' maps to 0, ..., '9' maps to 9. 26 | 'a' and 'A' map to 10, ..., 'z' and 'Z' map to 35. 27 | All other indices map to 37. 28 | Note that when converting a base B string, a char c is a legitimate 29 | base B digit iff PyLong_DigitValue[c] < B. 30 | ]## 31 | 32 | func isDigitOfBase*(c: char; base: uint8): bool = PyLong_DigitValue[c] < base 33 | 34 | template digitOr*(c: char; base: uint8; fallback): uint8 = 35 | bind PyLong_DigitValue 36 | let d = PyLong_DigitValue[c] 37 | if d >= base: fallback 38 | d 39 | -------------------------------------------------------------------------------- /Include/cpython/compile.nim: -------------------------------------------------------------------------------- 1 | 2 | import ../../Python/versionInfo 3 | import ../../Utils/intflags 4 | 5 | declareIntFlag PyCodeFutureOption: 6 | DIVISION = 0x020000 ## CO_FUTURE_DIVISION 7 | ABSOLUTE_IMPORT = 0x040000 ## CO_FUTURE_ABSOLUTE_IMPORT 8 | WITH_STATEMENT = 0x080000 ## CO_FUTURE_WITH_STATEMENT 9 | PRINT_FUNCTION = 0x100000 ## CO_FUTURE_PRINT_FUNCTION 10 | UNICODE_LITERALS = 0x200000 ## CO_FUTURE_UNICODE_LITERALS 11 | BARRY_AS_BDFL = 0x400000 ## CO_FUTURE_BARRY_AS_BDFL 12 | GENERATOR_STOP = 0x800000 ## CO_FUTURE_GENERATOR_STOP 13 | ANNOTATIONS = 0x1000000 ## CO_FUTURE_ANNOTATIONS 14 | 15 | const PyCF_ONLY_AST = 0x0400 16 | declareIntFlag PyCF: 17 | SOURCE_IS_UTF8 = 0x0100 18 | DONT_IMPLY_DEDENT = 0x0200 19 | ONLY_AST = PyCF_ONLY_AST 20 | IGNORE_COOKIE = 0x0800 21 | TYPE_COMMENTS = 0x1000 22 | ALLOW_TOP_LEVEL_AWAIT = 0x2000 23 | ALLOW_INCOMPLETE_INPUT= 0x4000 24 | OPTIMIZED_AST = (0x8000 or PyCF_ONLY_AST) 25 | 26 | type 27 | PyCFlag = IntFlag[PyCodeFutureOption|Py_CF] 28 | PyCompilerFlagsObj = object 29 | ## CPython's PyCompilerFlags 30 | flags*: PyCFlag ## bitmask of CO_xxx flags relevant to future 31 | feature_version*: int ## minor Python version (PyCF_ONLY_AST) 32 | PyCompilerFlags* = ref PyCompilerFlagsObj 33 | #[bpo-39562: CO_FUTURE_ and PyCF_ constants must be kept unique. 34 | PyCF_ constants can use bits from 0x0100 to 0x10000. 35 | CO_FUTURE_ constants use bits starting at 0x20000]# 36 | 37 | template genMixOp(typ){.dirty.} = 38 | proc `&`*(a: PyCFlag, b: typ): bool = (cint(a) and cint(b)) != 0 39 | proc `|`*(a: PyCFlag, b: typ): PyCFlag = PyCFlag(cint(a) or cint(b)) 40 | 41 | genMixOp PyCodeFutureOption 42 | genMixOp PyCF 43 | 44 | template initPyCompilerFlags*(f = PyCFlag(0); fv = PyMinor): PyCompilerFlags = 45 | PyCompilerFlags(flags: f, feature_version: fv) 46 | -------------------------------------------------------------------------------- /Python/sysmodule/init.nim: -------------------------------------------------------------------------------- 1 | 2 | import ./[initUtils, 3 | decl, initInfo, 4 | ] 5 | #TODO 6 | import ../coreconfig 7 | 8 | import ../../Objects/[ 9 | exceptions, 10 | dictobject, 11 | moduleobjectImpl, 12 | ] 13 | export Py_Int_Float_InitTypes 14 | 15 | import ../../Include/internal/pycore_initconfig 16 | 17 | proc PySys_UpdateConfig*(sysmod: var PySysModuleObject, config: PyConfig): PyBaseErrorObject = 18 | ##[ `_PySys_UpdateConfig` 19 | 20 | Update sys attributes for a new PyConfig configuration. 21 | This function also adds attributes that _PySys_InitCore() didn't add. 22 | ]## 23 | template COPY_FIELD_LIST(f, id) = 24 | sysmod.f = typeof(sysmod.f) !asList(config.id) 25 | template COPY_LIST(keyId, valueF) = 26 | when compiles(sysmod.keyId): 27 | COPY_FIELD_LIST(keyId, valueF) 28 | else: 29 | SET_SYS(astToStr(key), asList(config.valueF)) 30 | template COPY_LIST(id) = COPY_LIST(id, id) 31 | 32 | #template COPY_FIELD_LIST(id) = COPY_FIELD_LIST(id, id) 33 | #COPY_WSTR 34 | let sysdict = sysmod.getDict 35 | 36 | #template COPY_STR(attr, s) = SET_SYS(KEY, newPyStr(s)) 37 | template COPY_STR(id) = SET_SYS(astToStr(id), config.id) 38 | 39 | COPY_LIST path, module_search_paths 40 | 41 | COPY_STR executable 42 | 43 | COPY_LIST argv 44 | COPY_LIST orig_argv 45 | 46 | template PyImport_InitModules(): PyDictObject = newPyDict() #TODO:import_mod 47 | proc PySys_Create*(sysmod: var PySysModuleObject): PyBaseErrorObject = 48 | ##[`_PySys_Create` called by pylifecycle:pycore_interp_init 49 | 50 | Create sys module without all attributes. 51 | PySys_UpdateConfig() should be called later to add remaining attributes.]## 52 | let modules = PyImport_InitModules() 53 | let sysmodE = PyModule_CreateInitialized(sys) 54 | retIfExc sysmodE 55 | sysmod = PySysModuleObject sysmodE 56 | 57 | let sysdict = sysmod.getDict() 58 | 59 | #TODO:io 60 | 61 | retIfExc initCore(sysdict) 62 | 63 | sysmod.modules = modules 64 | 65 | #TODO:monioring 66 | 67 | -------------------------------------------------------------------------------- /Python/bltinmodule/unarys.nim: -------------------------------------------------------------------------------- 1 | 2 | 3 | import std/strformat 4 | import ../../Objects/[ 5 | pyobject, 6 | tupleobjectImpl, 7 | exceptions, 8 | stringobjectImpl, 9 | byteobjects, 10 | boolobject, 11 | numobjects/intobject, 12 | abstract/number, 13 | pyobject_apis/typeCheck, 14 | ] 15 | import ./utils 16 | 17 | import ../getargs/[kwargs, tovals, dispatch] 18 | 19 | proc abs*(c: PyObject): PyObject{.bltin_clinicGen.} = PyNumber_Absolute c 20 | proc ord*(c: PyObject): PyObject{.bltin_clinicGen.} = 21 | var size: int 22 | var typeChecked = false 23 | template chk(T; I) = 24 | if not typeChecked and c.`ofPy T Object`: 25 | let obj = `Py T Object`(c) 26 | size = obj.len 27 | if size == 1: 28 | let o = obj[0] 29 | return newPyInt cast[I](o) 30 | typeChecked = true 31 | chk Bytes, int 32 | chk Str, int32 33 | chk ByteArray, int 34 | if not typeChecked: 35 | return newTypeError newPyStr fmt"ord() expected a string of length 1, but {c.typeName:.200s} found" 36 | return newTypeError newPyStr fmt"ord() expected a character, but string of length {size} found" 37 | 38 | proc bin*(n: PyObject): PyObject{.bltin_clinicGen.} = PyNumber_ToBase n, 2 39 | proc oct*(n: PyObject): PyObject{.bltin_clinicGen.} = PyNumber_ToBase n, 8 40 | proc hex*(n: PyObject): PyObject{.bltin_clinicGen.} = PyNumber_ToBase n, 16 41 | 42 | proc chr*(c: PyObject): PyObject{.bltin_clinicGen.} = 43 | var overflow: IntSign 44 | var v: int 45 | retIfExc PyLong_AsLongAndOverflow(c, overflow, v) 46 | case overflow 47 | of Negative: v = low int 48 | of Positive: v = high int 49 | # Allow PyUnicode_FromOrdinal() to raise an exception 50 | of Zero: discard 51 | PyUnicode_FromOrdinal v 52 | 53 | proc callable*(obj: PyObject): PyObject{.bltin_clinicGen.} = newPyBool obj.ofPyCallable 54 | 55 | template register_unarys* = 56 | bind regfunc 57 | regfunc abs 58 | regfunc ord 59 | regfunc bin 60 | regfunc oct 61 | regfunc hex 62 | regfunc chr 63 | regfunc callable 64 | 65 | -------------------------------------------------------------------------------- /Utils/rtarrays/jsArrays.nim: -------------------------------------------------------------------------------- 1 | 2 | import std/macros 3 | import ./utils 4 | 5 | type JsArray*[T] = distinct seq[T] ## Currently, (as of Nim 2.3.1, seq is just js's Array) 6 | 7 | proc newBractetExpr(head, i: NimNode): NimNode = nnkBracketExpr.newTree(head, i) 8 | proc borrowT1Impl(def: NimNode): NimNode = 9 | ## borrow collection with one generic param and (only) the first param is the collection 10 | let 11 | name = def.name 12 | genericsParams = def[2] # GenericParams 13 | params = def.params 14 | self = params[1][0] 15 | assert genericsParams.len == 1 16 | let gDefs = genericsParams[0] # IdentDefs 17 | let T = gDefs[0] 18 | 19 | let castSelf = newCall(bindSym"seq".newBractetExpr T, self) 20 | var call = newCall(name, castSelf) 21 | for i in 2.. ${{ env.deploy-dir }}/CNAME 54 | # We must re-build CNAME as we use 'peaceiris/actions-gh-pages@v4', 55 | # where the old dir (including CNAME) will be purged. 56 | - name: Deploy documents 57 | uses: peaceiris/actions-gh-pages@v4 58 | with: 59 | github_token: ${{ secrets.GITHUB_TOKEN }} 60 | publish_dir: ${{ env.deploy-dir }} 61 | -------------------------------------------------------------------------------- /Modules/unicodedata/decimal.nim: -------------------------------------------------------------------------------- 1 | 2 | from std/algorithm import upperBound 3 | import std/enumerate 4 | import ./rune_decl 5 | import ./private/consts 6 | 7 | template decimalImpl(r: Rune; result: var int; ret) = 8 | let ir = int r 9 | let upperIdx = allZeros.upperBound(ir) 10 | if upperIdx > 0: 11 | # not too small 12 | let idx = upperIdx - 1 13 | result = ir - allZeros[idx] 14 | if result < 10: ret 15 | assert result >= 0 16 | 17 | template ret = return 18 | proc decimal*(r: Rune, default: int): int{.raises: [].} = 19 | decimalImpl r, result, ret 20 | return default 21 | 22 | proc decimal*(r: Rune): range[0..9]{.raises: [ValueError].} = 23 | decimalImpl r, result, ret 24 | raise newException(ValueError, "not a decimal") 25 | 26 | template decimalItOr*(r: Rune; doIt; fallback){.dirty.} = 27 | ## be faster than: 28 | ## ```Nim 29 | ## let it = decimal(r, -1) 30 | ## if it < 0: fallback 31 | ## else: doIt 32 | ## ``` 33 | runnableExamples: 34 | import std/unicode 35 | decimalItOr(r): 36 | echo it, ": a decimal" 37 | do: 38 | echo "not a decimal" 39 | bind decimalImpl 40 | block decimalItOrBlk: 41 | var it: int 42 | template ret = 43 | doIt 44 | break decimalOrBlk 45 | block decimalOrBlk: 46 | decimalImpl r, it, ret 47 | fallback 48 | 49 | iterator allDecimal*(i: range[0..9]): Rune = 50 | ## unstable 51 | for d in allZeros: 52 | yield Rune(d+i) 53 | 54 | proc allDecimals*(i: range[0..9]): seq[Rune] = 55 | ## unstable 56 | runnableExamples: 57 | import std/unicode 58 | let threes = allDecimals(3) 59 | asssert threes[0] == Rune'3' 60 | asssert threes[67] == "🯳".toRunes[0] 61 | result = (when declared(newSeqUninit): newSeqUninit else: newSeq)[Rune](allZeros.len) 62 | for idx, r in enumerate(allDecimal(i)): 63 | result[idx] = r 64 | 65 | when isMainModule: 66 | # dump decimals table 67 | import std/unicode 68 | for n in 0..9: 69 | var res = $n & ": " 70 | for i in allDecimal(n): 71 | res.add $i 72 | res.add ' ' 73 | echo res 74 | -------------------------------------------------------------------------------- /Python/pythonrun/pyerr.nim: -------------------------------------------------------------------------------- 1 | 2 | import ../../Objects/[ 3 | pyobject, 4 | noneobject, exceptions, 5 | stringobject, 6 | ] 7 | from ../errors import PyErr_FormatUnraisable 8 | import ../[ 9 | traceback, call, 10 | ] 11 | import ../../Include/internal/pycore_global_strings 12 | import ../pylifecycle/exit 13 | import ../sysmodule/[attrs, audit, io] 14 | import ./[ 15 | pyerr_display, pyerr_sysexit_keyinter, 16 | ] 17 | import ../../Utils/fileio 18 | 19 | using exc: PyBaseErrorObject 20 | proc handle_system_exit(exc) = 21 | var exitcode: int 22 | if Py_HandleSystemExitAndKeyboardInterrupt(exc, exitcode): 23 | Py_Exit(exitcode) 24 | 25 | proc PyErr_PrintEx*(exc; set_sys_last_vars: bool=false){.pyCFuncPragma.} = 26 | template ss(id, val) = 27 | discard PySys_SetAttrNonNil(pyId id, val) 28 | let 29 | typ = exc.pyType 30 | tb = if exc.traceBacks.len > 0: newPyTraceback exc.traceBacks[^1] else: pyNone 31 | if set_sys_last_vars: 32 | ss last_exc, exc 33 | # Legacy version: 34 | ss last_type, typ 35 | ss last_value, exc 36 | ss last_traceback, tb 37 | var hook: PyObject 38 | var exists: bool 39 | discard PySys_GetOptionalAttr(pyId excepthook, hook, exists) 40 | let e = audit("sys.excepthook", if hook.isNil: pyNone else: hook, typ, exc, tb) 41 | if not e.isNil: 42 | if e.ofPyRuntimeErrorObject: 43 | #goto_done 44 | return 45 | PyErr_FormatUnraisable newPyAscii"Exception ignored in audit hook" 46 | if not hook.isNil: 47 | let res = fastCall(hook, [typ, exc, tb]) 48 | if res.isThrownException: 49 | let exc2 = PyBaseErrorObject res 50 | handle_system_exit(exc2) 51 | stdout.flushFile 52 | PySys_EchoStderr "Error in sys.excepthook:" 53 | PyErr_DisplayException(exc2) 54 | PySys_EchoStderr "\nOriginal exception was:" 55 | PyErr_DisplayException(exc) 56 | else: 57 | assert not exists 58 | PySys_EchoStderr"sys.excepthook is missing" 59 | PyErr_DisplayException(exc) 60 | 61 | template PyErr_Print*(exc: PyBaseErrorObject) = 62 | bind PyErr_PrintEx 63 | PyErr_PrintEx(exc, true) 64 | -------------------------------------------------------------------------------- /Python/traceback.nim: -------------------------------------------------------------------------------- 1 | import strformat 2 | import strutils 3 | import algorithm 4 | 5 | import ../Objects/[ 6 | pyobject, 7 | exceptionsImpl, 8 | stringobject, 9 | ] 10 | import ../Objects/numobjects/intobject_decl 11 | import ../Parser/lexer 12 | import ../Utils/compat 13 | 14 | 15 | proc fmtTraceBack(tb: TraceBack): string = 16 | assert tb.fileName.ofPyStrObject 17 | # lineNo should starts from 1. 0 means not initialized properly 18 | assert tb.lineNo != 0 19 | let fileName = $PyStrObject(tb.fileName).str 20 | var atWhere: string 21 | if tb.funName.isNil: 22 | atWhere = "" 23 | else: 24 | assert tb.funName.ofPyStrObject 25 | atWhere = $(newPyAscii", in " & PyStrObject(tb.funName)) 26 | result &= fmt(" File \"{fileName}\", line {tb.lineNo}{atWhere}\n") 27 | result &= " " & getSource(fileName, tb.lineNo).strip(chars={' '}) 28 | if tb.colNo != -1: 29 | result &= "\n " & "^".indent(tb.colNo) 30 | 31 | 32 | proc printTb*(excp: PyExceptionObject) = 33 | var cur: PyBaseExceptionObject = excp 34 | var excpStrs: seq[string] 35 | while not cur.isNil: 36 | var singleExcpStrs: seq[string] 37 | singleExcpStrs.add "Traceback (most recent call last):" 38 | for tb in cur.traceBacks.reversed: 39 | singleExcpStrs.add tb.fmtTraceBack 40 | let msg = $PyStrObject(tpMagic(BaseError, str)(cur)).str 41 | var head = cur.pyType.name 42 | if msg.len > 0: 43 | head.add ": " 44 | head.add msg 45 | singleExcpStrs.add head 46 | excpStrs.add singleExcpStrs.join("\n") 47 | cur = cur.context 48 | let joinMsg = "\n\nDuring handling of the above exception, another exception occured\n\n" 49 | errEchoCompat excpStrs.reversed.join(joinMsg) 50 | 51 | declarePyType Traceback(): 52 | #TODO:traceback 53 | #tb_next: PyTracebackObject 54 | #tb_frame: PyFrameObject 55 | #tb_lasti{.member, readonly.}: PyIntObject 56 | tb_lineno{.member, readonly.}: PyIntObject 57 | 58 | proc newPyTraceback*(t: TraceBack): PyTracebackObject = 59 | result = newPyTracebackSimple() 60 | #result.colon = newPyInt t.colNo 61 | result.tb_lineno = newPyInt t.lineNo 62 | -------------------------------------------------------------------------------- /Objects/tupleobject.nim: -------------------------------------------------------------------------------- 1 | 2 | import std/macros 3 | import ./[ 4 | pyobject, exceptions, 5 | boolobject, stringobject, 6 | ] 7 | import ./numobjects/intobject/decl 8 | import ../Utils/trans_imp 9 | impExp tupleobject, 10 | decl 11 | 12 | proc isPyTrueObj*(obj: PyObject): bool = system.`==`(obj, pyTrueObj) ## inner 13 | proc tupleSeqToString*(ss: openArray[UnicodeVariant]): UnicodeVariant = 14 | ## inner 15 | ## one-element tuple must be out as "(1,)" 16 | result = newUnicodeUnicodeVariant "(" 17 | case ss.len 18 | of 0: discard 19 | of 1: 20 | result.unicodeStr.add ss[0].toRunes 21 | result.unicodeStr.add ',' 22 | else: 23 | result.unicodeStr.add ss.joinAsRunes", " 24 | result.unicodeStr.add ')' 25 | 26 | template genCollectMagics*(items, 27 | implNameMagic, 28 | ofPyNameObject, PyNameObject, 29 | mutRead, mutReadRepr, seqToStr){.dirty.} = 30 | bind newPyInt, pyTrueObj, pyFalseObj, isPyTrueObj 31 | bind newPyString, PyStrObject, UnicodeVariant 32 | bind isThrownException, errorIfNotString 33 | 34 | template len*(self: PyNameObject): int = self.items.len 35 | template `[]`*(self: PyNameObject, i: int): PyObject = self.items[i] 36 | iterator items*(self: PyNameObject): PyObject = 37 | for i in self.items: yield i 38 | 39 | implNameMagic contains, mutRead: 40 | for item in self: 41 | let retObj = item.callMagic(eq, other) 42 | if isThrownException(retObj): 43 | return retObj 44 | if isPyTrueObj(retObj): 45 | return pyTrueObj 46 | return pyFalseObj 47 | 48 | 49 | implNameMagic repr, mutReadRepr: 50 | var ss: seq[UnicodeVariant] 51 | for item in self: 52 | var itemRepr: PyStrObject 53 | let retObj = item.callMagic(repr) 54 | errorIfNotString(retObj, "__repr__") 55 | itemRepr = PyStrObject(retObj) 56 | ss.add itemRepr.str 57 | return newPyString(seqToStr(ss)) 58 | 59 | 60 | implNameMagic len, mutRead: 61 | newPyInt(self.len) 62 | 63 | 64 | methodMacroTmpl(Tuple) 65 | genCollectMagics items, 66 | implTupleMagic, 67 | ofPyTupleObject, PyTupleObject, 68 | [], [reprLock], 69 | tupleSeqToString 70 | -------------------------------------------------------------------------------- /Python/getargs/tovalUtils.nim: -------------------------------------------------------------------------------- 1 | 2 | import std/macros 3 | import ./paramsMeta 4 | import ../../Objects/[ 5 | pyobjectBase, 6 | exceptions, 7 | ] 8 | 9 | proc fetchPragmaArg(mayBePragmaExpr, tofind: NimNode, defval: NimNode, argsIsTyped=false): tuple[head: NimNode, val: NimNode] = 10 | ##[ do following pusedo match code 11 | ``` 12 | val = defval 13 | match mayBePragmaExpr 14 | | head{.tofind(val).} 15 | | head 16 | ``` 17 | ]## 18 | var v = mayBePragmaExpr 19 | result.val = defval 20 | if v.kind == nnkPragmaExpr: 21 | result.head = v[0] 22 | let pragmas = v[1] 23 | pragmas.expectKind nnkPragma 24 | for i, pragma in pragmas: 25 | if pragma.kind in {nnkCall, nnkCallStrLit} and ( 26 | if argsIsTyped: pragma[0] == tofind 27 | else: pragma[0].eqIdent tofind.strVal 28 | ): 29 | result.val = pragma[1] 30 | pragmas.del i, 1 31 | if pragmas.len > 0: 32 | v[1] = pragmas 33 | else: 34 | v = v[0] 35 | break 36 | else: 37 | result.head = v 38 | 39 | proc getPyParamConverterImpl*(v: NimNode, isTyped=false): tuple[varname, cvt: NimNode] = 40 | let res = v.fetchPragmaArg(bindSym"convertVia", ident"toval") 41 | (res[0], res[1]) 42 | 43 | macro tovalAux*(res: PyObject, v): PyBaseErrorObject = 44 | let (v, call) = v.getPyParamConverterImpl 45 | call.newCall(res, v) 46 | 47 | proc isKwOnlyStartImpl*(p: NimNode): bool = 48 | if p.kind != nnkPragmaExpr: return 49 | let pragmas = p[1] 50 | assert pragmas.kind == nnkPragma 51 | let id = bindSym"startKwOnly" 52 | for p in pragmas: 53 | if p.eqIdent id: 54 | return true 55 | 56 | proc getNameOfParam*(p: NimNode): NimNode = 57 | if p.kind == nnkPragmaExpr: p[0] 58 | else: p 59 | 60 | proc getPyNameOfParamAsStr*(n: NimNode): string = 61 | ## consider prefer `{.AsPyParam.}`'s argument if present 62 | if n.kind == nnkAccQuoted: 63 | n.expectLen 1 64 | n[0].strVal 65 | else: 66 | let tup = n.fetchPragmaArg(bindSym"AsPyParam", nil) 67 | var pyname = tup.val 68 | if pyname.isNil: 69 | pyname = tup.head 70 | pyname.strVal 71 | -------------------------------------------------------------------------------- /Objects/numobjects/intobject/fromx.nim: -------------------------------------------------------------------------------- 1 | 2 | 3 | import ./[decl, ops_imp_warn] 4 | import ../[numobjects_comm, helpers] 5 | import ../../stringobject/strformat 6 | import ../../../Modules/unicodedata/decimalAndSpace 7 | include ./bytes_h 8 | 9 | template PyLong_FromBytesImpl(buffer: openArray[char]; base = 10) = 10 | var nParsed: int 11 | result = PyLong_FromString(buffer, nParsed, base) 12 | if nParsed == buffer.len: 13 | return result 14 | 15 | proc PyLong_FromBytes*(buffer: openArray[char]; base = 10): PyObject = 16 | PyLong_FromBytesImpl(buffer, base) 17 | let bObj = newPyBytes @buffer 18 | retInvIntCall bObj, base 19 | 20 | proc PyLong_FromUnicodeObject*(u: PyStrObject, base = 10): PyObject = 21 | let asciidigObj = PyUnicode_TransformDecimalAndSpaceToASCII(u) 22 | retIfExc asciidigObj 23 | let asciidig = PyStrObject asciidigObj 24 | assert asciidig.isAscii 25 | # Simply get a pointer to existing ASCII characters. 26 | let buffer = asciidig.asUTF8 27 | 28 | PyLong_FromBytesImpl(buffer, base) 29 | retIfExc result 30 | 31 | retInvIntCall u, base 32 | 33 | proc PyNumber_Long*(o: PyObject; resi: var PyIntObject): PyBaseErrorObject{.pyCFuncPragma.} = 34 | PyNumber_FloatOrIntImpl(o, resi, int): 35 | ret res 36 | 37 | template retMayE(e: PyObject) = 38 | let res = e 39 | retIfExc e 40 | ret res 41 | if o.ofPyStrObject: 42 | # The below check is done in PyLong_FromUnicodeObject(). 43 | retMayE PyLong_FromUnicodeObject(PyStrObject o) 44 | if o.ofPyBytesObject: 45 | #[need to do extra error checking that PyLong_FromString() 46 | doesn't do. In particular int('9\x005') must raise an 47 | exception, not truncate at the null.]# 48 | let b = PyBytesObject o 49 | retMayE PyLong_FromBytes(b.items) 50 | if o.ofPyByteArrayObject: 51 | let b = PyByteArrayObject o 52 | retMayE PyLong_FromBytes(b.items) 53 | #TODO:buffer 54 | let s = newPyStr&("int() argument must be a string, a bytes-like object "& 55 | "or a real number, not '{o.typeName:.200s}'") 56 | retIfExc s 57 | return newTypeError PyStrObject s 58 | 59 | genNumberVariant Long, int 60 | 61 | -------------------------------------------------------------------------------- /Python/pyimport/main.nim: -------------------------------------------------------------------------------- 1 | 2 | 3 | import ../[ 4 | sysmodule_instance, 5 | neval_frame, 6 | compile, 7 | ] 8 | import ../../Objects/[pyobject, 9 | stringobject, 10 | dictobject, listobject, 11 | codeobject, funcobject, frameobject, 12 | exceptionsImpl, moduleobjectImpl, 13 | ] 14 | import std/os 15 | import ../../Utils/compat_io_os 16 | import ../../Objects/stringobject/strformat 17 | import ./utils 18 | 19 | let 20 | mnamekey = newPyAscii"__name__" 21 | mpathkey = newPyAscii"__file__" 22 | 23 | proc init(m: PyModuleObject, filepath: string) = 24 | let d = m.getDict 25 | d[mpathkey] = newPyStr filepath 26 | 27 | type Evaluator = object 28 | evalFrame: proc (f: PyFrameObject): PyObject {.raises: [].} 29 | template newEvaluator*(f): Evaluator = Evaluator(evalFrame: f) 30 | 31 | 32 | proc pyImport*(rt: Evaluator; name: PyStrObject): PyObject{.raises: [].} = 33 | var alreadyIn: bool 34 | let module = import_add_module(name, alreadyIn) 35 | 36 | if alreadyIn: 37 | return module 38 | 39 | var filepath: string 40 | let sname = $name 41 | for path in sys.path: 42 | let p = joinPath($path, sname).addFileExt("py") 43 | if p.fileExistsCompat: 44 | filepath = p 45 | 46 | if filepath == "": 47 | let msg = newPyStr&"No module named {name:R}" 48 | retIfExc msg 49 | let exc = newModuleNotFoundError(PyStrObject msg) 50 | exc.name = name 51 | exc.msg = msg 52 | return exc 53 | let input = try: 54 | readFileCompat(filepath) 55 | except IOError as e: 56 | #TODO:io maybe newIOError? 57 | return newImportError(newPyAscii"Import Failed due to IOError " & newPyAscii $e.msg) 58 | let compileRes = compile(input, filepath) 59 | if compileRes.isThrownException: 60 | return compileRes 61 | 62 | let co = PyCodeObject(compileRes) 63 | 64 | when defined(debug): 65 | echo co 66 | let fun = newPyFunc(name, co, newPyDict()) 67 | let f = newPyFrame(fun) 68 | # __name__ = '__main__' 69 | f.globals[mnamekey] = name 70 | let retObj = rt.evalFrame(f) 71 | if retObj.isThrownException: 72 | return retObj 73 | 74 | module.dict = f.globals 75 | module.init filepath 76 | module 77 | -------------------------------------------------------------------------------- /Objects/abstract/sequence.nim: -------------------------------------------------------------------------------- 1 | 2 | import std/strformat 3 | import ../[ 4 | pyobject, exceptions, 5 | ] 6 | import ../numobjects/intobject/[decl, ops] 7 | import ../typeobject/wraps 8 | import ./helpers 9 | 10 | type PyNimSequence = concept self 11 | self.items is seq[PyObject] 12 | 13 | template PyNimSequence_Check*(o: PyObject): bool = o is PyNimSequence 14 | template ifPyNimSequence_Check*(o: PyObject, body) = 15 | when PyNimSequence_Check(o): body 16 | template ifPyNimSequence_Check*(o: PyObject, body, elseDo): untyped = 17 | when PyNimSequence_Check(o): body 18 | else: elseDo 19 | 20 | template PySequence_Check*(o: PyObject): bool = 21 | not o.getMagic(getitem).isNil 22 | 23 | template ofPySequence*(o: PyObject): bool = 24 | ## PySequence_Check 25 | bind PySequence_Check 26 | PySequence_Check o 27 | 28 | proc PySequence_GetItemNonNil*(s: PyObject, i: Natural): PyObject = 29 | let gitem = s.getMagic(getitem) 30 | if gitem.isNil: 31 | return type_error fmt"'{s.typeName:.200s}' object does not support indexing" 32 | 33 | result = gitem(s, newPyInt i) 34 | assert Py_CheckSlotResult(s, "__getitem__", result) 35 | 36 | template lenImpl(s: PyObject, L: var int; succDo) = 37 | if s.has_sq_length: 38 | retIfExc s.sq_length(L) 39 | assert Py_CheckSlotResult(s, "__len__", L>=0, s) 40 | succDo 41 | 42 | proc PySequence_GetItemNonNil*(s: PyObject, i: int): PyObject{.raises: [].} = 43 | var i = i 44 | if s.has_sq_item: 45 | if i < 0: 46 | var L: int 47 | s.lenImpl L: 48 | i += L 49 | result = s.sq_item(i) 50 | assert Py_CheckSlotResult(s, "__getitem__", result) 51 | return 52 | return type_errorn("'$#' object does not support indexing", s) 53 | 54 | proc PySequence_GetItem*(s: PyObject, i: int): PyObject = 55 | if s.isNil: return null_error() 56 | PySequence_GetItemNonNil s, i 57 | 58 | proc PySequence_Size*(s: PyObject, res: var int): PyBaseErrorObject = 59 | s.lenImpl res: 60 | return 61 | return type_errorn("object of type '$#' has no len()", s) 62 | 63 | template PySequence_Length*(s: PyObject, res: var int): PyBaseErrorObject = 64 | bind PySequence_Size 65 | PySequence_Size(s, res) 66 | -------------------------------------------------------------------------------- /Objects/abstract/op_helpers.nim: -------------------------------------------------------------------------------- 1 | 2 | import std/strformat 3 | import ../[ 4 | pyobject, 5 | exceptions, 6 | stringobject, 7 | notimplementedobject, 8 | ] 9 | import ../typeobject/apis/subtype 10 | #import ../../Include/internal/pycore_object 11 | 12 | proc binop_type_error(v, w: PyObject, op_name: string): PyTypeErrorObject = 13 | newTypeError newPyStr fmt"unsupported operand type(s) for {op_name:.100s}: '{v.typeName:.100s}' and '{w.typeName:.100s}'" 14 | 15 | template BINARY_OP(v, w; magicop; opname) = 16 | bind BinaryMethod, isNotImplemented, pyNotImplemented 17 | bind PyType_IsSubtype 18 | # XXX: do not use ``` 19 | # Py_CheckSlotResult(v, opname, result)``` 20 | # which FatalError when raising exception 21 | 22 | let slotv = v.getMagic(magicop) 23 | var slotw: BinaryMethod 24 | if not Py_IS_TYPE(w, v.pyType): 25 | slotw = w.getMagic(magicop) 26 | if slotw == slotv: 27 | slotw = nil 28 | template check(a1, op, res) = 29 | retIfExc res 30 | 31 | if not slotv.isNil: 32 | if not slotw.isNil and 33 | PyType_IsSubtype(w.pyType, v.pyType): 34 | result = slotw(v, w) 35 | if not result.isNotImplemented: 36 | return 37 | slotw = nil 38 | result = slotv(v, w) 39 | check(v, opname, result) 40 | if not result.isNotImplemented: 41 | return 42 | if not slotw.isNil: 43 | result = slotw(v, w) 44 | check(v, opname, result) 45 | if not result.isNotImplemented: 46 | return 47 | 48 | return pyNotImplemented 49 | 50 | template binary_func*(op; magicop; opname){.dirty.} = 51 | bind BINARY_OP, binop_type_error, isNotImplemented 52 | 53 | proc op*(v, w: PyObject): PyObject{.pyCFuncPragma.} = 54 | ##[ Calling scheme used for binary operations: 55 | 56 | Order operations are tried until either a valid result or error: 57 | w.op(v,w)[*], v.op(v,w), w.op(v,w) 58 | 59 | [*] only when Py_TYPE(v) != Py_TYPE(w) && Py_TYPE(w) is a subclass of 60 | Py_TYPE(v)]## 61 | proc binary_op1(v, w: PyObject): PyObject = BINARY_OP(v, w, magicop, opname) 62 | result = binary_op1(v, w) 63 | if isNotImplemented(result): 64 | return binop_type_error(v, w, opname) 65 | 66 | -------------------------------------------------------------------------------- /Parser/string_parser.nim: -------------------------------------------------------------------------------- 1 | 2 | 3 | import ../Python/[ 4 | warnings, versionInfo, 5 | ] 6 | import ../Objects/[ 7 | pyobject, exceptions 8 | ] 9 | import ../Utils/[ 10 | utils, 11 | translateEscape, 12 | ] 13 | import lexerTypes 14 | 15 | template handleEscape(asSyntaxErrorMsg) = 16 | let category = 17 | when PyMinor >= 12: pySyntaxWarningObjectType 18 | else: pyDeprecationWarningObjectType 19 | let err = warnExplicit(category, arg, info.fileName, info.line) 20 | if err.isNil: return 21 | if err.pyType == category: 22 | #[Replace the Syntax/DeprecationWarning exception with a SyntaxError 23 | to get a more accurate error report]# 24 | raiseSyntaxError(asSyntaxErrorMsg, info.fileName, info.line, info.column) 25 | else: 26 | #TODO:string_parser spread the exception as is, instead of always raising an type of exception 27 | let serr = try: ": " & $err except Exception: "" 28 | raiseAssert "warnings.warn raises an exception: " & serr 29 | 30 | proc lexMessage(info: LineInfo, kind: TranslateEscapeErr, _: string){.raises: [SyntaxError].} = 31 | let arg = $kind 32 | case kind 33 | #TODO:string_parser change function signature to pass another string to capture current escape string 34 | of teeBadEscape: 35 | handleEscape """ 36 | "\%c" is an invalid escape sequence. Did you mean "\\%c"? A raw string is also an option.""" 37 | of teeBadOct: 38 | handleEscape """ 39 | "\%.3s" is an invalid escape octal sequence. Did you mean "\\%.3s"? A raw string is also an option.""" 40 | else: 41 | raiseSyntaxError(arg, info.fileName, info.line, info.column) 42 | 43 | template decode_string_with_escapes(lex; s: string): string = 44 | lex.lineInfo.fileName = L.fileName 45 | lex.lineInfo.line = L.lineNo 46 | lex.translateEscape 47 | 48 | {.push raises: [SyntaxError].} 49 | proc decode_unicode_with_escapes*(L: lexerTypes.Lexer, s: string): string = 50 | var lex = newLexer[true](s, lexMessage) 51 | lex.decode_string_with_escapes s 52 | 53 | proc decode_bytes_with_escapes*(L: lexerTypes.Lexer, s: string): string = 54 | var lex = newLexer[false](s, lexMessage) 55 | lex.decode_string_with_escapes s 56 | {.pop.} 57 | -------------------------------------------------------------------------------- /Objects/boolobjectImpl.nim: -------------------------------------------------------------------------------- 1 | import strformat 2 | import hashes 3 | import macros 4 | 5 | import pyobject 6 | import exceptions 7 | import stringobject 8 | import ./boolobject 9 | export boolobject 10 | import ./numobjects/intobject_decl 11 | import ./noneobject 12 | import ./bltcommon; export bltcommon 13 | 14 | 15 | method `$`*(obj: PyBoolObject): string = 16 | $obj.b 17 | 18 | methodMacroTmpl(Bool) 19 | 20 | implBoolMagic Not: 21 | newPyBool self != pyTrueObj 22 | 23 | implBoolMagic bool: 24 | self 25 | 26 | 27 | implBoolMagic And: 28 | let otherBoolObj = other.callMagic(bool) 29 | errorIfNotBool(otherBoolObj, "__bool__") 30 | newPyBool self.b and PyBoolObject(otherBoolObj).b 31 | 32 | implBoolMagic Xor: 33 | let otherBoolObj = other.callMagic(bool) 34 | errorIfNotBool(otherBoolObj, "__bool__") 35 | newPyBool self.b xor PyBoolObject(otherBoolObj).b 36 | 37 | implBoolMagic Or: 38 | let otherBoolObj = other.callMagic(bool) 39 | errorIfNotBool(otherBoolObj, "__bool__") 40 | newPyBool self.b or PyBoolObject(otherBoolObj).b 41 | 42 | implBoolMagic eq: 43 | let otherBoolObj = other.callMagic(bool) 44 | errorIfNotBool(otherBoolObj, "__bool__") 45 | let otherBool = PyBoolObject(otherBoolObj).b 46 | newPyBool self.b == otherBool 47 | 48 | implBoolMagic repr: 49 | if self.b: 50 | return newPyAscii("True") 51 | else: 52 | return newPyAscii("False") 53 | 54 | implBoolMagic hash: 55 | newPyInt(Hash(self.b)) 56 | 57 | 58 | proc PyObject_IsTrue*(v: PyObject, res: var bool): PyBaseErrorObject = 59 | template ret(b: bool) = 60 | res = b 61 | return nil 62 | if v == pyTrueObj: ret true 63 | if v == pyFalseObj: ret false 64 | if v == pyNone: ret false 65 | let boolMag = v.getMagic(bool) 66 | if not boolMag.isNil: 67 | let obj = boolMag(v) 68 | errorIfNotBool obj, "__bool__" 69 | ret obj.PyBoolObject.b 70 | elif not v.getMagic(len).isNil: 71 | let obj = v.getMagic(len)(v) 72 | errorIfNot int, obj, "__bool__" 73 | ret obj.PyIntObject.positive 74 | # We currently don't define: 75 | # as_sequence 76 | # as_mapping 77 | ret true 78 | 79 | implBoolMagic New(_: PyObject, obj): 80 | var b: bool 81 | retIfExc PyObject_IsTrue(obj, b) 82 | newPyBool b 83 | 84 | -------------------------------------------------------------------------------- /Objects/typeobject/apis/attrs.nim: -------------------------------------------------------------------------------- 1 | 2 | import ../../[ 3 | pyobject, stringobject, 4 | ] 5 | import ../../pyobject_apis/attrsGeneric 6 | export PyType_LookupStackRefAndVersion 7 | 8 | using typ: PyTypeObject 9 | using name: PyStrObject 10 | 11 | proc PyType_LookupRefAndVersion*(typ; name: PyStrObject, version: var uint): PyObject = 12 | ##[ `_PyType_LookupRefAndVersion` 13 | Internal API to look for a name through the MRO. 14 | This returns a strong reference, and doesn't set an exception! 15 | If nonzero, version is set to the value of type->tp_version at the time of 16 | the lookup.]## 17 | var o: PyStackRef 18 | version = PyType_LookupStackRefAndVersion(typ, name, o) 19 | if o.isNull: 20 | return nil 21 | return o.asPyObjectSteal 22 | 23 | proc PyType_LookupRefAndVersion*(typ; name: PyStrObject): PyObject = 24 | ## `_PyType_LookupRefAndVersion(typ, name, NULL)` 25 | var version: uint 26 | PyType_LookupRefAndVersion(typ, name, version) 27 | 28 | proc PyType_LookupRef*(typ; name): PyObject = 29 | ##[ `_PyType_LookupRef` 30 | Internal API to look for a name through the MRO. 31 | This returns a strong reference, and doesn't set an exception!]## 32 | PyType_LookupRefAndVersion(typ, name) 33 | 34 | 35 | proc PyObject_LookupSpecial*(self: PyObject, attr: PyStrObject): PyObject = 36 | ##[ `_PyObject_LookupSpecial` 37 | 38 | Routines to do a method lookup in the type without looking in the 39 | instance dictionary (so we can't use PyObject_GetAttr) but still 40 | binding it to the instance. 41 | 42 | Variants: 43 | 44 | - _PyObject_LookupSpecial() returns NULL without raising an exception 45 | when the _PyType_LookupRef() call fails; 46 | 47 | - lookup_maybe_method() and lookup_method() are internal routines similar 48 | to _PyObject_LookupSpecial(), but can return unbound PyFunction 49 | to avoid temporary method object. Pass self as first argument when 50 | unbound == 1. 51 | ]## 52 | result = PyType_LookupRef(self.pyType, attr) 53 | 54 | #XXX: do not use: retIfExc result 55 | # as may return nil over exception 56 | if result.isNil: return 57 | 58 | let descrGet = result.getMagic(get) 59 | if not descrGet.isNil: 60 | result = descrGet(result, self) 61 | -------------------------------------------------------------------------------- /Objects/methodobject.nim: -------------------------------------------------------------------------------- 1 | 2 | 3 | import pyobject 4 | import exceptions 5 | import ../Python/getargs 6 | 7 | 8 | import stringobject 9 | 10 | type 11 | NFunc* {. pure .} = enum 12 | BltinFunc, # general function, no self 13 | # methods with a `self` attribute 14 | UnaryMethod, # method with 1 arg 15 | BinaryMethod, # method with 2 args 16 | TernaryMethod, # method with 3 args 17 | BltinMethod, # method with arbitary args 18 | 19 | # mixing function and method is to some extent unnatural 20 | # however, this makes function and method call dispatch most efficient 21 | 22 | declarePyType NimFunc(tpToken): 23 | name: PyStrObject 24 | self: PyObject # not set for BltinFunc 25 | kind: NFunc 26 | fun: int # generic function pointer, determined by kind 27 | 28 | 29 | implNimFuncMagic call: 30 | case self.kind 31 | of NFunc.BltinFunc: 32 | return cast[BltinFunc](self.fun)(args, kwargs) 33 | of NFunc.UnaryMethod: 34 | PyArg_NoKw self.name 35 | checkArgNum(0) 36 | return cast[UnaryMethod](self.fun)(self.self) 37 | of NFunc.BinaryMethod: 38 | PyArg_NoKw self.name 39 | checkArgNum(1) 40 | return cast[BinaryMethod](self.fun)(self.self, args[0]) 41 | of NFunc.TernaryMethod: 42 | PyArg_NoKw self.name 43 | checkArgNum(2) 44 | return cast[TernaryMethod](self.fun)(self.self, args[0], args[1]) 45 | of NFunc.BltinMethod: 46 | return cast[BltinMethod](self.fun)(self.self, args, kwargs) 47 | 48 | 49 | # diff with methods: no `self` init 50 | proc newPyNimFunc*(fun: BltinFunc, name: PyStrObject): PyNimFuncObject = 51 | result = newPyNimFuncSimple() 52 | result.name = name 53 | result.kind = NFunc.BltinFunc 54 | result.fun = cast[int](fun) 55 | 56 | 57 | template newMethodTmpl(FunType) = 58 | proc newPyNimFunc*(fun: FunType, name: PyStrObject, self:PyObject=nil): PyNimFuncObject = 59 | # `self` should never be nil. The default arg here is to fool the compiler 60 | # when initializing type dict 61 | assert (not self.isNil) 62 | result = newPyNimFuncSimple() 63 | result.name = name 64 | result.kind = NFunc.FunType 65 | result.fun = cast[int](fun) 66 | result.self = self 67 | 68 | 69 | newMethodTmpl(UnaryMethod) 70 | newMethodTmpl(BinaryMethod) 71 | newMethodTmpl(TernaryMethod) 72 | newMethodTmpl(BltinMethod) 73 | -------------------------------------------------------------------------------- /Utils/jsdispatch.nim: -------------------------------------------------------------------------------- 1 | ## this module's APIs is unstable 2 | when defined(js): 3 | import std/jsffi 4 | export jsffi 5 | var ibindExpr{.compileTime.}: int 6 | template bindExpr*[T=JsObject](asIdent; exprOfJs: string) = 7 | ## helpers to cache exp (make sure `exp` evaluated by js only once) 8 | bind ibindExpr 9 | let tmp{.importjs: exprOfJs.}: T 10 | const `asIdent InJs`* = "_NPython_bindExpr" & $ibindExpr 11 | static: ibindExpr.inc 12 | let asIdent*{.exportc: `asIdent InJs`.} = tmp 13 | 14 | template notDecl*(s): string = "(typeof("&s&")==='undefined')" 15 | template `&&`(a, b: string): string = "("&a&"&&"&b&")" 16 | template `||`(a, b: string): string = "("&a&"||"&b&")" 17 | template `!`(a: string): string = "!("&a&")" 18 | 19 | 20 | bindExpr[bool] notDeno, notDecl"Deno" 21 | 22 | template ifOr*(cond, a, b: string): string = cond&'?'&a&':'&b 23 | template denoOr(deno, node: string): string = 24 | bind notDenoInJs 25 | notDenoInJs.ifOr node, deno 26 | bindExpr[bool] notHasProcess, notDecl"process" # as Deno also has `process` 27 | bindExpr[bool] notNode, (notHasProcessInJs || !notDenoInJs) 28 | template nodeno*(node, deno, def: string): string = 29 | ## node or node or def 30 | bind ifOr, notNodeInJs 31 | ifOr(notNodeInJs, denoOr(deno, def), node) 32 | #NOTE: As ./compat.nim uses js top-level import, so the whole JS file is 33 | # ES module, thus `require` is not defined, which we cannot use here. 34 | #template genX(name; s){.dirty.} = 35 | func importNodeExpr(s: string): string = 36 | "(await (import('node:"&s&"')))" 37 | template genXorDeno(name; s){.dirty.} = 38 | bindExpr[] name, nodeno(importNodeExpr s, "Deno", "null") 39 | template genX(name; s){.dirty.} = 40 | bindExpr[] name, ifOr(notNodeInJs && notDenoInJs, "null", importNodeExpr s) 41 | # using `await import` without paran will causes js SyntaxError on non-nodejs 42 | genXorDeno fsOrDeno, "fs" 43 | genXorDeno ttyOrDeno, "tty" # for .isatty 44 | genX fsMod, "fs" 45 | 46 | template jsFuncExpr(js, name: string): untyped{.dirty.} = 47 | js & '.' & name & "(@)" 48 | 49 | template genPragma(name, jsExp){.dirty.} = 50 | template name*(s): untyped = 51 | bind jsExp 52 | bind jsFuncExpr 53 | jsFuncExpr(jsExp, s) 54 | genPragma fsDeno, fsOrDenoInJs 55 | genPragma fs, fsModInJs 56 | -------------------------------------------------------------------------------- /Objects/numobjects/intobject/magicNew.nim: -------------------------------------------------------------------------------- 1 | 2 | 3 | import ../numobjects_comm 4 | import ./[ 5 | ops_imp_warn, fromx 6 | ] 7 | include ./bytes_h 8 | 9 | 10 | proc long_new_impl*(typ: PyTypeObject, x: PyObject, obase: PyObject): PyObject{.pyCFuncPragma.} 11 | proc long_subtype_new(typ: PyTypeObject, x: PyObject, obase: PyObject): PyObject{.pyCFuncPragma.} = 12 | ##[ Wimpy, slow approach to tp_new calls for subtypes of int: 13 | first create a regular int from whatever arguments we got, 14 | then allocate a subtype instance and initialize it from 15 | the regular int. The regular int is then thrown away. 16 | ]## 17 | 18 | when declared(PyType_IsSubtype): 19 | assert PyType_IsSubtype(typ, &PyLong_Type) 20 | let tmp = long_new_impl(pyIntObjectType, x, obase); 21 | retIfExc tmp 22 | let itmp = PyIntObject tmp 23 | var n = itmp.digitCount 24 | #[ Fast operations for single digit integers (including zero) 25 | assume that there is always at least one digit present. ]# 26 | if n == 0: n = 1 27 | 28 | result = typ.tp_alloc(typ, n) 29 | retIfExc result 30 | let newobj = PyIntObject result 31 | #newobj->long_value.lv_tag = tmp->long_value.lv_tag & ~IMMORTALITY_BIT_MASK; 32 | newobj.sign = itmp.sign 33 | newobj.digits = itmp.digits 34 | return newobj 35 | 36 | proc long_new_impl*(typ: PyTypeObject, x: PyObject, obase: PyObject): PyObject{.pyCFuncPragma.} = 37 | if not typ.isType pyIntObjectType: 38 | return long_subtype_new(typ, x, obase) # Wimp out 39 | if x.isNil: 40 | if not obase.isNil: 41 | return newTypeError newPyAscii"int() missing string argument" 42 | return pyIntZero 43 | # default base and limit, forward to standard implementation 44 | if obase.isNil: 45 | return PyNumber_Long(x) 46 | 47 | var exc: PyBaseErrorObject 48 | let base = PyNumber_AsSsize_t(obase, exc) 49 | retIfExc exc 50 | 51 | template retMayE(e: PyObject) = 52 | let res = e 53 | retIfExc e 54 | ret res 55 | template ret(obj) = return obj 56 | let o = x 57 | if o.ofPyStrObject: 58 | retMayE PyLong_FromUnicodeObject(PyStrObject o, base) 59 | if o.ofPyBytesObject: 60 | let b = PyBytesObject o 61 | retMayE PyLong_FromBytes(b.items, base) 62 | if o.ofPyByteArrayObject: 63 | let b = PyByteArrayObject o 64 | retMayE PyLong_FromBytes(b.items, base) 65 | 66 | return newTypeError newPyAscii"int() can't convert non-string with explicit base" 67 | -------------------------------------------------------------------------------- /Python/neval_frame.nim: -------------------------------------------------------------------------------- 1 | 2 | import std/strformat 3 | import ./[ 4 | symtable, 5 | ] 6 | import ../Objects/[pyobject, 7 | funcobject, 8 | cellobject, 9 | codeobject, frameobject, 10 | exceptions, 11 | stringobject, 12 | ] 13 | import ../Utils/[ 14 | utils, 15 | ] 16 | proc newPyFrame*(fun: PyFunctionObject, 17 | args: openArray[PyObject], 18 | back: PyFrameObject): PyObject{.raises: [].} 19 | 20 | proc newPyFrame*(fun: PyFunctionObject): PyFrameObject = 21 | let obj = newPyFrame(fun, @[], nil) 22 | if obj.isThrownException: 23 | unreachable 24 | else: 25 | return PyFrameObject(obj) 26 | 27 | proc newPyFrame*(fun: PyFunctionObject, 28 | args: openArray[PyObject], 29 | back: PyFrameObject): PyObject{.raises: [].} = 30 | let code = fun.code 31 | # handle wrong number of args 32 | if code.argScopes.len < args.len: 33 | let msg = fmt"{fun.name.str}() takes {code.argScopes.len} positional arguments but {args.len} were given" 34 | return newTypeError(newPyStr msg) 35 | elif args.len < code.argScopes.len: 36 | let diff = code.argScopes.len - args.len 37 | let msg = fmt"{fun.name.str}() missing {diff} required positional argument: " & 38 | fmt"{code.argNames[^diff..^1]}. {args.len} args are given." 39 | return newTypeError(newPyStr(msg)) 40 | let frame = newPyFrame() 41 | frame.back = back 42 | frame.code = code 43 | frame.globals = fun.globals 44 | # todo: use flags for faster simple function call 45 | frame.fastLocals = newSeq[PyObject](code.localVars.len) 46 | frame.cellVars = newSeq[PyCellObject](code.cellVars.len + code.freeVars.len) 47 | # init cells. Can do some optimizations here 48 | for i in 0..md_def->md_name` 18 | # optional 19 | #package{.dunder_member.}: PyObject 20 | #spec{.dunder_member.}: PyObject 21 | 22 | using self: PyModuleObject 23 | proc name*(self): PyStrObject = 24 | result = self.name 25 | assert $result == self.def.m_name 26 | proc `name=`*(self; name: PyStrObject) = 27 | self.name = name 28 | self.def.m_name = $name 29 | 30 | genProperty Module, "__name__", name, name(self): 31 | errorIfNotString other, "__name__'s rhs" 32 | `name=`(self, PyStrObject other) 33 | pyNone 34 | 35 | proc getDict*(self): PyDictObject = 36 | ## _PyModule_GetDict(mod) must not be used after calling module_clear(mod) 37 | result = PyDictObject self.dict 38 | assert not result.isNil 39 | 40 | proc init_dict(modu: PyModuleObject, md_dict: PyDictObject, name: PyStrObject) = 41 | md_dict[pyDUId package] = pyNone 42 | md_dict[pyDUId loader] = pyNone 43 | md_dict[pyDUId spec] = pyNone 44 | 45 | # `_add_methods_to_object` 46 | for name, (meth, _) in modu.pyType.bltinMethods: 47 | let namePyStr = newPyAscii(name) 48 | md_dict[namePyStr] = newPyNimFunc(meth, namePyStr, modu) 49 | 50 | proc initFrom_PyModule_NewObject(module: PyModuleObject) = 51 | ## like `PyModule_NewObject` but is a `init` 52 | let dict = newPyDict() 53 | module.dict = dict 54 | module.init_dict(dict, module.name) 55 | 56 | template newPyModuleImpl*(T: typedesc[PyModuleObject]; typ: PyTypeObject; nam: PyStrObject|string; 57 | tp_alloc_may_exc = true 58 | ){.dirty.} = 59 | ## for subtype 60 | bind newPyStr, initFrom_PyModule_NewObject, newPyModuleDef 61 | block: 62 | let resObj = typ.tp_alloc(typ, 0) 63 | when tp_alloc_may_exc: 64 | static:assert result is_not PyModuleObject, 65 | "tp_alloc_may_exc is true but we cannot return exception for type " & 66 | $typeof(result) 67 | retIfExc resObj 68 | let res = T resObj 69 | res.pyType = typ 70 | res.name = newPyStr(nam) 71 | res.def = newPyModuleDef($nam, typ) 72 | initFrom_PyModule_NewObject(res) 73 | result = res 74 | 75 | proc newPyModule*(name: PyStrObject|string): PyModuleObject = 76 | newPyModuleImpl PyModuleObject, pyModuleObjectType, name, false 77 | 78 | -------------------------------------------------------------------------------- /Objects/descrobjectImpl.nim: -------------------------------------------------------------------------------- 1 | 2 | import std/strformat 3 | import ./[ 4 | pyobject, 5 | noneobject, 6 | exceptions, 7 | stringobject, 8 | methodobject, 9 | ] 10 | import ./descrobject 11 | import ../Python/[ 12 | structmember, 13 | ] 14 | import ../Python/call 15 | import ../Python/sysmodule/audit 16 | {.used.} 17 | methodMacroTmpl(Property) 18 | implPropertyMagic get: 19 | fastCall(self.getter, [other]) 20 | 21 | 22 | methodMacroTmpl(MemberDescr) 23 | 24 | implMemberDescrMagic get: 25 | if other.isNil: 26 | return self 27 | descr_check(self, other) 28 | if unlikely self.d_member.flags.auditRead: 29 | retIfExc audit("object.__getattr__", 30 | #[ XXX: PY-BUG: `obj ? obj : Py_None` is unnecessary, 31 | as other (called `obj` in CPython) cannot be nil here. 32 | ]# 33 | other, self.d_member.name 34 | ) 35 | result = PyMember_GetOne(other, self.d_member) 36 | retIfExc result 37 | 38 | 39 | implMemberDescrMagic set: 40 | let obj = arg1 41 | descr_check(self, obj) 42 | retIfExc PyMember_SetOne(obj, self.d_member, arg2) 43 | pyNone 44 | 45 | 46 | methodMacroTmpl(MethodDescr) 47 | 48 | implMethodDescrMagic call: 49 | #call(bound, args, kwargs) 50 | let argc = len(args); 51 | if argc < 1: 52 | return newTypeError newPyStr( 53 | fmt"descriptor '{$?self}' of '{self.truncedTypeName}' " & 54 | "object needs an argument" 55 | ) 56 | let owner = args[0] 57 | let bound = tpMagic(MethodDescr, get)(self, owner) 58 | #let bound = method_get(self, owner) 59 | retIfExc bound 60 | tpMagic(NimFunc, call)(bound, args.toOpenArray(1, args.high), kwargs) 61 | 62 | methodMacroTmpl(ClassMethodDescr) 63 | implClassMethodDescrMagic call: 64 | ##[Instances of classmethod_descriptor are unlikely to be called directly. 65 | For one, the analogous class "classmethod" (for Python classes) is not 66 | callable. Second, users are not likely to access a classmethod_descriptor 67 | directly, since it means pulling it from the class __dict__. 68 | 69 | This is just an excuse to say that this doesn't need to be optimized: 70 | we implement this simply by calling __get__ and then calling the result.]## 71 | let argc = len(args); 72 | if argc < 1: 73 | return newTypeError newPyStr( 74 | fmt"descriptor '{$?self}' of '{self.truncedTypeName}' " & 75 | "object needs an argument" 76 | ) 77 | #let owner = args[0] 78 | let bound = classmethod_get(self, nil, self.dType) 79 | retIfExc bound 80 | tpMagic(NimFunc, call)(bound, args#.toOpenArray(1, args.high) 81 | , kwargs) 82 | 83 | 84 | -------------------------------------------------------------------------------- /Utils/sequtils.nim: -------------------------------------------------------------------------------- 1 | ## Nim lacks `find` with start, stop param 2 | ## 3 | ## .. note:: functions in this module assume `start..= 0: yield i 68 | else: break 69 | 70 | 71 | wrapOA findAll 72 | 73 | proc contains*[T](s, sub: openArray[T]): bool{.inline.} = s.find(sub) > 0 74 | -------------------------------------------------------------------------------- /Include/descrobject.nim: -------------------------------------------------------------------------------- 1 | 2 | from std/typeinfo import AnyKind 3 | export AnyKind 4 | #import ../Objects/stringobject 5 | 6 | #[ 7 | type 8 | Flag = distinct int 9 | template `|`(a, b: Flag): int = a or b.int 10 | 11 | const 12 | # Flags 13 | Py_READONLY* = Flag 1 14 | Py_AUDIT_READ* = Flag 2 ## Added in 3.10, harmless no-op before that 15 | #_Py_WRITE_RESTRICTED* = Flag 4 // Deprecated, no-op. Do not reuse the value. 16 | Py_RELATIVE_OFFSET* = Flag 8 17 | 18 | fpmdDef = 0 19 | fpmdRO = Py_READONLY.ord 20 | fpmdROAudit = Py_READONLY | Py_AUDIT_READ 21 | fpmdRORel = Py_READONTY | Py_RELATIVE_OFFSET 22 | template `&`*(a: PyMemberDefFlags, b: Flag): bool = bool a.ord and b.int 23 | ]# 24 | type 25 | PyMemberDefFlags* = object 26 | readonly*, auditRead*, relativeOffset*: bool 27 | 28 | import std/macros 29 | macro pyMemberDefFlagsFromTags*(tags: varargs[untyped]): PyMemberDefFlags = 30 | runnableExamples: 31 | let flags = pyMemberDefFlagsFromTags( 32 | readonly, auditRead 33 | ) 34 | assert flags.readonly 35 | assert not flags.relativeOffset 36 | result = nnkObjConstr.newTree bindSym"PyMemberDefFlags" 37 | let On = newLit(true) 38 | for t in tags: 39 | result.add nnkExprColonExpr.newTree(t, On) 40 | 41 | type 42 | PyMemberDef* = object 43 | name*: string 44 | `type`*: AnyKind 45 | offset*: int 46 | flags*: PyMemberDefFlags 47 | doc: cstring 48 | 49 | 50 | template noRelOff*(member: PyMemberDef, funcName: string) = 51 | assert not member.flags.relativeOffset, "CPython's SystemError: " & 52 | funcName & " used with Py_RELATIVE_OFFSET" 53 | 54 | const akPyObject* = akRef ## `Py_T_OBJECT_EX` 55 | 56 | proc initPyMemberDef*(name: string, `type`: AnyKind, 57 | offset: int; flags=default PyMemberDefFlags, doc=cstring nil): PyMemberDef = 58 | PyMemberDef(name: name, `type`: `type`, offset: offset, flags: flags, doc: doc) 59 | 60 | template genTypeToAnyKind*(PyObject){.dirty.} = 61 | bind AnyKind 62 | mixin parseEnum, newLit, strVal 63 | 64 | template typeToAnyKind*[T: PyObject](t: typedesc[T]): AnyKind = akPyObject 65 | template typeToAnyKind*[Py: PyObject](t: typedesc[seq[Py]]): AnyKind = akSequence 66 | macro typeToAnyKind*[T](t: typedesc[T]): AnyKind = 67 | let res = parseEnum[AnyKind]("ak" & t.strVal) 68 | newLit res 69 | 70 | 71 | proc initPyMemberDef*[T](name: string, `type`: typedesc[T], 72 | offset: int; flags=default PyMemberDefFlags, doc=cstring nil): PyMemberDef = 73 | initPyMemberDef(name, `type`.typeToAnyKind, offset, flags, doc) 74 | 75 | 76 | -------------------------------------------------------------------------------- /Python/pythonrun/utils.nim: -------------------------------------------------------------------------------- 1 | 2 | import ./pyerr 3 | import ../neval 4 | import ../../Include/internal/pycore_global_strings 5 | import ../../Objects/[pyobjectBase, 6 | noneobject, codeobject, 7 | stringobjectImpl, exceptions, 8 | pyobject_apis/strings, 9 | ] 10 | 11 | import ../sysmodule/[attrs, audit] 12 | 13 | 14 | using filename: PyStrObject 15 | using globals, locals: PyDictObject 16 | #[ 17 | import ./builtindict 18 | proc run_eval_code_obj_for_pyc(co: PyCodeObject, globals; locals): PyObject = 19 | let blt = newPyAscii "__builtins__" 20 | if not globals.contains blt: 21 | globals[blt] = PyEval_GetBuiltins() 22 | run_eval_code_obj(co, globals, locals) 23 | ]# 24 | proc run_eval_code_obj(co: PyCodeObject, globals; locals): PyObject{.inline.} = evalCode(co, globals, locals) 25 | 26 | proc run_eval_code_with_audit*(co: PyCodeObject, globals; locals): PyObject = 27 | retIfExc audit("exec", co) 28 | run_eval_code_obj(co, globals, locals) 29 | 30 | template priRetIfExc*(exc: PyBaseErrorObject; excRes: untyped = true) = 31 | if not exc.isNil: 32 | PyErr_Print(exc) 33 | return excRes 34 | template priRetIfExc*(res: PyObject; excRes: untyped = true) = 35 | if res.isThrownException: 36 | PyErr_Print(PyBaseErrorObject res) 37 | return excRes 38 | 39 | proc PyRun_InteractiveLoopPre*: bool{.discardable.} = 40 | # when set ps1 & ps2, err is ignored in CPython 41 | var v: PyObject 42 | template setSysIfNon(attr; str) = 43 | var exists: bool 44 | priRetIfExc(PySys_GetOptionalAttr(pyId attr, v, exists), true) 45 | if v.isNil: priRetIfExc(PySys_SetAttrNonNil(pyId attr, newPyAscii str), true) 46 | setSysIfNon ps1, ">>> " 47 | setSysIfNon ps2, "... " 48 | false 49 | 50 | proc getSysPsEnc*(): tuple[ps1, ps2, encoding: string] = 51 | ## encoding is sys.stdin.encoding 52 | var encoding = "" 53 | var attr: PyObject 54 | var exc: PyBaseErrorObject 55 | var exists: bool 56 | # if exc.isThrownException: discard 57 | var ps1, ps2: string 58 | template getSysTo(obj; itBeforeAsUtf8){.dirty.} = 59 | block it_blk: 60 | var it: PyObject 61 | exc = PySys_GetOptionalAttr(pyId obj, it, exists) 62 | if not it.isNil and it.ofPyStrObject: 63 | itBeforeAsUtf8 64 | obj = PyStrObject(it).asUTF8 65 | exc = PySys_GetOptionalAttr(pyId stdin, attr, exists) 66 | if not attr.isNil and not attr.isPyNone: 67 | getSysTo encoding: discard 68 | template str_it = 69 | it = PyObject_StrNonNil it 70 | if it.isThrownException: 71 | break it_blk 72 | getSysTo ps1, str_it 73 | getSysTo ps2, str_it 74 | (ps1, ps2, encoding) 75 | -------------------------------------------------------------------------------- /npython.nimble: -------------------------------------------------------------------------------- 1 | import "Python/versionInfo" as libver 2 | version = libver.Version 3 | author = "Weitang Li, litlighilit" 4 | description = "(Subset of) Python programming language implemented in Nim" 5 | license = "CPython license" 6 | srcDir = "Python" 7 | binDir = "bin" 8 | 9 | let srcName = "python" 10 | namedBin[srcName] = "npython" 11 | 12 | requires "nim >= 1.6.14" # 2.* (at least till 2.3.1) is okey, too. 13 | 14 | # copied from nimpylib.nimble 15 | # at 43378424222610f8ce4a10593bd719691fbb634b 16 | func getArgs(taskName: string): seq[string] = 17 | ## cmdargs: 1 2 3 4 5 -> 1 4 3 2 5 18 | var rargs: seq[string] 19 | let argn = paramCount() 20 | for i in countdown(argn, 0): 21 | let arg = paramStr i 22 | if arg == taskName: 23 | break 24 | rargs.add arg 25 | if rargs.len > 1: 26 | swap rargs[^1], rargs[0] # the file must be the last, others' order don't matter 27 | return rargs 28 | 29 | template mytask(name: untyped, taskDesc: string, body){.dirty.} = 30 | task name, taskDesc: 31 | let taskName = astToStr(name) 32 | body 33 | 34 | template taskWithArgs(name, taskDesc, body){.dirty.} = 35 | mytask name, taskDesc: 36 | var args = getArgs taskName 37 | body 38 | 39 | import std/os 40 | let binPathWithoutExt = absolutePath(binDir / namedBin[srcName]) 41 | 42 | proc test(pre, pyExe, pyExeToCheckExists: string, args: openArray[string]) = 43 | let subTest = 44 | if args.len == 0: "asserts" 45 | else: args[0] 46 | if not fileExists pyExeToCheckExists: 47 | raise newException(OSError, "please firstly run `nimble " & pre & "`") 48 | withDir "tests/" & subTest: 49 | for i in listFiles ".": 50 | echo "testing " & i 51 | exec pyExe & ' ' & i 52 | 53 | taskWithArgs test, "test, assuming after build": 54 | let pyExe = binPathWithoutExt.toExe 55 | test "build", pyExe, pyExe, args 56 | 57 | taskWithArgs testNodeJs, "test nodejs backend, assuming after build": 58 | let 59 | pyExeFile = binPathWithoutExt & ".js" 60 | pyExe = "node " & pyExeFile 61 | test "buildJs", pyExe, pyExeFile, args 62 | 63 | taskWithArgs buildJs, "build JS. supported backends: " & 64 | "-d:nodejs|-d:deno|-d:jsAlert": 65 | selfExec "js -o:" & binPathWithoutExt & ".js " & 66 | args.quoteShellCommand & ' '& srcDir & '/' & srcName 67 | 68 | taskRequires "buildKarax", "karax" 69 | taskWithArgs buildKarax, "build html page with karax": 70 | selfExec "r --hints:off -d:release Tools/mykarun -d:karax " & " --appName=" & namedBin[srcName] & ' ' & 71 | args.quoteShellCommand & ' '& srcDir & '/' & srcName 72 | -------------------------------------------------------------------------------- /Parser/lexer_utils.nim: -------------------------------------------------------------------------------- 1 | 2 | import std/parseutils 3 | from std/strutils import Digits, IdentChars 4 | from ../Objects/numobjects/intobject/fromStrUtils import isDigitOfBase 5 | 6 | template parseName*(s: string, res: var string, idx: var int; msg: var string): bool = 7 | bind parseIdent 8 | msg = "Invalid identifier" 9 | let i = parseIdent(s, res, idx) 10 | idx.inc i 11 | i != 0 12 | 13 | proc invKind(msg: var string, kind: string) = 14 | msg.add "invalid " 15 | msg.add kind 16 | msg.add " literal" 17 | 18 | proc verify_end_of_number(c: char, kind: string, msg: var string): bool = 19 | # XXX: NPython doesn't wanna support things like `1and xx` currently 20 | if c in IdentChars: 21 | msg.invKind kind 22 | return false 23 | return true 24 | 25 | proc parseNumber*(s: string, res: var string, idx: var int, msg: var string): bool = 26 | ## Rough equal to re"\b(0[XxOoBb])?[\d_]*\.?\d+([eE][-+]?[\d_]+)?\b" 27 | ## 28 | {.push boundChecks: off.} 29 | let start = idx 30 | let hi = s.high 31 | template strp: untyped = s.toOpenArray(idx, hi) 32 | template retVal(val) = 33 | res = s[start..