relax numeric conversions from string

The spec does not establish any particular constraints to observe in
casting from `YARN` to `NUMB(A)R`. Presently, `lci` checks that the
string consists only of numeric characters prior to attempting the
conversion, halting with an error if it's found not to be the case.

This behavior is often more inconvenient than helpful. Many numeric
strings encountered in the wild are "roughly numeric", and it would be
wise to account for this observation. As a simple example, a user may
be prompted to input a number; in the case of their inadvertently
providing leading or trailing whitespace, a naive program will crash
rather than gracefully extracting the otherwise sensible input.

This patch removes the `isDecString()` function and instead leverages
the `strtoll()` and `strtof()` functions to handle casts from `YARN`
to `NUMBR` and `NUMBAR`, respectively. Thus, strings to be converted
are permitted to contain leading whitespace, and trailing non-numeric
characters are ignored. Additionally, `YARN`s to be cast to `NUMBR`
may lead with `"0"` or `"0x"` to indicate that the string should be
interpreted as an octal or hexadecimal value, respectively.

This change required the modification of several tests which previously
checked that casting an empty or completely non-numeric `YARN` resulted
in an error. These now verify that such a conversion results in a zero
of the appropriate type.
This commit is contained in:
D.E. Akers 2015-08-15 19:43:05 -04:00 committed by Justin Meza
parent f1881ad63f
commit f5f5edb889
62 changed files with 101 additions and 89 deletions

View File

@ -20,34 +20,6 @@ char *copyString(char *data)
return p;
}
/**
* Checks if a string follows the format of a decimal number.
*
* \param [in] data The string to check the format of.
*
* \retval 0 The string is not a decimal number.
*
* \retval 1 The string is a decimal number.
*/
unsigned int isDecString(const char *data)
{
size_t n;
size_t len = strlen(data);
/* Check for an empty string */
if (len == 0) return 0;
/* Check for non-digit, non-hyphen, and non-period characters */
for (n = 0; n < len; n++) {
if (!isdigit(data[n])
&& data[n] != '.'
&& data[n] != '-')
return 0;
}
return 1;
}
/**
* Checks if a string follows the format of a hexadecimal number.
*
@ -1338,32 +1310,14 @@ ValueObject *castIntegerExplicit(ValueObject *node,
/* Perform interpolation */
ValueObject *ret = NULL;
ValueObject *interp = castStringExplicit(node, scope);
long long value;
if (!interp) return NULL;
if (!isDecString(getString(interp))) {
error(IN_UNABLE_TO_CAST_VALUE);
deleteValueObject(interp);
return NULL;
}
if (sscanf(getString(interp), "%lli", &value) != 1) {
error(IN_EXPECTED_INTEGER_VALUE);
deleteValueObject(interp);
return NULL;
}
long long value = strtoll(getString(interp), NULL, 0);
ret = createIntegerValueObject(value);
deleteValueObject(interp);
return ret;
}
else {
long long value;
if (!isDecString(getString(node))) {
error(IN_UNABLE_TO_CAST_VALUE);
return NULL;
}
if (sscanf(getString(node), "%lli", &value) != 1) {
error(IN_EXPECTED_INTEGER_VALUE);
return NULL;
}
long long value = strtoll(getString(node), NULL, 0);
return createIntegerValueObject(value);
}
case VT_FUNC:
@ -1409,32 +1363,14 @@ ValueObject *castFloatExplicit(ValueObject *node,
/* Perform interpolation */
ValueObject *ret = NULL;
ValueObject *interp = castStringExplicit(node, scope);
float value;
if (!interp) return NULL;
if (!isDecString(getString(interp))) {
error(IN_UNABLE_TO_CAST_VALUE);
deleteValueObject(interp);
return NULL;
}
if (sscanf(getString(interp), "%f", &value) != 1) {
error(IN_EXPECTED_DECIMAL);
deleteValueObject(interp);
return NULL;
}
float value = strtof(getString(interp), NULL);
ret = createFloatValueObject(value);
deleteValueObject(interp);
return ret;
}
else {
float value;
if (!isDecString(getString(node))) {
error(IN_UNABLE_TO_CAST_VALUE);
return NULL;
}
if (sscanf(getString(node), "%f", &value) != 1) {
error(IN_EXPECTED_DECIMAL);
return NULL;
}
float value = strtof(getString(node), NULL);
return createFloatValueObject(value);
}
case VT_FUNC:

