概要
マクロの勉強がてら、Rustのコード内でLispっぽく記述できるDSL(?)を作ってみました。
(Lispに静的型があったらどうなるのか、というのに興味あったのもある。)
使い道があるかどうかわかりませんが、とりあえず公開しました。
良くも悪くも所詮はRustのマクロ上で構築されたものなので、「静的型&所有権有り」の言語になっています。
使い方
Cargo.tomlファイルに以下の記述を追加します。
Cargo.toml
[dependencies]
macro_lisp="0.1.0"
後は、ソースコード内に、
#[macro_use]
extern crate macro_lisp;
と記述することでLispっぽいコードを書くことができるようになります。
サンプル
実際のコードをみるのが手っ取り早いと思いますので、サンプルを載せておきます。
Lispを好きでない方は気持ち悪くなるかもしれないので注意。
ちなみに、Lisp!()マクロ内にLispっぽい記述を書く仕様です。
FizzBuzz
fizzbuzz.rs
#[macro_use]
extern crate macro_lisp;
lisp!(defun main() ()
(dotimes (count 100)
(defconstant num (1+ count))
(if (== 0 (% num 3))
(if (== 0 (% num 5))
(println "FizzBuzz")
(println "Fizz"))
(if (== 0 (% num 5))
(println "Buzz")
(println "{}" num))))
);
Unixライクなwcコマンド
wc.rs
#[macro_use]
extern crate macro_lisp;
lisp!(use std::env);
lisp!(use std::process::exit);
lisp!(defun is_whitespace ((b u8)) bool // (b u8)はu8型の引数bをとるということ。boolは戻り値の型。
(match b
(0x20 | 0x09 | 0x85 | 0x0a | 0x0b | 0x0c | 0x0d => (true))
(_ => (false) )) // マクロの限界(?)で、カッコ無しでtrueやfalseと書けるようにできなかった。
);
lisp!(defun main () ()
(defconstant (args Vec<String>) env::args().collect()) // 型を指定した定数宣言(Rustのlet)
(if (< (len args) 2)
(progn
(println "usage: wc file")
(exit 0)))
(defvar char_count 0) // Rustのlet mut
(defvar word_count 0)
(defvar line_count 0)
(defvar in_word false)
(defconstant path &args[1])
(with-input-from-file (file path)
(doiter (byte file.bytes()) // for-in
(incf char_count)
(defconstant b byte.unwrap())
(if (== b 0x0a)
(incf line_count))
(if in_word
(if (is_whitespace b)
(setf in_word false))
(if (! (is_whitespace b))
(progn
(setf in_word true)
(incf word_count))))))
(println "{:>10} {:>10} {:>10} {}" line_count word_count char_count path)
);
リファレンスは気力がないので、後でその気になったら書く
約束は未定。
ソースコードをさらっとみれば、だいたいどんな機能があるかわかる……はず。
とりあえず、サンプルが動く程度の機能しかないと思っていればほぼ正解。
余談(個人的感想)
静的型付けのLispってわりといいかもって思えてきた。。。
今度はRustで静的型付Lispでも作ってみようかな……