added more examples
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Sat, 16 Jan 2021 02:15:38 +0000 (20:15 -0600)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Sat, 16 Jan 2021 02:15:38 +0000 (20:15 -0600)
core/map.onyx
examples/09_for_loops.onyx
examples/10_switch_statements.onyx
examples/11_map.onyx

index 5d05c0da2a485dffdf967372d1d0268edad4de20..1712c837858bb5d9f333ab8bc227402196126262 100644 (file)
@@ -18,11 +18,11 @@ MapEntry :: struct (K: type_expr, V: type_expr) {
     value : V;
 }
 
-init :: proc (use map: ^Map($K, $V), dv: V = ~~0, hash_count: i32 = 16) {
+init :: proc (use map: ^Map($K, $V), default: V = ~~0, hash_count: i32 = 16) {
     array.init(^hashes, hash_count);
     array.init(^entries, 4);
 
-    default_value = dv;
+    default_value = default;
 
     for i: 0 .. hash_count do array.push(^hashes, -1);
 }
index b8d94c49f13a1d6e23a8d7b55df96f6faa18fbf1..17d495d5f4161746b5fe890690c50b2add199df1 100644 (file)
@@ -1,6 +1,50 @@
+// Looping over data structures is a very common operation done in all programming
+// practices. In fact, I'd argue 97% of all programming problems can be solved by
+// putting information into an array and looping over it. For this reason, for loops
+// in Onyx are extremely simple to use by design and should make programming very
+// enjoyable. But I digress, let's look at some examples.
+
 #load "core/std/wasi"
 
 use package core
 
 main :: proc (args: [] cstr) {
+    // Currently, for loops can iterate over four kinds of data structures in Onyx:
+    //      * Ranges
+    //      * Fixed arrays
+    //      * Slices
+    //      * Dynamic arrays
+
+    // The syntax of for loops is very simple:
+    // for ^? <iteration variable>: <iterator> <block>
+
+    // For example, this will print out the numbers in the array literal:
+    for i: u32.[ 10, 20, 30, 40, 50 ] {
+        println(i);
+    }
+    print("\n\n");
+
+
+    // You may have noticed the options caret (^) in the definition of the for loop
+    // syntax. When a caret is placed before the iteration variable, the iteration
+    // variable will be a *pointer* to the data inside the iterator. This is useful
+    // for iterating over larger data structures that don't need to be copied for
+    // every iteration, or when you want to modify the elements of the iterator, as
+    // so:
+
+    primes := u32.[ 2, 3, 5, 7, 11, 13, 17 ];
+    for ^prime: primes {
+        *prime *= 2;
+    }
+
+    print("Doubled primes: ");
+    print_array(primes);
+
+    // Currently, there is not support for defining custom iterators for for-loops.
+    // At the moment, this pattern is the best for that purpose:
+    //
+    //  while it := iterator_create(...); !iterator_done(^it) {
+    //      defer iterator_next(^it);
+    //      ...
+    //  }
 }
index b8d94c49f13a1d6e23a8d7b55df96f6faa18fbf1..c337300cb6c1e7f8ef2a106fb1197f9008ad9cb8 100644 (file)
@@ -3,4 +3,63 @@
 use package core
 
 main :: proc (args: [] cstr) {
+    x := 10 + 3 * 5;
+
+    // The basic syntax of a switch statement:
+    switch x {
+        case 10 do println("x was 10.");
+
+        case 25 {
+            println("x was 25.");
+            println("Multiple statments go between '{}'.");
+        }
+
+        case #default {
+            println("Default case reached.");
+        }
+    }
+    println("\n");
+
+    // There are several semantic differences between Onyx switch statements and
+    // C/C++ switch statements. The most obvious one is that there is no implicit
+    // fallthrough to the next lexical case. This is more intuitive and less error
+    // prone. If you do with to fallthrough, the `fallthrough` keyword can be used
+    // as so:
+
+    switch x {
+        case 10 do println("x was 10.");
+
+        case 25 {
+            println("x was 25.");
+            println("Multiple statments go between '{}'.");
+            fallthrough; // Now we fallthrough
+        }
+
+        case #default {
+            println("Default case reached.");
+        }
+    }
+    println("\n");
+
+    // The other semantic different is that each case introduces a lexical block where
+    // local variables can be defined, and statements can be deferred. Take this
+    // interesting example:
+
+    switch 5 {
+        case 3 do println("This will never be reached.");
+
+        case 5 {
+            x := math.max(20, 50);
+            defer printf("x in case '5' is %i.\n", x);
+
+            if x >= 40 do fallthrough;
+
+            println("This statement will never be reached.");
+        }
+
+        case #default {
+            println("Default case reached.");
+        }
+    }
+    println("\n");
 }
index b8d94c49f13a1d6e23a8d7b55df96f6faa18fbf1..1400db296a05585dbce24562cf21dadce718a018 100644 (file)
@@ -3,4 +3,56 @@
 use package core
 
 main :: proc (args: [] cstr) {
+    // Onyx does not have map type built into the language semantics,
+    // but using polymorphic structs and overloaded procedures, it does
+    // provide an 'any-to-any' hash map in it's core libraries.
+
+    // To use it, simply create one like so:
+    ages : map.Map(str, u32);
+    map.init(^ages, default=0, hash_count=16);
+    defer map.free(^ages);
+
+    // The defer statement ensures that the map memory will be freed
+    // when this procedure exits, no matter through which way. The
+    // 'default' argument is used to specify what value should be
+    // returned when the map does not contain the requested key. The
+    // hash_count is an implementation detail that will go away in the
+    // near future. It is the maximum number of chains in the hash table.
+
+    // To put an entry into the map, use the map.put procedure.
+    map.put(^ages, "Dwight", 32);
+    map.put(^ages, "Jim", 25);
+    map.put(^ages, "Pam", 24);
+
+    // To retrieve an entry's value, use the map.get procedure.
+    print_age :: proc (ages: ^map.Map(str, u32), name: str) {
+        age := map.get(ages, name);
+        printf("%s's age is %i.\n", name, age);
+    }
+
+    print_age(^ages, "Dwight");
+    print_age(^ages, "Jim");
+    print_age(^ages, "Pam");
+    print_age(^ages, "Michael");
+
+    // You may noticed if you ran this program that it prints Michael's
+    // age is 0. This is because there was entry for the key 'Michael',
+    // and we provided the default of '0'. To ensure that a key is in the
+    // map, use the map.has procedure
+
+    println(map.has(^ages, "Dwight"));
+    println(map.has(^ages, "Michael"));
+
+
+
+    // Not now nor ever will Onyx support an "interface" concept to say
+    // which types can do which things. One thing that Onyx does have
+    // however is explicit overloaded procedures. They are talked about
+    // more in 14_overloaded_procs.onyx. For our purposes right now, we
+    // just need to know that Map uses two overloaded procedures to
+    // achieve its implementation: map.hash_function, map.cmp_function.
+    // To make Map work with any new datatype you come up with, simply
+    // define an overload for map.hash_function and map.cmp_function.
+    // If you want to see what this looks like, take a look at
+    // tests/aoc-2020/day17.onyx in this repository.
 }