Statistics
| Branch: | Tag: | Revision:

root / src / Ganeti / THH.hs @ 2886c58d

History | View | Annotate | Download (47.9 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 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
                  , OpCodeDescriptor
44
                  , genOpCode
45
                  , genStrOfOp
46
                  , genStrOfKey
47
                  , genLuxiOp
48
                  , Field (..)
49
                  , simpleField
50
                  , specialNumericalField
51
                  , withDoc
52
                  , defaultField
53
                  , optionalField
54
                  , optionalNullSerField
55
                  , renameField
56
                  , customField
57
                  , timeStampFields
58
                  , uuidFields
59
                  , serialFields
60
                  , tagsFields
61
                  , TagSet
62
                  , buildObject
63
                  , buildObjectSerialisation
64
                  , buildParam
65
                  , DictObject(..)
66
                  , genException
67
                  , excErrMsg
68
                  ) where
69

    
70
import Control.Monad (liftM)
71
import Data.Char
72
import Data.List
73
import qualified Data.Set as Set
74
import Language.Haskell.TH
75

    
76
import qualified Text.JSON as JSON
77
import Text.JSON.Pretty (pp_value)
78

    
79
import Ganeti.JSON
80
import Ganeti.PyValueInstances
81

    
82
import Data.Maybe
83
import Data.Functor ((<$>))
84

    
85
-- * Exported types
86

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

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

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

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

    
125
withDoc :: String -> Field -> Field
126
withDoc doc field =
127
  field { fieldDoc = doc }
128

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

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

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

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

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

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

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

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

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

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

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

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

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

    
231
-- * Common field declarations
232

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

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

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

    
249
-- | Tag set type alias.
250
type TagSet = Set.Set String
251

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

    
257
-- * Internal types
258

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

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

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

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

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

    
274
-- * Helper functions
275

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

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

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

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

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

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

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

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

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

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

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

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

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

    
353
-- * Template code for simple raw type-equivalent ADTs
354

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

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

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

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

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

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

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

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

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

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

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

    
513
-- * Template code for opcodes
514

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

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

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

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

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

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

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

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

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

    
586
-- * Python code generation
587

    
588
-- | Transfers opcode data between the opcode description (through
589
-- @genOpCode@) and the Python code generation functions.
590
type OpCodeDescriptor =
591
  (String, String, String, [String],
592
   [String], [Maybe PyValueEx], [String], String)
593

    
594
-- | Strips out the module name
595
--
596
-- @
597
-- pyBaseName "Data.Map" = "Map"
598
-- @
599
pyBaseName :: String -> String
600
pyBaseName str =
601
  case span (/= '.') str of
602
    (x, []) -> x
603
    (_, _:x) -> pyBaseName x
604

    
605
-- | Converts a Haskell type name into a Python type name.
606
--
607
-- @
608
-- pyTypename "Bool" = "ht.TBool"
609
-- @
610
pyTypeName :: Show a => a -> String
611
pyTypeName name =
612
  "ht.T" ++ (case pyBaseName (show name) of
613
                "()" -> "None"
614
                "Map" -> "DictOf"
615
                "Set" -> "SetOf"
616
                "ListSet" -> "SetOf"
617
                "Either" -> "Or"
618
                "GenericContainer" -> "DictOf"
619
                "JSValue" -> "Any"
620
                "JSObject" -> "Object"
621
                str -> str)
622

    
623
-- | Converts a Haskell type into a Python type.
624
--
625
-- @
626
-- pyType [Int] = "ht.TListOf(ht.TInt)"
627
-- @
628
pyType :: Type -> Q String
629
pyType (AppT typ1 typ2) =
630
  do t <- pyCall typ1 typ2
631
     return $ t ++ ")"
632

    
633
pyType (ConT name) = return (pyTypeName name)
634
pyType ListT = return "ht.TListOf"
635
pyType (TupleT 0) = return "ht.TNone"
636
pyType (TupleT _) = return "ht.TTupleOf"
637
pyType typ = error $ "unhandled case for type " ++ show typ
638
        
639
-- | Converts a Haskell type application into a Python type.
640
--
641
-- @
642
-- Maybe Int = "ht.TMaybe(ht.TInt)"
643
-- @
644
pyCall :: Type -> Type -> Q String
645
pyCall (AppT typ1 typ2) arg =
646
  do t <- pyCall typ1 typ2
647
     targ <- pyType arg
648
     return $ t ++ ", " ++ targ
649

    
650
pyCall typ1 typ2 =
651
  do t1 <- pyType typ1
652
     t2 <- pyType typ2
653
     return $ t1 ++ "(" ++ t2
