I am writing a thing to output something similar to JSON, from a perl structure. I want the quoting to behave like this:
"string" outputs "string"
"05" outputs "05"
"5" outputs "5"
5 outputs 5
05 outputs 5, or 05 would be acceptable
JSON::XS handles this by testing if a scalar has been "stringified" or not, which I think is very cool. But I can't find a way to do this test myself without writing XS, which I'd rather avoid. Is this possible? I can't find this anywhere on CPAN without finding vast pedantry about Scalar::Util::looks_like_number, etc which completely isn't what I want. The only stopgap I can find is Devel::Peek, which feels evil. And also, just like JSON::XS, I'm fine with this secenario:
my $a = 5;
print $a."\n";
# now $a outputs "5" instead of 5)
Inspect the output of B::svref_2object
:
use B;
($x, $y, $z) = ("5", 5, 5.0);
print ref(B::svref_2object( \$x )), "\n";
print ref(B::svref_2object( \$y )), "\n";
print ref(B::svref_2object( \$z )), "\n";
Output:
B::PV
B::IV
B::NV
Or, as ikegami suggests, if you'd rather lookup the pPOK
flag:
if (B::svref_2object( \$x )->FLAGS & B::SVp_POK) {
print "I guess \$x is stringy\n";
}
You can make it so a number no longer appears to have been stringified by using the following:
$x = 0+$x;
For example,
$ perl -MJSON::XS -E'
$_ = 4;
say encode_json([$_]); # [4]
"".$_;
say encode_json([$_]); # ["4"]
$_ = 0 + $_;
say encode_json([$_]); # [4]
'
Detecting whether something has been stringified is tougher because JSON::XS is looking into Perl internals. One could use the following:
sub is_stringy {
{ no warnings 'void'; "".$_[0]; }
return 1;
}
but I don't think that's what you want :) I don't know how to detect the "corruption" without writing some XS code. What you want to know is if SvPOKp
is true for the scalar (after you call SvGETMAGIC
on the scalar).
use Inline C => <<'__EOI__';
SV* is_stringy(SV* sv) {
SvGETMAGIC(sv);
return SvPOKp(sv) ? &PL_sv_yes : &PL_sv_no;
}
__EOI__
$_ = 4;
say is_stringy($_) ?1:0; # 0
{ no warnings 'void'; "".$_; }
say is_stringy($_) ?1:0; # 1
$_ = 0+$_;
say is_stringy($_) ?1:0; # 0
oo! It turns out that B does provide SVp_POK
, so it can (almost) be done in without writing new XS code
use B qw( svref_2object SVp_POK );
sub is_stringy {
my ($s) = @_;
my $sv = svref_2object(\$s);
#$sv->GETMAGIC(); # Not available
return $sv->FLAGS & SVp_POK;
}
Being unable to call SvGETMAGIC
has drawbacks, but it will work almost all of the time.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With