From: Brendan Hansen Date: Sun, 3 Mar 2024 05:30:55 +0000 (-0600) Subject: added: examples page X-Git-Url: https://git.brendanfh.com/?a=commitdiff_plain;h=55f176af2a5f02fd600e17da00003dc5c6493ff6;p=onyxlang.io.git added: examples page --- diff --git a/onyx-pkg.kdl b/onyx-pkg.kdl index a7cd0ed..229a462 100644 --- a/onyx-pkg.kdl +++ b/onyx-pkg.kdl @@ -30,3 +30,10 @@ build { } } +lsp { + mode "project" + source_files "build.onyx" + include_dirs "" + working_dir "." +} + diff --git a/src/app.onyx b/src/app.onyx index 5fc2827..3cf8497 100644 --- a/src/app.onyx +++ b/src/app.onyx @@ -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 index 0000000..8a0aace --- /dev/null +++ b/www/examples/fib.html @@ -0,0 +1,3 @@ +

+ This example shows three different ways to do the same thing: compute Fibonacci numbers. +

diff --git a/www/examples/fib.onyx b/www/examples/fib.onyx new file mode 100644 index 0000000..dd21612 --- /dev/null +++ b/www/examples/fib.onyx @@ -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 index 0000000..dc80a3f --- /dev/null +++ b/www/examples/files.html @@ -0,0 +1,4 @@ +

+ This example shows various ways of reading and writing to a file. + The core idea behind files in Onyx is that they extend the io.Stream structure, so you can use the core.io package to interact with them. +

diff --git a/www/examples/files.onyx b/www/examples/files.onyx new file mode 100644 index 0000000..b216796 --- /dev/null +++ b/www/examples/files.onyx @@ -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 index 0000000..39d78c3 --- /dev/null +++ b/www/examples/index.json @@ -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 index 0000000..e659177 --- /dev/null +++ b/www/examples/stdin.html @@ -0,0 +1 @@ +This example reads a single line of input from standard input, splits it in half on the first space using string.bisect, converts both parts to integers using conv.parse, then prints the results using printf for formatted printing. diff --git a/www/examples/stdin.onyx b/www/examples/stdin.onyx new file mode 100644 index 0000000..1f0d877 --- /dev/null +++ b/www/examples/stdin.onyx @@ -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); +} diff --git a/www/static/css/new_style.css b/www/static/css/new_style.css index 4bba997..86801b9 100644 --- a/www/static/css/new_style.css +++ b/www/static/css/new_style.css @@ -20,14 +20,14 @@ --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 { diff --git a/www/templates/pages/docs/env_setup.html b/www/templates/pages/docs/env_setup.html index 23bd618..77120a1 100644 --- a/www/templates/pages/docs/env_setup.html +++ b/www/templates/pages/docs/env_setup.html @@ -113,6 +113,13 @@ chmod +x ./install.sh # Install the LSP (compiles and places the WASM file into $ONYX_PATH/tools) ./install.sh +

On Windows, run the following commands.

+
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
+

Visual Studio Code

Installing the extension automatically enables the language server in VS Code.

diff --git a/www/templates/pages/docs/getting_started.html b/www/templates/pages/docs/getting_started.html index f93ea3c..92c7438 100644 --- a/www/templates/pages/docs/getting_started.html +++ b/www/templates/pages/docs/getting_started.html @@ -39,7 +39,7 @@

Create a new directory and setup a new project. Optionally project values when prompted.

$ 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
index 0000000..55fba14
--- /dev/null
+++ b/www/templates/pages/examples.html
@@ -0,0 +1,44 @@
+{{ block "title" }}
+Onyx Examples
+{{ endblock }}
+
+{{ let navbar_page = "examples" }}
+
+{{ block "page_content" }}
+
+
+

Onyx Examples

+

This page provides several simple examples of Onyx code, showing how to do common things with the standard library.

+
+ +{{ foreach ex in examples }} +
+
+

{% ex.name %}

+
+ +
+ {% ex.html %} +
+ +
{% ex.code %}
+
+{{ endforeach }} + +
+
+

Want to learn more?

+
+

+ Visit the docs! +

+

+ 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. +

+
+ +{{ endblock }} + +{{ extends "pages/normal_page" }} + diff --git a/www/templates/pages/homepage.html b/www/templates/pages/homepage.html index fbbb52c..6be55b1 100644 --- a/www/templates/pages/homepage.html +++ b/www/templates/pages/homepage.html @@ -32,9 +32,9 @@ Copy
$ # Read the docs
-
$ curl onyxlang.io/docs
+
$ curl onyxlang.io/docs
$ # Try Onyx in your browser
-
$ curl try.onyxlang.io
+
$ curl try.onyxlang.io
@@ -161,8 +161,15 @@ main :: () { msg: [] u8 = "Hello, libc!"; write(1, msg.data, msg.length); -} -
+} + + +
+
+

More examples

+
+

See more complete examples on the Examples page.

+
diff --git a/www/templates/partials/navbar.html b/www/templates/partials/navbar.html index a5d4539..c25429c 100644 --- a/www/templates/partials/navbar.html +++ b/www/templates/partials/navbar.html @@ -4,7 +4,9 @@ {{ endif }}