added: examples page
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Sun, 3 Mar 2024 05:30:55 +0000 (23:30 -0600)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Sun, 3 Mar 2024 05:30:55 +0000 (23:30 -0600)
15 files changed:
onyx-pkg.kdl
src/app.onyx
www/examples/fib.html [new file with mode: 0644]
www/examples/fib.onyx [new file with mode: 0644]
www/examples/files.html [new file with mode: 0644]
www/examples/files.onyx [new file with mode: 0644]
www/examples/index.json [new file with mode: 0644]
www/examples/stdin.html [new file with mode: 0644]
www/examples/stdin.onyx [new file with mode: 0644]
www/static/css/new_style.css
www/templates/pages/docs/env_setup.html
www/templates/pages/docs/getting_started.html
www/templates/pages/examples.html [new file with mode: 0644]
www/templates/pages/homepage.html
www/templates/partials/navbar.html

index a7cd0edf2959280788941154bff3bd601449a7e6..229a4627bcc4fdbbd5f921388dadcdecb3cb4f85 100644 (file)
@@ -30,3 +30,10 @@ build {
     }
 }
 
+lsp {
+    mode "project" 
+    source_files "build.onyx" 
+    include_dirs "" 
+    working_dir "." 
+}
+
index 5fc2827fabd9053b52ce344f3ea863cfb00e8f82..3cf849756c8050db412bffe1b8a81e11e99dee47 100644 (file)
@@ -128,6 +128,15 @@ news_articles: Cached_Resource([] Article);
     });
 }
 
+@route.{.GET, "/examples"}
+(req: &Req, res: &Res) {
+    exs := examples->get() ?? .[];
+
+    res->render("pages/examples", &.{
+        examples = exs
+    });
+}
+
 main :: () {
     default_log_level(.Error);
     load_library_list();
@@ -159,6 +168,13 @@ main :: () {
         }
     };
 
+    examples = .{
+        resource = .{},
+        max_age = 60 * 60 * 24, // 1 day
+        fetch_resource = load_examples,
+        release_resource = delete_examples,
+    };
+
     http.server.set_mime_type("svg", "image/svg+xml");
 
     pipes := http.server.pipeline();
@@ -210,9 +226,15 @@ Package :: struct {
     url, name, description: str;
 }
 
+Example :: struct {
+    name, code, html: str;
+}
+
 #local {
     core_packages: [] Package;
     third_party_packages: [] Package;
