Statistics
| Branch: | Tag: | Revision:

root / src / Ganeti / THH.hs @ 26e32dee

History | View | Annotate | Download (53 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
                  , genOpLowerStrip
40
                  , genAllConstr
41
                  , genAllOpIDs
42
                  , PyValue(..)
43
                  , PyValueEx(..)
44
                  , OpCodeField(..)
45
                  , OpCodeDescriptor(..)
46
                  , genOpCode
47
                  , genStrOfOp
48
                  , genStrOfKey
49
                  , genLuxiOp
50
                  , Field (..)
51
                  , simpleField
52
                  , andRestArguments
53
                  , specialNumericalField
54
                  , timeAsDoubleField
55
                  , withDoc
56
                  , defaultField
57
                  , optionalField
58
                  , optionalNullSerField
59
                  , renameField
60
                  , customField
61
                  , timeStampFields
62
                  , uuidFields
63
                  , serialFields
64
                  , tagsFields
65
                  , TagSet
66
                  , buildObject
67
                  , buildObjectSerialisation
68
                  , buildParam
69
                  , genException
70
                  , excErrMsg
71
                  ) where
72

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

    
88
import qualified Text.JSON as JSON
89
import Text.JSON.Pretty (pp_value)
90

    
91
import Ganeti.JSON
92
import Ganeti.PyValue
93
import Ganeti.THH.PyType
94

    
95

    
96
-- * Exported types
97

    
98
-- | Optional field information.
99
data OptionalType
100
  = NotOptional           -- ^ Field is not optional
101
  | OptionalOmitNull      -- ^ Field is optional, null is not serialised
102
  | OptionalSerializeNull -- ^ Field is optional, null is serialised
103
  | AndRestArguments      -- ^ Special field capturing all the remaining fields
104
                          -- as plain JSON values
105
  deriving (Show, Eq)
106

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

    
181
-- | Generates a simple field.
182
simpleField :: String -> Q Type -> Field
183
simpleField fname ftype =
184
  Field { fieldName        = fname
185
        , fieldType        = ftype
186
        , fieldRead        = Nothing
187
        , fieldShow        = Nothing
188
        , fieldExtraKeys   = []
189
        , fieldDefault     = Nothing
190
        , fieldConstr      = Nothing
191
        , fieldIsOptional  = NotOptional
192
        , fieldDoc         = ""
193
        }
194

    
195
-- | Generate an AndRestArguments catch-all field.
196
andRestArguments :: String -> Field
197
andRestArguments fname =
198
  Field { fieldName        = fname
199
        , fieldType        = [t| M.Map String JSON.JSValue |]
200
        , fieldRead        = Nothing
201
        , fieldShow        = Nothing
202
        , fieldExtraKeys   = []
203
        , fieldDefault     = Nothing
204
        , fieldConstr      = Nothing
205
        , fieldIsOptional  = AndRestArguments
206
        , fieldDoc         = ""
207
        }
208

    
209
withDoc :: String -> Field -> Field
210
withDoc doc field =
211
  field { fieldDoc = doc }
212

    
213
-- | Sets the renamed constructor field.
214
renameField :: String -> Field -> Field
215
renameField constrName field = field { fieldConstr = Just constrName }
216

    
217
-- | Sets the default value on a field (makes it optional with a
218
-- default value).
219
defaultField :: Q Exp -> Field -> Field
220
defaultField defval field = field { fieldDefault = Just defval }
221

    
222
-- | Marks a field optional (turning its base type into a Maybe).
223
optionalField :: Field -> Field
224
optionalField field = field { fieldIsOptional = OptionalOmitNull }
225

    
226
-- | Marks a field optional (turning its base type into a Maybe), but
227
-- with 'Nothing' serialised explicitly as /null/.
228
optionalNullSerField :: Field -> Field
229
optionalNullSerField field = field { fieldIsOptional = OptionalSerializeNull }
230

    
231
-- | Wrapper around a special parse function, suitable as field-parsing
232
-- function.
233
numericalReadFn :: JSON.JSON a => (String -> JSON.Result a)
234
                   -> [(String, JSON.JSValue)] -> JSON.JSValue -> JSON.Result a
235
numericalReadFn _ _ v@(JSON.JSRational _ _) = JSON.readJSON v
236
numericalReadFn f _ (JSON.JSString x) = f $ JSON.fromJSString x
237
numericalReadFn _ _ _ = JSON.Error "A numerical field has to be a number or\
238
                                   \ a string."
