/
contexts.lisp
204 lines (163 loc) · 7.92 KB
/
contexts.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
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
(in-package :parser-combinators)
(defparameter *tag-stack* nil)
(defclass context-front ()
((context :accessor context-of :initarg :context :initform nil)
(tags :accessor tags-of :initarg :tags :initform nil)))
(defclass context-common ()
((length :accessor length-of :initarg :length :initform 0)
(front :accessor front-of :initarg :front :initform (make-instance 'context-front))
(cache :accessor cache-of :initarg :cache :initform nil)
(seen-postions :accessor seen-postions-of :initarg :seen-position :initform (make-hash-table))))
(defclass context ()
((common :accessor common-of :initarg :common)
(position :accessor position-of :initarg :position :initform 0)))
(defmethod cache-of ((context context))
(cache-of (common-of context)))
(defmethod length-of ((context context))
(length-of (common-of context)))
(defmethod front-of ((context context))
(front-of (common-of context)))
(defmethod seen-positions-of ((context context))
(seen-postions-of (common-of context)))
(defmethod (setf front-of) (new-value (context context))
(setf (front-of (common-of context)) new-value))
(defmethod position-of ((context-front context-front))
(position-of (context-of context-front)))
(defmacro copy-context (context class &rest additional-arguments)
`(make-instance ,class
,@(iter (with default = (gensym))
(for (initarg accessor) in
'((:common common-of)
(:position position-of)))
(appending (when (eql (getf additional-arguments initarg default)
default)
`(,initarg (,accessor ,context)))))
,@additional-arguments))
(defun note-position (context posn)
(declare (type (integer 0) posn))
(incf (gethash posn (seen-positions-of context) 0)))
(defgeneric context-peek (context))
(defgeneric make-context-at-position (base-context position))
(defmethod make-context-at-position :around ((context context) position)
(let ((cache (cache-of context)))
(note-position context position)
(etypecase cache
(null (call-next-method))
(vector (or (aref cache position)
(setf (aref cache position)
(call-next-method))))
(hash-table (or (gethash position cache)
(setf (gethash position cache)
(call-next-method)))))))
(defgeneric context-next (context)
(:method ((context context))
(make-context-at-position context (1+ (position-of context)))))
(defgeneric context-equal (context1 context2)
(:method ((context1 context) (context2 context))
(or (eq context1 context2)
(and (eq (common-of context1)
(common-of context2))
(eql (position-of context1)
(position-of context2))))))
(defgeneric context-greater (context1 context2)
(:method ((context1 context) (context2 context))
(and (eq (common-of context1)
(common-of context2))
(> (position-of context1)
(position-of context2)))))
(defgeneric update-front-context (context)
(:method ((context context))
(let ((front (front-of context)))
(cond ((or (null (context-of front))
(context-greater context (context-of front)))
(setf (context-of front) context
(tags-of front) (list *tag-stack*)))
((context-equal context (context-of front))
(push *tag-stack* (tags-of front)))))))
(defmethod context-peek :after ((context context))
(update-front-context context))
(defgeneric context-interval (context1 context2 &optional result-type)
(:method :before ((context1 context) (context2 context) &optional result-type)
(declare (ignore result-type))
(assert (eql (common-of context1)
(common-of context2)))
(assert (<= (position-of context1)
(position-of context2))))
(:method ((context1 context) (context2 context) &optional (result-type 'string))
(if (= (position-of context1) (position-of context2))
(coerce nil result-type)
(coerce (iter (for c initially context1 then (context-next c))
(until (eq c context2))
(collect (context-peek c)))
result-type))))
(defclass end-context (context)
())
(defgeneric end-context-p (context)
(:method ((context t))
nil)
(:method ((context end-context))
t))
(defmethod context-next ((context end-context))
(error "Can't go past the end"))
(defmethod context-peek ((context end-context))
(warn "Trying to peek past the end.")
nil)
(defclass list-context (context)
((storage :accessor storage-of :initarg :storage)))
(defmethod make-context-at-position ((base-context list-context) position)
(assert (> position (position-of base-context)))
(assert (<= position (length-of base-context)))
(if (= position (length-of base-context))
(copy-context base-context 'end-context :position position)
(copy-context base-context 'list-context
:storage (nthcdr (- position (position-of base-context)) (storage-of base-context))
:position position)))
(defmethod context-peek ((context list-context))
(car (storage-of context)))
(defclass vector-context-common (context-common)
((storage :accessor storage-of :initarg :storage)))
(defclass vector-context (context)
())
(defmethod storage-of ((context vector-context))
(storage-of (common-of context)))
(defmethod make-context-at-position ((base-context vector-context) position)
(assert (<= position (length-of base-context)))
(if (= position (length-of base-context))
(copy-context base-context 'end-context :position position)
(copy-context base-context 'vector-context :position position)))
(defmethod context-peek ((context vector-context))
(aref (storage-of context) (position-of context)))
(defmethod context-interval ((context1 vector-context) (context2 vector-context) &optional (result-type 'string))
(let ((storage (storage-of context1)))
(coerce (subseq storage (position-of context1) (position-of context2)) result-type)))
(defmethod context-interval ((context1 vector-context) (context2 end-context) &optional (result-type 'string))
(let ((storage (storage-of context1)))
(coerce (subseq storage (position-of context1)) result-type)))
(defvar *default-context-cache* :vector)
(defun make-cache (cache-type length)
(ecase cache-type
((nil) nil)
(:vector (make-array (1+ length) :initial-element nil))
(:hashtable (make-hash-table))))
(defgeneric make-context (sequence &optional cache-type))
(defmethod make-context ((list list) &optional (cache-type *default-context-cache*))
(if (null list)
(make-instance 'end-context :common (make-instance 'context-common))
(make-instance 'list-context
:storage list
:common (make-instance 'context-common
:length (length list)
:cache (make-cache cache-type (length list))))))
(defmethod initialize-instance :around ((context context) &rest initargs &key &allow-other-keys)
(declare (ignore initargs))
(let ((rest (call-next-method)))
(note-position context (slot-value rest 'position))
rest))
(defmethod make-context ((vector vector) &optional (cache-type *default-context-cache*))
(if (zerop (length vector))
(make-instance 'end-context :common (make-instance 'vector-context-common))
(make-instance 'vector-context
:common (make-instance 'vector-context-common
:storage vector
:length (length vector)
:cache (make-cache cache-type (length vector))))))