Bladeren bron

add module `operator` and add `split` for single char

blueloveTH 2 jaren geleden
bovenliggende
commit
f2d5fbbb94
10 gewijzigde bestanden met toevoegingen van 82 en 46 verwijderingen
  1. 3 3
      README.md
  2. 14 0
      docs/modules/operator.md
  3. 1 0
      include/pocketpy/str.h
  4. 1 0
      include/pocketpy/vm.h
  5. 15 30
      python/builtins.py
  6. 19 1
      src/pocketpy.cpp
  7. 14 0
      src/str.cpp
  8. 8 11
      src/vm.cpp
  9. 3 0
      tests/04_str.py
  10. 4 1
      tests/99_builtin_func.py

+ 3 - 3
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
 ```

+ 14 - 0
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)`

+ 1 - 0
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<std::string_view> split(const Str& sep) const;
+    std::vector<std::string_view> split(char sep) const;
     int count(const Str& sub) const;
 
     /*************unicode*************/

+ 1 - 0
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<int ARGC>
     PyObject* bind_func(Str type, Str name, NativeFuncC fn) {

+ 15 - 30
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 #####

+ 19 - 1
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<std::string_view> parts = self.split(CAST(Str&, args[1]));
+        const Str& sep = CAST(Str&, args[1]);
+        if(sep.empty()) vm->ValueError("empty separator");
+        std::vector<std::string_view> parts;
+        if(sep.size == 1){
+            parts = self.split(sep[0]);
+        }else{
+            parts = self.split(sep);
+        }
         List ret(parts.size());
         for(int i=0; i<parts.size(); i++) ret[i] = VAR(Str(parts[i]));
         return VAR(std::move(ret));
@@ -1401,6 +1408,16 @@ void add_module_timeit(VM* vm){
     });
 }
 
+void add_module_operator(VM* vm){
+    PyObject* mod = vm->new_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];

+ 14 - 0
src/str.cpp

@@ -344,6 +344,20 @@ int utf8len(unsigned char c, bool suppress){
         return result;
     }
 
+    std::vector<std::string_view> Str::split(char sep) const{
+        std::vector<std::string_view> 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;

+ 8 - 11
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<std::string_view>& cpnts){
+        static auto f_join = [](const std::vector<std::string_view>& cpnts){
             SStream ss;
             for(int i=0; i<cpnts.size(); i++){
                 if(i != 0) ss << ".";
@@ -303,7 +303,7 @@ namespace pkpy{
             Str curr_path = _import_context.pending.back();
             bool curr_is_init = _import_context.pending_is_init.back();
             // convert relative path to absolute path
-            std::vector<std::string_view> cpnts = curr_path.split(".");
+            std::vector<std::string_view> cpnts = curr_path.split('.');
             int prefix = 0;     // how many dots in the prefix
             for(int i=0; i<path.length(); i++){
                 if(path[i] == '.') prefix++;
@@ -316,22 +316,19 @@ namespace pkpy{
             path = f_join(cpnts);
         }
 
-        // std::cout << "py_import(" << path.escape() << ")" << std::endl;
+        PK_ASSERT(path.begin()[0] != '.' && path.end()[-1] != '.');
 
-        PK_ASSERT(path.begin()[0] != '.');
-        PK_ASSERT(path.end()[-1] != '.');
-
-        auto path_cpnts = path.split(".");
+        // check existing module
+        StrName name(path);
+        PyObject* ext_mod = _modules.try_get(name);
+        if(ext_mod != nullptr) return ext_mod;
 
+        std::vector<std::string_view> 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;

+ 3 - 0
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

+ 4 - 1
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