Statistics
| Branch: | Tag: | Revision:

root / htools / Ganeti / THH.hs @ c2e60027

History | View | Annotate | Download (28.8 kB)

1 a1505857 Iustin Pop
{-# LANGUAGE TemplateHaskell #-}
2 e9aaa3c6 Iustin Pop
3 e9aaa3c6 Iustin Pop
{-| TemplateHaskell helper for HTools.
4 e9aaa3c6 Iustin Pop
5 e9aaa3c6 Iustin Pop
As TemplateHaskell require that splices be defined in a separate
6 e9aaa3c6 Iustin Pop
module, we combine all the TemplateHaskell functionality that HTools
7 e9aaa3c6 Iustin Pop
needs in this module (except the one for unittests).
8 e9aaa3c6 Iustin Pop
9 e9aaa3c6 Iustin Pop
-}
10 e9aaa3c6 Iustin Pop
11 e9aaa3c6 Iustin Pop
{-
12 e9aaa3c6 Iustin Pop
13 b1e81520 Iustin Pop
Copyright (C) 2011, 2012 Google Inc.
14 e9aaa3c6 Iustin Pop
15 e9aaa3c6 Iustin Pop
This program is free software; you can redistribute it and/or modify
16 e9aaa3c6 Iustin Pop
it under the terms of the GNU General Public License as published by
17 e9aaa3c6 Iustin Pop
the Free Software Foundation; either version 2 of the License, or
18 e9aaa3c6 Iustin Pop
(at your option) any later version.
19 e9aaa3c6 Iustin Pop
20 e9aaa3c6 Iustin Pop
This program is distributed in the hope that it will be useful, but
21 e9aaa3c6 Iustin Pop
WITHOUT ANY WARRANTY; without even the implied warranty of
22 e9aaa3c6 Iustin Pop
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
23 e9aaa3c6 Iustin Pop
General Public License for more details.
24 e9aaa3c6 Iustin Pop
25 e9aaa3c6 Iustin Pop
You should have received a copy of the GNU General Public License
26 e9aaa3c6 Iustin Pop
along with this program; if not, write to the Free Software
27 e9aaa3c6 Iustin Pop
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
28 e9aaa3c6 Iustin Pop
02110-1301, USA.
29 e9aaa3c6 Iustin Pop
30 e9aaa3c6 Iustin Pop
-}
31 e9aaa3c6 Iustin Pop
32 b20cbf06 Iustin Pop
module Ganeti.THH ( declareSADT
33 260d0bda Agata Murawska
                  , declareIADT
34 e9aaa3c6 Iustin Pop
                  , makeJSONInstance
35 6111e296 Iustin Pop
                  , genOpID
36 12c19659 Iustin Pop
                  , genOpCode
37 a0090487 Agata Murawska
                  , genStrOfOp
38 a0090487 Agata Murawska
                  , genStrOfKey
39 a0090487 Agata Murawska
                  , genLuxiOp
40 879273e3 Iustin Pop
                  , Field
41 879273e3 Iustin Pop
                  , simpleField
42 879273e3 Iustin Pop
                  , defaultField
43 879273e3 Iustin Pop
                  , optionalField
44 879273e3 Iustin Pop
                  , renameField
45 879273e3 Iustin Pop
                  , containerField
46 879273e3 Iustin Pop
                  , customField
47 879273e3 Iustin Pop
                  , timeStampFields
48 879273e3 Iustin Pop
                  , uuidFields
49 879273e3 Iustin Pop
                  , serialFields
50 879273e3 Iustin Pop
                  , buildObject
51 879273e3 Iustin Pop
                  , buildObjectSerialisation
52 879273e3 Iustin Pop
                  , buildParam
53 879273e3 Iustin Pop
                  , Container
54 e9aaa3c6 Iustin Pop
                  ) where
55 e9aaa3c6 Iustin Pop
56 879273e3 Iustin Pop
import Control.Arrow
57 60de49c3 Iustin Pop
import Control.Monad (liftM, liftM2)
58 e9aaa3c6 Iustin Pop
import Data.Char
59 6111e296 Iustin Pop
import Data.List
60 879273e3 Iustin Pop
import qualified Data.Map as M
61 e9aaa3c6 Iustin Pop
import Language.Haskell.TH
62 e9aaa3c6 Iustin Pop
63 e9aaa3c6 Iustin Pop
import qualified Text.JSON as JSON
64 e9aaa3c6 Iustin Pop
65 706f7f51 Iustin Pop
import Ganeti.HTools.JSON
66 706f7f51 Iustin Pop
67 879273e3 Iustin Pop
-- * Exported types
68 879273e3 Iustin Pop
69 879273e3 Iustin Pop
type Container = M.Map String
70 879273e3 Iustin Pop
71 879273e3 Iustin Pop
-- | Serialised field data type.
72 879273e3 Iustin Pop
data Field = Field { fieldName        :: String
73 879273e3 Iustin Pop
                   , fieldType        :: Q Type
74 879273e3 Iustin Pop
                   , fieldRead        :: Maybe (Q Exp)
75 879273e3 Iustin Pop
                   , fieldShow        :: Maybe (Q Exp)
76 879273e3 Iustin Pop
                   , fieldDefault     :: Maybe (Q Exp)
77 879273e3 Iustin Pop
                   , fieldConstr      :: Maybe String
78 879273e3 Iustin Pop
                   , fieldIsContainer :: Bool
79 879273e3 Iustin Pop
                   , fieldIsOptional  :: Bool
80 879273e3 Iustin Pop
                   }
81 879273e3 Iustin Pop
82 879273e3 Iustin Pop
-- | Generates a simple field.
83 879273e3 Iustin Pop
simpleField :: String -> Q Type -> Field
84 879273e3 Iustin Pop
simpleField fname ftype =
85 879273e3 Iustin Pop
  Field { fieldName        = fname
86 879273e3 Iustin Pop
        , fieldType        = ftype
87 879273e3 Iustin Pop
        , fieldRead        = Nothing
88 879273e3 Iustin Pop
        , fieldShow        = Nothing
89 879273e3 Iustin Pop
        , fieldDefault     = Nothing
90 879273e3 Iustin Pop
        , fieldConstr      = Nothing
91 879273e3 Iustin Pop
        , fieldIsContainer = False
92 879273e3 Iustin Pop
        , fieldIsOptional  = False
93 879273e3 Iustin Pop
        }
94 879273e3 Iustin Pop
95 879273e3 Iustin Pop
-- | Sets the renamed constructor field.
96 879273e3 Iustin Pop
renameField :: String -> Field -> Field
97 879273e3 Iustin Pop
renameField constrName field = field { fieldConstr = Just constrName }
98 879273e3 Iustin Pop
99 879273e3 Iustin Pop
-- | Sets the default value on a field (makes it optional with a
100 879273e3 Iustin Pop
-- default value).
101 879273e3 Iustin Pop
defaultField :: Q Exp -> Field -> Field
102 879273e3 Iustin Pop
defaultField defval field = field { fieldDefault = Just defval }
103 879273e3 Iustin Pop
104 879273e3 Iustin Pop
-- | Marks a field optional (turning its base type into a Maybe).
105 879273e3 Iustin Pop
optionalField :: Field -> Field
106 879273e3 Iustin Pop
optionalField field = field { fieldIsOptional = True }
107 879273e3 Iustin Pop
108 879273e3 Iustin Pop
-- | Marks a field as a container.
109 879273e3 Iustin Pop
containerField :: Field -> Field
110 879273e3 Iustin Pop
containerField field = field { fieldIsContainer = True }
111 879273e3 Iustin Pop
112 879273e3 Iustin Pop
-- | Sets custom functions on a field.
113 c2e60027 Iustin Pop
customField :: Name    -- ^ The name of the read function
114 c2e60027 Iustin Pop
            -> Name    -- ^ The name of the show function
115 c2e60027 Iustin Pop
            -> Field   -- ^ The original field
116 c2e60027 Iustin Pop
            -> Field   -- ^ Updated field
117 879273e3 Iustin Pop
customField readfn showfn field =
118 c2e60027 Iustin Pop
  field { fieldRead = Just (varE readfn), fieldShow = Just (varE showfn) }
119 879273e3 Iustin Pop
120 879273e3 Iustin Pop
fieldRecordName :: Field -> String
121 879273e3 Iustin Pop
fieldRecordName (Field { fieldName = name, fieldConstr = alias }) =
122 879273e3 Iustin Pop
  maybe (camelCase name) id alias
123 879273e3 Iustin Pop
124 a1505857 Iustin Pop
-- | Computes the preferred variable name to use for the value of this
125 a1505857 Iustin Pop
-- field. If the field has a specific constructor name, then we use a
126 a1505857 Iustin Pop
-- first-letter-lowercased version of that; otherwise, we simply use
127 a1505857 Iustin Pop
-- the field name. See also 'fieldRecordName'.
128 879273e3 Iustin Pop
fieldVariable :: Field -> String
129 a1505857 Iustin Pop
fieldVariable f =
130 a1505857 Iustin Pop
  case (fieldConstr f) of
131 a1505857 Iustin Pop
    Just name -> ensureLower name
132 a1505857 Iustin Pop
    _ -> fieldName f
133 879273e3 Iustin Pop
134 879273e3 Iustin Pop
actualFieldType :: Field -> Q Type
135 879273e3 Iustin Pop
actualFieldType f | fieldIsContainer f = [t| Container $t |]
136 879273e3 Iustin Pop
                  | fieldIsOptional f  = [t| Maybe $t     |]
137 879273e3 Iustin Pop
                  | otherwise = t
138 879273e3 Iustin Pop
                  where t = fieldType f
139 879273e3 Iustin Pop
140 879273e3 Iustin Pop
checkNonOptDef :: (Monad m) => Field -> m ()
141 879273e3 Iustin Pop
checkNonOptDef (Field { fieldIsOptional = True, fieldName = name }) =
142 879273e3 Iustin Pop
  fail $ "Optional field " ++ name ++ " used in parameter declaration"
143 879273e3 Iustin Pop
checkNonOptDef (Field { fieldDefault = (Just _), fieldName = name }) =
144 879273e3 Iustin Pop
  fail $ "Default field " ++ name ++ " used in parameter declaration"
145 879273e3 Iustin Pop
checkNonOptDef _ = return ()
146 879273e3 Iustin Pop
147 879273e3 Iustin Pop
loadFn :: Field -> Q Exp -> Q Exp
148 879273e3 Iustin Pop
loadFn (Field { fieldIsContainer = True }) expr = [| $expr >>= readContainer |]
149 879273e3 Iustin Pop
loadFn (Field { fieldRead = Just readfn }) expr = [| $expr >>= $readfn |]
150 879273e3 Iustin Pop
loadFn _ expr = expr
151 879273e3 Iustin Pop
152 879273e3 Iustin Pop
153 879273e3 Iustin Pop
-- * Common field declarations
154 879273e3 Iustin Pop
155 879273e3 Iustin Pop
timeStampFields :: [Field]
156 879273e3 Iustin Pop
timeStampFields =
157 879273e3 Iustin Pop
    [ defaultField [| 0::Double |] $ simpleField "ctime" [t| Double |]
158 879273e3 Iustin Pop
    , defaultField [| 0::Double |] $ simpleField "mtime" [t| Double |]
159 879273e3 Iustin Pop
    ]
160 879273e3 Iustin Pop
161 879273e3 Iustin Pop
serialFields :: [Field]
162 879273e3 Iustin Pop
serialFields =
163 879273e3 Iustin Pop
    [ renameField  "Serial" $ simpleField "serial_no" [t| Int |] ]
164 879273e3 Iustin Pop
165 879273e3 Iustin Pop
uuidFields :: [Field]
166 879273e3 Iustin Pop
uuidFields = [ simpleField "uuid" [t| String |] ]
167 879273e3 Iustin Pop
168 53664e15 Iustin Pop
-- * Helper functions
169 53664e15 Iustin Pop
170 e9aaa3c6 Iustin Pop
-- | Ensure first letter is lowercase.
171 e9aaa3c6 Iustin Pop
--
172 e9aaa3c6 Iustin Pop
-- Used to convert type name to function prefix, e.g. in @data Aa ->
173 5f828ce4 Agata Murawska
-- aaToRaw@.
174 e9aaa3c6 Iustin Pop
ensureLower :: String -> String
175 e9aaa3c6 Iustin Pop
ensureLower [] = []
176 e9aaa3c6 Iustin Pop
ensureLower (x:xs) = toLower x:xs
177 e9aaa3c6 Iustin Pop
178 879273e3 Iustin Pop
-- | Ensure first letter is uppercase.
179 879273e3 Iustin Pop
--
180 879273e3 Iustin Pop
-- Used to convert constructor name to component
181 879273e3 Iustin Pop
ensureUpper :: String -> String
182 879273e3 Iustin Pop
ensureUpper [] = []
183 879273e3 Iustin Pop
ensureUpper (x:xs) = toUpper x:xs
184 879273e3 Iustin Pop
185 53664e15 Iustin Pop
-- | Helper for quoted expressions.
186 53664e15 Iustin Pop
varNameE :: String -> Q Exp
187 53664e15 Iustin Pop
varNameE = varE . mkName
188 53664e15 Iustin Pop
189 53664e15 Iustin Pop
-- | showJSON as an expression, for reuse.
190 53664e15 Iustin Pop
showJSONE :: Q Exp
191 53664e15 Iustin Pop
showJSONE = varNameE "showJSON"
192 53664e15 Iustin Pop
193 5f828ce4 Agata Murawska
-- | ToRaw function name.
194 5f828ce4 Agata Murawska
toRawName :: String -> Name
195 5f828ce4 Agata Murawska
toRawName = mkName . (++ "ToRaw") . ensureLower
196 e9aaa3c6 Iustin Pop
197 5f828ce4 Agata Murawska
-- | FromRaw function name.
198 5f828ce4 Agata Murawska
fromRawName :: String -> Name
199 5f828ce4 Agata Murawska
fromRawName = mkName . (++ "FromRaw") . ensureLower
200 260d0bda Agata Murawska
201 6111e296 Iustin Pop
-- | Converts a name to it's varE/litE representations.
202 6111e296 Iustin Pop
--
203 6111e296 Iustin Pop
reprE :: Either String Name -> Q Exp
204 53664e15 Iustin Pop
reprE = either stringE varE
205 53664e15 Iustin Pop
206 60de49c3 Iustin Pop
-- | Smarter function application.
207 60de49c3 Iustin Pop
--
208 60de49c3 Iustin Pop
-- This does simply f x, except that if is 'id', it will skip it, in
209 60de49c3 Iustin Pop
-- order to generate more readable code when using -ddump-splices.
210 60de49c3 Iustin Pop
appFn :: Exp -> Exp -> Exp
211 60de49c3 Iustin Pop
appFn f x | f == VarE 'id = x
212 60de49c3 Iustin Pop
          | otherwise = AppE f x
213 60de49c3 Iustin Pop
214 879273e3 Iustin Pop
-- | Container loader
215 706f7f51 Iustin Pop
readContainer :: (Monad m, JSON.JSON a) =>
216 706f7f51 Iustin Pop
                 JSON.JSObject JSON.JSValue -> m (Container a)
217 706f7f51 Iustin Pop
readContainer obj = do
218 706f7f51 Iustin Pop
  let kjvlist = JSON.fromJSObject obj
219 706f7f51 Iustin Pop
  kalist <- mapM (\(k, v) -> fromKeyValue k v >>= \a -> return (k, a)) kjvlist
220 706f7f51 Iustin Pop
  return $ M.fromList kalist
221 879273e3 Iustin Pop
222 879273e3 Iustin Pop
-- | Container dumper
223 879273e3 Iustin Pop
showContainer :: (JSON.JSON a) => Container a -> JSON.JSValue
224 879273e3 Iustin Pop
showContainer = JSON.makeObj . map (second JSON.showJSON) . M.toList
225 879273e3 Iustin Pop
226 5f828ce4 Agata Murawska
-- * Template code for simple raw type-equivalent ADTs
227 6111e296 Iustin Pop
228 e9aaa3c6 Iustin Pop
-- | Generates a data type declaration.
229 e9aaa3c6 Iustin Pop
--
230 e9aaa3c6 Iustin Pop
-- The type will have a fixed list of instances.
231 e9aaa3c6 Iustin Pop
strADTDecl :: Name -> [String] -> Dec
232 e9aaa3c6 Iustin Pop
strADTDecl name constructors =
233 ebf38064 Iustin Pop
  DataD [] name []
234 ebf38064 Iustin Pop
          (map (flip NormalC [] . mkName) constructors)
235 ebf38064 Iustin Pop
          [''Show, ''Read, ''Eq, ''Enum, ''Bounded, ''Ord]
236 e9aaa3c6 Iustin Pop
237 5f828ce4 Agata Murawska
-- | Generates a toRaw function.
238 e9aaa3c6 Iustin Pop
--
239 e9aaa3c6 Iustin Pop
-- This generates a simple function of the form:
240 e9aaa3c6 Iustin Pop
--
241 e9aaa3c6 Iustin Pop
-- @
242 5f828ce4 Agata Murawska
-- nameToRaw :: Name -> /traw/
243 5f828ce4 Agata Murawska
-- nameToRaw Cons1 = var1
244 5f828ce4 Agata Murawska
-- nameToRaw Cons2 = \"value2\"
245 e9aaa3c6 Iustin Pop
-- @
246 5f828ce4 Agata Murawska
genToRaw :: Name -> Name -> Name -> [(String, Either String Name)] -> Q [Dec]
247 5f828ce4 Agata Murawska
genToRaw traw fname tname constructors = do
248 d80e3485 Iustin Pop
  let sigt = AppT (AppT ArrowT (ConT tname)) (ConT traw)
249 e9aaa3c6 Iustin Pop
  -- the body clauses, matching on the constructor and returning the
250 5f828ce4 Agata Murawska
  -- raw value
251 e9aaa3c6 Iustin Pop
  clauses <- mapM  (\(c, v) -> clause [recP (mkName c) []]
252 6111e296 Iustin Pop
                             (normalB (reprE v)) []) constructors
253 e9aaa3c6 Iustin Pop
  return [SigD fname sigt, FunD fname clauses]
254 e9aaa3c6 Iustin Pop
255 5f828ce4 Agata Murawska
-- | Generates a fromRaw function.
256 e9aaa3c6 Iustin Pop
--
257 e9aaa3c6 Iustin Pop
-- The function generated is monadic and can fail parsing the
258 5f828ce4 Agata Murawska
-- raw value. It is of the form:
259 e9aaa3c6 Iustin Pop
--
260 e9aaa3c6 Iustin Pop
-- @
261 5f828ce4 Agata Murawska
-- nameFromRaw :: (Monad m) => /traw/ -> m Name
262 5f828ce4 Agata Murawska
-- nameFromRaw s | s == var1       = Cons1
263 5f828ce4 Agata Murawska
--               | s == \"value2\" = Cons2
264 5f828ce4 Agata Murawska
--               | otherwise = fail /.../
265 e9aaa3c6 Iustin Pop
-- @
266 5f828ce4 Agata Murawska
genFromRaw :: Name -> Name -> Name -> [(String, Name)] -> Q [Dec]
267 5f828ce4 Agata Murawska
genFromRaw traw fname tname constructors = do
268 e9aaa3c6 Iustin Pop
  -- signature of form (Monad m) => String -> m $name
269 5f828ce4 Agata Murawska
  sigt <- [t| (Monad m) => $(conT traw) -> m $(conT tname) |]
270 e9aaa3c6 Iustin Pop
  -- clauses for a guarded pattern
271 e9aaa3c6 Iustin Pop
  let varp = mkName "s"
272 e9aaa3c6 Iustin Pop
      varpe = varE varp
273 e9aaa3c6 Iustin Pop
  clauses <- mapM (\(c, v) -> do
274 e9aaa3c6 Iustin Pop
                     -- the clause match condition
275 e9aaa3c6 Iustin Pop
                     g <- normalG [| $varpe == $(varE v) |]
276 e9aaa3c6 Iustin Pop
                     -- the clause result
277 e9aaa3c6 Iustin Pop
                     r <- [| return $(conE (mkName c)) |]
278 e9aaa3c6 Iustin Pop
                     return (g, r)) constructors
279 e9aaa3c6 Iustin Pop
  -- the otherwise clause (fallback)
280 e9aaa3c6 Iustin Pop
  oth_clause <- do
281 e9aaa3c6 Iustin Pop
    g <- normalG [| otherwise |]
282 e9aaa3c6 Iustin Pop
    r <- [|fail ("Invalid string value for type " ++
283 5f828ce4 Agata Murawska
                 $(litE (stringL (nameBase tname))) ++ ": " ++ show $varpe) |]
284 e9aaa3c6 Iustin Pop
    return (g, r)
285 e9aaa3c6 Iustin Pop
  let fun = FunD fname [Clause [VarP varp]
286 e9aaa3c6 Iustin Pop
                        (GuardedB (clauses++[oth_clause])) []]
287 e9aaa3c6 Iustin Pop
  return [SigD fname sigt, fun]
288 e9aaa3c6 Iustin Pop
289 5f828ce4 Agata Murawska
-- | Generates a data type from a given raw format.
290 e9aaa3c6 Iustin Pop
--
291 e9aaa3c6 Iustin Pop
-- The format is expected to multiline. The first line contains the
292 e9aaa3c6 Iustin Pop
-- type name, and the rest of the lines must contain two words: the
293 e9aaa3c6 Iustin Pop
-- constructor name and then the string representation of the
294 e9aaa3c6 Iustin Pop
-- respective constructor.
295 e9aaa3c6 Iustin Pop
--
296 e9aaa3c6 Iustin Pop
-- The function will generate the data type declaration, and then two
297 e9aaa3c6 Iustin Pop
-- functions:
298 e9aaa3c6 Iustin Pop
--
299 5f828ce4 Agata Murawska
-- * /name/ToRaw, which converts the type to a raw type
300 e9aaa3c6 Iustin Pop
--
301 5f828ce4 Agata Murawska
-- * /name/FromRaw, which (monadically) converts from a raw type to the type
302 e9aaa3c6 Iustin Pop
--
303 e9aaa3c6 Iustin Pop
-- Note that this is basically just a custom show/read instance,
304 e9aaa3c6 Iustin Pop
-- nothing else.
305 5f828ce4 Agata Murawska
declareADT :: Name -> String -> [(String, Name)] -> Q [Dec]
306 5f828ce4 Agata Murawska
declareADT traw sname cons = do
307 e9aaa3c6 Iustin Pop
  let name = mkName sname
308 e9aaa3c6 Iustin Pop
      ddecl = strADTDecl name (map fst cons)
309 5f828ce4 Agata Murawska
      -- process cons in the format expected by genToRaw
310 6111e296 Iustin Pop
      cons' = map (\(a, b) -> (a, Right b)) cons
311 5f828ce4 Agata Murawska
  toraw <- genToRaw traw (toRawName sname) name cons'
312 5f828ce4 Agata Murawska
  fromraw <- genFromRaw traw (fromRawName sname) name cons
313 5f828ce4 Agata Murawska
  return $ ddecl:toraw ++ fromraw
314 e9aaa3c6 Iustin Pop
315 5f828ce4 Agata Murawska
declareIADT :: String -> [(String, Name)] -> Q [Dec]
316 5f828ce4 Agata Murawska
declareIADT = declareADT ''Int
317 5f828ce4 Agata Murawska
318 5f828ce4 Agata Murawska
declareSADT :: String -> [(String, Name)] -> Q [Dec]
319 5f828ce4 Agata Murawska
declareSADT = declareADT ''String
320 e9aaa3c6 Iustin Pop
321 e9aaa3c6 Iustin Pop
-- | Creates the showJSON member of a JSON instance declaration.
322 e9aaa3c6 Iustin Pop
--
323 e9aaa3c6 Iustin Pop
-- This will create what is the equivalent of:
324 e9aaa3c6 Iustin Pop
--
325 e9aaa3c6 Iustin Pop
-- @
326 5f828ce4 Agata Murawska
-- showJSON = showJSON . /name/ToRaw
327 e9aaa3c6 Iustin Pop
-- @
328 e9aaa3c6 Iustin Pop
--
329 e9aaa3c6 Iustin Pop
-- in an instance JSON /name/ declaration
330 ffbd9592 Iustin Pop
genShowJSON :: String -> Q Dec
331 ffbd9592 Iustin Pop
genShowJSON name = do
332 ffbd9592 Iustin Pop
  body <- [| JSON.showJSON . $(varE (toRawName name)) |]
333 ffbd9592 Iustin Pop
  return $ FunD (mkName "showJSON") [Clause [] (NormalB body) []]
334 e9aaa3c6 Iustin Pop
335 e9aaa3c6 Iustin Pop
-- | Creates the readJSON member of a JSON instance declaration.
336 e9aaa3c6 Iustin Pop
--
337 e9aaa3c6 Iustin Pop
-- This will create what is the equivalent of:
338 e9aaa3c6 Iustin Pop
--
339 e9aaa3c6 Iustin Pop
-- @
340 e9aaa3c6 Iustin Pop
-- readJSON s = case readJSON s of
341 5f828ce4 Agata Murawska
--                Ok s' -> /name/FromRaw s'
342 e9aaa3c6 Iustin Pop
--                Error e -> Error /description/
343 e9aaa3c6 Iustin Pop
-- @
344 e9aaa3c6 Iustin Pop
--
345 e9aaa3c6 Iustin Pop
-- in an instance JSON /name/ declaration
346 e9aaa3c6 Iustin Pop
genReadJSON :: String -> Q Dec
347 e9aaa3c6 Iustin Pop
genReadJSON name = do
348 e9aaa3c6 Iustin Pop
  let s = mkName "s"
349 e9aaa3c6 Iustin Pop
  body <- [| case JSON.readJSON $(varE s) of
350 5f828ce4 Agata Murawska
               JSON.Ok s' -> $(varE (fromRawName name)) s'
351 e9aaa3c6 Iustin Pop
               JSON.Error e ->
352 5f828ce4 Agata Murawska
                   JSON.Error $ "Can't parse raw value for type " ++
353 6bd26f00 Iustin Pop
                           $(stringE name) ++ ": " ++ e ++ " from " ++
354 6bd26f00 Iustin Pop
                           show $(varE s)
355 e9aaa3c6 Iustin Pop
           |]
356 e9aaa3c6 Iustin Pop
  return $ FunD (mkName "readJSON") [Clause [VarP s] (NormalB body) []]
357 e9aaa3c6 Iustin Pop
358 e9aaa3c6 Iustin Pop
-- | Generates a JSON instance for a given type.
359 e9aaa3c6 Iustin Pop
--
360 5f828ce4 Agata Murawska
-- This assumes that the /name/ToRaw and /name/FromRaw functions
361 e9aaa3c6 Iustin Pop
-- have been defined as by the 'declareSADT' function.
362 e9aaa3c6 Iustin Pop
makeJSONInstance :: Name -> Q [Dec]
363 e9aaa3c6 Iustin Pop
makeJSONInstance name = do
364 e9aaa3c6 Iustin Pop
  let base = nameBase name
365 e9aaa3c6 Iustin Pop
  showJ <- genShowJSON base
366 e9aaa3c6 Iustin Pop
  readJ <- genReadJSON base
367 ffbd9592 Iustin Pop
  return [InstanceD [] (AppT (ConT ''JSON.JSON) (ConT name)) [readJ,showJ]]
368 6111e296 Iustin Pop
369 53664e15 Iustin Pop
-- * Template code for opcodes
370 53664e15 Iustin Pop
371 6111e296 Iustin Pop
-- | Transforms a CamelCase string into an_underscore_based_one.
372 6111e296 Iustin Pop
deCamelCase :: String -> String
373 6111e296 Iustin Pop
deCamelCase =
374 6111e296 Iustin Pop
    intercalate "_" . map (map toUpper) . groupBy (\_ b -> not $ isUpper b)
375 6111e296 Iustin Pop
376 879273e3 Iustin Pop
-- | Transform an underscore_name into a CamelCase one.
377 879273e3 Iustin Pop
camelCase :: String -> String
378 879273e3 Iustin Pop
camelCase = concatMap (ensureUpper . drop 1) .
379 879273e3 Iustin Pop
            groupBy (\_ b -> b /= '_') . ('_':)
380 879273e3 Iustin Pop
381 05ff7a00 Agata Murawska
-- | Computes the name of a given constructor.
382 6111e296 Iustin Pop
constructorName :: Con -> Q Name
383 6111e296 Iustin Pop
constructorName (NormalC name _) = return name
384 6111e296 Iustin Pop
constructorName (RecC name _)    = return name
385 6111e296 Iustin Pop
constructorName x                = fail $ "Unhandled constructor " ++ show x
386 6111e296 Iustin Pop
387 a0090487 Agata Murawska
-- | Builds the generic constructor-to-string function.
388 6111e296 Iustin Pop
--
389 6111e296 Iustin Pop
-- This generates a simple function of the following form:
390 6111e296 Iustin Pop
--
391 6111e296 Iustin Pop
-- @
392 a0090487 Agata Murawska
-- fname (ConStructorOne {}) = trans_fun("ConStructorOne")
393 a0090487 Agata Murawska
-- fname (ConStructorTwo {}) = trans_fun("ConStructorTwo")
394 6111e296 Iustin Pop
-- @
395 6111e296 Iustin Pop
--
396 6111e296 Iustin Pop
-- This builds a custom list of name/string pairs and then uses
397 5f828ce4 Agata Murawska
-- 'genToRaw' to actually generate the function
398 a0090487 Agata Murawska
genConstrToStr :: (String -> String) -> Name -> String -> Q [Dec]
399 a0090487 Agata Murawska
genConstrToStr trans_fun name fname = do
400 6111e296 Iustin Pop
  TyConI (DataD _ _ _ cons _) <- reify name
401 6111e296 Iustin Pop
  cnames <- mapM (liftM nameBase . constructorName) cons
402 a0090487 Agata Murawska
  let svalues = map (Left . trans_fun) cnames
403 5f828ce4 Agata Murawska
  genToRaw ''String (mkName fname) name $ zip cnames svalues
404 12c19659 Iustin Pop
405 a0090487 Agata Murawska
-- | Constructor-to-string for OpCode.
406 a0090487 Agata Murawska
genOpID :: Name -> String -> Q [Dec]
407 a0090487 Agata Murawska
genOpID = genConstrToStr deCamelCase
408 12c19659 Iustin Pop
409 05ff7a00 Agata Murawska
-- | OpCode parameter (field) type.
410 12c19659 Iustin Pop
type OpParam = (String, Q Type, Q Exp)
411 12c19659 Iustin Pop
412 12c19659 Iustin Pop
-- | Generates the OpCode data type.
413 12c19659 Iustin Pop
--
414 12c19659 Iustin Pop
-- This takes an opcode logical definition, and builds both the
415 12c19659 Iustin Pop
-- datatype and the JSON serialisation out of it. We can't use a
416 12c19659 Iustin Pop
-- generic serialisation since we need to be compatible with Ganeti's
417 12c19659 Iustin Pop
-- own, so we have a few quirks to work around.
418 12c19659 Iustin Pop
genOpCode :: String                -- ^ Type name to use
419 a1505857 Iustin Pop
          -> [(String, [Field])]   -- ^ Constructor name and parameters
420 12c19659 Iustin Pop
          -> Q [Dec]
421 12c19659 Iustin Pop
genOpCode name cons = do
422 12c19659 Iustin Pop
  decl_d <- mapM (\(cname, fields) -> do
423 12c19659 Iustin Pop
                    -- we only need the type of the field, without Q
424 a1505857 Iustin Pop
                    fields' <- mapM actualFieldType fields
425 a1505857 Iustin Pop
                    let fields'' = zip (repeat NotStrict) fields'
426 a1505857 Iustin Pop
                    return $ NormalC (mkName cname) fields'')
427 12c19659 Iustin Pop
            cons
428 12c19659 Iustin Pop
  let declD = DataD [] (mkName name) [] decl_d [''Show, ''Read, ''Eq]
429 12c19659 Iustin Pop
430 12c19659 Iustin Pop
  (savesig, savefn) <- genSaveOpCode cons
431 12c19659 Iustin Pop
  (loadsig, loadfn) <- genLoadOpCode cons
432 12c19659 Iustin Pop
  return [declD, loadsig, loadfn, savesig, savefn]
433 12c19659 Iustin Pop
434 05ff7a00 Agata Murawska
-- | Checks whether a given parameter is options.
435 12c19659 Iustin Pop
--
436 12c19659 Iustin Pop
-- This requires that it's a 'Maybe'.
437 12c19659 Iustin Pop
isOptional :: Type -> Bool
438 12c19659 Iustin Pop
isOptional (AppT (ConT dt) _) | dt == ''Maybe = True
439 12c19659 Iustin Pop
isOptional _ = False
440 12c19659 Iustin Pop
441 12c19659 Iustin Pop
-- | Generates the \"save\" clause for an entire opcode constructor.
442 12c19659 Iustin Pop
--
443 12c19659 Iustin Pop
-- This matches the opcode with variables named the same as the
444 12c19659 Iustin Pop
-- constructor fields (just so that the spliced in code looks nicer),
445 a1505857 Iustin Pop
-- and passes those name plus the parameter definition to 'saveObjectField'.
446 12c19659 Iustin Pop
saveConstructor :: String    -- ^ The constructor name
447 a1505857 Iustin Pop
                -> [Field]   -- ^ The parameter definitions for this
448 12c19659 Iustin Pop
                             -- constructor
449 12c19659 Iustin Pop
                -> Q Clause  -- ^ Resulting clause
450 12c19659 Iustin Pop
saveConstructor sname fields = do
451 12c19659 Iustin Pop
  let cname = mkName sname
452 a1505857 Iustin Pop
  let fnames = map (mkName . fieldVariable) fields
453 12c19659 Iustin Pop
  let pat = conP cname (map varP fnames)
454 a1505857 Iustin Pop
  let felems = map (uncurry saveObjectField) (zip fnames fields)
455 12c19659 Iustin Pop
      -- now build the OP_ID serialisation
456 53664e15 Iustin Pop
      opid = [| [( $(stringE "OP_ID"),
457 a07343b2 Iustin Pop
                   JSON.showJSON $(stringE . deCamelCase $ sname) )] |]
458 12c19659 Iustin Pop
      flist = listE (opid:felems)
459 12c19659 Iustin Pop
      -- and finally convert all this to a json object
460 53664e15 Iustin Pop
      flist' = [| $(varNameE "makeObj") (concat $flist) |]
461 12c19659 Iustin Pop
  clause [pat] (normalB flist') []
462 12c19659 Iustin Pop
463 12c19659 Iustin Pop
-- | Generates the main save opcode function.
464 12c19659 Iustin Pop
--
465 12c19659 Iustin Pop
-- This builds a per-constructor match clause that contains the
466 12c19659 Iustin Pop
-- respective constructor-serialisation code.
467 a1505857 Iustin Pop
genSaveOpCode :: [(String, [Field])] -> Q (Dec, Dec)
468 12c19659 Iustin Pop
genSaveOpCode opdefs = do
469 12c19659 Iustin Pop
  cclauses <- mapM (uncurry saveConstructor) opdefs
470 12c19659 Iustin Pop
  let fname = mkName "saveOpCode"
471 12c19659 Iustin Pop
  sigt <- [t| $(conT (mkName "OpCode")) -> JSON.JSValue |]
472 12c19659 Iustin Pop
  return $ (SigD fname sigt, FunD fname cclauses)
473 12c19659 Iustin Pop
474 a1505857 Iustin Pop
loadConstructor :: String -> [Field] -> Q Exp
475 12c19659 Iustin Pop
loadConstructor sname fields = do
476 12c19659 Iustin Pop
  let name = mkName sname
477 a1505857 Iustin Pop
  fbinds <- mapM loadObjectField fields
478 12c19659 Iustin Pop
  let (fnames, fstmts) = unzip fbinds
479 12c19659 Iustin Pop
  let cval = foldl (\accu fn -> AppE accu (VarE fn)) (ConE name) fnames
480 12c19659 Iustin Pop
      fstmts' = fstmts ++ [NoBindS (AppE (VarE 'return) cval)]
