blueloveTH 2 лет назад
Родитель
Сommit
c01a77d4b4
9 измененных файлов с 77 добавлено и 30 удалено
  1. 20 0
      src/ceval.h
  2. 1 1
      src/codeobject.h
  3. 8 2
      src/compiler.h
  4. 8 0
      src/expr.h
  5. 12 0
      src/frame.h
  6. 2 0
      src/opcodes.h
  7. 4 4
      src/pocketpy.h
  8. 1 1
      src/vm.h
  9. 21 22
      tests/43_eval.py

+ 20 - 0
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)){

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

+ 8 - 2
src/compiler.h

@@ -21,6 +21,7 @@ class Compiler {
     std::unique_ptr<Lexer> lexer;
     stack<CodeEmitContext> 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<CodeObject>(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<Lexer>(
             make_sp<SourceData>(source, filename, mode)
         );

+ 8 - 0
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;

+ 12 - 0
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);

+ 2 - 0
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)

+ 4 - 4
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>", EVAL_MODE);
+        CodeObject_ code = vm->compile(CAST(Str&, args[0]), "<eval>", 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>", EXEC_MODE);
+        CodeObject_ code = vm->compile(CAST(Str&, args[0]), "<exec>", EXEC_MODE, true);
         FrameId frame = vm->top_frame();
         vm->_exec(code.get(), frame->_module, frame->_locals, nullptr);
         return vm->None;

+ 1 - 1
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);

+ 21 - 22
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
+def f():
+    b = 1
+    exec(
+        "exec('b = eval(\"3 + 5\")')"
+    )
+    assert b == 8