BFコンパイラー
以下のように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
|





dankogai
#3886()
Rating0/2=0.00
「どう書く?」でまだ出ていないのが不思議なお題。それがBF処理系。 ここでは、BFで書かれたソースを、同じ言語に変換するコンパイラーを募集します。
私自身、すでにPerlとJavaScriptに関しては http://blog.livedoor.jp/dankogai/archives/50545151.html でやっているのですが、他の言語バージョンも是非見たいので。
Dan the Brainf.ucker
see: Brainfuck - Wikipedia
1 reply [ reply ]