MooseでLispを実装してみる(ラムダリストと変数の局所化)

やさしい Lisp の作り方 by Java and by C#を参考に書いてみる。(11日目のあたり)

lambda1

  1. argumentsをスタックに積む。その際積んだスタート地点を記憶しておく
  2. ラムダリストを順番にスタックの値と交換する(元の値は待避される)
  3. body部を評価する
  4. ラムダリストをスタックの値と交換する(元の値に戻す)
  5. 評価した値を返して終了

例としてa=>5,b=>1であるとき以下の式を評価してみる。

((lambda (a b) (+ a b)) 2 3)

スタックに引数の値を積む

lambda2

スタックの値とラムダリストの値を交換する

lambda3

body部を評価したらスタックに待避した値を戻す

lambda4

これでOK

Moospもこの通りに実装すればよいみたい。あとsexpEvalの

argList = (List)argList.cdr;

は必要ないような気がする。

MooseでLispを実装してみる(システム関数)

やさしい Lisp の作り方 by Java and by C#を参考に書いてみる。(10日目のあたり)

評価系まわりを書き始めるが、ここらへんから妙に省略度が高くなっているので追いかけるのが大変。14日まで頑張れば一通りできるようなのだけどそこまでたどり着けるのが不安になってきた。

さて、関数はS式関数と内部的な関数(システム関数)があって、システム関数は別のクラスとしてつくる。Moosp::Functionってのを用意してその下にMoosp::Function::Car,Moosp::Function::Quoteとかのクラスを用意するようにしてみた。システム関数は初期化の時に一括して環境(Env)のほうに登録するようになっているようだ。

ん?envとかevalはどっから出てきたんだ?と探したら14日目で

// システム関数の登録
Function funs = new Function(env, eval);  // 関数クラスの設定
funs.registSystemFunctions();      // システム関数の生成

初期化時にenvとevalを渡すのね。10日目のFunctionクラスのコードは省略されていたのであとでまた見直す必要があるな。

MooseでLispを実装してみる(RPL)

やさしい Lisp の作り方 by Java and by C#を参考に書いてみる。

READ-PRINT-LOOPまでのものを動かしてみる

$ perl moosp.pl 
Moosp> (1 2 3 4)
(1 2 3 4)
Moosp> (e r t)
(E R T)
Moosp> (2 . 3)
(2 . 3)
Moosp> (defun plus (a b) (+ a b))
(DEFUN PLUS (A B) (+ A B))

コード

package Moosp;

use Moose;
use Moosp::Env;
use Moosp::Reader;
use Term::ReadLine;

our $VERSION = 0.01;

sub run {
  my $self = shift;

  my $term = new Term::ReadLine 'Simple LISP REPL';
  my $prompt = "Moosp> ";
  my $OUT = $term->OUT || \*STDOUT;

  while ( defined ($_ = $term->readline($prompt)) ) {
    my $reader = Moosp::Reader->new();
    my $sexp = $reader->readFromString($_);
    warn $@ if $@;
    print $OUT $sexp->serialize(), "\n" unless $@;
  }

}

__PACKAGE__->meta->make_immutable;

no Moose;

1; # End of Moosp

moosp.pl

#!/usr/bin/env perl    
use lib "./lib";    
use Moosp;    
Moosp->new->run();

シンボル、オブジェクト、シニフィエ、シニフィアン

自分で実装しながら、オブジェクトとシンボルはシニフィエ、シニフィアンの関係みたいだなぁと思ってたわけだが、既にそういう話はあった

シニフィアンのほうをいじくればesotericなlispが出来るのであろう。それは括弧でくくらなくても良くて、例えば二次元平面に描画された円で囲まれた図形がS式となってもよいのかも。その場合泡のような絵がlispプログラムになるのか。

ProductName Rubyで作る奇妙なプログラミング言語 ~Esoteric Language~
原 悠
毎日コミュニケーションズ / ¥ 2,814 ()
在庫あり。

perlでread-eval-print-loop

クォートされたS式を受け取って文字列を返すものをつくってみた。

use Term::ReadLine;
my $term = new Term::ReadLine 'Simple LISP REPL';
my $prompt = "CL-USER> ";
my $OUT = $term->OUT || \*STDOUT;

while ( defined ($_ = $term->readline($prompt)) ) {
  my $res = $_;
  warn $@ if $@;
  $res =~ s/^\'//;
  print $OUT $res, "\n" unless $@;
}

実行

$ perl s.pl
CL-USER> '(+ 1 2)
(+ 1 2)
CL-USER>

という手抜き。

だが、Readerを実装して処理させれば良いのであろう。

MooseでLispを実装してみる(NilとT)

やさしい Lisp の作り方 by Java and by C#を参考に書いてみる。進行中のメモなのであちこち修正するかも。というか修正しまくりで全然進まないのでメモることにしたという。もう少しまとまったらLISPをみんなで勉強しよう!に投稿したい。

ちなみにMooseでlispなのでMoosp(ムースプ)だ。

まずNilとTはMooseX::SingletonでSingletonにした。

package Moosp::Nil;

use MooseX::Singleton;
with 'Moosp::Sexp';

