{-# LANGUAGE OverloadedStrings, QuasiQuotes #-}
{-|
Module      : Main
Description : Day 12 solution
Copyright   : (c) Eric Mertens, 2015
License     : ISC
Maintainer  : emertens@gmail.com

<https://adventofcode.com/2015/day/12>

Sum up the numbers in a JSON value.

Rather than pull in a heavy JSON parsing dependency, this
just parses out the subset of JSON that the problem uses.

>>> :main + "[1,2,3]\n"
6
6

>>> :main + "{\"a\":2,\"b\":4}\n"
6
6

>>> :main + "{\"a\":{\"b\":4},\"c\":-1}\n"
3
3

>>> :main + "[1,{\"c\":\"red\",\"b\":2},3]\n"
6
4

>>> :main + "{\"d\":\"red\",\"e\":[1,2,3,4],\"f\":5}\n"
15
0

>>> :main + "[1,\"red\",5]\n"
6
6

-}
module Main where

import Advent (format)
import Control.Applicative (Alternative((<|>)))
import Text.ParserCombinators.ReadP (ReadP, between, readS_to_P, sepBy)

-- | >>> :main
-- 119433
-- 68466
main :: IO ()
IO ()
main =
 do Value
value <- [format|2015 12 @p%n|]
    Int -> IO ()
forall a. Show a => a -> IO ()
print (Value -> Int
numbers       Value
value)
    Int -> IO ()
forall a. Show a => a -> IO ()
print (Value -> Int
nonredNumbers Value
value)

p :: ReadP Value
p :: ReadP Value
p =
  [Value] -> Value
Object ([Value] -> Value) -> ReadP [Value] -> ReadP Value
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> ReadP String -> ReadP String -> ReadP [Value] -> ReadP [Value]
forall open close a.
ReadP open -> ReadP close -> ReadP a -> ReadP a
between ReadP String
"{" ReadP String
"}" (ReadP Value
pEntry ReadP Value -> ReadP String -> ReadP [Value]
forall a sep. ReadP a -> ReadP sep -> ReadP [a]
`sepBy` ReadP String
",") ReadP Value -> ReadP Value -> ReadP Value
forall a. ReadP a -> ReadP a -> ReadP a
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|>
  [Value] -> Value
Array  ([Value] -> Value) -> ReadP [Value] -> ReadP Value
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> ReadP String -> ReadP String -> ReadP [Value] -> ReadP [Value]
forall open close a.
ReadP open -> ReadP close -> ReadP a -> ReadP a
between ReadP String
"[" ReadP String
"]" (ReadP Value
p ReadP Value -> ReadP String -> ReadP [Value]
forall a sep. ReadP a -> ReadP sep -> ReadP [a]
`sepBy` ReadP String
",") ReadP Value -> ReadP Value -> ReadP Value
forall a. ReadP a -> ReadP a -> ReadP a
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|>
  String -> Value
String (String -> Value) -> ReadP String -> ReadP Value
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> ReadP String
pString ReadP Value -> ReadP Value -> ReadP Value
forall a. ReadP a -> ReadP a -> ReadP a
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|>
  Int -> Value
Number (Int -> Value) -> ReadP Int -> ReadP Value
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> ReadS Int -> ReadP Int
forall a. ReadS a -> ReadP a
readS_to_P ReadS Int
forall a. Read a => ReadS a
reads

pString :: ReadP String
pString :: ReadP String
pString = ReadS String -> ReadP String
forall a. ReadS a -> ReadP a
readS_to_P ReadS String
forall a. Read a => ReadS a
reads

pEntry :: ReadP Value
pEntry :: ReadP Value
pEntry = ReadP String
pString ReadP String -> ReadP String -> ReadP String
forall a b. ReadP a -> ReadP b -> ReadP b
forall (f :: * -> *) a b. Applicative f => f a -> f b -> f b
*> ReadP String
":" ReadP String -> ReadP Value -> ReadP Value
forall a b. ReadP a -> ReadP b -> ReadP b
forall (f :: * -> *) a b. Applicative f => f a -> f b -> f b
*> ReadP Value
p

-- | Sum of all the number values in in JSON value.
numbers :: Value -> Int
numbers :: Value -> Int
numbers Value
v =
  case Value
v of
    Number Int
n -> Int
n
    Object [Value]
o -> [Int] -> Int
forall a. Num a => [a] -> a
forall (t :: * -> *) a. (Foldable t, Num a) => t a -> a
sum ((Value -> Int) -> [Value] -> [Int]
forall a b. (a -> b) -> [a] -> [b]
map Value -> Int
numbers [Value]
o)
    Array  [Value]
a -> [Int] -> Int
forall a. Num a => [a] -> a
forall (t :: * -> *) a. (Foldable t, Num a) => t a -> a
sum ((Value -> Int) -> [Value] -> [Int]
forall a b. (a -> b) -> [a] -> [b]
map Value -> Int
numbers [Value]
a)
    String String
_ -> Int
0

-- | Sum of all the number values in in JSON value
-- excluding objects containing the value @"red"@.
nonredNumbers :: Value -> Int
nonredNumbers :: Value -> Int
nonredNumbers Value
v =
  case Value
v of
    Number Int
n -> Int
n
    Object [Value]
o | String -> Value
String String
"red" Value -> [Value] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`notElem` [Value]
o -> [Int] -> Int
forall a. Num a => [a] -> a
forall (t :: * -> *) a. (Foldable t, Num a) => t a -> a
sum ((Value -> Int) -> [Value] -> [Int]
forall a b. (a -> b) -> [a] -> [b]
map Value -> Int
nonredNumbers [Value]
o)
    Array  [Value]
a -> [Int] -> Int
forall a. Num a => [a] -> a
forall (t :: * -> *) a. (Foldable t, Num a) => t a -> a
sum ((Value -> Int) -> [Value] -> [Int]
forall a b. (a -> b) -> [a] -> [b]
map Value -> Int
nonredNumbers [Value]
a)
    Value
_        -> Int
0

data Value
  = Number !Int
  | Array [Value]
  | Object [Value]
  | String String
  deriving (Value -> Value -> Bool
(Value -> Value -> Bool) -> (Value -> Value -> Bool) -> Eq Value
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: Value -> Value -> Bool
== :: Value -> Value -> Bool
$c/= :: Value -> Value -> Bool
/= :: Value -> Value -> Bool
Eq, Eq Value
Eq Value =>
(Value -> Value -> Ordering)
-> (Value -> Value -> Bool)
-> (Value -> Value -> Bool)
-> (Value -> Value -> Bool)
-> (Value -> Value -> Bool)
-> (Value -> Value -> Value)
-> (Value -> Value -> Value)
-> Ord Value
Value -> Value -> Bool
Value -> Value -> Ordering
Value -> Value -> Value
forall a.
Eq a =>
(a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
$ccompare :: Value -> Value -> Ordering
compare :: Value -> Value -> Ordering
$c< :: Value -> Value -> Bool
< :: Value -> Value -> Bool
$c<= :: Value -> Value -> Bool
<= :: Value -> Value -> Bool
$c> :: Value -> Value -> Bool
> :: Value -> Value -> Bool
$c>= :: Value -> Value -> Bool
>= :: Value -> Value -> Bool
$cmax :: Value -> Value -> Value
max :: Value -> Value -> Value
$cmin :: Value -> Value -> Value
min :: Value -> Value -> Value
Ord, Int -> Value -> ShowS
[Value] -> ShowS
Value -> String
(Int -> Value -> ShowS)
-> (Value -> String) -> ([Value] -> ShowS) -> Show Value
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> Value -> ShowS
showsPrec :: Int -> Value -> ShowS
$cshow :: Value -> String
show :: Value -> String
$cshowList :: [Value] -> ShowS
showList :: [Value] -> ShowS
Show)