starting to update examples
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Fri, 17 Feb 2023 18:11:03 +0000 (12:11 -0600)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Fri, 17 Feb 2023 18:11:03 +0000 (12:11 -0600)
examples/01_hello_world.onyx
examples/02_variables.onyx
examples/03_basics.onyx

index 44691db8466d7d9e3c05e8c624a71957add7e2ea..67e41eb11da2c04f3a84c1ec094dab52cfa51182 100644 (file)
@@ -1,60 +1,37 @@
-package main
+// This is a (heavily commented) "Hello, World!" program for Onyx. It explains
+// the very basics of an Onyx program.
+
 // Every file is part of a package, which is specified on the first line of the file.
 // If no package name is specified, the file is implicitly part of the 'main' package.
-// So, the package specification above is redudant, but is there for sake of completeness.
+// So, the package specification below is redudant, but is there for sake of completeness.
+// We will talk about the package system more later.
+package main
 
-// There are many source files that make up the 'core' package. While you can include them
-// all separately, they can all be included by including 'core/std/<target>'.
-//
-// Right now, there are two targets, wasi and js. They are mostly the same, except the js
-// target does not include functionality for file systems, since js does not have direct
-// access to the file system like wasi does.
-//
-// It is important to note, that these 'includes' are not C-style includes. They are NOT
-// textual substitutions. They simply tell the compiler that another file should be
-// added to the queue of files to load. When all files in the queue have been parsed,
-// the compiler can continue with the compilation process. You can also include the
-// same file as many times as you want. The redudant copies will be discarded.
+// By default, the core standard libraries are included in any Onyx program. When
+// designing the language, there wer two choices: include the standard libraries
+// by default, or have the user include them. As the vast majority of Onyx programs
+// use the standard library, I opted to include them by default. There is a compiler
+// option to disable loading the standard library, if that is needed. For the sake
+// of completeness, the following line manually includes the standard library, but
+// this is not necessary.
 #load "core/std"
 
-// All of the functionality we need is in the 'core' package. Unlike other package systems,
-// there is no way to reference symbols in a package without specifically 'using' it. This
-// prevents a cluttered global namespace.
-//
-// In Onyx, there are a couple way to control the 'use package' statement. The example below
-// brings in ALL symbols from the 'core' package to the current file scope. This is handy,
-// but if there are symbols with the same name in multiple packages, there will be conflicts.
-// To address this, Onyx has two ways of controlling which symbols are included.
-//
-//      use package core as core_pkg
-//
-// The above makes all symbols in the 'core' package available under the 'core_pkg' symbol,
-// i.e. core_pkg.some_symbol. This is called a qualified use. The name does not have to be
-// different from the package name, so you could say something like, 'use package core as core'.
-//
-//      use package core { some_symbol, some_other_symbol as sos }
-//
-// This next exmaple makes the symbol, 'some_symbol', accessible in the current file scope.
-// This is useful if there is only one thing you need from a package and you do not want to
-// worry about conflicting symbol names in the future. This example also makes the symbol,
-// 'some_other_symbol', accessible in the current file scope under the name, 'sos'. This
-// is useful if a symbol name is long and you use it a lot in a package.
-//
-// As you may expect, you can also combine both of these features into one statement, such as,
-//
-//      use package core as core_pkg { some_symbol, some_other_symbol as sos }
-use package core
+// Below is the function declaration for `main`. While it might look weird at first
+// sight, Onyx uses a consistent syntax for declaring static "things" (functions,
+// structures, enums, etc.). It is always,
+//
+//     the_name :: the_thing
+//
+// In Onyx a function is simply parentheses enclosing the arguments, an optional
+// return type, then the body. So below, `main` is an alias for a function that
+// takes no parameters, and simply prints "Hello, World!". You will see that this
+// is the same syntax for just about everything else in the language.
+//
+// `main`, like most programming languages, is the entry point for an Onyx program.
+// In Onyx, `main` can either take 0 or 1 parameter(s), that being the command-line
+// arguments passed. In programs that do not need them, they can simply be omitted.
+main :: () {
+    core.println("Hello, World!");
+}
 
