diff --git a/src/ceval.h b/src/ceval.h index 13d5f39e..57b792e8 100644 --- a/src/ceval.h +++ b/src/ceval.h @@ -165,6 +165,16 @@ __NEXT_STEP:; TARGET(STORE_FAST) frame->_locals[byte.arg] = POPX(); DISPATCH(); + TARGET(STORE_NAME) { + StrName name(byte.arg); + PyObject* val = POPX(); + if(frame->_locals.is_valid()){ + bool ok = frame->_locals.try_set(name, val); + if(!ok) vm->NameError(name); + }else{ + frame->f_globals().set(name, val); + } + } DISPATCH(); TARGET(STORE_GLOBAL) { StrName name(byte.arg); frame->f_globals().set(name, POPX()); @@ -188,6 +198,16 @@ __NEXT_STEP:; if(val == nullptr) vm->NameError(co->varnames[byte.arg]); frame->_locals[byte.arg] = nullptr; } DISPATCH(); + TARGET(DELETE_NAME) { + StrName name(byte.arg); + if(frame->_locals.is_valid()){ + if(!frame->_locals.contains(name)) vm->NameError(name); + frame->_locals.erase(name); + }else{ + if(!frame->f_globals().contains(name)) vm->NameError(name); + frame->f_globals().erase(name); + } + } DISPATCH(); TARGET(DELETE_GLOBAL) { StrName name(byte.arg); if(frame->f_globals().contains(name)){ diff --git a/src/codeobject.h b/src/codeobject.h index ec869834..222561f6 100644 --- a/src/codeobject.h +++ b/src/codeobject.h @@ -5,7 +5,7 @@ namespace pkpy{ -enum NameScope { NAME_LOCAL, NAME_GLOBAL }; +enum NameScope { NAME_LOCAL, NAME_GLOBAL, NAME_GLOBAL_UNKNOWN }; enum Opcode { #define OPCODE(name) OP_##name, diff --git a/src/compiler.h b/src/compiler.h index 59dbb5fb..af25de50 100644 --- a/src/compiler.h +++ b/src/compiler.h @@ -21,6 +21,7 @@ class Compiler { std::unique_ptr lexer; stack contexts; VM* vm; + bool unknown_global_scope; // for eval/exec() call bool used; // for parsing token stream int i = 0; @@ -37,7 +38,11 @@ class Compiler { CodeEmitContext* ctx() { return &contexts.top(); } CompileMode mode() const{ return lexer->src->mode; } - NameScope name_scope() const { return contexts.size()>1 ? NAME_LOCAL : NAME_GLOBAL; } + NameScope name_scope() const { + auto s = contexts.size()>1 ? NAME_LOCAL : NAME_GLOBAL; + if(unknown_global_scope && s == NAME_GLOBAL) s = NAME_GLOBAL_UNKNOWN; + return s; + } CodeObject_ push_global_context(){ CodeObject_ co = make_sp(lexer->src, lexer->src->filename); @@ -977,9 +982,10 @@ __SUBSCR_END: void IndentationError(Str msg){ lexer->throw_err("IndentationError", msg, err().line, err().start); } public: - Compiler(VM* vm, const Str& source, const Str& filename, CompileMode mode){ + Compiler(VM* vm, const Str& source, const Str& filename, CompileMode mode, bool unknown_global_scope=false){ this->vm = vm; this->used = false; + this->unknown_global_scope = unknown_global_scope; this->lexer = std::make_unique( make_sp(source, filename, mode) ); diff --git a/src/expr.h b/src/expr.h index f3db73f6..461d9a26 100644 --- a/src/expr.h +++ b/src/expr.h @@ -136,6 +136,8 @@ struct NameExpr: Expr{ ctx->emit(OP_LOAD_FAST, index, line); }else{ Opcode op = ctx->level <= 1 ? OP_LOAD_GLOBAL : OP_LOAD_NONLOCAL; + // we cannot determine the scope when calling exec()/eval() + if(scope == NAME_GLOBAL_UNKNOWN) op = OP_LOAD_NAME; ctx->emit(op, StrName(name).index, line); } } @@ -148,6 +150,9 @@ struct NameExpr: Expr{ case NAME_GLOBAL: ctx->emit(OP_DELETE_GLOBAL, StrName(name).index, line); break; + case NAME_GLOBAL_UNKNOWN: + ctx->emit(OP_DELETE_NAME, StrName(name).index, line); + break; default: FATAL_ERROR(); break; } return true; @@ -166,6 +171,9 @@ struct NameExpr: Expr{ case NAME_GLOBAL: ctx->emit(OP_STORE_GLOBAL, StrName(name).index, line); break; + case NAME_GLOBAL_UNKNOWN: + ctx->emit(OP_STORE_NAME, StrName(name).index, line); + break; default: FATAL_ERROR(); break; } return true; diff --git a/src/frame.h b/src/frame.h index 5de6ee74..2236c441 100644 --- a/src/frame.h +++ b/src/frame.h @@ -1,6 +1,7 @@ #pragma once #include "codeobject.h" +#include "common.h" #include "memory.h" #include "vector.h" @@ -35,6 +36,17 @@ struct FastLocals{ return a[index]; } + bool contains(StrName name){ + return varnames_inv->contains(name); + } + + void erase(StrName name){ + if(!is_valid()) return; + int index = varnames_inv->try_get(name); + if(index == -1) FATAL_ERROR(); + a[index] = nullptr; + } + bool try_set(StrName name, PyObject* value){ if(!is_valid()) return false; int index = varnames_inv->try_get(name); diff --git a/src/opcodes.h b/src/opcodes.h index 28658d52..4c19bdcb 100644 --- a/src/opcodes.h +++ b/src/opcodes.h @@ -27,11 +27,13 @@ OPCODE(LOAD_METHOD) OPCODE(LOAD_SUBSCR) OPCODE(STORE_FAST) +OPCODE(STORE_NAME) OPCODE(STORE_GLOBAL) OPCODE(STORE_ATTR) OPCODE(STORE_SUBSCR) OPCODE(DELETE_FAST) +OPCODE(DELETE_NAME) OPCODE(DELETE_GLOBAL) OPCODE(DELETE_ATTR) OPCODE(DELETE_SUBSCR) diff --git a/src/pocketpy.h b/src/pocketpy.h index 49dabf8e..b9d170d5 100644 --- a/src/pocketpy.h +++ b/src/pocketpy.h @@ -11,8 +11,8 @@ namespace pkpy { -inline CodeObject_ VM::compile(Str source, Str filename, CompileMode mode) { - Compiler compiler(this, source, filename, mode); +inline CodeObject_ VM::compile(Str source, Str filename, CompileMode mode, bool unknown_global_scope) { + Compiler compiler(this, source, filename, mode, unknown_global_scope); try{ return compiler.compile(); }catch(Exception& e){ @@ -97,13 +97,13 @@ inline void init_builtins(VM* _vm) { }); _vm->bind_builtin_func<1>("eval", [](VM* vm, Args& args) { - CodeObject_ code = vm->compile(CAST(Str&, args[0]), "", EVAL_MODE); + CodeObject_ code = vm->compile(CAST(Str&, args[0]), "", EVAL_MODE, true); FrameId frame = vm->top_frame(); return vm->_exec(code.get(), frame->_module, frame->_locals, nullptr); }); _vm->bind_builtin_func<1>("exec", [](VM* vm, Args& args) { - CodeObject_ code = vm->compile(CAST(Str&, args[0]), "", EXEC_MODE); + CodeObject_ code = vm->compile(CAST(Str&, args[0]), "", EXEC_MODE, true); FrameId frame = vm->top_frame(); vm->_exec(code.get(), frame->_module, frame->_locals, nullptr); return vm->None; diff --git a/src/vm.h b/src/vm.h index 03e3397b..38b3cc5a 100644 --- a/src/vm.h +++ b/src/vm.h @@ -333,7 +333,7 @@ public: _lazy_modules.clear(); } - CodeObject_ compile(Str source, Str filename, CompileMode mode); + CodeObject_ compile(Str source, Str filename, CompileMode mode, bool unknown_global_scope=false); PyObject* num_negated(PyObject* obj); f64 num_to_float(PyObject* obj); bool asBool(PyObject* obj); diff --git a/tests/43_eval.py b/tests/43_eval.py index c8ffdec2..a2fcb06b 100644 --- a/tests/43_eval.py +++ b/tests/43_eval.py @@ -1,33 +1,32 @@ assert eval('1+1') == 2 assert eval('[1,2,3]') == [1,2,3] -# some bugs here -# def f(x): -# return eval('x') +def f(x): + return eval('x') -# assert f(1) == 1 +assert f(1) == 1 -# a = 0 -# assert eval('a') == 0 +a = 0 +assert eval('a') == 0 -# exec('a = 1') -# assert a == 1 +exec('a = 1') +assert a == 1 -# def f(x): -# exec('a = x') -# return a +def f(a): + exec('a = 3') + return a -# assert f(2) == 2 +assert f(2) == 3 -# exec( -# "exec('a = eval(\"3 + 5\")')" -# ) -# assert a == 8 +exec( + "exec('a = eval(\"3 + 5\")')" +) +assert a == 8 -# def f(): -# b = 1 -# exec( -# "exec('b = eval(\"3 + 5\")')" -# ) -# assert b == 8 \ No newline at end of file +def f(): + b = 1 + exec( + "exec('b = eval(\"3 + 5\")')" + ) + assert b == 8 \ No newline at end of file