blueloveTH 1 rok temu
rodzic
commit
e365124af3
5 zmienionych plików z 311 dodań i 37 usunięć
  1. 1 0
      src/common/sourcedata.c
  2. 3 5
      src/common/sstream.c
  3. 275 27
      src/compiler/compiler.c
  4. 27 2
      src/interpreter/ceval.c
  5. 5 3
      src/public/vm.c

+ 1 - 0
src/common/sourcedata.c

@@ -27,6 +27,7 @@ static void pk_SourceData__ctor(struct pk_SourceData* self,
     }
     self->source = c11_sbuf__submit(&ss);
     self->is_precompiled = (strncmp(source, "pkpy:", 5) == 0);
+    self->is_dynamic = is_dynamic;
     c11_vector__push(const char*, &self->line_starts, self->source->data);
 }
 

+ 3 - 5
src/common/sstream.c

@@ -10,12 +10,10 @@
 #include <ctype.h>
 #include <math.h>
 
-const static int C11_STRING_HEADER_SIZE = sizeof(c11_string);
-
 void c11_sbuf__ctor(c11_sbuf* self) {
     c11_vector__ctor(&self->data, sizeof(char));
-    c11_vector__reserve(&self->data, 100 + C11_STRING_HEADER_SIZE);
-    self->data.count = C11_STRING_HEADER_SIZE;
+    c11_vector__reserve(&self->data, 100 + sizeof(c11_string));
+    self->data.count = sizeof(c11_string);
 }
 
 void c11_sbuf__dtor(c11_sbuf* self) { c11_vector__dtor(&self->data); }
@@ -137,7 +135,7 @@ c11_string* c11_sbuf__submit(c11_sbuf* self) {
     c11_vector__push(char, &self->data, '\0');
     c11_array arr = c11_vector__submit(&self->data);
     c11_string* retval = (c11_string*)arr.data;
-    retval->size = arr.count - C11_STRING_HEADER_SIZE - 1;
+    retval->size = arr.count - sizeof(c11_string) - 1;
     return retval;
 }
 

+ 275 - 27
src/compiler/compiler.c

@@ -64,7 +64,7 @@ typedef struct Ctx {
     int curr_iblock;
     bool is_compiling_class;
     c11_vector /*T=Expr* */ s_expr;
-    c11_vector /*T=py_Name*/ global_names;
+    c11_smallmap_n2i global_names;
     c11_smallmap_s2n co_consts_string_dedup_map;
 } Ctx;
 