654

    
655
-- | @pyType opt typ@ converts Haskell type @typ@ into a Python type,
656
-- where @opt@ determines if the converted type is optional (i.e.,
657
-- Maybe).
658
--
659
-- @
660
-- pyType False [Int] = "ht.TListOf(ht.TInt)" (mandatory)
661
-- pyType True [Int] = "ht.TMaybe(ht.TListOf(ht.TInt))" (optional)
662
-- @
663
pyOptionalType :: Bool -> Type -> Q String
664
pyOptionalType opt typ
665
  | opt = do t <- pyType typ
666
             return $ "ht.TMaybe(" ++ t ++ ")"
667
  | otherwise = pyType typ
668

    
669
-- | Optionally encapsulates default values in @PyValueEx@.
670
--
671
-- @maybeApp exp typ@ returns a quoted expression that encapsulates
672
-- the default value @exp@ of an opcode parameter cast to @typ@ in a
673
-- @PyValueEx@, if @exp@ is @Just@.  Otherwise, it returns a quoted
674
-- expression with @Nothing@.
675
maybeApp :: Maybe (Q Exp) -> Q Type -> Q Exp
676
maybeApp Nothing _ =
677
  [| Nothing |]
678

    
679
maybeApp (Just expr) typ =
680
  [| Just ($(conE (mkName "PyValueEx")) ($expr :: $typ)) |]
681

    
682

    
683
-- | Generates a Python type according to whether the field is
684
-- optional
685
genPyType :: OptionalType -> Q Type -> Q ExpQ
686
genPyType opt typ =
687
  do t <- typ
688
     stringE <$> pyOptionalType (opt /= NotOptional) t
689

    
690
-- | Generates Python types from opcode parameters.
691
genPyTypes :: [Field] -> Q ExpQ
692
genPyTypes fs =
693
  listE <$> mapM (\f -> genPyType (fieldIsOptional f) (fieldType f)) fs
694

    
695
-- | Generates Python default values from opcode parameters.
696
genPyDefaults :: [Field] -> ExpQ
697
genPyDefaults fs =
698
  listE $ map (\f -> maybeApp (fieldDefault f) (fieldType f)) fs
699

    
700
-- | Generates a Haskell function call to "showPyClass" with the
701
-- necessary information on how to build the Python class string.
702
pyClass :: OpCodeConstructor -> ExpQ
703
pyClass (consName, consType, consDoc, consFields, consDscField) =
704
  do let pyClassVar = varNameE "showPyClass"
705
         consName' = stringE consName
706
     consType' <- genPyType NotOptional consType
707
     let consDoc' = stringE consDoc
708
         consFieldNames = listE $ map (stringE . fieldName) consFields
709
         consFieldDocs = listE $ map (stringE . fieldDoc) consFields
710
     consFieldTypes <- genPyTypes consFields
711
     let consFieldDefaults = genPyDefaults consFields
712
     [| ($consName',
713
         $consType',
714
         $consDoc',
715
         $consFieldNames,
716
         $consFieldTypes,
717
         $consFieldDefaults,
718
         $consFieldDocs,
719
         consDscField) |]
720

    
721
-- | Generates a function called "pyClasses" that holds the list of
722
-- all the opcode descriptors necessary for generating the Python
723
-- opcodes.
724
pyClasses :: [OpCodeConstructor] -> Q [Dec]
725
pyClasses cons =
726
  do let name = mkName "pyClasses"
727
         sig = SigD name (AppT ListT (ConT ''OpCodeDescriptor))
728
     fn <- FunD name <$> (:[]) <$> declClause cons
729
     return [sig, fn]
730
  where declClause c =
731
          clause [] (normalB (ListE <$> mapM pyClass c)) []
732

    
733
-- | Converts from an opcode constructor to a Luxi constructor.
734
opcodeConsToLuxiCons :: (a, b, c, d, e) -> (a, d)
735
opcodeConsToLuxiCons (x, _, _, y, _) = (x, y)
736

    
737
-- | Generates the OpCode data type.
738
--
739
-- This takes an opcode logical definition, and builds both the
740
-- datatype and the JSON serialisation out of it. We can't use a
741
-- generic serialisation since we need to be compatible with Ganeti's
742
-- own, so we have a few quirks to work around.
743
genOpCode :: String              -- ^ Type name to use
744
          -> [OpCodeConstructor] -- ^ Constructor name and parameters
745
          -> Q [Dec]
746
genOpCode name cons = do
747
  let tname = mkName name
