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:
parent
f1881ad63f
commit
f5f5edb889
|
@ -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:
|
||||
|
|
|
@ -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 **);
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
INCLUDE(AddLolTest)
|
||||
ADD_LOL_TEST(2-EmptyString ERROR)
|
||||
ADD_LOL_TEST(2-EmptyString OUTPUT test.out)
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
HAI 1.3
|
||||
MAEK "" A NUMBR
|
||||
VISIBLE MAEK "" A NUMBR
|
||||
KTHXBYE
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
0
|
|
@ -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.
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
INCLUDE(AddLolTest)
|
||||
ADD_LOL_TEST(3-NonNumber ERROR)
|
||||
ADD_LOL_TEST(3-NonNumber OUTPUT test.out)
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
HAI 1.3
|
||||
MAEK "abc" A NUMBR
|
||||
VISIBLE MAEK "abc" A NUMBR
|
||||
KTHXBYE
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
0
|
|
@ -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.
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
INCLUDE(AddLolTest)
|
||||
ADD_LOL_TEST(4-RelaxedNumbers OUTPUT test.out)
|
|
@ -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
|
|
@ -0,0 +1,4 @@
|
|||
123
|
||||
123
|
||||
255
|
||||
255
|
|
@ -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.
|
|
@ -1,3 +1,4 @@
|
|||
add_subdirectory(1-Numbers)
|
||||
add_subdirectory(2-EmptyString)
|
||||
add_subdirectory(3-NonNumber)
|
||||
add_subdirectory(4-RelaxedNumbers)
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
INCLUDE(AddLolTest)
|
||||
ADD_LOL_TEST(2-EmptyString ERROR)
|
||||
ADD_LOL_TEST(2-EmptyString OUTPUT test.out)
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
HAI 1.3
|
||||
MAEK "" A NUMBAR
|
||||
VISIBLE MAEK "" A NUMBAR
|
||||
KTHXBYE
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
0.00
|
|
@ -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.
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
INCLUDE(AddLolTest)
|
||||
ADD_LOL_TEST(3-NonNumber ERROR)
|
||||
ADD_LOL_TEST(3-NonNumber OUTPUT test.out)
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
HAI 1.3
|
||||
MAEK "abc" A NUMBAR
|
||||
VISIBLE MAEK "abc" A NUMBAR
|
||||
KTHXBYE
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
0.00
|
|
@ -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.
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
INCLUDE(AddLolTest)
|
||||
ADD_LOL_TEST(4-RelaxedNumbers OUTPUT test.out)
|
|
@ -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
|
|
@ -0,0 +1,2 @@
|
|||
1.23
|
||||
1.23
|
|
@ -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.
|
|
@ -1,3 +1,4 @@
|
|||
add_subdirectory(1-Numbers)
|
||||
add_subdirectory(2-EmptyString)
|
||||
add_subdirectory(3-NonNumber)
|
||||
add_subdirectory(4-RelaxedNumbers)
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
INCLUDE(AddLolTest)
|
||||
ADD_LOL_TEST(2-EmptyString ERROR)
|
||||
ADD_LOL_TEST(2-EmptyString OUTPUT test.out)
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
HAI 1.3
|
||||
I HAS A var ITZ ""
|
||||
var IS NOW A NUMBR
|
||||
VISIBLE var
|
||||
KTHXBYE
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
0
|
|
@ -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.
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
INCLUDE(AddLolTest)
|
||||
ADD_LOL_TEST(3-NonNumber ERROR)
|
||||
ADD_LOL_TEST(3-NonNumber OUTPUT test.out)
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
HAI 1.3
|
||||
I HAS A var ITZ "abc"
|
||||
var IS NOW A NUMBR
|
||||
VISIBLE var
|
||||
KTHXBYE
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
0
|
|
@ -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.
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
INCLUDE(AddLolTest)
|
||||
ADD_LOL_TEST(4-RelaxedNumbers OUTPUT test.out)
|
|
@ -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
|
|
@ -0,0 +1,4 @@
|
|||
123
|
||||
123
|
||||
255
|
||||
255
|
|
@ -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.
|
|
@ -1,3 +1,4 @@
|
|||
add_subdirectory(1-Numbers)
|
||||
add_subdirectory(2-EmptyString)
|
||||
add_subdirectory(3-NonNumber)
|
||||
add_subdirectory(4-RelaxedNumbers)
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
INCLUDE(AddLolTest)
|
||||
ADD_LOL_TEST(2-EmptyString ERROR)
|
||||
ADD_LOL_TEST(2-EmptyString OUTPUT test.out)
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
HAI 1.3
|
||||
I HAS A var ITZ ""
|
||||
var IS NOW A NUMBAR
|
||||
VISIBLE var
|
||||
KTHXBYE
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
0.00
|
|
@ -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.
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
INCLUDE(AddLolTest)
|
||||
ADD_LOL_TEST(3-NonNumber ERROR)
|
||||
ADD_LOL_TEST(3-NonNumber OUTPUT test.out)
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
HAI 1.3
|
||||
I HAS A var ITZ "abc"
|
||||
var IS NOW A NUMBAR
|
||||
VISIBLE var
|
||||
KTHXBYE
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
0.00
|
|
@ -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.
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
INCLUDE(AddLolTest)
|
||||
ADD_LOL_TEST(4-RelaxedNumbers OUTPUT test.out)
|
|
@ -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
|
|
@ -0,0 +1,2 @@
|
|||
1.23
|
||||
1.23
|
|
@ -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.
|
|
@ -1,3 +1,4 @@
|
|||
add_subdirectory(1-Numbers)
|
||||
add_subdirectory(2-EmptyString)
|
||||
add_subdirectory(3-NonNumber)
|
||||
add_subdirectory(4-RelaxedNumbers)
|
||||
|
|
Loading…
Reference in New Issue