-// This is the main procedure. It is a convention when using the 'core/std/...' files that this
-// procedure is called when the program starts. There is nothing really special going on here;
-// you can go look in core/sys/wasi.onyx to see how the actual _start procedure works. At the
-// end it simply calls main.main, which is this procedure. By convention, main takes a slice
-// of cstr's, which were the arguments passed from the command line. We will talk about
-// slices later.
-main :: (args: [] cstr) {
 
-    // This is the actual call to print, which will print the message 'Hello World!\n' onto the screen.
-    // It is not too important to know how this works, but if you are interested you can find the
-    // definition for print at core/stdio.onyx.
-    print("Hello World!\n");
-}
index 2160c4b3e7540b9c3732391f2df105155dbeb9bd..813f2d98ad83faafc0aff8a0c3e0265ff9c09561 100644 (file)
@@ -1,24 +1,36 @@
-// This time, we are not adding, 'package main' to the top of the file, since
-// every file is automatically part of the main package unless specified otherwise.
+// Notice this time, we are not adding, 'package main' or '#load "core/std"'
+// to the top of the file, since every file is automatically part of the
+// main package unless specified otherwise, and every compilations includes
+// the standard library.
 
-#load "core/std"
+main :: () {
 
-use package core
-
-main :: (args: [] cstr) {
-
-    // This is the syntax for declaring a local variable in a procedure.
+    // To declare a local variable, the variable name is simply followed by
+    // a single ':', and then the type of the variable is given. In this case,
+    // `foo` is an `i32`, which is Onyx's 32-bit signed integer type. It is
+    // then immediately assigned the value of 10.
     foo: i32;
     foo = 10;
 
-    // You don't have to put the declaration and assignment on different lines.
-    footwo: i32 = 10;
+    // You can combine the two lines above into one, just as you would expected.
+    // This declares `bar` to be an `i32`, and assigns it the value 10.
+    bar: i32 = 10;
 
-    // If you leave the type out between the : and the =, the type is automatically
-    // inferred from the right hand side of the equals.
-    bar := 15;
+    // As a MAJOR convenience, you can omit the type of a variable, if it can
+    // be determined by the type of the right hand side expression. In this
+    // line, the integer `15` defaults to the type `i32`. This type is then
+    // used as the type of `qux`. This may not seem super useful yet, but
+    // when there are very compilated types in your program, this saves
+    // a lot of typing and _arguably_ makes the code more readable.
+    qux := 15;
 
-    printf("foo is {}\n", foo);
-    printf("footwo is {}\n", footwo);
-    printf("bar is {}\n", bar);
+    //
+    // As an aside, the `printf` function in the `core` package is used to
+    // print formatted strings. Because of a lot of language features that
+    // will be discussed later, `printf` simply uses '{}' to denote where
+    // a value should be formatted, instead of pre-typed strings, like "%s",
+    // or "%d" from C.
+    core.printf("foo is {}\n", foo);
+    core.printf("bar is {}\n", bar);
+    core.printf("qux is {}\n", qux);
 }
index 58e5275f3c0f1c753828381cbd397b726fbaeef9..f53c1d6b900555509a81120b71169bb246dc77d9 100644 (file)
@@ -1,9 +1,5 @@
 // Now, lets go over the basic types and control flow mechanisms in Onyx.
 
-#load "core/std"
-
-use package core
-
 main :: (args: [] cstr) {
     // Here is a list of all the builtin types:
     //       void
@@ -22,22 +18,16 @@ main :: (args: [] cstr) {
     // of these types in terms of other types. Let's look at some of those types.
 
     // A pointer in Onyx is written as ^<type>. This is read as 'a pointer to a <type>'.
-    // I will not try to explain what a pointer is here, but something to note is that
-    // pointers in Onyx are 8-bytes in size, but only have their lower 4-bytes used,
-    // since Onyx's compile target is WASM32. This way, when WASM64 is standardized,
-    // pointer will not cause any issues when moving over code to 64-bit.
     //
     // Here foo is an i32. The unary prefix ^ operator takes the address of a value.
     // The type of foo_ptr is ^i32, or a pointer to an i32. Another example of a pointer
     // is the cstr builtin. It is ^u8, which is what C would call a char*.
     //
-    // Also, this example uses printf which will be talked about later. If you are familar
-    // with the C printf, it is very similar, except %d in C is %i in this language.
     foo     := 10;
     foo_ptr := ^foo;
 
-    printf("foo is {}\n", foo);
-    printf("foo_ptr is {}\n", foo_ptr);
+    core.printf("foo is {}\n", foo);
+    core.printf("foo_ptr is {}\n", foo_ptr);
 
 
     // An important type to know before proceeding is the range type. When designing
@@ -51,8 +41,8 @@ main :: (args: [] cstr) {
     // a range with a low of 5, a high of 25, and a step of 1. Currently there is no way
     // to specify the step in a range literal.
     rng := 5 .. 25;
-    print("rng is ");
-    println(rng);
+    core.print("rng is ");
+    core.println(rng);
 
     // Onyx has 3 different array-like types:
     //  * Fixed-size arrays, written [N] <type>, where N is a compile time know integer.
@@ -67,7 +57,7 @@ main :: (args: [] cstr) {
     for i: 0 .. 10 {
         fixed_arr[i] = i * i;
     }
-    printf("fixed_arr[3] is {}\n", fixed_arr[3]);
+    core.printf("fixed_arr[3] is {}\n", fixed_arr[3]);
 
     // A quick thing to note about fixed size arrays is that they copy their contents
     // on assignment. In this example, another_arr declares space on the stack for 10
@@ -79,7 +69,7 @@ main :: (args: [] cstr) {
 
     fixed_arr[3] = 1234;
 
-    printf("another_arr[3] is {}\n", another_arr[3]);
+    core.printf("another_arr[3] is {}\n", another_arr[3]);
 
 
     // Slices are a concept not unique to Onyx. They represent a pointer and a count,
@@ -92,8 +82,7 @@ main :: (args: [] cstr) {
     // of fixed_arr. It is important to know that this does NOT make a copy of the
     // data. It simply points into the same memory as fixed_arr.
     slice_arr := fixed_arr[3 .. 9];
-    printf("slice_arr[3] is {}\n", slice_arr[3]);
-
+    core.printf("slice_arr[3] is {}\n", slice_arr[3]);
 
     // Dynamic arrays are the most common arrays used in practice. They represent a
     // pointer, a count, and a capacity. The extra capacity field allows implementations
@@ -101,14 +90,14 @@ main :: (args: [] cstr) {
     // just how many elements are currently in the array. To facilitate using dynamic
     // arrays, there are many procedures in the core.array package. Take this example.
     dyn_arr : [..] i32;
-    array.init(^dyn_arr);
-    defer array.free(^dyn_arr);
+    core.array.init(^dyn_arr);
+    defer core.array.free(^dyn_arr);
 
     for i: 0 .. 10 {
-        array.push(^dyn_arr, i * i * i);
+        core.array.push(^dyn_arr, i * i * i);
     }
 
-    printf("dyn_arr is {}\n", dyn_arr);
+    core.printf("dyn_arr is {}\n", dyn_arr);
 
 
 
@@ -129,17 +118,18 @@ main :: (args: [] cstr) {
 
     if_var := 5 * 2 + 3;
     if if_var == 13 {
-        print("if_var was 13\n");
-        print("another statement here!\n");
+        core.print("if_var was 13\n");
+        core.print("another statement here!\n");
     }
-    elseif if_var == 14 do print("if_var was actually 14\n");
-    else do print("if_var wasn't 13 or 14\n");
+    elseif if_var == 14 do core.print("if_var was actually 14\n");
+    else do core.print("if_var wasn't 13 or 14\n");
 
-    // Ifs, as well as whiles and switch statements, can have a single initialized variable
-    // that is scoped to the blocks of the if statement and all else statements.
+    // Ifs, as well as whiles and switch statements, can have an initialization statement.
+    // This statement is run before the condition is checked, and can be used to declare
+    // variables that are scoped to the blocks of the if statement and all else statements.
 
     if hidden_if_var := 6; hidden_if_var > 5 {
-        print("hidden_if_var was greater than 5!\n");
+        core.print("hidden_if_var was greater than 5!\n");
     }
 
     // If you uncomment this line, you will get an error, since hidden_if_var is only accessible
@@ -154,13 +144,13 @@ main :: (args: [] cstr) {
     // for i to be 11.
 
     while i := 0; i < 10 {
-        printf("{} ", i);
+        core.printf("{} ", i);
         i += 1;
     } else {
-        print("The while loop never ran...");
+        core.print("The while loop never ran...");
     }
 
-    print("\n");
+    core.print("\n");
 
 
     // Onyx also has the standard 'break' and 'continue' statement found in many other
@@ -171,7 +161,7 @@ main :: (args: [] cstr) {
     // break or continue the loop that you want to. Take this example.
     while i := 0; i < 10 {
         while j := 0; j < 10 {
-            printf("{} {}\n", i, j);
+            core.printf("{} {}\n", i, j);
 
             if i == 1 && j == 2 do break break;
             j += 1;