[Boston.pm] converting a thing to a number

email-2Ro/Dj86MvDSUeElwK9/[email protected] Tue, 22 Feb 2022 11:12:15 -0600
Newsgroups gmane.comp.lang.perl.perl-mongers.boston
Message-ID <[email protected]>
ok, so, I need to read in some numbers from a file.
Numbers need to allow integer, float, signed/unsigned,
as well as supporting decimal, hex, binary, octal.

Extra level of annoyance: using modules from cpan is difficult.
So, if it can be pure perl, all the better.

What I came up with was this:


our $TEMPVAL;

sub convert_thing_to_numeric{
	my ($orig_val)=@_;

	if($orig_val =~ m{[\'\"]}){  # " '
		confess "Error: to_numeric cannot handle input strings with quotes ";
	}


	$TEMPVAL = undef;

	my $evalstring = "use warnings FATAL => 'all'; \$TEMPVAL = $orig_val ; 
\$TEMPVAL = \$TEMPVAL + 0;";

	print ":DEBUG: to_numeric($orig_val) evalstring is '$evalstring' \n" 
if($DEBUG);

	eval($evalstring);

	if($@){
		confess "Error: to_numeric() could not convert input string 
'$orig_val' ";
	}

	unless(defined($TEMPVAL)){
		confess "Error: to_numeric() ended up with undefined value? ";
	}

	return $retval;
}

so the idea is use perl string eval to read the numerical thing as a 
perl numeric literal.
This will allow every format I need to support.


if $orig_val is any of these:
534
-234
4345.652
-256.234
0xbeef
0b1010101

then the eval string turns into
$TEMPVAL = 534;
$TEMPVAL = -234;
$TEMPVAL = 4345.625;
$TEMPVAL = -256.234;
$TEMPVAL = 0xbeef;
$TEMPVAL = 0b1010101;

And eval() will correctly convert the thing into a number and store it 
in TEMPVAL.

The last part of eval string adds zero to TEMPVAL
$TEMPVAL = $TEMPVAL + 0;

This will force perl to numify TEMPVAL.
If the user had a bad number in the original file, such as '0xdeadybeef'
then TEMPVAL will start out a string, and then adding zero will force 
perl to try to convert it into a number.

Normally, if perl can't turn the thing into a number to add zero,
it only throws a WARNING, which doesn't show up in $@ after an eval 
block.
so, the first part of the eval string is to force all warnings into 
fatals.
use warnings FATAL => 'all';

The only thing that concerns me a bit is that using string eval() means
the file could contain code I don't want to execute. and I can't figure 
out
an easy way to prevent that.

I have a check for quotation marks in the argument.
I could add a check for parens, but that would only catch some function 
calls,
not all, and would prevent legitimate users from using mathematical 
parens.
and a bad string might have a function call with no parens.

Any suggestions on how to put some checks in here to keep things 
safe-ish?

The file being read is local, mostly under our control, so its not like
I have to worry about reading strings passed in by random users from a 
website.
Its more a matter of catching mistakes and typos that end up doing weird 
things
without emitting any warning.