diff --git a/py/builtin.c b/py/builtin.c index e723fad334..725613385a 100644 --- a/py/builtin.c +++ b/py/builtin.c @@ -36,6 +36,8 @@ #include "runtime0.h" #include "runtime.h" #include "builtin.h" +#include "stream.h" +#include "pfenv.h" #if MICROPY_PY_BUILTINS_FLOAT #include @@ -424,13 +426,36 @@ STATIC mp_obj_t mp_builtin_print(uint n_args, const mp_obj_t *args, mp_map_t *kw if (end_elem != NULL && end_elem->value != mp_const_none) { end_data = mp_obj_str_get_data(end_elem->value, &end_len); } + #if MICROPY_PY_IO + mp_obj_t stream_obj = &mp_sys_stdout_obj; + mp_map_elem_t *file_elem = mp_map_lookup(kwargs, MP_OBJ_NEW_QSTR(MP_QSTR_file), MP_MAP_LOOKUP); + if (file_elem != NULL && file_elem->value != mp_const_none) { + stream_obj = file_elem->value; + } + + pfenv_t pfenv; + pfenv.data = stream_obj; + pfenv.print_strn = (void (*)(void *, const char *, unsigned int))mp_stream_write; + #endif for (int i = 0; i < n_args; i++) { if (i > 0) { + #if MICROPY_PY_IO + mp_stream_write(stream_obj, sep_data, sep_len); + #else printf("%.*s", sep_len, sep_data); + #endif } + #if MICROPY_PY_IO + mp_obj_print_helper((void (*)(void *env, const char *fmt, ...))pfenv_printf, &pfenv, args[i], PRINT_STR); + #else mp_obj_print(args[i], PRINT_STR); + #endif } + #if MICROPY_PY_IO + mp_stream_write(stream_obj, end_data, end_len); + #else printf("%.*s", end_len, end_data); + #endif return mp_const_none; } diff --git a/py/builtin.h b/py/builtin.h index 361cef604e..425cfec097 100644 --- a/py/builtin.h +++ b/py/builtin.h @@ -81,5 +81,10 @@ extern const mp_obj_module_t mp_module_struct; extern const mp_obj_module_t mp_module_sys; extern const mp_obj_module_t mp_module_gc; +struct _dummy_t; +extern struct _dummy_t mp_sys_stdin_obj; +extern struct _dummy_t mp_sys_stdout_obj; +extern struct _dummy_t mp_sys_stderr_obj; + // extmod modules extern const mp_obj_module_t mp_module_uctypes; diff --git a/py/modsys.c b/py/modsys.c index d215846762..c248e5626f 100644 --- a/py/modsys.c +++ b/py/modsys.c @@ -44,9 +44,6 @@ // only addresses. struct _dummy_t; extern struct _dummy_t mp_sys_exit_obj; -extern struct _dummy_t mp_sys_stdin_obj; -extern struct _dummy_t mp_sys_stdout_obj; -extern struct _dummy_t mp_sys_stderr_obj; extern mp_obj_int_t mp_maxsize_obj; diff --git a/py/pfenv.h b/py/pfenv.h index 22b1fc8492..781738f4db 100644 --- a/py/pfenv.h +++ b/py/pfenv.h @@ -49,3 +49,6 @@ int pfenv_print_mp_int(const pfenv_t *pfenv, mp_obj_t x, int sgn, int base, int #if MICROPY_PY_BUILTINS_FLOAT int pfenv_print_float(const pfenv_t *pfenv, mp_float_t f, char fmt, int flags, char fill, int width, int prec); #endif + +//int pfenv_vprintf(const pfenv_t *pfenv, const char *fmt, va_list args); +int pfenv_printf(const pfenv_t *pfenv, const char *fmt, ...); diff --git a/py/pfenv_printf.c b/py/pfenv_printf.c new file mode 100644 index 0000000000..003314e8f5 --- /dev/null +++ b/py/pfenv_printf.c @@ -0,0 +1,199 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#include +#include +#include +#include + +#include "mpconfig.h" +#include "misc.h" +#include "qstr.h" +#include "obj.h" +#include "pfenv.h" + +#if MICROPY_PY_BUILTINS_FLOAT +#include "formatfloat.h" +#endif + +int pfenv_vprintf(const pfenv_t *pfenv, const char *fmt, va_list args) { + int chrs = 0; + for (;;) { + { + const char *f = fmt; + while (*f != '\0' && *f != '%') { + ++f; // XXX UTF8 advance char + } + if (f > fmt) { + pfenv->print_strn(pfenv->data, fmt, f - fmt); + chrs += f - fmt; + fmt = f; + } + } + + if (*fmt == '\0') { + break; + } + + // move past % character + ++fmt; + + // parse flags, if they exist + int flags = 0; + char fill = ' '; + while (*fmt != '\0') { + if (*fmt == '-') flags |= PF_FLAG_LEFT_ADJUST; + else if (*fmt == '+') flags |= PF_FLAG_SHOW_SIGN; + else if (*fmt == ' ') flags |= PF_FLAG_SPACE_SIGN; + else if (*fmt == '!') flags |= PF_FLAG_NO_TRAILZ; + else if (*fmt == '0') { + flags |= PF_FLAG_PAD_AFTER_SIGN; + fill = '0'; + } else break; + ++fmt; + } + + // parse width, if it exists + int width = 0; + for (; '0' <= *fmt && *fmt <= '9'; ++fmt) { + width = width * 10 + *fmt - '0'; + } + + // parse precision, if it exists + int prec = -1; + if (*fmt == '.') { + ++fmt; + if (*fmt == '*') { + ++fmt; + prec = va_arg(args, int); + } else { + prec = 0; + for (; '0' <= *fmt && *fmt <= '9'; ++fmt) { + prec = prec * 10 + *fmt - '0'; + } + } + if (prec < 0) { + prec = 0; + } + } + + // parse long specifiers (current not used) + //bool long_arg = false; + if (*fmt == 'l') { + ++fmt; + //long_arg = true; + } + + if (*fmt == '\0') { + break; + } + + switch (*fmt) { + case 'b': + if (va_arg(args, int)) { + chrs += pfenv_print_strn(pfenv, "true", 4, flags, fill, width); + } else { + chrs += pfenv_print_strn(pfenv, "false", 5, flags, fill, width); + } + break; + case 'c': + { + char str = va_arg(args, int); + chrs += pfenv_print_strn(pfenv, &str, 1, flags, fill, width); + break; + } + case 's': + { + const char *str = va_arg(args, const char*); + if (str) { + if (prec < 0) { + prec = strlen(str); + } + chrs += pfenv_print_strn(pfenv, str, prec, flags, fill, width); + } else { + chrs += pfenv_print_strn(pfenv, "(null)", 6, flags, fill, width); + } + break; + } + case 'u': + chrs += pfenv_print_int(pfenv, va_arg(args, int), 0, 10, 'a', flags, fill, width); + break; + case 'd': + chrs += pfenv_print_int(pfenv, va_arg(args, int), 1, 10, 'a', flags, fill, width); + break; + case 'x': + case 'p': // ? + chrs += pfenv_print_int(pfenv, va_arg(args, int), 0, 16, 'a', flags, fill, width); + break; + case 'X': + case 'P': // ? + chrs += pfenv_print_int(pfenv, va_arg(args, int), 0, 16, 'A', flags, fill, width); + break; +#if MICROPY_PY_BUILTINS_FLOAT + case 'e': + case 'E': + case 'f': + case 'F': + case 'g': + case 'G': + { +#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT + mp_float_t f = va_arg(args, double); + chrs += pfenv_print_float(pfenv, f, *fmt, flags, fill, width, prec); +#elif MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_DOUBLE + // Currently pfenv_print_float uses snprintf, but snprintf + // itself may be implemented in terms of pfenv_vprintf() for + // some ports. So, for extra caution, this case is handled + // with assert below. Note that currently ports which + // use MICROPY_FLOAT_IMPL_DOUBLE, don't call pfenv_vprintf() + // with float format specifier at all. + // TODO: resolve this completely + assert(0); +//#error Calling pfenv_print_float with double not supported from within printf +#else +#error Unknown MICROPY FLOAT IMPL +#endif + break; + } +#endif + default: + pfenv->print_strn(pfenv->data, fmt, 1); + chrs += 1; + break; + } + ++fmt; + } + return chrs; +} + +int pfenv_printf(const pfenv_t *pfenv, const char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + int ret = pfenv_vprintf(pfenv, fmt, ap); + va_end(ap); + return ret; +} diff --git a/py/py.mk b/py/py.mk index 1615c4bbaa..f1ae3c3f1e 100644 --- a/py/py.mk +++ b/py/py.mk @@ -102,6 +102,7 @@ PY_O_BASENAME = \ repl.o \ smallint.o \ pfenv.o \ + pfenv_printf.o \ ../extmod/moductypes.o # prepend the build destination prefix to the py object files diff --git a/py/qstrdefs.h b/py/qstrdefs.h index e3a4dc01ab..0deb646c7b 100644 --- a/py/qstrdefs.h +++ b/py/qstrdefs.h @@ -413,6 +413,7 @@ Q(TextIOWrapper) Q(StringIO) Q(BytesIO) Q(getvalue) +Q(file) #endif #if MICROPY_PY_GC diff --git a/stmhal/printf.c b/stmhal/printf.c index c4731aa88d..d998a6277d 100644 --- a/stmhal/printf.c +++ b/stmhal/printf.c @@ -45,156 +45,12 @@ #include "formatfloat.h" #endif +int pfenv_vprintf(const pfenv_t *pfenv, const char *fmt, va_list args); + void pfenv_prints(const pfenv_t *pfenv, const char *str) { pfenv->print_strn(pfenv->data, str, strlen(str)); } -int pfenv_printf(const pfenv_t *pfenv, const char *fmt, va_list args) { - int chrs = 0; - for (;;) { - { - const char *f = fmt; - while (*f != '\0' && *f != '%') { - ++f; // XXX UTF8 advance char - } - if (f > fmt) { - pfenv->print_strn(pfenv->data, fmt, f - fmt); - chrs += f - fmt; - fmt = f; - } - } - - if (*fmt == '\0') { - break; - } - - // move past % character - ++fmt; - - // parse flags, if they exist - int flags = 0; - char fill = ' '; - while (*fmt != '\0') { - if (*fmt == '-') flags |= PF_FLAG_LEFT_ADJUST; - else if (*fmt == '+') flags |= PF_FLAG_SHOW_SIGN; - else if (*fmt == ' ') flags |= PF_FLAG_SPACE_SIGN; - else if (*fmt == '!') flags |= PF_FLAG_NO_TRAILZ; - else if (*fmt == '0') { - flags |= PF_FLAG_PAD_AFTER_SIGN; - fill = '0'; - } else break; - ++fmt; - } - - // parse width, if it exists - int width = 0; - for (; '0' <= *fmt && *fmt <= '9'; ++fmt) { - width = width * 10 + *fmt - '0'; - } - - // parse precision, if it exists - int prec = -1; - if (*fmt == '.') { - ++fmt; - if (*fmt == '*') { - ++fmt; - prec = va_arg(args, int); - } else { - prec = 0; - for (; '0' <= *fmt && *fmt <= '9'; ++fmt) { - prec = prec * 10 + *fmt - '0'; - } - } - if (prec < 0) { - prec = 0; - } - } - - // parse long specifiers (current not used) - //bool long_arg = false; - if (*fmt == 'l') { - ++fmt; - //long_arg = true; - } - - if (*fmt == '\0') { - break; - } - - switch (*fmt) { - case 'b': - if (va_arg(args, int)) { - chrs += pfenv_print_strn(pfenv, "true", 4, flags, fill, width); - } else { - chrs += pfenv_print_strn(pfenv, "false", 5, flags, fill, width); - } - break; - case 'c': - { - char str = va_arg(args, int); - chrs += pfenv_print_strn(pfenv, &str, 1, flags, fill, width); - break; - } - case 's': - { - const char *str = va_arg(args, const char*); - if (str) { - if (prec < 0) { - prec = strlen(str); - } - chrs += pfenv_print_strn(pfenv, str, prec, flags, fill, width); - } else { - chrs += pfenv_print_strn(pfenv, "(null)", 6, flags, fill, width); - } - break; - } - case 'u': - chrs += pfenv_print_int(pfenv, va_arg(args, int), 0, 10, 'a', flags, fill, width); - break; - case 'd': - chrs += pfenv_print_int(pfenv, va_arg(args, int), 1, 10, 'a', flags, fill, width); - break; - case 'x': - case 'p': // ? - chrs += pfenv_print_int(pfenv, va_arg(args, int), 0, 16, 'a', flags, fill, width); - break; - case 'X': - case 'P': // ? - chrs += pfenv_print_int(pfenv, va_arg(args, int), 0, 16, 'A', flags, fill, width); - break; -#if MICROPY_PY_BUILTINS_FLOAT - case 'e': - case 'E': - case 'f': - case 'F': - case 'g': - case 'G': - { -#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT - mp_float_t f = va_arg(args, double); - chrs += pfenv_print_float(pfenv, f, *fmt, flags, fill, width, prec); -#elif MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_DOUBLE - // Currently pfenv_print_float uses snprintf, so if you want - // to use pfenv_print_float with doubles then you'll need - // fix it to not use snprintf first. Otherwise you'll have - // inifinite recursion. -#error Calling pfenv_print_float with double not supported from within printf -#else -#error Unknown MICROPY FLOAT IMPL -#endif - break; - } -#endif - default: - pfenv->print_strn(pfenv->data, fmt, 1); - chrs += 1; - break; - } - ++fmt; - } - return chrs; -} - STATIC void stdout_print_strn(void *data, const char *str, unsigned int len) { // TODO this needs to be replaced with a proper stdio interface ala CPython // send stdout to UART and USB CDC VCP @@ -211,13 +67,13 @@ static const pfenv_t pfenv_stdout = {0, stdout_print_strn}; int printf(const char *fmt, ...) { va_list ap; va_start(ap, fmt); - int ret = pfenv_printf(&pfenv_stdout, fmt, ap); + int ret = pfenv_vprintf(&pfenv_stdout, fmt, ap); va_end(ap); return ret; } int vprintf(const char *fmt, va_list ap) { - return pfenv_printf(&pfenv_stdout, fmt, ap); + return pfenv_vprintf(&pfenv_stdout, fmt, ap); } #if MICROPY_DEBUG_PRINTERS