From 605ff91efdeb3cd7e4948c0398c839a9ae06044a Mon Sep 17 00:00:00 2001
From: Paul Sokolovsky <pfalcon@users.sourceforge.net>
Date: Tue, 11 Apr 2017 00:12:20 +0300
Subject: [PATCH] extmod/machine_signal: Support all Pin's arguments to the
 constructor.

This implements the orginal idea is that Signal is a subclass of Pin, and
thus can accept all the same argument as Pin, and additionally, "inverted"
param. On the practical side, it allows to avoid many enclosed parenses for
a typical declararion, e.g. for Zephyr:

Signal(Pin(("GPIO_0", 1))).

Of course, passing a Pin to Signal constructor is still supported and is the
most generic form (e.g. Unix port will only support such form, as it doesn't
have "builtin" Pins), what's introduces here is just practical readability
optimization.

"value" kwarg is treated as applying to a Signal (i.e. accounts for possible
inversion).
---
 esp8266/machine_pin.c   |  4 +--
 esp8266/mpconfigport.h  |  1 +
 extmod/machine_signal.c | 73 ++++++++++++++++++++++++++++++++++++-----
 extmod/virtpin.h        |  3 ++
 4 files changed, 70 insertions(+), 11 deletions(-)

diff --git a/esp8266/machine_pin.c b/esp8266/machine_pin.c
index d4c6d3dea..1a263601b 100644
--- a/esp8266/machine_pin.c
+++ b/esp8266/machine_pin.c
@@ -287,7 +287,7 @@ STATIC mp_obj_t pyb_pin_obj_init_helper(pyb_pin_obj_t *self, mp_uint_t n_args, c
 }
 
 // constructor(id, ...)
-STATIC mp_obj_t pyb_pin_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
+mp_obj_t mp_pin_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
     mp_arg_check_num(n_args, n_kw, 1, MP_OBJ_FUN_ARGS_MAX, true);
 
     // get the wanted pin object
