Browse Source

Merge branch 'main' of https://github.com/blueloveTH/pocketpy

blueloveTH 2 years ago
parent
commit
3e65101a0d

+ 35 - 0
.github/workflows/website.yml

@@ -0,0 +1,35 @@
+name: website
+
+on:
+  push:
+    branches: [ main ]
+
+jobs:
+  deploy:
+    runs-on: ubuntu-latest
+    steps:
+    - uses: actions/checkout@v3
+    ###################################################
+    - uses: actions/setup-node@v3.1.1
+    - name: Retype build
+      run: |
+        cd docs
+        npm install retypeapp --global
+        retype build
+    ###################################################
+    - name: Setup emsdk
+      uses: mymindstorm/setup-emsdk@v12
+      with:
+        version: 3.1.25
+        actions-cache-folder: 'emsdk-cache'
+    - name: Compile
+      run: |
+        bash build_web.sh
+        mv web docs/.retype/static
+    ###################################################
+    - uses: crazy-max/ghaction-github-pages@v3
+      with:
+        target_branch: gh-pages
+        build_dir: docs/.retype
+      env:
+        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

+ 7 - 5
CMakeLists.txt

@@ -20,11 +20,13 @@ endif()
 if(MSVC)
     set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /GR- /EHsc /utf-8 /O2")
 else()
-    find_program(CLANGPP clang++)
-    if(CLANGPP)
-        message(STATUS "Using clang with libc++")
-        set(CMAKE_CXX_COMPILER ${CLANGPP})
-        set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++")
+    if (NOT EMSCRIPTEN)
+        find_program(CLANGPP clang++)
+        if(CLANGPP)
+            message(STATUS "Using clang with libc++")
+            set(CMAKE_CXX_COMPILER ${CLANGPP})
+            set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++")
+        endif()
     endif()
 
     set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti -fexceptions -O2")

+ 7 - 0
build_web.sh

@@ -0,0 +1,7 @@
+python3 prebuild.py
+
+rm -rf web/lib
+mkdir web/lib
+
+SRC=$(find src/ -name "*.cpp")
+em++ $SRC -Iinclude/ -fno-rtti -fexceptions -O3 -sEXPORTED_FUNCTIONS=_pkpy_new_repl,_pkpy_repl_input,_pkpy_new_vm -sEXPORTED_RUNTIME_METHODS=ccall -o web/lib/pocketpy.js

+ 1 - 1
include/pocketpy/common.h

@@ -20,7 +20,7 @@
 #include <variant>
 #include <type_traits>
 
-#define PK_VERSION				"1.0.8"
+#define PK_VERSION				"1.0.9"
 
 #include "config.h"
 

+ 1 - 4
include/pocketpy/frame.h

@@ -22,10 +22,7 @@ struct FastLocals{
     FastLocals(const CodeObject* co, PyObject** a): varnames_inv(&co->varnames_inv), a(a) {}
     FastLocals(const FastLocals& other): varnames_inv(other.varnames_inv), a(other.a) {}
 
-    PyObject* try_get(StrName name);
-    bool contains(StrName name);
-    void erase(StrName name);
-    bool try_set(StrName name, PyObject* value);
+    PyObject** try_get_name(StrName name);
     NameDict_ to_namedict();
 };
 

+ 1 - 0
include/pocketpy/vm.h

@@ -351,6 +351,7 @@ public:
     void IndexError(const Str& msg){ _error("IndexError", msg); }
     void ValueError(const Str& msg){ _error("ValueError", msg); }
     void NameError(StrName name){ _error("NameError", fmt("name ", name.escape() + " is not defined")); }
+    void UnboundLocalError(StrName name){ _error("UnboundLocalError", fmt("local variable ", name.escape() + " referenced before assignment")); }
     void KeyError(PyObject* obj){ _error("KeyError", PK_OBJ_GET(Str, py_repr(obj))); }
     void BinaryOptError(const char* op) { TypeError(fmt("unsupported operand type(s) for ", op)); }
 

+ 16 - 10
src/ceval.cpp

@@ -111,11 +111,15 @@ __NEXT_STEP:;
         if(_0 == PY_NULL) vm->NameError(co->varnames[byte.arg]);
         PUSH(_0);
     } DISPATCH();
