|
@@ -0,0 +1,80 @@
|
|
1
|
+module Day7.Interpreter (
|
|
2
|
+ buildTree,
|
|
3
|
+ mkdir,
|
|
4
|
+ Directory (..),
|
|
5
|
+ calculateSize,
|
|
6
|
+ filterDirectories,
|
|
7
|
+ sizeOfDirectories,
|
|
8
|
+ sumUp,
|
|
9
|
+) where
|
|
10
|
+
|
|
11
|
+import Day7.Parser
|
|
12
|
+import Data.Map (Map)
|
|
13
|
+import qualified Data.Map as Map
|
|
14
|
+
|
|
15
|
+data Directory = Directory
|
|
16
|
+ { sub :: Map String Directory
|
|
17
|
+ , files :: Map String Int
|
|
18
|
+ , isRoot :: Bool
|
|
19
|
+} deriving (Eq)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+instance Show Directory where
|
|
23
|
+ show d = "DIR " ++ show (Map.toList $ files d) ++ " - " ++ show (Map.toList $ sub d) ++ "\n"
|
|
24
|
+
|
|
25
|
+mkdir :: Directory
|
|
26
|
+mkdir = Directory { sub = Map.empty, files = Map.empty, isRoot = False }
|
|
27
|
+
|
|
28
|
+rootDirectory :: Directory
|
|
29
|
+rootDirectory = mkdir { isRoot = True }
|
|
30
|
+
|
|
31
|
+buildTree :: [TerminalCommand] -> Directory
|
|
32
|
+buildTree commands = fst (buildTree' rootDirectory commands)
|
|
33
|
+ where
|
|
34
|
+ -- executes terminal commands and returns all remaining commands
|
|
35
|
+ buildTree' :: Directory -> [TerminalCommand] -> (Directory, [TerminalCommand])
|
|
36
|
+ buildTree' dir [] = (dir, [])
|
|
37
|
+ buildTree' dir (command:cs) = case command of
|
|
38
|
+ Listing entries ->
|
|
39
|
+ let asFiles = Map.fromList $ toFiles entries
|
|
40
|
+ newDir = dir { files = asFiles }
|
|
41
|
+ in buildTree' newDir cs -- update current directory and return all remaining commands
|
|
42
|
+ In dirName ->
|
|
43
|
+ let subFolders = sub dir
|
|
44
|
+ selectedDir = Map.findWithDefault mkdir dirName subFolders -- select or create folder
|
|
45
|
+ (replacement, rest) = buildTree' selectedDir cs -- recurse down
|
|
46
|
+ updatedSub = Map.insert dirName replacement subFolders -- update folder the map of the current directory
|
|
47
|
+ in buildTree' dir { sub = updatedSub} rest -- continue with updated folder and rest of commands
|
|
48
|
+ Out -> (dir, cs) -- return to previous caller and return all remaining commands
|
|
49
|
+ Root -> if isRoot dir
|
|
50
|
+ then buildTree' dir cs -- return to previous caller as long is current directory is not root
|
|
51
|
+ else (dir, command:cs) -- keep the Root command because it is not yet completed
|
|
52
|
+
|
|
53
|
+-- convert to file-pairs and ignore directories
|
|
54
|
+toFiles :: [ListingEntry] -> [(String, Int)]
|
|
55
|
+toFiles ((FileListing name size):rest) = (name, size): toFiles rest
|
|
56
|
+toFiles (_:rest) = toFiles rest
|
|
57
|
+toFiles [] = []
|
|
58
|
+
|
|
59
|
+calculateSize :: Directory -> Int
|
|
60
|
+calculateSize dir = sum (Map.elems (files dir)) + sum sizes
|
|
61
|
+ where
|
|
62
|
+ subFolders = Map.elems (sub dir)
|
|
63
|
+ sizes = map calculateSize subFolders
|
|
64
|
+
|
|
65
|
+flatten :: Directory -> [(String, Directory)]
|
|
66
|
+flatten dir = flatten' ("/", dir)
|
|
67
|
+ where
|
|
68
|
+ flatten' (name, d) = (name, d) : concatMap flatten' (Map.toList (sub d))
|
|
69
|
+
|
|
70
|
+sizeOfDirectories :: Directory -> [(String, Int)]
|
|
71
|
+sizeOfDirectories dir = map withSize allDirectories
|
|
72
|
+ where
|
|
73
|
+ allDirectories = flatten dir
|
|
74
|
+ withSize (name, directory) = (name, calculateSize directory)
|
|
75
|
+
|
|
76
|
+filterDirectories :: (Int -> Bool) -> Directory -> [(String, Int)]
|
|
77
|
+filterDirectories predicate dir = filter (\(_, size) -> predicate size) $ sizeOfDirectories dir
|
|
78
|
+
|
|
79
|
+sumUp :: [(String, Int)] -> Int
|
|
80
|
+sumUp = sum . map snd
|