@@ -436,7 +436,7 @@ const mp_obj_type_t pyb_pin_type = {
     { &mp_type_type },
     .name = MP_QSTR_Pin,
     .print = pyb_pin_print,
-    .make_new = pyb_pin_make_new,
+    .make_new = mp_pin_make_new,
     .call = pyb_pin_call,
     .protocol = &pin_pin_p,
     .locals_dict = (mp_obj_t)&pyb_pin_locals_dict,
diff --git a/esp8266/mpconfigport.h b/esp8266/mpconfigport.h
index b25bb8bb4..d0b4a7b4b 100644
--- a/esp8266/mpconfigport.h
+++ b/esp8266/mpconfigport.h
@@ -73,6 +73,7 @@
 #define MICROPY_PY_UZLIB            (1)
 #define MICROPY_PY_LWIP             (1)
 #define MICROPY_PY_MACHINE          (1)
+#define MICROPY_PY_MACHINE_PIN_MAKE_NEW mp_pin_make_new
 #define MICROPY_PY_MACHINE_PULSE    (1)
 #define MICROPY_PY_MACHINE_I2C      (1)
 #define MICROPY_PY_MACHINE_SPI      (1)
diff --git a/extmod/machine_signal.c b/extmod/machine_signal.c
index de6c3ff32..b10d90166 100644
--- a/extmod/machine_signal.c
+++ b/extmod/machine_signal.c
@@ -27,6 +27,8 @@
 #include "py/mpconfig.h"
 #if MICROPY_PY_MACHINE
 
+#include <string.h>
+
 #include "py/obj.h"
 #include "py/runtime.h"
 #include "extmod/virtpin.h"
@@ -41,20 +43,73 @@ typedef struct _machine_signal_t {
 } machine_signal_t;
 
 STATIC mp_obj_t signal_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
-    enum { ARG_pin, ARG_inverted };
-    static const mp_arg_t allowed_args[] = {
-        { MP_QSTR_, MP_ARG_OBJ | MP_ARG_REQUIRED },
-        { MP_QSTR_inverted, MP_ARG_BOOL, {.u_bool = false} },
-    };
+    mp_obj_t pin = args[0];
+    bool inverted = false;
+
+    #if defined(MICROPY_PY_MACHINE_PIN_MAKE_NEW)
+    mp_pin_p_t *pin_p = NULL;
+
+    if (MP_OBJ_IS_OBJ(pin)) {
+        mp_obj_base_t *pin_base = (mp_obj_base_t*)MP_OBJ_TO_PTR(args[0]);
+        pin_p = (mp_pin_p_t*)pin_base->type->protocol;
+    }
+
+    if (pin_p == NULL) {
+        // If first argument isn't a Pin-like object, we filter out "inverted"
+        // from keyword arguments and pass them all to the exported Pin
+        // constructor to create one.
+        mp_obj_t pin_args[n_args + n_kw * 2];
+        memcpy(pin_args, args, n_args * sizeof(mp_obj_t));
+        const mp_obj_t *src = args + n_args;
+        mp_obj_t *dst = pin_args + n_args;
+        mp_obj_t *sig_value = NULL;
+        for (size_t cnt = n_kw; cnt; cnt--) {
+            if (*src == MP_OBJ_NEW_QSTR(MP_QSTR_inverted)) {
+                inverted = mp_obj_is_true(src[1]);
+                n_kw--;
+            } else {
+                *dst++ = *src;
+                *dst++ = src[1];
+            }
+            if (*src == MP_OBJ_NEW_QSTR(MP_QSTR_value)) {
+                // Value is pertained to Signal, so we should invert
+                // it for Pin if needed, and we should do it only when
+                // inversion status is guaranteedly known.
+                sig_value = dst - 1;
+            }
+            src += 2;
+        }
 
-    mp_arg_val_t parsed_args[MP_ARRAY_SIZE(allowed_args)];
+        if (inverted && sig_value != NULL) {
+            *sig_value = mp_obj_is_true(*sig_value) ? MP_OBJ_NEW_SMALL_INT(0) : MP_OBJ_NEW_SMALL_INT(1);
+        }
 
-    mp_arg_parse_all_kw_array(n_args, n_kw, args, MP_ARRAY_SIZE(allowed_args), allowed_args, parsed_args);
+        // Here we pass NULL as a type, hoping that mp_pin_make_new()
+        // will just ignore it as set a concrete type. If not, we'd need
+        // to expose port's "default" pin type too.
+        pin = MICROPY_PY_MACHINE_PIN_MAKE_NEW(NULL, n_args, n_kw, pin_args);
+    }
+    else
+    #endif
+    // Otherwise there should be 1 or 2 args
+    {
+        if (n_args == 1) {
+            if (n_kw == 0) {
+            } else if (n_kw == 1 && args[1] == MP_OBJ_NEW_QSTR(MP_QSTR_inverted)) {
+                inverted = mp_obj_is_true(args[1]);
+            } else {
+                goto error;
+            }
+        } else {
+        error:
+            mp_raise_TypeError(NULL);
+        }
+    }
 
     machine_signal_t *o = m_new_obj(machine_signal_t);
     o->base.type = type;
-    o->pin = parsed_args[ARG_pin].u_obj;
-    o->inverted = parsed_args[ARG_inverted].u_bool;
+    o->pin = pin;
+    o->inverted = inverted;
     return MP_OBJ_FROM_PTR(o);
 }
 
diff --git a/extmod/virtpin.h b/extmod/virtpin.h
index 3821f9dec..041010350 100644
--- a/extmod/virtpin.h
+++ b/extmod/virtpin.h
@@ -38,3 +38,6 @@ typedef struct _mp_pin_p_t {
 
 int mp_virtual_pin_read(mp_obj_t pin);
 void mp_virtual_pin_write(mp_obj_t pin, int value);
+
+// If a port exposes a Pin object, it's constructor should be like this
+mp_obj_t mp_pin_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args);
-- 
GitLab