challenge BFコンパイラー

「どう書く?」でまだ出ていないのが不思議なお題。それがBF処理系。 ここでは、BFで書かれたソースを、同じ言語に変換するコンパイラーを募集します。

私自身、すでにPerlとJavaScriptに関しては http://blog.livedoor.jp/dankogai/archives/50545151.html でやっているのですが、他の言語バージョンも是非見たいので。

Dan the Brainf.ucker

以下のようにonelinerで可能です。 ただしLanguage::BF 0.03が必要です。 CodeRepos経由 で、

  • svn co svn.coderepos.org/share/lang/perl/Language-BF
  • cd Language-BF/trunk
  • perl Makefile.PL
  • make install

するか、CPANにVersion 0.03が現れるのをお待ち下さい。

Dan the Brainf.cker

1
2
3
4
perl -MLanguage::BF \
  -e 'print Language::BF->new_from_file(shift)->as_perl' t/hello.bf \
  | perl
Hello World!

Posted feedbacks - Haskell

あまり綺麗なコードではないですが...。
メモリは左右に伸びる無限リストで表現しました。

>=>演算子 (Kleisli composition)は非常に便利です。
GHC6.8で新しく追加されましたが,一応定義を書きました。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import Data.List
import System.Environment

putCode '>' = putStr "incP >=> "
putCode '<' = putStr "decP >=> "
putCode '+' = putStr "inc >=> " 
putCode '-' = putStr "dec >=> " 
putCode '.' = putStr "put >=> " 
putCode ',' = putStr "get >=> " 
putCode '[' = putStr "loop("
putCode ']' = putStr "return) >=> "

genCode source 
    = do putStr "runBF = "
         mapM_ putCode (source `intersect` "><+-.,[]")
         putStrLn "return"

main = do
    source <- readFile . head =<< getArgs

    putStrLn "import Data.Char"
    putStrLn "(>=>) m1 m2 = \\s -> m1 s >>= m2"
    putStrLn "incP (ps, n:ns)    = return (n:ps, ns)"
    putStrLn "decP (p:ps, ns)    = return (ps, p:ns)"
    putStrLn "inc (ps, n:ns)     = return (ps, n+1:ns)"
    putStrLn "dec (ps, n:ns)     = return (ps, n-1:ns)"
    putStrLn "put mem@(_, n:_)   = putChar (chr n) >> return mem"
    putStrLn "get mem@(ps, _:ns) = getChar >> (\\c -> return (ps, ord c:ns))"
    putStrLn "loop code mem@(_, 0:_) = return mem"
    putStrLn "loop code mem = code mem >>= loop code"
    genCode source
    putStrLn "main = runBF ([0,0..], [0,0..])"

仮想機械(機械状態とインストラクションセット)を明示的に定義した.

機械状態は,プログラムカウンタのスタック,プログラムカウンタ,ヒープ,ヒープポインタの4つ組み.インストラクションはBFのインストラクションに対応する.

  • '>' → incp
  • '<' → decp
  • '+' → incp
  • '-' → decc
  • '.' → putc
  • ',' → getc
  • ']' → jmpb
  • '[' → jmpf <PC>

jmpf 以外はオペランドのないインストラクション.jmpfは飛び先をオペランドとする.

% runghc bfc.hs hello_world

とやるとhello_world.bというBFコードファイルを読み,インストラクション列をbfrts.templというランタイムテンプレートの最後に追加して,hello_world.hsというHaskellのファイルを作成する.上のhello_world.bをコンパイルしてできたインストラクション列は,

codeL :: [Instruction]

codeL = [incc,incc,incc,incc,incc,incc,incc,incc,jmpf 22,incp,incc,incc,incc,incc,incc,incc,incc,incc,incc,decp,decc,jmpb,incp,putc,decp,incc,incc,incc,incc,incc,jmpf 41,incp,incc,incc,incc,incc,incc,incc,decp,decc,jmpb,incp,decc,putc,incc,incc,incc,incc,incc,incc,incc,putc,putc,incc,incc,incc,putc,decp,incc,incc,incc,incc,incc,incc,incc,incc,jmpf 77,incp,incp,incc,incc,incc,incc,decp,decp,decc,jmpb,incp,incp,putc,decp,decp,incc,incc,incc,incc,jmpf 97,incp,decc,decc,decc,decc,decc,decc,decp,decc,jmpb,incp,putc,decp,incc,incc,incc,incc,jmpf 115,incp,incc,incc,incc,incc,incc,incc,decp,decc,jmpb,incp,putc,incc,incc,incc,putc,decc,decc,decc,decc,decc,decc,putc,decc,decc,decc,decc,decc,decc,decc,decc,putc,incp,incc,putc]

