-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathInterface.hs
125 lines (108 loc) · 4.19 KB
/
Interface.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
{-# LANGUAGE TemplateHaskell #-}
module Interface where
import Function
import Schema
import SchemaTH
import SchemaTypes
import NowHs
import Data.Aeson
import Data.Maybe (mapMaybe)
import qualified Data.Map as Map
import qualified Data.Set as Set
import Control.Applicative
import Control.Monad (replicateM)
import Control.Monad.Error
import Control.Monad.State as S
import Control.Arrow
import Language.Haskell.TH
import Language.Haskell.TH.Lift as L
data Interface s
= Interface { interfaceInternal :: (Name, [Value]) -> NowHs s Value
, interfaceExternal :: ([Function], Set.Set AnySchema) }
-- $(interface names) :: MonadNowHs m => Interface m
genInterface :: [Name] -> Q Exp
genInterface namesR = do
-- uniqify
let names = Set.toList . Set.fromList $ namesR
funs <- mapM genFun names
-- mkName . show NEEDED OTHERWISE WONT BE ABLE TO CALL FROM js!!!
funPairs <- ListE <$> zipWithM (\n f -> [| (mkName . show $ n,
$(return f)) |]) names funs
internal <- [| \(nam, vals) ->
let mp = Map.fromList $(return funPairs) in do
case Map.lookup nam mp of
Nothing -> throwError (NoSuchFunction nam)
Just f -> f vals
|]
external <- [| runState (sequence $(ListE <$> mapM funSchemaNames names)) Set.empty |]
[| Interface { interfaceInternal = $(return internal)
, interfaceExternal = $(return external) } |]
-- $(funSchemaNames nam) :: State (Set.Set AnySchema) Function
funSchemaNames :: Name -> Q Exp
funSchemaNames nam = do
typ <- funType nam
(args, ret) <- getArgsRet typ
((argSchFields, retSchField), argSchNameSet) <- flip runStateT Set.empty $ do
argFs <- mapM schField args
retF <- schField ret
return (argFs, retF)
let anySchemas = mapM (schemaNam . ConT) $ Set.toList argSchNameSet
[| do
let argFs = $(return $ ListE argSchFields)
retF = $(return retSchField)
modify (\s -> foldl (flip Set.insert) s $(ListE <$> anySchemas))
return $ Function { functionName = nam
, functionNameRaw = nameBase nam
, functionArgTypes = argFs
, functionRetType = retF }
|]
-- $(schField typ) :: SchemaField
schField :: Type -> StateT (Set.Set Name) Q Exp
schField = flip evalStateT [] . deriveSchemaField
anySchemaName :: AnySchema -> Name
anySchemaName (MkAnySchema sch) = schemaName sch
getArgsRet :: Type -> Q ([Type], Type)
getArgsRet (AppT (AppT ArrowT typ) rest) = first (typ :) <$> getArgsRet rest
getArgsRet (AppT (AppT (ConT m) _) ret)
| m == ''NowHs = return ([], ret)
getArgsRet _ = fail "Function has to be monadic (NowHs)"
funType :: Name -> Q Type
funType name = do
info <- reify name
case info of
(VarI _ ty _ _) -> return ty
_ -> fail "Function name expected"
-- $(genFun fun) :: MonadNowHs m => [Value] -> m Value
genFun :: Name -> Q Exp
genFun nam = do
typ <- funType nam
(pat, names) <- listPat $ arity typ
vals <- newName "vals"
incor <- [| IncorrectNumArgs |]
(binds, names) <- unzip <$> mapM fJSON names
let vars = map VarE names
resNam <- newName "res"
returnEx <- [| return (toJSON $(return $ VarE resNam)) |]
return . LamE [VarP vals] $ CaseE (VarE vals)
[ Match pat (NormalB $ DoE
(binds ++ [ BindS (VarP resNam) $
foldl AppE (VarE nam) vars
, NoBindS returnEx
])) []
, Match WildP (NormalB $ AppE (VarE 'throwError) incor) []
]
fJSON :: Name -> Q (Stmt, Name)
fJSON nam = do
n <- newName "n"
ex <- [| case $(return $ AppE (VarE 'fromJSON) (VarE nam)) of
Error str -> throwError $ JSONParseError str
Success v -> return v
|]
return $ (BindS (VarP n) ex, n)
listPat :: Int -> Q (Pat, [Name])
listPat num = do
names <- replicateM num (newName "x")
return (ListP $ map VarP names, names)
arity :: Type -> Int
arity (AppT (AppT ArrowT _) rest) = succ $ arity rest
arity _ = 0