Statistics
| Branch: | Tag: | Revision:

root / htools / Ganeti / THH.hs @ 92678b3c

History | View | Annotate | Download (17.6 kB)

1 e9aaa3c6 Iustin Pop
{-# LANGUAGE TemplateHaskell, QuasiQuotes #-}
2 e9aaa3c6 Iustin Pop
3 e9aaa3c6 Iustin Pop
{-| TemplateHaskell helper for HTools.
4 e9aaa3c6 Iustin Pop
5 e9aaa3c6 Iustin Pop
As TemplateHaskell require that splices be defined in a separate
6 e9aaa3c6 Iustin Pop
module, we combine all the TemplateHaskell functionality that HTools
7 e9aaa3c6 Iustin Pop
needs in this module (except the one for unittests).
8 e9aaa3c6 Iustin Pop
9 e9aaa3c6 Iustin Pop
-}
10 e9aaa3c6 Iustin Pop
11 e9aaa3c6 Iustin Pop
{-
12 e9aaa3c6 Iustin Pop
13 e9aaa3c6 Iustin Pop
Copyright (C) 2011 Google Inc.
14 e9aaa3c6 Iustin Pop
15 e9aaa3c6 Iustin Pop
This program is free software; you can redistribute it and/or modify
16 e9aaa3c6 Iustin Pop
it under the terms of the GNU General Public License as published by
17 e9aaa3c6 Iustin Pop
the Free Software Foundation; either version 2 of the License, or
18 e9aaa3c6 Iustin Pop
(at your option) any later version.
19 e9aaa3c6 Iustin Pop
20 e9aaa3c6 Iustin Pop
This program is distributed in the hope that it will be useful, but
21 e9aaa3c6 Iustin Pop
WITHOUT ANY WARRANTY; without even the implied warranty of
22 e9aaa3c6 Iustin Pop
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
23 e9aaa3c6 Iustin Pop
General Public License for more details.
24 e9aaa3c6 Iustin Pop
25 e9aaa3c6 Iustin Pop
You should have received a copy of the GNU General Public License
26 e9aaa3c6 Iustin Pop
along with this program; if not, write to the Free Software
27 e9aaa3c6 Iustin Pop
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
28 e9aaa3c6 Iustin Pop
02110-1301, USA.
29 e9aaa3c6 Iustin Pop
30 e9aaa3c6 Iustin Pop
-}
31 e9aaa3c6 Iustin Pop
32 92678b3c Iustin Pop
module Ganeti.THH ( Store(..)
33 92678b3c Iustin Pop
                  , declareSADT
34 e9aaa3c6 Iustin Pop
                  , makeJSONInstance
35 6111e296 Iustin Pop
                  , genOpID
36 12c19659 Iustin Pop
                  , genOpCode
37 12c19659 Iustin Pop
                  , noDefault
38 a0090487 Agata Murawska
                  , genStrOfOp
39 a0090487 Agata Murawska
                  , genStrOfKey
40 a0090487 Agata Murawska
                  , genLuxiOp
41 e9aaa3c6 Iustin Pop
                  ) where
42 e9aaa3c6 Iustin Pop
43 60de49c3 Iustin Pop
import Control.Monad (liftM, liftM2)
44 e9aaa3c6 Iustin Pop
import Data.Char
45 6111e296 Iustin Pop
import Data.List
46 e9aaa3c6 Iustin Pop
import Language.Haskell.TH
47 e9aaa3c6 Iustin Pop
48 e9aaa3c6 Iustin Pop
import qualified Text.JSON as JSON
49 e9aaa3c6 Iustin Pop
50 53664e15 Iustin Pop
-- * Helper functions
51 53664e15 Iustin Pop
52 e9aaa3c6 Iustin Pop
-- | Ensure first letter is lowercase.
53 e9aaa3c6 Iustin Pop
--
54 e9aaa3c6 Iustin Pop
-- Used to convert type name to function prefix, e.g. in @data Aa ->
55 e9aaa3c6 Iustin Pop
-- aaToString@.
56 e9aaa3c6 Iustin Pop
ensureLower :: String -> String
57 e9aaa3c6 Iustin Pop
ensureLower [] = []
58 e9aaa3c6 Iustin Pop
ensureLower (x:xs) = toLower x:xs
59 e9aaa3c6 Iustin Pop
60 53664e15 Iustin Pop
-- | Helper for quoted expressions.
61 53664e15 Iustin Pop
varNameE :: String -> Q Exp
62 53664e15 Iustin Pop
varNameE = varE . mkName
63 53664e15 Iustin Pop
64 53664e15 Iustin Pop
-- | showJSON as an expression, for reuse.
65 53664e15 Iustin Pop
showJSONE :: Q Exp
66 53664e15 Iustin Pop
showJSONE = varNameE "showJSON"
67 53664e15 Iustin Pop
68 e9aaa3c6 Iustin Pop
-- | ToString function name.
69 e9aaa3c6 Iustin Pop
toStrName :: String -> Name
70 e9aaa3c6 Iustin Pop
toStrName = mkName . (++ "ToString") . ensureLower
71 e9aaa3c6 Iustin Pop
72 e9aaa3c6 Iustin Pop
-- | FromString function name.
73 e9aaa3c6 Iustin Pop
fromStrName :: String -> Name
74 e9aaa3c6 Iustin Pop
fromStrName = mkName . (++ "FromString") . ensureLower
75 e9aaa3c6 Iustin Pop
76 6111e296 Iustin Pop
-- | Converts a name to it's varE/litE representations.
77 6111e296 Iustin Pop
--
78 6111e296 Iustin Pop
reprE :: Either String Name -> Q Exp
79 53664e15 Iustin Pop
reprE = either stringE varE
80 53664e15 Iustin Pop
81 60de49c3 Iustin Pop
-- | Smarter function application.
82 60de49c3 Iustin Pop
--
83 60de49c3 Iustin Pop
-- This does simply f x, except that if is 'id', it will skip it, in
84 60de49c3 Iustin Pop
-- order to generate more readable code when using -ddump-splices.
85 60de49c3 Iustin Pop
appFn :: Exp -> Exp -> Exp
86 60de49c3 Iustin Pop
appFn f x | f == VarE 'id = x
87 60de49c3 Iustin Pop
          | otherwise = AppE f x
88 60de49c3 Iustin Pop
89 53664e15 Iustin Pop
-- * Template code for simple string-equivalent ADTs
90 6111e296 Iustin Pop
91 e9aaa3c6 Iustin Pop
-- | Generates a data type declaration.
92 e9aaa3c6 Iustin Pop
--
93 e9aaa3c6 Iustin Pop
-- The type will have a fixed list of instances.
94 e9aaa3c6 Iustin Pop
strADTDecl :: Name -> [String] -> Dec
95 e9aaa3c6 Iustin Pop
strADTDecl name constructors =
96 e9aaa3c6 Iustin Pop
    DataD [] name []
97 e9aaa3c6 Iustin Pop
              (map (flip NormalC [] . mkName) constructors)
98 e9aaa3c6 Iustin Pop
              [''Show, ''Read, ''Eq, ''Enum, ''Bounded, ''Ord]
99 e9aaa3c6 Iustin Pop
100 e9aaa3c6 Iustin Pop
-- | Generates a toString function.
101 e9aaa3c6 Iustin Pop
--
102 e9aaa3c6 Iustin Pop
-- This generates a simple function of the form:
103 e9aaa3c6 Iustin Pop
--
104 e9aaa3c6 Iustin Pop
-- @
105 e9aaa3c6 Iustin Pop
-- nameToString :: Name -> String
106 e9aaa3c6 Iustin Pop
-- nameToString Cons1 = var1
107 e9aaa3c6 Iustin Pop
-- nameToString Cons2 = \"value2\"
108 e9aaa3c6 Iustin Pop
-- @
109 6111e296 Iustin Pop
genToString :: Name -> Name -> [(String, Either String Name)] -> Q [Dec]
110 e9aaa3c6 Iustin Pop
genToString fname tname constructors = do
111 e9aaa3c6 Iustin Pop
  sigt <- [t| $(conT tname) -> String |]
112 e9aaa3c6 Iustin Pop
  -- the body clauses, matching on the constructor and returning the
113 e9aaa3c6 Iustin Pop
  -- string value
114 e9aaa3c6 Iustin Pop
  clauses <- mapM  (\(c, v) -> clause [recP (mkName c) []]
115 6111e296 Iustin Pop
                             (normalB (reprE v)) []) constructors
116 e9aaa3c6 Iustin Pop
  return [SigD fname sigt, FunD fname clauses]
117 e9aaa3c6 Iustin Pop
118 e9aaa3c6 Iustin Pop
-- | Generates a fromString function.
119 e9aaa3c6 Iustin Pop
--
120 e9aaa3c6 Iustin Pop
-- The function generated is monadic and can fail parsing the
121 e9aaa3c6 Iustin Pop
-- string. It is of the form:
122 e9aaa3c6 Iustin Pop
--
123 e9aaa3c6 Iustin Pop
-- @
124 e9aaa3c6 Iustin Pop
-- nameFromString :: (Monad m) => String -> m Name
125 e9aaa3c6 Iustin Pop
-- nameFromString s | s == var1       = Cons1
126 e9aaa3c6 Iustin Pop
--                  | s == \"value2\" = Cons2
127 e9aaa3c6 Iustin Pop
--                  | otherwise = fail /.../
128 e9aaa3c6 Iustin Pop
-- @
129 e9aaa3c6 Iustin Pop
genFromString :: Name -> Name -> [(String, Name)] -> Q [Dec]
130 e9aaa3c6 Iustin Pop
genFromString fname tname constructors = do
131 e9aaa3c6 Iustin Pop
  -- signature of form (Monad m) => String -> m $name
132 e9aaa3c6 Iustin Pop
  sigt <- [t| (Monad m) => String -> m $(conT tname) |]
133 e9aaa3c6 Iustin Pop
  -- clauses for a guarded pattern
134 e9aaa3c6 Iustin Pop
  let varp = mkName "s"
135 e9aaa3c6 Iustin Pop
      varpe = varE varp
136 e9aaa3c6 Iustin Pop
  clauses <- mapM (\(c, v) -> do
137 e9aaa3c6 Iustin Pop
                     -- the clause match condition
138 e9aaa3c6 Iustin Pop
                     g <- normalG [| $varpe == $(varE v) |]
139 e9aaa3c6 Iustin Pop
                     -- the clause result
140 e9aaa3c6 Iustin Pop
                     r <- [| return $(conE (mkName c)) |]
141 e9aaa3c6 Iustin Pop
                     return (g, r)) constructors
142 e9aaa3c6 Iustin Pop
  -- the otherwise clause (fallback)
143 e9aaa3c6 Iustin Pop
  oth_clause <- do
144 e9aaa3c6 Iustin Pop
    g <- normalG [| otherwise |]
145 e9aaa3c6 Iustin Pop
    r <- [|fail ("Invalid string value for type " ++
146 e9aaa3c6 Iustin Pop
                 $(litE (stringL (nameBase tname))) ++ ": " ++ $varpe) |]
147 e9aaa3c6 Iustin Pop
    return (g, r)
148 e9aaa3c6 Iustin Pop
  let fun = FunD fname [Clause [VarP varp]
149 e9aaa3c6 Iustin Pop
                        (GuardedB (clauses++[oth_clause])) []]
150 e9aaa3c6 Iustin Pop
  return [SigD fname sigt, fun]
151 e9aaa3c6 Iustin Pop
152 e9aaa3c6 Iustin Pop
-- | Generates a data type from a given string format.
153 e9aaa3c6 Iustin Pop
--
154 e9aaa3c6 Iustin Pop
-- The format is expected to multiline. The first line contains the
155 e9aaa3c6 Iustin Pop
-- type name, and the rest of the lines must contain two words: the
156 e9aaa3c6 Iustin Pop
-- constructor name and then the string representation of the
157 e9aaa3c6 Iustin Pop
-- respective constructor.
158 e9aaa3c6 Iustin Pop
--
159 e9aaa3c6 Iustin Pop
-- The function will generate the data type declaration, and then two
160 e9aaa3c6 Iustin Pop
-- functions:
161 e9aaa3c6 Iustin Pop
--
162 e9aaa3c6 Iustin Pop
-- * /name/ToString, which converts the type to a string
163 e9aaa3c6 Iustin Pop
--
164 e9aaa3c6 Iustin Pop
-- * /name/FromString, which (monadically) converts from a string to the type
165 e9aaa3c6 Iustin Pop
--
166 e9aaa3c6 Iustin Pop
-- Note that this is basically just a custom show/read instance,
167 e9aaa3c6 Iustin Pop
-- nothing else.
168 e9aaa3c6 Iustin Pop
declareSADT :: String -> [(String, Name)] -> Q [Dec]
169 e9aaa3c6 Iustin Pop
declareSADT sname cons = do
170 e9aaa3c6 Iustin Pop
  let name = mkName sname
171 e9aaa3c6 Iustin Pop
      ddecl = strADTDecl name (map fst cons)
172 6111e296 Iustin Pop
      -- process cons in the format expected by genToString
173 6111e296 Iustin Pop
      cons' = map (\(a, b) -> (a, Right b)) cons
174 6111e296 Iustin Pop
  tostr <- genToString (toStrName sname) name cons'
175 e9aaa3c6 Iustin Pop
  fromstr <- genFromString (fromStrName sname) name cons
176 e9aaa3c6 Iustin Pop
  return $ ddecl:tostr ++ fromstr
177 e9aaa3c6 Iustin Pop
178 e9aaa3c6 Iustin Pop
179 e9aaa3c6 Iustin Pop
-- | Creates the showJSON member of a JSON instance declaration.
180 e9aaa3c6 Iustin Pop
--
181 e9aaa3c6 Iustin Pop
-- This will create what is the equivalent of:
182 e9aaa3c6 Iustin Pop
--
183 e9aaa3c6 Iustin Pop
-- @
184 e9aaa3c6 Iustin Pop
-- showJSON = showJSON . /name/ToString
185 e9aaa3c6 Iustin Pop
-- @
186 e9aaa3c6 Iustin Pop
--
187 e9aaa3c6 Iustin Pop
-- in an instance JSON /name/ declaration
188 e9aaa3c6 Iustin Pop
genShowJSON :: String -> Q [Dec]
189 e9aaa3c6 Iustin Pop
genShowJSON name = [d| showJSON = JSON.showJSON . $(varE (toStrName name)) |]
190 e9aaa3c6 Iustin Pop
191 e9aaa3c6 Iustin Pop
-- | Creates the readJSON member of a JSON instance declaration.
192 e9aaa3c6 Iustin Pop
--
193 e9aaa3c6 Iustin Pop
-- This will create what is the equivalent of:
194 e9aaa3c6 Iustin Pop
--
195 e9aaa3c6 Iustin Pop
-- @
196 e9aaa3c6 Iustin Pop
-- readJSON s = case readJSON s of
197 e9aaa3c6 Iustin Pop
--                Ok s' -> /name/FromString s'
198 e9aaa3c6 Iustin Pop
--                Error e -> Error /description/
199 e9aaa3c6 Iustin Pop
-- @
200 e9aaa3c6 Iustin Pop
--
201 e9aaa3c6 Iustin Pop
-- in an instance JSON /name/ declaration
202 e9aaa3c6 Iustin Pop
genReadJSON :: String -> Q Dec
203 e9aaa3c6 Iustin Pop
genReadJSON name = do
204 e9aaa3c6 Iustin Pop
  let s = mkName "s"
205 e9aaa3c6 Iustin Pop
  body <- [| case JSON.readJSON $(varE s) of
206 e9aaa3c6 Iustin Pop
               JSON.Ok s' -> $(varE (fromStrName name)) s'
207 e9aaa3c6 Iustin Pop
               JSON.Error e ->
208 e9aaa3c6 Iustin Pop
                   JSON.Error $ "Can't parse string value for type " ++
209 53664e15 Iustin Pop
                           $(stringE name) ++ ": " ++ e
210 e9aaa3c6 Iustin Pop
           |]
211 e9aaa3c6 Iustin Pop
  return $ FunD (mkName "readJSON") [Clause [VarP s] (NormalB body) []]
212 e9aaa3c6 Iustin Pop
213 e9aaa3c6 Iustin Pop
-- | Generates a JSON instance for a given type.
214 e9aaa3c6 Iustin Pop
--
215 e9aaa3c6 Iustin Pop
-- This assumes that the /name/ToString and /name/FromString functions
216 e9aaa3c6 Iustin Pop
-- have been defined as by the 'declareSADT' function.
217 e9aaa3c6 Iustin Pop
makeJSONInstance :: Name -> Q [Dec]
218 e9aaa3c6 Iustin Pop
makeJSONInstance name = do
219 e9aaa3c6 Iustin Pop
  let base = nameBase name
220 e9aaa3c6 Iustin Pop
  showJ <- genShowJSON base
221 e9aaa3c6 Iustin Pop
  readJ <- genReadJSON base
222 e9aaa3c6 Iustin Pop
  return [InstanceD [] (AppT (ConT ''JSON.JSON) (ConT name)) (readJ:showJ)]
223 6111e296 Iustin Pop
224 53664e15 Iustin Pop
-- * Template code for opcodes
225 53664e15 Iustin Pop
226 6111e296 Iustin Pop
-- | Transforms a CamelCase string into an_underscore_based_one.
227 6111e296 Iustin Pop
deCamelCase :: String -> String
228 6111e296 Iustin Pop
deCamelCase =
229 6111e296 Iustin Pop
    intercalate "_" . map (map toUpper) . groupBy (\_ b -> not $ isUpper b)
230 6111e296 Iustin Pop
231 05ff7a00 Agata Murawska
-- | Computes the name of a given constructor.
232 6111e296 Iustin Pop
constructorName :: Con -> Q Name
233 6111e296 Iustin Pop
constructorName (NormalC name _) = return name
234 6111e296 Iustin Pop
constructorName (RecC name _)    = return name
235 6111e296 Iustin Pop
constructorName x                = fail $ "Unhandled constructor " ++ show x
236 6111e296 Iustin Pop
237 a0090487 Agata Murawska
-- | Builds the generic constructor-to-string function.
238 6111e296 Iustin Pop
--
239 6111e296 Iustin Pop
-- This generates a simple function of the following form:
240 6111e296 Iustin Pop
--
241 6111e296 Iustin Pop
-- @
242 a0090487 Agata Murawska
-- fname (ConStructorOne {}) = trans_fun("ConStructorOne")
243 a0090487 Agata Murawska
-- fname (ConStructorTwo {}) = trans_fun("ConStructorTwo")
244 6111e296 Iustin Pop
-- @
245 6111e296 Iustin Pop
--
246 6111e296 Iustin Pop
-- This builds a custom list of name/string pairs and then uses
247 6111e296 Iustin Pop
-- 'genToString' to actually generate the function
248 a0090487 Agata Murawska
genConstrToStr :: (String -> String) -> Name -> String -> Q [Dec]
249 a0090487 Agata Murawska
genConstrToStr trans_fun name fname = do
250 6111e296 Iustin Pop
  TyConI (DataD _ _ _ cons _) <- reify name
251 6111e296 Iustin Pop
  cnames <- mapM (liftM nameBase . constructorName) cons
252 a0090487 Agata Murawska
  let svalues = map (Left . trans_fun) cnames
253 6111e296 Iustin Pop
  genToString (mkName fname) name $ zip cnames svalues
254 12c19659 Iustin Pop
255 a0090487 Agata Murawska
-- | Constructor-to-string for OpCode.
256 a0090487 Agata Murawska
genOpID :: Name -> String -> Q [Dec]
257 a0090487 Agata Murawska
genOpID = genConstrToStr deCamelCase
258 12c19659 Iustin Pop
259 05ff7a00 Agata Murawska
-- | OpCode parameter (field) type.
260 12c19659 Iustin Pop
type OpParam = (String, Q Type, Q Exp)
261 12c19659 Iustin Pop
262 12c19659 Iustin Pop
-- | Generates the OpCode data type.
263 12c19659 Iustin Pop
--
264 12c19659 Iustin Pop
-- This takes an opcode logical definition, and builds both the
265 12c19659 Iustin Pop
-- datatype and the JSON serialisation out of it. We can't use a
266 12c19659 Iustin Pop
-- generic serialisation since we need to be compatible with Ganeti's
267 12c19659 Iustin Pop
-- own, so we have a few quirks to work around.
268 12c19659 Iustin Pop
--
269 12c19659 Iustin Pop
-- There are three things to be defined for each parameter:
270 12c19659 Iustin Pop
--
271 12c19659 Iustin Pop
-- * name
272 12c19659 Iustin Pop
--
273 12c19659 Iustin Pop
-- * type; if this is 'Maybe', will only be serialised if it's a
274 12c19659 Iustin Pop
--   'Just' value
275 12c19659 Iustin Pop
--
276 12c19659 Iustin Pop
-- * default; if missing, won't raise an exception, but will instead
277 12c19659 Iustin Pop
--   use the default
278 12c19659 Iustin Pop
--
279 12c19659 Iustin Pop
genOpCode :: String                -- ^ Type name to use
280 12c19659 Iustin Pop
          -> [(String, [OpParam])] -- ^ Constructor name and parameters
281 12c19659 Iustin Pop
          -> Q [Dec]
282 12c19659 Iustin Pop
genOpCode name cons = do
283 12c19659 Iustin Pop
  decl_d <- mapM (\(cname, fields) -> do
284 12c19659 Iustin Pop
                    -- we only need the type of the field, without Q
285 12c19659 Iustin Pop
                    fields' <- mapM (\(_, qt, _) ->
286 12c19659 Iustin Pop
                                         qt >>= \t -> return (NotStrict, t))
287 12c19659 Iustin Pop
                               fields
288 12c19659 Iustin Pop
                    return $ NormalC (mkName cname) fields')
289 12c19659 Iustin Pop
            cons
290 12c19659 Iustin Pop
  let declD = DataD [] (mkName name) [] decl_d [''Show, ''Read, ''Eq]
291 12c19659 Iustin Pop
292 12c19659 Iustin Pop
  (savesig, savefn) <- genSaveOpCode cons
293 12c19659 Iustin Pop
  (loadsig, loadfn) <- genLoadOpCode cons
294 12c19659 Iustin Pop
  return [declD, loadsig, loadfn, savesig, savefn]
295 12c19659 Iustin Pop
296 05ff7a00 Agata Murawska
-- | Checks whether a given parameter is options.
297 12c19659 Iustin Pop
--
298 12c19659 Iustin Pop
-- This requires that it's a 'Maybe'.
299 12c19659 Iustin Pop
isOptional :: Type -> Bool
300 12c19659 Iustin Pop
isOptional (AppT (ConT dt) _) | dt == ''Maybe = True
301 12c19659 Iustin Pop
isOptional _ = False
302 12c19659 Iustin Pop
303 12c19659 Iustin Pop
-- | Generates the \"save\" expression for a single opcode parameter.
304 12c19659 Iustin Pop
--
305 12c19659 Iustin Pop
-- There is only one special handling mode: if the parameter is of
306 12c19659 Iustin Pop
-- 'Maybe' type, then we only save it if it's a 'Just' value,
307 12c19659 Iustin Pop
-- otherwise we skip it.
308 12c19659 Iustin Pop
saveField :: Name    -- ^ The name of variable that contains the value
309 12c19659 Iustin Pop
          -> OpParam -- ^ Parameter definition
310 12c19659 Iustin Pop
          -> Q Exp
311 12c19659 Iustin Pop
saveField fvar (fname, qt, _) = do
312 12c19659 Iustin Pop
  t <- qt
313 53664e15 Iustin Pop
  let fnexp = stringE fname
314 12c19659 Iustin Pop
      fvare = varE fvar
315 12c19659 Iustin Pop
  (if isOptional t
316 12c19659 Iustin Pop
   then [| case $fvare of
317 53664e15 Iustin Pop
             Just v' -> [( $fnexp, $showJSONE v')]
318 12c19659 Iustin Pop
             Nothing -> []
319 12c19659 Iustin Pop
         |]
