Browse Source

Merge pull request #183 from hallzy/loop_string

Added a string iterator and loop function.
Marco Bambini 8 years ago
parent
commit
981b8bb536

+ 20 - 2
docs/loops.html

@@ -116,10 +116,10 @@
 	}
 	}
 	return count;
 	return count;
 			</code></pre>
 			</code></pre>
-			<p>The for in loop can be used over any object that supports iteration, such as <a href="lists.html">Lists</a> or <a href="maps.html">Maps</a>.</p>
+			<p>The for in loop can be used over any object that supports iteration, such as <a href="lists.html">Lists</a>, Strings or <a href="maps.html">Maps</a>.</p>
          	
          	
          	<h4 class="section-h4">Loop method</h4>
          	<h4 class="section-h4">Loop method</h4>
-         	<p>Performing a loop is very common operation in any programming language, so Gravity adds a very convenient way to run a loop by adding a special loop method to some classes (Int, Range, List and Map) that accepts a <a href="closures.html">closure</a> as parameter:</p>
+         	<p>Performing a loop is very common operation in any programming language, so Gravity adds a very convenient way to run a loop by adding a special loop method to some classes (Int, Range, List, String and Map) that accepts a <a href="closures.html">closure</a> as parameter:</p>
          	<pre><code class="swift">
          	<pre><code class="swift">
 	func main() {
 	func main() {
 		4.loop({System.print("Hello World");});
 		4.loop({System.print("Hello World");});
@@ -161,6 +161,24 @@
 		var target = [10,20,30,40,50,60,70,80,90];
 		var target = [10,20,30,40,50,60,70,80,90];
 		target.loop(func (value){System.print("Hello World " + value);});
 		target.loop(func (value){System.print("Hello World " + value);});
 	}
 	}
+			</code></pre>
+			<p>Loop within a String:</p>
+         	<pre><code class="swift">
+        func main() {
+                var s = "abcdefghijklmnopqrstuvwxyz";
+
+                var vowels = ""
+                s.loop(func (c) {
+                        if (c == "a" or
+                            c == "e" or
+                            c == "i" or
+                            c == "o" or
+                            c == "u") {
+                                vowels += c;
+                        }
+                })
+                System.print(vowels)  // aeiou
+        }
 			</code></pre>
 			</code></pre>
 			<p>Loop within a <a href="maps.html">Map</a> where key is passed as closure argument (please note that key order is not preserved):</p>
 			<p>Loop within a <a href="maps.html">Map</a> where key is passed as closure argument (please note that key order is not preserved):</p>
          	<pre><code class="swift">
          	<pre><code class="swift">

+ 59 - 0
src/runtime/gravity_core.c

@@ -2058,6 +2058,62 @@ static bool string_split (gravity_vm *vm, gravity_value_t *args, uint16_t nargs,
 	RETURN_VALUE(VALUE_FROM_OBJECT(list), rindex);
 	RETURN_VALUE(VALUE_FROM_OBJECT(list), rindex);
 }
 }
 
 
