1{-# LANGUAGE LambdaCase #-}
2{-# LANGUAGE OverloadedStrings #-}
3{-# LANGUAGE TupleSections #-}
5{-# OPTIONS_GHC -fno-warn-orphans #-}
8-- Copyright : (c) 2015 Brendan Hay
9-- License : Mozilla Public License, v. 2.0.
10-- Maintainer : Brendan Hay <brendan.g.hay@gmail.com>
11-- Stability : provisional
12-- Portability : non-portable (GHC extensions)
14module Gen.Syntax where
16import Control.Lens hiding (iso, mapping, op, pre,
18import Data.Foldable (foldl', foldr')
20import qualified Data.HashMap.Strict as Map
22import Data.Semigroup ((<>))
24import Data.Text (Text)
25import qualified Data.Text as Text
26import Data.Text.Manipulate
31import Language.Haskell.Exts.Build
32import Language.Haskell.Exts.SrcLoc
33import Language.Haskell.Exts.Syntax hiding (Int, Lit)
35 -- type API = :<|> ...
36apiAlias :: Name -> [Name] -> Decl
37apiAlias n ls = TypeDecl noLoc n [] alias
39 alias = case map (TyCon . UnQual) ls of
41 x:xs -> foldl' (\l r -> TyInfix l (UnQual (sym ":<|>")) r) x xs
43-- type Method = :> ...
44verbDecl :: Service s r -> Local -> Local -> Method -> Decl
45verbDecl s p l m = TypeDecl noLoc (vname p l) [] alias
47 alias = case path ++ qry of
49 x:xs -> foldl' (\l r -> TyInfix l (UnQual (sym ":>")) r) x xs
51-- Need to deal with interpolation?
55 . filter (not . Text.null)
56 $ Text.split (== '/') (_svcServicePath s)
57 <> Text.split (== '/') (_mPath m)
60 | Just (c, t) <- Text.uncons x
62 , Just k <- Map.lookup (Local (Text.init t)) params
63 , _prmLocation k == Path
64 = TyApp (TyApp (tycon "Capture")
66 (literal (_prmLiteral k))
69 -- FIXME: order by _mParameterOrder
71 qry = mapMaybe f (Map.toList params)
74 -- FIXME: types, many/repeated
75 let t = literal (_prmLiteral p)
77 in case _prmLocation p of
78 Query -> Just $ TyApp (TyApp (tycon "QueryParam") n) t
81 params = _svcParameters s <> _mParameters m
84 verb = TyApp (TyApp meth typ) res
86 meth = TyCon . unqual . Text.unpack $ Text.toTitle (_mMethod m)
88 res = maybe (tycon "()") (tycon . Free) (_mResponse m)
90objDecl :: Id -> Pre -> Map Local Solved -> AST Decl
91objDecl n p rs = DataDecl noLoc arity [] d [] [conDecl d p rs] <$> derivings n
95 arity | Map.size rs == 1 = NewType
96 | otherwise = DataType
98conDecl :: Name -> Pre -> Map Local Solved -> QualConDecl
99conDecl n p rs = QualConDecl noLoc [] [] body
101 body = case Map.toList rs of
103 [x] -> RecDecl n [field internal x]
104 xs -> RecDecl n (map (field (strict . internal)) xs)
106 field f (l, v) = ([fname p l], f (_type v))
108ctorSig :: Id -> Map Local Solved -> Decl
109ctorSig n rs = TypeSig noLoc [cname n] ts
111 ts = foldr' TyFun (TyCon (UnQual (dname n))) ps
112 ps = parameters (Map.elems rs)
114ctorDecl :: Id -> Pre -> Map Local Solved -> Decl
115ctorDecl n p rs = sfun noLoc c ps (UnGuardedRhs rhs) noBinds
120 rhs | Map.null rs = var d
121 | otherwise = RecConstr (UnQual d) $
122 map (uncurry (fieldUpdate p)) (Map.toList rs)
124 ps = map (pname p) . Map.keys $ Map.filter (view required) rs
126fieldUpdate :: Pre -> Local -> Solved -> FieldUpdate
127fieldUpdate p l s = FieldUpdate (UnQual (fname p l)) rhs
129 rhs | s ^. required = v
130 | Just x <- s ^. defaulted = def x (_schema s)
131 | Just x <- iso (_type s) = infixApp x "#" v
132 | otherwise = var (name "Nothing")
137 Enum {} -> var (bname (_prefix s) x)
138 Lit _ Bool -> lit (upperHead x)
141 --- FIXME: lift to AST
142 s -> error $ "Unmatched fieldUpdate:\n" ++ show s
144 lit = var . name . Text.unpack
146lensSig :: Id -> Pre -> Local -> Solved -> Decl
147lensSig n p l s = TypeSig noLoc [lname p l] $
148 TyApp (TyApp (tycon "Lens'") (tycon n))
151lensDecl :: Pre -> Local -> Solved -> Decl
152lensDecl p l s = sfun noLoc (lname p l) [] (UnGuardedRhs rhs) noBinds
158 app (app (var "lens") (var f))
159 (paren (lamE noLoc [pvar "s", pvar "a"]
160 (RecUpdate (var "s") [FieldUpdate (UnQual f) (var "a")])))
162derivings :: Id -> AST [Deriving]
163derivings k = map ((,[]) . unqual . drop 1 . show) <$> derive k
165parameters :: [Solved] -> [Type]
166parameters = map (external . _type) . filter (view required)
168external, internal :: TType -> Type
169external = internal . \case
170 TMaybe t@TList {} -> t
175 TMaybe t -> TyApp (tycon "Maybe") (internal t)
176 TEither a b -> TyApp (TyApp (tycon "Either") (internal a)) (internal b)
177 TList t -> TyList (internal t)
180literal :: Lit -> Type
184 Time -> tycon "UTCTime"
185 Date -> tycon "UTCTime"
186 Nat -> tycon "Natural"
187 Float -> tycon "Float"
188 Double -> tycon "Double"
189 Byte -> tycon "Word8"
190 UInt32 -> tycon "Word32"
191 UInt64 -> tycon "Word64"
192 Int32 -> tycon "Int32"
193 Int64 -> tycon "Int64"
195mapping :: TType -> Exp -> Exp
196mapping t e = infixE e "." (go t)
199 TMaybe x@(TList {}) -> var "_Default" : go x
200 TMaybe x -> nest (go x)
201 x -> maybeToList (iso x)
204 nest (x:xs) = [app (var "mapping") (infixE x "." xs)]
206iso :: TType -> Maybe Exp
208 TList {} -> Just (var "_Coerce")
211strict :: Type -> Type
212strict = TyBang BangedTy . \case
213 t@TyApp{} -> TyParen t
217sing = TyCon . unqual . Text.unpack . flip mappend "\"" . mappend "\""
220tycon = TyCon . unqual . Text.unpack . idToText
222unqual :: String -> QName
223unqual = UnQual . name
225infixE :: Exp -> QOp -> [Exp] -> Exp
227infixE l o (r:rs) = infixE (infixApp l o r) o rs
230str = strE . Text.unpack
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list