320 53664e15 Iustin Pop
   else [| [( $fnexp, $showJSONE $fvare )] |])
321 12c19659 Iustin Pop
322 12c19659 Iustin Pop
-- | Generates the \"save\" clause for an entire opcode constructor.
323 12c19659 Iustin Pop
--
324 12c19659 Iustin Pop
-- This matches the opcode with variables named the same as the
325 12c19659 Iustin Pop
-- constructor fields (just so that the spliced in code looks nicer),
326 12c19659 Iustin Pop
-- and passes those name plus the parameter definition to 'saveField'.
327 12c19659 Iustin Pop
saveConstructor :: String    -- ^ The constructor name
328 12c19659 Iustin Pop
                -> [OpParam] -- ^ The parameter definitions for this
329 12c19659 Iustin Pop
                             -- constructor
330 12c19659 Iustin Pop
                -> Q Clause  -- ^ Resulting clause
331 12c19659 Iustin Pop
saveConstructor sname fields = do
332 12c19659 Iustin Pop
  let cname = mkName sname
333 12c19659 Iustin Pop
  let fnames = map (\(n, _, _) -> mkName n) fields
334 12c19659 Iustin Pop
  let pat = conP cname (map varP fnames)
335 12c19659 Iustin Pop
  let felems = map (uncurry saveField) (zip fnames fields)
