Browse Source

Improved division by 0 check. Disabled automatic string to number conversion, the number() method must be explicitly called now. Unit test updated.

Marco Bambini 7 years ago
parent
commit
7918f1407f

+ 22 - 6
src/runtime/gravity_core.c

@@ -89,6 +89,12 @@ gravity_class_t *gravity_class_range;
 gravity_class_t *gravity_class_upvalue;
 gravity_class_t *gravity_class_system;
 
+typedef enum {
+    number_format_any,
+    number_format_int,
+    number_format_float
+} number_format_type;
+
 // MARK: - Utils -
 static void map_keys_array (gravity_hash_t *hashtable, gravity_value_t key, gravity_value_t value, void *data) {
     #pragma unused (hashtable, value)
@@ -98,9 +104,9 @@ static void map_keys_array (gravity_hash_t *hashtable, gravity_value_t key, grav
 
 // MARK: - Conversions -
 
-static gravity_value_t convert_string2number (gravity_string_t *string, bool float_preferred) {
+static gravity_value_t convert_string2number (gravity_string_t *string, number_format_type number_format) {
 	// empty string case
-	if (string->len == 0) return (float_preferred) ? VALUE_FROM_FLOAT(0.0) : VALUE_FROM_INT(0);
+	if (string->len == 0) return (number_format == number_format_float) ? VALUE_FROM_FLOAT(0.0) : VALUE_FROM_INT(0);
 
 	register const char *s = string->s;
 	register uint32_t len = string->len;
@@ -121,11 +127,14 @@ static gravity_value_t convert_string2number (gravity_string_t *string, bool flo
 		else if (c == 'O') n = number_from_oct(&s[2], len-2);
 		else if (c == 'X') n = number_from_hex(s, len);
 		if (sign == -1) n = -n;
-		return (float_preferred) ? VALUE_FROM_FLOAT((gravity_float_t)n) : VALUE_FROM_INT((gravity_int_t)n);
+		return (number_format == number_format_float) ? VALUE_FROM_FLOAT((gravity_float_t)n) : VALUE_FROM_INT((gravity_int_t)n);
 	}
 
+    // if dot character is contained into the string than force the float_preferred flag
+    if (number_format == number_format_any && (strchr(string->s, '.') != NULL)) number_format = number_format_float;
+    
 	// default case
-	return (float_preferred) ? VALUE_FROM_FLOAT(strtod(string->s, NULL)) : VALUE_FROM_INT(strtoll(string->s, NULL, 0));
+	return (number_format == number_format_float) ? VALUE_FROM_FLOAT(strtod(string->s, NULL)) : VALUE_FROM_INT(strtoll(string->s, NULL, 0));
 }
 
 static bool convert_object_int (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex) {
@@ -285,7 +294,7 @@ inline gravity_value_t convert_value2int (gravity_vm *vm, gravity_value_t v) {
 	if (VALUE_ISA_BOOL(v)) return VALUE_FROM_INT(v.n);
 	if (VALUE_ISA_NULL(v)) return VALUE_FROM_INT(0);
 	if (VALUE_ISA_UNDEFINED(v)) return VALUE_FROM_INT(0);
-	if (VALUE_ISA_STRING(v)) {return convert_string2number(VALUE_AS_STRING(v), false);}
+	if (VALUE_ISA_STRING(v)) return convert_string2number(VALUE_AS_STRING(v), number_format_int);
 
 	// check if class implements the Int method
 	gravity_closure_t *closure = gravity_vm_fastlookup(vm, gravity_value_getclass(v), GRAVITY_INT_INDEX);
@@ -308,7 +317,7 @@ inline gravity_value_t convert_value2float (gravity_vm *vm, gravity_value_t v) {
 	if (VALUE_ISA_BOOL(v)) return VALUE_FROM_FLOAT(v.n);
 	if (VALUE_ISA_NULL(v)) return VALUE_FROM_FLOAT(0);
 	if (VALUE_ISA_UNDEFINED(v)) return VALUE_FROM_FLOAT(0);
-	if (VALUE_ISA_STRING(v)) {return convert_string2number(VALUE_AS_STRING(v), true);}
+	if (VALUE_ISA_STRING(v)) return convert_string2number(VALUE_AS_STRING(v), number_format_float);
 
 	// check if class implements the Float method
 	gravity_closure_t *closure = gravity_vm_fastlookup(vm, gravity_value_getclass(v), GRAVITY_FLOAT_INDEX);
@@ -2033,6 +2042,12 @@ static bool string_repeat (gravity_vm *vm, gravity_value_t *args, uint16_t nargs
 	RETURN_VALUE(VALUE_FROM_OBJECT(s), rindex);
 }
 
+static bool string_number (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex) {
+    #pragma unused(nargs)
+    gravity_value_t value = convert_string2number(VALUE_AS_STRING(GET_VALUE(0)), number_format_any);
+    RETURN_VALUE(value, rindex);
+}
+
 static bool string_upper (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex) {
 	gravity_string_t *main_str = VALUE_AS_STRING(GET_VALUE(0));
 
@@ -2780,6 +2795,7 @@ static void gravity_core_init (void) {
 	gravity_class_bind(gravity_class_string, "loop", NEW_CLOSURE_VALUE(string_loop));
 	gravity_class_bind(gravity_class_string, "iterate", NEW_CLOSURE_VALUE(string_iterator));
 	gravity_class_bind(gravity_class_string, "next", NEW_CLOSURE_VALUE(string_iterator_next));
+    gravity_class_bind(gravity_class_string, "number", NEW_CLOSURE_VALUE(string_number));
     // Meta
     gravity_class_t *string_meta = gravity_class_get_meta(gravity_class_string);
     gravity_class_bind(string_meta, GRAVITY_INTERNAL_EXEC_NAME, NEW_CLOSURE_VALUE(string_exec));

+ 3 - 0
src/runtime/gravity_vm.c

@@ -809,7 +809,10 @@ static bool gravity_vm_exec (gravity_vm *vm) {
 
 				// check fast math operation first  (only in case of int and float)
 				// a special check macro is added in order to check for divide by zero cases
+                #pragma clang diagnostic push
+                #pragma clang diagnostic ignored "-Wdivision-by-zero"
 				CHECK_FAST_BINARY_MATH(r1, r2, r3, v2, v3, /, CHECK_ZERO(v3));
+                 #pragma clang diagnostic pop
 
 				// prepare function call for binary operation
 				PREPARE_FUNC_CALL2(closure, v2, v3, GRAVITY_DIV_INDEX, rwin);

+ 5 - 1
src/runtime/gravity_vmmacros.h

@@ -171,7 +171,7 @@
 #define DEFINE_INDEX_VARIABLE(_v,_r)				register gravity_value_t _v = (_r < MAX_REGISTERS) ? STACK_GET(_r) : VALUE_FROM_INT(_r-MAX_REGISTERS)
 
 #define NO_CHECK
-#define CHECK_ZERO(_v)								if ((VALUE_ISA_INT(_v) && (_v.n == 0)) || (VALUE_ISA_FLOAT(_v) && (_v.f == 0.0)))			\
+#define CHECK_ZERO(_v)								if ((VALUE_ISA_INT(_v) && (_v.n == 0)) || (VALUE_ISA_FLOAT(_v) && (_v.f == 0.0)) || (VALUE_ISA_NULL(_v))) \
 													RUNTIME_ERROR("Division by 0 error.")
 
 #define CHECK_FAST_BINARY_BOOL(r1,r2,r3,v2,v3,OP)	DEFINE_STACK_VARIABLE(v2,r2);																\
@@ -189,9 +189,13 @@
 													if (VALUE_ISA_INT(v2)) {																	\
 														if (VALUE_ISA_INT(v3)) FMATH_BIN_INT(r1, v2.n, v3.n, OP);								\
 														if (VALUE_ISA_FLOAT(v3)) FMATH_BIN_FLOAT(r1, v2.n, v3.f, OP);							\
+                                                        if (VALUE_ISA_NULL(v3)) FMATH_BIN_INT(r1, v2.n, 0, OP);                                 \
+                                                        if (VALUE_ISA_STRING(v3)) RUNTIME_ERROR("Right operand must be a number (use the number() method).");   \
 													} else if (VALUE_ISA_FLOAT(v2)) {															\
 														if (VALUE_ISA_FLOAT(v3)) FMATH_BIN_FLOAT(r1, v2.f, v3.f, OP);							\
 														if (VALUE_ISA_INT(v3)) FMATH_BIN_FLOAT(r1, v2.f, v3.n, OP);								\
+                                                        if (VALUE_ISA_NULL(v3)) FMATH_BIN_FLOAT(r1, v2.f, 0, OP);                               \
+                                                        if (VALUE_ISA_STRING(v3)) RUNTIME_ERROR("Right operand must be a number (use the number() method).");   \
 													}
 
 #define CHECK_FAST_UNARY_MATH(r1,r2,v2,OP)			DEFINE_STACK_VARIABLE(v2,r2);	\

+ 1 - 1
test/unittest/classes/classes_2.gravity

@@ -21,7 +21,7 @@ func main() {
 	var f1 = foo();
 	var b1 = bar(0);
 	var b2 = bar(4);
-	var b3 = bar("1-string");
+	var b3 = bar(1);
 	var r;
 	
 	r = b1.f1();	if (r != 0)		return "-1 (" + r + ")";

+ 1 - 1
test/unittest/expression/boolean/bool_expressions_3.gravity

@@ -6,7 +6,7 @@
 
 func main () {
     var bool_t = true;
-	var string = "hello";
+	var string = "hello".number();
 	
 	var r1 = bool_t + string + 1 + string + 1 + null;
     return r1;

+ 6 - 6
test/unittest/expression/math/sum_between_numbers_and_strings.gravity

@@ -1,16 +1,16 @@
 #unittest {
 	name: "Sum between numbers and strings and null;";
 	error: NONE;
-	result: 12.5;
+	result: 12.6;
 };
 
 func main() {
-	var string = "this is a string";
-	var string_int = "1";
-	var string_float = "1.1";
+	var string = "this is a string".number();
+	var string_int = "1".number();
+	var string_float = "1.1".number();
 	
 	var num_int_1 = 1 + string_int;								//  2	+
-	var num_int_2 = 1 + string_float;							//  2	+
+	var num_int_2 = 1 + string_float;							//  2.1	+
 	var num_int_3 = 1 + string;									//  1	+
 	var num_float_1 = 1.1 + string_int;							//  2.1	+
 	var num_float_2 = 1.1 + string_float;						//  2.2	+
@@ -19,5 +19,5 @@ func main() {
 	var	num_float_null = 1.1 + null;							//	1.1
 	return num_int_1 + num_int_2 + num_int_3 +					// ______
 		   num_float_1 + num_float_2 + num_float_3 +			//
-		   num_int_null + num_float_null;						// 12.5
+		   num_int_null + num_float_null;						// 12.6
  }

+ 3 - 3
test/unittest/string/string_to_number_conversion.gravity

@@ -5,9 +5,9 @@
 };
 
 func main() {
-	var str_hex_num = "-0x1a";
-	var str_dec_num = "-26";
-	var str_oct_num = "-0O32";
+	var str_hex_num = "-0x1a".number();
+	var str_dec_num = "-26".number();
+	var str_oct_num = "-0O32".number();
 	
 	var r;