View File

@ -133,7 +133,6 @@ typedef struct scopeobject {
/**@{*/
void printInterpreterError(const char *, IdentifierNode *, ScopeObject *);
char *copyString(char *);
unsigned int isDecString(const char *);
unsigned int isHexString(const char *);
char *resolveIdentifierName(IdentifierNode *, ScopeObject *);
int resolveTerminalSlot(ScopeObject *, ScopeObject *, IdentifierNode *, ScopeObject **, IdentifierNode **);

View File

@ -1,2 +1,2 @@
INCLUDE(AddLolTest)
ADD_LOL_TEST(2-EmptyString ERROR)
ADD_LOL_TEST(2-EmptyString OUTPUT test.out)

View File

@ -1,3 +1,3 @@
HAI 1.3
MAEK "" A NUMBR
VISIBLE MAEK "" A NUMBR
KTHXBYE

View File

@ -1,2 +1,2 @@
This test checks to make sure explicitly casting an empty string to an integer
type results in an error.
type results in 0.

View File

@ -1,2 +1,2 @@
INCLUDE(AddLolTest)
ADD_LOL_TEST(3-NonNumber ERROR)
ADD_LOL_TEST(3-NonNumber OUTPUT test.out)

View File

@ -1,3 +1,3 @@
HAI 1.3
MAEK "abc" A NUMBR
VISIBLE MAEK "abc" A NUMBR
KTHXBYE

View File

@ -1,2 +1,2 @@
This test checks to make sure explicitly casting a non-number string to an
integer type results in an error.
integer type results in 0.

View File

@ -0,0 +1,2 @@
INCLUDE(AddLolTest)
ADD_LOL_TEST(4-RelaxedNumbers OUTPUT test.out)

View File

@ -0,0 +1,6 @@
HAI 1.3
VISIBLE MAEK " 123" A NUMBR BTW leading whitespace is ignored
VISIBLE MAEK "123x" A NUMBR BTW parsing stops at the first non-digit
VISIBLE MAEK "0377" A NUMBR BTW octal prefix
VISIBLE MAEK "0xFF" A NUMBR BTW hexadecimal prefix
KTHXBYE

View File

@ -0,0 +1,2 @@
This test checks that the explicit cast operator correctly casts string values
to integer types as would be done by libc's strtoll() function.

View File

@ -1,3 +1,4 @@
add_subdirectory(1-Numbers)
add_subdirectory(2-EmptyString)
add_subdirectory(3-NonNumber)
add_subdirectory(4-RelaxedNumbers)

View File

@ -1,2 +1,2 @@
INCLUDE(AddLolTest)
ADD_LOL_TEST(2-EmptyString ERROR)
ADD_LOL_TEST(2-EmptyString OUTPUT test.out)

View File

@ -1,3 +1,3 @@
HAI 1.3
MAEK "" A NUMBAR
VISIBLE MAEK "" A NUMBAR
KTHXBYE

View File

@ -1,2 +1,2 @@
This test checks to make sure explicitly casting an empty string to a floating
point decimal type results in an error.
point decimal type results in 0.00.

View File

@ -1,2 +1,2 @@
INCLUDE(AddLolTest)
ADD_LOL_TEST(3-NonNumber ERROR)
ADD_LOL_TEST(3-NonNumber OUTPUT test.out)

View File

@ -1,3 +1,3 @@
HAI 1.3
MAEK "abc" A NUMBAR
VISIBLE MAEK "abc" A NUMBAR
KTHXBYE

View File

@ -1,2 +1,2 @@
This test checks to make sure explicitly casting a non-number string to a
floating point decimal type results in an error.
floating point decimal type results in 0.00.

View File

@ -0,0 +1,2 @@
INCLUDE(AddLolTest)
ADD_LOL_TEST(4-RelaxedNumbers OUTPUT test.out)

View File

@ -0,0 +1,4 @@
HAI 1.3
VISIBLE MAEK " 1.23" A NUMBAR BTW leading whitespace is ignored
VISIBLE MAEK "1.23x" A NUMBAR BTW parsing stops at the first non-digit
KTHXBYE

View File

@ -0,0 +1,2 @@
This test checks that the explicit cast operator correctly casts string values
to floating point decimal types as would be done by libc's strtof() function.

View File

@ -1,3 +1,4 @@
add_subdirectory(1-Numbers)
add_subdirectory(2-EmptyString)
add_subdirectory(3-NonNumber)
add_subdirectory(4-RelaxedNumbers)

View File

@ -1,2 +1,2 @@
INCLUDE(AddLolTest)
ADD_LOL_TEST(2-EmptyString ERROR)
ADD_LOL_TEST(2-EmptyString OUTPUT test.out)

View File

@ -1,4 +1,5 @@
HAI 1.3
I HAS A var ITZ ""
var IS NOW A NUMBR
VISIBLE var
KTHXBYE

View File

@ -1,2 +1,2 @@
This test checks to make sure explicitly recasting an empty string to an
integer type results in an error.
integer type results in 0.

View File

@ -1,2 +1,2 @@
INCLUDE(AddLolTest)
ADD_LOL_TEST(3-NonNumber ERROR)
ADD_LOL_TEST(3-NonNumber OUTPUT test.out)

View File

@ -1,4 +1,5 @@
HAI 1.3
I HAS A var ITZ "abc"
var IS NOW A NUMBR
VISIBLE var
KTHXBYE

View File

@ -1,2 +1,2 @@
This test checks to make sure explicitly recasting a non-number string to an
integer type results in an error.
integer type results in 0.

View File

@ -0,0 +1,2 @@
INCLUDE(AddLolTest)
ADD_LOL_TEST(4-RelaxedNumbers OUTPUT test.out)

View File

@ -0,0 +1,14 @@
HAI 1.3
I HAS A var1 ITZ " 123" BTW leading whitespace is ignored
I HAS A var2 ITZ "123x" BTW parsing stops at the first non-digit
I HAS A var3 ITZ "0377" BTW octal prefix
I HAS A var4 ITZ "0xFF" BTW hexadecimal prefix
var1 IS NOW A NUMBR
var2 IS NOW A NUMBR
var3 IS NOW A NUMBR
var4 IS NOW A NUMBR
VISIBLE var1
VISIBLE var2
VISIBLE var3
VISIBLE var4
KTHXBYE

View File

@ -0,0 +1,2 @@
This test checks that the explicit recast operator correctly casts string
values to integer types as would be done by libc's strtoll() function.

View File

@ -1,3 +1,4 @@
add_subdirectory(1-Numbers)
add_subdirectory(2-EmptyString)
add_subdirectory(3-NonNumber)
add_subdirectory(4-RelaxedNumbers)

View File

@ -1,2 +1,2 @@
INCLUDE(AddLolTest)
ADD_LOL_TEST(2-EmptyString ERROR)
ADD_LOL_TEST(2-EmptyString OUTPUT test.out)

View File

@ -1,4 +1,5 @@
HAI 1.3
I HAS A var ITZ ""
var IS NOW A NUMBAR
VISIBLE var
KTHXBYE

View File

@ -1,2 +1,2 @@
This test checks to make sure explicitly recasting an empty string to a
floating point decimal type results in an error.
floating point decimal type results in 0.00.

View File

@ -1,2 +1,2 @@
INCLUDE(AddLolTest)
ADD_LOL_TEST(3-NonNumber ERROR)
ADD_LOL_TEST(3-NonNumber OUTPUT test.out)

View File

@ -1,4 +1,5 @@
HAI 1.3
I HAS A var ITZ "abc"
var IS NOW A NUMBAR
VISIBLE var
KTHXBYE

View File

@ -1,2 +1,2 @@
This test checks to make sure explicitly recasting a non-number string to a
floating point decimal type results in an error.
floating point decimal type results in 0.00.

View File

@ -0,0 +1,2 @@
INCLUDE(AddLolTest)
ADD_LOL_TEST(4-RelaxedNumbers OUTPUT test.out)

View File

@ -0,0 +1,8 @@
HAI 1.3
I HAS A var1 ITZ " 1.23" BTW leading whitespace is ignored
I HAS A var2 ITZ "1.23x" BTW parsing stops at the first non-digit
var1 IS NOW A NUMBAR
var2 IS NOW A NUMBAR
VISIBLE var1
VISIBLE var2
KTHXBYE

View File

@ -0,0 +1,3 @@
This test checks that the explicit recast operator correctly casts string
values to floating point decimal types as would be done by libc's
strtof() function.

View File

@ -1,3 +1,4 @@
add_subdirectory(1-Numbers)
add_subdirectory(2-EmptyString)
add_subdirectory(3-NonNumber)
add_subdirectory(4-RelaxedNumbers)