miller/c/containers/xvfuncs.c
2017-04-26 07:07:41 -04:00

492 lines
15 KiB
C

#include "../lib/mlr_arch.h"
#include "../lib/string_builder.h"
#include "../lib/free_flags.h"
#include "../containers/xvfuncs.h"
// ----------------------------------------------------------------
boxed_xval_t b_xx_haskey_xfunc(boxed_xval_t* pmapval, boxed_xval_t* pkeyval) {
boxed_xval_t rv;
if (pmapval->xval.is_terminal) {
rv = box_ephemeral_val(mv_from_bool(FALSE));
} else if (!pkeyval->xval.is_terminal) {
rv = box_ephemeral_val(mv_from_bool(FALSE));
} else {
rv = box_ephemeral_val(
mv_from_bool(
mlhmmv_level_has_key(pmapval->xval.pnext_level, &pkeyval->xval.terminal_mlrval)
)
);
}
if (pmapval->is_ephemeral)
mlhmmv_xvalue_free(&pmapval->xval);
if (pkeyval->is_ephemeral)
mlhmmv_xvalue_free(&pkeyval->xval);
return rv;
}
// ----------------------------------------------------------------
boxed_xval_t i_x_length_xfunc(boxed_xval_t* pbxval1) {
boxed_xval_t rv;
if (pbxval1->xval.is_terminal) {
rv = box_ephemeral_val(
mv_from_int(1)
);
} else {
rv = box_ephemeral_val(
mv_from_int(
pbxval1->xval.pnext_level->num_occupied
)
);
}
if (pbxval1->is_ephemeral)
mlhmmv_xvalue_free(&pbxval1->xval);
return rv;
}
// ----------------------------------------------------------------
static int depth_aux(mlhmmv_xvalue_t* pxval) {
if (pxval->is_terminal) {
return 0;
} else {
int max = 0;
for (mlhmmv_level_entry_t* pe = pxval->pnext_level->phead; pe != NULL; pe = pe->pnext) {
int curr = depth_aux(&pe->level_xvalue);
max = (curr > max) ? curr : max;
}
return 1 + max;
}
}
boxed_xval_t i_x_depth_xfunc(boxed_xval_t* pbxval1) {
boxed_xval_t rv = box_ephemeral_val(
mv_from_int(
depth_aux(&pbxval1->xval)
)
);
if (pbxval1->is_ephemeral)
mlhmmv_xvalue_free(&pbxval1->xval);
return rv;
}
// ----------------------------------------------------------------
static int leafcount_aux(mlhmmv_xvalue_t* pxval) {
if (pxval->is_terminal) {
return 1;
} else {
int sum = 0;
for (mlhmmv_level_entry_t* pe = pxval->pnext_level->phead; pe != NULL; pe = pe->pnext) {
sum += leafcount_aux(&pe->level_xvalue);
}
return sum;
}
}
boxed_xval_t i_x_leafcount_xfunc(boxed_xval_t* pbxval1) {
boxed_xval_t rv = box_ephemeral_val(
mv_from_int(
leafcount_aux(&pbxval1->xval)
)
);
if (pbxval1->is_ephemeral)
mlhmmv_xvalue_free(&pbxval1->xval);
return rv;
}
// ----------------------------------------------------------------
boxed_xval_t variadic_mapsum_xfunc(boxed_xval_t* pbxvals, int nxvals) {
// xxx to-do optmization: transfer arg 1 if it's ephemeral
mlhmmv_xvalue_t sum = mlhmmv_xvalue_alloc_empty_map();
for (int i = 0; i < nxvals; i++) {
if (pbxvals[i].xval.is_terminal)
continue;
mlhmmv_level_t* plevel = pbxvals[i].xval.pnext_level;
for (mlhmmv_level_entry_t* pe = plevel->phead; pe != NULL; pe = pe->pnext) {
// xxx do refs/copies correctly
mlhmmv_xvalue_t xval_copy = mlhmmv_xvalue_copy(&pe->level_xvalue);
sllmve_t e = (sllmve_t) { .value = pe->level_key, .free_flags = 0, .pnext = NULL };
mlhmmv_level_put_xvalue(sum.pnext_level, &e, &xval_copy);
}
}
boxed_xval_t rv = box_ephemeral_xval(sum);
for (int i = 0; i < nxvals; i++) {
if (pbxvals[i].is_ephemeral)
mlhmmv_xvalue_free(&pbxvals[i].xval);
}
return rv;
}
// ----------------------------------------------------------------
boxed_xval_t variadic_mapdiff_xfunc(boxed_xval_t* pbxvals, int nxvals) {
mlhmmv_xvalue_t diff = mlhmmv_xvalue_alloc_empty_map();
if (nxvals == 0) {
return box_ephemeral_xval(diff);
}
// xxx to-do optmization: transfer arg 1 if it's ephemeral
// xxx methodize
int i = 0;
if (!pbxvals[i].xval.is_terminal) {
mlhmmv_level_t* plevel = pbxvals[i].xval.pnext_level;
for (mlhmmv_level_entry_t* pe = plevel->phead; pe != NULL; pe = pe->pnext) {
// xxx do refs/copies correctly
mlhmmv_xvalue_t xval_copy = mlhmmv_xvalue_copy(&pe->level_xvalue);
sllmve_t e = (sllmve_t) { .value = pe->level_key, .free_flags = 0, .pnext = NULL };
mlhmmv_level_put_xvalue(diff.pnext_level, &e, &xval_copy);
}
if (pbxvals[i].is_ephemeral)
mlhmmv_xvalue_free(&pbxvals[i].xval);
}
for (i = 1; i < nxvals; i++) {
if (!pbxvals[i].xval.is_terminal) {
mlhmmv_level_t* plevel = pbxvals[i].xval.pnext_level;
for (mlhmmv_level_entry_t* pe = plevel->phead; pe != NULL; pe = pe->pnext) {
sllmve_t e = (sllmve_t) { .value = pe->level_key, .free_flags = 0, .pnext = NULL };
mlhmmv_level_remove(diff.pnext_level, &e);
}
}
if (pbxvals[i].is_ephemeral)
mlhmmv_xvalue_free(&pbxvals[i].xval);
}
return box_ephemeral_xval(diff);
}
// ----------------------------------------------------------------
// Precondition (validated before we're called): there is at least one argument
// which is the map to be unkeyed.
boxed_xval_t variadic_mapexcept_xfunc(
boxed_xval_t* pbxvals,
int nxvals)
{
MLR_INTERNAL_CODING_ERROR_IF(nxvals < 1);
boxed_xval_t* pinbxval = &pbxvals[0];
boxed_xval_t outbxval = box_ephemeral_xval(mlhmmv_xvalue_copy(&pinbxval->xval));
if (pinbxval->xval.is_terminal) { // non-map
return outbxval;
}
mlhmmv_level_t* poutlevel = outbxval.xval.pnext_level;
for (int i = 1; i < nxvals; i++) {
if (pbxvals[i].xval.is_terminal) {
mv_t* pkey = &pbxvals[i].xval.terminal_mlrval;
sllmve_t e = (sllmve_t) { .value = *pkey, .free_flags = 0, .pnext = NULL };
mlhmmv_level_remove(poutlevel, &e);
}
if (pbxvals[i].is_ephemeral)
mlhmmv_xvalue_free(&pbxvals[i].xval);
}
return outbxval;
}
// ----------------------------------------------------------------
// Precondition (validated before we're called): there is at least one argument
// which is the map to be unkeyed.
boxed_xval_t variadic_mapselect_xfunc(
boxed_xval_t* pbxvals,
int nxvals)
{
MLR_INTERNAL_CODING_ERROR_IF(nxvals < 1);
boxed_xval_t* pinbxval = &pbxvals[0];
boxed_xval_t outbxval = box_ephemeral_xval(mlhmmv_xvalue_alloc_empty_map());
if (pinbxval->xval.is_terminal) { // non-map
return outbxval;
}
mlhmmv_level_t* pinlevel = pinbxval->xval.pnext_level;
mlhmmv_level_t* poutlevel = outbxval.xval.pnext_level;
for (int i = 1; i < nxvals; i++) {
if (pbxvals[i].xval.is_terminal) {
mv_t* pkey = &pbxvals[i].xval.terminal_mlrval;
sllmv_t* pkeylist = sllmv_single_no_free(pkey);
int unused = 0;
mlhmmv_xvalue_t* pval = mlhmmv_level_look_up_and_ref_xvalue(pinlevel, pkeylist, &unused);
if (pval != NULL) {
mlhmmv_xvalue_t copyval = mlhmmv_xvalue_copy(pval);
// mlhmmv_level_put_xvalue copies key not value
mlhmmv_level_put_xvalue(poutlevel, pkeylist->phead, &copyval);
}
sllmv_free(pkeylist);
}
if (pbxvals[i].is_ephemeral)
mlhmmv_xvalue_free(&pbxvals[i].xval);
}
return outbxval;
}
// ----------------------------------------------------------------
// Precondition: the caller has ensured that both arguments are string-valued terminals.
boxed_xval_t m_ss_splitnv_xfunc(boxed_xval_t* pstringval, boxed_xval_t* psepval) {
mlhmmv_xvalue_t map = mlhmmv_xvalue_alloc_empty_map();
char* input = mlr_strdup_or_die(pstringval->xval.terminal_mlrval.u.strv);
char* sep = psepval->xval.terminal_mlrval.u.strv;
int seplen = strlen(sep);
int i = 1;
char* walker = input;
char* piece;
while ((piece = mlr_strmsep(&walker, sep, seplen)) != NULL) {
mv_t key = mv_from_int(i);
mv_t val = mv_ref_type_infer_string_or_float_or_int(piece);
mlhmmv_level_put_terminal_singly_keyed(map.pnext_level, &key, &val);
i++;
}
free(input);
if (pstringval->is_ephemeral)
mlhmmv_xvalue_free(&pstringval->xval);
if (psepval->is_ephemeral)
mlhmmv_xvalue_free(&psepval->xval);
return box_ephemeral_xval(map);
}
// ----------------------------------------------------------------
// Precondition: the caller has ensured that both arguments are string-valued terminals.
boxed_xval_t m_ss_splitnvx_xfunc(boxed_xval_t* pstringval, boxed_xval_t* psepval) {
mlhmmv_xvalue_t map = mlhmmv_xvalue_alloc_empty_map();
char* input = mlr_strdup_or_die(pstringval->xval.terminal_mlrval.u.strv);
char* sep = psepval->xval.terminal_mlrval.u.strv;
int seplen = strlen(sep);
int i = 1;
char* walker = input;
char* piece;
while ((piece = mlr_strmsep(&walker, sep, seplen)) != NULL) {
mv_t key = mv_from_int(i);
// xxx do not ref here
mv_t val = mv_ref_type_infer_string(piece);
// xxx make clear the copy/ref semantics for mlhmmv put with scalar value.
// at the moment this does an mv_copy.
mlhmmv_level_put_terminal_singly_keyed(map.pnext_level, &key, &val);
i++;
}
free(input);
if (pstringval->is_ephemeral)
mlhmmv_xvalue_free(&pstringval->xval);
if (psepval->is_ephemeral)
mlhmmv_xvalue_free(&psepval->xval);
return box_ephemeral_xval(map);
}
// ----------------------------------------------------------------
// Precondition: the caller has ensured that all arguments are string-valued terminals.
boxed_xval_t m_sss_splitkv_xfunc(boxed_xval_t* pstringval, boxed_xval_t* ppairsepval, boxed_xval_t* plistsepval) {
mlhmmv_xvalue_t map = mlhmmv_xvalue_alloc_empty_map();
char* input = mlr_strdup_or_die(pstringval->xval.terminal_mlrval.u.strv);
char* listsep = plistsepval->xval.terminal_mlrval.u.strv;
char* pairsep = ppairsepval->xval.terminal_mlrval.u.strv;
int listseplen = strlen(listsep);
int pairseplen = strlen(pairsep);
int i = 1;
char* walker = input;
char* piece;
while ((piece = mlr_strmsep(&walker, listsep, listseplen)) != NULL) {
char* pair = piece;
char* left = mlr_strmsep(&pair, pairsep, pairseplen);
if (pair == NULL) {
mv_t key = mv_from_int(i);
mv_t val = mv_ref_type_infer_string_or_float_or_int(left);
mlhmmv_level_put_terminal_singly_keyed(map.pnext_level, &key, &val);
} else {
char* right = mlr_strmsep(&pair, pairsep, pairseplen);
mv_t key = mv_from_string(left, NO_FREE);
mv_t val = mv_ref_type_infer_string_or_float_or_int(right);
mlhmmv_level_put_terminal_singly_keyed(map.pnext_level, &key, &val);
}
i++;
}
free(input);
if (pstringval->is_ephemeral)
mlhmmv_xvalue_free(&pstringval->xval);
if (ppairsepval->is_ephemeral)
mlhmmv_xvalue_free(&ppairsepval->xval);
if (plistsepval->is_ephemeral)
mlhmmv_xvalue_free(&plistsepval->xval);
return box_ephemeral_xval(map);
}
// ----------------------------------------------------------------
// Precondition: the caller has ensured that all arguments are string-valued terminals.
boxed_xval_t m_sss_splitkvx_xfunc(boxed_xval_t* pstringval, boxed_xval_t* ppairsepval, boxed_xval_t* plistsepval) {
mlhmmv_xvalue_t map = mlhmmv_xvalue_alloc_empty_map();
char* input = mlr_strdup_or_die(pstringval->xval.terminal_mlrval.u.strv);
char* listsep = plistsepval->xval.terminal_mlrval.u.strv;
char* pairsep = ppairsepval->xval.terminal_mlrval.u.strv;
int listseplen = strlen(listsep);
int pairseplen = strlen(pairsep);
int i = 1;
char* walker = input;
char* piece;
while ((piece = mlr_strmsep(&walker, listsep, listseplen)) != NULL) {
char* pair = piece;
char* left = mlr_strmsep(&pair, pairsep, pairseplen);
if (pair == NULL) {
mv_t key = mv_from_int(i);
mv_t val = mv_ref_type_infer_string(left);
mlhmmv_level_put_terminal_singly_keyed(map.pnext_level, &key, &val);
} else {
char* right = mlr_strmsep(&pair, pairsep, pairseplen);
mv_t key = mv_from_string(left, NO_FREE);
mv_t val = mv_ref_type_infer_string(right);
mlhmmv_level_put_terminal_singly_keyed(map.pnext_level, &key, &val);
}
i++;
}
free(input);
if (pstringval->is_ephemeral)
mlhmmv_xvalue_free(&pstringval->xval);
if (ppairsepval->is_ephemeral)
mlhmmv_xvalue_free(&ppairsepval->xval);
if (plistsepval->is_ephemeral)
mlhmmv_xvalue_free(&plistsepval->xval);
return box_ephemeral_xval(map);
}
// ----------------------------------------------------------------
#define SB_JOIN_ALLOC_SIZE 128
// ----------------------------------------------------------------
// Precondition: the caller has ensured that the separator is a string-valued terminal.
boxed_xval_t s_ms_joink_xfunc(boxed_xval_t* pmapval, boxed_xval_t* psepval) {
if (pmapval->xval.is_terminal) {
return box_ephemeral_val(mv_absent());
}
string_builder_t* psb = sb_alloc(SB_JOIN_ALLOC_SIZE);
for (mlhmmv_level_entry_t* pentry = pmapval->xval.pnext_level->phead; pentry != NULL; pentry = pentry->pnext) {
// The string_builder object will copy the string so we can point into source string space,
// without a copy here, when possible.
char free_flags = 0;
char* sval = mv_maybe_alloc_format_val(&pentry->level_key, &free_flags);
sb_append_string(psb, sval);
if (free_flags)
free(sval);
if (pentry->pnext != NULL) {
sb_append_string(psb, psepval->xval.terminal_mlrval.u.strv);
}
}
char* sretval = sb_finish(psb);
sb_free(psb);
if (pmapval->is_ephemeral)
mlhmmv_xvalue_free(&pmapval->xval);
if (psepval->is_ephemeral)
mlhmmv_xvalue_free(&psepval->xval);
return box_ephemeral_val(
mv_from_string(sretval, FREE_ENTRY_VALUE)
);
}
// ----------------------------------------------------------------
// Precondition: the caller has ensured that the separator is a string-valued terminal.
boxed_xval_t s_ms_joinv_xfunc(boxed_xval_t* pmapval, boxed_xval_t* psepval) {
if (pmapval->xval.is_terminal) {
return box_ephemeral_val(mv_absent());
}
string_builder_t* psb = sb_alloc(SB_JOIN_ALLOC_SIZE);
for (mlhmmv_level_entry_t* pentry = pmapval->xval.pnext_level->phead; pentry != NULL; pentry = pentry->pnext) {
if (pentry->level_xvalue.is_terminal) {
// The string_builder object will copy the string so we can point into source string space,
// without a copy here, when possible.
char free_flags = 0;
char* sval = mv_maybe_alloc_format_val(&pentry->level_xvalue.terminal_mlrval, &free_flags);
sb_append_string(psb, sval);
if (free_flags)
free(sval);
if (pentry->pnext != NULL) {
sb_append_string(psb, psepval->xval.terminal_mlrval.u.strv);
}
}
}
char* sretval = sb_finish(psb);
sb_free(psb);
if (pmapval->is_ephemeral)
mlhmmv_xvalue_free(&pmapval->xval);
if (psepval->is_ephemeral)
mlhmmv_xvalue_free(&psepval->xval);
return box_ephemeral_val(
mv_from_string(sretval, FREE_ENTRY_VALUE)
);
}
// ----------------------------------------------------------------
// Precondition: the caller has ensured that the separators are string-valued terminals.
boxed_xval_t s_mss_joinkv_xfunc(boxed_xval_t* pmapval, boxed_xval_t* ppairsepval, boxed_xval_t* plistsepval) {
if (pmapval->xval.is_terminal) {
return box_ephemeral_val(mv_absent());
}
string_builder_t* psb = sb_alloc(SB_JOIN_ALLOC_SIZE);
for (mlhmmv_level_entry_t* pentry = pmapval->xval.pnext_level->phead; pentry != NULL; pentry = pentry->pnext) {
if (pentry->level_xvalue.is_terminal) {
// The string_builder object will copy the string so we can point into source string space,
// without a copy here, when possible.
char kfree_flags = 0;
char* skval = mv_maybe_alloc_format_val(&pentry->level_key, &kfree_flags);
sb_append_string(psb, skval);
if (kfree_flags)
free(skval);
sb_append_string(psb, ppairsepval->xval.terminal_mlrval.u.strv);
char vfree_flags = 0;
char* svval = mv_maybe_alloc_format_val(&pentry->level_xvalue.terminal_mlrval, &vfree_flags);
sb_append_string(psb, svval);
if (vfree_flags)
free(svval);
if (pentry->pnext != NULL) {
sb_append_string(psb, plistsepval->xval.terminal_mlrval.u.strv);
}
}
}
char* sretval = sb_finish(psb);
sb_free(psb);
if (pmapval->is_ephemeral)
mlhmmv_xvalue_free(&pmapval->xval);
if (ppairsepval->is_ephemeral)
mlhmmv_xvalue_free(&ppairsepval->xval);
if (plistsepval->is_ephemeral)
mlhmmv_xvalue_free(&plistsepval->xval);
return box_ephemeral_val(
mv_from_string(sretval, FREE_ENTRY_VALUE)
);
}