日本社会にテクノロジーが根付かない原因は信頼か? 独自開発のIoTとZen言語による解決を目指すコネクトフリーCEO、帝都久利寿さん。 マイクロソフトと任天堂があるシアトル出身、5才からプログラミングし、10代で会社を売却し、単独来日、再び創業。 日本&技術好きの同志。

プログラムが使うメモリ、使ったまま忘れたり、使ってはいけないところを触るとソフトウェアは停止する。 このいわゆるバグは、アプリを使う人をイライラさせ、車や社会インフラなどで起きると多大な影響を引き起こす。

現在主力となっているプログラミング言語の解決方法に、自動的にメモリを回収する仕組みを使う方法(GoやTypeScriptなど)、所有権という概念を入れて管理する(Rust)という方法がある。 Zen言語はそのどちらでもなく、後片付け構文とメモリ確保の明確化で安全にするというアプローチ。

IchigoJam BASICでは、そもそも使うメモリ量を固定化しているため問題が起きない。 Zen言語はこの古典定期かつ堅牢なメモリ使用方法を、一段上位で管理するイメージ。

GCを使う言語はランタイム(作ったプログラムを動かすために必要なベースプログラム)で容量が大きくなってしまうが、ZenはRust同様、ゼロランタイム。 IchigoJamなど、小さな組み込み用途にも使える。


zen4ij - Zen language for IcihgoJam
早速実験「かわくだり」のZen言語版が動いた。コンパイル済みバイナリ124byteと小さい。
(参考、Rust版 132byte、C版 116byte、Ruby版 325byte + runtime 2,376byte)

const ij = @import("std15.zen"); export fn main() i32 { ij.cls(); var x: i32 = 15; var score: i32 = 0; while (true) { ij.locate(x, 5); ij.putc(236); ij.locate(ij.rnd(32), 23); ij.putc('*'); ij.putc(10); ij.wait(3); const c = ij.inkey(); if (c == 28) { x -= 1; } if (c == 29) { x += 1; } if (ij.scr(x, 5) != 0) { break; } score += 1; } return score; }

IchigoJam BASICのかわくだり 150byteとの比較

10 CLS:X=15:S=0 20 LC X,5:?CHR$(236) 30 LC RND(32),23:?"*" 40 WAIT 3 50 C=INKEY() 60 X=X-(C=28)+(C=29) 70 IF SCR(X,5) ?S:END 80 S=S+1 90 GOTO 20

@マークで始まるものはZenの組み込み関数、std15.zenにてIchigoJamのAPIを定義している。 例えば、1文字表示するij.putcは、下記のように定義し、IchigoJam APIと接続している。

pub fn putc(x: u8) void { const addr = @intToPtr(*u16, 0xC4); const f = @intToPtr(fn(u8) void, addr.*); f(x); }

u8は、符号なし(unsigned)8bitの意味。Rustに似ているが、関数の返り値型に"->"が不要でよりコンパクト。 @intToPtrを使うことで、物理メモリにもアクセスできるので、OSづくりにも対応できる。(Rustではunsafeを使用する)


Zen言語のドキュメントだけでなく,コンパイラのエラーも日本語対応!優しい! (コンソールで export LANG=ja と設定してみよう)
ごきげんよう、Zenへようこそ | Zen Language Documentation


IchigoDyhookがあれば、亀蔵でもサクっと取り出し、 IchigoJam BASIC プログラミング!
久利寿氏、超久々のBASICでPRINT/GOTOループを決めているところ。

社会のあちこちで活躍するソフトウェアだが、本領発揮はまだまだこれから!
日本のソフトウェア技術力、どんどん上げていこう!


@yrm__ (IchigoLatte MINIScript)、 @kristopher (Zen language)、 @taisukef (IchigoJam BASIC)
国産言語プチサミット in 鯖江!

links
- C言語よりコンパクトに! Zen言語開発者からのプルリクとマシン語の気持ち