336 12c19659 Iustin Pop
      -- now build the OP_ID serialisation
337 53664e15 Iustin Pop
      opid = [| [( $(stringE "OP_ID"),
338 53664e15 Iustin Pop
                   $showJSONE $(stringE . deCamelCase $ sname) )] |]
339 12c19659 Iustin Pop
      flist = listE (opid:felems)
340 12c19659 Iustin Pop
      -- and finally convert all this to a json object
341 53664e15 Iustin Pop
      flist' = [| $(varNameE "makeObj") (concat $flist) |]
342 12c19659 Iustin Pop
  clause [pat] (normalB flist') []
343 12c19659 Iustin Pop
344 12c19659 Iustin Pop
-- | Generates the main save opcode function.
345 12c19659 Iustin Pop
--
346 12c19659 Iustin Pop
-- This builds a per-constructor match clause that contains the
347 12c19659 Iustin Pop
-- respective constructor-serialisation code.
348 12c19659 Iustin Pop
genSaveOpCode :: [(String, [OpParam])] -> Q (Dec, Dec)
349 12c19659 Iustin Pop
genSaveOpCode opdefs = do
350 12c19659 Iustin Pop
  cclauses <- mapM (uncurry saveConstructor) opdefs
351 12c19659 Iustin Pop
  let fname = mkName "saveOpCode"
352 12c19659 Iustin Pop
  sigt <- [t| $(conT (mkName "OpCode")) -> JSON.JSValue |]
353 12c19659 Iustin Pop
  return $ (SigD fname sigt, FunD fname cclauses)
354 12c19659 Iustin Pop
355 12c19659 Iustin Pop
-- | Generates the \"load\" field for a single parameter.
356 12c19659 Iustin Pop
--
357 12c19659 Iustin Pop
-- There is custom handling, depending on how the parameter is
358 12c19659 Iustin Pop
-- specified. For a 'Maybe' type parameter, we allow that it is not
359 12c19659 Iustin Pop
-- present (via 'Utils.maybeFromObj'). Otherwise, if there is a
360 12c19659 Iustin Pop
-- default value, we allow the parameter to be abset, and finally if
361 12c19659 Iustin Pop
-- there is no default value, we require its presence.
362 12c19659 Iustin Pop
loadField :: OpParam -> Q (Name, Stmt)
363 12c19659 Iustin Pop
loadField (fname, qt, qdefa) = do
364 12c19659 Iustin Pop
  let fvar = mkName fname
365 12c19659 Iustin Pop
  t <- qt
366 12c19659 Iustin Pop
  defa <- qdefa
367 12c19659 Iustin Pop
  -- these are used in all patterns below
368 53664e15 Iustin Pop
  let objvar = varNameE "o"
369 53664e15 Iustin Pop
      objfield = stringE fname
370 12c19659 Iustin Pop
  bexp <- if isOptional t
371 53664e15 Iustin Pop
          then [| $((varNameE "maybeFromObj")) $objvar $objfield |]
372 12c19659 Iustin Pop
          else case defa of
373 12c19659 Iustin Pop
                 AppE (ConE dt) defval | dt == 'Just ->
374 12c19659 Iustin Pop
                   -- but has a default value
375 53664e15 Iustin Pop
                   [| $(varNameE "fromObjWithDefault")
376 12c19659 Iustin Pop
                      $objvar $objfield $(return defval) |]
377 12c19659 Iustin Pop
                 ConE dt | dt == 'Nothing ->
378 53664e15 Iustin Pop
                     [| $(varNameE "fromObj") $objvar $objfield |]
379 12c19659 Iustin Pop
                 s -> fail $ "Invalid default value " ++ show s ++
380 12c19659 Iustin Pop
                      ", expecting either 'Nothing' or a 'Just defval'"
381 12c19659 Iustin Pop
  return (fvar, BindS (VarP fvar) bexp)
382 12c19659 Iustin Pop
383 12c19659 Iustin Pop
loadConstructor :: String -> [OpParam] -> Q Exp
384 12c19659 Iustin Pop
loadConstructor sname fields = do
385 12c19659 Iustin Pop
  let name = mkName sname
386 12c19659 Iustin Pop
  fbinds <- mapM loadField fields
387 12c19659 Iustin Pop
  let (fnames, fstmts) = unzip fbinds
388 12c19659 Iustin Pop
  let cval = foldl (\accu fn -> AppE accu (VarE fn)) (ConE name) fnames
389 12c19659 Iustin Pop
      fstmts' = fstmts ++ [NoBindS (AppE (VarE 'return) cval)]