481 12c19659 Iustin Pop
  return $ DoE fstmts'
482 12c19659 Iustin Pop
483 a1505857 Iustin Pop
genLoadOpCode :: [(String, [Field])] -> Q (Dec, Dec)
484 12c19659 Iustin Pop
genLoadOpCode opdefs = do
485 12c19659 Iustin Pop
  let fname = mkName "loadOpCode"
486 12c19659 Iustin Pop
      arg1 = mkName "v"
487 12c19659 Iustin Pop
      objname = mkName "o"
488 12c19659 Iustin Pop
      opid = mkName "op_id"
489 12c19659 Iustin Pop
  st1 <- bindS (varP objname) [| liftM JSON.fromJSObject
490 12c19659 Iustin Pop
                                 (JSON.readJSON $(varE arg1)) |]
491 53664e15 Iustin Pop
  st2 <- bindS (varP opid) [| $(varNameE "fromObj")
492 53664e15 Iustin Pop
                              $(varE objname) $(stringE "OP_ID") |]
493 12c19659 Iustin Pop
  -- the match results (per-constructor blocks)
494 12c19659 Iustin Pop
  mexps <- mapM (uncurry loadConstructor) opdefs
495 12c19659 Iustin Pop
  fails <- [| fail $ "Unknown opcode " ++ $(varE opid) |]
