On Fri, Dec 13, 2013 at 12:47:53PM +0100, Petr Pudlak wrote:
> Currently they are generated only as Strings.
> 
> Signed-off-by: Petr Pudlak <[email protected]>
> ---
>  Makefile.am                    |   2 +
>  src/Ganeti/Hs2Py/GenOpCodes.hs |   7 ++-
>  src/Ganeti/THH.hs              | 102 +++++----------------------------
>  src/Ganeti/THH/PyType.hs       | 126 
> +++++++++++++++++++++++++++++++++++++++++
>  4 files changed, 147 insertions(+), 90 deletions(-)
>  create mode 100644 src/Ganeti/THH/PyType.hs
> 
> diff --git a/Makefile.am b/Makefile.am
> index e50d369..d4489e7 100644
> --- a/Makefile.am
> +++ b/Makefile.am
> @@ -129,6 +129,7 @@ HS_DIRS = \
>       src/Ganeti/Storage/Diskstats \
>       src/Ganeti/Storage/Drbd \
>       src/Ganeti/Storage/Lvm \
> +     src/Ganeti/THH \
>       test/hs \
>       test/hs/Test \
>       test/hs/Test/Ganeti \
> @@ -726,6 +727,7 @@ HS_LIB_SRCS = \
>       src/Ganeti/Storage/Lvm/Types.hs \
>       src/Ganeti/Storage/Utils.hs \
>       src/Ganeti/THH.hs \
> +     src/Ganeti/THH/PyType.hs \
>       src/Ganeti/Types.hs \
>       src/Ganeti/Utils.hs
>  
> diff --git a/src/Ganeti/Hs2Py/GenOpCodes.hs b/src/Ganeti/Hs2Py/GenOpCodes.hs
> index 5f0c48c..d59602f 100644
> --- a/src/Ganeti/Hs2Py/GenOpCodes.hs
> +++ b/src/Ganeti/Hs2Py/GenOpCodes.hs
> @@ -44,10 +44,11 @@ pyClassDoc doc
>  -- | Generates an opcode parameter in Python.
>  pyClassField :: OpCodeField -> String
>  pyClassField (OpCodeField name typ Nothing doc) =
> -  "(" ++ intercalate ", " [show name, "None", typ, show doc] ++ ")"
> +  "(" ++ intercalate ", " [show name, "None", showValue typ, show doc] ++ ")"
>    
>  pyClassField (OpCodeField name typ (Just def) doc) =
> -  "(" ++ intercalate ", " [show name, showValue def, typ, show doc] ++ ")"
> +  "(" ++ intercalate ", "
> +           [show name, showValue def, showValue typ, show doc] ++ ")"
>    
>  -- | Comma intercalates and indents opcode parameters in Python.
>  intercalateIndent :: [String] -> String
> @@ -73,7 +74,7 @@ showPyClass (OpCodeDescriptor name typ doc fields dsc) =
>     "  OP_PARAMS = [" ++
>     intercalateIndent (map pyClassField fields) ++
>     "\n    ]" ++ "\n" ++
> -   "  OP_RESULT = " ++ typ ++
> +   "  OP_RESULT = " ++ showValue typ ++
>     withLU ++ "\n\n"
>  
>  -- | Generates all opcodes as Python classes.
> diff --git a/src/Ganeti/THH.hs b/src/Ganeti/THH.hs
> index c6b48e6..3092733 100644
> --- a/src/Ganeti/THH.hs
> +++ b/src/Ganeti/THH.hs
> @@ -10,7 +10,7 @@ needs in this module (except the one for unittests).
>  
>  {-
>  
> -Copyright (C) 2011, 2012 Google Inc.
> +Copyright (C) 2011, 2012, 2013 Google Inc.
>  
>  This program is free software; you can redistribute it and/or modify
>  it under the terms of the GNU General Public License as published by
> @@ -81,6 +81,7 @@ import Text.JSON.Pretty (pp_value)
>  
>  import Ganeti.JSON
>  import Ganeti.PyValue
> +import Ganeti.THH.PyType
>  
>  
>  -- * Exported types
> @@ -587,7 +588,7 @@ type OpParam = (String, Q Type, Q Exp)
>  -- * Python code generation
>  
>  data OpCodeField = OpCodeField { ocfName :: String
> -                               , ocfType :: String
> +                               , ocfType :: PyType
>                                 , ocfDefl :: Maybe PyValueEx
>                                 , ocfDoc  :: String
>                                 }
> @@ -595,87 +596,12 @@ data OpCodeField = OpCodeField { ocfName :: String
>  -- | Transfers opcode data between the opcode description (through
>  -- @genOpCode@) and the Python code generation functions.
>  data OpCodeDescriptor = OpCodeDescriptor { ocdName   :: String
> -                                         , ocdType   :: String
> +                                         , ocdType   :: PyType
>                                           , ocdDoc    :: String
>                                           , ocdFields :: [OpCodeField]
>                                           , ocdDescr  :: String
>                                           }
>  
> --- | Strips out the module name
> ---
> --- @
> --- pyBaseName "Data.Map" = "Map"
> --- @
> -pyBaseName :: String -> String
> -pyBaseName str =
> -  case span (/= '.') str of
> -    (x, []) -> x
> -    (_, _:x) -> pyBaseName x
> -
> --- | Converts a Haskell type name into a Python type name.
> ---
> --- @
> --- pyTypename "Bool" = "ht.TBool"
> --- @
> -pyTypeName :: Show a => a -> String
> -pyTypeName name =
> -  "ht.T" ++ (case pyBaseName (show name) of
> -                "()" -> "None"
> -                "Map" -> "DictOf"
> -                "Set" -> "SetOf"
> -                "ListSet" -> "SetOf"
> -                "Either" -> "Or"
> -                "GenericContainer" -> "DictOf"
> -                "JSValue" -> "Any"
> -                "JSObject" -> "Object"
> -                str -> str)
> -
> --- | Converts a Haskell type into a Python type.
> ---
> --- @
> --- pyType [Int] = "ht.TListOf(ht.TInt)"
> --- @
> -pyType :: Type -> Q String
> -pyType (AppT typ1 typ2) =
> -  do t <- pyCall typ1 typ2
> -     return $ t ++ ")"
> -
> -pyType (ConT name) = return (pyTypeName name)
> -pyType ListT = return "ht.TListOf"
> -pyType (TupleT 0) = return "ht.TNone"
> -pyType (TupleT _) = return "ht.TTupleOf"
> -pyType typ = error $ "unhandled case for type " ++ show typ
> -        

Trailing whitespace.

> --- | Converts a Haskell type application into a Python type.
> ---
> --- @
> --- Maybe Int = "ht.TMaybe(ht.TInt)"
> --- @
> -pyCall :: Type -> Type -> Q String
> -pyCall (AppT typ1 typ2) arg =
> -  do t <- pyCall typ1 typ2
> -     targ <- pyType arg
> -     return $ t ++ ", " ++ targ
> -
> -pyCall typ1 typ2 =
> -  do t1 <- pyType typ1
> -     t2 <- pyType typ2
> -     return $ t1 ++ "(" ++ t2
> -
> --- | @pyType opt typ@ converts Haskell type @typ@ into a Python type,
> --- where @opt@ determines if the converted type is optional (i.e.,
> --- Maybe).
> ---
> --- @
> --- pyType False [Int] = "ht.TListOf(ht.TInt)" (mandatory)
> --- pyType True [Int] = "ht.TMaybe(ht.TListOf(ht.TInt))" (optional)
> --- @
> -pyOptionalType :: Bool -> Type -> Q String
> -pyOptionalType opt typ
> -  | opt = do t <- pyType typ
> -             return $ "ht.TMaybe(" ++ t ++ ")"
> -  | otherwise = pyType typ
> -
>  -- | Optionally encapsulates default values in @PyValueEx@.
>  --
>  -- @maybeApp exp typ@ returns a quoted expression that encapsulates
> @@ -689,14 +615,15 @@ maybeApp Nothing _ =
>  maybeApp (Just expr) typ =
>    [| Just ($(conE (mkName "PyValueEx")) ($expr :: $typ)) |]
>  
> -
>  -- | Generates a Python type according to whether the field is
> --- optional
> -genPyType' :: OptionalType -> Q Type -> Q Exp
> -genPyType' opt typ = typ >>= pyOptionalType (opt /= NotOptional) >>= stringE
> +-- optional.
> +--
> +-- The type of created expression is PyType.
> +genPyType' :: OptionalType -> Q Type -> Q PyType
> +genPyType' opt typ = typ >>= pyOptionalType (opt /= NotOptional)
>  
>  -- | Generates Python types from opcode parameters.
> -genPyType :: Field -> Q Exp
> +genPyType :: Field -> Q PyType
>  genPyType f = genPyType' (fieldIsOptional f) (fieldType f)
>  
>  -- | Generates Python default values from opcode parameters.
> @@ -704,8 +631,9 @@ genPyDefault :: Field -> Q Exp
>  genPyDefault f = maybeApp (fieldDefault f) (fieldType f)
>  
>  pyField :: Field -> Q Exp
> -pyField f = [| OpCodeField $(stringE (fieldName f))
> -                           $(genPyType f)
> +pyField f = genPyType f >>= \t ->
> +            [| OpCodeField $(stringE (fieldName f))
> +                           t
>                             $(genPyDefault f)
>                             $(stringE (fieldDoc f)) |]
>  
> @@ -715,7 +643,7 @@ pyClass :: OpCodeConstructor -> Q Exp
>  pyClass (consName, consType, consDoc, consFields, consDscField) =
>    do let pyClassVar = varNameE "showPyClass"
>           consName' = stringE consName
> -     let consType' = genPyType' NotOptional consType
> +     consType' <- genPyType' NotOptional consType
>       let consDoc' = stringE consDoc
>  {-
>           consFieldNames = listE $ map (stringE . fieldName) consFields
> @@ -724,7 +652,7 @@ pyClass (consName, consType, consDoc, consFields, 
> consDscField) =
>       let consFieldDefaults = genPyDefaults consFields
>       -}
>       [| OpCodeDescriptor $consName'
> -                         $consType'
> +                         consType'
>                           $consDoc'
>                           $(listE $ map pyField consFields)
>                           consDscField |]
> diff --git a/src/Ganeti/THH/PyType.hs b/src/Ganeti/THH/PyType.hs
> new file mode 100644
> index 0000000..cf51b5d
> --- /dev/null
> +++ b/src/Ganeti/THH/PyType.hs
> @@ -0,0 +1,126 @@
> +{-# LANGUAGE TemplateHaskell #-}
> +
> +{-| PyType helper for Ganeti Haskell code.
> +
> +-}
> +
> +{-
> +
> +Copyright (C) 2013 Google Inc.
> +
> +This program is free software; you can redistribute it and/or modify
> +it under the terms of the GNU General Public License as published by
> +the Free Software Foundation; either version 2 of the License, or
> +(at your option) any later version.
> +
> +This program is distributed in the hope that it will be useful, but
> +WITHOUT ANY WARRANTY; without even the implied warranty of
> +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> +General Public License for more details.
> +
> +You should have received a copy of the GNU General Public License
> +along with this program; if not, write to the Free Software
> +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
> +02110-1301, USA.
> +
> +-}
> +module Ganeti.THH.PyType
> +  ( PyType(..)
> +  , pyType
> +  , pyOptionalType
> +  ) where
> +
> +import Control.Applicative
> +import Control.Monad
> +import Data.List (intercalate)
> +import Language.Haskell.TH
> +import Language.Haskell.TH.Syntax (Lift(..))
> +
> +import Ganeti.PyValue
> +
> +
> +-- | Represents a Python encoding of types.
> +data PyType
> +    = PTMaybe PyType
> +    | PTApp PyType [PyType]
> +    | PTOther String
> +    | PTAny
> +    | PTDictOf
> +    | PTListOf
> +    | PTNone
> +    | PTObject
> +    | PTOr
> +    | PTSetOf
> +    | PTTupleOf
> +  deriving (Show, Eq, Ord)
> +
> +-- TODO: We could use th-lift to generate this instance automatically.
> +instance Lift PyType where
> +  lift (PTMaybe x)   = [| PTMaybe x |]
> +  lift (PTApp tf as) = [| PTApp tf as |]
> +  lift (PTOther i)   = [| PTOther i |]
> +  lift PTAny         = [| PTAny |]
> +  lift PTDictOf      = [| PTDictOf |]
> +  lift PTListOf      = [| PTListOf |]
> +  lift PTNone        = [| PTNone |]
> +  lift PTObject      = [| PTObject |]
> +  lift PTOr          = [| PTOr |]
> +  lift PTSetOf       = [| PTSetOf |]
> +  lift PTTupleOf     = [| PTTupleOf |]
> +
> +instance PyValue PyType where
> +  showValue (PTMaybe x)   = ptApp (ht "Maybe") [x]
> +  showValue (PTApp tf as) = ptApp (showValue tf) as
> +  showValue (PTOther i)   = ht i
> +  showValue PTAny         = ht "Any"
> +  showValue PTDictOf      = ht "DictOf"
> +  showValue PTListOf      = ht "ListOf"
> +  showValue PTNone        = ht "None"
> +  showValue PTObject      = ht "Object"
> +  showValue PTOr          = ht "Or"
> +  showValue PTSetOf       = ht "SetOf"
> +  showValue PTTupleOf     = ht "TupleOf"
> +
> +ht :: String -> String
> +ht = ("ht.T" ++)
> +
> +ptApp :: String -> [PyType] -> String
> +ptApp name ts = name ++ "(" ++ intercalate ", " (map showValue ts) ++ ")"
> +
> +-- | Converts a Haskell type name into a Python type name.
> +pyTypeName :: Name -> PyType
> +pyTypeName name =
> +  case nameBase name of
> +                "()"                -> PTNone
> +                "Map"               -> PTDictOf
> +                "Set"               -> PTSetOf
> +                "ListSet"           -> PTSetOf
> +                "Either"            -> PTOr
> +                "GenericContainer"  -> PTDictOf
> +                "JSValue"           -> PTAny
> +                "JSObject"          -> PTObject
> +                str                 -> PTOther str
> +
> +-- | Converts a Haskell type into a Python type.
> +pyType :: Type -> Q PyType
> +pyType t | not (null args)  = PTApp `liftM` pyType fn `ap` mapM pyType args
> +  where (fn, args) = pyAppType t
> +pyType (ConT name)          = return $ pyTypeName name
> +pyType ListT                = return PTListOf
> +pyType (TupleT 0)           = return PTNone
> +pyType (TupleT _)           = return PTTupleOf
> +pyType typ                  = fail $ "unhandled case for type " ++ show typ
> +        

Trailing whitespace.

> +-- | Returns a type and its type arguments.
> +pyAppType :: Type -> (Type, [Type])
> +pyAppType = g []
> +  where
> +    g as (AppT typ1 typ2) = g (typ2 : as) typ1
> +    g as typ              = (typ, as)
> +
> +-- | @pyType opt typ@ converts Haskell type @typ@ into a Python type,
> +-- where @opt@ determines if the converted type is optional (i.e.,
> +-- Maybe).
> +pyOptionalType :: Bool -> Type -> Q PyType
> +pyOptionalType True  typ = PTMaybe <$> pyType typ
> +pyOptionalType False typ = pyType typ
> -- 
> 1.8.5.1
> 

-- 
Jose Antonio Lopes
Ganeti Engineering
Google Germany GmbH
Dienerstr. 12, 80331, München

Registergericht und -nummer: Hamburg, HRB 86891
Sitz der Gesellschaft: Hamburg
Geschäftsführer: Graham Law, Christine Elizabeth Flores
Steuernummer: 48/725/00206
Umsatzsteueridentifikationsnummer: DE813741370

Reply via email to