From 029394db31d0b2fdd7c8625da24cb5f19c24ccc2 Mon Sep 17 00:00:00 2001 From: Calvin Rose Date: Sat, 16 Feb 2019 13:59:38 -0500 Subject: [PATCH] Add buffer/format as well as string/format. buffer/format uses the old string/format behavior. `string/format` no longer requires a buffer, and returns a string. --- src/core/buffer.c | 13 +++++ src/core/pp.c | 146 ++++++++++++++++++++++++++++++++++++++++++++++ src/core/string.c | 143 ++------------------------------------------- src/core/util.h | 6 ++ test/suite4.janet | 22 ++++--- 5 files changed, 185 insertions(+), 145 deletions(-) diff --git a/src/core/buffer.c b/src/core/buffer.c index 25a2e61c..33e067d1 100644 --- a/src/core/buffer.c +++ b/src/core/buffer.c @@ -317,6 +317,14 @@ static Janet cfun_buffer_blit(int32_t argc, Janet *argv) { return argv[0]; } +static Janet cfun_buffer_format(int32_t argc, Janet *argv) { + janet_arity(argc, 2, -1); + JanetBuffer *buffer = janet_getbuffer(argv, 0); + const char *strfrmt = (const char *) janet_getstring(argv, 1); + janet_buffer_format(buffer, strfrmt, 1, argc, argv); + return argv[0]; +} + static const JanetReg buffer_cfuns[] = { {"buffer/new", cfun_buffer_new, JDOC("(buffer/new capacity)\n\n" @@ -383,6 +391,11 @@ static const JanetReg buffer_cfuns[] = { "indicate which part of src to copy into which part of dest. Indices can be " "negative to index from the end of src or dest. Returns dest.") }, + {"buffer/format", cfun_buffer_format, + JDOC("(buffer/format buffer format & args)\n\n" + "Snprintf like functionality for printing values into a buffer. Returns " + " the modified buffer.") + }, {NULL, NULL, NULL} }; diff --git a/src/core/pp.c b/src/core/pp.c index 872e4634..f083c662 100644 --- a/src/core/pp.c +++ b/src/core/pp.c @@ -20,6 +20,9 @@ * IN THE SOFTWARE. */ +#include +#include + #ifndef JANET_AMALG #include #include "util.h" @@ -549,3 +552,146 @@ const uint8_t *janet_formatc(const char *format, ...) { return ret; } +/* + * code adapted from lua/lstrlib.c http://lua.org + */ + +#define MAX_ITEM 256 +#define FMT_FLAGS "-+ #0" +#define MAX_FORMAT 32 + +static const char *scanformat( + const char *strfrmt, + char *form, + char width[3], + char precision[3]) { + const char *p = strfrmt; + memset(width, '\0', 3); + memset(precision, '\0', 3); + while (*p != '\0' && strchr(FMT_FLAGS, *p) != NULL) + p++; /* skip flags */ + if ((size_t) (p - strfrmt) >= sizeof(FMT_FLAGS) / sizeof(char)) + janet_panic("invalid format (repeated flags)"); + if (isdigit((int) (*p))) + width[0] = *p++; /* skip width */ + if (isdigit((int) (*p))) + width[1] = *p++; /* (2 digits at most) */ + if (*p == '.') { + p++; + if (isdigit((int) (*p))) + precision[0] = *p++; /* skip precision */ + if (isdigit((int) (*p))) + precision[1] = *p++; /* (2 digits at most) */ + } + if (isdigit((int) (*p))) + janet_panic("invalid format (width or precision too long)"); + *(form++) = '%'; + memcpy(form, strfrmt, ((p - strfrmt) + 1) * sizeof(char)); + form += (p - strfrmt) + 1; + *form = '\0'; + return p; +} + +/* Shared implementation between string/format and + * buffer/format */ +void janet_buffer_format( + JanetBuffer *b, + const char *strfrmt, + int32_t argstart, + int32_t argc, + Janet *argv) { + size_t sfl = strlen(strfrmt); + const char *strfrmt_end = strfrmt + sfl; + int32_t arg = argstart; + while (strfrmt < strfrmt_end) { + if (*strfrmt != '%') + janet_buffer_push_u8(b, (uint8_t) * strfrmt++); + else if (*++strfrmt == '%') + janet_buffer_push_u8(b, (uint8_t) * strfrmt++); /* %% */ + else { /* format item */ + char form[MAX_FORMAT], item[MAX_ITEM]; + char width[3], precision[3]; + int nb = 0; /* number of bytes in added item */ + if (++arg >= argc) + janet_panic("not enough values for format"); + strfrmt = scanformat(strfrmt, form, width, precision); + switch (*strfrmt++) { + case 'c': + { + nb = snprintf(item, MAX_ITEM, form, (int) + janet_getinteger(argv, arg)); + break; + } + case 'd': + case 'i': + case 'o': + case 'u': + case 'x': + case 'X': + { + int32_t n = janet_getinteger(argv, arg); + nb = snprintf(item, MAX_ITEM, form, n); + break; + } + case 'a': + case 'A': + case 'e': + case 'E': + case 'f': + case 'g': + case 'G': + { + double d = janet_getnumber(argv, arg); + nb = snprintf(item, MAX_ITEM, form, d); + break; + } + case 's': + { + const uint8_t *s = janet_getstring(argv, arg); + size_t l = janet_string_length(s); + if (form[2] == '\0') + janet_buffer_push_bytes(b, s, l); + else { + if (l != strlen((const char *) s)) + janet_panic("string contains zeros"); + if (!strchr(form, '.') && l >= 100) { + janet_panic + ("no precision and string is too long to be formatted"); + } else { + nb = snprintf(item, MAX_ITEM, form, s); + } + } + break; + } + case 'V': + { + janet_to_string_b(b, argv[arg]); + break; + } + case 'v': + { + janet_description_b(b, argv[arg]); + break; + } + case 'p': /* janet pretty , precision = depth */ + { + int depth = atoi(precision); + if (depth < 1) + depth = 4; + janet_pretty(b, depth, argv[arg]); + break; + } + default: + { /* also treat cases 'nLlh' */ + janet_panicf("invalid conversion '%s' to 'format'", + form); + } + } + if (nb >= MAX_ITEM) + janet_panicf("format buffer overflow", form); + if (nb > 0) + janet_buffer_push_bytes(b, (uint8_t *) item, nb); + } + } +} + diff --git a/src/core/string.c b/src/core/string.c index b239048c..84e918db 100644 --- a/src/core/string.c +++ b/src/core/string.c @@ -21,7 +21,6 @@ */ #include -#include #ifndef JANET_AMALG #include @@ -502,144 +501,12 @@ static Janet cfun_string_pretty(int32_t argc, Janet *argv) { return janet_wrap_buffer(buffer); } -/* - * code adapted from lua/lstrlib.c http://lua.org - */ - -#define MAX_ITEM 256 -#define FMT_FLAGS "-+ #0" -#define MAX_FORMAT 32 - -static const char *scanformat( - const char *strfrmt, - char *form, - char width[3], - char precision[3]) { - const char *p = strfrmt; - memset(width, '\0', 3); - memset(precision, '\0', 3); - while (*p != '\0' && strchr(FMT_FLAGS, *p) != NULL) - p++; /* skip flags */ - if ((size_t) (p - strfrmt) >= sizeof(FMT_FLAGS) / sizeof(char)) - janet_panic("invalid format (repeated flags)"); - if (isdigit((int) (*p))) - width[0] = *p++; /* skip width */ - if (isdigit((int) (*p))) - width[1] = *p++; /* (2 digits at most) */ - if (*p == '.') { - p++; - if (isdigit((int) (*p))) - precision[0] = *p++; /* skip precision */ - if (isdigit((int) (*p))) - precision[1] = *p++; /* (2 digits at most) */ - } - if (isdigit((int) (*p))) - janet_panic("invalid format (width or precision too long)"); - *(form++) = '%'; - memcpy(form, strfrmt, ((p - strfrmt) + 1) * sizeof(char)); - form += (p - strfrmt) + 1; - *form = '\0'; - return p; -} - static Janet cfun_string_format(int32_t argc, Janet *argv) { - janet_arity(argc, 2, -1); - JanetBuffer *b = janet_getbuffer(argv, 0); - const char *strfrmt = (const char *) janet_getstring(argv, 1); - size_t sfl = strlen(strfrmt); - const char *strfrmt_end = strfrmt + sfl; - int32_t arg = 1; - while (strfrmt < strfrmt_end) { - if (*strfrmt != '%') - janet_buffer_push_u8(b, (uint8_t) * strfrmt++); - else if (*++strfrmt == '%') - janet_buffer_push_u8(b, (uint8_t) * strfrmt++); /* %% */ - else { /* format item */ - char form[MAX_FORMAT],item[MAX_ITEM]; - char width[3], precision[3]; - int nb = 0; /* number of bytes in added item */ - if (++arg >= argc) - janet_panic("not enough values for format"); - strfrmt = scanformat(strfrmt, form, width, precision); - switch (*strfrmt++) { - case 'c': - { - nb = snprintf(item, MAX_ITEM, form, (int) - janet_getinteger(argv, arg)); - break; - } - case 'd': - case 'i': - case 'o': - case 'u': - case 'x': - case 'X': - { - int32_t n = janet_getinteger(argv, arg); - nb = snprintf(item, MAX_ITEM, form, n); - break; - } - case 'a': - case 'A': - case 'e': - case 'E': - case 'f': - case 'g': - case 'G': - { - double d = janet_getnumber(argv, arg); - nb = snprintf(item, MAX_ITEM, form, d); - break; - } - case 's': - { - const uint8_t *s = janet_getstring(argv, arg); - size_t l = janet_string_length(s); - if (form[2] == '\0') - janet_buffer_push_bytes(b, s, l); - else { - if (l != strlen((const char *) s)) - janet_panic("string contains zeros"); - if (!strchr(form, '.') && l >= 100) { - janet_panic - ("no precision and string is too long to be formatted"); - } else { - nb = snprintf(item, MAX_ITEM, form, s); - } - } - break; - } - case 'V': - { - janet_to_string_b(b, argv[arg]); - break; - } - case 'v': - { - janet_description_b(b, argv[arg]); - break; - } - case 'p': /* janet pretty , precision = depth */ - { - int depth = atoi(precision); - if (depth < 1) - depth = 4; - janet_pretty(b, depth, argv[arg]); - break; - } - default: - { /* also treat cases 'nLlh' */ - janet_panicf("invalid conversion '%s' to 'format'", - form); - } - } - if (nb >= MAX_ITEM) - janet_panicf("format buffer overflow", form); - if (nb > 0) - janet_buffer_push_bytes(b, (uint8_t *) item, nb); - } - } - return janet_wrap_buffer(b); + janet_arity(argc, 1, -1); + JanetBuffer *buffer = janet_buffer(0); + const char *strfrmt = (const char *) janet_getstring(argv, 0); + janet_buffer_format(buffer, strfrmt, 0, argc, argv); + return janet_stringv(buffer->data, buffer->count); } static const JanetReg string_cfuns[] = { diff --git a/src/core/util.h b/src/core/util.h index aa6acff3..60e441e9 100644 --- a/src/core/util.h +++ b/src/core/util.h @@ -52,6 +52,12 @@ const void *janet_strbinsearch( size_t tabcount, size_t itemsize, const uint8_t *key); +void janet_buffer_format( + JanetBuffer *b, + const char *strfrmt, + int32_t argstart, + int32_t argc, + Janet *argv); /* Inside the janet core, defining globals is different * at bootstrap time and normal runtime */ diff --git a/test/suite4.janet b/test/suite4.janet index 85f76a0c..b89d963a 100644 --- a/test/suite4.janet +++ b/test/suite4.janet @@ -20,15 +20,23 @@ (import test/helper :prefix "" :exit true) (start-suite 4) -# some tests for string/format +# some tests for string/format and buffer/format -(assert (= (string (string/format @"" "pi = %6.3f" math/pi)) "pi = 3.142") "%6.3f") -(assert (= (string (string/format @"" "pi = %+6.3f" math/pi)) "pi = +3.142") "%6.3f") -(assert (= (string (string/format @"" "pi = %40.20g" math/pi)) "pi = 3.141592653589793116") "%6.3f") +(assert (= (string (buffer/format @"" "pi = %6.3f" math/pi)) "pi = 3.142") "%6.3f") +(assert (= (string (buffer/format @"" "pi = %+6.3f" math/pi)) "pi = +3.142") "%6.3f") +(assert (= (string (buffer/format @"" "pi = %40.20g" math/pi)) "pi = 3.141592653589793116") "%6.3f") -(assert (= (string (string/format @"" "🐼 = %6.3f" math/pi)) "🐼 = 3.142") "UTF-8") -(assert (= (string (string/format @"" "π = %.8g" math/pi)) "π = 3.1415927") "π") -(assert (= (string (string/format @"" "\xCF\x80 = %.8g" math/pi)) "\xCF\x80 = 3.1415927") "\xCF\x80") +(assert (= (string (buffer/format @"" "🐼 = %6.3f" math/pi)) "🐼 = 3.142") "UTF-8") +(assert (= (string (buffer/format @"" "π = %.8g" math/pi)) "π = 3.1415927") "π") +(assert (= (string (buffer/format @"" "\xCF\x80 = %.8g" math/pi)) "\xCF\x80 = 3.1415927") "\xCF\x80") + +(assert (= (string/format "pi = %6.3f" math/pi) "pi = 3.142") "%6.3f") +(assert (= (string/format "pi = %+6.3f" math/pi) "pi = +3.142") "%6.3f") +(assert (= (string/format "pi = %40.20g" math/pi) "pi = 3.141592653589793116") "%6.3f") + +(assert (= (string/format "🐼 = %6.3f" math/pi) "🐼 = 3.142") "UTF-8") +(assert (= (string/format "π = %.8g" math/pi) "π = 3.1415927") "π") +(assert (= (string/format "\xCF\x80 = %.8g" math/pi) "\xCF\x80 = 3.1415927") "\xCF\x80") (end-suite)