@@ -671,7 +671,7 @@ static void FStringExpr__emit_(Expr* self_, Ctx* ctx) {
                 int conon = c11_sv__index(expr, ':');
                 if(conon >= 0) {
                     c11_sv spec = {expr.data + (conon + 1),
-                                       expr.size - (conon + 1)};  // expr[conon+1:]
+                                   expr.size - (conon + 1)};  // expr[conon+1:]
                     // filter some invalid spec
                     bool ok = true;
                     for(int k = 0; k < spec.size; k++) {
@@ -1209,7 +1209,7 @@ void Ctx__ctor(Ctx* self, CodeObject* co, FuncDecl* func, int level) {
     self->curr_iblock = 0;
     self->is_compiling_class = false;
     c11_vector__ctor(&self->s_expr, sizeof(Expr*));
-    c11_vector__ctor(&self->global_names, sizeof(py_Name));
+    c11_smallmap_n2i__ctor(&self->global_names);
     c11_smallmap_s2n__ctor(&self->co_consts_string_dedup_map);
 }
 
@@ -1220,7 +1220,7 @@ void Ctx__dtor(Ctx* self) {
     }
     c11_vector__clear(&self->s_expr);
     c11_vector__dtor(&self->s_expr);
-    c11_vector__dtor(&self->global_names);
+    c11_smallmap_n2i__dtor(&self->global_names);
     c11_smallmap_s2n__dtor(&self->co_consts_string_dedup_map);
 }
 
@@ -1455,7 +1455,7 @@ static void Compiler__dtor(Compiler* self) {
                            pk_TokenSymbols[expected],                                              \
                            pk_TokenSymbols[curr()->type]);
 #define consume_end_stmt()                                                                         \
-    if(!match_end_stmt()) return SyntaxError("expected statement end")
+    if(!match_end_stmt(self)) return SyntaxError("expected statement end")
 #define check_newlines_repl()                                                                      \
     do {                                                                                           \
         bool __nml;                                                                                \
@@ -1471,7 +1471,7 @@ static NameScope name_scope(Compiler* self) {
     return s;
 }
 
-static Error* SyntaxError(const char* fmt, ...) { return NULL; }
+#define SyntaxError(...) NULL
 
 static Error* NeedMoreLines() { return NULL; }
 
@@ -1798,10 +1798,7 @@ static Error* exprName(Compiler* self) {
     py_Name name = py_name2(Token__sv(prev()));
     NameScope scope = name_scope(self);
     // promote this name to global scope if needed
-    c11_vector* global_names = &ctx()->global_names;
-    c11__foreach(py_Name, global_names, it) {
-        if(*it == name) scope = NAME_GLOBAL;
-    }
+    if(c11_smallmap_n2i__contains(&ctx()->global_names, name)) { scope = NAME_GLOBAL; }
     NameExpr* e = NameExpr__new(prev()->line, name, scope);
     Ctx__s_push(ctx(), (Expr*)e);
     return NULL;
@@ -2005,6 +2002,258 @@ static Error* exprSubscr(Compiler* self) {
     return NULL;
 }
 
+////////////////
+static Error* consume_type_hints(Compiler* self) {
+    Error* err;
+    check(EXPR(self));
+    Ctx__s_pop(ctx());
+    return NULL;
+}
+
+Error* try_compile_assignment(Compiler* self, bool* is_assign) {
+    Error* err;
+    switch(curr()->type) {
+        case TK_IADD:
+        case TK_ISUB:
+        case TK_IMUL:
+        case TK_IDIV:
+        case TK_IFLOORDIV:
+        case TK_IMOD:
+        case TK_ILSHIFT:
+        case TK_IRSHIFT:
+        case TK_IAND:
+        case TK_IOR:
+        case TK_IXOR: {
+            if(Ctx__s_top(ctx())->vt->is_starred) return SyntaxError();
+            if(ctx()->is_compiling_class) {
+                return SyntaxError("can't use inplace operator in class definition");
+            }
+            advance();
+            // a[x] += 1;   a and x should be evaluated only once
+            // a.x += 1;    a should be evaluated only once
+            // -1 to remove =; inplace=true
+            int line = prev()->line;
+            TokenIndex op = (TokenIndex)(prev()->type - 1);
+            // [lhs]
+            check(EXPR_TUPLE(self));  // [lhs, rhs]
+            if(Ctx__s_top(ctx())->vt->is_starred) return SyntaxError();
+            BinaryExpr* e = BinaryExpr__new(line, op, true);
+            e->rhs = Ctx__s_popx(ctx());  // [lhs]
+            e->lhs = Ctx__s_popx(ctx());  // []
+            vtemit_((Expr*)e, ctx());
+            bool ok = vtemit_istore(e->lhs, ctx());
+            vtdelete((Expr*)e);
+            if(!ok) return SyntaxError();
+            *is_assign = true;
+            return NULL;
+        }
+        case TK_ASSIGN: {
+            int n = 0;
+            while(match(TK_ASSIGN)) {
+                check(EXPR_TUPLE(self));
+                n += 1;
+            }
+            // stack size is n+1
+            Ctx__s_emit_top(ctx());
+            for(int j = 1; j < n; j++)
+                Ctx__emit_(ctx(), OP_DUP_TOP, BC_NOARG, BC_KEEPLINE);
+            for(int j = 0; j < n; j++) {
+                if(Ctx__s_top(ctx())->vt->is_starred) return SyntaxError();
+                Expr* e = Ctx__s_top(ctx());
+                bool ok = vtemit_store(e, ctx());
+                Ctx__s_pop(ctx());
+                if(!ok) return SyntaxError();
+            }
+            *is_assign = true;
+            return NULL;
+        }
+        default: *is_assign = false;
+    }
+    return NULL;
+}
+
+static Error* compile_stmt(Compiler* self) {
+    Error* err;
+    if(match(TK_CLASS)) {
+        // check(compile_class());
+        assert(false);
+        return NULL;
+    }
+    advance();
+    int kw_line = prev()->line;  // backup line number
+    int curr_loop_block = Ctx__get_loop(ctx());
+    switch(prev()->type) {
+        case TK_BREAK:
+            if(curr_loop_block < 0) return SyntaxError("'break' outside loop");
+            Ctx__emit_(ctx(), OP_LOOP_BREAK, curr_loop_block, kw_line);
+            consume_end_stmt();
+            break;
+        case TK_CONTINUE:
+            if(curr_loop_block < 0) return SyntaxError("'continue' not properly in loop");
+            Ctx__emit_(ctx(), OP_LOOP_CONTINUE, curr_loop_block, kw_line);
+            consume_end_stmt();
+            break;
+        case TK_YIELD:
+            if(self->contexts.count <= 1) return SyntaxError("'yield' outside function");
+            check(EXPR_TUPLE(self));
+            Ctx__s_emit_top(ctx());
+            Ctx__emit_(ctx(), OP_YIELD_VALUE, BC_NOARG, kw_line);
+            consume_end_stmt();
+            break;
+        case TK_YIELD_FROM:
+            if(self->contexts.count <= 1) return SyntaxError("'yield from' outside function");
+            check(EXPR_TUPLE(self));
+            Ctx__s_emit_top(ctx());
+            Ctx__emit_(ctx(), OP_GET_ITER_NEW, BC_NOARG, kw_line);
+            Ctx__enter_block(ctx(), CodeBlockType_FOR_LOOP);
+            Ctx__emit_(ctx(), OP_FOR_ITER_YIELD_VALUE, BC_NOARG, kw_line);
+            Ctx__emit_(ctx(), OP_LOOP_CONTINUE, Ctx__get_loop(ctx()), kw_line);
+            Ctx__exit_block(ctx());
+            consume_end_stmt();
+            break;
+        case TK_RETURN:
+            if(self->contexts.count <= 1) return SyntaxError("'return' outside function");
+            if(match_end_stmt(self)) {
+                Ctx__emit_(ctx(), OP_RETURN_VALUE, 1, kw_line);
+            } else {
+                check(EXPR_TUPLE(self));
+                Ctx__s_emit_top(ctx());
+                consume_end_stmt();
+                Ctx__emit_(ctx(), OP_RETURN_VALUE, BC_NOARG, kw_line);
+            }
+            break;
+        /*************************************************/
+        // case TK_IF: check(compile_if_stmt()); break;
+        // case TK_WHILE: check(compile_while_loop()); break;
+        // case TK_FOR: check(compile_for_loop()); break;
+        // case TK_IMPORT: check(compile_normal_import()); break;
+        // case TK_FROM: check(compile_from_import()); break;
+        // case TK_DEF: check(compile_function()); break;
+        // case TK_DECORATOR: check(compile_decorated()); break;
+        // case TK_TRY: check(compile_try_except()); break;
+        case TK_PASS: consume_end_stmt(); break;
+        /*************************************************/
+        case TK_ASSERT: {
+            check(EXPR(self));  // condition
+            Ctx__s_emit_top(ctx());
+            int index = Ctx__emit_(ctx(), OP_POP_JUMP_IF_TRUE, BC_NOARG, kw_line);
+            int has_msg = 0;
+            if(match(TK_COMMA)) {
+                check(EXPR(self));  // message
+                Ctx__s_emit_top(ctx());
+                has_msg = 1;
+            }
+            Ctx__emit_(ctx(), OP_RAISE_ASSERT, has_msg, kw_line);
+            Ctx__patch_jump(ctx(), index);
+            consume_end_stmt();
+            break;
+        }
+        case TK_GLOBAL:
+            do {
+                consume(TK_ID);
+                py_Name name = py_name2(Token__sv(prev()));
+                c11_smallmap_n2i__set(&ctx()->global_names, name, 0);
+            } while(match(TK_COMMA));
+            consume_end_stmt();
+            break;
+        case TK_RAISE: {
+            check(EXPR(self));
+            Ctx__s_emit_top(ctx());
+            Ctx__emit_(ctx(), OP_RAISE, BC_NOARG, kw_line);
+            consume_end_stmt();
+        } break;
+        case TK_DEL: {
+            check(EXPR_TUPLE(self));
+            Expr* e = Ctx__s_top(ctx());
+            if(!vtemit_del(e, ctx())) return SyntaxError();
+            Ctx__s_pop(ctx());
+            consume_end_stmt();
+        } break;
+        // case TK_WITH: {
+        //     check(EXPR(self));  // [ <expr> ]
+        //     Ctx__s_emit_top(ctx());
+        //     Ctx__enter_block(ctx(), CodeBlockType_CONTEXT_MANAGER);
+        //     Expr* as_name = nullptr;
+        //     if(match(TK_AS)) {
+        //         consume(TK_ID);
+        //         as_name = make_expr<NameExpr>(prev().str(), name_scope());
+        //     }
+        //     Ctx__emit_(ctx(), OP_WITH_ENTER, BC_NOARG, prev().line);
+        //     // [ <expr> <expr>.__enter__() ]
+        //     if(as_name) {
+        //         bool ok = as_name->emit_store(ctx());
+        //         delete_expr(as_name);
+        //         if(!ok) return SyntaxError();
+        //     } else {
+        //         Ctx__emit_(ctx(), OP_POP_TOP, BC_NOARG, BC_KEEPLINE);
+        //     }
+        //     check(compile_block_body());
+        //     Ctx__emit_(ctx(), OP_WITH_EXIT, BC_NOARG, prev().line);
+        //     Ctx__exit_block(ctx());
+        // } break;
+        /*************************************************/
+        case TK_EQ: {
+            consume(TK_ID);
+            if(mode() != EXEC_MODE) return SyntaxError("'label' is only available in EXEC_MODE");
+            c11_sv name = Token__sv(prev());
+            bool ok = Ctx__add_label(ctx(), py_name2(name));
+            if(!ok) return SyntaxError("label %q already exists", name);
+            consume(TK_EQ);
+            consume_end_stmt();
+        } break;
+        case TK_ARROW:
+            consume(TK_ID);
+            if(mode() != EXEC_MODE) return SyntaxError("'goto' is only available in EXEC_MODE");
+            py_Name name = py_name2(Token__sv(prev()));
+            Ctx__emit_(ctx(), OP_GOTO, name, prev()->line);
+            consume_end_stmt();
+            break;
+        /*************************************************/
+        // handle dangling expression or assignment
+        default: {
+            // do revert since we have pre-called advance() at the beginning
+            --self->i;
+
+            check(EXPR_TUPLE(self));
+
+            bool is_typed_name = false;  // e.g. x: int
+            // eat variable's type hint if it is a single name
+            if(Ctx__s_top(ctx())->vt->is_name) {
+                if(match(TK_COLON)) {
+                    check(consume_type_hints(self));
+                    is_typed_name = true;
+
+                    if(ctx()->is_compiling_class) {
+                        NameExpr* ne = (NameExpr*)Ctx__s_top(ctx());
+                        Ctx__emit_(ctx(), OP_ADD_CLASS_ANNOTATION, ne->name, BC_KEEPLINE);
+                    }
+                }
+            }
+            bool is_assign = false;
+            check(try_compile_assignment(self, &is_assign));
+            if(!is_assign) {
+                if(Ctx__s_size(ctx()) > 0 && Ctx__s_top(ctx())->vt->is_starred) {
+                    return SyntaxError();
+                }
+                if(!is_typed_name) {
+                    Ctx__s_emit_top(ctx());
+                    if((mode() == CELL_MODE || mode() == REPL_MODE) &&
+                       name_scope(self) == NAME_GLOBAL) {
+                        Ctx__emit_(ctx(), OP_PRINT_EXPR, BC_NOARG, BC_KEEPLINE);
+                    } else {
+                        Ctx__emit_(ctx(), OP_POP_TOP, BC_NOARG, BC_KEEPLINE);
+                    }
+                } else {
+                    Ctx__s_pop(ctx());
+                }
+            }
+            consume_end_stmt();
+            break;
+        }
+    }
+    return NULL;
+}
+
 /////////////////////////////////////////////////////////////////
 
 Error* Compiler__compile(Compiler* self, CodeObject* out) {
@@ -2026,25 +2275,22 @@ Error* Compiler__compile(Compiler* self, CodeObject* out) {
         Ctx__emit_(ctx(), OP_RETURN_VALUE, BC_NOARG, BC_KEEPLINE);
         check(pop_context(self));
         return NULL;
+    } else if(mode() == JSON_MODE) {
+        check(EXPR(self));
+        Expr* e = Ctx__s_popx(ctx());
+        if(!e->vt->is_json_object) { return SyntaxError("expect a JSON object, literal or array"); }
+        consume(TK_EOF);
+        vtemit_(e, ctx());
+        Ctx__emit_(ctx(), OP_RETURN_VALUE, BC_NOARG, BC_KEEPLINE);
+        check(pop_context(self));
+        return NULL;
     }
-    // } else if(mode() == JSON_MODE) {
-    //     check(EXPR(self));
-    //     Expr* e = Ctx__s_popx(ctx());
-    //     if(!e->is_json_object()){
-    //         return SyntaxError("expect a JSON object, literal or array");
-    //     }
-    //     consume(TK_EOF);
-    //     e->emit_(ctx());
-    //     ctx()->emit_(OP_RETURN_VALUE, BC_NOARG, BC_KEEPLINE);
-    //     check(pop_context());
-    //     return NULL;
-    // }
 
-    // while(!match(TK_EOF)) {
-    //     check(compile_stmt());
-    //     match_newlines();
-    // }
-    // check(pop_context());
+    while(!match(TK_EOF)) {
+        check(compile_stmt(self));
+        match_newlines();
+    }
+    check(pop_context(self));
     return NULL;
 }
 
@@ -2069,6 +2315,8 @@ Error* pk_compile(pk_SourceData_ src, CodeObject* out) {
     if(err) {
         // if error occurs, dispose the code object
         CodeObject__dtor(out);
+    } else {
+        assert(out->codes.count);
     }
     Compiler__dtor(&compiler);
     return err;

+ 27 - 2
src/interpreter/ceval.c

@@ -605,9 +605,9 @@ pk_FrameResult pk_VM__run_top_frame(pk_VM* self) {
                 // }
                 /*****************************************/
             case OP_RETURN_VALUE: {
-                if(byte.arg == BC_NOARG){
+                if(byte.arg == BC_NOARG) {
                     self->last_retval = POPX();
-                }else{
+                } else {
                     py_newnone(&self->last_retval);
                 }
                 pk_VM__pop_frame(self);
@@ -620,6 +620,31 @@ pk_FrameResult pk_VM__run_top_frame(pk_VM* self) {
                 }
                 DISPATCH();
             }
+
+                /////////
+            case OP_UNARY_NEGATIVE: {
+                if(!py_callmagic(__neg__, 1, TOP())) goto __ERROR;
+                *TOP() = self->last_retval;
+                DISPATCH();
+            }
+            case OP_UNARY_NOT: {
+                int res = py_bool(TOP());
+                if(res < 0) goto __ERROR;
+                py_newbool(TOP(), !res);
+                DISPATCH();
+            }
+                // case OP_UNARY_STAR: TOP() = VAR(StarWrapper(byte.arg, TOP())); DISPATCH();
+                // case OP_UNARY_INVERT: {
+                //     PyVar _0;
+                //     auto _ti = _tp_info(TOP());
+                //     if(_ti->m__invert__)
+                //         _0 = _ti->m__invert__(this, TOP());
+                //     else
+                //         _0 = call_method(TOP(), __invert__);
+                //     TOP() = _0;
+                //     DISPATCH();
+                // }
+
             default: PK_UNREACHABLE();
         }
 

+ 5 - 3
src/public/vm.c

@@ -89,10 +89,12 @@ static void disassemble(CodeObject* co) {
                 case OP_LOAD_CONST:
                 case OP_FORMAT_STRING:
                 case OP_IMPORT_PATH: {
-                    c11_string* ud = py_touserdata(c11__at(py_TValue, &co->consts, byte.arg));
+                    py_Ref tmp = c11__at(py_TValue, &co->consts, byte.arg);
                     c11_sbuf__write_cstr(&ss, " (");
-                    c11_sbuf__write_cstr(&ss, ud->data);
-                    c11_sbuf__write_char(&ss, ')');
+                    // here we need to use py_repr, however this function is not ready yet
+                    c11_sbuf__write_cstr(&ss, "<class '");
+                    c11_sbuf__write_cstr(&ss, py_tpname(tmp->type));
+                    c11_sbuf__write_cstr(&ss, "'>)");
                     break;
                 }
                 case OP_LOAD_NAME: