updated: examples page
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Sun, 17 Mar 2024 17:27:28 +0000 (12:27 -0500)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Sun, 17 Mar 2024 17:27:28 +0000 (12:27 -0500)
15 files changed:
examples [new submodule]
src/app.onyx
src/resources.onyx [new file with mode: 0644]
src/static_routes.onyx [new file with mode: 0644]
www/examples/fib.html
www/examples/fib.onyx [deleted file]
www/examples/files.html
www/examples/files.onyx [deleted file]
www/examples/index.json
www/examples/stdin.html
www/examples/stdin.onyx [deleted file]
www/packages/core_packages.json
www/static/css/new_style.css
www/templates/pages/example.html [new file with mode: 0644]
www/templates/pages/examples.html

diff --git a/examples b/examples
new file mode 160000 (submodule)
index 0000000..1df16a3
--- /dev/null
+++ b/examples
@@ -0,0 +1 @@
+Subproject commit 1df16a331e64f15a5095d2e79dbc2dcece461382
index 3cf849756c8050db412bffe1b8a81e11e99dee47..02731928dd846c0983217b761c798c2eacc61124 100644 (file)
@@ -29,114 +29,6 @@ reg: otmp.TemplateRegistry;
     }
 }
 
-@route.{.GET, "/"}
-(req: &Req, res: &Res) {
-    articles := iter.as_iter(news_articles->get() ?? .[])->take(2)->collect();
-    res->render("pages/homepage", &.{ articles = articles });
-}
-
-@route.{.GET, "/ovmwasm"}
-(req: &Req, res: &Res) => res->render("pages/ovmwasm", null);
-
-@route.{.GET, "/docs"}
-(req: &Req, res: &Res) => res->render("pages/docs", null);
-
-@route.{.GET, "/docs/install"}
-(req: &Req, res: &Res) => res->render("pages/docs/install", null);
-
-@route.{.GET, "/docs/setup"}
-(req: &Req, res: &Res) => res->render("pages/docs/env_setup", null);
-
-@route.{.GET, "/docs/getting_started"}
-(req: &Req, res: &Res) => res->render("pages/docs/getting_started", null);
-
-@route.{.GET, "/docs/guides"}
-(req: &Req, res: &Res) => res->render("pages/docs/guides", null);
-
-@route.{.GET, "/docs/guides/http-server"}
-(req: &Req, res: &Res) => res->render("pages/docs/guide_http_server", null);
-
-@route.{.GET, "/docs/guides/raylib"}
-(req: &Req, res: &Res) => res->render("pages/docs/guide_raylib", null);
-
-@route.{.GET, "/docs/packages"}
-(req: &Req, res: &Res) => res->render("pages/docs/package", null);
-
-@route.{.GET, "/docs/packages/list"}
-(req: &Req, res: &Res) {
-    res->render("pages/docs/package_list", &.{
-        core_packages = core_packages,
-        third_party_packages = third_party_packages
-    });
-}
-
-@route.{.GET, "/playground"}
-(req: &Req, res: &Res) {
-    res->body("text/html", "This has been moved to <a href=\"https://try.onyxlang.io\">try.onyxlang.io</a>.");
-    res->status(200);
-    res->end();
-}
-
-@route.{.GET, "/community"}
-(req: &Req, res: &Res) => res->render("pages/community", null);
-
-
-@route.{.GET, "/robots.txt"}
-(req: &Req, res: &Res) {
-    res->file("./www/static/robots.txt");
-    res->status(200);
-    res->end();
-}
-
-
-Article :: struct { name, description, path, date: str }
-news_articles: Cached_Resource([] Article);
-
-@route.{.GET, "/news/:article"}
-(req: &Req, res: &Res) {
-    article_path := req.url_params["article"] ?? "";
-    article := array.first(news_articles->get() ?? .[], [n](n.path == article_path));
-    if !article {
-        res->render("pages/404", null);
-        res->status(404);
-        return;
-    }
-
-    filename := tprintf("www/news-articles/{}.html", article.path);
-
-    if os.file_exists(filename) {
-        contents := os.get_contents(filename);
-        defer delete(&contents);
-
-        res->render("pages/news_article", &.{
-            article = .{ contents = contents, name = article.name, description = article.description }
-        });
-
-    } else {
-        res->render("pages/404", null);
-        res->status(404);
-        return;
-    }
-}
-
-@route.{.GET, "/news"}
-(req: &Req, res: &Res) {
-    articles := news_articles->get() ?? .[];
-
-    res->render("pages/news", &.{
-        articles = articles
-    });
-}
-
-@route.{.GET, "/examples"}
-(req: &Req, res: &Res) {
-    exs := examples->get() ?? .[];
-
-    res->render("pages/examples", &.{
-        examples = exs
-    });
-}
-
 main :: () {
     default_log_level(.Error);
     load_library_list();
@@ -148,24 +40,8 @@ main :: () {
         resource = .{},
         max_age = 60 * 60, // 1 hour
 
-        fetch_resource = () -> ? [] Article {
-            article_file := os.get_contents("www/news-articles/index.json");
-            article_index, json_err := json.decode_with_error(article_file);
-            if json_err->has_error() {
-                return .{};
-            }
-
-            articles: [] Article;
-            #context_scope {
-                context.allocator = alloc.heap_allocator;
-                json.as_any(article_index.root, &articles);
-            }
-            return articles;
-        },
-
-        release_resource = (articles: &[] Article) {
-            delete(articles, allocator=alloc.heap_allocator);
-        }
+        fetch_resource = fetch_articles,
+        release_resource = release_articles
     };
 
     examples = .{
@@ -221,63 +97,3 @@ main :: () {
     }
 }
 
