Statistics
| Branch: | Tag: | Revision:

root / src / Ganeti / THH.hs @ 4651c69f

History | View | Annotate | Download (46.1 kB)

1
{-# LANGUAGE ExistentialQuantification, 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 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
                  , declareIADT
34
                  , makeJSONInstance
35
                  , deCamelCase
36
                  , genOpID
37
                  , genAllConstr
38
                  , genAllOpIDs
39
                  , PyValue(..)
40
                  , PyValueEx(..)
41
                  , OpCodeDescriptor
42
                  , genOpCode
43
                  , genStrOfOp
44
                  , genStrOfKey
45
                  , genLuxiOp
46
                  , Field (..)
47
                  , simpleField
48
                  , withDoc
49
                  , defaultField
50
                  , optionalField
51
                  , optionalNullSerField
52
                  , renameField
53
                  , customField
54
                  , timeStampFields
55
                  , uuidFields
56
                  , serialFields
57
                  , tagsFields
58
                  , TagSet
59
                  , buildObject
60
                  , buildObjectSerialisation
61
                  , buildParam
62
                  , DictObject(..)
63
                  , genException
64
                  , excErrMsg
65
                  ) where
66

    
67
import Control.Monad (liftM)
68
import Data.Char
69
import Data.List
70
import qualified Data.Set as Set
71
import Language.Haskell.TH
72

    
73
import qualified Text.JSON as JSON
74
import Text.JSON.Pretty (pp_value)
75

    
76
import Ganeti.JSON
77

    
78
import Data.Maybe
79
import Data.Functor ((<$>))
80

    
81
-- * Exported types
82

    
83
-- | Class of objects that can be converted to 'JSObject'
84
-- lists-format.
85
class DictObject a where
86
  toDict :: a -> [(String, JSON.JSValue)]
87

    
88
-- | Optional field information.
89
data OptionalType
90
  = NotOptional           -- ^ Field is not optional
91
  | OptionalOmitNull      -- ^ Field is optional, null is not serialised
92
  | OptionalSerializeNull -- ^ Field is optional, null is serialised
93
  deriving (Show, Eq)
94

    
95
-- | Serialised field data type.
96
data Field = Field { fieldName        :: String
97
                   , fieldType        :: Q Type
98
                   , fieldRead        :: Maybe (Q Exp)
99
                   , fieldShow        :: Maybe (Q Exp)
100
                   , fieldExtraKeys   :: [String]
101
                   , fieldDefault     :: Maybe (Q Exp)
102
                   , fieldConstr      :: Maybe String
103
                   , fieldIsOptional  :: OptionalType
104
                   , fieldDoc         :: String
105
                   }
106

    
107
-- | Generates a simple field.
108
simpleField :: String -> Q Type -> Field
109
simpleField fname ftype =
110
  Field { fieldName        = fname
111
        , fieldType        = ftype
112
        , fieldRead        = Nothing
113
        , fieldShow        = Nothing
114
        , fieldExtraKeys   = []
115
        , fieldDefault     = Nothing
116
        , fieldConstr      = Nothing
117
        , fieldIsOptional  = NotOptional
118
        , fieldDoc         = ""
119
        }
120

    
121
withDoc :: String -> Field -> Field
122
withDoc doc field =
123
  field { fieldDoc = doc }
124

    
125
-- | Sets the renamed constructor field.
126
renameField :: String -> Field -> Field
127
renameField constrName field = field { fieldConstr = Just constrName }
128

    
129
-- | Sets the default value on a field (makes it optional with a
130
-- default value).
131
defaultField :: Q Exp -> Field -> Field
132
defaultField defval field = field { fieldDefault = Just defval }
133

    
134
-- | Marks a field optional (turning its base type into a Maybe).
135
optionalField :: Field -> Field
136
optionalField field = field { fieldIsOptional = OptionalOmitNull }
137

    
138
-- | Marks a field optional (turning its base type into a Maybe), but
139
-- with 'Nothing' serialised explicitly as /null/.
140
optionalNullSerField :: Field -> Field
141
optionalNullSerField field = field { fieldIsOptional = OptionalSerializeNull }
142

    
143
-- | Sets custom functions on a field.
144
customField :: Name      -- ^ The name of the read function
145
            -> Name      -- ^ The name of the show function
146
            -> [String]  -- ^ The name of extra field keys
147
            -> Field     -- ^ The original field
148
            -> Field     -- ^ Updated field
149
customField readfn showfn extra field =
150
  field { fieldRead = Just (varE readfn), fieldShow = Just (varE showfn)
151
        , fieldExtraKeys = extra }
152

    
153
-- | Computes the record name for a given field, based on either the
154
-- string value in the JSON serialisation or the custom named if any
155
-- exists.
156
fieldRecordName :: Field -> String
157
fieldRecordName (Field { fieldName = name, fieldConstr = alias }) =
158
  fromMaybe (camelCase name) alias
159

    
160
-- | Computes the preferred variable name to use for the value of this
161
-- field. If the field has a specific constructor name, then we use a
162
-- first-letter-lowercased version of that; otherwise, we simply use
163
-- the field name. See also 'fieldRecordName'.
164
fieldVariable :: Field -> String
165
fieldVariable f =
166
  case (fieldConstr f) of
167
    Just name -> ensureLower name
168
    _ -> map (\c -> if c == '-' then '_' else c) $ fieldName f
169

    
170
-- | Compute the actual field type (taking into account possible
171
-- optional status).
172
actualFieldType :: Field -> Q Type
173
actualFieldType f | fieldIsOptional f /= NotOptional = [t| Maybe $t |]
174
                  | otherwise = t
175
                  where t = fieldType f
176

    
177
-- | Checks that a given field is not optional (for object types or
178
-- fields which should not allow this case).
179
checkNonOptDef :: (Monad m) => Field -> m ()
180
checkNonOptDef (Field { fieldIsOptional = OptionalOmitNull
181
                      , fieldName = name }) =
182
  fail $ "Optional field " ++ name ++ " used in parameter declaration"
183
checkNonOptDef (Field { fieldIsOptional = OptionalSerializeNull
184
                      , fieldName = name }) =
185
  fail $ "Optional field " ++ name ++ " used in parameter declaration"
186
checkNonOptDef (Field { fieldDefault = (Just _), fieldName = name }) =
187
  fail $ "Default field " ++ name ++ " used in parameter declaration"
188
checkNonOptDef _ = return ()
189

    
190
-- | Produces the expression that will de-serialise a given
191
-- field. Since some custom parsing functions might need to use the
192
-- entire object, we do take and pass the object to any custom read
193
-- functions.
194
loadFn :: Field   -- ^ The field definition
195
       -> Q Exp   -- ^ The value of the field as existing in the JSON message
196
       -> Q Exp   -- ^ The entire object in JSON object format
197
       -> Q Exp   -- ^ Resulting expression
198
loadFn (Field { fieldRead = Just readfn }) expr o = [| $expr >>= $readfn $o |]
199
loadFn _ expr _ = expr
200

    
201
-- * Common field declarations
202

    
203
-- | Timestamp fields description.
204
timeStampFields :: [Field]
205
timeStampFields =
206
    [ defaultField [| 0::Double |] $ simpleField "ctime" [t| Double |]
207
    , defaultField [| 0::Double |] $ simpleField "mtime" [t| Double |]
208
    ]
