@@ -10,18 +10,11 @@ import Data.Map.Strict (Map)
1010import Data.Map.Strict qualified as Map
1111import Data.Maybe
1212import Language.Haskell.TH
13- import Numeric
1413
15- -- | A raw define from the header file.
16- data RawDefine
17- = RawPrimary String Int -- ^ Name and numeric value
18- | RawAlias String String -- ^ Alias name and target name
19- deriving (Show )
20-
21- -- | A processed define, after deduplication.
14+ -- | A define from the header file: either a primary (value is a number) or an alias (value is another name).
2215data Define
23- = Primary String -- ^ A name that should become a constructor
24- | Alias String String -- ^ An alias name pointing to a target name
16+ = Primary String String -- ^ Name and raw value string (for dedup grouping)
17+ | Alias String String -- ^ Alias name and target name
2518 deriving (Show )
2619
2720-- | Configuration for a group of defines that map to a single Haskell type.
@@ -51,68 +44,57 @@ skippedNames :: [String]
5144skippedNames = [" KEY_MIN_INTERESTING" ]
5245
5346-- | Parse a single @#define@ line.
54- parseLine :: String -> Maybe RawDefine
47+ parseLine :: String -> Maybe Define
5548parseLine line = case words line of
5649 (" #define" : name : value : _)
5750 | any (`isSuffixOf'` name) [" _MAX" , " _CNT" ] -> Nothing
5851 | name `elem` skippedNames -> Nothing
5952 | name == " _INPUT_EVENT_CODES_H" -> Nothing
60- | Just n <- parseNumericValue value -> Just (RawPrimary name n)
61- | " (" `isPrefixOf` value -> Nothing
62- | all (\ c -> isAlphaNum c || c == ' _' ) value -> Just (RawAlias name value)
53+ | isDigit (head value) -> Just (Primary name value)
54+ | isAlpha (head value) -> Just (Alias name value)
6355 | otherwise -> Nothing
6456 _ -> Nothing
6557 where
6658 isSuffixOf' suffix str = drop (length str - length suffix) str == suffix
6759
68- -- | Parse a numeric value (decimal or hex).
69- parseNumericValue :: String -> Maybe Int
70- parseNumericValue (' 0' : ' x' : rest)
71- | all isHexDigit rest, [(n, " " )] <- readHex rest = Just n
72- parseNumericValue (' 0' : ' X' : rest)
73- | all isHexDigit rest, [(n, " " )] <- readHex rest = Just n
74- parseNumericValue s
75- | all isDigit s = Just (read s)
76- parseNumericValue _ = Nothing
77-
78- -- | Parse the header file, returning all raw defines.
79- parseHeader :: String -> [RawDefine ]
60+ -- | Parse the header file, returning all defines.
61+ parseHeader :: String -> [Define ]
8062parseHeader = mapMaybe parseLine . lines
8163
82- -- | Get the C name from a 'RawDefine '.
83- rawDefineName :: RawDefine -> String
84- rawDefineName ( RawPrimary n _) = n
85- rawDefineName ( RawAlias n _) = n
64+ -- | Get the C name from a 'Define '.
65+ defineName :: Define -> String
66+ defineName ( Primary n _) = n
67+ defineName ( Alias n _) = n
8668
8769-- | Check if a define belongs to a group.
88- defInGroup :: Group -> RawDefine -> Bool
89- defInGroup grp def = any (`isPrefixOf` rawDefineName def) (groupPrefixes grp)
70+ defInGroup :: Group -> Define -> Bool
71+ defInGroup grp def = any (`isPrefixOf` defineName def) (groupPrefixes grp)
9072
91- -- | Deduplicate primaries: when multiple primaries share a numeric value,
73+ -- | Deduplicate primaries: when multiple primaries share a value string ,
9274-- keep the last one as the constructor and turn earlier ones into aliases.
9375-- This handles cases like @BTN_GAMEPAD 0x130@ followed by @BTN_SOUTH 0x130@,
9476-- where @BTN_SOUTH@ becomes the constructor and @BTN_GAMEPAD@ becomes an alias.
95- dedup :: [RawDefine ] -> [Define ]
96- dedup rawDefs =
77+ dedup :: [Define ] -> [Define ]
78+ dedup defs =
9779 let -- First pass: find which name is the "winner" for each value (last one wins)
98- valueToName :: Map Int String
80+ valueToName :: Map String String
9981 valueToName = foldl'
100- (\ m rd -> case rd of
101- RawPrimary name val -> Map. insert val name m
102- RawAlias _ _ -> m
82+ (\ m d -> case d of
83+ Primary name val -> Map. insert val name m
84+ Alias _ _ -> m
10385 )
10486 Map. empty
105- rawDefs
87+ defs
10688
107- -- Second pass: convert, turning losers into aliases
108- convert :: RawDefine -> Define
109- convert (RawPrimary name val) =
89+ -- Second pass: convert losers into aliases pointing to the winner
90+ convert :: Define -> Define
91+ convert (Primary name val) =
11092 let winner = valueToName Map. ! val
11193 in if name == winner
112- then Primary name
94+ then Primary name val
11395 else Alias name winner
114- convert ( RawAlias name target) = Alias name target
115- in map convert rawDefs
96+ convert a @ Alias {} = a
97+ in map convert defs
11698
11799-- | Transform a C name like @KEY_LEFT_SHIFT@ into a Haskell constructor name like @KeyLeftShift@.
118100toCamelCase :: [String ] -> String -> String
@@ -150,15 +132,14 @@ toRawName (c : cs) = toLower c : cs
150132generateCodes :: Q [Dec ]
151133generateCodes = do
152134 contents <- runIO $ readFile " /nix/store/7iwv8dcgsjmkrnn752hnfdxh3f7wahmd-linux-headers-6.16.7/include/linux/input-event-codes.h"
153- let rawDefs = parseHeader contents
154- concat <$> mapM (generateGroup rawDefs ) groups
135+ let defs = parseHeader contents
136+ concat <$> mapM (generateGroup defs ) groups
155137
156138-- | Generate declarations for a single group: data type, SimpleEnum instance, and pattern synonyms.
157- generateGroup :: [RawDefine ] -> Group -> Q [Dec ]
158- generateGroup allRawDefs grp = do
159- let myRawDefs = filter (defInGroup grp) allRawDefs
160- myDefs = dedup myRawDefs
161- primaries = [n | Primary n <- myDefs]
139+ generateGroup :: [Define ] -> Group -> Q [Dec ]
140+ generateGroup allDefs grp = do
141+ let myDefs = dedup $ filter (defInGroup grp) allDefs
142+ primaries = [n | Primary n _ <- myDefs]
162143 aliases = [(a, t) | Alias a t <- myDefs]
163144 prefixes = groupPrefixes grp
164145 tyName = mkName (groupTypeName grp)
0 commit comments