{-# LANGUAGE TupleSections #-} module Text.Papillon.SyntaxTree ( Peg, Definition, Selection, Expression, PlainExpression, Check, ReadFrom(..), Q, STPegQ, PegQ, DefinitionQ, SelectionQ, ExpressionQ, PlainExpressionQ, CheckQ, ReadFromQ, stPegQ, definitionQ, normalSelectionQ, plainSelectionQ, expressionQ, plainExpressionQ, check, fromSelectionQ, Lookahead(..), Lists(..), fromTokenChars, expressionSugar, selectionType, pprCheck, nameFromRF, PegFileQ, mkPegFile, PPragma(..), ModuleName, Exports, Code, dvCharsN ) where import Language.Haskell.TH import Language.Haskell.TH.PprLib import Control.Arrow ((***)) import Data.List dvCharsN :: String dvCharsN = "char" data Lookahead = Here | Ahead | NAhead String deriving (Show, Eq) data Lists = List | List1 | Optional deriving (Show, Eq) type STPeg = (Maybe Type, Type, Peg) type Peg = [Definition] type Definition = (String, Maybe Type, Selection) type Selection = Either [Expression] [PlainExpression] type Expression = Either ([(Lookahead, Check)], Exp) Exp type PlainExpression = [(Lookahead, ReadFrom)] type Check = ((Pat, String), ReadFrom, Maybe (Exp, String)) data ReadFrom = FromVariable (Maybe String) | FromSelection Selection | FromL Lists ReadFrom deriving Show type STPegQ = STPeg type PegQ = Peg type DefinitionQ = Definition type SelectionQ = Selection type ExpressionQ = Expression type PlainExpressionQ = PlainExpression type CheckQ = Check type ReadFromQ = ReadFrom stPegQ :: Maybe Type -> Type -> PegQ -> STPegQ stPegQ = (,,) fromSelectionQ :: SelectionQ -> ReadFromQ fromSelectionQ sel = FromSelection sel definitionQ :: String -> Maybe Type -> SelectionQ -> DefinitionQ definitionQ name typq selq = let sel = selq typ = case typq of Just t -> Just t _ -> Nothing in (name, typ, sel) normalSelectionQ :: [ExpressionQ] -> SelectionQ normalSelectionQ expqs = Left expqs plainSelectionQ :: [PlainExpressionQ] -> SelectionQ plainSelectionQ expqs = Right expqs expressionQ :: ([(Lookahead, CheckQ)], Exp) -> ExpressionQ expressionQ (ls, ex) = let e = ex l = map (\(la, c) -> (la ,) c) ls in Left (l, e) plainExpressionQ :: [(Lookahead, ReadFromQ)] -> PlainExpressionQ plainExpressionQ ls = map (\(la, c) -> (la ,) c) ls check :: (Pat, String) -> ReadFromQ -> Maybe (Exp, String) -> CheckQ check (pat, pcom) rfq (Just (test, tcom)) = do let rf = rfq p = pat t = test in ((p, pcom), rf, Just (t, tcom)) check (pat, pcom) rfq Nothing = do let rf = rfq p = pat in ((p, pcom), rf, Nothing) expressionSugar :: Exp -> ExpressionQ expressionSugar pm = Right pm fromTokenChars :: String -> ReadFromQ fromTokenChars cs = do let ex = expressionSugar $ InfixE Nothing (VarE $ mkName "elem") $ Just $ LitE $ StringL cs FromSelection $ Left [ex] pprCheck :: Check -> Doc pprCheck ((pat, _), rf, test) = ppr pat <> colon <> ppr rf <> maybe empty (brackets . ppr . fst) test instance Ppr ReadFrom where ppr (FromVariable (Just v)) = text v ppr (FromVariable _) = empty ppr (FromL l rf) = ppr rf <> ppr l ppr (FromSelection sel) = parens $ ps sel where ps = hsep . intersperse (char '/') . either (map pe) (map ppe) pe (Left (ex, hs)) = (<+> braces (ppr hs)) $ hsep $ map (uncurry ($) . (((<>) . ppr) *** pprCheck)) ex pe (Right ex) = char '<' <> ppr ex <> char '>' ppe = hsep . map (uncurry (<>) . (ppr *** ppr)) instance Ppr Lookahead where ppr Here = empty ppr Ahead = char '&' ppr (NAhead _) = char '!' instance Ppr Lists where ppr List = char '*' ppr List1 = char '+' ppr Optional = char '?' definitionType :: Peg -> Type -> Definition -> Type definitionType _ _ (_, Just typ, _) = typ definitionType peg tk (_, _, sel) = selectionType peg tk sel selectionType :: Peg -> Type -> Selection -> Type selectionType peg tk e = do case e of Right ex -> foldr (\x y -> (eitherT `AppT` x) `AppT` y) (last $ types ex) (init $ types ex) Left [Left ex] | tc ex -> tk Left [Right _] -> tk _ -> error "selectionType: can't get type" where eitherT = ConT $ mkName "Either" types e' = map (plainExpressionType peg tk) e' tc ([(Here, ((VarP p, _), FromVariable Nothing, _))], VarE v) = p == v tc _ = False plainExpressionType :: Peg -> Type -> PlainExpression -> Type plainExpressionType peg tk e = let fe = filter ((== Here) . fst) e in foldl AppT (TupleT $ length fe) $ map (readFromType peg tk . snd) $ fe readFromType :: Peg -> Type -> ReadFrom -> Type readFromType peg tk (FromVariable (Just v)) = definitionType peg tk $ searchDefinition peg v readFromType peg tk (FromSelection sel) = selectionType peg tk sel readFromType _ tk (FromVariable _) = tk readFromType peg tk (FromL l rf) = lt l `AppT` readFromType peg tk rf where lt Optional = ConT $ mkName "Maybe" lt _ = ListT searchDefinition :: Peg -> String -> Definition searchDefinition peg name = case flip filter peg $ (== name) . \(n, _, _) -> n of [d] -> d _ -> error "searchDefinitionQ: bad" nameFromSelection :: Selection -> [String] nameFromSelection exs = concat $ (either (mapM nameFromExpression) (mapM nameFromPlainExpression) exs) nameFromExpression :: Expression -> [String] nameFromExpression (Left e) = nameFromCheck $ snd $ head $ fst e nameFromExpression (Right _) = [dvCharsN] nameFromPlainExpression :: PlainExpression -> [String] nameFromPlainExpression = concat . map (nameFromRF . snd) nameFromCheck :: Check -> [String] nameFromCheck (_, rf, _) = nameFromRF rf nameFromRF :: ReadFrom -> [String] nameFromRF (FromVariable (Just s)) = [s] nameFromRF (FromVariable _) = [dvCharsN] nameFromRF (FromL _ rf) = nameFromRF rf nameFromRF (FromSelection sel) = nameFromSelection sel type PegFile = ([PPragma], ModuleName, Maybe Exports, Code, STPeg, Code) type PegFileQ = Q PegFile data PPragma = LanguagePragma [String] | OtherPragma String deriving Show type ModuleName = [String] type Exports = String type Code = String mkPegFile :: [PPragma] -> Maybe ([String], Maybe String) -> String -> String -> STPegQ -> String -> PegFileQ mkPegFile ps (Just md) x y zq w = do let z = zq return (ps, fst md, snd md, x ++ "\n" ++ y, z, w) mkPegFile ps Nothing x y zq w = do let z = zq return (ps, [], Nothing, x ++ "\n" ++ y, z, w)