Statistics
| Branch: | Tag: | Revision:

root / src / Ganeti / THH.hs @ 6897a51e

History | View | Annotate | Download (46.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 Google Inc.
14

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

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

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

    
30
-}
31

    
32
module Ganeti.THH ( declareSADT
33
                  , declareLADT
34
                  , declareILADT
35
                  , declareIADT
36
                  , makeJSONInstance
37
                  , deCamelCase
38
                  , genOpID
39
                  , genAllConstr
40
                  , genAllOpIDs
41
                  , PyValue(..)
42
                  , PyValueEx(..)
43
                  , OpCodeField(..)
44
                  , OpCodeDescriptor(..)
45
                  , genOpCode
46
                  , genStrOfOp
47
                  , genStrOfKey
48
                  , genLuxiOp
49
                  , Field (..)
50
                  , simpleField
51
                  , specialNumericalField
52
                  , withDoc
53
                  , defaultField
54
                  , optionalField
55
                  , optionalNullSerField
56
                  , renameField
57
                  , customField
58
                  , timeStampFields
59
                  , uuidFields
60
                  , serialFields
61
                  , tagsFields
62
                  , TagSet
63
                  , buildObject
64
                  , buildObjectSerialisation
65
                  , buildParam
66
                  , DictObject(..)
67
                  , genException
68
                  , excErrMsg
69
                  ) where
70

    
71
import Control.Applicative
72
import Control.Monad
73
import Data.Char
74
import Data.List
75
import Data.Maybe
76
import qualified Data.Set as Set
77
import Language.Haskell.TH
78

    
79
import qualified Text.JSON as JSON
80
import Text.JSON.Pretty (pp_value)
81

    
82
import Ganeti.JSON
83
import Ganeti.PyValue
84
import Ganeti.THH.PyType
85

    
86

    
87
-- * Exported types
88

    
89
-- | Class of objects that can be converted to 'JSObject'
90
-- lists-format.
91
class DictObject a where
92
  toDict :: a -> [(String, JSON.JSValue)]
93

    
94
-- | Optional field information.
95
data OptionalType
96
  = NotOptional           -- ^ Field is not optional
97
  | OptionalOmitNull      -- ^ Field is optional, null is not serialised
98
  | OptionalSerializeNull -- ^ Field is optional, null is serialised
99
  deriving (Show, Eq)
100

    
101
-- | Serialised field data type.
102
data Field = Field { fieldName        :: String
103
                   , fieldType        :: Q Type
104
                   , fieldRead        :: Maybe (Q Exp)
105
                   , fieldShow        :: Maybe (Q Exp)
106
                   , fieldExtraKeys   :: [String]
107
                   , fieldDefault     :: Maybe (Q Exp)
108
                   , fieldConstr      :: Maybe String
109
                   , fieldIsOptional  :: OptionalType
110
                   , fieldDoc         :: String
111
                   }
112

    
113
-- | Generates a simple field.
114
simpleField :: String -> Q Type -> Field
115
simpleField fname ftype =
116
  Field { fieldName        = fname
117
        , fieldType        = ftype
118
        , fieldRead        = Nothing
119
        , fieldShow        = Nothing
120
        , fieldExtraKeys   = []
121
        , fieldDefault     = Nothing
122
        , fieldConstr      = Nothing
123
        , fieldIsOptional  = NotOptional
124
        , fieldDoc         = ""
125
        }
126

    
127
withDoc :: String -> Field -> Field
128
withDoc doc field =
129
  field { fieldDoc = doc }
130

    
131
-- | Sets the renamed constructor field.
132
renameField :: String -> Field -> Field
133
renameField constrName field = field { fieldConstr = Just constrName }
134

    
135
-- | Sets the default value on a field (makes it optional with a
136
-- default value).
137
defaultField :: Q Exp -> Field -> Field
138
defaultField defval field = field { fieldDefault = Just defval }
139

    
140
-- | Marks a field optional (turning its base type into a Maybe).
141
optionalField :: Field -> Field
142
optionalField field = field { fieldIsOptional = OptionalOmitNull }
143

    
144
-- | Marks a field optional (turning its base type into a Maybe), but
145
-- with 'Nothing' serialised explicitly as /null/.
146
optionalNullSerField :: Field -> Field
147
optionalNullSerField field = field { fieldIsOptional = OptionalSerializeNull }
148

    
149
-- | Wrapper around a special parse function, suitable as field-parsing
150
-- function.
151
numericalReadFn :: JSON.JSON a => (String -> JSON.Result a)
152
                   -> [(String, JSON.JSValue)] -> JSON.JSValue -> JSON.Result a
153
numericalReadFn _ _ v@(JSON.JSRational _ _) = JSON.readJSON v
154
numericalReadFn f _ (JSON.JSString x) = f $ JSON.fromJSString x
155
numericalReadFn _ _ _ = JSON.Error "A numerical field has to be a number or\ 
156
                                   \ a string."
157

    
158
-- | Wrapper to lift a read function to optional values
159
makeReadOptional :: ([(String, JSON.JSValue)] -> JSON.JSValue -> JSON.Result a)
160
                    -> [(String, JSON.JSValue)]
161
                    -> Maybe JSON.JSValue -> JSON.Result (Maybe a)
162
makeReadOptional _ _ Nothing = JSON.Ok Nothing
163
makeReadOptional f o (Just x) = fmap Just $ f o x
164

    
165
-- | Sets the read function to also accept string parsable by the given
166
-- function.
167
specialNumericalField :: Name -> Field -> Field
168
specialNumericalField f field =
169
  if (fieldIsOptional field == NotOptional)
