-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathrun.hs
91 lines (71 loc) · 2.08 KB
/
run.hs
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
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
{-# LANGUAGE LambdaCase #-}
import AoC
import AoC.Grid
import Data.Maybe
import Data.List.Split (splitOn)
import Text.Megaparsec
import Text.Megaparsec.Char
import qualified Text.Megaparsec.Char.Lexer as L
import Data.Void (Void)
type Parser = Parsec Void String
sc :: Parser ()
sc = L.space space1 empty empty
symbol :: String -> Parser String
symbol = L.symbol sc
lexeme :: Parser a -> Parser a
lexeme = L.lexeme sc
parens :: Parser a -> Parser a
parens = between (symbol "(") (symbol ")")
operator :: Parser String
operator = choice (map (symbol . pure) "+*")
type Expr = [Term]
data Term = Num Int | Op String | Sub Expr
deriving (Show, Eq)
numP :: Parser Term
numP = Num <$> lexeme L.decimal
opP :: Parser Term
opP = Op <$> operator
parensExprP :: Parser Term
parensExprP = Sub <$> parens exprP
exprP :: Parser Expr
exprP = many (try opP <|> try numP <|> parensExprP)
eval :: Expr -> Int
eval = go
where -- Recursively reduce the first operator encountered.
go (e1:Op "+":e2:rest) =
go (Num (go [e1] + go [e2]):rest)
go (e1:Op "*":e2:rest) =
go (Num (go [e1] * go [e2]):rest)
-- Eval the sub-expression
go ((Sub expr):rest) =
let n = go expr
in go (Num n:rest)
-- Stop when we only have a number left.
go [Num n] = n
eval2 :: Expr -> Int
eval2 = go
where -- Eval top level by splitting on the weakest operator,
-- i.e. *, and taking the product of each evaluated factor.
go = product . map go' . splitOn [Op "*"]
-- Eval next level by summing the terms.
go' = sum . map f
-- At this point we're left with nums sub-expressions and the
-- symbol '+'.
f = \case
Num n -> n
Sub e -> go e
Op "+" -> 0
Op "*" -> error "wut"
parseAll :: String -> [Expr]
parseAll =
map fromJust
. map (parseMaybe exprP)
. lines
part1 = sum . map eval
part2 = sum . map eval2
main = main' "input.txt"
exampleMain = main' "example.txt"
main' file = do
input <- parseAll <$> readFile file
print (part1 input)
print (part2 input)