-    TARGET(LOAD_NAME)
+    TARGET(LOAD_NAME) {
         heap._auto_collect();
         _name = StrName(byte.arg);
-        _0 = frame->_locals.try_get(_name);
-        if(_0 != nullptr) { PUSH(_0); DISPATCH(); }
+        PyObject** slot = frame->_locals.try_get_name(_name);
+        if(slot != nullptr) {
+            if(*slot == PY_NULL) vm->UnboundLocalError(_name);
+            PUSH(*slot);
+            DISPATCH();
+        }
         _0 = frame->f_closure_try_get(_name);
         if(_0 != nullptr) { PUSH(_0); DISPATCH(); }
         _0 = frame->f_globals().try_get(_name);
@@ -123,7 +127,7 @@ __NEXT_STEP:;
         _0 = vm->builtins->attr().try_get(_name);
         if(_0 != nullptr) { PUSH(_0); DISPATCH(); }
         vm->NameError(_name);
-        DISPATCH();
+    } DISPATCH();
     TARGET(LOAD_NONLOCAL) {
         heap._auto_collect();
         _name = StrName(byte.arg);
@@ -164,16 +168,17 @@ __NEXT_STEP:;
     TARGET(STORE_FAST)
         frame->_locals[byte.arg] = POPX();
         DISPATCH();
-    TARGET(STORE_NAME)
+    TARGET(STORE_NAME){
         _name = StrName(byte.arg);
         _0 = POPX();
         if(frame->_callable != nullptr){
-            bool ok = frame->_locals.try_set(_name, _0);
-            if(!ok) vm->NameError(_name);
+            PyObject** slot = frame->_locals.try_get_name(_name);
+            if(slot == nullptr) vm->UnboundLocalError(_name);
+            *slot = _0;
         }else{
             frame->f_globals().set(_name, _0);
         }
-        DISPATCH();
+    } DISPATCH();
     TARGET(STORE_GLOBAL)
         frame->f_globals().set(StrName(byte.arg), POPX());
         DISPATCH();
@@ -202,8 +207,9 @@ __NEXT_STEP:;
     TARGET(DELETE_NAME)
         _name = StrName(byte.arg);
         if(frame->_callable != nullptr){
-            if(!frame->_locals.contains(_name)) vm->NameError(_name);
-            frame->_locals.erase(_name);
+            PyObject** slot = frame->_locals.try_get_name(_name);
+            if(slot == nullptr) vm->UnboundLocalError(_name);
+            *slot = PY_NULL;
         }else{
             if(!frame->f_globals().contains(_name)) vm->NameError(_name);
             frame->f_globals().erase(_name);

+ 2 - 20
src/frame.cpp

@@ -1,28 +1,10 @@
 #include "pocketpy/frame.h"
 
 namespace pkpy{
-
-    PyObject* FastLocals::try_get(StrName name){
+    PyObject** FastLocals::try_get_name(StrName name){
         int index = varnames_inv->try_get(name);
         if(index == -1) return nullptr;
-        return a[index];
-    }
-
-    bool FastLocals::contains(StrName name){
-        return varnames_inv->contains(name);
-    }
-
-    void FastLocals::erase(StrName name){
-        int index = varnames_inv->try_get(name);
-        if(index == -1) FATAL_ERROR();
-        a[index] = nullptr;
-    }
-
-    bool FastLocals::try_set(StrName name, PyObject* value){
-        int index = varnames_inv->try_get(name);
-        if(index == -1) return false;
-        a[index] = value;
-        return true;
+        return &a[index];
     }
 
     NameDict_ FastLocals::to_namedict(){

+ 7 - 3
src/vm.cpp

@@ -398,7 +398,11 @@ PyObject* VM::format(Str spec, PyObject* obj){
     int width, precision;
     try{
         if(dot >= 0){
-            width = Number::stoi(spec.substr(0, dot).str());
+            if(dot == 0){
+                width = -1;
+            }else{
+                width = Number::stoi(spec.substr(0, dot).str());
+            }
             precision = Number::stoi(spec.substr(dot+1).str());
         }else{
             width = Number::stoi(spec.str());
@@ -424,7 +428,7 @@ PyObject* VM::format(Str spec, PyObject* obj){
     }else{
         ret = CAST(Str&, py_str(obj));
     }
-    if(width > ret.length()){
+    if(width != -1 && width > ret.length()){
         int pad = width - ret.length();
         std::string padding(pad, pad_c);
         if(align == '>') ret = padding.c_str() + ret;
@@ -446,7 +450,7 @@ PyObject* VM::new_module(StrName name) {
 static std::string _opcode_argstr(VM* vm, Bytecode byte, const CodeObject* co){
     std::string argStr = byte.arg == -1 ? "" : std::to_string(byte.arg);
     switch(byte.op){
-        case OP_LOAD_CONST:
+        case OP_LOAD_CONST: case OP_FORMAT_STRING:
             if(vm != nullptr){
                 argStr += fmt(" (", CAST(Str, vm->py_repr(co->consts[byte.arg])), ")");
             }

+ 2 - 0
tests/25_rawstring.py

@@ -42,6 +42,8 @@ assert f'{a:010}' == '0000000010'
 assert f'{a:010d}' == '0000000010'
 assert f'{a:010f}' == '010.000000'
 assert f'{a:010.2f}' == '0000010.00'
+assert f'{a:.2f}' == '10.00'
+assert f'{a:.5f}' == '10.00000'
 
 b = '123'
 assert f'{b:10}' == '123       '

+ 16 - 1
tests/99_bugs.py

@@ -56,4 +56,19 @@ def f():
 f()
 
 # class A: a=b=1
-# class A: a, b = 1, 2
+# class A: a, b = 1, 2
+
+bmi = 0.0
+
+def test(a):
+    if a:
+        bmi = 1.4
+    return f'{bmi:.2f}'
+
+assert test(1) == '1.40'
+
+try:
+    assert test(0) == '0.00'
+    exit(1)
+except UnboundLocalError:
+    pass