390 12c19659 Iustin Pop
  return $ DoE fstmts'
391 12c19659 Iustin Pop
392 12c19659 Iustin Pop
genLoadOpCode :: [(String, [OpParam])] -> Q (Dec, Dec)
393 12c19659 Iustin Pop
genLoadOpCode opdefs = do
394 12c19659 Iustin Pop
  let fname = mkName "loadOpCode"
395 12c19659 Iustin Pop
      arg1 = mkName "v"
396 12c19659 Iustin Pop
      objname = mkName "o"
397 12c19659 Iustin Pop
      opid = mkName "op_id"
398 12c19659 Iustin Pop
  st1 <- bindS (varP objname) [| liftM JSON.fromJSObject
399 12c19659 Iustin Pop
                                 (JSON.readJSON $(varE arg1)) |]
400 53664e15 Iustin Pop
  st2 <- bindS (varP opid) [| $(varNameE "fromObj")
401 53664e15 Iustin Pop
                              $(varE objname) $(stringE "OP_ID") |]
402 12c19659 Iustin Pop
  -- the match results (per-constructor blocks)
403 12c19659 Iustin Pop
  mexps <- mapM (uncurry loadConstructor) opdefs
404 12c19659 Iustin Pop
  fails <- [| fail $ "Unknown opcode " ++ $(varE opid) |]