気持ちよく大規模webアプリを開発するために、静的型付け言語を使用するのも有効です。 APIを使用する際、間違ったパラメータを渡してハマることを防いでくれ、エディタの支援も受けやすいのが特徴です。

ブラウザで動作するクライアント側がサポートする2つの言語、JavaScriptとWebAssembly。 最近のお気に入り、静的型付け言語 Rust は WebAssembly のバイトコードを生成してくれます。 (もう一つの人気の静的型付け言語TypeScriptは、動的型付け言語JavaScriptへと変換するトランスパイラGLSLを含めると3つ?)


早速実験してみましょう。

まず準備、Rustをインストールし、WebAssembly用にコンパイルするために、targetを追加します(一度だけでok)

$ rustup target add wasm32-unknown-unknown 次のコードは、超コンパクトな、足し算するだけのRustのプログラム add.rs です。

#![no_main] #![no_std] #[panic_handler] fn panic(_info: &core::panic::PanicInfo) -> ! { loop {} } #[no_mangle] pub fn add(a: i32, b: i32) -> i32 { a + b }

mainがなく、標準ライブラリを使わないための設定をするのは、IchigoJam用のRust「rust4ij」と同様です。 no_mangleで、最適化して行方不明になってしまわないよう設定します。

コンパイルはこのように

$ rustc --target wasm32-unknown-unknown add.rs -C opt-level=1

131byte、小さな add.wasm ファイルができました!
(wasm-strip add.wasm で116byteに!opt-level=1 を付けないとデバッグ情報で1.5MBに!)

wasmをJavaScriptから呼び出すのはこんなコードを書きます。

<script type="module"> (async () => { const bin = await (await fetch("./add.wasm")).arrayBuffer(); const wasm = await WebAssembly.instantiate(bin); const ex = wasm.instance.exports; const add = ex.add; document.body.textContent = "WebAssembly in Rust, add(1,2) = " + add(1, 2); })(); </script>

* ブラウザがトップレベルawaitに対応してくれると、async関数で囲む必要がなくなります
* SafariがWebAssembly.instantiateStreaming対応したらもっと短くかけます

wasmの読み込みはファイル上ではだめなので、live-serverなど、何かHTTPサーバーをローカルに立てて、http://127.0.0.1:8080/ などでアクセスするか、手持ちのサーバーにアップして試してみましょう!

$ live-server

コンパクトで簡単ですね!

JavaScriptやブラウザとの相互やり取りには共有メモリを使うイメージで連携します。
本格的なwebアプリ、RustとJavaScriptの連携で一度作ってみたいと思います。

DenoやNode.jsでもwasmは動きます。

$ deno run -A test-file.mjs $ node test-node.mjs

src on GitHub、いろいろ遊んでみてください!

links
- マシン語で検証、RustはC言語の代わりになりうるか? IchigoJamでライトに楽しむ組み込み開発 panic編と情報処理
- IchigoJamでかんたん組み込みRust開発 - rust4ij x IchigoJam API

IchigoJamで動作する数十byte単位のプログラムも実装可能なモダンプログラミング言語 Rust。 サーバー実装の実験にリアルタイムwebの要、WebSocketを使って単純に送った文字列が返ってくる、echoサーバーづくり!

JavaScriptのランタイム Denoと、HTTPサーバーモジュール Servest を使った WebSocket の echo server のコードは Servest を使ってこんな感じ。 (servest 1.1.0 では、Deno 1.2.0 でエラーが発生、stdモジュールを新版への差し替えプルリク、下記は servest v1.1.1で動作します!)

import { createApp } from "https://servestjs.org/@v1.1.1/mod.ts"; function handleHandshake(sock) { async function handleMessage(sock) { for await (const msg of sock) { if (typeof msg === "string") { sock.send(msg); } } } handleMessage(sock); } const app = createApp(); app.ws("/ws/", handleHandshake); app.listen({ port: 8080 });

続いて、WebSocketのclient

