mirror of
https://github.com/janet-lang/janet
synced 2024-12-26 00:10:27 +00:00
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.
This commit is contained in:
parent
00020ba8ab
commit
029394db31
@ -317,6 +317,14 @@ static Janet cfun_buffer_blit(int32_t argc, Janet *argv) {
|
|||||||
return argv[0];
|
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[] = {
|
static const JanetReg buffer_cfuns[] = {
|
||||||
{"buffer/new", cfun_buffer_new,
|
{"buffer/new", cfun_buffer_new,
|
||||||
JDOC("(buffer/new capacity)\n\n"
|
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 "
|
"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.")
|
"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}
|
{NULL, NULL, NULL}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
146
src/core/pp.c
146
src/core/pp.c
@ -20,6 +20,9 @@
|
|||||||
* IN THE SOFTWARE.
|
* IN THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
#ifndef JANET_AMALG
|
#ifndef JANET_AMALG
|
||||||
#include <janet/janet.h>
|
#include <janet/janet.h>
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
@ -549,3 +552,146 @@ const uint8_t *janet_formatc(const char *format, ...) {
|
|||||||
return ret;
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -21,7 +21,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <ctype.h>
|
|
||||||
|
|
||||||
#ifndef JANET_AMALG
|
#ifndef JANET_AMALG
|
||||||
#include <janet/janet.h>
|
#include <janet/janet.h>
|
||||||
@ -502,144 +501,12 @@ static Janet cfun_string_pretty(int32_t argc, Janet *argv) {
|
|||||||
return janet_wrap_buffer(buffer);
|
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) {
|
static Janet cfun_string_format(int32_t argc, Janet *argv) {
|
||||||
janet_arity(argc, 2, -1);
|
janet_arity(argc, 1, -1);
|
||||||
JanetBuffer *b = janet_getbuffer(argv, 0);
|
JanetBuffer *buffer = janet_buffer(0);
|
||||||
const char *strfrmt = (const char *) janet_getstring(argv, 1);
|
const char *strfrmt = (const char *) janet_getstring(argv, 0);
|
||||||
size_t sfl = strlen(strfrmt);
|
janet_buffer_format(buffer, strfrmt, 0, argc, argv);
|
||||||
const char *strfrmt_end = strfrmt + sfl;
|
return janet_stringv(buffer->data, buffer->count);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static const JanetReg string_cfuns[] = {
|
static const JanetReg string_cfuns[] = {
|
||||||
|
@ -52,6 +52,12 @@ const void *janet_strbinsearch(
|
|||||||
size_t tabcount,
|
size_t tabcount,
|
||||||
size_t itemsize,
|
size_t itemsize,
|
||||||
const uint8_t *key);
|
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
|
/* Inside the janet core, defining globals is different
|
||||||
* at bootstrap time and normal runtime */
|
* at bootstrap time and normal runtime */
|
||||||
|
@ -20,15 +20,23 @@
|
|||||||
|
|
||||||
(import test/helper :prefix "" :exit true)
|
(import test/helper :prefix "" :exit true)
|
||||||
(start-suite 4)
|
(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 (buffer/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 (buffer/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 = %40.20g" math/pi)) "pi = 3.141592653589793116") "%6.3f")
|
||||||
|
|
||||||
(assert (= (string (string/format @"" "🐼 = %6.3f" math/pi)) "🐼 = 3.142") "UTF-8")
|
(assert (= (string (buffer/format @"" "🐼 = %6.3f" math/pi)) "🐼 = 3.142") "UTF-8")
|
||||||
(assert (= (string (string/format @"" "π = %.8g" math/pi)) "π = 3.1415927") "π")
|
(assert (= (string (buffer/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 @"" "\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)
|
(end-suite)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user