209

    
210
-- | Serial number fields description.
211
serialFields :: [Field]
212
serialFields =
213
    [ renameField  "Serial" $ simpleField "serial_no" [t| Int |] ]
214

    
215
-- | UUID fields description.
216
uuidFields :: [Field]
217
uuidFields = [ simpleField "uuid" [t| String |] ]
218

    
219
-- | Tag set type alias.
220
type TagSet = Set.Set String
221

    
222
-- | Tag field description.
223
tagsFields :: [Field]
224
tagsFields = [ defaultField [| Set.empty |] $
225
               simpleField "tags" [t| TagSet |] ]
226

    
227
-- * Internal types
228

    
229
-- | A simple field, in constrast to the customisable 'Field' type.
230
type SimpleField = (String, Q Type)
231

    
232
-- | A definition for a single constructor for a simple object.
233
type SimpleConstructor = (String, [SimpleField])
234

    
235
-- | A definition for ADTs with simple fields.
236
type SimpleObject = [SimpleConstructor]
237

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

    
241
-- | A type alias for a Luxi constructor of a regular object.
242
type LuxiConstructor = (String, [Field])
243

    
244
-- * Helper functions
245

    
246
-- | Ensure first letter is lowercase.
247
--
248
-- Used to convert type name to function prefix, e.g. in @data Aa ->
249
-- aaToRaw@.
250
ensureLower :: String -> String
251
ensureLower [] = []
252
ensureLower (x:xs) = toLower x:xs
253

    
254
-- | Ensure first letter is uppercase.
255
--
256
-- Used to convert constructor name to component
257
ensureUpper :: String -> String
258
ensureUpper [] = []
259
ensureUpper (x:xs) = toUpper x:xs
260

    
261
-- | Helper for quoted expressions.
262
varNameE :: String -> Q Exp
263
varNameE = varE . mkName
264

    
265
-- | showJSON as an expression, for reuse.
266
showJSONE :: Q Exp
267
showJSONE = varE 'JSON.showJSON
268

    
269
-- | makeObj as an expression, for reuse.
270
makeObjE :: Q Exp
271
makeObjE = varE 'JSON.makeObj
272

    
273
-- | fromObj (Ganeti specific) as an expression, for reuse.
274
fromObjE :: Q Exp
275
fromObjE = varE 'fromObj
276

    
277
-- | ToRaw function name.
278
toRawName :: String -> Name
279
toRawName = mkName . (++ "ToRaw") . ensureLower
280

    
281
-- | FromRaw function name.
282
fromRawName :: String -> Name
283
fromRawName = mkName . (++ "FromRaw") . ensureLower
284

    
285
-- | Converts a name to it's varE\/litE representations.
286
reprE :: Either String Name -> Q Exp
287
reprE = either stringE varE
288

    
289
-- | Smarter function application.
290
--
291
-- This does simply f x, except that if is 'id', it will skip it, in
292
-- order to generate more readable code when using -ddump-splices.
293
appFn :: Exp -> Exp -> Exp
294
appFn f x | f == VarE 'id = x
295
          | otherwise = AppE f x
296

    
297
-- | Builds a field for a normal constructor.
298
buildConsField :: Q Type -> StrictTypeQ
299
buildConsField ftype = do
300
  ftype' <- ftype
