Statistics
| Branch: | Tag: | Revision:

root / htools / Ganeti / THH.hs @ 6bd26f00

History | View | Annotate | Download (30.5 kB)

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