Statistics
| Branch: | Tag: | Revision:

root / htools / Ganeti / THH.hs @ a583ec5d

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