-
-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 :: () {
-    os.get_contents("www/packages/core_packages.json")
-    |> json.decode_into(&core_packages);
-
-    slice.sort(core_packages, (a, b) => string.compare(a.name, b.name));
-
-    os.get_contents("www/packages/third_party_packages.json")
-    |> json.decode_into(&third_party_packages);
-
-    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/src/resources.onyx b/src/resources.onyx
new file mode 100644 (file)
index 0000000..bb5302a
--- /dev/null
@@ -0,0 +1,87 @@
+use core.alloc
+use core.string
+use core.encoding.json
+use core.os
+use core.slice
+use core {
+    tprintf
+}
+
+Package :: struct {
+    url, name, description: str;
+}
+
+Example :: struct {
+    title, name, description, author, html: str;
+    tags: [] str;
+}
+
+core_packages: [] Package;
+third_party_packages: [] Package;
+
+examples: Cached_Resource([] Example);
+
+
+
+load_library_list :: () {
+    os.get_contents("www/packages/core_packages.json")
+    |> json.decode_into(&core_packages);
+
+    slice.sort(core_packages, (a, b) => string.compare(a.name, b.name));
+
+    os.get_contents("www/packages/third_party_packages.json")
+    |> json.decode_into(&third_party_packages);
+
+    slice.sort(third_party_packages, (a, b) => string.compare(a.name, b.name));
+}
+
+load_examples :: () -> ? [] Example {
+    #context_scope {
+        context.allocator = alloc.heap_allocator;
+
+        examples: struct { list: [..] Example };
+
+        os.get_contents("www/examples/index.json")
+        |> json.decode_into(&examples);
+
+        for &example in examples.list {
+            code_path := os.path_join("www/examples", tprintf("{}.html", example.name));
+            example.html = os.get_contents(code_path);
+        }
+
+        return examples.list;
+    }
+}
+
+delete_examples :: (exs: &[] Example) do #context_scope {
+    context.allocator = alloc.heap_allocator;
+
+    for &ex in *exs {
+        delete(&ex.html);
+    }
+
+    delete(exs);
+}
+
+
+Article :: struct { name, description, path, date: str }
+news_articles: Cached_Resource([] Article);
+
+fetch_articles :: () -> ? [] Article {
+    article_file := os.get_contents("www/news-articles/index.json");
+    article_index, json_err := json.decode_with_error(article_file);
+    if json_err->has_error() {
+        return .{};
+    }
+
+    articles: [] Article;
+    #context_scope {
+        context.allocator = alloc.heap_allocator;
+        json.as_any(article_index.root, &articles);
+    }
+    return articles;
+}
+
+release_articles :: (articles: &[] Article) {
+    delete(articles, allocator=alloc.heap_allocator);
+}
diff --git a/src/static_routes.onyx b/src/static_routes.onyx
new file mode 100644 (file)
index 0000000..87a62b7
--- /dev/null
@@ -0,0 +1,127 @@
+use core {package, *}
+use runtime
+use otmp
+use http
+use http.server {Req :: Request, Res :: Response, route}
+use core.encoding.json
+
+
+@route.{.GET, "/"}
+(req: &Req, res: &Res) {
+    articles := iter.as_iter(news_articles->get() ?? .[])->take(2)->collect();
+    res->render("pages/homepage", &.{ articles = articles });
+}
+
+@route.{.GET, "/ovmwasm"}
+(req: &Req, res: &Res) => res->render("pages/ovmwasm", null);
+
+@route.{.GET, "/docs"}
+(req: &Req, res: &Res) => res->render("pages/docs", null);
+
+@route.{.GET, "/docs/install"}
+(req: &Req, res: &Res) => res->render("pages/docs/install", null);
+
+@route.{.GET, "/docs/setup"}
+(req: &Req, res: &Res) => res->render("pages/docs/env_setup", null);
+
+@route.{.GET, "/docs/getting_started"}
+(req: &Req, res: &Res) => res->render("pages/docs/getting_started", null);
+
+@route.{.GET, "/docs/guides"}
+(req: &Req, res: &Res) => res->render("pages/docs/guides", null);
+
+@route.{.GET, "/docs/guides/http-server"}
+(req: &Req, res: &Res) => res->render("pages/docs/guide_http_server", null);
+
+@route.{.GET, "/docs/guides/raylib"}
+(req: &Req, res: &Res) => res->render("pages/docs/guide_raylib", null);
+
+@route.{.GET, "/docs/packages"}
+(req: &Req, res: &Res) => res->render("pages/docs/package", null);
+
+@route.{.GET, "/docs/packages/list"}
+(req: &Req, res: &Res) {
+    res->render("pages/docs/package_list", &.{
+        core_packages = core_packages,
+        third_party_packages = third_party_packages
+    });
+}
+
+@route.{.GET, "/playground"}
+(req: &Req, res: &Res) {
+    res->body("text/html", "This has been moved to <a href=\"https://try.onyxlang.io\">try.onyxlang.io</a>.");
+    res->status(200);
+    res->end();
+}
+
+@route.{.GET, "/community"}
+(req: &Req, res: &Res) => res->render("pages/community", null);
+
+
+@route.{.GET, "/robots.txt"}
+(req: &Req, res: &Res) {
+    res->file("./www/static/robots.txt");
+    res->status(200);
+    res->end();
+}
+
+
+
+@route.{.GET, "/news/:article"}
+(req: &Req, res: &Res) {
+    article_path := req.url_params["article"] ?? "";
+    article := array.first(news_articles->get() ?? .[], [n](n.path == article_path));
+    if !article {
+        res->render("pages/404", null);
+        res->status(404);
+        return;
+    }
+
+    filename := tprintf("www/news-articles/{}.html", article.path);
+
+    if os.file_exists(filename) {
+        contents := os.get_contents(filename);
+        defer delete(&contents);
+
+        res->render("pages/news_article", &.{
+            article = .{ contents = contents, name = article.name, description = article.description }
+        });
+
+    } else {
+        res->render("pages/404", null);
+        res->status(404);
+        return;
+    }
+}
+
+@route.{.GET, "/news"}
+(req: &Req, res: &Res) {
+    articles := news_articles->get() ?? .[];
+
+    res->render("pages/news", &.{
+        articles = articles
+    });
+}
+
+@route.{.GET, "/examples"}
+(req: &Req, res: &Res) {
+    exs := examples->get() ?? .[];
+
+    res->render("pages/examples", &.{
+        examples = exs
+    });
+}
+
+@route.{.GET, "/examples/:name"}
+(req: &Req, res: &Res) {
+    exs := examples->get() ?? .[];
+    name := req.url_params["name"] ?? "";
+
+    ex := slice.first(exs, [x](x.name == name));
+
+    res->render("pages/example", &.{
+        ex = ex
+    });
+}
+
+
index 8a0aacebe9996b42ab82d87e82a48313a7fc23ba..e4a6959e827e01360909d15532c4ca879e69cac1 100644 (file)
@@ -1,3 +1,115 @@
-<p>
-    This example shows three different ways to do the same thing: compute Fibonacci numbers.
-</p>
+<h1 class='title'>Fibonacci Sequence</h1>
+<p class='author'>Brendan Hansen</p>
+<div class='description'>This example shows three different ways to do the same thing: compute Fibonacci numbers.</div>
+<pre class='hljs'><code class='language-onyx'>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);
+    }
+}</code></pre>
diff --git a/www/examples/fib.onyx b/www/examples/fib.onyx
deleted file mode 100644 (file)
index dd21612..0000000
+++ /dev/null
@@ -1,113 +0,0 @@
-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);
-    }
-}
-
index dc80a3f3ab2ab2d7ec06f2f2ae61d4407f8b13fb..1f54a5015843dc77745318ec48f5495bc11f4691 100644 (file)
@@ -1,4 +1,78 @@
-<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>
+<h1 class='title'>File Operations</h1>
+<p class='author'>Brendan Hansen</p>
+<div class='description'>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.</div>
+<pre class='hljs'><code class='language-onyx'>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);
+}</code></pre>
diff --git a/www/examples/files.onyx b/www/examples/files.onyx
deleted file mode 100644 (file)
index b216796..0000000
+++ /dev/null
@@ -1,76 +0,0 @@
-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);
-}
-
index 39d78c3187ecf17619ea85f2caefed064b751b60..e395ae33fd0617ea10a74078a2ff129593576abf 100644 (file)
@@ -1,19 +1 @@
-[
-    {
-        "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"
-    }
-]
-    
-
+{"by_tag":{"io":["files","stdin"],"reader":["files","stdin"],"os":["files"],"file":["files"],"loops":["fib"],"iterators":["fib"],"input":["stdin"],"conv":["stdin"]},"list":[{"code":"","title":"File Operations","name":"files","tags":["io","reader","os","file"],"author":"Brendan Hansen","description":"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."},{"code":"","title":"Fibonacci Sequence","name":"fib","tags":["loops","iterators"],"author":"Brendan Hansen","description":"This example shows three different ways to do the same thing: compute Fibonacci numbers."},{"code":"","title":"Standard Input","name":"stdin","tags":["input","io","reader","conv"],"author":"Brendan Hansen","description":"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."}]}
\ No newline at end of file
index e659177c32b476a5e20a8cd7c30af7c98f0922db..e9ad9dca24ec9bd4dfb6f2bc6feb5d990c618e21 100644 (file)
@@ -1 +1,39 @@
-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.
+<h1 class='title'>Standard Input</h1>
+<p class='author'>Brendan Hansen</p>
+<div class='description'>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.</div>
+<pre class='hljs'><code class='language-onyx'>// 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);
+}</code></pre>
diff --git a/www/examples/stdin.onyx b/www/examples/stdin.onyx
deleted file mode 100644 (file)
index 1f0d877..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-// 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 45e878e1ddba1450bd0c6105ce8ae122f27485f0..fe0c0158ecce67892b1d737919f3b44ece33e643 100644 (file)
         "name": "otmp",
         "url": "https://github.com/onyx-lang/pkg-otmp",
         "description": "A string templating language, similar to Jinja or Mustache."
