use cpython style next()

This commit is contained in:
blueloveTH 2024-04-24 22:06:02 +08:00
parent 508cbfdb75
commit 6189aaca94
14 changed files with 96 additions and 82 deletions

View File

@ -30,40 +30,13 @@ The easiest way to test a feature is to [try it on your browser](https://pocketp
## Different behaviors
1. positional and keyword arguments are strictly evaluated.
2. When a generator is exhausted, `StopIteration` is returned instead of raised.
3. `++i` and `--j` is an increment/decrement statement, not an expression.
4. `int` does not derive from `bool`.
5. `int` is 64-bit. You can use `long` type explicitly for arbitrary sized integers.
6. `__ne__` is not required. Define `__eq__` is enough.
7. Raw string cannot have boundary quotes in it, even escaped. See [#55](https://github.com/pocketpy/pocketpy/issues/55).
8. In a starred unpacked assignment, e.g. `a, b, *c = x`, the starred variable can only be presented in the last position. `a, *b, c = x` is not supported.
9. A `Tab` is equivalent to 4 spaces. You can mix `Tab` and spaces in indentation, but it is not recommended.
10. `%`, `&`, `//`, `^` and `|` for `int` behave the same as C, not python.
11. `str.split` and `str.splitlines` will remove all empty entries.
12. `__getattr__`, `__setattr__` and `__delattr__` can only be set in cpp.
### Make `next()` compatible with cpython
You can use the following code to make `next()` compatible with cpython temporarily.
```python
import builtins
def next(obj):
res = builtins.next(obj)
if res is StopIteration:
raise StopIteration
return res
a = iter([1])
assert next(a) == 1
try:
next(a)
except StopIteration:
print("a is exhausted")
```
!!!
Do not change `builtins.next`. It will break internal functions which rely on `StopIteration` as return value.
!!!
2. `++i` and `--j` is an increment/decrement statement, not an expression.
3. `int` does not derive from `bool`.
4. `int` is 64-bit. You can use `long` type explicitly for arbitrary sized integers.
5. `__ne__` is not required. Define `__eq__` is enough.
6. Raw string cannot have boundary quotes in it, even escaped. See [#55](https://github.com/pocketpy/pocketpy/issues/55).
7. In a starred unpacked assignment, e.g. `a, b, *c = x`, the starred variable can only be presented in the last position. `a, *b, c = x` is not supported.
8. A `Tab` is equivalent to 4 spaces. You can mix `Tab` and spaces in indentation, but it is not recommended.
9. `%`, `&`, `//`, `^` and `|` for `int` behave the same as C, not python.
10. `str.split` and `str.splitlines` will remove all empty entries.
11. `__getattr__`, `__setattr__` and `__delattr__` can only be set in cpp.

View File

@ -11,7 +11,7 @@ pkpy is licensed under the [MIT License](http://opensource.org/licenses/MIT).
```
MIT License
Copyright (c) 2023 blueloveTH
Copyright (c) 2024 blueloveTH
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@ -14,5 +14,6 @@ void add_module_dis(VM* vm);
void add_module_gc(VM* vm);
void add_module_line_profiler(VM* vm);
void add_module_enum(VM* vm);
void add_module___builtins(VM* vm);
} // namespace pkpy

View File

@ -0,0 +1,7 @@
from typing import Any
def next(iter) -> Any | StopIteration:
...
def _enable_instance_dict(obj) -> None:
...

5
pyrightconfig.json Normal file
View File

@ -0,0 +1,5 @@
{
"stubPath": "include/typings",
"reportMissingModuleSource": "none",
"pythonVersion": "3.10"
}

View File

@ -1,36 +1,36 @@
import sys as _sys
import operator as _operator
from operator import lt as __operator_lt
from operator import gt as __operator_gt
from __builtins import next as __builtins_next
def print(*args, sep=' ', end='\n'):
s = sep.join([str(i) for i in args])
_sys.stdout.write(s + end)
def _minmax_reduce(op, args, key):
if key is None:
def __minmax_reduce(op, args, key):
if key is None:
if len(args) == 2:
return args[0] if op(args[0], args[1]) else args[1]
key = lambda x: x
if len(args) == 0:
raise TypeError('expected 1 arguments, got 0')
if len(args) == 1:
args = args[0]
args = iter(args)
res = next(args)
res = __builtins_next(args)
if res is StopIteration:
raise ValueError('args is an empty sequence')
while True:
i = next(args)
i = __builtins_next(args)
if i is StopIteration:
break
if op(key(i), key(res)):
res = i
if key is None:
if op(i, res):
res = i
else:
if op(key(i), key(res)):
res = i
return res
def min(*args, key=None):
return _minmax_reduce(_operator.lt, args, key)
return __minmax_reduce(__operator_lt, args, key)
def max(*args, key=None):
return _minmax_reduce(_operator.gt, args, key)
return __minmax_reduce(__operator_gt, args, key)
def all(iterable):
for i in iterable:
@ -69,8 +69,8 @@ def zip(a, b):
a = iter(a)
b = iter(b)
while True:
ai = next(a)
bi = next(b)
ai = __builtins_next(a)
bi = __builtins_next(b)
if ai is StopIteration or bi is StopIteration:
break
yield ai, bi

View File

@ -7,10 +7,12 @@ def Counter(iterable):
a[x] = 1
return a
from __builtins import _enable_instance_dict
class defaultdict(dict):
def __init__(self, default_factory, *args):
super().__init__(*args)
self._enable_instance_dict()
_enable_instance_dict(self)
self.default_factory = default_factory
def __missing__(self, key):

View File

@ -1,3 +1,5 @@
from __builtins import next
class cache:
def __init__(self, f):
self.f = f

View File

@ -1,3 +1,5 @@
from __builtins import next
def zip_longest(a, b):
a = iter(a)
b = iter(b)

File diff suppressed because one or more lines are too long

View File

@ -337,4 +337,20 @@ void add_module_enum(VM* vm){
};
}
void add_module___builtins(VM* vm){
PyObject* mod = vm->new_module("__builtins");
vm->bind_func<1>(mod, "next", [](VM* vm, ArgsView args){
return vm->py_next(args[0]);
});
vm->bind_func<1>(mod, "_enable_instance_dict", [](VM* vm, ArgsView args){
PyObject* self = args[0];
if(is_tagged(self)) vm->TypeError("object: tagged object cannot enable instance dict");
if(self->is_attr_valid()) vm->RuntimeError("object: instance dict is already enabled");
self->_enable_instance_dict();
return vm->None;
});
}
} // namespace pkpy

View File

@ -297,7 +297,9 @@ void init_builtins(VM* _vm) {
});
_vm->bind_func<1>(_vm->builtins, "next", [](VM* vm, ArgsView args) {
return vm->py_next(args[0]);
PyObject* retval = vm->py_next(args[0]);
if(retval == vm->StopIteration) vm->_error(vm->call(vm->StopIteration));
return retval;
});
_vm->bind_func<1>(_vm->builtins, "bin", [](VM* vm, ArgsView args) {
@ -350,18 +352,6 @@ void init_builtins(VM* _vm) {
return vm->heap.gcnew<DummyInstance>(t);
});
_vm->bind_method<0>(VM::tp_object, "_enable_instance_dict", [](VM* vm, ArgsView args){
PyObject* self = args[0];
if(is_tagged(self)){
vm->TypeError("object: tagged object cannot enable instance dict");
}
if(self->is_attr_valid()){
vm->TypeError("object: instance dict is already enabled");
}
self->_enable_instance_dict();
return vm->None;
});
// tp_type
_vm->bind_constructor<2>(_vm->_t(VM::tp_type), PK_LAMBDA(vm->_t(args[1])));
@ -1588,6 +1578,21 @@ void VM::post_init(){
return VAR(MappingProxy(args[0]));
});
bind(builtins, "print(*args, sep=' ', end='\\n')", [](VM* vm, ArgsView args) {
const Tuple& _0 = CAST(Tuple&, args[0]);
const Str& _1 = CAST(Str&, args[1]);
const Str& _2 = CAST(Str&, args[2]);
SStream ss;
for(int i=0; i<_0.size(); i++){
ss << CAST(Str&, vm->py_str(_0[i]));
if(i != _0.size()-1) ss << _1;
}
ss << _2;
vm->stdout_write(ss.str());
return vm->None;
});
add_module___builtins(vm);
add_module_sys(this);
add_module_traceback(this);
add_module_time(this);
@ -1599,7 +1604,7 @@ void VM::post_init(){
add_module_random(this);
add_module_base64(this);
add_module_operator(this);
_lazy_modules["this"] = kPythonLibs_this;
_lazy_modules["functools"] = kPythonLibs_functools;
_lazy_modules["heapq"] = kPythonLibs_heapq;

View File

@ -1,3 +1,5 @@
from __builtins import next
a = [1, 2, 3]
a = iter(a)
@ -37,14 +39,9 @@ assert next(i) == 5
assert next(i) == StopIteration
assert next(i) == StopIteration
import builtins
# test normal next
from builtins import next
def next(obj):
res = builtins.next(obj)
if res is StopIteration:
raise StopIteration
return res
a = iter([1])
assert next(a) == 1

View File

@ -64,7 +64,11 @@ try:
except ValueError:
pass
assert next(t) == StopIteration
try:
next(t)
exit(1)
except StopIteration:
pass
def f():
yield 1