748
  decl_d <- mapM (\(cname, _, _, fields, _) -> do
749
                    -- we only need the type of the field, without Q
750
                    fields' <- mapM (fieldTypeInfo "op") fields
751
                    return $ RecC (mkName cname) fields')
752
            cons
753
  let declD = DataD [] tname [] decl_d [''Show, ''Eq]
754
  let (allfsig, allffn) = genAllOpFields "allOpFields" cons
755
  save_decs <- genSaveOpCode tname "saveOpCode" "toDictOpCode"
756
               (map opcodeConsToLuxiCons cons) saveConstructor True
757
  (loadsig, loadfn) <- genLoadOpCode cons
758
  pyDecls <- pyClasses cons
759
  return $ [declD, allfsig, allffn, loadsig, loadfn] ++ save_decs ++ pyDecls
760

    
761
-- | Generates the function pattern returning the list of fields for a
762
-- given constructor.
763
genOpConsFields :: OpCodeConstructor -> Clause
764
genOpConsFields (cname, _, _, fields, _) =
765
  let op_id = deCamelCase cname
766
      fvals = map (LitE . StringL) . sort . nub $
767
              concatMap (\f -> fieldName f:fieldExtraKeys f) fields
768
  in Clause [LitP (StringL op_id)] (NormalB $ ListE fvals) []
769

    
770
-- | Generates a list of all fields of an opcode constructor.
771
genAllOpFields  :: String              -- ^ Function name
772
                -> [OpCodeConstructor] -- ^ Object definition
773
                -> (Dec, Dec)
774
genAllOpFields sname opdefs =
775
  let cclauses = map genOpConsFields opdefs
776
      other = Clause [WildP] (NormalB (ListE [])) []
777
      fname = mkName sname
778
      sigt = AppT  (AppT ArrowT (ConT ''String)) (AppT ListT (ConT ''String))
779
  in (SigD fname sigt, FunD fname (cclauses++[other]))
780

    
781
-- | Generates the \"save\" clause for an entire opcode constructor.
782
--
783
-- This matches the opcode with variables named the same as the
784
-- constructor fields (just so that the spliced in code looks nicer),
785
-- and passes those name plus the parameter definition to 'saveObjectField'.
786
saveConstructor :: LuxiConstructor -- ^ The constructor
787
                -> Q Clause        -- ^ Resulting clause
788
saveConstructor (sname, fields) = do
789
  let cname = mkName sname
790
  fnames <- mapM (newName . fieldVariable) fields
791
  let pat = conP cname (map varP fnames)
792
  let felems = map (uncurry saveObjectField) (zip fnames fields)
793
      -- now build the OP_ID serialisation
794
      opid = [| [( $(stringE "OP_ID"),
795
                   JSON.showJSON $(stringE . deCamelCase $ sname) )] |]
796
      flist = listE (opid:felems)
797
      -- and finally convert all this to a json object
798
      flist' = [| concat $flist |]
799
  clause [pat] (normalB flist') []
800

    
801
-- | Generates the main save opcode function.
802
--
803
-- This builds a per-constructor match clause that contains the
804
-- respective constructor-serialisation code.
805
genSaveOpCode :: Name                          -- ^ Object ype
806
              -> String                        -- ^ To 'JSValue' function name
807
              -> String                        -- ^ To 'JSObject' function name
808
              -> [LuxiConstructor]             -- ^ Object definition
809
              -> (LuxiConstructor -> Q Clause) -- ^ Constructor save fn
810
              -> Bool                          -- ^ Whether to generate
811
                                               -- obj or just a
812
                                               -- list\/tuple of values
813
              -> Q [Dec]
814
genSaveOpCode tname jvalstr tdstr opdefs fn gen_object = do
815
  tdclauses <- mapM fn opdefs
816
  let typecon = ConT tname
817
      jvalname = mkName jvalstr
818
      jvalsig = AppT  (AppT ArrowT typecon) (ConT ''JSON.JSValue)
819
      tdname = mkName tdstr
820
  tdsig <- [t| $(return typecon) -> [(String, JSON.JSValue)] |]
821
  jvalclause <- if gen_object
822
                  then [| $makeObjE . $(varE tdname) |]
823
                  else [| JSON.showJSON . map snd . $(varE tdname) |]
824
  return [ SigD tdname tdsig
825
         , FunD tdname tdclauses
826
         , SigD jvalname jvalsig
827
         , ValD (VarP jvalname) (NormalB jvalclause) []]
828

    
829
-- | Generates load code for a single constructor of the opcode data type.
830
loadConstructor :: OpCodeConstructor -> Q Exp
831
loadConstructor (sname, _, _, fields, _) = do
832
  let name = mkName sname
833
  fbinds <- mapM loadObjectField fields
834
  let (fnames, fstmts) = unzip fbinds
835
  let cval = foldl (\accu fn -> AppE accu (VarE fn)) (ConE name) fnames
836
      fstmts' = fstmts ++ [NoBindS (AppE (VarE 'return) cval)]
837
  return $ DoE fstmts'
