四則演算

Haskellの型システムが気になって何年かぶりにHaskellを使ってみました。
もう殆ど忘れていたのだけど、一から勉強するよりは楽に使えたのだと思います。
正規表現ライブラリを使って標準入力の引数を四則演算するだけの物を作ってみました。

import Text.Regex.Posix
import System.Environment 

data E
  = EInt Integer
  | EVar String
  | EAdd E E
  | ESub E E
  | EMul E E
  | EDiv E E
  deriving(Show, Eq)

data Tk
  = TkInt Integer
  | TkVar String
  | TkOp String
  deriving(Show, Eq)

main = do
  v <- getArgs
  e <- return (head v)
  print e
  b <- return (let (c,_) = parse e in c)
  print b
  print (eval b)

lexx :: String -> [Tk]
lexx src = 
   case (src =~ "^[ \n\t\r]*(([0-9]+)|([_a-zA-Z][_a-zA-Z0-9]*)|([+*/\\-]+))" :: [[String]]) of
    [] -> []
    [[k1,_,i,"",""]] -> (TkInt (read i :: Integer)) : (lexx (drop (length k1) src))
    [[k1,_,"",id,""]] -> (TkVar id) : (lexx (drop (length k1) src))
    [[k1,_,"","",op]] -> (TkOp op) : (lexx (drop (length k1) src))


parse :: String -> (E,[Tk])
parse l = expr (lexx l)

expr :: [Tk] -> (E,[Tk])
expr xs = rep(term xs)
  where
    rep(x1,(TkOp "+"):xs2) = let (x3,xs3) = term xs2 in rep(EAdd x1 x3, xs3)
    rep(x1,(TkOp "-"):xs2) = let (x3,xs3) = term xs2 in rep(ESub x1 x3, xs3)
    rep(x,xs) = (x, xs)

term :: [Tk] -> (E,[Tk])
term xs = rep(fact xs)
  where
    rep(x1,(TkOp "*"):xs2) = let (x3,xs3) = fact xs2 in rep(EMul x1 x3, xs3)
    rep(x1,(TkOp "/"):xs2) = let (x3,xs3) = fact xs2 in rep(EDiv x1 x3, xs3)
    rep(x1,xs1) = (x1,xs1)

fact :: [Tk] -> (E,[Tk])
fact ((TkInt i):xs) = (EInt i, xs)
fact ((TkVar id):xs) = (EVar id, xs)
fact xs = (EInt 0, xs)

eval :: E -> Integer
eval(EInt a) = a
eval(EAdd a b) = (eval a) + (eval b)
eval(ESub a b) = (eval a) - (eval b)
eval(EMul a b) = (eval a) * (eval b)
eval(EDiv a b) = (eval a) `div` (eval b)

追記:

一般的なHaskellでのパーサはParsec3やattoparsecを使います。*1 *2
正規表現については*3*4を参考にしました。

*1:Utotch Blog:Haskell で parser を書くには 初心者編 http://utotch.blogspot.jp/2011/12/haskell-parser.html

*2:とりあえず雑記帳:04 attoparsec https://sites.google.com/site/toriaezuzakki/haskell/attoparsec

*3:sirocco の書いてもすぐに忘れるメモ:Haskell正規表現すげー !!http://d.hatena.ne.jp/sirocco/20090416/1239852340

*4:すぐに忘れる脳みそのためのメモ:Haskell正規表現 http://jutememo.blogspot.jp/2008/07/haskell.html