RubyでLispっぽいの書く(1)
大学の課題でScheme処理系をRubyで実装します。東大のminiPythonなんかが微妙に有名になってるけど電気通信大学情報工学科課程も負けてないじゃないかやったね!
まあそれは嘘で、大学の課題で「Rubyである程度大きくて実用的なプログラム書いてください」とかあったんで、RubyでLispっぽい処理系でも書こうと思っただけです。レポート締め切りが2/4。レポート書く作業含めるとそんなに時間に余裕があるわけでもねえな。あと他の講義の試験とかあるし。
とりいそぎいいかげんな構文解析だけ。
C:\hoge\docs\rbdsp>cat list-1.txt list-2.txt list-3.txt list-4.txt list-5.txt (1 2 3 4 5) ; ここまで1つめ (+ 2 3 4 5) ; ここまで2つめ (print "Hello, World!") ; ここまで3つめ (3 . 2) ; ここまで4つめ (3 . 2 4) ; ここまで5つめ C:\hoge\docs\rbdsp>ruby rbdsp.rb list-1.txt (1 2 3 4 5) C:\hoge\docs\rbdsp>ruby rbdsp.rb list-2.txt (+ 2 3 4 5) C:\hoge\docs\rbdsp>ruby rbdsp.rb list-3.txt (print "Hello, World!") C:\hoge\docs\rbdsp>ruby rbdsp.rb list-4.txt (3 . 2) C:\hoge\docs\rbdsp>ruby rbdsp.rb list-5.txt . のあとに色々入れすぎ
S式を読んでS式を出力するだけ。構文解析で50行くらいか? 読めるのは整数と文字列とシンボルとリストとコンスセル。
class Pair def initialize(car, cdr) @car = car @cdr = cdr end def is_pair(x) x.is_a(Pair) end def car @car end def cdr @cdr end def setcar!(a) @car = a end def setcdr!(d) @cdr = d end def set!(a, d) @car = a @cdr = d end end class List < Pair NILS = List def initialize(xs) cdr = NILS xs[1..xs.length].reverse_each{|x| cdr = Pair.new(x, cdr) } set!(xs[0], cdr) end def list?(x) x.is_a(List) end def append!(xs) cdr = self while(cdr.cdr!=NILS) cdr = cdr.cdr end cdr.setcdr!(xs) self end def print_list print "(" cdr = self while(true) printObject(cdr.car) cddr = cdr.cdr if(cddr==NILS) break elsif(!cddr.is_a?(Pair)) print " . " printObject(cddr) break else print " " end cdr = cddr end print ")" end end class ParseException < Exception end class Parser S_INT = /\-?\d+/ S_STR = /"(?:\.|[^"])*"/ S_SYM = /[^\s\r\n\(\)\#\'\`\.]+/ S_LPAR = /\(/ S_RPAR = /\)/ S_DOT = /\./ S_ITEM = /(?:#{S_INT}|#{S_STR}|#{S_SYM}|#{S_LPAR}|#{S_RPAR}|#{S_DOT})/m S_COMMENT = /;.*/ def initialize(input) @input = input.gsub(S_COMMENT, '') end def parse() mat = S_ITEM.match(@input) @input = mat.post_match item = mat[0] if(item =~ S_INT) item.to_i elsif(item =~ S_STR) item[1..item.length-2] elsif(item =~ S_SYM) item.intern elsif(item =~ S_LPAR) ls = [] while((pret = parse)!=S_RPAR) if(pret==S_DOT) cdr = parse rpar = parse if(rpar!=S_RPAR) raise ParseException.new('. のあとに色々入れすぎ') end return List.new(ls).append!(cdr) end ls.push(pret) end List.new(ls) elsif(item =~ S_RPAR) S_RPAR elsif(item =~ S_DOT) S_DOT end end end def printObject(x) if(x.is_a?(Integer)) print x elsif(x.is_a?(String)) print '"', x, '"' elsif(x.is_a?(Symbol)) print x elsif(x.is_a?(List)) x.print_list else print x end end input = ARGF.read pars = Parser.new(input) begin item = pars.parse printObject(item) rescue ParseException => pex print pex end