{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE TemplateHaskell #-}
import Criterion.Main
import Prelude hiding ((.), id)
import Control.Arrow
import Control.Category
import Data.Data (Data, Typeable)
import qualified Data.Text.Lazy as TL
import qualified Language.Sexp as Sexp
import Language.SexpGrammar
import Language.SexpGrammar.TH
newtype Ident = Ident String
deriving (Show)
data Expr
= Var Ident
| Lit Int
| Add Expr Expr
| Mul Expr Expr
| Inv Expr
| IfZero Expr Expr (Maybe Expr)
| Apply [Expr] String Prim -- inconvenient ordering: arguments, useless annotation, identifier
deriving (Show)
data Prim
= SquareRoot
| Factorial
| Fibonacci
deriving (Show, Eq, Enum, Bounded, Data, Typeable)
return []
instance SexpIso Prim
instance SexpIso Ident where
sexpIso = $(match ''Ident)
(\_Ident -> _Ident . symbol')
instance SexpIso Expr where
sexpIso = $(match ''Expr)
(\_Var -> _Var . sexpIso)
(\_Lit -> _Lit . int)
(\_Add -> _Add . list (el (sym "+") >>> el sexpIso >>> el sexpIso))
(\_Mul -> _Mul . list (el (sym "*") >>> el sexpIso >>> el sexpIso))
(\_Inv -> _Inv . list (el (sym "invert") >>> el sexpIso))
(\_IfZero -> _IfZero . list (el (sym "cond") >>> props ( Kw "pred" .: sexpIso
>>> Kw "true" .: sexpIso
>>> Kw "false" .:? sexpIso )))
(\_Apply -> _Apply . -- Convert prim :- "dummy" :- args :- () to Apply node
list
(el (sexpIso :: SexpG Prim) >>> -- Push prim: prim :- ()
el (kw (Kw "args")) >>> -- Recognize :args, push nothing
rest (sexpIso :: SexpG Expr) >>> -- Push args: args :- prim :- ()
swap >>> -- Swap: prim :- args :- ()
push "dummy" >>> -- Push "dummy": "dummy" :- prim :- args :- ()
swap -- Swap: prim :- "dummy" :- args :- ()
))
parseExpr :: Sexp -> Either String Expr
parseExpr = parseSexp sexpIso
genExpr :: Expr -> Either String Sexp
genExpr = genSexp sexpIso
expr :: TL.Text -> Expr
expr = either error id . decode
benchCases :: [(String, TL.Text)]
benchCases = map (\a -> ("expression " ++ take 40 (TL.unpack a) ++ "...", a))
[ "(+ 1 20)"
, "(+ (+ 2 20) 0)"
, "(+ (+ 3 20) (+ 10 20))"
, "(+ (+ (+ 4 20) (+ 10 20)) 0)"
, "(+ (+ (+ 5 20) (+ 10 20)) (+ 10 20))"
, "(+ (+ (+ 6 20) (+ 10 20)) (+ (+ 10 20) 0))"
, "(+ (+ (+ 7 20) (+ 10 20)) (+ (+ 10 20) (+ 10 20)))"
, "(cond :pred (+ 42 x) :false (fibonacci :args 3) :true (factorial :args (* 10 (+ 1 2))))"
, "(invert (* (+ (cond :pred (+ 42 314) :false (fibonacci :args 3) :true (factorial :args (* 10 (+ 1 2)))) (cond :pred (+ 42 28) :false (fibonacci :args 3) :true (factorial :args (* 10 (+ 1 2))))) (+ (cond :pred (+ 42 314) :false (fibonacci :args 3) :true (factorial :args (* 10 (+ foo bar)))) (cond :pred (+ 42 314) :false (fibonacci :args 3) :true (factorial :args (* 10 (+ 1 2)))))))"
]
benchCases_expr :: [(String, Expr)]
benchCases_expr = map (second expr) benchCases
benchCases_sexp :: [(String, Sexp)]
benchCases_sexp = map (second (either error id . genExpr)) benchCases_expr
main :: IO ()
main = defaultMain
[ bgroup "generation" . map (\(name, expr) -> bench name $ whnf genExpr expr) $ benchCases_expr
, bgroup "parsing" . map (\(name, sexp) -> bench name $ whnf parseExpr sexp) $ benchCases_sexp
]