838

    
839
-- | Generates the loadOpCode function.
840
genLoadOpCode :: [OpCodeConstructor] -> Q (Dec, Dec)
841
genLoadOpCode opdefs = do
842
  let fname = mkName "loadOpCode"
843
      arg1 = mkName "v"
844
      objname = mkName "o"
845
      opid = mkName "op_id"
846
  st1 <- bindS (varP objname) [| liftM JSON.fromJSObject
847
                                 (JSON.readJSON $(varE arg1)) |]
848
  st2 <- bindS (varP opid) [| $fromObjE $(varE objname) $(stringE "OP_ID") |]
849
  -- the match results (per-constructor blocks)
850
  mexps <- mapM loadConstructor opdefs
851
  fails <- [| fail $ "Unknown opcode " ++ $(varE opid) |]
852
  let mpats = map (\(me, (consName, _, _, _, _)) ->
853
                       let mp = LitP . StringL . deCamelCase $ consName
854
                       in Match mp (NormalB me) []
855
                  ) $ zip mexps opdefs
856
      defmatch = Match WildP (NormalB fails) []
857
      cst = NoBindS $ CaseE (VarE opid) $ mpats++[defmatch]
858
      body = DoE [st1, st2, cst]
859
  sigt <- [t| JSON.JSValue -> JSON.Result $(conT (mkName "OpCode")) |]
860
  return $ (SigD fname sigt, FunD fname [Clause [VarP arg1] (NormalB body) []])
861

    
862
-- * Template code for luxi
863

    
864
-- | Constructor-to-string for LuxiOp.
865
genStrOfOp :: Name -> String -> Q [Dec]
866
genStrOfOp = genConstrToStr id
867

    
868
-- | Constructor-to-string for MsgKeys.
869
genStrOfKey :: Name -> String -> Q [Dec]
870
genStrOfKey = genConstrToStr ensureLower
871

    
872
-- | Generates the LuxiOp data type.
873
--
874
-- This takes a Luxi operation definition and builds both the
875
-- datatype and the function transforming the arguments to JSON.
876
-- We can't use anything less generic, because the way different
877
-- operations are serialized differs on both parameter- and top-level.
878
--
879
-- There are two things to be defined for each parameter:
880
--
881
-- * name
882
--
883
-- * type
884
--
885
genLuxiOp :: String -> [LuxiConstructor] -> Q [Dec]
886
genLuxiOp name cons = do
887
  let tname = mkName name
888
  decl_d <- mapM (\(cname, fields) -> do
889
                    -- we only need the type of the field, without Q
890
                    fields' <- mapM actualFieldType fields
891
                    let fields'' = zip (repeat NotStrict) fields'
892
                    return $ NormalC (mkName cname) fields'')
893
            cons
894
  let declD = DataD [] (mkName name) [] decl_d [''Show, ''Eq]
895
  save_decs <- genSaveOpCode tname "opToArgs" "opToDict"
896
               cons saveLuxiConstructor False
897
  req_defs <- declareSADT "LuxiReq" .
898
              map (\(str, _) -> ("Req" ++ str, mkName ("luxiReq" ++ str))) $
899
                  cons
900
  return $ declD:save_decs ++ req_defs
901

    
902
-- | Generates the \"save\" clause for entire LuxiOp constructor.
903
saveLuxiConstructor :: LuxiConstructor -> Q Clause
904
saveLuxiConstructor (sname, fields) = do
905
  let cname = mkName sname
906
  fnames <- mapM (newName . fieldVariable) fields
907
  let pat = conP cname (map varP fnames)
908
  let felems = map (uncurry saveObjectField) (zip fnames fields)
909
      flist = [| concat $(listE felems) |]
910
  clause [pat] (normalB flist) []
911

    
912
-- * "Objects" functionality
913

    
914
-- | Extract the field's declaration from a Field structure.
915
fieldTypeInfo :: String -> Field -> Q (Name, Strict, Type)
916
fieldTypeInfo field_pfx fd = do
917
  t <- actualFieldType fd
918
  let n = mkName . (field_pfx ++) . fieldRecordName $ fd
919
  return (n, NotStrict, t)
920

    
921
-- | Build an object declaration.
922
buildObject :: String -> String -> [Field] -> Q [Dec]
923
buildObject sname field_pfx fields = do
924
  let name = mkName sname
925
  fields_d <- mapM (fieldTypeInfo field_pfx) fields
926
  let decl_d = RecC name fields_d
927
  let declD = DataD [] name [] [decl_d] [''Show, ''Eq]
928
  ser_decls <- buildObjectSerialisation sname fields
929
  return $ declD:ser_decls
930

    
931
-- | Generates an object definition: data type and its JSON instance.
932
buildObjectSerialisation :: String -> [Field] -> Q [Dec]
933
buildObjectSerialisation sname fields = do
934
  let name = mkName sname
