1
0
blueloveTH 1 сар өмнө
parent
commit
48b70c944e

+ 1 - 1
docs/retype.yml

@@ -3,7 +3,7 @@ output: .retype
 url: https://pocketpy.dev
 branding:
   title: pocketpy
-  label: v2.1.7
+  label: v2.1.8
   logo: "./static/logo.png"
 favicon: "./static/logo.png"
 meta:

+ 2 - 2
include/pocketpy/config.h

@@ -1,10 +1,10 @@
 #pragma once
 // clang-format off
 
-#define PK_VERSION				"2.1.7"
+#define PK_VERSION				"2.1.8"
 #define PK_VERSION_MAJOR            2
 #define PK_VERSION_MINOR            1
-#define PK_VERSION_PATCH            7
+#define PK_VERSION_PATCH            8
 
 /*************** feature settings ***************/
 #ifndef PK_ENABLE_OS                // can be overridden by cmake

+ 1 - 1
plugins/flutter/pocketpy/ios/pocketpy.podspec

@@ -35,7 +35,7 @@ A new Flutter FFI plugin project.
 
   s.prepare_command = <<-CMD
   rm -rf pocketpy
-  git clone --branch v2.1.7 --depth 1 https://github.com/pocketpy/pocketpy.git
+  git clone --branch v2.1.8 --depth 1 https://github.com/pocketpy/pocketpy.git
   cd pocketpy
   git submodule update --init --recursive --depth 1
   bash build_ios_libs.sh

+ 1 - 1
plugins/flutter/pocketpy/macos/pocketpy.podspec

@@ -32,7 +32,7 @@ A new Flutter FFI plugin project.
 
   s.prepare_command = <<-CMD
   rm -rf pocketpy
-  git clone --branch v2.1.7 --depth 1 https://github.com/pocketpy/pocketpy.git
+  git clone --branch v2.1.8 --depth 1 https://github.com/pocketpy/pocketpy.git
   cd pocketpy
   git submodule update --init --recursive --depth 1
   bash build_darwin_libs.sh

+ 1 - 1
plugins/flutter/pocketpy/pubspec.yaml

@@ -1,6 +1,6 @@
 name: pocketpy
 description: A lightweight Python interpreter for game engines. It supports Android/iOS/Windows/Linux/MacOS.
-version: 2.1.7
+version: 2.1.8
 homepage: https://pocketpy.dev
 repository: https://github.com/pocketpy/pocketpy
 

+ 1 - 1
plugins/flutter/pocketpy/src/CMakeLists.txt

@@ -21,7 +21,7 @@ set(PK_BUILD_SHARED_LIB ON CACHE BOOL "" FORCE)
 FetchContent_Declare(
   pocketpy
   GIT_REPOSITORY https://github.com/pocketpy/pocketpy.git
-  GIT_TAG        v2.1.7
+  GIT_TAG        v2.1.8
 )
 
 FetchContent_MakeAvailable(pocketpy)

+ 111 - 0
src/bindings/py_str.c

@@ -41,6 +41,116 @@ static bool str__len__(int argc, py_Ref argv) {
     return true;
 }
 
