116 lines
3.1 KiB
Common Lisp
116 lines
3.1 KiB
Common Lisp
|
#!/usr/bin/env -S sbcl --script
|
||
|
|
||
|
(require 'asdf)
|
||
|
(require 'cl-ppcre)
|
||
|
(require 'str)
|
||
|
(require 'iterate)
|
||
|
(require 'uiop)
|
||
|
|
||
|
(defpackage :solution
|
||
|
(:use :cl)
|
||
|
(:import-from :iterate :iter)
|
||
|
(:import-from :uiop :if-let)
|
||
|
(:shadow :directory))
|
||
|
|
||
|
(in-package :solution)
|
||
|
|
||
|
(defclass dir ()
|
||
|
((name
|
||
|
:initarg :name
|
||
|
:reader name)
|
||
|
(parent
|
||
|
:initarg :parent
|
||
|
:reader parent)
|
||
|
(files
|
||
|
:initform nil
|
||
|
:accessor files)
|
||
|
(subdirs
|
||
|
:initform nil
|
||
|
:accessor subdirs)))
|
||
|
|
||
|
(defun mkdir (dir dirname)
|
||
|
(push (cons dirname (make-instance 'dir :name dirname :parent dir)) (subdirs dir)))
|
||
|
|
||
|
(defun touch (dir filename filesize)
|
||
|
(push (cons filename filesize) (files dir)))
|
||
|
|
||
|
(defun find-dir (dir dirname)
|
||
|
(if-let ((d (assoc dirname (subdirs dir) :test 'string=)))
|
||
|
(cdr d)
|
||
|
nil))
|
||
|
|
||
|
(defun depth (dir &optional (depth 0))
|
||
|
(if-let ((p (parent dir)))
|
||
|
(depth p (incf depth))
|
||
|
depth))
|
||
|
|
||
|
(defmethod print-object ((obj dir) stream)
|
||
|
(format stream
|
||
|
"~&name: ~a~%parent: ~a~%depth: ~a~%subdirs: ~a~%files: ~a ~&"
|
||
|
(name obj)
|
||
|
(if (> (depth obj) 0)
|
||
|
(name (parent obj)))
|
||
|
(depth obj)
|
||
|
(files obj)
|
||
|
(subdirs obj)))
|
||
|
|
||
|
(defclass fs ()
|
||
|
((root
|
||
|
:initform (make-instance 'dir :name "root" :parent nil)
|
||
|
:reader root)
|
||
|
(cwd
|
||
|
:initform nil
|
||
|
:accessor cwd)))
|
||
|
|
||
|
(defvar fs-indent-factor 4)
|
||
|
|
||
|
(defun cd (fs dirname)
|
||
|
(with-accessors ((cwd cwd)
|
||
|
(root root))
|
||
|
fs
|
||
|
(cond ((string= dirname "/")
|
||
|
(setf cwd root))
|
||
|
((string= dirname "..")
|
||
|
(setf cwd (parent cwd)))
|
||
|
(t
|
||
|
(setf cwd (find-dir cwd dirname))))))
|
||
|
|
||
|
(defun fs-print-tree (dir stream)
|
||
|
(let ((indent (* (depth dir) fs-indent-factor)))
|
||
|
(iter (iter:for (filename . filesize) in (files dir))
|
||
|
(format stream "~vt - ~a size=~a~%" indent filename filesize))
|
||
|
(iter (iter:for (subdirname . subdir) in (subdirs dir))
|
||
|
(format stream "~vt / ~a~%" indent subdirname)
|
||
|
(fs-print-tree subdir stream))))
|
||
|
|
||
|
(defmethod print-object ((obj fs) stream)
|
||
|
(format stream "/ root~%")
|
||
|
(fs-print-tree (root obj) stream))
|
||
|
|
||
|
(defun parse-input (lines)
|
||
|
(iter (iter:for line in lines)
|
||
|
(iter:for parts = (str:split " " line))
|
||
|
(cond ((string= (nth 1 parts) "cd")
|
||
|
(iter:collect (list 'cd (nth 2 parts))))
|
||
|
((string= (nth 0 parts) "dir")
|
||
|
(iter:collect (list 'dir (nth 1 parts))))
|
||
|
((str:digit? (nth 0 parts))
|
||
|
(iter:collect (list 'file (nth 1 parts) (parse-integer (nth 0 parts))))))))
|
||
|
|
||
|
(defun solution-part-1 (events)
|
||
|
(iter (iter:with fs = (make-instance 'fs))
|
||
|
(iter:for event in events)
|
||
|
(let* ((type (car event))
|
||
|
(dir-or-file-name (cadr event))
|
||
|
(size (if (equal type 'file) (car (last event)) nil)))
|
||
|
(with-accessors ((root root)
|
||
|
(cwd cwd))
|
||
|
fs
|
||
|
(cond ((equal type 'cd)
|
||
|
(cd fs dir-or-file-name))
|
||
|
((equal type 'dir)
|
||
|
(mkdir cwd dir-or-file-name))
|
||
|
((equal type 'file)
|
||
|
(touch cwd dir-or-file-name size))))
|
||
|
(iter:finally (return fs)))))
|