LoginSignup
23
12

More than 5 years have passed since last update.

RustのマクロでLispを作ってみた

Last updated at Posted at 2016-12-19

概要

マクロの勉強がてら、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でも作ってみようかな……

23
12
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
23
12