+static bool str__mod__(int argc, py_Ref argv) {
+    PY_CHECK_ARGC(2);
+    c11_string* self = pk_tostr(&argv[0]);
+    // %s
+    // %d %i
+    // %f
+    // %r
+    // %%
+    py_TValue* args;
+    int args_count;
+    if(py_istype(&argv[1], tp_tuple)) {
+        args_count = py_tuple_len(&argv[1]);
+        args = py_tuple_data(&argv[1]);
+    } else {
+        args_count = 1;
+        args = &argv[1];
+    }
+
+    int arg_index = 0;
+    const char* p = self->data;
+    const char* p_end = self->data + self->size;
+
+    c11_sbuf buf;
+    c11_sbuf__ctor(&buf);
+
+    while(p < p_end) {
+        if(*p == '%') {
+            p++;
+            if(p >= p_end) {
+                c11_sbuf__dtor(&buf);
+                return ValueError("incomplete format");
+            }
+            char spec = *p;
+            p++;
+            if(spec == '%') {
+                // %% -> %
+                c11_sbuf__write_char(&buf, '%');
+            } else if(spec == 's') {
+                // %s -> string
+                if(arg_index >= args_count) {
+                    c11_sbuf__dtor(&buf);
+                    return TypeError("not enough arguments for format string");
+                }
+                if(!py_str(&args[arg_index])) {
+                    c11_sbuf__dtor(&buf);
+                    return false;
+                }
+                c11_sbuf__write_sv(&buf, py_tosv(py_retval()));
+                arg_index++;
+            } else if(spec == 'd' || spec == 'i') {
+                // %d or %i -> integer
+                if(arg_index >= args_count) {
+                    c11_sbuf__dtor(&buf);
+                    return TypeError("not enough arguments for format string");
+                }
+                if(!py_checktype(&args[arg_index], tp_int)) {
+                    c11_sbuf__dtor(&buf);
+                    return false;
+                }
+                py_i64 val = py_toint(&args[arg_index]);
+                c11_sbuf__write_i64(&buf, val);
+                arg_index++;
+            } else if(spec == 'f') {
+                // %f -> float
+                if(arg_index >= args_count) {
+                    c11_sbuf__dtor(&buf);
+                    return TypeError("not enough arguments for format string");
+                }
+                py_f64 val;
+                if(py_istype(&args[arg_index], tp_float)) {
+                    val = py_tofloat(&args[arg_index]);
+                } else if(py_istype(&args[arg_index], tp_int)) {
+                    val = (py_f64)py_toint(&args[arg_index]);
+                } else {
+                    c11_sbuf__dtor(&buf);
+                    return TypeError("a float is required");
+                }
+                c11_sbuf__write_f64(&buf, val, 6);
+                arg_index++;
+            } else if(spec == 'r') {
+                // %r -> repr
+                if(arg_index >= args_count) {
+                    c11_sbuf__dtor(&buf);
+                    return TypeError("not enough arguments for format string");
+                }
+                if(!py_repr(&args[arg_index])) {
+                    c11_sbuf__dtor(&buf);
+                    return false;
+                }
+                c11_sbuf__write_sv(&buf, py_tosv(py_retval()));
+                arg_index++;
+            } else {
+                c11_sbuf__dtor(&buf);
+                return ValueError("unsupported format character '%c'", spec);
+            }
+        } else {
+            c11_sbuf__write_char(&buf, *p);
+            p++;
+        }
+    }
+
+    if(arg_index != args_count) {
+        c11_sbuf__dtor(&buf);
+        return TypeError("not all arguments converted during string formatting");
+    }
+
+    c11_sbuf__py_submit(&buf, py_retval());
+    return true;
+}
+
 static bool str__add__(int argc, py_Ref argv) {
     PY_CHECK_ARGC(2);
     c11_string* self = pk_tostr(&argv[0]);
@@ -503,6 +613,7 @@ py_Type pk_str__register() {
     py_bindmagic(tp_str, __new__, str__new__);
     py_bindmagic(tp_str, __hash__, str__hash__);
     py_bindmagic(tp_str, __len__, str__len__);
+    py_bindmagic(tp_str, __mod__, str__mod__);
     py_bindmagic(tp_str, __add__, str__add__);
     py_bindmagic(tp_str, __mul__, str__mul__);
     py_bindmagic(tp_str, __rmul__, str__rmul__);

+ 51 - 0
tests/042_str_mod.py

@@ -0,0 +1,51 @@
+# Test str.__mod__ (old-style % formatting)
+
+# Test %s - string formatting
+assert "hello %s" % "world" == "hello world"
+assert "%s" % 123 == "123"
+assert "%s %s" % ("a", "b") == "a b"
+assert "name: %s, age: %s" % ("Alice", 30) == "name: Alice, age: 30"
+
+# Test %d - integer formatting
+assert "%d" % 42 == "42"
+assert "%d" % -123 == "-123"
+assert "%d" % 0 == "0"
+assert "count: %d" % 100 == "count: 100"
+
+# Test %i - integer formatting (same as %d)
+assert "%i" % 42 == "42"
+assert "%i" % -123 == "-123"
+assert "value: %i" % 999 == "value: 999"
+
+# Test %f - float formatting
+assert "%f" % 3.14 == "3.140000"
+assert "%f" % -2.5 == "-2.500000"
+assert "%f" % 0.0 == "0.000000"
+assert "%f" % 42 == "42.000000"  # int to float
+
+# Test %r - repr formatting
+assert "%r" % "hello" == "'hello'"
+assert "%r" % 123 == "123"
+assert "%r" % [1, 2, 3] == "[1, 2, 3]"
+
+# Test %% - literal percent
+assert "%%" % () == "%"
+assert "100%%" % () == "100%"
+assert "%%s" % () == "%s"
+assert "%d%%" % 50 == "50%"
+
+# Test combined format specifiers
+assert "%s is %d years old" % ("Bob", 25) == "Bob is 25 years old"
+assert "%s: %f" % ("pi", 3.14159) == "pi: 3.141590"
+assert "%d + %d = %d" % (1, 2, 3) == "1 + 2 = 3"
+assert "Hello %s! You have %d messages." % ("User", 5) == "Hello User! You have 5 messages."
+
+# Test single value (not tuple)
+assert "value: %s" % "test" == "value: test"
+assert "number: %d" % 42 == "number: 42"
+
+# Test empty string
+assert "" % () == ""
+
+# Test no format specifiers
+assert "hello world" % () == "hello world"