496 12c19659 Iustin Pop
  let mpats = map (\(me, c) ->
497 12c19659 Iustin Pop
                       let mp = LitP . StringL . deCamelCase . fst $ c
498 12c19659 Iustin Pop
                       in Match mp (NormalB me) []
499 12c19659 Iustin Pop
                  ) $ zip mexps opdefs
500 12c19659 Iustin Pop
      defmatch = Match WildP (NormalB fails) []
501 12c19659 Iustin Pop
      cst = NoBindS $ CaseE (VarE opid) $ mpats++[defmatch]
502 12c19659 Iustin Pop
      body = DoE [st1, st2, cst]
503 12c19659 Iustin Pop
  sigt <- [t| JSON.JSValue -> JSON.Result $(conT (mkName "OpCode")) |]
504 12c19659 Iustin Pop
  return $ (SigD fname sigt, FunD fname [Clause [VarP arg1] (NormalB body) []])
505 12c19659 Iustin Pop
506 a0090487 Agata Murawska
-- * Template code for luxi
507 a0090487 Agata Murawska
508 a0090487 Agata Murawska
-- | Constructor-to-string for LuxiOp.
509 a0090487 Agata Murawska
genStrOfOp :: Name -> String -> Q [Dec]
510 a0090487 Agata Murawska
genStrOfOp = genConstrToStr id
511 a0090487 Agata Murawska
512 a0090487 Agata Murawska
-- | Constructor-to-string for MsgKeys.
513 a0090487 Agata Murawska
genStrOfKey :: Name -> String -> Q [Dec]
514 a0090487 Agata Murawska
genStrOfKey = genConstrToStr ensureLower
515 a0090487 Agata Murawska
516 a0090487 Agata Murawska
-- | LuxiOp parameter type.
517 a0090487 Agata Murawska
type LuxiParam = (String, Q Type, Q Exp)
518 a0090487 Agata Murawska
519 a0090487 Agata Murawska
-- | Generates the LuxiOp data type.
520 a0090487 Agata Murawska
--
521 a0090487 Agata Murawska
-- This takes a Luxi operation definition and builds both the
522 a0090487 Agata Murawska
-- datatype and the function trnasforming the arguments to JSON.
523 a0090487 Agata Murawska
-- We can't use anything less generic, because the way different
524 a0090487 Agata Murawska
-- operations are serialized differs on both parameter- and top-level.
525 a0090487 Agata Murawska
--
526 a0090487 Agata Murawska
-- There are three things to be defined for each parameter:
527 a0090487 Agata Murawska
--
528 a0090487 Agata Murawska
-- * name
529 a0090487 Agata Murawska
--
530 a0090487 Agata Murawska
-- * type
531 a0090487 Agata Murawska
--
532 a0090487 Agata Murawska
-- * operation; this is the operation performed on the parameter before
533 a0090487 Agata Murawska
--   serialization
534 a0090487 Agata Murawska
--
535 b20cbf06 Iustin Pop
genLuxiOp :: String -> [(String, [LuxiParam])] -> Q [Dec]
536 a0090487 Agata Murawska
genLuxiOp name cons = do
537 b20cbf06 Iustin Pop
  decl_d <- mapM (\(cname, fields) -> do
538 a0090487 Agata Murawska
                    fields' <- mapM (\(_, qt, _) ->
539 a0090487 Agata Murawska
                                         qt >>= \t -> return (NotStrict, t))
