diff --git a/README.md b/README.md index 8d790084..6f233314 100644 --- a/README.md +++ b/README.md @@ -58,12 +58,12 @@ To compile it with your project, these flags must be set: + Exception must be enabled + For MSVC, `/utf-8` flag must be set -For development build on Linux, use this snippet. +For development build, use this snippet. ```bash # prerequisites -sudo apt-get install libc++-dev libc++abi-dev clang +pip install cmake # build the repo -bash build.sh +python cmake_build.py # unittest python scripts/run_tests.py ``` diff --git a/docs/modules/operator.md b/docs/modules/operator.md new file mode 100644 index 00000000..95c21a37 --- /dev/null +++ b/docs/modules/operator.md @@ -0,0 +1,14 @@ +--- +icon: package +label: operator +--- + +The operator module exports a set of efficient functions corresponding to the intrinsic operators of Python. For example, operator.add(x, y) is equivalent to the expression x+y. +Many function names are those used for special methods, without the double underscores. + ++ `operator.lt(a, b)` ++ `operator.le(a, b)` ++ `operator.eq(a, b)` ++ `operator.ne(a, b)` ++ `operator.ge(a, b)` ++ `operator.gt(a, b)` \ No newline at end of file diff --git a/include/pocketpy/str.h b/include/pocketpy/str.h index d0b85f2b..ed1e0021 100644 --- a/include/pocketpy/str.h +++ b/include/pocketpy/str.h @@ -76,6 +76,7 @@ struct Str{ Str replace(char old, char new_) const; Str replace(const Str& old, const Str& new_, int count=-1) const; std::vector split(const Str& sep) const; + std::vector split(char sep) const; int count(const Str& sub) const; /*************unicode*************/ diff --git a/include/pocketpy/vm.h b/include/pocketpy/vm.h index 8ab0710d..8249d62f 100644 --- a/include/pocketpy/vm.h +++ b/include/pocketpy/vm.h @@ -310,6 +310,7 @@ public: bool py_le(PyObject* lhs, PyObject* rhs); bool py_gt(PyObject* lhs, PyObject* rhs); bool py_ge(PyObject* lhs, PyObject* rhs); + bool py_ne(PyObject* lhs, PyObject* rhs) { return !py_eq(lhs, rhs); } template PyObject* bind_func(Str type, Str name, NativeFuncC fn) { diff --git a/python/builtins.py b/python/builtins.py index a089f460..84ccd66b 100644 --- a/python/builtins.py +++ b/python/builtins.py @@ -4,49 +4,34 @@ def print(*args, sep=' ', end='\n'): s = sep.join([str(i) for i in args]) _sys.stdout.write(s + end) -def max(*args, key=None): - 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('max expected 1 arguments, got 0') + raise TypeError('expected 1 arguments, got 0') if len(args) == 1: args = args[0] - if len(args) == 2: - a, b = args - return a if key(a) > key(b) else b args = iter(args) res = next(args) if res is StopIteration: - raise ValueError('max() arg is an empty sequence') + raise ValueError('args is an empty sequence') while True: i = next(args) if i is StopIteration: break - if key(i) > key(res): + if op(key(i), key(res)): res = i return res def min(*args, key=None): - if key is None: - key = lambda x: x - if len(args) == 0: - raise TypeError('min expected 1 arguments, got 0') - if len(args) == 1: - args = args[0] - if len(args) == 2: - a, b = args - return a if key(a) < key(b) else b - args = iter(args) - res = next(args) - if res is StopIteration: - raise ValueError('min() arg is an empty sequence') - while True: - i = next(args) - if i is StopIteration: - break - if key(i) < key(res): - res = i - return res + from operator import lt + return _minmax_reduce(lt, args, key) + +def max(*args, key=None): + from operator import gt + return _minmax_reduce(gt, args, key) def all(iterable): for i in iterable: @@ -96,9 +81,9 @@ def reversed(iterable): a.reverse() return a -def sorted(iterable, reverse=False, key=None): +def sorted(iterable, key=None, reverse=False): a = list(iterable) - a.sort(reverse=reverse, key=key) + a.sort(key=key, reverse=reverse) return a ##### str ##### diff --git a/src/pocketpy.cpp b/src/pocketpy.cpp index a9f92a21..f03d0687 100644 --- a/src/pocketpy.cpp +++ b/src/pocketpy.cpp @@ -623,7 +623,14 @@ void init_builtins(VM* _vm) { _vm->bind(_vm->_t(_vm->tp_str), "split(self, sep=' ')", [](VM* vm, ArgsView args) { const Str& self = _CAST(Str&, args[0]); - std::vector parts = self.split(CAST(Str&, args[1])); + const Str& sep = CAST(Str&, args[1]); + if(sep.empty()) vm->ValueError("empty separator"); + std::vector parts; + if(sep.size == 1){ + parts = self.split(sep[0]); + }else{ + parts = self.split(sep); + } List ret(parts.size()); for(int i=0; inew_module("operator"); + vm->bind_func<2>(mod, "lt", [](VM* vm, ArgsView args) { return VAR(vm->py_lt(args[0], args[1]));}); + vm->bind_func<2>(mod, "le", [](VM* vm, ArgsView args) { return VAR(vm->py_le(args[0], args[1]));}); + vm->bind_func<2>(mod, "eq", [](VM* vm, ArgsView args) { return VAR(vm->py_eq(args[0], args[1]));}); + vm->bind_func<2>(mod, "ne", [](VM* vm, ArgsView args) { return VAR(vm->py_ne(args[0], args[1]));}); + vm->bind_func<2>(mod, "ge", [](VM* vm, ArgsView args) { return VAR(vm->py_ge(args[0], args[1]));}); + vm->bind_func<2>(mod, "gt", [](VM* vm, ArgsView args) { return VAR(vm->py_gt(args[0], args[1]));}); +} + struct PyStructTime{ PY_CLASS(PyStructTime, time, struct_time) @@ -1702,6 +1719,7 @@ void VM::post_init(){ add_module_random(this); add_module_base64(this); add_module_timeit(this); + add_module_operator(this); for(const char* name: {"this", "functools", "heapq", "bisect", "pickle", "_long", "colorsys", "typing", "datetime"}){ _lazy_modules[name] = kPythonLibs[name]; diff --git a/src/str.cpp b/src/str.cpp index 7c32ac0a..5e00be5b 100644 --- a/src/str.cpp +++ b/src/str.cpp @@ -344,6 +344,20 @@ int utf8len(unsigned char c, bool suppress){ return result; } + std::vector Str::split(char sep) const{ + std::vector result; + int i = 0; + for(int j = 0; j < size; j++){ + if(data[j] == sep){ + if(j > i) result.emplace_back(data+i, j-i); + i = j + 1; + continue; + } + } + if(size > i) result.emplace_back(data+i, size-i); + return result; + } + int Str::count(const Str& sub) const{ if(sub.empty()) return size + 1; int cnt = 0; diff --git a/src/vm.cpp b/src/vm.cpp index 8489aaa0..921f2b10 100644 --- a/src/vm.cpp +++ b/src/vm.cpp @@ -287,7 +287,7 @@ namespace pkpy{ PyObject* VM::py_import(Str path, bool throw_err){ if(path.empty()) vm->ValueError("empty module name"); - auto f_join = [](const std::vector& cpnts){ + static auto f_join = [](const std::vector& cpnts){ SStream ss; for(int i=0; i cpnts = curr_path.split("."); + std::vector cpnts = curr_path.split('.'); int prefix = 0; // how many dots in the prefix for(int i=0; i path_cpnts = path.split('.'); // check circular import if(_import_context.pending.size() > 128){ ImportError("maximum recursion depth exceeded while importing"); } - StrName name(path); // path to StrName - PyObject* ext_mod = _modules.try_get(name); - if(ext_mod != nullptr) return ext_mod; - // try import Str filename = path.replace('.', kPlatformSep) + ".py"; Str source; diff --git a/tests/04_str.py b/tests/04_str.py index 6a2e1a44..327b4a9a 100644 --- a/tests/04_str.py +++ b/tests/04_str.py @@ -47,6 +47,9 @@ assert 'foo!!bar!!baz'.split('!!') == ['foo', 'bar', 'baz'] assert ' 4 3 '.split() == ['4', '3'] assert ' 4 3 '.split(' ') == ['4', '3'] +x = 'aa bb cccc' +assert x.split('cc') == ['aa bb '] + assert '111'.count('1') == 3 assert '111'.count('11') == 1 assert '1111'.count('11') == 2 diff --git a/tests/99_builtin_func.py b/tests/99_builtin_func.py index f481690d..2e445111 100644 --- a/tests/99_builtin_func.py +++ b/tests/99_builtin_func.py @@ -925,7 +925,7 @@ assert min([1, 2], key=lambda x: -x) == 2 assert max(1, 2) == 2 assert max(1, 2, 3) == 3 assert max([1, 2]) == 2 -assert max([1, 2], key=lambda x: -x) == 1 +assert max([1, 2, 3], key=lambda x: -x) == 1 assert min([ (1, 2), @@ -933,6 +933,9 @@ assert min([ (1, 4), ]) == (1, 2) +assert min(1, 2) == 1 +assert max(1, 2) == 2 + # test callable assert callable(lambda: 1) is True # function