From 8dd8af742a8d8a6d3c463d96570e3a0f7a34a0ee Mon Sep 17 00:00:00 2001 From: Calvin Rose Date: Sat, 19 Oct 2019 10:30:29 -0500 Subject: [PATCH] Add eprintf and make printf a C function. This allows some more optimizations when printing to buffers or when output is disabled. It also makes printf more consistent with print and prin (Same with eprintf). --- src/boot/boot.janet | 7 ---- src/core/io.c | 98 ++++++++++++++++++++++++++++++++++++++------- 2 files changed, 83 insertions(+), 22 deletions(-) diff --git a/src/boot/boot.janet b/src/boot/boot.janet index d1f318bd..81712f96 100644 --- a/src/boot/boot.janet +++ b/src/boot/boot.janet @@ -1182,18 +1182,11 @@ (file/close f) nil) -(defn printf - "Print formatted strings to stdout or (dyn :out), followed by - a new line." - [f & args] - (prin (buffer/format @"" f ;args))) - (defn pp "Pretty print to stdout or (dyn :out)." [x] (print (buffer/format @"" (dyn :pretty-format "%q") x))) - ### ### ### Pattern Matching diff --git a/src/core/io.c b/src/core/io.c index c35bde39..a4109b07 100644 --- a/src/core/io.c +++ b/src/core/io.c @@ -390,13 +390,13 @@ static Janet cfun_io_print_impl(int32_t argc, Janet *argv, Janet x = janet_dyn(name); switch (janet_type(x)) { default: - goto err; + /* Other values simply do nothing */ + return janet_wrap_nil(); case JANET_BUFFER: { /* Special case buffer */ JanetBuffer *buf = janet_unwrap_buffer(x); for (int32_t i = 0; i < argc; ++i) { - const uint8_t *vstr = janet_to_string(argv[i]); - janet_buffer_push_string(buf, vstr); + janet_to_string_b(buf, argv[i]); } if (newline) janet_buffer_push_u8(buf, '\n'); @@ -407,25 +407,31 @@ static Janet cfun_io_print_impl(int32_t argc, Janet *argv, break; case JANET_ABSTRACT: { void *abstract = janet_unwrap_abstract(x); - if (janet_abstract_type(abstract) != &cfun_io_filetype) goto err; + if (janet_abstract_type(abstract) != &cfun_io_filetype) + return janet_wrap_nil(); IOFile *iofile = abstract; f = iofile->file; break; } } for (int32_t i = 0; i < argc; ++i) { - int32_t j, len; - const uint8_t *vstr = janet_to_string(argv[i]); - len = janet_string_length(vstr); - for (j = 0; j < len; ++j) { - putc(vstr[j], f); + int32_t len; + const uint8_t *vstr; + if (janet_checktype(argv[i], JANET_BUFFER)) { + JanetBuffer *b = janet_unwrap_buffer(argv[i]); + vstr = b->data; + len = b->count; + } else { + vstr = janet_to_string(argv[i]); + len = janet_string_length(vstr); + } + if (1 != fwrite(vstr, len, 1, f)) { + janet_panicf("could not print %d bytes to (dyn :%s)", len, name); } } if (newline) putc('\n', f); return janet_wrap_nil(); -err: - janet_panicf("expected (dyn :%s) to be a file, buffer, or nil; got %v", name, x); } static Janet cfun_io_print(int32_t argc, Janet *argv) { @@ -444,6 +450,55 @@ static Janet cfun_io_eprin(int32_t argc, Janet *argv) { return cfun_io_print_impl(argc, argv, 0, "err", stderr); } +static Janet cfun_io_printf_impl(int32_t argc, Janet *argv, + const char *name, FILE *dflt_file) { + FILE *f; + janet_arity(argc, 1, -1); + const char *fmt = janet_getcstring(argv, 0); + Janet x = janet_dyn(name); + switch (janet_type(x)) { + default: + /* Other values simply do nothing */ + return janet_wrap_nil(); + case JANET_BUFFER: { + /* Special case buffer */ + JanetBuffer *buf = janet_unwrap_buffer(x); + janet_buffer_format(buf, fmt, 0, argc, argv); + return janet_wrap_nil(); + } + case JANET_NIL: + f = dflt_file; + break; + case JANET_ABSTRACT: { + void *abstract = janet_unwrap_abstract(x); + if (janet_abstract_type(abstract) != &cfun_io_filetype) + return janet_wrap_nil(); + IOFile *iofile = abstract; + f = iofile->file; + break; + } + } + JanetBuffer *buf = janet_buffer(10); + janet_buffer_format(buf, fmt, 0, argc, argv); + if (1 != fwrite(buf->data, buf->count, 1, f)) { + janet_panicf("could not print %d bytes to file", buf->count, name); + } + /* Clear buffer to make things easier for GC */ + buf->count = 0; + buf->capacity = 0; + free(buf->data); + buf->data = NULL; + return janet_wrap_nil(); +} + +static Janet cfun_io_printf(int32_t argc, Janet *argv) { + return cfun_io_printf_impl(argc, argv, "out", stdout); +} + +static Janet cfun_io_eprintf(int32_t argc, Janet *argv) { + return cfun_io_printf_impl(argc, argv, "err", stderr); +} + void janet_dynprintf(const char *name, FILE *dflt_file, const char *format, ...) { va_list args; va_start(args, format); @@ -451,6 +506,9 @@ void janet_dynprintf(const char *name, FILE *dflt_file, const char *format, ...) JanetType xtype = janet_type(x); switch (xtype) { default: + /* Other values simply do nothing */ + break; + case JANET_NIL: case JANET_ABSTRACT: { FILE *f = dflt_file; JanetBuffer buffer; @@ -460,10 +518,10 @@ void janet_dynprintf(const char *name, FILE *dflt_file, const char *format, ...) janet_formatb(&buffer, format, args); if (xtype == JANET_ABSTRACT) { void *abstract = janet_unwrap_abstract(x); - if (janet_abstract_type(abstract) == &cfun_io_filetype) { - IOFile *iofile = abstract; - f = iofile->file; - } + if (janet_abstract_type(abstract) != &cfun_io_filetype) + break; + IOFile *iofile = abstract; + f = iofile->file; } fwrite(buffer.data, buffer.count, 1, f); janet_buffer_deinit(&buffer); @@ -492,6 +550,11 @@ static const JanetReg io_cfuns[] = { JDOC("(prin & xs)\n\n" "Same as print, but does not add trailing newline.") }, + { + "printf", cfun_io_printf, + JDOC("(printf fmt & xs)\n\n" + "Prints output formatted as if with (string/format fmt ;xs) to (dyn :out stdout).") + }, { "eprin", cfun_io_eprin, JDOC("(eprin & xs)\n\n" @@ -502,6 +565,11 @@ static const JanetReg io_cfuns[] = { JDOC("(eprint & xs)\n\n" "Same as print, but uses (dyn :err stderr) instead of (dyn :out stdout).") }, + { + "eprintf", cfun_io_eprintf, + JDOC("(eprintf fmt & xs)\n\n" + "Prints output formatted as if with (string/format fmt ;xs) to (dyn :err stderr).") + }, { "file/open", cfun_io_fopen, JDOC("(file/open path &opt mode)\n\n"