Statistics
| Branch: | Tag: | Revision:

root / src / Ganeti / THH.hs @ 58b37916

History | View | Annotate | Download (51.5 kB)

1
{-# LANGUAGE ParallelListComp, TemplateHaskell #-}
2

    
3
{-| TemplateHaskell helper for Ganeti Haskell code.
4

    
5
As TemplateHaskell require that splices be defined in a separate
6
module, we combine all the TemplateHaskell functionality that HTools
7
needs in this module (except the one for unittests).
8

    
9
-}
10

    
11
{-
12

    
13
Copyright (C) 2011, 2012, 2013, 2014 Google Inc.
14

    
15
This program is free software; you can redistribute it and/or modify
16
it under the terms of the GNU General Public License as published by
17
the Free Software Foundation; either version 2 of the License, or
18
(at your option) any later version.
19

    
20
This program is distributed in the hope that it will be useful, but
21
WITHOUT ANY WARRANTY; without even the implied warranty of
22
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
23
General Public License for more details.
24

    
25
You should have received a copy of the GNU General Public License
26
along with this program; if not, write to the Free Software
27
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
28
02110-1301, USA.
29

    
30
-}
31

    
32
module Ganeti.THH ( declareSADT
33
                  , declareLADT
34
                  , declareILADT
35
                  , declareIADT
36
                  , makeJSONInstance
37
                  , deCamelCase
38
                  , genOpID
39
                  , 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
                  , genException
71
                  , excErrMsg
72
                  ) where
73

    
74
import Control.Arrow ((&&&))
75
import Control.Applicative
76
import Control.Monad
77
import Control.Monad.Base () -- Needed to prevent spurious GHC linking errors.
78
import Data.Attoparsec () -- Needed to prevent spurious GHC 7.4 linking errors.
79
  -- See issue #683 and https://ghc.haskell.org/trac/ghc/ticket/4899
80
import Data.Char
81
import Data.List
82
import Data.Maybe
83
import qualified Data.Map as M
84
import qualified Data.Set as Set
85
import Language.Haskell.TH
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
-- | Class of objects that can be converted from and to 'JSObject'
99
-- lists-format.
100
class DictObject a where
101
  toDict :: a -> [(String, JSON.JSValue)]
102
  fromDict :: [(String, JSON.JSValue)] -> JSON.Result a
103

    
104
-- | Optional field information.
105
data OptionalType
106
  = NotOptional           -- ^ Field is not optional
107
  | OptionalOmitNull      -- ^ Field is optional, null is not serialised
108
  | OptionalSerializeNull -- ^ Field is optional, null is serialised
109
  | AndRestArguments      -- ^ Special field capturing all the remaining fields
110
                          -- as plain JSON values
111
  deriving (Show, Eq)
112

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

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

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

    
214
withDoc :: String -> Field -> Field
215
withDoc doc field =
216
  field { fieldDoc = doc }
217

    
218
-- | Sets the renamed constructor field.
219
renameField :: String -> Field -> Field
220
renameField constrName field = field { fieldConstr = Just constrName }
221

    
222
-- | Sets the default value on a field (makes it optional with a
223
-- default value).
224
defaultField :: Q Exp -> Field -> Field
225
defaultField defval field = field { fieldDefault = Just defval }
226

    
227
-- | Marks a field optional (turning its base type into a Maybe).
228
optionalField :: Field -> Field
229
optionalField field = field { fieldIsOptional = OptionalOmitNull }
230

    
231
-- | Marks a field optional (turning its base type into a Maybe), but
232
-- with 'Nothing' serialised explicitly as /null/.
233
optionalNullSerField :: Field -> Field
234
optionalNullSerField field = field { fieldIsOptional = OptionalSerializeNull }
235

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

    
245
-- | Sets the read function to also accept string parsable by the given
246
-- function.
247
specialNumericalField :: Name -> Field -> Field
248
specialNumericalField f field =
249
     field { fieldRead = Just (appE (varE 'numericalReadFn) (varE f)) }
250

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

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

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

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

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

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

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

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

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

    
343
-- * Common field declarations
344

    
345
-- | Timestamp fields description.
346
timeStampFields :: [Field]
347
timeStampFields = map (defaultField [| TOD 0 0 |] . timeAsDoubleField)
348
                      ["ctime", "mtime"]
349

    
350

    
351
-- | Serial number fields description.
352
serialFields :: [Field]
353
serialFields =
354
    [ renameField  "Serial" $ simpleField "serial_no" [t| Int |] ]
355

    
356
-- | UUID fields description.
357
uuidFields :: [Field]
358
uuidFields = [ simpleField "uuid" [t| String |] ]
359

    
360
-- | Tag set type alias.
361
type TagSet = Set.Set String
362

    
363
-- | Tag field description.
364
tagsFields :: [Field]
365
tagsFields = [ defaultField [| Set.empty |] $
366
               simpleField "tags" [t| TagSet |] ]
367

    
368
-- * Internal types
369

    
370
-- | A simple field, in constrast to the customisable 'Field' type.
371
type SimpleField = (String, Q Type)
372

    
373
-- | A definition for a single constructor for a simple object.
374
type SimpleConstructor = (String, [SimpleField])
375

    
376
-- | A definition for ADTs with simple fields.
377
type SimpleObject = [SimpleConstructor]
378

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

    
382
-- | A type alias for a Luxi constructor of a regular object.
383
type LuxiConstructor = (String, [Field])
384

    
385
-- * Helper functions
386

    
387
-- | Ensure first letter is lowercase.
388
--
389
-- Used to convert type name to function prefix, e.g. in @data Aa ->
390
-- aaToRaw@.
391
ensureLower :: String -> String
392
ensureLower [] = []
393
ensureLower (x:xs) = toLower x:xs
394

    
395
-- | Ensure first letter is uppercase.
396
--
397
-- Used to convert constructor name to component
398
ensureUpper :: String -> String
399
ensureUpper [] = []
400
ensureUpper (x:xs) = toUpper x:xs
401

    
402
-- | Helper for quoted expressions.
403
varNameE :: String -> Q Exp
404
varNameE = varE . mkName
405

    
406
-- | showJSON as an expression, for reuse.
407
showJSONE :: Q Exp
408
showJSONE = varE 'JSON.showJSON
409

    
410
-- | makeObj as an expression, for reuse.
411
makeObjE :: Q Exp
412
makeObjE = varE 'JSON.makeObj
413

    
414
-- | fromObj (Ganeti specific) as an expression, for reuse.
415
fromObjE :: Q Exp
416
fromObjE = varE 'fromObj
417

    
418
-- | ToRaw function name.
419
toRawName :: String -> Name
420
toRawName = mkName . (++ "ToRaw") . ensureLower
421

    
422
-- | FromRaw function name.
423
fromRawName :: String -> Name
424
fromRawName = mkName . (++ "FromRaw") . ensureLower
425

    
426
-- | Converts a name to it's varE\/litE representations.
427
reprE :: Either String Name -> Q Exp
428
reprE = either stringE varE
429

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

    
438
-- | Builds a field for a normal constructor.
439
buildConsField :: Q Type -> StrictTypeQ
440
buildConsField ftype = do
441
  ftype' <- ftype
442
  return (NotStrict, ftype')
443

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

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

    
464
-- * Template code for simple raw type-equivalent ADTs
465

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

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

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

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

    
554
declareLADT :: Name -> String -> [(String, String)] -> Q [Dec]
555
declareLADT = declareADT Left
556

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

    
570
declareIADT :: String -> [(String, Name)] -> Q [Dec]
571
declareIADT = declareADT Right ''Int
572

    
573
declareSADT :: String -> [(String, Name)] -> Q [Dec]
574
declareSADT = declareADT Right ''String
575

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

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

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

    
619
-- * Template code for opcodes
620

    
621
-- | Transforms a CamelCase string into an_underscore_based_one.
622
deCamelCase :: String -> String
623
deCamelCase =
624
    intercalate "_" . map (map toUpper) . groupBy (\_ b -> not $ isUpper b)
625

    
626
-- | Transform an underscore_name into a CamelCase one.
627
camelCase :: String -> String
628
camelCase = concatMap (ensureUpper . drop 1) .
629
            groupBy (\_ b -> b /= '_' && b /= '-') . ('_':)
630

    
631
-- | Computes the name of a given constructor.
632
constructorName :: Con -> Q Name
633
constructorName (NormalC name _) = return name
634
constructorName (RecC name _)    = return name
635
constructorName x                = fail $ "Unhandled constructor " ++ show x
636

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

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

    
663
-- | Constructor-to-string for OpCode.
664
genOpID :: Name -> String -> Q [Dec]
665
genOpID = genConstrToStr (return . deCamelCase)
666

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

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

    
697
-- | Generates a list of all defined opcode IDs.
698
genAllOpIDs :: Name -> String -> Q [Dec]
699
genAllOpIDs = genAllConstr deCamelCase
700

    
701
-- | OpCode parameter (field) type.
702
type OpParam = (String, Q Type, Q Exp)
703

    
704
-- * Python code generation
705

    
706
data OpCodeField = OpCodeField { ocfName :: String
707
                               , ocfType :: PyType
708
                               , ocfDefl :: Maybe PyValueEx
709
                               , ocfDoc  :: String
710
                               }
711

    
712
-- | Transfers opcode data between the opcode description (through
713
-- @genOpCode@) and the Python code generation functions.
714
data OpCodeDescriptor = OpCodeDescriptor { ocdName   :: String
715
                                         , ocdType   :: PyType
716
                                         , ocdDoc    :: String
717
                                         , ocdFields :: [OpCodeField]
718
                                         , ocdDescr  :: String
719
                                         }
720

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

    
731
maybeApp (Just expr) typ =
732
  [| Just ($(conE (mkName "PyValueEx")) ($expr :: $typ)) |]
733

    
734
-- | Generates a Python type according to whether the field is
735
-- optional.
736
--
737
-- The type of created expression is PyType.
738
genPyType' :: OptionalType -> Q Type -> Q PyType
739
genPyType' opt typ = typ >>= pyOptionalType (opt /= NotOptional)
740

    
741
-- | Generates Python types from opcode parameters.
742
genPyType :: Field -> Q PyType
743
genPyType f = genPyType' (fieldIsOptional f) (fieldType f)
744

    
745
-- | Generates Python default values from opcode parameters.
746
genPyDefault :: Field -> Q Exp
747
genPyDefault f = maybeApp (fieldDefault f) (fieldType f)
748

    
749
pyField :: Field -> Q Exp
750
pyField f = genPyType f >>= \t ->
751
            [| OpCodeField $(stringE (fieldName f))
752
                           t
753
                           $(genPyDefault f)
754
                           $(stringE (fieldDoc f)) |]
755

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

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

    
782
-- | Converts from an opcode constructor to a Luxi constructor.
783
opcodeConsToLuxiCons :: (a, b, c, d, e) -> (a, d)
784
opcodeConsToLuxiCons (x, _, _, y, _) = (x, y)
785

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

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

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

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

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

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

    
890
-- | Generates load code for a single constructor of the opcode data type.
891
loadOpConstructor :: OpCodeConstructor -> Q Exp
892
loadOpConstructor (sname, _, _, fields, _) =
893
  loadConstructor (mkName sname) (loadObjectField fields) fields
894

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

    
918
-- * Template code for luxi
919

    
920
-- | Constructor-to-string for LuxiOp.
921
genStrOfOp :: Name -> String -> Q [Dec]
922
genStrOfOp = genConstrToStr return
923

    
924
-- | Constructor-to-string for MsgKeys.
925
genStrOfKey :: Name -> String -> Q [Dec]
926
genStrOfKey = genConstrToStr (return . ensureLower)
927

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

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

    
968
-- * "Objects" functionality
969

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

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

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

    
1005
-- | An internal name used for naming variables that hold the entire
1006
-- object of type @[(String,JSValue)]@.
1007
objVarName :: Name
1008
objVarName = mkName "_o"
1009

    
1010
-- | Generates 'DictObject' instance.
1011
genDictObject :: (Name -> Field -> Q Exp)  -- ^ a saving function
1012
              -> (Field -> Q Exp)          -- ^ a loading function
1013
              -> String                    -- ^ an object name
1014
              -> [Field]                   -- ^ a list of fields
1015
              -> Q [Dec]
1016
genDictObject save_fn load_fn sname fields = do
1017
  let name = mkName sname
1018
  -- toDict
1019
  fnames <- mapM (newName . fieldVariable) fields
1020
  let pat = conP name (map varP fnames)
1021
      tdexp = [| concat $(listE $ zipWith save_fn fnames fields) |]
1022
  tdclause <- clause [pat] (normalB tdexp) []
1023
  -- fromDict
1024
  fdexp <- loadConstructor name load_fn fields
1025
  let fdclause = Clause [VarP objVarName] (NormalB fdexp) []
1026
  -- the final instance
1027
  return [InstanceD [] (AppT (ConT ''DictObject) (ConT name))
1028
           [ FunD 'toDict [tdclause]
1029
           , FunD 'fromDict [fdclause]
1030
           ]]
1031

    
1032
-- | Generates the save object functionality.
1033
genSaveObject :: String -> Q [Dec]
1034
genSaveObject sname = do
1035
  let fname = mkName ("save" ++ sname)
1036
  sigt <- [t| $(conT $ mkName sname) -> JSON.JSValue |]
1037
  cclause <- [| $makeObjE . $(varE $ 'toDict) |]
1038
  return [SigD fname sigt, ValD (VarP fname) (NormalB cclause) []]
1039

    
1040
-- | Generates the code for saving an object's field, handling the
1041
-- various types of fields that we have.
1042
saveObjectField :: Name -> Field -> Q Exp
1043
saveObjectField fvar field =
1044
  let formatFn = fromMaybe [| JSON.showJSON &&& (const []) |] $
1045
                           fieldShow field
1046
      formatCode v = [| let (actual, extra) = $formatFn $(v)
1047
                         in ($nameE, actual) : extra |]
1048
  in case fieldIsOptional field of
1049
    OptionalOmitNull ->       [| case $(fvarE) of
1050
                                   Nothing -> []
1051
                                   Just v  -> $(formatCode [| v |])
1052
                              |]
1053
    OptionalSerializeNull ->  [| case $(fvarE) of
1054
                                   Nothing -> [( $nameE, JSON.JSNull )]
1055
                                   Just v  -> $(formatCode [| v |])
1056
                              |]
1057
    NotOptional ->            formatCode fvarE
1058
    AndRestArguments -> [| M.toList $(varE fvar) |]
1059
  where nameE = stringE (fieldName field)
1060
        fvarE = varE fvar
1061

    
1062
-- | Generates the showJSON clause for a given object name.
1063
objectShowJSON :: String -> Q Dec
1064
objectShowJSON name = do
1065
  body <- [| JSON.showJSON . $(varE . mkName $ "save" ++ name) |]
1066
  return $ FunD 'JSON.showJSON [Clause [] (NormalB body) []]
1067

    
1068
-- | Generates the load object functionality.
1069
genLoadObject :: String -> Q (Dec, Dec)
1070
genLoadObject sname = do
1071
  let fname = mkName $ "load" ++ sname
1072
  sigt <- [t| JSON.JSValue -> JSON.Result $(conT $ mkName sname) |]
1073
  cclause <- [| fromDict <=< liftM JSON.fromJSObject . JSON.readJSON |]
1074
  return $ (SigD fname sigt,
1075
            FunD fname [Clause [] (NormalB cclause) []])
1076

    
1077
-- | Generates code for loading an object's field.
1078
loadObjectField :: [Field] -> Field -> Q Exp
1079
loadObjectField allFields field = do
1080
  let name = fieldVariable field
1081
      names = map fieldVariable allFields
1082
      otherNames = listE . map stringE $ names \\ [name]
1083
  -- these are used in all patterns below
1084
  let objvar = varE objVarName
1085
      objfield = stringE (fieldName field)
1086
  case (fieldDefault field, fieldIsOptional field) of
1087
            -- Only non-optional fields without defaults must have a value;
1088
            -- we treat both optional types the same, since
1089
            -- 'maybeFromObj' can deal with both missing and null values
1090
            -- appropriately (the same)
1091
            (Nothing, NotOptional) ->
1092
                  loadFn field [| fromObj $objvar $objfield |] objvar
1093
            -- AndRestArguments need not to be parsed at all,
1094
            -- they're just extracted from the list of other fields.
1095
            (Nothing, AndRestArguments) ->
1096
                  [| return . M.fromList
1097
                     $ filter (not . (`elem` $otherNames) . fst) $objvar |]
1098
            _ ->  loadFnOpt field [| maybeFromObj $objvar $objfield |] objvar
1099

    
1100
-- | Builds the readJSON instance for a given object name.
1101
objectReadJSON :: String -> Q Dec
1102
objectReadJSON name = do
1103
  let s = mkName "s"
1104
  body <- [| $(varE . mkName $ "load" ++ name) =<<
1105
             readJSONWithDesc $(stringE name) False $(varE s) |]
1106
  return $ FunD 'JSON.readJSON [Clause [VarP s] (NormalB body) []]
1107

    
1108
-- * Inheritable parameter tables implementation
1109

    
1110
-- | Compute parameter type names.
1111
paramTypeNames :: String -> (String, String)
1112
paramTypeNames root = ("Filled"  ++ root ++ "Params",
1113
                       "Partial" ++ root ++ "Params")
1114

    
1115
-- | Compute information about the type of a parameter field.
1116
paramFieldTypeInfo :: String -> Field -> Q (Name, Strict, Type)
1117
paramFieldTypeInfo field_pfx fd = do
1118
  t <- actualFieldType fd
1119
  let n = mkName . (++ "P") . (field_pfx ++) .
1120
          fieldRecordName $ fd
1121
  return (n, NotStrict, AppT (ConT ''Maybe) t)
1122

    
1123
-- | Build a parameter declaration.
1124
--
1125
-- This function builds two different data structures: a /filled/ one,
1126
-- in which all fields are required, and a /partial/ one, in which all
1127
-- fields are optional. Due to the current record syntax issues, the
1128
-- fields need to be named differrently for the two structures, so the
1129
-- partial ones get a /P/ suffix.
1130
buildParam :: String -> String -> [Field] -> Q [Dec]
1131
buildParam sname field_pfx fields = do
1132
  let (sname_f, sname_p) = paramTypeNames sname
1133
      name_f = mkName sname_f
1134
      name_p = mkName sname_p
1135
  fields_f <- mapM (fieldTypeInfo field_pfx) fields
1136
  fields_p <- mapM (paramFieldTypeInfo field_pfx) fields
1137
  let decl_f = RecC name_f fields_f
1138
      decl_p = RecC name_p fields_p
1139
  let declF = DataD [] name_f [] [decl_f] [''Show, ''Eq]
1140
      declP = DataD [] name_p [] [decl_p] [''Show, ''Eq]
1141
  ser_decls_f <- buildObjectSerialisation sname_f fields
1142
  ser_decls_p <- buildPParamSerialisation sname_p fields
1143
  fill_decls <- fillParam sname field_pfx fields
1144
  return $ [declF, declP] ++ ser_decls_f ++ ser_decls_p ++ fill_decls ++
1145
           buildParamAllFields sname fields
1146

    
1147
-- | Builds a list of all fields of a parameter.
1148
buildParamAllFields :: String -> [Field] -> [Dec]
1149
buildParamAllFields sname fields =
1150
  let vname = mkName ("all" ++ sname ++ "ParamFields")
1151
      sig = SigD vname (AppT ListT (ConT ''String))
1152
      val = ListE $ map (LitE . StringL . fieldName) fields
1153
  in [sig, ValD (VarP vname) (NormalB val) []]
1154

    
1155
-- | Generates the serialisation for a partial parameter.
1156
buildPParamSerialisation :: String -> [Field] -> Q [Dec]
1157
buildPParamSerialisation sname fields = do
1158
  let name = mkName sname
1159
  dictdecls <- genDictObject savePParamField loadPParamField sname fields
1160
  savedecls <- genSaveObject sname
1161
  (loadsig, loadfn) <- genLoadObject sname
1162
  shjson <- objectShowJSON sname
1163
  rdjson <- objectReadJSON sname
1164
  let instdecl = InstanceD [] (AppT (ConT ''JSON.JSON) (ConT name))
1165
                 [rdjson, shjson]
1166
  return $ dictdecls ++ savedecls ++ [loadsig, loadfn, instdecl]
1167

    
1168
-- | Generates code to save an optional parameter field.
1169
savePParamField :: Name -> Field -> Q Exp
1170
savePParamField fvar field = do
1171
  checkNonOptDef field
1172
  let actualVal = mkName "v"
1173
  normalexpr <- saveObjectField actualVal field
1174
  -- we have to construct the block here manually, because we can't
1175
  -- splice-in-splice
1176
  return $ CaseE (VarE fvar) [ Match (ConP 'Nothing [])
1177
                                       (NormalB (ConE '[])) []
1178
                             , Match (ConP 'Just [VarP actualVal])
1179
                                       (NormalB normalexpr) []
1180
                             ]
1181

    
1182
-- | Generates code to load an optional parameter field.
1183
loadPParamField :: Field -> Q Exp
1184
loadPParamField field = do
1185
  checkNonOptDef field
1186
  let name = fieldName field
1187
  -- these are used in all patterns below
1188
  let objvar = varE objVarName
1189
      objfield = stringE name
1190
      loadexp = [| $(varE 'maybeFromObj) $objvar $objfield |]
1191
  loadFnOpt field loadexp objvar
1192

    
1193
-- | Builds a simple declaration of type @n_x = fromMaybe f_x p_x@.
1194
buildFromMaybe :: String -> Q Dec
1195
buildFromMaybe fname =
1196
  valD (varP (mkName $ "n_" ++ fname))
1197
         (normalB [| $(varE 'fromMaybe)
1198
                        $(varNameE $ "f_" ++ fname)
1199
                        $(varNameE $ "p_" ++ fname) |]) []
1200

    
1201
-- | Builds a function that executes the filling of partial parameter
1202
-- from a full copy (similar to Python's fillDict).
1203
fillParam :: String -> String -> [Field] -> Q [Dec]
1204
fillParam sname field_pfx fields = do
1205
  let fnames = map (\fd -> field_pfx ++ fieldRecordName fd) fields
1206
      (sname_f, sname_p) = paramTypeNames sname
1207
      oname_f = "fobj"
1208
      oname_p = "pobj"
1209
      name_f = mkName sname_f
1210
      name_p = mkName sname_p
1211
      fun_name = mkName $ "fill" ++ sname ++ "Params"
1212
      le_full = ValD (ConP name_f (map (VarP . mkName . ("f_" ++)) fnames))
1213
                (NormalB . VarE . mkName $ oname_f) []
1214
      le_part = ValD (ConP name_p (map (VarP . mkName . ("p_" ++)) fnames))
1215
                (NormalB . VarE . mkName $ oname_p) []
1216
      obj_new = foldl (\accu vname -> AppE accu (VarE vname)) (ConE name_f)
1217
                $ map (mkName . ("n_" ++)) fnames
1218
  le_new <- mapM buildFromMaybe fnames
1219
  funt <- [t| $(conT name_f) -> $(conT name_p) -> $(conT name_f) |]
1220
  let sig = SigD fun_name funt
1221
      fclause = Clause [VarP (mkName oname_f), VarP (mkName oname_p)]
1222
                (NormalB $ LetE (le_full:le_part:le_new) obj_new) []
1223
      fun = FunD fun_name [fclause]
1224
  return [sig, fun]
1225

    
1226
-- * Template code for exceptions
1227

    
1228
-- | Exception simple error message field.
1229
excErrMsg :: (String, Q Type)
1230
excErrMsg = ("errMsg", [t| String |])
1231

    
1232
-- | Builds an exception type definition.
1233
genException :: String                  -- ^ Name of new type
1234
             -> SimpleObject -- ^ Constructor name and parameters
1235
             -> Q [Dec]
1236
genException name cons = do
1237
  let tname = mkName name
1238
  declD <- buildSimpleCons tname cons
1239
  (savesig, savefn) <- genSaveSimpleObj tname ("save" ++ name) cons $
1240
                         uncurry saveExcCons
1241
  (loadsig, loadfn) <- genLoadExc tname ("load" ++ name) cons
1242
  return [declD, loadsig, loadfn, savesig, savefn]
1243

    
1244
-- | Generates the \"save\" clause for an entire exception constructor.
1245
--
1246
-- This matches the exception with variables named the same as the
1247
-- constructor fields (just so that the spliced in code looks nicer),
1248
-- and calls showJSON on it.
1249
saveExcCons :: String        -- ^ The constructor name
1250
            -> [SimpleField] -- ^ The parameter definitions for this
1251
                             -- constructor
1252
            -> Q Clause      -- ^ Resulting clause
1253
saveExcCons sname fields = do
1254
  let cname = mkName sname
1255
  fnames <- mapM (newName . fst) fields
1256
  let pat = conP cname (map varP fnames)
1257
      felems = if null fnames
1258
                 then conE '() -- otherwise, empty list has no type
1259
                 else listE $ map (\f -> [| JSON.showJSON $(varE f) |]) fnames
1260
  let tup = tupE [ litE (stringL sname), felems ]
1261
  clause [pat] (normalB [| JSON.showJSON $tup |]) []
1262

    
1263
-- | Generates load code for a single constructor of an exception.
1264
--
1265
-- Generates the code (if there's only one argument, we will use a
1266
-- list, not a tuple:
1267
--
1268
-- @
1269
-- do
1270
--  (x1, x2, ...) <- readJSON args
1271
--  return $ Cons x1 x2 ...
1272
-- @
1273
loadExcConstructor :: Name -> String -> [SimpleField] -> Q Exp
1274
loadExcConstructor inname sname fields = do
1275
  let name = mkName sname
1276
  f_names <- mapM (newName . fst) fields
1277
  let read_args = AppE (VarE 'JSON.readJSON) (VarE inname)
1278
  let binds = case f_names of
1279
                [x] -> BindS (ListP [VarP x])
1280
                _   -> BindS (TupP (map VarP f_names))
1281
      cval = foldl (\accu fn -> AppE accu (VarE fn)) (ConE name) f_names
1282
  return $ DoE [binds read_args, NoBindS (AppE (VarE 'return) cval)]
1283

    
1284
{-| Generates the loadException function.
1285

    
1286
This generates a quite complicated function, along the lines of:
1287

    
1288
@
1289
loadFn (JSArray [JSString name, args]) = case name of
1290
   "A1" -> do
1291
     (x1, x2, ...) <- readJSON args
1292
     return $ A1 x1 x2 ...
1293
   "a2" -> ...
1294
   s -> fail $ "Unknown exception" ++ s
1295
loadFn v = fail $ "Expected array but got " ++ show v
1296
@
1297
-}
1298
genLoadExc :: Name -> String -> SimpleObject -> Q (Dec, Dec)
1299
genLoadExc tname sname opdefs = do
1300
  let fname = mkName sname
1301
  exc_name <- newName "name"
1302
  exc_args <- newName "args"
1303
  exc_else <- newName "s"
1304
  arg_else <- newName "v"
1305
  fails <- [| fail $ "Unknown exception '" ++ $(varE exc_else) ++ "'" |]
1306
  -- default match for unknown exception name
1307
  let defmatch = Match (VarP exc_else) (NormalB fails) []
1308
  -- the match results (per-constructor blocks)
1309
  str_matches <-
1310
    mapM (\(s, params) -> do
1311
            body_exp <- loadExcConstructor exc_args s params
1312
            return $ Match (LitP (StringL s)) (NormalB body_exp) [])
1313
    opdefs
1314
  -- the first function clause; we can't use [| |] due to TH
1315
  -- limitations, so we have to build the AST by hand
1316
  let clause1 = Clause [ConP 'JSON.JSArray
1317
                               [ListP [ConP 'JSON.JSString [VarP exc_name],
1318
                                            VarP exc_args]]]
1319
                (NormalB (CaseE (AppE (VarE 'JSON.fromJSString)
1320
                                        (VarE exc_name))
1321
                          (str_matches ++ [defmatch]))) []
1322
  -- the fail expression for the second function clause
1323
  fail_type <- [| fail $ "Invalid exception: expected '(string, [args])' " ++
1324
                  "      but got " ++ show (pp_value $(varE arg_else)) ++ "'"
1325
                |]
1326
  -- the second function clause
1327
  let clause2 = Clause [VarP arg_else] (NormalB fail_type) []
1328
  sigt <- [t| JSON.JSValue -> JSON.Result $(conT tname) |]
1329
  return $ (SigD fname sigt, FunD fname [clause1, clause2])