リーダマクロはこり始めると大変。なんだかわけわからんものができる。例えば、set-macro-character これは各キャラクタにアサインされたマクロ定義を変えることが出来る。get-macro-charater で何がアサインされているか見てみよう。
[2]> (get-macro-character #\;) #<SYSTEM-FUNCTION SYSTEM::LINE-COMMENT-READER> ;
; というキャラクタは(clispでは)システムの line-comment-reader と定義されている。だからこいつを ' に定義されているマクロに置き換えると妙な文法も思いのままだ。
[5]> (get-macro-character #\') #<SYSTEM-FUNCTION SYSTEM::QUOTE-READER> ; NIL [6]> (set-macro-character #\; (get-macro-character #\')) T [7]> ;(a b c) (A B C)
上の例では ; が ' と同じ意味を持っている。もはや ; の後はコメントじゃない。lisp で { [ を括弧として使いたいという人がいたが、やろうと思えば出来るわけだ。やっているひとがいる。
http://www.geocities.co.jp/SiliconValley-SanJose/7474/cmuclMemo.html#0013
気持ち悪いぞ。結局、どっかの blub言語(差別用語か?)がやっていることはキャラクターの組み合わせでいろんな処理を”さも便利そうに”見せているだけで、実は人間がそれを”覚えなきゃいけない”わけで、そもそも少ない記憶力を考える力ではなく”処理のスタイル”を覚えることに使ってしまい、労力の無駄を引き起こす。Verilog の3文字の比較にはまいる。そのうち !=<= という演算子や <==< 演算子などが出てくるに違いない。覚えきれないだろう。一つのルールだけでほぼかけることが望ましいわけだ。lisp はかなりそれに近い。
もっとも私は純粋 lisper ではなく C++ も大好きだけど。C++ の operator のオーバライドとか template はすき。ただ C++ に数学的な美しさだけをもともめるなら + も他の関数と同じにすべきだよね。func(5, 3) と +(5, 3) のようにね。
lisp はいいも悪いもデータ構造を API にしている側面があるので、読んでいかないとどういう構造になっているかわからないのが欠点。他人のソースはおろか、自分の書いたソースでさえ、コメントが必要だ。C++ では処理の流れがそのまま意味に繋がるからわかりやすい。(というかわかった気になる)
というわけでリーダマクロを屈指していいかわるいかわからんが、bnf もどきとそれに従う構文を書くことが出来るようになりました。だんだん道がそれてきている、、、。以下簡単な計算機の例。
(setq *rules* #0%END program ::= expr = expr ::= term expr2 expr2 ::= op0 term expr2 expr2 ::= op0 ::= + term ::= fact term2 term2 ::= * fact term2 term2 ::= fact ::= ( expr ) fact ::= number END ) (format t "result:~a~%" (llparse ' #%END ( number + number ) * number = END ) : その結果 LPAR : (PROGRAM) LPAR : (EXPR =) LPAR : (TERM EXPR2 =) LPAR : (FACT TERM2 EXPR2 =) LPAR : (LPAR EXPR RPAR TERM2 EXPR2 =) NUMBER : (EXPR RPAR TERM2 EXPR2 =) NUMBER : (TERM EXPR2 RPAR TERM2 EXPR2 =) NUMBER : (FACT TERM2 EXPR2 RPAR TERM2 EXPR2 =) NUMBER : (NUMBER TERM2 EXPR2 RPAR TERM2 EXPR2 =) + : (TERM2 EXPR2 RPAR TERM2 EXPR2 =) + : (EXPR2 RPAR TERM2 EXPR2 =) + : (OP0 TERM EXPR2 RPAR TERM2 EXPR2 =) + : (+ TERM EXPR2 RPAR TERM2 EXPR2 =) NUMBER : (TERM EXPR2 RPAR TERM2 EXPR2 =) NUMBER : (FACT TERM2 EXPR2 RPAR TERM2 EXPR2 =) NUMBER : (NUMBER TERM2 EXPR2 RPAR TERM2 EXPR2 =) RPAR : (TERM2 EXPR2 RPAR TERM2 EXPR2 =) RPAR : (EXPR2 RPAR TERM2 EXPR2 =) RPAR : (RPAR TERM2 EXPR2 =) * : (TERM2 EXPR2 =) * : (* FACT TERM2 EXPR2 =) NUMBER : (FACT TERM2 EXPR2 =) NUMBER : (NUMBER TERM2 EXPR2 =) = : (TERM2 EXPR2 =) = : (EXPR2 =) = : (=) EOF : NIL result:OK