scallop

ムラムラしてきたので、なんとなく俺Lisp作り始めた。SICPはもはや積読タワーで埃をかぶってます。
んで、ついでに先日作ったGitHubアカウントでリポジトリhttps://github.com/yagiey/scallop)を公開しておいた。ぼちぼち勉強しながらとりあえずはR5RSに準拠するようにするのが目標。そのためにはもっと勉強しなきゃなぁ...。処理系の名前は以前と同じくscallopにした。前のscallopはdefineが無かったので名前をつけられなかったけど、今回はできるようにしたよ。開発環境はMac OS X 10.6.5上のmono 2.8.1。
とりあえず、現時点で純LISPにはなってるみたい?自信ないけど。

実行するには、monoをインストールしてGitHubからscallopをcloneして

$cd scallop
$make
$mono ./scallop.exe

するとREPLが起動。手続きexitを引数なしで実行すればREPLは終了。REPLでソースコードの改行はまだできないよ><

scallop> cons
{procedure:cons}
scallop> lambda
{syntax:lambda}
scallop> (lambda (n) n)
{closure}
scallop> (define n 42)
n
scallop> n
42
scallop> (set! n 23)
n
scallop> n
23
scallop> (+ 1 2 3)
6
scallop> (* 1 2 3)
6
scallop> (define Y (lambda (f) ((lambda (g) (g g)) (lambda (g) (f (lambda (x) ((g g) x)))))))
Y
scallop> ((Y (lambda (f) (lambda (n) (if (< n 2) 1 (* n (f (- n 1))))))) 5)
120
scallop> (eval (quote (+ 1 2 3)) ())
Identifier '+' is undefined.
scallop> (eval (quote (+ 1 2 3)) (interaction-environment))
6
scallop> (exit)
Scalop was terminated.

現時点でできていることを以下にまとめてみる。

使える値

  • 論理
  • 整数
  • 文字列
  • シンボル
  • ペア
  • nil

論理値はリテラルを書けないけど、内部的には実装している。(not 1)とかやると#fが取り出せる。#f以外は全部#tとみなされるようにしている。もちろん(not (not 1))ってやれば#tになる。
負数も同じくリテラルを書けないが、(- 1)ってやると-1ができる。R5RSでは多倍長整数は必須ではないそうなので実装の予定なし。ってかそもそもC#のInt32型に丸投げなので、バイト数とか全然気にしていないけどそれでいいのかなー。正確?非正確?何スかそれ?おいしいの?
文字列中のエスケープ(\nとか\tとか)はサポートしていない。
シンボルは、数値で始まらなければなんでもあり。
コンスセルはドット対の形式で表記できないけど、(cons 1 2)とかやると(1 . 2)ができる。
実数は未実装。よって除算は用意していない。

用意している手続き

  • cons
  • car
  • cdr
  • eq?
  • atom?
  • eval
  • exit
  • null?
  • not
  • +
  • -
  • *
  • >
  • =
  • interaction-environment

上記の全ての手続きは、それらに対応するC#の手続きを呼び出しているだけ。だから簡単にスタックがあふれてしまう。解決するには「継続を伴った引数付きgoto文」ってなやつを実現しないと無理ぽい?うーん...。

用意している構文

    • define
    • lambda
    • quote
    • if
    • cond
    • or
    • and
    • let

構文も同じように環境に保持しているけど、これで良いのかは不明。マクロ展開に対する先入観 - チキン煮込みチーズミックス4辛なんかも考慮すると、かなーり怪しい。
ユーザ定義の構文はまだできない。