import { WebSocket } from "https://deno.land/x/websocket/mod.ts"; const endpoint = "ws://127.0.0.1:8080/ws/"; const ws = new WebSocket(endpoint); ws.on("open", () =< { ws.send("hello!"); }); ws.on("message", message =< { console.log("recv", message); ws.close(); });

シンプルに書けて、いい感じ!(deno run -A server.js と den run -A client.js で実行)

続いて、Rustで書く WebSocket の echo server には、速そうな(TechEmpower Web Framework Benchmarksで現在2位)の Actix を使ってみました。

use actix::*; use actix_web::{web, App, Error, HttpRequest, HttpResponse, HttpServer}; use actix_web_actors::ws; struct EchoSession { } impl Actor for EchoSession { type Context = ws::WebsocketContext<Self>; fn started(&mut self, _ctx: &mut Self::Context) { } fn stopping(&mut self, _ctx: &mut Self::Context) -> Running { Running::Stop } } impl StreamHandler<Result<ws::Message, ws::ProtocolError>> for EchoSession { fn handle(&mut self, msg: Result<ws::Message, ws::ProtocolError>, ctx: &mut Self::Context) { let msg = match msg { Err(_) => { ctx.stop(); return; } Ok(msg) => msg, }; match msg { ws::Message::Ping(_) => { }, ws::Message::Pong(_) => { }, ws::Message::Text(text) => { ctx.text(text); } ws::Message::Binary(_) => println!("Unexpected binary"), ws::Message::Close(reason) => { ctx.close(reason); ctx.stop(); } ws::Message::Continuation(_) => { ctx.stop(); } ws::Message::Nop => (), } } } async fn echo_route(req: HttpRequest, stream: web::Payload) -> Result<HttpResponse, Error> { ws::start(EchoSession {}, &req, stream) } #[actix_rt::main] async fn main() -> std::io::Result<()> { HttpServer::new(move || { App::new() .service(web::resource("/ws/").to(echo_route)) }) .bind("127.0.0.1:8080")? .run() .await }

必要なものはきっちり書くスタイルなので、少し長いですが、やっていることは一緒です。Cargo.tomlにパッケージ名を記入して、cargo runでコンパイル&実行しておきましょう。

[dependencies] actix = "0.9.0" actix-web = "2.0.0" actix-web-actors = "2.0.0"

続いて、client は、こう書けます。

use websocket::{ClientBuilder, Message}; fn main() { let mut client = ClientBuilder::new("ws://127.0.0.1:8080/ws/") .unwrap() .connect_insecure() .unwrap(); let message = Message::text("hello!"); client.send_message(&message).unwrap() let mes = client.recv_message().unwrap(); println!("{:?}", mes); }

Cargo.toml の dependencies に websocket="*" を追加し、Cargo run で動きます。

コネクション確立後、「hello!」を送って返ってくるものを100回繰り返して、時間を計測。 最速タイムで、Deno:35msec、Rust:25msecと、Rustが速いですが、Deno/JavaScriptでもベースは十分速そうです。

ちょっと応用、RustのActix、ブラウザから接続するチャットのサンプルを動かしてみました。 JavaScriptのコードが、jQueryコードだったので、モダンスタイルに更新!(src on GitHub)

速さが要のリアルタイムweb用サーバーづくり、Rustで実装、楽しいかも?

