mirror of
https://github.com/tailix/libkernaux.git
synced 2025-02-17 15:45:32 -05:00
mruby: implement KernAux.snprintf1 (with some bug)
This commit is contained in:
parent
081dc4019b
commit
7698d0f3c6
5 changed files with 193 additions and 4 deletions
45
pkgs/mruby/src/dynarg.c
Normal file
45
pkgs/mruby/src/dynarg.c
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
#include "dynarg.h"
|
||||||
|
|
||||||
|
struct DynArg DynArg_create()
|
||||||
|
{
|
||||||
|
struct DynArg dynarg;
|
||||||
|
DynArg_init(&dynarg);
|
||||||
|
return dynarg;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DynArg_init(struct DynArg *const dynarg)
|
||||||
|
{
|
||||||
|
dynarg->use_dbl = false;
|
||||||
|
dynarg->dbl = 0.0;
|
||||||
|
dynarg->arg.str = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
void DynArg_use_char(struct DynArg *const dynarg, const char chr)
|
||||||
|
{
|
||||||
|
dynarg->use_dbl = false;
|
||||||
|
dynarg->arg.chr = chr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DynArg_use_double(struct DynArg *const dynarg, const double dbl)
|
||||||
|
{
|
||||||
|
dynarg->use_dbl = true;
|
||||||
|
dynarg->dbl = dbl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DynArg_use_long_long(struct DynArg *const dynarg, const long long ll)
|
||||||
|
{
|
||||||
|
dynarg->use_dbl = false;
|
||||||
|
dynarg->arg.ll = ll;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DynArg_use_str(struct DynArg *const dynarg, const char *const str)
|
||||||
|
{
|
||||||
|
dynarg->use_dbl = false;
|
||||||
|
dynarg->arg.str = str;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DynArg_use_unsigned_long_long(struct DynArg *const dynarg, const unsigned long long ull)
|
||||||
|
{
|
||||||
|
dynarg->use_dbl = false;
|
||||||
|
dynarg->arg.ull = ull;
|
||||||
|
}
|
35
pkgs/mruby/src/dynarg.h
Normal file
35
pkgs/mruby/src/dynarg.h
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
#ifndef INCLUDED_DYNARG
|
||||||
|
#define INCLUDED_DYNARG
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
struct DynArg {
|
||||||
|
bool use_dbl;
|
||||||
|
double dbl;
|
||||||
|
// TODO: check if this will work on different endianness.
|
||||||
|
union {
|
||||||
|
char chr;
|
||||||
|
long long ll;
|
||||||
|
const char *str;
|
||||||
|
unsigned long long ull;
|
||||||
|
} __attribute__((packed)) arg;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DynArg DynArg_create();
|
||||||
|
void DynArg_init(struct DynArg *dynarg);
|
||||||
|
|
||||||
|
void DynArg_use_char(struct DynArg *dynarg, char chr);
|
||||||
|
void DynArg_use_double(struct DynArg *dynarg, double dbl);
|
||||||
|
void DynArg_use_long_long(struct DynArg *dynarg, long long ll);
|
||||||
|
void DynArg_use_str(struct DynArg *dynarg, const char *str);
|
||||||
|
void DynArg_use_unsigned_long_long(struct DynArg *dynarg, unsigned long long ull);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
|
@ -1,3 +1,4 @@
|
||||||
|
#include "dynarg.h"
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
|
|
||||||
#include <kernaux.h>
|
#include <kernaux.h>
|
||||||
|
@ -5,6 +6,9 @@
|
||||||
#include <mruby.h>
|
#include <mruby.h>
|
||||||
#include <mruby/array.h>
|
#include <mruby/array.h>
|
||||||
#include <mruby/presym.h>
|
#include <mruby/presym.h>
|
||||||
|
#include <mruby/string.h>
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
static mrb_value rb_KernAux_snprintf1(mrb_state *mrb, mrb_value self);
|
static mrb_value rb_KernAux_snprintf1(mrb_state *mrb, mrb_value self);
|
||||||
|
|
||||||
|
@ -16,10 +20,103 @@ void init_printf(mrb_state *const mrb)
|
||||||
MRB_ARGS_REQ(2) | MRB_ARGS_OPT(2));
|
MRB_ARGS_REQ(2) | MRB_ARGS_OPT(2));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME: rewrite to ensure no memory leak on exception.
|
||||||
mrb_value rb_KernAux_snprintf1(mrb_state *const mrb, mrb_value self)
|
mrb_value rb_KernAux_snprintf1(mrb_state *const mrb, mrb_value self)
|
||||||
{
|
{
|
||||||
|
mrb_int size = 0;
|
||||||
|
const char *format = NULL;
|
||||||
|
mrb_value rest[3];
|
||||||
|
mrb_bool rest_given[3];
|
||||||
|
|
||||||
|
mrb_get_args(
|
||||||
|
mrb,
|
||||||
|
"iz|o?o?o?",
|
||||||
|
&size,
|
||||||
|
&format,
|
||||||
|
&rest[0], &rest_given[0],
|
||||||
|
&rest[1], &rest_given[1],
|
||||||
|
&rest[2], &rest_given[2]
|
||||||
|
);
|
||||||
|
|
||||||
|
if (size < 0) mrb_raise(mrb, E_RANGE_ERROR, "expected non-negative size");
|
||||||
|
|
||||||
|
const char *fmt = format;
|
||||||
|
|
||||||
|
while (*fmt && *fmt != '%') ++fmt;
|
||||||
|
if (*(fmt++) != '%') mrb_raise(mrb, E_ARGUMENT_ERROR, "invalid format");
|
||||||
|
|
||||||
|
struct KernAux_PrintfFmt_Spec spec = KernAux_PrintfFmt_Spec_create();
|
||||||
|
|
||||||
|
fmt = KernAux_PrintfFmt_Spec_parse(&spec, fmt);
|
||||||
|
|
||||||
|
while (*fmt) {
|
||||||
|
if (*(fmt++) == '%') mrb_raise(mrb, E_ARGUMENT_ERROR, "invalid format");
|
||||||
|
}
|
||||||
|
|
||||||
|
int argc = 0;
|
||||||
|
for (int i = 0; i < 3; ++i) if (rest_given[i]) ++argc;
|
||||||
|
|
||||||
|
int arg_index = 0;
|
||||||
|
if (spec.set_width && argc > arg_index) {
|
||||||
|
KernAux_PrintfFmt_Spec_set_width(&spec, mrb_integer(rest[arg_index++]));
|
||||||
|
}
|
||||||
|
if (spec.set_precision && argc > arg_index) {
|
||||||
|
KernAux_PrintfFmt_Spec_set_precision(&spec, mrb_integer(rest[arg_index++]));
|
||||||
|
}
|
||||||
|
|
||||||
|
struct DynArg dynarg = DynArg_create();
|
||||||
|
if (argc > arg_index) {
|
||||||
|
mrb_value arg_rb = rest[arg_index];
|
||||||
|
|
||||||
|
if (spec.type == KERNAUX_PRINTF_FMT_TYPE_INT) {
|
||||||
|
DynArg_use_long_long(&dynarg, mrb_integer(arg_rb));
|
||||||
|
} else if (spec.type == KERNAUX_PRINTF_FMT_TYPE_UINT) {
|
||||||
|
mrb_int arg = mrb_integer(arg_rb);
|
||||||
|
if (arg < 0) mrb_raise(mrb, E_ARGUMENT_ERROR, "expected non-negative argument");
|
||||||
|
DynArg_use_unsigned_long_long(&dynarg, arg);
|
||||||
|
} else if (spec.type == KERNAUX_PRINTF_FMT_TYPE_FLOAT ||
|
||||||
|
spec.type == KERNAUX_PRINTF_FMT_TYPE_EXP)
|
||||||
|
{
|
||||||
|
DynArg_use_double(&dynarg, mrb_as_float(mrb, arg_rb));
|
||||||
|
} else if (spec.type == KERNAUX_PRINTF_FMT_TYPE_CHAR) {
|
||||||
|
DynArg_use_char(&dynarg, *RSTRING_CSTR(mrb, arg_rb));
|
||||||
|
} else if (spec.type == KERNAUX_PRINTF_FMT_TYPE_STR) {
|
||||||
|
DynArg_use_str(&dynarg, RSTRING_CSTR(mrb, arg_rb));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
char *const str = malloc(size);
|
||||||
|
if (!str) mrb_raise(mrb, mrb_exc_get_id(mrb, MRB_ERROR_SYM(NoMemoryError)), "snprintf1 buffer malloc");
|
||||||
|
|
||||||
|
int slen;
|
||||||
|
if (spec.set_width) {
|
||||||
|
if (spec.set_precision) {
|
||||||
|
slen = dynarg.use_dbl
|
||||||
|
? kernaux_snprintf(str, size, format, spec.width, spec.precision, dynarg.dbl)
|
||||||
|
: kernaux_snprintf(str, size, format, spec.width, spec.precision, dynarg.arg);
|
||||||
|
} else {
|
||||||
|
slen = dynarg.use_dbl
|
||||||
|
? kernaux_snprintf(str, size, format, spec.width, dynarg.dbl)
|
||||||
|
: kernaux_snprintf(str, size, format, spec.width, dynarg.arg);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (spec.set_precision) {
|
||||||
|
slen = dynarg.use_dbl
|
||||||
|
? kernaux_snprintf(str, size, format, spec.precision, dynarg.dbl)
|
||||||
|
: kernaux_snprintf(str, size, format, spec.precision, dynarg.arg);
|
||||||
|
} else {
|
||||||
|
slen = dynarg.use_dbl
|
||||||
|
? kernaux_snprintf(str, size, format, dynarg.dbl)
|
||||||
|
: kernaux_snprintf(str, size, format, dynarg.arg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mrb_value output_rb =
|
||||||
|
mrb_obj_freeze(mrb, mrb_str_cat_cstr(mrb, mrb_str_new_lit(mrb, ""), str));
|
||||||
|
free(str);
|
||||||
|
|
||||||
mrb_value values[2];
|
mrb_value values[2];
|
||||||
values[0] = mrb_obj_freeze(mrb, mrb_str_new_lit(mrb, ""));
|
values[0] = output_rb;
|
||||||
values[1] = mrb_nil_value();
|
values[1] = mrb_fixnum_value(slen);
|
||||||
return mrb_obj_freeze(mrb, mrb_ary_new_from_values(mrb, 2, values));
|
return mrb_obj_freeze(mrb, mrb_ary_new_from_values(mrb, 2, values));
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,20 @@ assert 'KernAux.sprintf' do
|
||||||
|
|
||||||
YAML.load(File.read(printf_yml)).each do |test|
|
YAML.load(File.read(printf_yml)).each do |test|
|
||||||
expected = test['result']
|
expected = test['result']
|
||||||
args = test['args']
|
|
||||||
|
args = test['args'].map do |arg|
|
||||||
|
if arg.is_a? String
|
||||||
|
arg
|
||||||
|
else
|
||||||
|
arg.map do |item|
|
||||||
|
if item.is_a? Array
|
||||||
|
item[0]
|
||||||
|
else
|
||||||
|
item
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
assert "transforms #{args.inspect} to #{expected.inspect}" do
|
assert "transforms #{args.inspect} to #{expected.inspect}" do
|
||||||
assert_equal expected, KernAux.sprintf(*args)
|
assert_equal expected, KernAux.sprintf(*args)
|
||||||
|
|
|
@ -19,7 +19,6 @@ void init_printf()
|
||||||
rb_KernAux_snprintf1, -1);
|
rb_KernAux_snprintf1, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: is this implementation correct?
|
|
||||||
// FIXME: rewrite to ensure no memory leak on exception.
|
// FIXME: rewrite to ensure no memory leak on exception.
|
||||||
VALUE rb_KernAux_snprintf1(
|
VALUE rb_KernAux_snprintf1(
|
||||||
const int argc,
|
const int argc,
|
||||||
|
|
Loading…
Add table
Reference in a new issue