Statistics
| Branch: | Tag: | Revision:

root / src / Ganeti / THH.hs @ a3dabca9

History | View | Annotate | Download (49.8 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 <- [| case JSON.readJSON $(varE s) of
579
               JSON.Ok s' -> $(varE (fromRawName name)) s'
580
               JSON.Error e ->
581
                   JSON.Error $ "Can't parse raw value for type " ++
582
                           $(stringE name) ++ ": " ++ e ++ " from " ++
583
                           show $(varE s)
584
           |]
585
  return $ FunD 'JSON.readJSON [Clause [VarP s] (NormalB body) []]
586

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

    
598
-- * Template code for opcodes
599

    
600
-- | Transforms a CamelCase string into an_underscore_based_one.
601
deCamelCase :: String -> String
602
deCamelCase =
603
    intercalate "_" . map (map toUpper) . groupBy (\_ b -> not $ isUpper b)
604

    
605
-- | Transform an underscore_name into a CamelCase one.
606
camelCase :: String -> String
607
camelCase = concatMap (ensureUpper . drop 1) .
608
            groupBy (\_ b -> b /= '_' && b /= '-') . ('_':)
609

    
610
-- | Computes the name of a given constructor.
611
constructorName :: Con -> Q Name
612
constructorName (NormalC name _) = return name
613
constructorName (RecC name _)    = return name
614
constructorName x                = fail $ "Unhandled constructor " ++ show x
615

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

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

    
642
-- | Constructor-to-string for OpCode.
643
genOpID :: Name -> String -> Q [Dec]
644
genOpID = genConstrToStr deCamelCase
645

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

    
664
-- | Generates a list of all defined opcode IDs.
665
genAllOpIDs :: Name -> String -> Q [Dec]
666
genAllOpIDs = genAllConstr deCamelCase
667

    
668
-- | OpCode parameter (field) type.
669
type OpParam = (String, Q Type, Q Exp)
670

    
671
-- * Python code generation
672

    
673
data OpCodeField = OpCodeField { ocfName :: String
674
                               , ocfType :: PyType
675
                               , ocfDefl :: Maybe PyValueEx
676
                               , ocfDoc  :: String
677
                               }
678

    
679
-- | Transfers opcode data between the opcode description (through
680
-- @genOpCode@) and the Python code generation functions.
681
data OpCodeDescriptor = OpCodeDescriptor { ocdName   :: String
682
                                         , ocdType   :: PyType
683
                                         , ocdDoc    :: String
684
                                         , ocdFields :: [OpCodeField]
685
                                         , ocdDescr  :: String
686
                                         }
687

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

    
698
maybeApp (Just expr) typ =
699
  [| Just ($(conE (mkName "PyValueEx")) ($expr :: $typ)) |]
700

    
701
-- | Generates a Python type according to whether the field is
702
-- optional.
703
--
704
-- The type of created expression is PyType.
705
genPyType' :: OptionalType -> Q Type -> Q PyType
706
genPyType' opt typ = typ >>= pyOptionalType (opt /= NotOptional)
707

    
708
-- | Generates Python types from opcode parameters.
709
genPyType :: Field -> Q PyType
710
genPyType f = genPyType' (fieldIsOptional f) (fieldType f)
711

    
712
-- | Generates Python default values from opcode parameters.
713
genPyDefault :: Field -> Q Exp
714
genPyDefault f = maybeApp (fieldDefault f) (fieldType f)
715

    
716
pyField :: Field -> Q Exp
717
pyField f = genPyType f >>= \t ->
718
            [| OpCodeField $(stringE (fieldName f))
719
                           t
720
                           $(genPyDefault f)
721
                           $(stringE (fieldDoc f)) |]
722

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

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

    
749
-- | Converts from an opcode constructor to a Luxi constructor.
750
opcodeConsToLuxiCons :: (a, b, c, d, e) -> (a, d)
751
opcodeConsToLuxiCons (x, _, _, y, _) = (x, y)
752

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

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

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

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

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

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

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

    
878
-- * Template code for luxi
879

    
880
-- | Constructor-to-string for LuxiOp.
881
genStrOfOp :: Name -> String -> Q [Dec]
882
genStrOfOp = genConstrToStr id
883

    
884
-- | Constructor-to-string for MsgKeys.
885
genStrOfKey :: Name -> String -> Q [Dec]
886
genStrOfKey = genConstrToStr ensureLower
887

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

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

    
928
-- * "Objects" functionality
929

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

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

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

    
959
-- | The toDict function name for a given type.
960
toDictName :: String -> Name
961
toDictName sname = mkName ("toDict" ++ sname)
962

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

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

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

    
1006
-- | Generates the showJSON clause for a given object name.
1007
objectShowJSON :: String -> Q Dec
1008
objectShowJSON name = do
1009
  body <- [| JSON.showJSON . $(varE . mkName $ "save" ++ name) |]
1010
  return $ FunD 'JSON.showJSON [Clause [] (NormalB body) []]
1011

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

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

    
1053
  return (fvar, BindS (VarP fvar) bexp)
1054

    
1055
-- | Builds the readJSON instance for a given object name.
1056
objectReadJSON :: String -> Q Dec
1057
objectReadJSON name = do
1058
  let s = mkName "s"
1059
  body <- [| case JSON.readJSON $(varE s) of
1060
               JSON.Ok s' -> $(varE .mkName $ "load" ++ name) s'
1061
               JSON.Error e ->
1062
                 JSON.Error $ "Can't parse value for type " ++
1063
                       $(stringE name) ++ ": " ++ e
1064
           |]
1065
  return $ FunD 'JSON.readJSON [Clause [VarP s] (NormalB body) []]
1066

    
1067
-- * Inheritable parameter tables implementation
1068

    
1069
-- | Compute parameter type names.
1070
paramTypeNames :: String -> (String, String)
1071
paramTypeNames root = ("Filled"  ++ root ++ "Params",
1072
                       "Partial" ++ root ++ "Params")
1073

    
1074
-- | Compute information about the type of a parameter field.
1075
paramFieldTypeInfo :: String -> Field -> Q (Name, Strict, Type)
1076
paramFieldTypeInfo field_pfx fd = do
1077
  t <- actualFieldType fd
1078
  let n = mkName . (++ "P") . (field_pfx ++) .
1079
          fieldRecordName $ fd
1080
  return (n, NotStrict, AppT (ConT ''Maybe) t)
1081

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

    
1107
-- | Builds a list of all fields of a parameter.
1108
buildParamAllFields :: String -> [Field] -> [Dec]
1109
buildParamAllFields sname fields =
1110
  let vname = mkName ("all" ++ sname ++ "ParamFields")
1111
      sig = SigD vname (AppT ListT (ConT ''String))
1112
      val = ListE $ map (LitE . StringL . fieldName) fields
1113
  in [sig, ValD (VarP vname) (NormalB val) []]
1114

    
1115
-- | Builds the 'DictObject' instance for a filled parameter.
1116
buildDictObjectInst :: Name -> String -> [Dec]
1117
buildDictObjectInst name sname =
1118
  [InstanceD [] (AppT (ConT ''DictObject) (ConT name))
1119
   [ValD (VarP 'toDict) (NormalB (VarE (toDictName sname))) []]]
1120

    
1121
-- | Generates the serialisation for a partial parameter.
1122
buildPParamSerialisation :: String -> [Field] -> Q [Dec]
1123
buildPParamSerialisation sname fields = do
1124
  let name = mkName sname
1125
  savedecls <- genSaveObject savePParamField sname fields
1126
  (loadsig, loadfn) <- genLoadObject loadPParamField sname fields
1127
  shjson <- objectShowJSON sname
1128
  rdjson <- objectReadJSON sname
1129
  let instdecl = InstanceD [] (AppT (ConT ''JSON.JSON) (ConT name))
1130
                 [rdjson, shjson]
1131
  return $ savedecls ++ [loadsig, loadfn, instdecl]
1132

    
1133
-- | Generates code to save an optional parameter field.
1134
savePParamField :: Name -> Field -> Q Exp
1135
savePParamField fvar field = do
1136
  checkNonOptDef field
1137
  let actualVal = mkName "v"
1138
  normalexpr <- saveObjectField actualVal field
1139
  -- we have to construct the block here manually, because we can't
1140
  -- splice-in-splice
1141
  return $ CaseE (VarE fvar) [ Match (ConP 'Nothing [])
1142
                                       (NormalB (ConE '[])) []
1143
                             , Match (ConP 'Just [VarP actualVal])
1144
                                       (NormalB normalexpr) []
1145
                             ]
1146

    
1147
-- | Generates code to load an optional parameter field.
1148
loadPParamField :: Field -> Q (Name, Stmt)
1149
loadPParamField field = do
1150
  checkNonOptDef field
1151
  let name = fieldName field
1152
  fvar <- newName name
1153
  -- these are used in all patterns below
1154
  let objvar = varNameE "o"
1155
      objfield = stringE name
1156
      loadexp = [| $(varE 'maybeFromObj) $objvar $objfield |]
1157
  bexp <- loadFnOpt field loadexp objvar
1158
  return (fvar, BindS (VarP fvar) bexp)
1159

    
1160
-- | Builds a simple declaration of type @n_x = fromMaybe f_x p_x@.
1161
buildFromMaybe :: String -> Q Dec
1162
buildFromMaybe fname =
1163
  valD (varP (mkName $ "n_" ++ fname))
1164
         (normalB [| $(varE 'fromMaybe)
1165
                        $(varNameE $ "f_" ++ fname)
1166
                        $(varNameE $ "p_" ++ fname) |]) []
1167

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

    
1193
-- * Template code for exceptions
1194

    
1195
-- | Exception simple error message field.
1196
excErrMsg :: (String, Q Type)
1197
excErrMsg = ("errMsg", [t| String |])
1198

    
1199
-- | Builds an exception type definition.
1200
genException :: String                  -- ^ Name of new type
1201
             -> SimpleObject -- ^ Constructor name and parameters
1202
             -> Q [Dec]
1203
genException name cons = do
1204
  let tname = mkName name
1205
  declD <- buildSimpleCons tname cons
1206
  (savesig, savefn) <- genSaveSimpleObj tname ("save" ++ name) cons $
1207
                         uncurry saveExcCons
1208
  (loadsig, loadfn) <- genLoadExc tname ("load" ++ name) cons
1209
  return [declD, loadsig, loadfn, savesig, savefn]
1210

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

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

    
1251
{-| Generates the loadException function.
1252

    
1253
This generates a quite complicated function, along the lines of:
1254

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