540 a0090487 Agata Murawska
                               fields
541 a0090487 Agata Murawska
                    return $ NormalC (mkName cname) fields')
542 a0090487 Agata Murawska
            cons
543 cdd495ae Iustin Pop
  let declD = DataD [] (mkName name) [] decl_d [''Show, ''Read, ''Eq]
544 a0090487 Agata Murawska
  (savesig, savefn) <- genSaveLuxiOp cons
545 95d0d502 Iustin Pop
  req_defs <- declareSADT "LuxiReq" .
546 95d0d502 Iustin Pop
              map (\(str, _) -> ("Req" ++ str, mkName ("luxiReq" ++ str))) $
547 95d0d502 Iustin Pop
                  cons
548 95d0d502 Iustin Pop
  return $ [declD, savesig, savefn] ++ req_defs
549 a0090487 Agata Murawska
550 92678b3c Iustin Pop
-- | Generates the \"save\" expression for a single luxi parameter.
551 b20cbf06 Iustin Pop
saveLuxiField :: Name -> LuxiParam -> Q Exp
552 b20cbf06 Iustin Pop
saveLuxiField fvar (_, qt, fn) =
553 b20cbf06 Iustin Pop
    [| JSON.showJSON ( $(liftM2 appFn fn $ varE fvar) ) |]
