packages feed

gdiff-th-0.1.0.0: examples/Parser.hs

{-# LANGUAGE NoMonomorphismRestriction, GADTs, FlexibleContexts,
    TemplateHaskell, LiberalTypeSynonyms, MultiParamTypeClasses,
    DeriveDataTypeable #-}
module Parser where
import Expr
import Text.Parsec hiding ((<+>), string)
import qualified Text.Parsec as P    
import qualified Text.Parsec.Token as P
import qualified Text.Parsec.Language as P
import Control.Applicative ((<$>))
import Data.Generic.Diff.TH
import Data.Generic.Diff   -- (EditScript(..), diff, Type, compress)
import System.Console.Terminfo.Color
import Text.PrettyPrint.Free hiding (parens)
import System.Console.Terminfo.Base
import System.Console.Terminfo.PrettyPrint
import Test.Feat
import Data.Typeable
import Utils
         
--Pretty printer
ppr :: Exp -> String
ppr e = case e of
   x :+: y -> ppr x ++ " + " ++ ppr y
   x :*: y -> ppr x ++ " * " ++ ppr y
   B x     -> show x

-- parser    
badParser :: String -> Either ParseError Exp                     
badParser x = runParser pExp () "" x where
    pExp = foldl chainr1 (parens pExp <|> pInt) $ 
                    map binOp [("+", (:+:)), ("*", (:*:))] 

    binOp (x, rest) = do
        spaces
        P.string x
        spaces
        return rest

    pInt = B <$> P.integer P.haskell

    parens = P.parens P.haskell
   
deriveEnumerable ''Exp 

-- A very important variant that will fail
pprToParseRoundTrip :: Exp -> Bool
pprToParseRoundTrip x = either (const False) (x ==) $ (badParser . ppr) x

checkParser = featCheck 12 pprToParseRoundTrip

-- A convienent hack for readablility       
instance Num Exp where
    (+) = (:+:)
    (*) = (:*:)
    fromInteger = B
    abs    = undefined
    signum = undefined

badParserBug = (*) ((*) 0 0) 0

fromRight' (Right x) = x

badParserDiff = showEdits $ diffExp badParserBug (fromRight' . badParser . ppr $ badParserBug) 

-- the fixed parser 
goodParser :: String -> Either ParseError Exp                     
goodParser x = runParser pExp () "" x where
    pExp = foldl chainl1 (parens pExp <|> pInt) $ 
                    map binOp [("+", (:+:)), ("*", (:*:))] 

    binOp (x, rest) = do
        spaces
        P.string x
        spaces
        return rest

    pInt = B <$> P.integer P.haskell

    parens = P.parens P.haskell