summaryrefslogtreecommitdiff
path: root/3-2.hs
blob: 877fc987f7985f1409c571c90e4bfc78b6be3fa3 (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
import Data.Bifunctor
import Data.Char

newtype Parser a b = Parser {parse :: a -> Maybe (b,a)}

instance Functor (Parser a) where
  fmap f p = Parser (fmap (first f) . parse p)

instance Monoid a => Applicative (Parser a) where
  pure x = Parser . const . Just $ (x, mempty)
  p <*> q = Parser $ \ x -> let r = parse p x
    in case r of
         Nothing    -> Nothing
         Just (a,b) -> let s = parse q b
           in case s of
                Nothing    -> Nothing
                Just (c,d) -> Just (a c, d)

string :: String -> Parser String String
string s = Parser $ \ r -> if take (length s) r == s
                           then Just (s, drop (length s) r)
                           else Nothing

while :: (a -> Bool) -> Parser [a] [a]
while p = Parser $ \ xs -> case while' xs of
                             ([], _) -> Nothing
                             a -> Just a
  where
    while' [] = ([], [])
    while' (x:xs) | p x = first (x:) (while' xs)
                  | otherwise = ([], x:xs)

number :: Parser String Integer
number = read <$> while isDigit

scan :: Parser [a] b -> [a] -> [b]
scan _ [] = []
scan p s@(_:xs) = case parse p s of
  Nothing    -> scan p xs
  Just (a,b) -> a : scan p b

exclude :: String -> String -> String -> String
exclude _ _ [] = []
exclude from to s@(x:xs) = case parse (string from) s of
  Nothing -> x : exclude from to xs
  Just (_,b) -> exclude' from to b

exclude' :: String -> String -> String -> String
exclude' _ _ [] = []
exclude' from to s@(_:xs) = case parse (string to) s of
  Nothing -> exclude' from to xs
  Just (_,b) -> exclude from to b

instruction :: Parser String (Integer, Integer)
instruction = (,)
  <$  string "mul("
  <*> number
  <*  string ","
  <*> number
  <*  string ")"

result :: String -> Integer
result = sum . map (uncurry (*)) . scan instruction

main :: IO ()
main = do
  s <- getContents
  let s' = exclude "don't()" "do()" s
  print $ result s'