239

    
240
-- | Sets the read function to also accept string parsable by the given
241
-- function.
242
specialNumericalField :: Name -> Field -> Field
243
specialNumericalField f field =
244
     field { fieldRead = Just (appE (varE 'numericalReadFn) (varE f)) }
245

    
246
-- | Creates a new mandatory field that reads time as the (floating point)
247
-- number of seconds since the standard UNIX epoch, and represents it in
248
-- Haskell as 'ClockTime'.
249
timeAsDoubleField :: String -> Field
250
timeAsDoubleField fname =
251
  (simpleField fname [t| ClockTime |])
252
    { fieldRead = Just $ [| \_ -> liftM unTimeAsDoubleJSON . JSON.readJSON |]
253
    , fieldShow = Just $ [| \c -> (JSON.showJSON $ TimeAsDoubleJSON c, []) |]
254
    }
255

    
256
-- | Sets custom functions on a field.
257
customField :: Name      -- ^ The name of the read function
258
            -> Name      -- ^ The name of the show function
259
            -> [String]  -- ^ The name of extra field keys
260
            -> Field     -- ^ The original field
261
            -> Field     -- ^ Updated field
262
customField readfn showfn extra field =
263
  field { fieldRead = Just (varE readfn), fieldShow = Just (varE showfn)
264
        , fieldExtraKeys = extra }
265

    
266
-- | Computes the record name for a given field, based on either the
267
-- string value in the JSON serialisation or the custom named if any
268
-- exists.
269
fieldRecordName :: Field -> String
270
fieldRecordName (Field { fieldName = name, fieldConstr = alias }) =
271
  fromMaybe (camelCase name) alias
272

    
273
-- | Computes the preferred variable name to use for the value of this
274
-- field. If the field has a specific constructor name, then we use a
275
-- first-letter-lowercased version of that; otherwise, we simply use
276
-- the field name. See also 'fieldRecordName'.
277
fieldVariable :: Field -> String
278
fieldVariable f =
279
  case (fieldConstr f) of
280
    Just name -> ensureLower name
281
    _ -> map (\c -> if c == '-' then '_' else c) $ fieldName f
282

    
283
-- | Compute the actual field type (taking into account possible
284
-- optional status).
285
actualFieldType :: Field -> Q Type
286
actualFieldType f | fieldIsOptional f `elem` [NotOptional, AndRestArguments] = t
287
                  | otherwise =  [t| Maybe $t |]
288
                  where t = fieldType f
289

    
290
-- | Checks that a given field is not optional (for object types or
291
-- fields which should not allow this case).
292
checkNonOptDef :: (Monad m) => Field -> m ()
293
checkNonOptDef (Field { fieldIsOptional = OptionalOmitNull
294
                      , fieldName = name }) =
295
  fail $ "Optional field " ++ name ++ " used in parameter declaration"
296
checkNonOptDef (Field { fieldIsOptional = OptionalSerializeNull
297
                      , fieldName = name }) =
298
  fail $ "Optional field " ++ name ++ " used in parameter declaration"
299
checkNonOptDef (Field { fieldDefault = (Just _), fieldName = name }) =
300
  fail $ "Default field " ++ name ++ " used in parameter declaration"
301
checkNonOptDef _ = return ()
302

    
303
-- | Construct a function that parses a field value. If the field has
304
-- a custom 'fieldRead', it's applied to @o@ and used. Otherwise
305
-- @JSON.readJSON@ is used.
306
parseFn :: Field   -- ^ The field definition
307
        -> Q Exp   -- ^ The entire object in JSON object format
308
        -> Q Exp   -- ^ The resulting function that parses a JSON message
309
parseFn field o
310
  = maybe [| readJSONWithDesc $(stringE $ fieldName field) False |]
311
          (`appE` o) (fieldRead field)
312

    
313
-- | Produces the expression that will de-serialise a given
314
-- field. Since some custom parsing functions might need to use the
315
-- entire object, we do take and pass the object to any custom read
316
-- functions.
317
loadFn :: Field   -- ^ The field definition
318
       -> Q Exp   -- ^ The value of the field as existing in the JSON message
319
       -> Q Exp   -- ^ The entire object in JSON object format
320
       -> Q Exp   -- ^ Resulting expression
321
loadFn field expr o = [| $expr >>= $(parseFn field o) |]
322

    
323
-- | Just as 'loadFn', but for optional fields.
324
loadFnOpt :: Field   -- ^ The field definition
325
          -> Q Exp   -- ^ The value of the field as existing in the JSON message
326
                     -- as Maybe
327
          -> Q Exp   -- ^ The entire object in JSON object format
328
          -> Q Exp   -- ^ Resulting expression
329
loadFnOpt field@(Field { fieldDefault = Just def }) expr o
330
  = case fieldIsOptional field of
331
      NotOptional -> [| $expr >>= maybe (return $def) $(parseFn field o) |]
332
      _           -> fail $ "Field " ++ fieldName field ++ ":\
333
                            \ A field can't be optional and\
334
                            \ have a default value at the same time."
335
loadFnOpt field expr o
336
  = [| $expr >>= maybe (return Nothing) (liftM Just . $(parseFn field o)) |]
337

    
338
-- * Common field declarations
339

    
340
-- | Timestamp fields description.
341
timeStampFields :: [Field]
342
timeStampFields = map (defaultField [| TOD 0 0 |] . timeAsDoubleField)
343
                      ["ctime", "mtime"]
344

    
345

    
346
-- | Serial number fields description.
347
serialFields :: [Field]
348
serialFields =
349
    [ renameField  "Serial" $ simpleField "serial_no" [t| Int |] ]
350

    
351
-- | UUID fields description.
352
uuidFields :: [Field]
353
uuidFields = [ simpleField "uuid" [t| String |] ]
354

    
355
-- | Tag set type alias.
356
type TagSet = Set.Set String
357

    
358
-- | Tag field description.
359
tagsFields :: [Field]
360
tagsFields = [ defaultField [| Set.empty |] $
361
               simpleField "tags" [t| TagSet |] ]
362

    
363
-- * Internal types
364

    
365
-- | A simple field, in constrast to the customisable 'Field' type.
366
type SimpleField = (String, Q Type)
367

    
368
-- | A definition for a single constructor for a simple object.
369
type SimpleConstructor = (String, [SimpleField])
370

    
371
-- | A definition for ADTs with simple fields.
372
type SimpleObject = [SimpleConstructor]
373

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

    
377
-- | A type alias for a Luxi constructor of a regular object.
378
type LuxiConstructor = (String, [Field])
379

    
380
-- * Helper functions
381

    
382
-- | Ensure first letter is lowercase.
383
--
384
-- Used to convert type name to function prefix, e.g. in @data Aa ->
385
-- aaToRaw@.
386
ensureLower :: String -> String
387
ensureLower [] = []
388
ensureLower (x:xs) = toLower x:xs
389

    
390
-- | Ensure first letter is uppercase.
391
--
392
-- Used to convert constructor name to component
393
ensureUpper :: String -> String
394
ensureUpper [] = []
395
ensureUpper (x:xs) = toUpper x:xs
396

    
397
-- | Helper for quoted expressions.
398
varNameE :: String -> Q Exp
399
varNameE = varE . mkName
400

    
401
-- | showJSON as an expression, for reuse.
402
showJSONE :: Q Exp
403
showJSONE = varE 'JSON.showJSON
404

    
405
-- | makeObj as an expression, for reuse.
406
makeObjE :: Q Exp
407
makeObjE = varE 'JSON.makeObj
408

    
409
-- | fromObj (Ganeti specific) as an expression, for reuse.
410
fromObjE :: Q Exp
411
fromObjE = varE 'fromObj
412

    
413
-- | ToRaw function name.
414
toRawName :: String -> Name
415
toRawName = mkName . (++ "ToRaw") . ensureLower
416

    
417
-- | FromRaw function name.
418
fromRawName :: String -> Name
419
fromRawName = mkName . (++ "FromRaw") . ensureLower
420

    
421
-- | Converts a name to it's varE\/litE representations.
422
reprE :: Either String Name -> Q Exp
423
reprE = either stringE varE
424

    
425
-- | Smarter function application.
426
--
427
-- This does simply f x, except that if is 'id', it will skip it, in
428
-- order to generate more readable code when using -ddump-splices.
429
appFn :: Exp -> Exp -> Exp
430
appFn f x | f == VarE 'id = x
431
          | otherwise = AppE f x
432

    
433
-- | Builds a field for a normal constructor.
434
buildConsField :: Q Type -> StrictTypeQ
435
buildConsField ftype = do
436
  ftype' <- ftype
437
  return (NotStrict, ftype')
438

    
439
-- | Builds a constructor based on a simple definition (not field-based).
440
buildSimpleCons :: Name -> SimpleObject -> Q Dec
441
buildSimpleCons tname cons = do
442
  decl_d <- mapM (\(cname, fields) -> do
443
                    fields' <- mapM (buildConsField . snd) fields
444
                    return $ NormalC (mkName cname) fields') cons
445
  return $ DataD [] tname [] decl_d [''Show, ''Eq]
446

    
447
-- | Generate the save function for a given type.
448
genSaveSimpleObj :: Name                            -- ^ Object type
449
                 -> String                          -- ^ Function name
450
                 -> SimpleObject                    -- ^ Object definition
451
                 -> (SimpleConstructor -> Q Clause) -- ^ Constructor save fn
452
                 -> Q (Dec, Dec)
453
genSaveSimpleObj tname sname opdefs fn = do
454
  let sigt = AppT (AppT ArrowT (ConT tname)) (ConT ''JSON.JSValue)
455
      fname = mkName sname
456
  cclauses <- mapM fn opdefs
457
  return $ (SigD fname sigt, FunD fname cclauses)
458

    
459
-- * Template code for simple raw type-equivalent ADTs
460

    
461
-- | Generates a data type declaration.
462
--
463
-- The type will have a fixed list of instances.
464
strADTDecl :: Name -> [String] -> Dec
465
strADTDecl name constructors =
466
  DataD [] name []
467
          (map (flip NormalC [] . mkName) constructors)
468
          [''Show, ''Eq, ''Enum, ''Bounded, ''Ord]
469

    
470
-- | Generates a toRaw function.
471
--
472
-- This generates a simple function of the form:
473
--
474
-- @
475
-- nameToRaw :: Name -> /traw/
476
-- nameToRaw Cons1 = var1
477
-- nameToRaw Cons2 = \"value2\"
478
-- @
479
genToRaw :: Name -> Name -> Name -> [(String, Either String Name)] -> Q [Dec]
480
genToRaw traw fname tname constructors = do
481
  let sigt = AppT (AppT ArrowT (ConT tname)) (ConT traw)
482
  -- the body clauses, matching on the constructor and returning the
483
  -- raw value
484
  clauses <- mapM  (\(c, v) -> clause [recP (mkName c) []]
485
                             (normalB (reprE v)) []) constructors
486
  return [SigD fname sigt, FunD fname clauses]
487

    
488
-- | Generates a fromRaw function.
489
--
490
-- The function generated is monadic and can fail parsing the
491
-- raw value. It is of the form:
492
--
493
-- @
494
-- nameFromRaw :: (Monad m) => /traw/ -> m Name
495
-- nameFromRaw s | s == var1       = Cons1
496
--               | s == \"value2\" = Cons2
497
--               | otherwise = fail /.../
498
-- @
499
genFromRaw :: Name -> Name -> Name -> [(String, Either String Name)] -> Q [Dec]
500
genFromRaw traw fname tname constructors = do
501
  -- signature of form (Monad m) => String -> m $name
502
  sigt <- [t| (Monad m) => $(conT traw) -> m $(conT tname) |]
503
  -- clauses for a guarded pattern
504
  let varp = mkName "s"
505
      varpe = varE varp
506
  clauses <- mapM (\(c, v) -> do
507
                     -- the clause match condition
508
                     g <- normalG [| $varpe == $(reprE v) |]
509
                     -- the clause result
510
                     r <- [| return $(conE (mkName c)) |]
511
                     return (g, r)) constructors
512
  -- the otherwise clause (fallback)
513
  oth_clause <- do
514
    g <- normalG [| otherwise |]
515
    r <- [|fail ("Invalid string value for type " ++
516
                 $(litE (stringL (nameBase tname))) ++ ": " ++ show $varpe) |]
517
    return (g, r)
518
  let fun = FunD fname [Clause [VarP varp]
519
                        (GuardedB (clauses++[oth_clause])) []]
520
  return [SigD fname sigt, fun]
521

    
522
-- | Generates a data type from a given raw format.
523
--
524
-- The format is expected to multiline. The first line contains the
525
-- type name, and the rest of the lines must contain two words: the
526
-- constructor name and then the string representation of the
527
-- respective constructor.
528
--
529
-- The function will generate the data type declaration, and then two
530
-- functions:
531
--
532
-- * /name/ToRaw, which converts the type to a raw type
533
--
534
-- * /name/FromRaw, which (monadically) converts from a raw type to the type
535
--
536
-- Note that this is basically just a custom show\/read instance,
537
-- nothing else.
538
declareADT
539
  :: (a -> Either String Name) -> Name -> String -> [(String, a)] -> Q [Dec]
540
declareADT fn traw sname cons = do
541
  let name = mkName sname
542
      ddecl = strADTDecl name (map fst cons)
543
      -- process cons in the format expected by genToRaw
544
      cons' = map (\(a, b) -> (a, fn b)) cons
545
  toraw <- genToRaw traw (toRawName sname) name cons'
546
  fromraw <- genFromRaw traw (fromRawName sname) name cons'
547
  return $ ddecl:toraw ++ fromraw
548

    
549
declareLADT :: Name -> String -> [(String, String)] -> Q [Dec]
550
declareLADT = declareADT Left
551

    
552
declareILADT :: String -> [(String, Int)] -> Q [Dec]
553
declareILADT sname cons = do
554
  consNames <- sequence [ newName ('_':n) | (n, _) <- cons ]
555
  consFns <- concat <$> sequence
556
             [ do sig <- sigD n [t| Int |]
557
                  let expr = litE (IntegerL (toInteger i))
558
                  fn <- funD n [clause [] (normalB expr) []]
559
                  return [sig, fn]
560
             | n <- consNames
561
             | (_, i) <- cons ]
562
  let cons' = [ (n, n') | (n, _) <- cons | n' <- consNames ]
563
  (consFns ++) <$> declareADT Right ''Int sname cons'
564

    
565
declareIADT :: String -> [(String, Name)] -> Q [Dec]
566
declareIADT = declareADT Right ''Int
567

    
568
declareSADT :: String -> [(String, Name)] -> Q [Dec]
569
declareSADT = declareADT Right ''String
570

    
571
-- | Creates the showJSON member of a JSON instance declaration.
572
--
573
-- This will create what is the equivalent of:
574
--
575
-- @
576
-- showJSON = showJSON . /name/ToRaw
577
-- @
578
--
579
-- in an instance JSON /name/ declaration
580
genShowJSON :: String -> Q Dec
581
genShowJSON name = do
582
  body <- [| JSON.showJSON . $(varE (toRawName name)) |]
583
  return $ FunD 'JSON.showJSON [Clause [] (NormalB body) []]
584

    
585
-- | Creates the readJSON member of a JSON instance declaration.
586
--
587
-- This will create what is the equivalent of:
588
--
589
-- @
590
-- readJSON s = case readJSON s of
591
--                Ok s' -> /name/FromRaw s'
592
--                Error e -> Error /description/
593
-- @
594
--
595
-- in an instance JSON /name/ declaration
596
genReadJSON :: String -> Q Dec
597
genReadJSON name = do
598
  let s = mkName "s"
599
  body <- [| $(varE (fromRawName name)) =<<
600
             readJSONWithDesc $(stringE name) True $(varE s) |]
601
  return $ FunD 'JSON.readJSON [Clause [VarP s] (NormalB body) []]
602

    
603
-- | Generates a JSON instance for a given type.
604
--
605
-- This assumes that the /name/ToRaw and /name/FromRaw functions
606
-- have been defined as by the 'declareSADT' function.
607
makeJSONInstance :: Name -> Q [Dec]
608
makeJSONInstance name = do
609
  let base = nameBase name
610
  showJ <- genShowJSON base
611
  readJ <- genReadJSON base
612
  return [InstanceD [] (AppT (ConT ''JSON.JSON) (ConT name)) [readJ,showJ]]
613

    
614
-- * Template code for opcodes
615

    
616
-- | Transforms a CamelCase string into an_underscore_based_one.
617
deCamelCase :: String -> String
618
deCamelCase =
619
    intercalate "_" . map (map toUpper) . groupBy (\_ b -> not $ isUpper b)
620

    
621
-- | Transform an underscore_name into a CamelCase one.
622
camelCase :: String -> String
623
camelCase = concatMap (ensureUpper . drop 1) .
624
            groupBy (\_ b -> b /= '_' && b /= '-') . ('_':)
625

    
626
-- | Computes the name of a given constructor.
627
constructorName :: Con -> Q Name
628
constructorName (NormalC name _) = return name
629
constructorName (RecC name _)    = return name
630
constructorName x                = fail $ "Unhandled constructor " ++ show x
631

    
632
-- | Extract all constructor names from a given type.
633
reifyConsNames :: Name -> Q [String]
634
reifyConsNames name = do
635
  reify_result <- reify name
636
  case reify_result of
637
    TyConI (DataD _ _ _ cons _) -> mapM (liftM nameBase . constructorName) cons
638
    o -> fail $ "Unhandled name passed to reifyConsNames, expected\
639
                \ type constructor but got '" ++ show o ++ "'"
640

    
641
-- | Builds the generic constructor-to-string function.
642
--
643
-- This generates a simple function of the following form:
644
--
645
-- @
646
-- fname (ConStructorOne {}) = trans_fun("ConStructorOne")
647
-- fname (ConStructorTwo {}) = trans_fun("ConStructorTwo")
648
-- @
649
--
650
-- This builds a custom list of name\/string pairs and then uses
651
-- 'genToRaw' to actually generate the function.
652
genConstrToStr :: (String -> Q String) -> Name -> String -> Q [Dec]
653
genConstrToStr trans_fun name fname = do
654
  cnames <- reifyConsNames name
655
  svalues <- mapM (liftM Left . trans_fun) cnames
656
  genToRaw ''String (mkName fname) name $ zip cnames svalues
657

    
658
-- | Constructor-to-string for OpCode.
659
genOpID :: Name -> String -> Q [Dec]
660
genOpID = genConstrToStr (return . deCamelCase)
661

    
662
-- | Strips @Op@ from the constructor name, converts to lower-case
663
-- and adds a given prefix.
664
genOpLowerStrip :: String -> Name -> String -> Q [Dec]
665
genOpLowerStrip prefix =
666
    genConstrToStr (liftM ((prefix ++) . map toLower . deCamelCase)
667
                    . stripPrefixM "Op")
668
  where
669
    stripPrefixM :: String -> String -> Q String
670
    stripPrefixM pfx s = maybe (fail $ s ++ " doesn't start with " ++ pfx)
671
                               return
672
                         $ stripPrefix pfx s
673

    
674
-- | Builds a list with all defined constructor names for a type.
675
--
676
-- @
677
-- vstr :: String
678
-- vstr = [...]
679
-- @
680
--
681
-- Where the actual values of the string are the constructor names
682
-- mapped via @trans_fun@.
683
genAllConstr :: (String -> String) -> Name -> String -> Q [Dec]
684
genAllConstr trans_fun name vstr = do
685
  cnames <- reifyConsNames name
686
  let svalues = sort $ map trans_fun cnames
687
      vname = mkName vstr
688
      sig = SigD vname (AppT ListT (ConT ''String))
689
      body = NormalB (ListE (map (LitE . StringL) svalues))
690
  return $ [sig, ValD (VarP vname) body []]
691

    
692
-- | Generates a list of all defined opcode IDs.
693
genAllOpIDs :: Name -> String -> Q [Dec]
694
genAllOpIDs = genAllConstr deCamelCase
695

    
696
-- | OpCode parameter (field) type.
697
type OpParam = (String, Q Type, Q Exp)
698

    
699
-- * Python code generation
700

    
701
data OpCodeField = OpCodeField { ocfName :: String
702
                               , ocfType :: PyType
703
                               , ocfDefl :: Maybe PyValueEx
704
                               , ocfDoc  :: String
705
                               }
706

    
707
-- | Transfers opcode data between the opcode description (through
708
-- @genOpCode@) and the Python code generation functions.
709
data OpCodeDescriptor = OpCodeDescriptor { ocdName   :: String
710
                                         , ocdType   :: PyType
711
                                         , ocdDoc    :: String
712
                                         , ocdFields :: [OpCodeField]
713
                                         , ocdDescr  :: String
714
                                         }
715

    
716
-- | Optionally encapsulates default values in @PyValueEx@.
717
--
718
-- @maybeApp exp typ@ returns a quoted expression that encapsulates
719
-- the default value @exp@ of an opcode parameter cast to @typ@ in a
720
-- @PyValueEx@, if @exp@ is @Just@.  Otherwise, it returns a quoted
721
-- expression with @Nothing@.
722
maybeApp :: Maybe (Q Exp) -> Q Type -> Q Exp
723
maybeApp Nothing _ =
724
  [| Nothing |]
725

    
726
maybeApp (Just expr) typ =
727
  [| Just ($(conE (mkName "PyValueEx")) ($expr :: $typ)) |]
728

    
729
-- | Generates a Python type according to whether the field is
730
-- optional.
731
--
732
-- The type of created expression is PyType.
733
genPyType' :: OptionalType -> Q Type -> Q PyType
734
genPyType' opt typ = typ >>= pyOptionalType (opt /= NotOptional)
735

    
736
-- | Generates Python types from opcode parameters.
737
genPyType :: Field -> Q PyType
738
genPyType f = genPyType' (fieldIsOptional f) (fieldType f)
739

    
740
-- | Generates Python default values from opcode parameters.
741
genPyDefault :: Field -> Q Exp
742
genPyDefault f = maybeApp (fieldDefault f) (fieldType f)
743

    
744
pyField :: Field -> Q Exp
745
pyField f = genPyType f >>= \t ->
746
            [| OpCodeField $(stringE (fieldName f))
747
                           t
748
                           $(genPyDefault f)
749
                           $(stringE (fieldDoc f)) |]
750

    
751
-- | Generates a Haskell function call to "showPyClass" with the
752
-- necessary information on how to build the Python class string.
753
pyClass :: OpCodeConstructor -> Q Exp
754
pyClass (consName, consType, consDoc, consFields, consDscField) =
755
  do let pyClassVar = varNameE "showPyClass"
756
         consName' = stringE consName
757
     consType' <- genPyType' NotOptional consType
758
     let consDoc' = stringE consDoc
759
     [| OpCodeDescriptor $consName'
760
                         consType'
761
                         $consDoc'
762
                         $(listE $ map pyField consFields)
763
                         consDscField |]
764

    
765
-- | Generates a function called "pyClasses" that holds the list of
766
-- all the opcode descriptors necessary for generating the Python
767
-- opcodes.
768
pyClasses :: [OpCodeConstructor] -> Q [Dec]
769
pyClasses cons =
770
  do let name = mkName "pyClasses"
771
         sig = SigD name (AppT ListT (ConT ''OpCodeDescriptor))
772
     fn <- FunD name <$> (:[]) <$> declClause cons
773
     return [sig, fn]
774
  where declClause c =
775
          clause [] (normalB (ListE <$> mapM pyClass c)) []
776

    
777
-- | Converts from an opcode constructor to a Luxi constructor.
778
opcodeConsToLuxiCons :: (a, b, c, d, e) -> (a, d)
779
opcodeConsToLuxiCons (x, _, _, y, _) = (x, y)
780

    
781
-- | Generates the OpCode data type.
782
--
783
-- This takes an opcode logical definition, and builds both the
784
-- datatype and the JSON serialisation out of it. We can't use a
785
-- generic serialisation since we need to be compatible with Ganeti's
786
-- own, so we have a few quirks to work around.
787
genOpCode :: String              -- ^ Type name to use
788
          -> [OpCodeConstructor] -- ^ Constructor name and parameters
789
          -> Q [Dec]
790
genOpCode name cons = do
791
  let tname = mkName name
792
  decl_d <- mapM (\(cname, _, _, fields, _) -> do
793
                    -- we only need the type of the field, without Q
794
                    fields' <- mapM (fieldTypeInfo "op") fields
795
                    return $ RecC (mkName cname) fields')
796
            cons
797
  let declD = DataD [] tname [] decl_d [''Show, ''Eq]
798
  let (allfsig, allffn) = genAllOpFields "allOpFields" cons
799
  save_decs <- genSaveOpCode tname "saveOpCode" "toDictOpCode"
800
               (map opcodeConsToLuxiCons cons) saveConstructor True
801
  (loadsig, loadfn) <- genLoadOpCode cons
802
  pyDecls <- pyClasses cons
803
  return $ [declD, allfsig, allffn, loadsig, loadfn] ++ save_decs ++ pyDecls
804

    
805
-- | Generates the function pattern returning the list of fields for a
806
-- given constructor.
807
genOpConsFields :: OpCodeConstructor -> Clause
808
genOpConsFields (cname, _, _, fields, _) =
809
  let op_id = deCamelCase cname
810
      fvals = map (LitE . StringL) . sort . nub $
811
              concatMap (\f -> fieldName f:fieldExtraKeys f) fields
812
  in Clause [LitP (StringL op_id)] (NormalB $ ListE fvals) []
813

    
814
-- | Generates a list of all fields of an opcode constructor.
815
genAllOpFields  :: String              -- ^ Function name
816
                -> [OpCodeConstructor] -- ^ Object definition
817
                -> (Dec, Dec)
818
genAllOpFields sname opdefs =
819
  let cclauses = map genOpConsFields opdefs
820
      other = Clause [WildP] (NormalB (ListE [])) []
821
      fname = mkName sname
822
      sigt = AppT  (AppT ArrowT (ConT ''String)) (AppT ListT (ConT ''String))
823
  in (SigD fname sigt, FunD fname (cclauses++[other]))
824

    
825
-- | Generates the \"save\" clause for an entire opcode constructor.
826
--
827
-- This matches the opcode with variables named the same as the
828
-- constructor fields (just so that the spliced in code looks nicer),
829
-- and passes those name plus the parameter definition to 'saveObjectField'.
830
saveConstructor :: LuxiConstructor -- ^ The constructor
831
                -> Q Clause        -- ^ Resulting clause
832
saveConstructor (sname, fields) = do
833
  let cname = mkName sname
834
  fnames <- mapM (newName . fieldVariable) fields
835
  let pat = conP cname (map varP fnames)
836
  let felems = zipWith saveObjectField fnames fields
837
      -- now build the OP_ID serialisation
838
      opid = [| [( $(stringE "OP_ID"),
839
                   JSON.showJSON $(stringE . deCamelCase $ sname) )] |]
840
      flist = listE (opid:felems)
841
      -- and finally convert all this to a json object
842
      flist' = [| concat $flist |]
843
  clause [pat] (normalB flist') []
844

    
845
-- | Generates the main save opcode function.
846
--
847
-- This builds a per-constructor match clause that contains the
848
-- respective constructor-serialisation code.
849
genSaveOpCode :: Name                          -- ^ Object ype
850
              -> String                        -- ^ To 'JSValue' function name
851
              -> String                        -- ^ To 'JSObject' function name
852
              -> [LuxiConstructor]             -- ^ Object definition
853
              -> (LuxiConstructor -> Q Clause) -- ^ Constructor save fn
854
              -> Bool                          -- ^ Whether to generate
855
                                               -- obj or just a
856
                                               -- list\/tuple of values
857
              -> Q [Dec]
858
genSaveOpCode tname jvalstr tdstr opdefs fn gen_object = do
859
  tdclauses <- mapM fn opdefs
860
  let typecon = ConT tname
861
      jvalname = mkName jvalstr
862
      jvalsig = AppT  (AppT ArrowT typecon) (ConT ''JSON.JSValue)
863
      tdname = mkName tdstr
864
  tdsig <- [t| $(return typecon) -> [(String, JSON.JSValue)] |]
865
  jvalclause <- if gen_object
866
                  then [| $makeObjE . $(varE tdname) |]
867
                  else [| JSON.showJSON . map snd . $(varE tdname) |]
868
  return [ SigD tdname tdsig
869
         , FunD tdname tdclauses
870
         , SigD jvalname jvalsig
871
         , ValD (VarP jvalname) (NormalB jvalclause) []]
872

    
873
-- | Generates load code for a single constructor of the opcode data type.
874
loadConstructor :: Name -> (Field -> Q Exp) -> [Field] -> Q Exp
875
loadConstructor name loadfn fields = do
876
  fnames <- mapM (newName . ("r_" ++) . fieldName) fields
877
  fexps <- mapM loadfn fields
878
  let fstmts = zipWith (BindS . VarP) fnames fexps
879
      cexp = foldl (\accu fn -> AppE accu (VarE fn)) (ConE name) fnames
880
      retstmt = [NoBindS (AppE (VarE 'return) cexp)]
881
      -- FIXME: should we require an empty dict for an empty type?
882
      -- this allows any JSValue right now
883
  return $ DoE (fstmts ++ retstmt)
884

    
885
-- | Generates load code for a single constructor of the opcode data type.
886
loadOpConstructor :: OpCodeConstructor -> Q Exp
887
loadOpConstructor (sname, _, _, fields, _) =
888
  loadConstructor (mkName sname) (loadObjectField fields) fields
889

    
890
-- | Generates the loadOpCode function.
891
genLoadOpCode :: [OpCodeConstructor] -> Q (Dec, Dec)
892
genLoadOpCode opdefs = do
893
  let fname = mkName "loadOpCode"
894
      arg1 = mkName "v"
895
      objname = objVarName
896
      opid = mkName "op_id"
897
  st1 <- bindS (varP objname) [| liftM JSON.fromJSObject
898
                                 (JSON.readJSON $(varE arg1)) |]
899
  st2 <- bindS (varP opid) [| $fromObjE $(varE objname) $(stringE "OP_ID") |]
900
  -- the match results (per-constructor blocks)
901
  mexps <- mapM loadOpConstructor opdefs
902
  fails <- [| fail $ "Unknown opcode " ++ $(varE opid) |]
903
  let mpats = map (\(me, (consName, _, _, _, _)) ->
904
                       let mp = LitP . StringL . deCamelCase $ consName
905
                       in Match mp (NormalB me) []
906
                  ) $ zip mexps opdefs
907
      defmatch = Match WildP (NormalB fails) []
908
      cst = NoBindS $ CaseE (VarE opid) $ mpats++[defmatch]
909
      body = DoE [st1, st2, cst]
910
  sigt <- [t| JSON.JSValue -> JSON.Result $(conT (mkName "OpCode")) |]
911
  return $ (SigD fname sigt, FunD fname [Clause [VarP arg1] (NormalB body) []])
912

    
913
-- * Template code for luxi
914

    
915
-- | Constructor-to-string for LuxiOp.
916
genStrOfOp :: Name -> String -> Q [Dec]
917
genStrOfOp = genConstrToStr return
918

    
919
-- | Constructor-to-string for MsgKeys.
920
genStrOfKey :: Name -> String -> Q [Dec]
921
genStrOfKey = genConstrToStr (return . ensureLower)
922

    
923
-- | Generates the LuxiOp data type.
924
--
925
-- This takes a Luxi operation definition and builds both the
926
-- datatype and the function transforming the arguments to JSON.
927
-- We can't use anything less generic, because the way different
928
-- operations are serialized differs on both parameter- and top-level.
929
--
930
-- There are two things to be defined for each parameter:
931
--
932
-- * name
933
--
934
-- * type
935
--
936
genLuxiOp :: String -> [LuxiConstructor] -> Q [Dec]
937
genLuxiOp name cons = do
938
  let tname = mkName name
939
  decl_d <- mapM (\(cname, fields) -> do
940
                    -- we only need the type of the field, without Q
941
                    fields' <- mapM actualFieldType fields
942
                    let fields'' = zip (repeat NotStrict) fields'
943
                    return $ NormalC (mkName cname) fields'')
944
            cons
945
  let declD = DataD [] (mkName name) [] decl_d [''Show, ''Eq]
946
  save_decs <- genSaveOpCode tname "opToArgs" "opToDict"
947
               cons saveLuxiConstructor False
948
  req_defs <- declareSADT "LuxiReq" .
949
              map (\(str, _) -> ("Req" ++ str, mkName ("luxiReq" ++ str))) $
950
                  cons
951
  return $ declD:save_decs ++ req_defs
952

    
953
-- | Generates the \"save\" clause for entire LuxiOp constructor.
954
saveLuxiConstructor :: LuxiConstructor -> Q Clause
955
saveLuxiConstructor (sname, fields) = do
956
  let cname = mkName sname
957
  fnames <- mapM (newName . fieldVariable) fields
958
  let pat = conP cname (map varP fnames)
959
  let felems = zipWith saveObjectField fnames fields
960
      flist = [| concat $(listE felems) |]
961
  clause [pat] (normalB flist) []
962

    
963
-- * "Objects" functionality
964

    
965
-- | Extract the field's declaration from a Field structure.
966
fieldTypeInfo :: String -> Field -> Q (Name, Strict, Type)
967
fieldTypeInfo field_pfx fd = do
968
  t <- actualFieldType fd
969
  let n = mkName . (field_pfx ++) . fieldRecordName $ fd
970
  return (n, NotStrict, t)
971

    
972
-- | Build an object declaration.
973
buildObject :: String -> String -> [Field] -> Q [Dec]
974
buildObject sname field_pfx fields = do
975
  when (any ((==) AndRestArguments . fieldIsOptional)
976
         . drop 1 $ reverse fields)
977
    $ fail "Objects may have only one AndRestArguments field,\
978
           \ and it must be the last one."
979
  let name = mkName sname
980
  fields_d <- mapM (fieldTypeInfo field_pfx) fields
981
  let decl_d = RecC name fields_d
982
  let declD = DataD [] name [] [decl_d] [''Show, ''Eq]
983
  ser_decls <- buildObjectSerialisation sname fields
984
  return $ declD:ser_decls
985

    
986
-- | Generates an object definition: data type and its JSON instance.
987
buildObjectSerialisation :: String -> [Field] -> Q [Dec]
988
buildObjectSerialisation sname fields = do
989
  let name = mkName sname
990
  dictdecls <- genDictObject saveObjectField
991
                             (loadObjectField fields) sname fields
992
  savedecls <- genSaveObject sname
993
  (loadsig, loadfn) <- genLoadObject sname
994
  shjson <- objectShowJSON sname
995
  rdjson <- objectReadJSON sname
996
  let instdecl = InstanceD [] (AppT (ConT ''JSON.JSON) (ConT name))
997
                 [rdjson, shjson]
998
  return $ dictdecls ++ savedecls ++ [loadsig, loadfn, instdecl]
999

    
1000
-- | An internal name used for naming variables that hold the entire
1001
-- object of type @[(String,JSValue)]@.
1002
objVarName :: Name
1003
objVarName = mkName "_o"
1004

    
1005
-- | Provides a default 'toJSArray' for 'ArrayObject' instance using its
1006
-- existing 'DictObject' instance. The keys are serialized in the order
1007
-- they're declared. The list must contain all keys possibly generated by
1008
-- 'toDict'.
1009
defaultToJSArray :: (DictObject a) => [String] -> a -> [JSON.JSValue]
1010
defaultToJSArray keys o =
1011
  let m = M.fromList $ toDict o
1012
  in map (fromMaybe JSON.JSNull . flip M.lookup m) keys
1013

    
1014
-- | Provides a default 'fromJSArray' for 'ArrayObject' instance using its
1015
-- existing 'DictObject' instance. The fields are deserialized in the order
1016
-- they're declared.
1017
defaultFromJSArray :: (DictObject a)
1018
                   => [String] -> [JSON.JSValue] -> JSON.Result a
1019
defaultFromJSArray keys xs = do
1020
  let xslen = length xs
1021
      explen = length keys
1022
  unless (xslen == explen) (fail $ "Expected " ++ show explen
1023
                                   ++ " arguments, got " ++ show xslen)
1024
  fromDict $ zip keys xs
1025

    
1026
-- | Generates an additional 'ArrayObject' instance using its
1027
-- existing 'DictObject' instance.
1028
--
1029
-- See 'defaultToJSArray' and 'defaultFromJSArray'.
1030
genArrayObjectInstance :: Name -> [Field] -> Q Dec
1031
genArrayObjectInstance name fields = do
1032
  let fnames = concatMap (liftA2 (:) fieldName fieldExtraKeys) fields
1033
  instanceD (return []) (appT (conT ''ArrayObject) (conT name))
1034
    [ valD (varP 'toJSArray) (normalB [| defaultToJSArray $(lift fnames) |]) []
1035
    , valD (varP 'fromJSArray) (normalB [| defaultFromJSArray fnames |]) []
1036
    ]
1037

    
1038
-- | Generates 'DictObject' instance.
1039
genDictObject :: (Name -> Field -> Q Exp)  -- ^ a saving function
1040
              -> (Field -> Q Exp)          -- ^ a loading function
1041
              -> String                    -- ^ an object name
1042
              -> [Field]                   -- ^ a list of fields
1043
              -> Q [Dec]
1044
genDictObject save_fn load_fn sname fields = do
1045
  let name = mkName sname
1046
  -- toDict
1047
  fnames <- mapM (newName . fieldVariable) fields
1048
  let pat = conP name (map varP fnames)
1049
      tdexp = [| concat $(listE $ zipWith save_fn fnames fields) |]
1050
  tdclause <- clause [pat] (normalB tdexp) []
1051
  -- fromDict
1052
  fdexp <- loadConstructor name load_fn fields
1053
  let fdclause = Clause [VarP objVarName] (NormalB fdexp) []
1054
  -- the ArrayObject instance generated from DictObject
1055
  arrdec <- genArrayObjectInstance name fields
1056
  -- the final instance
1057
  return $ [InstanceD [] (AppT (ConT ''DictObject) (ConT name))
1058
             [ FunD 'toDict [tdclause]
1059
             , FunD 'fromDict [fdclause]
1060
             ]]
1061
         ++ [arrdec]
1062

    
1063
-- | Generates the save object functionality.
1064
genSaveObject :: String -> Q [Dec]
1065
genSaveObject sname = do
1066
  let fname = mkName ("save" ++ sname)
1067
  sigt <- [t| $(conT $ mkName sname) -> JSON.JSValue |]
1068
  cclause <- [| $makeObjE . $(varE $ 'toDict) |]
1069
  return [SigD fname sigt, ValD (VarP fname) (NormalB cclause) []]
1070

    
1071
-- | Generates the code for saving an object's field, handling the
1072
-- various types of fields that we have.
1073
saveObjectField :: Name -> Field -> Q Exp
1074
saveObjectField fvar field =
1075
  let formatFn = fromMaybe [| JSON.showJSON &&& (const []) |] $
1076
                           fieldShow field
1077
      formatCode v = [| let (actual, extra) = $formatFn $(v)
1078
                         in ($nameE, actual) : extra |]
1079
  in case fieldIsOptional field of
1080
    OptionalOmitNull ->       [| case $(fvarE) of
1081
                                   Nothing -> []
1082
                                   Just v  -> $(formatCode [| v |])
1083
                              |]
1084
    OptionalSerializeNull ->  [| case $(fvarE) of
1085
                                   Nothing -> [( $nameE, JSON.JSNull )]
1086
                                   Just v  -> $(formatCode [| v |])
1087
                              |]
1088
    NotOptional ->            formatCode fvarE
1089
    AndRestArguments -> [| M.toList $(varE fvar) |]
1090
  where nameE = stringE (fieldName field)
1091
        fvarE = varE fvar
1092

    
1093
-- | Generates the showJSON clause for a given object name.
1094
objectShowJSON :: String -> Q Dec
1095
objectShowJSON name = do
1096
  body <- [| JSON.showJSON . $(varE . mkName $ "save" ++ name) |]
1097
  return $ FunD 'JSON.showJSON [Clause [] (NormalB body) []]
1098

    
1099
-- | Generates the load object functionality.
1100
genLoadObject :: String -> Q (Dec, Dec)
1101
genLoadObject sname = do
1102
  let fname = mkName $ "load" ++ sname
1103
  sigt <- [t| JSON.JSValue -> JSON.Result $(conT $ mkName sname) |]
1104
  cclause <- [| fromDict <=< liftM JSON.fromJSObject . JSON.readJSON |]
1105
  return $ (SigD fname sigt,
1106
            FunD fname [Clause [] (NormalB cclause) []])
1107

    
1108
-- | Generates code for loading an object's field.
1109
loadObjectField :: [Field] -> Field -> Q Exp
1110
loadObjectField allFields field = do
1111
  let name = fieldVariable field
1112
      names = map fieldVariable allFields
1113
      otherNames = listE . map stringE $ names \\ [name]
1114
  -- these are used in all patterns below
1115
  let objvar = varE objVarName
1116
      objfield = stringE (fieldName field)
1117
  case (fieldDefault field, fieldIsOptional field) of
1118
            -- Only non-optional fields without defaults must have a value;
1119
            -- we treat both optional types the same, since
1120
            -- 'maybeFromObj' can deal with both missing and null values
1121
            -- appropriately (the same)
1122
            (Nothing, NotOptional) ->
1123
                  loadFn field [| fromObj $objvar $objfield |] objvar
1124
            -- AndRestArguments need not to be parsed at all,
1125
            -- they're just extracted from the list of other fields.
1126
            (Nothing, AndRestArguments) ->
1127
                  [| return . M.fromList
1128
                     $ filter (not . (`elem` $otherNames) . fst) $objvar |]
1129
            _ ->  loadFnOpt field [| maybeFromObj $objvar $objfield |] objvar
1130

    
1131
-- | Builds the readJSON instance for a given object name.
1132
objectReadJSON :: String -> Q Dec
1133
objectReadJSON name = do
1134
  let s = mkName "s"
1135
  body <- [| $(varE . mkName $ "load" ++ name) =<<
1136
             readJSONWithDesc $(stringE name) False $(varE s) |]
1137
  return $ FunD 'JSON.readJSON [Clause [VarP s] (NormalB body) []]
1138

    
1139
-- * Inheritable parameter tables implementation
1140

    
1141
-- | Compute parameter type names.
1142
paramTypeNames :: String -> (String, String)
1143
paramTypeNames root = ("Filled"  ++ root ++ "Params",
1144
                       "Partial" ++ root ++ "Params")
1145

    
1146
-- | Compute information about the type of a parameter field.
1147
paramFieldTypeInfo :: String -> Field -> Q (Name, Strict, Type)
1148
paramFieldTypeInfo field_pfx fd = do
1149
  t <- actualFieldType fd
1150
  let n = mkName . (++ "P") . (field_pfx ++) .
1151
          fieldRecordName $ fd
1152
  return (n, NotStrict, AppT (ConT ''Maybe) t)
1153

    
1154
-- | Build a parameter declaration.
1155
--
1156
-- This function builds two different data structures: a /filled/ one,
1157
-- in which all fields are required, and a /partial/ one, in which all
1158
-- fields are optional. Due to the current record syntax issues, the
1159
-- fields need to be named differrently for the two structures, so the
1160
-- partial ones get a /P/ suffix.
1161
buildParam :: String -> String -> [Field] -> Q [Dec]
1162
buildParam sname field_pfx fields = do
1163
  let (sname_f, sname_p) = paramTypeNames sname
1164
      name_f = mkName sname_f
1165
      name_p = mkName sname_p
1166
  fields_f <- mapM (fieldTypeInfo field_pfx) fields
1167
  fields_p <- mapM (paramFieldTypeInfo field_pfx) fields
1168
  let decl_f = RecC name_f fields_f
1169
      decl_p = RecC name_p fields_p
1170
  let declF = DataD [] name_f [] [decl_f] [''Show, ''Eq]
1171
      declP = DataD [] name_p [] [decl_p] [''Show, ''Eq]
1172
  ser_decls_f <- buildObjectSerialisation sname_f fields
1173
  ser_decls_p <- buildPParamSerialisation sname_p fields
1174
  fill_decls <- fillParam sname field_pfx fields
1175
  return $ [declF, declP] ++ ser_decls_f ++ ser_decls_p ++ fill_decls ++
1176
           buildParamAllFields sname fields
1177

    
1178
-- | Builds a list of all fields of a parameter.
1179
buildParamAllFields :: String -> [Field] -> [Dec]
1180
buildParamAllFields sname fields =
1181
  let vname = mkName ("all" ++ sname ++ "ParamFields")
1182
      sig = SigD vname (AppT ListT (ConT ''String))
1183
      val = ListE $ map (LitE . StringL . fieldName) fields
1184
  in [sig, ValD (VarP vname) (NormalB val) []]
1185

    
1186
-- | Generates the serialisation for a partial parameter.
1187
buildPParamSerialisation :: String -> [Field] -> Q [Dec]
1188
buildPParamSerialisation sname fields = do
1189
  let name = mkName sname
1190
  dictdecls <- genDictObject savePParamField loadPParamField sname fields
1191
  savedecls <- genSaveObject sname
1192
  (loadsig, loadfn) <- genLoadObject sname
1193
  shjson <- objectShowJSON sname
1194
  rdjson <- objectReadJSON sname
1195
  let instdecl = InstanceD [] (AppT (ConT ''JSON.JSON) (ConT name))
1196
                 [rdjson, shjson]
1197
  return $ dictdecls ++ savedecls ++ [loadsig, loadfn, instdecl]
1198

    
1199
-- | Generates code to save an optional parameter field.
1200
savePParamField :: Name -> Field -> Q Exp
1201
savePParamField fvar field = do
1202
  checkNonOptDef field
1203
  let actualVal = mkName "v"
1204
  normalexpr <- saveObjectField actualVal field
1205
  -- we have to construct the block here manually, because we can't
1206
  -- splice-in-splice
1207
  return $ CaseE (VarE fvar) [ Match (ConP 'Nothing [])
1208
                                       (NormalB (ConE '[])) []
1209
                             , Match (ConP 'Just [VarP actualVal])
1210
                                       (NormalB normalexpr) []
1211
                             ]
1212

    
1213
-- | Generates code to load an optional parameter field.
1214
loadPParamField :: Field -> Q Exp
1215
loadPParamField field = do
1216
  checkNonOptDef field
1217
  let name = fieldName field
1218
  -- these are used in all patterns below
1219
  let objvar = varE objVarName
1220
      objfield = stringE name
1221
      loadexp = [| $(varE 'maybeFromObj) $objvar $objfield |]
1222
  loadFnOpt field loadexp objvar
1223

    
1224
-- | Builds a simple declaration of type @n_x = fromMaybe f_x p_x@.
1225
buildFromMaybe :: String -> Q Dec
1226
buildFromMaybe fname =
1227
  valD (varP (mkName $ "n_" ++ fname))
1228
         (normalB [| $(varE 'fromMaybe)
1229
                        $(varNameE $ "f_" ++ fname)
1230
                        $(varNameE $ "p_" ++ fname) |]) []
1231

    
1232
-- | Builds a function that executes the filling of partial parameter
1233
-- from a full copy (similar to Python's fillDict).
1234
fillParam :: String -> String -> [Field] -> Q [Dec]
1235
fillParam sname field_pfx fields = do
1236
  let fnames = map (\fd -> field_pfx ++ fieldRecordName fd) fields
1237
      (sname_f, sname_p) = paramTypeNames sname
1238
      oname_f = "fobj"
1239
      oname_p = "pobj"
1240
      name_f = mkName sname_f
1241
      name_p = mkName sname_p
1242
      fun_name = mkName $ "fill" ++ sname ++ "Params"
1243
      le_full = ValD (ConP name_f (map (VarP . mkName . ("f_" ++)) fnames))
1244
                (NormalB . VarE . mkName $ oname_f) []
1245
      le_part = ValD (ConP name_p (map (VarP . mkName . ("p_" ++)) fnames))
1246
                (NormalB . VarE . mkName $ oname_p) []
1247
      obj_new = foldl (\accu vname -> AppE accu (VarE vname)) (ConE name_f)
1248
                $ map (mkName . ("n_" ++)) fnames
1249
  le_new <- mapM buildFromMaybe fnames
1250
  funt <- [t| $(conT name_f) -> $(conT name_p) -> $(conT name_f) |]
1251
  let sig = SigD fun_name funt
1252
      fclause = Clause [VarP (mkName oname_f), VarP (mkName oname_p)]
1253
                (NormalB $ LetE (le_full:le_part:le_new) obj_new) []
1254
      fun = FunD fun_name [fclause]
1255
  return [sig, fun]
1256

    
1257
-- * Template code for exceptions
1258

    
1259
-- | Exception simple error message field.
1260
excErrMsg :: (String, Q Type)
1261
excErrMsg = ("errMsg", [t| String |])
1262

    
1263
-- | Builds an exception type definition.
1264
genException :: String                  -- ^ Name of new type
1265
             -> SimpleObject -- ^ Constructor name and parameters
1266
             -> Q [Dec]
1267
genException name cons = do
1268
  let tname = mkName name
1269
  declD <- buildSimpleCons tname cons
1270
  (savesig, savefn) <- genSaveSimpleObj tname ("save" ++ name) cons $
1271
                         uncurry saveExcCons
1272
  (loadsig, loadfn) <- genLoadExc tname ("load" ++ name) cons
1273
  return [declD, loadsig, loadfn, savesig, savefn]
1274

    
1275
-- | Generates the \"save\" clause for an entire exception constructor.
1276
--
1277
-- This matches the exception with variables named the same as the
1278
-- constructor fields (just so that the spliced in code looks nicer),
1279
-- and calls showJSON on it.
1280
saveExcCons :: String        -- ^ The constructor name
1281
            -> [SimpleField] -- ^ The parameter definitions for this
1282
                             -- constructor
1283
            -> Q Clause      -- ^ Resulting clause
1284
saveExcCons sname fields = do
1285
  let cname = mkName sname
1286
  fnames <- mapM (newName . fst) fields
1287
  let pat = conP cname (map varP fnames)
1288
      felems = if null fnames
1289
                 then conE '() -- otherwise, empty list has no type
1290
                 else listE $ map (\f -> [| JSON.showJSON $(varE f) |]) fnames
1291
  let tup = tupE [ litE (stringL sname), felems ]
1292
  clause [pat] (normalB [| JSON.showJSON $tup |]) []
1293

    
1294
-- | Generates load code for a single constructor of an exception.
1295
--
1296
-- Generates the code (if there's only one argument, we will use a
1297
-- list, not a tuple:
1298
--
1299
-- @
1300
-- do
1301
--  (x1, x2, ...) <- readJSON args
1302
--  return $ Cons x1 x2 ...
1303
-- @
1304
loadExcConstructor :: Name -> String -> [SimpleField] -> Q Exp
1305
loadExcConstructor inname sname fields = do
1306
  let name = mkName sname
1307
  f_names <- mapM (newName . fst) fields
1308
  let read_args = AppE (VarE 'JSON.readJSON) (VarE inname)
1309
  let binds = case f_names of
1310
                [x] -> BindS (ListP [VarP x])
1311
                _   -> BindS (TupP (map VarP f_names))
1312
      cval = foldl (\accu fn -> AppE accu (VarE fn)) (ConE name) f_names
1313
  return $ DoE [binds read_args, NoBindS (AppE (VarE 'return) cval)]
1314

    
1315
{-| Generates the loadException function.
1316

    
1317
This generates a quite complicated function, along the lines of:
1318

    
1319
@
1320
loadFn (JSArray [JSString name, args]) = case name of
1321
   "A1" -> do
1322
     (x1, x2, ...) <- readJSON args
1323
     return $ A1 x1 x2 ...
1324
   "a2" -> ...
1325
   s -> fail $ "Unknown exception" ++ s
1326
loadFn v = fail $ "Expected array but got " ++ show v
1327
@
1328
-}
1329
genLoadExc :: Name -> String -> SimpleObject -> Q (Dec, Dec)
1330
genLoadExc tname sname opdefs = do
1331
  let fname = mkName sname
1332
  exc_name <- newName "name"
1333
  exc_args <- newName "args"
1334
  exc_else <- newName "s"
1335
  arg_else <- newName "v"
1336
  fails <- [| fail $ "Unknown exception '" ++ $(varE exc_else) ++ "'" |]
1337
  -- default match for unknown exception name
1338
  let defmatch = Match (VarP exc_else) (NormalB fails) []
1339
  -- the match results (per-constructor blocks)
1340
  str_matches <-
1341
    mapM (\(s, params) -> do
1342
            body_exp <- loadExcConstructor exc_args s params
1343
            return $ Match (LitP (StringL s)) (NormalB body_exp) [])
1344
    opdefs
1345
  -- the first function clause; we can't use [| |] due to TH
1346
  -- limitations, so we have to build the AST by hand
1347
  let clause1 = Clause [ConP 'JSON.JSArray
1348
                               [ListP [ConP 'JSON.JSString [VarP exc_name],
1349
                                            VarP exc_args]]]
1350
                (NormalB (CaseE (AppE (VarE 'JSON.fromJSString)
1351
                                        (VarE exc_name))
1352
                          (str_matches ++ [defmatch]))) []
1353
  -- the fail expression for the second function clause
1354
  fail_type <- [| fail $ "Invalid exception: expected '(string, [args])' " ++
1355
                  "      but got " ++ show (pp_value $(varE arg_else)) ++ "'"
1356
                |]
1357
  -- the second function clause
1358
  let clause2 = Clause [VarP arg_else] (NormalB fail_type) []
1359
  sigt <- [t| JSON.JSValue -> JSON.Result $(conT tname) |]
1360
  return $ (SigD fname sigt, FunD fname [clause1, clause2])