-
Notifications
You must be signed in to change notification settings - Fork 0
/
date.lisp
executable file
·145 lines (120 loc) · 3.81 KB
/
date.lisp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
;;;; Hey, Emacs, this is a -*- Mode: Lisp; Syntax: Common-Lisp -*- file!
;;;;
;;;; APL is like a perfect diamond: if you add anything to it, it becomes flawed. In contrast, Lisp is like a bean bag--you can sit on a bean bag and squash it, but it will always rise again.
;;;; -- Joel Moses (attributed)
;;;;
;;;; Name: date.lisp
;;;;
;;;; Started: Wed Nov 15 18:54:09 2023
;;;; Modifications:
;;;;
;;;; Purpose:
;;;;
;;;;
;;;;
;;;; Calling Sequence:
;;;;
;;;;
;;;; Inputs:
;;;;
;;;; Outputs:
;;;;
;;;; Example:
;;;;
;;;; Notes: SETF expander examples from Slade ch. 5
;;;;
;;;;
(load "/home/slytobias/lisp/packages/core.lisp")
(load "/home/slytobias/lisp/packages/test.lisp")
(defpackage :date (:use :common-lisp :core :test))
(in-package :date)
(defun make-date (month day year)
(list month day year))
(defun month (date) (first date))
(defun day (date) (second date))
(defun year (date) (third date))
(defun leap-year-p (year)
(cond ((zerop (mod year 400)) t)
((zerop (mod year 100)) nil)
(t (zerop (mod year 4)))) )
(defun legal-date-p (date)
(cond ((or (> (first date) 12)
(< (first date) 1))
'illegal-month)
((or (> (second date) 31)
(< (second date) 1)
(and (= (first date) 2)
(not (leap-year-p (third date)))
(> (second date) 28))
(and (= (first date) 2)
(leap-year-p (third date))
(> (second date) 29))
(and (member (first date) '(4 6 9 11))
(> (second date) 30)))
'illegal-day)
((not (plusp (third date))) 'illegal-year)
(t 'ok)))
(defun legal-month-p (date)
(<= 1 (month date) 12))
(defun legal-year-p (date)
(plusp (year date)))
(defun legal-day-p (date)
(let ((month (month date))
(day (day date))
(year (year date)))
(cond ((< day 1) nil)
((= month 2) (if (leap-year-p year)
(<= day 29)
(<= day 28)))
((member month '(4 6 9 11)) (<= day 30))
(t (<= day 31)))))
(defun legal-date-p (date)
(cond ((not (legal-month-p date)) 'illegal-month)
((not (legal-day-p date)) 'illegal-day)
((not (legal-year-p date)) 'illegal-year)
(t 'ok)))
;;;
;;; I.
;;;
(defun (setf month) (month date) ; <-- Order!
(setf (first date) month))
; (setf (month date) month)) ; Must already be SETFable
;;;
;;; II.
;;;
(defun set-day (date day) ; <-- Order!
(setf (second date) day))
(defsetf day set-day)
;;;
;;; III.
;;;
(defsetf year (date) (year)
`(setf (third ,date) ,year))
;; define-setf-expander
;; (get-setf-expansion '(day d))
(defpackage :clos-date (:use :common-lisp :core :test))
(in-package :clos-date)
(defun leap-year-p (year)
(cond ((zerop (mod year 400)) t)
((zerop (mod year 100)) nil)
(t (zerop (mod year 4)))) )
(defun month-length (month year)
(ccase month
((4 6 9 11) 30)
(2 (if (leap-year-p year) 29 28))
((1 3 5 7 8 10 12) 31)))
;; (deftype month () '(integer 1 12)) ; Should be 0-11? See above.
;; (deftype day (m y) `(integer 1 ,(month-length m y)))
;; (deftype year () '(integer 1900 *)) ; ????
(defclass date ()
((month :accessor month :initarg :month :type (integer 1 12))
(day :accessor day :initarg :day :type (integer 1 31))
(year :accessor year :initarg :year :type (integer 0))))
(defun legal-date-p (date)
(with-slots (year month day) date
(check-type year (integer 0 *)) ; Allow pre-Gregorian?
(check-type month (integer 1 12))
(assert (typep day `(integer 1 ,(month-length month year))) (day) "Day should be between 1 and ~D." (month-length month year))))
(defmethod initialize-instance :after ((d date) &rest init-args)
(declare (ignore init-args))
(legal-date-p d))