OnyxというWASMファーストの言語に触れる
このスクラップについて
Onyxという言語を試してみる会
Onyxの哲学
ドキュメントを要約。
WASMの利点
- クロスプラットフォームでの動作
- サンドボックス環境で安全
- Onyxは趣味のプロジェクトだから、難しいLLVMより扱いやすい (作者にとって)
など
Onyxの利点
他の言語の代替を目指すものではなく、WASMの可能性を広げるニッチなユースケースのための言語
向いていないユースケースとして、次の3つを挙げている
- 高性能なデスクトップアプリケーション
- ネイティブライブラリ
- 組み込み環境
逆にどの場面に適しているかを挙げてほしい。。。
メモリ管理
ガベージコレクションなどは無く、割り当てと解放を考える必要がある。
ライフタイムは3つ。
-
Very short term: おそらく関数の終了まで
-
defer
キーワードにより、割り当てと解放を並べて書くことができる。
-
-
Short term: メインループの終了まで
- 一時アロケータを使用。
- 個別解放はできない。通常、メインループの最初か最後に全解放される。
- Infinte: 解放されない
- 手動管理
長時間実行されるようなアプリケーション(ゲーム、ウェブサーバ、グラフィカルアプリケーション)ではメモリ管理が重要。
補足でhttpパッケージでのGCアロケータの話などが書いてあるが、ちょっとわからない。
環境構築
インストール
sh <(curl https://get.onyxlang.io -sSfL)
ランタイムを聞かれる。デフォルトのWasmerを選択。
パス設定のために~/.zshrc
も更新してくれる。
ONYX_PATH
がセットされてないよ!というエラーが出たので、source ~/.zshrc
で読み込んであげた。
WASMランタイムが含まれているので、Node.jsやPythonのようにコマンド1つで実行できるのが便利。
拡張機能
VS Codeの場合、Onyx Programming Language拡張機能をインストールすることで利用できる。
シンタックスハイライトはできたが、文法チェックやフォーマットはうまくいかない。あとでやる
動かしてみる
プロジェクト初期化
onyx pkg init
で初期化できる
$ onyx pkg init
Creating new project manifest in ./onyx-pkg.kdl.
Package name: playground
Package description:
Package url:
Package author:
Package version (0.0.1):
onyx-pkg.kdl
というファイルが生成される。kdlってなんだろう
package {
name "playground"
author ""
url ""
description ""
version "0.0.1"
}
config {
dependency_source_path "./lib"
dependency_binary_path "./bin"
}
プログラムの作成と実行
main.onyx
を作成。
use core {printf}
main :: () {
printf("Hello world!\n");
}
WASMランタイムがあるのでコマンド1つですぐに実行できる。
$ onyx run main.onyx
Hello world!
文法
変数
x: i32 = 10;
y: i32; // 初期値0になる
z := 10; // 型の記述を省略できる。型推論によりi32型になる
プロシージャ (関数)
基本形
greet :: () -> void {
printf("Hello!\n");
}
greet()
引数
add :: (x: i32, y: i32) -> void {
printf("{}\n", x + y);
}
add(3, 5); // 8
add(y = 8, x = 2); // 10
引数の初期値
say :: (msg: str = "名無し") -> void {
printf("こんにちは{}さん\n", msg);
}
say("太郎"); // こんにちは太郎さん
say(); // こんにちは名無しさん
返り値
// 単一の値
add :: (x: i32, y: i32) -> i32 {
return x + y;
}
// 複数の値
swap :: (x: i32, y: i32) -> (i32, i32) {
return (y, x);
}
// 最初のreturn文から推測
sub :: (x: i32, y: i32) -> #auto {
return x - y;
}
a := add(3, 5); // 8
b, c := swap(10, 20); // b: 20, c: 20
d := sub(10, 7); // 3
パッケージ
スコープ
- 何もないと、外部からアクセス可能
-
#package
ディレクティブを直前におくとパッケージ内からのみアクセス可能 -
#local
ディレクティブを直前におくとファイル内からのみアクセス可能
package foo
use core {println}
greet1 :: () {
println("Good morning!");
}
#package
greet2 :: () {
println("Good afternoon!");
}
#local
greet3 :: () {
println("Good evening!");
}
パッケージの使用
core.encoding.base64
からencode
プロシージャにアクセス
use core {printf}
use core.encoding.base64 {encode}
main :: () {
msg := "Hello";
encoded := encode(msg);
printf("{} {}\n", msg, encoded);
}
ビルドサイズ
シンプルなHello worldで実験。
use core {println}
main :: () {
println("Hello world!");
}
-
onyx build main.onyx -r wasi
: 163KB -
onyx build main.onyx -r js
: 113KB -
onyx build main.onyx -r js --no-type-info
: 58KB
--no-core
オプションを使用するとビルドができないので、使い道がまだわからない
`onyx build`のオプション
$ onyx help build
Onyx toolchain version v0.1.8
Built on Wed Nov 29 01:49:11 2023
Runtime: wasmer
The toolchain for the Onyx programming language, created by Brendan Hansen.
Usage:
onyx build <input files> [-o target_file] OPTIONS
Required:
<input files> One or more Onyx files to include in the program.
Options:
-o <target_file> Specify the target file (default: out.wasm).
--output <target_file>
-I <dir> Include a directory in the search path.
--runtime, -r <runtime> Specifies the runtime. Can be: onyx, wasi, js, custom.
(default: onyx)
--verbose, -V Verbose output.
-VV Very verbose output.
-VVV Very very verbose output (to be used by compiler developers).
--multi-threaded Enables multi-threading for this compilation.
Automatically enabled for "onyx" runtime.
--doc <doc_file> Generates an O-DOC file, a.k.a an Onyx documentation file. Used by onyx-doc-gen.
--tag Generates a C-Tag file.
--syminfo <target_file> (DEPRECATED) Generates a symbol resolution information file. Used by onyx-lsp.
--lspinfo <target_file> Generates an LSP information file. Used by onyx-lsp.
--stack-trace Enable dynamic stack trace.
--no-core Disable automatically including "core/module".
--no-stale-code Disables use of `#allow_stale_code` directive
--no-type-info Disables generating type information
--generate-foreign-info Generate information for foreign blocks. Rarely needed, so disabled by default.
--wasm-mvp Use only WebAssembly MVP features.
Developer options:
--no-colors Disables colors in the error message.
--no-file-contents Disables '#file_contents' for security.
--show-all-errors Print all errors (can result in many consequencial errors from a single error)
--print-function-mappings Prints a mapping from WASM function index to source location.
--print-static-if-results Prints the conditional result of each #if statement. Useful for debugging.
wat形式にしてのぞいてみる
wasm2wat demoを使用する。残念ながらwasmの仕様に詳しくないので眺めることしかできない。
しかし、数学関数や日付まわりのテキストが含まれるのはわかった。
あと、ユーザー名が含まれるパスが何個も入っているのが気になる。
眠たいのでここまで。
ブラウザでの実行 (1)
単純な足し算。#export "名前" プロシージャ
の構文。
#export "add" (x, y: i32) -> i32 {
return x + y;
}
main ::() {}
ビルド時には-r js
または--runtime js
の指定が必要
onyx build main.onyx -r js
下記のJavaScriptをhtmlに埋め込めばOK (wasmのロード方法覚えていないのでCopilotに書いてもらった)
fetch('out.wasm')
.then((response) => response.arrayBuffer())
.then((bytes) =>
WebAssembly.instantiate(bytes, {
host: {
print_str: (obj) => console.log(obj),
time: () => console.log('time'),
},
})
)
.then((results) => {
const { add } = results.instance.exports;
console.log(add(10, 20));
});
instantiate
の第二引数はよくわからないけど、とりあえずGitHubのIssueにあったコードをコピペしてきた。どこに仕様があるんだろう?
ブラウザで動かす
できた!
ブラウザでの実行 (2)
もう少しいい方法があった。
まず、onyxのリポジトリ内にひっそり(?)置かれているonyx-loader.js
をダウンロードしてくる
そして、onyx-loader.js
と、wasmファイルを次のようにしてロードさせる。
script要素のtype属性がapplication/onyx
であるとロードしてくれる仕様。
<script type="application/onyx" src="out.wasm"></script>
<script src="onyx-loader.js"></script>
シンプルな文字列の出力プログラムを作り、
use core {println}
main ::() {
println("Good morning!");
}
onyx build main.onyx --runtime js
でコンパイルして、ブラウザを開くと、
できた!
謎のinstantiateの第二引数と戦う必要がなくなった
このonyx-loader.jsって、現状exportされたプロシージャ扱えるのか?そこだけ疑問
→ window.ONYX_INSTANCE
経由で扱えそう