I received a few comments on part 3 of this little mini-series and I just wanted to address them. While doing this I still want the main functions of the parser
parseXxx to read like the
maps file itself. That means I want to avoid “reversing order” like
thenSpace did in part2. I also don’t want to hide things, e.g. I don’t want to introduce a function that turns
(a <* char ' ') <*> b into
a <#> b.
So, first up is to do something about
hexStr2Int <$> many1 hexDigit which appears all over the place. I made it appear in even more places by moving around a few parentheses; the following two functions are the same:
foo = a <$> (b <* c) bar = (a <$> b) <* c
Then I scrapped
hexStr2Int completely and instead introduced
hexStr = Prelude.read . ("0x" ++) <$> many1 hexDigit
This means that
parseAddress can be rewritten to:
parseAddress = Address <$> hexStr <* char '-' <*> hexStr
Rather than, as Conal suggested, introduce an infix operation that addresses the pattern
(a <* char ' ') <*> b I decided to do something about
a <* char c. I feel Conal’s suggestion, while shortening the code more than my solution, goes against my wish to not hide things. This is the definition of
(<##>) l r = l <* char r
After this I rewrote
parseAddress = Address <$> hexStr <##> '-' <*> hexStr
(== c) <$> anyChar appears three times in
parsePerms so it got a name and moved down into the
where clause. I also modified
cA to use pattern matching. I haven’t spent much time considering error handling in the parser, so I didn’t introduce a pattern matching everything else.
parsePerms = Perms <$> pP 'r' <*> pP 'w' <*> pP 'x' <*> (cA <$> anyChar) where pP c = (== c) <$> anyChar cA 'p' = Private cA 's' = Shared
The last change I did was remove a bunch of parentheses. I’m always a little hesitant removing parentheses and relying on precedence rules, I find I’m even more hesitant doing it when programming Haskell. Probably due to Haskell having a lot of infix operators that I’m unused to.
The rest of the parser now looks like this:
parseDevice = Device <$> hexStr <##> ':' <*> hexStr parseRegion = MemRegion <$> parseAddress <##> ' ' <*> parsePerms <##> ' ' <*> hexStr <##> ' ' <*> parseDevice <##> ' ' <*> (Prelude.read <$> many1 digit) <##> ' ' <*> (parsePath <|> string "") where parsePath = (many1 $ char ' ') *> (many1 anyChar)
I think these changes address most of the comments Conal and Twan made on the previous part. Where they don’t I hope I’ve explained why I decided not to take their advice.