fin-lisp.lisp 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. (ql:quickload "cl-store")
  2. (ql:quickload "cl-ppcre")
  3. ;;;; Reimplementation of my bills tracker in Lisp
  4. (defun reload ()
  5. (load "~/Repos/fin-lisp/fin-lisp.lisp"))
  6. ;;; All records exist in this data structure
  7. ;;; nil on start and loaded in from file
  8. ;;; *records* represents as hash of months,
  9. ;;; where the key is the month stamp, eg 20210701
  10. ;;; and the value is the monthly expenses hash
  11. (defvar *records* (make-hash-table :test 'equalp))
  12. ;;; Lookup table for matching a "pretty" month name
  13. ;;; with a date eg. December2021 -> 202112
  14. (defparameter *month-table* (list (cons 'January 01)
  15. (cons 'February 02)
  16. (cons 'March 03)
  17. (cons 'April 04)
  18. (cons 'May 05)
  19. (cons 'June 06)
  20. (cons 'July 07)
  21. (cons 'August 08)
  22. (cons 'September 09)
  23. (cons 'October 10)
  24. (cons 'November 11)
  25. (cons 'December 12)))
  26. (defun reset-records ()
  27. (setf *records* (make-hash-table :test 'equalp)))
  28. ;; Called like: (add-month '202107)
  29. (defun add-month (month-key)
  30. (setf (gethash month-key *records*) (make-hash-table :test 'equalp))
  31. month-key)
  32. ;;; Taken from practical common lisp
  33. (defun prompt-read (prompt)
  34. (format *query-io* "~a: " prompt)
  35. (force-output *query-io*)
  36. (read-line *query-io*))
  37. (defun prompt-for-expense ()
  38. (list
  39. (prompt-read "Enter expense name")
  40. (parse-integer
  41. (prompt-read "Enter expense value"))))
  42. (defun add-expense-to-month (month)
  43. (if (gethash month *records*)
  44. (let ((innerhash (gethash month *records*))
  45. (exp-l (prompt-for-expense)))
  46. (setf (gethash (first exp-l) innerhash) (second exp-l)))
  47. ;;NIL))
  48. (add-expense-to-month (add-month month))))
  49. ;;; Given key for *records* hash,
  50. ;;; print expenses/values for month
  51. (defun dump-month (month-key)
  52. (format t "~a~C" month-key #\linefeed)
  53. (let ((month-hash)
  54. (exp-keys))
  55. (setf month-hash (gethash month-key *records*))
  56. (setf exp-keys (loop for key being the hash-keys of month-hash collect key))
  57. (dolist (exp-key exp-keys)
  58. (format t "~a : ~a~C" exp-key (gethash exp-key month-hash) #\linefeed))))
  59. ;;; Dump all records.
  60. ;;; This will also be used for data serialization at some point
  61. (defun dump-records ()
  62. (let ((record-key-list (loop for key being the hash-keys of *records* collect key)))
  63. (dolist (month-key record-key-list) (dump-month month-key))))
  64. (defun serialize-records (filename)
  65. (cl-store:store *records* filename))
  66. (defun deserialize-records (filename)
  67. ;(setf *records* (cl-store:restore (pathname filename))))
  68. (setf *records* (cl-store:restore filename)))
  69. ;; Import records from old perl version (plaintext file)
  70. (defun import-records (filename)
  71. (let ((old-file-lines
  72. (with-open-file (stream filename)
  73. (loop for line = (read-line stream nil)
  74. while line
  75. collect line)))
  76. (mre (ppcre:create-scanner "^(.*)[0-9]{4}$"))
  77. ;;(ere (ppcre:create-scanner "^([A-Za-z].*).*$([0-9]{1,4}).*"))
  78. (ere (ppcre:create-scanner "^([A-Z].*)\ -\ \\\$([0-9]{1,4}) - PAID"))
  79. (cur-mon)
  80. (cur-exp))
  81. (loop for line in old-file-lines
  82. do (progn
  83. ;;; (if (ppcre:scan mre line) (progn
  84. ;;; (let ((result)
  85. ;;; (month-num))
  86. ;;; (setf result (ppcre:register-groups-bind (first second) (mre line) :sharedp t (list first second)))
  87. ;;; (setf month-num (cdr (assoc '(first result) *month-table*)))
  88. ;;; (setf cur-mon (concatenate (second result) month-num)))))
  89. (if (ppcre:scan mre line) (setf cur-mon line))
  90. (if (ppcre:scan ere line)
  91. (progn
  92. (setf cur-exp (ppcre:register-groups-bind (first second) (ere line) :sharedp t (list first second)))
  93. (print cur-exp)
  94. (if (gethash cur-mon *records*)
  95. (let ((innerhash (gethash cur-mon *records*)))
  96. (setf (gethash (first cur-exp) innerhash) (second cur-exp))))
  97. (if (not (gethash cur-mon *records*))
  98. (progn
  99. (add-month cur-mon)
  100. (let ((innerhash (gethash cur-mon *records*)))
  101. (setf (gethash (first cur-exp) innerhash) (second cur-exp)))))))))))
  102. ;; Entry point
  103. (defun main ()
  104. (format t "Available options:~C" #\linefeed)
  105. (format t "1. Enter expense~C" #\linefeed)
  106. (format t "2. Display month~C" #\linefeed)
  107. (format t "3. Write records~C" #\linefeed)
  108. (format t "4. Read records~C" #\linefeed)
  109. (format t "5. Quit~C" #\linefeed)
  110. (format t "6. Import Records~C" #\linefeed)
  111. (let
  112. ((answer (prompt-read "Select an option")))
  113. (if (string= answer "1")
  114. (add-expense-to-month
  115. (read-from-string
  116. (prompt-read "Enter month"))))
  117. (if (string= answer "2")
  118. (dump-month
  119. (prompt-read "Enter month")))
  120. (if (string= answer "3")
  121. (serialize-records (prompt-read "Enter filename")))
  122. (if (string= answer "4")
  123. (deserialize-records (prompt-read "Enter filename")))
  124. (if (string= answer "5")
  125. (quit))
  126. (if (string= answer "6")
  127. (import-records (prompt-read "Enter filename"))))
  128. (main))