diff --git a/include/pocketpy/compiler.h b/include/pocketpy/compiler.h index afc2b8fb..30dd7fc5 100644 --- a/include/pocketpy/compiler.h +++ b/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(); diff --git a/include/pocketpy/opcodes.h b/include/pocketpy/opcodes.h index 0de0e080..0cc05081 100644 --- a/include/pocketpy/opcodes.h +++ b/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) diff --git a/include/pocketpy/str.h b/include/pocketpy/str.h index a13c97c3..8f78d98f 100644 --- a/include/pocketpy/str.h +++ b/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 split(Str sep) const; /*************unicode*************/ int _unicode_index_to_byte(int i) const; diff --git a/include/pocketpy/vm.h b/include/pocketpy/vm.h index 5cbf6c39..3c7e97f8 100644 --- a/include/pocketpy/vm.h +++ b/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 diff --git a/src/ceval.cpp b/src/ceval.cpp index 6600f752..e034729c 100644 --- a/src/ceval.cpp +++ b/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)){ diff --git a/src/compiler.cpp b/src/compiler.cpp index 1923cdbf..5619b993 100644 --- a/src/compiler.cpp +++ b/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 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 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")); diff --git a/src/pocketpy.cpp b/src/pocketpy.cpp index b7f214b7..6ddd1dea 100644 --- a/src/pocketpy.cpp +++ b/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 parts = self.split(CAST(Str&, args[1])); + List ret(parts.size()); + for(int i=0; ibind_method<1>("str", "index", [](VM* vm, ArgsView args) { const Str& self = _CAST(Str&, args[0]); const Str& sub = CAST(Str&, args[1]); diff --git a/src/str.cpp b/src/str.cpp index be2a72b7..264f1f73 100644 --- a/src/str.cpp +++ b/src/str.cpp @@ -308,6 +308,19 @@ int utf8len(unsigned char c, bool suppress){ return _byte_index_to_unicode(size); } + std::vector Str::split(Str sep) const{ + std::vector 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(); } diff --git a/src/vm.cpp b/src/vm.cpp index bf743f18..8b045b3d 100644 --- a/src/vm.cpp +++ b/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){ diff --git a/tests/04_str.py b/tests/04_str.py index e447f5fc..1709923c 100644 --- a/tests/04_str.py +++ b/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;