こちらは以下の投稿の続きです。
トランスパイル版を作ることで、とりあえず動かしましょうという戦略です。
僕はね、Rustの所有権システムが本当に嫌いだったんです。
でも、周りは全部Rust、VRの件のUniFFIもRust専用だし。
そろそろ時代に乗らなきゃと重い腰を上げようとしても「めんどくさい」という理由でできなかったんです。
ではどうするのか?
ならばRustの上に補助言語を作ればいいじゃないと。
せっかくRustの開発者が安全のために作ってくれている所有権システムをぶち壊しに行くわけですが。まあいいでしょう!
ちなみにTPってのはTranspileの略で、Simpleは3.0には本来大型の型システムと要素システムがあるじゃないですか?
あれは本来インタプリタ式の芸当で、Rustでどうやって書くねんと。
まあこの言語はJavaみたいに実行時チェックがあるので、作れるっちゃ作れます。
まあとりあえず、こうです。
import sleep, Duration from rust.tokio.time.sleep | (resolve=(version=1, features="full")); // Rustライブラリの場合は標準でない場合、resolveが必要です。
import println, meta from runtime.futures; // printlnなどはruntime.futuresにあります。
meta < {
Duration = namespaces; // このメタによってDuration.from_secsはDuration::from_secsに書き換わります。
load AsyncCompiler;
};
async fn (tag=tokio.main)main(): void(errors=everything(sideoff)) {
println("Start!");
await sleep(Duration.from_secs(1);
println("End! Done in 1 sec.");
}
main();
この言語のコンパイラによって、インポートは文頭に、関数は間に、普通の式や文はmainエントリポイントに配置されます。
変換後がこちらです。
// Generated by TomaLang-3.0/TP Compiler
// Stage "imports"
use tokio::time::{sleep as TLScope_sleep, TLScope_Duration}; // imported by User Code
use std::any::Any; // imported by runtime.RuntimeTypeChecker
// Stage "initialization"
// type checkers
fn typeCheck(value: &dyn Any) {
// ( ... 省略 ...)
// 本来はここに型チェックを書いたりする。
}
// function "println" from `runtime.futures`
fn TLScope_println(text: &str) {
println!("{}", text);
}
// Stage "user-code"
#[tokio::main]
async fn TLScope_main() {
TLScope_println("Start!");
sleep(Duration::from_secs(1)).await; // method added by runtime.AsyncCompiler.AwaitConvertion
TLScope_println("End! Done in 1 sec.");
}
// Stage "entry-point"
fn main() {
// Runtime generation by runtime.AsyncCompiler.BlockingAsyncExec
{
let rt = Runtime::new().unwrap();
rt.block_on(TLScope_main()); // Wrap calling by runtime.AsyncCompiler.BlockingAsyncExec
}
}
こうなります。
例えば所有権が絡む物の場合:
import println, meta from runtime.futures;
meta < {
load OwnershipAssist;
}
a: int = 1; // これは自動でi32になります。int(bits=64)でi64に変えれます。i8やi16なども。
b: everything(inferonly) = a; // 本来Rustでは所有権が移るため、aは無効になりますが...
println(a); // できる
println(b);
// Generated by TomaLang-3.0/TP Compiler
// Stage "imports"
use std::sync::Mutex; // imported by runtime.OwnershipAssist
// Stage "initialization"
// type checkers
fn typeCheck(value: &dyn Any) {
// ( ... 省略 ...)
// 本来はここに型チェックを書いたりする。
}
// function "println" from `runtime.futures`
fn TLScope_println(text: &str) {
println!("{}", text);
}
// Stage "user-code"
fn main() {
// let TLScope_a = 1; // どこでも+=やmethod呼び出しが使われていなければlet、使われていればlet mutになります。methodでnomutが指定されていればletのままになります。しかしこれはどこでも借りられていない場合です。
let TLInternalScope_a = Mutex::new(1);
// b = aは仮想的に実現されています。使用時に開放されます。
// Generated scope by OwnershipAssist.MutexCompiler
{
let TLScope_a = data.lock().unwrap();
TLScope_println(&TLScope_a);
}
{
let TLScope_b = data.lock().unwrap();
TLScope_println(&TLScope_b);
}
}
OwnershipAssistのコンパイラはかなり賢く、トランスパイル時に処理を追跡するため、書き込みならlet mut + *varをします。
b = aしていたのは実は参照コピーではなく仮想的なコピーであり、使用時に自動的にlockします。