で,これがbfrts.templの最後の追加されて hello_world.hs が出きる.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
-- bfc.hs : BF コンパイラ ---------------------------------------------------
module Main where

import Data.Char
import Data.List
import System.IO
import System.Environment

main = do { args <- getArgs
          ; case args of
              []  -> runcompiler stdin stdout
              s:_ -> do { ih <- openFile (s++".b") ReadMode
                        ; oh <- openFile (s++".hs") WriteMode
                        ; runcompiler ih oh
                        }
          }

runcompiler ih oh =
 do { cs <- hGetContents ih
    ; let cs' = zip [0..] $ filter (not . isSpace) cs
    ; outputCode oh $ compile cs'
    }

compile [] = []
compile (c:cs) = case c of
  (_,'>') -> "incp" : compile cs
  (_,'<') -> "decp" : compile cs
  (_,'+') -> "incc" : compile cs
  (_,'-') -> "decc" : compile cs
  (_,'.') -> "putc" : compile cs
  (_,',') -> "getc" : compile cs
  (_,']') -> "jmpb" : compile cs
  (_,'[') -> ("jmpf "++show pc) : compile cs
               where pc = findpc [] cs
  _       -> error "invalid input"

findpc s []     = error "syntax error"
findpc s (c:cs) = case c of
  (_,'[') -> findpc ('[':s) cs
  (p,']') -> case s of
               [] -> p+1
               _  -> findpc (tail s) cs
  _       -> findpc s cs

outputCode h s 
 = do { hd <- readFile "bfrts.tmpl"
      ; hPutStrLn h hd
      ; hPutStrLn h "codeL :: [Instruction]"
      ; hPutStr h "codeL = ["
      ; hPutStr h (concat (intersperse "," s))
      ; hPutStr h "]\n"
      }

-- bfrts.templ : ランタイムテンプレート -------------------------------------

module Main where

import Control.Monad.State
import Data.Array as A
import Data.Char
import Data.IntMap as M
import Debug.Trace

type PC      = Int
type Code    = Array PC Instruction
type Heap    = M.IntMap Char
type Stack   = [PC]
type Pointer = Int
type VM   = (Stack,PC,Heap,Pointer)

type Instruction = VM -> StateT VM IO ()

top = head
pop = tail
push = (:)

-- initial machine state

iStat = ([],0,M.fromList $ zip [0..29999] (repeat $ chr 0),0)

-- instruction set

incp,decp,incc,decc,putc,getc,jmpb :: Instruction
jmpf :: PC -> Instruction
incp (s,c,h,p) = put (s,succ c,h,succ p)
decp (s,c,h,p) = put (s,succ c,h,pred p)
incc (s,c,h,p) = put (s,succ c,M.update (Just . succ) p h,p)
decc (s,c,h,p) = put (s,succ c,M.update (Just . pred) p h,p)
putc (s,c,h,p) = liftIO (putChar (h M.! p)) >> put (s,succ c,h,p)
getc (s,c,h,p) = liftIO (getChar >>= \ ch -> return (s,succ c,M.update (const (Just ch)) p h,p)) >>= put
jmpb (s,c,h,p) = if h M.! p == chr 0 then put (pop s,succ c,h,p) else put (s,top s,h,p)
jmpf pc (s,c,h,p) = if h M.! p == chr 0 then put (s,pc,h,p) else put (push (succ c) s,succ c,h,p)

-- run compiled bf code
             
main :: IO ()
main = evalStateT execute iStat >> putStrLn ""

run = do { stat@(s,c,h,p) <- get
         ; if inRange (bounds code) c then (code A.! c) stat >> run
           else return () 
         }

code :: Code 
code = listArray (0,length codeL - 1) codeL

-- この下にコンパイル済みの codeL :: [Instruction] が追加される

高階関数を使ってみたい年頃なので高階関数使った版findpc を書いてみました。

findpc はループがネストしているとき '[' が来るごとに何度も呼ばれるのが
ちょっともったいない気もします。

それと hClose oh しないとバッファが flush されませんでした。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
-- import Maybe
-- import Control.Monad

-- -- where pc = findpc [] cs
-- where pc = findpc cs

findpc cs = fromMaybe (error "syntax error")
  $ msum $ snd $ mapAccumL f 0 cs  where
    f 0 (p,']') = (0, Just (p+1))
    f s (_,c) = (s+d, Nothing) where
      d = case c of; ']' -> (-1); '[' -> 1; _ -> 0

Index

Feed

Other

Link

Pathtraq

loading...