diff options
Diffstat (limited to '2020/18a.hs')
-rw-r--r-- | 2020/18a.hs | 86 |
1 files changed, 86 insertions, 0 deletions
diff --git a/2020/18a.hs b/2020/18a.hs new file mode 100644 index 0000000..7ed5a90 --- /dev/null +++ b/2020/18a.hs @@ -0,0 +1,86 @@ +module Day18A where + +import System.Environment (getArgs) +import Text.Printf + +-- Types +data Symbol = Num Int | Add | Mult | StartParen | EndParen + deriving (Eq, Show); + +data Expression = Expression :+: Expression | Expression :*: Expression | Paren Expression | Literal Int + deriving (Eq, Show); + +-- Read until there's some sort of operator, ie the entire number +readUntilOp :: String -> (String, String) -- read, remaining +readUntilOp [] = ("", "") +readUntilOp xs@('+':_) = ("", xs) +readUntilOp xs@('*':_) = ("", xs) +readUntilOp xs@('(':_) = ("", xs) +readUntilOp xs@(')':_) = ("", xs) +readUntilOp (x:xs) = (x : r, re) + where (r, re) = readUntilOp xs + +-- Tokenise a string to a list of symbols +toSymbols :: String -> [Symbol] +toSymbols [] = [] +toSymbols(' ':xs) = toSymbols xs +toSymbols ('+':xs) = Add : toSymbols xs +toSymbols ('*':xs) = Mult : toSymbols xs +toSymbols ('(':xs) = StartParen : toSymbols xs +toSymbols (')':xs) = EndParen : toSymbols xs +toSymbols xs = Num (read n) : toSymbols re + where (n, re) = readUntilOp xs + +-- Read till the next end paren at the same level +-- This needs to not start with a start paren +tillEndParen :: [Symbol] -> ([Symbol], [Symbol]) -- read, remaining +tillEndParen (StartParen:xs) = ([StartParen] ++ i ++ [EndParen] ++ r, re) + where (i, is) = tillEndParen xs + (r, re) = tillEndParen is +tillEndParen (EndParen:xs) = ([], xs) +tillEndParen [] = ([], []) +tillEndParen (x:xs) = (x:r, re) + where (r, re) = tillEndParen xs + +-- Reverse symbols, to get the right precedence level. +revSyms :: [Symbol] -> [Symbol] +revSyms = map swapParens . reverse + where swapParens StartParen = EndParen + swapParens EndParen = StartParen + swapParens x = x + +-- Parse a list of symbols to an expression tree +toExp :: [Symbol] -> Expression +toExp (StartParen:xs) = parenthesise r re + where (r, re) = tillEndParen xs + parenthesise r (Add:xs) = toExp r :+: toExp xs + parenthesise r (Mult:xs) = toExp r :*: toExp xs + parenthesise r [] = toExp r +toExp (x:Add:y) = toExp [x] :+: toExp y +toExp (x:Mult:y) = toExp [x] :*: toExp y +toExp [Num x] = Literal x +toExp [] = Literal 0 + +-- Evaluate an expression +evalExp :: Expression -> Int +evalExp (Literal x) = x +evalExp (x :+: y) = evalExp x + evalExp y +evalExp (x :*: y) = evalExp x * evalExp y +evalExp (Paren x) = evalExp x + +-- Parse the expressions in the given line seperated file. +parseFromFile :: String -> IO [Expression] +parseFromFile s = do + contents <- readFile s + return $ map (toExp . revSyms . toSymbols) (lines contents); + +-- runghc 18a.hs inputs/day18 +main :: IO () +main = do + args <- getArgs; + es <- parseFromFile (head args); + + let ns = map evalExp es; + printf "Answer = %d\n" (sum ns) :: IO (); + + return (); |