From 4db727afea0082780fca558ff251afb4a8b32ad7 Mon Sep 17 00:00:00 2001
From: Paul Sokolovsky <pfalcon@users.sourceforge.net>
Date: Mon, 31 Mar 2014 21:18:28 +0300
Subject: [PATCH] objstr: Very basic implementation of % string formatting
 operator.

---
 py/objstr.c                          | 55 ++++++++++++++++++++++++++++
 tests/basics/string-format-modulo.py | 22 +++++++++++
 2 files changed, 77 insertions(+)
 create mode 100644 tests/basics/string-format-modulo.py

diff --git a/py/objstr.c b/py/objstr.c
index 2ca2248b2..936542b0e 100644
--- a/py/objstr.c
+++ b/py/objstr.c
@@ -17,6 +17,7 @@ typedef struct _mp_obj_str_t {
     const byte *data;
 } mp_obj_str_t;
 
+STATIC mp_obj_t str_modulo_format(mp_obj_t pattern, uint n_args, const mp_obj_t *args);
 const mp_obj_t mp_const_empty_bytes;
 
 // use this macro to extract the string hash
@@ -283,6 +284,19 @@ STATIC mp_obj_t str_binary_op(int op, mp_obj_t lhs_in, mp_obj_t rhs_in) {
             return mp_obj_str_builder_end(s);
         }
 
+        case MP_BINARY_OP_MODULO: {
+            mp_obj_t *args;
+            uint n_args;
+            if (MP_OBJ_IS_TYPE(rhs_in, &mp_type_tuple)) {
+                // TODO: Support tuple subclasses?
+                mp_obj_tuple_get(rhs_in, &n_args, &args);
+            } else {
+                args = &rhs_in;
+                n_args = 1;
+            }
+            return str_modulo_format(lhs_in, n_args, args);
+        }
+
         // These 2 are never passed here, dealt with as a special case in mp_binary_op().
         //case MP_BINARY_OP_EQUAL:
         //case MP_BINARY_OP_NOT_EQUAL:
@@ -508,6 +522,47 @@ mp_obj_t str_format(uint n_args, const mp_obj_t *args) {
     return s;
 }
 
+STATIC mp_obj_t str_modulo_format(mp_obj_t pattern, uint n_args, const mp_obj_t *args) {
+    assert(MP_OBJ_IS_STR(pattern));
+
+    GET_STR_DATA_LEN(pattern, str, len);
+    int arg_i = 0;
+    vstr_t *vstr = vstr_new();
+    for (const byte *top = str + len; str < top; str++) {
+        if (*str == '%') {
+            if (++str >= top) {
+                break;
+            }
+            if (*str == '%') {
+                vstr_add_char(vstr, '%');
+            } else {
+                if (arg_i >= n_args) {
+                    nlr_jump(mp_obj_new_exception_msg(&mp_type_TypeError, "not enough arguments for format string"));
+                }
+                switch (*str) {
+                    case 's':
+                        mp_obj_print_helper((void (*)(void*, const char*, ...))vstr_printf, vstr, args[arg_i], PRINT_STR);
+                        break;
+                    case 'r':
+                        mp_obj_print_helper((void (*)(void*, const char*, ...))vstr_printf, vstr, args[arg_i], PRINT_REPR);
+                        break;
+                }
+                arg_i++;
+            }
+        } else {
+            vstr_add_char(vstr, *str);
+        }
+    }
+
+    if (arg_i != n_args) {
+        nlr_jump(mp_obj_new_exception_msg(&mp_type_TypeError, "not all arguments converted during string formatting"));
+    }
+
+    mp_obj_t s = mp_obj_new_str((byte*)vstr->buf, vstr->len, false);
+    vstr_free(vstr);
+    return s;
+}
+
 STATIC mp_obj_t str_replace(uint n_args, const mp_obj_t *args) {
     assert(MP_OBJ_IS_STR(args[0]));
     assert(MP_OBJ_IS_STR(args[1]));
diff --git a/tests/basics/string-format-modulo.py b/tests/basics/string-format-modulo.py
new file mode 100644
index 000000000..0b50c2674
--- /dev/null
+++ b/tests/basics/string-format-modulo.py
@@ -0,0 +1,22 @@
+print("=%s=" % 1)
+print("=%s=%s=" % (1, 2))
+print("=%s=" % (1,))
+print("=%s=" % [1, 2])
+
+print("=%s=" % "str")
+print("=%r=" % "str")
+
+try:
+    print("=%s=%s=" % 1)
+except TypeError:
+    print("TypeError")
+
+try:
+    print("=%s=%s=%s=" % (1, 2))
+except TypeError:
+    print("TypeError")
+
+try:
+    print("=%s=" % (1, 2))
+except TypeError:
+    print("TypeError")
-- 
GitLab