blueloveTH 2 лет назад
Родитель
Сommit
e21005e5d2
10 измененных файлов с 82 добавлено и 29 удалено
  1. 0 1
      include/pocketpy/compiler.h
  2. 1 1
      include/pocketpy/opcodes.h
  3. 1 0
      include/pocketpy/str.h
  4. 1 1
      include/pocketpy/vm.h
  5. 6 6
      src/ceval.cpp
  6. 45 15
      src/compiler.cpp
  7. 10 3
      src/pocketpy.cpp
  8. 13 0
      src/str.cpp
  9. 2 1
      src/vm.cpp
  10. 3 1
      tests/04_str.py

+ 0 - 1
include/pocketpy/compiler.h

@@ -105,7 +105,6 @@ class Compiler {
     void exprLiteral0();
 
     void compile_block_body();
-    Str _compile_import();
     void compile_normal_import();
     void compile_from_import();
     bool is_expression();

+ 1 - 1
include/pocketpy/opcodes.h

@@ -108,7 +108,7 @@ OPCODE(FOR_ITER)
 /**************************/
 OPCODE(IMPORT_NAME)
 OPCODE(IMPORT_NAME_REL)
-OPCODE(IMPORT_STAR)
+OPCODE(POP_IMPORT_STAR)
 /**************************/
 OPCODE(UNPACK_SEQUENCE)
 OPCODE(UNPACK_EX)

+ 1 - 0
include/pocketpy/str.h

@@ -73,6 +73,7 @@ struct Str{
     Str escape(bool single_quote=true) const;
     int index(const Str& sub, int start=0) const;
     Str replace(const Str& old, const Str& new_, int count=-1) const;
+    std::vector<std::string_view> split(Str sep) const;
 
     /*************unicode*************/
     int _unicode_index_to_byte(int i) const;

+ 1 - 1
include/pocketpy/vm.h

@@ -433,7 +433,7 @@ public:
     };
 
     ImportContext _import_context;
-    PyObject* py_import(StrName name, bool relative=false);
+    PyObject* py_import(Str path, bool relative=false);
     ~VM();
 
 #if PK_DEBUG_CEVAL_STEP

+ 6 - 6
src/ceval.cpp

@@ -591,15 +591,15 @@ __NEXT_STEP:;
         DISPATCH();
     /*****************************************/
     TARGET(IMPORT_NAME)
-        _name = StrName(byte.arg);
-        PUSH(py_import(_name));
+        _0 = co_consts[byte.arg];
+        PUSH(py_import(CAST(Str&, _0)));
         DISPATCH();
     TARGET(IMPORT_NAME_REL)
-        _name = StrName(byte.arg);
-        PUSH(py_import(_name, true));
+        _0 = co_consts[byte.arg];
+        PUSH(py_import(CAST(Str&, _0), true));
         DISPATCH();
-    TARGET(IMPORT_STAR) {
-        _0 = POPX();
+    TARGET(POP_IMPORT_STAR) {
+        _0 = POPX();        // pop the module
         _1 = _0->attr().try_get(__all__);
         if(_1 != nullptr){
             for(PyObject* key: CAST(List&, _1)){

+ 45 - 15
src/compiler.cpp

@@ -472,20 +472,14 @@ __SUBSCR_END:
         consume(TK("@dedent"));
     }
 
-    Str Compiler::_compile_import() {
-        if(name_scope() != NAME_GLOBAL) SyntaxError("import statement should be used in global scope");
-        Opcode op = OP_IMPORT_NAME;
-        if(match(TK("."))) op = OP_IMPORT_NAME_REL;
-        consume(TK("@id"));
-        Str name = prev().str();
-        ctx()->emit(op, StrName(name).index, prev().line);
-        return name;
-    }
-
-    // import a as b
+    // import a [as b]
+    // import a [as b], c [as d]
     void Compiler::compile_normal_import() {
+        if(name_scope() != NAME_GLOBAL) SyntaxError("import statement should be used in global scope");
         do {
-            Str name = _compile_import();
+            consume(TK("@id"));
+            Str name = prev().str();
+            ctx()->emit(OP_IMPORT_NAME, ctx()->add_const(VAR(name)), prev().line);
             if (match(TK("as"))) {
                 consume(TK("@id"));
                 name = prev().str();
@@ -495,19 +489,55 @@ __SUBSCR_END:
         consume_end_stmt();
     }
 
-    // from a import b as c, d as e
+    // from a import b [as c], d [as e]
+    // from a.b import c [as d]
+    // from . import a [as b]
+    // from .a import b [as c]
+    // from .a.b import c [as d]
+    // from xxx import *
     void Compiler::compile_from_import() {
-        _compile_import();
+        if(name_scope() != NAME_GLOBAL) SyntaxError("import statement should be used in global scope");
+        Opcode op = OP_IMPORT_NAME;
+        if(match(TK("."))) op = OP_IMPORT_NAME_REL;
+        std::vector<Str> parts;
+
+        if(op == OP_IMPORT_NAME_REL){
+            if(match(TK("@id"))){
+                parts.push_back(prev().str());
+                while (match(TK("."))) {
+                    consume(TK("@id"));
+                    parts.push_back(prev().str());
+                }
+            }
+        }else{
+            consume(TK("@id"));
+            parts.push_back(prev().str());
+            while (match(TK("."))) {
+                consume(TK("@id"));
+                parts.push_back(prev().str());
+            }
+        }
+
+        FastStrStream ss;
+        for (int i=0; i<parts.size(); i++) {
+            if(i > 0) ss << ".";
+            ss << parts[i];
+        }
+        ctx()->emit(op, ctx()->add_const(VAR(ss.str())), prev().line);
         consume(TK("import"));
+
         if (match(TK("*"))) {
-            ctx()->emit(OP_IMPORT_STAR, BC_NOARG, prev().line);
+            // pop the module and import __all__
+            ctx()->emit(OP_POP_IMPORT_STAR, BC_NOARG, prev().line);
             consume_end_stmt();
             return;
         }
+
         do {
             ctx()->emit(OP_DUP_TOP, BC_NOARG, BC_KEEPLINE);
             consume(TK("@id"));
             Str name = prev().str();
+            // module's __getattr__ should be customized or use a new opcode...
             ctx()->emit(OP_LOAD_ATTR, StrName(name).index, prev().line);
             if (match(TK("as"))) {
                 consume(TK("@id"));

+ 10 - 3
src/pocketpy.cpp

@@ -549,16 +549,23 @@ void init_builtins(VM* _vm) {
         return VAR(self.u8_getitem(i));
     });
 
-    _vm->bind_method<-1>("str", "replace", [](VM* vm, ArgsView args) {
-        if(args.size() != 1+2 && args.size() != 1+3) vm->TypeError("replace() takes 2 or 3 arguments");
+    _vm->bind(_vm->_t(_vm->tp_str), "replace(self, old, new, count=-1)", [](VM* vm, ArgsView args) {
         const Str& self = _CAST(Str&, args[0]);
         const Str& old = CAST(Str&, args[1]);
         if(old.empty()) vm->ValueError("empty substring");
         const Str& new_ = CAST(Str&, args[2]);
-        int count = args.size()==1+3 ? CAST(int, args[3]) : -1;
+        int count = CAST(int, args[3]);
         return VAR(self.replace(old, new_, count));
     });
 
+    _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]));
+        List ret(parts.size());
+        for(int i=0; i<parts.size(); i++) ret[i] = VAR(Str(parts[i]));
+        return VAR(std::move(ret));
+    });
+
     _vm->bind_method<1>("str", "index", [](VM* vm, ArgsView args) {
         const Str& self = _CAST(Str&, args[0]);
         const Str& sub = CAST(Str&, args[1]);

+ 13 - 0
src/str.cpp

@@ -308,6 +308,19 @@ int utf8len(unsigned char c, bool suppress){
         return _byte_index_to_unicode(size);
     }
 
+    std::vector<std::string_view> Str::split(Str sep) const{
+        std::vector<std::string_view> result;
+        int start = 0;
+        while(true){
+            int i = index(sep, start);
+            if(i == -1) break;
+            result.push_back(sv().substr(start, i - start));
+            start = i + sep.size;
+        }
+        result.push_back(sv().substr(start, size - start));
+        return result;
+    }
+
     std::ostream& operator<<(std::ostream& os, const StrName& sn){
         return os << sn.sv();
     }

+ 2 - 1
src/vm.cpp

@@ -215,7 +215,8 @@ namespace pkpy{
         return call_method(obj, __next__);
     }
 
-    PyObject* VM::py_import(StrName name, bool relative){
+    PyObject* VM::py_import(Str name, bool relative){
+        // path is '.' separated
         Str filename;
         int type;
         if(relative){

+ 3 - 1
tests/04_str.py

@@ -33,7 +33,9 @@ assert s[-3] == 'a'
 assert t[-5:] == 'ow!!!'
 assert t[3:-3] == 's is string example....wow'
 assert s > q;assert s < r
-assert s.replace("foo","ball") == "balltball"
+assert s.replace("o","") == "ftball"
+assert s.replace("o","O",1) == "fOotball"
+assert s.replace("foo","ball",1) == "balltball"
 assert s.startswith('f') == True;assert s.endswith('o') == False
 assert t.startswith('this') == True;