554 92678b3c Iustin Pop
555 a0090487 Agata Murawska
-- | Generates the \"save\" clause for entire LuxiOp constructor.
556 b20cbf06 Iustin Pop
saveLuxiConstructor :: (String, [LuxiParam]) -> Q Clause
557 b20cbf06 Iustin Pop
saveLuxiConstructor (sname, fields) = do
558 a0090487 Agata Murawska
  let cname = mkName sname
559 a0090487 Agata Murawska
      fnames = map (\(nm, _, _) -> mkName nm) fields
560 a0090487 Agata Murawska
      pat = conP cname (map varP fnames)
561 b20cbf06 Iustin Pop
      flist = map (uncurry saveLuxiField) (zip fnames fields)
562 b20cbf06 Iustin Pop
      finval = if null flist
563 b20cbf06 Iustin Pop
               then [| JSON.showJSON ()    |]
564 b20cbf06 Iustin Pop
               else [| JSON.showJSON $(listE flist) |]
565 92678b3c Iustin Pop
  clause [pat] (normalB finval) []
566 a0090487 Agata Murawska
567 a0090487 Agata Murawska
-- | Generates the main save LuxiOp function.
568 b20cbf06 Iustin Pop
genSaveLuxiOp :: [(String, [LuxiParam])]-> Q (Dec, Dec)
569 a0090487 Agata Murawska
genSaveLuxiOp opdefs = do
570 a0090487 Agata Murawska
  sigt <- [t| $(conT (mkName "LuxiOp")) -> JSON.JSValue |]