+
+    examples: Cached_Resource([] Example);
 }
 
 load_library_list :: () {
@@ -226,3 +248,36 @@ load_library_list :: () {
 
     slice.sort(third_party_packages, (a, b) => string.compare(a.name, b.name));
 }
+
+load_examples :: () -> ? [] Example {
+    #context_scope {
+        context.allocator = alloc.heap_allocator;
+
+        examples: [] Example;
+
+        os.get_contents("www/examples/index.json")
+        |> json.decode_into(&examples);
+
+        for &example in examples {
+            code_path := os.path_join("www/examples", example.code);
+            html_path := os.path_join("www/examples", example.html);
+
+            example.code = os.get_contents(code_path);
+            example.html = os.get_contents(html_path);
+        }
+
+        return examples;
+    }
+}
+
+delete_examples :: (exs: &[] Example) do #context_scope {
+    context.allocator = alloc.heap_allocator;
+
+    for &ex in *exs {
+        delete(&ex.code);
+        delete(&ex.html);
+    }
+
+    delete(exs);
+}
+
diff --git a/www/examples/fib.html b/www/examples/fib.html
new file mode 100644 (file)
index 0000000..8a0aace
--- /dev/null
@@ -0,0 +1,3 @@
+<p>
+    This example shows three different ways to do the same thing: compute Fibonacci numbers.
+</p>
diff --git a/www/examples/fib.onyx b/www/examples/fib.onyx
new file mode 100644 (file)
index 0000000..dd21612
--- /dev/null
@@ -0,0 +1,113 @@
+use core.iter
+use core { printf }
+
+//
+// Way number 1: A simple for-loop
+// This method simply uses a for-loop like you would in C to generate
+// the Fibonacci numbers. No tricks to it.
+//
+fib_for_loop :: (n: i32) -> u64 {
+    a: u64 = 0;
+    b: u64 = 1;
+
+    for 0 .. n {
+        tmp := a;
+        a = b;
+        b = tmp + b;
+    }
+
+    return a;
+}
+
+//
+// Way number 2: Functional folding
+// This way creates an iterator that will yield n integers, thats the
+// iter.counter piped to iter.take(n). Then, for each one of those numbers
+// it steps the state, computing the Fibonacci numbers in the process.
+// The final result is the "a" member of the final state.
+
+FibState :: struct { a, b: u64 }
+
+fib_by_fold :: (n: i32) => {
+    end_state := 
+        // This creates an infinite iterator that simply counts up from 0.
+        iter.counter()
+
+        // This limits only taking the first n values.
+        |> iter.take(n)
+
+        // This performs a "fold" or "reduce" operation.
+        |> iter.fold(
+            // This defines the initial accumulator state.
+            FibState.{ a = 0, b = 1 },
+
+            // This defines the "folding" function. It takes the next value
+            // from the iterator, which we simply ignore because we do not
+            // need it, and the previous value of the accumulator. It then
+            // computes and returns the next value for the accumulator.
+            // iter.fold returns the final value of the accumulator.
+            (_, state) => FibState.{
+                a = state.b,
+                b = state.a + state.b
+            }
+        );
+
+    return end_state.a;
+}
+
+
+//
+// Way number 3: A custom iterator
+// This way produces an iterator that yields consecutive Fibonacci numbers.
+// This is slightly faster than the previous methods because it does not have
+// to redo all the work for every query.
+//
+
+fib_iterator :: (n: i32) => 
+    // This is implemented using a generator, which is a custom iterator
+    // that yields values according to the "next" function defined below.
+    iter.generator(
+        // The initial state of the generator.
+        &.{ a = cast(u64) 0, b = cast(u64) 1, counter = n },
+
+        // The generation function. This takes in a pointer to the state
+        // and must return 2 things: the next value and a boolean of whether
+        // to continue or not.
+        // 
+        // Notice that the parameter's type is a polymorphic here; notice the $.
+        // This is because the above structure literal is entirely type infered;
+        // no explicit type was given to it. Therefore, there is no type we can
+        // write here that would be correct. We could make a structure for it,
+        // but in this case it is fine to let the compiler do a little magic.
+        (state: & $Ctx) -> (u64, bool) {
+            if state.counter <= 0 {
+                return 0, false;
+            }
+
+            tmp := state.a;
+            state.a = state.b;
+            state.b = state.b + tmp;
+
+            state.counter -= 1;
+            return tmp, true;
+        }
+    );
+
+
+main :: () {
+    // Print the results from fib_for_loop
+    for i in 0 .. 90 {
+        printf("{}: {}\n", i, fib_for_loop(i));
+    }
+
+    // Print the results from fib_by_fold
+    for i in 0 .. 90 {
+        printf("{}: {}\n", i, fib_by_fold(i));
+    }
+
+    // Print the results from fib_iterator
+    for value, index in fib_iterator(90) {
+        printf("{}: {}\n", index, value);
+    }
+}
+
diff --git a/www/examples/files.html b/www/examples/files.html
new file mode 100644 (file)
index 0000000..dc80a3f
--- /dev/null
@@ -0,0 +1,4 @@
+<p>
+    This example shows various ways of reading and writing to a file.
+    The core idea behind files in Onyx is that they extend the <code>io.Stream</code> structure, so you can use the <code>core.io</code> package to interact with them.
+</p>
diff --git a/www/examples/files.onyx b/www/examples/files.onyx
new file mode 100644 (file)
index 0000000..b216796
--- /dev/null
@@ -0,0 +1,76 @@
+use core.os
+use core.io
+use core.string
+use core {
+    printf
+}
+
+#doc "Writes example text into a file."
+write_data_into_file :: (filename: str) {
+    // This is one way of opening a file: call os.open and then os.close later.
+    // os.open returns a Result(os.File, os.FileError), so it must be handled
+    // to use the os.File. This program opts to unwrap the result.
+    file := os.open(filename, .Write)->unwrap();
+    defer os.close(&file);
+
+    // Create an io.Writer over the file stream using io.writer_make.
+    // Also, free it later by defering io.writer_free. This also flushes
+    // the internal buffer of io.Writer to make sure everything is written
+    // to the file.
+    file_writer := io.writer_make(&file);
+    defer io.writer_free(&file_writer);
+
+    // The simplest way of writing a string to the file.
+    io.write(&file_writer, "This is the first line of text.\n");
+
+    // io.write_format can be used to "printf" into a file.
+    // printf can be thought of as io.write_format(&stdio.stream, ...).
+    io.write_format(&file_writer, "This is a {} line of text.\n", "formatted");
+
+    for i in 0 .. 5 {
+        io.write_format(&file_writer, "Another line numbered {}.\n", i);
+    }
+}
+
+#doc "Reads example text from a file."
+read_data_from_file :: (filename: str) {
+    // This is another way of opening a file. Because of the semantics
+    // of `for` loops over Iterators, they can behave like `with`
+    // statements in Python or `using` statements in C#.
+    for file in os.with_file(filename, .Read) {
+        // Create a io.Reader over the file stream.
+        file_reader := io.reader_make(file);
+        defer io.reader_free(&file_reader);
+
+        // Read a single line.
+        first_line := io.read_line(&file_reader);
+        printf("First line: {}\n", first_line);
+
+        // Use io.lines to create an iterator over the remaining
+        // lines in the file.
+        printf("Remaining lines:\n");
+        for line, index in io.lines(&file_reader) {
+            printf("{}: {}\n", index, line);
+        }
+    }
+}
+
+#doc "Reads example text from a file."
+read_whole_file :: (filename: str) {
+    // This is the simplest way to get the entire content of a file
+    // into a string ([] u8). Also, defer "deleting" the string, which
+    // will free the memory allocated for the string.
+    contents := os.get_contents(filename);
+    defer delete(&contents);
+
+    printf("Whole contents:\n{}\n", contents);
+}
+
+main :: () {
+    filename := "test.txt";
+
+    write_data_into_file(filename);
+    read_data_from_file(filename);
+    read_whole_file(filename);
+}
+
diff --git a/www/examples/index.json b/www/examples/index.json
new file mode 100644 (file)
index 0000000..39d78c3
--- /dev/null
@@ -0,0 +1,19 @@
+[
+    {
+        "name": "Reading from standard input",
+        "code": "stdin.onyx",
+        "html": "stdin.html"
+    },
+    {
+        "name": "Interact with files",
+        "code": "files.onyx",
+        "html": "files.html"
+    },
+    {
+        "name": "Fibonacci printer",
+        "code": "fib.onyx",
+        "html": "fib.html"
+    }
+]
+    
+
diff --git a/www/examples/stdin.html b/www/examples/stdin.html
new file mode 100644 (file)
index 0000000..e659177
--- /dev/null
@@ -0,0 +1 @@
+This example reads a single line of input from <a href="https://en.wikipedia.org/wiki/Standard_streams">standard input</a>, splits it in half on the first space using <a href="https://docs.onyxlang.io/packages/core.string.html#bisect">string.bisect</a>, converts both parts to integers using <a href="https://docs.onyxlang.io/packages/core.conv.html#parse">conv.parse</a>, then prints the results using <a href="https://docs.onyxlang.io/packages/core.html#printf">printf</a> for formatted printing.
diff --git a/www/examples/stdin.onyx b/www/examples/stdin.onyx
new file mode 100644 (file)
index 0000000..1f0d877
--- /dev/null
@@ -0,0 +1,36 @@
+// Use the necessary core libraries
+use core.io
+use core.string
+use core.conv
+
+// Use the printf function that lives in the core package.
+// This cannot be `use core.printf`, because that will look
+// for a package called `printf` in `core`.
+use core {
+    printf
+}
+
+main :: () {
+    // Create a io.Reader over the stdio.stream to be able scan
+    // the input in parts. Also, defer freeing the reader until
+    // the end of `main`.
+    stdin_reader := io.reader_make(&stdio.stream);
+    defer io.reader_free(&stdin_reader);
+
+    // Read a single line of input.
+    line := io.read_line(&stdin_reader);
+
+    // Split the line on the first space.
+    a_str, b_str := string.bisect(line, " ");
+
+    // Parse and convert both parts to i32s, with a default value
+    // of 0 if it fails to parse as an i32.
+    a_value := conv.parse(i32, a_str)->value_or(0);
+    b_value := conv.parse(i32, b_str)->value_or(0);
+
+    // Compute our result.
+    result := a_value + b_value;
+
+    // Output our result using formatted printing.
+    printf("{} + {} = {}\n", a_value, b_value, result);
+}
index 4bba99712c78343604da88d98013c57445df1b6d..86801b95602aa2ddd1580c39c522a563dee54659 100644 (file)
     --accent-text: #000000;
 }
 
