{-# Language BlockArguments #-}
{-|
Module      : Main
Description : Day 18 solution
Copyright   : (c) Eric Mertens, 2020
License     : ISC
Maintainer  : emertens@gmail.com

<https://adventofcode.com/2020/day/18>

-}
module Main (main) where

import Advent (getInputLines)
import Control.Applicative ((<|>))
import Data.Char (isDigit, digitToInt)
import Text.ParserCombinators.ReadP

-- |
-- >>> :main
-- 14208061823964
-- 320536571743074
main :: IO ()
IO ()
main =
  do [String]
inp <- Int -> Int -> IO [String]
getInputLines Int
2020 Int
18
     Int -> IO ()
forall a. Show a => a -> IO ()
print ([Int] -> Int
forall a. Num a => [a] -> a
forall (t :: * -> *) a. (Foldable t, Num a) => t a -> a
sum ((String -> Int) -> [String] -> [Int]
forall a b. (a -> b) -> [a] -> [b]
map (ReadP Int -> String -> Int
forall a. ReadP a -> String -> a
run ReadP Int
expr1) [String]
inp))
     Int -> IO ()
forall a. Show a => a -> IO ()
print ([Int] -> Int
forall a. Num a => [a] -> a
forall (t :: * -> *) a. (Foldable t, Num a) => t a -> a
sum ((String -> Int) -> [String] -> [Int]
forall a b. (a -> b) -> [a] -> [b]
map (ReadP Int -> String -> Int
forall a. ReadP a -> String -> a
run ReadP Int
expr2) [String]
inp))

run :: ReadP a -> String -> a
run :: forall a. ReadP a -> String -> a
run ReadP a
p = (a, String) -> a
forall a b. (a, b) -> a
fst ((a, String) -> a) -> (String -> (a, String)) -> String -> a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [(a, String)] -> (a, String)
forall a. HasCallStack => [a] -> a
head ([(a, String)] -> (a, String))
-> (String -> [(a, String)]) -> String -> (a, String)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ReadP a -> String -> [(a, String)]
forall a. ReadP a -> ReadS a
readP_to_S (ReadP ()
skipSpaces ReadP () -> ReadP a -> ReadP a
forall a b. ReadP a -> ReadP b -> ReadP b
forall (f :: * -> *) a b. Applicative f => f a -> f b -> f b
*> ReadP a
p ReadP a -> ReadP () -> ReadP a
forall a b. ReadP a -> ReadP b -> ReadP a
forall (f :: * -> *) a b. Applicative f => f a -> f b -> f a
<* ReadP ()
eof)

l :: Char -> ReadP ()
l :: Char -> ReadP ()
l Char
c = Char -> ReadP Char
char Char
c ReadP Char -> ReadP () -> ReadP ()
forall a b. ReadP a -> ReadP b -> ReadP b
forall (f :: * -> *) a b. Applicative f => f a -> f b -> f b
*> ReadP ()
skipSpaces

add, mul :: ReadP (Int -> Int -> Int)
add :: ReadP (Int -> Int -> Int)
add = Int -> Int -> Int
forall a. Num a => a -> a -> a
(+) (Int -> Int -> Int) -> ReadP () -> ReadP (Int -> Int -> Int)
forall a b. a -> ReadP b -> ReadP a
forall (f :: * -> *) a b. Functor f => a -> f b -> f a
<$ Char -> ReadP ()
l Char
'+'
mul :: ReadP (Int -> Int -> Int)
mul = Int -> Int -> Int
forall a. Num a => a -> a -> a
(*) (Int -> Int -> Int) -> ReadP () -> ReadP (Int -> Int -> Int)
forall a b. a -> ReadP b -> ReadP a
forall (f :: * -> *) a b. Functor f => a -> f b -> f a
<$ Char -> ReadP ()
l Char
'*'

number :: ReadP Int
number :: ReadP Int
number = Char -> Int
digitToInt (Char -> Int) -> ReadP Char -> ReadP Int
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> (Char -> Bool) -> ReadP Char
satisfy Char -> Bool
isDigit ReadP Int -> ReadP () -> ReadP Int
forall a b. ReadP a -> ReadP b -> ReadP a
forall (f :: * -> *) a b. Applicative f => f a -> f b -> f a
<* ReadP ()
skipSpaces

aexpr :: ReadP Int -> ReadP Int
aexpr :: ReadP Int -> ReadP Int
aexpr ReadP Int
top = ReadP Int
number ReadP Int -> ReadP Int -> ReadP Int
forall a. ReadP a -> ReadP a -> ReadP a
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> ReadP () -> ReadP () -> ReadP Int -> ReadP Int
forall open close a.
ReadP open -> ReadP close -> ReadP a -> ReadP a
between (Char -> ReadP ()
l Char
'(') (Char -> ReadP ()
l Char
')') ReadP Int
top

expr1, expr2 :: ReadP Int
expr1 :: ReadP Int
expr1 = ReadP Int -> ReadP (Int -> Int -> Int) -> ReadP Int
forall a. ReadP a -> ReadP (a -> a -> a) -> ReadP a
chainl1 (ReadP Int -> ReadP Int
aexpr ReadP Int
expr1) (ReadP (Int -> Int -> Int)
add ReadP (Int -> Int -> Int)
-> ReadP (Int -> Int -> Int) -> ReadP (Int -> Int -> Int)
forall a. ReadP a -> ReadP a -> ReadP a
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> ReadP (Int -> Int -> Int)
mul)
expr2 :: ReadP Int
expr2 = ReadP Int -> ReadP (Int -> Int -> Int) -> ReadP Int
forall a. ReadP a -> ReadP (a -> a -> a) -> ReadP a
chainl1 (ReadP Int -> ReadP (Int -> Int -> Int) -> ReadP Int
forall a. ReadP a -> ReadP (a -> a -> a) -> ReadP a
chainl1 (ReadP Int -> ReadP Int
aexpr ReadP Int
expr2) ReadP (Int -> Int -> Int)
add) ReadP (Int -> Int -> Int)
mul