sub str {"NIL"}

__PACKAGE__->meta->make_immutable;

no MooseX::Singleton;

1;

Tも一緒。

package Moosp::T;

use MooseX::Singleton;

extends 'Moosp::Atom';
with 'Moosp::Sexp';

sub str {"T"}

__PACKAGE__->meta->make_immutable;

no MooseX::Singleton;

1;

javaでlispだとSexpはインターフェースなのでMoose::Roleのクラスにしてみたけどこれでいいのかはわからない(まだ最後まで動かしてないので)。

package Moosp::Sexp;

use Moose::Role;

requires 'str';

sub print { 
  my $self = shift;
  print $self->str;
}

sub serialize {
  my $self = shift;
  return $self->str;
}

no Moose::Role;
1;

Moosp::Atom

package Moosp::Atom;

use Moose;

__PACKAGE__->meta->make_immutable;

no Moose;
1; 

モダンperl入門が手放せない

ProductName モダンPerl入門 (CodeZine BOOKS)
牧 大輔
翔泳社 / ¥ 2,940 ()
在庫あり。

Common Lispとpythonのdocstring

Common Lispのdocstringについて調べてたのだけど、pythonって関数にしかdocstringを入れられないんだっけ?と思ったのでやってみた。

まず関数。__doc__でdocstring。

>>> def sumab (a,b):
...   "test sum"
...   return a + b
... 
>>> print sumab.__doc__
test sum

まぁ、ここまではok

続いて変数

>>> a = 10
>>> dir(a)
['__abs__', '__add__', '__and__', '__class__', '__cmp__', '__coerce__', 
'__delattr__', '__div__', '__divmod__', '__doc__', '__float__', '__floordiv__', 
...
'conjugate', 'denominator', 'imag', 'numerator', 'real']

おー__doc__あるじゃん。

>>> print (a.__doc__)
int(x[, base]) -> integer

Convert a string or number to an integer, if possible.  A floating point
argument will be truncated towards zero (this does not include a string
representation of a floating point number!)  When converting a string, use
the optional base.  It is an error to supply a base when converting a
non-string.  If base is zero, the proper base is guessed based on the
string content.  If the argument is outside the integer range a
long object will be returned instead.

ふむ、これは任意のstringに置き換えられるのかな?

>>> a.__doc__ = "test int"
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'int' object attribute '__doc__' is read-only

リードオンリーだった。str,float,listも一緒だった。

プリミティブな型にもdocstringはあるけど、それは書き換えられないってことかな。

printとprinc

昨晩からcl-wikiのソースを読んでる。cl-markdownは今の僕では歯が立ちません(むきー)。

print,princとかdefvar,defparameterの使い分けがいまいち分からん。特にdefvarはシングルトンみたいな性質が面白いが、単にdefvarが紳士的なだけであって、setfとかdefparameterで上書きできちゃうから使いどころが?とか思った。

(シングルトンっていう表現は変だな。)

それから、princのcはどういう意味とか気になったので調べていたら、g000001さんのエントリにぶつかったのであった。まめ知識は増えていく。

Common Lispでdisassemble

common lispのdisassembleを使うとアセンブラっぽく出力できる。

* (defun add1 (a) (+ a 1)) 

ADD1
* (disassemble 'add1)

; disassembly for ADD1
; 119CBD7A:       8B55F4           MOV EDX, [EBP-12]          ; no-arg-parsing entry point
;       7D:       BF04000000       MOV EDI, 4
;       82:       E8B94363F2       CALL #x4000140             ; GENERIC-+
;       87:       7302             JNB L0
;       89:       8BE3             MOV ESP, EBX
;       8B: L0:   8D65F8           LEA ESP, [EBP-8]
;       8E:       F8               CLC
;       8F:       8B6DFC           MOV EBP, [EBP-4]
;       92:       C20400           RET 4
;       95:       0F0B0A           BREAK 10                   ; error trap
;       98:       02               BYTE #X02
;       99:       18               BYTE #X18                  ; INVALID-ARG-COUNT-ERROR
;       9A:       4D               BYTE #X4D                  ; ECX
NIL

プログラミングの力を生み出す本を読んだので、アセンブラもちょっとは楽しくなってきた。

で、この本の後ろのほうの章(マルチスレッドとか)が気になっているんだけど、ちと高い。

Common Lispで文字列の分割

いわゆるsplitがないようなので、split-sequenceを入れてみた。

* (require 'split-sequence)

NIL
* (split-sequence:split-sequence #\Space "Yes we    can")

("Yes" "we" "" "" "" "can")
13
* (split-sequence:split-sequence #\Space "Yes we    can" :remove-empty-subseqs t)

("Yes" "we" "can")
13

一方、cl-markdownのほうはThere is no class named NIL.とかいうエラーが分かんなくて詰まり中。

* (cl-markdown:markdown "test mark do wn" :format :html :stream nil)

debugger invoked on a SIMPLE-ERROR in thread \
#<THREAD "initial thread" RUNNING {118FD789}>:
  There is no class named NIL.

んー。とりあえず他のテキストフォーマッターでもいい気がしてきた。