ピュアSwiftコード100行でLispインタープリター作ろうとしたけど185行になったよ
English version of this page: knj4484: Lisp interpreter implemented in 185 lines of Swift
背景
このLispインタープリターで出来ること
(+ 0 (* 11 22) (- 333 30 3)) (first (1 2 3)) (let (a 2 b 3 c 4) (+ a b c)) (if (< 1 3) 2 3) (map (list 1 2) (\ (x) (* 2 x))) (defun calc (a b) (+ a b)) (calc 4 8)
- 整数の算術演算 + - * / %
- リスト操作 list quote first rest
- ローカル変数 let
- 比較演算 < > =
- if
- プロシージャー \
- map
- 関数定義 defun
使い方
- Xcode 6 betaをダウンロードして、インストールする
- GitHubからXcodeプロジェクトのリポジトリをクローンする
- Xcode 6でプロジェクトを開く
- command+Rでプロジェクトを実行する
- Lispの式を一つずつ、入力ボックスに入力し、evalボタンを押す
※エラー処理を一切していませんので、実行できないLispプログラムを実行しようとすると、アプリが固まります。再起動して下さい
サンプルプログラム
フィボナッチ数列の計算もちゃんとできます
(defun f (x) (if (= x 0) 1 (if (= x 1) 1 (+ (f (- x 1)) (f (- x 2)))))) (map (list 1 2 3 4 5 6 7 8 9 10) (\ (x) (f x)))
結論
knj4484$ wc -l Sweme2/{Evaluator,Expression}.swift 153 Sweme2/Evaluator.swift 32 Sweme2/Expression.swift 185 total
少なくとも現時点の自分の技術力では、100行以内のSwiftコードでLispインタープリターを作ることは無理でしたが、Swiftはコードを非常に簡潔に書くことが出来る言語だと分かりました。
考察
Swiftの良いところ
Swiftのいまいちなところ
※まだβ版なので、正式リリースでは改善されているかもしれません
- 文字列の処理が貧弱(このせいでTokenizerの実装がとても大変だった)
- n番目の文字を取り出すのも一苦労
- defaultが必須なのはやりすぎ
- クロージャーでreturn省けるのなら、そもそも普通の関数でもreturn省けていいんじゃないか
- ifの条件ではoptional bindingができるのだから、caseラベルでも出来て欲しかった
- case let x = f(): x
- [1,2,3][1..<2]の型が[Int]じゃなくてSliceなのがきつい
- これはエラーメッセージも分かりにくかったし、ハマった
- 文字列のインデックスが演算できなくてadvance関数を使わなければいけない
今後の展望
- マクロ展開機能を実装する
- 文字列と小数の演算を実装する
参考
- 計算機プログラムの構造と解釈[第2版]
- この本は、読むのが結構大変ですが、Lispの動作原理、実装方法などが分かりました。
- きつねさんでもわかるLLVM ~コンパイラを自作するためのガイドブック~