Brainfuck

今更ながらにBrainfuckを実装してみた。
いつもどおりHaskellで…
http://www.muppetlabs.com/~breadbox/bf/

言語仕様的にどっちかと言うとコンパイラのほうが簡単である。

main = interact $ (header++).(++footer).(>>= cvt).(filter (`elem` "><+-.,[]")) where
  cvt '>' = "++p;"
  cvt '<' = "--p;"
  cvt '+' = "++*p;"
  cvt '-' = "--*p;"
  cvt '.' = "putchar(*p);"
  cvt ',' = "*p=getchar();"
  cvt '[' = "while(*p){"
  cvt ']' = "}"
  cvt _   = ""

  header = "#include \n"++
	   "int main(){ static char dat[30000],*p=dat; "
  footer = "}\n"

Cへのコンパイラ
文字列の置換しかやっていない。
まぁ、こんなコードには何の面白みも無いのだが。

import Text.ParserCombinators.Parsec
import Data.Array.MArray
import Data.Array.IO
import Data.Word
import Data.Char
import Control.Monad
import System

type State = (IOUArray Int Word8,Int)

bfp :: Parser (State -> IO State)
bfp = many p >>= \pp -> return (\st -> foldM (flip ($)) st pp) where
  p   = (char '>' >> return (\(a,i) -> return (a,i+1)))
    <|> (char '<' >> return (\(a,i) -> return (a,i-1)))
    <|> (char '+' >> return (\(a,i) -> modifyArray i (+1) a >> return (a,i)))
    <|> (char '-' >> return (\(a,i) -> modifyArray i (flip (-)1) a >> return (a,i)))
    <|> (char '.' >> return (\(a,i) -> readArray a i >>= putChar.chr.fromEnum >> return (a,i)))
    <|> (char ',' >> return (\(a,i) -> getChar >>= writeArray a i.toEnum.ord >> return (a,i)))
    <|> (between (char '[') (char ']') bfp >>= return.while)
  while b (a,i) = do
    n <- readArray a i
    if (n/=0) then b (a,i) >>= while b else return (a,i)

modifyArray i f a = readArray a i >>= writeArray a i . f

main = do
  [f] <- getArgs
  c   <- readFile f
  case parse bfp "" (filter (`elem` "><+-.,[]") c) of
    Left err -> putStrLn "error at " >> print err
    Right f  -> newArray (0,29999) (0::Word8) >>= \a -> f (a,0) >> return ()

これがインタプリタ
思ったより小さくない。
パーズにParsecを使ったから多少冗長になっている気もする。
パーザーが返すものが計算だというのがミソといえばミソか。
動作速度は、Unboxed Mutable Arrayを使って、
計算の構築も一回だけなはずなのにあまり速くない。
どうしたものか。