情報処理学会「情報処理」8月号の特集「プログラミング教育の最前線」(目次記事一覧
モダン言語全盛時代に、なぜBASICか?なぜはんだづけなのか?6,586文字、7ページで綴りました。コンピューター好きな人、増やしましょう!

気になるモダンプログラミング言語の1つ、Rust(ラスト)。Denoとrust4ijをきっかけに入門。 IchigoJam と、Arm Cortex-M0 マシン語を使って、C言語の代替手段にできるか検証開始!

Rustの特徴のひとつ、ゼロコスト抽象化は、C言語の構造体と構造体ポインタを使った関数群や、関数ポインタをすっきり記述できていい感じ。 trait(トレイト)は、C言語では関数ポインタで実装するJavaのインターフェイス的なもの、こちらもきれいな記述でゼロコスト。

メモリを扱う際にもよく使う配列、うっかり領域外アクセスがバグやセキュリティホールになりがちです。Rustでどのように防いでいるか逆アセンブルして確認してみました。

let mut ar:[i32; 4] = [1, 2, 3, 4]; ar[param as usize] = 100;

長さ4の配列を作って、パラメータで指定された場所に100を書き込むプログラム。4以上の値が指定されると領域外アクセス、まずいです。

14: 9100 str r1, [sp, #0] 16: 2803 cmp r0, #3 18: d80c bhi.n 0x34 1a: 0080 lsls r0, r0, #2 1c: 4669 mov r1, sp 1e: 2264 movs r2, #100 ; 0x64 20: 500a str r2, [r1, r0]

Rustでコンパイルしたコードを見てみると、配列の初期化後、「cmp r0, #3」で配列の長さ-1と比較し「bhi.n 0x34」で範囲外ならジャンプ(GOTO)させ、不正なプログラムを走らせないように保護するコードが生成されていました。 2命令、たった2クロックのオーバーヘッドなので、サイズ的にも速度的にも問題になることは少ないでしょう。

領域外として判定された際のジャンプ先が panic 処理となります。panic_handlerとして自由に定義できるので、試しにただのループに変更してみました。

use core::panic::PanicInfo; #[panic_handler] fn panic(_info: &PanicInfo) -&lt; ! { loop {} }

すると、なんと、下記のように領域外チェックが消えました。仕様なのかどうなのかは未調査ですが、これならそこそこ攻めた作りもできそうです。

14: 9100 str r1, [sp, #0] 12: 0080 lsls r0, r0, #2 14: 4669 mov r1, sp 16: 2264 movs r2, #100 ; 0x64 18: 500a str r2, [r1, r0]

panic 処理内で、volatile_storeとunsafeを使って、不正なメモリアクセスをあえて実行して、IchigoJam BASICの「Segmentation Fault」を起こすようにすると使いやすくなります。

#![no_std] #![no_main] #![feature(core_intrinsics)] use rust4ij::std15::*; use core::intrinsics::volatile_store; use core::panic::PanicInfo; #[panic_handler] fn panic(_info: &PanicInfo) -> ! { //putstr("panic!\n".as_bytes().as_ptr()); // show message (+23byte) unsafe { volatile_store(1 as *mut i32, 1); } // invoke the Segmentation fault for safe (+32byte) loop {} } #[link_section = ".main"] #[no_mangle] fn main(param:i32, _ram:i32, _rom:i32, _divfunc: fn(u32,u32) -> u64) -> i32 { let mut ar:[i32; 4] = [1, 2, 3, 4]; ar[param as usize] = 100; let mut sum:i32 = 0; for i in 0..ar.len() { sum += ar[i]; } return sum; }

転送して、?USR(#800,4) と領域外アクセスするとエラーとなることが確認できます。不正アクセスするマシン語は下記のように、最小限のマシン語に変換されています。

44: 2001 movs r0, #1 46: 6000 str r0, [r0, #0]

前後に余計なコードが混ざってしまうため、数byte単位でも容量がほしい、現行 IchigoJam の移植はちょっと無理にしろ、かなり広範囲でのマイコン組み込み開発に良さそうです。

Rust、なかなかかわいく思えてきました。 次は、所有権の概念を使った動的メモリ確保を多用するメモアプリとかの実装にチャレンジして、より仲良くなってみようと思います。


創造はじめのいっぽ,Apple I/TK-80/MSX が生んだ感動をすべての子どもたちへ!」と題したIchigoJamにかけるこどもプログラミングの想いでも触れたコンピューター自体を理解し、好きになることと大切さ。

使うだけじゃなくて、IchigoJamみたいな素晴らしいハードウェアのきっかけになったのはすごく嬉しいことだ。あれは素晴らしい。
また英国がコンピューターの歴史を作るときが来た——「英国のMakerムーブメントは世界一」Raspberry Pi財団代表エベン・アプトンが語る | fabcross

Raspberry Pi、安価なパソコンをこども達に!想いに共感しに出会った2013年。小学生にはもう一段簡単なパソコンが必要と思って作った IchigoJam。 イベントを通じて、創始者、Eben Upton とも日本で会え、それがきっかけで産まれた IchigoJam RPi は、ラズパイで使えるサードパーティーOSとしても紹介してくれました!

世界中のこどもたちが「創造はじめのいっぽ」を楽しく踏み出せるよう、日々精進。

メモリに所有権を導入して安全性を確保するプログラミング言語「Rust」がおもしろそうです。お気に入りのJavaScriptの実行環境 Deno でも使われていることもあり、本格的に入門してみます。 今回のターゲットは、IchigoJam (Arm NXP LPC1114)です!


1. シンプルに 1+1 を計算するRustプログラム
2. エルチカ(LEDをチカチカさせるループ)
3. かわくだりゲーム in Rust!
4. CPUに書き込んでいるので、ケーブルを外しても動きます!

Rustで書くメイン関数はこんな感じです。c4ijをベースに作っていただいた rust4ij by mitsujifork して改造しながら使いました。 main関数の返り値は、IchigoJAM BASICのコンソールで表示されます。マシン語起動用のBASICコードは、Makefile内にあります。

fn main(_param:i32, _ram:i32, _rom:i32, _divfunc: fn(u32, u32) -> u64) -> i32 { return 1 + 1; }

i32は符号付き32bit整数、u32は符号なし32bit整数。fnは関数を表します。

エルチカはこんな感じ。実際には、IchigoJam用のライブラリ以外、余計なものをリンクしないような設定が6行、加わります。(src on GitHub)

fn main(_param:i32, _ram:i32, _rom:i32, _divfunc: fn(u32,u32) -> u64) -> i32 { loop { out(7, 1); wait(10); out(7, 0); wait(10); } }

led関数を定義しておくのもいいですね。コンパイル型の言語の場合、使わない関数はメモリを消費しないので、分かりやすいライブラリをいろいろ作っていくのがお得です。


ゲームもこのように作れます。IchigoJAM BASIC版C言語版Ruby版Armマシン語版とも比べてみましょう。

fn main(_param:i32, _ram:i32, _rom:i32, _divfunc: fn(u32, u32) -> u64) -> i32 { cls(); let mut x:u32 = 15; let mut score:u32 = 0; loop { locate(x, 5); putc(236 as char); locate(rnd(32), 23); putc('*'); putc(10 as char); wait(3); let c:u32 = inkey(); if c == 28 { x -= 1; } if c == 29 { x += 1; } if scr(x, 5) != 0 { break; } score += 1; } putstr(b"SCORE:\0" as *const u8); putnum(score); putc(10 as char); return 0; }

Rustの真骨頂は、メモリ管理にあり!ここまではほぼBASICやC言語などと変わらない感じですが、必要に応じてメモリを確保したり開放したりする際、Rustらしさがでてくるはず。 プログラムで7KBまで、RAMも1KBくらいはフリーで使えるので、IchigoJam BASICでは難しい大作づくりにチャレンジするのもおもしろいかも!?


MacとUSBシリアルで接続して、sector指定書き込み対応のlpc21ispを使用して、書き込みます。

LEDを付ける out(7, 1) だけのプログラムを逆アセンブルしてみました(make disasm)

Disassembly of section .data: 00000000 <.data>: 0: b580 push {r7, lr} 2: af00 add r7, sp, #0 4: 20d8 movs r0, #216 ; 0xd8 6: 8802 ldrh r2, [r0, #0] 8: 2007 movs r0, #7 a: 2101 movs r1, #1 c: 4790 blx r2 e: 2000 movs r0, #0 10: bd80 pop {r7, pc} 12: 0000 movs r0, r0 14: ffec 7fff ; <UNDEFINED> instruction: 0xffec7fff 18: 0001 movs r1, r0 ...

素直でコンパクトなマシン語になってました!
小さいコンピューターにコンパクトに実装するのが好きうれしい Rust でした!

links
- Rust Programming Language
- taisukef/rust4ij: Rust language for IchigoJam (forked from rust4ij by mitsuji)
- 高専でなぜC言語を学ぶのか? IchigoJamマシン語生成プログラム c4ij で作る、C言語版かわくだり

気にはなりつつも、使う機会がなかったプログラミング言語 Rust(ラスト)。 お気に入りのJavaScriptの実行環境、Denoで使われています。

Denoで直したいところがあり、最新版1.0.5でも、Issuesプルリクでの話題にもなっていなかったのでプログラムを取得し、改造し。プルリクしたら、主要開発者からコメントが付き、反映された! (プルリクとは、GitHubなどで使われる共同制作ソフトGitの重要機能で、反映要求。承認されて初めて反映される仕組み。)


added mjs files for default test by taisukef · Pull Request #6122 · denoland/deno
有名OSSへの初貢献の内容はこちら。Deno test の mjs 対応。

歴史が長いJavaScript、変数宣言が不要だったり、スコープが分かりづらいvarとの決別した、モジュールベースの新しいJavaScriptの拡張子 .mjs でいこうと決めて、開発しているが、Denoのテストでは、.jsや.tsしか認識してくれない。(参考、jsからmjsへ

.mjs も普通にコンパイルなどは普通にやってくれるので、きっと追加し忘れているに違いない。分かりやすくまとまったRustのコード、該当箇所を発見。

C/C++と同等またはそれ以上の記述力を持つRust。C言語からの系統、ほとんどのプログラムはRustを知らなくても普通に読める。 「||」という演算子は、C、Java、JavaScript、IchigoJam BASICでも同じ「または」を意味する論理演算子。 deno test の対象となる拡張子が並んでいるので、"mjs"を追加。


失敗したのがこちらドキュメントの訂正。mjsの追記だけと思って油断し、チェック後に手書きしたのが悪かった。 PR後の自動テストではねられてしまい、整形してねとコメントがついた。ドキュメントも書式が荒れないよう、規定桁数が設定されていた。 Denoの開発者さん、優しくて助かりましたが、大いに反省。自動テスト、偉い。

$ cargo test $ tools/format.py $ tools/lint.py

Contributing(貢献方法)のプルリク前の確認事項。

1. イシューやプルリクで同様内容をチェック
2. テストが変更点をカバーしていることをチェック
3. 全体テストプログラムを実行して問題ないことを確認 (cargo test)
4. 書式整形プログラムを実行(tools/format.py)
5. プログラムに問題がないことを確認(tools/lint.py)

オープンソースへの参加は、チーム開発に関する最新技術に触れられるので、何かお気に入りにツールへの貢献。チャレンジしてみると良いですね!

自動書式整形の優秀さに触れたので、使うことにした deno fmt。こちらもデフォルトに mjs が含まれていないので、もうひとつプルリク。(→ 後日、無事マージされました)
added mjs files for default fmt by taisukef · Pull Request #6134 · denoland/deno
ちょっとしたコードですが、世界中で使われると思うとうれしいですよ!


鯖江市役所にて、鯖江市長の牧野さん、牧田さんとIT話。
まちづくりもオープンソースにでいきましょう!
条例や、ウェブサイトなど、個人情報に絡まない公開情報は、全部GitHub管理でいいかも?

links
- KEN_ALL!? 郵便番号ESモジュールで開発効率化! 経産省もGitHub公開スタート
- Deno にコントリビュートする (1) - Qiita
- Deno にコントリビュートする (2) - Qiita

Tweet
クリエイティブ・コモンズ・ライセンス
この作品は「Creative Commons — CC BY 4.0」の下に提供されています。
CC BY / @taisukef / アイコン画像 / プロフィール画像 / RSS