3 import Text.ParserCombinators.ReadP
4 -- import Text.ParserCombinators.ReadP ((+++))
5 import Data.Char (isDigit, isAlpha)
7 import qualified Data.IntMap.Strict as M
8 import Data.IntMap.Strict ((!))
10 import Data.Functor (void)
14 data Rule = Letter Char
20 type RuleSet = M.IntMap Rule
25 do text <- readFile "data/advent19.txt"
27 let (rules, messages) = parse inputP text
28 print $ part1 rules messages
29 print $ part2 rules messages
32 do text <- readFile fname
33 let (rules, messages) = parse inputP text
34 let newRules = parse rulesP "8: 42 | 42 8\n11: 42 31 | 42 11 31"
35 let updatedRules = M.union newRules rules
36 let myParser = makeParser updatedRules (See 0)
37 return (myParser, updatedRules, messages)
42 part2 rules messages = countMatches updatedRules messages
43 where newRules = parse rulesP "8: 42 | 42 8\n11: 42 31 | 42 11 31"
44 updatedRules = M.union newRules rules
46 countMatches rules messages
47 = length $ filter ((== "") . snd) results
48 where myParser = makeParser rules (See 0)
49 results = concatMap (readP_to_S myParser) messages
52 parse :: ReadP a -> String -> a
53 parse parser str = fst $ head $ filter ((== "") . snd) $ readP_to_S parser str
58 makeParser :: RuleSet -> Rule -> ReadP ()
59 makeParser m (Letter c) = void $ char c
60 makeParser m (Then rs) = mapM_ (makeParser m) rs
61 makeParser m (Or a b) = (makeParser m a) +++ (makeParser m b)
62 makeParser m (See i) = makeParser m (m!i)
66 inputP = (,) <$> rulesP <* blankLines <*> messagesP
68 rulesP = M.fromList <$> ruleP `sepBy` endOfLine
69 ruleP = (,) <$> decimal <* (string ": ") <*> ruleBodyP
70 ruleBodyP = choice [letterRuleP, orRuleP, thenRuleP, seeRuleP]
72 letterRuleP = Letter <$> between (string "\"") (string "\"") get
73 orRuleP = Or <$> thenRuleP <* (string " | ") <*> thenRuleP
74 thenRuleP = Then <$> seeRuleP `sepBy` (string " ")
75 seeRuleP = See <$> decimal
77 messagesP = (munch1 isAlpha) `sepBy` endOfLine
79 blankLines = skipMany1 endOfLine
81 decimal = read <$> many1 (satisfy isDigit)