{-# LANGUAGE RecursiveDo, FlexibleContexts #-} module Main where import Control.Applicative import Control.Exception import Control.DeepSeq import Criterion.Main import Data.Char import Data.Maybe import Text.Earley import qualified Text.Parsec as Parsec import qualified Text.Parsec.Pos as Parsec data Expr = Add Expr Expr | Mul Expr Expr | Var String deriving (Eq, Ord, Show) instance NFData Expr where rnf (Add a b) = rnf a `seq` rnf b rnf (Mul a b) = rnf a `seq` rnf b rnf (Var s) = rnf s type Token = String tokenParens :: Bool -> [Token] -> [Token] tokenParens True s = ["("] ++ s ++ [")"] tokenParens False s = s tokenExpr :: Int -> Expr -> [Token] tokenExpr _ (Var s) = [s] tokenExpr d (Add a b) = tokenParens (d > 0) $ tokenExpr 0 a ++ ["+"] ++ tokenExpr 1 b tokenExpr d (Mul a b) = tokenParens (d > 1) $ tokenExpr 1 a ++ ["*"] ++ tokenExpr 2 b linearSum :: Int -> Expr linearSum 1 = Var "x" linearSum n = Add (linearSum $ n - 1) (Var "x") treeSum :: Int -> Expr treeSum 1 = Var "x" treeSum n = let a = n `div` 2 -- will be at least 1 b = n - a in Add (treeSum a) (treeSum b) -- Earley parser expr :: Grammar r (Prod r String Token Expr) expr = mdo x1 <- rule $ Add <$> x1 <* namedToken "+" <*> x2 <|> x2 "sum" x2 <- rule $ Mul <$> x2 <* namedToken "*" <*> x3 <|> x3 "product" x3 <- rule $ Var <$> (satisfy isIdent "identifier") <|> namedToken "(" *> x1 <* namedToken ")" return x1 isIdent :: String -> Bool isIdent (x:_) = isAlpha x isIdent _ = False sepBy1 :: Prod r e t a -> Prod r e t op -> Grammar r (Prod r e t [a]) sepBy1 p op = mdo ops <- rule $ pure [] <|> (:) <$ op <*> p <*> ops rule $ (:) <$> p <*> ops expr' :: Grammar r (Prod r String Token Expr) expr' = mdo let var = Var <$> satisfy isIdent <|> token "(" *> mul <* token ")" mul <- fmap (foldl1 Mul) <$> add `sepBy1` token "*" add <- fmap (foldl1 Add) <$> var `sepBy1` token "+" return mul parseEarley :: [Token] -> Maybe Expr parseEarley input = listToMaybe (fst (fullParses (parser expr) input)) parseEarley' :: [Token] -> Maybe Expr parseEarley' input = listToMaybe (fst (fullParses (parser expr') input)) -- Parsec parsec type Parsec = Parsec.Parsec [Token] () parsecExpr :: Parsec Expr parsecExpr = mul where mul = foldl1 Mul <$> add `Parsec.sepBy1` t "*" add = foldl1 Add <$> var `Parsec.sepBy1` t "+" ident = Parsec.token id pos $ \y -> if isIdent y then Just (Var y) else Nothing var = ident <|> (t "(" *> mul <* t ")") t x = Parsec.token id pos $ \y -> if x == y then Just x else Nothing pos = const (Parsec.initialPos "") parseParsec :: [Token] -> Maybe Expr parseParsec = either (const Nothing) Just . Parsec.parse parsecExpr "" -- Our benchmark harness. linearInput :: Int -> (String, [Token]) linearInput size = (show size, tokenExpr 0 $ linearSum size) treeInput :: Int -> (String, [Token]) treeInput size = (show size, tokenExpr 0 $ treeSum size) inputBench :: (String, [Token]) -> Benchmark inputBench (name, input) = bench name $ nf id input earleyBench :: (String, [Token]) -> Benchmark earleyBench (name, input) = bench name $ nf parseEarley input earley'Bench :: (String, [Token]) -> Benchmark earley'Bench (name, input) = bench name $ nf parseEarley' input parsecBench :: (String, [Token]) -> Benchmark parsecBench (name, input) = bench name $ nf parseParsec input benchSizes :: [Int] benchSizes = [100, 200] -- [51, 101, 151, 201] linearInputs :: [(String, [Token])] linearInputs = map linearInput benchSizes treeInputs :: [(String, [Token])] treeInputs = map treeInput benchSizes main :: IO () main = do evaluate (rnf linearInputs) evaluate (rnf treeInputs) defaultMain [ -- bgroup "inputs" $ map inputBench linearInputs bgroup "earley" $ map earleyBench linearInputs , bgroup "earley'" $ map earley'Bench linearInputs , bgroup "parsec" $ map parsecBench linearInputs -- , bgroup "inputsTree" $ map inputBench treeInputs , bgroup "earleyTree" $ map earleyBench treeInputs , bgroup "earley'Tree" $ map earley'Bench treeInputs , bgroup "parsecTree" $ map parsecBench treeInputs ]