Statistics
| Branch: | Tag: | Revision:

root / src / Ganeti / THH.hs @ 0df2d967

History | View | Annotate | Download (51.6 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
                     -- ^ a list of extra keys added by 'fieldShow'
178
                   , fieldDefault     :: Maybe (Q Exp)
179
                     -- ^ an optional default value of type @t@
180
                   , fieldConstr      :: Maybe String
181
                   , fieldIsOptional  :: OptionalType
182
                     -- ^ determines if a field is optional, and if yes,
183
                     -- how
184
                   , fieldDoc         :: String
185
                   }
186

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
344
-- * Common field declarations
345

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

    
351

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

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

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

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

    
369
-- * Internal types
370

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

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

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

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

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

    
386
-- * Helper functions
387

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
620
-- * Template code for opcodes
621

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

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

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

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

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

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

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

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

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

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

    
705
-- * Python code generation
706

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
919
-- * Template code for luxi
920

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

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

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

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

    
969
-- * "Objects" functionality
970

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

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

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

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

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

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

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

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

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

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

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

    
1109
-- * Inheritable parameter tables implementation
1110

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

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

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

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

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

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

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

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

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

    
1227
-- * Template code for exceptions
1228

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

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

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

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

    
1285
{-| Generates the loadException function.
1286

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

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