/* neat.c */
#include "data-util.h"
#define PV_LIMIT 20
static int
is_identifier_cstr(const char* pv, const STRLEN len){
if(isIDFIRST(*pv)){
const char* const end = pv + len - 1 /* '\0' */;
while(pv != end){
++pv;
if(!isALNUM(*pv)){
return FALSE;
}
}
return TRUE;
}
return FALSE;
}
static void
du_neat_cat(pTHX_ SV* const dsv, SV* x, const int level){
if(level > 2){
sv_catpvs(dsv, "...");
return;
}
if(SvRXOK(x)){ /* regex */
Perl_sv_catpvf(aTHX_ dsv, "qr{%"SVf"}", x);
return;
}
else if(SvROK(x)){
x = SvRV(x);
if(SvOBJECT(x)){
Perl_sv_catpvf(aTHX_ dsv, "%s=%s(0x%p)",
sv_reftype(x, TRUE), sv_reftype(x, FALSE), x);
return;
}
else if(SvTYPE(x) == SVt_PVAV){
I32 const len = av_len((AV*)x);
sv_catpvs(dsv, "[");
if(len >= 0){
SV** const svp = av_fetch((AV*)x, 0, FALSE);
if(*svp){
du_neat_cat(aTHX_ dsv, *svp, level+1);
}
else{
sv_catpvs(dsv, "undef");
}
if(len > 0){
sv_catpvs(dsv, ", ...");
}
}
sv_catpvs(dsv, "]");
}
else if(SvTYPE(x) == SVt_PVHV){
I32 klen;
char* key;
SV* val;
hv_iterinit((HV*)x);
val = hv_iternextsv((HV*)x, &key, &klen);
sv_catpvs(dsv, "{");
if(val){
if(!is_identifier_cstr(key, klen)){
SV* const sv = sv_newmortal();
key = pv_display(sv, key, klen, klen, PV_LIMIT);
}
Perl_sv_catpvf(aTHX_ dsv, "%s => ", key);
du_neat_cat(aTHX_ dsv, val, level+1);
if(hv_iternext((HV*)x)){
sv_catpvs(dsv, ", ...");
}
}
sv_catpvs(dsv, "}");
}
else if(SvTYPE(x) == SVt_PVCV){
GV* const gv = CvGV((CV*)x);
Perl_sv_catpvf(aTHX_ dsv, "\\&%s::%s(0x%p)", HvNAME(GvSTASH(gv)), GvNAME(gv), x);
}
else{
sv_catpvs(dsv, "\\");
du_neat_cat(aTHX_ dsv, x, level+1);
}
}
else if(isGV(x)){
sv_catsv(dsv, x);
}
else if(SvOK(x)){
if(SvPOKp(x)){
STRLEN cur;
char* const pv = SvPV(x, cur); /* pv_sisplay requires char*, not const char* */
SV* const sv = sv_newmortal();
pv_display(sv, pv, cur, cur, PV_LIMIT);
sv_catsv(dsv, sv);
}
else{
NV const nv = SvNV(x);
if(nv == NV_INF){
sv_catpvs(dsv, "+Inf");
}
else if(nv == -NV_INF){
sv_catpvs(dsv, "-Inf");
}
else if(Perl_isnan(nv)){
sv_catpvs(dsv, "NaN");
}
else{
Perl_sv_catpvf(aTHX_ dsv, "%"NVgf, nv);
}
}
}
else{
sv_catpvs(dsv, "undef");
}
}
const char*
du_neat(pTHX_ SV* x){
SV* const dsv = newSV(100);
sv_2mortal(dsv);
sv_setpvs(dsv, "");
ENTER;
SAVETMPS;
SvGETMAGIC(x);
du_neat_cat(aTHX_ dsv, x, 0);
FREETMPS;
LEAVE;
return SvPVX(dsv);
}