170
     then field { fieldRead = Just (appE (varE 'numericalReadFn) (varE f)) }
171
     else field { fieldRead = Just (appE (varE 'makeReadOptional)
172
                                         (appE (varE 'numericalReadFn)
173
                                               (varE f))) }
174

    
175
-- | Sets custom functions on a field.
176
customField :: Name      -- ^ The name of the read function
177
            -> Name      -- ^ The name of the show function
178
            -> [String]  -- ^ The name of extra field keys
179
            -> Field     -- ^ The original field
180
            -> Field     -- ^ Updated field
181
customField readfn showfn extra field =
182
  field { fieldRead = Just (varE readfn), fieldShow = Just (varE showfn)
183
        , fieldExtraKeys = extra }
184

    
185
-- | Computes the record name for a given field, based on either the
186
-- string value in the JSON serialisation or the custom named if any
187
-- exists.
188
fieldRecordName :: Field -> String
189
fieldRecordName (Field { fieldName = name, fieldConstr = alias }) =
190
  fromMaybe (camelCase name) alias
191

    
192
-- | Computes the preferred variable name to use for the value of this
193
-- field. If the field has a specific constructor name, then we use a
194
-- first-letter-lowercased version of that; otherwise, we simply use
195
-- the field name. See also 'fieldRecordName'.
196
fieldVariable :: Field -> String
197
fieldVariable f =
198
  case (fieldConstr f) of
199
    Just name -> ensureLower name
200
    _ -> map (\c -> if c == '-' then '_' else c) $ fieldName f
201

    
202
-- | Compute the actual field type (taking into account possible
203
-- optional status).
204
actualFieldType :: Field -> Q Type
205
actualFieldType f | fieldIsOptional f /= NotOptional = [t| Maybe $t |]
206
                  | otherwise = t
207
                  where t = fieldType f
208

    
209
-- | Checks that a given field is not optional (for object types or
210
-- fields which should not allow this case).
211
checkNonOptDef :: (Monad m) => Field -> m ()
212
checkNonOptDef (Field { fieldIsOptional = OptionalOmitNull
213
                      , fieldName = name }) =
214
  fail $ "Optional field " ++ name ++ " used in parameter declaration"
215
checkNonOptDef (Field { fieldIsOptional = OptionalSerializeNull
216
                      , fieldName = name }) =
217
  fail $ "Optional field " ++ name ++ " used in parameter declaration"
218
checkNonOptDef (Field { fieldDefault = (Just _), fieldName = name }) =
219
  fail $ "Default field " ++ name ++ " used in parameter declaration"
220
checkNonOptDef _ = return ()
221

    
222
-- | Produces the expression that will de-serialise a given
223
-- field. Since some custom parsing functions might need to use the
224
-- entire object, we do take and pass the object to any custom read
225
-- functions.
226
loadFn :: Field   -- ^ The field definition
227
       -> Q Exp   -- ^ The value of the field as existing in the JSON message
228
       -> Q Exp   -- ^ The entire object in JSON object format
229
       -> Q Exp   -- ^ Resulting expression
230
loadFn (Field { fieldRead = Just readfn }) expr o = [| $expr >>= $readfn $o |]
231
loadFn _ expr _ = expr
232

    
233
-- * Common field declarations
234

    
235
-- | Timestamp fields description.
236
timeStampFields :: [Field]
237
timeStampFields =
238
    [ defaultField [| 0::Double |] $ simpleField "ctime" [t| Double |]
239
    , defaultField [| 0::Double |] $ simpleField "mtime" [t| Double |]
240
    ]
241

    
242
-- | Serial number fields description.
243
serialFields :: [Field]
244
serialFields =
245
    [ renameField  "Serial" $ simpleField "serial_no" [t| Int |] ]
246

    
247
-- | UUID fields description.
248
uuidFields :: [Field]
249
uuidFields = [ simpleField "uuid" [t| String |] ]
250

    
251
-- | Tag set type alias.
252
type TagSet = Set.Set String
253

    
254
-- | Tag field description.
255
tagsFields :: [Field]
256
tagsFields = [ defaultField [| Set.empty |] $
257
               simpleField "tags" [t| TagSet |] ]
258

    
259
-- * Internal types
260

    
261
-- | A simple field, in constrast to the customisable 'Field' type.
262
type SimpleField = (String, Q Type)
263

    
264
-- | A definition for a single constructor for a simple object.
265
type SimpleConstructor = (String, [SimpleField])
266

    
267
-- | A definition for ADTs with simple fields.
268
type SimpleObject = [SimpleConstructor]
269

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

    
273
-- | A type alias for a Luxi constructor of a regular object.
274
type LuxiConstructor = (String, [Field])
275

    
276
-- * Helper functions
277

    
278
-- | Ensure first letter is lowercase.
279
--
280
-- Used to convert type name to function prefix, e.g. in @data Aa ->
281
-- aaToRaw@.
282
ensureLower :: String -> String
283
ensureLower [] = []
284
ensureLower (x:xs) = toLower x:xs
285

    
286
-- | Ensure first letter is uppercase.
287
--
288
-- Used to convert constructor name to component
289
ensureUpper :: String -> String
290
ensureUpper [] = []
291
ensureUpper (x:xs) = toUpper x:xs
292

    
293
-- | Helper for quoted expressions.
294
varNameE :: String -> Q Exp
295
varNameE = varE . mkName
296

    
297
-- | showJSON as an expression, for reuse.
298
showJSONE :: Q Exp
299
showJSONE = varE 'JSON.showJSON
300

    
301
-- | makeObj as an expression, for reuse.
302
makeObjE :: Q Exp
303
makeObjE = varE 'JSON.makeObj
304

    
305
-- | fromObj (Ganeti specific) as an expression, for reuse.
306
fromObjE :: Q Exp
307
fromObjE = varE 'fromObj
308

    
309
-- | ToRaw function name.
310
toRawName :: String -> Name
311
toRawName = mkName . (++ "ToRaw") . ensureLower
312

    
313
-- | FromRaw function name.
314
fromRawName :: String -> Name
315
fromRawName = mkName . (++ "FromRaw") . ensureLower
316

    
317
-- | Converts a name to it's varE\/litE representations.
318
reprE :: Either String Name -> Q Exp
319
reprE = either stringE varE
320

    
321
-- | Smarter function application.
322
--
323
-- This does simply f x, except that if is 'id', it will skip it, in
324
-- order to generate more readable code when using -ddump-splices.
325
appFn :: Exp -> Exp -> Exp
326
appFn f x | f == VarE 'id = x
327
          | otherwise = AppE f x
328

    
329
-- | Builds a field for a normal constructor.
330
buildConsField :: Q Type -> StrictTypeQ
331
buildConsField ftype = do
332
  ftype' <- ftype
333
  return (NotStrict, ftype')
334

    
335
-- | Builds a constructor based on a simple definition (not field-based).
336
buildSimpleCons :: Name -> SimpleObject -> Q Dec
337
buildSimpleCons tname cons = do
338
  decl_d <- mapM (\(cname, fields) -> do
339
                    fields' <- mapM (buildConsField . snd) fields
340
                    return $ NormalC (mkName cname) fields') cons
341
  return $ DataD [] tname [] decl_d [''Show, ''Eq]
342

    
343
-- | Generate the save function for a given type.
344
genSaveSimpleObj :: Name                            -- ^ Object type
345
                 -> String                          -- ^ Function name
346
                 -> SimpleObject                    -- ^ Object definition
347
                 -> (SimpleConstructor -> Q Clause) -- ^ Constructor save fn
348
                 -> Q (Dec, Dec)
349
genSaveSimpleObj tname sname opdefs fn = do
350
  let sigt = AppT (AppT ArrowT (ConT tname)) (ConT ''JSON.JSValue)
351
      fname = mkName sname
352
  cclauses <- mapM fn opdefs
353
  return $ (SigD fname sigt, FunD fname cclauses)
354

    
355
-- * Template code for simple raw type-equivalent ADTs
356

    
357
-- | Generates a data type declaration.
358
--
359
-- The type will have a fixed list of instances.
360
strADTDecl :: Name -> [String] -> Dec
361
strADTDecl name constructors =
362
  DataD [] name []
363
          (map (flip NormalC [] . mkName) constructors)
364
          [''Show, ''Eq, ''Enum, ''Bounded, ''Ord]
365

    
366
-- | Generates a toRaw function.
367
--
368
-- This generates a simple function of the form:
369
--
370
-- @
371
-- nameToRaw :: Name -> /traw/
372
-- nameToRaw Cons1 = var1
373
-- nameToRaw Cons2 = \"value2\"
374
-- @
375
genToRaw :: Name -> Name -> Name -> [(String, Either String Name)] -> Q [Dec]
376
genToRaw traw fname tname constructors = do
377
  let sigt = AppT (AppT ArrowT (ConT tname)) (ConT traw)
378
  -- the body clauses, matching on the constructor and returning the
379
  -- raw value
380
  clauses <- mapM  (\(c, v) -> clause [recP (mkName c) []]
381
                             (normalB (reprE v)) []) constructors
382
  return [SigD fname sigt, FunD fname clauses]
383

    
384
-- | Generates a fromRaw function.
385
--
386
-- The function generated is monadic and can fail parsing the
387
-- raw value. It is of the form:
388
--
389
-- @
390
-- nameFromRaw :: (Monad m) => /traw/ -> m Name
391
-- nameFromRaw s | s == var1       = Cons1
392
--               | s == \"value2\" = Cons2
393
--               | otherwise = fail /.../
394
-- @
395
genFromRaw :: Name -> Name -> Name -> [(String, Either String Name)] -> Q [Dec]
396
genFromRaw traw fname tname constructors = do
397
  -- signature of form (Monad m) => String -> m $name
398
  sigt <- [t| (Monad m) => $(conT traw) -> m $(conT tname) |]
399
  -- clauses for a guarded pattern
400
  let varp = mkName "s"
401
      varpe = varE varp
402
  clauses <- mapM (\(c, v) -> do
403
                     -- the clause match condition
404
                     g <- normalG [| $varpe == $(reprE v) |]
405
                     -- the clause result
406
                     r <- [| return $(conE (mkName c)) |]
407
                     return (g, r)) constructors
408
  -- the otherwise clause (fallback)
409
  oth_clause <- do
410
    g <- normalG [| otherwise |]
411
    r <- [|fail ("Invalid string value for type " ++
412
                 $(litE (stringL (nameBase tname))) ++ ": " ++ show $varpe) |]
413
    return (g, r)
414
  let fun = FunD fname [Clause [VarP varp]
415
                        (GuardedB (clauses++[oth_clause])) []]
416
  return [SigD fname sigt, fun]
417

    
418
-- | Generates a data type from a given raw format.
419
--
420
-- The format is expected to multiline. The first line contains the
421
-- type name, and the rest of the lines must contain two words: the
422
-- constructor name and then the string representation of the
423
-- respective constructor.
424
--
425
-- The function will generate the data type declaration, and then two
426
-- functions:
427
--
428
-- * /name/ToRaw, which converts the type to a raw type
429
--
430
-- * /name/FromRaw, which (monadically) converts from a raw type to the type
431
--
432
-- Note that this is basically just a custom show\/read instance,
433
-- nothing else.
434
declareADT
435
  :: (a -> Either String Name) -> Name -> String -> [(String, a)] -> Q [Dec]
436
declareADT fn traw sname cons = do
437
  let name = mkName sname
438
      ddecl = strADTDecl name (map fst cons)
439
      -- process cons in the format expected by genToRaw
440
      cons' = map (\(a, b) -> (a, fn b)) cons
441
  toraw <- genToRaw traw (toRawName sname) name cons'
442
  fromraw <- genFromRaw traw (fromRawName sname) name cons'
443
  return $ ddecl:toraw ++ fromraw
444

    
445
declareLADT :: Name -> String -> [(String, String)] -> Q [Dec]
446
declareLADT = declareADT Left
447

    
448
declareILADT :: String -> [(String, Int)] -> Q [Dec]
449
declareILADT sname cons = do
450
  consNames <- sequence [ newName ('_':n) | (n, _) <- cons ]
451
  consFns <- concat <$> sequence
452
             [ do sig <- sigD n [t| Int |]
453
                  let expr = litE (IntegerL (toInteger i))
454
                  fn <- funD n [clause [] (normalB expr) []]
455
                  return [sig, fn]
456
             | n <- consNames
457
             | (_, i) <- cons ]
458
  let cons' = [ (n, n') | (n, _) <- cons | n' <- consNames ]
459
  (consFns ++) <$> declareADT Right ''Int sname cons'
460

    
461
declareIADT :: String -> [(String, Name)] -> Q [Dec]
462
declareIADT = declareADT Right ''Int
463

    
464
declareSADT :: String -> [(String, Name)] -> Q [Dec]
465
declareSADT = declareADT Right ''String
466

    
467
-- | Creates the showJSON member of a JSON instance declaration.
468
--
469
-- This will create what is the equivalent of:
470
--
471
-- @
472
-- showJSON = showJSON . /name/ToRaw
473
-- @
474
--
475
-- in an instance JSON /name/ declaration
476
genShowJSON :: String -> Q Dec
477
genShowJSON name = do
478
  body <- [| JSON.showJSON . $(varE (toRawName name)) |]
479
  return $ FunD 'JSON.showJSON [Clause [] (NormalB body) []]
480

    
481
-- | Creates the readJSON member of a JSON instance declaration.
482
--
483
-- This will create what is the equivalent of:
484
--
485
-- @
486
-- readJSON s = case readJSON s of
487
--                Ok s' -> /name/FromRaw s'
488
--                Error e -> Error /description/
489
-- @
490
--
491
-- in an instance JSON /name/ declaration
492
genReadJSON :: String -> Q Dec
493
genReadJSON name = do
494
  let s = mkName "s"
495
  body <- [| case JSON.readJSON $(varE s) of
496
               JSON.Ok s' -> $(varE (fromRawName name)) s'
497
               JSON.Error e ->
498
                   JSON.Error $ "Can't parse raw value for type " ++
499
                           $(stringE name) ++ ": " ++ e ++ " from " ++
500
                           show $(varE s)
501
           |]
502
  return $ FunD 'JSON.readJSON [Clause [VarP s] (NormalB body) []]
503

    
504
-- | Generates a JSON instance for a given type.
505
--
506
-- This assumes that the /name/ToRaw and /name/FromRaw functions
507
-- have been defined as by the 'declareSADT' function.
508
makeJSONInstance :: Name -> Q [Dec]
509
makeJSONInstance name = do
510
  let base = nameBase name
511
  showJ <- genShowJSON base
512
  readJ <- genReadJSON base
513
  return [InstanceD [] (AppT (ConT ''JSON.JSON) (ConT name)) [readJ,showJ]]
514

    
515
-- * Template code for opcodes
516

    
517
-- | Transforms a CamelCase string into an_underscore_based_one.
518
deCamelCase :: String -> String
519
deCamelCase =
520
    intercalate "_" . map (map toUpper) . groupBy (\_ b -> not $ isUpper b)
521

    
522
-- | Transform an underscore_name into a CamelCase one.
523
camelCase :: String -> String
524
camelCase = concatMap (ensureUpper . drop 1) .
525
            groupBy (\_ b -> b /= '_' && b /= '-') . ('_':)
526

    
527
-- | Computes the name of a given constructor.
528
constructorName :: Con -> Q Name
529
constructorName (NormalC name _) = return name
530
constructorName (RecC name _)    = return name
531
constructorName x                = fail $ "Unhandled constructor " ++ show x
532

    
533
-- | Extract all constructor names from a given type.
534
reifyConsNames :: Name -> Q [String]
535
reifyConsNames name = do
536
  reify_result <- reify name
537
  case reify_result of
538
    TyConI (DataD _ _ _ cons _) -> mapM (liftM nameBase . constructorName) cons
539
    o -> fail $ "Unhandled name passed to reifyConsNames, expected\
540
                \ type constructor but got '" ++ show o ++ "'"
541

    
542
-- | Builds the generic constructor-to-string function.
543
--
544
-- This generates a simple function of the following form:
545
--
546
-- @
547
-- fname (ConStructorOne {}) = trans_fun("ConStructorOne")
548
-- fname (ConStructorTwo {}) = trans_fun("ConStructorTwo")
549
-- @
550
--
551
-- This builds a custom list of name\/string pairs and then uses
552
-- 'genToRaw' to actually generate the function.
553
genConstrToStr :: (String -> String) -> Name -> String -> Q [Dec]
554
genConstrToStr trans_fun name fname = do
555
  cnames <- reifyConsNames name
556
  let svalues = map (Left . trans_fun) cnames
557
  genToRaw ''String (mkName fname) name $ zip cnames svalues
558

    
559
-- | Constructor-to-string for OpCode.
560
genOpID :: Name -> String -> Q [Dec]
561
genOpID = genConstrToStr deCamelCase
562

    
563
-- | Builds a list with all defined constructor names for a type.
564
--
565
-- @
566
-- vstr :: String
567
-- vstr = [...]
568
-- @
569
--
570
-- Where the actual values of the string are the constructor names
571
-- mapped via @trans_fun@.
572
genAllConstr :: (String -> String) -> Name -> String -> Q [Dec]
573
genAllConstr trans_fun name vstr = do
574
  cnames <- reifyConsNames name
575
  let svalues = sort $ map trans_fun cnames
576
      vname = mkName vstr
577
      sig = SigD vname (AppT ListT (ConT ''String))
578
      body = NormalB (ListE (map (LitE . StringL) svalues))
579
  return $ [sig, ValD (VarP vname) body []]
580

    
581
-- | Generates a list of all defined opcode IDs.
582
genAllOpIDs :: Name -> String -> Q [Dec]
583
genAllOpIDs = genAllConstr deCamelCase
584

    
585
-- | OpCode parameter (field) type.
586
type OpParam = (String, Q Type, Q Exp)
587

    
588
-- * Python code generation
589

    
590
data OpCodeField = OpCodeField { ocfName :: String
591
                               , ocfType :: PyType
592
                               , ocfDefl :: Maybe PyValueEx
593
                               , ocfDoc  :: String
594
                               }
595

    
596
-- | Transfers opcode data between the opcode description (through
597
-- @genOpCode@) and the Python code generation functions.
598
data OpCodeDescriptor = OpCodeDescriptor { ocdName   :: String
599
                                         , ocdType   :: PyType
600
                                         , ocdDoc    :: String
601
                                         , ocdFields :: [OpCodeField]
602
                                         , ocdDescr  :: String
603
                                         }
604

    
605
-- | Optionally encapsulates default values in @PyValueEx@.
606
--
607
-- @maybeApp exp typ@ returns a quoted expression that encapsulates
608
-- the default value @exp@ of an opcode parameter cast to @typ@ in a
609
-- @PyValueEx@, if @exp@ is @Just@.  Otherwise, it returns a quoted
610
-- expression with @Nothing@.
611
maybeApp :: Maybe (Q Exp) -> Q Type -> Q Exp
612
maybeApp Nothing _ =
613
  [| Nothing |]
614

    
615
maybeApp (Just expr) typ =
616
  [| Just ($(conE (mkName "PyValueEx")) ($expr :: $typ)) |]
617

    
618
-- | Generates a Python type according to whether the field is
619
-- optional.
620
--
621
-- The type of created expression is PyType.
622
genPyType' :: OptionalType -> Q Type -> Q PyType
623
genPyType' opt typ = typ >>= pyOptionalType (opt /= NotOptional)
624

    
625
-- | Generates Python types from opcode parameters.
626
genPyType :: Field -> Q PyType
627
genPyType f = genPyType' (fieldIsOptional f) (fieldType f)
628

    
629
-- | Generates Python default values from opcode parameters.
630
genPyDefault :: Field -> Q Exp
631
genPyDefault f = maybeApp (fieldDefault f) (fieldType f)
632

    
633
pyField :: Field -> Q Exp
634
pyField f = genPyType f >>= \t ->
635
            [| OpCodeField $(stringE (fieldName f))
636
                           t
637
                           $(genPyDefault f)
638
                           $(stringE (fieldDoc f)) |]
639

    
640
-- | Generates a Haskell function call to "showPyClass" with the
641
-- necessary information on how to build the Python class string.
642
pyClass :: OpCodeConstructor -> Q Exp
643
pyClass (consName, consType, consDoc, consFields, consDscField) =
644
  do let pyClassVar = varNameE "showPyClass"
645
         consName' = stringE consName
646
     consType' <- genPyType' NotOptional consType
647
     let consDoc' = stringE consDoc
648
     [| OpCodeDescriptor $consName'
649
                         consType'
650
                         $consDoc'
651
                         $(listE $ map pyField consFields)
652
                         consDscField |]
653

    
654
-- | Generates a function called "pyClasses" that holds the list of
655
-- all the opcode descriptors necessary for generating the Python
656
-- opcodes.
657
pyClasses :: [OpCodeConstructor] -> Q [Dec]
658
pyClasses cons =
659
  do let name = mkName "pyClasses"
660
         sig = SigD name (AppT ListT (ConT ''OpCodeDescriptor))
661
     fn <- FunD name <$> (:[]) <$> declClause cons
662
     return [sig, fn]
663
  where declClause c =
664
          clause [] (normalB (ListE <$> mapM pyClass c)) []
665

    
666
-- | Converts from an opcode constructor to a Luxi constructor.
667
opcodeConsToLuxiCons :: (a, b, c, d, e) -> (a, d)
668
opcodeConsToLuxiCons (x, _, _, y, _) = (x, y)
669

    
670
-- | Generates the OpCode data type.
671
--
672
-- This takes an opcode logical definition, and builds both the
673
-- datatype and the JSON serialisation out of it. We can't use a
674
-- generic serialisation since we need to be compatible with Ganeti's
675
-- own, so we have a few quirks to work around.
676
genOpCode :: String              -- ^ Type name to use
677
          -> [OpCodeConstructor] -- ^ Constructor name and parameters
678
          -> Q [Dec]
679
genOpCode name cons = do
680
  let tname = mkName name
681
  decl_d <- mapM (\(cname, _, _, fields, _) -> do
682
                    -- we only need the type of the field, without Q
683
                    fields' <- mapM (fieldTypeInfo "op") fields
684
                    return $ RecC (mkName cname) fields')
685
            cons
686
  let declD = DataD [] tname [] decl_d [''Show, ''Eq]
687
  let (allfsig, allffn) = genAllOpFields "allOpFields" cons
688
  save_decs <- genSaveOpCode tname "saveOpCode" "toDictOpCode"
689
               (map opcodeConsToLuxiCons cons) saveConstructor True
690
  (loadsig, loadfn) <- genLoadOpCode cons
691
  pyDecls <- pyClasses cons
692
  return $ [declD, allfsig, allffn, loadsig, loadfn] ++ save_decs ++ pyDecls
693

    
694
-- | Generates the function pattern returning the list of fields for a
695
-- given constructor.
696
genOpConsFields :: OpCodeConstructor -> Clause
697
genOpConsFields (cname, _, _, fields, _) =
698
  let op_id = deCamelCase cname
699
      fvals = map (LitE . StringL) . sort . nub $
700
              concatMap (\f -> fieldName f:fieldExtraKeys f) fields
701
  in Clause [LitP (StringL op_id)] (NormalB $ ListE fvals) []
702

    
703
-- | Generates a list of all fields of an opcode constructor.
704
genAllOpFields  :: String              -- ^ Function name
705
                -> [OpCodeConstructor] -- ^ Object definition
706
                -> (Dec, Dec)
707
genAllOpFields sname opdefs =
708
  let cclauses = map genOpConsFields opdefs
709
      other = Clause [WildP] (NormalB (ListE [])) []
710
      fname = mkName sname
711
      sigt = AppT  (AppT ArrowT (ConT ''String)) (AppT ListT (ConT ''String))
712
  in (SigD fname sigt, FunD fname (cclauses++[other]))
713

    
714
-- | Generates the \"save\" clause for an entire opcode constructor.
715
--
716
-- This matches the opcode with variables named the same as the
717
-- constructor fields (just so that the spliced in code looks nicer),
718
-- and passes those name plus the parameter definition to 'saveObjectField'.
719
saveConstructor :: LuxiConstructor -- ^ The constructor
720
                -> Q Clause        -- ^ Resulting clause
721
saveConstructor (sname, fields) = do
722
  let cname = mkName sname
723
  fnames <- mapM (newName . fieldVariable) fields
724
  let pat = conP cname (map varP fnames)
725
  let felems = map (uncurry saveObjectField) (zip fnames fields)
726
      -- now build the OP_ID serialisation
727
      opid = [| [( $(stringE "OP_ID"),
728
                   JSON.showJSON $(stringE . deCamelCase $ sname) )] |]
729
      flist = listE (opid:felems)
730
      -- and finally convert all this to a json object
731
      flist' = [| concat $flist |]
732
  clause [pat] (normalB flist') []
733

    
734
-- | Generates the main save opcode function.
735
--
736
-- This builds a per-constructor match clause that contains the
737
-- respective constructor-serialisation code.
738
genSaveOpCode :: Name                          -- ^ Object ype
739
              -> String                        -- ^ To 'JSValue' function name
740
              -> String                        -- ^ To 'JSObject' function name
741
              -> [LuxiConstructor]             -- ^ Object definition
742
              -> (LuxiConstructor -> Q Clause) -- ^ Constructor save fn
743
              -> Bool                          -- ^ Whether to generate
744
                                               -- obj or just a
745
                                               -- list\/tuple of values
746
              -> Q [Dec]
747
genSaveOpCode tname jvalstr tdstr opdefs fn gen_object = do
748
  tdclauses <- mapM fn opdefs
749
  let typecon = ConT tname
750
      jvalname = mkName jvalstr
751
      jvalsig = AppT  (AppT ArrowT typecon) (ConT ''JSON.JSValue)
752
      tdname = mkName tdstr
753
  tdsig <- [t| $(return typecon) -> [(String, JSON.JSValue)] |]
754
  jvalclause <- if gen_object
755
                  then [| $makeObjE . $(varE tdname) |]
756
                  else [| JSON.showJSON . map snd . $(varE tdname) |]
757
  return [ SigD tdname tdsig
758
         , FunD tdname tdclauses
759
         , SigD jvalname jvalsig
760
         , ValD (VarP jvalname) (NormalB jvalclause) []]
761

    
762
-- | Generates load code for a single constructor of the opcode data type.
763
loadConstructor :: OpCodeConstructor -> Q Exp
764
loadConstructor (sname, _, _, fields, _) = do
765
  let name = mkName sname
766
  fbinds <- mapM loadObjectField fields
767
  let (fnames, fstmts) = unzip fbinds
768
  let cval = foldl (\accu fn -> AppE accu (VarE fn)) (ConE name) fnames
769
      fstmts' = fstmts ++ [NoBindS (AppE (VarE 'return) cval)]
770
  return $ DoE fstmts'
771

    
772
-- | Generates the loadOpCode function.
773
genLoadOpCode :: [OpCodeConstructor] -> Q (Dec, Dec)
774
genLoadOpCode opdefs = do
775
  let fname = mkName "loadOpCode"
776
      arg1 = mkName "v"
777
      objname = mkName "o"
778
      opid = mkName "op_id"
779
  st1 <- bindS (varP objname) [| liftM JSON.fromJSObject
780
                                 (JSON.readJSON $(varE arg1)) |]
781
  st2 <- bindS (varP opid) [| $fromObjE $(varE objname) $(stringE "OP_ID") |]
782
  -- the match results (per-constructor blocks)
783
  mexps <- mapM loadConstructor opdefs
784
  fails <- [| fail $ "Unknown opcode " ++ $(varE opid) |]
785
  let mpats = map (\(me, (consName, _, _, _, _)) ->
786
                       let mp = LitP . StringL . deCamelCase $ consName
787
                       in Match mp (NormalB me) []
788
                  ) $ zip mexps opdefs
789
      defmatch = Match WildP (NormalB fails) []
790
      cst = NoBindS $ CaseE (VarE opid) $ mpats++[defmatch]
791
      body = DoE [st1, st2, cst]
792
  sigt <- [t| JSON.JSValue -> JSON.Result $(conT (mkName "OpCode")) |]
793
  return $ (SigD fname sigt, FunD fname [Clause [VarP arg1] (NormalB body) []])
794

    
795
-- * Template code for luxi
796

    
797
-- | Constructor-to-string for LuxiOp.
798
genStrOfOp :: Name -> String -> Q [Dec]
799
genStrOfOp = genConstrToStr id
800

    
801
-- | Constructor-to-string for MsgKeys.
802
genStrOfKey :: Name -> String -> Q [Dec]
803
genStrOfKey = genConstrToStr ensureLower
804

    
805
-- | Generates the LuxiOp data type.
806
--
807
-- This takes a Luxi operation definition and builds both the
808
-- datatype and the function transforming the arguments to JSON.
809
-- We can't use anything less generic, because the way different
810
-- operations are serialized differs on both parameter- and top-level.
811
--
812
-- There are two things to be defined for each parameter:
813
--
814
-- * name
815
--
816
-- * type
817
--
818
genLuxiOp :: String -> [LuxiConstructor] -> Q [Dec]
819
genLuxiOp name cons = do
820
  let tname = mkName name
821
  decl_d <- mapM (\(cname, fields) -> do
822
                    -- we only need the type of the field, without Q
823
                    fields' <- mapM actualFieldType fields
824
                    let fields'' = zip (repeat NotStrict) fields'
825
                    return $ NormalC (mkName cname) fields'')
826
            cons
827
  let declD = DataD [] (mkName name) [] decl_d [''Show, ''Eq]
828
  save_decs <- genSaveOpCode tname "opToArgs" "opToDict"
829
               cons saveLuxiConstructor False
830
  req_defs <- declareSADT "LuxiReq" .
831
              map (\(str, _) -> ("Req" ++ str, mkName ("luxiReq" ++ str))) $
832
                  cons
833
  return $ declD:save_decs ++ req_defs
834

    
835
-- | Generates the \"save\" clause for entire LuxiOp constructor.
836
saveLuxiConstructor :: LuxiConstructor -> Q Clause
837
saveLuxiConstructor (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 = map (uncurry saveObjectField) (zip fnames fields)
842
      flist = [| concat $(listE felems) |]
843
  clause [pat] (normalB flist) []
844

    
845
-- * "Objects" functionality
846

    
847
-- | Extract the field's declaration from a Field structure.
848
fieldTypeInfo :: String -> Field -> Q (Name, Strict, Type)
849
fieldTypeInfo field_pfx fd = do
850
  t <- actualFieldType fd
851
  let n = mkName . (field_pfx ++) . fieldRecordName $ fd
852
  return (n, NotStrict, t)
853

    
854
-- | Build an object declaration.
855
buildObject :: String -> String -> [Field] -> Q [Dec]
856
buildObject sname field_pfx fields = do
857
  let name = mkName sname
858
  fields_d <- mapM (fieldTypeInfo field_pfx) fields
859
  let decl_d = RecC name fields_d
860
  let declD = DataD [] name [] [decl_d] [''Show, ''Eq]
861
  ser_decls <- buildObjectSerialisation sname fields
862
  return $ declD:ser_decls
863

    
864
-- | Generates an object definition: data type and its JSON instance.
865
buildObjectSerialisation :: String -> [Field] -> Q [Dec]
866
buildObjectSerialisation sname fields = do
867
  let name = mkName sname
868
  savedecls <- genSaveObject saveObjectField sname fields
869
  (loadsig, loadfn) <- genLoadObject loadObjectField sname fields
870
  shjson <- objectShowJSON sname
871
  rdjson <- objectReadJSON sname
872
  let instdecl = InstanceD [] (AppT (ConT ''JSON.JSON) (ConT name))
873
                 [rdjson, shjson]
874
  return $ savedecls ++ [loadsig, loadfn, instdecl]
875

    
876
-- | The toDict function name for a given type.
877
toDictName :: String -> Name
878
toDictName sname = mkName ("toDict" ++ sname)
879

    
880
-- | Generates the save object functionality.
881
genSaveObject :: (Name -> Field -> Q Exp)
882
              -> String -> [Field] -> Q [Dec]
883
genSaveObject save_fn sname fields = do
884
  let name = mkName sname
885
  fnames <- mapM (newName . fieldVariable) fields
886
  let pat = conP name (map varP fnames)
887
  let tdname = toDictName sname
888
  tdsigt <- [t| $(conT name) -> [(String, JSON.JSValue)] |]
889

    
890
  let felems = map (uncurry save_fn) (zip fnames fields)
891
      flist = listE felems
892
      -- and finally convert all this to a json object
893
      tdlist = [| concat $flist |]
894
      iname = mkName "i"
895
  tclause <- clause [pat] (normalB tdlist) []
896
  cclause <- [| $makeObjE . $(varE tdname) |]
897
  let fname = mkName ("save" ++ sname)
898
  sigt <- [t| $(conT name) -> JSON.JSValue |]
899
  return [SigD tdname tdsigt, FunD tdname [tclause],
900
          SigD fname sigt, ValD (VarP fname) (NormalB cclause) []]
901

    
902
-- | Generates the code for saving an object's field, handling the
903
-- various types of fields that we have.
904
saveObjectField :: Name -> Field -> Q Exp
905
saveObjectField fvar field =
906
  case fieldIsOptional field of
907
    OptionalOmitNull -> [| case $(varE fvar) of
908
                             Nothing -> []
909
                             Just v  -> [( $nameE, JSON.showJSON v )]
910
                         |]
911
    OptionalSerializeNull -> [| case $(varE fvar) of
912
                                  Nothing -> [( $nameE, JSON.JSNull )]
913
                                  Just v  -> [( $nameE, JSON.showJSON v )]
914
                              |]
915
    NotOptional ->
916
      case fieldShow field of
917
        -- Note: the order of actual:extra is important, since for
918
        -- some serialisation types (e.g. Luxi), we use tuples
919
        -- (positional info) rather than object (name info)
920
        Nothing -> [| [( $nameE, JSON.showJSON $fvarE)] |]
921
        Just fn -> [| let (actual, extra) = $fn $fvarE
922
                      in ($nameE, JSON.showJSON actual):extra
923
                    |]
924
  where nameE = stringE (fieldName field)
925
        fvarE = varE fvar
926

    
927
-- | Generates the showJSON clause for a given object name.
928
objectShowJSON :: String -> Q Dec
929
objectShowJSON name = do
930
  body <- [| JSON.showJSON . $(varE . mkName $ "save" ++ name) |]
931
  return $ FunD 'JSON.showJSON [Clause [] (NormalB body) []]
932

    
933
-- | Generates the load object functionality.
934
genLoadObject :: (Field -> Q (Name, Stmt))
935
              -> String -> [Field] -> Q (Dec, Dec)
936
genLoadObject load_fn sname fields = do
937
  let name = mkName sname
938
      funname = mkName $ "load" ++ sname
939
      arg1 = mkName $ if null fields then "_" else "v"
940
      objname = mkName "o"
941
      opid = mkName "op_id"
942
  st1 <- bindS (varP objname) [| liftM JSON.fromJSObject
943
                                 (JSON.readJSON $(varE arg1)) |]
944
  fbinds <- mapM load_fn fields
945
  let (fnames, fstmts) = unzip fbinds
946
  let cval = foldl (\accu fn -> AppE accu (VarE fn)) (ConE name) fnames
947
      retstmt = [NoBindS (AppE (VarE 'return) cval)]
948
      -- FIXME: should we require an empty dict for an empty type?
949
      -- this allows any JSValue right now
950
      fstmts' = if null fields
951
                  then retstmt
952
                  else st1:fstmts ++ retstmt
953
  sigt <- [t| JSON.JSValue -> JSON.Result $(conT name) |]
954
  return $ (SigD funname sigt,
955
            FunD funname [Clause [VarP arg1] (NormalB (DoE fstmts')) []])
956

    
957
-- | Generates code for loading an object's field.
958
loadObjectField :: Field -> Q (Name, Stmt)
959
loadObjectField field = do
960
  let name = fieldVariable field
961
  fvar <- newName name
962
  -- these are used in all patterns below
963
  let objvar = varNameE "o"
964
      objfield = stringE (fieldName field)
965
      loadexp =
966
        if fieldIsOptional field /= NotOptional
967
          -- we treat both optional types the same, since
968
          -- 'maybeFromObj' can deal with both missing and null values
969
          -- appropriately (the same)
970
          then [| $(varE 'maybeFromObj) $objvar $objfield |]
971
          else case fieldDefault field of
972
                 Just defv ->
973
                   [| $(varE 'fromObjWithDefault) $objvar
974
                      $objfield $defv |]
975
                 Nothing -> [| $fromObjE $objvar $objfield |]
976
  bexp <- loadFn field loadexp objvar
977

    
978
  return (fvar, BindS (VarP fvar) bexp)
979

    
980
-- | Builds the readJSON instance for a given object name.
981
objectReadJSON :: String -> Q Dec
982
objectReadJSON name = do
983
  let s = mkName "s"
984
  body <- [| case JSON.readJSON $(varE s) of
985
               JSON.Ok s' -> $(varE .mkName $ "load" ++ name) s'
986
               JSON.Error e ->
987
                 JSON.Error $ "Can't parse value for type " ++
988
                       $(stringE name) ++ ": " ++ e
989
           |]
990
  return $ FunD 'JSON.readJSON [Clause [VarP s] (NormalB body) []]
991

    
992
-- * Inheritable parameter tables implementation
993

    
994
-- | Compute parameter type names.
995
paramTypeNames :: String -> (String, String)
996
paramTypeNames root = ("Filled"  ++ root ++ "Params",
997
                       "Partial" ++ root ++ "Params")
998

    
999
-- | Compute information about the type of a parameter field.
1000
paramFieldTypeInfo :: String -> Field -> Q (Name, Strict, Type)
1001
paramFieldTypeInfo field_pfx fd = do
1002
  t <- actualFieldType fd
1003
  let n = mkName . (++ "P") . (field_pfx ++) .
1004
          fieldRecordName $ fd
1005
  return (n, NotStrict, AppT (ConT ''Maybe) t)
1006

    
1007
-- | Build a parameter declaration.
1008
--
1009
-- This function builds two different data structures: a /filled/ one,
1010
-- in which all fields are required, and a /partial/ one, in which all
1011
-- fields are optional. Due to the current record syntax issues, the
1012
-- fields need to be named differrently for the two structures, so the
1013
-- partial ones get a /P/ suffix.
1014
buildParam :: String -> String -> [Field] -> Q [Dec]
1015
buildParam sname field_pfx fields = do
1016
  let (sname_f, sname_p) = paramTypeNames sname
1017
      name_f = mkName sname_f
1018
      name_p = mkName sname_p
1019
  fields_f <- mapM (fieldTypeInfo field_pfx) fields
1020
  fields_p <- mapM (paramFieldTypeInfo field_pfx) fields
1021
  let decl_f = RecC name_f fields_f
1022
      decl_p = RecC name_p fields_p
1023
  let declF = DataD [] name_f [] [decl_f] [''Show, ''Eq]
1024
      declP = DataD [] name_p [] [decl_p] [''Show, ''Eq]
1025
  ser_decls_f <- buildObjectSerialisation sname_f fields
1026
  ser_decls_p <- buildPParamSerialisation sname_p fields
1027
  fill_decls <- fillParam sname field_pfx fields
1028
  return $ [declF, declP] ++ ser_decls_f ++ ser_decls_p ++ fill_decls ++
1029
           buildParamAllFields sname fields ++
1030
           buildDictObjectInst name_f sname_f
1031

    
1032
-- | Builds a list of all fields of a parameter.
1033
buildParamAllFields :: String -> [Field] -> [Dec]
1034
buildParamAllFields sname fields =
1035
  let vname = mkName ("all" ++ sname ++ "ParamFields")
1036
      sig = SigD vname (AppT ListT (ConT ''String))
1037
      val = ListE $ map (LitE . StringL . fieldName) fields
1038
  in [sig, ValD (VarP vname) (NormalB val) []]
1039

    
1040
-- | Builds the 'DictObject' instance for a filled parameter.
1041
buildDictObjectInst :: Name -> String -> [Dec]
1042
buildDictObjectInst name sname =
1043
  [InstanceD [] (AppT (ConT ''DictObject) (ConT name))
1044
   [ValD (VarP 'toDict) (NormalB (VarE (toDictName sname))) []]]
1045

    
1046
-- | Generates the serialisation for a partial parameter.
1047
buildPParamSerialisation :: String -> [Field] -> Q [Dec]
1048
buildPParamSerialisation sname fields = do
1049
  let name = mkName sname
1050
  savedecls <- genSaveObject savePParamField sname fields
1051
  (loadsig, loadfn) <- genLoadObject loadPParamField sname fields
1052
  shjson <- objectShowJSON sname
1053
  rdjson <- objectReadJSON sname
1054
  let instdecl = InstanceD [] (AppT (ConT ''JSON.JSON) (ConT name))
1055
                 [rdjson, shjson]
1056
  return $ savedecls ++ [loadsig, loadfn, instdecl]
1057

    
1058
-- | Generates code to save an optional parameter field.
1059
savePParamField :: Name -> Field -> Q Exp
1060
savePParamField fvar field = do
1061
  checkNonOptDef field
1062
  let actualVal = mkName "v"
1063
  normalexpr <- saveObjectField actualVal field
1064
  -- we have to construct the block here manually, because we can't
1065
  -- splice-in-splice
1066
  return $ CaseE (VarE fvar) [ Match (ConP 'Nothing [])
1067
                                       (NormalB (ConE '[])) []
1068
                             , Match (ConP 'Just [VarP actualVal])
1069
                                       (NormalB normalexpr) []
1070
                             ]
1071

    
1072
-- | Generates code to load an optional parameter field.
1073
loadPParamField :: Field -> Q (Name, Stmt)
1074
loadPParamField field = do
1075
  checkNonOptDef field
1076
  let name = fieldName field
1077
  fvar <- newName name
1078
  -- these are used in all patterns below
1079
  let objvar = varNameE "o"
1080
      objfield = stringE name
1081
      loadexp = [| $(varE 'maybeFromObj) $objvar $objfield |]
1082
      field' = field {fieldRead=fmap (appE (varE 'makeReadOptional))
1083
                                  $ fieldRead field}
1084
  bexp <- loadFn field' loadexp objvar
1085
  return (fvar, BindS (VarP fvar) bexp)
1086

    
1087
-- | Builds a simple declaration of type @n_x = fromMaybe f_x p_x@.
1088
buildFromMaybe :: String -> Q Dec
1089
buildFromMaybe fname =
1090
  valD (varP (mkName $ "n_" ++ fname))
1091
         (normalB [| $(varE 'fromMaybe)
1092
                        $(varNameE $ "f_" ++ fname)
1093
                        $(varNameE $ "p_" ++ fname) |]) []
1094

    
1095
-- | Builds a function that executes the filling of partial parameter
1096
-- from a full copy (similar to Python's fillDict).
1097
fillParam :: String -> String -> [Field] -> Q [Dec]
1098
fillParam sname field_pfx fields = do
1099
  let fnames = map (\fd -> field_pfx ++ fieldRecordName fd) fields
1100
      (sname_f, sname_p) = paramTypeNames sname
1101
      oname_f = "fobj"
1102
      oname_p = "pobj"
1103
      name_f = mkName sname_f
1104
      name_p = mkName sname_p
1105
      fun_name = mkName $ "fill" ++ sname ++ "Params"
1106
      le_full = ValD (ConP name_f (map (VarP . mkName . ("f_" ++)) fnames))
1107
                (NormalB . VarE . mkName $ oname_f) []
1108
      le_part = ValD (ConP name_p (map (VarP . mkName . ("p_" ++)) fnames))
1109
                (NormalB . VarE . mkName $ oname_p) []
1110
      obj_new = foldl (\accu vname -> AppE accu (VarE vname)) (ConE name_f)
1111
                $ map (mkName . ("n_" ++)) fnames
1112
  le_new <- mapM buildFromMaybe fnames
1113
  funt <- [t| $(conT name_f) -> $(conT name_p) -> $(conT name_f) |]
1114
  let sig = SigD fun_name funt
1115
      fclause = Clause [VarP (mkName oname_f), VarP (mkName oname_p)]
1116
                (NormalB $ LetE (le_full:le_part:le_new) obj_new) []
1117
      fun = FunD fun_name [fclause]
1118
  return [sig, fun]
1119

    
1120
-- * Template code for exceptions
1121

    
1122
-- | Exception simple error message field.
1123
excErrMsg :: (String, Q Type)
1124
excErrMsg = ("errMsg", [t| String |])
1125

    
1126
-- | Builds an exception type definition.
1127
genException :: String                  -- ^ Name of new type
1128
             -> SimpleObject -- ^ Constructor name and parameters
1129
             -> Q [Dec]
1130
genException name cons = do
1131
  let tname = mkName name
1132
  declD <- buildSimpleCons tname cons
1133
  (savesig, savefn) <- genSaveSimpleObj tname ("save" ++ name) cons $
1134
                         uncurry saveExcCons
1135
  (loadsig, loadfn) <- genLoadExc tname ("load" ++ name) cons
1136
  return [declD, loadsig, loadfn, savesig, savefn]
1137

    
1138
-- | Generates the \"save\" clause for an entire exception constructor.
1139
--
1140
-- This matches the exception with variables named the same as the
1141
-- constructor fields (just so that the spliced in code looks nicer),
1142
-- and calls showJSON on it.
1143
saveExcCons :: String        -- ^ The constructor name
1144
            -> [SimpleField] -- ^ The parameter definitions for this
1145
                             -- constructor
1146
            -> Q Clause      -- ^ Resulting clause
1147
saveExcCons sname fields = do
1148
  let cname = mkName sname
1149
  fnames <- mapM (newName . fst) fields
1150
  let pat = conP cname (map varP fnames)
1151
      felems = if null fnames
1152
                 then conE '() -- otherwise, empty list has no type
1153
                 else listE $ map (\f -> [| JSON.showJSON $(varE f) |]) fnames
1154
  let tup = tupE [ litE (stringL sname), felems ]
1155
  clause [pat] (normalB [| JSON.showJSON $tup |]) []
1156

    
1157
-- | Generates load code for a single constructor of an exception.
1158
--
1159
-- Generates the code (if there's only one argument, we will use a
1160
-- list, not a tuple:
1161
--
1162
-- @
1163
-- do
1164
--  (x1, x2, ...) <- readJSON args
1165
--  return $ Cons x1 x2 ...
1166
-- @
1167
loadExcConstructor :: Name -> String -> [SimpleField] -> Q Exp
1168
loadExcConstructor inname sname fields = do
1169
  let name = mkName sname
1170
  f_names <- mapM (newName . fst) fields
1171
  let read_args = AppE (VarE 'JSON.readJSON) (VarE inname)
1172
  let binds = case f_names of
1173
                [x] -> BindS (ListP [VarP x])
1174
                _   -> BindS (TupP (map VarP f_names))
1175
      cval = foldl (\accu fn -> AppE accu (VarE fn)) (ConE name) f_names
1176
  return $ DoE [binds read_args, NoBindS (AppE (VarE 'return) cval)]
1177

    
1178
{-| Generates the loadException function.
1179

    
1180
This generates a quite complicated function, along the lines of:
1181

    
1182
@
1183
loadFn (JSArray [JSString name, args]) = case name of
1184
   "A1" -> do
1185
     (x1, x2, ...) <- readJSON args
1186
     return $ A1 x1 x2 ...
1187
   "a2" -> ...
1188
   s -> fail $ "Unknown exception" ++ s
1189
loadFn v = fail $ "Expected array but got " ++ show v
1190
@
1191
-}
1192
genLoadExc :: Name -> String -> SimpleObject -> Q (Dec, Dec)
1193
genLoadExc tname sname opdefs = do
1194
  let fname = mkName sname
1195
  exc_name <- newName "name"
1196
  exc_args <- newName "args"
1197
  exc_else <- newName "s"
1198
  arg_else <- newName "v"
1199
  fails <- [| fail $ "Unknown exception '" ++ $(varE exc_else) ++ "'" |]
1200
  -- default match for unknown exception name
1201
  let defmatch = Match (VarP exc_else) (NormalB fails) []
1202
  -- the match results (per-constructor blocks)
1203
  str_matches <-
1204
    mapM (\(s, params) -> do
1205
            body_exp <- loadExcConstructor exc_args s params
1206
            return $ Match (LitP (StringL s)) (NormalB body_exp) [])
1207
    opdefs
1208
  -- the first function clause; we can't use [| |] due to TH
1209
  -- limitations, so we have to build the AST by hand
1210
  let clause1 = Clause [ConP 'JSON.JSArray
1211
                               [ListP [ConP 'JSON.JSString [VarP exc_name],
1212
                                            VarP exc_args]]]
1213
                (NormalB (CaseE (AppE (VarE 'JSON.fromJSString)
1214
                                        (VarE exc_name))
1215
                          (str_matches ++ [defmatch]))) []
1216
  -- the fail expression for the second function clause
1217
  fail_type <- [| fail $ "Invalid exception: expected '(string, [args])' " ++
1218
                  "      but got " ++ show (pp_value $(varE arg_else)) ++ "'"
1219
                |]
1220
  -- the second function clause
1221
  let clause2 = Clause [VarP arg_else] (NormalB fail_type) []
1222
  sigt <- [t| JSON.JSValue -> JSON.Result $(conT tname) |]
1223
  return $ (SigD fname sigt, FunD fname [clause1, clause2])