import Data.Char import Control.Monad.State type Lexeme = String type Lexer l = State String l runLexer :: Lexer l -> String -> (l, String) runLexer = runState lexResult :: Lexer l -> String -> l lexResult = evalState lexWord :: Lexer Lexeme lexWord = State $ break isSpace . dropWhile isSpace lexSkipChar :: Lexer () -- 入力文字列の先頭の1文字を読み捨てる lexSkipChar = modify $ \ s -> if null s then s else tail s -- modify は Control.Monad.State で定義されている. -- modify :: (MonadState s m) => (s -> s) -> m () -- modify f = do { s <- get; return (f s) } lexUntilSep :: Char -> Lexer Lexeme -- 指定した区切り文字の直前までを読み込む lexUntilSep sep = State $ break (sep==) lexBracketed :: Char -> Char -> Lexer Lexeme -- 指定した文字に挟まれている部分を読み込む lexBracketed open close = do { lexUntilSep open -- 字句開始位置手前まで読み飛ばす ; lexSkipChar -- 字句開始文字読み捨てる ; b <- lexUntilSep close -- 字句終了位置手前まで読み込む ; lexSkipChar -- 字句終了文字読み捨てる ; return b -- 字句を返す } lex_h, lex_l, lex_u, lex_t, lex_r, lex_s, lex_b :: Lexer Lexeme lex_h = lexWord -- リモートホスト lex_l = lexWord -- リモートログ名 lex_u = lexWord -- リモートユーザー lex_t = lexBracketed '[' ']' -- リクエスト受付け時刻 lex_r = lexBracketed '"' '"' -- リクエストライン lex_s = lexWord -- ステータス lex_b = lexWord -- レスポンスボディ長 -- Lexer 部品で構成した accessLog accessLog :: String -> [String] accessLog = lexResult $ sequence [lex_h,lex_l,lex_u,lex_t,lex_r,lex_s,lex_b]