301
  return (NotStrict, ftype')
302

    
303
-- | Builds a constructor based on a simple definition (not field-based).
304
buildSimpleCons :: Name -> SimpleObject -> Q Dec
305
buildSimpleCons tname cons = do
306
  decl_d <- mapM (\(cname, fields) -> do
307
                    fields' <- mapM (buildConsField . snd) fields
308
                    return $ NormalC (mkName cname) fields') cons
309
  return $ DataD [] tname [] decl_d [''Show, ''Eq]
310

    
311
-- | Generate the save function for a given type.
312
genSaveSimpleObj :: Name                            -- ^ Object type
313
                 -> String                          -- ^ Function name
314
                 -> SimpleObject                    -- ^ Object definition
315
                 -> (SimpleConstructor -> Q Clause) -- ^ Constructor save fn
316
                 -> Q (Dec, Dec)
317
genSaveSimpleObj tname sname opdefs fn = do
318
  let sigt = AppT (AppT ArrowT (ConT tname)) (ConT ''JSON.JSValue)
319
      fname = mkName sname
320
  cclauses <- mapM fn opdefs
321
  return $ (SigD fname sigt, FunD fname cclauses)
322

    
323
-- * Template code for simple raw type-equivalent ADTs
324

    
325
-- | Generates a data type declaration.
326
--
327
-- The type will have a fixed list of instances.
328
strADTDecl :: Name -> [String] -> Dec
329
strADTDecl name constructors =
330
  DataD [] name []
331
          (map (flip NormalC [] . mkName) constructors)
332
          [''Show, ''Eq, ''Enum, ''Bounded, ''Ord]
333

    
334
-- | Generates a toRaw function.
335
--
336
-- This generates a simple function of the form:
337
--
338
-- @
339
-- nameToRaw :: Name -> /traw/
340
-- nameToRaw Cons1 = var1
341
-- nameToRaw Cons2 = \"value2\"
342
-- @
343
genToRaw :: Name -> Name -> Name -> [(String, Either String Name)] -> Q [Dec]
344
genToRaw traw fname tname constructors = do
345
  let sigt = AppT (AppT ArrowT (ConT tname)) (ConT traw)
346
  -- the body clauses, matching on the constructor and returning the
347
  -- raw value
348
  clauses <- mapM  (\(c, v) -> clause [recP (mkName c) []]
349
                             (normalB (reprE v)) []) constructors
350
  return [SigD fname sigt, FunD fname clauses]
351

    
352
-- | Generates a fromRaw function.
353
--
354
-- The function generated is monadic and can fail parsing the
355
-- raw value. It is of the form:
356
--
357
-- @
358
-- nameFromRaw :: (Monad m) => /traw/ -> m Name
359
-- nameFromRaw s | s == var1       = Cons1
360
--               | s == \"value2\" = Cons2
361
--               | otherwise = fail /.../
362
-- @
363
genFromRaw :: Name -> Name -> Name -> [(String, Name)] -> Q [Dec]
364
genFromRaw traw fname tname constructors = do
365
  -- signature of form (Monad m) => String -> m $name
366
  sigt <- [t| (Monad m) => $(conT traw) -> m $(conT tname) |]
367
  -- clauses for a guarded pattern
368
  let varp = mkName "s"
369
      varpe = varE varp
370
  clauses <- mapM (\(c, v) -> do
371
                     -- the clause match condition
372
                     g <- normalG [| $varpe == $(varE v) |]
373
                     -- the clause result
374
                     r <- [| return $(conE (mkName c)) |]
375
                     return (g, r)) constructors
376
  -- the otherwise clause (fallback)
377
  oth_clause <- do
378
    g <- normalG [| otherwise |]
379
    r <- [|fail ("Invalid string value for type " ++
380
                 $(litE (stringL (nameBase tname))) ++ ": " ++ show $varpe) |]
381
    return (g, r)
382
  let fun = FunD fname [Clause [VarP varp]
383
                        (GuardedB (clauses++[oth_clause])) []]
384
  return [SigD fname sigt, fun]
385

    
386
-- | Generates a data type from a given raw format.
387
--
388
-- The format is expected to multiline. The first line contains the
389
-- type name, and the rest of the lines must contain two words: the
390
-- constructor name and then the string representation of the
391
-- respective constructor.
392
--
393
-- The function will generate the data type declaration, and then two
394
-- functions:
395
--
396
-- * /name/ToRaw, which converts the type to a raw type
397
--
398
-- * /name/FromRaw, which (monadically) converts from a raw type to the type
399
--
400
-- Note that this is basically just a custom show\/read instance,
401
-- nothing else.
402
declareADT :: Name -> String -> [(String, Name)] -> Q [Dec]
403
declareADT traw sname cons = do
404
  let name = mkName sname
405
      ddecl = strADTDecl name (map fst cons)
406
      -- process cons in the format expected by genToRaw
407
      cons' = map (\(a, b) -> (a, Right b)) cons
408
  toraw <- genToRaw traw (toRawName sname) name cons'
409
  fromraw <- genFromRaw traw (fromRawName sname) name cons
410
  return $ ddecl:toraw ++ fromraw
411

    
412
declareIADT :: String -> [(String, Name)] -> Q [Dec]
413
declareIADT = declareADT ''Int
414

    
415
declareSADT :: String -> [(String, Name)] -> Q [Dec]
416
declareSADT = declareADT ''String
417

    
418
-- | Creates the showJSON member of a JSON instance declaration.
419
--
420
-- This will create what is the equivalent of:
421
--
422
-- @
423
-- showJSON = showJSON . /name/ToRaw
424
-- @
425
--
426
-- in an instance JSON /name/ declaration
427
genShowJSON :: String -> Q Dec
428
genShowJSON name = do
429
  body <- [| JSON.showJSON . $(varE (toRawName name)) |]
430
  return $ FunD 'JSON.showJSON [Clause [] (NormalB body) []]
431

    
432
-- | Creates the readJSON member of a JSON instance declaration.
433
--
434
-- This will create what is the equivalent of:
435
--
436
-- @
437
-- readJSON s = case readJSON s of
438
--                Ok s' -> /name/FromRaw s'
439
--                Error e -> Error /description/
440
-- @
441
--
442
-- in an instance JSON /name/ declaration
443
genReadJSON :: String -> Q Dec
444
genReadJSON name = do
445
  let s = mkName "s"
446
  body <- [| case JSON.readJSON $(varE s) of
447
               JSON.Ok s' -> $(varE (fromRawName name)) s'
448
               JSON.Error e ->
449
                   JSON.Error $ "Can't parse raw value for type " ++
450
                           $(stringE name) ++ ": " ++ e ++ " from " ++
451
                           show $(varE s)
452
           |]
453
  return $ FunD 'JSON.readJSON [Clause [VarP s] (NormalB body) []]
454

    
455
-- | Generates a JSON instance for a given type.
456
--
457
-- This assumes that the /name/ToRaw and /name/FromRaw functions
458
-- have been defined as by the 'declareSADT' function.
459
makeJSONInstance :: Name -> Q [Dec]
460
makeJSONInstance name = do
461
  let base = nameBase name
462
  showJ <- genShowJSON base
463
  readJ <- genReadJSON base
464
  return [InstanceD [] (AppT (ConT ''JSON.JSON) (ConT name)) [readJ,showJ]]
465

    
466
-- * Template code for opcodes
467

    
468
-- | Transforms a CamelCase string into an_underscore_based_one.
469
deCamelCase :: String -> String
470
deCamelCase =
471
    intercalate "_" . map (map toUpper) . groupBy (\_ b -> not $ isUpper b)
472

    
473
-- | Transform an underscore_name into a CamelCase one.
474
camelCase :: String -> String
475
camelCase = concatMap (ensureUpper . drop 1) .
476
            groupBy (\_ b -> b /= '_' && b /= '-') . ('_':)
477

    
478
-- | Computes the name of a given constructor.
479
constructorName :: Con -> Q Name
480
constructorName (NormalC name _) = return name
481
constructorName (RecC name _)    = return name
482
constructorName x                = fail $ "Unhandled constructor " ++ show x
483

    
484
-- | Extract all constructor names from a given type.
485
reifyConsNames :: Name -> Q [String]
486
reifyConsNames name = do
487
  reify_result <- reify name
488
  case reify_result of
489
    TyConI (DataD _ _ _ cons _) -> mapM (liftM nameBase . constructorName) cons
490
    o -> fail $ "Unhandled name passed to reifyConsNames, expected\
491
                \ type constructor but got '" ++ show o ++ "'"
492

    
493
-- | Builds the generic constructor-to-string function.
494
--
495
-- This generates a simple function of the following form:
496
--
497
-- @
498
-- fname (ConStructorOne {}) = trans_fun("ConStructorOne")
499
-- fname (ConStructorTwo {}) = trans_fun("ConStructorTwo")
500
-- @
501
--
502
-- This builds a custom list of name\/string pairs and then uses
503
-- 'genToRaw' to actually generate the function.
504
genConstrToStr :: (String -> String) -> Name -> String -> Q [Dec]
505
genConstrToStr trans_fun name fname = do
506
  cnames <- reifyConsNames name
507
  let svalues = map (Left . trans_fun) cnames
508
  genToRaw ''String (mkName fname) name $ zip cnames svalues
509

    
510
-- | Constructor-to-string for OpCode.
511
genOpID :: Name -> String -> Q [Dec]
512
genOpID = genConstrToStr deCamelCase
513

    
514
-- | Builds a list with all defined constructor names for a type.
515
--
516
-- @
517
-- vstr :: String
518
-- vstr = [...]
519
-- @
520
--
521
-- Where the actual values of the string are the constructor names
522
-- mapped via @trans_fun@.
523
genAllConstr :: (String -> String) -> Name -> String -> Q [Dec]
524
genAllConstr trans_fun name vstr = do
525
  cnames <- reifyConsNames name
526
  let svalues = sort $ map trans_fun cnames
527
      vname = mkName vstr
528
      sig = SigD vname (AppT ListT (ConT ''String))
529
      body = NormalB (ListE (map (LitE . StringL) svalues))
530
  return $ [sig, ValD (VarP vname) body []]
531

    
532
-- | Generates a list of all defined opcode IDs.
533
genAllOpIDs :: Name -> String -> Q [Dec]
534
genAllOpIDs = genAllConstr deCamelCase
535

    
536
-- | OpCode parameter (field) type.
537
type OpParam = (String, Q Type, Q Exp)
538

    
539
-- * Python code generation
540

    
541
-- | Converts Haskell values into Python values
542
--
543
-- This is necessary for the default values of opcode parameters and
544
-- return values.  For example, if a default value or return type is a
545
-- Data.Map, then it must be shown as a Python dictioanry.
546
class Show a => PyValue a where
547
  showValue :: a -> String
548
  showValue = show
549

    
550
-- | Encapsulates Python default values
551
data PyValueEx = forall a. PyValue a => PyValueEx a
552

    
553
-- | Transfers opcode data between the opcode description (through
554
-- @genOpCode@) and the Python code generation functions.
555
type OpCodeDescriptor =
556
  (String, String, String, [String],
557
   [String], [Maybe PyValueEx], [String], String)
558

    
559
-- | Strips out the module name
560
--
561
-- @
562
-- pyBaseName "Data.Map" = "Map"
563
-- @
564
pyBaseName :: String -> String
565
pyBaseName str =
566
  case span (/= '.') str of
567
    (x, []) -> x
568
    (_, _:x) -> pyBaseName x
569

    
570
-- | Converts a Haskell type name into a Python type name.
571
--
572
-- @
573
-- pyTypename "Bool" = "ht.TBool"
574
-- @
575
pyTypeName :: Show a => a -> String
576
pyTypeName name =
577
  "ht.T" ++ (case pyBaseName (show name) of
578
                "()" -> "None"
579
                "Map" -> "DictOf"
580
                "Set" -> "SetOf"
581
                "ListSet" -> "SetOf"
582
                "Either" -> "Or"
583
                "GenericContainer" -> "DictOf"
584
                "JSValue" -> "Any"
585
                "JSObject" -> "Object"
586
                str -> str)
587

    
588
-- | Converts a Haskell type into a Python type.
589
--
590
-- @
591
-- pyType [Int] = "ht.TListOf(ht.TInt)"
592
-- @
593
pyType :: Type -> Q String
594
pyType (AppT typ1 typ2) =
595
  do t <- pyCall typ1 typ2
596
     return $ t ++ ")"
597

    
598
pyType (ConT name) = return (pyTypeName name)
599
pyType ListT = return "ht.TListOf"
600
pyType (TupleT 0) = return "ht.TNone"
601
pyType (TupleT _) = return "ht.TTupleOf"
602
pyType typ = error $ "unhandled case for type " ++ show typ
603
        
604
-- | Converts a Haskell type application into a Python type.
605
--
606
-- @
607
-- Maybe Int = "ht.TMaybe(ht.TInt)"
608
-- @
609
pyCall :: Type -> Type -> Q String
610
pyCall (AppT typ1 typ2) arg =
611
  do t <- pyCall typ1 typ2
612
     targ <- pyType arg
613
     return $ t ++ ", " ++ targ
614

    
615
pyCall typ1 typ2 =
616
  do t1 <- pyType typ1
617
     t2 <- pyType typ2
618
     return $ t1 ++ "(" ++ t2
619

    
620
-- | @pyType opt typ@ converts Haskell type @typ@ into a Python type,
621
-- where @opt@ determines if the converted type is optional (i.e.,
622
-- Maybe).
623
--
624
-- @
625
-- pyType False [Int] = "ht.TListOf(ht.TInt)" (mandatory)
626
-- pyType True [Int] = "ht.TMaybe(ht.TListOf(ht.TInt))" (optional)
627
-- @
628
pyOptionalType :: Bool -> Type -> Q String
629
pyOptionalType opt typ
630
  | opt = do t <- pyType typ
631
             return $ "ht.TMaybe(" ++ t ++ ")"
632
  | otherwise = pyType typ
633

    
634
-- | Optionally encapsulates default values in @PyValueEx@.
635
--
636
-- @maybeApp exp typ@ returns a quoted expression that encapsulates
637
-- the default value @exp@ of an opcode parameter cast to @typ@ in a
638
-- @PyValueEx@, if @exp@ is @Just@.  Otherwise, it returns a quoted
639
-- expression with @Nothing@.
640
maybeApp :: Maybe (Q Exp) -> Q Type -> Q Exp
641
maybeApp Nothing _ =
642
  [| Nothing |]
643

    
644
maybeApp (Just expr) typ =
645
  [| Just ($(conE (mkName "PyValueEx")) ($expr :: $typ)) |]
646

    
647

    
648
-- | Generates a Python type according to whether the field is
649
-- optional
650
genPyType :: OptionalType -> Q Type -> Q ExpQ
651
genPyType opt typ =
652
  do t <- typ
653
     stringE <$> pyOptionalType (opt /= NotOptional) t
654

    
655
-- | Generates Python types from opcode parameters.
656
genPyTypes :: [Field] -> Q ExpQ
657
genPyTypes fs =
658
  listE <$> mapM (\f -> genPyType (fieldIsOptional f) (fieldType f)) fs
659

    
660
-- | Generates Python default values from opcode parameters.
661
genPyDefaults :: [Field] -> ExpQ
662
genPyDefaults fs =
663
  listE $ map (\f -> maybeApp (fieldDefault f) (fieldType f)) fs
664

    
665
-- | Generates a Haskell function call to "showPyClass" with the
666
-- necessary information on how to build the Python class string.
667
pyClass :: OpCodeConstructor -> ExpQ
668
pyClass (consName, consType, consDoc, consFields, consDscField) =
669
  do let pyClassVar = varNameE "showPyClass"
670
         consName' = stringE consName
671
     consType' <- genPyType NotOptional consType
672
     let consDoc' = stringE consDoc
673
         consFieldNames = listE $ map (stringE . fieldName) consFields
674
         consFieldDocs = listE $ map (stringE . fieldDoc) consFields
675
     consFieldTypes <- genPyTypes consFields
676
     let consFieldDefaults = genPyDefaults consFields
677
     [| ($consName',
678
         $consType',
679
         $consDoc',
680
         $consFieldNames,
681
         $consFieldTypes,
682
         $consFieldDefaults,
683
         $consFieldDocs,
684
         consDscField) |]
685

    
686
-- | Generates a function called "pyClasses" that holds the list of
687
-- all the opcode descriptors necessary for generating the Python
688
-- opcodes.
689
pyClasses :: [OpCodeConstructor] -> Q [Dec]
690
pyClasses cons =
691
  do let name = mkName "pyClasses"
692
         sig = SigD name (AppT ListT (ConT ''OpCodeDescriptor))
693
     fn <- FunD name <$> (:[]) <$> declClause cons
694
     return [sig, fn]
695
  where declClause c =
696
          clause [] (normalB (ListE <$> mapM pyClass c)) []
697

    
698
-- | Converts from an opcode constructor to a Luxi constructor.
699
opcodeConsToLuxiCons :: (a, b, c, d, e) -> (a, d)
700
opcodeConsToLuxiCons (x, _, _, y, _) = (x, y)
701

    
702
-- | Generates the OpCode data type.
703
--
704
-- This takes an opcode logical definition, and builds both the
705
-- datatype and the JSON serialisation out of it. We can't use a
706
-- generic serialisation since we need to be compatible with Ganeti's
707
-- own, so we have a few quirks to work around.
708
genOpCode :: String              -- ^ Type name to use
709
          -> [OpCodeConstructor] -- ^ Constructor name and parameters
710
          -> Q [Dec]
711
genOpCode name cons = do
712
  let tname = mkName name
713
  decl_d <- mapM (\(cname, _, _, fields, _) -> do
714
                    -- we only need the type of the field, without Q
715
                    fields' <- mapM (fieldTypeInfo "op") fields
716
                    return $ RecC (mkName cname) fields')
717
            cons
718
  let declD = DataD [] tname [] decl_d [''Show, ''Eq]
719
  let (allfsig, allffn) = genAllOpFields "allOpFields" cons
720
  save_decs <- genSaveOpCode tname "saveOpCode" "toDictOpCode"
721
               (map opcodeConsToLuxiCons cons) saveConstructor True
722
  (loadsig, loadfn) <- genLoadOpCode cons
723
  pyDecls <- pyClasses cons
724
  return $ [declD, allfsig, allffn, loadsig, loadfn] ++ save_decs ++ pyDecls
725

    
726
-- | Generates the function pattern returning the list of fields for a
727
-- given constructor.
728
genOpConsFields :: OpCodeConstructor -> Clause
729
genOpConsFields (cname, _, _, fields, _) =
730
  let op_id = deCamelCase cname
731
      fvals = map (LitE . StringL) . sort . nub $
732
              concatMap (\f -> fieldName f:fieldExtraKeys f) fields
733
  in Clause [LitP (StringL op_id)] (NormalB $ ListE fvals) []
734

    
735
-- | Generates a list of all fields of an opcode constructor.
736
genAllOpFields  :: String              -- ^ Function name
737
                -> [OpCodeConstructor] -- ^ Object definition
738
                -> (Dec, Dec)
739
genAllOpFields sname opdefs =
740
  let cclauses = map genOpConsFields opdefs
741
      other = Clause [WildP] (NormalB (ListE [])) []
742
      fname = mkName sname
743
      sigt = AppT  (AppT ArrowT (ConT ''String)) (AppT ListT (ConT ''String))
744
  in (SigD fname sigt, FunD fname (cclauses++[other]))
745

    
746
-- | Generates the \"save\" clause for an entire opcode constructor.
747
--
748
-- This matches the opcode with variables named the same as the
749
-- constructor fields (just so that the spliced in code looks nicer),
750
-- and passes those name plus the parameter definition to 'saveObjectField'.
751
saveConstructor :: LuxiConstructor -- ^ The constructor
752
                -> Q Clause        -- ^ Resulting clause
753
saveConstructor (sname, fields) = do
754
  let cname = mkName sname
755
  fnames <- mapM (newName . fieldVariable) fields
756
  let pat = conP cname (map varP fnames)
757
  let felems = map (uncurry saveObjectField) (zip fnames fields)
758
      -- now build the OP_ID serialisation
759
      opid = [| [( $(stringE "OP_ID"),
760
                   JSON.showJSON $(stringE . deCamelCase $ sname) )] |]
761
      flist = listE (opid:felems)
762
      -- and finally convert all this to a json object
763
      flist' = [| concat $flist |]
764
  clause [pat] (normalB flist') []
765

    
766
-- | Generates the main save opcode function.
767
--
768
-- This builds a per-constructor match clause that contains the
769
-- respective constructor-serialisation code.
770
genSaveOpCode :: Name                          -- ^ Object ype
771
              -> String                        -- ^ To 'JSValue' function name
772
              -> String                        -- ^ To 'JSObject' function name
773
              -> [LuxiConstructor]             -- ^ Object definition
774
              -> (LuxiConstructor -> Q Clause) -- ^ Constructor save fn
775
              -> Bool                          -- ^ Whether to generate
776
                                               -- obj or just a
777
                                               -- list\/tuple of values
778
              -> Q [Dec]
779
genSaveOpCode tname jvalstr tdstr opdefs fn gen_object = do
780
  tdclauses <- mapM fn opdefs
781
  let typecon = ConT tname
782
      jvalname = mkName jvalstr
783
      jvalsig = AppT  (AppT ArrowT typecon) (ConT ''JSON.JSValue)
784
      tdname = mkName tdstr
785
  tdsig <- [t| $(return typecon) -> [(String, JSON.JSValue)] |]
786
  jvalclause <- if gen_object
787
                  then [| $makeObjE . $(varE tdname) |]
788
                  else [| JSON.showJSON . map snd . $(varE tdname) |]
789
  return [ SigD tdname tdsig
790
         , FunD tdname tdclauses
791
         , SigD jvalname jvalsig
792
         , ValD (VarP jvalname) (NormalB jvalclause) []]
793

    
794
-- | Generates load code for a single constructor of the opcode data type.
795
loadConstructor :: OpCodeConstructor -> Q Exp
796
loadConstructor (sname, _, _, fields, _) = do
797
  let name = mkName sname
798
  fbinds <- mapM loadObjectField fields
799
  let (fnames, fstmts) = unzip fbinds
800
  let cval = foldl (\accu fn -> AppE accu (VarE fn)) (ConE name) fnames
801
      fstmts' = fstmts ++ [NoBindS (AppE (VarE 'return) cval)]
802
  return $ DoE fstmts'
803

    
804
-- | Generates the loadOpCode function.
805
genLoadOpCode :: [OpCodeConstructor] -> Q (Dec, Dec)
806
genLoadOpCode opdefs = do
807
  let fname = mkName "loadOpCode"
808
      arg1 = mkName "v"
809
      objname = mkName "o"
810
      opid = mkName "op_id"
811
  st1 <- bindS (varP objname) [| liftM JSON.fromJSObject
812
                                 (JSON.readJSON $(varE arg1)) |]
813
  st2 <- bindS (varP opid) [| $fromObjE $(varE objname) $(stringE "OP_ID") |]
814
  -- the match results (per-constructor blocks)
815
  mexps <- mapM loadConstructor opdefs
816
  fails <- [| fail $ "Unknown opcode " ++ $(varE opid) |]
817
  let mpats = map (\(me, (consName, _, _, _, _)) ->
818
                       let mp = LitP . StringL . deCamelCase $ consName
819
                       in Match mp (NormalB me) []
820
                  ) $ zip mexps opdefs
821
      defmatch = Match WildP (NormalB fails) []
822
      cst = NoBindS $ CaseE (VarE opid) $ mpats++[defmatch]
823
      body = DoE [st1, st2, cst]
824
  sigt <- [t| JSON.JSValue -> JSON.Result $(conT (mkName "OpCode")) |]
825
  return $ (SigD fname sigt, FunD fname [Clause [VarP arg1] (NormalB body) []])
826

    
827
-- * Template code for luxi
828

    
829
-- | Constructor-to-string for LuxiOp.
830
genStrOfOp :: Name -> String -> Q [Dec]
831
genStrOfOp = genConstrToStr id
832

    
833
-- | Constructor-to-string for MsgKeys.
834
genStrOfKey :: Name -> String -> Q [Dec]
835
genStrOfKey = genConstrToStr ensureLower
836

    
837
-- | Generates the LuxiOp data type.
838
--
839
-- This takes a Luxi operation definition and builds both the
840
-- datatype and the function transforming the arguments to JSON.
841
-- We can't use anything less generic, because the way different
842
-- operations are serialized differs on both parameter- and top-level.
843
--
844
-- There are two things to be defined for each parameter:
845
--
846
-- * name
847
--
848
-- * type
849
--
850
genLuxiOp :: String -> [LuxiConstructor] -> Q [Dec]
851
genLuxiOp name cons = do
852
  let tname = mkName name
853
  decl_d <- mapM (\(cname, fields) -> do
854
                    -- we only need the type of the field, without Q
855
                    fields' <- mapM actualFieldType fields
856
                    let fields'' = zip (repeat NotStrict) fields'
857
                    return $ NormalC (mkName cname) fields'')
858
            cons
859
  let declD = DataD [] (mkName name) [] decl_d [''Show, ''Eq]
860
  save_decs <- genSaveOpCode tname "opToArgs" "opToDict"
861
               cons saveLuxiConstructor False
862
  req_defs <- declareSADT "LuxiReq" .
863
              map (\(str, _) -> ("Req" ++ str, mkName ("luxiReq" ++ str))) $
864
                  cons
865
  return $ declD:save_decs ++ req_defs
866

    
867
-- | Generates the \"save\" clause for entire LuxiOp constructor.
868
saveLuxiConstructor :: LuxiConstructor -> Q Clause
869
saveLuxiConstructor (sname, fields) = do
870
  let cname = mkName sname
871
  fnames <- mapM (newName . fieldVariable) fields
872
  let pat = conP cname (map varP fnames)
873
  let felems = map (uncurry saveObjectField) (zip fnames fields)
874
      flist = [| concat $(listE felems) |]
875
  clause [pat] (normalB flist) []
876

    
877
-- * "Objects" functionality
878

    
879
-- | Extract the field's declaration from a Field structure.
880
fieldTypeInfo :: String -> Field -> Q (Name, Strict, Type)
881
fieldTypeInfo field_pfx fd = do
882
  t <- actualFieldType fd
883
  let n = mkName . (field_pfx ++) . fieldRecordName $ fd
884
  return (n, NotStrict, t)
885

    
886
-- | Build an object declaration.
887
buildObject :: String -> String -> [Field] -> Q [Dec]
888
buildObject sname field_pfx fields = do
889
  let name = mkName sname
890
  fields_d <- mapM (fieldTypeInfo field_pfx) fields
891
  let decl_d = RecC name fields_d
892
  let declD = DataD [] name [] [decl_d] [''Show, ''Eq]
893
  ser_decls <- buildObjectSerialisation sname fields
894
  return $ declD:ser_decls
895

    
896
-- | Generates an object definition: data type and its JSON instance.
897
buildObjectSerialisation :: String -> [Field] -> Q [Dec]
898
buildObjectSerialisation sname fields = do
899
  let name = mkName sname
900
  savedecls <- genSaveObject saveObjectField sname fields
901
  (loadsig, loadfn) <- genLoadObject loadObjectField sname fields
902
  shjson <- objectShowJSON sname
903
  rdjson <- objectReadJSON sname
904
  let instdecl = InstanceD [] (AppT (ConT ''JSON.JSON) (ConT name))
905
                 [rdjson, shjson]
906
  return $ savedecls ++ [loadsig, loadfn, instdecl]
907

    
908
-- | The toDict function name for a given type.
909
toDictName :: String -> Name
910
toDictName sname = mkName ("toDict" ++ sname)
911

    
912
-- | Generates the save object functionality.
913
genSaveObject :: (Name -> Field -> Q Exp)
914
              -> String -> [Field] -> Q [Dec]
915
genSaveObject save_fn sname fields = do
916
  let name = mkName sname
917
  fnames <- mapM (newName . fieldVariable) fields
918
  let pat = conP name (map varP fnames)
919
  let tdname = toDictName sname
920
  tdsigt <- [t| $(conT name) -> [(String, JSON.JSValue)] |]
921

    
922
  let felems = map (uncurry save_fn) (zip fnames fields)
923
      flist = listE felems
924
      -- and finally convert all this to a json object
925
      tdlist = [| concat $flist |]
926
      iname = mkName "i"
927
  tclause <- clause [pat] (normalB tdlist) []
928
  cclause <- [| $makeObjE . $(varE tdname) |]
929
  let fname = mkName ("save" ++ sname)
930
  sigt <- [t| $(conT name) -> JSON.JSValue |]
931
  return [SigD tdname tdsigt, FunD tdname [tclause],
932
          SigD fname sigt, ValD (VarP fname) (NormalB cclause) []]
933

    
934
-- | Generates the code for saving an object's field, handling the
935
-- various types of fields that we have.
936
saveObjectField :: Name -> Field -> Q Exp
937
saveObjectField fvar field =
938
  case fieldIsOptional field of
939
    OptionalOmitNull -> [| case $(varE fvar) of
940
                             Nothing -> []
941
                             Just v  -> [( $nameE, JSON.showJSON v )]
942
                         |]
943
    OptionalSerializeNull -> [| case $(varE fvar) of
944
                                  Nothing -> [( $nameE, JSON.JSNull )]
945
                                  Just v  -> [( $nameE, JSON.showJSON v )]
946
                              |]
947
    NotOptional ->
948
      case fieldShow field of
949
        -- Note: the order of actual:extra is important, since for
950
        -- some serialisation types (e.g. Luxi), we use tuples
951
        -- (positional info) rather than object (name info)
952
        Nothing -> [| [( $nameE, JSON.showJSON $fvarE)] |]
953
        Just fn -> [| let (actual, extra) = $fn $fvarE
954
                      in ($nameE, JSON.showJSON actual):extra
955
                    |]
956
  where nameE = stringE (fieldName field)
957
        fvarE = varE fvar
958

    
959
-- | Generates the showJSON clause for a given object name.
960
objectShowJSON :: String -> Q Dec
961
objectShowJSON name = do
962
  body <- [| JSON.showJSON . $(varE . mkName $ "save" ++ name) |]
963
  return $ FunD 'JSON.showJSON [Clause [] (NormalB body) []]
964

    
965
-- | Generates the load object functionality.
966
genLoadObject :: (Field -> Q (Name, Stmt))
967
              -> String -> [Field] -> Q (Dec, Dec)
968
genLoadObject load_fn sname fields = do
969
  let name = mkName sname
970
      funname = mkName $ "load" ++ sname
971
      arg1 = mkName $ if null fields then "_" else "v"
972
      objname = mkName "o"
973
      opid = mkName "op_id"
974
  st1 <- bindS (varP objname) [| liftM JSON.fromJSObject
975
                                 (JSON.readJSON $(varE arg1)) |]
976
  fbinds <- mapM load_fn fields
977
  let (fnames, fstmts) = unzip fbinds
978
  let cval = foldl (\accu fn -> AppE accu (VarE fn)) (ConE name) fnames
979
      retstmt = [NoBindS (AppE (VarE 'return) cval)]
980
      -- FIXME: should we require an empty dict for an empty type?
981
      -- this allows any JSValue right now
982
      fstmts' = if null fields
983
                  then retstmt
984
                  else st1:fstmts ++ retstmt
985
  sigt <- [t| JSON.JSValue -> JSON.Result $(conT name) |]
986
  return $ (SigD funname sigt,
987
            FunD funname [Clause [VarP arg1] (NormalB (DoE fstmts')) []])
988

    
989
-- | Generates code for loading an object's field.
990
loadObjectField :: Field -> Q (Name, Stmt)
991
loadObjectField field = do
992
  let name = fieldVariable field
993
  fvar <- newName name
994
  -- these are used in all patterns below
995
  let objvar = varNameE "o"
996
      objfield = stringE (fieldName field)
997
      loadexp =
998
        if fieldIsOptional field /= NotOptional
999
          -- we treat both optional types the same, since
1000
          -- 'maybeFromObj' can deal with both missing and null values
1001
          -- appropriately (the same)
1002
          then [| $(varE 'maybeFromObj) $objvar $objfield |]
1003
          else case fieldDefault field of
1004
                 Just defv ->
1005
                   [| $(varE 'fromObjWithDefault) $objvar
1006
                      $objfield $defv |]
1007
                 Nothing -> [| $fromObjE $objvar $objfield |]
1008
  bexp <- loadFn field loadexp objvar
1009

    
1010
  return (fvar, BindS (VarP fvar) bexp)
1011

    
1012
-- | Builds the readJSON instance for a given object name.
1013
objectReadJSON :: String -> Q Dec
1014
objectReadJSON name = do
1015
  let s = mkName "s"
1016
  body <- [| case JSON.readJSON $(varE s) of
1017
               JSON.Ok s' -> $(varE .mkName $ "load" ++ name) s'
1018
               JSON.Error e ->
1019
                 JSON.Error $ "Can't parse value for type " ++
1020
                       $(stringE name) ++ ": " ++ e
1021
           |]
1022
  return $ FunD 'JSON.readJSON [Clause [VarP s] (NormalB body) []]
1023

    
1024
-- * Inheritable parameter tables implementation
1025

    
1026
-- | Compute parameter type names.
1027
paramTypeNames :: String -> (String, String)
1028
paramTypeNames root = ("Filled"  ++ root ++ "Params",
1029
                       "Partial" ++ root ++ "Params")
1030

    
1031
-- | Compute information about the type of a parameter field.
1032
paramFieldTypeInfo :: String -> Field -> Q (Name, Strict, Type)
1033
paramFieldTypeInfo field_pfx fd = do
1034
  t <- actualFieldType fd
1035
  let n = mkName . (++ "P") . (field_pfx ++) .
1036
          fieldRecordName $ fd
1037
  return (n, NotStrict, AppT (ConT ''Maybe) t)
1038

    
1039
-- | Build a parameter declaration.
1040
--
1041
-- This function builds two different data structures: a /filled/ one,
1042
-- in which all fields are required, and a /partial/ one, in which all
1043
-- fields are optional. Due to the current record syntax issues, the
1044
-- fields need to be named differrently for the two structures, so the
1045
-- partial ones get a /P/ suffix.
1046
buildParam :: String -> String -> [Field] -> Q [Dec]
1047
buildParam sname field_pfx fields = do
1048
  let (sname_f, sname_p) = paramTypeNames sname
1049
      name_f = mkName sname_f
1050
      name_p = mkName sname_p
1051
  fields_f <- mapM (fieldTypeInfo field_pfx) fields
1052
  fields_p <- mapM (paramFieldTypeInfo field_pfx) fields
1053
  let decl_f = RecC name_f fields_f
1054
      decl_p = RecC name_p fields_p
1055
  let declF = DataD [] name_f [] [decl_f] [''Show, ''Eq]
1056
      declP = DataD [] name_p [] [decl_p] [''Show, ''Eq]
1057
  ser_decls_f <- buildObjectSerialisation sname_f fields
1058
  ser_decls_p <- buildPParamSerialisation sname_p fields
1059
  fill_decls <- fillParam sname field_pfx fields
1060
  return $ [declF, declP] ++ ser_decls_f ++ ser_decls_p ++ fill_decls ++
1061
           buildParamAllFields sname fields ++
1062
           buildDictObjectInst name_f sname_f
1063

    
1064
-- | Builds a list of all fields of a parameter.
1065
buildParamAllFields :: String -> [Field] -> [Dec]
1066
buildParamAllFields sname fields =
1067
  let vname = mkName ("all" ++ sname ++ "ParamFields")
1068
      sig = SigD vname (AppT ListT (ConT ''String))
1069
      val = ListE $ map (LitE . StringL . fieldName) fields
1070
  in [sig, ValD (VarP vname) (NormalB val) []]
1071

    
1072
-- | Builds the 'DictObject' instance for a filled parameter.
1073
buildDictObjectInst :: Name -> String -> [Dec]
1074
buildDictObjectInst name sname =
1075
  [InstanceD [] (AppT (ConT ''DictObject) (ConT name))
1076
   [ValD (VarP 'toDict) (NormalB (VarE (toDictName sname))) []]]
1077

    
1078
-- | Generates the serialisation for a partial parameter.
1079
buildPParamSerialisation :: String -> [Field] -> Q [Dec]
1080
buildPParamSerialisation sname fields = do
1081
  let name = mkName sname
1082
  savedecls <- genSaveObject savePParamField sname fields
1083
  (loadsig, loadfn) <- genLoadObject loadPParamField sname fields
1084
  shjson <- objectShowJSON sname
1085
  rdjson <- objectReadJSON sname
1086
  let instdecl = InstanceD [] (AppT (ConT ''JSON.JSON) (ConT name))
1087
                 [rdjson, shjson]
1088
  return $ savedecls ++ [loadsig, loadfn, instdecl]
1089

    
1090
-- | Generates code to save an optional parameter field.
1091
savePParamField :: Name -> Field -> Q Exp
1092
savePParamField fvar field = do
1093
  checkNonOptDef field
1094
  let actualVal = mkName "v"
1095
  normalexpr <- saveObjectField actualVal field
1096
  -- we have to construct the block here manually, because we can't
1097
  -- splice-in-splice
1098
  return $ CaseE (VarE fvar) [ Match (ConP 'Nothing [])
1099
                                       (NormalB (ConE '[])) []
1100
                             , Match (ConP 'Just [VarP actualVal])
1101
                                       (NormalB normalexpr) []
1102
                             ]
1103

    
1104
-- | Generates code to load an optional parameter field.
1105
loadPParamField :: Field -> Q (Name, Stmt)
1106
loadPParamField field = do
1107
  checkNonOptDef field
1108
  let name = fieldName field
1109
  fvar <- newName name
1110
  -- these are used in all patterns below
1111
  let objvar = varNameE "o"
1112
      objfield = stringE name
1113
      loadexp = [| $(varE 'maybeFromObj) $objvar $objfield |]
1114
  bexp <- loadFn field loadexp objvar
1115
  return (fvar, BindS (VarP fvar) bexp)
1116

    
1117
-- | Builds a simple declaration of type @n_x = fromMaybe f_x p_x@.
1118
buildFromMaybe :: String -> Q Dec
1119
buildFromMaybe fname =
1120
  valD (varP (mkName $ "n_" ++ fname))
1121
         (normalB [| $(varE 'fromMaybe)
1122
                        $(varNameE $ "f_" ++ fname)
1123
                        $(varNameE $ "p_" ++ fname) |]) []
1124

    
1125
-- | Builds a function that executes the filling of partial parameter
1126
-- from a full copy (similar to Python's fillDict).
1127
fillParam :: String -> String -> [Field] -> Q [Dec]
1128
fillParam sname field_pfx fields = do
1129
  let fnames = map (\fd -> field_pfx ++ fieldRecordName fd) fields
1130
      (sname_f, sname_p) = paramTypeNames sname
1131
      oname_f = "fobj"
1132
      oname_p = "pobj"
1133
      name_f = mkName sname_f
1134
      name_p = mkName sname_p
1135
      fun_name = mkName $ "fill" ++ sname ++ "Params"
1136
      le_full = ValD (ConP name_f (map (VarP . mkName . ("f_" ++)) fnames))
1137
                (NormalB . VarE . mkName $ oname_f) []
1138
      le_part = ValD (ConP name_p (map (VarP . mkName . ("p_" ++)) fnames))
1139
                (NormalB . VarE . mkName $ oname_p) []
1140
      obj_new = foldl (\accu vname -> AppE accu (VarE vname)) (ConE name_f)
1141
                $ map (mkName . ("n_" ++)) fnames
1142
  le_new <- mapM buildFromMaybe fnames
1143
  funt <- [t| $(conT name_f) -> $(conT name_p) -> $(conT name_f) |]
1144
  let sig = SigD fun_name funt
1145
      fclause = Clause [VarP (mkName oname_f), VarP (mkName oname_p)]
1146
                (NormalB $ LetE (le_full:le_part:le_new) obj_new) []
1147
      fun = FunD fun_name [fclause]
1148
  return [sig, fun]
1149

    
1150
-- * Template code for exceptions
1151

    
1152
-- | Exception simple error message field.
1153
excErrMsg :: (String, Q Type)
1154
excErrMsg = ("errMsg", [t| String |])
1155

    
1156
-- | Builds an exception type definition.
1157
genException :: String                  -- ^ Name of new type
1158
             -> SimpleObject -- ^ Constructor name and parameters
1159
             -> Q [Dec]
1160
genException name cons = do
1161
  let tname = mkName name
1162
  declD <- buildSimpleCons tname cons
1163
  (savesig, savefn) <- genSaveSimpleObj tname ("save" ++ name) cons $
1164
                         uncurry saveExcCons
1165
  (loadsig, loadfn) <- genLoadExc tname ("load" ++ name) cons
1166
  return [declD, loadsig, loadfn, savesig, savefn]
1167

    
1168
-- | Generates the \"save\" clause for an entire exception constructor.
1169
--
1170
-- This matches the exception with variables named the same as the
1171
-- constructor fields (just so that the spliced in code looks nicer),
1172
-- and calls showJSON on it.
1173
saveExcCons :: String        -- ^ The constructor name
1174
            -> [SimpleField] -- ^ The parameter definitions for this
1175
                             -- constructor
1176
            -> Q Clause      -- ^ Resulting clause
1177
saveExcCons sname fields = do
1178
  let cname = mkName sname
1179
  fnames <- mapM (newName . fst) fields
1180
  let pat = conP cname (map varP fnames)
1181
      felems = if null fnames
1182
                 then conE '() -- otherwise, empty list has no type
1183
                 else listE $ map (\f -> [| JSON.showJSON $(varE f) |]) fnames
1184
  let tup = tupE [ litE (stringL sname), felems ]
1185
  clause [pat] (normalB [| JSON.showJSON $tup |]) []
1186

    
1187
-- | Generates load code for a single constructor of an exception.
1188
--
1189
-- Generates the code (if there's only one argument, we will use a
1190
-- list, not a tuple:
1191
--
1192
-- @
1193
-- do
1194
--  (x1, x2, ...) <- readJSON args
1195
--  return $ Cons x1 x2 ...
1196
-- @
1197
loadExcConstructor :: Name -> String -> [SimpleField] -> Q Exp
1198
loadExcConstructor inname sname fields = do
1199
  let name = mkName sname
1200
  f_names <- mapM (newName . fst) fields
1201
  let read_args = AppE (VarE 'JSON.readJSON) (VarE inname)
1202
  let binds = case f_names of
1203
                [x] -> BindS (ListP [VarP x])
1204
                _   -> BindS (TupP (map VarP f_names))
1205
      cval = foldl (\accu fn -> AppE accu (VarE fn)) (ConE name) f_names
1206
  return $ DoE [binds read_args, NoBindS (AppE (VarE 'return) cval)]
1207

    
1208
{-| Generates the loadException function.
1209

    
1210
This generates a quite complicated function, along the lines of:
1211

    
1212
@
1213
loadFn (JSArray [JSString name, args]) = case name of
1214
   "A1" -> do
1215
     (x1, x2, ...) <- readJSON args
1216
     return $ A1 x1 x2 ...
1217
   "a2" -> ...
1218
   s -> fail $ "Unknown exception" ++ s
1219
loadFn v = fail $ "Expected array but got " ++ show v
1220
@
1221
-}
1222
genLoadExc :: Name -> String -> SimpleObject -> Q (Dec, Dec)
1223
genLoadExc tname sname opdefs = do
1224
  let fname = mkName sname
1225
  exc_name <- newName "name"
1226
  exc_args <- newName "args"
1227
  exc_else <- newName "s"
1228
  arg_else <- newName "v"
1229
  fails <- [| fail $ "Unknown exception '" ++ $(varE exc_else) ++ "'" |]
1230
  -- default match for unknown exception name
1231
  let defmatch = Match (VarP exc_else) (NormalB fails) []
1232
  -- the match results (per-constructor blocks)
1233
  str_matches <-
1234
    mapM (\(s, params) -> do
1235
            body_exp <- loadExcConstructor exc_args s params
1236
            return $ Match (LitP (StringL s)) (NormalB body_exp) [])
1237
    opdefs
1238
  -- the first function clause; we can't use [| |] due to TH
1239
  -- limitations, so we have to build the AST by hand
1240
  let clause1 = Clause [ConP 'JSON.JSArray
1241
                               [ListP [ConP 'JSON.JSString [VarP exc_name],
1242
                                            VarP exc_args]]]
1243
                (NormalB (CaseE (AppE (VarE 'JSON.fromJSString)
1244
                                        (VarE exc_name))
1245
                          (str_matches ++ [defmatch]))) []
1246
  -- the fail expression for the second function clause
1247
  fail_type <- [| fail $ "Invalid exception: expected '(string, [args])' " ++
1248
                  "      but got " ++ show (pp_value $(varE arg_else)) ++ "'"
1249
                |]
1250
  -- the second function clause
1251
  let clause2 = Clause [VarP arg_else] (NormalB fail_type) []
1252
  sigt <- [t| JSON.JSValue -> JSON.Result $(conT tname) |]
1253
  return $ (SigD fname sigt, FunD fname [clause1, clause2])