Statistics
| Branch: | Tag: | Revision:

root / htools / Ganeti / THH.hs @ 02cccecd

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