2012/01/15

brainf*ckインタプリタを書いてみた

brainf*ckインタプリタを書いたことがなければプログラマに非ず
などと言われることもあるようなので書いてみた。Gauche。

しかしプログラマには「hogehogeでなければプログラマに非ず」が多いですよね。ハードル高いの多いし。どうなってんだこの業界。
どの道ついてけないので、書けそうなものの中で面白そうなものをやってみる、というスタンス。

取りあえずHello worldが動くところまで書いたところ。replもつけてみた。コードがすごく納得できない状態なので、もう何度か書いてみたい。特にinterpretとparse(つまりメインディッシュ)がもうちょっとどうにかできないもんかと。

今回brainf*ckがどういう言語かちゃんと知って心惹かれました。面白い言語なんですねーこれ。しかも作るの簡単。なるほどlispと同じく使う人より実装する人の方が多い言語。lispと違うのは使う人がいなくて実装する人しかいないところか。

ソース

#!/usr/local/bin/gosh

;;;
;;; bfi - brainf*ck interpreter
;;;

;; sample - Hello world
;; >+++++++++[<++++++++>-]<.>+++++++[<++++>-]<+.+++++++..+++.[-]>++++++++[<++++>-]<.>+++++++++++[<+++++>-]<.>++++++++[<+++>-]<.+++.------.--------.[-]>++++++++[<++++>-]<+.[-]++++++++++.


(use srfi-1)
(use file.util) ; file->string
(use gauche.parseopt)

(define (usage)
  (print "Usage: bfi [options ...] [file]")
  (exit 1))

(define (interpret code ptr mem)
  (let rec ((code code))
    (if (null? code)
        (values ptr mem)
        (begin
          (if (list? (car code))
              (until (zero? (vector-ref mem ptr))
                (rec (car code)))
              (case (car code)
                ((#\+)(inc! (vector-ref mem ptr)))
                ((#\-)(dec! (vector-ref mem ptr)))
                ((#\<)(dec! ptr))
                ((#\>)(inc! ptr))
                ((#\.)(write-byte (vector-ref mem ptr)) (flush))
                ((#\,)(set! (vector-ref mem ptr)(read-byte (current-input-port))))))
          (rec (cdr code))))))

(define (parse str)
  (let1 ls (string->list str)
    (let rec ((ls ls)(acc '()))
      (if (null? ls)
          (reverse acc)
          (case (car ls)
            ((#\+ #\- #\< #\> #\. #\,)(rec (cdr ls)(cons (car ls) acc)))
            ((#\[)(rec (let1 rest (drop-while (pa$ (complement equal?) #\])(cdr ls))
                         (if (null? rest)
                             '()
                             (cdr rest)))
                       (cons (rec (cdr ls) '()) acc)))
            ((#\])(reverse acc))
            (else (rec (cdr ls) acc)))))))

(define (repl mem)
  (print ";; brainf*ck interactive shell")
  (print ";; q to exit.")
  (let rec ((ptr 0)(mem mem))
    (display "bfi> ")
    (flush)
    (let1 input (read-line)
      (when (equal? input "q")
        (exit 0))
      (receive (ptr mem)
          (interpret (parse (x->string input)) ptr mem)
        (rec ptr mem)))))

(define (run-bf str mem)
  (interpret (parse str) 0 mem))

(define (main args)
  (let-args (cdr args)
      ((memory-size "m|memory-size=i" 1024)
       (interactive "i|interactive")
       (help "h|help" => usage)
       (else (opt . _)
             (print "Unknown option : " opt)
             (usage))
       . rest)
    (let ((mem (make-vector memory-size 0)))
      (cond ((and (null? rest) interactive)(repl mem))
            ((null? rest)(run-bf (port->string (current-input-port)) mem))
            ((run-bf (file->string (car rest)) mem))
            (else (usage))))))


使い方

 $ bfi hello.bf 
Hello World!
 $ echo '>+++++++++[<++++++++>-]<.>+++++++[<++++>-]<+.+++++++..+++.[-]>++++++++[<++++>-]<.>+++++++++++[<+++++>-]<.>++++++++[<+++>-]<.+++.------.--------.[-]>++++++++[<++++>-]<+.[-]++++++++++.' | bfi
Hello World!
 $ bfi -i
;; brainf*ck interactive shell
;; q to exit.
bfi> >+++++++++[<++++++++>-]<.>+++++++[<++++>-]<+.+++++++..+++.[-]>++++++++[<++++>-]<.>+++++++++++[<+++++>-]<.>++++++++[<+++>-]<.+++.------.--------.[-]>++++++++[<++++>-]<+.[-]++++++++++.
Hello World!
bfi> q
 $


参考

基本的にここのbfiを参考にしました。 shiroさんのbfi2が理解できない。。。

プログラミングGauche

0 件のコメント:

コメントを投稿