405 12c19659 Iustin Pop
  let mpats = map (\(me, c) ->
406 12c19659 Iustin Pop
                       let mp = LitP . StringL . deCamelCase . fst $ c
407 12c19659 Iustin Pop
                       in Match mp (NormalB me) []
408 12c19659 Iustin Pop
                  ) $ zip mexps opdefs
409 12c19659 Iustin Pop
      defmatch = Match WildP (NormalB fails) []
410 12c19659 Iustin Pop
      cst = NoBindS $ CaseE (VarE opid) $ mpats++[defmatch]
411 12c19659 Iustin Pop
      body = DoE [st1, st2, cst]
412 12c19659 Iustin Pop
  sigt <- [t| JSON.JSValue -> JSON.Result $(conT (mkName "OpCode")) |]
413 12c19659 Iustin Pop
  return $ (SigD fname sigt, FunD fname [Clause [VarP arg1] (NormalB body) []])
414 12c19659 Iustin Pop
415 12c19659 Iustin Pop
-- | No default type.
416 12c19659 Iustin Pop
noDefault :: Q Exp
417 12c19659 Iustin Pop
noDefault = conE 'Nothing
418 a0090487 Agata Murawska
419 a0090487 Agata Murawska
-- * Template code for luxi
420 a0090487 Agata Murawska
421 a0090487 Agata Murawska
-- | Constructor-to-string for LuxiOp.
422 a0090487 Agata Murawska
genStrOfOp :: Name -> String -> Q [Dec]
423 a0090487 Agata Murawska
genStrOfOp = genConstrToStr id
424 a0090487 Agata Murawska
425 a0090487 Agata Murawska
-- | Constructor-to-string for MsgKeys.
426 a0090487 Agata Murawska
genStrOfKey :: Name -> String -> Q [Dec]
427 a0090487 Agata Murawska
genStrOfKey = genConstrToStr ensureLower
428 a0090487 Agata Murawska
429 a0090487 Agata Murawska
-- | LuxiOp parameter type.
430 a0090487 Agata Murawska
type LuxiParam = (String, Q Type, Q Exp)
431 a0090487 Agata Murawska
432 92678b3c Iustin Pop
-- | Storage options for JSON.
433 92678b3c Iustin Pop
data Store = SList | SDict
434 92678b3c Iustin Pop
435 a0090487 Agata Murawska
-- | Generates the LuxiOp data type.
436 a0090487 Agata Murawska
--
437 a0090487 Agata Murawska
-- This takes a Luxi operation definition and builds both the
438 a0090487 Agata Murawska
-- datatype and the function trnasforming the arguments to JSON.
439 a0090487 Agata Murawska
-- We can't use anything less generic, because the way different
440 a0090487 Agata Murawska
-- operations are serialized differs on both parameter- and top-level.
441 a0090487 Agata Murawska
--
442 a0090487 Agata Murawska
-- There are three things to be defined for each parameter:
443 a0090487 Agata Murawska
--
444 a0090487 Agata Murawska
-- * name
445 a0090487 Agata Murawska
--
446 a0090487 Agata Murawska
-- * type
447 a0090487 Agata Murawska
--
448 a0090487 Agata Murawska
-- * operation; this is the operation performed on the parameter before
449 a0090487 Agata Murawska
--   serialization
450 a0090487 Agata Murawska
--
451 92678b3c Iustin Pop
genLuxiOp :: String -> [(String, [LuxiParam], Store)] -> Q [Dec]
452 a0090487 Agata Murawska
genLuxiOp name cons = do
453 92678b3c Iustin Pop
  decl_d <- mapM (\(cname, fields, _) -> do
454 a0090487 Agata Murawska
                    fields' <- mapM (\(_, qt, _) ->
455 a0090487 Agata Murawska
                                         qt >>= \t -> return (NotStrict, t))
