Statistics
| Branch: | Tag: | Revision:

root / src / Ganeti / THH.hs @ b9202225

History | View | Annotate | Download (49.5 kB)

1
{-# LANGUAGE ParallelListComp, 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, 2013, 2014 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
                  , declareLADT
34
                  , declareILADT
35
                  , declareIADT
36
                  , makeJSONInstance
37
                  , deCamelCase
38
                  , genOpID
39
                  , genAllConstr
40
                  , genAllOpIDs
41
                  , PyValue(..)
42
                  , PyValueEx(..)
43
                  , OpCodeField(..)
44
                  , OpCodeDescriptor(..)
45
                  , genOpCode
46
                  , genStrOfOp
47
                  , genStrOfKey
48
                  , genLuxiOp
49
                  , Field (..)
50
                  , simpleField
51
                  , specialNumericalField
52
                  , timeAsDoubleField
53
                  , withDoc
54
                  , defaultField
55
                  , optionalField
56
                  , optionalNullSerField
57
                  , renameField
58
                  , customField
59
                  , timeStampFields
60
                  , uuidFields
61
                  , serialFields
62
                  , tagsFields
63
                  , TagSet
64
                  , buildObject
65
                  , buildObjectSerialisation
66
                  , buildParam
67
                  , DictObject(..)
68
                  , genException
69
                  , excErrMsg
70
                  ) where
71

    
72
import Control.Arrow ((&&&))
73
import Control.Applicative
74
import Control.Monad
75
import Data.Attoparsec () -- Needed to prevent spurious GHC 7.4 linking errors.
76
  -- See issue #683 and https://ghc.haskell.org/trac/ghc/ticket/4899
77
import Data.Char
78
import Data.List
79
import Data.Maybe
80
import qualified Data.Set as Set
81
import Language.Haskell.TH
82
import System.Time (ClockTime(..))
83

    
84
import qualified Text.JSON as JSON
85
import Text.JSON.Pretty (pp_value)
86

    
87
import Ganeti.JSON
88
import Ganeti.PyValue
89
import Ganeti.THH.PyType
90

    
91

    
92
-- * Exported types
93

    
94
-- | Class of objects that can be converted to 'JSObject'
95
-- lists-format.
96
class DictObject a where
97
  toDict :: a -> [(String, JSON.JSValue)]
98

    
99
-- | Optional field information.
100
data OptionalType
101
  = NotOptional           -- ^ Field is not optional
102
  | OptionalOmitNull      -- ^ Field is optional, null is not serialised
103
  | OptionalSerializeNull -- ^ Field is optional, null is serialised
104
  deriving (Show, Eq)
