Add a function for all fields of a given OP_ID
[ganeti-local] / htools / Ganeti / THH.hs
1 {-# LANGUAGE TemplateHaskell #-}
2
3 {-| TemplateHaskell helper for Ganeti Haskell code.
4
5 As TemplateHaskell require that splices be defined in a separate
6 module, we combine all the TemplateHaskell functionality that HTools
7 needs in this module (except the one for unittests).
8
9 -}
10
11 {-
12
13 Copyright (C) 2011, 2012 Google Inc.
14
15 This program is free software; you can redistribute it and/or modify
16 it under the terms of the GNU General Public License as published by
17 the Free Software Foundation; either version 2 of the License, or
18 (at your option) any later version.
19
20 This program is distributed in the hope that it will be useful, but
21 WITHOUT ANY WARRANTY; without even the implied warranty of
22 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
23 General Public License for more details.
24
25 You should have received a copy of the GNU General Public License
26 along with this program; if not, write to the Free Software
27 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
28 02110-1301, USA.
29
30 -}
31
32 module Ganeti.THH ( declareSADT
33                   , declareIADT
34                   , makeJSONInstance
35                   , genOpID
36                   , genAllConstr
37                   , genAllOpIDs
38                   , genOpCode
39                   , genStrOfOp
40                   , genStrOfKey
41                   , genLuxiOp
42                   , Field
43                   , simpleField
44                   , defaultField
45                   , optionalField
46                   , optionalNullSerField
47                   , renameField
48                   , customField
49                   , timeStampFields
50                   , uuidFields
51                   , serialFields
52                   , tagsFields
53                   , TagSet
54                   , buildObject
55                   , buildObjectSerialisation
56                   , buildParam
57                   , DictObject(..)
58                   , genException
59                   , excErrMsg
60                   ) where
61
62 import Control.Monad (liftM)
63 import Data.Char
64 import Data.List
65 import Data.Maybe (fromMaybe)
66 import qualified Data.Set as Set
67 import Language.Haskell.TH
68
69 import qualified Text.JSON as JSON
70 import Text.JSON.Pretty (pp_value)
71
72 import Ganeti.JSON
73
74 -- * Exported types
75
76 -- | Class of objects that can be converted to 'JSObject'
77 -- lists-format.
78 class DictObject a where
79   toDict :: a -> [(String, JSON.JSValue)]
80
81 -- | Optional field information.
82 data OptionalType
83   = NotOptional           -- ^ Field is not optional
84   | OptionalOmitNull      -- ^ Field is optional, null is not serialised
85   | OptionalSerializeNull -- ^ Field is optional, null is serialised
86   deriving (Show, Eq)
87
88 -- | Serialised field data type.
89 data Field = Field { fieldName        :: String
90                    , fieldType        :: Q Type
91                    , fieldRead        :: Maybe (Q Exp)
92                    , fieldShow        :: Maybe (Q Exp)
93                    , fieldExtraKeys   :: [String]
94                    , fieldDefault     :: Maybe (Q Exp)
95                    , fieldConstr      :: Maybe String
96                    , fieldIsOptional  :: OptionalType
97                    }
98
99 -- | Generates a simple field.
100 simpleField :: String -> Q Type -> Field
101 simpleField fname ftype =
102   Field { fieldName        = fname
103         , fieldType        = ftype
104         , fieldRead        = Nothing
105         , fieldShow        = Nothing
106         , fieldExtraKeys   = []
107         , fieldDefault     = Nothing
108         , fieldConstr      = Nothing
109         , fieldIsOptional  = NotOptional
110         }
111
112 -- | Sets the renamed constructor field.
113 renameField :: String -> Field -> Field
114 renameField constrName field = field { fieldConstr = Just constrName }
115
116 -- | Sets the default value on a field (makes it optional with a
117 -- default value).
118 defaultField :: Q Exp -> Field -> Field
119 defaultField defval field = field { fieldDefault = Just defval }
120
121 -- | Marks a field optional (turning its base type into a Maybe).
122 optionalField :: Field -> Field
123 optionalField field = field { fieldIsOptional = OptionalOmitNull }
124
125 -- | Marks a field optional (turning its base type into a Maybe), but
126 -- with 'Nothing' serialised explicitly as /null/.
127 optionalNullSerField :: Field -> Field
128 optionalNullSerField field = field { fieldIsOptional = OptionalSerializeNull }
129
130 -- | Sets custom functions on a field.
131 customField :: Name      -- ^ The name of the read function
132             -> Name      -- ^ The name of the show function
133             -> [String]  -- ^ The name of extra field keys
134             -> Field     -- ^ The original field
135             -> Field     -- ^ Updated field
136 customField readfn showfn extra field =
137   field { fieldRead = Just (varE readfn), fieldShow = Just (varE showfn)
138         , fieldExtraKeys = extra }
139
140 -- | Computes the record name for a given field, based on either the
141 -- string value in the JSON serialisation or the custom named if any
142 -- exists.
143 fieldRecordName :: Field -> String
144 fieldRecordName (Field { fieldName = name, fieldConstr = alias }) =
145   fromMaybe (camelCase name) alias
146
147 -- | Computes the preferred variable name to use for the value of this
148 -- field. If the field has a specific constructor name, then we use a
149 -- first-letter-lowercased version of that; otherwise, we simply use
150 -- the field name. See also 'fieldRecordName'.
151 fieldVariable :: Field -> String
152 fieldVariable f =
153   case (fieldConstr f) of
154     Just name -> ensureLower name
155     _ -> map (\c -> if c == '-' then '_' else c) $ fieldName f
156
157 -- | Compute the actual field type (taking into account possible
158 -- optional status).
159 actualFieldType :: Field -> Q Type
160 actualFieldType f | fieldIsOptional f /= NotOptional = [t| Maybe $t |]
161                   | otherwise = t
162                   where t = fieldType f
163
164 -- | Checks that a given field is not optional (for object types or
165 -- fields which should not allow this case).
166 checkNonOptDef :: (Monad m) => Field -> m ()
167 checkNonOptDef (Field { fieldIsOptional = OptionalOmitNull
168                       , fieldName = name }) =
169   fail $ "Optional field " ++ name ++ " used in parameter declaration"
170 checkNonOptDef (Field { fieldIsOptional = OptionalSerializeNull
171                       , fieldName = name }) =
172   fail $ "Optional field " ++ name ++ " used in parameter declaration"
173 checkNonOptDef (Field { fieldDefault = (Just _), fieldName = name }) =
174   fail $ "Default field " ++ name ++ " used in parameter declaration"
175 checkNonOptDef _ = return ()
176
177 -- | Produces the expression that will de-serialise a given
178 -- field. Since some custom parsing functions might need to use the
179 -- entire object, we do take and pass the object to any custom read
180 -- functions.
181 loadFn :: Field   -- ^ The field definition
182        -> Q Exp   -- ^ The value of the field as existing in the JSON message
183        -> Q Exp   -- ^ The entire object in JSON object format
184        -> Q Exp   -- ^ Resulting expression
185 loadFn (Field { fieldRead = Just readfn }) expr o = [| $expr >>= $readfn $o |]
186 loadFn _ expr _ = expr
187
188 -- * Common field declarations
189
190 -- | Timestamp fields description.
191 timeStampFields :: [Field]
192 timeStampFields =
193     [ defaultField [| 0::Double |] $ simpleField "ctime" [t| Double |]
194     , defaultField [| 0::Double |] $ simpleField "mtime" [t| Double |]
195     ]
196
197 -- | Serial number fields description.
198 serialFields :: [Field]
199 serialFields =
200     [ renameField  "Serial" $ simpleField "serial_no" [t| Int |] ]
201
202 -- | UUID fields description.
203 uuidFields :: [Field]
204 uuidFields = [ simpleField "uuid" [t| String |] ]
205
206 -- | Tag set type alias.
207 type TagSet = Set.Set String
208
209 -- | Tag field description.
210 tagsFields :: [Field]
211 tagsFields = [ defaultField [| Set.empty |] $
212                simpleField "tags" [t| TagSet |] ]
213
214 -- * Internal types
215
216 -- | A simple field, in constrast to the customisable 'Field' type.
217 type SimpleField = (String, Q Type)
218
219 -- | A definition for a single constructor for a simple object.
220 type SimpleConstructor = (String, [SimpleField])
221
222 -- | A definition for ADTs with simple fields.
223 type SimpleObject = [SimpleConstructor]
224
225 -- * Helper functions
226
227 -- | Ensure first letter is lowercase.
228 --
229 -- Used to convert type name to function prefix, e.g. in @data Aa ->
230 -- aaToRaw@.
231 ensureLower :: String -> String
232 ensureLower [] = []
233 ensureLower (x:xs) = toLower x:xs
234
235 -- | Ensure first letter is uppercase.
236 --
237 -- Used to convert constructor name to component
238 ensureUpper :: String -> String
239 ensureUpper [] = []
240 ensureUpper (x:xs) = toUpper x:xs
241
242 -- | Helper for quoted expressions.
243 varNameE :: String -> Q Exp
244 varNameE = varE . mkName
245
246 -- | showJSON as an expression, for reuse.
247 showJSONE :: Q Exp
248 showJSONE = varE 'JSON.showJSON
249
250 -- | makeObj as an expression, for reuse.
251 makeObjE :: Q Exp
252 makeObjE = varE 'JSON.makeObj
253
254 -- | fromObj (Ganeti specific) as an expression, for reuse.
255 fromObjE :: Q Exp
256 fromObjE = varE 'fromObj
257
258 -- | ToRaw function name.
259 toRawName :: String -> Name
260 toRawName = mkName . (++ "ToRaw") . ensureLower
261
262 -- | FromRaw function name.
263 fromRawName :: String -> Name
264 fromRawName = mkName . (++ "FromRaw") . ensureLower
265
266 -- | Converts a name to it's varE\/litE representations.
267 reprE :: Either String Name -> Q Exp
268 reprE = either stringE varE
269
270 -- | Smarter function application.
271 --
272 -- This does simply f x, except that if is 'id', it will skip it, in
273 -- order to generate more readable code when using -ddump-splices.
274 appFn :: Exp -> Exp -> Exp
275 appFn f x | f == VarE 'id = x
276           | otherwise = AppE f x
277
278 -- | Builds a field for a normal constructor.
279 buildConsField :: Q Type -> StrictTypeQ
280 buildConsField ftype = do
281   ftype' <- ftype
282   return (NotStrict, ftype')
283
284 -- | Builds a constructor based on a simple definition (not field-based).
285 buildSimpleCons :: Name -> SimpleObject -> Q Dec
286 buildSimpleCons tname cons = do
287   decl_d <- mapM (\(cname, fields) -> do
288                     fields' <- mapM (buildConsField . snd) fields
289                     return $ NormalC (mkName cname) fields') cons
290   return $ DataD [] tname [] decl_d [''Show, ''Eq]
291
292 -- | Generate the save function for a given type.
293 genSaveSimpleObj :: Name                            -- ^ Object type
294                  -> String                          -- ^ Function name
295                  -> SimpleObject                    -- ^ Object definition
296                  -> (SimpleConstructor -> Q Clause) -- ^ Constructor save fn
297                  -> Q (Dec, Dec)
298 genSaveSimpleObj tname sname opdefs fn = do
299   let sigt = AppT (AppT ArrowT (ConT tname)) (ConT ''JSON.JSValue)
300       fname = mkName sname
301   cclauses <- mapM fn opdefs
302   return $ (SigD fname sigt, FunD fname cclauses)
303
304 -- * Template code for simple raw type-equivalent ADTs
305
306 -- | Generates a data type declaration.
307 --
308 -- The type will have a fixed list of instances.
309 strADTDecl :: Name -> [String] -> Dec
310 strADTDecl name constructors =
311   DataD [] name []
312           (map (flip NormalC [] . mkName) constructors)
313           [''Show, ''Eq, ''Enum, ''Bounded, ''Ord]
314
315 -- | Generates a toRaw function.
316 --
317 -- This generates a simple function of the form:
318 --
319 -- @
320 -- nameToRaw :: Name -> /traw/
321 -- nameToRaw Cons1 = var1
322 -- nameToRaw Cons2 = \"value2\"
323 -- @
324 genToRaw :: Name -> Name -> Name -> [(String, Either String Name)] -> Q [Dec]
325 genToRaw traw fname tname constructors = do
326   let sigt = AppT (AppT ArrowT (ConT tname)) (ConT traw)
327   -- the body clauses, matching on the constructor and returning the
328   -- raw value
329   clauses <- mapM  (\(c, v) -> clause [recP (mkName c) []]
330                              (normalB (reprE v)) []) constructors
331   return [SigD fname sigt, FunD fname clauses]
332
333 -- | Generates a fromRaw function.
334 --
335 -- The function generated is monadic and can fail parsing the
336 -- raw value. It is of the form:
337 --
338 -- @
339 -- nameFromRaw :: (Monad m) => /traw/ -> m Name
340 -- nameFromRaw s | s == var1       = Cons1
341 --               | s == \"value2\" = Cons2
342 --               | otherwise = fail /.../
343 -- @
344 genFromRaw :: Name -> Name -> Name -> [(String, Name)] -> Q [Dec]
345 genFromRaw traw fname tname constructors = do
346   -- signature of form (Monad m) => String -> m $name
347   sigt <- [t| (Monad m) => $(conT traw) -> m $(conT tname) |]
348   -- clauses for a guarded pattern
349   let varp = mkName "s"
350       varpe = varE varp
351   clauses <- mapM (\(c, v) -> do
352                      -- the clause match condition
353                      g <- normalG [| $varpe == $(varE v) |]
354                      -- the clause result
355                      r <- [| return $(conE (mkName c)) |]
356                      return (g, r)) constructors
357   -- the otherwise clause (fallback)
358   oth_clause <- do
359     g <- normalG [| otherwise |]
360     r <- [|fail ("Invalid string value for type " ++
361                  $(litE (stringL (nameBase tname))) ++ ": " ++ show $varpe) |]
362     return (g, r)
363   let fun = FunD fname [Clause [VarP varp]
364                         (GuardedB (clauses++[oth_clause])) []]
365   return [SigD fname sigt, fun]
366
367 -- | Generates a data type from a given raw format.
368 --
369 -- The format is expected to multiline. The first line contains the
370 -- type name, and the rest of the lines must contain two words: the
371 -- constructor name and then the string representation of the
372 -- respective constructor.
373 --
374 -- The function will generate the data type declaration, and then two
375 -- functions:
376 --
377 -- * /name/ToRaw, which converts the type to a raw type
378 --
379 -- * /name/FromRaw, which (monadically) converts from a raw type to the type
380 --
381 -- Note that this is basically just a custom show\/read instance,
382 -- nothing else.
383 declareADT :: Name -> String -> [(String, Name)] -> Q [Dec]
384 declareADT traw sname cons = do
385   let name = mkName sname
386       ddecl = strADTDecl name (map fst cons)
387       -- process cons in the format expected by genToRaw
388       cons' = map (\(a, b) -> (a, Right b)) cons
389   toraw <- genToRaw traw (toRawName sname) name cons'
390   fromraw <- genFromRaw traw (fromRawName sname) name cons
391   return $ ddecl:toraw ++ fromraw
392
393 declareIADT :: String -> [(String, Name)] -> Q [Dec]
394 declareIADT = declareADT ''Int
395
396 declareSADT :: String -> [(String, Name)] -> Q [Dec]
397 declareSADT = declareADT ''String
398
399 -- | Creates the showJSON member of a JSON instance declaration.
400 --
401 -- This will create what is the equivalent of:
402 --
403 -- @
404 -- showJSON = showJSON . /name/ToRaw
405 -- @
406 --
407 -- in an instance JSON /name/ declaration
408 genShowJSON :: String -> Q Dec
409 genShowJSON name = do
410   body <- [| JSON.showJSON . $(varE (toRawName name)) |]
411   return $ FunD 'JSON.showJSON [Clause [] (NormalB body) []]
412
413 -- | Creates the readJSON member of a JSON instance declaration.
414 --
415 -- This will create what is the equivalent of:
416 --
417 -- @
418 -- readJSON s = case readJSON s of
419 --                Ok s' -> /name/FromRaw s'
420 --                Error e -> Error /description/
421 -- @
422 --
423 -- in an instance JSON /name/ declaration
424 genReadJSON :: String -> Q Dec
425 genReadJSON name = do
426   let s = mkName "s"
427   body <- [| case JSON.readJSON $(varE s) of
428                JSON.Ok s' -> $(varE (fromRawName name)) s'
429                JSON.Error e ->
430                    JSON.Error $ "Can't parse raw value for type " ++
431                            $(stringE name) ++ ": " ++ e ++ " from " ++
432                            show $(varE s)
433            |]
434   return $ FunD 'JSON.readJSON [Clause [VarP s] (NormalB body) []]
435
436 -- | Generates a JSON instance for a given type.
437 --
438 -- This assumes that the /name/ToRaw and /name/FromRaw functions
439 -- have been defined as by the 'declareSADT' function.
440 makeJSONInstance :: Name -> Q [Dec]
441 makeJSONInstance name = do
442   let base = nameBase name
443   showJ <- genShowJSON base
444   readJ <- genReadJSON base
445   return [InstanceD [] (AppT (ConT ''JSON.JSON) (ConT name)) [readJ,showJ]]
446
447 -- * Template code for opcodes
448
449 -- | Transforms a CamelCase string into an_underscore_based_one.
450 deCamelCase :: String -> String
451 deCamelCase =
452     intercalate "_" . map (map toUpper) . groupBy (\_ b -> not $ isUpper b)
453
454 -- | Transform an underscore_name into a CamelCase one.
455 camelCase :: String -> String
456 camelCase = concatMap (ensureUpper . drop 1) .
457             groupBy (\_ b -> b /= '_' && b /= '-') . ('_':)
458
459 -- | Computes the name of a given constructor.
460 constructorName :: Con -> Q Name
461 constructorName (NormalC name _) = return name
462 constructorName (RecC name _)    = return name
463 constructorName x                = fail $ "Unhandled constructor " ++ show x
464
465 -- | Extract all constructor names from a given type.
466 reifyConsNames :: Name -> Q [String]
467 reifyConsNames name = do
468   reify_result <- reify name
469   case reify_result of
470     TyConI (DataD _ _ _ cons _) -> mapM (liftM nameBase . constructorName) cons
471     o -> fail $ "Unhandled name passed to reifyConsNames, expected\
472                 \ type constructor but got '" ++ show o ++ "'"
473
474 -- | Builds the generic constructor-to-string function.
475 --
476 -- This generates a simple function of the following form:
477 --
478 -- @
479 -- fname (ConStructorOne {}) = trans_fun("ConStructorOne")
480 -- fname (ConStructorTwo {}) = trans_fun("ConStructorTwo")
481 -- @
482 --
483 -- This builds a custom list of name\/string pairs and then uses
484 -- 'genToRaw' to actually generate the function.
485 genConstrToStr :: (String -> String) -> Name -> String -> Q [Dec]
486 genConstrToStr trans_fun name fname = do
487   cnames <- reifyConsNames name
488   let svalues = map (Left . trans_fun) cnames
489   genToRaw ''String (mkName fname) name $ zip cnames svalues
490
491 -- | Constructor-to-string for OpCode.
492 genOpID :: Name -> String -> Q [Dec]
493 genOpID = genConstrToStr deCamelCase
494
495 -- | Builds a list with all defined constructor names for a type.
496 --
497 -- @
498 -- vstr :: String
499 -- vstr = [...]
500 -- @
501 --
502 -- Where the actual values of the string are the constructor names
503 -- mapped via @trans_fun@.
504 genAllConstr :: (String -> String) -> Name -> String -> Q [Dec]
505 genAllConstr trans_fun name vstr = do
506   cnames <- reifyConsNames name
507   let svalues = sort $ map trans_fun cnames
508       vname = mkName vstr
509       sig = SigD vname (AppT ListT (ConT ''String))
510       body = NormalB (ListE (map (LitE . StringL) svalues))
511   return $ [sig, ValD (VarP vname) body []]
512
513 -- | Generates a list of all defined opcode IDs.
514 genAllOpIDs :: Name -> String -> Q [Dec]
515 genAllOpIDs = genAllConstr deCamelCase
516
517 -- | OpCode parameter (field) type.
518 type OpParam = (String, Q Type, Q Exp)
519
520 -- | Generates the OpCode data type.
521 --
522 -- This takes an opcode logical definition, and builds both the
523 -- datatype and the JSON serialisation out of it. We can't use a
524 -- generic serialisation since we need to be compatible with Ganeti's
525 -- own, so we have a few quirks to work around.
526 genOpCode :: String                -- ^ Type name to use
527           -> [(String, [Field])]   -- ^ Constructor name and parameters
528           -> Q [Dec]
529 genOpCode name cons = do
530   let tname = mkName name
531   decl_d <- mapM (\(cname, fields) -> do
532                     -- we only need the type of the field, without Q
533                     fields' <- mapM (fieldTypeInfo "op") fields
534                     return $ RecC (mkName cname) fields')
535             cons
536   let declD = DataD [] tname [] decl_d [''Show, ''Eq]
537
538   let (allfsig, allffn) = genAllOpFields "allOpFields" cons
539   (savesig, savefn) <- genSaveOpCode tname "saveOpCode" cons
540                          (uncurry saveConstructor)
541   (loadsig, loadfn) <- genLoadOpCode cons
542   return [declD, allfsig, allffn, loadsig, loadfn, savesig, savefn]
543
544 -- | Generates the function pattern returning the list of fields for a
545 -- given constructor.
546 genOpConsFields :: (String, [Field]) -> Clause
547 genOpConsFields (cname, fields) =
548   let op_id = deCamelCase cname
549       fvals = map (LitE . StringL) . sort . nub $
550               concatMap (\f -> fieldName f:fieldExtraKeys f) fields
551   in Clause [LitP (StringL op_id)] (NormalB $ ListE fvals) []
552
553 -- | Generates a list of all fields of an opcode constructor.
554 genAllOpFields  :: String                          -- ^ Function name
555                 -> [(String, [Field])]             -- ^ Object definition
556                 -> (Dec, Dec)
557 genAllOpFields sname opdefs =
558   let cclauses = map genOpConsFields opdefs
559       other = Clause [WildP] (NormalB (ListE [])) []
560       fname = mkName sname
561       sigt = AppT  (AppT ArrowT (ConT ''String)) (AppT ListT (ConT ''String))
562   in (SigD fname sigt, FunD fname (cclauses++[other]))
563
564 -- | Generates the \"save\" clause for an entire opcode constructor.
565 --
566 -- This matches the opcode with variables named the same as the
567 -- constructor fields (just so that the spliced in code looks nicer),
568 -- and passes those name plus the parameter definition to 'saveObjectField'.
569 saveConstructor :: String    -- ^ The constructor name
570                 -> [Field]   -- ^ The parameter definitions for this
571                              -- constructor
572                 -> Q Clause  -- ^ Resulting clause
573 saveConstructor sname fields = do
574   let cname = mkName sname
575   fnames <- mapM (newName . fieldVariable) fields
576   let pat = conP cname (map varP fnames)
577   let felems = map (uncurry saveObjectField) (zip fnames fields)
578       -- now build the OP_ID serialisation
579       opid = [| [( $(stringE "OP_ID"),
580                    JSON.showJSON $(stringE . deCamelCase $ sname) )] |]
581       flist = listE (opid:felems)
582       -- and finally convert all this to a json object
583       flist' = [| $makeObjE (concat $flist) |]
584   clause [pat] (normalB flist') []
585
586 -- | Generates the main save opcode function.
587 --
588 -- This builds a per-constructor match clause that contains the
589 -- respective constructor-serialisation code.
590 genSaveOpCode :: Name                            -- ^ Object ype
591               -> String                          -- ^ Function name
592               -> [(String, [Field])]             -- ^ Object definition
593               -> ((String, [Field]) -> Q Clause) -- ^ Constructor save fn
594               -> Q (Dec, Dec)
595 genSaveOpCode tname sname opdefs fn = do
596   cclauses <- mapM fn opdefs
597   let fname = mkName sname
598       sigt = AppT  (AppT ArrowT (ConT tname)) (ConT ''JSON.JSValue)
599   return $ (SigD fname sigt, FunD fname cclauses)
600
601 -- | Generates load code for a single constructor of the opcode data type.
602 loadConstructor :: String -> [Field] -> Q Exp
603 loadConstructor sname fields = do
604   let name = mkName sname
605   fbinds <- mapM loadObjectField fields
606   let (fnames, fstmts) = unzip fbinds
607   let cval = foldl (\accu fn -> AppE accu (VarE fn)) (ConE name) fnames
608       fstmts' = fstmts ++ [NoBindS (AppE (VarE 'return) cval)]
609   return $ DoE fstmts'
610
611 -- | Generates the loadOpCode function.
612 genLoadOpCode :: [(String, [Field])] -> Q (Dec, Dec)
613 genLoadOpCode opdefs = do
614   let fname = mkName "loadOpCode"
615       arg1 = mkName "v"
616       objname = mkName "o"
617       opid = mkName "op_id"
618   st1 <- bindS (varP objname) [| liftM JSON.fromJSObject
619                                  (JSON.readJSON $(varE arg1)) |]
620   st2 <- bindS (varP opid) [| $fromObjE $(varE objname) $(stringE "OP_ID") |]
621   -- the match results (per-constructor blocks)
622   mexps <- mapM (uncurry loadConstructor) opdefs
623   fails <- [| fail $ "Unknown opcode " ++ $(varE opid) |]
624   let mpats = map (\(me, c) ->
625                        let mp = LitP . StringL . deCamelCase . fst $ c
626                        in Match mp (NormalB me) []
627                   ) $ zip mexps opdefs
628       defmatch = Match WildP (NormalB fails) []
629       cst = NoBindS $ CaseE (VarE opid) $ mpats++[defmatch]
630       body = DoE [st1, st2, cst]
631   sigt <- [t| JSON.JSValue -> JSON.Result $(conT (mkName "OpCode")) |]
632   return $ (SigD fname sigt, FunD fname [Clause [VarP arg1] (NormalB body) []])
633
634 -- * Template code for luxi
635
636 -- | Constructor-to-string for LuxiOp.
637 genStrOfOp :: Name -> String -> Q [Dec]
638 genStrOfOp = genConstrToStr id
639
640 -- | Constructor-to-string for MsgKeys.
641 genStrOfKey :: Name -> String -> Q [Dec]
642 genStrOfKey = genConstrToStr ensureLower
643
644 -- | Generates the LuxiOp data type.
645 --
646 -- This takes a Luxi operation definition and builds both the
647 -- datatype and the function trnasforming the arguments to JSON.
648 -- We can't use anything less generic, because the way different
649 -- operations are serialized differs on both parameter- and top-level.
650 --
651 -- There are two things to be defined for each parameter:
652 --
653 -- * name
654 --
655 -- * type
656 --
657 genLuxiOp :: String -> [(String, [Field])] -> Q [Dec]
658 genLuxiOp name cons = do
659   let tname = mkName name
660   decl_d <- mapM (\(cname, fields) -> do
661                     -- we only need the type of the field, without Q
662                     fields' <- mapM actualFieldType fields
663                     let fields'' = zip (repeat NotStrict) fields'
664                     return $ NormalC (mkName cname) fields'')
665             cons
666   let declD = DataD [] (mkName name) [] decl_d [''Show, ''Eq]
667   (savesig, savefn) <- genSaveOpCode tname "opToArgs"
668                          cons saveLuxiConstructor
669   req_defs <- declareSADT "LuxiReq" .
670               map (\(str, _) -> ("Req" ++ str, mkName ("luxiReq" ++ str))) $
671                   cons
672   return $ [declD, savesig, savefn] ++ req_defs
673
674 -- | Generates the \"save\" expression for a single luxi parameter.
675 saveLuxiField :: Name -> SimpleField -> Q Exp
676 saveLuxiField fvar (_, qt) =
677     [| JSON.showJSON $(varE fvar) |]
678
679 -- | Generates the \"save\" clause for entire LuxiOp constructor.
680 saveLuxiConstructor :: (String, [Field]) -> Q Clause
681 saveLuxiConstructor (sname, fields) = do
682   let cname = mkName sname
683   fnames <- mapM (newName . fieldVariable) fields
684   let pat = conP cname (map varP fnames)
685   let felems = map (uncurry saveObjectField) (zip fnames fields)
686       flist = if null felems
687                 then [| JSON.showJSON () |]
688                 else [| JSON.showJSON (map snd $ concat $(listE felems)) |]
689   clause [pat] (normalB flist) []
690
691 -- * "Objects" functionality
692
693 -- | Extract the field's declaration from a Field structure.
694 fieldTypeInfo :: String -> Field -> Q (Name, Strict, Type)
695 fieldTypeInfo field_pfx fd = do
696   t <- actualFieldType fd
697   let n = mkName . (field_pfx ++) . fieldRecordName $ fd
698   return (n, NotStrict, t)
699
700 -- | Build an object declaration.
701 buildObject :: String -> String -> [Field] -> Q [Dec]
702 buildObject sname field_pfx fields = do
703   let name = mkName sname
704   fields_d <- mapM (fieldTypeInfo field_pfx) fields
705   let decl_d = RecC name fields_d
706   let declD = DataD [] name [] [decl_d] [''Show, ''Eq]
707   ser_decls <- buildObjectSerialisation sname fields
708   return $ declD:ser_decls
709
710 -- | Generates an object definition: data type and its JSON instance.
711 buildObjectSerialisation :: String -> [Field] -> Q [Dec]
712 buildObjectSerialisation sname fields = do
713   let name = mkName sname
714   savedecls <- genSaveObject saveObjectField sname fields
715   (loadsig, loadfn) <- genLoadObject loadObjectField sname fields
716   shjson <- objectShowJSON sname
717   rdjson <- objectReadJSON sname
718   let instdecl = InstanceD [] (AppT (ConT ''JSON.JSON) (ConT name))
719                  [rdjson, shjson]
720   return $ savedecls ++ [loadsig, loadfn, instdecl]
721
722 -- | The toDict function name for a given type.
723 toDictName :: String -> Name
724 toDictName sname = mkName ("toDict" ++ sname)
725
726 -- | Generates the save object functionality.
727 genSaveObject :: (Name -> Field -> Q Exp)
728               -> String -> [Field] -> Q [Dec]
729 genSaveObject save_fn sname fields = do
730   let name = mkName sname
731   fnames <- mapM (newName . fieldVariable) fields
732   let pat = conP name (map varP fnames)
733   let tdname = toDictName sname
734   tdsigt <- [t| $(conT name) -> [(String, JSON.JSValue)] |]
735
736   let felems = map (uncurry save_fn) (zip fnames fields)
737       flist = listE felems
738       -- and finally convert all this to a json object
739       tdlist = [| concat $flist |]
740       iname = mkName "i"
741   tclause <- clause [pat] (normalB tdlist) []
742   cclause <- [| $makeObjE . $(varE tdname) |]
743   let fname = mkName ("save" ++ sname)
744   sigt <- [t| $(conT name) -> JSON.JSValue |]
745   return [SigD tdname tdsigt, FunD tdname [tclause],
746           SigD fname sigt, ValD (VarP fname) (NormalB cclause) []]
747
748 -- | Generates the code for saving an object's field, handling the
749 -- various types of fields that we have.
750 saveObjectField :: Name -> Field -> Q Exp
751 saveObjectField fvar field =
752   case fieldIsOptional field of
753     OptionalOmitNull -> [| case $(varE fvar) of
754                              Nothing -> []
755                              Just v  -> [( $nameE, JSON.showJSON v )]
756                          |]
757     OptionalSerializeNull -> [| case $(varE fvar) of
758                                   Nothing -> [( $nameE, JSON.JSNull )]
759                                   Just v  -> [( $nameE, JSON.showJSON v )]
760                               |]
761     NotOptional ->
762       case fieldShow field of
763         -- Note: the order of actual:extra is important, since for
764         -- some serialisation types (e.g. Luxi), we use tuples
765         -- (positional info) rather than object (name info)
766         Nothing -> [| [( $nameE, JSON.showJSON $fvarE)] |]
767         Just fn -> [| let (actual, extra) = $fn $fvarE
768                       in ($nameE, JSON.showJSON actual):extra
769                     |]
770   where nameE = stringE (fieldName field)
771         fvarE = varE fvar
772
773 -- | Generates the showJSON clause for a given object name.
774 objectShowJSON :: String -> Q Dec
775 objectShowJSON name = do
776   body <- [| JSON.showJSON . $(varE . mkName $ "save" ++ name) |]
777   return $ FunD 'JSON.showJSON [Clause [] (NormalB body) []]
778
779 -- | Generates the load object functionality.
780 genLoadObject :: (Field -> Q (Name, Stmt))
781               -> String -> [Field] -> Q (Dec, Dec)
782 genLoadObject load_fn sname fields = do
783   let name = mkName sname
784       funname = mkName $ "load" ++ sname
785       arg1 = mkName "v"
786       objname = mkName "o"
787       opid = mkName "op_id"
788   st1 <- bindS (varP objname) [| liftM JSON.fromJSObject
789                                  (JSON.readJSON $(varE arg1)) |]
790   fbinds <- mapM load_fn fields
791   let (fnames, fstmts) = unzip fbinds
792   let cval = foldl (\accu fn -> AppE accu (VarE fn)) (ConE name) fnames
793       fstmts' = st1:fstmts ++ [NoBindS (AppE (VarE 'return) cval)]
794   sigt <- [t| JSON.JSValue -> JSON.Result $(conT name) |]
795   return $ (SigD funname sigt,
796             FunD funname [Clause [VarP arg1] (NormalB (DoE fstmts')) []])
797
798 -- | Generates code for loading an object's field.
799 loadObjectField :: Field -> Q (Name, Stmt)
800 loadObjectField field = do
801   let name = fieldVariable field
802   fvar <- newName name
803   -- these are used in all patterns below
804   let objvar = varNameE "o"
805       objfield = stringE (fieldName field)
806       loadexp =
807         if fieldIsOptional field /= NotOptional
808           -- we treat both optional types the same, since
809           -- 'maybeFromObj' can deal with both missing and null values
810           -- appropriately (the same)
811           then [| $(varE 'maybeFromObj) $objvar $objfield |]
812           else case fieldDefault field of
813                  Just defv ->
814                    [| $(varE 'fromObjWithDefault) $objvar
815                       $objfield $defv |]
816                  Nothing -> [| $fromObjE $objvar $objfield |]
817   bexp <- loadFn field loadexp objvar
818
819   return (fvar, BindS (VarP fvar) bexp)
820
821 -- | Builds the readJSON instance for a given object name.
822 objectReadJSON :: String -> Q Dec
823 objectReadJSON name = do
824   let s = mkName "s"
825   body <- [| case JSON.readJSON $(varE s) of
826                JSON.Ok s' -> $(varE .mkName $ "load" ++ name) s'
827                JSON.Error e ->
828                  JSON.Error $ "Can't parse value for type " ++
829                        $(stringE name) ++ ": " ++ e
830            |]
831   return $ FunD 'JSON.readJSON [Clause [VarP s] (NormalB body) []]
832
833 -- * Inheritable parameter tables implementation
834
835 -- | Compute parameter type names.
836 paramTypeNames :: String -> (String, String)
837 paramTypeNames root = ("Filled"  ++ root ++ "Params",
838                        "Partial" ++ root ++ "Params")
839
840 -- | Compute information about the type of a parameter field.
841 paramFieldTypeInfo :: String -> Field -> Q (Name, Strict, Type)
842 paramFieldTypeInfo field_pfx fd = do
843   t <- actualFieldType fd
844   let n = mkName . (++ "P") . (field_pfx ++) .
845           fieldRecordName $ fd
846   return (n, NotStrict, AppT (ConT ''Maybe) t)
847
848 -- | Build a parameter declaration.
849 --
850 -- This function builds two different data structures: a /filled/ one,
851 -- in which all fields are required, and a /partial/ one, in which all
852 -- fields are optional. Due to the current record syntax issues, the
853 -- fields need to be named differrently for the two structures, so the
854 -- partial ones get a /P/ suffix.
855 buildParam :: String -> String -> [Field] -> Q [Dec]
856 buildParam sname field_pfx fields = do
857   let (sname_f, sname_p) = paramTypeNames sname
858       name_f = mkName sname_f
859       name_p = mkName sname_p
860   fields_f <- mapM (fieldTypeInfo field_pfx) fields
861   fields_p <- mapM (paramFieldTypeInfo field_pfx) fields
862   let decl_f = RecC name_f fields_f
863       decl_p = RecC name_p fields_p
864   let declF = DataD [] name_f [] [decl_f] [''Show, ''Eq]
865       declP = DataD [] name_p [] [decl_p] [''Show, ''Eq]
866   ser_decls_f <- buildObjectSerialisation sname_f fields
867   ser_decls_p <- buildPParamSerialisation sname_p fields
868   fill_decls <- fillParam sname field_pfx fields
869   return $ [declF, declP] ++ ser_decls_f ++ ser_decls_p ++ fill_decls ++
870            buildParamAllFields sname fields ++
871            buildDictObjectInst name_f sname_f
872
873 -- | Builds a list of all fields of a parameter.
874 buildParamAllFields :: String -> [Field] -> [Dec]
875 buildParamAllFields sname fields =
876   let vname = mkName ("all" ++ sname ++ "ParamFields")
877       sig = SigD vname (AppT ListT (ConT ''String))
878       val = ListE $ map (LitE . StringL . fieldName) fields
879   in [sig, ValD (VarP vname) (NormalB val) []]
880
881 -- | Builds the 'DictObject' instance for a filled parameter.
882 buildDictObjectInst :: Name -> String -> [Dec]
883 buildDictObjectInst name sname =
884   [InstanceD [] (AppT (ConT ''DictObject) (ConT name))
885    [ValD (VarP 'toDict) (NormalB (VarE (toDictName sname))) []]]
886
887 -- | Generates the serialisation for a partial parameter.
888 buildPParamSerialisation :: String -> [Field] -> Q [Dec]
889 buildPParamSerialisation sname fields = do
890   let name = mkName sname
891   savedecls <- genSaveObject savePParamField sname fields
892   (loadsig, loadfn) <- genLoadObject loadPParamField sname fields
893   shjson <- objectShowJSON sname
894   rdjson <- objectReadJSON sname
895   let instdecl = InstanceD [] (AppT (ConT ''JSON.JSON) (ConT name))
896                  [rdjson, shjson]
897   return $ savedecls ++ [loadsig, loadfn, instdecl]
898
899 -- | Generates code to save an optional parameter field.
900 savePParamField :: Name -> Field -> Q Exp
901 savePParamField fvar field = do
902   checkNonOptDef field
903   let actualVal = mkName "v"
904   normalexpr <- saveObjectField actualVal field
905   -- we have to construct the block here manually, because we can't
906   -- splice-in-splice
907   return $ CaseE (VarE fvar) [ Match (ConP 'Nothing [])
908                                        (NormalB (ConE '[])) []
909                              , Match (ConP 'Just [VarP actualVal])
910                                        (NormalB normalexpr) []
911                              ]
912
913 -- | Generates code to load an optional parameter field.
914 loadPParamField :: Field -> Q (Name, Stmt)
915 loadPParamField field = do
916   checkNonOptDef field
917   let name = fieldName field
918   fvar <- newName name
919   -- these are used in all patterns below
920   let objvar = varNameE "o"
921       objfield = stringE name
922       loadexp = [| $(varE 'maybeFromObj) $objvar $objfield |]
923   bexp <- loadFn field loadexp objvar
924   return (fvar, BindS (VarP fvar) bexp)
925
926 -- | Builds a simple declaration of type @n_x = fromMaybe f_x p_x@.
927 buildFromMaybe :: String -> Q Dec
928 buildFromMaybe fname =
929   valD (varP (mkName $ "n_" ++ fname))
930          (normalB [| $(varE 'fromMaybe)
931                         $(varNameE $ "f_" ++ fname)
932                         $(varNameE $ "p_" ++ fname) |]) []
933
934 -- | Builds a function that executes the filling of partial parameter
935 -- from a full copy (similar to Python's fillDict).
936 fillParam :: String -> String -> [Field] -> Q [Dec]
937 fillParam sname field_pfx fields = do
938   let fnames = map (\fd -> field_pfx ++ fieldRecordName fd) fields
939       (sname_f, sname_p) = paramTypeNames sname
940       oname_f = "fobj"
941       oname_p = "pobj"
942       name_f = mkName sname_f
943       name_p = mkName sname_p
944       fun_name = mkName $ "fill" ++ sname ++ "Params"
945       le_full = ValD (ConP name_f (map (VarP . mkName . ("f_" ++)) fnames))
946                 (NormalB . VarE . mkName $ oname_f) []
947       le_part = ValD (ConP name_p (map (VarP . mkName . ("p_" ++)) fnames))
948                 (NormalB . VarE . mkName $ oname_p) []
949       obj_new = foldl (\accu vname -> AppE accu (VarE vname)) (ConE name_f)
950                 $ map (mkName . ("n_" ++)) fnames
951   le_new <- mapM buildFromMaybe fnames
952   funt <- [t| $(conT name_f) -> $(conT name_p) -> $(conT name_f) |]
953   let sig = SigD fun_name funt
954       fclause = Clause [VarP (mkName oname_f), VarP (mkName oname_p)]
955                 (NormalB $ LetE (le_full:le_part:le_new) obj_new) []
956       fun = FunD fun_name [fclause]
957   return [sig, fun]
958
959 -- * Template code for exceptions
960
961 -- | Exception simple error message field.
962 excErrMsg :: (String, Q Type)
963 excErrMsg = ("errMsg", [t| String |])
964
965 -- | Builds an exception type definition.
966 genException :: String                  -- ^ Name of new type
967              -> SimpleObject -- ^ Constructor name and parameters
968              -> Q [Dec]
969 genException name cons = do
970   let tname = mkName name
971   declD <- buildSimpleCons tname cons
972   (savesig, savefn) <- genSaveSimpleObj tname ("save" ++ name) cons $
973                          uncurry saveExcCons
974   (loadsig, loadfn) <- genLoadExc tname ("load" ++ name) cons
975   return [declD, loadsig, loadfn, savesig, savefn]
976
977 -- | Generates the \"save\" clause for an entire exception constructor.
978 --
979 -- This matches the exception with variables named the same as the
980 -- constructor fields (just so that the spliced in code looks nicer),
981 -- and calls showJSON on it.
982 saveExcCons :: String        -- ^ The constructor name
983             -> [SimpleField] -- ^ The parameter definitions for this
984                              -- constructor
985             -> Q Clause      -- ^ Resulting clause
986 saveExcCons sname fields = do
987   let cname = mkName sname
988   fnames <- mapM (newName . fst) fields
989   let pat = conP cname (map varP fnames)
990       felems = if null fnames
991                  then conE '() -- otherwise, empty list has no type
992                  else listE $ map (\f -> [| JSON.showJSON $(varE f) |]) fnames
993   let tup = tupE [ litE (stringL sname), felems ]
994   clause [pat] (normalB [| JSON.showJSON $tup |]) []
995
996 -- | Generates load code for a single constructor of an exception.
997 --
998 -- Generates the code (if there's only one argument, we will use a
999 -- list, not a tuple:
1000 --
1001 -- @
1002 -- do
1003 --  (x1, x2, ...) <- readJSON args
1004 --  return $ Cons x1 x2 ...
1005 -- @
1006 loadExcConstructor :: Name -> String -> [SimpleField] -> Q Exp
1007 loadExcConstructor inname sname fields = do
1008   let name = mkName sname
1009   f_names <- mapM (newName . fst) fields
1010   let read_args = AppE (VarE 'JSON.readJSON) (VarE inname)
1011   let binds = case f_names of
1012                 [x] -> BindS (ListP [VarP x])
1013                 _   -> BindS (TupP (map VarP f_names))
1014       cval = foldl (\accu fn -> AppE accu (VarE fn)) (ConE name) f_names
1015   return $ DoE [binds read_args, NoBindS (AppE (VarE 'return) cval)]
1016
1017 {-| Generates the loadException function.
1018
1019 This generates a quite complicated function, along the lines of:
1020
1021 @
1022 loadFn (JSArray [JSString name, args]) = case name of
1023    "A1" -> do
1024      (x1, x2, ...) <- readJSON args
1025      return $ A1 x1 x2 ...
1026    "a2" -> ...
1027    s -> fail $ "Unknown exception" ++ s
1028 loadFn v = fail $ "Expected array but got " ++ show v
1029 @
1030 -}
1031 genLoadExc :: Name -> String -> SimpleObject -> Q (Dec, Dec)
1032 genLoadExc tname sname opdefs = do
1033   let fname = mkName sname
1034   exc_name <- newName "name"
1035   exc_args <- newName "args"
1036   exc_else <- newName "s"
1037   arg_else <- newName "v"
1038   fails <- [| fail $ "Unknown exception '" ++ $(varE exc_else) ++ "'" |]
1039   -- default match for unknown exception name
1040   let defmatch = Match (VarP exc_else) (NormalB fails) []
1041   -- the match results (per-constructor blocks)
1042   str_matches <-
1043     mapM (\(s, params) -> do
1044             body_exp <- loadExcConstructor exc_args s params
1045             return $ Match (LitP (StringL s)) (NormalB body_exp) [])
1046     opdefs
1047   -- the first function clause; we can't use [| |] due to TH
1048   -- limitations, so we have to build the AST by hand
1049   let clause1 = Clause [ConP 'JSON.JSArray
1050                                [ListP [ConP 'JSON.JSString [VarP exc_name],
1051                                             VarP exc_args]]]
1052                 (NormalB (CaseE (AppE (VarE 'JSON.fromJSString)
1053                                         (VarE exc_name))
1054                           (str_matches ++ [defmatch]))) []
1055   -- the fail expression for the second function clause
1056   fail_type <- [| fail $ "Invalid exception: expected '(string, [args])' " ++
1057                   "      but got " ++ show (pp_value $(varE arg_else)) ++ "'"
1058                 |]
1059   -- the second function clause
1060   let clause2 = Clause [VarP arg_else] (NormalB fail_type) []
1061   sigt <- [t| JSON.JSValue -> JSON.Result $(conT tname) |]
1062   return $ (SigD fname sigt, FunD fname [clause1, clause2])