読者です 読者をやめる 読者になる 読者になる

わどぅー

記事については、中間出力の場合も多いので間違ってたらごめんなさい。twitter は waddlaw です。どうぞよろしく。

Haskell でHQ9+ のインタプリタ書いてみた

Ruby で作る奇妙なプログラミング言語という本を読んでいるのですが、めちゃめちゃ面白いので Haskell の勉強をかねて実装してみた。

インタプリタのドライバ部分は GHCi の実装で Haskeline 使ってたので真似した。
 -http://hackage.haskell.org/packages/archive/haskeline/0.6.4.5/doc/html/System-Console-Haskeline.html

eval でループしててカッコ悪いから、もう1つ外側の関数作った方が良かったのかなぁ。関数的に書くのってむずい。

module HQ9Plus (main) where

import System.Console.Haskeline
import Data.Char (toUpper)

data HqState = HqState {
	  src :: SrcCode
	, count :: Int
	}

type SrcCode = String

main :: IO ()
main = runInputT defaultSettings loop
	where 
		loop :: InputT IO ()
		loop = do
			minput <- getInputLine "HQ9Plus> "
			case minput of
				Nothing -> return ()
				Just "quit" -> return ()
				Just input -> do
					eval (defaultState input) input
					loop

eval :: HqState -> String -> InputT IO ()
eval _ [] = return ()
eval s ('H':cs) = outputStrLn hello >> eval s cs
eval s ('Q':cs) = outputStrLn (src s) >> eval s cs
eval s ('9':cs) = outputStrLn (bottlesOfBeer 99) >> eval s cs
eval s ('+':cs) = eval (countUpState s) cs
eval s (c:cs) = eval s cs

hello :: String
hello = "Hello, world!"

bottlesOfBeer :: Int -> String
bottlesOfBeer n 
	| n == 0 = createMessage ("no more bottles", "99 bottles", "Go to the store and buy some more")
	| n == 1 = createMessage ("1 bottle", "no more bottles", "Take one down and pass it around") ++ bottlesOfBeer (n-1)
	| n == 2 = createMessage ("2 bottles", "1 bottle", "Take one down and pass it around") ++ bottlesOfBeer (n-1)
	| otherwise = createMessage (show n ++ " bottles", (show $ n-1) ++ " bottles", "Take one down and pass it around") ++ bottlesOfBeer (n-1)


createMessage :: (String, String, String) -> String
createMessage (before, after, action) =  sentence1 ++ sentence2 ++ "\n"
	where
		s1 = " of beer on the wall"
		s2 = " of beer.\n"
		sentence1 = capitalize before ++ s1 ++ ", " ++ before ++ s2
		sentence2 = action ++ ", " ++ after ++ s1 ++ ".\n"
		capitalize (c:cs) = toUpper c : cs

defaultState :: SrcCode -> HqState
defaultState code = HqState {src=code, count=0}

countUpState :: HqState -> HqState
countUpState s = s {count=count s + 1}

その他の Haskell 実装