935
  savedecls <- genSaveObject saveObjectField sname fields
936
  (loadsig, loadfn) <- genLoadObject loadObjectField sname fields
937
  shjson <- objectShowJSON sname
938
  rdjson <- objectReadJSON sname
939
  let instdecl = InstanceD [] (AppT (ConT ''JSON.JSON) (ConT name))
940
                 [rdjson, shjson]
941
  return $ savedecls ++ [loadsig, loadfn, instdecl]
942

    
943
-- | The toDict function name for a given type.
944
toDictName :: String -> Name
945
toDictName sname = mkName ("toDict" ++ sname)
946

    
947
-- | Generates the save object functionality.
948
genSaveObject :: (Name -> Field -> Q Exp)
949
              -> String -> [Field] -> Q [Dec]
950
genSaveObject save_fn sname fields = do
951
  let name = mkName sname
952
  fnames <- mapM (newName . fieldVariable) fields
953
  let pat = conP name (map varP fnames)
954
  let tdname = toDictName sname
955
  tdsigt <- [t| $(conT name) -> [(String, JSON.JSValue)] |]
956

    
957
  let felems = map (uncurry save_fn) (zip fnames fields)
958
      flist = listE felems
959
      -- and finally convert all this to a json object
960
      tdlist = [| concat $flist |]
961
      iname = mkName "i"
962
  tclause <- clause [pat] (normalB tdlist) []
963
  cclause <- [| $makeObjE . $(varE tdname) |]
964
  let fname = mkName ("save" ++ sname)
965
  sigt <- [t| $(conT name) -> JSON.JSValue |]
966
  return [SigD tdname tdsigt, FunD tdname [tclause],
967
          SigD fname sigt, ValD (VarP fname) (NormalB cclause) []]
968

    
969
-- | Generates the code for saving an object's field, handling the
970
-- various types of fields that we have.
971
saveObjectField :: Name -> Field -> Q Exp
972
saveObjectField fvar field =
973
  case fieldIsOptional field of
974
    OptionalOmitNull -> [| case $(varE fvar) of
975
                             Nothing -> []
976
                             Just v  -> [( $nameE, JSON.showJSON v )]
977
                         |]
978
    OptionalSerializeNull -> [| case $(varE fvar) of
979
                                  Nothing -> [( $nameE, JSON.JSNull )]
980
                                  Just v  -> [( $nameE, JSON.showJSON v )]
981
                              |]
982
    NotOptional ->
983
      case fieldShow field of
984
        -- Note: the order of actual:extra is important, since for
985
        -- some serialisation types (e.g. Luxi), we use tuples
986
        -- (positional info) rather than object (name info)
987
        Nothing -> [| [( $nameE, JSON.showJSON $fvarE)] |]
988
        Just fn -> [| let (actual, extra) = $fn $fvarE
989
                      in ($nameE, JSON.showJSON actual):extra
990
                    |]
991
  where nameE = stringE (fieldName field)
992
        fvarE = varE fvar
993

    
994
-- | Generates the showJSON clause for a given object name.
995
objectShowJSON :: String -> Q Dec
996
objectShowJSON name = do
997
  body <- [| JSON.showJSON . $(varE . mkName $ "save" ++ name) |]
998
  return $ FunD 'JSON.showJSON [Clause [] (NormalB body) []]
999

    
1000
-- | Generates the load object functionality.
1001
genLoadObject :: (Field -> Q (Name, Stmt))
1002
              -> String -> [Field] -> Q (Dec, Dec)
1003
genLoadObject load_fn sname fields = do
1004
  let name = mkName sname
1005
      funname = mkName $ "load" ++ sname
1006
      arg1 = mkName $ if null fields then "_" else "v"
1007
      objname = mkName "o"
1008
      opid = mkName "op_id"
1009
  st1 <- bindS (varP objname) [| liftM JSON.fromJSObject
1010
                                 (JSON.readJSON $(varE arg1)) |]
1011
  fbinds <- mapM load_fn fields
1012
  let (fnames, fstmts) = unzip fbinds
1013
  let cval = foldl (\accu fn -> AppE accu (VarE fn)) (ConE name) fnames
