summaryrefslogtreecommitdiff
path: root/protocols/codegen/Wayland/CodeGen/Utils.hs
blob: b8596f1e95f40ba43c5ed17b8d2c312e23d0cd44 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
module Wayland.CodeGen.Utils
  ( pascalCase
  , camelCase
  , recordField
  , protocolModule
  , interfaceModule
  , fullModule
  , FlatModules
  , flattenModules
  , resolveTypes
  ) where

import Data.Char (toUpper, toLower)
import Wayland.CodeGen.Types
import qualified Data.Map as M
import Control.Monad.State.Strict (State, runState, gets, modify)
import Data.Functor (($>))

type FlatModules = M.Map String (TypedProtocol, TypedInterfaceDesc)

pascalCase :: String -> String
pascalCase (c:cs) = toUpper c : camelCase cs
pascalCase []     = []

camelCase :: String -> String
camelCase ('_':cs) = pascalCase cs
camelCase (c  :cs) = c : camelCase cs
camelCase []       = []

recordField :: String -> String -> String
recordField name field = applyFirst toLower name ++ applyFirst toUpper field
  where applyFirst _ []     = []
        applyFirst f (x:xs) = f x : xs

protocolModule :: ProtocolT i -> String
protocolModule = fromName . protName
  where fromName "wayland" = "Core"
        fromName name      = pascalCase name

interfaceModule :: InterfaceDesc -> String
interfaceModule = pascalCase . ifName

fullModule :: ProtocolT i -> InterfaceDesc -> String
fullModule p i = "Wayland.Protocols." ++ protocolModule p ++ "." ++ interfaceModule i

flattenModules :: [TypedProtocol] -> FlatModules
flattenModules protocols = M.unions $ fmap flattenProtocol protocols
  where flattenProtocol p    = M.fromList $ fmap (flattenInterface p) (protInterfaces p)
        flattenInterface p i = (ifName (fst i), (p, i))

resolveTypes :: Protocol -> TypedProtocol
resolveTypes p = Protocol { protName = protName p
                          , protDescription = protDescription p
                          , protCopyright   = protCopyright p
                          , protInterfaces  = runInterface <$> protInterfaces p
                          }
  where runInterface i = runState (resolveInterfaceTypes i $> i) emptyTypeInfo

resolveInterfaceTypes :: InterfaceDesc -> State TypeInfo ()
resolveInterfaceTypes i = do
  let assignRequestArgs r a (TypeInfo requests events enums) = TypeInfo (M.insert r a requests) events enums
  let assignEventArgs   e a (TypeInfo requests events enums) = TypeInfo requests (M.insert e a events) enums
  let assignEnumName    e n (TypeInfo requests events enums) = TypeInfo requests events (M.insert e n enums)
  let resolveAndAssign res ass = do x <- res; modify $ ass x
  mapM_ (\m -> resolveAndAssign (resolveMessageType m) (assignEventArgs   (msgName  m))) (ifEvents   i)
  mapM_ (\m -> resolveAndAssign (resolveMessageType m) (assignRequestArgs (msgName  m))) (ifRequests i)
  mapM_ (\e -> resolveAndAssign (resolveEnumType    e) (assignEnumName    (enumName e))) (ifEnums    i)

resolveMessageType :: MessageDesc -> State TypeInfo ArgumentName
resolveMessageType m = case msgArguments m of
                         []  -> pure ArgumentNameNone
                         [a] -> pure $ ArgumentNameSingle a
                         as  -> ArgumentNameRecord as <$> getFreeTypeName (pascalCase (msgName m)) Nothing

resolveEnumType :: EnumDesc -> State TypeInfo EnumName
resolveEnumType e = EnumName e <$> name
  where name = getFreeTypeName (pascalCase (enumName e)) $ Just (if enumBitfield e then "Flag" else "Value")

getFreeTypeName :: String -> Maybe String -> State TypeInfo String
getFreeTypeName requested conflictSuffix = attemptRequested
  where attemptName :: String -> State TypeInfo Bool
        attemptName name = gets $ isTypeFree name
        attemptRequested = attemptName requested >>= \s -> if s then pure requested else attemptSuffix
        attemptSuffix = case conflictSuffix of
                          Just suffix -> let n = requested ++ suffix
                                         in attemptName n >>= \s -> if s then pure n else attemptPrime n
                          Nothing     -> attemptPrime requested
        attemptPrime n = let n' = n ++ "'"
                         in attemptName n' >>= \s -> if s then pure n' else attemptPrime n'