Statistics
| Branch: | Tag: | Revision:

root / src / Ganeti / THH.hs @ 948f6540

History | View | Annotate | Download (53.4 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
                  , DictObject(..)
70
                  , ArrayObject(..)
71
                  , genException
72
                  , excErrMsg
73
                  ) where
74

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

    
90
import qualified Text.JSON as JSON
91
import Text.JSON.Pretty (pp_value)
92

    
93
import Ganeti.JSON
94
import Ganeti.PyValue
95
import Ganeti.THH.PyType
96

    
97

    
98
-- * Exported types
99

    
100
-- | Class of objects that can be converted from and to 'JSObject'
101
-- lists-format.
102
class DictObject a where
103
  toDict :: a -> [(String, JSON.JSValue)]
104
  fromDict :: [(String, JSON.JSValue)] -> JSON.Result a
105

    
106
-- | Class of objects that can be converted from and to @[JSValue]@ with
107
-- a fixed length and order.
108
class ArrayObject a where
109
  toJSArray :: a -> [JSON.JSValue]
110
  fromJSArray :: [JSON.JSValue] -> JSON.Result a
111

    
112
-- | Optional field information.
113
data OptionalType
114
  = NotOptional           -- ^ Field is not optional
115
  | OptionalOmitNull      -- ^ Field is optional, null is not serialised
116
  | OptionalSerializeNull -- ^ Field is optional, null is serialised
117
  | AndRestArguments      -- ^ Special field capturing all the remaining fields
118
                          -- as plain JSON values
119
  deriving (Show, Eq)
120

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

    
195
-- | Generates a simple field.
196
simpleField :: String -> Q Type -> Field
197
simpleField fname ftype =
198
  Field { fieldName        = fname
199
        , fieldType        = ftype
200
        , fieldRead        = Nothing
201
        , fieldShow        = Nothing
202
        , fieldExtraKeys   = []
203
        , fieldDefault     = Nothing
204
        , fieldConstr      = Nothing
205
        , fieldIsOptional  = NotOptional
206
        , fieldDoc         = ""
207
        }
208

    
209
-- | Generate an AndRestArguments catch-all field.
210
andRestArguments :: String -> Field
211
andRestArguments fname =
212
  Field { fieldName        = fname
213
        , fieldType        = [t| M.Map String JSON.JSValue |]
214
        , fieldRead        = Nothing
215
        , fieldShow        = Nothing
216
        , fieldExtraKeys   = []
217
        , fieldDefault     = Nothing
218
        , fieldConstr      = Nothing
219
        , fieldIsOptional  = AndRestArguments
220
        , fieldDoc         = ""
221
        }
222

    
223
withDoc :: String -> Field -> Field
224
withDoc doc field =
225
  field { fieldDoc = doc }
226

    
227
-- | Sets the renamed constructor field.
228
renameField :: String -> Field -> Field
229
renameField constrName field = field { fieldConstr = Just constrName }
230

    
231
-- | Sets the default value on a field (makes it optional with a
232
-- default value).
233
defaultField :: Q Exp -> Field -> Field
234
defaultField defval field = field { fieldDefault = Just defval }
235

    
236
-- | Marks a field optional (turning its base type into a Maybe).
237
optionalField :: Field -> Field
238
optionalField field = field { fieldIsOptional = OptionalOmitNull }
239

    
240
-- | Marks a field optional (turning its base type into a Maybe), but
241
-- with 'Nothing' serialised explicitly as /null/.
242
optionalNullSerField :: Field -> Field
243
optionalNullSerField field = field { fieldIsOptional = OptionalSerializeNull }
244

    
245
-- | Wrapper around a special parse function, suitable as field-parsing
246
-- function.
247
numericalReadFn :: JSON.JSON a => (String -> JSON.Result a)
248
                   -> [(String, JSON.JSValue)] -> JSON.JSValue -> JSON.Result a
249
numericalReadFn _ _ v@(JSON.JSRational _ _) = JSON.readJSON v
250
numericalReadFn f _ (JSON.JSString x) = f $ JSON.fromJSString x
251
numericalReadFn _ _ _ = JSON.Error "A numerical field has to be a number or\
252
                                   \ a string."