1014
      retstmt = [NoBindS (AppE (VarE 'return) cval)]
1015
      -- FIXME: should we require an empty dict for an empty type?
1016
      -- this allows any JSValue right now
1017
      fstmts' = if null fields
1018
                  then retstmt
1019
                  else st1:fstmts ++ retstmt
1020
  sigt <- [t| JSON.JSValue -> JSON.Result $(conT name) |]
1021
  return $ (SigD funname sigt,
1022
            FunD funname [Clause [VarP arg1] (NormalB (DoE fstmts')) []])
1023

    
1024
-- | Generates code for loading an object's field.
1025
loadObjectField :: Field -> Q (Name, Stmt)
1026
loadObjectField field = do
1027
  let name = fieldVariable field
1028
  fvar <- newName name
1029
  -- these are used in all patterns below
1030
  let objvar = varNameE "o"
1031
      objfield = stringE (fieldName field)
1032
      loadexp =
1033
        if fieldIsOptional field /= NotOptional
1034
          -- we treat both optional types the same, since
1035
          -- 'maybeFromObj' can deal with both missing and null values
1036
          -- appropriately (the same)
1037
          then [| $(varE 'maybeFromObj) $objvar $objfield |]
1038
          else case fieldDefault field of
1039
                 Just defv ->
1040
                   [| $(varE 'fromObjWithDefault) $objvar
1041
                      $objfield $defv |]
1042
                 Nothing -> [| $fromObjE $objvar $objfield |]
1043
  bexp <- loadFn field loadexp objvar
1044

    
1045
  return (fvar, BindS (VarP fvar) bexp)
1046

    
1047
-- | Builds the readJSON instance for a given object name.
1048
objectReadJSON :: String -> Q Dec
1049
objectReadJSON name = do
1050
  let s = mkName "s"
1051
  body <- [| case JSON.readJSON $(varE s) of
1052
               JSON.Ok s' -> $(varE .mkName $ "load" ++ name) s'
1053
               JSON.Error e ->
1054
                 JSON.Error $ "Can't parse value for type " ++
1055
                       $(stringE name) ++ ": " ++ e
1056
           |]
1057
  return $ FunD 'JSON.readJSON [Clause [VarP s] (NormalB body) []]
1058

    
1059
-- * Inheritable parameter tables implementation
1060

    
1061
-- | Compute parameter type names.
1062
paramTypeNames :: String -> (String, String)
1063
paramTypeNames root = ("Filled"  ++ root ++ "Params",
1064
                       "Partial" ++ root ++ "Params")
1065

    
1066
-- | Compute information about the type of a parameter field.
1067
paramFieldTypeInfo :: String -> Field -> Q (Name, Strict, Type)
1068
paramFieldTypeInfo field_pfx fd = do
1069
  t <- actualFieldType fd
1070
  let n = mkName . (++ "P") . (field_pfx ++) .
1071
          fieldRecordName $ fd
1072
  return (n, NotStrict, AppT (ConT ''Maybe) t)
1073

    
1074
-- | Build a parameter declaration.
1075
--
1076
-- This function builds two different data structures: a /filled/ one,
1077
-- in which all fields are required, and a /partial/ one, in which all
1078
-- fields are optional. Due to the current record syntax issues, the
1079
-- fields need to be named differrently for the two structures, so the
1080
-- partial ones get a /P/ suffix.
1081
buildParam :: String -> String -> [Field] -> Q [Dec]
1082
buildParam sname field_pfx fields = do
1083
  let (sname_f, sname_p) = paramTypeNames sname
1084
      name_f = mkName sname_f
1085
      name_p = mkName sname_p
1086
  fields_f <- mapM (fieldTypeInfo field_pfx) fields
1087
  fields_p <- mapM (paramFieldTypeInfo field_pfx) fields
1088
  let decl_f = RecC name_f fields_f
1089
      decl_p = RecC name_p fields_p
1090
  let declF = DataD [] name_f [] [decl_f] [''Show, ''Eq]
1091
      declP = DataD [] name_p [] [decl_p] [''Show, ''Eq]
1092
  ser_decls_f <- buildObjectSerialisation sname_f fields
1093
  ser_decls_p <- buildPParamSerialisation sname_p fields
1094
  fill_decls <- fillParam sname field_pfx fields
1095
  return $ [declF, declP] ++ ser_decls_f ++ ser_decls_p ++ fill_decls ++
1096
           buildParamAllFields sname fields ++
1097
           buildDictObjectInst name_f sname_f
1098

    
1099
-- | Builds a list of all fields of a parameter.
1100
buildParamAllFields :: String -> [Field] -> [Dec]
1101
buildParamAllFields sname fields =
1102
  let vname = mkName ("all" ++ sname ++ "ParamFields")
1103
      sig = SigD vname (AppT ListT (ConT ''String))
1104
      val = ListE $ map (LitE . StringL . fieldName) fields
1105
  in [sig, ValD (VarP vname) (NormalB val) []]
1106

    
1107
-- | Builds the 'DictObject' instance for a filled parameter.
1108
buildDictObjectInst :: Name -> String -> [Dec]
1109
buildDictObjectInst name sname =
1110
  [InstanceD [] (AppT (ConT ''DictObject) (ConT name))
1111
   [ValD (VarP 'toDict) (NormalB (VarE (toDictName sname))) []]]
1112

    
1113
-- | Generates the serialisation for a partial parameter.
1114
buildPParamSerialisation :: String -> [Field] -> Q [Dec]
1115
buildPParamSerialisation sname fields = do
1116
  let name = mkName sname
1117
  savedecls <- genSaveObject savePParamField sname fields
1118
  (loadsig, loadfn) <- genLoadObject loadPParamField sname fields
1119
  shjson <- objectShowJSON sname
1120
  rdjson <- objectReadJSON sname
1121
  let instdecl = InstanceD [] (AppT (ConT ''JSON.JSON) (ConT name))
1122
                 [rdjson, shjson]
1123
  return $ savedecls ++ [loadsig, loadfn, instdecl]
1124

    
1125
-- | Generates code to save an optional parameter field.
1126
savePParamField :: Name -> Field -> Q Exp
1127
savePParamField fvar field = do
1128
  checkNonOptDef field
1129
  let actualVal = mkName "v"
1130
  normalexpr <- saveObjectField actualVal field
1131
  -- we have to construct the block here manually, because we can't
1132
  -- splice-in-splice
1133
  return $ CaseE (VarE fvar) [ Match (ConP 'Nothing [])
1134
                                       (NormalB (ConE '[])) []
1135
                             , Match (ConP 'Just [VarP actualVal])
1136
                                       (NormalB normalexpr) []
1137
                             ]
1138

    
1139
-- | Generates code to load an optional parameter field.
1140
loadPParamField :: Field -> Q (Name, Stmt)
1141
loadPParamField field = do
1142
  checkNonOptDef field
1143
  let name = fieldName field
1144
  fvar <- newName name
1145
  -- these are used in all patterns below
1146
  let objvar = varNameE "o"
1147
      objfield = stringE name
1148
      loadexp = [| $(varE 'maybeFromObj) $objvar $objfield |]
1149
      field' = field {fieldRead=fmap (appE (varE 'makeReadOptional))
1150
                                  $ fieldRead field}
1151
  bexp <- loadFn field' loadexp objvar
1152
  return (fvar, BindS (VarP fvar) bexp)
1153

    
1154
-- | Builds a simple declaration of type @n_x = fromMaybe f_x p_x@.
1155
buildFromMaybe :: String -> Q Dec
1156
buildFromMaybe fname =
1157
  valD (varP (mkName $ "n_" ++ fname))
1158
         (normalB [| $(varE 'fromMaybe)
1159
                        $(varNameE $ "f_" ++ fname)
1160
                        $(varNameE $ "p_" ++ fname) |]) []
1161

    
1162
-- | Builds a function that executes the filling of partial parameter
1163
-- from a full copy (similar to Python's fillDict).
1164
fillParam :: String -> String -> [Field] -> Q [Dec]
1165
fillParam sname field_pfx fields = do
1166
  let fnames = map (\fd -> field_pfx ++ fieldRecordName fd) fields
1167
      (sname_f, sname_p) = paramTypeNames sname
1168
      oname_f = "fobj"
1169
      oname_p = "pobj"
1170
      name_f = mkName sname_f
1171
      name_p = mkName sname_p
1172
      fun_name = mkName $ "fill" ++ sname ++ "Params"
1173
      le_full = ValD (ConP name_f (map (VarP . mkName . ("f_" ++)) fnames))
1174
                (NormalB . VarE . mkName $ oname_f) []
1175
      le_part = ValD (ConP name_p (map (VarP . mkName . ("p_" ++)) fnames))
1176
                (NormalB . VarE . mkName $ oname_p) []
1177
      obj_new = foldl (\accu vname -> AppE accu (VarE vname)) (ConE name_f)
1178
                $ map (mkName . ("n_" ++)) fnames
1179
  le_new <- mapM buildFromMaybe fnames
1180
  funt <- [t| $(conT name_f) -> $(conT name_p) -> $(conT name_f) |]
1181
  let sig = SigD fun_name funt
1182
      fclause = Clause [VarP (mkName oname_f), VarP (mkName oname_p)]
1183
                (NormalB $ LetE (le_full:le_part:le_new) obj_new) []
1184
      fun = FunD fun_name [fclause]
1185
  return [sig, fun]
1186

    
1187
-- * Template code for exceptions
1188

    
1189
-- | Exception simple error message field.
1190
excErrMsg :: (String, Q Type)
1191
excErrMsg = ("errMsg", [t| String |])
1192

    
1193
-- | Builds an exception type definition.
1194
genException :: String                  -- ^ Name of new type
1195
             -> SimpleObject -- ^ Constructor name and parameters
1196
             -> Q [Dec]
1197
genException name cons = do
1198
  let tname = mkName name
1199
  declD <- buildSimpleCons tname cons
1200
  (savesig, savefn) <- genSaveSimpleObj tname ("save" ++ name) cons $
1201
                         uncurry saveExcCons
1202
  (loadsig, loadfn) <- genLoadExc tname ("load" ++ name) cons
1203
  return [declD, loadsig, loadfn, savesig, savefn]
1204

    
1205
-- | Generates the \"save\" clause for an entire exception constructor.
1206
--
1207
-- This matches the exception with variables named the same as the
1208
-- constructor fields (just so that the spliced in code looks nicer),
1209
-- and calls showJSON on it.
1210
saveExcCons :: String        -- ^ The constructor name
1211
            -> [SimpleField] -- ^ The parameter definitions for this
1212
                             -- constructor
1213
            -> Q Clause      -- ^ Resulting clause
1214
saveExcCons sname fields = do
1215
  let cname = mkName sname
1216
  fnames <- mapM (newName . fst) fields
1217
  let pat = conP cname (map varP fnames)
1218
      felems = if null fnames
1219
                 then conE '() -- otherwise, empty list has no type
1220
                 else listE $ map (\f -> [| JSON.showJSON $(varE f) |]) fnames
1221
  let tup = tupE [ litE (stringL sname), felems ]
1222
  clause [pat] (normalB [| JSON.showJSON $tup |]) []
1223

    
1224
-- | Generates load code for a single constructor of an exception.
1225
--
1226
-- Generates the code (if there's only one argument, we will use a
1227
-- list, not a tuple:
1228
--
1229
-- @
1230
-- do
1231
--  (x1, x2, ...) <- readJSON args
1232
--  return $ Cons x1 x2 ...
1233
-- @
1234
loadExcConstructor :: Name -> String -> [SimpleField] -> Q Exp
1235
loadExcConstructor inname sname fields = do
1236
  let name = mkName sname
1237
  f_names <- mapM (newName . fst) fields
1238
  let read_args = AppE (VarE 'JSON.readJSON) (VarE inname)
1239
  let binds = case f_names of
1240
                [x] -> BindS (ListP [VarP x])
1241
                _   -> BindS (TupP (map VarP f_names))
1242
      cval = foldl (\accu fn -> AppE accu (VarE fn)) (ConE name) f_names
1243
  return $ DoE [binds read_args, NoBindS (AppE (VarE 'return) cval)]
1244

    
1245
{-| Generates the loadException function.
1246

    
1247
This generates a quite complicated function, along the lines of:
1248

    
1249
@
1250
loadFn (JSArray [JSString name, args]) = case name of
1251
   "A1" -> do
1252
     (x1, x2, ...) <- readJSON args
1253
     return $ A1 x1 x2 ...
1254
   "a2" -> ...
1255
   s -> fail $ "Unknown exception" ++ s
1256
loadFn v = fail $ "Expected array but got " ++ show v
1257
@
1258
-}
1259
genLoadExc :: Name -> String -> SimpleObject -> Q (Dec, Dec)
1260
genLoadExc tname sname opdefs = do
1261
  let fname = mkName sname
1262
  exc_name <- newName "name"
1263
  exc_args <- newName "args"
1264
  exc_else <- newName "s"
1265
  arg_else <- newName "v"
1266
  fails <- [| fail $ "Unknown exception '" ++ $(varE exc_else) ++ "'" |]
1267
  -- default match for unknown exception name
1268
  let defmatch = Match (VarP exc_else) (NormalB fails) []
1269
  -- the match results (per-constructor blocks)
1270
  str_matches <-
1271
    mapM (\(s, params) -> do
1272
            body_exp <- loadExcConstructor exc_args s params
1273
            return $ Match (LitP (StringL s)) (NormalB body_exp) [])
1274
    opdefs
1275
  -- the first function clause; we can't use [| |] due to TH
1276
  -- limitations, so we have to build the AST by hand
1277
  let clause1 = Clause [ConP 'JSON.JSArray
1278
                               [ListP [ConP 'JSON.JSString [VarP exc_name],
1279
                                            VarP exc_args]]]
1280
                (NormalB (CaseE (AppE (VarE 'JSON.fromJSString)
1281
                                        (VarE exc_name))
1282
                          (str_matches ++ [defmatch]))) []
1283
  -- the fail expression for the second function clause
1284
  fail_type <- [| fail $ "Invalid exception: expected '(string, [args])' " ++
1285
                  "      but got " ++ show (pp_value $(varE arg_else)) ++ "'"
1286
                |]
1287
  -- the second function clause
1288
  let clause2 = Clause [VarP arg_else] (NormalB fail_type) []
1289
  sigt <- [t| JSON.JSValue -> JSON.Result $(conT tname) |]
1290
  return $ (SigD fname sigt, FunD fname [clause1, clause2])