2024-12-04 17:15:59 +11:00
|
|
|
{-# OPTIONS_GHC -Wno-x-partial #-}
|
|
|
|
|
2024-12-03 10:34:35 +11:00
|
|
|
import Aoc
|
|
|
|
import Data.Char ( isDigit )
|
|
|
|
import Control.Arrow ( first, second )
|
|
|
|
|
|
|
|
data JSON a = Int a | Str String | Array [JSON a] | Object [(String, JSON a)]
|
|
|
|
deriving ( Show, Eq )
|
|
|
|
|
|
|
|
parseElements :: (String -> (p, String)) -> Char -> String -> ([p], String)
|
|
|
|
parseElements parseElement closing cs
|
|
|
|
| head cs == closing = ([], tail cs)
|
|
|
|
| head cs == ',' = parseElements parseElement closing (tail cs)
|
|
|
|
| otherwise =
|
|
|
|
let (element, rest0) = parseElement cs in
|
|
|
|
let (elements, rest1) = parseElements parseElement closing rest0 in
|
|
|
|
(element:elements, rest1)
|
|
|
|
|
|
|
|
parseString :: String -> (String, String)
|
|
|
|
parseString rest0 =
|
|
|
|
let (string, rest1) = span (/= '"') rest0 in -- no escaping
|
|
|
|
let ('"':rest2) = rest1 in
|
|
|
|
(string, rest2)
|
|
|
|
|
|
|
|
parseKeyValue :: Read a => [Char] -> ((String, JSON a), String)
|
|
|
|
parseKeyValue ('"':rest0) =
|
|
|
|
let (string, rest1) = parseString rest0 in
|
|
|
|
let (':':rest2) = rest1 in
|
|
|
|
let (object, rest3) = parseJSON rest2 in
|
|
|
|
((string, object), rest3)
|
|
|
|
|
|
|
|
parseJSON :: Read a => String -> (JSON a, String)
|
|
|
|
parseJSON ('[':rest0) =
|
|
|
|
let (elements, rest1) = parseElements parseJSON ']' rest0 in
|
|
|
|
(Array elements, rest1)
|
|
|
|
parseJSON ('{':rest0) =
|
|
|
|
let (elements, rest1) = parseElements parseKeyValue '}' rest0 in
|
|
|
|
(Object elements, rest1)
|
|
|
|
parseJSON ('"':rest0) = first Str $ parseString rest0
|
|
|
|
parseJSON rest = if isNum (head rest) then first (Int . read) $ span isNum rest else error rest where isNum c = isDigit c || c == '-'
|
|
|
|
|
|
|
|
parseDone :: (p, String) -> p
|
|
|
|
parseDone (p, "") = p
|
|
|
|
|
|
|
|
instance Foldable JSON where
|
|
|
|
foldMap :: Monoid m => (a -> m) -> JSON a -> m
|
|
|
|
foldMap f (Int a) = f a
|
|
|
|
foldMap f (Str _) = mempty
|
|
|
|
foldMap f (Array xs) = foldMap (foldMap f) xs
|
|
|
|
foldMap f (Object kvs) = foldMap (foldMap f . snd) kvs
|
|
|
|
|
|
|
|
hasRed :: Eq a => JSON a -> Bool
|
|
|
|
hasRed (Object kvs) = any ((==Str "red") . snd) kvs
|
|
|
|
hasRed _ = False
|
|
|
|
|
|
|
|
removeRed :: Eq a => JSON a -> JSON a
|
|
|
|
removeRed json = case json of
|
|
|
|
Object kvs -> if hasRed json then Object [] else Object $ map (second removeRed) kvs
|
|
|
|
Array xs -> Array $ map removeRed xs
|
|
|
|
_ -> json
|
|
|
|
|
|
|
|
part1 :: JSON Int -> Int
|
|
|
|
part1 json = sum $ foldMap (:[]) json
|
|
|
|
|
|
|
|
part2 :: JSON Int -> Int
|
|
|
|
part2 = part1 . removeRed
|
|
|
|
|
|
|
|
main :: IO ()
|
|
|
|
main = aocMain (parseDone . parseJSON . single) part1 part2
|