xcffib 1.5.0 → 1.6.0
raw patch · 11 files changed
+613/−304 lines, 11 filesdep +prettydep −language-pythondep −semigroupsdep ~HUnitdep ~attoparsecdep ~bytestringPVP ok
version bump matches the API change (PVP)
Dependencies added: pretty
Dependencies removed: language-python, semigroups
Dependency ranges changed: HUnit, attoparsec, bytestring, containers, directory, either, filemanip, filepath, mtl, optparse-applicative, split, test-framework, test-framework-hunit, xcb-types
API changes (from Hackage documentation)
- Data.XCB.Python.PyHelpers: ident :: String -> Ident ()
- Data.XCB.Python.PyHelpers: instance Data.XCB.Python.PyHelpers.PseudoArgument (Language.Python.Common.AST.Argument ())
- Data.XCB.Python.PyHelpers: instance Data.XCB.Python.PyHelpers.PseudoArgument (Language.Python.Common.AST.Expr ())
- Data.XCB.Python.PyHelpers: instance Data.XCB.Python.PyHelpers.PseudoExpr (Language.Python.Common.AST.Expr ())
- Data.XCB.Python.PyHelpers: instance Data.XCB.Python.PyHelpers.PseudoExpr GHC.Base.String
- Data.XCB.Python.PyHelpers: mkArg :: String -> Argument ()
- Data.XCB.Python.PyHelpers: mkImport :: String -> Statement ()
- Data.XCB.Python.PyHelpers: mkInt :: Int -> Expr ()
- Data.XCB.Python.PyHelpers: mkParams :: [String] -> [Parameter ()]
- Data.XCB.Python.PyHelpers: noArgs :: [Argument ()]
- Data.XCB.Python.PyHelpers: pyNone :: Expr ()
- Data.XCB.Python.PyHelpers: pyTruth :: Bool -> Expr ()
- Data.XCB.Python.PyHelpers: repeatStr :: String -> Expr () -> Expr ()
+ Data.XCB.Python.AST: Assign :: Expr -> Expr -> Statement
+ Data.XCB.Python.AST: AugmentedAssign :: Expr -> Op -> Expr -> Statement
+ Data.XCB.Python.AST: BinaryAnd :: Op
+ Data.XCB.Python.AST: BinaryOp :: Op -> Expr -> Expr -> Expr
+ Data.XCB.Python.AST: Bool :: Bool -> Expr
+ Data.XCB.Python.AST: Call :: Expr -> [Expr] -> Expr
+ Data.XCB.Python.AST: Class :: Ident -> [Ident] -> Suite -> Statement
+ Data.XCB.Python.AST: CondExpr :: Expr -> Expr -> Expr -> Expr
+ Data.XCB.Python.AST: Conditional :: Expr -> Suite -> Suite -> Statement
+ Data.XCB.Python.AST: Decorated :: Ident -> Ident -> [Ident] -> Suite -> Statement
+ Data.XCB.Python.AST: Dot :: Expr -> Ident -> Expr
+ Data.XCB.Python.AST: EmptyDict :: Expr
+ Data.XCB.Python.AST: Equality :: Op
+ Data.XCB.Python.AST: FloorDivide :: Op
+ Data.XCB.Python.AST: FromImport :: Ident -> Ident -> Statement
+ Data.XCB.Python.AST: Fun :: Ident -> [Ident] -> Suite -> Statement
+ Data.XCB.Python.AST: Import :: Ident -> Statement
+ Data.XCB.Python.AST: Int :: Int -> Expr
+ Data.XCB.Python.AST: Invert :: Op
+ Data.XCB.Python.AST: LessThan :: Op
+ Data.XCB.Python.AST: Minus :: Op
+ Data.XCB.Python.AST: Modulo :: Op
+ Data.XCB.Python.AST: Multiply :: Op
+ Data.XCB.Python.AST: None :: Expr
+ Data.XCB.Python.AST: Paren :: Expr -> Expr
+ Data.XCB.Python.AST: Pass :: Statement
+ Data.XCB.Python.AST: Plus :: Op
+ Data.XCB.Python.AST: Raise :: Ident -> Statement
+ Data.XCB.Python.AST: Return :: Maybe Expr -> Statement
+ Data.XCB.Python.AST: ShiftLeft :: Op
+ Data.XCB.Python.AST: ShiftRight :: Op
+ Data.XCB.Python.AST: StmtExpr :: Expr -> Statement
+ Data.XCB.Python.AST: Strings :: [String] -> Expr
+ Data.XCB.Python.AST: Subscript :: Expr -> Expr -> Expr
+ Data.XCB.Python.AST: Tuple :: [Expr] -> Expr
+ Data.XCB.Python.AST: UnaryOp :: Op -> Expr -> Expr
+ Data.XCB.Python.AST: Var :: Ident -> Expr
+ Data.XCB.Python.AST: [assign_expr] :: Statement -> Expr
+ Data.XCB.Python.AST: [assign_to] :: Statement -> Expr
+ Data.XCB.Python.AST: [aug_assign_expr] :: Statement -> Expr
+ Data.XCB.Python.AST: [aug_assign_op] :: Statement -> Op
+ Data.XCB.Python.AST: [aug_assign_to] :: Statement -> Expr
+ Data.XCB.Python.AST: [binop_left] :: Expr -> Expr
+ Data.XCB.Python.AST: [binop_right] :: Expr -> Expr
+ Data.XCB.Python.AST: [bool_value] :: Expr -> Bool
+ Data.XCB.Python.AST: [call_args] :: Expr -> [Expr]
+ Data.XCB.Python.AST: [call_fun] :: Expr -> Expr
+ Data.XCB.Python.AST: [ce_conditon] :: Expr -> Expr
+ Data.XCB.Python.AST: [ce_false_branch] :: Expr -> Expr
+ Data.XCB.Python.AST: [ce_true_branch] :: Expr -> Expr
+ Data.XCB.Python.AST: [class_args] :: Statement -> [Ident]
+ Data.XCB.Python.AST: [class_body] :: Statement -> Suite
+ Data.XCB.Python.AST: [class_name] :: Statement -> Ident
+ Data.XCB.Python.AST: [decorations] :: Statement -> Ident
+ Data.XCB.Python.AST: [dot_attribute] :: Expr -> Ident
+ Data.XCB.Python.AST: [dot_expr] :: Expr -> Expr
+ Data.XCB.Python.AST: [else_body] :: Statement -> Suite
+ Data.XCB.Python.AST: [from_item] :: Statement -> Ident
+ Data.XCB.Python.AST: [from_module] :: Statement -> Ident
+ Data.XCB.Python.AST: [fun_args] :: Statement -> [Ident]
+ Data.XCB.Python.AST: [fun_body] :: Statement -> Suite
+ Data.XCB.Python.AST: [fun_name] :: Statement -> Ident
+ Data.XCB.Python.AST: [if_body] :: Statement -> Suite
+ Data.XCB.Python.AST: [if_cond] :: Statement -> Expr
+ Data.XCB.Python.AST: [import_item] :: Statement -> Ident
+ Data.XCB.Python.AST: [int_value] :: Expr -> Int
+ Data.XCB.Python.AST: [operator] :: Expr -> Op
+ Data.XCB.Python.AST: [paren_expr] :: Expr -> Expr
+ Data.XCB.Python.AST: [raise_exception] :: Statement -> Ident
+ Data.XCB.Python.AST: [return_expr] :: Statement -> Maybe Expr
+ Data.XCB.Python.AST: [stmt_expr] :: Statement -> Expr
+ Data.XCB.Python.AST: [strings_strings] :: Expr -> [String]
+ Data.XCB.Python.AST: [subscript_expr] :: Expr -> Expr
+ Data.XCB.Python.AST: [subscriptee] :: Expr -> Expr
+ Data.XCB.Python.AST: [tuple_exprs] :: Expr -> [Expr]
+ Data.XCB.Python.AST: [unop_arg] :: Expr -> Expr
+ Data.XCB.Python.AST: [var] :: Expr -> Ident
+ Data.XCB.Python.AST: class Pretty a
+ Data.XCB.Python.AST: class PseudoExpr a
+ Data.XCB.Python.AST: data Expr
+ Data.XCB.Python.AST: data Op
+ Data.XCB.Python.AST: data Statement
+ Data.XCB.Python.AST: getExpr :: PseudoExpr a => a -> Expr
+ Data.XCB.Python.AST: instance Data.XCB.Python.AST.Pretty Data.XCB.Python.AST.Expr
+ Data.XCB.Python.AST: instance Data.XCB.Python.AST.Pretty Data.XCB.Python.AST.Ident
+ Data.XCB.Python.AST: instance Data.XCB.Python.AST.Pretty Data.XCB.Python.AST.Op
+ Data.XCB.Python.AST: instance Data.XCB.Python.AST.Pretty Data.XCB.Python.AST.Statement
+ Data.XCB.Python.AST: instance Data.XCB.Python.AST.Pretty Data.XCB.Python.AST.Suite
+ Data.XCB.Python.AST: instance Data.XCB.Python.AST.PseudoExpr Data.XCB.Python.AST.Expr
+ Data.XCB.Python.AST: instance Data.XCB.Python.AST.PseudoExpr GHC.Base.String
+ Data.XCB.Python.AST: instance GHC.Classes.Eq Data.XCB.Python.AST.Expr
+ Data.XCB.Python.AST: instance GHC.Classes.Eq Data.XCB.Python.AST.Op
+ Data.XCB.Python.AST: instance GHC.Classes.Eq Data.XCB.Python.AST.Statement
+ Data.XCB.Python.AST: instance GHC.Classes.Ord Data.XCB.Python.AST.Expr
+ Data.XCB.Python.AST: instance GHC.Classes.Ord Data.XCB.Python.AST.Op
+ Data.XCB.Python.AST: instance GHC.Classes.Ord Data.XCB.Python.AST.Statement
+ Data.XCB.Python.AST: instance GHC.Show.Show Data.XCB.Python.AST.Expr
+ Data.XCB.Python.AST: instance GHC.Show.Show Data.XCB.Python.AST.Op
+ Data.XCB.Python.AST: instance GHC.Show.Show Data.XCB.Python.AST.Statement
+ Data.XCB.Python.AST: pretty :: Pretty a => a -> Doc
+ Data.XCB.Python.AST: prettyText :: Pretty a => a -> String
+ Data.XCB.Python.AST: type Ident = String
+ Data.XCB.Python.AST: type Suite = [Statement]
+ Data.XCB.Python.PyHelpers: notImplemented :: Statement
- Data.XCB.Python.Parse: renderPy :: Suite () -> String
+ Data.XCB.Python.Parse: renderPy :: Suite -> String
- Data.XCB.Python.Parse: xform :: [XHeader] -> [(String, Suite ())]
+ Data.XCB.Python.Parse: xform :: [XHeader] -> [(String, Suite)]
- Data.XCB.Python.PyHelpers: mkAssign :: PseudoExpr a => a -> Expr () -> Statement ()
+ Data.XCB.Python.PyHelpers: mkAssign :: PseudoExpr a => a -> Expr -> Statement
- Data.XCB.Python.PyHelpers: mkAttr :: String -> Expr ()
+ Data.XCB.Python.PyHelpers: mkAttr :: String -> Expr
- Data.XCB.Python.PyHelpers: mkCall :: (PseudoExpr a, PseudoArgument b) => a -> [b] -> Expr ()
+ Data.XCB.Python.PyHelpers: mkCall :: PseudoExpr a => a -> [Expr] -> Expr
- Data.XCB.Python.PyHelpers: mkClass :: String -> String -> Suite () -> Statement ()
+ Data.XCB.Python.PyHelpers: mkClass :: String -> String -> Suite -> Statement
- Data.XCB.Python.PyHelpers: mkDict :: String -> Statement ()
+ Data.XCB.Python.PyHelpers: mkDict :: String -> Statement
- Data.XCB.Python.PyHelpers: mkDictUpdate :: String -> Int -> String -> Statement ()
+ Data.XCB.Python.PyHelpers: mkDictUpdate :: String -> Int -> String -> Statement
- Data.XCB.Python.PyHelpers: mkDot :: PseudoExpr a => a -> String -> Expr ()
+ Data.XCB.Python.PyHelpers: mkDot :: PseudoExpr a => a -> String -> Expr
- Data.XCB.Python.PyHelpers: mkEmptyClass :: String -> String -> Statement ()
+ Data.XCB.Python.PyHelpers: mkEmptyClass :: String -> String -> Statement
- Data.XCB.Python.PyHelpers: mkEnum :: String -> [(String, Expr ())] -> Statement ()
+ Data.XCB.Python.PyHelpers: mkEnum :: String -> [(String, Expr)] -> Statement
- Data.XCB.Python.PyHelpers: mkIf :: Expr () -> Suite () -> Statement ()
+ Data.XCB.Python.PyHelpers: mkIf :: Expr -> Suite -> Statement
- Data.XCB.Python.PyHelpers: mkIncr :: String -> Expr () -> Statement ()
+ Data.XCB.Python.PyHelpers: mkIncr :: String -> Expr -> Statement
- Data.XCB.Python.PyHelpers: mkMethod :: String -> [Parameter ()] -> Suite () -> Statement ()
+ Data.XCB.Python.PyHelpers: mkMethod :: String -> [Ident] -> Suite -> Statement
- Data.XCB.Python.PyHelpers: mkName :: String -> Expr ()
+ Data.XCB.Python.PyHelpers: mkName :: String -> Expr
- Data.XCB.Python.PyHelpers: mkRelImport :: String -> Statement ()
+ Data.XCB.Python.PyHelpers: mkRelImport :: String -> Statement
- Data.XCB.Python.PyHelpers: mkReturn :: Expr () -> Statement ()
+ Data.XCB.Python.PyHelpers: mkReturn :: Expr -> Statement
- Data.XCB.Python.PyHelpers: mkStr :: String -> Expr ()
+ Data.XCB.Python.PyHelpers: mkStr :: String -> Expr
- Data.XCB.Python.PyHelpers: mkUnpackFrom :: PseudoExpr a => a -> [String] -> String -> Suite ()
+ Data.XCB.Python.PyHelpers: mkUnpackFrom :: PseudoExpr a => a -> [String] -> String -> Suite
- Data.XCB.Python.PyHelpers: mkXClass :: String -> String -> Bool -> Suite () -> Suite () -> Statement ()
+ Data.XCB.Python.PyHelpers: mkXClass :: String -> String -> Bool -> Suite -> Suite -> Statement
Files
- generator/Data/XCB/Python/AST.hs +236/−0
- generator/Data/XCB/Python/Parse.hs +138/−129
- generator/Data/XCB/Python/PyHelpers.hs +43/−117
- generator/xcffibgen.hs +1/−10
- test/GeneratorTests.hs +1/−2
- test/PyHelpersTests.hs +6/−7
- test/generator/event.py +1/−1
- test/generator/no_sequence.py +1/−1
- test/generator/record.py +118/−0
- test/generator/record.xml +33/−0
- xcffib.cabal +35/−37
+ generator/Data/XCB/Python/AST.hs view
@@ -0,0 +1,236 @@+{-+ - Copyright 2024 Tycho Andersen+ -+ - This module exists as a small AST to replace language-python, which seems+ - largely unmaintained at this point. This is not really intended to be a+ - complete python grammer, mostly it is just enough to be what xcffib needs to+ - generate its trees.+ -+ - It has no annotations as in language-python, because xcffib doesn't use them.+ -+ - The grammar not complete: it cannot express anything other than empty+ - dictionaries, cannot express sets at all, cannot do async, etc. all because+ - these features of the language are unused by xcffib.+ -+ - The grammar is not sound: it has one "op" class, representing both unary and+ - binary operators.+ -+ - Use at your own risk :)+ -}+{-# LANGUAGE TypeSynonymInstances #-}+{-# LANGUAGE FlexibleInstances #-}+module Data.XCB.Python.AST (+ Expr(..),+ Suite,+ Statement(..),+ Ident,+ Op(..),+ Pretty(..),+ prettyText,+ PseudoExpr(..)+) where++import Prelude hiding ((<>))++import Data.Maybe++import Text.PrettyPrint++type Suite = [Statement]++type Ident = String++-- we don't use that many operations :)+data Op+ = Plus+ | Minus+ | Multiply+ | FloorDivide+ | BinaryAnd+ | ShiftRight+ | ShiftLeft+ | Invert+ | Equality+ | LessThan+ | Modulo+ deriving (Eq, Ord, Show)++data Statement+ = Import+ { import_item :: Ident }+ -- this is only a one level dotted import; other levels are not supported+ | FromImport+ { from_module :: Ident+ , from_item :: Ident+ }+ -- we skip While, For, AsyncFor, etc. because we don't use them+ | Fun+ { fun_name :: Ident+ -- only named variables, no default values, kwargs, etc.+ , fun_args :: [Ident]+ , fun_body :: Suite+ }+ | Decorated+ { decorations :: Ident+ -- only decorated functions/methods are supported+ , fun_name :: Ident+ , fun_args :: [Ident]+ , fun_body :: Suite+ }+ -- skip AsyncFun...+ | Class+ { class_name :: Ident+ -- same as functions, only identifiers allowed+ , class_args :: [Ident]+ , class_body :: Suite+ }+ | Conditional+ { if_cond :: Expr+ , if_body :: Suite+ , else_body :: Suite+ }+ | Assign+ { assign_to :: Expr+ , assign_expr :: Expr+ }+ | AugmentedAssign+ { aug_assign_to :: Expr+ , aug_assign_op :: Op+ , aug_assign_expr :: Expr+ }+ -- skip AnnotatedAssign, Decorated+ | Return+ { return_expr :: Maybe (Expr) }+ -- skip Try, With, AsyncWith+ | Pass {}+ -- skip Break, Continue, Delete+ | StmtExpr+ { stmt_expr :: Expr }+ -- skip Global, NonLocal, Assert, Print, Exec+ | Raise { raise_exception :: Ident }+ deriving (Eq, Ord, Show)++data Expr+ = Var { var :: Ident }+ | Int { int_value :: Int }+ | Bool { bool_value :: Bool }+ | None+ | Strings { strings_strings :: [String] }+ | Call+ { call_fun :: Expr+ , call_args :: [Expr]+ }+ | CondExpr+ { ce_true_branch :: Expr+ , ce_conditon :: Expr+ , ce_false_branch :: Expr+ }+ | Subscript+ { subscriptee :: Expr+ , subscript_expr :: Expr+ }+ | BinaryOp+ { operator :: Op+ , binop_left :: Expr+ , binop_right :: Expr+ }+ | UnaryOp+ { operator :: Op+ , unop_arg :: Expr+ }+ | Dot+ { dot_expr :: Expr+ , dot_attribute :: Ident+ }+ | Tuple { tuple_exprs :: [Expr] }+ | EmptyDict {} -- an empty dictionary+ | Paren { paren_expr :: Expr }+ deriving (Eq, Ord, Show)++prettyText :: Pretty a => a -> String+prettyText = render . pretty++class Pretty a where+ pretty :: a -> Doc++instance Pretty Op where+ pretty Plus = text "+"+ pretty Minus = text "-"+ pretty Multiply = text "*"+ pretty FloorDivide = text "//"+ pretty BinaryAnd = text "&"+ pretty ShiftRight = text ">>"+ pretty ShiftLeft = text "<<"+ pretty Invert = text "~"+ pretty Equality = text "=="+ pretty LessThan = text "<"+ pretty Modulo = text "%"++_reserved :: [String]+_reserved = [ "None"+ , "def"+ , "class"+ , "and"+ , "or"+ ]++-- | Sanitize identifiers.+instance Pretty Ident where+ pretty s | s `elem` _reserved = text $ "_" ++ s+ pretty s | isInt s = text $ "_" ++ s+ where+ isInt str = isJust $ ((maybeRead str) :: Maybe Int)+ maybeRead = fmap fst . listToMaybe . reads+ pretty s = text s++instance Pretty Suite where+ pretty stmts = vcat $ map pretty stmts++indent :: Doc -> Doc+indent = nest 4++instance Pretty Statement where+ pretty (Import item) = text "import" <+> pretty item+ pretty (FromImport source item) = text "from" <+> text source <+> text "import" <+> pretty item+ pretty (Fun name args bod) = text "def" <+> pretty name <> (parens (addCommas args)) <> colon+ $+$ indent (pretty bod)+ pretty (Decorated decorator name args bod) = text "@" <> text decorator $+$ pretty (Fun name args bod)+ pretty (Class name [] body) = text "class" <+> pretty name <> colon $+$ indent (pretty body)+ pretty (Class name superclasses body) = text "class" <+> pretty name <> parens (addCommas superclasses) <> colon $+$ indent (pretty body)+ pretty (Conditional cond if_ else_) = text "if" <+> pretty cond <> colon $+$ indent (pretty if_) $+$ pretty else_+ pretty (Assign to expr) = pretty to <+> text "=" <+> pretty expr+ pretty (AugmentedAssign to op expr) = pretty to <+> pretty op <> text "=" <+> pretty expr+ pretty (Return (Just expr)) = text "return" <+> pretty expr+ pretty (Return Nothing) = text "return"+ pretty Pass = text "pass"+ pretty (StmtExpr expr) = pretty expr+ pretty (Raise exc) = text "raise" <+> pretty exc++class PseudoExpr a where+ getExpr :: a -> Expr++instance PseudoExpr String where+ getExpr s = Var s+instance PseudoExpr Expr where+ getExpr = id++addCommas :: PseudoExpr a => [a] -> Doc+addCommas exprs = hsep $ punctuate (text ",") (map (pretty . getExpr) exprs)++instance Pretty Expr where+ pretty (Var v) = pretty v+ pretty (Int i) = integer (toInteger i)+ pretty (Bool True) = text "True"+ pretty (Bool False) = text "False"+ pretty None = text "None"+ pretty (Strings xs) = hcat (map text xs)+ pretty (Call fun args) = pretty fun <> lparen <> addCommas args <> rparen+ pretty (CondExpr trueB cond falseB) = pretty trueB <+> text "if" <+> pretty cond <+> text "else" <+> pretty falseB+ pretty (Subscript thing expr) = pretty thing <> brackets (pretty expr)+ pretty (BinaryOp op left right) = pretty left <+> pretty op <+> pretty right+ pretty (UnaryOp op arg) = pretty op <> pretty arg+ pretty (Dot thing attr) = pretty thing <> text "." <> pretty attr+ pretty (Tuple [expr]) = pretty expr <> comma -- one element still needs to be a tuple+ pretty (Tuple exprs) = addCommas exprs+ pretty EmptyDict = text "{}"+ pretty (Paren expr) = lparen <> pretty expr <> rparen
generator/Data/XCB/Python/Parse.hs view
@@ -34,10 +34,9 @@ import Data.Maybe import Data.XCB.FromXML import Data.XCB.Types as X+import Data.XCB.Python.AST (Expr(..), Op(..), Statement(..), Suite, prettyText) import Data.XCB.Python.PyHelpers -import Language.Python.Common as P- import System.FilePath import System.FilePath.Glob @@ -55,15 +54,15 @@ type TypeInfoMap = M.Map X.Type TypeInfo data BindingPart =- Request (Statement ()) (Suite ()) |- Declaration (Suite ()) |+ Request Statement Suite |+ Declaration Suite | Noop deriving (Show) -collectBindings :: [BindingPart] -> (Suite (), Suite ())+collectBindings :: [BindingPart] -> (Suite, Suite) collectBindings = foldr collectR ([], []) where- collectR :: BindingPart -> (Suite (), Suite ()) -> (Suite (), Suite ())+ collectR :: BindingPart -> (Suite, Suite) -> (Suite, Suite) collectR (Request def decl) (defs, decls) = (def : defs, decl ++ decls) collectR (Declaration decl) (defs, decls) = (defs, decl ++ decls) collectR Noop x = x@@ -73,25 +72,25 @@ files <- namesMatching $ fp </> "*.xml" fromFiles files -renderPy :: Suite () -> String+renderPy :: Suite -> String renderPy s = ((intercalate "\n") $ map prettyText s) ++ "\n" -- | Generate the code for a set of X headers. Note that the code is generated -- in dependency order, NOT in the order you pass them in. Thus, you get a -- string (a suggested filename) along with the python code for that XHeader -- back.-xform :: [XHeader] -> [(String, Suite ())]+xform :: [XHeader] -> [(String, Suite)] xform = map buildPython . dependencyOrder where- buildPython :: Tree XHeader -> (String, Suite ())+ buildPython :: Tree XHeader -> (String, Suite) buildPython forest = let forest' = (mapM processXHeader $ postOrder forest) results = evalState forest' baseTypeInfo in last results processXHeader :: XHeader- -> State TypeInfoMap (String, Suite ())+ -> State TypeInfoMap (String, Suite) processXHeader header = do- let imports = [mkImport "xcffib", mkImport "struct", mkImport "io"]+ let imports = [Import "xcffib", Import "struct", Import "io"] version = mkVersion header key = maybeToList $ mkKey header globals = [mkDict "_events", mkDict "_errors"]@@ -120,20 +119,20 @@ postOrder (Node e cs) = (concat $ map postOrder cs) ++ [e] -mkAddExt :: XHeader -> Statement ()+mkAddExt :: XHeader -> Statement mkAddExt (xheader_header -> "xproto") =- flip StmtExpr () $ mkCall "xcffib._add_core" [ mkName "xprotoExtension"- , mkName "Setup"- , mkName "_events"- , mkName "_errors"- ]+ StmtExpr $ mkCall "xcffib._add_core" [ mkName "xprotoExtension"+ , mkName "Setup"+ , mkName "_events"+ , mkName "_errors"+ ] mkAddExt header = let name = xheader_header header- in flip StmtExpr () $ mkCall "xcffib._add_ext" [ mkName "key"- , mkName (name ++ "Extension")- , mkName "_events"- , mkName "_errors"- ]+ in StmtExpr $ mkCall "xcffib._add_ext" [ mkName "key"+ , mkName (name ++ "Extension")+ , mkName "_events"+ , mkName "_errors"+ ] -- | Information on basic X types. baseTypeInfo :: TypeInfoMap@@ -189,26 +188,26 @@ lang = many $ (,) <$> optional decimal <*> (satisfy $ inClass $ M.keys sizeM) -xBinopToPyOp :: X.Binop -> P.Op ()-xBinopToPyOp X.Add = P.Plus ()-xBinopToPyOp X.Sub = P.Minus ()-xBinopToPyOp X.Mult = P.Multiply ()-xBinopToPyOp X.Div = P.FloorDivide ()-xBinopToPyOp X.And = P.BinaryAnd ()-xBinopToPyOp X.RShift = P.ShiftRight ()+xBinopToPyOp :: X.Binop -> Op+xBinopToPyOp X.Add = Plus+xBinopToPyOp X.Sub = Minus+xBinopToPyOp X.Mult = Multiply+xBinopToPyOp X.Div = FloorDivide+xBinopToPyOp X.And = BinaryAnd+xBinopToPyOp X.RShift = ShiftRight -xUnopToPyOp :: X.Unop -> P.Op ()-xUnopToPyOp X.Complement = P.Invert ()+xUnopToPyOp :: X.Unop -> Op+xUnopToPyOp X.Complement = Invert -xExpressionToNestedPyExpr :: (String -> String) -> XExpression -> Expr ()+xExpressionToNestedPyExpr :: (String -> String) -> XExpression -> Expr xExpressionToNestedPyExpr acc (Op o e1 e2) =- Paren (xExpressionToPyExpr acc (Op o e1 e2)) ()+ Paren (xExpressionToPyExpr acc (Op o e1 e2)) xExpressionToNestedPyExpr acc xexpr = xExpressionToPyExpr acc xexpr -xExpressionToPyExpr :: (String -> String) -> XExpression -> Expr ()-xExpressionToPyExpr _ (Value i) = mkInt i-xExpressionToPyExpr _ (Bit i) = BinaryOp (ShiftLeft ()) (mkInt 1) (mkInt i) ()+xExpressionToPyExpr :: (String -> String) -> XExpression -> Expr+xExpressionToPyExpr _ (Value i) = Int i+xExpressionToPyExpr _ (Bit i) = BinaryOp ShiftLeft (Int 1) (Int i) xExpressionToPyExpr acc (FieldRef n) = mkName $ acc n xExpressionToPyExpr _ (EnumRef (UnQualType enum) n) = mkName $ enum ++ "." ++ n -- Currently xcb only uses unqualified types, not sure how qualtype should behave@@ -221,11 +220,11 @@ let o' = xBinopToPyOp o e1' = xExpressionToNestedPyExpr acc e1 e2' = xExpressionToNestedPyExpr acc e2- in BinaryOp o' e1' e2' ()+ in BinaryOp o' e1' e2' xExpressionToPyExpr acc (Unop o e) = let o' = xUnopToPyOp o e' = xExpressionToNestedPyExpr acc e- in Paren (UnaryOp o' e' ()) ()+ in Paren (UnaryOp o' e') xExpressionToPyExpr acc (ParamRef n) = if n == "num_axes" then mkName $ acc n@@ -251,13 +250,13 @@ getConst (PopCount e) = fmap popCount $ getConst e getConst _ = Nothing -xEnumElemsToPyEnum :: (String -> String) -> [XEnumElem] -> [(String, Expr ())]+xEnumElemsToPyEnum :: (String -> String) -> [XEnumElem] -> [(String, Expr)] xEnumElemsToPyEnum accessor membs = reverse $ conv membs [] [0..] where exprConv = xExpressionToPyExpr accessor- conv :: [XEnumElem] -> [(String, Expr ())] -> [Int] -> [(String, Expr ())]+ conv :: [XEnumElem] -> [(String, Expr)] -> [Int] -> [(String, Expr)] conv ((EnumElem name expr) : els) acc is =- let expr' = fromMaybe (mkInt (head is)) $ fmap exprConv expr+ let expr' = fromMaybe (Int (head is)) $ fmap exprConv expr is' = dropWhile (<= (fromIntegral (int_value expr'))) is acc' = (name, expr') : acc in conv els acc' is'@@ -283,13 +282,13 @@ mkPad 1 = "x" mkPad i = (show i) ++ "x" -structElemToPyUnpack :: Expr ()+structElemToPyUnpack :: Expr -> String -> TypeInfoMap -> GenStructElem Type -> Either (Maybe String, String)- (String, Either (Expr (), Expr ())- ([(Expr (), [GenStructElem Type])]), Maybe Int)+ (String, Either (Expr, Expr)+ ([(Expr, [GenStructElem Type])]), Maybe Int) structElemToPyUnpack _ _ _ (Pad PadBytes i) = Left (Nothing, mkPad i) structElemToPyUnpack _ _ _ (Pad PadAlignment _) = Left (Nothing, "") @@ -305,23 +304,27 @@ switch = map (mkSwitch cmp) bitcases in Right (name, Right switch, Nothing) where- mkSwitch :: Expr ()+ mkSwitch :: Expr -> BitCase- -> (Expr (), [GenStructElem Type])+ -> (Expr, [GenStructElem Type]) mkSwitch cmp (BitCase Nothing bcCmp _ elems) =- let cmpVal = xExpressionToPyExpr id bcCmp- equality = BinaryOp (P.BinaryAnd ()) cmp cmpVal ()+ -- note: head here and below is a hack; i am not exactly sure the+ -- semantics of the multi-expression BitCase, since it's "fake" and+ -- "encoded only once" see b3b5e029e7ad ("XKB: Fix GetKbdByName") for+ -- details. needs some wiresniffing to investigate.+ let cmpVal = xExpressionToPyExpr id (head bcCmp)+ equality = BinaryOp BinaryAnd cmp cmpVal in (equality, elems) mkSwitch cmp (BitCase (Just _) bcCmp _ elems) =- let cmpVal = xExpressionToPyExpr id bcCmp- equality = BinaryOp (P.Equality ()) cmp cmpVal ()+ let cmpVal = xExpressionToPyExpr id (head bcCmp)+ equality = BinaryOp Equality cmp cmpVal in (equality, elems) -- The enum field is mostly for user information, so we ignore it. structElemToPyUnpack unpacker ext m (X.List n typ len _) = let attr = ((++) "self.")- len' = fromMaybe pyNone $ fmap (xExpressionToPyExpr attr) len+ len' = fromMaybe None $ fmap (xExpressionToPyExpr attr) len cons = case m M.! typ of BaseType c -> mkStr c CompositeType tExt c | ext /= tExt -> mkName $ tExt ++ "." ++ c@@ -355,8 +358,8 @@ -> TypeInfoMap -> (String -> String) -> GenStructElem Type- -> Either (Maybe String, String) [(String, Either (Maybe (Expr ()))- [(Expr (), [GenStructElem Type])]+ -> Either (Maybe String, String) [(String, Either (Maybe (Expr))+ [(Expr, [GenStructElem Type])] )] structElemToPyPack _ _ _ (Pad _ i) = Left (Nothing, mkPad i) -- TODO: implement these?@@ -369,25 +372,26 @@ elems = map (mkSwitch cmp) bitcases in Right $ [(name, Right elems)] where- mkSwitch :: Expr ()+ mkSwitch :: Expr -> BitCase- -> (Expr (), [GenStructElem Type])+ -> (Expr, [GenStructElem Type]) mkSwitch cmp (BitCase _ bcCmp _ elems') =- let cmpVal = xExpressionToPyExpr accessor bcCmp- equality = BinaryOp (P.BinaryAnd ()) cmp cmpVal ()+ -- see comment above about head for BitCase+ let cmpVal = xExpressionToPyExpr accessor (head bcCmp)+ equality = BinaryOp BinaryAnd cmp cmpVal in (equality, elems') structElemToPyPack ext m accessor (SField n typ _ _) = let name = accessor n in case m M.! typ of BaseType c -> Left (Just name, c) CompositeType tExt typNam ->- let cond = mkCall "hasattr" [mkArg name, ArgExpr (mkStr "pack") ()]- trueB = mkCall (name ++ ".pack") noArgs+ let cond = mkCall "hasattr" [mkName name, (mkStr "pack")]+ trueB = mkCall (name ++ ".pack") [] typNam' = if ext == tExt then typNam else tExt ++ "." ++ typNam- synthetic = mkCall (typNam' ++ ".synthetic") [mkArg ("*" ++ name)]- falseB = mkCall (mkDot synthetic "pack") noArgs+ synthetic = mkCall (typNam' ++ ".synthetic") [mkName ("*" ++ name)]+ falseB = mkCall (mkDot synthetic "pack") [] in Right $ [(name- , Left (Just (CondExpr trueB cond falseB ()))+ , Left (Just (CondExpr trueB cond falseB)) )] -- TODO: assert values are in enum? structElemToPyPack ext m accessor (X.List n typ expr _) =@@ -421,7 +425,7 @@ ])) )] CompositeType _ _ -> Right $ [(name'- , Left (Just (mkCall (mkDot e "pack") noArgs))+ , Left (Just (mkCall (mkDot e "pack") [])) )] -- As near as I can tell here the padding param is unused.@@ -436,8 +440,8 @@ CompositeType _ _ -> error ( "ValueParams other than CARD{16,32} not allowed.") -buf :: Suite ()-buf = [mkAssign "buf" (mkCall "io.BytesIO" noArgs)]+buf :: Suite+buf = [mkAssign "buf" (mkCall "io.BytesIO" [])] mkPackStmts :: String -> String@@ -445,7 +449,7 @@ -> (String -> String) -> String -> [GenStructElem Type]- -> ([String], Suite ())+ -> ([String], Suite) mkPackStmts ext name m accessor prefix membs = let packF = structElemToPyPack ext m accessor (toPack, stmts) = span EC.isLeft $ map packF membs@@ -471,30 +475,30 @@ packStr = addStructData prefix $ intercalate "" keys write = mkCall "buf.write" [mkCall "struct.pack" (mkStr ('=' : packStr) : (map mkName args))]- writeStmt = if length packStr > 0 then [StmtExpr write ()] else []+ writeStmt = if length packStr > 0 then [StmtExpr write] else [] in (args ++ listNames', writeStmt ++ listWrites) where mkWrites :: String- -> Either (Maybe (Expr ()))- [(Expr (), [GenStructElem Type])]- -> Suite ()+ -> Either (Maybe (Expr))+ [(Expr, [GenStructElem Type])]+ -> Suite mkWrites _ (Left Nothing) = [] mkWrites _ (Left (Just expr)) = [mkListWrite expr] mkWrites valueList (Right condList) = let (conds, exprs) = unzip condList (names, stmts) = unzip $ map (mkPackStmts ext name m accessor "") exprs- in map (\(x, y, z) -> Conditional [(x, map (mkPop valueList) y ++ z)] [] ()) $ zip3 conds names stmts+ in map (\(x, y, z) -> Conditional x (map (mkPop valueList) y ++ z) []) $ zip3 conds names stmts - mkListWrite :: Expr ()- -> Statement ()- mkListWrite expr' = flip StmtExpr () . mkCall "buf.write" $ (: []) expr'+ mkListWrite :: Expr+ -> Statement+ mkListWrite expr' = StmtExpr . mkCall "buf.write" $ (: []) expr' mkPop :: String -> String- -> Statement ()+ -> Statement mkPop toPop n =- let pop = mkCall (mkDot toPop "pop") [mkInt 0]- in if null n then StmtExpr pop () else mkAssign n pop+ let pop = mkCall (mkDot toPop "pop") [Int 0]+ in if null n then StmtExpr pop else mkAssign n pop mkBasePack (Nothing, "") = [] mkBasePack (n, c) =@@ -507,28 +511,29 @@ -> Maybe (String, Int) -> [GenStructElem Type] -> Maybe Int- -> Statement ()+ -> Statement mkPackMethod ext name m prefixAndOp structElems minLen = let accessor = ((++) "self.") (prefix, op) = case prefixAndOp of Just ('x' : rest, i) ->- let packOpcode = mkCall "struct.pack" [mkStr "=B", mkInt i]+ let packOpcode = mkCall "struct.pack" [mkStr "=B", Int i] write = mkCall "buf.write" [packOpcode]- in (rest, [StmtExpr write ()])+ in (rest, [StmtExpr write]) Just (rest, _) -> error ("internal API error: " ++ show rest) Nothing -> ("", []) (_, packStmts) = mkPackStmts ext name m accessor prefix structElems extend = concat $ do len <- maybeToList minLen let bufLen = mkName "buf_len"- bufLenAssign = mkAssign bufLen $ mkCall "len" [mkCall "buf.getvalue" noArgs]- test = (BinaryOp (LessThan ()) bufLen (mkInt len)) ()- bufWriteLen = Paren (BinaryOp (Minus ()) (mkInt 32) bufLen ()) ()- extra = mkCall "struct.pack" [repeatStr "x" bufWriteLen]- writeExtra = [StmtExpr (mkCall "buf.write" [extra]) ()]+ bufLenAssign = mkAssign bufLen $ mkCall "len" [mkCall "buf.getvalue" []]+ test = (BinaryOp LessThan bufLen (Int len))+ bufWriteLen = Paren (BinaryOp Minus (Int 32) bufLen)+ extraPackFmt = Paren (BinaryOp Modulo (mkStr "%dx") bufWriteLen)+ extra = mkCall "struct.pack" [extraPackFmt]+ writeExtra = [StmtExpr (mkCall "buf.write" [extra])] return $ [bufLenAssign, mkIf test writeExtra]- ret = [mkReturn $ mkCall "buf.getvalue" noArgs]- in mkMethod "pack" (mkParams ["self"]) $ buf ++ op ++ packStmts ++ extend ++ ret+ ret = [mkReturn $ mkCall "buf.getvalue" []]+ in mkMethod "pack" ["self"] $ buf ++ op ++ packStmts ++ extend ++ ret data StructUnpackState = StructUnpackState { -- | stNeedsPad is whether or not a type_pad() is needed. As near@@ -552,14 +557,14 @@ -> String -> TypeInfoMap -> [GenStructElem Type]- -> (Suite (), Maybe Int)+ -> (Suite, Maybe Int) mkStructStyleUnpack prefix ext m membs = let unpacked = map (structElemToPyUnpack (mkName "unpacker") ext m) membs initial = StructUnpackState False [] prefix (_, unpackStmts, size) = evalState (mkUnpackStmts unpacked) initial base = [mkAssign "base" $ mkName "unpacker.offset"] bufsize =- let rhs = BinaryOp (Minus ()) (mkName "unpacker.offset") (mkName "base") ()+ let rhs = BinaryOp Minus (mkName "unpacker.offset") (mkName "base") in [mkAssign (mkAttr "bufsize") rhs] statements = base ++ unpackStmts ++ bufsize in (statements, size)@@ -568,9 +573,9 @@ -- Apparently you only type_pad before unpacking Structs or Lists, never -- base types. mkUnpackStmts :: [Either (Maybe String, String)- (String, Either (Expr (), Expr ())- ([(Expr (), [GenStructElem Type])]), Maybe Int)]- -> State StructUnpackState ([String], Suite (), Maybe Int)+ (String, Either (Expr, Expr)+ ([(Expr, [GenStructElem Type])]), Maybe Int)]+ -> State StructUnpackState ([String], Suite, Maybe Int) mkUnpackStmts [] = flushAcc @@ -601,11 +606,11 @@ ) where mkUnpackListOrSwitch :: String- -> Either (Expr (), Expr ())- ([(Expr (), [GenStructElem Type])])+ -> Either (Expr, Expr)+ ([(Expr, [GenStructElem Type])]) -> Bool -> StructUnpackState- -> Suite ()+ -> Suite mkUnpackListOrSwitch name' (Left (list, cons)) needsPad _ = let pad = if needsPad then [typePad cons]@@ -614,17 +619,17 @@ mkUnpackListOrSwitch _ (Right switchList) _ st' = let (conds, elems) = unzip switchList stmts = map (mkUnpackSwitchElems st') elems- in map (\x -> Conditional [x] [] ()) $ zip conds stmts+ in map (\(cond,body) -> Conditional cond body []) $ zip conds stmts mkUnpackSwitchElems :: StructUnpackState -> [GenStructElem Type]- -> Suite ()+ -> Suite mkUnpackSwitchElems st' elems' = let unpacked' = map (structElemToPyUnpack (mkName "unpacker") ext m) elems' (_, stmts', _) = evalState (mkUnpackStmts unpacked') st' in stmts' - flushAcc :: State StructUnpackState ([String], Suite (), Maybe Int)+ flushAcc :: State StructUnpackState ([String], Suite, Maybe Int) flushAcc = do StructUnpackState needsPad args keys <- get let size = calcsize keys@@ -632,7 +637,7 @@ put $ StructUnpackState needsPad [] "" return (args, assign, Just size) - typePad e = StmtExpr (mkCall "unpacker.pad" [e]) ()+ typePad e = StmtExpr (mkCall "unpacker.pad" [e]) -- | Given a (qualified) type name and a target type, generate a TypeInfoMap -- updater.@@ -643,16 +648,15 @@ ] in M.union m m' -mkSyntheticMethod :: [GenStructElem Type] -> [Statement ()]+mkSyntheticMethod :: [GenStructElem Type] -> [Statement] mkSyntheticMethod membs = do let names = catMaybes $ map getName membs- args = mkParams $ "cls" : names+ args = "cls" : names self = mkAssign "self" $ mkCall (mkDot "cls" "__new__") [mkName "cls"] body = map assign names ret = mkReturn $ mkName "self"- synthetic = mkMethod "synthetic" args $ (self : body) ++ [ret]- classmethod = Decorator [ident "classmethod"] noArgs ()- if null names then [] else [Decorated [classmethod] synthetic ()]+ synthetic = Decorated "classmethod" "synthetic" args $ (self : body) ++ [ret]+ if null names then [] else [synthetic] where getName :: GenStructElem Type -> Maybe String getName (Pad _ _) = Nothing@@ -665,7 +669,7 @@ getName (Fd n) = Just n getName (Length _ _) = Nothing - assign :: String -> Statement ()+ assign :: String -> Statement assign n = mkAssign (mkDot "self" n) $ mkName n processXDecl :: String@@ -689,7 +693,7 @@ synthetic = mkSyntheticMethod membs fixedLength = maybeToList $ do theLen <- len- let rhs = mkInt theLen+ let rhs = Int theLen return $ mkAssign "fixed_size" rhs modify $ mkModify ext n (CompositeType ext n) return $ Declaration [mkXClass n "xcffib.Struct" False statements (pack : fixedLength ++ synthetic)]@@ -736,18 +740,20 @@ return [theReply, cookie] hasReply = if length replyDecl > 0- then [ArgExpr (mkName cookieName) ()]+ then [mkName cookieName] else []- isChecked = pyTruth $ isJust reply- argChecked = ArgKeyword (ident "is_checked") (mkName "is_checked") ()- checkedParam = Param (ident "is_checked") Nothing (Just isChecked) ()- allArgs = (mkParams $ "self" : (filter (not . null) args)) ++ [checkedParam]- mkArg' = flip ArgExpr ()- ret = mkReturn $ mkCall "self.send_request" ((map mkArg' [ mkInt opcode- , mkName "buf"- ])- ++ hasReply- ++ [argChecked])+ -- another hack for not supporting named parameters+ -- argChecked = ArgKeyword (ident "is_checked") (mkName "is_checked")+ argChecked = mkName ("is_checked=is_checked")+ -- a hack to not have to support separate params/exprs+ -- checkedParam = Param (ident "is_checked") Nothing (Just isChecked)+ checkedParam = "is_checked=" ++ (show (isJust reply))+ allArgs = ("self" : (filter (not . null) args)) ++ [checkedParam]+ ret = mkReturn $ mkCall "self.send_request" ([ Int opcode+ , mkName "buf"+ ]+ ++ hasReply+ ++ [argChecked]) requestBody = buf ++ packStmts ++ [ret] request = mkMethod name allArgs requestBody return $ Request request replyDecl@@ -756,11 +762,9 @@ let unpackF = structElemToPyUnpack unpackerCopy ext m (fields, listInfo) = span EC.isLeft $ map unpackF membs toUnpack = concat $ map (mkUnionUnpack . EC.fromLeft') fields- listInfo' = map (either mkBaseUnpack id) listInfo- (names, listOrSwitches, _) = unzip3 listInfo'- (exprs, _) = unzip $ map EC.fromLeft' listOrSwitches- lists = map (uncurry mkAssign) $ zip (map mkAttr names) exprs- initMethod = lists ++ toUnpack+ initMethod = if any EC.isLeft listInfo+ then [notImplemented]+ else (mkListUnpack listInfo) ++ toUnpack -- Here, we only want to pack the first member of the union, since every -- member is the same data and we don't want to repeatedly pack it. pack = mkPackMethod ext name m Nothing [head membs] Nothing@@ -768,13 +772,18 @@ modify $ mkModify ext name (CompositeType ext name) return $ Declaration decl where- unpackerCopy = mkCall "unpacker.copy" noArgs+ unpackerCopy = mkCall "unpacker.copy" [] mkUnionUnpack :: (Maybe String, String)- -> Suite ()+ -> Suite mkUnionUnpack (n, typ) = mkUnpackFrom unpackerCopy (maybeToList n) typ - mkBaseUnpack _ = error "xcffib: trailing base types unpack not implemented"+ mkListUnpack listInfo =+ let listInfo' = map EC.fromRight' listInfo+ (names, listOrSwitches, _) = unzip3 listInfo'+ (exprs, _) = unzip $ map EC.fromLeft' listOrSwitches+ lists = map (uncurry mkAssign) $ zip (map mkAttr names) exprs+ in lists processXDecl ext (XidUnion name _) = -- These are always unions of only XIDs.@@ -796,16 +805,16 @@ modify $ mkModify ext name (CompositeType ext name) return $ Declaration $ [mkXClass name "xcffib.Buffer" False [] []] -mkVersion :: XHeader -> Suite ()+mkVersion :: XHeader -> Suite mkVersion header = let major = ver "MAJOR_VERSION" (xheader_major_version header) minor = ver "MINOR_VERSION" (xheader_minor_version header) in major ++ minor where- ver :: String -> Maybe Int -> Suite ()- ver target i = maybeToList $ fmap (\x -> mkAssign target (mkInt x)) i+ ver :: String -> Maybe Int -> Suite+ ver target i = maybeToList $ fmap (\x -> mkAssign target (Int x)) i -mkKey :: XHeader -> Maybe (Statement ())+mkKey :: XHeader -> Maybe (Statement) mkKey header = do name <- xheader_xname header let call = mkCall "xcffib.ExtensionKey" [mkStr name]
generator/Data/XCB/Python/PyHelpers.hs view
@@ -15,13 +15,9 @@ -} {-# LANGUAGE TypeSynonymInstances, FlexibleInstances #-} module Data.XCB.Python.PyHelpers (- mkImport, mkRelImport,- mkInt, mkAssign, mkCall,- noArgs,- mkArg, mkEnum, mkName, mkDot,@@ -36,113 +32,52 @@ mkDictUpdate, mkMethod, mkReturn,- pyTruth,- mkParams,- ident,- pyNone, mkIf,- repeatStr+ notImplemented ) where import Data.List.Split-import Data.Maybe -import Language.Python.Common--_reserved :: [String]-_reserved = [ "None"- , "def"- , "class"- , "and"- , "or"- ]--class PseudoExpr a where- getExpr :: a -> Expr ()--instance PseudoExpr String where- getExpr s = mkName s-instance PseudoExpr (Expr ()) where- getExpr = id---- | Create and sanatize a python identifier.-ident :: String -> Ident ()-ident s | s `elem` _reserved = Ident ("_" ++ s) ()-ident s | isInt s = Ident ("_" ++ s) ()- where- isInt str = isJust $ ((maybeRead str) :: Maybe Int)- maybeRead = fmap fst . listToMaybe . reads-ident s = Ident s ()---- Make a DottedName out of a string like "foo.bar" for use in imports.-mkDottedName :: String -> DottedName ()-mkDottedName = map ident . splitOn "."--mkVar :: String -> Expr ()-mkVar name = Var (ident name) ()+import Data.XCB.Python.AST (Expr(..), Op(..), Statement(..), Suite, Ident, PseudoExpr, getExpr) -- | Make an Expr out of a string like "foo.bar" describing the name.-mkName :: String -> Expr ()+mkName :: String -> Expr mkName s = let strings = splitOn "." s- in foldl mkDot (mkVar $ head strings) (tail strings)+ in foldl mkDot (Var $ head strings) (tail strings) -mkDot :: PseudoExpr a => a -> String -> Expr ()-mkDot e1 attr = Dot (getExpr e1) (ident attr) ()+mkDot :: PseudoExpr a => a -> String -> Expr+mkDot e1 attr = Dot (getExpr e1) attr -- | Make an attribute access, i.e. self.<string>.-mkAttr :: String -> Expr ()+mkAttr :: String -> Expr mkAttr s = mkName ("self." ++ s) -mkImport :: String -> Statement ()-mkImport name = Import [ImportItem (mkDottedName name) Nothing ()] ()--mkRelImport :: String -> Statement ()-mkRelImport name = FromImport (ImportRelative 1 Nothing ()) (FromItems [FromItem (ident name) Nothing ()] ()) ()--mkInt :: Int -> Expr ()-mkInt i = Int (toInteger i) (show i) ()--mkAssign :: PseudoExpr a => a -> Expr () -> Statement ()-mkAssign name expr = Assign [getExpr name] expr ()--mkIncr :: String -> Expr () -> Statement ()-mkIncr name expr = AugmentedAssign (mkName name) (PlusAssign ()) expr ()--class PseudoArgument a where- getArgument :: a -> Argument ()+mkRelImport :: String -> Statement+mkRelImport name = FromImport "." name -instance PseudoArgument (Expr ()) where- getArgument p = ArgExpr p ()-instance PseudoArgument (Argument ()) where- getArgument = id+mkAssign :: PseudoExpr a => a -> Expr -> Statement+mkAssign name expr = Assign (getExpr name) expr -mkCall :: (PseudoExpr a, PseudoArgument b) => a -> [b] -> Expr ()-mkCall name args = Call (getExpr name) (map getArgument args) ()+mkIncr :: String -> Expr -> Statement+mkIncr name expr = AugmentedAssign (mkName name) Plus expr -noArgs :: [Argument ()]-noArgs = []+mkCall :: PseudoExpr a => a -> [Expr] -> Expr+mkCall name args = Call (getExpr name) args -mkEnum :: String -> [(String, Expr ())] -> Statement ()+mkEnum :: String -> [(String, Expr)] -> Statement mkEnum cname values = let body = map (uncurry mkAssign) values- in Class (Ident cname ()) [] body ()--mkParams :: [String] -> [Parameter ()]-mkParams = map (\x -> Param (ident x) Nothing Nothing ())--mkArg :: String -> Argument ()-mkArg n = ArgExpr (mkName n) ()+ in Class cname [] body -mkXClass :: String -> String -> Bool -> Suite () -> Suite () -> Statement ()+mkXClass :: String -> String -> Bool -> Suite -> Suite -> Statement mkXClass clazz superclazz False [] [] = mkEmptyClass clazz superclazz mkXClass clazz superclazz xge constructor methods = let args = [ "self", "unpacker" ] super = mkCall (superclazz ++ ".__init__") $ map mkName args- body = eventToUnpacker : (StmtExpr super ()) : constructor- initParams = mkParams args+ body = eventToUnpacker : (StmtExpr super) : constructor xgeexp = mkAssign "xge" (if xge then (mkName "True") else (mkName "False"))- initMethod = Fun (ident "__init__") initParams Nothing body ()+ initMethod = Fun "__init__" args body in mkClass clazz superclazz $ xgeexp : initMethod : methods where@@ -151,55 +86,46 @@ -- passed directly to __init__. Since we don't keep track of the -- underlying buffers after the event is created, we have to re-pack -- things so they can be unpacked again.- eventToUnpacker :: Statement ()+ eventToUnpacker :: Statement eventToUnpacker = let newUnpacker = mkAssign "unpacker" (mkCall "xcffib.MemoryUnpacker"- [mkCall "unpacker.pack" noArgs])+ [mkCall "unpacker.pack" []]) cond = mkCall "isinstance" [mkName "unpacker", mkName "xcffib.Protobj"] in mkIf cond [newUnpacker] -mkEmptyClass :: String -> String -> Statement ()-mkEmptyClass clazz superclazz = mkClass clazz superclazz [Pass ()]--mkClass :: String -> String -> Suite () -> Statement ()-mkClass clazz superclazz body = Class (ident clazz) [mkArg superclazz] body ()+mkEmptyClass :: String -> String -> Statement+mkEmptyClass clazz superclazz = mkClass clazz superclazz [Pass] -mkStr :: String -> Expr ()-mkStr s = Strings ["\"", s, "\""] ()+mkClass :: String -> String -> Suite -> Statement+mkClass clazz superclazz body = Class clazz [superclazz] body -mkTuple :: [Expr ()] -> Expr ()-mkTuple = flip Tuple ()+mkStr :: String -> Expr+mkStr s = Strings ["\"", s, "\""] -mkUnpackFrom :: PseudoExpr a => a -> [String] -> String -> Suite ()+mkUnpackFrom :: PseudoExpr a => a -> [String] -> String -> Suite mkUnpackFrom unpacker names packs =- let lhs = mkTuple $ map mkAttr names+ let lhs = Tuple $ map mkAttr names -- Don't spam with this default arg unless it is really necessary. unpackF = mkDot unpacker "unpack" rhs = mkCall unpackF [mkStr packs]- stmt = if length names > 0 then mkAssign lhs rhs else StmtExpr rhs ()+ stmt = if length names > 0 then mkAssign lhs rhs else StmtExpr rhs in if length packs > 0 then [stmt] else [] -mkDict :: String -> Statement ()-mkDict name = mkAssign name (Dictionary [] ())+mkDict :: String -> Statement+mkDict name = mkAssign name EmptyDict -mkDictUpdate :: String -> Int -> String -> Statement ()+mkDictUpdate :: String -> Int -> String -> Statement mkDictUpdate dict key value =- mkAssign (Subscript (mkName dict) (mkInt key) ()) (mkName value)--mkMethod :: String -> [Parameter ()] -> Suite () -> Statement ()-mkMethod name args body = Fun (ident name) args Nothing body ()--mkReturn :: Expr () -> Statement ()-mkReturn = flip Return () . Just+ mkAssign (Subscript (mkName dict) (Int key)) (mkName value) -pyTruth :: Bool -> Expr ()-pyTruth = flip Bool ()+mkMethod :: String -> [Ident] -> Suite -> Statement+mkMethod name args body = Fun name args body -pyNone :: Expr ()-pyNone = None ()+mkReturn :: Expr -> Statement+mkReturn = Return . Just -mkIf :: Expr () -> Suite () -> Statement ()-mkIf e s = Conditional [(e, s)] [] ()+mkIf :: Expr -> Suite -> Statement+mkIf e s = Conditional e s [] -repeatStr :: String -> Expr () -> Expr ()-repeatStr s i = BinaryOp (Multiply ()) (mkStr s) i ()+notImplemented :: Statement+notImplemented = Raise "xcffib.XcffibNotImplemented"
generator/xcffibgen.hs view
@@ -15,8 +15,6 @@ -} module Main where --import Data.XCB.Types import Data.XCB.Python.Parse import Options.Applicative@@ -39,18 +37,11 @@ <> metavar "DIR" <> help "Output directory for generated python.") --- Headers we can't emit right now. Obviously we want to get rid of this :-)-badHeaders :: [String]-badHeaders = [ "xkb"- , "xprint"- ]- run :: Xcffibgen -> IO () run (Xcffibgen inp out) = do headers <- parseXHeaders inp- let headers' = filter (flip notElem badHeaders . xheader_header) headers createDirectoryIfMissing True out- sequence_ $ map processFile $ xform headers'+ sequence_ $ map processFile $ xform headers where processFile (fname, suite) = do putStrLn fname
test/GeneratorTests.hs view
@@ -15,8 +15,7 @@ -} module Main (main) where -import Language.Python.Common-+import Data.XCB.Python.AST import Data.XCB.Python.Parse import Data.XCB.FromXML import Data.XCB.Types
test/PyHelpersTests.hs view
@@ -15,9 +15,8 @@ -} module Main (main) where -import Language.Python.Common- import Data.XCB.Python.PyHelpers+import Data.XCB.Python.AST import Test.Framework ( defaultMain, Test ) import Test.Framework.Providers.HUnit@@ -29,15 +28,15 @@ testMkName :: Test testMkName = let result = mkName "self.foo.bar"- expected = (Dot (Dot (Var (Ident "self" ()) ())- (Ident "foo" ()) ())- (Ident "bar" ()) ())+ expected = (Dot (Dot (Var "self")+ "foo")+ "bar") in mkTest "testMkName" expected result testReserves :: Test testReserves =- let result = mkName "None"- expected = (Var (Ident "_None" ()) ())+ let result = prettyText (mkName "None")+ expected = "_None" in mkTest "testReserves" expected result main :: IO ()
test/generator/event.py view
@@ -21,7 +21,7 @@ buf.write(struct.pack("=B2xIIIIHHHHHH", self.rotation, self.timestamp, self.config_timestamp, self.root, self.request_window, self.sizeID, self.subpixel_order, self.width, self.height, self.mwidth, self.mheight)) buf_len = len(buf.getvalue()) if buf_len < 32:- buf.write(struct.pack("x" * (32 - buf_len)))+ buf.write(struct.pack(("%dx" % (32 - buf_len)))) return buf.getvalue() @classmethod def synthetic(cls, rotation, timestamp, config_timestamp, root, request_window, sizeID, subpixel_order, width, height, mwidth, mheight):
test/generator/no_sequence.py view
@@ -19,7 +19,7 @@ buf.write(xcffib.pack_list(self.keys, "B")) buf_len = len(buf.getvalue()) if buf_len < 32:- buf.write(struct.pack("x" * (32 - buf_len)))+ buf.write(struct.pack(("%dx" % (32 - buf_len)))) return buf.getvalue() @classmethod def synthetic(cls, keys):
+ test/generator/record.py view
@@ -0,0 +1,118 @@+import xcffib+import struct+import io+MAJOR_VERSION = 1+MINOR_VERSION = 13+key = xcffib.ExtensionKey("RECORD")+_events = {}+_errors = {}+class Range8(xcffib.Struct):+ xge = False+ def __init__(self, unpacker):+ if isinstance(unpacker, xcffib.Protobj):+ unpacker = xcffib.MemoryUnpacker(unpacker.pack())+ xcffib.Struct.__init__(self, unpacker)+ base = unpacker.offset+ self.first, self.last = unpacker.unpack("BB")+ self.bufsize = unpacker.offset - base+ def pack(self):+ buf = io.BytesIO()+ buf.write(struct.pack("=BB", self.first, self.last))+ return buf.getvalue()+ fixed_size = 2+ @classmethod+ def synthetic(cls, first, last):+ self = cls.__new__(cls)+ self.first = first+ self.last = last+ return self+class Range16(xcffib.Struct):+ xge = False+ def __init__(self, unpacker):+ if isinstance(unpacker, xcffib.Protobj):+ unpacker = xcffib.MemoryUnpacker(unpacker.pack())+ xcffib.Struct.__init__(self, unpacker)+ base = unpacker.offset+ self.first, self.last = unpacker.unpack("HH")+ self.bufsize = unpacker.offset - base+ def pack(self):+ buf = io.BytesIO()+ buf.write(struct.pack("=HH", self.first, self.last))+ return buf.getvalue()+ fixed_size = 4+ @classmethod+ def synthetic(cls, first, last):+ self = cls.__new__(cls)+ self.first = first+ self.last = last+ return self+class ExtRange(xcffib.Struct):+ xge = False+ def __init__(self, unpacker):+ if isinstance(unpacker, xcffib.Protobj):+ unpacker = xcffib.MemoryUnpacker(unpacker.pack())+ xcffib.Struct.__init__(self, unpacker)+ base = unpacker.offset+ self.major = Range8(unpacker)+ unpacker.pad(Range16)+ self.minor = Range16(unpacker)+ self.bufsize = unpacker.offset - base+ def pack(self):+ buf = io.BytesIO()+ buf.write(self.major.pack() if hasattr(self.major, "pack") else Range8.synthetic(*self.major).pack())+ buf.write(self.minor.pack() if hasattr(self.minor, "pack") else Range16.synthetic(*self.minor).pack())+ return buf.getvalue()+ @classmethod+ def synthetic(cls, major, minor):+ self = cls.__new__(cls)+ self.major = major+ self.minor = minor+ return self+class Range(xcffib.Struct):+ xge = False+ def __init__(self, unpacker):+ if isinstance(unpacker, xcffib.Protobj):+ unpacker = xcffib.MemoryUnpacker(unpacker.pack())+ xcffib.Struct.__init__(self, unpacker)+ base = unpacker.offset+ self.core_requests = Range8(unpacker)+ unpacker.pad(Range8)+ self.core_replies = Range8(unpacker)+ unpacker.pad(ExtRange)+ self.ext_requests = ExtRange(unpacker)+ unpacker.pad(ExtRange)+ self.ext_replies = ExtRange(unpacker)+ unpacker.pad(Range8)+ self.delivered_events = Range8(unpacker)+ unpacker.pad(Range8)+ self.device_events = Range8(unpacker)+ unpacker.pad(Range8)+ self.errors = Range8(unpacker)+ self.client_started, self.client_died = unpacker.unpack("BB")+ self.bufsize = unpacker.offset - base+ def pack(self):+ buf = io.BytesIO()+ buf.write(self.core_requests.pack() if hasattr(self.core_requests, "pack") else Range8.synthetic(*self.core_requests).pack())+ buf.write(self.core_replies.pack() if hasattr(self.core_replies, "pack") else Range8.synthetic(*self.core_replies).pack())+ buf.write(self.ext_requests.pack() if hasattr(self.ext_requests, "pack") else ExtRange.synthetic(*self.ext_requests).pack())+ buf.write(self.ext_replies.pack() if hasattr(self.ext_replies, "pack") else ExtRange.synthetic(*self.ext_replies).pack())+ buf.write(self.delivered_events.pack() if hasattr(self.delivered_events, "pack") else Range8.synthetic(*self.delivered_events).pack())+ buf.write(self.device_events.pack() if hasattr(self.device_events, "pack") else Range8.synthetic(*self.device_events).pack())+ buf.write(self.errors.pack() if hasattr(self.errors, "pack") else Range8.synthetic(*self.errors).pack())+ buf.write(struct.pack("=B", self.client_started))+ buf.write(struct.pack("=B", self.client_died))+ return buf.getvalue()+ @classmethod+ def synthetic(cls, core_requests, core_replies, ext_requests, ext_replies, delivered_events, device_events, errors, client_started, client_died):+ self = cls.__new__(cls)+ self.core_requests = core_requests+ self.core_replies = core_replies+ self.ext_requests = ext_requests+ self.ext_replies = ext_replies+ self.delivered_events = delivered_events+ self.device_events = device_events+ self.errors = errors+ self.client_started = client_started+ self.client_died = client_died+ return self+xcffib._add_ext(key, recordExtension, _events, _errors)
+ test/generator/record.xml view
@@ -0,0 +1,33 @@+<xcb header="record" extension-xname="RECORD" extension-name="Record"+ major-version="1" minor-version="13">++ <!-- Types -->+ <xidtype name="CONTEXT" />++ <struct name="Range8">+ <field type="CARD8" name="first" />+ <field type="CARD8" name="last" />+ </struct>++ <struct name="Range16">+ <field type="CARD16" name="first" />+ <field type="CARD16" name="last" />+ </struct>++ <struct name="ExtRange">+ <field type="Range8" name="major" />+ <field type="Range16" name="minor" />+ </struct>++ <struct name="Range">+ <field type="Range8" name="core_requests" />+ <field type="Range8" name="core_replies" />+ <field type="ExtRange" name="ext_requests" />+ <field type="ExtRange" name="ext_replies" />+ <field type="Range8" name="delivered_events" />+ <field type="Range8" name="device_events" />+ <field type="Range8" name="errors" />+ <field type="BOOL" name="client_started" />+ <field type="BOOL" name="client_died" />+ </struct>+</xcb>
xcffib.cabal view
@@ -1,14 +1,14 @@+cabal-version: 2.4 name: xcffib-version: 1.5.0+version: 1.6.0 synopsis: A cffi-based python binding for X homepage: http://github.com/tych0/xcffib-license: OtherLicense+license: Apache-2.0 license-file: LICENSE author: Tycho Andersen maintainer: Tycho Andersen <tycho@tycho.pizza> category: X11 build-type: Simple-cabal-version: >=1.10 bug-reports: https://github.com/tych0/xcffib/issues description: A cffi-based python binding for X, comparable to xpyb extra-source-files: test/generator/*.py,@@ -25,19 +25,20 @@ library build-depends: base ==4.*,- xcb-types >= 0.13.0,- language-python >= 0.5.6,- filepath,- filemanip,- split,- containers,- mtl >= 2.1,- attoparsec,- bytestring,- either+ xcb-types >= 0.15.0 && < 0.16,+ filepath >= 1.4.2 && < 1.5,+ filemanip >= 0.3.6 && < 0.4,+ split >= 0.2.3 && < 0.3,+ containers >= 0.6.5 && < 0.7,+ mtl >= 2.2.2 && < 2.4,+ attoparsec >= 0.14.4 && < 0.15,+ bytestring >= 0.11.3 && < 0.12,+ either >= 5.0.2 && < 5.1,+ pretty >= 1.1 && < 1.2 hs-source-dirs: generator exposed-modules: Data.XCB.Python.Parse, Data.XCB.Python.PyHelpers+ Data.XCB.Python.AST ghc-options: -Wall default-language: Haskell2010 @@ -46,22 +47,21 @@ hs-source-dirs: generator build-depends: base ==4.*, xcffib,- language-python >= 0.5.6,- split,- xcb-types >= 0.13.0,- optparse-applicative >= 0.13,- filepath,- filemanip,- directory >= 1.2,- containers,- mtl >= 2.1,- attoparsec,- bytestring,- either- if impl(ghc < 8.0)- build-depends: semigroups+ xcb-types >= 0.15.0 && < 0.16,+ filepath >= 1.4.2 && < 1.5,+ filemanip >= 0.3.6 && < 0.4,+ split >= 0.2.3 && < 0.3,+ containers >= 0.6.5 && < 0.7,+ mtl >= 2.2.2 && < 2.4,+ attoparsec >= 0.14.4 && < 0.15,+ bytestring >= 0.11.3 && < 0.12,+ either >= 5.0.2 && < 5.1,+ pretty >= 1.1 && < 1.2,+ directory >= 1.3.6 && < 1.4,+ optparse-applicative >= 0.18.1 && < 0.19 other-modules: Data.XCB.Python.Parse, Data.XCB.Python.PyHelpers+ Data.XCB.Python.AST ghc-options: -Wall default-language: Haskell2010 @@ -71,10 +71,9 @@ type: exitcode-stdio-1.0 build-depends: base ==4.*, xcffib,- language-python >= 0.5.6,- HUnit,- test-framework,- test-framework-hunit+ HUnit >= 1.6.2 && < 1.7,+ test-framework >= 0.8.2 && < 0.9,+ test-framework-hunit >= 0.3.0 && < 0.4 default-language: Haskell2010 test-suite GeneratorTests.hs@@ -83,10 +82,9 @@ type: exitcode-stdio-1.0 build-depends: base ==4.*, xcffib,- xcb-types >= 0.13.0,- language-python >= 0.5.6,- HUnit,- test-framework,- test-framework-hunit,- filepath+ xcb-types >= 0.15.0 && < 0.16,+ filepath >= 1.4.2 && < 1.5,+ HUnit >= 1.6.2 && < 1.7,+ test-framework >= 0.8.2 && < 0.9,+ test-framework-hunit >= 0.3.0 && < 0.4 default-language: Haskell2010