105

    
106
-- | Serialised field data type describing how to generate code for the field.
107
-- Each field has a type, which isn't captured in the type of the data type,
108
-- but is saved in the 'Q' monad in 'fieldType'.
109
--
110
-- Let @t@ be a type we want to parametrize the field with. There are the
111
-- following possible types of fields:
112
--
113
--   [Mandatory with no default.] Then @fieldType@ holds @t@,
114
--     @fieldDefault = Nothing@ and @fieldIsOptional = NotOptional@.
115
--
116
--   [Field with a default value.] Then @fieldType@ holds @t@ and
117
--     @fieldDefault = Just exp@ where @exp@ is an expression of type @t@ and
118
--    @fieldIsOptional = NotOptional@.
119
--
120
--   [Optional, no default value.] Then @fieldType@ holds @Maybe t@,
121
--     @fieldDefault = Nothing@ and @fieldIsOptional@ is either
122
--     'OptionalOmitNull' or 'OptionalSerializeNull'.
123
--
124
-- Optional fields with a default value are prohibited, as their main
125
-- intention is to represent the information that a request didn't contain
126
-- the field data.
127
--
128
-- /Custom (de)serialization:/
129
-- Field can have custom (de)serialization functions that are stored in
130
-- 'fieldRead' and 'fieldShow'. If they aren't provided, the default is to use
131
-- 'readJSON' and 'showJSON' for the field's type @t@. If they are provided,
132
-- the type of the contained deserializing expression must be
133
--
134
-- @
135
--   [(String, JSON.JSValue)] -> JSON.JSValue -> JSON.Result t
136
-- @
137
--
138
-- where the first argument carries the whole record in the case the
139
-- deserializing function needs to process additional information.
140
--
141
-- The type of the contained serializing experssion must be
142
--
143
-- @
144
--   t -> (JSON.JSValue, [(String, JSON.JSValue)])
145
-- @
146
--
147
-- where the result can provide extra JSON fields to include in the output
148
-- record (or just return @[]@ if they're not needed).
149
--
150
-- Note that for optional fields the type appearing in the custom functions
151
-- is still @t@. Therefore making a field optional doesn't change the
152
-- functions.
153
data Field = Field { fieldName        :: String
154
                   , fieldType        :: Q Type
155
                     -- ^ the type of the field, @t@ for non-optional fields,
156
                     -- @Maybe t@ for optional ones.
157
                   , fieldRead        :: Maybe (Q Exp)
158
                     -- ^ an optional custom deserialization function of type
159
                     -- @[(String, JSON.JSValue)] -> JSON.JSValue ->
160
                     -- JSON.Result t@
161
                   , fieldShow        :: Maybe (Q Exp)
162
                     -- ^ an optional custom serialization function of type
163
                     -- @t -> (JSON.JSValue, [(String, JSON.JSValue)])@
164
                   , fieldExtraKeys   :: [String]
165
                   , fieldDefault     :: Maybe (Q Exp)
166
                     -- ^ an optional default value of type @t@
167
                   , fieldConstr      :: Maybe String
168
                   , fieldIsOptional  :: OptionalType
169
                     -- ^ determines if a field is optional, and if yes,
170
                     -- how
171
                   , fieldDoc         :: String
172
                   }
173

    
174
-- | Generates a simple field.
175
simpleField :: String -> Q Type -> Field
176
simpleField fname ftype =
177
  Field { fieldName        = fname
178
        , fieldType        = ftype
179
        , fieldRead        = Nothing
180
        , fieldShow        = Nothing
181
        , fieldExtraKeys   = []
182
        , fieldDefault     = Nothing
183
        , fieldConstr      = Nothing
184
        , fieldIsOptional  = NotOptional
185
        , fieldDoc         = ""
186
        }
187

    
188
withDoc :: String -> Field -> Field
189
withDoc doc field =
190
  field { fieldDoc = doc }
191

    
192
-- | Sets the renamed constructor field.
193
renameField :: String -> Field -> Field
194
renameField constrName field = field { fieldConstr = Just constrName }
195

    
196
-- | Sets the default value on a field (makes it optional with a
197
-- default value).
198
defaultField :: Q Exp -> Field -> Field
199
defaultField defval field = field { fieldDefault = Just defval }
200

    
201
-- | Marks a field optional (turning its base type into a Maybe).
202
optionalField :: Field -> Field
203
optionalField field = field { fieldIsOptional = OptionalOmitNull }
204

    
205
-- | Marks a field optional (turning its base type into a Maybe), but
206
-- with 'Nothing' serialised explicitly as /null/.
207
optionalNullSerField :: Field -> Field
208
optionalNullSerField field = field { fieldIsOptional = OptionalSerializeNull }
209

    
210
-- | Wrapper around a special parse function, suitable as field-parsing
211
-- function.
212
numericalReadFn :: JSON.JSON a => (String -> JSON.Result a)
213
                   -> [(String, JSON.JSValue)] -> JSON.JSValue -> JSON.Result a
214
numericalReadFn _ _ v@(JSON.JSRational _ _) = JSON.readJSON v
215
numericalReadFn f _ (JSON.JSString x) = f $ JSON.fromJSString x
216
numericalReadFn _ _ _ = JSON.Error "A numerical field has to be a number or\ 
217
                                   \ a string."
218

    
219
-- | Sets the read function to also accept string parsable by the given
220
-- function.
221
specialNumericalField :: Name -> Field -> Field
222
specialNumericalField f field =
223
     field { fieldRead = Just (appE (varE 'numericalReadFn) (varE f)) }
224

    
225
-- | Creates a new mandatory field that reads time as the (floating point)
226
-- number of seconds since the standard UNIX epoch, and represents it in
227
-- Haskell as 'ClockTime'.
228
timeAsDoubleField :: String -> Field
229
timeAsDoubleField fname =
230
  (simpleField fname [t| ClockTime |])
231
    { fieldRead = Just $ [| \_ -> liftM unTimeAsDoubleJSON . JSON.readJSON |]
232
    , fieldShow = Just $ [| \c -> (JSON.showJSON $ TimeAsDoubleJSON c, []) |]
233
    }
234

    
235
-- | Sets custom functions on a field.
236
customField :: Name      -- ^ The name of the read function
237
            -> Name      -- ^ The name of the show function
238
            -> [String]  -- ^ The name of extra field keys
239
            -> Field     -- ^ The original field
240
            -> Field     -- ^ Updated field
241
customField readfn showfn extra field =
242
  field { fieldRead = Just (varE readfn), fieldShow = Just (varE showfn)
243
        , fieldExtraKeys = extra }
244

    
245
-- | Computes the record name for a given field, based on either the
246
-- string value in the JSON serialisation or the custom named if any
247
-- exists.
248
fieldRecordName :: Field -> String
249
fieldRecordName (Field { fieldName = name, fieldConstr = alias }) =
250
  fromMaybe (camelCase name) alias
251

    
252
-- | Computes the preferred variable name to use for the value of this
253
-- field. If the field has a specific constructor name, then we use a
254
-- first-letter-lowercased version of that; otherwise, we simply use
255
-- the field name. See also 'fieldRecordName'.
256
fieldVariable :: Field -> String
257
fieldVariable f =
258
  case (fieldConstr f) of
259
    Just name -> ensureLower name
260
    _ -> map (\c -> if c == '-' then '_' else c) $ fieldName f
261

    
262
-- | Compute the actual field type (taking into account possible
263
-- optional status).
264
actualFieldType :: Field -> Q Type
265
actualFieldType f | fieldIsOptional f /= NotOptional = [t| Maybe $t |]
266
                  | otherwise = t
267
                  where t = fieldType f
268

    
269
-- | Checks that a given field is not optional (for object types or
270
-- fields which should not allow this case).
271
checkNonOptDef :: (Monad m) => Field -> m ()
272
checkNonOptDef (Field { fieldIsOptional = OptionalOmitNull
273
                      , fieldName = name }) =
274
  fail $ "Optional field " ++ name ++ " used in parameter declaration"
275
checkNonOptDef (Field { fieldIsOptional = OptionalSerializeNull
276
                      , fieldName = name }) =
277
  fail $ "Optional field " ++ name ++ " used in parameter declaration"
278
checkNonOptDef (Field { fieldDefault = (Just _), fieldName = name }) =
279
  fail $ "Default field " ++ name ++ " used in parameter declaration"
280
checkNonOptDef _ = return ()
281

    
282
-- | Construct a function that parses a field value. If the field has
283
-- a custom 'fieldRead', it's applied to @o@ and used. Otherwise
284
-- @JSON.readJSON@ is used.
285
parseFn :: Field   -- ^ The field definition
286
        -> Q Exp   -- ^ The entire object in JSON object format
287
        -> Q Exp   -- ^ The resulting function that parses a JSON message
288
parseFn field o
289
  = maybe [| readJSONWithDesc $(stringE $ fieldName field) False |]
290
          (`appE` o) (fieldRead field)
291

    
292
-- | Produces the expression that will de-serialise a given
293
-- field. Since some custom parsing functions might need to use the
294
-- entire object, we do take and pass the object to any custom read
295
-- functions.
296
loadFn :: Field   -- ^ The field definition
297
       -> Q Exp   -- ^ The value of the field as existing in the JSON message
298
       -> Q Exp   -- ^ The entire object in JSON object format
299
       -> Q Exp   -- ^ Resulting expression
300
loadFn field expr o = [| $expr >>= $(parseFn field o) |]
301

    
302
-- | Just as 'loadFn', but for optional fields.
303
loadFnOpt :: Field   -- ^ The field definition
304
          -> Q Exp   -- ^ The value of the field as existing in the JSON message
305
                     -- as Maybe
306
          -> Q Exp   -- ^ The entire object in JSON object format
307
          -> Q Exp   -- ^ Resulting expression
308
loadFnOpt field@(Field { fieldDefault = Just def }) expr o
309
  = case fieldIsOptional field of
310
      NotOptional -> [| $expr >>= maybe (return $def) $(parseFn field o) |]
311
      _           -> fail $ "Field " ++ fieldName field ++ ":\
312
                            \ A field can't be optional and\
313
                            \ have a default value at the same time."
314
loadFnOpt field expr o
315
  = [| $expr >>= maybe (return Nothing) (liftM Just . $(parseFn field o)) |]
316

    
317
-- * Common field declarations
318

    
319
-- | Timestamp fields description.
320
timeStampFields :: [Field]
321
timeStampFields = map (defaultField [| TOD 0 0 |] . timeAsDoubleField)
322
                      ["ctime", "mtime"]
323

    
324

    
325
-- | Serial number fields description.
326
serialFields :: [Field]
327
serialFields =
328
    [ renameField  "Serial" $ simpleField "serial_no" [t| Int |] ]
329

    
330
-- | UUID fields description.
331
uuidFields :: [Field]
332
uuidFields = [ simpleField "uuid" [t| String |] ]
333

    
334
-- | Tag set type alias.
335
type TagSet = Set.Set String
336

    
337
-- | Tag field description.
338
tagsFields :: [Field]
339
tagsFields = [ defaultField [| Set.empty |] $
340
               simpleField "tags" [t| TagSet |] ]
341

    
342
-- * Internal types
343

    
344
-- | A simple field, in constrast to the customisable 'Field' type.
345
type SimpleField = (String, Q Type)
346

    
347
-- | A definition for a single constructor for a simple object.
348
type SimpleConstructor = (String, [SimpleField])
349

    
350
-- | A definition for ADTs with simple fields.
351
type SimpleObject = [SimpleConstructor]
352

    
353
-- | A type alias for an opcode constructor of a regular object.
354
type OpCodeConstructor = (String, Q Type, String, [Field], String)
355

    
356
-- | A type alias for a Luxi constructor of a regular object.
357
type LuxiConstructor = (String, [Field])
358

    
359
-- * Helper functions
360

    
361
-- | Ensure first letter is lowercase.
362
--
363
-- Used to convert type name to function prefix, e.g. in @data Aa ->
364
-- aaToRaw@.
365
ensureLower :: String -> String
366
ensureLower [] = []
367
ensureLower (x:xs) = toLower x:xs
368

    
369
-- | Ensure first letter is uppercase.
370
--
371
-- Used to convert constructor name to component
372
ensureUpper :: String -> String
373
ensureUpper [] = []
374
ensureUpper (x:xs) = toUpper x:xs
375

    
376
-- | Helper for quoted expressions.
377
varNameE :: String -> Q Exp
378
varNameE = varE . mkName
379

    
380
-- | showJSON as an expression, for reuse.
381
showJSONE :: Q Exp
382
showJSONE = varE 'JSON.showJSON
383

    
384
-- | makeObj as an expression, for reuse.
385
makeObjE :: Q Exp
386
makeObjE = varE 'JSON.makeObj
387

    
388
-- | fromObj (Ganeti specific) as an expression, for reuse.
389
fromObjE :: Q Exp
390
fromObjE = varE 'fromObj
391

    
392
-- | ToRaw function name.
393
toRawName :: String -> Name
394
toRawName = mkName . (++ "ToRaw") . ensureLower
395

    
396
-- | FromRaw function name.
397
fromRawName :: String -> Name
398
fromRawName = mkName . (++ "FromRaw") . ensureLower
399

    
400
-- | Converts a name to it's varE\/litE representations.
401
reprE :: Either String Name -> Q Exp
402
reprE = either stringE varE
403

    
404
-- | Smarter function application.
405
--
406
-- This does simply f x, except that if is 'id', it will skip it, in
407
-- order to generate more readable code when using -ddump-splices.
408
appFn :: Exp -> Exp -> Exp
409
appFn f x | f == VarE 'id = x
410
          | otherwise = AppE f x
411

    
412
-- | Builds a field for a normal constructor.
413
buildConsField :: Q Type -> StrictTypeQ
414
buildConsField ftype = do
415
  ftype' <- ftype
416
  return (NotStrict, ftype')
417

    
418
-- | Builds a constructor based on a simple definition (not field-based).
419
buildSimpleCons :: Name -> SimpleObject -> Q Dec
420
buildSimpleCons tname cons = do
421
  decl_d <- mapM (\(cname, fields) -> do
422
                    fields' <- mapM (buildConsField . snd) fields
423
                    return $ NormalC (mkName cname) fields') cons
424
  return $ DataD [] tname [] decl_d [''Show, ''Eq]
425

    
426
-- | Generate the save function for a given type.
427
genSaveSimpleObj :: Name                            -- ^ Object type
428
                 -> String                          -- ^ Function name
429
                 -> SimpleObject                    -- ^ Object definition
430
                 -> (SimpleConstructor -> Q Clause) -- ^ Constructor save fn
431
                 -> Q (Dec, Dec)
432
genSaveSimpleObj tname sname opdefs fn = do
433
  let sigt = AppT (AppT ArrowT (ConT tname)) (ConT ''JSON.JSValue)
434
      fname = mkName sname
435
  cclauses <- mapM fn opdefs
436
  return $ (SigD fname sigt, FunD fname cclauses)
437

    
438
-- * Template code for simple raw type-equivalent ADTs
439

    
440
-- | Generates a data type declaration.
441
--
442
-- The type will have a fixed list of instances.
443
strADTDecl :: Name -> [String] -> Dec
444
strADTDecl name constructors =
445
  DataD [] name []
446
          (map (flip NormalC [] . mkName) constructors)
447
          [''Show, ''Eq, ''Enum, ''Bounded, ''Ord]
448

    
449
-- | Generates a toRaw function.
450
--
451
-- This generates a simple function of the form:
452
--
453
-- @
454
-- nameToRaw :: Name -> /traw/
455
-- nameToRaw Cons1 = var1
456
-- nameToRaw Cons2 = \"value2\"
457
-- @
458
genToRaw :: Name -> Name -> Name -> [(String, Either String Name)] -> Q [Dec]
459
genToRaw traw fname tname constructors = do
460
  let sigt = AppT (AppT ArrowT (ConT tname)) (ConT traw)
461
  -- the body clauses, matching on the constructor and returning the
462
  -- raw value
463
  clauses <- mapM  (\(c, v) -> clause [recP (mkName c) []]
464
                             (normalB (reprE v)) []) constructors
465
  return [SigD fname sigt, FunD fname clauses]
466

    
467
-- | Generates a fromRaw function.
468
--
469
-- The function generated is monadic and can fail parsing the
470
-- raw value. It is of the form:
471
--
472
-- @
473
-- nameFromRaw :: (Monad m) => /traw/ -> m Name
474
-- nameFromRaw s | s == var1       = Cons1
475
--               | s == \"value2\" = Cons2
476
--               | otherwise = fail /.../
477
-- @
478
genFromRaw :: Name -> Name -> Name -> [(String, Either String Name)] -> Q [Dec]
479
genFromRaw traw fname tname constructors = do
480
  -- signature of form (Monad m) => String -> m $name
481
  sigt <- [t| (Monad m) => $(conT traw) -> m $(conT tname) |]
482
  -- clauses for a guarded pattern
483
  let varp = mkName "s"
484
      varpe = varE varp
485
  clauses <- mapM (\(c, v) -> do
486
                     -- the clause match condition
487
                     g <- normalG [| $varpe == $(reprE v) |]
488
                     -- the clause result
489
                     r <- [| return $(conE (mkName c)) |]
490
                     return (g, r)) constructors
491
  -- the otherwise clause (fallback)
492
  oth_clause <- do
493
    g <- normalG [| otherwise |]
494
    r <- [|fail ("Invalid string value for type " ++
495
                 $(litE (stringL (nameBase tname))) ++ ": " ++ show $varpe) |]
496
    return (g, r)
497
  let fun = FunD fname [Clause [VarP varp]
498
                        (GuardedB (clauses++[oth_clause])) []]
499
  return [SigD fname sigt, fun]
500

    
501
-- | Generates a data type from a given raw format.
502
--
503
-- The format is expected to multiline. The first line contains the
504
-- type name, and the rest of the lines must contain two words: the
505
-- constructor name and then the string representation of the
506
-- respective constructor.
507
--
508
-- The function will generate the data type declaration, and then two
509
-- functions:
510
--
511
-- * /name/ToRaw, which converts the type to a raw type
512
--
513
-- * /name/FromRaw, which (monadically) converts from a raw type to the type
514
--
515
-- Note that this is basically just a custom show\/read instance,
516
-- nothing else.
517
declareADT
518
  :: (a -> Either String Name) -> Name -> String -> [(String, a)] -> Q [Dec]
519
declareADT fn traw sname cons = do
520
  let name = mkName sname
521
      ddecl = strADTDecl name (map fst cons)
522
      -- process cons in the format expected by genToRaw
523
      cons' = map (\(a, b) -> (a, fn b)) cons
524
  toraw <- genToRaw traw (toRawName sname) name cons'
525
  fromraw <- genFromRaw traw (fromRawName sname) name cons'
526
  return $ ddecl:toraw ++ fromraw
527

    
528
declareLADT :: Name -> String -> [(String, String)] -> Q [Dec]
529
declareLADT = declareADT Left
530

    
531
declareILADT :: String -> [(String, Int)] -> Q [Dec]
532
declareILADT sname cons = do
533
  consNames <- sequence [ newName ('_':n) | (n, _) <- cons ]
534
  consFns <- concat <$> sequence
535
             [ do sig <- sigD n [t| Int |]
536
                  let expr = litE (IntegerL (toInteger i))
537
                  fn <- funD n [clause [] (normalB expr) []]
538
                  return [sig, fn]
539
             | n <- consNames
540
             | (_, i) <- cons ]
541
  let cons' = [ (n, n') | (n, _) <- cons | n' <- consNames ]
542
  (consFns ++) <$> declareADT Right ''Int sname cons'
543

    
544
declareIADT :: String -> [(String, Name)] -> Q [Dec]
545
declareIADT = declareADT Right ''Int
546

    
547
declareSADT :: String -> [(String, Name)] -> Q [Dec]
548
declareSADT = declareADT Right ''String
549

    
550
-- | Creates the showJSON member of a JSON instance declaration.
551
--
552
-- This will create what is the equivalent of:
553
--
554
-- @
555
-- showJSON = showJSON . /name/ToRaw
556
-- @
557
--
558
-- in an instance JSON /name/ declaration
559
genShowJSON :: String -> Q Dec
560
genShowJSON name = do
561
  body <- [| JSON.showJSON . $(varE (toRawName name)) |]
562
  return $ FunD 'JSON.showJSON [Clause [] (NormalB body) []]
563

    
564
-- | Creates the readJSON member of a JSON instance declaration.
565
--
566
-- This will create what is the equivalent of:
567
--
568
-- @
569
-- readJSON s = case readJSON s of
570
--                Ok s' -> /name/FromRaw s'
571
--                Error e -> Error /description/
572
-- @
573
--
574
-- in an instance JSON /name/ declaration
575
genReadJSON :: String -> Q Dec
576
genReadJSON name = do
577
  let s = mkName "s"
578
  body <- [| $(varE (fromRawName name)) =<<
579
             readJSONWithDesc $(stringE name) True $(varE s) |]
580
  return $ FunD 'JSON.readJSON [Clause [VarP s] (NormalB body) []]
581

    
582
-- | Generates a JSON instance for a given type.
583
--
584
-- This assumes that the /name/ToRaw and /name/FromRaw functions
585
-- have been defined as by the 'declareSADT' function.
586
makeJSONInstance :: Name -> Q [Dec]
587
makeJSONInstance name = do
588
  let base = nameBase name
589
  showJ <- genShowJSON base
590
  readJ <- genReadJSON base
591
  return [InstanceD [] (AppT (ConT ''JSON.JSON) (ConT name)) [readJ,showJ]]
592

    
593
-- * Template code for opcodes
594

    
595
-- | Transforms a CamelCase string into an_underscore_based_one.
596
deCamelCase :: String -> String
597
deCamelCase =
598
    intercalate "_" . map (map toUpper) . groupBy (\_ b -> not $ isUpper b)
599

    
600
-- | Transform an underscore_name into a CamelCase one.
601
camelCase :: String -> String
602
camelCase = concatMap (ensureUpper . drop 1) .
603
            groupBy (\_ b -> b /= '_' && b /= '-') . ('_':)
604

    
605
-- | Computes the name of a given constructor.
606
constructorName :: Con -> Q Name
607
constructorName (NormalC name _) = return name
608
constructorName (RecC name _)    = return name
609
constructorName x                = fail $ "Unhandled constructor " ++ show x
610

    
611
-- | Extract all constructor names from a given type.
612
reifyConsNames :: Name -> Q [String]
613
reifyConsNames name = do
614
  reify_result <- reify name
615
  case reify_result of
616
    TyConI (DataD _ _ _ cons _) -> mapM (liftM nameBase . constructorName) cons
617
    o -> fail $ "Unhandled name passed to reifyConsNames, expected\
618
                \ type constructor but got '" ++ show o ++ "'"
619

    
620
-- | Builds the generic constructor-to-string function.
621
--
622
-- This generates a simple function of the following form:
623
--
624
-- @
625
-- fname (ConStructorOne {}) = trans_fun("ConStructorOne")
626
-- fname (ConStructorTwo {}) = trans_fun("ConStructorTwo")
627
-- @
628
--
629
-- This builds a custom list of name\/string pairs and then uses
630
-- 'genToRaw' to actually generate the function.
631
genConstrToStr :: (String -> String) -> Name -> String -> Q [Dec]
632
genConstrToStr trans_fun name fname = do
633
  cnames <- reifyConsNames name
634
  let svalues = map (Left . trans_fun) cnames
635
  genToRaw ''String (mkName fname) name $ zip cnames svalues
636

    
637
-- | Constructor-to-string for OpCode.
638
genOpID :: Name -> String -> Q [Dec]
639
genOpID = genConstrToStr deCamelCase
640

    
641
-- | Builds a list with all defined constructor names for a type.
642
--
643
-- @
644
-- vstr :: String
645
-- vstr = [...]
646
-- @
647
--
648
-- Where the actual values of the string are the constructor names
649
-- mapped via @trans_fun@.
650
genAllConstr :: (String -> String) -> Name -> String -> Q [Dec]
651
genAllConstr trans_fun name vstr = do
652
  cnames <- reifyConsNames name
653
  let svalues = sort $ map trans_fun cnames
654
      vname = mkName vstr
655
      sig = SigD vname (AppT ListT (ConT ''String))
656
      body = NormalB (ListE (map (LitE . StringL) svalues))
657
  return $ [sig, ValD (VarP vname) body []]
658

    
659
-- | Generates a list of all defined opcode IDs.
660
genAllOpIDs :: Name -> String -> Q [Dec]
661
genAllOpIDs = genAllConstr deCamelCase
662

    
663
-- | OpCode parameter (field) type.
664
type OpParam = (String, Q Type, Q Exp)
665

    
666
-- * Python code generation
667

    
668
data OpCodeField = OpCodeField { ocfName :: String
669
                               , ocfType :: PyType
670
                               , ocfDefl :: Maybe PyValueEx
671
                               , ocfDoc  :: String
672
                               }
673

    
674
-- | Transfers opcode data between the opcode description (through
675
-- @genOpCode@) and the Python code generation functions.
676
data OpCodeDescriptor = OpCodeDescriptor { ocdName   :: String
677
                                         , ocdType   :: PyType
678
                                         , ocdDoc    :: String
679
                                         , ocdFields :: [OpCodeField]
680
                                         , ocdDescr  :: String
681
                                         }
682

    
683
-- | Optionally encapsulates default values in @PyValueEx@.
684
--
685
-- @maybeApp exp typ@ returns a quoted expression that encapsulates
686
-- the default value @exp@ of an opcode parameter cast to @typ@ in a
687
-- @PyValueEx@, if @exp@ is @Just@.  Otherwise, it returns a quoted
688
-- expression with @Nothing@.
689
maybeApp :: Maybe (Q Exp) -> Q Type -> Q Exp
690
maybeApp Nothing _ =
691
  [| Nothing |]
692

    
693
maybeApp (Just expr) typ =
694
  [| Just ($(conE (mkName "PyValueEx")) ($expr :: $typ)) |]
695

    
696
-- | Generates a Python type according to whether the field is
697
-- optional.
698
--
699
-- The type of created expression is PyType.
700
genPyType' :: OptionalType -> Q Type -> Q PyType
701
genPyType' opt typ = typ >>= pyOptionalType (opt /= NotOptional)
702

    
703
-- | Generates Python types from opcode parameters.
704
genPyType :: Field -> Q PyType
705
genPyType f = genPyType' (fieldIsOptional f) (fieldType f)
706

    
707
-- | Generates Python default values from opcode parameters.
708
genPyDefault :: Field -> Q Exp
709
genPyDefault f = maybeApp (fieldDefault f) (fieldType f)
710

    
711
pyField :: Field -> Q Exp
712
pyField f = genPyType f >>= \t ->
713
            [| OpCodeField $(stringE (fieldName f))
714
                           t
715
                           $(genPyDefault f)
716
                           $(stringE (fieldDoc f)) |]
717

    
718
-- | Generates a Haskell function call to "showPyClass" with the
719
-- necessary information on how to build the Python class string.
720
pyClass :: OpCodeConstructor -> Q Exp
721
pyClass (consName, consType, consDoc, consFields, consDscField) =
722
  do let pyClassVar = varNameE "showPyClass"
723
         consName' = stringE consName
724
     consType' <- genPyType' NotOptional consType
725
     let consDoc' = stringE consDoc
726
     [| OpCodeDescriptor $consName'
727
                         consType'
728
                         $consDoc'
729
                         $(listE $ map pyField consFields)
730
                         consDscField |]
731

    
732
-- | Generates a function called "pyClasses" that holds the list of
733
-- all the opcode descriptors necessary for generating the Python
734
-- opcodes.
735
pyClasses :: [OpCodeConstructor] -> Q [Dec]
736
pyClasses cons =
737
  do let name = mkName "pyClasses"
738
         sig = SigD name (AppT ListT (ConT ''OpCodeDescriptor))
739
     fn <- FunD name <$> (:[]) <$> declClause cons
740
     return [sig, fn]
741
  where declClause c =
742
          clause [] (normalB (ListE <$> mapM pyClass c)) []
743

    
744
-- | Converts from an opcode constructor to a Luxi constructor.
745
opcodeConsToLuxiCons :: (a, b, c, d, e) -> (a, d)
746
opcodeConsToLuxiCons (x, _, _, y, _) = (x, y)
747

    
748
-- | Generates the OpCode data type.
749
--
750
-- This takes an opcode logical definition, and builds both the
751
-- datatype and the JSON serialisation out of it. We can't use a
752
-- generic serialisation since we need to be compatible with Ganeti's
753
-- own, so we have a few quirks to work around.
754
genOpCode :: String              -- ^ Type name to use
755
          -> [OpCodeConstructor] -- ^ Constructor name and parameters
756
          -> Q [Dec]
757
genOpCode name cons = do
758
  let tname = mkName name
759
  decl_d <- mapM (\(cname, _, _, fields, _) -> do
760
                    -- we only need the type of the field, without Q
761
                    fields' <- mapM (fieldTypeInfo "op") fields
762
                    return $ RecC (mkName cname) fields')
763
            cons
764
  let declD = DataD [] tname [] decl_d [''Show, ''Eq]
765
  let (allfsig, allffn) = genAllOpFields "allOpFields" cons
766
  save_decs <- genSaveOpCode tname "saveOpCode" "toDictOpCode"
767
               (map opcodeConsToLuxiCons cons) saveConstructor True
768
  (loadsig, loadfn) <- genLoadOpCode cons
769
  pyDecls <- pyClasses cons
770
  return $ [declD, allfsig, allffn, loadsig, loadfn] ++ save_decs ++ pyDecls
771

    
772
-- | Generates the function pattern returning the list of fields for a
773
-- given constructor.
774
genOpConsFields :: OpCodeConstructor -> Clause
775
genOpConsFields (cname, _, _, fields, _) =
776
  let op_id = deCamelCase cname
777
      fvals = map (LitE . StringL) . sort . nub $
778
              concatMap (\f -> fieldName f:fieldExtraKeys f) fields
779
  in Clause [LitP (StringL op_id)] (NormalB $ ListE fvals) []
780

    
781
-- | Generates a list of all fields of an opcode constructor.
782
genAllOpFields  :: String              -- ^ Function name
783
                -> [OpCodeConstructor] -- ^ Object definition
784
                -> (Dec, Dec)
785
genAllOpFields sname opdefs =
786
  let cclauses = map genOpConsFields opdefs
787
      other = Clause [WildP] (NormalB (ListE [])) []
788
      fname = mkName sname
789
      sigt = AppT  (AppT ArrowT (ConT ''String)) (AppT ListT (ConT ''String))
790
  in (SigD fname sigt, FunD fname (cclauses++[other]))
791

    
792
-- | Generates the \"save\" clause for an entire opcode constructor.
793
--
794
-- This matches the opcode with variables named the same as the
795
-- constructor fields (just so that the spliced in code looks nicer),
796
-- and passes those name plus the parameter definition to 'saveObjectField'.
797
saveConstructor :: LuxiConstructor -- ^ The constructor
798
                -> Q Clause        -- ^ Resulting clause
799
saveConstructor (sname, fields) = do
800
  let cname = mkName sname
801
  fnames <- mapM (newName . fieldVariable) fields
802
  let pat = conP cname (map varP fnames)
803
  let felems = map (uncurry saveObjectField) (zip fnames fields)
804
      -- now build the OP_ID serialisation
805
      opid = [| [( $(stringE "OP_ID"),
806
                   JSON.showJSON $(stringE . deCamelCase $ sname) )] |]
807
      flist = listE (opid:felems)
808
      -- and finally convert all this to a json object
809
      flist' = [| concat $flist |]
810
  clause [pat] (normalB flist') []
811

    
812
-- | Generates the main save opcode function.
813
--
814
-- This builds a per-constructor match clause that contains the
815
-- respective constructor-serialisation code.
816
genSaveOpCode :: Name                          -- ^ Object ype
817
              -> String                        -- ^ To 'JSValue' function name
818
              -> String                        -- ^ To 'JSObject' function name
819
              -> [LuxiConstructor]             -- ^ Object definition
820
              -> (LuxiConstructor -> Q Clause) -- ^ Constructor save fn
821
              -> Bool                          -- ^ Whether to generate
822
                                               -- obj or just a
823
                                               -- list\/tuple of values
824
              -> Q [Dec]
825
genSaveOpCode tname jvalstr tdstr opdefs fn gen_object = do
826
  tdclauses <- mapM fn opdefs
827
  let typecon = ConT tname
828
      jvalname = mkName jvalstr
829
      jvalsig = AppT  (AppT ArrowT typecon) (ConT ''JSON.JSValue)
830
      tdname = mkName tdstr
831
  tdsig <- [t| $(return typecon) -> [(String, JSON.JSValue)] |]
832
  jvalclause <- if gen_object
833
                  then [| $makeObjE . $(varE tdname) |]
834
                  else [| JSON.showJSON . map snd . $(varE tdname) |]
835
  return [ SigD tdname tdsig
836
         , FunD tdname tdclauses
837
         , SigD jvalname jvalsig
838
         , ValD (VarP jvalname) (NormalB jvalclause) []]
839

    
840
-- | Generates load code for a single constructor of the opcode data type.
841
loadConstructor :: OpCodeConstructor -> Q Exp
842
loadConstructor (sname, _, _, fields, _) = do
843
  let name = mkName sname
844
  fbinds <- mapM loadObjectField fields
845
  let (fnames, fstmts) = unzip fbinds
846
  let cval = foldl (\accu fn -> AppE accu (VarE fn)) (ConE name) fnames
847
      fstmts' = fstmts ++ [NoBindS (AppE (VarE 'return) cval)]
848
  return $ DoE fstmts'
849

    
850
-- | Generates the loadOpCode function.
851
genLoadOpCode :: [OpCodeConstructor] -> Q (Dec, Dec)
852
genLoadOpCode opdefs = do
853
  let fname = mkName "loadOpCode"
854
      arg1 = mkName "v"
855
      objname = mkName "o"
856
      opid = mkName "op_id"
857
  st1 <- bindS (varP objname) [| liftM JSON.fromJSObject
858
                                 (JSON.readJSON $(varE arg1)) |]
859
  st2 <- bindS (varP opid) [| $fromObjE $(varE objname) $(stringE "OP_ID") |]
860
  -- the match results (per-constructor blocks)
861
  mexps <- mapM loadConstructor opdefs
862
  fails <- [| fail $ "Unknown opcode " ++ $(varE opid) |]
863
  let mpats = map (\(me, (consName, _, _, _, _)) ->
864
                       let mp = LitP . StringL . deCamelCase $ consName
865
                       in Match mp (NormalB me) []
866
                  ) $ zip mexps opdefs
867
      defmatch = Match WildP (NormalB fails) []
868
      cst = NoBindS $ CaseE (VarE opid) $ mpats++[defmatch]
869
      body = DoE [st1, st2, cst]
870
  sigt <- [t| JSON.JSValue -> JSON.Result $(conT (mkName "OpCode")) |]
871
  return $ (SigD fname sigt, FunD fname [Clause [VarP arg1] (NormalB body) []])
872

    
873
-- * Template code for luxi
874

    
875
-- | Constructor-to-string for LuxiOp.
876
genStrOfOp :: Name -> String -> Q [Dec]
877
genStrOfOp = genConstrToStr id
878

    
879
-- | Constructor-to-string for MsgKeys.
880
genStrOfKey :: Name -> String -> Q [Dec]
881
genStrOfKey = genConstrToStr ensureLower
882

    
883
-- | Generates the LuxiOp data type.
884
--
885
-- This takes a Luxi operation definition and builds both the
886
-- datatype and the function transforming the arguments to JSON.
887
-- We can't use anything less generic, because the way different
888
-- operations are serialized differs on both parameter- and top-level.
889
--
890
-- There are two things to be defined for each parameter:
891
--
892
-- * name
893
--
894
-- * type
895
--
896
genLuxiOp :: String -> [LuxiConstructor] -> Q [Dec]
897
genLuxiOp name cons = do
898
  let tname = mkName name
899
  decl_d <- mapM (\(cname, fields) -> do
900
                    -- we only need the type of the field, without Q
901
                    fields' <- mapM actualFieldType fields
902
                    let fields'' = zip (repeat NotStrict) fields'
903
                    return $ NormalC (mkName cname) fields'')
904
            cons
905
  let declD = DataD [] (mkName name) [] decl_d [''Show, ''Eq]
906
  save_decs <- genSaveOpCode tname "opToArgs" "opToDict"
907
               cons saveLuxiConstructor False
908
  req_defs <- declareSADT "LuxiReq" .
909
              map (\(str, _) -> ("Req" ++ str, mkName ("luxiReq" ++ str))) $
910
                  cons
911
  return $ declD:save_decs ++ req_defs
912

    
913
-- | Generates the \"save\" clause for entire LuxiOp constructor.
914
saveLuxiConstructor :: LuxiConstructor -> Q Clause
915
saveLuxiConstructor (sname, fields) = do
916
  let cname = mkName sname
917
  fnames <- mapM (newName . fieldVariable) fields
918
  let pat = conP cname (map varP fnames)
919
  let felems = map (uncurry saveObjectField) (zip fnames fields)
920
      flist = [| concat $(listE felems) |]
921
  clause [pat] (normalB flist) []
922

    
923
-- * "Objects" functionality
924

    
925
-- | Extract the field's declaration from a Field structure.
926
fieldTypeInfo :: String -> Field -> Q (Name, Strict, Type)
927
fieldTypeInfo field_pfx fd = do
928
  t <- actualFieldType fd
929
  let n = mkName . (field_pfx ++) . fieldRecordName $ fd
930
  return (n, NotStrict, t)
931

    
932
-- | Build an object declaration.
933
buildObject :: String -> String -> [Field] -> Q [Dec]
934
buildObject sname field_pfx fields = do
935
  let name = mkName sname
936
  fields_d <- mapM (fieldTypeInfo field_pfx) fields
937
  let decl_d = RecC name fields_d
938
  let declD = DataD [] name [] [decl_d] [''Show, ''Eq]
939
  ser_decls <- buildObjectSerialisation sname fields
940
  return $ declD:ser_decls
941

    
942
-- | Generates an object definition: data type and its JSON instance.
943
buildObjectSerialisation :: String -> [Field] -> Q [Dec]
944
buildObjectSerialisation sname fields = do
945
  let name = mkName sname
946
  savedecls <- genSaveObject saveObjectField sname fields
947
  (loadsig, loadfn) <- genLoadObject loadObjectField sname fields
948
  shjson <- objectShowJSON sname
949
  rdjson <- objectReadJSON sname
950
  let instdecl = InstanceD [] (AppT (ConT ''JSON.JSON) (ConT name))
951
                 [rdjson, shjson]
952
  return $ savedecls ++ [loadsig, loadfn, instdecl]
953

    
954
-- | The toDict function name for a given type.
955
toDictName :: String -> Name
956
toDictName sname = mkName ("toDict" ++ sname)
957

    
958
-- | Generates the save object functionality.
959
genSaveObject :: (Name -> Field -> Q Exp)
960
              -> String -> [Field] -> Q [Dec]
961
genSaveObject save_fn sname fields = do
962
  let name = mkName sname
963
  fnames <- mapM (newName . fieldVariable) fields
964
  let pat = conP name (map varP fnames)
965
  let tdname = toDictName sname
966
  tdsigt <- [t| $(conT name) -> [(String, JSON.JSValue)] |]
967

    
968
  let felems = map (uncurry save_fn) (zip fnames fields)
969
      flist = listE felems
970
      -- and finally convert all this to a json object
971
      tdlist = [| concat $flist |]
972
      iname = mkName "i"
973
  tclause <- clause [pat] (normalB tdlist) []
974
  cclause <- [| $makeObjE . $(varE tdname) |]
975
  let fname = mkName ("save" ++ sname)
976
  sigt <- [t| $(conT name) -> JSON.JSValue |]
977
  return [SigD tdname tdsigt, FunD tdname [tclause],
978
          SigD fname sigt, ValD (VarP fname) (NormalB cclause) []]
979

    
980
-- | Generates the code for saving an object's field, handling the
981
-- various types of fields that we have.
982
saveObjectField :: Name -> Field -> Q Exp
983
saveObjectField fvar field =
984
  let formatFn = fromMaybe [| JSON.showJSON &&& (const []) |] $
985
                           fieldShow field
986
      formatCode v = [| let (actual, extra) = $formatFn $(v)
987
                         in ($nameE, actual) : extra |]
988
  in case fieldIsOptional field of
989
    OptionalOmitNull ->       [| case $(fvarE) of
990
                                   Nothing -> []
991
                                   Just v  -> $(formatCode [| v |])
992
                              |]
993
    OptionalSerializeNull ->  [| case $(fvarE) of
994
                                   Nothing -> [( $nameE, JSON.JSNull )]
995
                                   Just v  -> $(formatCode [| v |])
996
                              |]
997
    NotOptional ->            formatCode fvarE
998
  where nameE = stringE (fieldName field)
999
        fvarE = varE fvar
1000

    
1001
-- | Generates the showJSON clause for a given object name.
1002
objectShowJSON :: String -> Q Dec
1003
objectShowJSON name = do
1004
  body <- [| JSON.showJSON . $(varE . mkName $ "save" ++ name) |]
1005
  return $ FunD 'JSON.showJSON [Clause [] (NormalB body) []]
1006

    
1007
-- | Generates the load object functionality.
1008
genLoadObject :: (Field -> Q (Name, Stmt))
1009
              -> String -> [Field] -> Q (Dec, Dec)
1010
genLoadObject load_fn sname fields = do
1011
  let name = mkName sname
1012
      funname = mkName $ "load" ++ sname
1013
      arg1 = mkName $ if null fields then "_" else "v"
1014
      objname = mkName "o"
1015
      opid = mkName "op_id"
1016
  st1 <- bindS (varP objname) [| liftM JSON.fromJSObject
1017
                                 (JSON.readJSON $(varE arg1)) |]
1018
  fbinds <- mapM load_fn fields
1019
  let (fnames, fstmts) = unzip fbinds
1020
  let cval = foldl (\accu fn -> AppE accu (VarE fn)) (ConE name) fnames
1021
      retstmt = [NoBindS (AppE (VarE 'return) cval)]
1022
      -- FIXME: should we require an empty dict for an empty type?
1023
      -- this allows any JSValue right now
1024
      fstmts' = if null fields
1025
                  then retstmt
1026
                  else st1:fstmts ++ retstmt
1027
  sigt <- [t| JSON.JSValue -> JSON.Result $(conT name) |]
1028
  return $ (SigD funname sigt,
1029
            FunD funname [Clause [VarP arg1] (NormalB (DoE fstmts')) []])
1030

    
1031
-- | Generates code for loading an object's field.
1032
loadObjectField :: Field -> Q (Name, Stmt)
1033
loadObjectField field = do
1034
  let name = fieldVariable field
1035
  fvar <- newName name
1036
  -- these are used in all patterns below
1037
  let objvar = varNameE "o"
1038
      objfield = stringE (fieldName field)
1039
  bexp <- case fieldDefault field of
1040
            -- Only non-optional fields without defaults must have a value;
1041
            -- we treat both optional types the same, since
1042
            -- 'maybeFromObj' can deal with both missing and null values
1043
            -- appropriately (the same)
1044
            Nothing | fieldIsOptional field == NotOptional ->
1045
                 loadFn field [| fromObj $objvar $objfield |] objvar
1046
            _ -> loadFnOpt field [| maybeFromObj $objvar $objfield |] objvar
1047

    
1048
  return (fvar, BindS (VarP fvar) bexp)
1049

    
1050
-- | Builds the readJSON instance for a given object name.
1051
objectReadJSON :: String -> Q Dec
1052
objectReadJSON name = do
1053
  let s = mkName "s"
1054
  body <- [| $(varE . mkName $ "load" ++ name) =<<
1055
             readJSONWithDesc $(stringE name) False $(varE s) |]
1056
  return $ FunD 'JSON.readJSON [Clause [VarP s] (NormalB body) []]
1057

    
1058
-- * Inheritable parameter tables implementation
1059

    
1060
-- | Compute parameter type names.
1061
paramTypeNames :: String -> (String, String)
1062
paramTypeNames root = ("Filled"  ++ root ++ "Params",
1063
                       "Partial" ++ root ++ "Params")
1064

    
1065
-- | Compute information about the type of a parameter field.
1066
paramFieldTypeInfo :: String -> Field -> Q (Name, Strict, Type)
1067
paramFieldTypeInfo field_pfx fd = do
1068
  t <- actualFieldType fd
1069
  let n = mkName . (++ "P") . (field_pfx ++) .
1070
          fieldRecordName $ fd
1071
  return (n, NotStrict, AppT (ConT ''Maybe) t)
1072

    
1073
-- | Build a parameter declaration.
1074
--
1075
-- This function builds two different data structures: a /filled/ one,
1076
-- in which all fields are required, and a /partial/ one, in which all
1077
-- fields are optional. Due to the current record syntax issues, the
1078
-- fields need to be named differrently for the two structures, so the
1079
-- partial ones get a /P/ suffix.
1080
buildParam :: String -> String -> [Field] -> Q [Dec]
1081
buildParam sname field_pfx fields = do
1082
  let (sname_f, sname_p) = paramTypeNames sname
1083
      name_f = mkName sname_f
1084
      name_p = mkName sname_p
1085
  fields_f <- mapM (fieldTypeInfo field_pfx) fields
1086
  fields_p <- mapM (paramFieldTypeInfo field_pfx) fields
1087
  let decl_f = RecC name_f fields_f
1088
      decl_p = RecC name_p fields_p
1089
  let declF = DataD [] name_f [] [decl_f] [''Show, ''Eq]
1090
      declP = DataD [] name_p [] [decl_p] [''Show, ''Eq]
1091
  ser_decls_f <- buildObjectSerialisation sname_f fields
1092
  ser_decls_p <- buildPParamSerialisation sname_p fields
1093
  fill_decls <- fillParam sname field_pfx fields
1094
  return $ [declF, declP] ++ ser_decls_f ++ ser_decls_p ++ fill_decls ++
1095
           buildParamAllFields sname fields ++
1096
           buildDictObjectInst name_f sname_f
1097

    
1098
-- | Builds a list of all fields of a parameter.
1099
buildParamAllFields :: String -> [Field] -> [Dec]
1100
buildParamAllFields sname fields =
1101
  let vname = mkName ("all" ++ sname ++ "ParamFields")
1102
      sig = SigD vname (AppT ListT (ConT ''String))
1103
      val = ListE $ map (LitE . StringL . fieldName) fields
1104
  in [sig, ValD (VarP vname) (NormalB val) []]
1105

    
1106
-- | Builds the 'DictObject' instance for a filled parameter.
1107
buildDictObjectInst :: Name -> String -> [Dec]
1108
buildDictObjectInst name sname =
1109
  [InstanceD [] (AppT (ConT ''DictObject) (ConT name))
1110
   [ValD (VarP 'toDict) (NormalB (VarE (toDictName sname))) []]]
1111

    
1112
-- | Generates the serialisation for a partial parameter.
1113
buildPParamSerialisation :: String -> [Field] -> Q [Dec]
1114
buildPParamSerialisation sname fields = do
1115
  let name = mkName sname
1116
  savedecls <- genSaveObject savePParamField sname fields
1117
  (loadsig, loadfn) <- genLoadObject loadPParamField sname fields
1118
  shjson <- objectShowJSON sname
1119
  rdjson <- objectReadJSON sname
1120
  let instdecl = InstanceD [] (AppT (ConT ''JSON.JSON) (ConT name))
1121
                 [rdjson, shjson]
1122
  return $ savedecls ++ [loadsig, loadfn, instdecl]
1123

    
1124
-- | Generates code to save an optional parameter field.
1125
savePParamField :: Name -> Field -> Q Exp
1126
savePParamField fvar field = do
1127
  checkNonOptDef field
1128
  let actualVal = mkName "v"
1129
  normalexpr <- saveObjectField actualVal field
1130
  -- we have to construct the block here manually, because we can't
1131
  -- splice-in-splice
1132
  return $ CaseE (VarE fvar) [ Match (ConP 'Nothing [])
1133
                                       (NormalB (ConE '[])) []
1134
                             , Match (ConP 'Just [VarP actualVal])
1135
                                       (NormalB normalexpr) []
1136
                             ]
1137

    
1138
-- | Generates code to load an optional parameter field.
1139
loadPParamField :: Field -> Q (Name, Stmt)
1140
loadPParamField field = do
1141
  checkNonOptDef field
1142
  let name = fieldName field
1143
  fvar <- newName name
1144
  -- these are used in all patterns below
1145
  let objvar = varNameE "o"
1146
      objfield = stringE name
1147
      loadexp = [| $(varE 'maybeFromObj) $objvar $objfield |]
1148
  bexp <- loadFnOpt field loadexp objvar
1149
  return (fvar, BindS (VarP fvar) bexp)
1150

    
1151
-- | Builds a simple declaration of type @n_x = fromMaybe f_x p_x@.
1152
buildFromMaybe :: String -> Q Dec
1153
buildFromMaybe fname =
1154
  valD (varP (mkName $ "n_" ++ fname))
1155
         (normalB [| $(varE 'fromMaybe)
1156
                        $(varNameE $ "f_" ++ fname)
1157
                        $(varNameE $ "p_" ++ fname) |]) []
1158

    
1159
-- | Builds a function that executes the filling of partial parameter
1160
-- from a full copy (similar to Python's fillDict).
1161
fillParam :: String -> String -> [Field] -> Q [Dec]
1162
fillParam sname field_pfx fields = do
1163
  let fnames = map (\fd -> field_pfx ++ fieldRecordName fd) fields
1164
      (sname_f, sname_p) = paramTypeNames sname
1165
      oname_f = "fobj"
1166
      oname_p = "pobj"
1167
      name_f = mkName sname_f
1168
      name_p = mkName sname_p
1169
      fun_name = mkName $ "fill" ++ sname ++ "Params"
1170
      le_full = ValD (ConP name_f (map (VarP . mkName . ("f_" ++)) fnames))
1171
                (NormalB . VarE . mkName $ oname_f) []
1172
      le_part = ValD (ConP name_p (map (VarP . mkName . ("p_" ++)) fnames))
1173
                (NormalB . VarE . mkName $ oname_p) []
1174
      obj_new = foldl (\accu vname -> AppE accu (VarE vname)) (ConE name_f)
1175
                $ map (mkName . ("n_" ++)) fnames
1176
  le_new <- mapM buildFromMaybe fnames
1177
  funt <- [t| $(conT name_f) -> $(conT name_p) -> $(conT name_f) |]
1178
  let sig = SigD fun_name funt
1179
      fclause = Clause [VarP (mkName oname_f), VarP (mkName oname_p)]
1180
                (NormalB $ LetE (le_full:le_part:le_new) obj_new) []
1181
      fun = FunD fun_name [fclause]
1182
  return [sig, fun]
1183

    
1184
-- * Template code for exceptions
1185

    
1186
-- | Exception simple error message field.
1187
excErrMsg :: (String, Q Type)
1188
excErrMsg = ("errMsg", [t| String |])
1189

    
1190
-- | Builds an exception type definition.
1191
genException :: String                  -- ^ Name of new type
1192
             -> SimpleObject -- ^ Constructor name and parameters
1193
             -> Q [Dec]
1194
genException name cons = do
1195
  let tname = mkName name
1196
  declD <- buildSimpleCons tname cons
1197
  (savesig, savefn) <- genSaveSimpleObj tname ("save" ++ name) cons $
1198
                         uncurry saveExcCons
1199
  (loadsig, loadfn) <- genLoadExc tname ("load" ++ name) cons
1200
  return [declD, loadsig, loadfn, savesig, savefn]
1201

    
1202
-- | Generates the \"save\" clause for an entire exception constructor.
1203
--
1204
-- This matches the exception with variables named the same as the
1205
-- constructor fields (just so that the spliced in code looks nicer),
1206
-- and calls showJSON on it.
1207
saveExcCons :: String        -- ^ The constructor name
1208
            -> [SimpleField] -- ^ The parameter definitions for this
1209
                             -- constructor
1210
            -> Q Clause      -- ^ Resulting clause
1211
saveExcCons sname fields = do
1212
  let cname = mkName sname
1213
  fnames <- mapM (newName . fst) fields
1214
  let pat = conP cname (map varP fnames)
1215
      felems = if null fnames
1216
                 then conE '() -- otherwise, empty list has no type
1217
                 else listE $ map (\f -> [| JSON.showJSON $(varE f) |]) fnames
1218
  let tup = tupE [ litE (stringL sname), felems ]
1219
  clause [pat] (normalB [| JSON.showJSON $tup |]) []
1220

    
1221
-- | Generates load code for a single constructor of an exception.
1222
--
1223
-- Generates the code (if there's only one argument, we will use a
1224
-- list, not a tuple:
1225
--
1226
-- @
1227
-- do
1228
--  (x1, x2, ...) <- readJSON args
1229
--  return $ Cons x1 x2 ...
1230
-- @
1231
loadExcConstructor :: Name -> String -> [SimpleField] -> Q Exp
1232
loadExcConstructor inname sname fields = do
1233
  let name = mkName sname
1234
  f_names <- mapM (newName . fst) fields
1235
  let read_args = AppE (VarE 'JSON.readJSON) (VarE inname)
1236
  let binds = case f_names of
1237
                [x] -> BindS (ListP [VarP x])
1238
                _   -> BindS (TupP (map VarP f_names))
1239
      cval = foldl (\accu fn -> AppE accu (VarE fn)) (ConE name) f_names
1240
  return $ DoE [binds read_args, NoBindS (AppE (VarE 'return) cval)]
1241

    
1242
{-| Generates the loadException function.
1243

    
1244
This generates a quite complicated function, along the lines of:
1245

    
1246
@
1247
loadFn (JSArray [JSString name, args]) = case name of
1248
   "A1" -> do
1249
     (x1, x2, ...) <- readJSON args
1250
     return $ A1 x1 x2 ...
1251
   "a2" -> ...
1252
   s -> fail $ "Unknown exception" ++ s
1253
loadFn v = fail $ "Expected array but got " ++ show v
1254
@
1255
-}
1256
genLoadExc :: Name -> String -> SimpleObject -> Q (Dec, Dec)
1257
genLoadExc tname sname opdefs = do
1258
  let fname = mkName sname
1259
  exc_name <- newName "name"
1260
  exc_args <- newName "args"
1261
  exc_else <- newName "s"
1262
  arg_else <- newName "v"
1263
  fails <- [| fail $ "Unknown exception '" ++ $(varE exc_else) ++ "'" |]
1264
  -- default match for unknown exception name
1265
  let defmatch = Match (VarP exc_else) (NormalB fails) []
1266
  -- the match results (per-constructor blocks)
1267
  str_matches <-
1268
    mapM (\(s, params) -> do
1269
            body_exp <- loadExcConstructor exc_args s params
1270
            return $ Match (LitP (StringL s)) (NormalB body_exp) [])
1271
    opdefs
1272
  -- the first function clause; we can't use [| |] due to TH
1273
  -- limitations, so we have to build the AST by hand
1274
  let clause1 = Clause [ConP 'JSON.JSArray
1275
                               [ListP [ConP 'JSON.JSString [VarP exc_name],
1276
                                            VarP exc_args]]]
1277
                (NormalB (CaseE (AppE (VarE 'JSON.fromJSString)
1278
                                        (VarE exc_name))
1279
                          (str_matches ++ [defmatch]))) []
1280
  -- the fail expression for the second function clause
1281
  fail_type <- [| fail $ "Invalid exception: expected '(string, [args])' " ++
1282
                  "      but got " ++ show (pp_value $(varE arg_else)) ++ "'"
1283
                |]
1284
  -- the second function clause
1285
  let clause2 = Clause [VarP arg_else] (NormalB fail_type) []
1286
  sigt <- [t| JSON.JSValue -> JSON.Result $(conT tname) |]
1287
  return $ (SigD fname sigt, FunD fname [clause1, clause2])