456 a0090487 Agata Murawska
                               fields
457 a0090487 Agata Murawska
                    return $ NormalC (mkName cname) fields')
458 a0090487 Agata Murawska
            cons
459 a0090487 Agata Murawska
  let declD = DataD [] (mkName name) [] decl_d [''Show, ''Read]
460 a0090487 Agata Murawska
  (savesig, savefn) <- genSaveLuxiOp cons
461 a0090487 Agata Murawska
  return [declD, savesig, savefn]
462 a0090487 Agata Murawska
463 92678b3c Iustin Pop
-- | Generates a Q Exp for an element, depending of the JSON return type.
464 92678b3c Iustin Pop
helperLuxiField :: Store -> String -> Q Exp -> Q Exp
465 92678b3c Iustin Pop
helperLuxiField SList name val = [| [ JSON.showJSON $val ] |]
466 92678b3c Iustin Pop
helperLuxiField SDict name val = [| [(name, JSON.showJSON $val)] |]
467 92678b3c Iustin Pop
468 92678b3c Iustin Pop
-- | Generates the \"save\" expression for a single luxi parameter.
469 92678b3c Iustin Pop
saveLuxiField :: Store -> Name -> LuxiParam -> Q Exp
470 92678b3c Iustin Pop
saveLuxiField store fvar (fname, qt, fn) = do
471 92678b3c Iustin Pop
  t <- qt
