(ql:quickload "cl-store") (ql:quickload "cl-ppcre") ;;;; Reimplementation of my bills tracker in Lisp (defun reload () (load "~/Repos/fin-lisp/fin-lisp.lisp")) ;;; All records exist in this data structure ;;; nil on start and loaded in from file ;;; *records* represents as hash of months, ;;; where the key is the month stamp, eg 20210701 ;;; and the value is the monthly expenses hash (defvar *records* (make-hash-table :test 'equalp)) (defun reset-records () (setf *records* (make-hash-table :test 'equalp))) ;; Called like: (add-month '202107) (defun add-month (month-key) (setf (gethash month-key *records*) (make-hash-table :test 'equalp)) month-key) ;;; Taken from practical common lisp (defun prompt-read (prompt) (format *query-io* "~a: " prompt) (force-output *query-io*) (read-line *query-io*)) (defun prompt-for-expense () (list (prompt-read "Enter expense name") (parse-integer (prompt-read "Enter expense value")))) (defun add-expense-to-month (month) (if (gethash month *records*) (let ((innerhash (gethash month *records*)) (exp-l (prompt-for-expense))) (setf (gethash (first exp-l) innerhash) (second exp-l))) ;;NIL)) (add-expense-to-month (add-month month)))) ;;; Given key for *records* hash, ;;; print expenses/values for month (defun dump-month (month-key) (format t "~a~C" month-key #\linefeed) (let ((month-hash) (exp-keys)) (setf month-hash (gethash month-key *records*)) (setf exp-keys (loop for key being the hash-keys of month-hash collect key)) (dolist (exp-key exp-keys) (format t "~a : ~a~C" exp-key (gethash exp-key month-hash) #\linefeed)))) ;;; Dump all records. ;;; This will also be used for data serialization at some point (defun dump-records () (let ((record-key-list (loop for key being the hash-keys of *records* collect key))) (dolist (month-key record-key-list) (dump-month month-key)))) (defun serialize-records (filename) (cl-store:store *records* filename)) (defun deserialize-records (filename) ;(setf *records* (cl-store:restore (pathname filename)))) (setf *records* (cl-store:restore filename))) ;; Import records from old perl version (plaintext file) (defun import-records (filename) (let ((old-file-lines (with-open-file (stream filename) (loop for line = (read-line stream nil) while line collect line))) (mre (ppcre:create-scanner "^(.*)[0-9]{4}$")) ;;(ere (ppcre:create-scanner "^([A-Za-z].*).*$([0-9]{1,4}).*")) (ere (ppcre:create-scanner "^([A-Z].*)\ -\ \\\$([0-9]{1,4}) - PAID")) (cur-mon) (cur-exp)) (loop for line in old-file-lines do (progn ;;; (if (ppcre:scan mre line) (progn ;;; (let ((result) ;;; (month-num)) ;;; (setf result (ppcre:register-groups-bind (first second) (mre line) :sharedp t (list first second))) ;;; (setf month-num (cdr (assoc '(first result) *month-table*))) ;;; (setf cur-mon (concatenate (second result) month-num))))) (if (ppcre:scan mre line) (setf cur-mon line)) (if (ppcre:scan ere line) (progn (setf cur-exp (ppcre:register-groups-bind (first second) (ere line) :sharedp t (list first second))) (print cur-exp) (if (gethash cur-mon *records*) (let ((innerhash (gethash cur-mon *records*))) (setf (gethash (first cur-exp) innerhash) (second cur-exp)))) (if (not (gethash cur-mon *records*)) (progn (add-month cur-mon) (let ((innerhash (gethash cur-mon *records*))) (setf (gethash (first cur-exp) innerhash) (second cur-exp))))))))))) ;; Entry point (defun main () (format t "Available options:~C" #\linefeed) (format t "1. Enter expense~C" #\linefeed) (format t "2. Display month~C" #\linefeed) (format t "3. Write records~C" #\linefeed) (format t "4. Read records~C" #\linefeed) (format t "5. Quit~C" #\linefeed) (format t "6. Import Records~C" #\linefeed) (let ((answer (prompt-read "Select an option"))) (if (string= answer "1") (add-expense-to-month (prompt-read "Enter month"))) (if (string= answer "2") (dump-month (prompt-read "Enter month"))) (if (string= answer "3") (serialize-records (prompt-read "Enter filename"))) (if (string= answer "4") (deserialize-records (prompt-read "Enter filename"))) (if (string= answer "5") (quit)) (if (string= answer "6") (import-records (prompt-read "Enter filename")))) (main))