571 a0090487 Agata Murawska
  let fname = mkName "opToArgs"
572 a0090487 Agata Murawska
  cclauses <- mapM saveLuxiConstructor opdefs
573 a0090487 Agata Murawska
  return $ (SigD fname sigt, FunD fname cclauses)
574 879273e3 Iustin Pop
575 879273e3 Iustin Pop
-- * "Objects" functionality
576 879273e3 Iustin Pop
577 879273e3 Iustin Pop
-- | Extract the field's declaration from a Field structure.
578 879273e3 Iustin Pop
fieldTypeInfo :: String -> Field -> Q (Name, Strict, Type)
579 879273e3 Iustin Pop
fieldTypeInfo field_pfx fd = do
580 879273e3 Iustin Pop
  t <- actualFieldType fd
581 879273e3 Iustin Pop
  let n = mkName . (field_pfx ++) . fieldRecordName $ fd
582 879273e3 Iustin Pop
  return (n, NotStrict, t)
583 879273e3 Iustin Pop
584 879273e3 Iustin Pop
-- | Build an object declaration.
585 879273e3 Iustin Pop
buildObject :: String -> String -> [Field] -> Q [Dec]
586 879273e3 Iustin Pop
buildObject sname field_pfx fields = do
587 879273e3 Iustin Pop
  let name = mkName sname
588 879273e3 Iustin Pop
  fields_d <- mapM (fieldTypeInfo field_pfx) fields
589 879273e3 Iustin Pop
  let decl_d = RecC name fields_d
590 3c3c796e Iustin Pop
  let declD = DataD [] name [] [decl_d] [''Show, ''Read, ''Eq]
591 879273e3 Iustin Pop
  ser_decls <- buildObjectSerialisation sname fields
592 879273e3 Iustin Pop
  return $ declD:ser_decls
593 879273e3 Iustin Pop
594 879273e3 Iustin Pop
buildObjectSerialisation :: String -> [Field] -> Q [Dec]
595 879273e3 Iustin Pop
buildObjectSerialisation sname fields = do
596 879273e3 Iustin Pop
  let name = mkName sname
597 879273e3 Iustin Pop
  savedecls <- genSaveObject saveObjectField sname fields
598 879273e3 Iustin Pop
  (loadsig, loadfn) <- genLoadObject loadObjectField sname fields
599 879273e3 Iustin Pop
  shjson <- objectShowJSON sname
600 879273e3 Iustin Pop
  rdjson <- objectReadJSON sname
601 879273e3 Iustin Pop
  let instdecl = InstanceD [] (AppT (ConT ''JSON.JSON) (ConT name))
602 ffbd9592 Iustin Pop
                 [rdjson, shjson]
603 879273e3 Iustin Pop
  return $ savedecls ++ [loadsig, loadfn, instdecl]
604 879273e3 Iustin Pop
605 879273e3 Iustin Pop
genSaveObject :: (Name -> Field -> Q Exp)
606 879273e3 Iustin Pop
              -> String -> [Field] -> Q [Dec]
607 879273e3 Iustin Pop
genSaveObject save_fn sname fields = do
608 879273e3 Iustin Pop
  let name = mkName sname
609 879273e3 Iustin Pop
  let fnames = map (mkName . fieldVariable) fields
610 879273e3 Iustin Pop
  let pat = conP name (map varP fnames)