-@media (min-width: 800px) {
+@media (min-width: 900px) {
     :root {
         --default-flex-direction: row;
         font-size: 14pt;
     }
 }
 
-@media (max-width: 799px) {
+@media (max-width: 899px) {
     :root {
         --default-flex-direction: column;
         font-size: 10pt;
@@ -144,7 +144,7 @@ a.cta-button:hover {
     background-position: bottom;
 }
 
-@media screen and (min-width: 800px) {
+@media screen and (min-width: 900px) {
     .container .split {
         display: flex;
         flex-direction: row;
@@ -157,7 +157,7 @@ a.cta-button:hover {
     }
 }
 
-@media screen and (max-width: 799px) {
+@media screen and (max-width: 899px) {
     .container .split {
         display: flex;
         flex-direction: column;
@@ -216,7 +216,7 @@ p code {
 
 
 /* Navigation */
-@media screen and (min-width: 800px) {
+@media screen and (min-width: 900px) {
     .navbar-container {
         position: relative;
         background: none;
@@ -233,7 +233,7 @@ p code {
     }
 }
 
-@media screen and (max-width: 799px) {
+@media screen and (max-width: 899px) {
     .navbar-container {
         position: relative;
         background: none;
@@ -290,13 +290,13 @@ navbar a {
     border-bottom: none;
 }
 
-@media (min-width: 800px) {
-    #mobile-hamburger, .hamburger-piece {
+@media (min-width: 900px) {
+    #mobile-hamburger, span.hamburger {
         display: none;
     }
 }
 
-@media (max-width: 799px) {
+@media (max-width: 899px) {
     navbar div {
         display: none;
     }
@@ -389,7 +389,7 @@ main li {
     margin-top: 24px;
 }
 
-@media (min-width: 800px) {
+@media (min-width: 900px) {
     #desktop_logo {
         position: relative;
         top: 6px;
@@ -424,7 +424,7 @@ main li {
 #homepage-logo:hover #path274  { transform: matrix(0.307405,0,0,0.303728,34.782897,16.545361); }
 #homepage-logo:hover #path67   { opacity: 0; transform: matrix(0.14795386,0,0,0.14795386,100.382719,-1000.6710591);  }
 
-@media (max-width: 799px) {
+@media (max-width: 899px) {
     #mobile_logo {
         position: relative;
         top: 6px;
@@ -458,7 +458,7 @@ main li {
     opacity: 1;
 }
 
-@media screen and (min-width: 800px) {
+@media screen and (min-width: 900px) {
     #install-card {
         width: 800px;
     }
@@ -472,6 +472,10 @@ main li {
     position: relative;
 }
 
+.scrollport > * {
+    margin-top: 6em;
+}
+
 /*
 @media screen and (min-width: 800px) {
     .scrollport {
index 23bd6186b8ef117d2bce9ca85b8e0114a16f3c65..77120a1fd78ea7103b676f3ab51b29ae95af6eb8 100644 (file)
@@ -113,6 +113,13 @@ chmod +x ./install.sh
 # Install the LSP (compiles and places the WASM file into $ONYX_PATH/tools)
 ./install.sh</code></pre>
 
+    <p>On Windows, run the following commands.</p>
+    <pre class="hljs"><code class="language-bat">REM Clone the Onyx Language Server
+git clone https://github.com/onyx-lang/onyx-lsp
+cd onyx-lsp
+REM Install the LSP (compiles and places the WASM file into %ONYX_PATH%/tools)
+install.bat</code></pre>
+
     <h3>Visual Studio Code</h3>
     <p>Installing the extension automatically enables the language server in VS Code.</p>
 
index f93ea3cf6ba2192d0941266bf6b824402de6b841..92c74385626ccb35425995b0ba9c76809e725a4b 100644 (file)
@@ -39,7 +39,7 @@
     <p>Create a new directory and setup a new project. Optionally project values when prompted.</p>
     <pre class="hljs"><code class="language-sh">$ mkdir my-onyx-project
 $ cd my-onyx-project
-$ onyx pkg init
+$ onyx package init
 Creating new project manifest in ./onyx-pkg.kdl.
 
 Package name: my-onyx-project
diff --git a/www/templates/pages/examples.html b/www/templates/pages/examples.html
new file mode 100644 (file)
index 0000000..55fba14
--- /dev/null
@@ -0,0 +1,44 @@
+{{ block "title" }}
+Onyx Examples
+{{ endblock }}
+
+{{ let navbar_page = "examples" }}
+
+{{ block "page_content" }}
+
+<div class="container header">
+    <h1>Onyx Examples</h1>
+    <p>This page provides several simple examples of Onyx code, showing how to do common things with the standard library.</p>
+</div>
+
+{{ foreach ex in examples }}
+<div class="container">
+    <div class="title">
+        <h2>{% ex.name %}</h2>
+    </div>
+
+    <div>
+        {% ex.html %}
+    </div>
+
+    <pre class="hljs"><code class="language-onyx">{% ex.code %}</code></pre>
+</div>
+{{ endforeach }}
+
+<div class="container">
+    <div class="title">
+        <h2>Want to learn more?</h2>
+    </div>
+    <p>
+        <a class="link-button" href="/docs">Visit the docs!</a>
+    </p>
+    <p>
+        You can learn more details about Onyx by visiting the docs!
+        There is more examples, a reference manual for the language, and documentation for the standard library.
+    </p>
+</div>
+
+{{ endblock }}
+
+{{ extends "pages/normal_page" }}
+
index fbbb52c101e9845ad4a7629f463319cd508c5378..6be55b1abaf473475684fff534c0456493592dae 100644 (file)
@@ -32,9 +32,9 @@
                     <span class="copy-button">Copy</span>
                 </div>
                 <div><span style="color: #444;">$</span> <span style="font-style: italic; color: #99a;"># Read the docs</span></div>
-                <div><span style="color: #444;">$</span> <span style="color: var(--accent)">curl</span> <a style="text-decoration: underline" href="/docs">onyxlang.io/docs</a></div>
+                <div><span style="color: #444;">$</span> <span style="color: var(--accent)">curl</span> <a href="/docs">onyxlang.io/docs</a></div>
                 <div><span style="color: #444;">$</span> <span style="font-style: italic; color: #99a;"># Try Onyx in your browser</span></div>
-                <div><span style="color: #444;">$</span> <span style="color: var(--accent)">curl</span> <a style="text-decoration: underline" href="https://try.onyxlang.io/">try.onyxlang.io</a></div>
+                <div><span style="color: #444;">$</span> <span style="color: var(--accent)">curl</span> <a href="https://try.onyxlang.io/">try.onyxlang.io</a></div>
             </div>
         </div>
     </div>
@@ -161,8 +161,15 @@ main :: () {
     msg: [] u8 = "Hello, libc!";
 
     write(1, msg.data, msg.length);
-}
-    </code></pre>
+}</code></pre>
+</div>
+
+<div>
+    <div>
+        <h2>More examples</h2>
+        <br />
+        <p>See more complete examples on the <a href="/examples">Examples</a> page.</p>
+    </div>
 </div>
 </div>
 
index a5d45391aa2ea92087ec4a82c85bfcf2034007fd..c25429c998e86ddd43c25c1e8747f9601627c83d 100644 (file)
@@ -4,7 +4,9 @@
 {{ endif }}
 
 <div class="navbar-container">
-    <navbar>
+    <navbar
+        {{ if homepage == true }} style="justify-content: center" {{ endif }}
+        >
         {{ if homepage == false }}
         <a href="/" aria-label="Go to the homepage">
             <svg viewBox="0 0 64 16" version="1.1" id="desktop_logo" alt="Onyx Logo">