diff --git a/src/compiler.h b/src/compiler.h index e9d57295..363ecbe9 100644 --- a/src/compiler.h +++ b/src/compiler.h @@ -664,7 +664,7 @@ __LISTCOMP: return tkmodule; } - // import module1 [as alias1 [, module2 [as alias2 ...]] + // import a as b void compileRegularImport() { do { Token tkmodule = compileImportPath(); @@ -678,6 +678,25 @@ __LISTCOMP: consumeEndStatement(); } + // from a import b as c, d as e + void compileFromImport() { + Token tkmodule = compileImportPath(); + consume(TK("import")); + do { + consume(TK("@id")); + Token tkname = parser->previous; + int index = getCode()->addName(tkname.str(), NAME_GLOBAL); + emitCode(OP_BUILD_ATTR_PTR, index); + if (match(TK("as"))) { + consume(TK("@id")); + tkname = parser->previous; + } + index = getCode()->addName(tkname.str(), NAME_GLOBAL); + emitCode(OP_STORE_NAME_PTR, index); + } while (match(TK(","))); + consumeEndStatement(); + } + void parsePrecedence(Precedence precedence) { lexToken(); GrammarFn prefix = rules[parser->previous.type].prefix; @@ -786,6 +805,21 @@ __LISTCOMP: EXPR(); emitCode(OP_ASSERT); consumeEndStatement(); + } else if(match(TK("with"))){ + EXPR(); + consume(TK("as")); + consume(TK("@id")); + Token tkname = parser->previous; + int index = getCode()->addName( + tkname.str(), + codes.size()>1 ? NAME_LOCAL : NAME_GLOBAL + ); + emitCode(OP_STORE_NAME_PTR, index); + emitCode(OP_LOAD_NAME_PTR, index); + emitCode(OP_WITH_ENTER); + compileBlockBody(); + emitCode(OP_LOAD_NAME_PTR, index); + emitCode(OP_WITH_EXIT); } else if(match(TK("label"))){ if(mode() != EXEC_MODE) syntaxError("'label' is only available in EXEC_MODE"); consume(TK(".")); consume(TK("@id")); @@ -926,6 +960,8 @@ __LISTCOMP: compileFunction(); } else if (match(TK("import"))) { compileRegularImport(); + } else if (match(TK("from"))) { + compileFromImport(); } else { compileStatement(); } diff --git a/src/obj.h b/src/obj.h index ab3b82cf..be95c0e3 100644 --- a/src/obj.h +++ b/src/obj.h @@ -10,7 +10,7 @@ const _Int _Int_MAX_NEG = -9223372036854775807LL; const _Float _FLOAT_INF_POS = INFINITY; const _Float _FLOAT_INF_NEG = -INFINITY; -#define PK_VERSION "0.3.5" +#define PK_VERSION "0.3.6" class CodeObject; class BasePointer; diff --git a/src/opcodes.h b/src/opcodes.h index 1c6911f4..c36b6b27 100644 --- a/src/opcodes.h +++ b/src/opcodes.h @@ -63,4 +63,7 @@ OPCODE(GOTO) OPCODE(UNARY_REF) // for & OPCODE(UNARY_DEREF) // for * +OPCODE(WITH_ENTER) +OPCODE(WITH_EXIT) + #endif \ No newline at end of file diff --git a/src/parser.h b/src/parser.h index 3b9ed41f..82c64e5a 100644 --- a/src/parser.h +++ b/src/parser.h @@ -12,7 +12,7 @@ constexpr const char* __TOKENS[] = { "==", "!=", ">=", "<=", "+=", "-=", "*=", "/=", "//=", /** KW_BEGIN **/ - "class", "import", "as", "def", "lambda", "pass", "del", + "class", "import", "as", "def", "lambda", "pass", "del", "from", "with", "None", "in", "is", "and", "or", "not", "True", "False", "global", "goto", "label", // extended keywords, not available in cpython "while", "for", "if", "elif", "else", "break", "continue", "return", "assert", "raise", diff --git a/src/pocketpy.h b/src/pocketpy.h index c90ac45d..38ea2b37 100644 --- a/src/pocketpy.h +++ b/src/pocketpy.h @@ -404,11 +404,18 @@ void __initializeBuiltinFunctions(VM* _vm) { _vm->bindMethod("str", "join", [](VM* vm, const pkpy::ArgList& args) { vm->__checkArgSize(args, 2, true); const _Str& _self = vm->PyStr_AS_C(args[0]); - const PyVarList& _list = vm->PyList_AS_C(args[1]); + PyVarList* _list; + if(args[1]->isType(vm->_tp_list)){ + _list = &vm->PyList_AS_C(args[1]); + }else if(args[1]->isType(vm->_tp_tuple)){ + _list = &vm->PyTuple_AS_C(args[1]); + }else{ + vm->typeError("can only join a list or tuple"); + } _StrStream ss; - for(int i = 0; i < _list.size(); i++){ + for(int i = 0; i < _list->size(); i++){ if(i > 0) ss << _self; - ss << vm->PyStr_AS_C(vm->asStr(_list[i])); + ss << vm->PyStr_AS_C(vm->asStr(_list->operator[](i))); } return vm->PyStr(ss.str()); }); diff --git a/src/vm.h b/src/vm.h index e7a94107..df0f3271 100644 --- a/src/vm.h +++ b/src/vm.h @@ -345,6 +345,18 @@ protected: frame->push(it->second); } } break; + case OP_WITH_ENTER: + { + PyVar obj = frame->popValue(this); + PyVar enter_fn = getAttr(obj, "__enter__"_c); + call(enter_fn, {}); + } break; + case OP_WITH_EXIT: + { + PyVar obj = frame->popValue(this); + PyVar exit_fn = getAttr(obj, "__exit__"_c); + call(exit_fn, {}); + } break; default: systemError(_Str("opcode ") + OP_NAMES[byte.op] + " is not implemented"); break; diff --git a/tests/builtin_ty.py b/tests/builtin_ty.py index fb310e97..a6b4f322 100644 --- a/tests/builtin_ty.py +++ b/tests/builtin_ty.py @@ -48,6 +48,10 @@ seq = ["r","u","n","o","o","b"] assert s1.join( seq ) == "r-u-n-o-o-b" assert s2.join( seq ) == "runoob" +def test(*seq): + return s1.join(seq) +assert test("r", "u", "n", "o", "o", "b") == "r-u-n-o-o-b" + ##num = 6 ##assert str(num) == '6' TypeError: 'str' object is not callable diff --git a/tests/random.py b/tests/random.py index 64abcef9..dcda3aa7 100644 --- a/tests/random.py +++ b/tests/random.py @@ -1,6 +1,26 @@ -import random +import random as r, sys as s for _ in range(100): - i = random.randint(1, 10) + i = r.randint(1, 10) assert i <= 10 - assert i >= 1 \ No newline at end of file + assert i >= 1 + + +from sys import version as v + +assert type(v) is str + +class Context: + def __init__(self): + self.x = 0 + + def __enter__(self): + self.x = 1 + + def __exit__(self): + self.x = 2 + +with Context() as c: + assert c.x == 1 + +assert c.x == 2 \ No newline at end of file