611 879273e3 Iustin Pop
  let tdname = mkName ("toDict" ++ sname)
612 879273e3 Iustin Pop
  tdsigt <- [t| $(conT name) -> [(String, JSON.JSValue)] |]
613 879273e3 Iustin Pop
614 879273e3 Iustin Pop
  let felems = map (uncurry save_fn) (zip fnames fields)
615 879273e3 Iustin Pop
      flist = listE felems
616 879273e3 Iustin Pop
      -- and finally convert all this to a json object
617 879273e3 Iustin Pop
      tdlist = [| concat $flist |]
618 879273e3 Iustin Pop
      iname = mkName "i"
619 879273e3 Iustin Pop
  tclause <- clause [pat] (normalB tdlist) []
620 879273e3 Iustin Pop
  cclause <- [| $(varNameE "makeObj") . $(varE tdname) |]
621 879273e3 Iustin Pop
  let fname = mkName ("save" ++ sname)
622 879273e3 Iustin Pop
  sigt <- [t| $(conT name) -> JSON.JSValue |]
623 879273e3 Iustin Pop
  return [SigD tdname tdsigt, FunD tdname [tclause],
624 879273e3 Iustin Pop
          SigD fname sigt, ValD (VarP fname) (NormalB cclause) []]
625 879273e3 Iustin Pop
626 879273e3 Iustin Pop
saveObjectField :: Name -> Field -> Q Exp
627 879273e3 Iustin Pop
saveObjectField fvar field
628 a07343b2 Iustin Pop
  | isContainer = [| [( $nameE , JSON.showJSON . showContainer $ $fvarE)] |]
629 879273e3 Iustin Pop
  | fisOptional = [| case $(varE fvar) of
630 879273e3 Iustin Pop
                      Nothing -> []
631 a07343b2 Iustin Pop
                      Just v -> [( $nameE, JSON.showJSON v)]
632 879273e3 Iustin Pop
                  |]
633 879273e3 Iustin Pop
  | otherwise = case fieldShow field of
634 a07343b2 Iustin Pop
      Nothing -> [| [( $nameE, JSON.showJSON $fvarE)] |]
635 a07343b2 Iustin Pop
      Just fn -> [| [( $nameE, JSON.showJSON . $fn $ $fvarE)] |]
636 879273e3 Iustin Pop
  where isContainer = fieldIsContainer field
637 879273e3 Iustin Pop
        fisOptional  = fieldIsOptional field
638 879273e3 Iustin Pop
        nameE = stringE (fieldName field)
639 879273e3 Iustin Pop
        fvarE = varE fvar
640 879273e3 Iustin Pop
641 ffbd9592 Iustin Pop
objectShowJSON :: String -> Q Dec
642 ffbd9592 Iustin Pop
objectShowJSON name = do
643 ffbd9592 Iustin Pop
  body <- [| JSON.showJSON . $(varE . mkName $ "save" ++ name) |]
644 ffbd9592 Iustin Pop
  return $ FunD (mkName "showJSON") [Clause [] (NormalB body) []]
645 879273e3 Iustin Pop
646 879273e3 Iustin Pop
genLoadObject :: (Field -> Q (Name, Stmt))
647 879273e3 Iustin Pop
              -> String -> [Field] -> Q (Dec, Dec)
648 879273e3 Iustin Pop
genLoadObject load_fn sname fields = do
649 879273e3 Iustin Pop
  let name = mkName sname
650 879273e3 Iustin Pop
      funname = mkName $ "load" ++ sname
651 879273e3 Iustin Pop
      arg1 = mkName "v"
652 879273e3 Iustin Pop
      objname = mkName "o"
653 879273e3 Iustin Pop
      opid = mkName "op_id"
654 879273e3 Iustin Pop
  st1 <- bindS (varP objname) [| liftM JSON.fromJSObject
655 879273e3 Iustin Pop
                                 (JSON.readJSON $(varE arg1)) |]
656 879273e3 Iustin Pop
  fbinds <- mapM load_fn fields
657 879273e3 Iustin Pop
  let (fnames, fstmts) = unzip fbinds
658 879273e3 Iustin Pop
  let cval = foldl (\accu fn -> AppE accu (VarE fn)) (ConE name) fnames
659 879273e3 Iustin Pop
      fstmts' = st1:fstmts ++ [NoBindS (AppE (VarE 'return) cval)]
660 879273e3 Iustin Pop
  sigt <- [t| JSON.JSValue -> JSON.Result $(conT name) |]
