added macro example
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Fri, 29 Oct 2021 16:39:54 +0000 (11:39 -0500)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Fri, 29 Oct 2021 16:39:54 +0000 (11:39 -0500)
examples/18_macros.onyx

index 32e750e140afea8b53b9e5563978f7eeda07f2a9..69bf03b14617d5b14b3969941deeb19c613d3fb2 100644 (file)
-// Unfinished Example
+// Macros allow you to write reusable blocks of code, much like procedures. Macros
+// are different in that they are expanded at the call site, and then symbol resolution
+// occurs. This means that every time a macro is called, it is expanded at the call site,
+// and then it uses the symbols from the current scope.
 
-#load "core/std"
+main :: (args: [] cstr) {
+    // A macro in Onyx looks just like a procedure, just with the work "macro" in front of it.
+    a_simple_macro :: macro () {
+        println("This is a simple macro");
+    }
 
-use package core
+    a_simple_macro();
 
-say_something :: macro (thing: Code) {
-    #persist increment := 0;
-    increment += 1;
-    printf("[INFO:{}] {}\n", increment, #insert thing);
-}
+    // The powerful thing about macros is that they can access variables from their enclosing scope.
+    // For example, this macro prints the value of 'x'.
+    print_x :: macro () {
+        printf("x is {}\n", x);
+    }
 
-main :: (args: [] cstr) {
-    data := "Some string in the main function";
+    {
+        x := 10;
+        print_x(); 
+    }
+
+    {
+        x := "a message";
+        print_x();
+    }
+
+    // Macros can take on two different forms depending on if they return a value or not.
+    // Macros that return a value are called "expression macros". Expression macros behave
+    // mostly just like inlined functions, with the addition that they can access variables
+    // from the calling-site's scope. Below is a simple expression macro.
+
+    add_x_y :: macro () -> i32 {
+        return x + y;
+    }
+
+    {
+        x := 10;
+        y := 20;
+        z := add_x_y();
+        printf("x + y is {}\n", z);
+    }
+
+    // Macros that do not return a value are called "block macros". Block macros behave differently
+    // from expression macros when it comes to "return" and "defer" statements. Block macros do not
+    // flush deferred statements when they end. A return statement in a block macro behaves like a
+    // return in the caller.
+
+    block_macro :: macro () {
+        defer println("Block macro's deferred print.");
+
+        println("Block macro's print.");
+    }
+
+    block_macro_that_returns :: macro () {
+        println("Returning...");
+        return;
+    }
 
-    say_something(#code "Foo");
-    say_something(#code 10);
-    say_something(#code context);
-    say_something(#code data);
+    calls_block_macro :: () {
+        block_macro();
+
+        println("This would be doing some work.");
+
+        block_macro_that_returns();
+        println("This will never be printed");
+    }
+
+    calls_block_macro();
+
+
+    // A powerful feature of Onyx that enables macros to be very useful is code blocks.
+    // Code blocks allow you to capture code and treat it as a compile-time object that can
+    // be passed around. To create a code blocks, simply put '#code' in front of the block
+    // of code you want to capture. Notice that this uses '::' because this is a compile-time
+    // value.
+    simple_code :: #code {
+        println("This is a code block!");
+    }
+
+    // You can then use the '#insert' directive to place the code wherever you need it.
+    // We can paste it 3 times for examples:
+    #insert simple_code;
+    #insert simple_code;
+    #insert simple_code;
+
+    // Because simple_code is a compile-time value, it can be passed to procedures as so:
+    triple :: ($body: Code) {
+        println("Running 3 times!");
+        #insert simple_code;
+        #insert simple_code;
+        #insert simple_code;
+    }
+
+    triple(simple_code);
+
+    // Because macros just alias their parameter names to the values passed, you don't need the
+    // compile-time parameter (the '$').
+    triple_macro :: macro (body: Code) {
+        println("Running 3 times in a macro!");
+        #insert simple_code;
+        #insert simple_code;
+        #insert simple_code;
+    }
+
+    triple_macro(simple_code);
 }
+
+#load "core/std"
+use package core
\ No newline at end of file