+    },
+    {
+        "name": "webgl2",
+        "url": "https://github.com/onyx-lang/pkg-webgl2",
+        "description": "Bindings to WebGL 2. Accessible when targetting JS runtime."
     }
 ]
index 86801b95602aa2ddd1580c39c522a563dee54659..bdc10ec632001a2eccff77a7172670643404de0f 100644 (file)
@@ -38,7 +38,7 @@ body {
     background: var(--background);
     color: var(--text);
     
-    font-family: 'Oxanium', sans-serif;
+    font-family: sans-serif;
 }
 
 a {
@@ -554,3 +554,31 @@ canvas#animation {
        background-color: var(--primary);
 }
 
+.example-container {
+    margin: 0 auto;
+    max-width: 1200px;
+    display: grid;
+
+    grid-template-columns: 1fr 1fr;
+    gap: 16px;
+}
+
+.example-container > div {
+    padding: 8px;
+}
+
+.example-container > div > div {
+    padding: 8px;
+}
+
+.example-container > div > div > a {
+    border-bottom: none;
+}
+
+.example-container .example-tag {
+    padding: 2px;
+    background-color: var(--primary);
+    border-radius: 4px;
+}
+
+.example > .author { display: none; }
diff --git a/www/templates/pages/example.html b/www/templates/pages/example.html
new file mode 100644 (file)
index 0000000..fb8757b
--- /dev/null
@@ -0,0 +1,34 @@
+{{ block "title" }}
+Onyx Examples
+{{ endblock }}
+
+{{ let navbar_page = "examples" }}
+
+{{ block "page_content" }}
+
+<div class="container header">
+    <h1>Onyx Examples</h1>
+    <p><a href="/examples">Back to example list</a></p>
+</div>
+
+<div class="container example">
+    {% ex.html %}
+</div>
+
+<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 55fba14705b4d345356766ba32f22f3bfb48c4b7..226897c9b80a549977b39b41e1d558144e20ca18 100644 (file)
@@ -11,19 +11,26 @@ Onyx Examples
     <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 class="example-container">
+    {{ foreach ex in examples }}
+    <div class="container card">
+        <div style="position: relative">
+            <a href="/examples/{% ex.name %}" style="position: absolute; inset: 0">
+            </a>
+
+            <h3>{% ex.title %}</h3>
+            
+            <div>
+                {{ foreach tag in ex.tags }}
+                <span class="example-tag">{% tag %}</span>
+                {{ endforeach }}
+            </div>
+
+            <div style="margin: 8px 0;">{% ex.description %}</div>
+        </div>
     </div>
-
-    <div>
-        {% ex.html %}
-    </div>
-
-    <pre class="hljs"><code class="language-onyx">{% ex.code %}</code></pre>
+    {{ endforeach }}
 </div>
-{{ endforeach }}
 
 <div class="container">
     <div class="title">