661 879273e3 Iustin Pop
  return $ (SigD funname sigt,
662 879273e3 Iustin Pop
            FunD funname [Clause [VarP arg1] (NormalB (DoE fstmts')) []])
663 879273e3 Iustin Pop
664 879273e3 Iustin Pop
loadObjectField :: Field -> Q (Name, Stmt)
665 879273e3 Iustin Pop
loadObjectField field = do
666 879273e3 Iustin Pop
  let name = fieldVariable field
667 879273e3 Iustin Pop
      fvar = mkName name
668 879273e3 Iustin Pop
  -- these are used in all patterns below
669 879273e3 Iustin Pop
  let objvar = varNameE "o"
670 879273e3 Iustin Pop
      objfield = stringE (fieldName field)
671 879273e3 Iustin Pop
      loadexp =
672 879273e3 Iustin Pop
        if fieldIsOptional field
673 879273e3 Iustin Pop
          then [| $(varNameE "maybeFromObj") $objvar $objfield |]
674 879273e3 Iustin Pop
          else case fieldDefault field of
675 879273e3 Iustin Pop
                 Just defv ->
676 879273e3 Iustin Pop
                   [| $(varNameE "fromObjWithDefault") $objvar
677 879273e3 Iustin Pop
                      $objfield $defv |]
678 879273e3 Iustin Pop
                 Nothing -> [| $(varNameE "fromObj") $objvar $objfield |]
679 879273e3 Iustin Pop
  bexp <- loadFn field loadexp
680 879273e3 Iustin Pop
681 879273e3 Iustin Pop
  return (fvar, BindS (VarP fvar) bexp)
682 879273e3 Iustin Pop
683 879273e3 Iustin Pop
objectReadJSON :: String -> Q Dec
684 879273e3 Iustin Pop
objectReadJSON name = do
685 879273e3 Iustin Pop
  let s = mkName "s"
686 879273e3 Iustin Pop
  body <- [| case JSON.readJSON $(varE s) of
687 879273e3 Iustin Pop
               JSON.Ok s' -> $(varE .mkName $ "load" ++ name) s'
688 879273e3 Iustin Pop
               JSON.Error e ->
689 879273e3 Iustin Pop
                 JSON.Error $ "Can't parse value for type " ++
690 879273e3 Iustin Pop
                       $(stringE name) ++ ": " ++ e
691 879273e3 Iustin Pop
           |]
692 879273e3 Iustin Pop
  return $ FunD (mkName "readJSON") [Clause [VarP s] (NormalB body) []]
693 879273e3 Iustin Pop
694 879273e3 Iustin Pop
-- * Inheritable parameter tables implementation
695 879273e3 Iustin Pop
696 879273e3 Iustin Pop
-- | Compute parameter type names.
697 879273e3 Iustin Pop
paramTypeNames :: String -> (String, String)
698 879273e3 Iustin Pop
paramTypeNames root = ("Filled"  ++ root ++ "Params",
699 879273e3 Iustin Pop
                       "Partial" ++ root ++ "Params")
700 879273e3 Iustin Pop
701 879273e3 Iustin Pop
-- | Compute information about the type of a parameter field.
702 879273e3 Iustin Pop
paramFieldTypeInfo :: String -> Field -> Q (Name, Strict, Type)
703 879273e3 Iustin Pop
paramFieldTypeInfo field_pfx fd = do
704 879273e3 Iustin Pop
  t <- actualFieldType fd
705 879273e3 Iustin Pop
  let n = mkName . (++ "P") . (field_pfx ++) .
706 879273e3 Iustin Pop
          fieldRecordName $ fd
707 879273e3 Iustin Pop
  return (n, NotStrict, AppT (ConT ''Maybe) t)
708 879273e3 Iustin Pop
709 879273e3 Iustin Pop
-- | Build a parameter declaration.
710 879273e3 Iustin Pop
--
711 879273e3 Iustin Pop
-- This function builds two different data structures: a /filled/ one,
712 879273e3 Iustin Pop
-- in which all fields are required, and a /partial/ one, in which all
713 879273e3 Iustin Pop
-- fields are optional. Due to the current record syntax issues, the
714 879273e3 Iustin Pop
-- fields need to be named differrently for the two structures, so the
715 879273e3 Iustin Pop
-- partial ones get a /P/ suffix.
716 879273e3 Iustin Pop
buildParam :: String -> String -> [Field] -> Q [Dec]
717 879273e3 Iustin Pop
buildParam sname field_pfx fields = do
718 879273e3 Iustin Pop
  let (sname_f, sname_p) = paramTypeNames sname
719 879273e3 Iustin Pop
      name_f = mkName sname_f
720 879273e3 Iustin Pop
      name_p = mkName sname_p
721 879273e3 Iustin Pop
  fields_f <- mapM (fieldTypeInfo field_pfx) fields
722 879273e3 Iustin Pop
  fields_p <- mapM (paramFieldTypeInfo field_pfx) fields
723 879273e3 Iustin Pop
  let decl_f = RecC name_f fields_f
724 879273e3 Iustin Pop
      decl_p = RecC name_p fields_p
725 b1e81520 Iustin Pop
  let declF = DataD [] name_f [] [decl_f] [''Show, ''Read, ''Eq]
726 b1e81520 Iustin Pop
      declP = DataD [] name_p [] [decl_p] [''Show, ''Read, ''Eq]
727 879273e3 Iustin Pop
  ser_decls_f <- buildObjectSerialisation sname_f fields
728 879273e3 Iustin Pop
  ser_decls_p <- buildPParamSerialisation sname_p fields
729 879273e3 Iustin Pop
  fill_decls <- fillParam sname field_pfx fields
730 879273e3 Iustin Pop
  return $ [declF, declP] ++ ser_decls_f ++ ser_decls_p ++ fill_decls
731 879273e3 Iustin Pop
732 879273e3 Iustin Pop
buildPParamSerialisation :: String -> [Field] -> Q [Dec]
733 879273e3 Iustin Pop
buildPParamSerialisation sname fields = do
734 879273e3 Iustin Pop
  let name = mkName sname
735 879273e3 Iustin Pop
  savedecls <- genSaveObject savePParamField sname fields
736 879273e3 Iustin Pop
  (loadsig, loadfn) <- genLoadObject loadPParamField sname fields
737 879273e3 Iustin Pop
  shjson <- objectShowJSON sname
738 879273e3 Iustin Pop
  rdjson <- objectReadJSON sname
739 879273e3 Iustin Pop
  let instdecl = InstanceD [] (AppT (ConT ''JSON.JSON) (ConT name))
740 ffbd9592 Iustin Pop
                 [rdjson, shjson]
741 879273e3 Iustin Pop
  return $ savedecls ++ [loadsig, loadfn, instdecl]
742 879273e3 Iustin Pop
743 879273e3 Iustin Pop
savePParamField :: Name -> Field -> Q Exp
744 879273e3 Iustin Pop
savePParamField fvar field = do
745 879273e3 Iustin Pop
  checkNonOptDef field
746 879273e3 Iustin Pop
  let actualVal = mkName "v"
747 879273e3 Iustin Pop
  normalexpr <- saveObjectField actualVal field
748 879273e3 Iustin Pop
  -- we have to construct the block here manually, because we can't
749 879273e3 Iustin Pop
  -- splice-in-splice
750 879273e3 Iustin Pop
  return $ CaseE (VarE fvar) [ Match (ConP 'Nothing [])
751 879273e3 Iustin Pop
                                       (NormalB (ConE '[])) []
752 879273e3 Iustin Pop
                             , Match (ConP 'Just [VarP actualVal])
753 879273e3 Iustin Pop
                                       (NormalB normalexpr) []
754 879273e3 Iustin Pop
                             ]
755 879273e3 Iustin Pop
loadPParamField :: Field -> Q (Name, Stmt)
756 879273e3 Iustin Pop
loadPParamField field = do
757 879273e3 Iustin Pop
  checkNonOptDef field
758 879273e3 Iustin Pop
  let name = fieldName field
759 879273e3 Iustin Pop
      fvar = mkName name
760 879273e3 Iustin Pop
  -- these are used in all patterns below
761 879273e3 Iustin Pop
  let objvar = varNameE "o"
762 879273e3 Iustin Pop
      objfield = stringE name
763 879273e3 Iustin Pop
      loadexp = [| $(varNameE "maybeFromObj") $objvar $objfield |]
764 879273e3 Iustin Pop
  bexp <- loadFn field loadexp
765 879273e3 Iustin Pop
  return (fvar, BindS (VarP fvar) bexp)
766 879273e3 Iustin Pop
767 879273e3 Iustin Pop
-- | Builds a simple declaration of type @n_x = fromMaybe f_x p_x@.
768 879273e3 Iustin Pop
buildFromMaybe :: String -> Q Dec
769 879273e3 Iustin Pop
buildFromMaybe fname =
770 879273e3 Iustin Pop
  valD (varP (mkName $ "n_" ++ fname))
771 879273e3 Iustin Pop
         (normalB [| $(varNameE "fromMaybe")
772 879273e3 Iustin Pop
                        $(varNameE $ "f_" ++ fname)
773 879273e3 Iustin Pop
                        $(varNameE $ "p_" ++ fname) |]) []
774 879273e3 Iustin Pop
775 879273e3 Iustin Pop
fillParam :: String -> String -> [Field] -> Q [Dec]
776 879273e3 Iustin Pop
fillParam sname field_pfx fields = do
777 879273e3 Iustin Pop
  let fnames = map (\fd -> field_pfx ++ fieldRecordName fd) fields
778 879273e3 Iustin Pop
      (sname_f, sname_p) = paramTypeNames sname
779 879273e3 Iustin Pop
      oname_f = "fobj"
780 879273e3 Iustin Pop
      oname_p = "pobj"
781 879273e3 Iustin Pop
      name_f = mkName sname_f
782 879273e3 Iustin Pop
      name_p = mkName sname_p
783 879273e3 Iustin Pop
      fun_name = mkName $ "fill" ++ sname ++ "Params"
784 879273e3 Iustin Pop
      le_full = ValD (ConP name_f (map (VarP . mkName . ("f_" ++)) fnames))
785 879273e3 Iustin Pop
                (NormalB . VarE . mkName $ oname_f) []
786 879273e3 Iustin Pop
      le_part = ValD (ConP name_p (map (VarP . mkName . ("p_" ++)) fnames))
787 879273e3 Iustin Pop
                (NormalB . VarE . mkName $ oname_p) []
788 879273e3 Iustin Pop
      obj_new = foldl (\accu vname -> AppE accu (VarE vname)) (ConE name_f)
789 879273e3 Iustin Pop
                $ map (mkName . ("n_" ++)) fnames
790 879273e3 Iustin Pop
  le_new <- mapM buildFromMaybe fnames
791 879273e3 Iustin Pop
  funt <- [t| $(conT name_f) -> $(conT name_p) -> $(conT name_f) |]
792 879273e3 Iustin Pop
  let sig = SigD fun_name funt
793 879273e3 Iustin Pop
      fclause = Clause [VarP (mkName oname_f), VarP (mkName oname_p)]
794 879273e3 Iustin Pop
                (NormalB $ LetE (le_full:le_part:le_new) obj_new) []
795 879273e3 Iustin Pop
      fun = FunD fun_name [fclause]
796 879273e3 Iustin Pop
  return [sig, fun]