/* vim:set ts=4 sw=4 et syntax=c.doxygen: */
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#include "ppport.h"
#include "convert.h"
static SV *
_convert_struct(const ps_node *node, enum type_preference prefer, const char *prefix)
{
SV *result = NULL;
const union nodeval *v = &node->val;
const struct array *what =
node->type == NODE_OBJECT
? &node->val.o.val
: &node->val.a;
if (node->type != NODE_OBJECT && what->len == 0 && prefer == PREFER_UNDEF) {
result = newSVsv(&PL_sv_undef);
} else {
SV *a = NULL;
if (prefer == PREFER_HASH || !what->is_array) {
// len == 0 could be hash still
a = (SV*)newHV();
for (int i = 0; i < what->len; i++) {
STRLEN len;
char *key = SvPV(sv_2mortal(_convert_recurse(what->pairs[i].key, prefer, prefix)), len);
SV *val = _convert_recurse(what->pairs[i].val, prefer, prefix);
hv_store((HV*)a, key, len, val, 0);
}
} else {
a = (SV*)newAV();
av_extend((AV*)a, what->len - 1);
for (int i = 0; i < what->len; i++)
av_push((AV*)a, _convert_recurse(what->pairs[i].val, prefer, prefix));
}
result = newRV_noinc(a);
if (node->type == NODE_OBJECT) {
char *typename = v->o.type;
if (prefix) {
size_t size = strlen(prefix) + sizeof "::" + strlen(typename) + sizeof "\0";
char *built = malloc(size);;
snprintf(built, size, "%s::%s", prefix, typename);
sv_bless(result, gv_stashpv(built, true));
free(built);
} else {
sv_bless(result, gv_stashpv(typename, true));
}
}
}
return result;
}
SV *
_convert_recurse(const ps_node *node, enum type_preference prefer, const char *prefix)
{
SV *result = NULL;
const union nodeval *v = &node->val;
switch (node->type) {
case NODE_STRING: result = newSVpv(v->s.val, v->s.len); break;
case NODE_INT: result = newSViv(v->i); break;
case NODE_FLOAT: result = newSVnv(v->d); break;
case NODE_BOOL: result = newSVsv(v->b ? &PL_sv_yes : &PL_sv_no); break;
case NODE_NULL: result = newSVsv(&PL_sv_undef); break;
case NODE_OBJECT: /* FALLTHROUGH */
case NODE_ARRAY: result = _convert_struct(node, prefer, prefix); break;
}
return result;
}