use parsec for the parser
[clean-tests.git] / datatype / Language / Quote.hs
1 {-# LANGUAGE TemplateHaskell #-}
2 {-# LANGUAGE QuasiQuotes #-}
3 {-# LANGUAGE TupleSections #-}
4 {-# LANGUAGE ParallelListComp #-}
5 module Language.Quote (cp) where
6
7 import Data.Char
8 import Data.Functor.Identity
9 --
10 import Language.Haskell.TH.Syntax
11 import Language.Haskell.TH.Quote
12 --
13 import Text.Parsec
14 import Text.Parsec.String
15 import Text.Parsec.Expr as E
16 import qualified Text.Parsec.Token as P
17 import Text.Parsec.Language (haskell)
18 --
19 import Language.GenDSL
20
21 cp :: QuasiQuoter
22 cp = QuasiQuoter
23 { quoteExp = \s->location >>= parseExpr s
24 , quotePat = undefined
25 , quoteType = undefined
26 , quoteDec = undefined
27 }
28 where
29 parseExpr :: MonadFail m => String -> Loc -> m Exp
30 parseExpr s loc =
31 case runParser p () "" s of
32 Left err -> fail $ show err
33 Right e -> return e
34 where
35 file = loc_filename loc
36 (line, col) = loc_start loc
37 p = getPosition >>= setPosition . mPos >> expr <* eof
38 mPos = (flip setSourceName) file .
39 (flip setSourceLine) line .
40 (flip setSourceColumn) col
41
42 -- Lexer
43 identifier,operator :: Parser String
44 identifier = P.identifier haskell
45 operator = P.operator haskell
46
47 parens,braces :: Parser a -> Parser a
48 braces = P.braces haskell
49 parens = P.parens haskell
50
51 commaSep :: Parser a -> Parser [a]
52 commaSep = P.commaSep haskell
53
54 symbol :: String -> Parser String
55 symbol = P.symbol haskell
56
57 reserved,reservedOp :: String -> Parser ()
58 reserved = P.reserved haskell
59 reservedOp = P.reservedOp haskell
60
61 -- Parser
62 pat :: Parser Pat
63 pat
64 = try (RecP <$> con <*> braces (commaSep $ (,) <$> var <* reservedOp "=" <*> pat))
65 <|> ConP <$> con <*> many pat
66 <|> try (AsP <$> var <* reservedOp "@" <*> pat)
67 <|> VarP <$> var
68 <|> WildP <$ symbol "_"
69 <|> LitP <$> lit
70 <|> parens pat
71
72 expr :: Parser Exp
73 expr = buildExpressionParser
74 [ [bin "^." AssocRight]
75 , [bin "*." AssocLeft, bin "/." AssocLeft]
76 , [bin "+." AssocLeft, bin "-." AssocLeft]
77 , [bin o AssocNone | o <- ["==.", "/=.", "<.", ">.", "<=.", ">=."]]
78 , [bin "&." AssocRight]
79 , [bin "|." AssocRight]
80 ] basic
81 where
82 bin :: String -> Assoc -> Operator String () Identity Exp
83 bin str = E.Infix $ ifx str <$ sat operator (str==)
84 (("Expected operator " ++ str ++ " but got ")++)
85
86 basic :: Parser Exp
87 basic
88 = try (AppE . VarE <$> var <*> (TupE <$> parens (commaSep expr)))
89 <|> VarE <$> var
90 <|> AppE (VarE (mkName "lit")) . LitE <$> lit
91 <|> parens expr
92 <|> mkCase <$ reserved "case" <*> expr <* reserved "of" <*> many1 match
93
94 match = (,) <$> pat <* reservedOp "->" <*> expr
95
96 sat :: Parser a -> (a -> Bool) -> (a -> String) -> Parser a
97 sat p f msg = try (p >>= \a->if f a then pure a else fail (msg a))
98
99 lit :: Parser Lit
100 lit
101 = CharL <$> P.charLiteral haskell
102 <|> IntegerL <$> P.natural haskell
103
104 con,var :: Parser Name
105 con = mkName <$> sat identifier (isUpper . head) ("Not a data constructor: "++)
106 var = mkName <$> sat identifier (isLower . head) ("Not a variable identifier: "++)
107
108 -- Convert case to if statements
109 mkCase :: Exp -> [(Pat, Exp)] -> Exp
110 mkCase name cases = foldr (uncurry mkCaseMatch) (VarE (mkName "bottom") `AppE` LitE (StringL "Exhausted case")) cases
111 where
112 mkCaseMatch :: Pat -> Exp -> Exp -> Exp
113 mkCaseMatch a e rest = case mkCasePred name a of
114 [] -> mkCaseLets (mkCaseBinding name a) e
115 ps -> VarE (mkName "if'") `AppE` foldl1 (ifx "&.") ps `AppE` (mkCaseLets (mkCaseBinding name a) e) `AppE` rest
116
117 mkCaseLets :: [Dec] -> Exp -> Exp
118 mkCaseLets [] e = e
119 mkCaseLets defs e = LetE defs e
120
121 conPtoRecP :: Name -> [Pat] -> Pat
122 conPtoRecP consName = RecP consName . zip (map (adtFieldName consName) [0..])
123
124 mkCasePred :: Exp -> Pat -> [Exp]
125 mkCasePred e (LitP l) = [ifx "==." (VarE (mkName "lit") `AppE` LitE l) e]
126 mkCasePred _ (VarP _) = []
127 mkCasePred e (ConP cons fs) = mkCasePred e $ conPtoRecP cons fs
128 mkCasePred e (InfixP l cons r) = mkCasePred e (ConP cons [l,r])
129 mkCasePred e (UInfixP l cons r) = mkCasePred e (ConP cons [l,r])
130 mkCasePred e (ParensP p) = mkCasePred e p
131 mkCasePred e (TildeP p) = mkCasePred e p
132 mkCasePred e (BangP p) = mkCasePred e p
133 mkCasePred e (AsP _ p) = mkCasePred e p
134 mkCasePred _ WildP = []
135 mkCasePred e (RecP cons fs) = VarE (predicateName cons) `AppE` e
136 : concatMap (\(n, p)->mkCasePred (VarE (selectorName n) `AppE` e) p) fs
137 mkCasePred _ p = error $ "Unsupported pat: " ++ show p
138
139 mkCaseBinding :: Exp -> Pat -> [Dec]
140 mkCaseBinding _ (LitP _) = []
141 mkCaseBinding e (VarP v) = [FunD v [Clause [] (NormalB $ e) []]]
142 mkCaseBinding e (ConP cons fs) = mkCaseBinding e $ conPtoRecP cons fs
143 mkCaseBinding e (InfixP l cons r) = mkCaseBinding e (ConP cons [l,r])
144 mkCaseBinding e (UInfixP l cons r) = mkCaseBinding e (ConP cons [l,r])
145 mkCaseBinding e (ParensP p) = mkCaseBinding e p
146 mkCaseBinding e (TildeP p) = mkCaseBinding e p
147 mkCaseBinding e (BangP p) = mkCaseBinding e p
148 mkCaseBinding e (AsP n p) = FunD n [Clause [] (NormalB $ e) []]:mkCaseBinding e p
149 mkCaseBinding _ WildP = []
150 mkCaseBinding e (RecP _ fs) = concatMap (\(n, p)->mkCaseBinding (VarE (selectorName n) `AppE` e) p) fs
151 mkCaseBinding _ p = error $ "Unsupported pat: " ++ show p