253

    
254
-- | Sets the read function to also accept string parsable by the given
255
-- function.
256
specialNumericalField :: Name -> Field -> Field
257
specialNumericalField f field =
258
     field { fieldRead = Just (appE (varE 'numericalReadFn) (varE f)) }
259

    
260
-- | Creates a new mandatory field that reads time as the (floating point)
261
-- number of seconds since the standard UNIX epoch, and represents it in
262
-- Haskell as 'ClockTime'.
263
timeAsDoubleField :: String -> Field
264
timeAsDoubleField fname =
265
  (simpleField fname [t| ClockTime |])
266
    { fieldRead = Just $ [| \_ -> liftM unTimeAsDoubleJSON . JSON.readJSON |]
267
    , fieldShow = Just $ [| \c -> (JSON.showJSON $ TimeAsDoubleJSON c, []) |]
268
    }
269

    
270
-- | Sets custom functions on a field.
271
customField :: Name      -- ^ The name of the read function
272
            -> Name      -- ^ The name of the show function
273
            -> [String]  -- ^ The name of extra field keys
274
            -> Field     -- ^ The original field
275
            -> Field     -- ^ Updated field
276
customField readfn showfn extra field =
277
  field { fieldRead = Just (varE readfn), fieldShow = Just (varE showfn)
278
        , fieldExtraKeys = extra }
279

    
280
-- | Computes the record name for a given field, based on either the
281
-- string value in the JSON serialisation or the custom named if any
282
-- exists.
283
fieldRecordName :: Field -> String
284
fieldRecordName (Field { fieldName = name, fieldConstr = alias }) =
285
  fromMaybe (camelCase name) alias
286

    
287
-- | Computes the preferred variable name to use for the value of this
288
-- field. If the field has a specific constructor name, then we use a
289
-- first-letter-lowercased version of that; otherwise, we simply use
290
-- the field name. See also 'fieldRecordName'.
291
fieldVariable :: Field -> String
292
fieldVariable f =
293
  case (fieldConstr f) of
294
    Just name -> ensureLower name
295
    _ -> map (\c -> if c == '-' then '_' else c) $ fieldName f
296

    
297
-- | Compute the actual field type (taking into account possible
298
-- optional status).
299
actualFieldType :: Field -> Q Type
300
actualFieldType f | fieldIsOptional f `elem` [NotOptional, AndRestArguments] = t
301
                  | otherwise =  [t| Maybe $t |]
302
                  where t = fieldType f
303

    
304
-- | Checks that a given field is not optional (for object types or
305
-- fields which should not allow this case).
306
checkNonOptDef :: (Monad m) => Field -> m ()
307
checkNonOptDef (Field { fieldIsOptional = OptionalOmitNull
308
                      , fieldName = name }) =
309
  fail $ "Optional field " ++ name ++ " used in parameter declaration"
310
checkNonOptDef (Field { fieldIsOptional = OptionalSerializeNull
311
                      , fieldName = name }) =
312
  fail $ "Optional field " ++ name ++ " used in parameter declaration"
313
checkNonOptDef (Field { fieldDefault = (Just _), fieldName = name }) =
314
  fail $ "Default field " ++ name ++ " used in parameter declaration"
315
checkNonOptDef _ = return ()
316

    
317
-- | Construct a function that parses a field value. If the field has
318
-- a custom 'fieldRead', it's applied to @o@ and used. Otherwise
319
-- @JSON.readJSON@ is used.
320
parseFn :: Field   -- ^ The field definition
321
        -> Q Exp   -- ^ The entire object in JSON object format
322
        -> Q Exp   -- ^ The resulting function that parses a JSON message
323
parseFn field o
324
  = maybe [| readJSONWithDesc $(stringE $ fieldName field) False |]
325
          (`appE` o) (fieldRead field)
326

    
327
-- | Produces the expression that will de-serialise a given
328
-- field. Since some custom parsing functions might need to use the
329
-- entire object, we do take and pass the object to any custom read
330
-- functions.
331
loadFn :: Field   -- ^ The field definition
332
       -> Q Exp   -- ^ The value of the field as existing in the JSON message
333
       -> Q Exp   -- ^ The entire object in JSON object format
334
       -> Q Exp   -- ^ Resulting expression
335
loadFn field expr o = [| $expr >>= $(parseFn field o) |]
336

    
337
-- | Just as 'loadFn', but for optional fields.
338
loadFnOpt :: Field   -- ^ The field definition
339
          -> Q Exp   -- ^ The value of the field as existing in the JSON message
340
                     -- as Maybe
341
          -> Q Exp   -- ^ The entire object in JSON object format
342
          -> Q Exp   -- ^ Resulting expression
343
loadFnOpt field@(Field { fieldDefault = Just def }) expr o
344
  = case fieldIsOptional field of
345
      NotOptional -> [| $expr >>= maybe (return $def) $(parseFn field o) |]
346
      _           -> fail $ "Field " ++ fieldName field ++ ":\
347
                            \ A field can't be optional and\
348
                            \ have a default value at the same time."
349
loadFnOpt field expr o
350
  = [| $expr >>= maybe (return Nothing) (liftM Just . $(parseFn field o)) |]
351

    
352
-- * Common field declarations
353

    
354
-- | Timestamp fields description.
355
timeStampFields :: [Field]
356
timeStampFields = map (defaultField [| TOD 0 0 |] . timeAsDoubleField)
357
                      ["ctime", "mtime"]
358

    
359

    
360
-- | Serial number fields description.
361
serialFields :: [Field]
362
serialFields =
363
    [ renameField  "Serial" $ simpleField "serial_no" [t| Int |] ]
364

    
365
-- | UUID fields description.
366
uuidFields :: [Field]
367
uuidFields = [ simpleField "uuid" [t| String |] ]
368

    
369
-- | Tag set type alias.
370
type TagSet = Set.Set String
371

    
372
-- | Tag field description.
373
tagsFields :: [Field]
374
tagsFields = [ defaultField [| Set.empty |] $
375
               simpleField "tags" [t| TagSet |] ]
376

    
377
-- * Internal types
378

    
379
-- | A simple field, in constrast to the customisable 'Field' type.
380
type SimpleField = (String, Q Type)
381

    
382
-- | A definition for a single constructor for a simple object.
383
type SimpleConstructor = (String, [SimpleField])
384

    
385
-- | A definition for ADTs with simple fields.
386
type SimpleObject = [SimpleConstructor]
387

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

    
391
-- | A type alias for a Luxi constructor of a regular object.
392
type LuxiConstructor = (String, [Field])
393

    
394
-- * Helper functions
395

    
396
-- | Ensure first letter is lowercase.
397
--
398
-- Used to convert type name to function prefix, e.g. in @data Aa ->
399
-- aaToRaw@.
400
ensureLower :: String -> String
401
ensureLower [] = []
402
ensureLower (x:xs) = toLower x:xs
403

    
404
-- | Ensure first letter is uppercase.
405
--
406
-- Used to convert constructor name to component
407
ensureUpper :: String -> String
408
ensureUpper [] = []
409
ensureUpper (x:xs) = toUpper x:xs
410

    
411
-- | Helper for quoted expressions.
412
varNameE :: String -> Q Exp
413
varNameE = varE . mkName
414

    
415
-- | showJSON as an expression, for reuse.
416
showJSONE :: Q Exp
417
showJSONE = varE 'JSON.showJSON
418

    
419
-- | makeObj as an expression, for reuse.
420
makeObjE :: Q Exp
421
makeObjE = varE 'JSON.makeObj
422

    
423
-- | fromObj (Ganeti specific) as an expression, for reuse.
424
fromObjE :: Q Exp
425
fromObjE = varE 'fromObj
426

    
427
-- | ToRaw function name.
428
toRawName :: String -> Name
429
toRawName = mkName . (++ "ToRaw") . ensureLower
430

    
431
-- | FromRaw function name.
432
fromRawName :: String -> Name
433
fromRawName = mkName . (++ "FromRaw") . ensureLower
434

    
435
-- | Converts a name to it's varE\/litE representations.
436
reprE :: Either String Name -> Q Exp
437
reprE = either stringE varE
438

    
439
-- | Smarter function application.
440
--
441
-- This does simply f x, except that if is 'id', it will skip it, in
442
-- order to generate more readable code when using -ddump-splices.
443
appFn :: Exp -> Exp -> Exp
444
appFn f x | f == VarE 'id = x
445
          | otherwise = AppE f x
446

    
447
-- | Builds a field for a normal constructor.
448
buildConsField :: Q Type -> StrictTypeQ
449
buildConsField ftype = do
450
  ftype' <- ftype
451
  return (NotStrict, ftype')
452

    
453
-- | Builds a constructor based on a simple definition (not field-based).
454
buildSimpleCons :: Name -> SimpleObject -> Q Dec
455
buildSimpleCons tname cons = do
456
  decl_d <- mapM (\(cname, fields) -> do
457
                    fields' <- mapM (buildConsField . snd) fields
458
                    return $ NormalC (mkName cname) fields') cons
459
  return $ DataD [] tname [] decl_d [''Show, ''Eq]
460

    
461
-- | Generate the save function for a given type.
462
genSaveSimpleObj :: Name                            -- ^ Object type
463
                 -> String                          -- ^ Function name
464
                 -> SimpleObject                    -- ^ Object definition
465
                 -> (SimpleConstructor -> Q Clause) -- ^ Constructor save fn
466
                 -> Q (Dec, Dec)
467
genSaveSimpleObj tname sname opdefs fn = do
468
  let sigt = AppT (AppT ArrowT (ConT tname)) (ConT ''JSON.JSValue)
469
      fname = mkName sname
470
  cclauses <- mapM fn opdefs
471
  return $ (SigD fname sigt, FunD fname cclauses)
472

    
473
-- * Template code for simple raw type-equivalent ADTs
474

    
475
-- | Generates a data type declaration.
476
--
477
-- The type will have a fixed list of instances.
478
strADTDecl :: Name -> [String] -> Dec
479
strADTDecl name constructors =
480
  DataD [] name []
481
          (map (flip NormalC [] . mkName) constructors)
482
          [''Show, ''Eq, ''Enum, ''Bounded, ''Ord]
483

    
484
-- | Generates a toRaw function.
485
--
486
-- This generates a simple function of the form:
487
--
488
-- @
489
-- nameToRaw :: Name -> /traw/
490
-- nameToRaw Cons1 = var1
491
-- nameToRaw Cons2 = \"value2\"
492
-- @
493
genToRaw :: Name -> Name -> Name -> [(String, Either String Name)] -> Q [Dec]
494
genToRaw traw fname tname constructors = do
495
  let sigt = AppT (AppT ArrowT (ConT tname)) (ConT traw)
496
  -- the body clauses, matching on the constructor and returning the
497
  -- raw value
498
  clauses <- mapM  (\(c, v) -> clause [recP (mkName c) []]
499
                             (normalB (reprE v)) []) constructors
500
  return [SigD fname sigt, FunD fname clauses]
501

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

    
536
-- | Generates a data type from a given raw format.
537
--
538
-- The format is expected to multiline. The first line contains the
539
-- type name, and the rest of the lines must contain two words: the
540
-- constructor name and then the string representation of the
541
-- respective constructor.
542
--
543
-- The function will generate the data type declaration, and then two
544
-- functions:
545
--
546
-- * /name/ToRaw, which converts the type to a raw type
547
--
548
-- * /name/FromRaw, which (monadically) converts from a raw type to the type
549
--
550
-- Note that this is basically just a custom show\/read instance,
551
-- nothing else.
552
declareADT
553
  :: (a -> Either String Name) -> Name -> String -> [(String, a)] -> Q [Dec]
554
declareADT fn traw sname cons = do
555
  let name = mkName sname
556
      ddecl = strADTDecl name (map fst cons)
557
      -- process cons in the format expected by genToRaw
558
      cons' = map (\(a, b) -> (a, fn b)) cons
559
  toraw <- genToRaw traw (toRawName sname) name cons'
560
  fromraw <- genFromRaw traw (fromRawName sname) name cons'
561
  return $ ddecl:toraw ++ fromraw
562

    
563
declareLADT :: Name -> String -> [(String, String)] -> Q [Dec]
564
declareLADT = declareADT Left
565

    
566
declareILADT :: String -> [(String, Int)] -> Q [Dec]
567
declareILADT sname cons = do
568
  consNames <- sequence [ newName ('_':n) | (n, _) <- cons ]
569
  consFns <- concat <$> sequence
570
             [ do sig <- sigD n [t| Int |]
571
                  let expr = litE (IntegerL (toInteger i))
572
                  fn <- funD n [clause [] (normalB expr) []]
573
                  return [sig, fn]
574
             | n <- consNames
575
             | (_, i) <- cons ]
576
  let cons' = [ (n, n') | (n, _) <- cons | n' <- consNames ]
577
  (consFns ++) <$> declareADT Right ''Int sname cons'
578

    
579
declareIADT :: String -> [(String, Name)] -> Q [Dec]
580
declareIADT = declareADT Right ''Int
581

    
582
declareSADT :: String -> [(String, Name)] -> Q [Dec]
583
declareSADT = declareADT Right ''String
584

    
585
-- | Creates the showJSON member of a JSON instance declaration.
586
--
587
-- This will create what is the equivalent of:
588
--
589
-- @
590
-- showJSON = showJSON . /name/ToRaw
591
-- @
592
--
593
-- in an instance JSON /name/ declaration
594
genShowJSON :: String -> Q Dec
595
genShowJSON name = do
596
  body <- [| JSON.showJSON . $(varE (toRawName name)) |]
597
  return $ FunD 'JSON.showJSON [Clause [] (NormalB body) []]
598

    
599
-- | Creates the readJSON member of a JSON instance declaration.
600
--
601
-- This will create what is the equivalent of:
602
--
603
-- @
604
-- readJSON s = case readJSON s of
605
--                Ok s' -> /name/FromRaw s'
606
--                Error e -> Error /description/
607
-- @
608
--
609
-- in an instance JSON /name/ declaration
610
genReadJSON :: String -> Q Dec
611
genReadJSON name = do
612
  let s = mkName "s"
613
  body <- [| $(varE (fromRawName name)) =<<
614
             readJSONWithDesc $(stringE name) True $(varE s) |]
615
  return $ FunD 'JSON.readJSON [Clause [VarP s] (NormalB body) []]
616

    
617
-- | Generates a JSON instance for a given type.
618
--
619
-- This assumes that the /name/ToRaw and /name/FromRaw functions
620
-- have been defined as by the 'declareSADT' function.
621
makeJSONInstance :: Name -> Q [Dec]
622
makeJSONInstance name = do
623
  let base = nameBase name
624
  showJ <- genShowJSON base
625
  readJ <- genReadJSON base
626
  return [InstanceD [] (AppT (ConT ''JSON.JSON) (ConT name)) [readJ,showJ]]
627

    
628
-- * Template code for opcodes
629

    
630
-- | Transforms a CamelCase string into an_underscore_based_one.
631
deCamelCase :: String -> String
632
deCamelCase =
633
    intercalate "_" . map (map toUpper) . groupBy (\_ b -> not $ isUpper b)
634

    
635
-- | Transform an underscore_name into a CamelCase one.
636
camelCase :: String -> String
637
camelCase = concatMap (ensureUpper . drop 1) .
638
            groupBy (\_ b -> b /= '_' && b /= '-') . ('_':)
639

    
640
-- | Computes the name of a given constructor.
641
constructorName :: Con -> Q Name
642
constructorName (NormalC name _) = return name
643
constructorName (RecC name _)    = return name
644
constructorName x                = fail $ "Unhandled constructor " ++ show x
645

    
646
-- | Extract all constructor names from a given type.
647
reifyConsNames :: Name -> Q [String]
648
reifyConsNames name = do
649
  reify_result <- reify name
650
  case reify_result of
651
    TyConI (DataD _ _ _ cons _) -> mapM (liftM nameBase . constructorName) cons
652
    o -> fail $ "Unhandled name passed to reifyConsNames, expected\
653
                \ type constructor but got '" ++ show o ++ "'"
654

    
655
-- | Builds the generic constructor-to-string function.
656
--
657
-- This generates a simple function of the following form:
658
--
659
-- @
660
-- fname (ConStructorOne {}) = trans_fun("ConStructorOne")
661
-- fname (ConStructorTwo {}) = trans_fun("ConStructorTwo")
662
-- @
663
--
664
-- This builds a custom list of name\/string pairs and then uses
665
-- 'genToRaw' to actually generate the function.
666
genConstrToStr :: (String -> Q String) -> Name -> String -> Q [Dec]
667
genConstrToStr trans_fun name fname = do
668
  cnames <- reifyConsNames name
669
  svalues <- mapM (liftM Left . trans_fun) cnames
670
  genToRaw ''String (mkName fname) name $ zip cnames svalues
671

    
672
-- | Constructor-to-string for OpCode.
673
genOpID :: Name -> String -> Q [Dec]
674
genOpID = genConstrToStr (return . deCamelCase)
675

    
676
-- | Strips @Op@ from the constructor name, converts to lower-case
677
-- and adds a given prefix.
678
genOpLowerStrip :: String -> Name -> String -> Q [Dec]
679
genOpLowerStrip prefix =
680
    genConstrToStr (liftM ((prefix ++) . map toLower . deCamelCase)
681
                    . stripPrefixM "Op")
682
  where
683
    stripPrefixM :: String -> String -> Q String
684
    stripPrefixM pfx s = maybe (fail $ s ++ " doesn't start with " ++ pfx)
685
                               return
686
                         $ stripPrefix pfx s
687

    
688
-- | Builds a list with all defined constructor names for a type.
689
--
690
-- @
691
-- vstr :: String
692
-- vstr = [...]
693
-- @
694
--
695
-- Where the actual values of the string are the constructor names
696
-- mapped via @trans_fun@.
697
genAllConstr :: (String -> String) -> Name -> String -> Q [Dec]
698
genAllConstr trans_fun name vstr = do
699
  cnames <- reifyConsNames name
700
  let svalues = sort $ map trans_fun cnames
701
      vname = mkName vstr
702
      sig = SigD vname (AppT ListT (ConT ''String))
703
      body = NormalB (ListE (map (LitE . StringL) svalues))
704
  return $ [sig, ValD (VarP vname) body []]
705

    
706
-- | Generates a list of all defined opcode IDs.
707
genAllOpIDs :: Name -> String -> Q [Dec]
708
genAllOpIDs = genAllConstr deCamelCase
709

    
710
-- | OpCode parameter (field) type.
711
type OpParam = (String, Q Type, Q Exp)
712

    
713
-- * Python code generation
714

    
715
data OpCodeField = OpCodeField { ocfName :: String
716
                               , ocfType :: PyType
717
                               , ocfDefl :: Maybe PyValueEx
718
                               , ocfDoc  :: String
719
                               }
720

    
721
-- | Transfers opcode data between the opcode description (through
722
-- @genOpCode@) and the Python code generation functions.
723
data OpCodeDescriptor = OpCodeDescriptor { ocdName   :: String
724
                                         , ocdType   :: PyType
725
                                         , ocdDoc    :: String
726
                                         , ocdFields :: [OpCodeField]
727
                                         , ocdDescr  :: String
728
                                         }
729

    
730
-- | Optionally encapsulates default values in @PyValueEx@.
731
--
732
-- @maybeApp exp typ@ returns a quoted expression that encapsulates
733
-- the default value @exp@ of an opcode parameter cast to @typ@ in a
734
-- @PyValueEx@, if @exp@ is @Just@.  Otherwise, it returns a quoted
735
-- expression with @Nothing@.
736
maybeApp :: Maybe (Q Exp) -> Q Type -> Q Exp
737
maybeApp Nothing _ =
738
  [| Nothing |]
739

    
740
maybeApp (Just expr) typ =
741
  [| Just ($(conE (mkName "PyValueEx")) ($expr :: $typ)) |]
742

    
743
-- | Generates a Python type according to whether the field is
744
-- optional.
745
--
746
-- The type of created expression is PyType.
747
genPyType' :: OptionalType -> Q Type -> Q PyType
748
genPyType' opt typ = typ >>= pyOptionalType (opt /= NotOptional)
749

    
750
-- | Generates Python types from opcode parameters.
751
genPyType :: Field -> Q PyType
752
genPyType f = genPyType' (fieldIsOptional f) (fieldType f)
753

    
754
-- | Generates Python default values from opcode parameters.
755
genPyDefault :: Field -> Q Exp
756
genPyDefault f = maybeApp (fieldDefault f) (fieldType f)
757

    
758
pyField :: Field -> Q Exp
759
pyField f = genPyType f >>= \t ->
760
            [| OpCodeField $(stringE (fieldName f))
761
                           t
762
                           $(genPyDefault f)
763
                           $(stringE (fieldDoc f)) |]
764

    
765
-- | Generates a Haskell function call to "showPyClass" with the
766
-- necessary information on how to build the Python class string.
767
pyClass :: OpCodeConstructor -> Q Exp
768
pyClass (consName, consType, consDoc, consFields, consDscField) =
769
  do let pyClassVar = varNameE "showPyClass"
770
         consName' = stringE consName
771
     consType' <- genPyType' NotOptional consType
772
     let consDoc' = stringE consDoc
773
     [| OpCodeDescriptor $consName'
774
                         consType'
775
                         $consDoc'
776
                         $(listE $ map pyField consFields)
777
                         consDscField |]
778

    
779
-- | Generates a function called "pyClasses" that holds the list of
780
-- all the opcode descriptors necessary for generating the Python
781
-- opcodes.
782
pyClasses :: [OpCodeConstructor] -> Q [Dec]
783
pyClasses cons =
784
  do let name = mkName "pyClasses"
785
         sig = SigD name (AppT ListT (ConT ''OpCodeDescriptor))
786
     fn <- FunD name <$> (:[]) <$> declClause cons
787
     return [sig, fn]
788
  where declClause c =
789
          clause [] (normalB (ListE <$> mapM pyClass c)) []
790

    
791
-- | Converts from an opcode constructor to a Luxi constructor.
792
opcodeConsToLuxiCons :: (a, b, c, d, e) -> (a, d)
793
opcodeConsToLuxiCons (x, _, _, y, _) = (x, y)
794

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

    
819
-- | Generates the function pattern returning the list of fields for a
820
-- given constructor.
821
genOpConsFields :: OpCodeConstructor -> Clause
822
genOpConsFields (cname, _, _, fields, _) =
823
  let op_id = deCamelCase cname
824
      fvals = map (LitE . StringL) . sort . nub $
825
              concatMap (\f -> fieldName f:fieldExtraKeys f) fields
826
  in Clause [LitP (StringL op_id)] (NormalB $ ListE fvals) []
827

    
828
-- | Generates a list of all fields of an opcode constructor.
829
genAllOpFields  :: String              -- ^ Function name
830
                -> [OpCodeConstructor] -- ^ Object definition
831
                -> (Dec, Dec)
832
genAllOpFields sname opdefs =
833
  let cclauses = map genOpConsFields opdefs
834
      other = Clause [WildP] (NormalB (ListE [])) []
835
      fname = mkName sname
836
      sigt = AppT  (AppT ArrowT (ConT ''String)) (AppT ListT (ConT ''String))
837
  in (SigD fname sigt, FunD fname (cclauses++[other]))
838

    
839
-- | Generates the \"save\" clause for an entire opcode constructor.
840
--
841
-- This matches the opcode with variables named the same as the
842
-- constructor fields (just so that the spliced in code looks nicer),
843
-- and passes those name plus the parameter definition to 'saveObjectField'.
844
saveConstructor :: LuxiConstructor -- ^ The constructor
845
                -> Q Clause        -- ^ Resulting clause
846
saveConstructor (sname, fields) = do
847
  let cname = mkName sname
848
  fnames <- mapM (newName . fieldVariable) fields
849
  let pat = conP cname (map varP fnames)
850
  let felems = zipWith saveObjectField fnames fields
851
      -- now build the OP_ID serialisation
852
      opid = [| [( $(stringE "OP_ID"),
853
                   JSON.showJSON $(stringE . deCamelCase $ sname) )] |]
854
      flist = listE (opid:felems)
855
      -- and finally convert all this to a json object
856
      flist' = [| concat $flist |]
857
  clause [pat] (normalB flist') []
858

    
859
-- | Generates the main save opcode function.
860
--
861
-- This builds a per-constructor match clause that contains the
862
-- respective constructor-serialisation code.
863
genSaveOpCode :: Name                          -- ^ Object ype
864
              -> String                        -- ^ To 'JSValue' function name
865
              -> String                        -- ^ To 'JSObject' function name
866
              -> [LuxiConstructor]             -- ^ Object definition
867
              -> (LuxiConstructor -> Q Clause) -- ^ Constructor save fn
868
              -> Bool                          -- ^ Whether to generate
869
                                               -- obj or just a
870
                                               -- list\/tuple of values
871
              -> Q [Dec]
872
genSaveOpCode tname jvalstr tdstr opdefs fn gen_object = do
873
  tdclauses <- mapM fn opdefs
874
  let typecon = ConT tname
875
      jvalname = mkName jvalstr
876
      jvalsig = AppT  (AppT ArrowT typecon) (ConT ''JSON.JSValue)
877
      tdname = mkName tdstr
878
  tdsig <- [t| $(return typecon) -> [(String, JSON.JSValue)] |]
879
  jvalclause <- if gen_object
880
                  then [| $makeObjE . $(varE tdname) |]
881
                  else [| JSON.showJSON . map snd . $(varE tdname) |]
882
  return [ SigD tdname tdsig
883
         , FunD tdname tdclauses
884
         , SigD jvalname jvalsig
885
         , ValD (VarP jvalname) (NormalB jvalclause) []]
886

    
887
-- | Generates load code for a single constructor of the opcode data type.
888
loadConstructor :: Name -> (Field -> Q Exp) -> [Field] -> Q Exp
889
loadConstructor name loadfn fields = do
890
  fnames <- mapM (newName . ("r_" ++) . fieldName) fields
891
  fexps <- mapM loadfn fields
892
  let fstmts = zipWith (BindS . VarP) fnames fexps
893
      cexp = foldl (\accu fn -> AppE accu (VarE fn)) (ConE name) fnames
894
      retstmt = [NoBindS (AppE (VarE 'return) cexp)]
895
      -- FIXME: should we require an empty dict for an empty type?
896
      -- this allows any JSValue right now
897
  return $ DoE (fstmts ++ retstmt)
898

    
899
-- | Generates load code for a single constructor of the opcode data type.
900
loadOpConstructor :: OpCodeConstructor -> Q Exp
901
loadOpConstructor (sname, _, _, fields, _) =
902
  loadConstructor (mkName sname) (loadObjectField fields) fields
903

    
904
-- | Generates the loadOpCode function.
905
genLoadOpCode :: [OpCodeConstructor] -> Q (Dec, Dec)
906
genLoadOpCode opdefs = do
907
  let fname = mkName "loadOpCode"
908
      arg1 = mkName "v"
909
      objname = objVarName
910
      opid = mkName "op_id"
911
  st1 <- bindS (varP objname) [| liftM JSON.fromJSObject
912
                                 (JSON.readJSON $(varE arg1)) |]
913
  st2 <- bindS (varP opid) [| $fromObjE $(varE objname) $(stringE "OP_ID") |]
914
  -- the match results (per-constructor blocks)
915
  mexps <- mapM loadOpConstructor opdefs
916
  fails <- [| fail $ "Unknown opcode " ++ $(varE opid) |]
917
  let mpats = map (\(me, (consName, _, _, _, _)) ->
918
                       let mp = LitP . StringL . deCamelCase $ consName
919
                       in Match mp (NormalB me) []
920
                  ) $ zip mexps opdefs
921
      defmatch = Match WildP (NormalB fails) []
922
      cst = NoBindS $ CaseE (VarE opid) $ mpats++[defmatch]
923
      body = DoE [st1, st2, cst]
924
  sigt <- [t| JSON.JSValue -> JSON.Result $(conT (mkName "OpCode")) |]
925
  return $ (SigD fname sigt, FunD fname [Clause [VarP arg1] (NormalB body) []])
926

    
927
-- * Template code for luxi
928

    
929
-- | Constructor-to-string for LuxiOp.
930
genStrOfOp :: Name -> String -> Q [Dec]
931
genStrOfOp = genConstrToStr return
932

    
933
-- | Constructor-to-string for MsgKeys.
934
genStrOfKey :: Name -> String -> Q [Dec]
935
genStrOfKey = genConstrToStr (return . ensureLower)
936

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

    
967
-- | Generates the \"save\" clause for entire LuxiOp constructor.
968
saveLuxiConstructor :: LuxiConstructor -> Q Clause
969
saveLuxiConstructor (sname, fields) = do
970
  let cname = mkName sname
971
  fnames <- mapM (newName . fieldVariable) fields
972
  let pat = conP cname (map varP fnames)
973
  let felems = zipWith saveObjectField fnames fields
974
      flist = [| concat $(listE felems) |]
975
  clause [pat] (normalB flist) []
976

    
977
-- * "Objects" functionality
978

    
979
-- | Extract the field's declaration from a Field structure.
980
fieldTypeInfo :: String -> Field -> Q (Name, Strict, Type)
981
fieldTypeInfo field_pfx fd = do
982
  t <- actualFieldType fd
983
  let n = mkName . (field_pfx ++) . fieldRecordName $ fd
984
  return (n, NotStrict, t)
985

    
986
-- | Build an object declaration.
987
buildObject :: String -> String -> [Field] -> Q [Dec]
988
buildObject sname field_pfx fields = do
989
  when (any ((==) AndRestArguments . fieldIsOptional)
990
         . drop 1 $ reverse fields)
991
    $ fail "Objects may have only one AndRestArguments field,\
992
           \ and it must be the last one."
993
  let name = mkName sname
994
  fields_d <- mapM (fieldTypeInfo field_pfx) fields
995
  let decl_d = RecC name fields_d
996
  let declD = DataD [] name [] [decl_d] [''Show, ''Eq]
997
  ser_decls <- buildObjectSerialisation sname fields
998
  return $ declD:ser_decls
999

    
1000
-- | Generates an object definition: data type and its JSON instance.
1001
buildObjectSerialisation :: String -> [Field] -> Q [Dec]
1002
buildObjectSerialisation sname fields = do
1003
  let name = mkName sname
1004
  dictdecls <- genDictObject saveObjectField
1005
                             (loadObjectField fields) sname fields
1006
  savedecls <- genSaveObject sname
1007
  (loadsig, loadfn) <- genLoadObject sname
1008
  shjson <- objectShowJSON sname
1009
  rdjson <- objectReadJSON sname
1010
  let instdecl = InstanceD [] (AppT (ConT ''JSON.JSON) (ConT name))
1011
                 [rdjson, shjson]
1012
  return $ dictdecls ++ savedecls ++ [loadsig, loadfn, instdecl]
1013

    
1014
-- | An internal name used for naming variables that hold the entire
1015
-- object of type @[(String,JSValue)]@.
1016
objVarName :: Name
1017
objVarName = mkName "_o"
1018

    
1019
-- | Provides a default 'toJSArray' for 'ArrayObject' instance using its
1020
-- existing 'DictObject' instance. The keys are serialized in the order
1021
-- they're declared. The list must contain all keys possibly generated by
1022
-- 'toDict'.
1023
defaultToJSArray :: (DictObject a) => [String] -> a -> [JSON.JSValue]
1024
defaultToJSArray keys o =
1025
  let m = M.fromList $ toDict o
1026
  in map (fromMaybe JSON.JSNull . flip M.lookup m) keys
1027

    
1028
-- | Provides a default 'fromJSArray' for 'ArrayObject' instance using its
1029
-- existing 'DictObject' instance. The fields are deserialized in the order
1030
-- they're declared.
1031
defaultFromJSArray :: (DictObject a)
1032
                   => [String] -> [JSON.JSValue] -> JSON.Result a
1033
defaultFromJSArray keys xs = do
1034
  let xslen = length xs
1035
      explen = length keys
1036
  unless (xslen == explen) (fail $ "Expected " ++ show explen
1037
                                   ++ " arguments, got " ++ show xslen)
1038
  fromDict $ zip keys xs
1039

    
1040
-- | Generates an additional 'ArrayObject' instance using its
1041
-- existing 'DictObject' instance.
1042
--
1043
-- See 'defaultToJSArray' and 'defaultFromJSArray'.
1044
genArrayObjectInstance :: Name -> [Field] -> Q Dec
1045
genArrayObjectInstance name fields = do
1046
  let fnames = concatMap (liftA2 (:) fieldName fieldExtraKeys) fields
1047
  instanceD (return []) (appT (conT ''ArrayObject) (conT name))
1048
    [ valD (varP 'toJSArray) (normalB [| defaultToJSArray $(lift fnames) |]) []
1049
    , valD (varP 'fromJSArray) (normalB [| defaultFromJSArray fnames |]) []
1050
    ]
1051

    
1052
-- | Generates 'DictObject' instance.
1053
genDictObject :: (Name -> Field -> Q Exp)  -- ^ a saving function
1054
              -> (Field -> Q Exp)          -- ^ a loading function
1055
              -> String                    -- ^ an object name
1056
              -> [Field]                   -- ^ a list of fields
1057
              -> Q [Dec]
1058
genDictObject save_fn load_fn sname fields = do
1059
  let name = mkName sname
1060
  -- toDict
1061
  fnames <- mapM (newName . fieldVariable) fields
1062
  let pat = conP name (map varP fnames)
1063
      tdexp = [| concat $(listE $ zipWith save_fn fnames fields) |]
1064
  tdclause <- clause [pat] (normalB tdexp) []
1065
  -- fromDict
1066
  fdexp <- loadConstructor name load_fn fields
1067
  let fdclause = Clause [VarP objVarName] (NormalB fdexp) []
1068
  -- the ArrayObject instance generated from DictObject
1069
  arrdec <- genArrayObjectInstance name fields
1070
  -- the final instance
1071
  return $ [InstanceD [] (AppT (ConT ''DictObject) (ConT name))
1072
             [ FunD 'toDict [tdclause]
1073
             , FunD 'fromDict [fdclause]
1074
             ]]
1075
         ++ [arrdec]
1076

    
1077
-- | Generates the save object functionality.
1078
genSaveObject :: String -> Q [Dec]
1079
genSaveObject sname = do
1080
  let fname = mkName ("save" ++ sname)
1081
  sigt <- [t| $(conT $ mkName sname) -> JSON.JSValue |]
1082
  cclause <- [| $makeObjE . $(varE $ 'toDict) |]
1083
  return [SigD fname sigt, ValD (VarP fname) (NormalB cclause) []]
1084

    
1085
-- | Generates the code for saving an object's field, handling the
1086
-- various types of fields that we have.
1087
saveObjectField :: Name -> Field -> Q Exp
1088
saveObjectField fvar field =
1089
  let formatFn = fromMaybe [| JSON.showJSON &&& (const []) |] $
1090
                           fieldShow field
1091
      formatCode v = [| let (actual, extra) = $formatFn $(v)
1092
                         in ($nameE, actual) : extra |]
1093
  in case fieldIsOptional field of
1094
    OptionalOmitNull ->       [| case $(fvarE) of
1095
                                   Nothing -> []
1096
                                   Just v  -> $(formatCode [| v |])
1097
                              |]
1098
    OptionalSerializeNull ->  [| case $(fvarE) of
1099
                                   Nothing -> [( $nameE, JSON.JSNull )]
1100
                                   Just v  -> $(formatCode [| v |])
1101
                              |]
1102
    NotOptional ->            formatCode fvarE
1103
    AndRestArguments -> [| M.toList $(varE fvar) |]
1104
  where nameE = stringE (fieldName field)
1105
        fvarE = varE fvar
1106

    
1107
-- | Generates the showJSON clause for a given object name.
1108
objectShowJSON :: String -> Q Dec
1109
objectShowJSON name = do
1110
  body <- [| JSON.showJSON . $(varE . mkName $ "save" ++ name) |]
1111
  return $ FunD 'JSON.showJSON [Clause [] (NormalB body) []]
1112

    
1113
-- | Generates the load object functionality.
1114
genLoadObject :: String -> Q (Dec, Dec)
1115
genLoadObject sname = do
1116
  let fname = mkName $ "load" ++ sname
1117
  sigt <- [t| JSON.JSValue -> JSON.Result $(conT $ mkName sname) |]
1118
  cclause <- [| fromDict <=< liftM JSON.fromJSObject . JSON.readJSON |]
1119
  return $ (SigD fname sigt,
1120
            FunD fname [Clause [] (NormalB cclause) []])
1121

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

    
1145
-- | Builds the readJSON instance for a given object name.
1146
objectReadJSON :: String -> Q Dec
1147
objectReadJSON name = do
1148
  let s = mkName "s"
1149
  body <- [| $(varE . mkName $ "load" ++ name) =<<
1150
             readJSONWithDesc $(stringE name) False $(varE s) |]
1151
  return $ FunD 'JSON.readJSON [Clause [VarP s] (NormalB body) []]
1152

    
1153
-- * Inheritable parameter tables implementation
1154

    
1155
-- | Compute parameter type names.
1156
paramTypeNames :: String -> (String, String)
1157
paramTypeNames root = ("Filled"  ++ root ++ "Params",
1158
                       "Partial" ++ root ++ "Params")
1159

    
1160
-- | Compute information about the type of a parameter field.
1161
paramFieldTypeInfo :: String -> Field -> Q (Name, Strict, Type)
1162
paramFieldTypeInfo field_pfx fd = do
1163
  t <- actualFieldType fd
1164
  let n = mkName . (++ "P") . (field_pfx ++) .
1165
          fieldRecordName $ fd
1166
  return (n, NotStrict, AppT (ConT ''Maybe) t)
1167

    
1168
-- | Build a parameter declaration.
1169
--
1170
-- This function builds two different data structures: a /filled/ one,
1171
-- in which all fields are required, and a /partial/ one, in which all
1172
-- fields are optional. Due to the current record syntax issues, the
1173
-- fields need to be named differrently for the two structures, so the
1174
-- partial ones get a /P/ suffix.
1175
buildParam :: String -> String -> [Field] -> Q [Dec]
1176
buildParam sname field_pfx fields = do
1177
  let (sname_f, sname_p) = paramTypeNames sname
1178
      name_f = mkName sname_f
1179
      name_p = mkName sname_p
1180
  fields_f <- mapM (fieldTypeInfo field_pfx) fields
1181
  fields_p <- mapM (paramFieldTypeInfo field_pfx) fields
1182
  let decl_f = RecC name_f fields_f
1183
      decl_p = RecC name_p fields_p
1184
  let declF = DataD [] name_f [] [decl_f] [''Show, ''Eq]
1185
      declP = DataD [] name_p [] [decl_p] [''Show, ''Eq]
1186
  ser_decls_f <- buildObjectSerialisation sname_f fields
1187
  ser_decls_p <- buildPParamSerialisation sname_p fields
1188
  fill_decls <- fillParam sname field_pfx fields
1189
  return $ [declF, declP] ++ ser_decls_f ++ ser_decls_p ++ fill_decls ++
1190
           buildParamAllFields sname fields
1191

    
1192
-- | Builds a list of all fields of a parameter.
1193
buildParamAllFields :: String -> [Field] -> [Dec]
1194
buildParamAllFields sname fields =
1195
  let vname = mkName ("all" ++ sname ++ "ParamFields")
1196
      sig = SigD vname (AppT ListT (ConT ''String))
1197
      val = ListE $ map (LitE . StringL . fieldName) fields
1198
  in [sig, ValD (VarP vname) (NormalB val) []]
1199

    
1200
-- | Generates the serialisation for a partial parameter.
1201
buildPParamSerialisation :: String -> [Field] -> Q [Dec]
1202
buildPParamSerialisation sname fields = do
1203
  let name = mkName sname
1204
  dictdecls <- genDictObject savePParamField loadPParamField sname fields
1205
  savedecls <- genSaveObject sname
1206
  (loadsig, loadfn) <- genLoadObject sname
1207
  shjson <- objectShowJSON sname
1208
  rdjson <- objectReadJSON sname
1209
  let instdecl = InstanceD [] (AppT (ConT ''JSON.JSON) (ConT name))
1210
                 [rdjson, shjson]
1211
  return $ dictdecls ++ savedecls ++ [loadsig, loadfn, instdecl]
1212

    
1213
-- | Generates code to save an optional parameter field.
1214
savePParamField :: Name -> Field -> Q Exp
1215
savePParamField fvar field = do
1216
  checkNonOptDef field
1217
  let actualVal = mkName "v"
1218
  normalexpr <- saveObjectField actualVal field
1219
  -- we have to construct the block here manually, because we can't
1220
  -- splice-in-splice
1221
  return $ CaseE (VarE fvar) [ Match (ConP 'Nothing [])
1222
                                       (NormalB (ConE '[])) []
1223
                             , Match (ConP 'Just [VarP actualVal])
1224
                                       (NormalB normalexpr) []
1225
                             ]
1226

    
1227
-- | Generates code to load an optional parameter field.
1228
loadPParamField :: Field -> Q Exp
1229
loadPParamField field = do
1230
  checkNonOptDef field
1231
  let name = fieldName field
1232
  -- these are used in all patterns below
1233
  let objvar = varE objVarName
1234
      objfield = stringE name
1235
      loadexp = [| $(varE 'maybeFromObj) $objvar $objfield |]
1236
  loadFnOpt field loadexp objvar
1237

    
1238
-- | Builds a simple declaration of type @n_x = fromMaybe f_x p_x@.
1239
buildFromMaybe :: String -> Q Dec
1240
buildFromMaybe fname =
1241
  valD (varP (mkName $ "n_" ++ fname))
1242
         (normalB [| $(varE 'fromMaybe)
1243
                        $(varNameE $ "f_" ++ fname)
1244
                        $(varNameE $ "p_" ++ fname) |]) []
1245

    
1246
-- | Builds a function that executes the filling of partial parameter
1247
-- from a full copy (similar to Python's fillDict).
1248
fillParam :: String -> String -> [Field] -> Q [Dec]
1249
fillParam sname field_pfx fields = do
1250
  let fnames = map (\fd -> field_pfx ++ fieldRecordName fd) fields
1251
      (sname_f, sname_p) = paramTypeNames sname
1252
      oname_f = "fobj"
1253
      oname_p = "pobj"
1254
      name_f = mkName sname_f
1255
      name_p = mkName sname_p
1256
      fun_name = mkName $ "fill" ++ sname ++ "Params"
1257
      le_full = ValD (ConP name_f (map (VarP . mkName . ("f_" ++)) fnames))
1258
                (NormalB . VarE . mkName $ oname_f) []
1259
      le_part = ValD (ConP name_p (map (VarP . mkName . ("p_" ++)) fnames))
1260
                (NormalB . VarE . mkName $ oname_p) []
1261
      obj_new = foldl (\accu vname -> AppE accu (VarE vname)) (ConE name_f)
1262
                $ map (mkName . ("n_" ++)) fnames
1263
  le_new <- mapM buildFromMaybe fnames
1264
  funt <- [t| $(conT name_f) -> $(conT name_p) -> $(conT name_f) |]
1265
  let sig = SigD fun_name funt
1266
      fclause = Clause [VarP (mkName oname_f), VarP (mkName oname_p)]
1267
                (NormalB $ LetE (le_full:le_part:le_new) obj_new) []
1268
      fun = FunD fun_name [fclause]
1269
  return [sig, fun]
1270

    
1271
-- * Template code for exceptions
1272

    
1273
-- | Exception simple error message field.
1274
excErrMsg :: (String, Q Type)
1275
excErrMsg = ("errMsg", [t| String |])
1276

    
1277
-- | Builds an exception type definition.
1278
genException :: String                  -- ^ Name of new type
1279
             -> SimpleObject -- ^ Constructor name and parameters
1280
             -> Q [Dec]
1281
genException name cons = do
1282
  let tname = mkName name
1283
  declD <- buildSimpleCons tname cons
1284
  (savesig, savefn) <- genSaveSimpleObj tname ("save" ++ name) cons $
1285
                         uncurry saveExcCons
1286
  (loadsig, loadfn) <- genLoadExc tname ("load" ++ name) cons
1287
  return [declD, loadsig, loadfn, savesig, savefn]
1288

    
1289
-- | Generates the \"save\" clause for an entire exception constructor.
1290
--
1291
-- This matches the exception with variables named the same as the
1292
-- constructor fields (just so that the spliced in code looks nicer),
1293
-- and calls showJSON on it.
1294
saveExcCons :: String        -- ^ The constructor name
1295
            -> [SimpleField] -- ^ The parameter definitions for this
1296
                             -- constructor
1297
            -> Q Clause      -- ^ Resulting clause
1298
saveExcCons sname fields = do
1299
  let cname = mkName sname
1300
  fnames <- mapM (newName . fst) fields
1301
  let pat = conP cname (map varP fnames)
1302
      felems = if null fnames
1303
                 then conE '() -- otherwise, empty list has no type
1304
                 else listE $ map (\f -> [| JSON.showJSON $(varE f) |]) fnames
1305
  let tup = tupE [ litE (stringL sname), felems ]
1306
  clause [pat] (normalB [| JSON.showJSON $tup |]) []
1307

    
1308
-- | Generates load code for a single constructor of an exception.
1309
--
1310
-- Generates the code (if there's only one argument, we will use a
1311
-- list, not a tuple:
1312
--
1313
-- @
1314
-- do
1315
--  (x1, x2, ...) <- readJSON args
1316
--  return $ Cons x1 x2 ...
1317
-- @
1318
loadExcConstructor :: Name -> String -> [SimpleField] -> Q Exp
1319
loadExcConstructor inname sname fields = do
1320
  let name = mkName sname
1321
  f_names <- mapM (newName . fst) fields
1322
  let read_args = AppE (VarE 'JSON.readJSON) (VarE inname)
1323
  let binds = case f_names of
1324
                [x] -> BindS (ListP [VarP x])
1325
                _   -> BindS (TupP (map VarP f_names))
1326
      cval = foldl (\accu fn -> AppE accu (VarE fn)) (ConE name) f_names
1327
  return $ DoE [binds read_args, NoBindS (AppE (VarE 'return) cval)]
1328

    
1329
{-| Generates the loadException function.
1330

    
1331
This generates a quite complicated function, along the lines of:
1332

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