472 92678b3c Iustin Pop
  let fvare = varE fvar
473 92678b3c Iustin Pop
  (if isOptional t
474 92678b3c Iustin Pop
   then [| case $fvare of
475 92678b3c Iustin Pop
             Just v' ->
476 92678b3c Iustin Pop
                 $(helperLuxiField store fname $ liftM2 appFn fn [| v' |])
477 92678b3c Iustin Pop
             Nothing -> []
478 92678b3c Iustin Pop
         |]
479 92678b3c Iustin Pop
   else helperLuxiField store fname $ liftM2 appFn fn fvare)
480 92678b3c Iustin Pop
481 92678b3c Iustin Pop
-- | Generates final JSON Q Exp for constructor.
482 92678b3c Iustin Pop
helperLuxiConstructor :: Store -> Q Exp -> Q Exp
483 92678b3c Iustin Pop
helperLuxiConstructor SDict val = [| JSON.showJSON $ JSON.makeObj $val |]
484 92678b3c Iustin Pop
helperLuxiConstructor SList val = [| JSON.JSArray $val |]
485 92678b3c Iustin Pop
486 a0090487 Agata Murawska
-- | Generates the \"save\" clause for entire LuxiOp constructor.
487 92678b3c Iustin Pop
saveLuxiConstructor :: (String, [LuxiParam], Store) -> Q Clause
488 92678b3c Iustin Pop
saveLuxiConstructor (sname, fields, store) = do
489 a0090487 Agata Murawska
  let cname = mkName sname
490 a0090487 Agata Murawska
      fnames = map (\(nm, _, _) -> mkName nm) fields
491 a0090487 Agata Murawska
      pat = conP cname (map varP fnames)
492 92678b3c Iustin Pop
      flist = map (uncurry $ saveLuxiField store) (zip fnames fields)
493 92678b3c Iustin Pop
      flist' = appE [| concat |] (listE flist)
494 92678b3c Iustin Pop
      finval = helperLuxiConstructor store flist'
495 92678b3c Iustin Pop
  clause [pat] (normalB finval) []
496 a0090487 Agata Murawska
497 a0090487 Agata Murawska
-- | Generates the main save LuxiOp function.
498 92678b3c Iustin Pop
genSaveLuxiOp :: [(String, [LuxiParam], Store)]-> Q (Dec, Dec)
499 a0090487 Agata Murawska
genSaveLuxiOp opdefs = do
500 a0090487 Agata Murawska
  sigt <- [t| $(conT (mkName "LuxiOp")) -> JSON.JSValue |]
501 a0090487 Agata Murawska
  let fname = mkName "opToArgs"
502 a0090487 Agata Murawska
  cclauses <- mapM saveLuxiConstructor opdefs
503 a0090487 Agata Murawska
  return $ (SigD fname sigt, FunD fname cclauses)