+static bool string_loop (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex) {
+	if (nargs < 2) RETURN_ERROR("Incorrect number of arguments.");
+	if (!VALUE_ISA_CLOSURE(GET_VALUE(1))) RETURN_ERROR("Argument must be a Closure.");
+
+	gravity_closure_t *closure = VALUE_AS_CLOSURE(GET_VALUE(1));	// closure to execute
+	gravity_value_t value = GET_VALUE(0);							// self parameter
+	gravity_string_t *string = VALUE_AS_STRING(value);
+	char *str = string->s;
+	register gravity_int_t n = string->len;  // Times to execute the loop
+	register gravity_int_t i = 0;
+
+	nanotime_t t1 = nanotime();
+	while (i < n) {
+		gravity_value_t v_str = VALUE_FROM_STRING(vm, str + i, 1);
+		if (!gravity_vm_runclosure(vm, closure, value, &v_str, 1)) return false;
+		++i;
+	}
+	nanotime_t t2 = nanotime();
+	RETURN_VALUE(VALUE_FROM_INT(t2-t1), rindex);
+}
+
+static bool string_iterator (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex) {
+	#pragma unused(vm, nargs)
+	gravity_string_t *string = VALUE_AS_STRING(GET_VALUE(0));
+
+	// check for empty string first
+	if (string->len == 0) RETURN_VALUE(VALUE_FROM_FALSE, rindex);
+
+	// check for start of iteration
+	if (VALUE_ISA_NULL(GET_VALUE(1))) RETURN_VALUE(VALUE_FROM_INT(0), rindex);
+
+	// extract value
+	gravity_value_t value = GET_VALUE(1);
+
+	// check error condition
+	if (!VALUE_ISA_INT(value)) RETURN_ERROR("Iterator expects a numeric value here.");
+
+	// compute new value
+	gravity_int_t n = value.n;
+	if (n+1 < string->len) {
+		++n;
+	} else {
+		RETURN_VALUE(VALUE_FROM_FALSE, rindex);
+	}
+
+	// return new iterator
+	RETURN_VALUE(VALUE_FROM_INT(n), rindex);
+}
+
+static bool string_iterator_next (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex) {
+	#pragma unused(vm, nargs)
+	gravity_string_t	*string = VALUE_AS_STRING(GET_VALUE(0));
+	register int32_t index = (int32_t)VALUE_AS_INT(GET_VALUE(1));
+	RETURN_VALUE(VALUE_FROM_STRING(vm, string->s + index, 1), rindex);
+}
+
 static bool string_exec (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex) {
 static bool string_exec (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, uint32_t rindex) {
     if (nargs != 2) RETURN_ERROR("A single argument is expected in String casting.");
     if (nargs != 2) RETURN_ERROR("A single argument is expected in String casting.");
     
     
@@ -2542,6 +2598,9 @@ static void gravity_core_init (void) {
 	gravity_class_bind(gravity_class_string, "upper", NEW_CLOSURE_VALUE(string_upper));
 	gravity_class_bind(gravity_class_string, "upper", NEW_CLOSURE_VALUE(string_upper));
 	gravity_class_bind(gravity_class_string, "lower", NEW_CLOSURE_VALUE(string_lower));
 	gravity_class_bind(gravity_class_string, "lower", NEW_CLOSURE_VALUE(string_lower));
 	gravity_class_bind(gravity_class_string, "split", NEW_CLOSURE_VALUE(string_split));
 	gravity_class_bind(gravity_class_string, "split", NEW_CLOSURE_VALUE(string_split));
+	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));
     // Meta
     // Meta
     gravity_class_t *string_meta = gravity_class_get_meta(gravity_class_string);
     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));
     gravity_class_bind(string_meta, GRAVITY_INTERNAL_EXEC_NAME, NEW_CLOSURE_VALUE(string_exec));

+ 21 - 0
test/unittest/string/for_in.gravity

@@ -0,0 +1,21 @@
+#unittest {
+	name: "for() for string";
+	error: NONE;
+	result: "aeiouAEIOU";
+};
+
+func main () {
+	var s = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
+
+	var ret = ""
+	for (var c in s) {
+		if (c == "a" or c == "A" or
+				c == "e" or c == "E" or
+				c == "i" or c == "I" or
+				c == "o" or c == "O" or
+				c == "u" or c == "U") {
+			ret += c;
+		}
+	}
+	return ret;
+}

+ 21 - 0
test/unittest/string/loop.gravity

@@ -0,0 +1,21 @@
+#unittest {
+	name: "loop() for string";
+	error: NONE;
+	result: "aeiouAEIOU";
+};
+
+func main () {
+	var s = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
+
+	var ret = ""
+	s.loop(func (c) {
+		if (c == "a" or c == "A" or
+				c == "e" or c == "E" or
+				c == "i" or c == "I" or
+				c == "o" or c == "O" or
+				c == "u" or c == "U") {
+			ret += c;
+		}
+	})
+	return ret;
+}