{-# LANGUAGE TemplateHaskell, QuasiQuotes, FlexibleContexts, PackageImports, PatternGuards #-} {-# OPTIONS_GHC -fno-warn-name-shadowing -fno-warn-unused-binds -fno-warn-unused-matches -fno-warn-unused-do-bind #-} module File.Binary.Parse ( parse, Structure, sName, sDerive, sArgName, sArgType, sItems, SItem, argOf, valueOf, constant, Value, expression, rights' ) where import Text.Peggy (peggy, parseString, space, defaultDelimiter) import Language.Haskell.TH ( ExpQ, integerL, litE, varE, conE, listE, tupE, appE, uInfixE, parensE, TypeQ, conT, listT, tupleT, appT, Name, mkName, FieldExp) import "monads-tf" Control.Monad.Reader (Reader, runReader, ask) import Control.Applicative ((<$>), (<*>)) import Control.Arrow(first) import Data.Maybe (fromMaybe) import Numeric (readHex) -------------------------------------------------------------------------------- parse :: String -> Structure parse = either (error . show) id . parseString top "" data Structure = Structure { sName :: Name, sDerive :: [Name], sArgName :: String, sArgType :: TypeQ, sItems :: [SItem] } data SItem = SItem { argOf :: Expression, valueOf :: Value } type Expression = Reader ([FieldExp], ExpQ, String) ExpQ rights' :: [Value] -> [(Name, TypeQ)] rights' = map (first fromRight) . filter (isRight . fst) fromRight :: Either a b -> b fromRight (Right x) = x fromRight _ = error "not Right" isRight :: Either a b -> Bool isRight (Right x) = True isRight _ = False expression :: [FieldExp] -> ExpQ -> String -> Expression -> ExpQ expression ret arg argn e = runReader e (ret, arg, argn) type Value = (Either Constant Name, TypeQ) data Constant = Integer Integer | String String | Bool Bool deriving Show constant :: (Integer -> a) -> (String -> a) -> (Bool -> a) -> Constant -> a constant f _ _ (Integer i) = f i constant _ f _ (String s) = f s constant _ _ f (Bool b) = f b identify :: String -> ([FieldExp], ExpQ, String) -> ExpQ -- identify "_" _ = wildP identify var (ret, arg, argn) | var == argn = arg | Just var' <- lookup (mkName var) ret = return var' | otherwise = varE $ mkName var [peggy| top :: Structure = emp lname der arg dat* { Structure $2 $3 (fst $4) (snd $4) $5 } der :: [Name] = emp 'deriving' sp ln (sp ',' sp ln)* { mkName $3 : map (\(_, _, n) -> mkName n) $4 } / '' { [] } arg :: (String, TypeQ) = emp var sp '::' sp typ { ($2, $5) } / '' { ("_", conT $ mkName "()") } dat :: SItem = emp ex? spn typS sp ':' spn val { SItem (fromMaybe (return $ conE $ mkName "()") $2) ($7, $4) } -- { SItem (fromMaybe (return $ conE $ mkName "()") $2) $ -- either Left (Right . second (const $4)) $7 } typS :: TypeQ = '{' typ '}' / '' { conT $ mkName "Int" } val :: Either Constant Name = var { Right $ mkName $1 } / num { Left $ Integer $1 } / string { Left $ String $1 } / 'True' { Left $ Bool True } / 'False' { Left $ Bool False } ex :: Expression = ex sp op sp exOp1 { uInfixE <$> $1 <*> $3 <*> $5 } / exOp1 exOp1 :: Expression = exOp1 sp ex1 { appE <$> $1 <*> $3 } / ex1 ex1 :: Expression = '(' ex (sp ',' sp ex)+ ')' { (.) tupE . (:) <$> $1 <*> mapM (\(_, _, e) -> e) $2 } / '(' ex ')' { parensE <$> $1 } / '()' { return $ conE $ mkName "()" } / num { return $ litE $ integerL $1 } / lname { return $ conE $1 } / var { identify $1 <$> ask } / '[' list ']' { listE <$> $1 } list :: Reader ([FieldExp], ExpQ, String) [ExpQ] = ex sp "," sp list { (:) <$> $1 <*> $4 } / ex { (: []) <$> $1 } op :: Expression = [!\\#$%&*+./<=>?@^|~\-]+ { return $ varE $ mkName $1 } / '`' var '`' { return $ varE $ mkName $1 } typ :: TypeQ = typ typ1 { appT $1 $2 } / typ1 typ1 :: TypeQ = '(' typ sp (',' sp typ)+ ')' { foldl appT (tupleT $ length $3 + 1) $ $1 : map snd $3 } / '(' typ ')' / '()' { conT $ mkName "()" } / '[' typ ']' { appT listT $1 } / lname { conT $1 } lname :: Name = (ln '.')* ln { mkName $ concatMap (++ ".") $1 ++ $2 } var :: String = (ln '.')* sn { concatMap (++ ".") $1 ++ $2 } num :: Integer = '0x' [0-9a-fA-F]+ { fst $ head $ readHex $1 } / [1-9][0-9]* { read $ $1 : $2 } / '0' { 0 } string :: String = '\"' char* '\"' char :: Char = [^\\\"] / '\\' esc esc :: Char = 'n' { '\n' } / 'r' { '\r' } / '\\' { '\\' } / 'SUB' { '\SUB' } ln :: String = [A-Z][_a-zA-Z0-9]* { $1 : $2 } sn :: String = [_a-z][_a-zA-Z0-9]* { $1 : $2 } spn :: () = (comm / [ \t\n])* { () } sp :: () = (comm / [ \t])* { () } emp :: () = (comm / lcomm / [ \n])* { () } lcomm :: Char = '--' [^\n]* [\n] { ' ' } comm :: Char = '{-' (!'{-' !'-}' . / comm)* '-}' { ' ' } |]